From nobody Sun May 31 17:19:54 2026 X-Original-To: dev-commits-src-main@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 4gT3kL3y5jz6gPGW for ; Sun, 31 May 2026 17:19:54 +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 4gT3kL3fjHz3Vx6 for ; Sun, 31 May 2026 17:19:54 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1780247994; 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=L1fUFdYwtjOApX8xdaqr5/l/CSifnTiLUD55G+rCPA0=; b=Xl5WbSPGI+mXhuKPukmOQlzQ1zSC/jNSflgsH4ihI2ySOyhYSA3gB6Ktv0ctxs43ZS7n4C ryAiFJv1KX8oJgG27qwG6HUAluSEBWTMFBOMVpyDMy/+bzMO1zuI6rhl0pHaChHybIK57g 4Tk0mKmRRZXaj6J2vwnPGWrJP9tOfS/RAhyW8yigiXn7RVfkE/xK+ZJX5R3IKF0ynpcLXy 4bhF43K+MMMkBqlXMU2ZAfqdCPH1usFcO2yGyrQp+RIuzrM9xS2LauTwtAiC3JxfqeGLz7 b5r/d80keXjkOJ3cU6/FhAuBhDPj34g4J4bnyUmnDkrMQYubAdE+YandyBrLhg== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1780247994; a=rsa-sha256; cv=none; b=E4p9UATvNJQZKBD04CNGmaP1c0xjMzw/Cw2LepCG/nvLCQxt+1vP0sS9QSfAxTbfyfZijK JCE96LXwpVgg7HjSvj1dyJz70kaYcksNDzp8xFEwY/pBUn1dST7xHVAJO2c1shj+0Tsk+q JZHnCcVr/yy0fRBy4YdiFfKN9WNEncduvZkTgQ5xcvGt7nUntTPGtdEtSqObM+4RpdgBTT zwD3iZyfPwRY/FOd/EwnK13mXNN1SLQplBuCzSgAvo5h0huD7FttqhvhuzyKV5agxTVwmr fFO3mPIyWEAabEDs/bahMuoPXi44tBb4zTYS+m4zh7jN3wfgR0C0G2z8vhTPLQ== 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=1780247994; 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=L1fUFdYwtjOApX8xdaqr5/l/CSifnTiLUD55G+rCPA0=; b=l/0VOBtZCdmnBodVCrYO3GQ3Lbcvi5i4eBZiX6cOp8BomZcsGA9B7FA/RtXQaB090SpCXb dLnpIY81CZJLALZQYCw+sGRAfUSFQXN6CWpKy7/IUCTgKzBYzBKzUCsJdM7akvFc/FB0/Y jmUmQD639MDC0SxgjOuhJqioTyaK8GsinkbcSLIvSox3D14qh17dv02uN3y1FxszYzC0Mh DgJQRrW7ABF1CM2WVIbilpX5H0ULR3CNSIC8aVdTQZb42BYimoEhR6LoxSYWKFoZQKX+Ns LbAfbVgf9TiveSaeiWlKTxNKR9ZpcaJ2XBTn1vks3lYxIS30LCkE82T4pxkozA== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4gT3kL2rTTz10w8 for ; Sun, 31 May 2026 17:19:54 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 3d173 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Sun, 31 May 2026 17:19:54 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Konstantin Belousov Subject: git: 03ca6dbdb80d - main - ntsync(4) List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@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/main X-Git-Reftype: branch X-Git-Commit: 03ca6dbdb80da79408f135d823fbd9a00fd4f25b Auto-Submitted: auto-generated Date: Sun, 31 May 2026 17:19:54 +0000 Message-Id: <6a1c6dba.3d173.67c47bb@gitrepo.freebsd.org> The branch main has been updated by kib: URL: https://cgit.FreeBSD.org/src/commit/?id=03ca6dbdb80da79408f135d823fbd9a00fd4f25b commit 03ca6dbdb80da79408f135d823fbd9a00fd4f25b Author: Konstantin Belousov AuthorDate: 2026-05-11 09:49:13 +0000 Commit: Konstantin Belousov CommitDate: 2026-05-31 17:10:46 +0000 ntsync(4) The driver implements the ntsync interface as specified in the Linux 7.0-rc3 document Documentation/userspace-api/ntsync.rst. Only the documentation and the userspace tests (Linux' tools/testing/selftests/drivers/ntsync/ntsync.c) were used for reference. When the documentation contradicted the tests, tests behavior was implemented. One quirk is that Linux API needs to return an error from ioctl() and to copyout the modified ioctl() argument. Our generic ioctl() is not flexible enough to implement this, so the ntsync_ioctl_copyout() hack allows to copyout the ioctl parameter directly from the ioctl method, instead of relying on the ioctl infra. The FreeBSD port of the tests, that can be compiled both on FreeBSD and Linux, is available at https://github.com/kostikbel/freebsd-ntsync-test. The Linux binary compiled with the Linux test harness, cannot be run under linuxolator due to unimplemented syscalls, but the shims in freebsd-ntsync-test can be compiled on Linux and resulting Linux/glibc binary run on linuxolator to test linux compat. Sponsored by: The FreeBSD Foundation MFC after: 1 week Differential revision: https://reviews.freebsd.org/D57038 --- 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) +{ *** 675 LINES SKIPPED ***