Merge tag 'lkdtm-next' of https://git.kernel.org/pub/scm/linux/kernel/git/kees/linux...
[linux-2.6-microblaze.git] / drivers / usb / core / devio.c
index fa66e6e..b5b85bf 100644 (file)
@@ -139,30 +139,42 @@ MODULE_PARM_DESC(usbfs_memory_mb,
 /* Hard limit, necessary to avoid arithmetic overflow */
 #define USBFS_XFER_MAX         (UINT_MAX / 2 - 1000000)
 
-static atomic64_t usbfs_memory_usage;  /* Total memory currently allocated */
+static DEFINE_SPINLOCK(usbfs_memory_usage_lock);
+static u64 usbfs_memory_usage; /* Total memory currently allocated */
 
 /* Check whether it's okay to allocate more memory for a transfer */
 static int usbfs_increase_memory_usage(u64 amount)
 {
-       u64 lim;
+       u64 lim, total_mem;
+       unsigned long flags;
+       int ret;
 
        lim = READ_ONCE(usbfs_memory_mb);
        lim <<= 20;
 
-       atomic64_add(amount, &usbfs_memory_usage);
-
-       if (lim > 0 && atomic64_read(&usbfs_memory_usage) > lim) {
-               atomic64_sub(amount, &usbfs_memory_usage);
-               return -ENOMEM;
-       }
+       ret = 0;
+       spin_lock_irqsave(&usbfs_memory_usage_lock, flags);
+       total_mem = usbfs_memory_usage + amount;
+       if (lim > 0 && total_mem > lim)
+               ret = -ENOMEM;
+       else
+               usbfs_memory_usage = total_mem;
+       spin_unlock_irqrestore(&usbfs_memory_usage_lock, flags);
 
-       return 0;
+       return ret;
 }
 
 /* Memory for a transfer is being deallocated */
 static void usbfs_decrease_memory_usage(u64 amount)
 {
-       atomic64_sub(amount, &usbfs_memory_usage);
+       unsigned long flags;
+
+       spin_lock_irqsave(&usbfs_memory_usage_lock, flags);
+       if (amount > usbfs_memory_usage)
+               usbfs_memory_usage = 0;
+       else
+               usbfs_memory_usage -= amount;
+       spin_unlock_irqrestore(&usbfs_memory_usage_lock, flags);
 }
 
 static int connected(struct usb_dev_state *ps)
@@ -1197,12 +1209,16 @@ static int do_proc_control(struct usb_dev_state *ps,
 
                usb_unlock_device(dev);
                i = usbfs_start_wait_urb(urb, tmo, &actlen);
+
+               /* Linger a bit, prior to the next control message. */
+               if (dev->quirks & USB_QUIRK_DELAY_CTRL_MSG)
+                       msleep(200);
                usb_lock_device(dev);
                snoop_urb(dev, NULL, pipe, actlen, i, COMPLETE, tbuf, actlen);
                if (!i && actlen) {
                        if (copy_to_user(ctrl->data, tbuf, actlen)) {
                                ret = -EFAULT;
-                               goto recv_fault;
+                               goto done;
                        }
                }
        } else {
@@ -1219,6 +1235,10 @@ static int do_proc_control(struct usb_dev_state *ps,
 
                usb_unlock_device(dev);
                i = usbfs_start_wait_urb(urb, tmo, &actlen);
+
+               /* Linger a bit, prior to the next control message. */
+               if (dev->quirks & USB_QUIRK_DELAY_CTRL_MSG)
+                       msleep(200);
                usb_lock_device(dev);
                snoop_urb(dev, NULL, pipe, actlen, i, COMPLETE, NULL, 0);
        }
@@ -1230,10 +1250,6 @@ static int do_proc_control(struct usb_dev_state *ps,
        }
        ret = (i < 0 ? i : actlen);
 
- recv_fault:
-       /* Linger a bit, prior to the next control message. */
-       if (dev->quirks & USB_QUIRK_DELAY_CTRL_MSG)
-               msleep(200);
  done:
        kfree(dr);
        usb_free_urb(urb);