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

Commit c9050b64 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman
Browse files

Merge tag 'for-usb-next-2014-03-06' of...

Merge tag 'for-usb-next-2014-03-06' of git://git.kernel.org/pub/scm/linux/kernel/git/sarah/xhci into usb-next

Sarah writes:

xhci: Streams and UAS cleanups, misc cleanups for 3.15

Hi Greg,

Here's 76 patches to queue to usb-next for 3.15.

The bulk of this rather large pull request is the UAS driver cleanup, the
xHCI streams fixes, and the new userspace API for usbfs to be able to use
and alloc/free bulk streams.  I've hammered on these changes, and the UAS
driver seems solid.  The performance numbers are pretty spiffy too:

root@xanatos:~# echo 3 > /proc/sys/vm/drop_caches; dd if=/dev/sdb of=/dev/null bs=4k count=1000M iflag=count_bytes
256000+0 records in
256000+0 records out
1048576000 bytes (1.0 GB) copied, 3.28557 s, 319 MB/s

That's about 100 MB/s faster than my fastest Bulk-only-Transport mass
storage drive.

There's a couple of miscellaneous cleanup patches and non-urgent bug fixes
in here as well:

79699437 xhci: add the meaningful IRQ description if it is empty
bcffae77 xhci: Prevent runtime pm from autosuspending during initialization
e587b8b2 xhci: make warnings greppable
25cd2882 usb/xhci: Change how we indicate a host supports Link PM.

Sarah Sharp
parents 86e2864d 79699437
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -9063,8 +9063,7 @@ S: Maintained
F:	drivers/net/wireless/ath/ar5523/

USB ATTACHED SCSI
M:	Matthew Wilcox <willy@linux.intel.com>
M:	Sarah Sharp <sarah.a.sharp@linux.intel.com>
M:	Hans de Goede <hdegoede@redhat.com>
M:	Gerd Hoffmann <kraxel@redhat.com>
L:	linux-usb@vger.kernel.org
L:	linux-scsi@vger.kernel.org
+0 −1
Original line number Diff line number Diff line
@@ -10,7 +10,6 @@


#define USB_MAXALTSETTING		128	/* Hard limit */
#define USB_MAXENDPOINTS		30	/* Hard limit */

#define USB_MAXCONFIG			8	/* Arbitrary limit */

+143 −15
Original line number Diff line number Diff line
@@ -769,6 +769,88 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype,
	return ret;
}

static struct usb_host_endpoint *ep_to_host_endpoint(struct usb_device *dev,
						     unsigned char ep)
{
	if (ep & USB_ENDPOINT_DIR_MASK)
		return dev->ep_in[ep & USB_ENDPOINT_NUMBER_MASK];
	else
		return dev->ep_out[ep & USB_ENDPOINT_NUMBER_MASK];
}

static int parse_usbdevfs_streams(struct dev_state *ps,
				  struct usbdevfs_streams __user *streams,
				  unsigned int *num_streams_ret,
				  unsigned int *num_eps_ret,
				  struct usb_host_endpoint ***eps_ret,
				  struct usb_interface **intf_ret)
{
	unsigned int i, num_streams, num_eps;
	struct usb_host_endpoint **eps;
	struct usb_interface *intf = NULL;
	unsigned char ep;
	int ifnum, ret;

	if (get_user(num_streams, &streams->num_streams) ||
	    get_user(num_eps, &streams->num_eps))
		return -EFAULT;

	if (num_eps < 1 || num_eps > USB_MAXENDPOINTS)
		return -EINVAL;

	/* The XHCI controller allows max 2 ^ 16 streams */
	if (num_streams_ret && (num_streams < 2 || num_streams > 65536))
		return -EINVAL;

	eps = kmalloc(num_eps * sizeof(*eps), GFP_KERNEL);
	if (!eps)
		return -ENOMEM;

