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

Merge tag 'for-5.12/block-ipi-2021-02-21' of git://git.kernel.dk/linux-block

Pull block IPI updates from Jens Axboe:
"Avoid IRQ locking for the block IPI handling (Sebastian Andrzej
Siewior)"

* tag 'for-5.12/block-ipi-2021-02-21' of git://git.kernel.dk/linux-block:
blk-mq: Use llist_head for blk_cpu_done
blk-mq: Always complete remote completions requests in softirq
smp: Process pending softirqs in flush_smp_call_function_from_idle()

+42 -69
+41 -68
block/blk-mq.c
··· 41 41 #include "blk-mq-sched.h" 42 42 #include "blk-rq-qos.h" 43 43 44 - static DEFINE_PER_CPU(struct list_head, blk_cpu_done); 44 + static DEFINE_PER_CPU(struct llist_head, blk_cpu_done); 45 45 46 46 static void blk_mq_poll_stats_start(struct request_queue *q); 47 47 static void blk_mq_poll_stats_fn(struct blk_stat_callback *cb); ··· 567 567 } 568 568 EXPORT_SYMBOL(blk_mq_end_request); 569 569 570 - /* 571 - * Softirq action handler - move entries to local list and loop over them 572 - * while passing them to the queue registered handler. 573 - */ 574 - static __latent_entropy void blk_done_softirq(struct softirq_action *h) 570 + static void blk_complete_reqs(struct llist_head *list) 575 571 { 576 - struct list_head *cpu_list, local_list; 572 + struct llist_node *entry = llist_reverse_order(llist_del_all(list)); 573 + struct request *rq, *next; 577 574 578 - local_irq_disable(); 579 - cpu_list = this_cpu_ptr(&blk_cpu_done); 580 - list_replace_init(cpu_list, &local_list); 581 - local_irq_enable(); 582 - 583 - while (!list_empty(&local_list)) { 584 - struct request *rq; 585 - 586 - rq = list_entry(local_list.next, struct request, ipi_list); 587 - list_del_init(&rq->ipi_list); 575 + llist_for_each_entry_safe(rq, next, entry, ipi_list) 588 576 rq->q->mq_ops->complete(rq); 589 - } 590 577 } 591 578 592 - static void blk_mq_trigger_softirq(struct request *rq) 579 + static __latent_entropy void blk_done_softirq(struct softirq_action *h) 593 580 { 594 - struct list_head *list; 595 - unsigned long flags; 596 - 597 - local_irq_save(flags); 598 - list = this_cpu_ptr(&blk_cpu_done); 599 - list_add_tail(&rq->ipi_list, list); 600 - 601 - /* 602 - * If the list only contains our just added request, signal a raise of 603 - * the softirq. If there are already entries there, someone already 604 - * raised the irq but it hasn't run yet. 605 - */ 606 - if (list->next == &rq->ipi_list) 607 - raise_softirq_irqoff(BLOCK_SOFTIRQ); 608 - local_irq_restore(flags); 581 + blk_complete_reqs(this_cpu_ptr(&blk_cpu_done)); 609 582 } 610 583 611 584 static int blk_softirq_cpu_dead(unsigned int cpu) 612 585 { 613 - /* 614 - * If a CPU goes away, splice its entries to the current CPU 615 - * and trigger a run of the softirq 616 - */ 617 - local_irq_disable(); 618 - list_splice_init(&per_cpu(blk_cpu_done, cpu), 619 - this_cpu_ptr(&blk_cpu_done)); 620 - raise_softirq_irqoff(BLOCK_SOFTIRQ); 621 - local_irq_enable(); 622 - 586 + blk_complete_reqs(&per_cpu(blk_cpu_done, cpu)); 623 587 return 0; 624 588 } 625 589 626 - 627 590 static void __blk_mq_complete_request_remote(void *data) 628 591 { 629 - struct request *rq = data; 630 - 631 - /* 632 - * For most of single queue controllers, there is only one irq vector 633 - * for handling I/O completion, and the only irq's affinity is set 634 - * to all possible CPUs. On most of ARCHs, this affinity means the irq 635 - * is handled on one specific CPU. 636 - * 637 - * So complete I/O requests in softirq context in case of single queue 638 - * devices to avoid degrading I/O performance due to irqsoff latency. 639 - */ 640 - if (rq->q->nr_hw_queues == 1) 641 - blk_mq_trigger_softirq(rq); 642 - else 643 - rq->q->mq_ops->complete(rq); 592 + __raise_softirq_irqoff(BLOCK_SOFTIRQ); 644 593 } 645 594 646 595 static inline bool blk_mq_complete_need_ipi(struct request *rq) ··· 618 669 return cpu_online(rq->mq_ctx->cpu); 619 670 } 620 671 672 + static void blk_mq_complete_send_ipi(struct request *rq) 673 + { 674 + struct llist_head *list; 675 + unsigned int cpu; 676 + 677 + cpu = rq->mq_ctx->cpu; 678 + list = &per_cpu(blk_cpu_done, cpu); 679 + if (llist_add(&rq->ipi_list, list)) { 680 + INIT_CSD(&rq->csd, __blk_mq_complete_request_remote, rq); 681 + smp_call_function_single_async(cpu, &rq->csd); 682 + } 683 + } 684 + 685 + static void blk_mq_raise_softirq(struct request *rq) 686 + { 687 + struct llist_head *list; 688 + 689 + preempt_disable(); 690 + list = this_cpu_ptr(&blk_cpu_done); 691 + if (llist_add(&rq->ipi_list, list)) 692 + raise_softirq(BLOCK_SOFTIRQ); 693 + preempt_enable(); 694 + } 695 + 621 696 bool blk_mq_complete_request_remote(struct request *rq) 622 697 { 623 698 WRITE_ONCE(rq->state, MQ_RQ_COMPLETE); ··· 654 681 return false; 655 682 656 683 if (blk_mq_complete_need_ipi(rq)) { 657 - INIT_CSD(&rq->csd, __blk_mq_complete_request_remote, rq); 658 - smp_call_function_single_async(rq->mq_ctx->cpu, &rq->csd); 659 - } else { 660 - if (rq->q->nr_hw_queues > 1) 661 - return false; 662 - blk_mq_trigger_softirq(rq); 684 + blk_mq_complete_send_ipi(rq); 685 + return true; 663 686 } 664 687 665 - return true; 688 + if (rq->q->nr_hw_queues == 1) { 689 + blk_mq_raise_softirq(rq); 690 + return true; 691 + } 692 + return false; 666 693 } 667 694 EXPORT_SYMBOL_GPL(blk_mq_complete_request_remote); 668 695 ··· 3930 3957 int i; 3931 3958 3932 3959 for_each_possible_cpu(i) 3933 - INIT_LIST_HEAD(&per_cpu(blk_cpu_done, i)); 3960 + init_llist_head(&per_cpu(blk_cpu_done, i)); 3934 3961 open_softirq(BLOCK_SOFTIRQ, blk_done_softirq); 3935 3962 3936 3963 cpuhp_setup_state_nocalls(CPUHP_BLOCK_SOFTIRQ_DEAD,
+1 -1
include/linux/blkdev.h
··· 153 153 */ 154 154 union { 155 155 struct hlist_node hash; /* merge hash */ 156 - struct list_head ipi_list; 156 + struct llist_node ipi_list; 157 157 }; 158 158 159 159 /*