summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/usb/error-codes.txt5
-rw-r--r--drivers/char/drm/drm_context.c5
-rw-r--r--drivers/hwmon/w83792d.c25
-rw-r--r--drivers/usb/atm/cxacru.c3
-rw-r--r--drivers/usb/core/hcd-pci.c3
-rw-r--r--drivers/usb/core/hcd.c15
-rw-r--r--drivers/usb/core/hcd.h7
-rw-r--r--drivers/usb/host/ehci-pci.c46
-rw-r--r--drivers/usb/host/ehci-q.c24
-rw-r--r--drivers/usb/host/ehci-sched.c18
-rw-r--r--drivers/usb/host/ohci-hcd.c6
-rw-r--r--drivers/usb/host/ohci-hub.c24
-rw-r--r--drivers/usb/host/ohci-pci.c27
-rw-r--r--drivers/usb/host/uhci-hcd.c6
-rw-r--r--include/linux/mm.h1
-rw-r--r--mm/memory.c36
16 files changed, 207 insertions, 44 deletions
diff --git a/Documentation/usb/error-codes.txt b/Documentation/usb/error-codes.txt
index 1e36f16..867f4c3 100644
--- a/Documentation/usb/error-codes.txt
+++ b/Documentation/usb/error-codes.txt
@@ -46,8 +46,9 @@ USB-specific:
-EMSGSIZE (a) endpoint maxpacket size is zero; it is not usable
in the current interface altsetting.
- (b) ISO packet is biger than endpoint maxpacket
- (c) requested data transfer size is invalid (negative)
+ (b) ISO packet is larger than the endpoint maxpacket.
+ (c) requested data transfer length is invalid: negative
+ or too large for the host controller.
-ENOSPC This request would overcommit the usb bandwidth reserved
for periodic transfers (interrupt, isochronous).
diff --git a/drivers/char/drm/drm_context.c b/drivers/char/drm/drm_context.c
index bdd168d..bd958d6 100644
--- a/drivers/char/drm/drm_context.c
+++ b/drivers/char/drm/drm_context.c
@@ -432,7 +432,10 @@ int drm_addctx(struct inode *inode, struct file *filp,
if (ctx.handle != DRM_KERNEL_CONTEXT) {
if (dev->driver->context_ctor)
- dev->driver->context_ctor(dev, ctx.handle);
+ if (!dev->driver->context_ctor(dev, ctx.handle)) {
+ DRM_DEBUG( "Running out of ctxs or memory.\n");
+ return -ENOMEM;
+ }
}
ctx_entry = drm_alloc(sizeof(*ctx_entry), DRM_MEM_CTXLIST);
diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c
index 4be59db..1ba0726 100644
--- a/drivers/hwmon/w83792d.c
+++ b/drivers/hwmon/w83792d.c
@@ -193,6 +193,7 @@ static const u8 W83792D_REG_LEVELS[3][4] = {
0xE2 } /* (bit3-0) SmartFanII: Fan3 Level 3 */
};
+#define W83792D_REG_GPIO_EN 0x1A
#define W83792D_REG_CONFIG 0x40
#define W83792D_REG_VID_FANDIV 0x47
#define W83792D_REG_CHIPID 0x49
@@ -257,7 +258,7 @@ DIV_TO_REG(long val)
{
int i;
val = SENSORS_LIMIT(val, 1, 128) >> 1;
- for (i = 0; i < 6; i++) {
+ for (i = 0; i < 7; i++) {
if (val == 0)
break;
val >>= 1;
@@ -1282,8 +1283,8 @@ w83792d_detect(struct i2c_adapter *adapter, int address, int kind)
w83792d_init_client(new_client);
/* A few vars need to be filled upon startup */
- for (i = 1; i <= 7; i++) {
- data->fan_min[i - 1] = w83792d_read_value(new_client,
+ for (i = 0; i < 7; i++) {
+ data->fan_min[i] = w83792d_read_value(new_client,
W83792D_REG_FAN_MIN[i]);
}
@@ -1306,10 +1307,20 @@ w83792d_detect(struct i2c_adapter *adapter, int address, int kind)
device_create_file_fan(new_client, 1);
device_create_file_fan(new_client, 2);
device_create_file_fan(new_client, 3);
- device_create_file_fan(new_client, 4);
- device_create_file_fan(new_client, 5);
- device_create_file_fan(new_client, 6);
- device_create_file_fan(new_client, 7);
+
+ /* Read GPIO enable register to check if pins for fan 4,5 are used as
+ GPIO */
+ val1 = w83792d_read_value(new_client, W83792D_REG_GPIO_EN);
+ if (!(val1 & 0x40))
+ device_create_file_fan(new_client, 4);
+ if (!(val1 & 0x20))
+ device_create_file_fan(new_client, 5);
+
+ val1 = w83792d_read_value(new_client, W83792D_REG_PIN);
+ if (val1 & 0x40)
+ device_create_file_fan(new_client, 6);
+ if (val1 & 0x04)
+ device_create_file_fan(new_client, 7);
device_create_file_temp1(new_client); /* Temp1 */
device_create_file_temp_add(new_client, 2); /* Temp2 */
diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c
index 79861ee..9d59dc6 100644
--- a/drivers/usb/atm/cxacru.c
+++ b/drivers/usb/atm/cxacru.c
@@ -787,6 +787,9 @@ static const struct usb_device_id cxacru_usb_ids[] = {
{ /* V = Conexant P = ADSL modem (Hasbani project) */
USB_DEVICE(0x0572, 0xcb00), .driver_info = (unsigned long) &cxacru_cb00
},
+ { /* V = Conexant P = ADSL modem (Well PTI-800 */
+ USB_DEVICE(0x0572, 0xcb02), .driver_info = (unsigned long) &cxacru_cb00
+ },
{ /* V = Conexant P = ADSL modem */
USB_DEVICE(0x0572, 0xcb01), .driver_info = (unsigned long) &cxacru_cb00
},
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index 5131d88e..29b5b2a 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -219,6 +219,7 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message)
goto done;
}
}
+ synchronize_irq(dev->irq);
/* FIXME until the generic PM interfaces change a lot more, this
* can't use PCI D1 and D2 states. For example, the confusion
@@ -392,7 +393,7 @@ int usb_hcd_pci_resume (struct pci_dev *dev)
dev->dev.power.power_state = PMSG_ON;
- hcd->saw_irq = 0;
+ clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
if (hcd->driver->resume) {
retval = hcd->driver->resume(hcd);
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 5e5f65a..da24c31 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -1315,11 +1315,12 @@ static int hcd_unlink_urb (struct urb *urb, int status)
* finish unlinking the initial failed usb_set_address()
* or device descriptor fetch.
*/
- if (!hcd->saw_irq && hcd->self.root_hub != urb->dev) {
+ if (!test_bit(HCD_FLAG_SAW_IRQ, &hcd->flags)
+ && hcd->self.root_hub != urb->dev) {
dev_warn (hcd->self.controller, "Unlink after no-IRQ? "
"Controller is probably using the wrong IRQ."
"\n");
- hcd->saw_irq = 1;
+ set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
}
urb->status = status;
@@ -1649,13 +1650,15 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs * r)
struct usb_hcd *hcd = __hcd;
int start = hcd->state;
- if (start == HC_STATE_HALT)
+ if (unlikely(start == HC_STATE_HALT ||
+ !test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)))
return IRQ_NONE;
if (hcd->driver->irq (hcd, r) == IRQ_NONE)
return IRQ_NONE;
- hcd->saw_irq = 1;
- if (hcd->state == HC_STATE_HALT)
+ set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
+
+ if (unlikely(hcd->state == HC_STATE_HALT))
usb_hc_died (hcd);
return IRQ_HANDLED;
}
@@ -1768,6 +1771,8 @@ int usb_add_hcd(struct usb_hcd *hcd,
dev_info(hcd->self.controller, "%s\n", hcd->product_desc);
+ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+
/* till now HC has been in an indeterminate state ... */
if (hcd->driver->reset && (retval = hcd->driver->reset(hcd)) < 0) {
dev_err(hcd->self.controller, "can't reset\n");
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
index 24a62a2..c8a1b35 100644
--- a/drivers/usb/core/hcd.h
+++ b/drivers/usb/core/hcd.h
@@ -72,7 +72,12 @@ struct usb_hcd { /* usb_bus.hcpriv points to this */
* hardware info/state
*/
const struct hc_driver *driver; /* hw-specific hooks */
- unsigned saw_irq : 1;
+
+ /* Flags that need to be manipulated atomically */
+ unsigned long flags;
+#define HCD_FLAG_HW_ACCESSIBLE 0x00000001
+#define HCD_FLAG_SAW_IRQ 0x00000002
+
unsigned can_wakeup:1; /* hw supports wakeup? */
unsigned remote_wakeup:1;/* sw should use wakeup? */
unsigned rh_registered:1;/* is root hub registered? */
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
index 441c260..13f73a8 100644
--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -121,8 +121,8 @@ static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev)
return 0;
}
-/* called by khubd or root hub (re)init threads; leaves HC in halt state */
-static int ehci_pci_reset(struct usb_hcd *hcd)
+/* called during probe() after chip reset completes */
+static int ehci_pci_setup(struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
@@ -141,6 +141,11 @@ static int ehci_pci_reset(struct usb_hcd *hcd)
if (retval)
return retval;
+ /* data structure init */
+ retval = ehci_init(hcd);
+ if (retval)
+ return retval;
+
/* NOTE: only the parts below this line are PCI-specific */
switch (pdev->vendor) {
@@ -154,7 +159,8 @@ static int ehci_pci_reset(struct usb_hcd *hcd)
/* AMD8111 EHCI doesn't work, according to AMD errata */
if (pdev->device == 0x7463) {
ehci_info(ehci, "ignoring AMD8111 (errata)\n");
- return -EIO;
+ retval = -EIO;
+ goto done;
}
break;
case PCI_VENDOR_ID_NVIDIA:
@@ -207,9 +213,8 @@ static int ehci_pci_reset(struct usb_hcd *hcd)
/* REVISIT: per-port wake capability (PCI 0x62) currently unused */
retval = ehci_pci_reinit(ehci, pdev);
-
- /* finish init */
- return ehci_init(hcd);
+done:
+ return retval;
}
/*-------------------------------------------------------------------------*/
@@ -228,14 +233,36 @@ static int ehci_pci_reset(struct usb_hcd *hcd)
static int ehci_pci_suspend(struct usb_hcd *hcd, pm_message_t message)
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ unsigned long flags;
+ int rc = 0;
if (time_before(jiffies, ehci->next_statechange))
msleep(10);
+ /* Root hub was already suspended. Disable irq emission and
+ * mark HW unaccessible, bail out if RH has been resumed. Use
+ * the spinlock to properly synchronize with possible pending
+ * RH suspend or resume activity.
+ *
+ * This is still racy as hcd->state is manipulated outside of
+ * any locks =P But that will be a different fix.
+ */
+ spin_lock_irqsave (&ehci->lock, flags);
+ if (hcd->state != HC_STATE_SUSPENDED) {
+ rc = -EINVAL;
+ goto bail;
+ }
+ writel (0, &ehci->regs->intr_enable);
+ (void)readl(&ehci->regs->intr_enable);
+
+ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ bail:
+ spin_unlock_irqrestore (&ehci->lock, flags);
+
// could save FLADJ in case of Vaux power loss
// ... we'd only use it to handle clock skew
- return 0;
+ return rc;
}
static int ehci_pci_resume(struct usb_hcd *hcd)
@@ -251,6 +278,9 @@ static int ehci_pci_resume(struct usb_hcd *hcd)
if (time_before(jiffies, ehci->next_statechange))
msleep(100);
+ /* Mark hardware accessible again as we are out of D3 state by now */
+ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+
/* If CF is clear, we lost PCI Vaux power and need to restart. */
if (readl(&ehci->regs->configured_flag) != FLAG_CF)
goto restart;
@@ -319,7 +349,7 @@ static const struct hc_driver ehci_pci_hc_driver = {
/*
* basic lifecycle operations
*/
- .reset = ehci_pci_reset,
+ .reset = ehci_pci_setup,
.start = ehci_run,
#ifdef CONFIG_PM
.suspend = ehci_pci_suspend,
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 5bb872c..bf03ec0 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -912,6 +912,7 @@ submit_async (
int epnum;
unsigned long flags;
struct ehci_qh *qh = NULL;
+ int rc = 0;
qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list);
epnum = ep->desc.bEndpointAddress;
@@ -926,21 +927,28 @@ submit_async (
#endif
spin_lock_irqsave (&ehci->lock, flags);
+ if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE,
+ &ehci_to_hcd(ehci)->flags))) {
+ rc = -ESHUTDOWN;
+ goto done;
+ }
+
qh = qh_append_tds (ehci, urb, qtd_list, epnum, &ep->hcpriv);
+ if (unlikely(qh == NULL)) {
+ rc = -ENOMEM;
+ goto done;
+ }
/* Control/bulk operations through TTs don't need scheduling,
* the HC and TT handle it when the TT has a buffer ready.
*/
- if (likely (qh != NULL)) {
- if (likely (qh->qh_state == QH_STATE_IDLE))
- qh_link_async (ehci, qh_get (qh));
- }
+ if (likely (qh->qh_state == QH_STATE_IDLE))
+ qh_link_async (ehci, qh_get (qh));
+ done:
spin_unlock_irqrestore (&ehci->lock, flags);
- if (unlikely (qh == NULL)) {
+ if (unlikely (qh == NULL))
qtd_list_free (ehci, urb, qtd_list);
- return -ENOMEM;
- }
- return 0;
+ return rc;
}
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index f0c8aa1..57e7737 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -602,6 +602,12 @@ static int intr_submit (
spin_lock_irqsave (&ehci->lock, flags);
+ if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE,
+ &ehci_to_hcd(ehci)->flags))) {
+ status = -ESHUTDOWN;
+ goto done;
+ }
+
/* get qh and force any scheduling errors */
INIT_LIST_HEAD (&empty);
qh = qh_append_tds (ehci, urb, &empty, epnum, &ep->hcpriv);
@@ -1456,7 +1462,11 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb,
/* schedule ... need to lock */
spin_lock_irqsave (&ehci->lock, flags);
- status = iso_stream_schedule (ehci, urb, stream);
+ if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE,
+ &ehci_to_hcd(ehci)->flags)))
+ status = -ESHUTDOWN;
+ else
+ status = iso_stream_schedule (ehci, urb, stream);
if (likely (status == 0))
itd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
spin_unlock_irqrestore (&ehci->lock, flags);
@@ -1815,7 +1825,11 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb,
/* schedule ... need to lock */
spin_lock_irqsave (&ehci->lock, flags);
- status = iso_stream_schedule (ehci, urb, stream);
+ if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE,
+ &ehci_to_hcd(ehci)->flags)))
+ status = -ESHUTDOWN;
+ else
+ status = iso_stream_schedule (ehci, urb, stream);
if (status == 0)
sitd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
spin_unlock_irqrestore (&ehci->lock, flags);
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 5c0c6c8..bf1d9ab 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -115,7 +115,7 @@
/*-------------------------------------------------------------------------*/
-// #define OHCI_VERBOSE_DEBUG /* not always helpful */
+#undef OHCI_VERBOSE_DEBUG /* not always helpful */
/* For initializing controller (mask in an HCFS mode too) */
#define OHCI_CONTROL_INIT OHCI_CTRL_CBSR
@@ -253,6 +253,10 @@ static int ohci_urb_enqueue (
spin_lock_irqsave (&ohci->lock, flags);
/* don't submit to a dead HC */
+ if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ retval = -ENODEV;
+ goto fail;
+ }
if (!HC_IS_RUNNING(hcd->state)) {
retval = -ENODEV;
goto fail;
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
index e01e77b..72e3b12 100644
--- a/drivers/usb/host/ohci-hub.c
+++ b/drivers/usb/host/ohci-hub.c
@@ -53,6 +53,11 @@ static int ohci_bus_suspend (struct usb_hcd *hcd)
spin_lock_irqsave (&ohci->lock, flags);
+ if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) {
+ spin_unlock_irqrestore (&ohci->lock, flags);
+ return -ESHUTDOWN;
+ }
+
ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
switch (ohci->hc_control & OHCI_CTRL_HCFS) {
case OHCI_USB_RESUME:
@@ -140,11 +145,19 @@ static int ohci_bus_resume (struct usb_hcd *hcd)
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
u32 temp, enables;
int status = -EINPROGRESS;
+ unsigned long flags;
if (time_before (jiffies, ohci->next_statechange))
msleep(5);
- spin_lock_irq (&ohci->lock);
+ spin_lock_irqsave (&ohci->lock, flags);
+
+ if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) {
+ spin_unlock_irqrestore (&ohci->lock, flags);
+ return -ESHUTDOWN;
+ }
+
+
ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
if (ohci->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) {
@@ -179,7 +192,7 @@ static int ohci_bus_resume (struct usb_hcd *hcd)
ohci_dbg (ohci, "lost power\n");
status = -EBUSY;
}
- spin_unlock_irq (&ohci->lock);
+ spin_unlock_irqrestore (&ohci->lock, flags);
if (status == -EBUSY) {
(void) ohci_init (ohci);
return ohci_restart (ohci);
@@ -297,8 +310,8 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
/* handle autosuspended root: finish resuming before
* letting khubd or root hub timer see state changes.
*/
- if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER
- || !HC_IS_RUNNING(hcd->state)) {
+ if (unlikely((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER
+ || !HC_IS_RUNNING(hcd->state))) {
can_suspend = 0;
goto done;
}
@@ -508,6 +521,9 @@ static int ohci_hub_control (
u32 temp;
int retval = 0;
+ if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)))
+ return -ESHUTDOWN;
+
switch (typeReq) {
case ClearHubFeature:
switch (wValue) {
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c
index 5f22e65..1b09dde 100644
--- a/drivers/usb/host/ohci-pci.c
+++ b/drivers/usb/host/ohci-pci.c
@@ -105,13 +105,36 @@ ohci_pci_start (struct usb_hcd *hcd)
static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message)
{
- /* root hub was already suspended */
- return 0;
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ unsigned long flags;
+ int rc = 0;
+
+ /* Root hub was already suspended. Disable irq emission and
+ * mark HW unaccessible, bail out if RH has been resumed. Use
+ * the spinlock to properly synchronize with possible pending
+ * RH suspend or resume activity.
+ *
+ * This is still racy as hcd->state is manipulated outside of
+ * any locks =P But that will be a different fix.
+ */
+ spin_lock_irqsave (&ohci->lock, flags);
+ if (hcd->state != HC_STATE_SUSPENDED) {
+ rc = -EINVAL;
+ goto bail;
+ }
+ ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
+ (void)ohci_readl(ohci, &ohci->regs->intrdisable);
+ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ bail:
+ spin_unlock_irqrestore (&ohci->lock, flags);
+
+ return rc;
}
static int ohci_pci_resume (struct usb_hcd *hcd)
{
+ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
usb_hcd_resume_root_hub(hcd);
return 0;
}
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index d33ce39..ed55013 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -717,6 +717,7 @@ static int uhci_suspend(struct usb_hcd *hcd, pm_message_t message)
* at the source, so we must turn off PIRQ.
*/
pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, 0);
+ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
uhci->hc_inaccessible = 1;
hcd->poll_rh = 0;
@@ -733,6 +734,11 @@ static int uhci_resume(struct usb_hcd *hcd)
dev_dbg(uhci_dev(uhci), "%s\n", __FUNCTION__);
+ /* We aren't in D3 state anymore, we do that even if dead as I
+ * really don't want to keep a stale HCD_FLAG_HW_ACCESSIBLE=0
+ */
+ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+
if (uhci->rh_state == UHCI_RH_RESET) /* Dead */
return 0;
spin_lock_irq(&uhci->lock);
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 0e73f15..29f02d8 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -956,6 +956,7 @@ struct page *vmalloc_to_page(void *addr);
unsigned long vmalloc_to_pfn(void *addr);
int remap_pfn_range(struct vm_area_struct *, unsigned long addr,
unsigned long pfn, unsigned long size, pgprot_t);
+int vm_insert_page(struct vm_area_struct *, unsigned long addr, struct page *);
struct page *follow_page(struct vm_area_struct *, unsigned long address,
unsigned int foll_flags);
diff --git a/mm/memory.c b/mm/memory.c
index 8d10b55..4b4fc3a 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -1172,7 +1172,7 @@ static int insert_page(struct mm_struct *mm, unsigned long addr, struct page *pa
spinlock_t *ptl;
retval = -EINVAL;
- if (PageAnon(page) || !PageReserved(page))
+ if (PageAnon(page))
goto out;
retval = -ENOMEM;
flush_dcache_page(page);
@@ -1197,6 +1197,35 @@ out:
}
/*
+ * This allows drivers to insert individual pages they've allocated
+ * into a user vma.
+ *
+ * The page has to be a nice clean _individual_ kernel allocation.
+ * If you allocate a compound page, you need to have marked it as
+ * such (__GFP_COMP), or manually just split the page up yourself
+ * (which is mainly an issue of doing "set_page_count(page, 1)" for
+ * each sub-page, and then freeing them one by one when you free
+ * them rather than freeing it as a compound page).
+ *
+ * NOTE! Traditionally this was done with "remap_pfn_range()" which
+ * took an arbitrary page protection parameter. This doesn't allow
+ * that. Your vma protection will have to be set up correctly, which
+ * means that if you want a shared writable mapping, you'd better
+ * ask for a shared writable mapping!
+ *
+ * The page does not need to be reserved.
+ */
+int vm_insert_page(struct vm_area_struct *vma, unsigned long addr, struct page *page)
+{
+ if (addr < vma->vm_start || addr >= vma->vm_end)
+ return -EFAULT;
+ if (!page_count(page))
+ return -EINVAL;
+ return insert_page(vma->vm_mm, addr, page, vma->vm_page_prot);
+}
+EXPORT_SYMBOL_GPL(vm_insert_page);
+
+/*
* Somebody does a pfn remapping that doesn't actually work as a vma.
*
* Do it as individual pages instead, and warn about it. It's bad form,
@@ -1225,8 +1254,11 @@ static int incomplete_pfn_remap(struct vm_area_struct *vma,
if (!pfn_valid(pfn))
return -EINVAL;
- retval = 0;
page = pfn_to_page(pfn);
+ if (!PageReserved(page))
+ return -EINVAL;
+
+ retval = 0;
while (start < end) {
retval = insert_page(vma->vm_mm, start, page, prot);
if (retval < 0)