Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0 2 : : /* 3 : : * Functions related to softirq rq completions 4 : : */ 5 : : #include <linux/kernel.h> 6 : : #include <linux/module.h> 7 : : #include <linux/init.h> 8 : : #include <linux/bio.h> 9 : : #include <linux/blkdev.h> 10 : : #include <linux/interrupt.h> 11 : : #include <linux/cpu.h> 12 : : #include <linux/sched.h> 13 : : #include <linux/sched/topology.h> 14 : : 15 : : #include "blk.h" 16 : : 17 : : static DEFINE_PER_CPU(struct list_head, blk_cpu_done); 18 : : 19 : : /* 20 : : * Softirq action handler - move entries to local list and loop over them 21 : : * while passing them to the queue registered handler. 22 : : */ 23 : 86821 : static __latent_entropy void blk_done_softirq(struct softirq_action *h) 24 : : { 25 : 86821 : struct list_head *cpu_list, local_list; 26 : : 27 : 86821 : local_irq_disable(); 28 : 86821 : cpu_list = this_cpu_ptr(&blk_cpu_done); 29 : 86821 : list_replace_init(cpu_list, &local_list); 30 : 86821 : local_irq_enable(); 31 : : 32 [ + + ]: 173648 : while (!list_empty(&local_list)) { 33 : 86827 : struct request *rq; 34 : : 35 : 86827 : rq = list_entry(local_list.next, struct request, ipi_list); 36 : 86827 : list_del_init(&rq->ipi_list); 37 : 86827 : rq->q->mq_ops->complete(rq); 38 : : } 39 : 86821 : } 40 : : 41 : : #ifdef CONFIG_SMP 42 : 0 : static void trigger_softirq(void *data) 43 : : { 44 : 0 : struct request *rq = data; 45 : 0 : struct list_head *list; 46 : : 47 : 0 : list = this_cpu_ptr(&blk_cpu_done); 48 [ # # ]: 0 : list_add_tail(&rq->ipi_list, list); 49 : : 50 [ # # ]: 0 : if (list->next == &rq->ipi_list) 51 : 0 : raise_softirq_irqoff(BLOCK_SOFTIRQ); 52 : 0 : } 53 : : 54 : : /* 55 : : * Setup and invoke a run of 'trigger_softirq' on the given cpu. 56 : : */ 57 : 0 : static int raise_blk_irq(int cpu, struct request *rq) 58 : : { 59 [ # # ]: 0 : if (cpu_online(cpu)) { 60 : 0 : call_single_data_t *data = &rq->csd; 61 : : 62 : 0 : data->func = trigger_softirq; 63 : 0 : data->info = rq; 64 : 0 : data->flags = 0; 65 : : 66 : 0 : smp_call_function_single_async(cpu, data); 67 : 0 : return 0; 68 : : } 69 : : 70 : : return 1; 71 : : } 72 : : #else /* CONFIG_SMP */ 73 : : static int raise_blk_irq(int cpu, struct request *rq) 74 : : { 75 : : return 1; 76 : : } 77 : : #endif 78 : : 79 : 0 : static int blk_softirq_cpu_dead(unsigned int cpu) 80 : : { 81 : : /* 82 : : * If a CPU goes away, splice its entries to the current CPU 83 : : * and trigger a run of the softirq 84 : : */ 85 : 0 : local_irq_disable(); 86 : 0 : list_splice_init(&per_cpu(blk_cpu_done, cpu), 87 [ # # ]: 0 : this_cpu_ptr(&blk_cpu_done)); 88 : 0 : raise_softirq_irqoff(BLOCK_SOFTIRQ); 89 : 0 : local_irq_enable(); 90 : : 91 : 0 : return 0; 92 : : } 93 : : 94 : 86827 : void __blk_complete_request(struct request *req) 95 : : { 96 : 86827 : struct request_queue *q = req->q; 97 : 86827 : int cpu, ccpu = req->mq_ctx->cpu; 98 : 86827 : unsigned long flags; 99 : 86827 : bool shared = false; 100 : : 101 [ - + ]: 86827 : BUG_ON(!q->mq_ops->complete); 102 : : 103 : 86827 : local_irq_save(flags); 104 : 86827 : cpu = smp_processor_id(); 105 : : 106 : : /* 107 : : * Select completion CPU 108 : : */ 109 [ + - + - ]: 86827 : if (test_bit(QUEUE_FLAG_SAME_COMP, &q->queue_flags) && ccpu != -1) { 110 [ + - ]: 86827 : if (!test_bit(QUEUE_FLAG_SAME_FORCE, &q->queue_flags)) 111 : 86827 : shared = cpus_share_cache(cpu, ccpu); 112 : : } else 113 : : ccpu = cpu; 114 : : 115 : : /* 116 : : * If current CPU and requested CPU share a cache, run the softirq on 117 : : * the current CPU. One might concern this is just like 118 : : * QUEUE_FLAG_SAME_FORCE, but actually not. blk_complete_request() is 119 : : * running in interrupt handler, and currently I/O controller doesn't 120 : : * support multiple interrupts, so current CPU is unique actually. This 121 : : * avoids IPI sending from current CPU to the first CPU of a group. 122 : : */ 123 [ + - ]: 86827 : if (ccpu == cpu || shared) { 124 : 86827 : struct list_head *list; 125 : 86827 : do_local: 126 : 86827 : list = this_cpu_ptr(&blk_cpu_done); 127 [ + + ]: 86827 : list_add_tail(&req->ipi_list, list); 128 : : 129 : : /* 130 : : * if the list only contains our just added request, 131 : : * signal a raise of the softirq. If there are already 132 : : * entries there, someone already raised the irq but it 133 : : * hasn't run yet. 134 : : */ 135 [ + + ]: 86827 : if (list->next == &req->ipi_list) 136 : 86821 : raise_softirq_irqoff(BLOCK_SOFTIRQ); 137 [ # # ]: 0 : } else if (raise_blk_irq(ccpu, req)) 138 : 0 : goto do_local; 139 : : 140 : 86827 : local_irq_restore(flags); 141 : 86827 : } 142 : : 143 : 78 : static __init int blk_softirq_init(void) 144 : : { 145 : 78 : int i; 146 : : 147 [ + + ]: 156 : for_each_possible_cpu(i) 148 : 78 : INIT_LIST_HEAD(&per_cpu(blk_cpu_done, i)); 149 : : 150 : 78 : open_softirq(BLOCK_SOFTIRQ, blk_done_softirq); 151 : 78 : cpuhp_setup_state_nocalls(CPUHP_BLOCK_SOFTIRQ_DEAD, 152 : : "block/softirq:dead", NULL, 153 : : blk_softirq_cpu_dead); 154 : 78 : return 0; 155 : : } 156 : : subsys_initcall(blk_softirq_init);