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: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Ingo Rohloff <ingo.rohloff@lauterbach.com> Link: https://lore.kernel.org/r/20220209123303.103340-2-ingo.rohloff@lauterbach.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
c3c9cee592
commit
6a3cd5bef2
@ -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);
|
||||
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);
|
||||
|
||||
if (lim > 0 && atomic64_read(&usbfs_memory_usage) > lim) {
|
||||
atomic64_sub(amount, &usbfs_memory_usage);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
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)
|
||||
|
Loading…
Reference in New Issue
Block a user