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

ioat3: interrupt coalescing

The hardware automatically disables further interrupts after each event
until rearmed. This allows a delay to be injected between the occurence
of the interrupt and the running of the cleanup routine. The delay is
scaled by the descriptor backlog and then written to the INTRDELAY
register which specifies the number of microseconds to hold off
interrupt delivery after an interrupt event occurs. According to
powertop this reduces the interrupt rate from ~5000 intr/s to ~150
intr/s per without affecting throughput (simple dd to a raid6 array).

Signed-off-by: Dan Williams <dan.j.williams@intel.com>

+34 -5
+33 -4
drivers/dma/ioat/dma_v3.c
··· 295 295 ioat->tail += i; 296 296 BUG_ON(active && !seen_current); /* no active descs have written a completion? */ 297 297 chan->last_completion = phys_complete; 298 - if (ioat->head == ioat->tail) { 298 + 299 + active = ioat2_ring_active(ioat); 300 + if (active == 0) { 299 301 dev_dbg(to_dev(chan), "%s: cancel completion timeout\n", 300 302 __func__); 301 303 clear_bit(IOAT_COMPLETION_PENDING, &chan->state); 302 304 mod_timer(&chan->timer, jiffies + IDLE_TIMEOUT); 303 305 } 306 + /* 5 microsecond delay per pending descriptor */ 307 + writew(min((5 * active), IOAT_INTRDELAY_MASK), 308 + chan->device->reg_base + IOAT_INTRDELAY_OFFSET); 304 309 } 305 310 306 - static void ioat3_cleanup(struct ioat2_dma_chan *ioat) 311 + /* try to cleanup, but yield (via spin_trylock) to incoming submissions 312 + * with the expectation that we will immediately poll again shortly 313 + */ 314 + static void ioat3_cleanup_poll(struct ioat2_dma_chan *ioat) 307 315 { 308 316 struct ioat_chan_common *chan = &ioat->base; 309 317 unsigned long phys_complete; ··· 337 329 spin_unlock_bh(&chan->cleanup_lock); 338 330 } 339 331 332 + /* run cleanup now because we already delayed the interrupt via INTRDELAY */ 333 + static void ioat3_cleanup_sync(struct ioat2_dma_chan *ioat) 334 + { 335 + struct ioat_chan_common *chan = &ioat->base; 336 + unsigned long phys_complete; 337 + 338 + prefetch(chan->completion); 339 + 340 + spin_lock_bh(&chan->cleanup_lock); 341 + if (!ioat_cleanup_preamble(chan, &phys_complete)) { 342 + spin_unlock_bh(&chan->cleanup_lock); 343 + return; 344 + } 345 + spin_lock_bh(&ioat->ring_lock); 346 + 347 + __cleanup(ioat, phys_complete); 348 + 349 + spin_unlock_bh(&ioat->ring_lock); 350 + spin_unlock_bh(&chan->cleanup_lock); 351 + } 352 + 340 353 static void ioat3_cleanup_tasklet(unsigned long data) 341 354 { 342 355 struct ioat2_dma_chan *ioat = (void *) data; 343 356 344 - ioat3_cleanup(ioat); 357 + ioat3_cleanup_sync(ioat); 345 358 writew(IOAT_CHANCTRL_RUN, ioat->base.reg_base + IOAT_CHANCTRL_OFFSET); 346 359 } 347 360 ··· 446 417 if (ioat_is_complete(c, cookie, done, used) == DMA_SUCCESS) 447 418 return DMA_SUCCESS; 448 419 449 - ioat3_cleanup(ioat); 420 + ioat3_cleanup_poll(ioat); 450 421 451 422 return ioat_is_complete(c, cookie, done, used); 452 423 }
+1 -1
drivers/dma/ioat/registers.h
··· 60 60 #define IOAT_PERPORTOFFSET_OFFSET 0x0A /* 16-bit */ 61 61 62 62 #define IOAT_INTRDELAY_OFFSET 0x0C /* 16-bit */ 63 - #define IOAT_INTRDELAY_INT_DELAY_MASK 0x3FFF /* Interrupt Delay Time */ 63 + #define IOAT_INTRDELAY_MASK 0x3FFF /* Interrupt Delay Time */ 64 64 #define IOAT_INTRDELAY_COALESE_SUPPORT 0x8000 /* Interrupt Coalescing Supported */ 65 65 66 66 #define IOAT_DEVICE_STATUS_OFFSET 0x0E /* 16-bit */