1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2 * vim:expandtab:shiftwidth=8:tabstop=8:
4 * Copyright (c) 2002 Cray Inc.
5 * Copyright (c) 2002 Eric Hoffman
7 * This file is part of Lustre, http://www.lustre.org.
9 * Lustre is free software; you can redistribute it and/or
10 * modify it under the terms of version 2 of the GNU General Public
11 * License as published by the Free Software Foundation.
13 * Lustre is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with Lustre; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 * Provides a general mechanism for registering and dispatching
25 * io events through the select system call.
28 #define DEBUG_SUBSYSTEM S_NAL
31 #include <sys/filio.h>
33 #include <sys/ioctl.h>
37 #include <sys/types.h>
44 #include <procbridge.h>
47 static struct timeval beginning_of_epoch;
48 static io_handler io_handlers;
52 * Return: the current time in canonical units: a 64 bit number
53 * where the most significant 32 bits contains the number
54 * of seconds, and the least signficant a count of (1/(2^32))ths
59 struct timeval result;
61 gettimeofday(&result,0);
62 return((((unsigned long long)result.tv_sec)<<32)|
63 (((unsigned long long)result.tv_usec)<<32)/1000000);
67 /* Function: register_io_handler
68 * Arguments: fd: the file descriptor of interest
69 * type: a mask of READ_HANDLER, WRITE_HANDLER, EXCEPTION_HANDLER
70 * function: a function to call when io is available on fd
71 * arg: an opaque correlator to return to the handler
72 * Returns: a pointer to the io_handler structure
74 io_handler register_io_handler(int fd,
76 int (*function)(void *),
79 io_handler i=(io_handler)malloc(sizeof(struct io_handler));
86 if ((i->next=io_handlers)) i->next->last=&i->next;
92 /* Function: remove_io_handler
93 * Arguments: i: a pointer to the handler to stop servicing
95 * remove_io_handler() doesn't actually free the handler, due
96 * to reentrancy problems. it just marks the handler for
97 * later cleanup by the blocking function.
99 void remove_io_handler (io_handler i)
104 static void set_flag(io_handler n,fd_set *r, fd_set *w, fd_set *e)
106 if (n->type & READ_HANDLER) FD_SET(n->fd, r);
107 if (n->type & WRITE_HANDLER) FD_SET(n->fd, w);
108 if (n->type & EXCEPTION_HANDLER) FD_SET(n->fd, e);
111 static int prepare_fd_sets(fd_set *r, fd_set *w, fd_set *e)
120 for (k=&io_handlers;*k;){
136 static int execute_callbacks(fd_set *r, fd_set *w, fd_set *e)
141 for (j = io_handlers; j; j = j->next) {
146 if (FD_ISSET(j->fd, r) && (j->type & READ_HANDLER)) {
150 if (FD_ISSET(j->fd, w) && (j->type & WRITE_HANDLER)) {
154 if (FD_ISSET(j->fd, e) && (j->type & EXCEPTION_HANDLER)) {
161 if (!(*j->function)(j->argument))
170 #ifdef ENABLE_SELECT_DISPATCH
173 pthread_mutex_t mutex;
181 struct timeval *timeout;
182 struct timeval submit_time;
184 PTHREAD_MUTEX_INITIALIZER,
185 PTHREAD_COND_INITIALIZER,
187 NULL, NULL, NULL, NULL,
190 extern int liblustre_wait_event(int timeout);
191 extern procbridge __global_procbridge;
194 * this will intercept syscall select() of user apps
197 int select(int n, fd_set *rset, fd_set *wset, fd_set *eset,
198 struct timeval *timeout)
200 LASSERT(fd_extra.submitted == 0);
204 fd_extra.rset = rset;
205 fd_extra.wset = wset;
206 fd_extra.eset = eset;
207 fd_extra.timeout = timeout;
209 liblustre_wait_event(0);
210 pthread_mutex_lock(&fd_extra.mutex);
211 gettimeofday(&fd_extra.submit_time, NULL);
212 fd_extra.submitted = 1;
213 LASSERT(__global_procbridge);
214 procbridge_wakeup_nal(__global_procbridge);
217 if (fd_extra.submitted)
218 pthread_cond_wait(&fd_extra.cond, &fd_extra.mutex);
219 pthread_mutex_unlock(&fd_extra.mutex);
221 liblustre_wait_event(0);
223 pthread_mutex_lock(&fd_extra.mutex);
224 if (fd_extra.submitted)
226 pthread_mutex_unlock(&fd_extra.mutex);
228 LASSERT(fd_extra.nready >= 0);
229 LASSERT(fd_extra.submitted == 0);
230 return fd_extra.nready;
233 static int merge_fds(int max, fd_set *rset, fd_set *wset, fd_set *eset)
241 for (i = 0; i < __FD_SETSIZE/__NFDBITS; i++) {
242 LASSERT(!fd_extra.rset ||
243 !(__FDS_BITS(rset)[i] & __FDS_BITS(fd_extra.rset)[i]));
244 LASSERT(!fd_extra.wset ||
245 !(__FDS_BITS(wset)[i] & __FDS_BITS(fd_extra.wset)[i]));
246 LASSERT(!fd_extra.eset ||
247 !(__FDS_BITS(eset)[i] & __FDS_BITS(fd_extra.eset)[i]));
249 if (fd_extra.rset && __FDS_BITS(fd_extra.rset)[i])
250 __FDS_BITS(rset)[i] |= __FDS_BITS(fd_extra.rset)[i];
251 if (fd_extra.wset && __FDS_BITS(fd_extra.wset)[i])
252 __FDS_BITS(wset)[i] |= __FDS_BITS(fd_extra.wset)[i];
253 if (fd_extra.eset && __FDS_BITS(fd_extra.eset)[i])
254 __FDS_BITS(eset)[i] |= __FDS_BITS(fd_extra.eset)[i];
257 return (fd_extra.maxfd > max ? fd_extra.maxfd : max);
261 int timeval_ge(struct timeval *tv1, struct timeval *tv2)
264 return ((tv1->tv_sec - tv2->tv_sec) * 1000000 +
265 (tv1->tv_usec - tv2->tv_usec) >= 0);
269 * choose the most recent timeout value
271 static struct timeval *choose_timeout(struct timeval *tv1,
279 if (timeval_ge(tv1, tv2))
285 /* Function: select_timer_block
286 * Arguments: until: an absolute time when the select should return
288 * This function dispatches the various file descriptors' handler
289 * functions, if the kernel indicates there is io available.
291 void select_timer_block(when until)
294 struct timeval timeout;
295 struct timeval *timeout_pointer, *select_timeout;
296 int max, nready, nexec;
303 interval = until - now();
304 timeout.tv_sec = (interval >> 32);
305 timeout.tv_usec = ((interval << 32) / 1000000) >> 32;
306 timeout_pointer = &timeout;
308 timeout_pointer = NULL;
311 max = prepare_fd_sets(&fds[0], &fds[1], &fds[2]);
312 select_timeout = timeout_pointer;
314 pthread_mutex_lock(&fd_extra.mutex);
315 fd_handling = fd_extra.submitted;
316 pthread_mutex_unlock(&fd_extra.mutex);
318 max = merge_fds(max, &fds[0], &fds[1], &fds[2]);
319 select_timeout = choose_timeout(timeout_pointer, fd_extra.timeout);
322 /* XXX only compile for linux */
324 nready = syscall(SYS_select, max, &fds[0], &fds[1], &fds[2],
327 nready = syscall(SYS__newselect, max, &fds[0], &fds[1], &fds[2],
331 CERROR("select return err %d, errno %d\n", nready, errno);
336 nexec = execute_callbacks(&fds[0], &fds[1], &fds[2]);
341 /* even both nready & nexec are 0, we still need try to wakeup
342 * upper thread since it may have timed out
345 LASSERT(nready >= 0);
347 pthread_mutex_lock(&fd_extra.mutex);
350 *fd_extra.rset = fds[0];
352 *fd_extra.wset = fds[1];
354 *fd_extra.eset = fds[2];
355 fd_extra.nready = nready;
356 fd_extra.submitted = 0;
361 if (fd_extra.timeout) {
362 gettimeofday(&t, NULL);
363 if (timeval_ge(&t, &fd_extra.submit_time))
364 fd_extra.submitted = 0;
368 pthread_cond_signal(&fd_extra.cond);
369 pthread_mutex_unlock(&fd_extra.mutex);
372 /* haven't found portals event, go back to loop if time
375 if (timeout_pointer == NULL || now() >= until)
380 #else /* !ENABLE_SELECT_DISPATCH */
382 /* Function: select_timer_block
383 * Arguments: until: an absolute time when the select should return
385 * This function dispatches the various file descriptors' handler
386 * functions, if the kernel indicates there is io available.
388 void select_timer_block(when until)
391 struct timeval timeout;
392 struct timeval *timeout_pointer;
398 interval = until - now();
399 timeout.tv_sec = (interval >> 32);
400 timeout.tv_usec = ((interval << 32) / 1000000) >> 32;
401 timeout_pointer = &timeout;
403 timeout_pointer = NULL;
405 max = prepare_fd_sets(&fds[0], &fds[1], &fds[2]);
407 nready = select(max, &fds[0], &fds[1], &fds[2], timeout_pointer);
409 execute_callbacks(&fds[0], &fds[1], &fds[2]);
411 #endif /* ENABLE_SELECT_DISPATCH */
413 /* Function: init_unix_timer()
414 * is called to initialize the library
416 void init_unix_timer()
419 gettimeofday(&beginning_of_epoch, 0);
420 initialize_timer(select_timer_block);