irq_work.c 4.4 KB
Newer Older
1 2 3 4 5 6 7
/*
 * Copyright (C) 2010 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
 *
 * Provides a framework for enqueueing and running callbacks from hardirq
 * context. The enqueueing is NMI-safe.
 */

8
#include <linux/bug.h>
9
#include <linux/kernel.h>
10
#include <linux/export.h>
11
#include <linux/irq_work.h>
12
#include <linux/percpu.h>
13
#include <linux/hardirq.h>
14
#include <linux/irqflags.h>
15 16
#include <linux/sched.h>
#include <linux/tick.h>
S
Steven Rostedt 已提交
17 18
#include <linux/cpu.h>
#include <linux/notifier.h>
19
#include <asm/processor.h>
20 21


22 23
static DEFINE_PER_CPU(struct llist_head, raised_list);
static DEFINE_PER_CPU(struct llist_head, lazy_list);
24 25 26 27

/*
 * Claim the entry so that no one else will poke at it.
 */
28
static bool irq_work_claim(struct irq_work *work)
29
{
30
	unsigned long flags, oflags, nflags;
31

32 33 34 35 36
	/*
	 * Start with our best wish as a premise but only trust any
	 * flag value after cmpxchg() result.
	 */
	flags = work->flags & ~IRQ_WORK_PENDING;
37 38
	for (;;) {
		nflags = flags | IRQ_WORK_FLAGS;
39 40
		oflags = cmpxchg(&work->flags, flags, nflags);
		if (oflags == flags)
41
			break;
42 43 44
		if (oflags & IRQ_WORK_PENDING)
			return false;
		flags = oflags;
45 46
		cpu_relax();
	}
47 48 49 50 51 52 53 54 55 56 57 58

	return true;
}

void __weak arch_irq_work_raise(void)
{
	/*
	 * Lame architectures will get the timer tick callback
	 */
}

/*
59 60 61 62
 * Enqueue the irq_work @entry unless it's already pending
 * somewhere.
 *
 * Can be re-enqueued while the callback is still in progress.
63
 */
64
bool irq_work_queue(struct irq_work *work)
65
{
66 67
	/* Only queue if not already pending */
	if (!irq_work_claim(work))
68
		return false;
69 70

	/* Queue the entry and raise the IPI if needed. */
71
	preempt_disable();
72

73 74 75 76 77 78 79
	/* If the work is "lazy", handle it from next tick if any */
	if (work->flags & IRQ_WORK_LAZY) {
		if (llist_add(&work->llnode, &__get_cpu_var(lazy_list)) &&
		    tick_nohz_tick_stopped())
			arch_irq_work_raise();
	} else {
		if (llist_add(&work->llnode, &__get_cpu_var(raised_list)))
80 81
			arch_irq_work_raise();
	}
82

83
	preempt_enable();
84 85

	return true;
86 87 88
}
EXPORT_SYMBOL_GPL(irq_work_queue);

89 90
bool irq_work_needs_cpu(void)
{
91
	struct llist_head *raised, *lazy;
92

93 94 95
	raised = &__get_cpu_var(raised_list);
	lazy = &__get_cpu_var(lazy_list);
	if (llist_empty(raised) && llist_empty(lazy))
96 97
		return false;

98 99 100
	/* All work should have been flushed before going offline */
	WARN_ON_ONCE(cpu_is_offline(smp_processor_id()));

101 102 103
	return true;
}

104
static void irq_work_run_list(struct llist_head *list)
105
{
106
	unsigned long flags;
107 108
	struct irq_work *work;
	struct llist_node *llnode;
109

110
	BUG_ON(!irqs_disabled());
111

112
	if (llist_empty(list))
113 114
		return;

115
	llnode = llist_del_all(list);
116 117
	while (llnode != NULL) {
		work = llist_entry(llnode, struct irq_work, llnode);
118

P
Peter Zijlstra 已提交
119
		llnode = llist_next(llnode);
120 121

		/*
122
		 * Clear the PENDING bit, after this point the @work
123
		 * can be re-used.
124 125 126
		 * Make it immediately visible so that other CPUs trying
		 * to claim that work don't rely on us to handle their data
		 * while we are in the middle of the func.
127
		 */
128 129 130
		flags = work->flags & ~IRQ_WORK_PENDING;
		xchg(&work->flags, flags);

131
		work->func(work);
132 133 134 135
		/*
		 * Clear the BUSY bit and return to the free state if
		 * no-one else claimed it meanwhile.
		 */
136
		(void)cmpxchg(&work->flags, flags, flags & ~IRQ_WORK_BUSY);
137 138
	}
}
S
Steven Rostedt 已提交
139

140 141 142 143 144 145
static void __irq_work_run(void)
{
	irq_work_run_list(&__get_cpu_var(raised_list));
	irq_work_run_list(&__get_cpu_var(lazy_list));
}

S
Steven Rostedt 已提交
146 147 148 149 150 151 152 153 154
/*
 * Run the irq_work entries on this cpu. Requires to be ran from hardirq
 * context with local IRQs disabled.
 */
void irq_work_run(void)
{
	BUG_ON(!in_irq());
	__irq_work_run();
}
155 156 157 158 159 160
EXPORT_SYMBOL_GPL(irq_work_run);

/*
 * Synchronize against the irq_work @entry, ensures the entry is not
 * currently in use.
 */
161
void irq_work_sync(struct irq_work *work)
162 163 164
{
	WARN_ON_ONCE(irqs_disabled());

165
	while (work->flags & IRQ_WORK_BUSY)
166 167 168
		cpu_relax();
}
EXPORT_SYMBOL_GPL(irq_work_sync);
S
Steven Rostedt 已提交
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200

#ifdef CONFIG_HOTPLUG_CPU
static int irq_work_cpu_notify(struct notifier_block *self,
			       unsigned long action, void *hcpu)
{
	long cpu = (long)hcpu;

	switch (action) {
	case CPU_DYING:
		/* Called from stop_machine */
		if (WARN_ON_ONCE(cpu != smp_processor_id()))
			break;
		__irq_work_run();
		break;
	default:
		break;
	}
	return NOTIFY_OK;
}

static struct notifier_block cpu_notify;

static __init int irq_work_init_cpu_notifier(void)
{
	cpu_notify.notifier_call = irq_work_cpu_notify;
	cpu_notify.priority = 0;
	register_cpu_notifier(&cpu_notify);
	return 0;
}
device_initcall(irq_work_init_cpu_notifier);

#endif /* CONFIG_HOTPLUG_CPU */