Changeset View
Changeset View
Standalone View
Standalone View
head/sys/compat/linux/linux_event.c
Show First 20 Lines • Show All 92 Lines • ▼ Show 20 Lines | |||||
#if defined(__amd64__) | #if defined(__amd64__) | ||||
__attribute__((packed)) | __attribute__((packed)) | ||||
#endif | #endif | ||||
; | ; | ||||
#define LINUX_MAX_EVENTS (INT_MAX / sizeof(struct epoll_event)) | #define LINUX_MAX_EVENTS (INT_MAX / sizeof(struct epoll_event)) | ||||
static void epoll_fd_install(struct thread *td, int fd, epoll_udata_t udata); | static void epoll_fd_install(struct thread *td, int fd, epoll_udata_t udata); | ||||
static int epoll_to_kevent(struct thread *td, struct file *epfp, | static int epoll_to_kevent(struct thread *td, int fd, | ||||
int fd, struct epoll_event *l_event, int *kev_flags, | struct epoll_event *l_event, struct kevent *kevent, | ||||
struct kevent *kevent, int *nkevents); | int *nkevents); | ||||
static void kevent_to_epoll(struct kevent *kevent, struct epoll_event *l_event); | static void kevent_to_epoll(struct kevent *kevent, struct epoll_event *l_event); | ||||
static int epoll_kev_copyout(void *arg, struct kevent *kevp, int count); | static int epoll_kev_copyout(void *arg, struct kevent *kevp, int count); | ||||
static int epoll_kev_copyin(void *arg, struct kevent *kevp, int count); | static int epoll_kev_copyin(void *arg, struct kevent *kevp, int count); | ||||
static int epoll_delete_event(struct thread *td, struct file *epfp, | static int epoll_register_kevent(struct thread *td, struct file *epfp, | ||||
int fd, int filter); | int fd, int filter, unsigned int flags); | ||||
static int epoll_fd_registered(struct thread *td, struct file *epfp, | |||||
int fd); | |||||
static int epoll_delete_all_events(struct thread *td, struct file *epfp, | static int epoll_delete_all_events(struct thread *td, struct file *epfp, | ||||
int fd); | int fd); | ||||
struct epoll_copyin_args { | struct epoll_copyin_args { | ||||
struct kevent *changelist; | struct kevent *changelist; | ||||
}; | }; | ||||
struct epoll_copyout_args { | struct epoll_copyout_args { | ||||
▲ Show 20 Lines • Show All 174 Lines • ▼ Show 20 Lines | linux_epoll_create1(struct thread *td, struct linux_epoll_create1_args *args) | ||||
if ((args->flags & LINUX_O_CLOEXEC) != 0) | if ((args->flags & LINUX_O_CLOEXEC) != 0) | ||||
flags |= O_CLOEXEC; | flags |= O_CLOEXEC; | ||||
return (epoll_create_common(td, flags)); | return (epoll_create_common(td, flags)); | ||||
} | } | ||||
/* Structure converting function from epoll to kevent. */ | /* Structure converting function from epoll to kevent. */ | ||||
static int | static int | ||||
epoll_to_kevent(struct thread *td, struct file *epfp, | epoll_to_kevent(struct thread *td, int fd, struct epoll_event *l_event, | ||||
int fd, struct epoll_event *l_event, int *kev_flags, | |||||
struct kevent *kevent, int *nkevents) | struct kevent *kevent, int *nkevents) | ||||
{ | { | ||||
uint32_t levents = l_event->events; | uint32_t levents = l_event->events; | ||||
struct linux_pemuldata *pem; | struct linux_pemuldata *pem; | ||||
struct proc *p; | struct proc *p; | ||||
unsigned short kev_flags = EV_ADD | EV_ENABLE; | |||||
/* flags related to how event is registered */ | /* flags related to how event is registered */ | ||||
if ((levents & LINUX_EPOLLONESHOT) != 0) | if ((levents & LINUX_EPOLLONESHOT) != 0) | ||||
*kev_flags |= EV_DISPATCH; | kev_flags |= EV_DISPATCH; | ||||
if ((levents & LINUX_EPOLLET) != 0) | if ((levents & LINUX_EPOLLET) != 0) | ||||
*kev_flags |= EV_CLEAR; | kev_flags |= EV_CLEAR; | ||||
if ((levents & LINUX_EPOLLERR) != 0) | if ((levents & LINUX_EPOLLERR) != 0) | ||||
*kev_flags |= EV_ERROR; | kev_flags |= EV_ERROR; | ||||
if ((levents & LINUX_EPOLLRDHUP) != 0) | if ((levents & LINUX_EPOLLRDHUP) != 0) | ||||
*kev_flags |= EV_EOF; | kev_flags |= EV_EOF; | ||||
/* flags related to what event is registered */ | /* flags related to what event is registered */ | ||||
if ((levents & LINUX_EPOLL_EVRD) != 0) { | if ((levents & LINUX_EPOLL_EVRD) != 0) { | ||||
EV_SET(kevent++, fd, EVFILT_READ, *kev_flags, 0, 0, 0); | EV_SET(kevent++, fd, EVFILT_READ, kev_flags, 0, 0, 0); | ||||
++(*nkevents); | ++(*nkevents); | ||||
} | } | ||||
if ((levents & LINUX_EPOLL_EVWR) != 0) { | if ((levents & LINUX_EPOLL_EVWR) != 0) { | ||||
EV_SET(kevent++, fd, EVFILT_WRITE, *kev_flags, 0, 0, 0); | EV_SET(kevent++, fd, EVFILT_WRITE, kev_flags, 0, 0, 0); | ||||
++(*nkevents); | ++(*nkevents); | ||||
} | } | ||||
if ((levents & ~(LINUX_EPOLL_EVSUP)) != 0) { | if ((levents & ~(LINUX_EPOLL_EVSUP)) != 0) { | ||||
p = td->td_proc; | p = td->td_proc; | ||||
pem = pem_find(p); | pem = pem_find(p); | ||||
KASSERT(pem != NULL, ("epoll proc emuldata not found.\n")); | KASSERT(pem != NULL, ("epoll proc emuldata not found.\n")); | ||||
▲ Show 20 Lines • Show All 114 Lines • ▼ Show 20 Lines | linux_epoll_ctl(struct thread *td, struct linux_epoll_ctl_args *args) | ||||
struct file *epfp, *fp; | struct file *epfp, *fp; | ||||
struct epoll_copyin_args ciargs; | struct epoll_copyin_args ciargs; | ||||
struct kevent kev[2]; | struct kevent kev[2]; | ||||
struct kevent_copyops k_ops = { &ciargs, | struct kevent_copyops k_ops = { &ciargs, | ||||
NULL, | NULL, | ||||
epoll_kev_copyin}; | epoll_kev_copyin}; | ||||
struct epoll_event le; | struct epoll_event le; | ||||
cap_rights_t rights; | cap_rights_t rights; | ||||
int kev_flags; | |||||
int nchanges = 0; | int nchanges = 0; | ||||
int error; | int error; | ||||
if (args->op != LINUX_EPOLL_CTL_DEL) { | if (args->op != LINUX_EPOLL_CTL_DEL) { | ||||
error = copyin(args->event, &le, sizeof(le)); | error = copyin(args->event, &le, sizeof(le)); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
} | } | ||||
Show All 16 Lines | linux_epoll_ctl(struct thread *td, struct linux_epoll_ctl_args *args) | ||||
if (epfp == fp) { | if (epfp == fp) { | ||||
error = EINVAL; | error = EINVAL; | ||||
goto leave0; | goto leave0; | ||||
} | } | ||||
ciargs.changelist = kev; | ciargs.changelist = kev; | ||||
if (args->op != LINUX_EPOLL_CTL_DEL) { | if (args->op != LINUX_EPOLL_CTL_DEL) { | ||||
kev_flags = EV_ADD | EV_ENABLE; | error = epoll_to_kevent(td, args->fd, &le, kev, &nchanges); | ||||
error = epoll_to_kevent(td, epfp, args->fd, &le, | |||||
&kev_flags, kev, &nchanges); | |||||
if (error != 0) | if (error != 0) | ||||
goto leave0; | goto leave0; | ||||
} | } | ||||
switch (args->op) { | switch (args->op) { | ||||
case LINUX_EPOLL_CTL_MOD: | case LINUX_EPOLL_CTL_MOD: | ||||
error = epoll_delete_all_events(td, epfp, args->fd); | error = epoll_delete_all_events(td, epfp, args->fd); | ||||
if (error != 0) | if (error != 0) | ||||
goto leave0; | goto leave0; | ||||
break; | break; | ||||
case LINUX_EPOLL_CTL_ADD: | case LINUX_EPOLL_CTL_ADD: | ||||
/* | if (epoll_fd_registered(td, epfp, args->fd)) { | ||||
* kqueue_register() return ENOENT if event does not exists | |||||
* and the EV_ADD flag is not set. Reset EV_ENABLE flag to | |||||
* avoid accidental activation of fired oneshot events. | |||||
*/ | |||||
kev[0].flags &= ~(EV_ADD | EV_ENABLE); | |||||
error = kqfd_register(args->epfd, &kev[0], td, M_WAITOK); | |||||
if (error != ENOENT) { | |||||
error = EEXIST; | error = EEXIST; | ||||
goto leave0; | goto leave0; | ||||
} | } | ||||
error = 0; | |||||
kev[0].flags |= (EV_ADD | EV_ENABLE); | |||||
break; | break; | ||||
case LINUX_EPOLL_CTL_DEL: | case LINUX_EPOLL_CTL_DEL: | ||||
/* CTL_DEL means unregister this fd with this epoll */ | /* CTL_DEL means unregister this fd with this epoll */ | ||||
error = epoll_delete_all_events(td, epfp, args->fd); | error = epoll_delete_all_events(td, epfp, args->fd); | ||||
goto leave0; | goto leave0; | ||||
default: | default: | ||||
▲ Show 20 Lines • Show All 123 Lines • ▼ Show 20 Lines | if (args->mask != NULL) { | ||||
pmask = &mask; | pmask = &mask; | ||||
} else | } else | ||||
pmask = NULL; | pmask = NULL; | ||||
return (linux_epoll_wait_common(td, args->epfd, args->events, | return (linux_epoll_wait_common(td, args->epfd, args->events, | ||||
args->maxevents, args->timeout, pmask)); | args->maxevents, args->timeout, pmask)); | ||||
} | } | ||||
static int | static int | ||||
epoll_delete_event(struct thread *td, struct file *epfp, int fd, int filter) | epoll_register_kevent(struct thread *td, struct file *epfp, int fd, int filter, | ||||
unsigned int flags) | |||||
{ | { | ||||
struct epoll_copyin_args ciargs; | struct epoll_copyin_args ciargs; | ||||
struct kevent kev; | struct kevent kev; | ||||
struct kevent_copyops k_ops = { &ciargs, | struct kevent_copyops k_ops = { &ciargs, | ||||
NULL, | NULL, | ||||
epoll_kev_copyin}; | epoll_kev_copyin}; | ||||
ciargs.changelist = &kev; | ciargs.changelist = &kev; | ||||
EV_SET(&kev, fd, filter, EV_DELETE | EV_DISABLE, 0, 0, 0); | EV_SET(&kev, fd, filter, flags, 0, 0, 0); | ||||
return (kern_kevent_fp(td, epfp, 1, 0, &k_ops, NULL)); | return (kern_kevent_fp(td, epfp, 1, 0, &k_ops, NULL)); | ||||
} | } | ||||
static int | static int | ||||
epoll_fd_registered(struct thread *td, struct file *epfp, int fd) | |||||
{ | |||||
/* | |||||
* Set empty filter flags to avoid accidental modification of already | |||||
* registered events. In the case of event re-registration: | |||||
* 1. If event does not exists kevent() does nothing and returns ENOENT | |||||
* 2. If event does exists, it's enabled/disabled state is preserved | |||||
* but fflags, data and udata fields are overwritten. So we can not | |||||
* set socket lowats and store user's context pointer in udata. | |||||
*/ | |||||
if (epoll_register_kevent(td, epfp, fd, EVFILT_READ, 0) != ENOENT || | |||||
epoll_register_kevent(td, epfp, fd, EVFILT_WRITE, 0) != ENOENT) | |||||
return (1); | |||||
return (0); | |||||
} | |||||
static int | |||||
epoll_delete_all_events(struct thread *td, struct file *epfp, int fd) | epoll_delete_all_events(struct thread *td, struct file *epfp, int fd) | ||||
{ | { | ||||
int error1, error2; | int error1, error2; | ||||
error1 = epoll_delete_event(td, epfp, fd, EVFILT_READ); | error1 = epoll_register_kevent(td, epfp, fd, EVFILT_READ, EV_DELETE); | ||||
error2 = epoll_delete_event(td, epfp, fd, EVFILT_WRITE); | error2 = epoll_register_kevent(td, epfp, fd, EVFILT_WRITE, EV_DELETE); | ||||
/* return 0 if at least one result positive */ | /* return 0 if at least one result positive */ | ||||
return (error1 == 0 ? 0 : error2); | return (error1 == 0 ? 0 : error2); | ||||
} | } | ||||
static int | static int | ||||
eventfd_create(struct thread *td, uint32_t initval, int flags) | eventfd_create(struct thread *td, uint32_t initval, int flags) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 647 Lines • Show Last 20 Lines |