Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/bhyve/mevent.c
Show First 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | |||||
#include <pthread.h> | #include <pthread.h> | ||||
#include <pthread_np.h> | #include <pthread_np.h> | ||||
#include "mevent.h" | #include "mevent.h" | ||||
#define MEVENT_MAX 64 | #define MEVENT_MAX 64 | ||||
static pthread_t mevent_tid; | static pthread_t mevent_tid; | ||||
static pthread_once_t mevent_once = PTHREAD_ONCE_INIT; | |||||
static int mevent_timid = 43; | static int mevent_timid = 43; | ||||
static int mevent_pipefd[2]; | static int mevent_pipefd[2]; | ||||
static int mfd; | |||||
static pthread_mutex_t mevent_lmutex = PTHREAD_MUTEX_INITIALIZER; | static pthread_mutex_t mevent_lmutex = PTHREAD_MUTEX_INITIALIZER; | ||||
struct mevent { | struct mevent { | ||||
void (*me_func)(int, enum ev_type, void *); | void (*me_func)(int, enum ev_type, void *); | ||||
#define me_msecs me_fd | #define me_msecs me_fd | ||||
int me_fd; | int me_fd; | ||||
int me_timid; | int me_timid; | ||||
enum ev_type me_type; | enum ev_type me_type; | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | mevent_notify(void) | ||||
* If calling from outside the i/o thread, write a byte on the | * If calling from outside the i/o thread, write a byte on the | ||||
* pipe to force the i/o thread to exit the blocking kevent call. | * pipe to force the i/o thread to exit the blocking kevent call. | ||||
*/ | */ | ||||
if (mevent_pipefd[1] != 0 && pthread_self() != mevent_tid) { | if (mevent_pipefd[1] != 0 && pthread_self() != mevent_tid) { | ||||
write(mevent_pipefd[1], &c, 1); | write(mevent_pipefd[1], &c, 1); | ||||
} | } | ||||
} | } | ||||
static void | |||||
mevent_init(void) | |||||
{ | |||||
#ifndef WITHOUT_CAPSICUM | |||||
cap_rights_t rights; | |||||
#endif | |||||
mfd = kqueue(); | |||||
assert(mfd > 0); | |||||
#ifndef WITHOUT_CAPSICUM | |||||
cap_rights_init(&rights, CAP_KQUEUE); | |||||
if (caph_rights_limit(mfd, &rights) == -1) | |||||
errx(EX_OSERR, "Unable to apply rights for sandbox"); | |||||
#endif | |||||
LIST_INIT(&change_head); | |||||
LIST_INIT(&global_head); | |||||
} | |||||
static int | static int | ||||
mevent_kq_filter(struct mevent *mevp) | mevent_kq_filter(struct mevent *mevp) | ||||
{ | { | ||||
int retval; | int retval; | ||||
retval = 0; | retval = 0; | ||||
if (mevp->me_type == EVF_READ) | if (mevp->me_type == EVF_READ) | ||||
Show All 19 Lines | |||||
static int | static int | ||||
mevent_kq_fflags(struct mevent *mevp) | mevent_kq_fflags(struct mevent *mevp) | ||||
{ | { | ||||
/* XXX nothing yet, perhaps EV_EOF for reads ? */ | /* XXX nothing yet, perhaps EV_EOF for reads ? */ | ||||
return (0); | return (0); | ||||
} | } | ||||
static void | |||||
mevent_populate(struct mevent *mevp, struct kevent *kev) | |||||
{ | |||||
if (mevp->me_type == EVF_TIMER) { | |||||
kev->ident = mevp->me_timid; | |||||
kev->data = mevp->me_msecs; | |||||
} else { | |||||
kev->ident = mevp->me_fd; | |||||
kev->data = 0; | |||||
} | |||||
kev->filter = mevent_kq_filter(mevp); | |||||
kev->flags = mevent_kq_flags(mevp); | |||||
kev->fflags = mevent_kq_fflags(mevp); | |||||
kev->udata = mevp; | |||||
} | |||||
static int | static int | ||||
mevent_build(int mfd, struct kevent *kev) | mevent_build(struct kevent *kev) | ||||
{ | { | ||||
struct mevent *mevp, *tmpp; | struct mevent *mevp, *tmpp; | ||||
int i; | int i; | ||||
i = 0; | i = 0; | ||||
mevent_qlock(); | mevent_qlock(); | ||||
LIST_FOREACH_SAFE(mevp, &change_head, me_list, tmpp) { | LIST_FOREACH_SAFE(mevp, &change_head, me_list, tmpp) { | ||||
if (mevp->me_closefd) { | if (mevp->me_closefd) { | ||||
/* | /* | ||||
* A close of the file descriptor will remove the | * A close of the file descriptor will remove the | ||||
* event | * event | ||||
*/ | */ | ||||
close(mevp->me_fd); | close(mevp->me_fd); | ||||
} else { | } else { | ||||
if (mevp->me_type == EVF_TIMER) { | assert((mevp->me_state & EV_ADD) == 0); | ||||
kev[i].ident = mevp->me_timid; | mevent_populate(mevp, &kev[i]); | ||||
kev[i].data = mevp->me_msecs; | |||||
} else { | |||||
kev[i].ident = mevp->me_fd; | |||||
kev[i].data = 0; | |||||
} | |||||
kev[i].filter = mevent_kq_filter(mevp); | |||||
kev[i].flags = mevent_kq_flags(mevp); | |||||
kev[i].fflags = mevent_kq_fflags(mevp); | |||||
kev[i].udata = mevp; | |||||
i++; | i++; | ||||
} | } | ||||
mevp->me_cq = 0; | mevp->me_cq = 0; | ||||
LIST_REMOVE(mevp, me_list); | LIST_REMOVE(mevp, me_list); | ||||
if (mevp->me_state & EV_DELETE) { | if (mevp->me_state & EV_DELETE) { | ||||
free(mevp); | free(mevp); | ||||
} else { | } else { | ||||
/* | |||||
* We need to add the event only once, so we can | |||||
* reset the EV_ADD bit after it has been propagated | |||||
* to the kevent() arguments the first time. | |||||
*/ | |||||
mevp->me_state &= ~EV_ADD; | |||||
LIST_INSERT_HEAD(&global_head, mevp, me_list); | LIST_INSERT_HEAD(&global_head, mevp, me_list); | ||||
} | } | ||||
assert(i < MEVENT_MAX); | assert(i < MEVENT_MAX); | ||||
} | } | ||||
mevent_qunlock(); | mevent_qunlock(); | ||||
Show All 15 Lines | mevent_handle(struct kevent *kev, int numev) | ||||
} | } | ||||
} | } | ||||
static struct mevent * | static struct mevent * | ||||
mevent_add_state(int tfd, enum ev_type type, | mevent_add_state(int tfd, enum ev_type type, | ||||
void (*func)(int, enum ev_type, void *), void *param, | void (*func)(int, enum ev_type, void *), void *param, | ||||
int state) | int state) | ||||
{ | { | ||||
struct kevent kev; | |||||
struct mevent *lp, *mevp; | struct mevent *lp, *mevp; | ||||
int ret; | |||||
if (tfd < 0 || func == NULL) { | if (tfd < 0 || func == NULL) { | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
mevp = NULL; | mevp = NULL; | ||||
pthread_once(&mevent_once, mevent_init); | |||||
mevent_qlock(); | mevent_qlock(); | ||||
/* | /* | ||||
* Verify that the fd/type tuple is not present in any list | * Verify that the fd/type tuple is not present in any list | ||||
*/ | */ | ||||
LIST_FOREACH(lp, &global_head, me_list) { | LIST_FOREACH(lp, &global_head, me_list) { | ||||
if (type != EVF_TIMER && lp->me_fd == tfd && | if (type != EVF_TIMER && lp->me_fd == tfd && | ||||
lp->me_type == type) { | lp->me_type == type) { | ||||
goto exit; | goto exit; | ||||
} | } | ||||
} | } | ||||
LIST_FOREACH(lp, &change_head, me_list) { | LIST_FOREACH(lp, &change_head, me_list) { | ||||
if (type != EVF_TIMER && lp->me_fd == tfd && | if (type != EVF_TIMER && lp->me_fd == tfd && | ||||
lp->me_type == type) { | lp->me_type == type) { | ||||
goto exit; | goto exit; | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* Allocate an entry, populate it, and add it to the change list. | * Allocate an entry and populate it. | ||||
*/ | */ | ||||
mevp = calloc(1, sizeof(struct mevent)); | mevp = calloc(1, sizeof(struct mevent)); | ||||
if (mevp == NULL) { | if (mevp == NULL) { | ||||
goto exit; | goto exit; | ||||
} | } | ||||
if (type == EVF_TIMER) { | if (type == EVF_TIMER) { | ||||
mevp->me_msecs = tfd; | mevp->me_msecs = tfd; | ||||
mevp->me_timid = mevent_timid++; | mevp->me_timid = mevent_timid++; | ||||
} else | } else | ||||
mevp->me_fd = tfd; | mevp->me_fd = tfd; | ||||
mevp->me_type = type; | mevp->me_type = type; | ||||
mevp->me_func = func; | mevp->me_func = func; | ||||
mevp->me_param = param; | mevp->me_param = param; | ||||
LIST_INSERT_HEAD(&change_head, mevp, me_list); | |||||
mevp->me_cq = 1; | |||||
mevp->me_state = state; | mevp->me_state = state; | ||||
mevent_notify(); | |||||
/* | |||||
* Try to add the event. If this fails, report the failure to | |||||
* the caller. | |||||
*/ | |||||
mevent_populate(mevp, &kev); | |||||
ret = kevent(mfd, &kev, 1, NULL, 0, NULL); | |||||
if (ret == -1) { | |||||
free(mevp); | |||||
mevp = NULL; | |||||
goto exit; | |||||
} | |||||
mevp->me_state &= ~EV_ADD; | |||||
LIST_INSERT_HEAD(&global_head, mevp, me_list); | |||||
exit: | exit: | ||||
mevent_qunlock(); | mevent_qunlock(); | ||||
return (mevp); | return (mevp); | ||||
} | } | ||||
struct mevent * | struct mevent * | ||||
mevent_add(int tfd, enum ev_type type, | mevent_add(int tfd, enum ev_type type, | ||||
▲ Show 20 Lines • Show All 116 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
void | void | ||||
mevent_dispatch(void) | mevent_dispatch(void) | ||||
{ | { | ||||
struct kevent changelist[MEVENT_MAX]; | struct kevent changelist[MEVENT_MAX]; | ||||
struct kevent eventlist[MEVENT_MAX]; | struct kevent eventlist[MEVENT_MAX]; | ||||
struct mevent *pipev; | struct mevent *pipev; | ||||
int mfd; | |||||
int numev; | int numev; | ||||
int ret; | int ret; | ||||
#ifndef WITHOUT_CAPSICUM | #ifndef WITHOUT_CAPSICUM | ||||
cap_rights_t rights; | cap_rights_t rights; | ||||
#endif | #endif | ||||
mevent_tid = pthread_self(); | mevent_tid = pthread_self(); | ||||
mevent_set_name(); | mevent_set_name(); | ||||
mfd = kqueue(); | pthread_once(&mevent_once, mevent_init); | ||||
markj: Perhaps assert that no error occurred, or cast the return value away. | |||||
Done Inline Actionsbhyve is pretty consistent about assuming no errors from mutexes, condvars, and once and not using either void casts or assertions of that (e.g. there's a once in block_if.c), so I think this is consistent with the rest of bhyve. jhb: bhyve is pretty consistent about assuming no errors from mutexes, condvars, and once and not… | |||||
assert(mfd > 0); | |||||
#ifndef WITHOUT_CAPSICUM | |||||
cap_rights_init(&rights, CAP_KQUEUE); | |||||
if (caph_rights_limit(mfd, &rights) == -1) | |||||
errx(EX_OSERR, "Unable to apply rights for sandbox"); | |||||
#endif | |||||
/* | /* | ||||
* Open the pipe that will be used for other threads to force | * Open the pipe that will be used for other threads to force | ||||
* the blocking kqueue call to exit by writing to it. Set the | * the blocking kqueue call to exit by writing to it. Set the | ||||
* descriptor to non-blocking. | * descriptor to non-blocking. | ||||
*/ | */ | ||||
ret = pipe(mevent_pipefd); | ret = pipe(mevent_pipefd); | ||||
if (ret < 0) { | if (ret < 0) { | ||||
perror("pipe"); | perror("pipe"); | ||||
Show All 16 Lines | #endif | ||||
for (;;) { | for (;;) { | ||||
/* | /* | ||||
* Build changelist if required. | * Build changelist if required. | ||||
* XXX the changelist can be put into the blocking call | * XXX the changelist can be put into the blocking call | ||||
* to eliminate the extra syscall. Currently better for | * to eliminate the extra syscall. Currently better for | ||||
* debug. | * debug. | ||||
*/ | */ | ||||
numev = mevent_build(mfd, changelist); | numev = mevent_build(changelist); | ||||
if (numev) { | if (numev) { | ||||
ret = kevent(mfd, changelist, numev, NULL, 0, NULL); | ret = kevent(mfd, changelist, numev, NULL, 0, NULL); | ||||
if (ret == -1) { | if (ret == -1) { | ||||
perror("Error return from kevent change"); | perror("Error return from kevent change"); | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
Show All 13 Lines |
Perhaps assert that no error occurred, or cast the return value away.