Hi,
I'd like to get some feedback on this patch before
sending it to Linus.
It adds infrastructure to usb.c [usb_check_bandwidth(),
usb_claim_bandwidth(), and usb_release_bandwidth()]
and modifies uhci.c to use these functions [I used
uhci.c simply because I know it better].
I have a few questions about when/where I need to
add spinlocks and I'm not real fond of using
urb->bandwidth to store the URB's bandwidth usage
since a driver could maliciously change it, but
this isn't the only example of where a driver could
do something like that to lie to the system.
With this patch, usb.c (usbcore.o) has a "bandwidth" parameter
that is used to enable/disable enforcement of bandwidth
allocation. For now it defaults to "disable" (0).
To enable it, use: insmod usbcore.o bandwidth=1
Even with bandwidth allocation disabled, it still goes
thru all of the steps and then prints/logs a message
if the allocation would have failed.
Comments?
~Randy
--- linux/include/linux/usb.h.orig Sun May 14 10:49:29 2000
+++ linux/include/linux/usb.h Sun May 14 15:22:28 2000
@@ -331,6 +331,9 @@
* New USB Structures *
*----------------------------------------------------------------------------*/
+/*
+ * urb->transfer_flags:
+ */
#define USB_DISABLE_SPD 0x0001
#define USB_ISO_ASAP 0x0002
#define USB_URB_EARLY_COMPLETE 0x0004
@@ -362,10 +365,11 @@
void *transfer_buffer; // associated data buffer
int transfer_buffer_length; // data buffer length
int actual_length; // actual data buffer length
+ int bandwidth; // bandwidth for this transfer request (INT or
+ISO)
unsigned char *setup_packet; // setup packet (control only)
//
int start_frame; // start frame (iso/irq only)
- int number_of_packets; // number of packets in this request (iso/irq
only)
+ int number_of_packets; // number of packets in this request (iso)
int interval; // polling interval (irq only)
int error_count; // number of errors in this transfer (iso only)
int timeout; // timeout (in jiffies)
@@ -483,7 +487,7 @@
struct list_head bus_list;
void *hcpriv; /* Host Controller private data */
- unsigned int bandwidth_allocated; /* on this Host Controller; */
+ int bandwidth_allocated; /* on this Host Controller; */
/* applies to Int. and Isoc. pipes; */
/* measured in microseconds/frame; */
/* range is 0..900, where 900 = */
@@ -554,7 +558,10 @@
extern void usb_free_dev(struct usb_device *);
extern void usb_inc_dev_use(struct usb_device *);
#define usb_dec_dev_use usb_free_dev
-extern void usb_release_bandwidth(struct usb_device *, int);
+
+extern int usb_check_bandwidth (struct usb_device *dev, struct urb *urb);
+extern void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb, int
+bustime, int isoc);
+extern void usb_release_bandwidth(struct usb_device *dev, struct urb *urb, int isoc);
extern int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
__u8 requesttype, __u16 value, __u16 index, void *data, __u16 size, int timeout);
--- linux/drivers/usb-2399-pre91/uhci.c Fri May 12 13:21:55 2000
+++ linux/drivers/usb/uhci.c Sun May 14 16:11:05 2000
@@ -1294,6 +1294,7 @@
struct uhci *uhci;
unsigned long flags;
struct urb *u;
+ int bustime;
if (!urb)
return -EINVAL;
@@ -1323,13 +1324,40 @@
ret = uhci_submit_control(urb);
break;
case PIPE_INTERRUPT:
- ret = uhci_submit_interrupt(urb);
+ if (urb->bandwidth == 0) { /* not yet checked/allocated */
+ bustime = usb_check_bandwidth (urb->dev, urb);
+ if (bustime < 0)
+ ret = bustime;
+ else {
+ ret = uhci_submit_interrupt(urb);
+ if (ret == -EINPROGRESS)
+ usb_claim_bandwidth (urb->dev, urb, bustime,
+0);
+ }
+ } else { /* bandwidth is already set */
+ ret = uhci_submit_interrupt(urb);
+ }
break;
case PIPE_BULK:
ret = uhci_submit_bulk(urb, u);
break;
case PIPE_ISOCHRONOUS:
- ret = uhci_submit_isochronous(urb);
+ if (urb->bandwidth == 0) { /* not yet checked/allocated */
+ if (urb->number_of_packets <= 0) {
+ ret = -EINVAL;
+ break;
+ }
+ bustime = usb_check_bandwidth (urb->dev, urb);
+ if (bustime < 0) {
+ ret = bustime;
+ break;
+ }
+
+ ret = uhci_submit_isochronous(urb);
+ if (ret == -EINPROGRESS)
+ usb_claim_bandwidth (urb->dev, urb, bustime, 1);
+ } else { /* bandwidth is already set */
+ ret = uhci_submit_isochronous(urb);
+ }
break;
}
@@ -1385,6 +1413,10 @@
case PIPE_CONTROL:
case PIPE_BULK:
case PIPE_ISOCHRONOUS:
+ /* Release bandwidth for Interrupt or Isoc. transfers */
+ /* Spinlock needed ? */
+ if (urb->bandwidth)
+ usb_release_bandwidth (urb->dev, urb, 1);
uhci_unlink_generic(urb);
break;
case PIPE_INTERRUPT:
@@ -1392,8 +1424,13 @@
urb->complete(urb);
if (urb->interval)
uhci_reset_interrupt(urb);
- else
+ else {
+ /* Release bandwidth for Interrupt or Isoc. transfers */
+ /* Spinlock needed ? */
+ if (urb->bandwidth)
+ usb_release_bandwidth (urb->dev, urb, 0);
uhci_unlink_generic(urb);
+ }
return; /* <-- Note the return */
}
@@ -1473,6 +1510,21 @@
/* Short circuit the virtual root hub */
if (usb_pipedevice(urb->pipe) == uhci->rh.devnum)
return rh_unlink_urb(urb);
+
+ /* Release bandwidth for Interrupt or Isoc. transfers */
+ /* Spinlock needed ? */
+ if (urb->bandwidth) {
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_INTERRUPT:
+ usb_release_bandwidth (urb->dev, urb, 0);
+ break;
+ case PIPE_ISOCHRONOUS:
+ usb_release_bandwidth (urb->dev, urb, 1);
+ break;
+ default:
+ break;
+ }
+ }
if (urb->status == -EINPROGRESS) {
uhci_unlink_generic(urb);
--- linux/drivers/usb-2399-pre91/usb.c Mon May 8 17:37:28 2000
+++ linux/drivers/usb/usb.c Sun May 14 14:28:25 2000
@@ -6,6 +6,7 @@
* (C) Copyright Andreas Gal 1999
* (C) Copyright Gregory P. Smith 1999
* (C) Copyright Deti Fliegl 1999 (new USB architecture)
+ * (C) Copyright Randy Dunlap 2000
*
* NOTE! This is not actually a driver at all, rather this is
* just a collection of helper routines that implement the
@@ -31,6 +32,9 @@
#endif
#include <linux/usb.h>
+static int bandwidth = 0; /* default to NOT enforcing bandwidth allocation */
+MODULE_PARM(bandwidth, "i");
+
/*
* Prototypes for the device driver probing/loading functions
*/
@@ -144,11 +148,11 @@
/*
- * calc_bus_time:
+ * usb_calc_bus_time:
*
* returns (approximate) USB bus time in nanoseconds for a USB transaction.
*/
-static long calc_bus_time (int low_speed, int input_dir, int isoc, int bytecount)
+static long usb_calc_bus_time (int low_speed, int input_dir, int isoc, int bytecount)
{
unsigned long tmp;
@@ -178,16 +182,16 @@
tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
return (((input_dir) ? 7268L : 6265L) + BW_HOST_DELAY + tmp);
-} /* end calc_bus_time */
+}
/*
- * check_bandwidth_alloc():
+ * usb_check_bandwidth():
*
* old_alloc is from host_controller->bandwidth_allocated in microseconds;
* bustime is from calc_bus_time(), but converted to microseconds.
*
- * returns 0 if successful,
- * -1 if bandwidth request fails.
+ * returns <bustime in us> if successful,
+ * or USB_ST_BANDWIDTH_ERROR if bandwidth request fails.
*
* FIXME:
* This initial implementation does not use Endpoint.bInterval
@@ -204,21 +208,67 @@
* However, this first cut at USB bandwidth allocation does not
* contain any frame allocation tracking.
*/
-static int check_bandwidth_alloc (unsigned int old_alloc, long bustime)
+int usb_check_bandwidth (struct usb_device *dev, struct urb *urb)
{
- unsigned int new_alloc;
+ int new_alloc;
+ int old_alloc = dev->bus->bandwidth_allocated;
+ unsigned int pipe = urb->pipe;
+ long bustime;
+
+ bustime = usb_calc_bus_time (usb_pipeslow(pipe), usb_pipein(pipe),
+ usb_pipeisoc(pipe), usb_maxpacket(dev, pipe,
+usb_pipeout(pipe)));
+ if (usb_pipeisoc(pipe))
+ bustime = NS_TO_US(bustime) / urb->number_of_packets;
+ else
+ bustime = NS_TO_US(bustime);
- new_alloc = old_alloc + bustime;
+ new_alloc = old_alloc + (int)bustime;
/* what new total allocated bus time would be */
- dbg("usb-bandwidth-alloc: was: %u, new: %u, "
- "bustime = %ld us, Pipe allowed: %s",
- old_alloc, new_alloc, bustime,
- (new_alloc <= FRAME_TIME_MAX_USECS_ALLOC) ?
- "yes" : "no");
+ if (new_alloc > FRAME_TIME_MAX_USECS_ALLOC)
+ dbg("usb-check-bandwidth %sFAILED: was %u, would be %u, bustime = %ld
+us",
+ bandwidth ? "" : "would have ",
+ old_alloc, new_alloc, bustime);
+
+ if (!bandwidth)
+ return (bustime);
+ return (new_alloc <= FRAME_TIME_MAX_USECS_ALLOC) ? bustime :
+USB_ST_BANDWIDTH_ERROR;
+}
+
+void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb, int bustime, int
+isoc)
+{
+ dev->bus->bandwidth_allocated += bustime;
+ if (isoc)
+ dev->bus->bandwidth_isoc_reqs++;
+ else
+ dev->bus->bandwidth_int_reqs++;
+ urb->bandwidth = bustime;
+
+ dbg("bw_alloc increased by %d to %d for %d requesters",
+ bustime,
+ dev->bus->bandwidth_allocated,
+ dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);
+}
+
+/*
+ * usb_release_bandwidth():
+ *
+ * called to release a pipe's bandwidth (in microseconds)
+ */
+void usb_release_bandwidth(struct usb_device *dev, struct urb *urb, int isoc)
+{
+ dev->bus->bandwidth_allocated -= urb->bandwidth;
+ if (isoc)
+ dev->bus->bandwidth_isoc_reqs--;
+ else
+ dev->bus->bandwidth_int_reqs--;
- return (new_alloc <= FRAME_TIME_MAX_USECS_ALLOC) ? 0 : -1;
-} /* end check_bandwidth_alloc */
+ dbg("bw_alloc reduced by %d to %d for %d requesters",
+ urb->bandwidth,
+ dev->bus->bandwidth_allocated,
+ dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);
+ urb->bandwidth = 0;
+}
/*
* New functions for (de)registering a controller
@@ -659,21 +709,6 @@
}
/*
- * usb_release_bandwidth():
- *
- * called to release an interrupt pipe's bandwidth (in microseconds)
- */
-void usb_release_bandwidth(struct usb_device *dev, int bw_alloc)
-{
- dev->bus->bandwidth_allocated -= bw_alloc;
- dev->bus->bandwidth_int_reqs--;
- dbg("bw_alloc reduced to %d for %d requesters",
- dev->bus->bandwidth_allocated,
- dev->bus->bandwidth_int_reqs +
- dev->bus->bandwidth_isoc_reqs);
-}
-
-/*
* usb_get_current_frame_number()
*
* returns the current frame number for the parent USB bus/controller
@@ -684,6 +719,7 @@
return usb_dev->bus->op->get_frame_number (usb_dev);
}
/*-------------------------------------------------------------------*/
+
static int usb_parse_endpoint(struct usb_device *dev, struct usb_endpoint_descriptor
*endpoint, unsigned char *buffer, int size)
{
struct usb_descriptor_header *header;
@@ -1724,6 +1760,9 @@
EXPORT_SYMBOL(usb_reset_device);
EXPORT_SYMBOL(usb_connect);
EXPORT_SYMBOL(usb_disconnect);
+
+EXPORT_SYMBOL(usb_check_bandwidth);
+EXPORT_SYMBOL(usb_claim_bandwidth);
EXPORT_SYMBOL(usb_release_bandwidth);
EXPORT_SYMBOL(usb_set_address);
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]