xref: /freebsd/sys/dev/ntsync/ntsync.c (revision 255538cd906045095d0c2113ae6c4731ce36c0cf)
1 /*
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright 2026 The FreeBSD Foundation
5  *
6  * This software was developed by Konstantin Belousov <kib@FreeBSD.org>
7  * under sponsorship from the FreeBSD Foundation.
8  */
9 
10 #include <sys/systm.h>
11 #include <sys/conf.h>
12 #include <sys/fcntl.h>
13 #include <sys/file.h>
14 #include <sys/filedesc.h>
15 #include <sys/kernel.h>
16 #include <sys/limits.h>
17 #include <sys/malloc.h>
18 #include <sys/module.h>
19 #include <sys/proc.h>
20 #include <sys/stat.h>
21 #include <sys/sysent.h>
22 #include <sys/user.h>
23 #include <dev/ntsync/ntsyncvar.h>
24 
25 static struct cdev *ntsync_cdev;
26 MALLOC_DEFINE(M_NTSYNC, "ntsync", "ntsync");
27 
28 static void ntsync_free_priv(struct ntsync_priv *priv);
29 
30 /*
31  * Returning error from an ioctl handler prevents the generic ioctl
32  * code from copying out the result.  Use direct access to ioctl(2)
33  * args to get the parameters block pointer to implement Linux
34  * semantic of both returning an error and updating the parameters
35  * block.
36  */
37 static int
38 ntsync_ioctl_copyout(struct thread *td, const void *ptr, size_t sz)
39 {
40 	void *uptr;
41 
42 	if (SV_PROC_ABI(td->td_proc) != SV_ABI_FREEBSD)
43 		return (0);
44 	uptr = (void *)(uintptr_t)td->td_sa.args[2];
45 	return (copyout(ptr, uptr, sz));
46 }
47 
48 static bool
49 ntsync_wait_any(struct ntsync_wait_state *state)
50 {
51 	struct ntsync_obj *obj;
52 	int i;
53 
54 	MPASS(state->any);
55 	NTSYNC_PRIV_ASSERT(state->owner);
56 
57 	for (i = 0; i < state->obj_count; i++) {
58 		obj = state->objs[i];
59 		if (obj->is_signaled(obj, state, i)) {
60 			state->index = i;
61 			obj->consume(obj, state, state->index);
62 			return (true);
63 		}
64 	}
65 	return (false);
66 }
67 
68 static bool
69 ntsync_wait_all_prepare(struct ntsync_wait_state *state, bool *stop)
70 {
71 	struct ntsync_obj *obj;
72 	int alerti, i;
73 	bool first;
74 
75 	MPASS(state->all);
76 	MPASS(state->error == 0);
77 	MPASS(!*stop);
78 	NTSYNC_PRIV_ASSERT(state->owner);
79 
80 	alerti = state->alert_event == NULL ? 0 : 1;
81 	first = true;
82 
83 	for (i = 0; i < state->obj_count - alerti; i++) {
84 		obj = state->objs[i];
85 		if (!obj->prepare(obj, state, i, stop))
86 			return (false);
87 		if (*stop) {
88 			MPASS(state->error != 0);
89 			return (false);
90 		}
91 		MPASS (state->error == 0);
92 		if (first) {
93 			first = false;
94 			state->index = i;
95 		}
96 	}
97 	return (true);
98 }
99 
100 static void
101 ntsync_wait_all_commit(struct ntsync_wait_state *state)
102 {
103 	struct ntsync_obj *obj;
104 	int i, alerti;
105 
106 	MPASS(state->all);
107 	NTSYNC_PRIV_ASSERT(state->owner);
108 	alerti = state->alert_event == NULL ? 0 : 1;
109 
110 	for (i = 0; i < state->obj_count - alerti; i++) {
111 		obj = state->objs[i];
112 		obj->commit(obj, state, i);
113 	}
114 }
115 
116 static void
117 ntsync_wait_link_waiters(struct ntsync_wait_state *state)
118 {
119 	struct ntsync_obj *obj;
120 	struct ntsync_obj_waiter *waiter;
121 	int i;
122 
123 	NTSYNC_PRIV_ASSERT(state->owner);
124 
125 	for (i = 0; i < state->obj_count; i++) {
126 		obj = state->objs[i];
127 		waiter = &state->waiters[i];
128 		waiter->state = state;
129 		TAILQ_INSERT_TAIL(&obj->waiters, waiter, link);
130 	}
131 }
132 
133 static void
134 ntsync_wait_unlink_waiters(struct ntsync_wait_state *state)
135 {
136 	struct ntsync_obj *obj;
137 	struct ntsync_obj_waiter *waiter;
138 	int i;
139 
140 	NTSYNC_PRIV_ASSERT(state->owner);
141 
142 	for (i = 0; i < state->obj_count; i++) {
143 		obj = state->objs[i];
144 		waiter = &state->waiters[i];
145 		TAILQ_REMOVE(&obj->waiters, waiter, link);
146 	}
147 }
148 
149 static void
150 ntsync_wait_post_commit(struct ntsync_wait_state *state)
151 {
152 	struct ntsync_obj *obj;
153 	int alerti, i;
154 
155 	NTSYNC_PRIV_ASSERT(state->owner);
156 
157 	alerti = state->alert_event == NULL ? 0 : 1;
158 	for (i = 0; i < state->obj_count - alerti; i++) {
159 		obj = state->objs[i];
160 		obj->post_commit(obj, state, i);
161 	}
162 }
163 
164 static void
165 ntsync_wait_check_ready(struct ntsync_wait_state *state)
166 {
167 	struct ntsync_obj *ae;
168 	int index;
169 	bool stop;
170 
171 	NTSYNC_PRIV_ASSERT(state->owner);
172 
173 	if (state->ready)
174 		return;
175 
176 	if (state->all) {
177 		stop = false;
178 		if (ntsync_wait_all_prepare(state, &stop)) {
179 			MPASS(!stop);
180 			ntsync_wait_all_commit(state);
181 			state->ready = true;
182 			ntsync_wait_post_commit(state);
183 		} else if (stop) {
184 			/* skip */
185 		} else if (state->alert_event != NULL) {
186 			ae = &state->alert_event->obj;
187 			index = state->obj_count - 1;
188 			if (ae->is_signaled(ae, state, index)) {
189 				state->index = index;
190 				ae->consume(ae, state, index);
191 				ae->post_commit(ae, state, index);
192 				state->ready = true;
193 			}
194 		}
195 	} else {	/* state->any */
196 		if (ntsync_wait_any(state))
197 			state->ready = true;
198 	}
199 }
200 
201 /*
202  * Perform the wait.  Errors returned through state->error still
203  * result in the copyout of the ntsync_wait_args after the wait, while
204  * errors returned as the function result do not.
205  */
206 static int
207 ntsync_wait_locked(struct ntsync_wait_state *state, struct thread *td)
208 {
209 	int error;
210 
211 	NTSYNC_PRIV_ASSERT(state->owner);
212 
213 	for (;;) {
214 		ntsync_wait_check_ready(state);
215 		if (state->ready)
216 			break;
217 		error = msleep_sbt(state, &state->owner->lock,
218 		    PCATCH, "ntsync", state->sb, state->prec,
219 		    C_ABSOLUTE /* | C_HARDCLOCK XXXKIB */);
220 
221 		/*
222 		 * Check state->ready before checking error from
223 		 * msleep().  If there was a wake up that set the
224 		 * readiness before us receiving a signal or timeout,
225 		 * the objects states are modified to reflect wakeup.
226 		 * Due to this, ready should result in normal return.
227 		 */
228 		if (state->ready) {
229 			error = 0;
230 			break;
231 		}
232 
233 		if (error != 0) {
234 			if (error == EAGAIN)
235 				error = ETIMEDOUT;
236 			break;
237 		}
238 	}
239 	return (error);
240 }
241 
242 static int
243 ntsync_wait(struct ntsync_wait_state *state, struct thread *td)
244 {
245 	int error;
246 
247 	NTSYNC_PRIV_LOCK(state->owner);
248 	ntsync_wait_link_waiters(state);
249 	error = ntsync_wait_locked(state, td);
250 	ntsync_wait_unlink_waiters(state);
251 	NTSYNC_PRIV_UNLOCK(state->owner);
252 	return (error);
253 }
254 
255 static void
256 ntsync_wakeup_waiters(struct ntsync_obj *obj)
257 {
258 	struct ntsync_obj_waiter *w;
259 
260 	NTSYNC_PRIV_ASSERT(obj->owner);
261 
262 	TAILQ_FOREACH(w, &obj->waiters, link) {
263 		ntsync_wait_check_ready(w->state);
264 		if (w->state->ready)
265 			wakeup(w->state);
266 	}
267 }
268 
269 static int
270 ntsync_create_obj(struct ntsync_obj *obj, struct fileops *fops,
271     struct ntsync_priv *priv, struct thread *td)
272 {
273 	struct file *fp;
274 	int error, fd;
275 
276 	error = falloc_noinstall(td, &fp);
277 	if (error != 0)
278 		return (error);
279 
280 	/*
281 	 * The priv fd cannot be closed during object creation since
282 	 * it is fget-ed around ioctl.
283 	 */
284 	obj->owner = priv;
285 
286 	TAILQ_INIT(&obj->waiters);
287 	NTSYNC_PRIV_LOCK(priv);
288 	MPASS(!priv->closed);
289 	if (priv->objs_cnt == UINT_MAX) {
290 		NTSYNC_PRIV_UNLOCK(priv);
291 		fdrop(fp, td);
292 		return (EMFILE);
293 	}
294 	priv->objs_cnt++;
295 	NTSYNC_PRIV_UNLOCK(priv);
296 
297 	finit(fp, FREAD | FWRITE, DTYPE_NTSYNC, obj, fops);
298 	error = finstall(td, fp, &fd, 0, NULL);
299 	if (error != 0) {
300 		finit(fp, FREAD | FWRITE, DTYPE_NONE, NULL, &badfileops);
301 		NTSYNC_PRIV_LOCK(priv);
302 		MPASS(priv->objs_cnt > 0);
303 		priv->objs_cnt--;
304 		NTSYNC_PRIV_UNLOCK(priv);
305 	} else {
306 		td->td_retval[0] = fd;
307 	}
308 	fdrop(fp, td);
309 	return (error);
310 }
311 
312 static void
313 ntsync_close_obj(struct ntsync_obj *obj, struct thread *td)
314 {
315 	struct ntsync_priv *priv;
316 
317 	priv = obj->owner;
318 	NTSYNC_PRIV_LOCK(priv);
319 	MPASS(priv->objs_cnt > 0);
320 	MPASS(TAILQ_EMPTY(&obj->waiters));
321 	priv->objs_cnt--;
322 	NTSYNC_PRIV_UNLOCK(priv);
323 	ntsync_free_priv(priv);
324 }
325 
326 static bool
327 ntsync_sem_is_signaled(struct ntsync_obj *obj, struct ntsync_wait_state *state,
328     int index)
329 {
330 	struct ntsync_obj_sem *sem;
331 
332 	MPASS(obj->type == NTSYNC_OBJ_SEM);
333 	NTSYNC_PRIV_ASSERT(obj->owner);
334 	sem = OBJ_TO_SEM(obj);
335 	return (sem->a.count != 0);
336 }
337 
338 static void
339 ntsync_sem_consume(struct ntsync_obj *obj, struct ntsync_wait_state *state,
340     int index)
341 {
342 	struct ntsync_obj_sem *sem;
343 
344 	MPASS(obj->type == NTSYNC_OBJ_SEM);
345 	NTSYNC_PRIV_ASSERT(obj->owner);
346 	sem = OBJ_TO_SEM(obj);
347 	MPASS(sem->a.count != 0);
348 	sem->a.count--;
349 }
350 
351 static bool
352 ntsync_sem_prepare(struct ntsync_obj *obj, struct ntsync_wait_state *state,
353     int index, bool *stop)
354 {
355 	struct ntsync_obj_sem *sem;
356 
357 	MPASS(obj->type == NTSYNC_OBJ_SEM);
358 	NTSYNC_PRIV_ASSERT(obj->owner);
359 	sem = OBJ_TO_SEM(obj);
360 	if (sem->a.count == 0)
361 		return (false);
362 	sem->a1 = sem->a;
363 	sem->a1.count--;
364 	return (true);
365 }
366 
367 static void
368 ntsync_sem_commit(struct ntsync_obj *obj, struct ntsync_wait_state *state,
369     int index)
370 {
371 	struct ntsync_obj_sem *sem;
372 
373 	MPASS(obj->type == NTSYNC_OBJ_SEM);
374 	NTSYNC_PRIV_ASSERT(obj->owner);
375 	sem = OBJ_TO_SEM(obj);
376 	sem->a = sem->a1;
377 }
378 
379 static void
380 ntsync_sem_post_commit(struct ntsync_obj *obj, struct ntsync_wait_state *state,
381     int index)
382 {
383 }
384 
385 static int
386 ntsync_sem_close(struct file *fp, struct thread *td)
387 {
388 	struct ntsync_obj_sem *sem;
389 
390 	sem = fp->f_data;
391 	ntsync_close_obj(&sem->obj, td);
392 	free(sem, M_NTSYNC);
393 	return (0);
394 }
395 
396 int
397 ntsync_sem_release(struct thread *td, struct file *fp, uint32_t *val)
398 {
399 	struct ntsync_obj *obj;
400 	struct ntsync_obj_sem *sem;
401 	struct ntsync_priv *priv;
402 	uint32_t prev;
403 	int error;
404 
405 	obj = fp->f_data;
406 	if (obj->type != NTSYNC_OBJ_SEM)
407 		return (EINVAL);
408 	sem = OBJ_TO_SEM(obj);
409 	priv = obj->owner;
410 	error = 0;
411 
412 	NTSYNC_PRIV_LOCK(priv);
413 	if (sem->a.count + *val < sem->a.count ||
414 	    sem->a.count + *val > sem->a.max) {
415 		error = EOVERFLOW;
416 	} else {
417 		prev = sem->a.count;
418 		sem->a.count += *val;
419 		if (sem->a.count != 0)
420 			ntsync_wakeup_waiters(obj);
421 		*val = prev;
422 	}
423 	NTSYNC_PRIV_UNLOCK(priv);
424 	return (error);
425 }
426 
427 int
428 ntsync_sem_read(struct thread *td, struct file *fp, struct ntsync_sem_args *a)
429 {
430 	struct ntsync_obj *obj;
431 	struct ntsync_obj_sem *sem;
432 	struct ntsync_priv *priv;
433 
434 	obj = fp->f_data;
435 	if (obj->type != NTSYNC_OBJ_SEM)
436 		return (EINVAL);
437 	sem = OBJ_TO_SEM(obj);
438 	priv = obj->owner;
439 	NTSYNC_PRIV_LOCK(priv);
440 	*a = sem->a;
441 	NTSYNC_PRIV_UNLOCK(priv);
442 	return (0);
443 }
444 
445 static int
446 ntsync_sem_ioctl(struct file *fp, u_long com, void *data,
447     struct ucred *active_cred, struct thread *td)
448 {
449 	int error;
450 
451 	switch (com) {
452 	case NTSYNC_IOC_SEM_RELEASE:
453 		error = ntsync_sem_release(td, fp, data);
454 		break;
455 	case NTSYNC_IOC_SEM_READ:
456 		error = ntsync_sem_read(td, fp, data);
457 		break;
458 	default:
459 		error = ENOTTY;
460 		break;
461 	}
462 	return (error);
463 }
464 
465 static int
466 ntsync_sem_stat(struct file *fp, struct stat *sbp, struct ucred *cred)
467 {
468 	struct ntsync_obj *obj;
469 	struct ntsync_obj_sem *sem;
470 
471 	MPASS(fp->f_type == DTYPE_NTSYNC);
472 	obj = fp->f_data;
473 	MPASS(obj->type == NTSYNC_OBJ_SEM);
474 	sem = OBJ_TO_SEM(obj);
475 
476 	memset(sbp, 0, sizeof(*sbp));
477 	sbp->st_mode = S_IFREG /* XXXKIB */ | S_IRUSR | S_IWUSR;
478 	NTSYNC_PRIV_LOCK(obj->owner);
479 	sbp->st_size = sem->a.max;
480 	sbp->st_nlink = sem->a.count;
481 	NTSYNC_PRIV_UNLOCK(obj->owner);
482 	return (0);
483 }
484 
485 static int
486 ntsync_sem_fill_kinfo(struct file *fp, struct kinfo_file *kif,
487     struct filedesc *fdp)
488 {
489 	struct ntsync_obj *obj;
490 	struct ntsync_obj_sem *sem;
491 
492 	MPASS(fp->f_type == DTYPE_NTSYNC);
493 	obj = fp->f_data;
494 	MPASS(obj->type == NTSYNC_OBJ_SEM);
495 	sem = OBJ_TO_SEM(obj);
496 
497 	kif->kf_type = KF_TYPE_NTSYNC;
498 	kif->kf_un.kf_ntsync.kf_ntsync_type = KF_NTSYNC_TYPE_SEM;
499 	kif->kf_un.kf_ntsync.kf_ntsync_dev = (uintptr_t)obj->owner;
500 	kif->kf_un.kf_ntsync.kf_ntsync_un.kf_ntsync_sem.count = sem->a.count;
501 	kif->kf_un.kf_ntsync.kf_ntsync_un.kf_ntsync_sem.max = sem->a.max;
502 	return (0);
503 }
504 
505 struct fileops ntsync_sem_fops = {
506 	.fo_read = invfo_rdwr,
507 	.fo_write = invfo_rdwr,
508 	.fo_truncate = invfo_truncate,
509 	.fo_ioctl = ntsync_sem_ioctl,
510 	.fo_poll = invfo_poll,
511 	.fo_kqfilter = invfo_kqfilter,
512 	.fo_stat = ntsync_sem_stat,
513 	.fo_close = ntsync_sem_close,
514 	.fo_chmod = invfo_chmod,
515 	.fo_chown = invfo_chown,
516 	.fo_sendfile = invfo_sendfile,
517 	.fo_fill_kinfo = ntsync_sem_fill_kinfo,
518 	.fo_flags = DFLAG_PASSABLE,
519 };
520 
521 static int
522 ntsync_create_sem(struct ntsync_sem_args *args, struct ntsync_priv *priv,
523     struct thread *td)
524 {
525 	struct ntsync_obj_sem *sem;
526 	int error;
527 
528 	if (args->count > args->max)
529 		return (EINVAL);
530 
531 	sem = malloc(sizeof(*sem), M_NTSYNC, M_WAITOK | M_ZERO);
532 	sem->obj.type = NTSYNC_OBJ_SEM;
533 	sem->obj.is_signaled = ntsync_sem_is_signaled;
534 	sem->obj.consume = ntsync_sem_consume;
535 	sem->obj.prepare = ntsync_sem_prepare;
536 	sem->obj.commit = ntsync_sem_commit;
537 	sem->obj.post_commit = ntsync_sem_post_commit;
538 	sem->a = *args;
539 
540 	error = ntsync_create_obj(&sem->obj, &ntsync_sem_fops, priv, td);
541 	if (error != 0)
542 		free(sem, M_NTSYNC);
543 
544 	return (error);
545 }
546 
547 static bool
548 ntsync_mutex_can_lock(struct ntsync_obj_mutex *mutex, uint32_t nwa_owner)
549 {
550 	return (mutex->a.owner == 0 ||
551 	    (mutex->a.owner == nwa_owner && mutex->a.count < UINT32_MAX) ||
552 	    mutex->abandoned);
553 }
554 
555 static bool
556 ntsync_mutex_is_signaled(struct ntsync_obj *obj,
557     struct ntsync_wait_state *state, int index)
558 {
559 	struct ntsync_obj_mutex *mutex;
560 
561 	MPASS(obj->type == NTSYNC_OBJ_MUTEX);
562 	NTSYNC_PRIV_ASSERT(obj->owner);
563 	mutex = OBJ_TO_MUTEX(obj);
564 	return (ntsync_mutex_can_lock(mutex, state->nwa->owner));
565 }
566 
567 static void
568 ntsync_mutex_consume(struct ntsync_obj *obj, struct ntsync_wait_state *state,
569     int index)
570 {
571 	struct ntsync_obj_mutex *mutex;
572 
573 	MPASS(obj->type == NTSYNC_OBJ_MUTEX);
574 	NTSYNC_PRIV_ASSERT(obj->owner);
575 	mutex = OBJ_TO_MUTEX(obj);
576 	MPASS(ntsync_mutex_can_lock(mutex, state->nwa->owner));
577 	if (state->nwa->owner == 0) {
578 		state->error = EINVAL;
579 		return;
580 	}
581 	if (mutex->a.owner == 0 || mutex->abandoned)
582 		mutex->a.count = 1;
583 	else
584 		mutex->a.count++;
585 	mutex->a.owner = state->nwa->owner;
586 	if (mutex->abandoned && state->error == 0)
587 		state->error = EOWNERDEAD;
588 	mutex->abandoned = false;
589 }
590 
591 static bool
592 ntsync_mutex_prepare(struct ntsync_obj *obj, struct ntsync_wait_state *state,
593     int index, bool *stop)
594 {
595 	struct ntsync_obj_mutex *mutex;
596 
597 	MPASS(obj->type == NTSYNC_OBJ_MUTEX);
598 	NTSYNC_PRIV_ASSERT(obj->owner);
599 	mutex = OBJ_TO_MUTEX(obj);
600 	if (!ntsync_mutex_can_lock(mutex, state->nwa->owner))
601 		return (false);
602 	if (state->nwa->owner == 0) {
603 		state->error = EINVAL;
604 		*stop = true;
605 		return (false);
606 	}
607 	mutex->a1 = mutex->a;
608 	if (mutex->a.owner == 0 || mutex->abandoned)
609 		mutex->a1.count = 1;
610 	else
611 		mutex->a1.count++;
612 	mutex->a1.owner = state->nwa->owner;
613 	return (true);
614 }
615 
616 static void
617 ntsync_mutex_commit(struct ntsync_obj *obj, struct ntsync_wait_state *state,
618     int index)
619 {
620 	struct ntsync_obj_mutex *mutex;
621 
622 	MPASS(obj->type == NTSYNC_OBJ_MUTEX);
623 	NTSYNC_PRIV_ASSERT(obj->owner);
624 	mutex = OBJ_TO_MUTEX(obj);
625 	mutex->a = mutex->a1;
626 	if (mutex->abandoned)
627 		state->error = EOWNERDEAD;
628 	mutex->abandoned = false;
629 }
630 
631 static void
632 ntsync_mutex_post_commit(struct ntsync_obj *obj,
633     struct ntsync_wait_state *state, int index)
634 {
635 }
636 
637 static int
638 ntsync_mutex_close(struct file *fp, struct thread *td)
639 {
640 	struct ntsync_obj_mutex *mutex;
641 
642 	mutex = fp->f_data;
643 	ntsync_close_obj(&mutex->obj, td);
644 	free(mutex, M_NTSYNC);
645 	return (0);
646 }
647 
648 int
649 ntsync_mutex_unlock(struct thread *td, struct file *fp,
650     struct ntsync_mutex_args *a)
651 {
652 	struct ntsync_obj *obj;
653 	struct ntsync_obj_mutex *mutex;
654 	struct ntsync_priv *priv;
655 	uint32_t prev;
656 	int error;
657 
658 	obj = fp->f_data;
659 	if (obj->type != NTSYNC_OBJ_MUTEX)
660 		return (EINVAL);
661 	mutex = OBJ_TO_MUTEX(obj);
662 	priv = obj->owner;
663 
664 	NTSYNC_PRIV_LOCK(priv);
665 	if (a->owner == 0) {
666 		error = EINVAL;
667 	} else if (a->owner != mutex->a.owner) {
668 		error = EPERM;
669 	} else {
670 		error = 0;
671 		prev = mutex->a.count;
672 		MPASS(mutex->a.count > 0);
673 		mutex->a.count--;
674 		a->count = prev;
675 		if (mutex->a.count == 0) {
676 			mutex->a.owner = 0;
677 			ntsync_wakeup_waiters(obj);
678 		}
679 	}
680 	NTSYNC_PRIV_UNLOCK(priv);
681 	return (error);
682 }
683 
684 int
685 ntsync_mutex_kill(struct thread *td, struct file *fp, uint32_t val)
686 {
687 	struct ntsync_obj *obj;
688 	struct ntsync_obj_mutex *mutex;
689 	struct ntsync_priv *priv;
690 	int error;
691 
692 	obj = fp->f_data;
693 	if (obj->type != NTSYNC_OBJ_MUTEX)
694 		return (EINVAL);
695 	mutex = OBJ_TO_MUTEX(obj);
696 	priv = obj->owner;
697 
698 	NTSYNC_PRIV_LOCK(priv);
699 	if (val == 0) {
700 		error = EINVAL;
701 	} else if (mutex->a.owner != val) {
702 		error = EPERM;
703 	} else {
704 		error = 0;
705 		mutex->a.owner = 0;
706 		mutex->a.count = 0;
707 		mutex->abandoned = true;
708 		ntsync_wakeup_waiters(obj);
709 	}
710 	NTSYNC_PRIV_UNLOCK(priv);
711 	return (error);
712 }
713 
714 int
715 ntsync_mutex_read(struct thread *td, struct file *fp,
716     struct ntsync_mutex_args *a, bool *doco)
717 {
718 	struct ntsync_obj *obj;
719 	struct ntsync_obj_mutex *mutex;
720 	struct ntsync_priv *priv;
721 	int error;
722 
723 	*doco = false;
724 	obj = fp->f_data;
725 	if (obj->type != NTSYNC_OBJ_MUTEX)
726 		return (EINVAL);
727 	mutex = OBJ_TO_MUTEX(obj);
728 	priv = obj->owner;
729 	error = 0;
730 
731 	NTSYNC_PRIV_LOCK(priv);
732 	*a = mutex->a;
733 	if (mutex->abandoned)
734 		error = EOWNERDEAD;
735 	NTSYNC_PRIV_UNLOCK(priv);
736 	*doco = true;
737 	return (error);
738 }
739 
740 static int
741 ntsync_mutex_ioctl(struct file *fp, u_long com, void *data,
742     struct ucred *active_cred, struct thread *td)
743 {
744 	struct ntsync_mutex_args aa;
745 	int error, error1;
746 	bool doco;
747 
748 	doco = false;
749 	switch (com) {
750 	case NTSYNC_IOC_MUTEX_UNLOCK:
751 		error = ntsync_mutex_unlock(td, fp, data);
752 		break;
753 	case NTSYNC_IOC_MUTEX_KILL:
754 		error = ntsync_mutex_kill(td, fp, *(uint32_t *)data);
755 		break;
756 	case NTSYNC_IOC_MUTEX_READ:
757 		error = ntsync_mutex_read(td, fp, &aa, &doco);
758 		if (doco) {
759 			error1 = ntsync_ioctl_copyout(td, &aa, sizeof(aa));
760 			if (error1 != 0)
761 				error = error1;
762 		}
763 		break;
764 	default:
765 		error = ENOTTY;
766 		break;
767 	}
768 	return (error);
769 }
770 
771 static int
772 ntsync_mutex_stat(struct file *fp, struct stat *sbp, struct ucred *cred)
773 {
774 	struct ntsync_obj *obj;
775 	struct ntsync_obj_mutex *mutex;
776 
777 	MPASS(fp->f_type == DTYPE_NTSYNC);
778 	obj = fp->f_data;
779 	MPASS(obj->type == NTSYNC_OBJ_MUTEX);
780 	mutex = OBJ_TO_MUTEX(obj);
781 
782 	memset(sbp, 0, sizeof(*sbp));
783 	sbp->st_mode = S_IFREG /* XXXKIB */ | S_IRUSR | S_IWUSR;
784 	NTSYNC_PRIV_LOCK(obj->owner);
785 	sbp->st_size = mutex->a.owner;
786 	sbp->st_nlink = mutex->a.count;
787 	NTSYNC_PRIV_UNLOCK(obj->owner);
788 	return (0);
789 }
790 
791 static int
792 ntsync_mutex_fill_kinfo(struct file *fp, struct kinfo_file *kif,
793     struct filedesc *fdp)
794 {
795 	struct ntsync_obj *obj;
796 	struct ntsync_obj_mutex *mutex;
797 
798 	MPASS(fp->f_type == DTYPE_NTSYNC);
799 	obj = fp->f_data;
800 	MPASS(obj->type == NTSYNC_OBJ_MUTEX);
801 	mutex = OBJ_TO_MUTEX(obj);
802 
803 	kif->kf_type = KF_TYPE_NTSYNC;
804 	kif->kf_un.kf_ntsync.kf_ntsync_type = KF_NTSYNC_TYPE_MUTEX;
805 	kif->kf_un.kf_ntsync.kf_ntsync_dev = (uintptr_t)obj->owner;
806 	kif->kf_un.kf_ntsync.kf_ntsync_un.kf_ntsync_mutex.owner =
807 	    mutex->a.owner;
808 	kif->kf_un.kf_ntsync.kf_ntsync_un.kf_ntsync_mutex.count =
809 	    mutex->a.count;
810 	return (0);
811 }
812 
813 struct fileops ntsync_mutex_fops = {
814 	.fo_read = invfo_rdwr,
815 	.fo_write = invfo_rdwr,
816 	.fo_truncate = invfo_truncate,
817 	.fo_ioctl = ntsync_mutex_ioctl,
818 	.fo_poll = invfo_poll,
819 	.fo_kqfilter = invfo_kqfilter,
820 	.fo_stat = ntsync_mutex_stat,
821 	.fo_close = ntsync_mutex_close,
822 	.fo_chmod = invfo_chmod,
823 	.fo_chown = invfo_chown,
824 	.fo_sendfile = invfo_sendfile,
825 	.fo_fill_kinfo = ntsync_mutex_fill_kinfo,
826 	.fo_flags = DFLAG_PASSABLE,
827 };
828 
829 static int
830 ntsync_create_mutex(struct ntsync_mutex_args *args, struct ntsync_priv *priv,
831     struct thread *td)
832 {
833 	struct ntsync_obj_mutex *mutex;
834 	int error;
835 
836 	if ((args->owner != 0 && args->count == 0) ||
837 	    (args->owner == 0 && args->count != 0))
838 		return (EINVAL);
839 
840 	mutex = malloc(sizeof(*mutex), M_NTSYNC, M_WAITOK | M_ZERO);
841 	mutex->obj.type = NTSYNC_OBJ_MUTEX;
842 	mutex->obj.is_signaled = ntsync_mutex_is_signaled;
843 	mutex->obj.consume = ntsync_mutex_consume;
844 	mutex->obj.prepare = ntsync_mutex_prepare;
845 	mutex->obj.commit = ntsync_mutex_commit;
846 	mutex->obj.post_commit = ntsync_mutex_post_commit;
847 	mutex->a = *args;
848 	mutex->abandoned = false;
849 
850 	error = ntsync_create_obj(&mutex->obj, &ntsync_mutex_fops, priv, td);
851 	if (error != 0)
852 		free(mutex, M_NTSYNC);
853 
854 	return (error);
855 }
856 
857 static bool
858 ntsync_event_is_signaled(struct ntsync_obj *obj,
859     struct ntsync_wait_state *state, int index)
860 {
861 	struct ntsync_obj_event *event;
862 
863 	MPASS(obj->type == NTSYNC_OBJ_EVENT);
864 	NTSYNC_PRIV_ASSERT(obj->owner);
865 	event = OBJ_TO_EVENT(obj);
866 	return (event->a.signaled != 0);
867 }
868 
869 static void
870 ntsync_event_consume(struct ntsync_obj *obj, struct ntsync_wait_state *state,
871     int index)
872 {
873 	struct ntsync_obj_event *event;
874 
875 	MPASS(obj->type == NTSYNC_OBJ_EVENT);
876 	NTSYNC_PRIV_ASSERT(obj->owner);
877 	MPASS(ntsync_event_is_signaled(obj, state, index));
878 
879 	event = OBJ_TO_EVENT(obj);
880 	if (event->a.manual == 0)
881 		event->a.signaled = 0;
882 }
883 
884 static bool
885 ntsync_event_prepare(struct ntsync_obj *obj, struct ntsync_wait_state *state,
886     int index, bool *stop)
887 {
888 	struct ntsync_obj_event *event;
889 
890 	MPASS(obj->type == NTSYNC_OBJ_EVENT);
891 	NTSYNC_PRIV_ASSERT(obj->owner);
892 	event = OBJ_TO_EVENT(obj);
893 	if (!ntsync_event_is_signaled(obj, state, index))
894 		return (false);
895 	event->a1 = event->a;
896 	return (true);
897 }
898 
899 static void
900 ntsync_event_commit(struct ntsync_obj *obj, struct ntsync_wait_state *state,
901     int index)
902 {
903 	struct ntsync_obj_event *event;
904 
905 	MPASS(obj->type == NTSYNC_OBJ_EVENT);
906 	NTSYNC_PRIV_ASSERT(obj->owner);
907 	event = OBJ_TO_EVENT(obj);
908 	event->a = event->a1;
909 	if (event->pulse && event->a.manual == 0) {
910 		event->a.signaled = 0;
911 		event->pulse = false;
912 	}
913 }
914 
915 static void
916 ntsync_event_post_commit(struct ntsync_obj *obj,
917     struct ntsync_wait_state *state, int index)
918 {
919 	struct ntsync_obj_event *event;
920 
921 	MPASS(obj->type == NTSYNC_OBJ_EVENT);
922 	NTSYNC_PRIV_ASSERT(obj->owner);
923 	event = OBJ_TO_EVENT(obj);
924 	if (event->a.manual == 0)
925 		event->a.signaled = 0;
926 }
927 
928 static int
929 ntsync_event_close(struct file *fp, struct thread *td)
930 {
931 	struct ntsync_obj_event *event;
932 
933 	event = fp->f_data;
934 	ntsync_close_obj(&event->obj, td);
935 	free(event, M_NTSYNC);
936 	return (0);
937 }
938 
939 int
940 ntsync_event_set(struct thread *td, struct file *fp, uint32_t *val)
941 {
942 	struct ntsync_obj *obj;
943 	struct ntsync_obj_event *event;
944 	struct ntsync_priv *priv;
945 	uint32_t prev;
946 
947 	obj = fp->f_data;
948 	if (obj->type != NTSYNC_OBJ_EVENT)
949 		return (EINVAL);
950 	event = OBJ_TO_EVENT(obj);
951 	priv = obj->owner;
952 
953 	NTSYNC_PRIV_LOCK(priv);
954 	prev = event->a.signaled;
955 	event->a.signaled = 1;
956 	ntsync_wakeup_waiters(obj);
957 	NTSYNC_PRIV_UNLOCK(priv);
958 
959 	*val = prev;
960 	return (0);
961 }
962 
963 int
964 ntsync_event_reset(struct thread *td, struct file *fp, uint32_t *val)
965 {
966 	struct ntsync_obj *obj;
967 	struct ntsync_obj_event *event;
968 	struct ntsync_priv *priv;
969 	uint32_t prev;
970 
971 	obj = fp->f_data;
972 	if (obj->type != NTSYNC_OBJ_EVENT)
973 		return (EINVAL);
974 	event = OBJ_TO_EVENT(obj);
975 	priv = obj->owner;
976 
977 	NTSYNC_PRIV_LOCK(priv);
978 	prev = event->a.signaled;
979 	event->a.signaled = 0;
980 	NTSYNC_PRIV_UNLOCK(priv);
981 
982 	*val = prev;
983 	return (0);
984 }
985 
986 int
987 ntsync_event_pulse(struct thread *td, struct file *fp, uint32_t *val)
988 {
989 	struct ntsync_obj *obj;
990 	struct ntsync_obj_event *event;
991 	struct ntsync_priv *priv;
992 	uint32_t prev;
993 
994 	obj = fp->f_data;
995 	if (obj->type != NTSYNC_OBJ_EVENT)
996 		return (EINVAL);
997 	event = OBJ_TO_EVENT(obj);
998 	priv = obj->owner;
999 
1000 	NTSYNC_PRIV_LOCK(priv);
1001 	prev = event->a.signaled;
1002 	event->a.signaled = 1;
1003 	event->pulse = true;
1004 	ntsync_wakeup_waiters(obj);
1005 	event->a.signaled = 0;
1006 	event->pulse = false;
1007 	NTSYNC_PRIV_UNLOCK(priv);
1008 
1009 	*val = prev;
1010 	return (0);
1011 }
1012 
1013 int
1014 ntsync_event_read(struct thread *td, struct file *fp,
1015     struct ntsync_event_args *a)
1016 {
1017 	struct ntsync_obj *obj;
1018 	struct ntsync_obj_event *event;
1019 	struct ntsync_priv *priv;
1020 
1021 	obj = fp->f_data;
1022 	if (obj->type != NTSYNC_OBJ_EVENT)
1023 		return (EINVAL);
1024 	event = OBJ_TO_EVENT(obj);
1025 	priv = obj->owner;
1026 
1027 	NTSYNC_PRIV_LOCK(priv);
1028 	*a = event->a;
1029 	NTSYNC_PRIV_UNLOCK(priv);
1030 
1031 	return (0);
1032 }
1033 
1034 static int
1035 ntsync_event_ioctl(struct file *fp, u_long com, void *data,
1036     struct ucred *active_cred, struct thread *td)
1037 {
1038 	int error;
1039 
1040 	switch (com) {
1041 	case NTSYNC_IOC_EVENT_SET:
1042 		error = ntsync_event_set(td, fp, data);
1043 		break;
1044 	case NTSYNC_IOC_EVENT_RESET:
1045 		error = ntsync_event_reset(td, fp, data);
1046 		break;
1047 	case NTSYNC_IOC_EVENT_PULSE:
1048 		error = ntsync_event_pulse(td, fp, data);
1049 		break;
1050 	case NTSYNC_IOC_EVENT_READ:
1051 		error = ntsync_event_read(td, fp, data);
1052 		break;
1053 	default:
1054 		error = ENOTTY;
1055 		break;
1056 	}
1057 	return (error);
1058 }
1059 
1060 static int
1061 ntsync_event_stat(struct file *fp, struct stat *sbp, struct ucred *cred)
1062 {
1063 	struct ntsync_obj *obj;
1064 	struct ntsync_obj_event *event;
1065 
1066 	MPASS(fp->f_type == DTYPE_NTSYNC);
1067 	obj = fp->f_data;
1068 	MPASS(obj->type == NTSYNC_OBJ_EVENT);
1069 	event = OBJ_TO_EVENT(obj);
1070 
1071 	memset(sbp, 0, sizeof(*sbp));
1072 	sbp->st_mode = S_IFREG /* XXXKIB */ | S_IRUSR | S_IWUSR;
1073 	NTSYNC_PRIV_LOCK(obj->owner);
1074 	sbp->st_size = event->a.signaled;
1075 	sbp->st_nlink = event->a.manual;
1076 	NTSYNC_PRIV_UNLOCK(obj->owner);
1077 	return (0);
1078 }
1079 
1080 static int
1081 ntsync_event_fill_kinfo(struct file *fp, struct kinfo_file *kif,
1082     struct filedesc *fdp)
1083 {
1084 	struct ntsync_obj *obj;
1085 	struct ntsync_obj_event *event;
1086 
1087 	MPASS(fp->f_type == DTYPE_NTSYNC);
1088 	obj = fp->f_data;
1089 	MPASS(obj->type == NTSYNC_OBJ_EVENT);
1090 	event = OBJ_TO_EVENT(obj);
1091 
1092 	kif->kf_type = KF_TYPE_NTSYNC;
1093 	kif->kf_un.kf_ntsync.kf_ntsync_type = KF_NTSYNC_TYPE_EVENT;
1094 	kif->kf_un.kf_ntsync.kf_ntsync_dev = (uintptr_t)obj->owner;
1095 	kif->kf_un.kf_ntsync.kf_ntsync_un.kf_ntsync_event.signaled =
1096 		event->a.signaled;
1097 	kif->kf_un.kf_ntsync.kf_ntsync_un.kf_ntsync_event.manual =
1098 		event->a.manual;
1099 	return (0);
1100 }
1101 
1102 struct fileops ntsync_event_fops = {
1103 	.fo_read = invfo_rdwr,
1104 	.fo_write = invfo_rdwr,
1105 	.fo_truncate = invfo_truncate,
1106 	.fo_ioctl = ntsync_event_ioctl,
1107 	.fo_poll = invfo_poll,
1108 	.fo_kqfilter = invfo_kqfilter,
1109 	.fo_stat = ntsync_event_stat,
1110 	.fo_close = ntsync_event_close,
1111 	.fo_chmod = invfo_chmod,
1112 	.fo_chown = invfo_chown,
1113 	.fo_sendfile = invfo_sendfile,
1114 	.fo_fill_kinfo = ntsync_event_fill_kinfo,
1115 	.fo_flags = DFLAG_PASSABLE,
1116 };
1117 
1118 static int
1119 ntsync_create_event(struct ntsync_event_args *args, struct ntsync_priv *priv,
1120     struct thread *td)
1121 {
1122 	struct ntsync_obj_event *event;
1123 	int error;
1124 
1125 	event = malloc(sizeof(*event), M_NTSYNC, M_WAITOK | M_ZERO);
1126 	event->obj.type = NTSYNC_OBJ_EVENT;
1127 	event->obj.is_signaled = ntsync_event_is_signaled;
1128 	event->obj.consume = ntsync_event_consume;
1129 	event->obj.prepare = ntsync_event_prepare;
1130 	event->obj.commit = ntsync_event_commit;
1131 	event->obj.post_commit = ntsync_event_post_commit;
1132 	event->a = *args;
1133 
1134 	error = ntsync_create_obj(&event->obj, &ntsync_event_fops, priv, td);
1135 	if (error != 0)
1136 		free(event, M_NTSYNC);
1137 
1138 	return (error);
1139 }
1140 
1141 static void
1142 ntsync_free_priv(struct ntsync_priv *priv)
1143 {
1144 	bool do_free;
1145 
1146 	NTSYNC_PRIV_LOCK(priv);
1147 	do_free = priv->closed && priv->objs_cnt == 0;
1148 	NTSYNC_PRIV_UNLOCK(priv);
1149 	if (do_free) {
1150 		mtx_destroy(&priv->lock);
1151 		free(priv, M_NTSYNC);
1152 	}
1153 }
1154 
1155 static void
1156 ntsync_priv_dtr(void *data)
1157 {
1158 	ntsync_free_priv(data);
1159 }
1160 
1161 static int
1162 ntsync_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
1163 {
1164 	struct ntsync_priv *priv;
1165 
1166 	priv = malloc(sizeof(*priv), M_NTSYNC, M_WAITOK);
1167 	priv->closed = false;
1168 	priv->objs_cnt = 0;
1169 	mtx_init(&priv->lock, "ntsync", "ntsync", MTX_DEF | MTX_NEW);
1170 	devfs_set_cdevpriv(priv, ntsync_priv_dtr);
1171 	return (0);
1172 }
1173 
1174 static int
1175 ntsync_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
1176 {
1177 	struct ntsync_priv *priv;
1178 	void *a;
1179 	int error;
1180 
1181 	error = devfs_get_cdevpriv(&a);
1182 	if (error == 0) {
1183 		priv = a;
1184 		NTSYNC_PRIV_LOCK(priv);
1185 		priv->closed = true;
1186 		NTSYNC_PRIV_UNLOCK(priv);
1187 	}
1188 	devfs_clear_cdevpriv();
1189 	return (0);
1190 }
1191 
1192 static int
1193 ntsync_wait_state_get(struct ntsync_wait_args *nwa, u_long cmd,
1194     struct ntsync_priv *owner, struct ntsync_wait_state **statep,
1195     struct thread *td)
1196 {
1197 	struct ntsync_wait_state *state;
1198 	struct ntsync_obj *obj;
1199 	int error, i, j;
1200 
1201 	if (nwa->count > NTSYNC_MAX_WAIT_COUNT)
1202 		return (EINVAL);
1203 	if ((nwa->flags & ~NTSYNC_WAIT_REALTIME) != 0)
1204 		return (EINVAL);
1205 
1206 	state = malloc(sizeof(*state), M_NTSYNC, M_WAITOK | M_ZERO);
1207 	state->nwa = nwa;
1208 	state->owner = owner;
1209 	state->all = cmd == NTSYNC_IOC_WAIT_ALL;
1210 	state->any = !state->all;
1211 	error = copyin((void *)(uintptr_t)nwa->objs, &state->fds[0],
1212 	    nwa->count * sizeof(state->fds[0]));
1213 	if (error != 0)
1214 		goto error_ret;
1215 
1216 	i = 0;
1217 	if (nwa->alert != 0) {
1218 		error = fget_cap(td, nwa->alert, &cap_no_rights, NULL,
1219 		    &state->fp_alert, NULL);
1220 		if (error != 0) {
1221 			state->fp_alert = NULL;
1222 			goto error_out;
1223 		}
1224 		if (state->fp_alert->f_type != DTYPE_NTSYNC) {
1225 			error = EINVAL;
1226 			goto error_out;
1227 		}
1228 		obj = state->fp_alert->f_data;
1229 		if (obj->type != NTSYNC_OBJ_EVENT || obj->owner != owner) {
1230 			error = EINVAL;
1231 			goto error_out;
1232 		}
1233 		state->alert_event = OBJ_TO_EVENT(obj);
1234 	}
1235 
1236 	for (; i < nwa->count; i++) {
1237 		error = fget_cap(td, state->fds[i], &cap_no_rights, NULL,
1238 		    &state->fps[i], NULL);
1239 		if (error != 0) {
1240 			state->fps[i] = NULL;
1241 			goto error_out;
1242 		}
1243 		if (state->fps[i]->f_type != DTYPE_NTSYNC ||
1244 		    (obj = state->fps[i]->f_data)->owner != owner) {
1245 			i++;
1246 			error = EINVAL;
1247 			goto error_out;
1248 		}
1249 	}
1250 
1251 	state->obj_count = nwa->count;
1252 	for (i = 0; i < nwa->count; i++)
1253 		state->objs[i] = state->fps[i]->f_data;
1254 	if (state->alert_event != NULL) {
1255 		state->objs[i] = &state->alert_event->obj;
1256 		state->obj_count++;
1257 	}
1258 
1259 	if (state->all) {
1260 		/* Check no dups */
1261 		for (i = 0; i < state->obj_count; i++) {
1262 			obj = state->objs[i];
1263 			for (j = i + 1; j < state->obj_count; j++) {
1264 				if (obj == state->objs[j]) {
1265 					i = state->obj_count;
1266 					error = EINVAL;
1267 					goto error_out;
1268 				}
1269 			}
1270 		}
1271 	}
1272 
1273 	state->prec = 0;
1274 	if (nwa->timeout == UINT64_MAX) {
1275 		state->sb = 0;
1276 	} else {
1277 		state->sb = nstosbt(nwa->timeout);
1278 		if ((nwa->flags & NTSYNC_WAIT_REALTIME) != 0) {
1279 			struct bintime btb;
1280 
1281 			bintime(&btb);
1282 			state->sb -= bttosbt(btb);
1283 		} else {
1284 			struct timespec ts;
1285 
1286 			nanouptime(&ts);
1287 			state->sb -= tstosbt(ts);
1288 		}
1289 		state->sb += sbinuptime();
1290 	}
1291 
1292 	*statep = state;
1293 	return (0);
1294 
1295 error_out:
1296 	for (j = 0; j < i; j++)
1297 		fdrop(state->fps[j], td);
1298 	if (state->fp_alert != NULL)
1299 		fdrop(state->fp_alert, td);
1300 error_ret:
1301 	free(state, M_NTSYNC);
1302 	return (error);
1303 }
1304 
1305 static void
1306 ntsync_wait_state_put(struct ntsync_wait_state *state, struct thread *td)
1307 {
1308 	int i;
1309 
1310 	for (i = 0; i < state->nwa->count; i++)
1311 		fdrop(state->fps[i], td);
1312 	if (state->fp_alert != NULL)
1313 		fdrop(state->fp_alert, td);
1314 	free(state, M_NTSYNC);
1315 }
1316 
1317 static int
1318 ntsync_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
1319     struct thread *td)
1320 {
1321 	struct ntsync_priv *owner;
1322 	struct ntsync_wait_args *nwa;
1323 	struct ntsync_wait_state *state;
1324 	void *a;
1325 	int error;
1326 
1327 	error = devfs_get_cdevpriv(&a);
1328 	if (error != 0)
1329 		return (error);
1330 	owner = a;
1331 
1332 	switch (cmd) {
1333 	case NTSYNC_IOC_CREATE_SEM:
1334 		error = ntsync_create_sem((struct ntsync_sem_args *)data,
1335 		    owner, td);
1336 		break;
1337 	case NTSYNC_IOC_CREATE_MUTEX:
1338 		error = ntsync_create_mutex((struct ntsync_mutex_args *)data,
1339 		    owner, td);
1340 		break;
1341 	case NTSYNC_IOC_CREATE_EVENT:
1342 		error = ntsync_create_event((struct ntsync_event_args *)data,
1343 		    owner, td);
1344 		break;
1345 	case NTSYNC_IOC_WAIT_ANY:
1346 		nwa = (struct ntsync_wait_args *)data;
1347 		error = ntsync_wait_state_get(nwa, cmd, owner, &state, td);
1348 		if (error != 0)
1349 			break;
1350 		error = ntsync_wait(state, td);
1351 		if (error == 0) {
1352 			nwa->index = state->index;
1353 			error = ntsync_ioctl_copyout(td, nwa, sizeof(*nwa));
1354 			if (error == 0)
1355 				error = state->error;
1356 		}
1357 		ntsync_wait_state_put(state, td);
1358 		break;
1359 	case NTSYNC_IOC_WAIT_ALL:
1360 		nwa = (struct ntsync_wait_args *)data;
1361 		error = ntsync_wait_state_get(nwa, cmd, owner, &state, td);
1362 		if (error != 0)
1363 			break;
1364 		error = ntsync_wait(state, td);
1365 		if (error == 0) {
1366 			nwa->index = state->index;
1367 			error = ntsync_ioctl_copyout(td, nwa, sizeof(*nwa));
1368 			if (error == 0)
1369 				error = state->error;
1370 		}
1371 		ntsync_wait_state_put(state, td);
1372 		break;
1373 
1374 	default:
1375 		error = ENOTTY;
1376 		break;
1377 	}
1378 	return (error);
1379 }
1380 
1381 struct cdevsw ntsync_cdevsw = {
1382 	.d_version =	D_VERSION,
1383 	.d_flags =	0,
1384 	.d_open =	ntsync_open,
1385 	.d_close =	ntsync_close,
1386 	.d_ioctl =	ntsync_ioctl,
1387 	.d_name =	"ntsync",
1388 };
1389 
1390 static int
1391 ntsync_modevent(module_t mod __unused, int type, void *data __unused)
1392 {
1393 	struct make_dev_args mda;
1394 	int error;
1395 
1396 	error = 0;
1397 	switch (type) {
1398 	case MOD_LOAD:
1399 		make_dev_args_init(&mda);
1400 		mda.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME;
1401 		mda.mda_devsw = &ntsync_cdevsw;
1402 		mda.mda_uid = UID_ROOT;
1403 		mda.mda_gid = GID_GAMES;
1404 		mda.mda_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP |
1405 		    S_IROTH | S_IWOTH;
1406 
1407 		error = make_dev_s(&mda, &ntsync_cdev, "ntsync");
1408 		if (error != 0) {
1409 			printf("cannot create ntsync dev err %d\n", error);
1410 			break;
1411 		}
1412 		if (bootverbose)
1413 			printf("ntsync\n");
1414 		break;
1415 
1416 	case MOD_UNLOAD:
1417 		destroy_dev(ntsync_cdev);
1418 		break;
1419 
1420 	case MOD_SHUTDOWN:
1421 		break;
1422 
1423 	default:
1424 		error = EOPNOTSUPP;
1425 	}
1426 
1427 	return (error);
1428 }
1429 
1430 DEV_MODULE(ntsync, ntsync_modevent, NULL);
1431 MODULE_VERSION(ntsync, 1);
1432