softirq.c 8.22 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8
/*
 *	linux/kernel/softirq.c
 *
 *	Copyright (C) 1992 Linus Torvalds
 *
 * Rewritten. Old one was good in 2.2, but in 2.3 it was immoral. --ANK (990903)
 */

9
#include <linux/module.h>
Linus Torvalds's avatar
Linus Torvalds committed
10 11
#include <linux/kernel_stat.h>
#include <linux/interrupt.h>
12 13
#include <linux/init.h>
#include <linux/mm.h>
14
#include <linux/notifier.h>
15
#include <linux/percpu.h>
16
#include <linux/cpu.h>
Andrew Morton's avatar
Andrew Morton committed
17
#include <linux/kthread.h>
Linus Torvalds's avatar
Linus Torvalds committed
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36

/*
   - No shared variables, all the data are CPU local.
   - If a softirq needs serialization, let it serialize itself
     by its own spinlocks.
   - Even if softirq is serialized, only local cpu is marked for
     execution. Hence, we get something sort of weak cpu binding.
     Though it is still not clear, will it result in better locality
     or will not.

   Examples:
   - NET RX softirq. It is multithreaded and does not require
     any global serialization.
   - NET TX softirq. It kicks software netdevice queues, hence
     it is logically serialized per device, but this serialization
     is invisible to common code.
   - Tasklets: serialized wrt itself.
 */

Andrew Morton's avatar
Andrew Morton committed
37
#ifndef __ARCH_IRQ_STAT
38
irq_cpustat_t irq_stat[NR_CPUS] ____cacheline_aligned;
Andrew Morton's avatar
Andrew Morton committed
39 40
EXPORT_SYMBOL(irq_stat);
#endif
Linus Torvalds's avatar
Linus Torvalds committed
41

Linus Torvalds's avatar
Linus Torvalds committed
42
static struct softirq_action softirq_vec[32] __cacheline_aligned_in_smp;
Linus Torvalds's avatar
Linus Torvalds committed
43

44 45
static DEFINE_PER_CPU(struct task_struct *, ksoftirqd);

Linus Torvalds's avatar
Linus Torvalds committed
46 47 48 49 50 51
/*
 * we cannot loop indefinitely here to avoid userspace starvation,
 * but we also don't want to introduce a worst case 1/HZ latency
 * to the pending events, so lets the scheduler to balance
 * the softirq load for us.
 */
52
static inline void wakeup_softirqd(void)
Linus Torvalds's avatar
Linus Torvalds committed
53
{
54 55
	/* Interrupts are disabled: no need to stop preemption */
	struct task_struct *tsk = __get_cpu_var(ksoftirqd);
Linus Torvalds's avatar
Linus Torvalds committed
56 57 58 59 60

	if (tsk && tsk->state != TASK_RUNNING)
		wake_up_process(tsk);
}

61 62 63 64 65 66 67 68 69 70 71
/*
 * We restart softirq processing MAX_SOFTIRQ_RESTART times,
 * and we fall back to softirqd after that.
 *
 * This number has been established via experimentation.
 * The two things to balance is latency against fairness -
 * we want to handle softirqs as soon as possible, but they
 * should not be able to lock up the box.
 */
#define MAX_SOFTIRQ_RESTART 10

72
asmlinkage void do_softirq(void)
Linus Torvalds's avatar
Linus Torvalds committed
73
{
74
	int max_restart = MAX_SOFTIRQ_RESTART;
Linus Torvalds's avatar
Linus Torvalds committed
75
	__u32 pending;
Robert Love's avatar
Robert Love committed
76
	unsigned long flags;
Linus Torvalds's avatar
Linus Torvalds committed
77 78

	if (in_interrupt())
Linus Torvalds's avatar
Linus Torvalds committed
79
		return;
Linus Torvalds's avatar
Linus Torvalds committed
80

Linus Torvalds's avatar
Linus Torvalds committed
81
	local_irq_save(flags);
82

83
	pending = local_softirq_pending();
Linus Torvalds's avatar
Linus Torvalds committed
84

Linus Torvalds's avatar
Linus Torvalds committed
85
	if (pending) {
Linus Torvalds's avatar
Linus Torvalds committed
86 87
		struct softirq_action *h;

Linus Torvalds's avatar
Linus Torvalds committed
88
		local_bh_disable();
Linus Torvalds's avatar
Linus Torvalds committed
89
restart:
Linus Torvalds's avatar
Linus Torvalds committed
90
		/* Reset the pending bitmask before enabling irqs */
91
		local_softirq_pending() = 0;
Linus Torvalds's avatar
Linus Torvalds committed
92 93 94 95 96 97

		local_irq_enable();

		h = softirq_vec;

		do {
Linus Torvalds's avatar
Linus Torvalds committed
98
			if (pending & 1)
Linus Torvalds's avatar
Linus Torvalds committed
99 100
				h->action(h);
			h++;
Linus Torvalds's avatar
Linus Torvalds committed
101 102
			pending >>= 1;
		} while (pending);
Linus Torvalds's avatar
Linus Torvalds committed
103 104 105

		local_irq_disable();

106
		pending = local_softirq_pending();
107
		if (pending && --max_restart)
Linus Torvalds's avatar
Linus Torvalds committed
108
			goto restart;
Linus Torvalds's avatar
Linus Torvalds committed
109
		if (pending)
110
			wakeup_softirqd();
111
		__local_bh_enable();
Linus Torvalds's avatar
Linus Torvalds committed
112 113
	}

Linus Torvalds's avatar
Linus Torvalds committed
114 115 116
	local_irq_restore(flags);
}

