root / drivers / scheduler.c @ abdb293f
History | View | Annotate | Download (8.2 kB)
1 |
/*
|
---|---|
2 |
* Copyright (c) 2008, XenSource Inc.
|
3 |
* All rights reserved.
|
4 |
*
|
5 |
* Redistribution and use in source and binary forms, with or without
|
6 |
* modification, are permitted provided that the following conditions are met:
|
7 |
* * Redistributions of source code must retain the above copyright
|
8 |
* notice, this list of conditions and the following disclaimer.
|
9 |
* * Redistributions in binary form must reproduce the above copyright
|
10 |
* notice, this list of conditions and the following disclaimer in the
|
11 |
* documentation and/or other materials provided with the distribution.
|
12 |
* * Neither the name of XenSource Inc. nor the names of its contributors
|
13 |
* may be used to endorse or promote products derived from this software
|
14 |
* without specific prior written permission.
|
15 |
*
|
16 |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
17 |
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
18 |
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
19 |
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
20 |
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
21 |
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
22 |
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
23 |
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
24 |
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
25 |
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
26 |
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
27 |
*/
|
28 |
|
29 |
#ifdef HAVE_CONFIG_H
|
30 |
#include "config.h" |
31 |
#endif
|
32 |
|
33 |
#include <errno.h> |
34 |
#include <stdlib.h> |
35 |
#include <unistd.h> |
36 |
#include <string.h> |
37 |
#include <sys/time.h> |
38 |
|
39 |
#include "tapdisk.h" |
40 |
#include "scheduler.h" |
41 |
#include "tapdisk-log.h" |
42 |
|
43 |
#define DBG(_f, _a...) if (0) { tlog_syslog(TLOG_DBG, _f, ##_a); } |
44 |
#define BUG_ON(_cond) if (_cond) td_panic() |
45 |
|
46 |
#define SCHEDULER_MAX_TIMEOUT 600 |
47 |
#define SCHEDULER_POLL_FD (SCHEDULER_POLL_READ_FD | \
|
48 |
SCHEDULER_POLL_WRITE_FD | \ |
49 |
SCHEDULER_POLL_EXCEPT_FD) |
50 |
|
51 |
#define MIN(a, b) ((a) <= (b) ? (a) : (b))
|
52 |
#define MAX(a, b) ((a) >= (b) ? (a) : (b))
|
53 |
|
54 |
#define scheduler_for_each_event(s, event) \
|
55 |
list_for_each_entry(event, &(s)->events, next) |
56 |
#define scheduler_for_each_event_safe(s, event, tmp) \
|
57 |
list_for_each_entry_safe(event, tmp, &(s)->events, next) |
58 |
|
59 |
typedef struct event { |
60 |
char mode;
|
61 |
char dead;
|
62 |
char pending;
|
63 |
char masked;
|
64 |
|
65 |
event_id_t id; |
66 |
|
67 |
int fd;
|
68 |
int timeout;
|
69 |
int deadline;
|
70 |
|
71 |
event_cb_t cb; |
72 |
void *private;
|
73 |
|
74 |
struct list_head next;
|
75 |
} event_t; |
76 |
|
77 |
static void |
78 |
scheduler_prepare_events(scheduler_t *s) |
79 |
{ |
80 |
int diff;
|
81 |
struct timeval now;
|
82 |
event_t *event; |
83 |
|
84 |
FD_ZERO(&s->read_fds); |
85 |
FD_ZERO(&s->write_fds); |
86 |
FD_ZERO(&s->except_fds); |
87 |
|
88 |
s->max_fd = -1;
|
89 |
s->timeout = SCHEDULER_MAX_TIMEOUT; |
90 |
|
91 |
gettimeofday(&now, NULL);
|
92 |
|
93 |
scheduler_for_each_event(s, event) { |
94 |
if (event->masked || event->dead)
|
95 |
continue;
|
96 |
|
97 |
if (event->mode & SCHEDULER_POLL_READ_FD) {
|
98 |
FD_SET(event->fd, &s->read_fds); |
99 |
s->max_fd = MAX(event->fd, s->max_fd); |
100 |
} |
101 |
|
102 |
if (event->mode & SCHEDULER_POLL_WRITE_FD) {
|
103 |
FD_SET(event->fd, &s->write_fds); |
104 |
s->max_fd = MAX(event->fd, s->max_fd); |
105 |
} |
106 |
|
107 |
if (event->mode & SCHEDULER_POLL_EXCEPT_FD) {
|
108 |
FD_SET(event->fd, &s->except_fds); |
109 |
s->max_fd = MAX(event->fd, s->max_fd); |
110 |
} |
111 |
|
112 |
if (event->mode & SCHEDULER_POLL_TIMEOUT) {
|
113 |
diff = event->deadline - now.tv_sec; |
114 |
if (diff > 0) |
115 |
s->timeout = MIN(s->timeout, diff); |
116 |
else
|
117 |
s->timeout = 0;
|
118 |
} |
119 |
} |
120 |
|
121 |
s->timeout = MIN(s->timeout, s->max_timeout); |
122 |
} |
123 |
|
124 |
static int |
125 |
scheduler_check_fd_events(scheduler_t *s, int nfds)
|
126 |
{ |
127 |
event_t *event; |
128 |
|
129 |
scheduler_for_each_event(s, event) { |
130 |
if (!nfds)
|
131 |
break;
|
132 |
|
133 |
if (event->dead)
|
134 |
continue;
|
135 |
|
136 |
if ((event->mode & SCHEDULER_POLL_READ_FD) &&
|
137 |
FD_ISSET(event->fd, &s->read_fds)) { |
138 |
FD_CLR(event->fd, &s->read_fds); |
139 |
event->pending |= SCHEDULER_POLL_READ_FD; |
140 |
--nfds; |
141 |
} |
142 |
|
143 |
if ((event->mode & SCHEDULER_POLL_WRITE_FD) &&
|
144 |
FD_ISSET(event->fd, &s->write_fds)) { |
145 |
FD_CLR(event->fd, &s->write_fds); |
146 |
event->pending |= SCHEDULER_POLL_WRITE_FD; |
147 |
--nfds; |
148 |
} |
149 |
|
150 |
if ((event->mode & SCHEDULER_POLL_EXCEPT_FD) &&
|
151 |
FD_ISSET(event->fd, &s->except_fds)) { |
152 |
FD_CLR(event->fd, &s->except_fds); |
153 |
event->pending |= SCHEDULER_POLL_EXCEPT_FD; |
154 |
--nfds; |
155 |
} |
156 |
} |
157 |
|
158 |
return nfds;
|
159 |
} |
160 |
|
161 |
static void |
162 |
scheduler_check_timeouts(scheduler_t *s) |
163 |
{ |
164 |
struct timeval now;
|
165 |
event_t *event; |
166 |
|
167 |
gettimeofday(&now, NULL);
|
168 |
|
169 |
scheduler_for_each_event(s, event) { |
170 |
BUG_ON(event->pending && event->masked); |
171 |
|
172 |
if (event->dead)
|
173 |
continue;
|
174 |
|
175 |
if (event->pending)
|
176 |
continue;
|
177 |
|
178 |
if (!(event->mode & SCHEDULER_POLL_TIMEOUT))
|
179 |
continue;
|
180 |
|
181 |
if (event->deadline > now.tv_sec)
|
182 |
continue;
|
183 |
|
184 |
event->pending = SCHEDULER_POLL_TIMEOUT; |
185 |
} |
186 |
} |
187 |
|
188 |
static int |
189 |
scheduler_check_events(scheduler_t *s, int nfds)
|
190 |
{ |
191 |
if (nfds)
|
192 |
nfds = scheduler_check_fd_events(s, nfds); |
193 |
|
194 |
scheduler_check_timeouts(s); |
195 |
|
196 |
return nfds;
|
197 |
} |
198 |
|
199 |
static void |
200 |
scheduler_event_callback(event_t *event, char mode)
|
201 |
{ |
202 |
if (event->mode & SCHEDULER_POLL_TIMEOUT) {
|
203 |
struct timeval now;
|
204 |
gettimeofday(&now, NULL);
|
205 |
event->deadline = now.tv_sec + event->timeout; |
206 |
} |
207 |
|
208 |
if (!event->masked)
|
209 |
event->cb(event->id, mode, event->private); |
210 |
} |
211 |
|
212 |
static int |
213 |
scheduler_run_events(scheduler_t *s) |
214 |
{ |
215 |
event_t *event; |
216 |
int n_dispatched = 0; |
217 |
|
218 |
scheduler_for_each_event(s, event) { |
219 |
char pending;
|
220 |
|
221 |
if (event->dead)
|
222 |
continue;
|
223 |
|
224 |
pending = event->pending; |
225 |
if (pending) {
|
226 |
event->pending = 0;
|
227 |
/* NB. must clear before cb */
|
228 |
scheduler_event_callback(event, pending); |
229 |
n_dispatched++; |
230 |
} |
231 |
} |
232 |
|
233 |
return n_dispatched;
|
234 |
} |
235 |
|
236 |
int
|
237 |
scheduler_register_event(scheduler_t *s, char mode, int fd, |
238 |
int timeout, event_cb_t cb, void *private) |
239 |
{ |
240 |
event_t *event; |
241 |
struct timeval now;
|
242 |
|
243 |
if (!cb)
|
244 |
return -EINVAL;
|
245 |
|
246 |
if (!(mode & SCHEDULER_POLL_TIMEOUT) && !(mode & SCHEDULER_POLL_FD))
|
247 |
return -EINVAL;
|
248 |
|
249 |
event = calloc(1, sizeof(event_t)); |
250 |
if (!event)
|
251 |
return -ENOMEM;
|
252 |
|
253 |
gettimeofday(&now, NULL);
|
254 |
|
255 |
INIT_LIST_HEAD(&event->next); |
256 |
|
257 |
event->mode = mode; |
258 |
event->fd = fd; |
259 |
event->timeout = timeout; |
260 |
event->deadline = now.tv_sec + timeout; |
261 |
event->cb = cb; |
262 |
event->private = private; |
263 |
event->id = s->uuid++; |
264 |
event->masked = 0;
|
265 |
|
266 |
if (!s->uuid)
|
267 |
s->uuid++; |
268 |
|
269 |
list_add_tail(&event->next, &s->events); |
270 |
|
271 |
return event->id;
|
272 |
} |
273 |
|
274 |
void
|
275 |
scheduler_unregister_event(scheduler_t *s, event_id_t id) |
276 |
{ |
277 |
event_t *event; |
278 |
|
279 |
if (!id)
|
280 |
return;
|
281 |
|
282 |
scheduler_for_each_event(s, event) |
283 |
if (event->id == id) {
|
284 |
event->dead = 1;
|
285 |
break;
|
286 |
} |
287 |
} |
288 |
|
289 |
void
|
290 |
scheduler_mask_event(scheduler_t *s, event_id_t id, int masked)
|
291 |
{ |
292 |
event_t *event; |
293 |
|
294 |
if (!id)
|
295 |
return;
|
296 |
|
297 |
scheduler_for_each_event(s, event) |
298 |
if (event->id == id) {
|
299 |
event->masked = !!masked; |
300 |
break;
|
301 |
} |
302 |
} |
303 |
|
304 |
static void |
305 |
scheduler_gc_events(scheduler_t *s) |
306 |
{ |
307 |
event_t *event, *next; |
308 |
|
309 |
scheduler_for_each_event_safe(s, event, next) |
310 |
if (event->dead) {
|
311 |
list_del(&event->next); |
312 |
free(event); |
313 |
} |
314 |
} |
315 |
|
316 |
void
|
317 |
scheduler_set_max_timeout(scheduler_t *s, int timeout)
|
318 |
{ |
319 |
if (timeout >= 0) |
320 |
s->max_timeout = MIN(s->max_timeout, timeout); |
321 |
} |
322 |
|
323 |
int
|
324 |
scheduler_wait_for_events(scheduler_t *s) |
325 |
{ |
326 |
int ret;
|
327 |
struct timeval tv;
|
328 |
|
329 |
s->depth++; |
330 |
ret = 0;
|
331 |
|
332 |
if (s->depth > 1 && scheduler_run_events(s)) |
333 |
/* NB. recursive invocations continue with the pending
|
334 |
* event set. We return as soon as we made some
|
335 |
* progress. */
|
336 |
goto out;
|
337 |
|
338 |
scheduler_prepare_events(s); |
339 |
|
340 |
tv.tv_sec = s->timeout; |
341 |
tv.tv_usec = 0;
|
342 |
|
343 |
DBG("timeout: %d, max_timeout: %d\n",
|
344 |
s->timeout, s->max_timeout); |
345 |
|
346 |
ret = select(s->max_fd + 1, &s->read_fds,
|
347 |
&s->write_fds, &s->except_fds, &tv); |
348 |
|
349 |
if (ret < 0) |
350 |
goto out;
|
351 |
|
352 |
ret = scheduler_check_events(s, ret); |
353 |
BUG_ON(ret); |
354 |
|
355 |
s->timeout = SCHEDULER_MAX_TIMEOUT; |
356 |
s->max_timeout = SCHEDULER_MAX_TIMEOUT; |
357 |
|
358 |
scheduler_run_events(s); |
359 |
|
360 |
if (s->depth == 1) |
361 |
scheduler_gc_events(s); |
362 |
|
363 |
out:
|
364 |
s->depth--; |
365 |
|
366 |
return ret;
|
367 |
} |
368 |
|
369 |
void
|
370 |
scheduler_initialize(scheduler_t *s) |
371 |
{ |
372 |
memset(s, 0, sizeof(scheduler_t)); |
373 |
|
374 |
s->uuid = 1;
|
375 |
s->depth = 0;
|
376 |
|
377 |
FD_ZERO(&s->read_fds); |
378 |
FD_ZERO(&s->write_fds); |
379 |
FD_ZERO(&s->except_fds); |
380 |
|
381 |
INIT_LIST_HEAD(&s->events); |
382 |
} |