Main Page | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

tqueue.h

Go to the documentation of this file.
00001 /*
00002  * tqueue.h --- task queue handling for Linux.
00003  *
00004  * Mostly based on a proposed bottom-half replacement code written by
00005  * Kai Petzke, wpp@marie.physik.tu-berlin.de.
00006  *
00007  * Modified for use in the Linux kernel by Theodore Ts'o,
00008  * tytso@mit.edu.  Any bugs are my fault, not Kai's.
00009  *
00010  * The original comment follows below.
00011  */
00012 
00013 #ifndef _LINUX_TQUEUE_H
00014 #define _LINUX_TQUEUE_H
00015 
00016 #include <asm/bitops.h>
00017 #include <asm/system.h>
00018 #include <asm/spinlock.h>
00019 
00020 /*
00021  * New proposed "bottom half" handlers:
00022  * (C) 1994 Kai Petzke, wpp@marie.physik.tu-berlin.de
00023  *
00024  * Advantages:
00025  * - Bottom halfs are implemented as a linked list.  You can have as many
00026  *   of them, as you want.
00027  * - No more scanning of a bit field is required upon call of a bottom half.
00028  * - Support for chained bottom half lists.  The run_task_queue() function can be
00029  *   used as a bottom half handler.  This is for example useful for bottom
00030  *   halfs, which want to be delayed until the next clock tick.
00031  *
00032  * Problems:
00033  * - The queue_task_irq() inline function is only atomic with respect to itself.
00034  *   Problems can occur, when queue_task_irq() is called from a normal system
00035  *   call, and an interrupt comes in.  No problems occur, when queue_task_irq()
00036  *   is called from an interrupt or bottom half, and interrupted, as run_task_queue()
00037  *   will not be executed/continued before the last interrupt returns.  If in
00038  *   doubt, use queue_task(), not queue_task_irq().
00039  * - Bottom halfs are called in the reverse order that they were linked into
00040  *   the list.
00041  */
00042 
00043 struct tq_struct {
00044         struct tq_struct *next;         /* linked list of active bh's */
00045         unsigned long sync;             /* must be initialized to zero */
00046         void (*routine)(void *);        /* function to call */
00047         void *data;                     /* argument to function */
00048 };
00049 
00050 typedef struct tq_struct * task_queue;
00051 
00052 #define DECLARE_TASK_QUEUE(q)  task_queue q = NULL
00053 #define TQ_ACTIVE(q)           ((q) != NULL)
00054 
00055 extern task_queue tq_timer, tq_immediate, tq_scheduler, tq_disk;
00056 
00057 /*
00058  * To implement your own list of active bottom halfs, use the following
00059  * two definitions:
00060  *
00061  * struct tq_struct *my_bh = NULL;
00062  * struct tq_struct run_my_bh = {
00063  *      0, 0, (void (*)(void *)) run_task_queue, &my_bh
00064  * };
00065  *
00066  * To activate a bottom half on your list, use:
00067  *
00068  *     queue_task(tq_pointer, &my_bh);
00069  *
00070  * To run the bottom halfs on your list put them on the immediate list by:
00071  *
00072  *     queue_task(&run_my_bh, &tq_immediate);
00073  *
00074  * This allows you to do deferred procession.  For example, you could
00075  * have a bottom half list tq_timer, which is marked active by the timer
00076  * interrupt.
00077  */
00078 
00079 extern spinlock_t tqueue_lock;
00080 
00081 /*
00082  * Queue a task on a tq.  Return non-zero if it was successfully
00083  * added.
00084  */
00085 extern __inline__ int queue_task(struct tq_struct *bh_pointer,
00086                            task_queue *bh_list)
00087 {
00088         int ret = 0;
00089         if (!test_and_set_bit(0,&bh_pointer->sync)) {
00090                 unsigned long flags;
00091                 spin_lock_irqsave(&tqueue_lock, flags);
00092                 bh_pointer->next = *bh_list;
00093                 *bh_list = bh_pointer;
00094                 spin_unlock_irqrestore(&tqueue_lock, flags);
00095                 ret = 1;
00096         }
00097         return ret;
00098 }
00099 
00100 
00101 /*
00102  * Call all "bottom halfs" on a given list.
00103  */
00104 extern __inline__ void run_task_queue(task_queue *list)
00105 {
00106         if (*list) {
00107                 unsigned long flags;
00108                 struct tq_struct *p;
00109 
00110                 spin_lock_irqsave(&tqueue_lock, flags);
00111                 p = *list;
00112                 *list = NULL;
00113                 spin_unlock_irqrestore(&tqueue_lock, flags);
00114                 
00115                 while (p) {
00116                         void *arg;
00117                         void (*f) (void *);
00118                         struct tq_struct *save_p;
00119                         arg    = p -> data;
00120                         f      = p -> routine;
00121                         save_p = p;
00122                         p      = p -> next;
00123                         mb();
00124                         save_p -> sync = 0;
00125                         (*f)(arg);
00126                 }
00127         }
00128 }
00129 
00130 #endif /* _LINUX_TQUEUE_H */