117 118
EXPORT_SYMBOL(do_softirq);

119 120 121
void local_bh_enable(void)
{
	__local_bh_enable();
122
	WARN_ON(irqs_disabled());
123
	if (unlikely(!in_interrupt() &&
124
		     local_softirq_pending()))
125
		invoke_softirq();
126 127 128 129
	preempt_check_resched();
}
EXPORT_SYMBOL(local_bh_enable);

Linus Torvalds's avatar
Linus Torvalds committed
130
/*
131
 * This function must run with irqs disabled!
Linus Torvalds's avatar
Linus Torvalds committed
132
 */
133
inline fastcall void raise_softirq_irqoff(unsigned int nr)
Linus Torvalds's avatar
Linus Torvalds committed
134
{
135
	__raise_softirq_irqoff(nr);
Linus Torvalds's avatar
Linus Torvalds committed
136 137

	/*
138 139
	 * If we're in an interrupt or softirq, we're done
	 * (this also catches softirq-disabled code). We will
Linus Torvalds's avatar
Linus Torvalds committed
140
	 * actually run the softirq once we return from
141
	 * the irq or softirq.
Linus Torvalds's avatar
Linus Torvalds committed
142 143 144 145
	 *
	 * Otherwise we wake up ksoftirqd to make sure we
	 * schedule the softirq soon.
	 */
146
	if (!in_interrupt())
147
		wakeup_softirqd();
Linus Torvalds's avatar
Linus Torvalds committed
148 149
}

150 151
EXPORT_SYMBOL(raise_softirq_irqoff);

152
void fastcall raise_softirq(unsigned int nr)
Linus Torvalds's avatar
Linus Torvalds committed
153
{
Robert Love's avatar
Robert Love committed
154
	unsigned long flags;
Linus Torvalds's avatar
Linus Torvalds committed
155 156

	local_irq_save(flags);
157
	raise_softirq_irqoff(nr);
Linus Torvalds's avatar
Linus Torvalds committed
158
	local_irq_restore(flags);
Linus Torvalds's avatar
Linus Torvalds committed
159
}
Linus Torvalds's avatar
Linus Torvalds committed
160

161 162
EXPORT_SYMBOL(raise_softirq);

Linus Torvalds's avatar
Linus Torvalds committed
163 164 165 166 167 168
void open_softirq(int nr, void (*action)(struct softirq_action*), void *data)
{
	softirq_vec[nr].data = data;
	softirq_vec[nr].action = action;
}

169
EXPORT_SYMBOL(open_softirq);
Linus Torvalds's avatar
Linus Torvalds committed
170 171

/* Tasklets */
Rusty Russell's avatar
Rusty Russell committed
172 173 174 175
struct tasklet_head
{
	struct tasklet_struct *list;
};
Linus Torvalds's avatar
Linus Torvalds committed
176

Rusty Russell's avatar
Rusty Russell committed
177 178
/* Some compilers disobey section attribute on statics when not
   initialized -- RR */
179 180
static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec) = { NULL };
static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec) = { NULL };
Linus Torvalds's avatar
Linus Torvalds committed
181

182
void fastcall __tasklet_schedule(struct tasklet_struct *t)
Linus Torvalds's avatar
Linus Torvalds committed
183 184 185 186
{
	unsigned long flags;

	local_irq_save(flags);
Rusty Russell's avatar
Rusty Russell committed
187 188
	t->next = __get_cpu_var(tasklet_vec).list;
	__get_cpu_var(tasklet_vec).list = t;
189
	raise_softirq_irqoff(TASKLET_SOFTIRQ);
Linus Torvalds's avatar
Linus Torvalds committed
190 191 192
	local_irq_restore(flags);
}

