The branch stable/15 has been updated by kib:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=43702cfc0ccbd9b357243aedcf18e19d94a64ef9

commit 43702cfc0ccbd9b357243aedcf18e19d94a64ef9
Author:     Konstantin Belousov <[email protected]>
AuthorDate: 2026-05-11 09:49:13 +0000
Commit:     Konstantin Belousov <[email protected]>
CommitDate: 2026-06-13 00:58:31 +0000

    ntsync(4)
    
    (cherry picked from commit 03ca6dbdb80da79408f135d823fbd9a00fd4f25b)
---
 sys/dev/ntsync/ntsync.c     | 1379 +++++++++++++++++++++++++++++++++++++++++++
 sys/dev/ntsync/ntsync.h     |   66 +++
 sys/dev/ntsync/ntsyncvar.h  |  119 ++++
 sys/modules/Makefile        |    1 +
 sys/modules/ntsync/Makefile |    6 +
 sys/sys/file.h              |    1 +
 6 files changed, 1572 insertions(+)

diff --git a/sys/dev/ntsync/ntsync.c b/sys/dev/ntsync/ntsync.c
new file mode 100644
index 000000000000..8b0984d12c75
--- /dev/null
+++ b/sys/dev/ntsync/ntsync.c
@@ -0,0 +1,1379 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2026 The FreeBSD Foundation
+ *
+ * This software was developed by Konstantin Belousov <[email protected]>
+ * 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 <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)
+{
+       // XXXKIB
+       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, bool *doco)
+{
+       struct ntsync_obj *obj;
+       struct ntsync_obj_mutex *mutex;
+       struct ntsync_priv *priv;
+       int error;
+
+       *doco = false;
+       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);
+       *doco = true;
+       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;
+       bool doco;
+
+       doco = false;
+       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, &doco);
+               if (doco) {
+                       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)
+{
+       // XXXKIB
+       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)
*** 653 LINES SKIPPED ***

Reply via email to