Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 9 Dec 2009 22:37:06 +0000 (UTC)
From:      Andrew Thompson <thompsa@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-8@freebsd.org
Subject:   svn commit: r200328 - in stable/8/sys/dev/usb: . controller
Message-ID:  <200912092237.nB9Mb61k041968@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: thompsa
Date: Wed Dec  9 22:37:06 2009
New Revision: 200328
URL: http://svn.freebsd.org/changeset/base/200328

Log:
  MFC r199672
  
   Improve High Speed slot allocation mechanism by moving the computation to the
   endpoint rather than per xfer and provide functions around get/free of resources.
  
  Submitted by:	Hans Petter Selasky

Modified:
  stable/8/sys/dev/usb/controller/ehci.c
  stable/8/sys/dev/usb/usb_core.h
  stable/8/sys/dev/usb/usb_device.c
  stable/8/sys/dev/usb/usb_hub.c
  stable/8/sys/dev/usb/usb_hub.h
  stable/8/sys/dev/usb/usb_transfer.c
  stable/8/sys/dev/usb/usbdi.h
Directory Properties:
  stable/8/sys/   (props changed)
  stable/8/sys/amd64/include/xen/   (props changed)
  stable/8/sys/cddl/contrib/opensolaris/   (props changed)
  stable/8/sys/contrib/dev/acpica/   (props changed)
  stable/8/sys/contrib/pf/   (props changed)
  stable/8/sys/dev/xen/xenpci/   (props changed)