	for (i = 0; i < num_eps; i++) {
		if (get_user(ep, &streams->eps[i])) {
			ret = -EFAULT;
			goto error;
		}
		eps[i] = ep_to_host_endpoint(ps->dev, ep);
		if (!eps[i]) {
			ret = -EINVAL;
			goto error;
		}

		/* usb_alloc/free_streams operate on an usb_interface */
		ifnum = findintfep(ps->dev, ep);
		if (ifnum < 0) {
			ret = ifnum;
			goto error;
		}

		if (i == 0) {
			ret = checkintf(ps, ifnum);
			if (ret < 0)
				goto error;
			intf = usb_ifnum_to_if(ps->dev, ifnum);
		} else {
			/* Verify all eps belong to the same interface */
			if (ifnum != intf->altsetting->desc.bInterfaceNumber) {
				ret = -EINVAL;
				goto error;
			}
		}
	}

	if (num_streams_ret)
		*num_streams_ret = num_streams;
	*num_eps_ret = num_eps;
	*eps_ret = eps;
	*intf_ret = intf;

	return 0;

error:
	kfree(eps);
	return ret;
}

static int match_devt(struct device *dev, void *data)
{
	return dev->devt == (dev_t) (unsigned long) data;
@@ -1143,6 +1225,9 @@ static int proc_setintf(struct dev_state *ps, void __user *arg)
		return -EFAULT;
	if ((ret = checkintf(ps, setintf.interface)))
		return ret;

	destroy_async_on_interface(ps, setintf.interface);

	return usb_set_interface(ps->dev, setintf.interface,
			setintf.altsetting);
}
@@ -1205,6 +1290,8 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
	struct usb_ctrlrequest *dr = NULL;
	unsigned int u, totlen, isofrmlen;
	int i, ret, is_in, num_sgs = 0, ifnum = -1;
	int number_of_packets = 0;
	unsigned int stream_id = 0;
	void *buf;

	if (uurb->flags & ~(USBDEVFS_URB_ISO_ASAP |
@@ -1225,15 +1312,10 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
		if (ret)
			return ret;
	}
	if ((uurb->endpoint & USB_ENDPOINT_DIR_MASK) != 0) {
		is_in = 1;
		ep = ps->dev->ep_in[uurb->endpoint & USB_ENDPOINT_NUMBER_MASK];
	} else {
		is_in = 0;
		ep = ps->dev->ep_out[uurb->endpoint & USB_ENDPOINT_NUMBER_MASK];
	}
	ep = ep_to_host_endpoint(ps->dev, uurb->endpoint);
	if (!ep)
		return -ENOENT;
	is_in = (uurb->endpoint & USB_ENDPOINT_DIR_MASK) != 0;

	u = 0;
	switch(uurb->type) {
@@ -1258,7 +1340,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
				      le16_to_cpup(&dr->wIndex));
		if (ret)
			goto error;
		uurb->number_of_packets = 0;
		uurb->buffer_length = le16_to_cpup(&dr->wLength);
		uurb->buffer += 8;
		if ((dr->bRequestType & USB_DIR_IN) && uurb->buffer_length) {
@@ -1288,17 +1369,17 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
			uurb->type = USBDEVFS_URB_TYPE_INTERRUPT;
			goto interrupt_urb;
		}
		uurb->number_of_packets = 0;
		num_sgs = DIV_ROUND_UP(uurb->buffer_length, USB_SG_SIZE);
		if (num_sgs == 1 || num_sgs > ps->dev->bus->sg_tablesize)
			num_sgs = 0;
		if (ep->streams)
			stream_id = uurb->stream_id;
		break;

	case USBDEVFS_URB_TYPE_INTERRUPT:
		if (!usb_endpoint_xfer_int(&ep->desc))
			return -EINVAL;
 interrupt_urb:
		uurb->number_of_packets = 0;
		break;

	case USBDEVFS_URB_TYPE_ISO:
@@ -1308,15 +1389,16 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
			return -EINVAL;
		if (!usb_endpoint_xfer_isoc(&ep->desc))
			return -EINVAL;
		number_of_packets = uurb->number_of_packets;
		isofrmlen = sizeof(struct usbdevfs_iso_packet_desc) *
				   uurb->number_of_packets;
				   number_of_packets;
		if (!(isopkt = kmalloc(isofrmlen, GFP_KERNEL)))
			return -ENOMEM;
		if (copy_from_user(isopkt, iso_frame_desc, isofrmlen)) {
			ret = -EFAULT;
			goto error;
		}
		for (totlen = u = 0; u < uurb->number_of_packets; u++) {
		for (totlen = u = 0; u < number_of_packets; u++) {
			/*
			 * arbitrary limit need for USB 3.0
			 * bMaxBurst (0~15 allowed, 1~16 packets)
@@ -1347,7 +1429,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
		ret = -EFAULT;
		goto error;
	}
	as = alloc_async(uurb->number_of_packets);
	as = alloc_async(number_of_packets);
	if (!as) {
		ret = -ENOMEM;
		goto error;
@@ -1441,7 +1523,8 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
	as->urb->setup_packet = (unsigned char *)dr;
	dr = NULL;
	as->urb->start_frame = uurb->start_frame;
	as->urb->number_of_packets = uurb->number_of_packets;
	as->urb->number_of_packets = number_of_packets;
	as->urb->stream_id = stream_id;
	if (uurb->type == USBDEVFS_URB_TYPE_ISO ||
			ps->dev->speed == USB_SPEED_HIGH)
		as->urb->interval = 1 << min(15, ep->desc.bInterval - 1);
@@ -1449,7 +1532,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
		as->urb->interval = ep->desc.bInterval;
	as->urb->context = as;
	as->urb->complete = async_completed;
	for (totlen = u = 0; u < uurb->number_of_packets; u++) {
	for (totlen = u = 0; u < number_of_packets; u++) {
		as->urb->iso_frame_desc[u].offset = totlen;
		as->urb->iso_frame_desc[u].length = isopkt[u].length;
		totlen += isopkt[u].length;
@@ -1999,6 +2082,45 @@ static int proc_disconnect_claim(struct dev_state *ps, void __user *arg)
	return claimintf(ps, dc.interface);
}

static int proc_alloc_streams(struct dev_state *ps, void __user *arg)
{
	unsigned num_streams, num_eps;
	struct usb_host_endpoint **eps;
	struct usb_interface *intf;
	int r;

	r = parse_usbdevfs_streams(ps, arg, &num_streams, &num_eps,
				   &eps, &intf);
	if (r)
		return r;

	destroy_async_on_interface(ps,
				   intf->altsetting[0].desc.bInterfaceNumber);

	r = usb_alloc_streams(intf, eps, num_eps, num_streams, GFP_KERNEL);
	kfree(eps);
	return r;
}

static int proc_free_streams(struct dev_state *ps, void __user *arg)
{
	unsigned num_eps;
	struct usb_host_endpoint **eps;
	struct usb_interface *intf;
	int r;

	r = parse_usbdevfs_streams(ps, arg, NULL, &num_eps, &eps, &intf);
	if (r)
		return r;

	destroy_async_on_interface(ps,
				   intf->altsetting[0].desc.bInterfaceNumber);

	r = usb_free_streams(intf, eps, num_eps, GFP_KERNEL);
	kfree(eps);
	return r;
}

/*
 * NOTE:  All requests here that have interface numbers as parameters
 * are assuming that somehow the configuration has been prevented from
@@ -2175,6 +2297,12 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
	case USBDEVFS_DISCONNECT_CLAIM:
		ret = proc_disconnect_claim(ps, p);
		break;
	case USBDEVFS_ALLOC_STREAMS:
		ret = proc_alloc_streams(ps, p);
		break;
	case USBDEVFS_FREE_STREAMS:
		ret = proc_free_streams(ps, p);
		break;
	}
	usb_unlock_device(dev);
	if (ret >= 0)
+22 −1
Original line number Diff line number Diff line
@@ -400,8 +400,9 @@ static int usb_unbind_interface(struct device *dev)
{
	struct usb_driver *driver = to_usb_driver(dev->driver);
	struct usb_interface *intf = to_usb_interface(dev);
	struct usb_host_endpoint *ep, **eps = NULL;
	struct usb_device *udev;
	int error, r, lpm_disable_error;
	int i, j, error, r, lpm_disable_error;

	intf->condition = USB_INTERFACE_UNBINDING;

@@ -425,6 +426,26 @@ static int usb_unbind_interface(struct device *dev)
	driver->disconnect(intf);
	usb_cancel_queued_reset(intf);

	/* Free streams */
	for (i = 0, j = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
		ep = &intf->cur_altsetting->endpoint[i];
		if (ep->streams == 0)
			continue;
		if (j == 0) {
			eps = kmalloc(USB_MAXENDPOINTS * sizeof(void *),
				      GFP_KERNEL);
			if (!eps) {
				dev_warn(dev, "oom, leaking streams\n");
				break;
			}
		}
		eps[j++] = ep;
	}
	if (j) {
		usb_free_streams(intf, eps, j, GFP_KERNEL);
		kfree(eps);
	}

	/* Reset other interface state.
	 * We cannot do a Set-Interface if the device is suspended or
	 * if it is prepared for a system sleep (since installing a new
+27 −10
Original line number Diff line number Diff line
@@ -2049,7 +2049,7 @@ int usb_alloc_streams(struct usb_interface *interface,
{
	struct usb_hcd *hcd;
	struct usb_device *dev;
	int i;
	int i, ret;

	dev = interface_to_usbdev(interface);
	hcd = bus_to_hcd(dev->bus);
@@ -2058,13 +2058,24 @@ int usb_alloc_streams(struct usb_interface *interface,
	if (dev->speed != USB_SPEED_SUPER)
		return -EINVAL;

	for (i = 0; i < num_eps; i++) {
		/* Streams only apply to bulk endpoints. */
	for (i = 0; i < num_eps; i++)
		if (!usb_endpoint_xfer_bulk(&eps[i]->desc))
			return -EINVAL;
		/* Re-alloc is not allowed */
		if (eps[i]->streams)
			return -EINVAL;
	}

	return hcd->driver->alloc_streams(hcd, dev, eps, num_eps,
	ret = hcd->driver->alloc_streams(hcd, dev, eps, num_eps,
			num_streams, mem_flags);
	if (ret < 0)
		return ret;

	for (i = 0; i < num_eps; i++)
		eps[i]->streams = ret;

	return ret;
}
EXPORT_SYMBOL_GPL(usb_alloc_streams);

@@ -2078,8 +2089,7 @@ EXPORT_SYMBOL_GPL(usb_alloc_streams);
 * Reverts a group of bulk endpoints back to not using stream IDs.
 * Can fail if we are given bad arguments, or HCD is broken.
 *
 * Return: On success, the number of allocated streams. On failure, a negative
 * error code.
 * Return: 0 on success. On failure, a negative error code.
 */
int usb_free_streams(struct usb_interface *interface,
		struct usb_host_endpoint **eps, unsigned int num_eps,
@@ -2087,19 +2097,26 @@ int usb_free_streams(struct usb_interface *interface,
{
	struct usb_hcd *hcd;
	struct usb_device *dev;
	int i;
	int i, ret;

	dev = interface_to_usbdev(interface);
	hcd = bus_to_hcd(dev->bus);
	if (dev->speed != USB_SPEED_SUPER)
		return -EINVAL;

	/* Streams only apply to bulk endpoints. */
	/* Double-free is not allowed */
	for (i = 0; i < num_eps; i++)
		if (!eps[i] || !usb_endpoint_xfer_bulk(&eps[i]->desc))
		if (!eps[i] || !eps[i]->streams)
			return -EINVAL;

	return hcd->driver->free_streams(hcd, dev, eps, num_eps, mem_flags);
	ret = hcd->driver->free_streams(hcd, dev, eps, num_eps, mem_flags);
	if (ret < 0)
		return ret;

	for (i = 0; i < num_eps; i++)
		eps[i]->streams = 0;

	return ret;
}
EXPORT_SYMBOL_GPL(usb_free_streams);

Loading