ChangeSet 1.1557.49.6, 2004/02/17 16:21:17-08:00, stern@rowland.harvard.edu [PATCH] USB: Even out distribution of UHCI interrupt transfers This patch evens out the distribution of interrupt transfers for the UHCI driver. It insures that no frame must handle interrupt queues for more than two different periods, thus improving the bandwidth utilization. It also simplifies the initialization code by replacing a multi-nested "if" statement with a simple bit-operation (thanks to Eric Piel for suggesting this approach). drivers/usb/host/uhci-debug.c | 7 ++-- drivers/usb/host/uhci-hcd.c | 66 +++++++++++++++++++----------------------- 2 files changed, 35 insertions(+), 38 deletions(-) diff -Nru a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c --- a/drivers/usb/host/uhci-debug.c Thu Feb 19 17:22:37 2004 +++ b/drivers/usb/host/uhci-debug.c Thu Feb 19 17:22:37 2004 @@ -418,7 +418,7 @@ { unsigned long flags; char *out = buf; - int i; + int i, j; struct uhci_qh *qh; struct uhci_td *td; struct list_head *tmp, *head; @@ -473,10 +473,11 @@ continue; } + j = (i < 7) ? 7 : i+1; /* Next skeleton */ if (list_empty(&qh->list)) { if (i < UHCI_NUM_SKELQH - 1) { if (qh->link != - (cpu_to_le32(uhci->skelqh[i + 1]->dma_handle) | UHCI_PTR_QH)) { + (cpu_to_le32(uhci->skelqh[j]->dma_handle) | UHCI_PTR_QH)) { show_qh_name(); out += sprintf(out, " skeleton QH not linked to next skeleton QH!\n"); } @@ -500,7 +501,7 @@ if (i < UHCI_NUM_SKELQH - 1) { if (qh->link != - (cpu_to_le32(uhci->skelqh[i + 1]->dma_handle) | UHCI_PTR_QH)) + (cpu_to_le32(uhci->skelqh[j]->dma_handle) | UHCI_PTR_QH)) out += sprintf(out, " last QH not linked to next skeleton!\n"); } } diff -Nru a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c --- a/drivers/usb/host/uhci-hcd.c Thu Feb 19 17:22:37 2004 +++ b/drivers/usb/host/uhci-hcd.c Thu Feb 19 17:22:37 2004 @@ -48,6 +48,7 @@ #endif #include +#include #include #include #include @@ -2316,16 +2317,17 @@ } /* - * 8 Interrupt queues; link int2 to int1, int4 to int2, etc + * 8 Interrupt queues; link all higher int queues to int1, * then link int1 to control and control to bulk */ - uhci->skel_int128_qh->link = cpu_to_le32(uhci->skel_int64_qh->dma_handle) | UHCI_PTR_QH; - uhci->skel_int64_qh->link = cpu_to_le32(uhci->skel_int32_qh->dma_handle) | UHCI_PTR_QH; - uhci->skel_int32_qh->link = cpu_to_le32(uhci->skel_int16_qh->dma_handle) | UHCI_PTR_QH; - uhci->skel_int16_qh->link = cpu_to_le32(uhci->skel_int8_qh->dma_handle) | UHCI_PTR_QH; - uhci->skel_int8_qh->link = cpu_to_le32(uhci->skel_int4_qh->dma_handle) | UHCI_PTR_QH; - uhci->skel_int4_qh->link = cpu_to_le32(uhci->skel_int2_qh->dma_handle) | UHCI_PTR_QH; - uhci->skel_int2_qh->link = cpu_to_le32(uhci->skel_int1_qh->dma_handle) | UHCI_PTR_QH; + uhci->skel_int128_qh->link = + uhci->skel_int64_qh->link = + uhci->skel_int32_qh->link = + uhci->skel_int16_qh->link = + uhci->skel_int8_qh->link = + uhci->skel_int4_qh->link = + uhci->skel_int2_qh->link = + cpu_to_le32(uhci->skel_int1_qh->dma_handle) | UHCI_PTR_QH; uhci->skel_int1_qh->link = cpu_to_le32(uhci->skel_ls_control_qh->dma_handle) | UHCI_PTR_QH; uhci->skel_ls_control_qh->link = cpu_to_le32(uhci->skel_hs_control_qh->dma_handle) | UHCI_PTR_QH; @@ -2341,39 +2343,33 @@ uhci->skel_term_qh->element = cpu_to_le32(uhci->term_td->dma_handle); /* - * Fill the frame list: make all entries point to - * the proper interrupt queue. + * Fill the frame list: make all entries point to the proper + * interrupt queue. * - * This is probably silly, but it's a simple way to - * scatter the interrupt queues in a way that gives - * us a reasonable dynamic range for irq latencies. + * The interrupt queues will be interleaved as evenly as possible. + * There's not much to be done about period-1 interrupts; they have + * to occur in every frame. But we can schedule period-2 interrupts + * in odd-numbered frames, period-4 interrupts in frames congruent + * to 2 (mod 4), and so on. This way each frame only has two + * interrupt QHs, which will help spread out bandwidth utilization. */ for (i = 0; i < UHCI_NUMFRAMES; i++) { - int irq = 0; + int irq; - if (i & 1) { - irq++; - if (i & 2) { - irq++; - if (i & 4) { - irq++; - if (i & 8) { - irq++; - if (i & 16) { - irq++; - if (i & 32) { - irq++; - if (i & 64) - irq++; - } - } - } - } - } - } + /* + * ffs (Find First bit Set) does exactly what we need: + * 1,3,5,... => ffs = 0 => use skel_int2_qh = skelqh[6], + * 2,6,10,... => ffs = 1 => use skel_int4_qh = skelqh[5], etc. + * ffs > 6 => not on any high-period queue, so use + * skel_int1_qh = skelqh[7]. + * Add UHCI_NUMFRAMES to insure at least one bit is set. + */ + irq = 6 - (int) __ffs(i + UHCI_NUMFRAMES); + if (irq < 0) + irq = 7; /* Only place we don't use the frame list routines */ - uhci->fl->frame[i] = cpu_to_le32(uhci->skelqh[7 - irq]->dma_handle); + uhci->fl->frame[i] = cpu_to_le32(uhci->skelqh[irq]->dma_handle); } start_hc(uhci);