Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1129d270 authored by Mateusz Berezecki's avatar Mateusz Berezecki Committed by Greg Kroah-Hartman
Browse files

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: default avatarMateusz Berezecki <mateuszb@fastmail.fm>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 444d9309
Loading
Loading
Loading
Loading
+16 −27
Original line number Diff line number Diff line
@@ -134,42 +134,35 @@ enum snoop_when {
#define USB_DEVICE_DEV		MKDEV(USB_DEVICE_MAJOR, 0)

/* 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_PARM_DESC(usbfs_memory_mb,
		"maximum MB allowed for usbfs buffers (0 = no limit)");

/* Hard limit, necessary to avoid arithmetic overflow */
#define USBFS_XFER_MAX		(UINT_MAX / 2 - 1000000)

static atomic_t usbfs_memory_usage;	/* Total memory currently allocated */
static atomic64_t 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(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);
	if (lim == 0 || lim > (USBFS_XFER_MAX >> 20))
		lim = USBFS_XFER_MAX;
	else
	lim <<= 20;

	atomic_add(amount, &usbfs_memory_usage);
	if (atomic_read(&usbfs_memory_usage) <= lim)
		return 0;
	atomic_sub(amount, &usbfs_memory_usage);
	atomic64_add(amount, &usbfs_memory_usage);

	if (lim > 0 && atomic64_read(&usbfs_memory_usage) > lim) {
		atomic64_sub(amount, &usbfs_memory_usage);
		return -ENOMEM;
	}

	return 0;
}

/* 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)
@@ -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)))
		return -EINVAL;
	len1 = bulk.len;
	if (len1 >= USBFS_XFER_MAX)
	if (len1 >= (INT_MAX - sizeof(struct urb)))
		return -EINVAL;
	ret = usbfs_increase_memory_usage(len1 + sizeof(struct urb));
	if (ret)
@@ -1584,10 +1577,6 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
		return -EINVAL;
	}

	if (uurb->buffer_length >= USBFS_XFER_MAX) {
		ret = -EINVAL;
		goto error;
	}
	if (uurb->buffer_length > 0 &&
			!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ,
				uurb->buffer, uurb->buffer_length)) {