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

Configure Feed

Select the types of activity you want to include in your feed.

scsi: zfcp: Lift Request Queue tasklet & timer from qdio

The qdio layer currently provides its own infrastructure to scan for
Request Queue completions & to report them to the device driver. This
comes with several drawbacks - having an async tasklet & timer construct in
qdio introduces additional lifetime complexity, and makes it harder to
integrate them with the rest of the device driver. The timeouts are also
currently hard-coded, and can't be tweaked without affecting other qdio
drivers (ie. qeth).

But due to recent enhancements to the qdio layer, zfcp can actually take
full control of the Request Queue completion processing. It merely needs to
opt-out from the qdio layer mechanisms by setting the scan_threshold to 0,
and then use qdio_inspect_queue() to scan for completions.

So re-implement the tasklet & timer mechanism in zfcp, while initially
copying the scan conditions from qdio's handle_outbound() and
qdio_outbound_tasklet(). One minor behavioural change is that
zfcp_qdio_send() will unconditionally reduce the timeout to 1 HZ, rather
than leaving it at 10 Hz if it was last armed by the tasklet. This just
makes things more consistent. Also note that we can drop a lot of the
accumulated cruft in qdio_outbound_tasklet(), as zfcp doesn't even use PCI
interrupt requests any longer.

This also slightly touches the Response Queue processing, as
qdio_get_next_buffers() will no longer implicitly scan for Request Queue
completions. So complete the migration to qdio_inspect_queue() here as well
and make the tasklet_schedule() visible.

Link: https://lore.kernel.org/r/018d3ddd029f8d6ac00cf4184880288c637c4fd1.1618417667.git.bblock@linux.ibm.com
Reviewed-by: Benjamin Block <bblock@linux.ibm.com>
Signed-off-by: Julian Wiedmann <jwi@linux.ibm.com>
Signed-off-by: Benjamin Block <bblock@linux.ibm.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>

authored by

Julian Wiedmann and committed by
Martin K. Petersen
b3f0a1ee be46e39a

