USB: Increase usbfs transfer limit
Promote a variable keeping track of USB transfer memory usage to a wider data type and allow for higher bandwidth transfers from a large number of USB devices connected to a single host. Signed-off-by: Mateusz Berezecki <mateuszb@fastmail.fm> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
444d930998
commit
1129d270cb
|
@ -134,42 +134,35 @@ enum snoop_when {
|
||||||
#define USB_DEVICE_DEV MKDEV(USB_DEVICE_MAJOR, 0)
|
#define USB_DEVICE_DEV MKDEV(USB_DEVICE_MAJOR, 0)
|
||||||
|
|
||||||
/* Limit on the total amount of memory we can allocate for transfers */
|
/* Limit on the total amount of memory we can allocate for transfers */
|
||||||
static unsigned usbfs_memory_mb = 16;
|
static u32 usbfs_memory_mb = 16;
|
||||||
module_param(usbfs_memory_mb, uint, 0644);
|
module_param(usbfs_memory_mb, uint, 0644);
|
||||||
MODULE_PARM_DESC(usbfs_memory_mb,
|
MODULE_PARM_DESC(usbfs_memory_mb,
|
||||||
"maximum MB allowed for usbfs buffers (0 = no limit)");
|
"maximum MB allowed for usbfs buffers (0 = no limit)");
|
||||||
|
|
||||||
/* Hard limit, necessary to avoid arithmetic overflow */
|
static atomic64_t usbfs_memory_usage; /* Total memory currently allocated */
|
||||||
#define USBFS_XFER_MAX (UINT_MAX / 2 - 1000000)
|
|
||||||
|
|
||||||
static atomic_t usbfs_memory_usage; /* Total memory currently allocated */
|
|
||||||
|
|
||||||
/* Check whether it's okay to allocate more memory for a transfer */
|
/* Check whether it's okay to allocate more memory for a transfer */
|
||||||
static int usbfs_increase_memory_usage(unsigned amount)
|
static int usbfs_increase_memory_usage(u64 amount)
|
||||||
{
|
{
|
||||||
unsigned lim;
|
u64 lim;
|
||||||
|
|
||||||
/*
|
|
||||||
* Convert usbfs_memory_mb to bytes, avoiding overflows.
|
|
||||||
* 0 means use the hard limit (effectively unlimited).
|
|
||||||
*/
|
|
||||||
lim = ACCESS_ONCE(usbfs_memory_mb);
|
lim = ACCESS_ONCE(usbfs_memory_mb);
|
||||||
if (lim == 0 || lim > (USBFS_XFER_MAX >> 20))
|
lim <<= 20;
|
||||||
lim = USBFS_XFER_MAX;
|
|
||||||
else
|
|
||||||
lim <<= 20;
|
|
||||||
|
|
||||||
atomic_add(amount, &usbfs_memory_usage);
|
atomic64_add(amount, &usbfs_memory_usage);
|
||||||
if (atomic_read(&usbfs_memory_usage) <= lim)
|
|
||||||
return 0;
|
if (lim > 0 && atomic64_read(&usbfs_memory_usage) > lim) {
|
||||||
atomic_sub(amount, &usbfs_memory_usage);
|
atomic64_sub(amount, &usbfs_memory_usage);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Memory for a transfer is being deallocated */
|
/* Memory for a transfer is being deallocated */
|
||||||
static void usbfs_decrease_memory_usage(unsigned amount)
|
static void usbfs_decrease_memory_usage(u64 amount)
|
||||||
{
|
{
|
||||||
atomic_sub(amount, &usbfs_memory_usage);
|
atomic64_sub(amount, &usbfs_memory_usage);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int connected(struct usb_dev_state *ps)
|
static int connected(struct usb_dev_state *ps)
|
||||||
|
@ -1191,7 +1184,7 @@ static int proc_bulk(struct usb_dev_state *ps, void __user *arg)
|
||||||
if (!usb_maxpacket(dev, pipe, !(bulk.ep & USB_DIR_IN)))
|
if (!usb_maxpacket(dev, pipe, !(bulk.ep & USB_DIR_IN)))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
len1 = bulk.len;
|
len1 = bulk.len;
|
||||||
if (len1 >= USBFS_XFER_MAX)
|
if (len1 >= (INT_MAX - sizeof(struct urb)))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
ret = usbfs_increase_memory_usage(len1 + sizeof(struct urb));
|
ret = usbfs_increase_memory_usage(len1 + sizeof(struct urb));
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -1584,10 +1577,6 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uurb->buffer_length >= USBFS_XFER_MAX) {
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
if (uurb->buffer_length > 0 &&
|
if (uurb->buffer_length > 0 &&
|
||||||
!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ,
|
!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ,
|
||||||
uurb->buffer, uurb->buffer_length)) {
|
uurb->buffer, uurb->buffer_length)) {
|
||||||
|
|
Loading…
Reference in New Issue