1
2
3
4
5
6
7
8
9
10
11
12
13#include <linux/sched.h>
14#include <linux/list.h>
15#include <linux/signal.h>
16#include <linux/spinlock.h>
17#include <linux/gfp.h>
18#include <linux/init.h>
19#include <linux/module.h>
20#include <linux/suspend.h>
21#include <linux/fs.h>
22#include <linux/writeback.h>
23#include <linux/kthread.h>
24
25
26
27
28
29#define MIN_PDFLUSH_THREADS 2
30#define MAX_PDFLUSH_THREADS 8
31
32static void start_one_pdflush_thread(void);
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47static LIST_HEAD(pdflush_list);
48static spinlock_t pdflush_lock = SPIN_LOCK_UNLOCKED;
49
50
51
52
53
54
55
56
57int nr_pdflush_threads = 0;
58
59
60
61
62static unsigned long last_empty_jifs;
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83struct pdflush_work {
84 struct task_struct *who;
85 void (*fn)(unsigned long);
86 unsigned long arg0;
87 struct list_head list;
88 unsigned long when_i_went_to_sleep;
89};
90
91static int __pdflush(struct pdflush_work *my_work)
92{
93 current->flags |= PF_FLUSHER;
94 my_work->fn = NULL;
95 my_work->who = current;
96 INIT_LIST_HEAD(&my_work->list);
97
98 spin_lock_irq(&pdflush_lock);
99 nr_pdflush_threads++;
100 for ( ; ; ) {
101 struct pdflush_work *pdf;
102
103 set_current_state(TASK_INTERRUPTIBLE);
104 list_move(&my_work->list, &pdflush_list);
105 my_work->when_i_went_to_sleep = jiffies;
106 spin_unlock_irq(&pdflush_lock);
107
108 schedule();
109 if (current->flags & PF_FREEZE) {
110 refrigerator(PF_FREEZE);
111 spin_lock_irq(&pdflush_lock);
112 continue;
113 }
114
115 spin_lock_irq(&pdflush_lock);
116 if (!list_empty(&my_work->list)) {
117 printk("pdflush: bogus wakeup!\n");
118 my_work->fn = NULL;
119 continue;
120 }
121 if (my_work->fn == NULL) {
122 printk("pdflush: NULL work function\n");
123 continue;
124 }
125 spin_unlock_irq(&pdflush_lock);
126
127 (*my_work->fn)(my_work->arg0);
128
129
130
131
132
133 if (jiffies - last_empty_jifs > 1 * HZ) {
134
135 if (list_empty(&pdflush_list)) {
136
137 if (nr_pdflush_threads < MAX_PDFLUSH_THREADS)
138 start_one_pdflush_thread();
139 }
140 }
141
142 spin_lock_irq(&pdflush_lock);
143 my_work->fn = NULL;
144
145
146
147
148
149 if (list_empty(&pdflush_list))
150 continue;
151 if (nr_pdflush_threads <= MIN_PDFLUSH_THREADS)
152 continue;
153 pdf = list_entry(pdflush_list.prev, struct pdflush_work, list);
154 if (jiffies - pdf->when_i_went_to_sleep > 1 * HZ) {
155
156 pdf->when_i_went_to_sleep = jiffies;
157 break;
158 }
159 }
160 nr_pdflush_threads--;
161 spin_unlock_irq(&pdflush_lock);
162 return 0;
163}
164
165
166
167
168
169
170
171
172static int pdflush(void *dummy)
173{
174 struct pdflush_work my_work;
175
176
177
178
179
180 set_user_nice(current, 0);
181 return __pdflush(&my_work);
182}
183
184
185
186
187
188
189int pdflush_operation(void (*fn)(unsigned long), unsigned long arg0)
190{
191 unsigned long flags;
192 int ret = 0;
193
194 if (fn == NULL)
195 BUG();
196
197 spin_lock_irqsave(&pdflush_lock, flags);
198 if (list_empty(&pdflush_list)) {
199 spin_unlock_irqrestore(&pdflush_lock, flags);
200 ret = -1;
201 } else {
202 struct pdflush_work *pdf;
203
204 pdf = list_entry(pdflush_list.next, struct pdflush_work, list);
205 list_del_init(&pdf->list);
206 if (list_empty(&pdflush_list))
207 last_empty_jifs = jiffies;
208 pdf->fn = fn;
209 pdf->arg0 = arg0;
210 wake_up_process(pdf->who);
211 spin_unlock_irqrestore(&pdflush_lock, flags);
212 }
213 return ret;
214}
215
216static void start_one_pdflush_thread(void)
217{
218 kthread_run(pdflush, NULL, "pdflush");
219}
220
221static int __init pdflush_init(void)
222{
223 int i;
224
225 for (i = 0; i < MIN_PDFLUSH_THREADS; i++)
226 start_one_pdflush_thread();
227 return 0;
228}
229
230module_init(pdflush_init);
231