mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
266 lines
6.4 KiB
C
266 lines
6.4 KiB
C
/*
|
|
* Copyright (c) 2008, XenSource Inc.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* * Neither the name of XenSource Inc. nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
|
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <sys/time.h>
|
|
|
|
#include "scheduler.h"
|
|
#include "tapdisk-log.h"
|
|
|
|
#define DBG(_f, _a...) tlog_write(TLOG_DBG, _f, ##_a)
|
|
|
|
#define SCHEDULER_MAX_TIMEOUT 600
|
|
#define SCHEDULER_POLL_FD (SCHEDULER_POLL_READ_FD | \
|
|
SCHEDULER_POLL_WRITE_FD | \
|
|
SCHEDULER_POLL_EXCEPT_FD)
|
|
|
|
#define MIN(a, b) ((a) <= (b) ? (a) : (b))
|
|
#define MAX(a, b) ((a) >= (b) ? (a) : (b))
|
|
|
|
#define scheduler_for_each_event(s, event, tmp) \
|
|
list_for_each_entry_safe(event, tmp, &(s)->events, next)
|
|
|
|
typedef struct event {
|
|
char mode;
|
|
event_id_t id;
|
|
|
|
int fd;
|
|
int timeout;
|
|
int deadline;
|
|
|
|
event_cb_t cb;
|
|
void *private;
|
|
|
|
struct list_head next;
|
|
} event_t;
|
|
|
|
static void
|
|
scheduler_prepare_events(scheduler_t *s)
|
|
{
|
|
int diff;
|
|
struct timeval now;
|
|
event_t *event, *tmp;
|
|
|
|
FD_ZERO(&s->read_fds);
|
|
FD_ZERO(&s->write_fds);
|
|
FD_ZERO(&s->except_fds);
|
|
|
|
s->max_fd = 0;
|
|
s->timeout = SCHEDULER_MAX_TIMEOUT;
|
|
|
|
gettimeofday(&now, NULL);
|
|
|
|
scheduler_for_each_event(s, event, tmp) {
|
|
if (event->mode & SCHEDULER_POLL_READ_FD) {
|
|
FD_SET(event->fd, &s->read_fds);
|
|
s->max_fd = MAX(event->fd, s->max_fd);
|
|
}
|
|
|
|
if (event->mode & SCHEDULER_POLL_WRITE_FD) {
|
|
FD_SET(event->fd, &s->write_fds);
|
|
s->max_fd = MAX(event->fd, s->max_fd);
|
|
}
|
|
|
|
if (event->mode & SCHEDULER_POLL_EXCEPT_FD) {
|
|
FD_SET(event->fd, &s->except_fds);
|
|
s->max_fd = MAX(event->fd, s->max_fd);
|
|
}
|
|
|
|
if (event->mode & SCHEDULER_POLL_TIMEOUT) {
|
|
diff = event->deadline - now.tv_sec;
|
|
if (diff > 0)
|
|
s->timeout = MIN(s->timeout, diff);
|
|
else
|
|
s->timeout = 0;
|
|
}
|
|
}
|
|
|
|
s->timeout = MIN(s->timeout, s->max_timeout);
|
|
}
|
|
|
|
static void
|
|
scheduler_event_callback(event_t *event, char mode)
|
|
{
|
|
if (event->mode & SCHEDULER_POLL_TIMEOUT) {
|
|
struct timeval now;
|
|
gettimeofday(&now, NULL);
|
|
event->deadline = now.tv_sec + event->timeout;
|
|
}
|
|
|
|
event->cb(event->id, mode, event->private);
|
|
}
|
|
|
|
static void
|
|
scheduler_run_events(scheduler_t *s)
|
|
{
|
|
struct timeval now;
|
|
event_t *event, *tmp;
|
|
|
|
gettimeofday(&now, NULL);
|
|
|
|
again:
|
|
s->restart = 0;
|
|
|
|
scheduler_for_each_event(s, event, tmp) {
|
|
if ((event->mode & SCHEDULER_POLL_READ_FD) &&
|
|
FD_ISSET(event->fd, &s->read_fds)) {
|
|
FD_CLR(event->fd, &s->read_fds);
|
|
scheduler_event_callback(event, SCHEDULER_POLL_READ_FD);
|
|
goto next;
|
|
}
|
|
|
|
if ((event->mode & SCHEDULER_POLL_WRITE_FD) &&
|
|
FD_ISSET(event->fd, &s->write_fds)) {
|
|
FD_CLR(event->fd, &s->write_fds);
|
|
scheduler_event_callback(event, SCHEDULER_POLL_WRITE_FD);
|
|
goto next;
|
|
}
|
|
|
|
if ((event->mode & SCHEDULER_POLL_EXCEPT_FD) &&
|
|
FD_ISSET(event->fd, &s->except_fds)) {
|
|
FD_CLR(event->fd, &s->except_fds);
|
|
scheduler_event_callback(event, SCHEDULER_POLL_EXCEPT_FD);
|
|
goto next;
|
|
}
|
|
|
|
if ((event->mode & SCHEDULER_POLL_TIMEOUT) &&
|
|
(event->deadline <= now.tv_sec))
|
|
scheduler_event_callback(event, SCHEDULER_POLL_TIMEOUT);
|
|
|
|
next:
|
|
if (s->restart)
|
|
goto again;
|
|
}
|
|
}
|
|
|
|
int
|
|
scheduler_register_event(scheduler_t *s, char mode, int fd,
|
|
int timeout, event_cb_t cb, void *private)
|
|
{
|
|
event_t *event;
|
|
struct timeval now;
|
|
|
|
if (!cb)
|
|
return -EINVAL;
|
|
|
|
if (!(mode & SCHEDULER_POLL_TIMEOUT) && !(mode & SCHEDULER_POLL_FD))
|
|
return -EINVAL;
|
|
|
|
event = calloc(1, sizeof(event_t));
|
|
if (!event)
|
|
return -ENOMEM;
|
|
|
|
gettimeofday(&now, NULL);
|
|
|
|
INIT_LIST_HEAD(&event->next);
|
|
|
|
event->mode = mode;
|
|
event->fd = fd;
|
|
event->timeout = timeout;
|
|
event->deadline = now.tv_sec + timeout;
|
|
event->cb = cb;
|
|
event->private = private;
|
|
event->id = s->uuid++;
|
|
|
|
if (!s->uuid)
|
|
s->uuid++;
|
|
|
|
list_add_tail(&event->next, &s->events);
|
|
|
|
return event->id;
|
|
}
|
|
|
|
void
|
|
scheduler_unregister_event(scheduler_t *s, event_id_t id)
|
|
{
|
|
event_t *event, *tmp;
|
|
|
|
if (!id)
|
|
return;
|
|
|
|
scheduler_for_each_event(s, event, tmp)
|
|
if (event->id == id) {
|
|
list_del(&event->next);
|
|
free(event);
|
|
s->restart = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
scheduler_set_max_timeout(scheduler_t *s, int timeout)
|
|
{
|
|
if (timeout >= 0)
|
|
s->max_timeout = MIN(s->max_timeout, timeout);
|
|
}
|
|
|
|
int
|
|
scheduler_wait_for_events(scheduler_t *s)
|
|
{
|
|
int ret;
|
|
struct timeval tv;
|
|
|
|
scheduler_prepare_events(s);
|
|
|
|
tv.tv_sec = s->timeout;
|
|
tv.tv_usec = 0;
|
|
|
|
DBG("timeout: %d, max_timeout: %d\n",
|
|
s->timeout, s->max_timeout);
|
|
|
|
ret = select(s->max_fd + 1, &s->read_fds,
|
|
&s->write_fds, &s->except_fds, &tv);
|
|
|
|
s->restart = 0;
|
|
s->timeout = SCHEDULER_MAX_TIMEOUT;
|
|
s->max_timeout = SCHEDULER_MAX_TIMEOUT;
|
|
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
scheduler_run_events(s);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
scheduler_initialize(scheduler_t *s)
|
|
{
|
|
memset(s, 0, sizeof(scheduler_t));
|
|
|
|
s->uuid = 1;
|
|
|
|
FD_ZERO(&s->read_fds);
|
|
FD_ZERO(&s->write_fds);
|
|
FD_ZERO(&s->except_fds);
|
|
|
|
INIT_LIST_HEAD(&s->events);
|
|
}
|