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