ChangeSet 1.1722.97.75, 2004/06/17 15:20:50-07:00, stern@rowland.harvard.edu [PATCH] USB: Only process ports with change events pending This patch adds a bit-array to the hub driver's private data structure, used for storing the contents of the hub's interrupt status message. That message indicates which ports have events pending (and whether the hub itself has events pending). By only polling the status of the ports listed in the bit-array we can save a fair amount of overhead in hub communication. (The #error test added to hub.h is a little awkward, but it's purely precautionary -- it won't matter until someone decides to support hubs with more than 31 ports!) Also included in the patch, since this seemed the perfect opportunity for it, is Byron's suggestion for handling hub events in the order in which they were received. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman drivers/usb/core/hub.c | 16 ++++++++++++++-- drivers/usb/core/hub.h | 4 ++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff -Nru a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c --- a/drivers/usb/core/hub.c Fri Jun 18 10:53:23 2004 +++ b/drivers/usb/core/hub.c Fri Jun 18 10:53:23 2004 @@ -231,6 +231,8 @@ { struct usb_hub *hub = (struct usb_hub *)urb->context; int status; + int i; + unsigned long bits; spin_lock(&hub_event_lock); hub->urb_active = 0; @@ -255,6 +257,11 @@ /* let khubd handle things */ case 0: /* we got data: port status changed */ + bits = 0; + for (i = 0; i < urb->actual_length; ++i) + bits |= ((unsigned long) ((*hub->buffer)[i])) + << (i*8); + hub->event_bits[0] = bits; break; } @@ -262,7 +269,7 @@ /* Something happened, let khubd figure it out */ if (list_empty(&hub->event_list)) { - list_add(&hub->event_list, &hub_event_list); + list_add_tail(&hub->event_list, &hub_event_list); wake_up(&khubd_wait); } @@ -1776,7 +1783,10 @@ hub->error = 0; } + /* deal with port status changes */ for (i = 0; i < hub->descriptor->bNbrPorts; i++) { + if (!test_and_clear_bit(i+1, hub->event_bits)) + continue; ret = hub_port_status(hdev, i, &portstatus, &portchange); if (ret < 0) continue; @@ -1846,7 +1856,9 @@ } /* end for i */ /* deal with hub status changes */ - if (hub_hub_status(hub, &hubstatus, &hubchange) < 0) + if (test_and_clear_bit(0, hub->event_bits) == 0) + ; /* do nothing */ + else if (hub_hub_status(hub, &hubstatus, &hubchange) < 0) dev_err (hub_dev, "get_hub_status failed\n"); else { if (hubchange & HUB_CHANGE_LOCAL_POWER) { diff -Nru a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h --- a/drivers/usb/core/hub.h Fri Jun 18 10:53:23 2004 +++ b/drivers/usb/core/hub.h Fri Jun 18 10:53:23 2004 @@ -203,6 +203,10 @@ int nerrors; /* track consecutive errors */ struct list_head event_list; /* hubs w/data or errs ready */ + unsigned long event_bits[1]; /* status change bitmask */ +#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */ +#error event_bits[] is too short! +#endif struct usb_hub_descriptor *descriptor; /* class descriptor */ struct usb_tt tt; /* Transaction Translator */