Statistics
| Branch: | Revision:

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
}