Modified: stable/8/sys/dev/usb/controller/ehci.c
==============================================================================
--- stable/8/sys/dev/usb/controller/ehci.c	Wed Dec  9 22:36:16 2009	(r200327)
+++ stable/8/sys/dev/usb/controller/ehci.c	Wed Dec  9 22:37:06 2009	(r200328)
@@ -2016,8 +2016,8 @@ ehci_setup_standard_chain(struct usb_xfe
 
 	qh_endphub =
 	    (EHCI_QH_SET_MULT(xfer->max_packet_count & 3) |
-	    EHCI_QH_SET_CMASK(xfer->usb_cmask) |
-	    EHCI_QH_SET_SMASK(xfer->usb_smask) |
+	    EHCI_QH_SET_CMASK(xfer->endpoint->usb_cmask) |
+	    EHCI_QH_SET_SMASK(xfer->endpoint->usb_smask) |
 	    EHCI_QH_SET_HUBA(xfer->xroot->udev->hs_hub_addr) |
 	    EHCI_QH_SET_PORT(xfer->xroot->udev->hs_port_no));
 
@@ -2162,7 +2162,7 @@ ehci_isoc_hs_done(ehci_softc_t *sc, stru
 
 		DPRINTFN(2, "status=0x%08x, len=%u\n", status, len);
 
-		if (xfer->usb_smask & (1 << td_no)) {
+		if (xfer->endpoint->usb_smask & (1 << td_no)) {
 
 			if (*plen >= len) {
 				/*
@@ -2348,22 +2348,8 @@ ehci_device_intr_open(struct usb_xfer *x
 	uint16_t best;
 	uint16_t bit;
 	uint16_t x;
-	uint8_t slot;
 
-	/* Allocate a microframe slot first: */
-
-	slot = usb_intr_schedule_adjust
-	    (xfer->xroot->udev, xfer->max_frame_size, USB_HS_MICRO_FRAMES_MAX);
-
-	if (usbd_get_speed(xfer->xroot->udev) == USB_SPEED_HIGH) {
-		xfer->usb_uframe = slot;
-		xfer->usb_smask = (1 << slot) & 0xFF;
-		xfer->usb_cmask = 0;
-	} else {
-		xfer->usb_uframe = slot;
-		xfer->usb_smask = (1 << slot) & 0x3F;
-		xfer->usb_cmask = (-(4 << slot)) & 0xFE;
-	}
+	usb_hs_bandwidth_alloc(xfer);
 
 	/*
 	 * Find the best QH position corresponding to the given interval:
@@ -2399,12 +2385,12 @@ ehci_device_intr_close(struct usb_xfer *
 {
 	ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
 
-	usb_intr_schedule_adjust(xfer->xroot->udev,
-	    -(xfer->max_frame_size), xfer->usb_uframe);
-
 	sc->sc_intr_stat[xfer->qh_pos]--;
 
 	ehci_device_done(xfer, USB_ERR_CANCELLED);
+
+	/* bandwidth must be freed after device done */
+	usb_hs_bandwidth_free(xfer);
 }
 
 static void
@@ -2726,28 +2712,8 @@ ehci_device_isoc_hs_open(struct usb_xfer
 	ehci_itd_t *td;
 	uint32_t temp;
 	uint8_t ds;
-	uint8_t slot;
-
-	slot = usb_intr_schedule_adjust(xfer->xroot->udev, xfer->max_frame_size,
-	    USB_HS_MICRO_FRAMES_MAX);
 
-	xfer->usb_uframe = slot;
-	xfer->usb_cmask = 0;
-
-	switch (usbd_xfer_get_fps_shift(xfer)) {
-	case 0:
-		xfer->usb_smask = 0xFF;
-		break;
-	case 1:
-		xfer->usb_smask = 0x55 << (slot & 1);
-		break;
-	case 2:
-		xfer->usb_smask = 0x11 << (slot & 3);
-		break;
-	default:
-		xfer->usb_smask = 0x01 << (slot & 7);
-		break;
-	}
+	usb_hs_bandwidth_alloc(xfer);
 
 	/* initialize all TD's */
 
@@ -2791,11 +2757,10 @@ ehci_device_isoc_hs_open(struct usb_xfer
 static void
 ehci_device_isoc_hs_close(struct usb_xfer *xfer)
 {
-
-	usb_intr_schedule_adjust(xfer->xroot->udev,
-	    -(xfer->max_frame_size), xfer->usb_uframe);
-
 	ehci_device_done(xfer, USB_ERR_CANCELLED);
+
+	/* bandwidth must be freed after device done */
+	usb_hs_bandwidth_free(xfer);
 }
 
 static void
@@ -2905,7 +2870,7 @@ ehci_device_isoc_hs_enter(struct usb_xfe
 			*plen = xfer->max_frame_size;
 		}
 
-		if (xfer->usb_smask & (1 << td_no)) {
+		if (xfer->endpoint->usb_smask & (1 << td_no)) {
 			status = (EHCI_ITD_SET_LEN(*plen) |
 			    EHCI_ITD_ACTIVE |
 			    EHCI_ITD_SET_PG(0));

Modified: stable/8/sys/dev/usb/usb_core.h
==============================================================================
--- stable/8/sys/dev/usb/usb_core.h	Wed Dec  9 22:36:16 2009	(r200327)
+++ stable/8/sys/dev/usb/usb_core.h	Wed Dec  9 22:37:06 2009	(r200328)
@@ -161,9 +161,6 @@ struct usb_xfer {
 	uint8_t	address;		/* physical USB address */
 	uint8_t	endpointno;		/* physical USB endpoint */
 	uint8_t	max_packet_count;
-	uint8_t	usb_smask;
-	uint8_t	usb_cmask;
-	uint8_t	usb_uframe;
 	uint8_t	usb_state;
 	uint8_t fps_shift;		/* down shift of FPS, 0..3 */
 

Modified: stable/8/sys/dev/usb/usb_device.c
==============================================================================
--- stable/8/sys/dev/usb/usb_device.c	Wed Dec  9 22:36:16 2009	(r200327)
+++ stable/8/sys/dev/usb/usb_device.c	Wed Dec  9 22:37:06 2009	(r200328)
@@ -665,7 +665,7 @@ usb_config_parse(struct usb_device *udev
 			/* look for matching endpoints */
 			if ((iface_index == USB_IFACE_INDEX_ANY) ||
 			    (iface_index == ep->iface_index)) {
-				if (ep->refcount != 0) {
+				if (ep->refcount_alloc != 0) {
 					/*
 					 * This typically indicates a
 					 * more serious error.

Modified: stable/8/sys/dev/usb/usb_hub.c
==============================================================================
--- stable/8/sys/dev/usb/usb_hub.c	Wed Dec  9 22:36:16 2009	(r200327)
+++ stable/8/sys/dev/usb/usb_hub.c	Wed Dec  9 22:37:06 2009	(r200328)
@@ -1106,43 +1106,62 @@ done:
  *   The best Transaction Translation slot for an interrupt endpoint.
  *------------------------------------------------------------------------*/
 static uint8_t
-usb_intr_find_best_slot(usb_size_t *ptr, uint8_t start, uint8_t end)
+usb_intr_find_best_slot(usb_size_t *ptr, uint8_t start,
+    uint8_t end, uint8_t mask)
 {
-	usb_size_t max = 0 - 1;
+	usb_size_t min = 0 - 1;
+	usb_size_t sum;
 	uint8_t x;
 	uint8_t y;
+	uint8_t z;
 
 	y = 0;
 
 	/* find the last slot with lesser used bandwidth */
 
 	for (x = start; x < end; x++) {
-		if (max >= ptr[x]) {
-			max = ptr[x];
+
+		sum = 0;
+
+		/* compute sum of bandwidth */
+		for (z = x; z < end; z++) {
+			if (mask & (1U << (z - x)))
+				sum += ptr[z];
+		}
+
+		/* check if the current multi-slot is more optimal */
+		if (min >= sum) {
+			min = sum;
 			y = x;
 		}
+
+		/* check if the mask is about to be shifted out */
+		if (mask & (1U << (end - 1 - x)))
+			break;
 	}
 	return (y);
 }
 
 /*------------------------------------------------------------------------*
- *	usb_intr_schedule_adjust
+ *	usb_hs_bandwidth_adjust
  *
  * This function will update the bandwith usage for the microframe
  * having index "slot" by "len" bytes. "len" can be negative.  If the
  * "slot" argument is greater or equal to "USB_HS_MICRO_FRAMES_MAX"
  * the "slot" argument will be replaced by the slot having least used
- * bandwidth.
+ * bandwidth. The "mask" argument is used for multi-slot allocations.
  *
  * Returns:
- *   The slot on which the bandwidth update was done.
+ *    The slot in which the bandwidth update was done: 0..7
  *------------------------------------------------------------------------*/
-uint8_t
-usb_intr_schedule_adjust(struct usb_device *udev, int16_t len, uint8_t slot)
+static uint8_t
+usb_hs_bandwidth_adjust(struct usb_device *udev, int16_t len,
+    uint8_t slot, uint8_t mask)
 {
 	struct usb_bus *bus = udev->bus;
 	struct usb_hub *hub;
 	enum usb_dev_speed speed;
+	uint8_t x;
 
 	USB_BUS_LOCK_ASSERT(bus, MA_OWNED);
 
@@ -1164,23 +1183,157 @@ usb_intr_schedule_adjust(struct usb_devi
 		hub = udev->parent_hs_hub->hub;
 		if (slot >= USB_HS_MICRO_FRAMES_MAX) {
 			slot = usb_intr_find_best_slot(hub->uframe_usage,
-			    USB_FS_ISOC_UFRAME_MAX, 6);
+			    USB_FS_ISOC_UFRAME_MAX, 6, mask);
+		}
+		for (x = slot; x < 8; x++) {
+			if (mask & (1U << (x - slot))) {
+				hub->uframe_usage[x] += len;
+				bus->uframe_usage[x] += len;
+			}
 		}
-		hub->uframe_usage[slot] += len;
-		bus->uframe_usage[slot] += len;
 		break;
 	default:
 		if (slot >= USB_HS_MICRO_FRAMES_MAX) {
 			slot = usb_intr_find_best_slot(bus->uframe_usage, 0,
-			    USB_HS_MICRO_FRAMES_MAX);
+			    USB_HS_MICRO_FRAMES_MAX, mask);
+		}
+		for (x = slot; x < 8; x++) {
+			if (mask & (1U << (x - slot))) {
+				bus->uframe_usage[x] += len;
+			}
 		}
-		bus->uframe_usage[slot] += len;
 		break;
 	}
 	return (slot);
 }
 
 /*------------------------------------------------------------------------*
+ *	usb_hs_bandwidth_alloc
+ *
+ * This function is a wrapper function for "usb_hs_bandwidth_adjust()".
+ *------------------------------------------------------------------------*/
+void
+usb_hs_bandwidth_alloc(struct usb_xfer *xfer)
+{
+	struct usb_device *udev;
+	uint8_t slot;
+	uint8_t mask;
+	uint8_t speed;
+
+	udev = xfer->xroot->udev;
+
+	if (udev->flags.usb_mode != USB_MODE_HOST)
+		return;		/* not supported */
+
+	xfer->endpoint->refcount_bw++;
+	if (xfer->endpoint->refcount_bw != 1)
+		return;		/* already allocated */
+
+	speed = usbd_get_speed(udev);
+
+	switch (xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE) {
+	case UE_INTERRUPT:
+		/* allocate a microframe slot */
+
+		mask = 0x01;
+		slot = usb_hs_bandwidth_adjust(udev,
+		    xfer->max_frame_size, USB_HS_MICRO_FRAMES_MAX, mask);
+
+		xfer->endpoint->usb_uframe = slot;
+		xfer->endpoint->usb_smask = mask << slot;
+
+		if ((speed != USB_SPEED_FULL) &&
+		    (speed != USB_SPEED_LOW)) {
+			xfer->endpoint->usb_cmask = 0x00 ;
+		} else {
+			xfer->endpoint->usb_cmask = (-(0x04 << slot)) & 0xFE;
+		}
+		break;
+
+	case UE_ISOCHRONOUS:
+		switch (usbd_xfer_get_fps_shift(xfer)) {
+		case 0:
+			mask = 0xFF;
+			break;
+		case 1:
+			mask = 0x55;
+			break;
+		case 2:
+			mask = 0x11;
+			break;
+		default:
+			mask = 0x01;
+			break;
+		}
+
+		/* allocate a microframe multi-slot */
+
+		slot = usb_hs_bandwidth_adjust(udev,
+		    xfer->max_frame_size, USB_HS_MICRO_FRAMES_MAX, mask);
+
+		xfer->endpoint->usb_uframe = slot;
+		xfer->endpoint->usb_cmask = 0;
+		xfer->endpoint->usb_smask = mask << slot;
+		break;
+
+	default:
+		xfer->endpoint->usb_uframe = 0;
+		xfer->endpoint->usb_cmask = 0;
+		xfer->endpoint->usb_smask = 0;
+		break;
+	}
+
+	DPRINTFN(11, "slot=%d, mask=0x%02x\n", 
+	    xfer->endpoint->usb_uframe, 
+	    xfer->endpoint->usb_smask >> xfer->endpoint->usb_uframe);
+}
+
+/*------------------------------------------------------------------------*
+ *	usb_hs_bandwidth_free
+ *
+ * This function is a wrapper function for "usb_hs_bandwidth_adjust()".
+ *------------------------------------------------------------------------*/
+void
+usb_hs_bandwidth_free(struct usb_xfer *xfer)
+{
+	struct usb_device *udev;
+	uint8_t slot;
+	uint8_t mask;
+
+	udev = xfer->xroot->udev;
+
+	if (udev->flags.usb_mode != USB_MODE_HOST)
+		return;		/* not supported */
+
+	xfer->endpoint->refcount_bw--;
+	if (xfer->endpoint->refcount_bw != 0)
+		return;		/* still allocated */
+
+	switch (xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE) {
+	case UE_INTERRUPT:
+	case UE_ISOCHRONOUS:
+
+		slot = xfer->endpoint->usb_uframe;
+		mask = xfer->endpoint->usb_smask;
+
+		/* free microframe slot(s): */ 	  
+		usb_hs_bandwidth_adjust(udev,
+		    -xfer->max_frame_size, slot, mask >> slot);
+
+		DPRINTFN(11, "slot=%d, mask=0x%02x\n", 
+		    slot, mask >> slot);
+
+		xfer->endpoint->usb_uframe = 0;
+		xfer->endpoint->usb_cmask = 0;
+		xfer->endpoint->usb_smask = 0;
+		break;
+
+	default:
+		break;
+	}
+}
+
+/*------------------------------------------------------------------------*
  *	usbd_fs_isoc_schedule_init_sub
  *
  * This function initialises an USB FULL speed isochronous schedule

Modified: stable/8/sys/dev/usb/usb_hub.h
==============================================================================
--- stable/8/sys/dev/usb/usb_hub.h	Wed Dec  9 22:36:16 2009	(r200327)
+++ stable/8/sys/dev/usb/usb_hub.h	Wed Dec  9 22:37:06 2009	(r200328)
@@ -66,8 +66,8 @@ struct usb_hub {
 
 /* function prototypes */
 
-uint8_t	usb_intr_schedule_adjust(struct usb_device *udev, int16_t len,
-	    uint8_t slot);
+void	usb_hs_bandwidth_alloc(struct usb_xfer *xfer);
+void	usb_hs_bandwidth_free(struct usb_xfer *xfer);
 void	usbd_fs_isoc_schedule_init_all(struct usb_fs_isoc_schedule *fss);
 void	usb_bus_port_set_device(struct usb_bus *bus, struct usb_port *up,
 	    struct usb_device *udev, uint8_t device_index);

Modified: stable/8/sys/dev/usb/usb_transfer.c
==============================================================================
--- stable/8/sys/dev/usb/usb_transfer.c	Wed Dec  9 22:36:16 2009	(r200327)
+++ stable/8/sys/dev/usb/usb_transfer.c	Wed Dec  9 22:37:06 2009	(r200328)
@@ -942,10 +942,18 @@ usbd_transfer_setup(struct usb_device *u
 				 * configuration and alternate setting
 				 * when USB transfers are in use on
 				 * the given interface. Search the USB
-				 * code for "endpoint->refcount" if you
+				 * code for "endpoint->refcount_alloc" if you
 				 * want more information.
 				 */
-				xfer->endpoint->refcount++;
+				USB_BUS_LOCK(info->bus);
+				if (xfer->endpoint->refcount_alloc >= USB_EP_REF_MAX)
+					parm.err = USB_ERR_INVAL;
+
+				xfer->endpoint->refcount_alloc++;
+
+				if (xfer->endpoint->refcount_alloc == 0)
+					panic("usbd_transfer_setup(): Refcount wrapped to zero\n");
+				USB_BUS_UNLOCK(info->bus);
 
 				/*
 				 * Whenever we set ppxfer[] then we
@@ -960,6 +968,10 @@ usbd_transfer_setup(struct usb_device *u
 				 */
 				ppxfer[n] = xfer;
 			}
+
+			/* check for error */
+			if (parm.err)
+				goto done;
 		}
 
 		if (buf || parm.err) {
@@ -1179,7 +1191,9 @@ usbd_transfer_unsetup(struct usb_xfer **
 		 * NOTE: default endpoint does not have an
 		 * interface, even if endpoint->iface_index == 0
 		 */
-		xfer->endpoint->refcount--;
+		USB_BUS_LOCK(info->bus);
+		xfer->endpoint->refcount_alloc--;
+		USB_BUS_UNLOCK(info->bus);
 
 		usb_callout_drain(&xfer->timeout_handle);
 

Modified: stable/8/sys/dev/usb/usbdi.h
==============================================================================
--- stable/8/sys/dev/usb/usbdi.h	Wed Dec  9 22:36:16 2009	(r200327)
+++ stable/8/sys/dev/usb/usbdi.h	Wed Dec  9 22:37:06 2009	(r200328)
@@ -130,13 +130,22 @@ struct usb_endpoint {
 	struct usb_pipe_methods *methods;	/* set by HC driver */
 
 	uint16_t isoc_next;
-	uint16_t refcount;
 
 	uint8_t	toggle_next:1;		/* next data toggle value */
 	uint8_t	is_stalled:1;		/* set if endpoint is stalled */
 	uint8_t	is_synced:1;		/* set if we a synchronised */
 	uint8_t	unused:5;
 	uint8_t	iface_index;		/* not used by "default endpoint" */
+
+	uint8_t refcount_alloc;		/* allocation refcount */
+	uint8_t refcount_bw;		/* bandwidth refcount */
+#define	USB_EP_REF_MAX 0x3f
+
+	/* High-Speed resource allocation (valid if "refcount_bw" > 0) */
+
+	uint8_t	usb_smask;		/* USB start mask */
+	uint8_t	usb_cmask;		/* USB complete mask */
+	uint8_t	usb_uframe;		/* USB microframe */
 };
 
 /*



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200912092237.nB9Mb61k041968>