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