Commit 6a3cd5be authored by Ingo Rohloff's avatar Ingo Rohloff Committed by Greg Kroah-Hartman

USB: usbfs: Use a spinlock instead of atomic accesses to tally used memory.

While the existing code code imposes a limit on the used memory, it might be
over pessimistic (even if this is unlikely).

Example scenario:
8 threads running in parallel, all entering
"usbfs_increase_memory_usage()" at the same time.
The atomic accesses in "usbfs_increase_memory_usage()" could be
serialized like this:
  8 x "atomic64_add"
  8 x "atomic64_read"
If the 8 x "atomic64_add" raise "usbfs_memory_usage" above the limit,
then all 8 calls of "usbfs_increase_memory_usage()" will return with
-ENOMEM.  If you instead serialize over the whole access to
"usbfs_memory_usage" by using a spinlock, some of these calls will
succeed.
Acked-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarIngo Rohloff <ingo.rohloff@lauterbach.com>
Link: https://lore.kernel.org/r/20220209123303.103340-2-ingo.rohloff@lauterbach.comSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent c3c9cee5
......@@ -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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment