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