From nobody Sat Jun 13 01:03:39 2026 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4gcdRv6l86z6hShC for ; Sat, 13 Jun 2026 01:03:39 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R13" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4gcdRv63NDz3lWH for ; Sat, 13 Jun 2026 01:03:39 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1781312619; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=1TyLPgSLGokCFFTjbzw2dhrAsw3Ew1DK/XnR2ii//xA=; b=ns5sfY1JGhHvOSQTpcOJ52S6hahyXXuatn6uCLVi6jXl7B3bAOH4MR0XGGDt6ZoHuOldTw q2LZfQ9RA4eME2nzlriMBBWXlctAZ9mEIB0r+xAGELBwO7MUVnOvgapFIEGTNzXPeUPoxa uXyA9GKTxN3FdDQLQuq/NkIlCpz6YOfmPLJ7Q+pxafbjGix8aTW29dHLU3US9acQXk4lQo mLmzCWSxBztA9/dwgTDKtpJXBsPX+6Kam/AOyumX3L6+7p4Z5+BV+aMR3vlg2T9IR/TC8S TXRQhxERww5Jv5CTniaqK9hR3hOj8aJVuCBUu1TVjZpUWnNOIK1vJtyjZz7rtA== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1781312619; a=rsa-sha256; cv=none; b=yj8BL/fLob2NZyp893epe6yI+2k9zu2MiNw31jnb75Q1OXmQI2GjTditP6zJreGZ6ocvyS 2a66+qiv3BJDOwF+t1ZDzu/prZIUngqH8geRp3RY5Py5mw07IDAJIew/0u774w7VlRFD7e br49CxWRnSM+apclE3ddE/CqjZxFWiyHxoUmF2w/szqf+bU+mNb0hz1W1AbokKua90mH/y B9MPJDlPoJrg37XXBpJiaQRkP0SNp9sc17SeMcMUltMmoJKumhOaCeZfrMEwOYm5Ku7z6h pldX/OwXo1q6Ay5YfFVZaa0aS9cHPLkQfKr+WIMb/lU97K1/uWqzENGDYglEFA== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1781312619; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=1TyLPgSLGokCFFTjbzw2dhrAsw3Ew1DK/XnR2ii//xA=; b=pidK7gGXqYW6iAA/xcxAENzr9qZENWNkKxPTcAQOVzWDQeCjLXw0Ed8+nzFo4ZM21wEL43 U2LITcjQl6YUHE8rjy9XYsdfysNqdKhfg9zIRqfHgsjf6dGvxEImx54zom8+Muqds6+AcQ nssXv890vJaTKLQbIvX/Q36WOTmr3BDWwVI8fhLK0ps9No9IZWH9i2KjHnJeGEKg9mKEOU a7WBh7oHp3IM+Qhik532t9uoLS7ASMRQSrKj9j1NxpGdHbwbgpd/bUKfdqfDP7Tk9JTNB5 7qdgAqBSuoobjHMzXYHtZEvyHV5Nsjh2v2FRLoumKn3Sjae6BntVAKmpNxXXlw== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4gcdRv5JFNz11tL for ; Sat, 13 Jun 2026 01:03:39 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 20722 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Sat, 13 Jun 2026 01:03:39 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Konstantin Belousov Subject: git: 43702cfc0ccb - stable/15 - ntsync(4) List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: kib X-Git-Repository: src X-Git-Refname: refs/heads/stable/15 X-Git-Reftype: branch X-Git-Commit: 43702cfc0ccbd9b357243aedcf18e19d94a64ef9 Auto-Submitted: auto-generated Date: Sat, 13 Jun 2026 01:03:39 +0000 Message-Id: <6a2cac6b.20722.232cd014@gitrepo.freebsd.org> The branch stable/15 has been updated by kib: URL: https://cgit.FreeBSD.org/src/commit/?id=43702cfc0ccbd9b357243aedcf18e19d94a64ef9 commit 43702cfc0ccbd9b357243aedcf18e19d94a64ef9 Author: Konstantin Belousov AuthorDate: 2026-05-11 09:49:13 +0000 Commit: Konstantin Belousov 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 + * under sponsorship from the FreeBSD Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 ***