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 #include <errno.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <sys/time.h>
33
34 #include "scheduler.h"
35 #include "tapdisk-log.h"
36
37 #define DBG(_f, _a...) tlog_write(TLOG_DBG, _f, ##_a)
38
39 #define SCHEDULER_MAX_TIMEOUT 600
40 #define SCHEDULER_POLL_FD (SCHEDULER_POLL_READ_FD | \
41 SCHEDULER_POLL_WRITE_FD | \
42 SCHEDULER_POLL_EXCEPT_FD)
43
44 #define MIN(a, b) ((a) <= (b) ? (a) : (b))
45 #define MAX(a, b) ((a) >= (b) ? (a) : (b))
46
47 #define scheduler_for_each_event(s, event, tmp) \
48 list_for_each_entry_safe(event, tmp, &(s)->events, next)
49
50 typedef struct event {
51 char mode;
52 event_id_t id;
53
54 int fd;
55 int timeout;
56 int deadline;
57
58 event_cb_t cb;
59 void *private;
60
61 struct list_head next;
62 } event_t;
63
64 static void
scheduler_prepare_events(scheduler_t * s)65 scheduler_prepare_events(scheduler_t *s)
66 {
67 int diff;
68 struct timeval now;
69 event_t *event, *tmp;
70
71 FD_ZERO(&s->read_fds);
72 FD_ZERO(&s->write_fds);
73 FD_ZERO(&s->except_fds);
74
75 s->max_fd = 0;
76 s->timeout = SCHEDULER_MAX_TIMEOUT;
77
78 gettimeofday(&now, NULL);
79
80 scheduler_for_each_event(s, event, tmp) {
81 if (event->mode & SCHEDULER_POLL_READ_FD) {
82 FD_SET(event->fd, &s->read_fds);
83 s->max_fd = MAX(event->fd, s->max_fd);
84 }
85
86 if (event->mode & SCHEDULER_POLL_WRITE_FD) {
87 FD_SET(event->fd, &s->write_fds);
88 s->max_fd = MAX(event->fd, s->max_fd);
89 }
90
91 if (event->mode & SCHEDULER_POLL_EXCEPT_FD) {
92 FD_SET(event->fd, &s->except_fds);
93 s->max_fd = MAX(event->fd, s->max_fd);
94 }
95
96 if (event->mode & SCHEDULER_POLL_TIMEOUT) {
97 diff = event->deadline - now.tv_sec;
98 if (diff > 0)
99 s->timeout = MIN(s->timeout, diff);
100 else
101 s->timeout = 0;
102 }
103 }
104
105 s->timeout = MIN(s->timeout, s->max_timeout);
106 }
107
108 static void
scheduler_event_callback(event_t * event,char mode)109 scheduler_event_callback(event_t *event, char mode)
110 {
111 if (event->mode & SCHEDULER_POLL_TIMEOUT) {
112 struct timeval now;
113 gettimeofday(&now, NULL);
114 event->deadline = now.tv_sec + event->timeout;
115 }
116
117 event->cb(event->id, mode, event->private);
118 }
119
120 static void
scheduler_run_events(scheduler_t * s)121 scheduler_run_events(scheduler_t *s)
122 {
123 struct timeval now;
124 event_t *event, *tmp;
125
126 gettimeofday(&now, NULL);
127
128 again:
129 s->restart = 0;
130
131 scheduler_for_each_event(s, event, tmp) {
132 if ((event->mode & SCHEDULER_POLL_READ_FD) &&
133 FD_ISSET(event->fd, &s->read_fds)) {
134 FD_CLR(event->fd, &s->read_fds);
135 scheduler_event_callback(event, SCHEDULER_POLL_READ_FD);
136 goto next;
137 }
138
139 if ((event->mode & SCHEDULER_POLL_WRITE_FD) &&
140 FD_ISSET(event->fd, &s->write_fds)) {
141 FD_CLR(event->fd, &s->write_fds);
142 scheduler_event_callback(event, SCHEDULER_POLL_WRITE_FD);
143 goto next;
144 }
145
146 if ((event->mode & SCHEDULER_POLL_EXCEPT_FD) &&
147 FD_ISSET(event->fd, &s->except_fds)) {
148 FD_CLR(event->fd, &s->except_fds);
149 scheduler_event_callback(event, SCHEDULER_POLL_EXCEPT_FD);
150 goto next;
151 }
152
153 if ((event->mode & SCHEDULER_POLL_TIMEOUT) &&
154 (event->deadline <= now.tv_sec))
155 scheduler_event_callback(event, SCHEDULER_POLL_TIMEOUT);
156
157 next:
158 if (s->restart)
159 goto again;
160 }
161 }
162
163 int
scheduler_register_event(scheduler_t * s,char mode,int fd,int timeout,event_cb_t cb,void * private)164 scheduler_register_event(scheduler_t *s, char mode, int fd,
165 int timeout, event_cb_t cb, void *private)
166 {
167 event_t *event;
168 struct timeval now;
169
170 if (!cb)
171 return -EINVAL;
172
173 if (!(mode & SCHEDULER_POLL_TIMEOUT) && !(mode & SCHEDULER_POLL_FD))
174 return -EINVAL;
175
176 event = calloc(1, sizeof(event_t));
177 if (!event)
178 return -ENOMEM;
179
180 gettimeofday(&now, NULL);
181
182 INIT_LIST_HEAD(&event->next);
183
184 event->mode = mode;
185 event->fd = fd;
186 event->timeout = timeout;
187 event->deadline = now.tv_sec + timeout;
188 event->cb = cb;
189 event->private = private;
190 event->id = s->uuid++;
191
192 if (!s->uuid)
193 s->uuid++;
194
195 list_add_tail(&event->next, &s->events);
196
197 return event->id;
198 }
199
200 void
scheduler_unregister_event(scheduler_t * s,event_id_t id)201 scheduler_unregister_event(scheduler_t *s, event_id_t id)
202 {
203 event_t *event, *tmp;
204
205 if (!id)
206 return;
207
208 scheduler_for_each_event(s, event, tmp)
209 if (event->id == id) {
210 list_del(&event->next);
211 free(event);
212 s->restart = 1;
213 break;
214 }
215 }
216
217 void
scheduler_set_max_timeout(scheduler_t * s,int timeout)218 scheduler_set_max_timeout(scheduler_t *s, int timeout)
219 {
220 if (timeout >= 0)
221 s->max_timeout = MIN(s->max_timeout, timeout);
222 }
223
224 int
scheduler_wait_for_events(scheduler_t * s)225 scheduler_wait_for_events(scheduler_t *s)
226 {
227 int ret;
228 struct timeval tv;
229
230 scheduler_prepare_events(s);
231
232 tv.tv_sec = s->timeout;
233 tv.tv_usec = 0;
234
235 DBG("timeout: %d, max_timeout: %d\n",
236 s->timeout, s->max_timeout);
237
238 ret = select(s->max_fd + 1, &s->read_fds,
239 &s->write_fds, &s->except_fds, &tv);
240
241 s->restart = 0;
242 s->timeout = SCHEDULER_MAX_TIMEOUT;
243 s->max_timeout = SCHEDULER_MAX_TIMEOUT;
244
245 if (ret < 0)
246 return ret;
247
248 scheduler_run_events(s);
249
250 return ret;
251 }
252
253 void
scheduler_initialize(scheduler_t * s)254 scheduler_initialize(scheduler_t *s)
255 {
256 memset(s, 0, sizeof(scheduler_t));
257
258 s->uuid = 1;
259
260 FD_ZERO(&s->read_fds);
261 FD_ZERO(&s->write_fds);
262 FD_ZERO(&s->except_fds);
263
264 INIT_LIST_HEAD(&s->events);
265 }
266