+61 -12
+56 -12
drivers/s390/scsi/zfcp_qdio.c
··· 20 20 module_param_named(datarouter, enable_multibuffer, bool, 0400); 21 21 MODULE_PARM_DESC(datarouter, "Enable hardware data router support (default on)"); 22 22 23 + #define ZFCP_QDIO_REQUEST_RESCAN_MSECS (MSEC_PER_SEC * 10) 24 + #define ZFCP_QDIO_REQUEST_SCAN_MSECS MSEC_PER_SEC 25 + 23 26 static void zfcp_qdio_handler_error(struct zfcp_qdio *qdio, char *dbftag, 24 27 unsigned int qdio_err) 25 28 { ··· 73 70 zfcp_qdio_handler_error(qdio, "qdireq1", qdio_err); 74 71 return; 75 72 } 73 + } 76 74 77 - /* cleanup all SBALs being program-owned now */ 78 - zfcp_qdio_zero_sbals(qdio->req_q, idx, count); 75 + static void zfcp_qdio_request_tasklet(struct tasklet_struct *tasklet) 76 + { 77 + struct zfcp_qdio *qdio = from_tasklet(qdio, tasklet, request_tasklet); 78 + struct ccw_device *cdev = qdio->adapter->ccw_device; 79 + unsigned int start, error; 80 + int completed; 79 81 80 - spin_lock_irq(&qdio->stat_lock); 81 - zfcp_qdio_account(qdio); 82 - spin_unlock_irq(&qdio->stat_lock); 83 - atomic_add(count, &qdio->req_q_free); 84 - wake_up(&qdio->req_q_wq); 82 + completed = qdio_inspect_queue(cdev, 0, false, &start, &error); 83 + if (completed > 0) { 84 + if (error) { 85 + zfcp_qdio_handler_error(qdio, "qdreqt1", error); 86 + } else { 87 + /* cleanup all SBALs being program-owned now */ 88 + zfcp_qdio_zero_sbals(qdio->req_q, start, completed); 89 + 90 + spin_lock_irq(&qdio->stat_lock); 91 + zfcp_qdio_account(qdio); 92 + spin_unlock_irq(&qdio->stat_lock); 93 + atomic_add(completed, &qdio->req_q_free); 94 + wake_up(&qdio->req_q_wq); 95 + } 96 + } 97 + 98 + if (atomic_read(&qdio->req_q_free) < QDIO_MAX_BUFFERS_PER_Q) 99 + timer_reduce(&qdio->request_timer, 100 + jiffies + msecs_to_jiffies(ZFCP_QDIO_REQUEST_RESCAN_MSECS)); 101 + } 102 + 103 + static void zfcp_qdio_request_timer(struct timer_list *timer) 104 + { 105 + struct zfcp_qdio *qdio = from_timer(qdio, timer, request_timer); 106 + 107 + tasklet_schedule(&qdio->request_tasklet); 85 108 } 86 109 87 110 static void zfcp_qdio_int_resp(struct ccw_device *cdev, unsigned int qdio_err, ··· 168 139 unsigned int start, error; 169 140 int completed; 170 141 171 - /* Check the Response Queue, and kick off the Request Queue tasklet: */ 172 - completed = qdio_get_next_buffers(cdev, 0, &start, &error); 142 + if (atomic_read(&qdio->req_q_free) < QDIO_MAX_BUFFERS_PER_Q) 143 + tasklet_schedule(&qdio->request_tasklet); 144 + 145 + /* Check the Response Queue: */ 146 + completed = qdio_inspect_queue(cdev, 0, true, &start, &error); 173 147 if (completed < 0) 174 148 return; 175 149 if (completed > 0) ··· 318 286 319 287 /* 320 288 * This should actually be a spin_lock_bh(stat_lock), to protect against 321 - * zfcp_qdio_int_req() in tasklet context. 289 + * Request Queue completion processing in tasklet context. 322 290 * But we can't do so (and are safe), as we always get called with IRQs 323 291 * disabled by spin_lock_irq[save](req_q_lock). 324 292 */ ··· 339 307 sbal_number); 340 308 return retval; 341 309 } 310 + 311 + if (atomic_read(&qdio->req_q_free) <= 2 * ZFCP_QDIO_MAX_SBALS_PER_REQ) 312 + tasklet_schedule(&qdio->request_tasklet); 313 + else 314 + timer_reduce(&qdio->request_timer, 315 + jiffies + msecs_to_jiffies(ZFCP_QDIO_REQUEST_SCAN_MSECS)); 342 316 343 317 /* account for transferred buffers */ 344 318 qdio->req_q_idx += sbal_number; ··· 406 368 wake_up(&qdio->req_q_wq); 407 369 408 370 tasklet_disable(&qdio->irq_tasklet); 371 + tasklet_disable(&qdio->request_tasklet); 372 + del_timer_sync(&qdio->request_timer); 409 373 qdio_stop_irq(adapter->ccw_device); 410 374 qdio_shutdown(adapter->ccw_device, QDIO_FLAG_CLEANUP_USING_CLEAR); 411 375 ··· 468 428 init_data.int_parm = (unsigned long) qdio; 469 429 init_data.input_sbal_addr_array = input_sbals; 470 430 init_data.output_sbal_addr_array = output_sbals; 471 - init_data.scan_threshold = 472 - QDIO_MAX_BUFFERS_PER_Q - ZFCP_QDIO_MAX_SBALS_PER_REQ * 2; 473 431 474 432 if (qdio_establish(cdev, &init_data)) 475 433 goto failed_establish; ··· 509 471 atomic_set(&qdio->req_q_free, QDIO_MAX_BUFFERS_PER_Q); 510 472 atomic_or(ZFCP_STATUS_ADAPTER_QDIOUP, &qdio->adapter->status); 511 473 474 + /* Enable processing for Request Queue completions: */ 475 + tasklet_enable(&qdio->request_tasklet); 512 476 /* Enable processing for QDIO interrupts: */ 513 477 tasklet_enable(&qdio->irq_tasklet); 514 478 /* This results in a qdio_start_irq(): */ ··· 534 494 return; 535 495 536 496 tasklet_kill(&qdio->irq_tasklet); 497 + tasklet_kill(&qdio->request_tasklet); 537 498 538 499 if (qdio->adapter->ccw_device) 539 500 qdio_free(qdio->adapter->ccw_device); ··· 561 520 562 521 spin_lock_init(&qdio->req_q_lock); 563 522 spin_lock_init(&qdio->stat_lock); 523 + timer_setup(&qdio->request_timer, zfcp_qdio_request_timer, 0); 564 524 tasklet_setup(&qdio->irq_tasklet, zfcp_qdio_irq_tasklet); 525 + tasklet_setup(&qdio->request_tasklet, zfcp_qdio_request_tasklet); 565 526 tasklet_disable(&qdio->irq_tasklet); 527 + tasklet_disable(&qdio->request_tasklet); 566 528 567 529 adapter->qdio = qdio; 568 530 return 0;
+5
drivers/s390/scsi/zfcp_qdio.h
··· 30 30 * @req_q_util: used for accounting 31 31 * @req_q_full: queue full incidents 32 32 * @req_q_wq: used to wait for SBAL availability 33 + * @irq_tasklet: used for QDIO interrupt processing 34 + * @request_tasklet: used for Request Queue completion processing 35 + * @request_timer: used to trigger the Request Queue completion processing 33 36 * @adapter: adapter used in conjunction with this qdio structure 34 37 * @max_sbale_per_sbal: qdio limit per sbal 35 38 * @max_sbale_per_req: qdio limit per request ··· 49 46 atomic_t req_q_full; 50 47 wait_queue_head_t req_q_wq; 51 48 struct tasklet_struct irq_tasklet; 49 + struct tasklet_struct request_tasklet; 50 + struct timer_list request_timer; 52 51 struct zfcp_adapter *adapter; 53 52 u16 max_sbale_per_sbal; 54 53 u16 max_sbale_per_req;