Page MenuHomeFreeBSD

D57038.diff
No OneTemporary

D57038.diff

diff --git a/etc/mtree/BSD.include.dist b/etc/mtree/BSD.include.dist
--- a/etc/mtree/BSD.include.dist
+++ b/etc/mtree/BSD.include.dist
@@ -235,6 +235,8 @@
mpilib
..
..
+ ntsync
+ ..
nvme
..
nvmf
diff --git a/include/Makefile b/include/Makefile
--- a/include/Makefile
+++ b/include/Makefile
@@ -51,7 +51,7 @@
LSUBDIRS= dev/acpica dev/agp dev/ciss dev/filemon dev/firewire \
dev/hwpmc dev/hyperv \
- dev/ic dev/iicbus dev/io dev/mfi dev/mmc \
+ dev/ic dev/iicbus dev/io dev/mfi dev/mmc dev/ntsync \
dev/ofw dev/pbio dev/pci ${_dev_powermac_nvram} dev/ppbus dev/pwm \
dev/smbus dev/speaker dev/tcp_log dev/veriexec dev/vkbd dev/wg \
fs/devfs fs/fdescfs fs/msdosfs fs/nfs fs/nullfs \
diff --git a/sys/dev/ntsync/linux_ntsync.h b/sys/dev/ntsync/linux_ntsync.h
new file mode 100644
--- /dev/null
+++ b/sys/dev/ntsync/linux_ntsync.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Kernel support for NT synchronization primitive emulation
+ *
+ * Copyright (C) 2021-2022 Elizabeth Figura <zfigura@codeweavers.com>
+ */
+
+#ifndef __LINUX_NTSYNC_H
+#define __LINUX_NTSYNC_H
+
+#include <sys/types.h>
+
+typedef uint32_t __u32;
+typedef uint64_t __u64;
+
+struct linux_ntsync_sem_args {
+ __u32 count;
+ __u32 max;
+};
+
+struct linux_ntsync_mutex_args {
+ __u32 owner;
+ __u32 count;
+};
+
+struct linux_ntsync_event_args {
+ __u32 manual;
+ __u32 signaled;
+};
+
+#define LINUX_NTSYNC_WAIT_REALTIME 0x1
+
+struct linux_ntsync_wait_args {
+ __u64 timeout;
+ __u64 objs;
+ __u32 count;
+ __u32 index;
+ __u32 flags;
+ __u32 owner;
+ __u32 alert;
+ __u32 pad;
+};
+
+#define LNTSYNC_IOC_CREATE_SEM 0x40084e80
+#define LNTSYNC_IOC_WAIT_ANY 0xc0284e82
+#define LNTSYNC_IOC_WAIT_ALL 0xc0284e83
+#define LNTSYNC_IOC_CREATE_MUTEX 0x40084e84
+#define LNTSYNC_IOC_CREATE_EVENT 0x40084e87
+#define LNTSYNC_IOC_SEM_RELEASE 0xc0044e81
+#define LNTSYNC_IOC_MUTEX_UNLOCK 0xc0084e85
+#define LNTSYNC_IOC_MUTEX_KILL 0x40044e86
+#define LNTSYNC_IOC_EVENT_SET 0x80044e88
+#define LNTSYNC_IOC_EVENT_RESET 0x80044e89
+#define LNTSYNC_IOC_EVENT_PULSE 0x80044e8a
+#define LNTSYNC_IOC_SEM_READ 0x80084e8b
+#define LNTSYNC_IOC_MUTEX_READ 0x80084e8c
+#define LNTSYNC_IOC_EVENT_READ 0x80084e8d
+
+#define LNTSYNC_IOCTL_MIN 0x4e80
+#define LNTSYNC_IOCTL_MAX 0x4eff
+
+#endif
diff --git a/sys/dev/ntsync/linux_ntsync.c b/sys/dev/ntsync/linux_ntsync.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/ntsync/linux_ntsync.c
@@ -0,0 +1,301 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2026 The FreeBSD Foundation
+ *
+ * This software was developed by Konstantin Belousov <kib@FreeBSD.org>
+ * under sponsorship from the FreeBSD Foundation.
+ */
+
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/filedesc.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <dev/ntsync/ntsyncvar.h>
+
+#include <machine/../linux/linux.h>
+#include <machine/../linux/linux_proto.h>
+#include <compat/linux/linux_common.h>
+#include <compat/linux/linux_ioctl.h>
+#include <dev/ntsync/linux_ntsync.h>
+
+MODULE_DEPEND(linux_ntsync, linux, 1, 1, 1);
+MODULE_DEPEND(linux_ntsync, ntsync, 1, 1, 1);
+
+static linux_ioctl_function_t linux_ntsync_ioctl;
+static struct linux_ioctl_handler linux_ntsync_handler = {linux_ntsync_ioctl,
+ LNTSYNC_IOCTL_MIN, LNTSYNC_IOCTL_MAX};
+
+static int
+linux_ntsync_modevent(module_t mod __unused, int type, void *data __unused)
+{
+ int error;
+
+ error = 0;
+ switch (type) {
+ case MOD_LOAD:
+ error = linux_ioctl_register_handler(&linux_ntsync_handler);
+ if (error != 0) {
+ printf("linux_ntsync: cannot register ioctl handler, "
+ "error %d\n", error);
+ } else if (bootverbose)
+ printf("linux_ntsync\n");
+ break;
+
+ case MOD_UNLOAD:
+ linux_ioctl_unregister_handler(&linux_ntsync_handler);
+ break;
+
+ case MOD_SHUTDOWN:
+ break;
+
+ default:
+ error = EOPNOTSUPP;
+ }
+
+ return (error);
+}
+
+DEV_MODULE(linux_ntsync, linux_ntsync_modevent, NULL);
+MODULE_VERSION(linux_ntsync, 1);
+
+/* XXXKIB no translation of structs */
+static void
+ntsync_lsa_to_sa(struct ntsync_sem_args *sa,
+ const struct linux_ntsync_sem_args *lsa)
+{
+ memcpy(sa, lsa, sizeof(*sa));
+}
+
+static void
+ntsync_sa_to_lsa(struct linux_ntsync_sem_args *lsa,
+ const struct ntsync_sem_args *sa)
+{
+ memcpy(lsa, sa, sizeof(*lsa));
+}
+
+static void
+ntsync_lma_to_ma(struct ntsync_mutex_args *ma,
+ const struct linux_ntsync_mutex_args *lma)
+{
+ memcpy(ma, lma, sizeof(*ma));
+}
+
+static void
+ntsync_ma_to_lma(struct linux_ntsync_mutex_args *ma,
+ const struct ntsync_mutex_args *lma)
+{
+ memcpy(ma, lma, sizeof(*ma));
+}
+
+static void
+ntsync_lea_to_ea(struct ntsync_event_args *ea,
+ const struct linux_ntsync_event_args *lea)
+{
+ memcpy(ea, lea, sizeof(*ea));
+}
+
+static void
+ntsync_ea_to_lea(struct linux_ntsync_event_args *lea,
+ const struct ntsync_event_args *ea)
+{
+ memcpy(lea, ea, sizeof(*lea));
+}
+
+static void
+ntsync_lwa_to_wa(struct ntsync_wait_args *wa,
+ const struct linux_ntsync_wait_args *lwa)
+{
+ memcpy(wa, lwa, sizeof(*wa));
+}
+
+static void
+ntsync_wa_to_lwa(struct linux_ntsync_wait_args *lwa,
+ const struct ntsync_wait_args *wa)
+{
+ memcpy(lwa, wa, sizeof(*lwa));
+}
+
+static int
+linux_ntsync_cdev_ioctl(struct thread *td, u_long cmd, void *data,
+ struct file *fp)
+{
+ struct cdev *dev;
+ struct cdevsw *dsw;
+ struct vnode *vp;
+ struct file *fpop;
+ int error, ref;
+
+ /* XXXKIB FREAD | FWRITE */
+ if (fp->f_type != DTYPE_VNODE)
+ return (error = ENOIOCTL);
+
+ vp = fp->f_vnode;
+ if (vp->v_type != VCHR)
+ return (ENOIOCTL);
+ dev = vp->v_rdev;
+ dsw = dev_refthread(dev, &ref);
+ if (dsw == NULL)
+ return (ENXIO);
+ if (dsw != &ntsync_cdevsw) {
+ error = ENOIOCTL;
+ } else {
+ fpop = td->td_fpop;
+ td->td_fpop = fp;
+ error = dsw->d_ioctl(dev, cmd, data, 0, td);
+ td->td_fpop = fpop;
+ }
+ dev_relthread(dev, ref);
+ return (error);
+}
+
+static int
+linux_ntsync_ioctl(struct thread *td, struct linux_ioctl_args *args)
+{
+ struct file *fp;
+ void *data;
+ struct linux_ntsync_sem_args lsa;
+ struct linux_ntsync_mutex_args lma;
+ struct linux_ntsync_event_args lea;
+ struct linux_ntsync_wait_args lwa;
+ struct ntsync_sem_args sa;
+ struct ntsync_mutex_args ma;
+ struct ntsync_event_args ea;
+ struct ntsync_wait_args wa;
+ uint32_t val;
+ int error, error1, lcmd;
+
+ lcmd = args->cmd;
+ data = (void *)args->arg;
+
+ error = fget_cap(td, args->fd, &cap_no_rights, NULL, &fp, NULL);
+ if (error != 0)
+ goto out;
+
+ switch (lcmd) {
+ case LNTSYNC_IOC_CREATE_SEM:
+ error = copyin(data, &lsa, sizeof(lsa));
+ ntsync_lsa_to_sa(&sa, &lsa);
+ if (error == 0) {
+ error = linux_ntsync_cdev_ioctl(td,
+ NTSYNC_IOC_CREATE_SEM, &sa, fp);
+ }
+ break;
+ case LNTSYNC_IOC_CREATE_MUTEX:
+ error = copyin(data, &lma, sizeof(lma));
+ ntsync_lma_to_ma(&ma, &lma);
+ if (error == 0) {
+ error = linux_ntsync_cdev_ioctl(td,
+ NTSYNC_IOC_CREATE_MUTEX, &ma, fp);
+ }
+ break;
+ case LNTSYNC_IOC_CREATE_EVENT:
+ error = copyin(data, &lea, sizeof(lea));
+ ntsync_lea_to_ea(&ea, &lea);
+ if (error == 0) {
+ error = linux_ntsync_cdev_ioctl(td,
+ NTSYNC_IOC_CREATE_EVENT, &ea, fp);
+ }
+ break;
+ case LNTSYNC_IOC_WAIT_ANY:
+ error = copyin(data, &lwa, sizeof(lwa));
+ ntsync_lwa_to_wa(&wa, &lwa);
+ if (error == 0) {
+ error = linux_ntsync_cdev_ioctl(td,
+ NTSYNC_IOC_WAIT_ANY, &wa, fp);
+ if (error == 0 || error == EOWNERDEAD) {
+ ntsync_wa_to_lwa(&lwa, &wa);
+ error1 = copyout(&lwa, data, sizeof(lwa));
+ if (error == 0)
+ error = error1;
+ }
+ }
+ break;
+ case LNTSYNC_IOC_WAIT_ALL:
+ error = copyin(data, &lwa, sizeof(lwa));
+ ntsync_lwa_to_wa(&wa, &lwa);
+ if (error == 0) {
+ error = linux_ntsync_cdev_ioctl(td,
+ NTSYNC_IOC_WAIT_ALL, &wa, fp);
+ if (error == 0 || error == EOWNERDEAD) {
+ ntsync_wa_to_lwa(&lwa, &wa);
+ error1 = copyout(&lwa, data, sizeof(lwa));
+ if (error == 0)
+ error = error1;
+ }
+ }
+ break;
+ case LNTSYNC_IOC_SEM_RELEASE:
+ error = copyin(data, &val, sizeof(val));
+ if (error == 0) {
+ error = ntsync_sem_release(td, fp, &val);
+ if (error == 0)
+ error = copyout(&val, data, sizeof(val));
+ }
+ break;
+ case LNTSYNC_IOC_SEM_READ:
+ error = ntsync_sem_read(td, fp, &sa);
+ if (error == 0) {
+ ntsync_sa_to_lsa(&lsa, &sa);
+ error = copyout(&lsa, data, sizeof(lsa));
+ }
+ break;
+ case LNTSYNC_IOC_MUTEX_UNLOCK:
+ error = copyin(data, &lma, sizeof(lma));
+ ntsync_lma_to_ma(&ma, &lma);
+ if (error == 0) {
+ error = ntsync_mutex_unlock(td, fp, &ma);
+ if (error == 0) {
+ ntsync_ma_to_lma(&lma, &ma);
+ error = copyout(&lma, data, sizeof(lma));
+ }
+ }
+ break;
+ case LNTSYNC_IOC_MUTEX_KILL:
+ error = copyin(data, &val, sizeof(val));
+ if (error == 0)
+ error = ntsync_mutex_kill(td, fp, val);
+ break;
+ case LNTSYNC_IOC_MUTEX_READ:
+ error = ntsync_mutex_read(td, fp, &ma);
+ if (error == 0 || error == EOWNERDEAD) {
+ ntsync_ma_to_lma(&lma, &ma);
+ error1 = copyout(&lma, data, sizeof(lma));
+ if (error == 0)
+ error = error1;
+ }
+ break;
+ case LNTSYNC_IOC_EVENT_SET:
+ error = ntsync_event_set(td, fp, &val);
+ if (error == 0)
+ error = copyout(&val, data, sizeof(val));
+ break;
+ case LNTSYNC_IOC_EVENT_RESET:
+ error = ntsync_event_reset(td, fp, &val);
+ if (error == 0)
+ error = copyout(&val, data, sizeof(val));
+ break;
+ case LNTSYNC_IOC_EVENT_PULSE:
+ error = ntsync_event_pulse(td, fp, &val);
+ if (error == 0)
+ error = copyout(&val, data, sizeof(val));
+ break;
+ case LNTSYNC_IOC_EVENT_READ:
+ error = ntsync_event_read(td, fp, &ea);
+ if (error == 0) {
+ ntsync_ea_to_lea(&lea, &ea);
+ error = copyout(&lea, data, sizeof(lea));
+ }
+ break;
+ default:
+ error = ENOTTY;
+ break;
+ }
+ fdrop(fp, td);
+out:
+ return (error);
+}
diff --git a/sys/dev/ntsync/ntsync.h b/sys/dev/ntsync/ntsync.h
new file mode 100644
--- /dev/null
+++ b/sys/dev/ntsync/ntsync.h
@@ -0,0 +1,66 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2026 The FreeBSD Foundation
+ *
+ * This software was developed by Konstantin Belousov <kib@FreeBSD.org>
+ * under sponsorship from the FreeBSD Foundation.
+ */
+
+#ifndef __DEV_NTSYNC_H__
+#define __DEV_NTSYNC_H__
+
+#include <sys/types.h>
+#include <sys/ioccom.h>
+
+struct ntsync_sem_args {
+ uint32_t count;
+ uint32_t max;
+};
+
+struct ntsync_mutex_args {
+ uint32_t owner;
+ uint32_t count;
+};
+
+struct ntsync_event_args {
+ uint32_t manual;
+ uint32_t signaled;
+};
+
+struct ntsync_wait_args {
+ uint64_t timeout;
+ uint64_t objs;
+ uint32_t count;
+ uint32_t index;
+ uint32_t flags;
+ uint32_t owner;
+ uint32_t alert;
+ uint32_t pad;
+};
+
+#define NTSYNC_WAIT_REALTIME 0x00000001
+
+#define NTSYNC_MAX_WAIT_COUNT 64
+
+/*
+ * 'sp' means that the ioctl is special, it might return both error
+ * and copy out parameters. See ntsync_ioctl_copyout().
+ */
+
+#define NTSYNC_IOC_CREATE_SEM _IOW('n', 1, struct ntsync_sem_args)
+#define NTSYNC_IOC_CREATE_MUTEX _IOW('n', 2, struct ntsync_mutex_args)
+#define NTSYNC_IOC_CREATE_EVENT _IOW('n', 3, struct ntsync_event_args)
+#define NTSYNC_IOC_SEM_RELEASE _IOWR('n', 4, uint32_t)
+#define NTSYNC_IOC_MUTEX_UNLOCK _IOWR('n', 5, struct ntsync_mutex_args)
+#define NTSYNC_IOC_EVENT_SET _IOR('n', 6, uint32_t)
+#define NTSYNC_IOC_EVENT_RESET _IOR('n', 7, uint32_t)
+#define NTSYNC_IOC_EVENT_PULSE _IOR('n', 8, uint32_t)
+#define NTSYNC_IOC_SEM_READ _IOR('n', 9, struct ntsync_sem_args)
+#define NTSYNC_IOC_MUTEX_READ _IO('n', 10) /* sp */
+#define NTSYNC_IOC_EVENT_READ _IOR('n', 11, struct ntsync_event_args)
+#define NTSYNC_IOC_MUTEX_KILL _IOW('n', 12, uint32_t)
+#define NTSYNC_IOC_WAIT_ANY _IOW('n', 13, struct ntsync_wait_args) /* sp */
+#define NTSYNC_IOC_WAIT_ALL _IOW('n', 14, struct ntsync_wait_args) /* sp */
+
+#endif
diff --git a/sys/dev/ntsync/ntsync.c b/sys/dev/ntsync/ntsync.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/ntsync/ntsync.c
@@ -0,0 +1,1414 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2026 The FreeBSD Foundation
+ *
+ * This software was developed by Konstantin Belousov <kib@FreeBSD.org>
+ * under sponsorship from the FreeBSD Foundation.
+ */
+
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/file.h>
+#include <sys/filedesc.h>
+#include <sys/kernel.h>
+#include <sys/limits.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/proc.h>
+#include <sys/stat.h>
+#include <sys/sysent.h>
+#include <sys/user.h>
+#include <dev/ntsync/ntsyncvar.h>
+
+static struct cdev *ntsync_cdev;
+MALLOC_DEFINE(M_NTSYNC, "ntsync", "ntsync");
+
+static void ntsync_free_priv(struct ntsync_priv *priv);
+
+/*
+ * Returning error from an ioctl handler prevents the generic ioctl
+ * code from copying out the result. Use direct access to ioctl(2)
+ * args to get the parameters block pointer to implement Linux
+ * semantic of both returning an error and updating the parameters
+ * block.
+ */
+static int
+ntsync_ioctl_copyout(struct thread *td, const void *ptr, size_t sz)
+{
+ void *uptr;
+
+ if (SV_PROC_ABI(td->td_proc) != SV_ABI_FREEBSD)
+ return (0);
+ uptr = (void *)(uintptr_t)td->td_sa.args[2];
+ return (copyout(ptr, uptr, sz));
+}
+
+static bool
+ntsync_wait_any(struct ntsync_wait_state *state)
+{
+ struct ntsync_obj *obj;
+ int i;
+
+ MPASS(state->any);
+ NTSYNC_PRIV_ASSERT(state->owner);
+
+ for (i = 0; i < state->obj_count; i++) {
+ obj = state->objs[i];
+ if (obj->is_signaled(obj, state, i)) {
+ state->index = i;
+ obj->consume(obj, state, state->index);
+ return (true);
+ }
+ }
+ return (false);
+}
+
+static bool
+ntsync_wait_all_prepare(struct ntsync_wait_state *state, bool *stop)
+{
+ struct ntsync_obj *obj;
+ int alerti, i;
+ bool first;
+
+ MPASS(state->all);
+ MPASS(state->error == 0);
+ MPASS(!*stop);
+ NTSYNC_PRIV_ASSERT(state->owner);
+
+ alerti = state->alert_event == NULL ? 0 : 1;
+ first = true;
+
+ for (i = 0; i < state->obj_count - alerti; i++) {
+ obj = state->objs[i];
+ if (!obj->prepare(obj, state, i, stop))
+ return (false);
+ if (*stop) {
+ MPASS(state->error != 0);
+ return (false);
+ }
+ MPASS (state->error == 0);
+ if (first) {
+ first = false;
+ state->index = i;
+ }
+ }
+ return (true);
+}
+
+static void
+ntsync_wait_all_commit(struct ntsync_wait_state *state)
+{
+ struct ntsync_obj *obj;
+ int i, alerti;
+
+ MPASS(state->all);
+ NTSYNC_PRIV_ASSERT(state->owner);
+ alerti = state->alert_event == NULL ? 0 : 1;
+
+ for (i = 0; i < state->obj_count - alerti; i++) {
+ obj = state->objs[i];
+ obj->commit(obj, state, i);
+ }
+}
+
+static void
+ntsync_wait_link_waiters(struct ntsync_wait_state *state)
+{
+ struct ntsync_obj *obj;
+ struct ntsync_obj_waiter *waiter;
+ int i;
+
+ NTSYNC_PRIV_ASSERT(state->owner);
+
+ for (i = 0; i < state->obj_count; i++) {
+ obj = state->objs[i];
+ waiter = &state->waiters[i];
+ waiter->state = state;
+ TAILQ_INSERT_TAIL(&obj->waiters, waiter, link);
+ }
+}
+
+static void
+ntsync_wait_unlink_waiters(struct ntsync_wait_state *state)
+{
+ struct ntsync_obj *obj;
+ struct ntsync_obj_waiter *waiter;
+ int i;
+
+ NTSYNC_PRIV_ASSERT(state->owner);
+
+ for (i = 0; i < state->obj_count; i++) {
+ obj = state->objs[i];
+ waiter = &state->waiters[i];
+ TAILQ_REMOVE(&obj->waiters, waiter, link);
+ }
+}
+
+static void
+ntsync_wait_post_commit(struct ntsync_wait_state *state)
+{
+ struct ntsync_obj *obj;
+ int alerti, i;
+
+ NTSYNC_PRIV_ASSERT(state->owner);
+
+ alerti = state->alert_event == NULL ? 0 : 1;
+ for (i = 0; i < state->obj_count - alerti; i++) {
+ obj = state->objs[i];
+ obj->post_commit(obj, state, i);
+ }
+}
+
+static void
+ntsync_wait_check_ready(struct ntsync_wait_state *state)
+{
+ struct ntsync_obj *ae;
+ int index;
+ bool stop;
+
+ NTSYNC_PRIV_ASSERT(state->owner);
+
+ if (state->ready)
+ return;
+
+ if (state->all) {
+ stop = false;
+ if (ntsync_wait_all_prepare(state, &stop)) {
+ MPASS(!stop);
+ ntsync_wait_all_commit(state);
+ state->ready = true;
+ ntsync_wait_post_commit(state);
+ } else if (stop) {
+ /* skip */
+ } else if (state->alert_event != NULL) {
+ ae = &state->alert_event->obj;
+ index = state->obj_count - 1;
+ if (ae->is_signaled(ae, state, index)) {
+ state->index = index;
+ ae->consume(ae, state, index);
+ ae->post_commit(ae, state, index);
+ state->ready = true;
+ }
+ }
+ } else { /* state->any */
+ if (ntsync_wait_any(state))
+ state->ready = true;
+ }
+}
+
+/*
+ * Perform the wait. Errors returned through state->error still
+ * result in the copyout of the ntsync_wait_args after the wait, while
+ * errors returned as the function result do not.
+ */
+static int
+ntsync_wait_locked(struct ntsync_wait_state *state, struct thread *td)
+{
+ int error;
+
+ NTSYNC_PRIV_ASSERT(state->owner);
+
+ for (;;) {
+ ntsync_wait_check_ready(state);
+ if (state->ready)
+ break;
+ error = msleep_sbt(state, &state->owner->lock,
+ PCATCH, "ntsync", state->sb, 0,
+ C_ABSOLUTE /* | C_HARDCLOCK XXXKIB */);
+
+ /*
+ * Check state->ready before checking error from
+ * msleep(). If there was a wake up that set the
+ * readiness before us receiving a signal or timeout,
+ * the objects states are modified to reflect wakeup.
+ * Due to this, ready should result in normal return.
+ */
+ if (state->ready) {
+ error = 0;
+ break;
+ }
+
+ if (error != 0) {
+ if (error == EAGAIN)
+ error = ETIMEDOUT;
+ break;
+ }
+ }
+ return (error);
+}
+
+static int
+ntsync_wait(struct ntsync_wait_state *state, struct thread *td)
+{
+ int error;
+
+ NTSYNC_PRIV_LOCK(state->owner);
+ ntsync_wait_link_waiters(state);
+ error = ntsync_wait_locked(state, td);
+ ntsync_wait_unlink_waiters(state);
+ NTSYNC_PRIV_UNLOCK(state->owner);
+ return (error);
+}
+
+static void
+ntsync_wakeup_waiters(struct ntsync_obj *obj)
+{
+ struct ntsync_obj_waiter *w;
+
+ NTSYNC_PRIV_ASSERT(obj->owner);
+
+ TAILQ_FOREACH(w, &obj->waiters, link) {
+ ntsync_wait_check_ready(w->state);
+ if (w->state->ready)
+ wakeup(w->state);
+ }
+}
+
+static int
+ntsync_create_obj(struct ntsync_obj *obj, struct fileops *fops,
+ struct ntsync_priv *priv, struct thread *td)
+{
+ struct file *fp;
+ int error, fd;
+
+ error = falloc_noinstall(td, &fp);
+ if (error != 0)
+ return (error);
+
+ /*
+ * The priv fd cannot be closed during object creation since
+ * it is fget-ed around ioctl.
+ */
+ obj->owner = priv;
+
+ TAILQ_INIT(&obj->waiters);
+ NTSYNC_PRIV_LOCK(priv);
+ MPASS(!priv->closed);
+ if (priv->objs_cnt == UINT_MAX) {
+ NTSYNC_PRIV_UNLOCK(priv);
+ fdrop(fp, td);
+ return (EMFILE);
+ }
+ priv->objs_cnt++;
+ NTSYNC_PRIV_UNLOCK(priv);
+
+ finit(fp, FREAD | FWRITE, DTYPE_NTSYNC, obj, fops);
+ error = finstall(td, fp, &fd, 0, NULL);
+ if (error != 0) {
+ NTSYNC_PRIV_LOCK(priv);
+ MPASS(priv->objs_cnt > 0);
+ priv->objs_cnt--;
+ NTSYNC_PRIV_UNLOCK(priv);
+ } else {
+ td->td_retval[0] = fd;
+ }
+ fdrop(fp, td);
+ return (error);
+}
+
+static void
+ntsync_close_obj(struct ntsync_obj *obj, struct thread *td)
+{
+ struct ntsync_priv *priv;
+
+ priv = obj->owner;
+ NTSYNC_PRIV_LOCK(priv);
+ MPASS(priv->objs_cnt > 0);
+ MPASS(TAILQ_EMPTY(&obj->waiters));
+ priv->objs_cnt--;
+ NTSYNC_PRIV_UNLOCK(priv);
+ ntsync_free_priv(priv);
+}
+
+static bool
+ntsync_sem_is_signaled(struct ntsync_obj *obj, struct ntsync_wait_state *state,
+ int index)
+{
+ struct ntsync_obj_sem *sem;
+
+ MPASS(obj->type == NTSYNC_OBJ_SEM);
+ NTSYNC_PRIV_ASSERT(obj->owner);
+ sem = OBJ_TO_SEM(obj);
+ return (sem->a.count != 0);
+}
+
+static void
+ntsync_sem_consume(struct ntsync_obj *obj, struct ntsync_wait_state *state,
+ int index)
+{
+ struct ntsync_obj_sem *sem;
+
+ MPASS(obj->type == NTSYNC_OBJ_SEM);
+ NTSYNC_PRIV_ASSERT(obj->owner);
+ sem = OBJ_TO_SEM(obj);
+ MPASS(sem->a.count != 0);
+ sem->a.count--;
+}
+
+static bool
+ntsync_sem_prepare(struct ntsync_obj *obj, struct ntsync_wait_state *state,
+ int index, bool *stop)
+{
+ struct ntsync_obj_sem *sem;
+
+ MPASS(obj->type == NTSYNC_OBJ_SEM);
+ NTSYNC_PRIV_ASSERT(obj->owner);
+ sem = OBJ_TO_SEM(obj);
+ if (sem->a.count == 0)
+ return (false);
+ sem->a1 = sem->a;
+ sem->a1.count--;
+ return (true);
+}
+
+static void
+ntsync_sem_commit(struct ntsync_obj *obj, struct ntsync_wait_state *state,
+ int index)
+{
+ struct ntsync_obj_sem *sem;
+
+ MPASS(obj->type == NTSYNC_OBJ_SEM);
+ NTSYNC_PRIV_ASSERT(obj->owner);
+ sem = OBJ_TO_SEM(obj);
+ sem->a = sem->a1;
+}
+
+static void
+ntsync_sem_post_commit(struct ntsync_obj *obj, struct ntsync_wait_state *state,
+ int index)
+{
+}
+
+static int
+ntsync_sem_close(struct file *fp, struct thread *td)
+{
+ struct ntsync_obj_sem *sem;
+
+ sem = fp->f_data;
+ ntsync_close_obj(&sem->obj, td);
+ free(sem, M_NTSYNC);
+ return (0);
+}
+
+int
+ntsync_sem_release(struct thread *td, struct file *fp, uint32_t *val)
+{
+ struct ntsync_obj *obj;
+ struct ntsync_obj_sem *sem;
+ struct ntsync_priv *priv;
+ uint32_t prev;
+ int error;
+
+ obj = fp->f_data;
+ if (obj->type != NTSYNC_OBJ_SEM)
+ return (EINVAL);
+ sem = OBJ_TO_SEM(obj);
+ priv = obj->owner;
+ error = 0;
+
+ NTSYNC_PRIV_LOCK(priv);
+ if (sem->a.count + *val < sem->a.count ||
+ sem->a.count + *val > sem->a.max) {
+ error = EOVERFLOW;
+ } else {
+ prev = sem->a.count;
+ sem->a.count += *val;
+ if (sem->a.count != 0)
+ ntsync_wakeup_waiters(obj);
+ *val = prev;
+ }
+ NTSYNC_PRIV_UNLOCK(priv);
+ return (error);
+}
+
+int
+ntsync_sem_read(struct thread *td, struct file *fp, struct ntsync_sem_args *a)
+{
+ struct ntsync_obj *obj;
+ struct ntsync_obj_sem *sem;
+ struct ntsync_priv *priv;
+
+ obj = fp->f_data;
+ if (obj->type != NTSYNC_OBJ_SEM)
+ return (EINVAL);
+ sem = OBJ_TO_SEM(obj);
+ priv = obj->owner;
+ NTSYNC_PRIV_LOCK(priv);
+ *a = sem->a;
+ NTSYNC_PRIV_UNLOCK(priv);
+ return (0);
+}
+
+static int
+ntsync_sem_ioctl(struct file *fp, u_long com, void *data,
+ struct ucred *active_cred, struct thread *td)
+{
+ int error;
+
+ switch (com) {
+ case NTSYNC_IOC_SEM_RELEASE:
+ error = ntsync_sem_release(td, fp, data);
+ break;
+ case NTSYNC_IOC_SEM_READ:
+ error = ntsync_sem_read(td, fp, data);
+ break;
+ default:
+ error = ENOTTY;
+ break;
+ }
+ return (error);
+}
+
+static int
+ntsync_sem_stat(struct file *fp, struct stat *sbp, struct ucred *cred)
+{
+ struct ntsync_obj *obj;
+ struct ntsync_obj_sem *sem;
+
+ MPASS(fp->f_type == DTYPE_NTSYNC);
+ obj = fp->f_data;
+ MPASS(obj->type == NTSYNC_OBJ_SEM);
+ sem = OBJ_TO_SEM(obj);
+
+ memset(sbp, 0, sizeof(*sbp));
+ sbp->st_mode = S_IFREG /* XXXKIB */ | S_IRUSR | S_IWUSR;
+ NTSYNC_PRIV_LOCK(obj->owner);
+ sbp->st_size = sem->a.max;
+ sbp->st_nlink = sem->a.count;
+ NTSYNC_PRIV_UNLOCK(obj->owner);
+ return (0);
+}
+
+static int
+ntsync_sem_fill_kinfo(struct file *fp, struct kinfo_file *kif,
+ struct filedesc *fdp)
+{
+ struct ntsync_obj *obj;
+ struct ntsync_obj_sem *sem;
+
+ MPASS(fp->f_type == DTYPE_NTSYNC);
+ obj = fp->f_data;
+ MPASS(obj->type == NTSYNC_OBJ_SEM);
+ sem = OBJ_TO_SEM(obj);
+
+ kif->kf_type = KF_TYPE_NTSYNC;
+ kif->kf_un.kf_ntsync.kf_ntsync_type = KF_NTSYNC_TYPE_SEM;
+ kif->kf_un.kf_ntsync.kf_ntsync_dev = (uintptr_t)obj->owner;
+ kif->kf_un.kf_ntsync.kf_ntsync_un.kf_ntsync_sem.count = sem->a.count;
+ kif->kf_un.kf_ntsync.kf_ntsync_un.kf_ntsync_sem.max = sem->a.max;
+ return (0);
+}
+
+struct fileops ntsync_sem_fops = {
+ .fo_read = invfo_rdwr,
+ .fo_write = invfo_rdwr,
+ .fo_truncate = invfo_truncate,
+ .fo_ioctl = ntsync_sem_ioctl,
+ .fo_poll = invfo_poll,
+ .fo_kqfilter = invfo_kqfilter,
+ .fo_stat = ntsync_sem_stat,
+ .fo_close = ntsync_sem_close,
+ .fo_chmod = invfo_chmod,
+ .fo_chown = invfo_chown,
+ .fo_sendfile = invfo_sendfile,
+ .fo_fill_kinfo = ntsync_sem_fill_kinfo,
+ .fo_flags = DFLAG_PASSABLE,
+};
+
+static int
+ntsync_create_sem(struct ntsync_sem_args *args, struct ntsync_priv *priv,
+ struct thread *td)
+{
+ struct ntsync_obj_sem *sem;
+ int error;
+
+ if (args->count > args->max)
+ return (EINVAL);
+
+ sem = malloc(sizeof(*sem), M_NTSYNC, M_WAITOK | M_ZERO);
+ sem->obj.type = NTSYNC_OBJ_SEM;
+ sem->obj.is_signaled = ntsync_sem_is_signaled;
+ sem->obj.consume = ntsync_sem_consume;
+ sem->obj.prepare = ntsync_sem_prepare;
+ sem->obj.commit = ntsync_sem_commit;
+ sem->obj.post_commit = ntsync_sem_post_commit;
+ sem->a = *args;
+
+ error = ntsync_create_obj(&sem->obj, &ntsync_sem_fops, priv, td);
+ if (error != 0)
+ free(sem, M_NTSYNC);
+
+ return (error);
+}
+
+static bool
+ntsync_mutex_can_lock(struct ntsync_obj_mutex *mutex, uint32_t nwa_owner)
+{
+ return (mutex->a.owner == 0 ||
+ (mutex->a.owner == nwa_owner && mutex->a.count < UINT32_MAX) ||
+ mutex->abandoned);
+}
+
+static bool
+ntsync_mutex_is_signaled(struct ntsync_obj *obj,
+ struct ntsync_wait_state *state, int index)
+{
+ struct ntsync_obj_mutex *mutex;
+
+ MPASS(obj->type == NTSYNC_OBJ_MUTEX);
+ NTSYNC_PRIV_ASSERT(obj->owner);
+ mutex = OBJ_TO_MUTEX(obj);
+ return (ntsync_mutex_can_lock(mutex, state->nwa->owner));
+}
+
+static void
+ntsync_mutex_consume(struct ntsync_obj *obj, struct ntsync_wait_state *state,
+ int index)
+{
+ struct ntsync_obj_mutex *mutex;
+
+ MPASS(obj->type == NTSYNC_OBJ_MUTEX);
+ NTSYNC_PRIV_ASSERT(obj->owner);
+ mutex = OBJ_TO_MUTEX(obj);
+ MPASS(ntsync_mutex_can_lock(mutex, state->nwa->owner));
+ if (state->nwa->owner == 0) {
+ state->error = EINVAL;
+ return;
+ }
+ if (mutex->a.owner == 0 || mutex->abandoned)
+ mutex->a.count = 1;
+ else
+ mutex->a.count++;
+ mutex->a.owner = state->nwa->owner;
+ if (mutex->abandoned && state->error == 0)
+ state->error = EOWNERDEAD;
+ mutex->abandoned = false;
+}
+
+static bool
+ntsync_mutex_prepare(struct ntsync_obj *obj, struct ntsync_wait_state *state,
+ int index, bool *stop)
+{
+ struct ntsync_obj_mutex *mutex;
+
+ MPASS(obj->type == NTSYNC_OBJ_MUTEX);
+ NTSYNC_PRIV_ASSERT(obj->owner);
+ mutex = OBJ_TO_MUTEX(obj);
+ if (!ntsync_mutex_can_lock(mutex, state->nwa->owner))
+ return (false);
+ if (state->nwa->owner == 0) {
+ state->error = EINVAL;
+ *stop = true;
+ return (false);
+ }
+ mutex->a1 = mutex->a;
+ if (mutex->a.owner == 0 || mutex->abandoned)
+ mutex->a1.count = 1;
+ else
+ mutex->a1.count++;
+ mutex->a1.owner = state->nwa->owner;
+ return (true);
+}
+
+static void
+ntsync_mutex_commit(struct ntsync_obj *obj, struct ntsync_wait_state *state,
+ int index)
+{
+ struct ntsync_obj_mutex *mutex;
+
+ MPASS(obj->type == NTSYNC_OBJ_MUTEX);
+ NTSYNC_PRIV_ASSERT(obj->owner);
+ mutex = OBJ_TO_MUTEX(obj);
+ mutex->a = mutex->a1;
+ if (mutex->abandoned)
+ state->error = EOWNERDEAD;
+ mutex->abandoned = false;
+}
+
+static void
+ntsync_mutex_post_commit(struct ntsync_obj *obj,
+ struct ntsync_wait_state *state, int index)
+{
+}
+
+static int
+ntsync_mutex_close(struct file *fp, struct thread *td)
+{
+ struct ntsync_obj_mutex *mutex;
+
+ mutex = fp->f_data;
+ ntsync_close_obj(&mutex->obj, td);
+ free(mutex, M_NTSYNC);
+ return (0);
+}
+
+int
+ntsync_mutex_unlock(struct thread *td, struct file *fp,
+ struct ntsync_mutex_args *a)
+{
+ struct ntsync_obj *obj;
+ struct ntsync_obj_mutex *mutex;
+ struct ntsync_priv *priv;
+ uint32_t prev;
+ int error;
+
+ obj = fp->f_data;
+ if (obj->type != NTSYNC_OBJ_MUTEX)
+ return (EINVAL);
+ mutex = OBJ_TO_MUTEX(obj);
+ priv = obj->owner;
+
+ NTSYNC_PRIV_LOCK(priv);
+ if (a->owner == 0) {
+ error = EINVAL;
+ } else if (a->owner != mutex->a.owner) {
+ error = EPERM;
+ } else {
+ error = 0;
+ prev = mutex->a.count;
+ MPASS(mutex->a.count > 0);
+ mutex->a.count--;
+ a->count = prev;
+ if (mutex->a.count == 0) {
+ mutex->a.owner = 0;
+ ntsync_wakeup_waiters(obj);
+ }
+ }
+ NTSYNC_PRIV_UNLOCK(priv);
+ return (error);
+}
+
+int
+ntsync_mutex_kill(struct thread *td, struct file *fp, uint32_t val)
+{
+ struct ntsync_obj *obj;
+ struct ntsync_obj_mutex *mutex;
+ struct ntsync_priv *priv;
+ int error;
+
+ obj = fp->f_data;
+ if (obj->type != NTSYNC_OBJ_MUTEX)
+ return (EINVAL);
+ mutex = OBJ_TO_MUTEX(obj);
+ priv = obj->owner;
+
+ NTSYNC_PRIV_LOCK(priv);
+ if (val == 0) {
+ error = EINVAL;
+ } else if (mutex->a.owner != val) {
+ error = EPERM;
+ } else {
+ error = 0;
+ mutex->a.owner = 0;
+ mutex->a.count = 0;
+ mutex->abandoned = true;
+ ntsync_wakeup_waiters(obj);
+ }
+ NTSYNC_PRIV_UNLOCK(priv);
+ return (error);
+}
+
+int
+ntsync_mutex_read(struct thread *td, struct file *fp,
+ struct ntsync_mutex_args *a)
+{
+ struct ntsync_obj *obj;
+ struct ntsync_obj_mutex *mutex;
+ struct ntsync_priv *priv;
+ int error;
+
+ obj = fp->f_data;
+ if (obj->type != NTSYNC_OBJ_MUTEX)
+ return (EINVAL);
+ mutex = OBJ_TO_MUTEX(obj);
+ priv = obj->owner;
+ error = 0;
+
+ NTSYNC_PRIV_LOCK(priv);
+ *a = mutex->a;
+ if (mutex->abandoned)
+ error = EOWNERDEAD;
+ NTSYNC_PRIV_UNLOCK(priv);
+ return (error);
+}
+
+static int
+ntsync_mutex_ioctl(struct file *fp, u_long com, void *data,
+ struct ucred *active_cred, struct thread *td)
+{
+ struct ntsync_mutex_args aa;
+ int error, error1;
+
+ switch (com) {
+ case NTSYNC_IOC_MUTEX_UNLOCK:
+ error = ntsync_mutex_unlock(td, fp, data);
+ break;
+ case NTSYNC_IOC_MUTEX_KILL:
+ error = ntsync_mutex_kill(td, fp, *(uint32_t *)data);
+ break;
+ case NTSYNC_IOC_MUTEX_READ:
+ error = ntsync_mutex_read(td, fp, &aa);
+ error1 = ntsync_ioctl_copyout(td, &aa, sizeof(aa));
+ if (error1 != 0)
+ error = error1;
+ break;
+ default:
+ error = ENOTTY;
+ break;
+ }
+ return (error);
+}
+
+static int
+ntsync_mutex_stat(struct file *fp, struct stat *sbp, struct ucred *cred)
+{
+ struct ntsync_obj *obj;
+ struct ntsync_obj_mutex *mutex;
+
+ MPASS(fp->f_type == DTYPE_NTSYNC);
+ obj = fp->f_data;
+ MPASS(obj->type == NTSYNC_OBJ_MUTEX);
+ mutex = OBJ_TO_MUTEX(obj);
+
+ memset(sbp, 0, sizeof(*sbp));
+ sbp->st_mode = S_IFREG /* XXXKIB */ | S_IRUSR | S_IWUSR;
+ NTSYNC_PRIV_LOCK(obj->owner);
+ sbp->st_size = mutex->a.owner;
+ sbp->st_nlink = mutex->a.count;
+ NTSYNC_PRIV_UNLOCK(obj->owner);
+ return (0);
+}
+
+static int
+ntsync_mutex_fill_kinfo(struct file *fp, struct kinfo_file *kif,
+ struct filedesc *fdp)
+{
+ struct ntsync_obj *obj;
+ struct ntsync_obj_mutex *mutex;
+
+ MPASS(fp->f_type == DTYPE_NTSYNC);
+ obj = fp->f_data;
+ MPASS(obj->type == NTSYNC_OBJ_MUTEX);
+ mutex = OBJ_TO_MUTEX(obj);
+
+ kif->kf_type = KF_TYPE_NTSYNC;
+ kif->kf_un.kf_ntsync.kf_ntsync_type = KF_NTSYNC_TYPE_MUTEX;
+ kif->kf_un.kf_ntsync.kf_ntsync_dev = (uintptr_t)obj->owner;
+ kif->kf_un.kf_ntsync.kf_ntsync_un.kf_ntsync_mutex.owner =
+ mutex->a.owner;
+ kif->kf_un.kf_ntsync.kf_ntsync_un.kf_ntsync_mutex.count =
+ mutex->a.count;
+ return (0);
+}
+
+struct fileops ntsync_mutex_fops = {
+ .fo_read = invfo_rdwr,
+ .fo_write = invfo_rdwr,
+ .fo_truncate = invfo_truncate,
+ .fo_ioctl = ntsync_mutex_ioctl,
+ .fo_poll = invfo_poll,
+ .fo_kqfilter = invfo_kqfilter,
+ .fo_stat = ntsync_mutex_stat,
+ .fo_close = ntsync_mutex_close,
+ .fo_chmod = invfo_chmod,
+ .fo_chown = invfo_chown,
+ .fo_sendfile = invfo_sendfile,
+ .fo_fill_kinfo = ntsync_mutex_fill_kinfo,
+ .fo_flags = DFLAG_PASSABLE,
+};
+
+static int
+ntsync_create_mutex(struct ntsync_mutex_args *args, struct ntsync_priv *priv,
+ struct thread *td)
+{
+ struct ntsync_obj_mutex *mutex;
+ int error;
+
+ if ((args->owner != 0 && args->count == 0) ||
+ (args->owner == 0 && args->count != 0))
+ return (EINVAL);
+
+ mutex = malloc(sizeof(*mutex), M_NTSYNC, M_WAITOK | M_ZERO);
+ mutex->obj.type = NTSYNC_OBJ_MUTEX;
+ mutex->obj.is_signaled = ntsync_mutex_is_signaled;
+ mutex->obj.consume = ntsync_mutex_consume;
+ mutex->obj.prepare = ntsync_mutex_prepare;
+ mutex->obj.commit = ntsync_mutex_commit;
+ mutex->obj.post_commit = ntsync_mutex_post_commit;
+ mutex->a = *args;
+ mutex->abandoned = false;
+
+ error = ntsync_create_obj(&mutex->obj, &ntsync_mutex_fops, priv, td);
+ if (error != 0)
+ free(mutex, M_NTSYNC);
+
+ return (error);
+}
+
+static bool
+ntsync_event_is_signaled(struct ntsync_obj *obj,
+ struct ntsync_wait_state *state, int index)
+{
+ struct ntsync_obj_event *event;
+
+ MPASS(obj->type == NTSYNC_OBJ_EVENT);
+ NTSYNC_PRIV_ASSERT(obj->owner);
+ event = OBJ_TO_EVENT(obj);
+ return (event->a.signaled != 0);
+}
+
+static void
+ntsync_event_consume(struct ntsync_obj *obj, struct ntsync_wait_state *state,
+ int index)
+{
+ struct ntsync_obj_event *event;
+
+ MPASS(obj->type == NTSYNC_OBJ_EVENT);
+ NTSYNC_PRIV_ASSERT(obj->owner);
+ MPASS(ntsync_event_is_signaled(obj, state, index));
+
+ event = OBJ_TO_EVENT(obj);
+ if (event->a.manual == 0)
+ event->a.signaled = 0;
+}
+
+static bool
+ntsync_event_prepare(struct ntsync_obj *obj, struct ntsync_wait_state *state,
+ int index, bool *stop)
+{
+ struct ntsync_obj_event *event;
+
+ MPASS(obj->type == NTSYNC_OBJ_EVENT);
+ NTSYNC_PRIV_ASSERT(obj->owner);
+ event = OBJ_TO_EVENT(obj);
+ if (!ntsync_event_is_signaled(obj, state, index))
+ return (false);
+ event->a1 = event->a;
+ return (true);
+}
+
+static void
+ntsync_event_commit(struct ntsync_obj *obj, struct ntsync_wait_state *state,
+ int index)
+{
+ struct ntsync_obj_event *event;
+
+ MPASS(obj->type == NTSYNC_OBJ_EVENT);
+ NTSYNC_PRIV_ASSERT(obj->owner);
+ event = OBJ_TO_EVENT(obj);
+ event->a = event->a1;
+ if (event->pulse && event->a.manual == 0) {
+ event->a.signaled = 0;
+ event->pulse = false;
+ }
+}
+
+static void
+ntsync_event_post_commit(struct ntsync_obj *obj,
+ struct ntsync_wait_state *state, int index)
+{
+ struct ntsync_obj_event *event;
+
+ MPASS(obj->type == NTSYNC_OBJ_EVENT);
+ NTSYNC_PRIV_ASSERT(obj->owner);
+ event = OBJ_TO_EVENT(obj);
+ if (event->a.manual == 0)
+ event->a.signaled = 0;
+}
+
+static int
+ntsync_event_close(struct file *fp, struct thread *td)
+{
+ struct ntsync_obj_event *event;
+
+ event = fp->f_data;
+ ntsync_close_obj(&event->obj, td);
+ free(event, M_NTSYNC);
+ return (0);
+}
+
+int
+ntsync_event_set(struct thread *td, struct file *fp, uint32_t *val)
+{
+ struct ntsync_obj *obj;
+ struct ntsync_obj_event *event;
+ struct ntsync_priv *priv;
+ uint32_t prev;
+
+ obj = fp->f_data;
+ if (obj->type != NTSYNC_OBJ_EVENT)
+ return (EINVAL);
+ event = OBJ_TO_EVENT(obj);
+ priv = obj->owner;
+
+ NTSYNC_PRIV_LOCK(priv);
+ prev = event->a.signaled;
+ event->a.signaled = 1;
+ ntsync_wakeup_waiters(obj);
+ NTSYNC_PRIV_UNLOCK(priv);
+
+ *val = prev;
+ return (0);
+}
+
+int
+ntsync_event_reset(struct thread *td, struct file *fp, uint32_t *val)
+{
+ struct ntsync_obj *obj;
+ struct ntsync_obj_event *event;
+ struct ntsync_priv *priv;
+ uint32_t prev;
+
+ obj = fp->f_data;
+ if (obj->type != NTSYNC_OBJ_EVENT)
+ return (EINVAL);
+ event = OBJ_TO_EVENT(obj);
+ priv = obj->owner;
+
+ NTSYNC_PRIV_LOCK(priv);
+ prev = event->a.signaled;
+ event->a.signaled = 0;
+ NTSYNC_PRIV_UNLOCK(priv);
+
+ *val = prev;
+ return (0);
+}
+
+int
+ntsync_event_pulse(struct thread *td, struct file *fp, uint32_t *val)
+{
+ struct ntsync_obj *obj;
+ struct ntsync_obj_event *event;
+ struct ntsync_priv *priv;
+ uint32_t prev;
+
+ obj = fp->f_data;
+ if (obj->type != NTSYNC_OBJ_EVENT)
+ return (EINVAL);
+ event = OBJ_TO_EVENT(obj);
+ priv = obj->owner;
+
+ NTSYNC_PRIV_LOCK(priv);
+ prev = event->a.signaled;
+ event->a.signaled = 1;
+ event->pulse = true;
+ ntsync_wakeup_waiters(obj);
+ event->a.signaled = 0;
+ event->pulse = false;
+ NTSYNC_PRIV_UNLOCK(priv);
+
+ *val = prev;
+ return (0);
+}
+
+int
+ntsync_event_read(struct thread *td, struct file *fp,
+ struct ntsync_event_args *a)
+{
+ struct ntsync_obj *obj;
+ struct ntsync_obj_event *event;
+ struct ntsync_priv *priv;
+
+ obj = fp->f_data;
+ if (obj->type != NTSYNC_OBJ_EVENT)
+ return (EINVAL);
+ event = OBJ_TO_EVENT(obj);
+ priv = obj->owner;
+
+ NTSYNC_PRIV_LOCK(priv);
+ *a = event->a;
+ NTSYNC_PRIV_UNLOCK(priv);
+
+ return (0);
+}
+
+static int
+ntsync_event_ioctl(struct file *fp, u_long com, void *data,
+ struct ucred *active_cred, struct thread *td)
+{
+ int error;
+
+ switch (com) {
+ case NTSYNC_IOC_EVENT_SET:
+ error = ntsync_event_set(td, fp, data);
+ break;
+ case NTSYNC_IOC_EVENT_RESET:
+ error = ntsync_event_reset(td, fp, data);
+ break;
+ case NTSYNC_IOC_EVENT_PULSE:
+ error = ntsync_event_pulse(td, fp, data);
+ break;
+ case NTSYNC_IOC_EVENT_READ:
+ error = ntsync_event_read(td, fp, data);
+ break;
+ default:
+ error = ENOTTY;
+ break;
+ }
+ return (error);
+}
+
+static int
+ntsync_event_stat(struct file *fp, struct stat *sbp, struct ucred *cred)
+{
+ struct ntsync_obj *obj;
+ struct ntsync_obj_event *event;
+
+ MPASS(fp->f_type == DTYPE_NTSYNC);
+ obj = fp->f_data;
+ MPASS(obj->type == NTSYNC_OBJ_EVENT);
+ event = OBJ_TO_EVENT(obj);
+
+ memset(sbp, 0, sizeof(*sbp));
+ sbp->st_mode = S_IFREG /* XXXKIB */ | S_IRUSR | S_IWUSR;
+ NTSYNC_PRIV_LOCK(obj->owner);
+ sbp->st_size = event->a.signaled;
+ sbp->st_nlink = event->a.manual;
+ NTSYNC_PRIV_UNLOCK(obj->owner);
+ return (0);
+}
+
+static int
+ntsync_event_fill_kinfo(struct file *fp, struct kinfo_file *kif,
+ struct filedesc *fdp)
+{
+ struct ntsync_obj *obj;
+ struct ntsync_obj_event *event;
+
+ MPASS(fp->f_type == DTYPE_NTSYNC);
+ obj = fp->f_data;
+ MPASS(obj->type == NTSYNC_OBJ_EVENT);
+ event = OBJ_TO_EVENT(obj);
+
+ kif->kf_type = KF_TYPE_NTSYNC;
+ kif->kf_un.kf_ntsync.kf_ntsync_type = KF_NTSYNC_TYPE_EVENT;
+ kif->kf_un.kf_ntsync.kf_ntsync_dev = (uintptr_t)obj->owner;
+ kif->kf_un.kf_ntsync.kf_ntsync_un.kf_ntsync_event.signaled =
+ event->a.signaled;
+ kif->kf_un.kf_ntsync.kf_ntsync_un.kf_ntsync_event.manual =
+ event->a.manual;
+ return (0);
+}
+
+struct fileops ntsync_event_fops = {
+ .fo_read = invfo_rdwr,
+ .fo_write = invfo_rdwr,
+ .fo_truncate = invfo_truncate,
+ .fo_ioctl = ntsync_event_ioctl,
+ .fo_poll = invfo_poll,
+ .fo_kqfilter = invfo_kqfilter,
+ .fo_stat = ntsync_event_stat,
+ .fo_close = ntsync_event_close,
+ .fo_chmod = invfo_chmod,
+ .fo_chown = invfo_chown,
+ .fo_sendfile = invfo_sendfile,
+ .fo_fill_kinfo = ntsync_event_fill_kinfo,
+ .fo_flags = DFLAG_PASSABLE,
+};
+
+static int
+ntsync_create_event(struct ntsync_event_args *args, struct ntsync_priv *priv,
+ struct thread *td)
+{
+ struct ntsync_obj_event *event;
+ int error;
+
+ event = malloc(sizeof(*event), M_NTSYNC, M_WAITOK | M_ZERO);
+ event->obj.type = NTSYNC_OBJ_EVENT;
+ event->obj.is_signaled = ntsync_event_is_signaled;
+ event->obj.consume = ntsync_event_consume;
+ event->obj.prepare = ntsync_event_prepare;
+ event->obj.commit = ntsync_event_commit;
+ event->obj.post_commit = ntsync_event_post_commit;
+ event->a = *args;
+
+ error = ntsync_create_obj(&event->obj, &ntsync_event_fops, priv, td);
+ if (error != 0)
+ free(event, M_NTSYNC);
+
+ return (error);
+}
+
+static void
+ntsync_free_priv(struct ntsync_priv *priv)
+{
+ bool do_free;
+
+ NTSYNC_PRIV_LOCK(priv);
+ do_free = priv->closed && priv->objs_cnt == 0;
+ NTSYNC_PRIV_UNLOCK(priv);
+ if (do_free) {
+ mtx_destroy(&priv->lock);
+ free(priv, M_NTSYNC);
+ }
+}
+
+static void
+ntsync_priv_dtr(void *data)
+{
+ ntsync_free_priv(data);
+}
+
+static int
+ntsync_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
+{
+ struct ntsync_priv *priv;
+
+ priv = malloc(sizeof(*priv), M_NTSYNC, M_WAITOK);
+ priv->closed = false;
+ priv->objs_cnt = 0;
+ mtx_init(&priv->lock, "ntsync", "ntsync", MTX_DEF | MTX_NEW);
+ devfs_set_cdevpriv(priv, ntsync_priv_dtr);
+ return (0);
+}
+
+static int
+ntsync_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
+{
+ struct ntsync_priv *priv;
+ void *a;
+ int error;
+
+ error = devfs_get_cdevpriv(&a);
+ if (error == 0) {
+ priv = a;
+ NTSYNC_PRIV_LOCK(priv);
+ priv->closed = true;
+ NTSYNC_PRIV_UNLOCK(priv);
+ }
+ devfs_clear_cdevpriv();
+ return (0);
+}
+
+static int
+ntsync_wait_state_get(struct ntsync_wait_args *nwa, u_long cmd,
+ struct ntsync_priv *owner, struct ntsync_wait_state **statep,
+ struct thread *td)
+{
+ struct ntsync_wait_state *state;
+ struct ntsync_obj *obj;
+ struct bintime btb;
+ int error, i, j;
+
+ if (nwa->count > NTSYNC_MAX_WAIT_COUNT)
+ return (EINVAL);
+ if ((nwa->flags & ~NTSYNC_WAIT_REALTIME) != 0)
+ return (EINVAL);
+
+ state = malloc(sizeof(*state), M_NTSYNC, M_WAITOK | M_ZERO);
+ state->nwa = nwa;
+ state->owner = owner;
+ state->all = cmd == NTSYNC_IOC_WAIT_ALL;
+ state->any = !state->all;
+ error = copyin((void *)(uintptr_t)nwa->objs, &state->fds[0],
+ nwa->count * sizeof(state->fds[0]));
+ if (error != 0)
+ return (error);
+
+ i = 0;
+ if (nwa->alert != 0) {
+ error = fget_cap(td, nwa->alert, &cap_no_rights, NULL,
+ &state->fp_alert, NULL);
+ if (error != 0) {
+ state->fp_alert = NULL;
+ goto error_out;
+ }
+ if (state->fp_alert->f_type != DTYPE_NTSYNC) {
+ error = EINVAL;
+ goto error_out;
+ }
+ obj = state->fp_alert->f_data;
+ if (obj->type != NTSYNC_OBJ_EVENT || obj->owner != owner) {
+ error = EINVAL;
+ goto error_out;
+ }
+ state->alert_event = OBJ_TO_EVENT(obj);
+ }
+
+ for (; i < nwa->count; i++) {
+ error = fget_cap(td, state->fds[i], &cap_no_rights, NULL,
+ &state->fps[i], NULL);
+ if (error != 0) {
+ state->fps[i] = NULL;
+ goto error_out;
+ }
+ if (state->fps[i]->f_type != DTYPE_NTSYNC ||
+ (obj = state->fps[i]->f_data)->owner != owner) {
+ i++;
+ error = EINVAL;
+ goto error_out;
+ }
+ }
+
+ state->obj_count = nwa->count;
+ for (i = 0; i < nwa->count; i++)
+ state->objs[i] = state->fps[i]->f_data;
+ if (state->alert_event != NULL) {
+ state->objs[i] = &state->alert_event->obj;
+ state->obj_count++;
+ }
+
+ if (state->all) {
+ /* Check no dups */
+ for (i = 0; i < state->obj_count; i++) {
+ obj = state->objs[i];
+ for (j = i + 1; j < state->obj_count; j++) {
+ if (obj == state->objs[j]) {
+ i = state->obj_count;
+ error = EINVAL;
+ goto error_out;
+ }
+ }
+ }
+ }
+
+ if (nwa->timeout == UINT64_MAX) {
+ state->sb = 0;
+ } else {
+ state->sb = nstosbt(nwa->timeout);
+ if ((nwa->flags & NTSYNC_WAIT_REALTIME) != 0) {
+ getboottimebin(&btb);
+ state->sb += bttosbt(btb);
+ }
+ }
+
+ *statep = state;
+ return (0);
+
+error_out:
+ for (j = 0; j < i; j++)
+ fdrop(state->fps[j], td);
+ if (state->fp_alert != NULL)
+ fdrop(state->fp_alert, td);
+ return (error);
+}
+
+static void
+ntsync_wait_state_put(struct ntsync_wait_state *state, struct thread *td)
+{
+ int i;
+
+ for (i = 0; i < state->nwa->count; i++)
+ fdrop(state->fps[i], td);
+ if (state->fp_alert != NULL)
+ fdrop(state->fp_alert, td);
+ free(state, M_NTSYNC);
+}
+
+static int
+ntsync_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
+ struct thread *td)
+{
+ struct ntsync_priv *owner;
+ struct ntsync_wait_args *nwa;
+ struct ntsync_wait_state *state;
+ void *a;
+ int error;
+
+ error = devfs_get_cdevpriv(&a);
+ if (error != 0)
+ return (error);
+ owner = a;
+
+ switch (cmd) {
+ case NTSYNC_IOC_CREATE_SEM:
+ error = ntsync_create_sem((struct ntsync_sem_args *)data,
+ owner, td);
+ break;
+ case NTSYNC_IOC_CREATE_MUTEX:
+ error = ntsync_create_mutex((struct ntsync_mutex_args *)data,
+ owner, td);
+ break;
+ case NTSYNC_IOC_CREATE_EVENT:
+ error = ntsync_create_event((struct ntsync_event_args *)data,
+ owner, td);
+ break;
+ case NTSYNC_IOC_WAIT_ANY:
+ nwa = (struct ntsync_wait_args *)data;
+ error = ntsync_wait_state_get(nwa, cmd, owner, &state, td);
+ if (error != 0)
+ break;
+ error = ntsync_wait(state, td);
+ if (error == 0) {
+ nwa->index = state->index;
+ error = ntsync_ioctl_copyout(td, nwa, sizeof(*nwa));
+ if (error == 0)
+ error = state->error;
+ }
+ ntsync_wait_state_put(state, td);
+ break;
+ case NTSYNC_IOC_WAIT_ALL:
+ nwa = (struct ntsync_wait_args *)data;
+ error = ntsync_wait_state_get(nwa, cmd, owner, &state, td);
+ if (error != 0)
+ break;
+ error = ntsync_wait(state, td);
+ if (error == 0) {
+ nwa->index = state->index;
+ error = ntsync_ioctl_copyout(td, nwa, sizeof(*nwa));
+ if (error == 0)
+ error = state->error;
+ }
+ ntsync_wait_state_put(state, td);
+ break;
+
+ default:
+ error = ENOTTY;
+ break;
+ }
+ return (error);
+}
+
+struct cdevsw ntsync_cdevsw = {
+ .d_version = D_VERSION,
+ .d_flags = 0,
+ .d_open = ntsync_open,
+ .d_close = ntsync_close,
+ .d_ioctl = ntsync_ioctl,
+ .d_name = "ntsync",
+};
+
+static int
+ntsync_modevent(module_t mod __unused, int type, void *data __unused)
+{
+ struct make_dev_args mda;
+ int error;
+
+ error = 0;
+ switch (type) {
+ case MOD_LOAD:
+ make_dev_args_init(&mda);
+ mda.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME;
+ mda.mda_devsw = &ntsync_cdevsw;
+ mda.mda_uid = UID_ROOT;
+ mda.mda_gid = GID_GAMES;
+ mda.mda_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP |
+ S_IROTH | S_IWOTH;
+
+ error = make_dev_s(&mda, &ntsync_cdev, "ntsync");
+ if (error != 0) {
+ printf("cannot create ntsync dev err %d\n", error);
+ break;
+ }
+ if (bootverbose)
+ printf("ntsync\n");
+ break;
+
+ case MOD_UNLOAD:
+ destroy_dev(ntsync_cdev);
+ break;
+
+ case MOD_SHUTDOWN:
+ break;
+
+ default:
+ error = EOPNOTSUPP;
+ }
+
+ return (error);
+}
+
+DEV_MODULE(ntsync, ntsync_modevent, NULL);
+MODULE_VERSION(ntsync, 1);
diff --git a/sys/dev/ntsync/ntsyncvar.h b/sys/dev/ntsync/ntsyncvar.h
new file mode 100644
--- /dev/null
+++ b/sys/dev/ntsync/ntsyncvar.h
@@ -0,0 +1,119 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2026 The FreeBSD Foundation
+ *
+ * This software was developed by Konstantin Belousov <kib@FreeBSD.org>
+ * under sponsorship from the FreeBSD Foundation.
+ */
+
+#ifndef __DEV_NTSYNCVAR_H__
+#define __DEV_NTSYNCVAR_H__
+
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/time.h>
+#include <sys/queue.h>
+#include <dev/ntsync/ntsync.h>
+
+enum ntsync_obj_type {
+ NTSYNC_OBJ_SEM,
+ NTSYNC_OBJ_MUTEX,
+ NTSYNC_OBJ_EVENT,
+};
+
+struct ntsync_wait_state;
+
+struct ntsync_obj_waiter {
+ struct ntsync_wait_state *state;
+ TAILQ_ENTRY(ntsync_obj_waiter) link;
+};
+
+struct ntsync_obj {
+ enum ntsync_obj_type type;
+ struct ntsync_priv *owner;
+ TAILQ_HEAD(, ntsync_obj_waiter) waiters;
+ /* any */
+ bool (*is_signaled)(struct ntsync_obj *,
+ struct ntsync_wait_state *state, int index);
+ void (*consume)(struct ntsync_obj *, struct ntsync_wait_state *,
+ int index);
+ /* all */
+ bool (*prepare)(struct ntsync_obj *, struct ntsync_wait_state *state,
+ int index, bool *stop);
+ void (*commit)(struct ntsync_obj *, struct ntsync_wait_state *state,
+ int index);
+ void (*post_commit)(struct ntsync_obj *,
+ struct ntsync_wait_state *state, int index);
+};
+
+struct ntsync_obj_sem {
+ struct ntsync_obj obj;
+ struct ntsync_sem_args a;
+ struct ntsync_sem_args a1;
+};
+#define OBJ_TO_SEM(obj) __containerof(obj, struct ntsync_obj_sem, obj)
+
+struct ntsync_obj_mutex {
+ struct ntsync_obj obj;
+ struct ntsync_mutex_args a;
+ struct ntsync_mutex_args a1;
+ bool abandoned;
+};
+#define OBJ_TO_MUTEX(obj) __containerof(obj, struct ntsync_obj_mutex, obj)
+
+struct ntsync_obj_event {
+ struct ntsync_obj obj;
+ struct ntsync_event_args a;
+ struct ntsync_event_args a1;
+ bool pulse;
+};
+#define OBJ_TO_EVENT(obj) __containerof(obj, struct ntsync_obj_event, obj)
+
+struct ntsync_wait_state {
+ struct ntsync_wait_args *nwa;
+ struct ntsync_priv *owner;
+ struct ntsync_obj_waiter waiters[NTSYNC_MAX_WAIT_COUNT + 1];
+ int fds[NTSYNC_MAX_WAIT_COUNT];
+ struct file *fps[NTSYNC_MAX_WAIT_COUNT];
+ struct file *fp_alert;
+ int obj_count;
+ struct ntsync_obj *objs[NTSYNC_MAX_WAIT_COUNT + 1];
+ struct ntsync_obj_event *alert_event;
+ sbintime_t sb;
+ int error;
+ int index;
+ bool any;
+ bool all;
+ bool ready;
+};
+
+struct ntsync_priv {
+ struct mtx lock;
+ unsigned objs_cnt;
+ bool closed;
+};
+
+#define NTSYNC_PRIV_LOCK(priv) mtx_lock(&priv->lock)
+#define NTSYNC_PRIV_UNLOCK(priv) mtx_unlock(&priv->lock)
+#define NTSYNC_PRIV_ASSERT(priv) mtx_assert(&priv->lock, MA_OWNED)
+
+extern struct cdevsw ntsync_cdevsw;
+
+struct file;
+struct thread;
+int ntsync_sem_release(struct thread *td, struct file *fp, uint32_t *val);
+int ntsync_sem_read(struct thread *td, struct file *fp,
+ struct ntsync_sem_args *a);
+int ntsync_mutex_unlock(struct thread *td, struct file *fp,
+ struct ntsync_mutex_args *a);
+int ntsync_mutex_kill(struct thread *td, struct file *fp, uint32_t val);
+int ntsync_mutex_read(struct thread *td, struct file *fp,
+ struct ntsync_mutex_args *a);
+int ntsync_event_set(struct thread *td, struct file *fp, uint32_t *val);
+int ntsync_event_reset(struct thread *td, struct file *fp, uint32_t *val);
+int ntsync_event_pulse(struct thread *td, struct file *fp, uint32_t *val);
+int ntsync_event_read(struct thread *td, struct file *fp,
+ struct ntsync_event_args *a);
+
+#endif
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -296,6 +296,7 @@
nlsysevent \
nge \
nmdm \
+ ntsync \
nullfs \
${_ntb} \
nvd \
@@ -542,6 +543,7 @@
${MACHINE_CPUARCH} == "i386"
SUBDIR+= linprocfs
SUBDIR+= linsysfs
+SUBDIR+= linux_ntsync
.endif
.if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "i386"
SUBDIR+= linux
diff --git a/sys/modules/linux_ntsync/Makefile b/sys/modules/linux_ntsync/Makefile
new file mode 100644
--- /dev/null
+++ b/sys/modules/linux_ntsync/Makefile
@@ -0,0 +1,6 @@
+.PATH: ${SRCTOP}/sys/dev/ntsync
+
+KMOD= linux_ntsync
+SRCS= linux_ntsync.c
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/ntsync/Makefile b/sys/modules/ntsync/Makefile
new file mode 100644
--- /dev/null
+++ b/sys/modules/ntsync/Makefile
@@ -0,0 +1,6 @@
+.PATH: ${SRCTOP}/sys/dev/ntsync
+
+KMOD= ntsync
+SRCS= ntsync.c
+
+.include <bsd.kmod.mk>
diff --git a/sys/sys/file.h b/sys/sys/file.h
--- a/sys/sys/file.h
+++ b/sys/sys/file.h
@@ -73,6 +73,7 @@
#define DTYPE_TIMERFD 14 /* timerfd */
#define DTYPE_INOTIFY 15 /* inotify descriptor */
#define DTYPE_JAILDESC 16 /* jail descriptor */
+#define DTYPE_NTSYNC 17 /* /dev/ntsync */
#ifdef _KERNEL
diff --git a/sys/sys/user.h b/sys/sys/user.h
--- a/sys/sys/user.h
+++ b/sys/sys/user.h
@@ -269,6 +269,7 @@
#define KF_TYPE_TIMERFD 14
#define KF_TYPE_INOTIFY 15
#define KF_TYPE_JAILDESC 16
+#define KF_TYPE_NTSYNC 17
#define KF_TYPE_UNKNOWN 255
#define KF_VTYPE_VNON 0
@@ -289,6 +290,11 @@
#define KF_FD_TYPE_TEXT -5 /* Text vnode */
#define KF_FD_TYPE_CTTY -6 /* Controlling terminal */
+#define KF_NTSYNC_TYPE_DEV 1 /* Not reported, reserved */
+#define KF_NTSYNC_TYPE_SEM 2
+#define KF_NTSYNC_TYPE_MUTEX 3
+#define KF_NTSYNC_TYPE_EVENT 4
+
#define KF_FLAG_READ 0x00000001
#define KF_FLAG_WRITE 0x00000002
#define KF_FLAG_APPEND 0x00000004
@@ -467,6 +473,24 @@
uint64_t kf_inotify_npending;
uint64_t kf_inotify_nbpending;
} kf_inotify;
+ struct {
+ uint32_t kf_ntsync_type;
+ uint64_t kf_ntsync_dev;
+ union {
+ struct {
+ uint32_t count;
+ uint32_t max;
+ } kf_ntsync_sem;
+ struct{
+ uint32_t owner;
+ uint32_t count;
+ } kf_ntsync_mutex;
+ struct {
+ uint32_t signaled;
+ uint32_t manual;
+ } kf_ntsync_event;
+ } kf_ntsync_un;
+ } kf_ntsync;
} kf_un;
};
uint16_t kf_status; /* Status flags. */

File Metadata

Mime Type
text/plain
Expires
Sat, May 23, 6:47 PM (12 h, 19 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
33451995
Default Alt Text
D57038.diff (49 KB)

Event Timeline