summaryrefslogtreecommitdiffstats
path: root/include/linux/tqueue.h
blob: 8bd4e7f306ae2d952ae2a6d39e029f27f755c2e5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/*
 * 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 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.
 */

#ifndef _LINUX_TQUEUE_H
#define _LINUX_TQUEUE_H

#include <asm/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.
 *
 * Problems:
 * - The queue_task_irq() inline function is only atomic with respect to itself.
 *   Problems can occur, when queue_task_irq() is called from a normal system
 *   call, and an interrupt comes in.  No problems occur, when queue_task_irq()
 *   is called from an interrupt or bottom half, and interrupted, as run_task_queue()
 *   will not be executed/continued before the last interrupt returns.  If in
 *   doubt, use queue_task(), not queue_task_irq().
 * - Bottom halfs are called in the reverse order that they were linked into
 *   the list.
 */

struct tq_struct {
	struct tq_struct *next;		/* linked list of active bh's */
	int sync;			/* must be initialized to zero */
	void (*routine)(void *);	/* function to call */
	void *data;			/* argument to function */
};

typedef struct tq_struct * task_queue;

#define DECLARE_TASK_QUEUE(q)  task_queue q = NULL

extern task_queue tq_timer, tq_immediate, tq_scheduler, tq_disk;

/*
 * To implement your own list of active bottom halfs, use the following
 * two definitions:
 *
 * struct tq_struct *my_bh = NULL;
 * struct tq_struct run_my_bh = {
 *	0, 0, (void *)(void *) run_task_queue, &my_bh
 * };
 *
 * To activate a bottom half on your list, use:
 *
 *     queue_task(tq_pointer, &my_bh);
 *
 * To run the bottom halfs on your list put them on the immediate list by:
 *
 *     queue_task(&run_my_bh, &tq_immediate);
 *
 * This allows you to do deferred procession.  For example, you could
 * have a bottom half list tq_timer, which is marked active by the timer
 * interrupt.
 */

/*
 * queue_task_irq: put the bottom half handler "bh_pointer" on the list
 * "bh_list".  You may call this function only from an interrupt
 * handler or a bottom half handler.
 */
extern __inline__ void queue_task_irq(struct tq_struct *bh_pointer,
			       task_queue *bh_list)
{
	if (!set_bit(0,&bh_pointer->sync)) {
		bh_pointer->next = *bh_list;
		*bh_list = bh_pointer;
	}
}

/*
 * queue_task_irq_off: put the bottom half handler "bh_pointer" on the list
 * "bh_list".  You may call this function only when interrupts are off.
 */
extern __inline__ void queue_task_irq_off(struct tq_struct *bh_pointer,
				 task_queue *bh_list)
{
	if (!(bh_pointer->sync & 1)) {
		bh_pointer->sync = 1;
		bh_pointer->next = *bh_list;
		*bh_list = bh_pointer;
	}
}


/*
 * queue_task: as queue_task_irq, but can be called from anywhere.
 */
extern __inline__ void queue_task(struct tq_struct *bh_pointer,
			   task_queue *bh_list)
{
	if (!set_bit(0,&bh_pointer->sync)) {
		unsigned long flags;
		save_flags(flags);
		cli();
		bh_pointer->next = *bh_list;
		*bh_list = bh_pointer;
		restore_flags(flags);
	}
}

/*
 * Call all "bottom halfs" on a given list.
 */
extern __inline__ void run_task_queue(task_queue *list)
{
	struct tq_struct *p;

	p = xchg(list,NULL);
	while (p) {
		void *arg;
		void (*f) (void *);
		struct tq_struct *save_p;
		arg    = p -> data;
		f      = p -> routine;
		save_p = p;
		p      = p -> next;
		save_p -> sync = 0;
		(*f)(arg);
	}
}

#endif /* _LINUX_TQUEUE_H */