diff options
| author | Ingo Molnar <mingo@elte.hu> | 2002-09-28 21:00:25 -0700 |
|---|---|---|
| committer | Jeff Garzik <jgarzik@mandrakesoft.com> | 2002-09-28 21:00:25 -0700 |
| commit | dd140c87e84fe29e4edd40db91f6411b2c009cab (patch) | |
| tree | 0a485f6bcee3e199d4c2243b3e278ceaa0f45289 /include | |
| parent | 5a5ec729b503205d5770f4f9543ca768db7b6a5b (diff) | |
[PATCH] smptimers, old BH removal, tq-cleanup
This is the smptimers patch plus the removal of old BHs and a rewrite of
task-queue handling.
Basically with the removal of TIMER_BH i think the time is right to get
rid of old BHs forever, and to do a massive cleanup of all related
fields. The following five basic 'execution context' abstractions are
supported by the kernel:
- hardirq
- softirq
- tasklet
- keventd-driven task-queues
- process contexts
I've done the following cleanups/simplifications to task-queues:
- removed the ability to define your own task-queue, what can be done is
to schedule_task() a given task to keventd, and to flush all pending
tasks.
This is actually a quite easy transition, since 90% of all task-queue
users in the kernel used BH_IMMEDIATE - which is very similar in
functionality to keventd.
I believe task-queues should not be removed from the kernel altogether.
It's true that they were written as a candidate replacement for BHs
originally, but they do make sense in a different way: it's perhaps the
easiest interface to do deferred processing from IRQ context, in
performance-uncritical code areas. They are easier to use than
tasklets.
code that cares about performance should convert to tasklets - as the
timer code and the serial subsystem has done already. For extreme
performance softirqs should be used - the net subsystem does this.
and we can do this for 2.6 - there are only a couple of areas left after
fixing all the BH_IMMEDIATE places.
i have moved all the taskqueue handling code into kernel/context.c, and
only kept the basic 'queue a task' definitions in include/linux/tqueue.h.
I've converted three of the most commonly used BH_IMMEDIATE users:
tty_io.c, floppy.c and random.c. [random.c might need more thought
though.]
i've also cleaned up kernel/timer.c over that of the stock smptimers
patch: privatized the timer-vec definitions (nothing needs it,
init_timer() used it mistakenly) and cleaned up the code. Plus i've moved
some code around that does not belong into timer.c, and within timer.c
i've organized data and functions along functionality and further
separated the base timer code from the NTP bits.
net_bh_lock: i have removed it, since it would synchronize to nothing. The
old protocol handlers should still run on UP, and on SMP the kernel prints
a warning upon use. Alexey, is this approach fine with you?
scalable timers: i've further improved the patch ported to 2.5 by wli and
Dipankar. There is only one pending issue i can see, the question of
whether to migrate timers in mod_timer() or not. I'm quite convinced that
they should be migrated, but i might be wrong. It's a 10 lines change to
switch between migrating and non-migrating timers, we can do performance
tests later on. The current, more complex migration code is pretty fast
and has been stable under extremely high networking loads in the past 2
years, so we can immediately switch to the simpler variant if someone
proves it improves performance. (I'd say if non-migrating timers improve
Apache performance on one of the bigger NUMA boxes then the point is
proven, no further though will be needed.)
Diffstat (limited to 'include')
| -rw-r--r-- | include/linux/interrupt.h | 36 | ||||
| -rw-r--r-- | include/linux/sched.h | 1 | ||||
| -rw-r--r-- | include/linux/timer.h | 39 | ||||
| -rw-r--r-- | include/linux/tqueue.h | 89 | ||||
| -rw-r--r-- | include/linux/tty_flip.h | 2 |
5 files changed, 37 insertions, 130 deletions
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 26588de0b514..99ca09224919 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -22,25 +22,6 @@ struct irqaction { struct irqaction *next; }; - -/* Who gets which entry in bh_base. Things which will occur most often - should come first */ - -enum { - TIMER_BH = 0, - TQUEUE_BH = 1, - DIGI_BH = 2, - SERIAL_BH = 3, - RISCOM8_BH = 4, - SPECIALIX_BH = 5, - AURORA_BH = 6, - ESP_BH = 7, - IMMEDIATE_BH = 9, - CYCLADES_BH = 10, - MACSERIAL_BH = 13, - ISICOM_BH = 14 -}; - #include <asm/hardirq.h> #include <asm/softirq.h> @@ -218,23 +199,6 @@ static void name (unsigned long dummy) \ #endif /* CONFIG_SMP */ - -/* Old BH definitions */ - -extern struct tasklet_struct bh_task_vec[]; - -/* It is exported _ONLY_ for wait_on_irq(). */ -extern spinlock_t global_bh_lock; - -static inline void mark_bh(int nr) -{ - tasklet_hi_schedule(bh_task_vec+nr); -} - -extern void init_bh(int nr, void (*routine)(void)); -extern void remove_bh(int nr); - - /* * Autoprobing for irqs: * diff --git a/include/linux/sched.h b/include/linux/sched.h index 0c381f63afff..3e55dbedfba3 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -172,7 +172,6 @@ extern unsigned long cache_decay_ticks; extern signed long FASTCALL(schedule_timeout(signed long timeout)); asmlinkage void schedule(void); -extern void flush_scheduled_tasks(void); extern int start_context_thread(void); extern int current_is_keventd(void); diff --git a/include/linux/timer.h b/include/linux/timer.h index bb498b6ff871..f890f4f3d668 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h @@ -2,11 +2,15 @@ #define _LINUX_TIMER_H #include <linux/config.h> +#include <linux/smp.h> #include <linux/stddef.h> #include <linux/list.h> +#include <linux/spinlock.h> +#include <linux/cache.h> + +struct tvec_t_base_s; /* - * In Linux 2.4, static timers have been removed from the kernel. * Timers may be dynamically created and destroyed, and should be initialized * by a call to init_timer() upon creation. * @@ -14,22 +18,31 @@ * timeouts. You can use this field to distinguish between the different * invocations. */ -struct timer_list { +typedef struct timer_list { struct list_head list; unsigned long expires; unsigned long data; void (*function)(unsigned long); -}; - -extern void add_timer(struct timer_list * timer); -extern int del_timer(struct timer_list * timer); + struct tvec_t_base_s *base; +} timer_t; +extern void add_timer(timer_t * timer); +extern int del_timer(timer_t * timer); + #ifdef CONFIG_SMP -extern int del_timer_sync(struct timer_list * timer); +extern int del_timer_sync(timer_t * timer); +extern void sync_timers(void); +#define timer_enter(base, t) do { base->running_timer = t; mb(); } while (0) +#define timer_exit(base) do { base->running_timer = NULL; } while (0) +#define timer_is_running(base,t) (base->running_timer == t) +#define timer_synchronize(base,t) while (timer_is_running(base,t)) barrier() #else #define del_timer_sync(t) del_timer(t) +#define sync_timers() do { } while (0) +#define timer_enter(base,t) do { } while (0) +#define timer_exit(base) do { } while (0) #endif - + /* * mod_timer is a more efficient way to update the expire field of an * active timer (if the timer is inactive it will be activated) @@ -37,16 +50,20 @@ extern int del_timer_sync(struct timer_list * timer); * If the timer is known to be not pending (ie, in the handler), mod_timer * is less efficient than a->expires = b; add_timer(a). */ -int mod_timer(struct timer_list *timer, unsigned long expires); +int mod_timer(timer_t *timer, unsigned long expires); extern void it_real_fn(unsigned long); -static inline void init_timer(struct timer_list * timer) +extern void init_timers(void); +extern void run_local_timers(void); + +static inline void init_timer(timer_t * timer) { timer->list.next = timer->list.prev = NULL; + timer->base = NULL; } -static inline int timer_pending (const struct timer_list * timer) +static inline int timer_pending(const timer_t * timer) { return timer->list.next != NULL; } diff --git a/include/linux/tqueue.h b/include/linux/tqueue.h index d4729c518f22..cca0b193617b 100644 --- a/include/linux/tqueue.h +++ b/include/linux/tqueue.h @@ -1,13 +1,12 @@ /* * tqueue.h --- task queue handling for Linux. * - * Mostly based on a proposed bottom-half replacement code written by - * Kai Petzke, wpp@marie.physik.tu-berlin.de. + * Modified version of previous incarnations of task-queues, + * written by: * + * (C) 1994 Kai Petzke, wpp@marie.physik.tu-berlin.de * Modified for use in the Linux kernel by Theodore Ts'o, - * tytso@mit.edu. Any bugs are my fault, not Kai's. - * - * The original comment follows below. + * tytso@mit.edu. */ #ifndef _LINUX_TQUEUE_H @@ -18,25 +17,8 @@ #include <linux/bitops.h> #include <asm/system.h> -/* - * New proposed "bottom half" handlers: - * (C) 1994 Kai Petzke, wpp@marie.physik.tu-berlin.de - * - * Advantages: - * - Bottom halfs are implemented as a linked list. You can have as many - * of them, as you want. - * - No more scanning of a bit field is required upon call of a bottom half. - * - Support for chained bottom half lists. The run_task_queue() function can be - * used as a bottom half handler. This is for example useful for bottom - * halfs, which want to be delayed until the next clock tick. - * - * Notes: - * - Bottom halfs are called in the reverse order that they were linked into - * the list. - */ - struct tq_struct { - struct list_head list; /* linked list of active bh's */ + struct list_head list; /* linked list of active tq's */ unsigned long sync; /* must be initialized to zero */ void (*routine)(void *); /* function to call */ void *data; /* argument to function */ @@ -61,68 +43,13 @@ struct tq_struct { PREPARE_TQUEUE((_tq), (_routine), (_data)); \ } while (0) -typedef struct list_head task_queue; - #define DECLARE_TASK_QUEUE(q) LIST_HEAD(q) -#define TQ_ACTIVE(q) (!list_empty(&q)) - -extern task_queue tq_timer, tq_immediate; - -/* - * To implement your own list of active bottom halfs, use the following - * two definitions: - * - * DECLARE_TASK_QUEUE(my_tqueue); - * struct tq_struct my_task = { - * routine: (void (*)(void *)) my_routine, - * data: &my_data - * }; - * - * To activate a bottom half on a list, use: - * - * queue_task(&my_task, &my_tqueue); - * - * To later run the queued tasks use - * - * run_task_queue(&my_tqueue); - * - * This allows you to do deferred processing. For example, you could - * have a task queue called tq_timer, which is executed within the timer - * interrupt. - */ - -extern spinlock_t tqueue_lock; - -/* - * Queue a task on a tq. Return non-zero if it was successfully - * added. - */ -static inline int queue_task(struct tq_struct *bh_pointer, task_queue *bh_list) -{ - int ret = 0; - if (!test_and_set_bit(0,&bh_pointer->sync)) { - unsigned long flags; - spin_lock_irqsave(&tqueue_lock, flags); - list_add_tail(&bh_pointer->list, bh_list); - spin_unlock_irqrestore(&tqueue_lock, flags); - ret = 1; - } - return ret; -} /* Schedule a tq to run in process context */ extern int schedule_task(struct tq_struct *task); -/* - * Call all "bottom halfs" on a given list. - */ - -extern void __run_task_queue(task_queue *list); +/* finish all currently pending tasks - do not call from irq context */ +extern void flush_scheduled_tasks(void); -static inline void run_task_queue(task_queue *list) -{ - if (TQ_ACTIVE(*list)) - __run_task_queue(list); -} +#endif -#endif /* _LINUX_TQUEUE_H */ diff --git a/include/linux/tty_flip.h b/include/linux/tty_flip.h index 51a74dff1541..738ffcd53264 100644 --- a/include/linux/tty_flip.h +++ b/include/linux/tty_flip.h @@ -19,7 +19,7 @@ _INLINE_ void tty_insert_flip_char(struct tty_struct *tty, _INLINE_ void tty_schedule_flip(struct tty_struct *tty) { - queue_task(&tty->flip.tqueue, &tq_timer); + schedule_task(&tty->flip.tqueue); } #undef _INLINE_ |
