]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
Merge with /pub/scm/linux/kernel/git/torvalds/linux-2.6.git
authorSteve French <sfrench@us.ibm.com>
Wed, 30 Nov 2005 23:56:59 +0000 (15:56 -0800)
committerSteve French <sfrench@us.ibm.com>
Wed, 30 Nov 2005 23:56:59 +0000 (15:56 -0800)
16 files changed:
Documentation/usb/error-codes.txt
drivers/char/drm/drm_context.c
drivers/hwmon/w83792d.c
drivers/usb/atm/cxacru.c
drivers/usb/core/hcd-pci.c
drivers/usb/core/hcd.c
drivers/usb/core/hcd.h
drivers/usb/host/ehci-pci.c
drivers/usb/host/ehci-q.c
drivers/usb/host/ehci-sched.c
drivers/usb/host/ohci-hcd.c
drivers/usb/host/ohci-hub.c
drivers/usb/host/ohci-pci.c
drivers/usb/host/uhci-hcd.c
include/linux/mm.h
mm/memory.c

index 1e36f1661cd0a6409a00defea1407162a9bdfa61..867f4c38f3564ae8fcfa8aa92df2b8eb41cfa31a 100644 (file)
@@ -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).
index bdd168d88f49b0ab6a0103f6c32b20e4fde46105..bd958d69a2ac2aeccb166739c83de1cb674fa4cc 100644 (file)
@@ -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);
index 4be59dbb78c472467c507587e9cb47279a557572..1ba072630361959678ef87def072a43755464345 100644 (file)
@@ -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 */
index 79861ee12a29c44c9837ea72f2feaf8312428848..9d59dc62e6d2271c61773651c5163559bdb64a7b 100644 (file)
@@ -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
        },
index 5131d88e8c5bd7c5b39b0e9b1f27d3a2c5a85eda..29b5b2a6e183e11aab66d4615d502869bfeb9812 100644 (file)
@@ -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);
index 5e5f65a475ab95c2d50b372d70d4f5533ad98f6e..da24c31ee00d97138d5db16d8e575b013a5056ff 100644 (file)
@@ -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");
index 24a62a2ff86dd38b11dd3494069b99a8bd9882b2..c8a1b350e2cf045ee71585d1b12a5ff8fe396186 100644 (file)
@@ -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? */
index 441c26064b44b3266ac055e4c708548f465183ce..13f73a836e455033c002c49d6efea2219262ac3d 100644 (file)
@@ -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,
index 5bb872c3496d70c3767d1b4f40a8fdd2979ba354..bf03ec0d8ee2222038ee0d928ef61feac9ea94fd 100644 (file)
@@ -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;
 }
 
 /*-------------------------------------------------------------------------*/
index f0c8aa1ccd5dc016aa8c61ea3b84944c99a25c1c..57e77374d228dd083053b78a26d331a44c4e20cd 100644 (file)
@@ -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);
index 5c0c6c8a7a82a65da4ce6b2a3f70ecef0c1f4746..bf1d9abc07ac37a87d18a3422aaeb23980e880e5 100644 (file)
 
 /*-------------------------------------------------------------------------*/
 
-// #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;
index e01e77bc324b190bdaf845a7606bed0b5e338c5f..72e3b12a19268c937434b933245d6ad7d014c518 100644 (file)
@@ -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) {
index 5f22e6590cd12b82141fe2724c21d9acaf15465a..1b09dde068e11085e08830edc0a38f29c733eeab 100644 (file)
@@ -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;
 }
index d33ce3982a5f0e08f249ebb00697f3de22907fdc..ed550132db0b1aedfac49a6f8fb7d8c10c98073c 100644 (file)
@@ -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);
index 0e73f1539d08e26269ff6b0cea0f099a05f6068f..29f02d8513f649fc74c3fcd3be1ade57fd4cf466 100644 (file)
@@ -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);
index 8d10b5540c73655f72fd50ebe55155d9b0996aa0..4b4fc3a7ea4858a1bba7e2aa60f1ace8fdbd4e0c 100644 (file)
@@ -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);
@@ -1196,6 +1196,35 @@ out:
        return retval;
 }
 
+/*
+ * 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.
  *
@@ -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)