193 194
EXPORT_SYMBOL(__tasklet_schedule);

195
void fastcall __tasklet_hi_schedule(struct tasklet_struct *t)
Linus Torvalds's avatar
Linus Torvalds committed
196 197 198 199
{
	unsigned long flags;

	local_irq_save(flags);
Rusty Russell's avatar
Rusty Russell committed
200 201
	t->next = __get_cpu_var(tasklet_hi_vec).list;
	__get_cpu_var(tasklet_hi_vec).list = t;
202
	raise_softirq_irqoff(HI_SOFTIRQ);
Linus Torvalds's avatar
Linus Torvalds committed
203 204 205
	local_irq_restore(flags);
}

206 207
EXPORT_SYMBOL(__tasklet_hi_schedule);

Linus Torvalds's avatar
Linus Torvalds committed
208 209 210 211 212
static void tasklet_action(struct softirq_action *a)
{
	struct tasklet_struct *list;

	local_irq_disable();
Rusty Russell's avatar
Rusty Russell committed
213 214
	list = __get_cpu_var(tasklet_vec).list;
	__get_cpu_var(tasklet_vec).list = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
215
	local_irq_enable();
Linus Torvalds's avatar
Linus Torvalds committed
216

Linus Torvalds's avatar
Linus Torvalds committed
217
	while (list) {
Linus Torvalds's avatar
Linus Torvalds committed
218 219 220 221
		struct tasklet_struct *t = list;

		list = list->next;

Linus Torvalds's avatar
Linus Torvalds committed
222 223 224 225 226 227 228 229
		if (tasklet_trylock(t)) {
			if (!atomic_read(&t->count)) {
				if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
					BUG();
				t->func(t->data);
				tasklet_unlock(t);
				continue;
			}
Linus Torvalds's avatar
Linus Torvalds committed
230
			tasklet_unlock(t);
Linus Torvalds's avatar
Linus Torvalds committed
231
		}
Linus Torvalds's avatar
Linus Torvalds committed
232 233

		local_irq_disable();
Rusty Russell's avatar
Rusty Russell committed
234 235
		t->next = __get_cpu_var(tasklet_vec).list;
		__get_cpu_var(tasklet_vec).list = t;
236
		__raise_softirq_irqoff(TASKLET_SOFTIRQ);
Linus Torvalds's avatar
Linus Torvalds committed
237
		local_irq_enable();
Linus Torvalds's avatar
Linus Torvalds committed
238 239 240 241 242 243 244 245
	}
}

static void tasklet_hi_action(struct softirq_action *a)
{
	struct tasklet_struct *list;

	local_irq_disable();
Rusty Russell's avatar
Rusty Russell committed
246 247
	list = __get_cpu_var(tasklet_hi_vec).list;
	__get_cpu_var(tasklet_hi_vec).list = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
248
	local_irq_enable();
Linus Torvalds's avatar
Linus Torvalds committed
249

Linus Torvalds's avatar
Linus Torvalds committed
250
	while (list) {
Linus Torvalds's avatar
Linus Torvalds committed
251 252 253 254
		struct tasklet_struct *t = list;

		list = list->next;

Linus Torvalds's avatar
Linus Torvalds committed
255 256 257 258 259 260 261 262
		if (tasklet_trylock(t)) {
			if (!atomic_read(&t->count)) {
				if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
					BUG();
				t->func(t->data);
				tasklet_unlock(t);
				continue;
			}
Linus Torvalds's avatar
Linus Torvalds committed
263
			tasklet_unlock(t);
Linus Torvalds's avatar
Linus Torvalds committed
264
		}
Linus Torvalds's avatar
Linus Torvalds committed
265 266

		local_irq_disable();
Rusty Russell's avatar
Rusty Russell committed
267 268
		t->next = __get_cpu_var(tasklet_hi_vec).list;
		__get_cpu_var(tasklet_hi_vec).list = t;
269
		__raise_softirq_irqoff(HI_SOFTIRQ);
Linus Torvalds's avatar
Linus Torvalds committed
270
		local_irq_enable();
Linus Torvalds's avatar
Linus Torvalds committed
271 272 273 274 275 276 277
	}
}


void tasklet_init(struct tasklet_struct *t,
		  void (*func)(unsigned long), unsigned long data)
{
Linus Torvalds's avatar
Linus Torvalds committed
278
	t->next = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
279 280
	t->state = 0;
	atomic_set(&t->count, 0);
Linus Torvalds's avatar
Linus Torvalds committed
281 282
	t->func = func;
	t->data = data;
Linus Torvalds's avatar
Linus Torvalds committed
283 284
}

