Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

sdio: adaptive interrupt polling

The interrupt polling frequency is a compromise between power usage and
interrupt latency. Unfortunately, it affects throughput rather severely
for devices which require an interrupt for every chunk of data.

By making the polling frequency adaptive, we get better throughput with
those devices without sacficing too much power. Polling will quickly
increase when there is an actual interrupt, and slowly fall back to the
idle frequency when the interrupts stop coming.

Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>

+23 -5
+23 -5
drivers/mmc/core/sdio_irq.c
··· 27 27 28 28 static int process_sdio_pending_irqs(struct mmc_card *card) 29 29 { 30 - int i, ret; 30 + int i, ret, count; 31 31 unsigned char pending; 32 32 33 33 ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTx, 0, &pending); ··· 37 37 return ret; 38 38 } 39 39 40 + count = 0; 40 41 for (i = 1; i <= 7; i++) { 41 42 if (pending & (1 << i)) { 42 43 struct sdio_func *func = card->sdio_func[i - 1]; ··· 47 46 sdio_func_id(func)); 48 47 } else if (func->irq_handler) { 49 48 func->irq_handler(func); 49 + count++; 50 50 } else 51 51 printk(KERN_WARNING "%s: pending IRQ with no handler\n", 52 52 sdio_func_id(func)); 53 53 } 54 54 } 55 55 56 - return 0; 56 + return count; 57 57 } 58 58 59 59 static int sdio_irq_thread(void *_host) 60 60 { 61 61 struct mmc_host *host = _host; 62 62 struct sched_param param = { .sched_priority = 1 }; 63 - unsigned long period; 63 + unsigned long period, idle_period; 64 64 int ret; 65 65 66 66 sched_setscheduler(current, SCHED_FIFO, &param); ··· 72 70 * asynchronous notification of pending SDIO card interrupts 73 71 * hence we poll for them in that case. 74 72 */ 73 + idle_period = msecs_to_jiffies(10); 75 74 period = (host->caps & MMC_CAP_SDIO_IRQ) ? 76 - MAX_SCHEDULE_TIMEOUT : msecs_to_jiffies(10); 75 + MAX_SCHEDULE_TIMEOUT : idle_period; 77 76 78 77 pr_debug("%s: IRQ thread started (poll period = %lu jiffies)\n", 79 78 mmc_hostname(host), period); ··· 104 101 * errors. FIXME: determine if due to card removal and 105 102 * possibly exit this thread if so. 106 103 */ 107 - if (ret) 104 + if (ret < 0) 108 105 ssleep(1); 106 + 107 + /* 108 + * Adaptive polling frequency based on the assumption 109 + * that an interrupt will be closely followed by more. 110 + * This has a substantial benefit for network devices. 111 + */ 112 + if (!(host->caps & MMC_CAP_SDIO_IRQ)) { 113 + if (ret > 0) 114 + period /= 2; 115 + else { 116 + period++; 117 + if (period > idle_period) 118 + period = idle_period; 119 + } 120 + } 109 121 110 122 set_task_state(current, TASK_INTERRUPTIBLE); 111 123 if (host->caps & MMC_CAP_SDIO_IRQ)