18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Functions related to interrupt-poll handling in the block layer. This 48c2ecf20Sopenharmony_ci * is similar to NAPI for network devices. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci#include <linux/kernel.h> 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/bio.h> 108c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 118c2ecf20Sopenharmony_ci#include <linux/cpu.h> 128c2ecf20Sopenharmony_ci#include <linux/irq_poll.h> 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_cistatic unsigned int irq_poll_budget __read_mostly = 256; 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(struct list_head, blk_cpu_iopoll); 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/** 208c2ecf20Sopenharmony_ci * irq_poll_sched - Schedule a run of the iopoll handler 218c2ecf20Sopenharmony_ci * @iop: The parent iopoll structure 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * Description: 248c2ecf20Sopenharmony_ci * Add this irq_poll structure to the pending poll list and trigger the 258c2ecf20Sopenharmony_ci * raise of the blk iopoll softirq. 268c2ecf20Sopenharmony_ci **/ 278c2ecf20Sopenharmony_civoid irq_poll_sched(struct irq_poll *iop) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci unsigned long flags; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci if (test_bit(IRQ_POLL_F_DISABLE, &iop->state)) 328c2ecf20Sopenharmony_ci return; 338c2ecf20Sopenharmony_ci if (test_and_set_bit(IRQ_POLL_F_SCHED, &iop->state)) 348c2ecf20Sopenharmony_ci return; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci local_irq_save(flags); 378c2ecf20Sopenharmony_ci list_add_tail(&iop->list, this_cpu_ptr(&blk_cpu_iopoll)); 388c2ecf20Sopenharmony_ci raise_softirq_irqoff(IRQ_POLL_SOFTIRQ); 398c2ecf20Sopenharmony_ci local_irq_restore(flags); 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ciEXPORT_SYMBOL(irq_poll_sched); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/** 448c2ecf20Sopenharmony_ci * __irq_poll_complete - Mark this @iop as un-polled again 458c2ecf20Sopenharmony_ci * @iop: The parent iopoll structure 468c2ecf20Sopenharmony_ci * 478c2ecf20Sopenharmony_ci * Description: 488c2ecf20Sopenharmony_ci * See irq_poll_complete(). This function must be called with interrupts 498c2ecf20Sopenharmony_ci * disabled. 508c2ecf20Sopenharmony_ci **/ 518c2ecf20Sopenharmony_cistatic void __irq_poll_complete(struct irq_poll *iop) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci list_del(&iop->list); 548c2ecf20Sopenharmony_ci smp_mb__before_atomic(); 558c2ecf20Sopenharmony_ci clear_bit_unlock(IRQ_POLL_F_SCHED, &iop->state); 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/** 598c2ecf20Sopenharmony_ci * irq_poll_complete - Mark this @iop as un-polled again 608c2ecf20Sopenharmony_ci * @iop: The parent iopoll structure 618c2ecf20Sopenharmony_ci * 628c2ecf20Sopenharmony_ci * Description: 638c2ecf20Sopenharmony_ci * If a driver consumes less than the assigned budget in its run of the 648c2ecf20Sopenharmony_ci * iopoll handler, it'll end the polled mode by calling this function. The 658c2ecf20Sopenharmony_ci * iopoll handler will not be invoked again before irq_poll_sched() 668c2ecf20Sopenharmony_ci * is called. 678c2ecf20Sopenharmony_ci **/ 688c2ecf20Sopenharmony_civoid irq_poll_complete(struct irq_poll *iop) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci unsigned long flags; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci local_irq_save(flags); 738c2ecf20Sopenharmony_ci __irq_poll_complete(iop); 748c2ecf20Sopenharmony_ci local_irq_restore(flags); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ciEXPORT_SYMBOL(irq_poll_complete); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic void __latent_entropy irq_poll_softirq(struct softirq_action *h) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci struct list_head *list = this_cpu_ptr(&blk_cpu_iopoll); 818c2ecf20Sopenharmony_ci int rearm = 0, budget = irq_poll_budget; 828c2ecf20Sopenharmony_ci unsigned long start_time = jiffies; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci local_irq_disable(); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci while (!list_empty(list)) { 878c2ecf20Sopenharmony_ci struct irq_poll *iop; 888c2ecf20Sopenharmony_ci int work, weight; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* 918c2ecf20Sopenharmony_ci * If softirq window is exhausted then punt. 928c2ecf20Sopenharmony_ci */ 938c2ecf20Sopenharmony_ci if (budget <= 0 || time_after(jiffies, start_time)) { 948c2ecf20Sopenharmony_ci rearm = 1; 958c2ecf20Sopenharmony_ci break; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci local_irq_enable(); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* Even though interrupts have been re-enabled, this 1018c2ecf20Sopenharmony_ci * access is safe because interrupts can only add new 1028c2ecf20Sopenharmony_ci * entries to the tail of this list, and only ->poll() 1038c2ecf20Sopenharmony_ci * calls can remove this head entry from the list. 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_ci iop = list_entry(list->next, struct irq_poll, list); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci weight = iop->weight; 1088c2ecf20Sopenharmony_ci work = 0; 1098c2ecf20Sopenharmony_ci if (test_bit(IRQ_POLL_F_SCHED, &iop->state)) 1108c2ecf20Sopenharmony_ci work = iop->poll(iop, weight); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci budget -= work; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci local_irq_disable(); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* 1178c2ecf20Sopenharmony_ci * Drivers must not modify the iopoll state, if they 1188c2ecf20Sopenharmony_ci * consume their assigned weight (or more, some drivers can't 1198c2ecf20Sopenharmony_ci * easily just stop processing, they have to complete an 1208c2ecf20Sopenharmony_ci * entire mask of commands).In such cases this code 1218c2ecf20Sopenharmony_ci * still "owns" the iopoll instance and therefore can 1228c2ecf20Sopenharmony_ci * move the instance around on the list at-will. 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_ci if (work >= weight) { 1258c2ecf20Sopenharmony_ci if (test_bit(IRQ_POLL_F_DISABLE, &iop->state)) 1268c2ecf20Sopenharmony_ci __irq_poll_complete(iop); 1278c2ecf20Sopenharmony_ci else 1288c2ecf20Sopenharmony_ci list_move_tail(&iop->list, list); 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (rearm) 1338c2ecf20Sopenharmony_ci __raise_softirq_irqoff(IRQ_POLL_SOFTIRQ); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci local_irq_enable(); 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci/** 1398c2ecf20Sopenharmony_ci * irq_poll_disable - Disable iopoll on this @iop 1408c2ecf20Sopenharmony_ci * @iop: The parent iopoll structure 1418c2ecf20Sopenharmony_ci * 1428c2ecf20Sopenharmony_ci * Description: 1438c2ecf20Sopenharmony_ci * Disable io polling and wait for any pending callbacks to have completed. 1448c2ecf20Sopenharmony_ci **/ 1458c2ecf20Sopenharmony_civoid irq_poll_disable(struct irq_poll *iop) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci set_bit(IRQ_POLL_F_DISABLE, &iop->state); 1488c2ecf20Sopenharmony_ci while (test_and_set_bit(IRQ_POLL_F_SCHED, &iop->state)) 1498c2ecf20Sopenharmony_ci msleep(1); 1508c2ecf20Sopenharmony_ci clear_bit(IRQ_POLL_F_DISABLE, &iop->state); 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ciEXPORT_SYMBOL(irq_poll_disable); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/** 1558c2ecf20Sopenharmony_ci * irq_poll_enable - Enable iopoll on this @iop 1568c2ecf20Sopenharmony_ci * @iop: The parent iopoll structure 1578c2ecf20Sopenharmony_ci * 1588c2ecf20Sopenharmony_ci * Description: 1598c2ecf20Sopenharmony_ci * Enable iopoll on this @iop. Note that the handler run will not be 1608c2ecf20Sopenharmony_ci * scheduled, it will only mark it as active. 1618c2ecf20Sopenharmony_ci **/ 1628c2ecf20Sopenharmony_civoid irq_poll_enable(struct irq_poll *iop) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci BUG_ON(!test_bit(IRQ_POLL_F_SCHED, &iop->state)); 1658c2ecf20Sopenharmony_ci smp_mb__before_atomic(); 1668c2ecf20Sopenharmony_ci clear_bit_unlock(IRQ_POLL_F_SCHED, &iop->state); 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(irq_poll_enable); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci/** 1718c2ecf20Sopenharmony_ci * irq_poll_init - Initialize this @iop 1728c2ecf20Sopenharmony_ci * @iop: The parent iopoll structure 1738c2ecf20Sopenharmony_ci * @weight: The default weight (or command completion budget) 1748c2ecf20Sopenharmony_ci * @poll_fn: The handler to invoke 1758c2ecf20Sopenharmony_ci * 1768c2ecf20Sopenharmony_ci * Description: 1778c2ecf20Sopenharmony_ci * Initialize and enable this irq_poll structure. 1788c2ecf20Sopenharmony_ci **/ 1798c2ecf20Sopenharmony_civoid irq_poll_init(struct irq_poll *iop, int weight, irq_poll_fn *poll_fn) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci memset(iop, 0, sizeof(*iop)); 1828c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&iop->list); 1838c2ecf20Sopenharmony_ci iop->weight = weight; 1848c2ecf20Sopenharmony_ci iop->poll = poll_fn; 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ciEXPORT_SYMBOL(irq_poll_init); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic int irq_poll_cpu_dead(unsigned int cpu) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci /* 1918c2ecf20Sopenharmony_ci * If a CPU goes away, splice its entries to the current CPU 1928c2ecf20Sopenharmony_ci * and trigger a run of the softirq 1938c2ecf20Sopenharmony_ci */ 1948c2ecf20Sopenharmony_ci local_irq_disable(); 1958c2ecf20Sopenharmony_ci list_splice_init(&per_cpu(blk_cpu_iopoll, cpu), 1968c2ecf20Sopenharmony_ci this_cpu_ptr(&blk_cpu_iopoll)); 1978c2ecf20Sopenharmony_ci __raise_softirq_irqoff(IRQ_POLL_SOFTIRQ); 1988c2ecf20Sopenharmony_ci local_irq_enable(); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci return 0; 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic __init int irq_poll_setup(void) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci int i; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci for_each_possible_cpu(i) 2088c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&per_cpu(blk_cpu_iopoll, i)); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci open_softirq(IRQ_POLL_SOFTIRQ, irq_poll_softirq); 2118c2ecf20Sopenharmony_ci cpuhp_setup_state_nocalls(CPUHP_IRQ_POLL_DEAD, "irq_poll:dead", NULL, 2128c2ecf20Sopenharmony_ci irq_poll_cpu_dead); 2138c2ecf20Sopenharmony_ci return 0; 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_cisubsys_initcall(irq_poll_setup); 216