285 286
EXPORT_SYMBOL(tasklet_init);

Linus Torvalds's avatar
Linus Torvalds committed
287 288 289 290 291 292
void tasklet_kill(struct tasklet_struct *t)
{
	if (in_interrupt())
		printk("Attempt to kill tasklet from interrupt\n");

	while (test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {
Linus Torvalds's avatar
Linus Torvalds committed
293 294 295
		do
			yield();
		while (test_bit(TASKLET_STATE_SCHED, &t->state));
Linus Torvalds's avatar
Linus Torvalds committed
296 297 298 299 300
	}
	tasklet_unlock_wait(t);
	clear_bit(TASKLET_STATE_SCHED, &t->state);
}

301
EXPORT_SYMBOL(tasklet_kill);
302

303
void __init softirq_init(void)
Linus Torvalds's avatar
Linus Torvalds committed
304 305 306 307 308
{
	open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);
	open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);
}

Linus Torvalds's avatar
Linus Torvalds committed
309 310
static int ksoftirqd(void * __bind_cpu)
{
311
	int cpu = (int) (long) __bind_cpu;
Linus Torvalds's avatar
Linus Torvalds committed
312

Linus Torvalds's avatar
Linus Torvalds committed
313
	set_user_nice(current, 19);
Pavel Machek's avatar
Pavel Machek committed
314
	current->flags |= PF_IOTHREAD;
Linus Torvalds's avatar
Linus Torvalds committed
315

316
	BUG_ON(smp_processor_id() != cpu);
Linus Torvalds's avatar
Linus Torvalds committed
317

Andrew Morton's avatar
Andrew Morton committed
318
	set_current_state(TASK_INTERRUPTIBLE);
Linus Torvalds's avatar
Linus Torvalds committed
319

Andrew Morton's avatar
Andrew Morton committed
320
	while (!kthread_should_stop()) {
321
		if (!local_softirq_pending())
Linus Torvalds's avatar
Linus Torvalds committed
322 323 324 325
			schedule();

		__set_current_state(TASK_RUNNING);

326
		while (local_softirq_pending()) {
Linus Torvalds's avatar
Linus Torvalds committed
327
			do_softirq();
Linus Torvalds's avatar
Linus Torvalds committed
328
			cond_resched();
Linus Torvalds's avatar
Linus Torvalds committed
329 330 331 332
		}

		__set_current_state(TASK_INTERRUPTIBLE);
	}
Andrew Morton's avatar
Andrew Morton committed
333
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
334 335
}

336 337 338
static int __devinit cpu_callback(struct notifier_block *nfb,
				  unsigned long action,
				  void *hcpu)
Linus Torvalds's avatar
Linus Torvalds committed
339
{
340
	int hotcpu = (unsigned long)hcpu;
Andrew Morton's avatar
Andrew Morton committed
341
	struct task_struct *p;
Linus Torvalds's avatar
Linus Torvalds committed
342

343 344 345 346
	switch (action) {
	case CPU_UP_PREPARE:
		BUG_ON(per_cpu(tasklet_vec, hotcpu).list);
		BUG_ON(per_cpu(tasklet_hi_vec, hotcpu).list);
Andrew Morton's avatar
Andrew Morton committed
347 348
		p = kthread_create(ksoftirqd, hcpu, "ksoftirqd/%d", hotcpu);
		if (IS_ERR(p)) {
349 350 351
			printk("ksoftirqd for %i failed\n", hotcpu);
			return NOTIFY_BAD;
		}
Andrew Morton's avatar
Andrew Morton committed
352 353
		per_cpu(ksoftirqd, hotcpu) = p;
		kthread_bind(p, hotcpu);
354 355 356 357 358
  		per_cpu(ksoftirqd, hotcpu) = p;
 		break;
	case CPU_ONLINE:
		wake_up_process(per_cpu(ksoftirqd, hotcpu));
		break;
359
 	}
360
	return NOTIFY_OK;
361 362
}

363 364 365
static struct notifier_block __devinitdata cpu_nfb = {
	.notifier_call = cpu_callback
};
366

367
__init int spawn_ksoftirqd(void)
368
{
369 370 371
	void *cpu = (void *)(long)smp_processor_id();
	cpu_callback(&cpu_nfb, CPU_UP_PREPARE, cpu);
	cpu_callback(&cpu_nfb, CPU_ONLINE, cpu);
372
	register_cpu_notifier(&cpu_nfb);
Linus Torvalds's avatar
Linus Torvalds committed
373 374
	return 0;
}