--- a/sys/platform/vkernel64/platform/kqueue.c +++ b/sys/platform/vkernel64/platform/kqueue.c @@ -55,10 +55,13 @@ struct kqueue_info { void (*func)(void *, struct intrframe *); void *data; int fd; + int inuse; /* in-flight dispatch fence */ }; #include +#include + static int KQueueFd = -1; /* @@ -114,6 +117,7 @@ kqueue_add(int fd, void (*func)(void *, s info = kmalloc(sizeof(*info), M_DEVBUF, M_ZERO|M_INTWAIT); info->func = func; info->data = data; + info->inuse = 0; info->fd = fd; EV_SET(&kev, fd, EVFILT_READ, EV_ADD|EV_ENABLE|EV_CLEAR, 0, 0, info); if (kevent(KQueueFd, &kev, 1, NULL, 0, &ts) < 0) @@ -157,17 +161,38 @@ kqueue_add_timer(void (*func)(void *, str void kqueue_del(struct kqueue_info *info) { struct timespec ts = { 0, 0 }; struct kevent kev; + int save_errno; KKASSERT(info->fd >= 0); EV_SET(&kev, info->fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); - if (kevent(KQueueFd, &kev, 1, NULL, 0, &ts) < 0) - panic("kevent: failed to delete descriptor %d", info->fd); - info->fd = -1; - kfree(info, M_DEVBUF); + if (kevent(KQueueFd, &kev, 1, NULL, 0, &ts) < 0) { + /* + * ENOENT is expected and must not be fatal: the host + * auto-detaches an EVFILT_READ knote when the watched fd's + * file description is closed, so close(fd)-before-kqueue_del() + * legitimately finds no knote. Anything else is still fatal. + */ + save_errno = errno; + if (save_errno != ENOENT) + panic("kqueue_del: cannot delete descriptor %d: %s", + info->fd, strerror(save_errno)); + } + /* + * Fence against a concurrent kqueue_intr() on another vCPU host + * thread which already copied this event out (udata == info) and + * has not dispatched yet. info->fd = -1 makes the dispatcher skip + * the call; the wait drains in-flight dispatches before the free. + * + * NOTE: a fully airtight cross-thread serialization needs a lock + * shared with kqueue_intr(); keep this simple fence unless + * kqueue_del gains multi-CPU callers. + */ + info->fd = -1; + while (info->inuse) + usleep(1000); + kfree(info, M_DEVBUF); } /* @@ -187,6 +212,7 @@ kqueue_intr(struct intrframe *frame) n = kevent(KQueueFd, NULL, 0, kevary, 8, &ts); for (i = 0; i < n; ++i) { struct kevent *kev = &kevary[i]; struct kqueue_info *info = (void *)kev->udata; + atomic_add_int(&info->inuse, 1); - info->func(info->data, frame); + if (info->fd != -1) + info->func(info->data, frame); + atomic_add_int(&info->inuse, -1); } } while (n == 8); }