xref: /freebsd/sys/dev/ntsync/ntsync.c (revision 73e0d6b44038d1c7764c5013a54ae17a8f680a69)
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, 0,
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 		NTSYNC_PRIV_LOCK(priv);
301 		MPASS(priv->objs_cnt > 0);
302 		priv->objs_cnt--;
303 		NTSYNC_PRIV_UNLOCK(priv);
304 	} else {
305 		td->td_retval[0] = fd;
306 	}
307 	fdrop(fp, td);
308 	return (error);
309 }
310 
311 static void
312 ntsync_close_obj(struct ntsync_obj *obj, struct thread *td)
313 {
314 	struct ntsync_priv *priv;
315 
316 	priv = obj->owner;
317 	NTSYNC_PRIV_LOCK(priv);
318 	MPASS(priv->objs_cnt > 0);
319 	MPASS(TAILQ_EMPTY(&obj->waiters));
320 	priv->objs_cnt--;
321 	NTSYNC_PRIV_UNLOCK(priv);
322 	ntsync_free_priv(priv);
323 }
324 
325 static bool
326 ntsync_sem_is_signaled(struct ntsync_obj *obj, struct ntsync_wait_state *state,
327     int index)
328 {
329 	struct ntsync_obj_sem *sem;
330 
331 	MPASS(obj->type == NTSYNC_OBJ_SEM);
332 	NTSYNC_PRIV_ASSERT(obj->owner);
333 	sem = OBJ_TO_SEM(obj);
334 	return (sem->a.count != 0);
335 }
336 
337 static void
338 ntsync_sem_consume(struct ntsync_obj *obj, struct ntsync_wait_state *state,
339     int index)
340 {
341 	struct ntsync_obj_sem *sem;
342 
343 	MPASS(obj->type == NTSYNC_OBJ_SEM);
344 	NTSYNC_PRIV_ASSERT(obj->owner);
345 	sem = OBJ_TO_SEM(obj);
346 	MPASS(sem->a.count != 0);
347 	sem->a.count--;
348 }
349 
350 static bool
351 ntsync_sem_prepare(struct ntsync_obj *obj, struct ntsync_wait_state *state,
352     int index, bool *stop)
353 {
354 	struct ntsync_obj_sem *sem;
355 
356 	MPASS(obj->type == NTSYNC_OBJ_SEM);
357 	NTSYNC_PRIV_ASSERT(obj->owner);
358 	sem = OBJ_TO_SEM(obj);
359 	if (sem->a.count == 0)
360 		return (false);
361 	sem->a1 = sem->a;
362 	sem->a1.count--;
363 	return (true);
364 }
365 
366 static void
367 ntsync_sem_commit(struct ntsync_obj *obj, struct ntsync_wait_state *state,
368     int index)
369 {
370 	struct ntsync_obj_sem *sem;
371 
372 	MPASS(obj->type == NTSYNC_OBJ_SEM);
373 	NTSYNC_PRIV_ASSERT(obj->owner);
374 	sem = OBJ_TO_SEM(obj);
375 	sem->a = sem->a1;
376 }
377 
378 static void
379 ntsync_sem_post_commit(struct ntsync_obj *obj, struct ntsync_wait_state *state,
380     int index)
381 {
382 }
383 
384 static int
385 ntsync_sem_close(struct file *fp, struct thread *td)
386 {
387 	struct ntsync_obj_sem *sem;
388 
389 	sem = fp->f_data;
390 	ntsync_close_obj(&sem->obj, td);
391 	free(sem, M_NTSYNC);
392 	return (0);
393 }
394 
395 int
396 ntsync_sem_release(struct thread *td, struct file *fp, uint32_t *val)
397 {
398 	struct ntsync_obj *obj;
399 	struct ntsync_obj_sem *sem;
400 	struct ntsync_priv *priv;
401 	uint32_t prev;
402 	int error;
403 
404 	obj = fp->f_data;
405 	if (obj->type != NTSYNC_OBJ_SEM)
406 		return (EINVAL);
407 	sem = OBJ_TO_SEM(obj);
408 	priv = obj->owner;
409 	error = 0;
410 
411 	NTSYNC_PRIV_LOCK(priv);
412 	if (sem->a.count + *val < sem->a.count ||
413 	    sem->a.count + *val > sem->a.max) {
414 		error = EOVERFLOW;
415 	} else {
416 		prev = sem->a.count;
417 		sem->a.count += *val;
418 		if (sem->a.count != 0)
419 			ntsync_wakeup_waiters(obj);
420 		*val = prev;
421 	}
422 	NTSYNC_PRIV_UNLOCK(priv);
423 	return (error);
424 }
425 
426 int
427 ntsync_sem_read(struct thread *td, struct file *fp, struct ntsync_sem_args *a)
428 {
429 	struct ntsync_obj *obj;
430 	struct ntsync_obj_sem *sem;
431 	struct ntsync_priv *priv;
432 
433 	obj = fp->f_data;
434 	if (obj->type != NTSYNC_OBJ_SEM)
435 		return (EINVAL);
436 	sem = OBJ_TO_SEM(obj);
437 	priv = obj->owner;
438 	NTSYNC_PRIV_LOCK(priv);
439 	*a = sem->a;
440 	NTSYNC_PRIV_UNLOCK(priv);
441 	return (0);
442 }
443 
444 static int
445 ntsync_sem_ioctl(struct file *fp, u_long com, void *data,
446     struct ucred *active_cred, struct thread *td)
447 {
448 	int error;
449 
450 	switch (com) {
451 	case NTSYNC_IOC_SEM_RELEASE:
452 		error = ntsync_sem_release(td, fp, data);
453 		break;
454 	case NTSYNC_IOC_SEM_READ:
455 		error = ntsync_sem_read(td, fp, data);
456 		break;
457 	default:
458 		error = ENOTTY;
459 		break;
460 	}
461 	return (error);
462 }
463 
464 static int
465 ntsync_sem_stat(struct file *fp, struct stat *sbp, struct ucred *cred)
466 {
467 	struct ntsync_obj *obj;
468 	struct ntsync_obj_sem *sem;
469 
470 	MPASS(fp->f_type == DTYPE_NTSYNC);
471 	obj = fp->f_data;
472 	MPASS(obj->type == NTSYNC_OBJ_SEM);
473 	sem = OBJ_TO_SEM(obj);
474 
475 	memset(sbp, 0, sizeof(*sbp));
476 	sbp->st_mode = S_IFREG /* XXXKIB */ | S_IRUSR | S_IWUSR;
477 	NTSYNC_PRIV_LOCK(obj->owner);
478 	sbp->st_size = sem->a.max;
479 	sbp->st_nlink = sem->a.count;
480 	NTSYNC_PRIV_UNLOCK(obj->owner);
481 	return (0);
482 }
483 
484 static int
485 ntsync_sem_fill_kinfo(struct file *fp, struct kinfo_file *kif,
486     struct filedesc *fdp)
487 {
488 	struct ntsync_obj *obj;
489 	struct ntsync_obj_sem *sem;
490 
491 	MPASS(fp->f_type == DTYPE_NTSYNC);
492 	obj = fp->f_data;
493 	MPASS(obj->type == NTSYNC_OBJ_SEM);
494 	sem = OBJ_TO_SEM(obj);
495 
496 	kif->kf_type = KF_TYPE_NTSYNC;
497 	kif->kf_un.kf_ntsync.kf_ntsync_type = KF_NTSYNC_TYPE_SEM;
498 	kif->kf_un.kf_ntsync.kf_ntsync_dev = (uintptr_t)obj->owner;
499 	kif->kf_un.kf_ntsync.kf_ntsync_un.kf_ntsync_sem.count = sem->a.count;
500 	kif->kf_un.kf_ntsync.kf_ntsync_un.kf_ntsync_sem.max = sem->a.max;
501 	return (0);
502 }
503 
504 struct fileops ntsync_sem_fops = {
505 	.fo_read = invfo_rdwr,
506 	.fo_write = invfo_rdwr,
507 	.fo_truncate = invfo_truncate,
508 	.fo_ioctl = ntsync_sem_ioctl,
509 	.fo_poll = invfo_poll,
510 	.fo_kqfilter = invfo_kqfilter,
511 	.fo_stat = ntsync_sem_stat,
512 	.fo_close = ntsync_sem_close,
513 	.fo_chmod = invfo_chmod,
514 	.fo_chown = invfo_chown,
515 	.fo_sendfile = invfo_sendfile,
516 	.fo_fill_kinfo = ntsync_sem_fill_kinfo,
517 	.fo_flags = DFLAG_PASSABLE,
518 };
519 
520 static int
521 ntsync_create_sem(struct ntsync_sem_args *args, struct ntsync_priv *priv,
522     struct thread *td)
523 {
524 	struct ntsync_obj_sem *sem;
525 	int error;
526 
527 	if (args->count > args->max)
528 		return (EINVAL);
529 
530 	sem = malloc(sizeof(*sem), M_NTSYNC, M_WAITOK | M_ZERO);
531 	sem->obj.type = NTSYNC_OBJ_SEM;
532 	sem->obj.is_signaled = ntsync_sem_is_signaled;
533 	sem->obj.consume = ntsync_sem_consume;
534 	sem->obj.prepare = ntsync_sem_prepare;
535 	sem->obj.commit = ntsync_sem_commit;
536 	sem->obj.post_commit = ntsync_sem_post_commit;
537 	sem->a = *args;
538 
539 	error = ntsync_create_obj(&sem->obj, &ntsync_sem_fops, priv, td);
540 	if (error != 0)
541 		free(sem, M_NTSYNC);
542 
543 	return (error);
544 }
545 
546 static bool
547 ntsync_mutex_can_lock(struct ntsync_obj_mutex *mutex, uint32_t nwa_owner)
548 {
549 	return (mutex->a.owner == 0 ||
550 	    (mutex->a.owner == nwa_owner && mutex->a.count < UINT32_MAX) ||
551 	    mutex->abandoned);
552 }
553 
554 static bool
555 ntsync_mutex_is_signaled(struct ntsync_obj *obj,
556     struct ntsync_wait_state *state, int index)
557 {
558 	struct ntsync_obj_mutex *mutex;
559 
560 	MPASS(obj->type == NTSYNC_OBJ_MUTEX);
561 	NTSYNC_PRIV_ASSERT(obj->owner);
562 	mutex = OBJ_TO_MUTEX(obj);
563 	return (ntsync_mutex_can_lock(mutex, state->nwa->owner));
564 }
565 
566 static void
567 ntsync_mutex_consume(struct ntsync_obj *obj, struct ntsync_wait_state *state,
568     int index)
569 {
570 	struct ntsync_obj_mutex *mutex;
571 
572 	MPASS(obj->type == NTSYNC_OBJ_MUTEX);
573 	NTSYNC_PRIV_ASSERT(obj->owner);
574 	mutex = OBJ_TO_MUTEX(obj);
575 	MPASS(ntsync_mutex_can_lock(mutex, state->nwa->owner));
576 	if (state->nwa->owner == 0) {
577 		state->error = EINVAL;
578 		return;
579 	}
580 	if (mutex->a.owner == 0 || mutex->abandoned)
581 		mutex->a.count = 1;
582 	else
583 		mutex->a.count++;
584 	mutex->a.owner = state->nwa->owner;
585 	if (mutex->abandoned && state->error == 0)
586 		state->error = EOWNERDEAD;
587 	mutex->abandoned = false;
588 }
589 
590 static bool
591 ntsync_mutex_prepare(struct ntsync_obj *obj, struct ntsync_wait_state *state,
592     int index, bool *stop)
593 {
594 	struct ntsync_obj_mutex *mutex;
595 
596 	MPASS(obj->type == NTSYNC_OBJ_MUTEX);
597 	NTSYNC_PRIV_ASSERT(obj->owner);
598 	mutex = OBJ_TO_MUTEX(obj);
599 	if (!ntsync_mutex_can_lock(mutex, state->nwa->owner))
600 		return (false);
601 	if (state->nwa->owner == 0) {
602 		state->error = EINVAL;
603 		*stop = true;
604 		return (false);
605 	}
606 	mutex->a1 = mutex->a;
607 	if (mutex->a.owner == 0 || mutex->abandoned)
608 		mutex->a1.count = 1;
609 	else
610 		mutex->a1.count++;
611 	mutex->a1.owner = state->nwa->owner;
612 	return (true);
613 }
614 
615 static void
616 ntsync_mutex_commit(struct ntsync_obj *obj, struct ntsync_wait_state *state,
617     int index)
618 {
619 	struct ntsync_obj_mutex *mutex;
620 
621 	MPASS(obj->type == NTSYNC_OBJ_MUTEX);
622 	NTSYNC_PRIV_ASSERT(obj->owner);
623 	mutex = OBJ_TO_MUTEX(obj);
624 	mutex->a = mutex->a1;
625 	if (mutex->abandoned)
626 		state->error = EOWNERDEAD;
627 	mutex->abandoned = false;
628 }
629 
630 static void
631 ntsync_mutex_post_commit(struct ntsync_obj *obj,
632     struct ntsync_wait_state *state, int index)
633 {
634 }
635 
636 static int
637 ntsync_mutex_close(struct file *fp, struct thread *td)
638 {
639 	struct ntsync_obj_mutex *mutex;
640 
641 	mutex = fp->f_data;
642 	ntsync_close_obj(&mutex->obj, td);
643 	free(mutex, M_NTSYNC);
644 	return (0);
645 }
646 
647 int
648 ntsync_mutex_unlock(struct thread *td, struct file *fp,
649     struct ntsync_mutex_args *a)
650 {
651 	struct ntsync_obj *obj;
652 	struct ntsync_obj_mutex *mutex;
653 	struct ntsync_priv *priv;
654 	uint32_t prev;
655 	int error;
656 
657 	obj = fp->f_data;
658 	if (obj->type != NTSYNC_OBJ_MUTEX)
659 		return (EINVAL);
660 	mutex = OBJ_TO_MUTEX(obj);
661 	priv = obj->owner;
662 
663 	NTSYNC_PRIV_LOCK(priv);
664 	if (a->owner == 0) {
665 		error = EINVAL;
666 	} else if (a->owner != mutex->a.owner) {
667 		error = EPERM;
668 	} else {
669 		error = 0;
670 		prev = mutex->a.count;
671 		MPASS(mutex->a.count > 0);
672 		mutex->a.count--;
673 		a->count = prev;
674 		if (mutex->a.count == 0) {
675 			mutex->a.owner = 0;
676 			ntsync_wakeup_waiters(obj);
677 		}
678 	}
679 	NTSYNC_PRIV_UNLOCK(priv);
680 	return (error);
681 }
682 
683 int
684 ntsync_mutex_kill(struct thread *td, struct file *fp, uint32_t val)
685 {
686 	struct ntsync_obj *obj;
687 	struct ntsync_obj_mutex *mutex;
688 	struct ntsync_priv *priv;
689 	int error;
690 
691 	obj = fp->f_data;
692 	if (obj->type != NTSYNC_OBJ_MUTEX)
693 		return (EINVAL);
694 	mutex = OBJ_TO_MUTEX(obj);
695 	priv = obj->owner;
696 
697 	NTSYNC_PRIV_LOCK(priv);
698 	if (val == 0) {
699 		error = EINVAL;
700 	} else if (mutex->a.owner != val) {
701 		error = EPERM;
702 	} else {
703 		error = 0;
704 		mutex->a.owner = 0;
705 		mutex->a.count = 0;
706 		mutex->abandoned = true;
707 		ntsync_wakeup_waiters(obj);
708 	}
709 	NTSYNC_PRIV_UNLOCK(priv);
710 	return (error);
711 }
712 
713 int
714 ntsync_mutex_read(struct thread *td, struct file *fp,
715     struct ntsync_mutex_args *a, bool *doco)
716 {
717 	struct ntsync_obj *obj;
718 	struct ntsync_obj_mutex *mutex;
719 	struct ntsync_priv *priv;
720 	int error;
721 
722 	*doco = false;
723 	obj = fp->f_data;
724 	if (obj->type != NTSYNC_OBJ_MUTEX)
725 		return (EINVAL);
726 	mutex = OBJ_TO_MUTEX(obj);
727 	priv = obj->owner;
728 	error = 0;
729 
730 	NTSYNC_PRIV_LOCK(priv);
731 	*a = mutex->a;
732 	if (mutex->abandoned)
733 		error = EOWNERDEAD;
734 	NTSYNC_PRIV_UNLOCK(priv);
735 	*doco = true;
736 	return (error);
737 }
738 
739 static int
740 ntsync_mutex_ioctl(struct file *fp, u_long com, void *data,
741     struct ucred *active_cred, struct thread *td)
742 {
743 	struct ntsync_mutex_args aa;
744 	int error, error1;
745 	bool doco;
746 
747 	doco = false;
748 	switch (com) {
749 	case NTSYNC_IOC_MUTEX_UNLOCK:
750 		error = ntsync_mutex_unlock(td, fp, data);
751 		break;
752 	case NTSYNC_IOC_MUTEX_KILL:
753 		error = ntsync_mutex_kill(td, fp, *(uint32_t *)data);
754 		break;
755 	case NTSYNC_IOC_MUTEX_READ:
756 		error = ntsync_mutex_read(td, fp, &aa, &doco);
757 		if (doco) {
758 			error1 = ntsync_ioctl_copyout(td, &aa, sizeof(aa));
759 			if (error1 != 0)
760 				error = error1;
761 		}
762 		break;
763 	default:
764 		error = ENOTTY;
765 		break;
766 	}
767 	return (error);
768 }
769 
770 static int
771 ntsync_mutex_stat(struct file *fp, struct stat *sbp, struct ucred *cred)
772 {
773 	struct ntsync_obj *obj;
774 	struct ntsync_obj_mutex *mutex;
775 
776 	MPASS(fp->f_type == DTYPE_NTSYNC);
777 	obj = fp->f_data;
778 	MPASS(obj->type == NTSYNC_OBJ_MUTEX);
779 	mutex = OBJ_TO_MUTEX(obj);
780 
781 	memset(sbp, 0, sizeof(*sbp));
782 	sbp->st_mode = S_IFREG /* XXXKIB */ | S_IRUSR | S_IWUSR;
783 	NTSYNC_PRIV_LOCK(obj->owner);
784 	sbp->st_size = mutex->a.owner;
785 	sbp->st_nlink = mutex->a.count;
786 	NTSYNC_PRIV_UNLOCK(obj->owner);
787 	return (0);
788 }
789 
790 static int
791 ntsync_mutex_fill_kinfo(struct file *fp, struct kinfo_file *kif,
792     struct filedesc *fdp)
793 {
794 	struct ntsync_obj *obj;
795 	struct ntsync_obj_mutex *mutex;
796 
797 	MPASS(fp->f_type == DTYPE_NTSYNC);
798 	obj = fp->f_data;
799 	MPASS(obj->type == NTSYNC_OBJ_MUTEX);
800 	mutex = OBJ_TO_MUTEX(obj);
801 
802 	kif->kf_type = KF_TYPE_NTSYNC;
803 	kif->kf_un.kf_ntsync.kf_ntsync_type = KF_NTSYNC_TYPE_MUTEX;
804 	kif->kf_un.kf_ntsync.kf_ntsync_dev = (uintptr_t)obj->owner;
805 	kif->kf_un.kf_ntsync.kf_ntsync_un.kf_ntsync_mutex.owner =
806 	    mutex->a.owner;
807 	kif->kf_un.kf_ntsync.kf_ntsync_un.kf_ntsync_mutex.count =
808 	    mutex->a.count;
809 	return (0);
810 }
811 
812 struct fileops ntsync_mutex_fops = {
813 	.fo_read = invfo_rdwr,
814 	.fo_write = invfo_rdwr,
815 	.fo_truncate = invfo_truncate,
816 	.fo_ioctl = ntsync_mutex_ioctl,
817 	.fo_poll = invfo_poll,
818 	.fo_kqfilter = invfo_kqfilter,
819 	.fo_stat = ntsync_mutex_stat,
820 	.fo_close = ntsync_mutex_close,
821 	.fo_chmod = invfo_chmod,
822 	.fo_chown = invfo_chown,
823 	.fo_sendfile = invfo_sendfile,
824 	.fo_fill_kinfo = ntsync_mutex_fill_kinfo,
825 	.fo_flags = DFLAG_PASSABLE,
826 };
827 
828 static int
829 ntsync_create_mutex(struct ntsync_mutex_args *args, struct ntsync_priv *priv,
830     struct thread *td)
831 {
832 	struct ntsync_obj_mutex *mutex;
833 	int error;
834 
835 	if ((args->owner != 0 && args->count == 0) ||
836 	    (args->owner == 0 && args->count != 0))
837 		return (EINVAL);
838 
839 	mutex = malloc(sizeof(*mutex), M_NTSYNC, M_WAITOK | M_ZERO);
840 	mutex->obj.type = NTSYNC_OBJ_MUTEX;
841 	mutex->obj.is_signaled = ntsync_mutex_is_signaled;
842 	mutex->obj.consume = ntsync_mutex_consume;
843 	mutex->obj.prepare = ntsync_mutex_prepare;
844 	mutex->obj.commit = ntsync_mutex_commit;
845 	mutex->obj.post_commit = ntsync_mutex_post_commit;
846 	mutex->a = *args;
847 	mutex->abandoned = false;
848 
849 	error = ntsync_create_obj(&mutex->obj, &ntsync_mutex_fops, priv, td);
850 	if (error != 0)
851 		free(mutex, M_NTSYNC);
852 
853 	return (error);
854 }
855 
856 static bool
857 ntsync_event_is_signaled(struct ntsync_obj *obj,
858     struct ntsync_wait_state *state, int index)
859 {
860 	struct ntsync_obj_event *event;
861 
862 	MPASS(obj->type == NTSYNC_OBJ_EVENT);
863 	NTSYNC_PRIV_ASSERT(obj->owner);
864 	event = OBJ_TO_EVENT(obj);
865 	return (event->a.signaled != 0);
866 }
867 
868 static void
869 ntsync_event_consume(struct ntsync_obj *obj, struct ntsync_wait_state *state,
870     int index)
871 {
872 	struct ntsync_obj_event *event;
873 
874 	MPASS(obj->type == NTSYNC_OBJ_EVENT);
875 	NTSYNC_PRIV_ASSERT(obj->owner);
876 	MPASS(ntsync_event_is_signaled(obj, state, index));
877 
878 	event = OBJ_TO_EVENT(obj);
879 	if (event->a.manual == 0)
880 		event->a.signaled = 0;
881 }
882 
883 static bool
884 ntsync_event_prepare(struct ntsync_obj *obj, struct ntsync_wait_state *state,
885     int index, bool *stop)
886 {
887 	struct ntsync_obj_event *event;
888 
889 	MPASS(obj->type == NTSYNC_OBJ_EVENT);
890 	NTSYNC_PRIV_ASSERT(obj->owner);
891 	event = OBJ_TO_EVENT(obj);
892 	if (!ntsync_event_is_signaled(obj, state, index))
893 		return (false);
894 	event->a1 = event->a;
895 	return (true);
896 }
897 
898 static void
899 ntsync_event_commit(struct ntsync_obj *obj, struct ntsync_wait_state *state,
900     int index)
901 {
902 	struct ntsync_obj_event *event;
903 
904 	MPASS(obj->type == NTSYNC_OBJ_EVENT);
905 	NTSYNC_PRIV_ASSERT(obj->owner);
906 	event = OBJ_TO_EVENT(obj);
907 	event->a = event->a1;
908 	if (event->pulse && event->a.manual == 0) {
909 		event->a.signaled = 0;
910 		event->pulse = false;
911 	}
912 }
913 
914 static void
915 ntsync_event_post_commit(struct ntsync_obj *obj,
916     struct ntsync_wait_state *state, int index)
917 {
918 	struct ntsync_obj_event *event;
919 
920 	MPASS(obj->type == NTSYNC_OBJ_EVENT);
921 	NTSYNC_PRIV_ASSERT(obj->owner);
922 	event = OBJ_TO_EVENT(obj);
923 	if (event->a.manual == 0)
924 		event->a.signaled = 0;
925 }
926 
927 static int
928 ntsync_event_close(struct file *fp, struct thread *td)
929 {
930 	struct ntsync_obj_event *event;
931 
932 	event = fp->f_data;
933 	ntsync_close_obj(&event->obj, td);
934 	free(event, M_NTSYNC);
935 	return (0);
936 }
937 
938 int
939 ntsync_event_set(struct thread *td, struct file *fp, uint32_t *val)
940 {
941 	struct ntsync_obj *obj;
942 	struct ntsync_obj_event *event;
943 	struct ntsync_priv *priv;
944 	uint32_t prev;
945 
946 	obj = fp->f_data;
947 	if (obj->type != NTSYNC_OBJ_EVENT)
948 		return (EINVAL);
949 	event = OBJ_TO_EVENT(obj);
950 	priv = obj->owner;
951 
952 	NTSYNC_PRIV_LOCK(priv);
953 	prev = event->a.signaled;
954 	event->a.signaled = 1;
955 	ntsync_wakeup_waiters(obj);
956 	NTSYNC_PRIV_UNLOCK(priv);
957 
958 	*val = prev;
959 	return (0);
960 }
961 
962 int
963 ntsync_event_reset(struct thread *td, struct file *fp, uint32_t *val)
964 {
965 	struct ntsync_obj *obj;
966 	struct ntsync_obj_event *event;
967 	struct ntsync_priv *priv;
968 	uint32_t prev;
969 
970 	obj = fp->f_data;
971 	if (obj->type != NTSYNC_OBJ_EVENT)
972 		return (EINVAL);
973 	event = OBJ_TO_EVENT(obj);
974 	priv = obj->owner;
975 
976 	NTSYNC_PRIV_LOCK(priv);
977 	prev = event->a.signaled;
978 	event->a.signaled = 0;
979 	NTSYNC_PRIV_UNLOCK(priv);
980 
981 	*val = prev;
982 	return (0);
983 }
984 
985 int
986 ntsync_event_pulse(struct thread *td, struct file *fp, uint32_t *val)
987 {
988 	struct ntsync_obj *obj;
989 	struct ntsync_obj_event *event;
990 	struct ntsync_priv *priv;
991 	uint32_t prev;
992 
993 	obj = fp->f_data;
994 	if (obj->type != NTSYNC_OBJ_EVENT)
995 		return (EINVAL);
996 	event = OBJ_TO_EVENT(obj);
997 	priv = obj->owner;
998 
999 	NTSYNC_PRIV_LOCK(priv);
1000 	prev = event->a.signaled;
1001 	event->a.signaled = 1;
1002 	event->pulse = true;
1003 	ntsync_wakeup_waiters(obj);
1004 	event->a.signaled = 0;
1005 	event->pulse = false;
1006 	NTSYNC_PRIV_UNLOCK(priv);
1007 
1008 	*val = prev;
1009 	return (0);
1010 }
1011 
1012 int
1013 ntsync_event_read(struct thread *td, struct file *fp,
1014     struct ntsync_event_args *a)
1015 {
1016 	struct ntsync_obj *obj;
1017 	struct ntsync_obj_event *event;
1018 	struct ntsync_priv *priv;
1019 
1020 	obj = fp->f_data;
1021 	if (obj->type != NTSYNC_OBJ_EVENT)
1022 		return (EINVAL);
1023 	event = OBJ_TO_EVENT(obj);
1024 	priv = obj->owner;
1025 
1026 	NTSYNC_PRIV_LOCK(priv);
1027 	*a = event->a;
1028 	NTSYNC_PRIV_UNLOCK(priv);
1029 
1030 	return (0);
1031 }
1032 
1033 static int
1034 ntsync_event_ioctl(struct file *fp, u_long com, void *data,
1035     struct ucred *active_cred, struct thread *td)
1036 {
1037 	int error;
1038 
1039 	switch (com) {
1040 	case NTSYNC_IOC_EVENT_SET:
1041 		error = ntsync_event_set(td, fp, data);
1042 		break;
1043 	case NTSYNC_IOC_EVENT_RESET:
1044 		error = ntsync_event_reset(td, fp, data);
1045 		break;
1046 	case NTSYNC_IOC_EVENT_PULSE:
1047 		error = ntsync_event_pulse(td, fp, data);
1048 		break;
1049 	case NTSYNC_IOC_EVENT_READ:
1050 		error = ntsync_event_read(td, fp, data);
1051 		break;
1052 	default:
1053 		error = ENOTTY;
1054 		break;
1055 	}
1056 	return (error);
1057 }
1058 
1059 static int
1060 ntsync_event_stat(struct file *fp, struct stat *sbp, struct ucred *cred)
1061 {
1062 	struct ntsync_obj *obj;
1063 	struct ntsync_obj_event *event;
1064 
1065 	MPASS(fp->f_type == DTYPE_NTSYNC);
1066 	obj = fp->f_data;
1067 	MPASS(obj->type == NTSYNC_OBJ_EVENT);
1068 	event = OBJ_TO_EVENT(obj);
1069 
1070 	memset(sbp, 0, sizeof(*sbp));
1071 	sbp->st_mode = S_IFREG /* XXXKIB */ | S_IRUSR | S_IWUSR;
1072 	NTSYNC_PRIV_LOCK(obj->owner);
1073 	sbp->st_size = event->a.signaled;
1074 	sbp->st_nlink = event->a.manual;
1075 	NTSYNC_PRIV_UNLOCK(obj->owner);
1076 	return (0);
1077 }
1078 
1079 static int
1080 ntsync_event_fill_kinfo(struct file *fp, struct kinfo_file *kif,
1081     struct filedesc *fdp)
1082 {
1083 	struct ntsync_obj *obj;
1084 	struct ntsync_obj_event *event;
1085 
1086 	MPASS(fp->f_type == DTYPE_NTSYNC);
1087 	obj = fp->f_data;
1088 	MPASS(obj->type == NTSYNC_OBJ_EVENT);
1089 	event = OBJ_TO_EVENT(obj);
1090 
1091 	kif->kf_type = KF_TYPE_NTSYNC;
1092 	kif->kf_un.kf_ntsync.kf_ntsync_type = KF_NTSYNC_TYPE_EVENT;
1093 	kif->kf_un.kf_ntsync.kf_ntsync_dev = (uintptr_t)obj->owner;
1094 	kif->kf_un.kf_ntsync.kf_ntsync_un.kf_ntsync_event.signaled =
1095 		event->a.signaled;
1096 	kif->kf_un.kf_ntsync.kf_ntsync_un.kf_ntsync_event.manual =
1097 		event->a.manual;
1098 	return (0);
1099 }
1100 
1101 struct fileops ntsync_event_fops = {
1102 	.fo_read = invfo_rdwr,
1103 	.fo_write = invfo_rdwr,
1104 	.fo_truncate = invfo_truncate,
1105 	.fo_ioctl = ntsync_event_ioctl,
1106 	.fo_poll = invfo_poll,
1107 	.fo_kqfilter = invfo_kqfilter,
1108 	.fo_stat = ntsync_event_stat,
1109 	.fo_close = ntsync_event_close,
1110 	.fo_chmod = invfo_chmod,
1111 	.fo_chown = invfo_chown,
1112 	.fo_sendfile = invfo_sendfile,
1113 	.fo_fill_kinfo = ntsync_event_fill_kinfo,
1114 	.fo_flags = DFLAG_PASSABLE,
1115 };
1116 
1117 static int
1118 ntsync_create_event(struct ntsync_event_args *args, struct ntsync_priv *priv,
1119     struct thread *td)
1120 {
1121 	struct ntsync_obj_event *event;
1122 	int error;
1123 
1124 	event = malloc(sizeof(*event), M_NTSYNC, M_WAITOK | M_ZERO);
1125 	event->obj.type = NTSYNC_OBJ_EVENT;
1126 	event->obj.is_signaled = ntsync_event_is_signaled;
1127 	event->obj.consume = ntsync_event_consume;
1128 	event->obj.prepare = ntsync_event_prepare;
1129 	event->obj.commit = ntsync_event_commit;
1130 	event->obj.post_commit = ntsync_event_post_commit;
1131 	event->a = *args;
1132 
1133 	error = ntsync_create_obj(&event->obj, &ntsync_event_fops, priv, td);
1134 	if (error != 0)
1135 		free(event, M_NTSYNC);
1136 
1137 	return (error);
1138 }
1139 
1140 static void
1141 ntsync_free_priv(struct ntsync_priv *priv)
1142 {
1143 	bool do_free;
1144 
1145 	NTSYNC_PRIV_LOCK(priv);
1146 	do_free = priv->closed && priv->objs_cnt == 0;
1147 	NTSYNC_PRIV_UNLOCK(priv);
1148 	if (do_free) {
1149 		mtx_destroy(&priv->lock);
1150 		free(priv, M_NTSYNC);
1151 	}
1152 }
1153 
1154 static void
1155 ntsync_priv_dtr(void *data)
1156 {
1157 	ntsync_free_priv(data);
1158 }
1159 
1160 static int
1161 ntsync_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
1162 {
1163 	struct ntsync_priv *priv;
1164 
1165 	priv = malloc(sizeof(*priv), M_NTSYNC, M_WAITOK);
1166 	priv->closed = false;
1167 	priv->objs_cnt = 0;
1168 	mtx_init(&priv->lock, "ntsync", "ntsync", MTX_DEF | MTX_NEW);
1169 	devfs_set_cdevpriv(priv, ntsync_priv_dtr);
1170 	return (0);
1171 }
1172 
1173 static int
1174 ntsync_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
1175 {
1176 	struct ntsync_priv *priv;
1177 	void *a;
1178 	int error;
1179 
1180 	error = devfs_get_cdevpriv(&a);
1181 	if (error == 0) {
1182 		priv = a;
1183 		NTSYNC_PRIV_LOCK(priv);
1184 		priv->closed = true;
1185 		NTSYNC_PRIV_UNLOCK(priv);
1186 	}
1187 	devfs_clear_cdevpriv();
1188 	return (0);
1189 }
1190 
1191 static int
1192 ntsync_wait_state_get(struct ntsync_wait_args *nwa, u_long cmd,
1193     struct ntsync_priv *owner, struct ntsync_wait_state **statep,
1194     struct thread *td)
1195 {
1196 	struct ntsync_wait_state *state;
1197 	struct ntsync_obj *obj;
1198 	struct bintime btb;
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 		return (error);
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 	if (nwa->timeout == UINT64_MAX) {
1274 		state->sb = 0;
1275 	} else {
1276 		state->sb = nstosbt(nwa->timeout);
1277 		if ((nwa->flags & NTSYNC_WAIT_REALTIME) != 0) {
1278 			getboottimebin(&btb);
1279 			state->sb += bttosbt(btb);
1280 		}
1281 	}
1282 
1283 	*statep = state;
1284 	return (0);
1285 
1286 error_out:
1287 	for (j = 0; j < i; j++)
1288 		fdrop(state->fps[j], td);
1289 	if (state->fp_alert != NULL)
1290 		fdrop(state->fp_alert, td);
1291 	return (error);
1292 }
1293 
1294 static void
1295 ntsync_wait_state_put(struct ntsync_wait_state *state, struct thread *td)
1296 {
1297 	int i;
1298 
1299 	for (i = 0; i < state->nwa->count; i++)
1300 		fdrop(state->fps[i], td);
1301 	if (state->fp_alert != NULL)
1302 		fdrop(state->fp_alert, td);
1303 	free(state, M_NTSYNC);
1304 }
1305 
1306 static int
1307 ntsync_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
1308     struct thread *td)
1309 {
1310 	struct ntsync_priv *owner;
1311 	struct ntsync_wait_args *nwa;
1312 	struct ntsync_wait_state *state;
1313 	void *a;
1314 	int error;
1315 
1316 	error = devfs_get_cdevpriv(&a);
1317 	if (error != 0)
1318 		return (error);
1319 	owner = a;
1320 
1321 	switch (cmd) {
1322 	case NTSYNC_IOC_CREATE_SEM:
1323 		error = ntsync_create_sem((struct ntsync_sem_args *)data,
1324 		    owner, td);
1325 		break;
1326 	case NTSYNC_IOC_CREATE_MUTEX:
1327 		error = ntsync_create_mutex((struct ntsync_mutex_args *)data,
1328 		    owner, td);
1329 		break;
1330 	case NTSYNC_IOC_CREATE_EVENT:
1331 		error = ntsync_create_event((struct ntsync_event_args *)data,
1332 		    owner, td);
1333 		break;
1334 	case NTSYNC_IOC_WAIT_ANY:
1335 		nwa = (struct ntsync_wait_args *)data;
1336 		error = ntsync_wait_state_get(nwa, cmd, owner, &state, td);
1337 		if (error != 0)
1338 			break;
1339 		error = ntsync_wait(state, td);
1340 		if (error == 0) {
1341 			nwa->index = state->index;
1342 			error = ntsync_ioctl_copyout(td, nwa, sizeof(*nwa));
1343 			if (error == 0)
1344 				error = state->error;
1345 		}
1346 		ntsync_wait_state_put(state, td);
1347 		break;
1348 	case NTSYNC_IOC_WAIT_ALL:
1349 		nwa = (struct ntsync_wait_args *)data;
1350 		error = ntsync_wait_state_get(nwa, cmd, owner, &state, td);
1351 		if (error != 0)
1352 			break;
1353 		error = ntsync_wait(state, td);
1354 		if (error == 0) {
1355 			nwa->index = state->index;
1356 			error = ntsync_ioctl_copyout(td, nwa, sizeof(*nwa));
1357 			if (error == 0)
1358 				error = state->error;
1359 		}
1360 		ntsync_wait_state_put(state, td);
1361 		break;
1362 
1363 	default:
1364 		error = ENOTTY;
1365 		break;
1366 	}
1367 	return (error);
1368 }
1369 
1370 struct cdevsw ntsync_cdevsw = {
1371 	.d_version =	D_VERSION,
1372 	.d_flags =	0,
1373 	.d_open =	ntsync_open,
1374 	.d_close =	ntsync_close,
1375 	.d_ioctl =	ntsync_ioctl,
1376 	.d_name =	"ntsync",
1377 };
1378 
1379 static int
1380 ntsync_modevent(module_t mod __unused, int type, void *data __unused)
1381 {
1382 	struct make_dev_args mda;
1383 	int error;
1384 
1385 	error = 0;
1386 	switch (type) {
1387 	case MOD_LOAD:
1388 		make_dev_args_init(&mda);
1389 		mda.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME;
1390 		mda.mda_devsw = &ntsync_cdevsw;
1391 		mda.mda_uid = UID_ROOT;
1392 		mda.mda_gid = GID_GAMES;
1393 		mda.mda_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP |
1394 		    S_IROTH | S_IWOTH;
1395 
1396 		error = make_dev_s(&mda, &ntsync_cdev, "ntsync");
1397 		if (error != 0) {
1398 			printf("cannot create ntsync dev err %d\n", error);
1399 			break;
1400 		}
1401 		if (bootverbose)
1402 			printf("ntsync\n");
1403 		break;
1404 
1405 	case MOD_UNLOAD:
1406 		destroy_dev(ntsync_cdev);
1407 		break;
1408 
1409 	case MOD_SHUTDOWN:
1410 		break;
1411 
1412 	default:
1413 		error = EOPNOTSUPP;
1414 	}
1415 
1416 	return (error);
1417 }
1418 
1419 DEV_MODULE(ntsync, ntsync_modevent, NULL);
1420 MODULE_VERSION(ntsync, 1);
1421