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
ntsync_ioctl_copyout(struct thread * td,const void * ptr,size_t sz)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
ntsync_wait_any(struct ntsync_wait_state * state)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
ntsync_wait_all_prepare(struct ntsync_wait_state * state,bool * stop)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
ntsync_wait_all_commit(struct ntsync_wait_state * state)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
ntsync_wait_link_waiters(struct ntsync_wait_state * state)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
ntsync_wait_unlink_waiters(struct ntsync_wait_state * state)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
ntsync_wait_post_commit(struct ntsync_wait_state * state)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
ntsync_wait_check_ready(struct ntsync_wait_state * state)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
ntsync_wait_locked(struct ntsync_wait_state * state,struct thread * td)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
ntsync_wait(struct ntsync_wait_state * state,struct thread * td)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
ntsync_wakeup_waiters(struct ntsync_obj * obj)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
ntsync_create_obj(struct ntsync_obj * obj,struct fileops * fops,struct ntsync_priv * priv,struct thread * td)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
ntsync_close_obj(struct ntsync_obj * obj,struct thread * td)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
ntsync_sem_is_signaled(struct ntsync_obj * obj,struct ntsync_wait_state * state,int index)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
ntsync_sem_consume(struct ntsync_obj * obj,struct ntsync_wait_state * state,int index)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
ntsync_sem_prepare(struct ntsync_obj * obj,struct ntsync_wait_state * state,int index,bool * stop)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
ntsync_sem_commit(struct ntsync_obj * obj,struct ntsync_wait_state * state,int index)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
ntsync_sem_post_commit(struct ntsync_obj * obj,struct ntsync_wait_state * state,int index)380 ntsync_sem_post_commit(struct ntsync_obj *obj, struct ntsync_wait_state *state,
381 int index)
382 {
383 }
384
385 static int
ntsync_sem_close(struct file * fp,struct thread * td)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
ntsync_sem_release(struct thread * td,struct file * fp,uint32_t * val)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
ntsync_sem_read(struct thread * td,struct file * fp,struct ntsync_sem_args * a)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
ntsync_sem_ioctl(struct file * fp,u_long com,void * data,struct ucred * active_cred,struct thread * td)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
ntsync_sem_stat(struct file * fp,struct stat * sbp,struct ucred * cred)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
ntsync_sem_fill_kinfo(struct file * fp,struct kinfo_file * kif,struct filedesc * fdp)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
ntsync_create_sem(struct ntsync_sem_args * args,struct ntsync_priv * priv,struct thread * td)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
ntsync_mutex_can_lock(struct ntsync_obj_mutex * mutex,uint32_t nwa_owner)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
ntsync_mutex_is_signaled(struct ntsync_obj * obj,struct ntsync_wait_state * state,int index)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
ntsync_mutex_consume(struct ntsync_obj * obj,struct ntsync_wait_state * state,int index)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
ntsync_mutex_prepare(struct ntsync_obj * obj,struct ntsync_wait_state * state,int index,bool * stop)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
ntsync_mutex_commit(struct ntsync_obj * obj,struct ntsync_wait_state * state,int index)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
ntsync_mutex_post_commit(struct ntsync_obj * obj,struct ntsync_wait_state * state,int index)632 ntsync_mutex_post_commit(struct ntsync_obj *obj,
633 struct ntsync_wait_state *state, int index)
634 {
635 }
636
637 static int
ntsync_mutex_close(struct file * fp,struct thread * td)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
ntsync_mutex_unlock(struct thread * td,struct file * fp,struct ntsync_mutex_args * a)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
ntsync_mutex_kill(struct thread * td,struct file * fp,uint32_t val)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
ntsync_mutex_read(struct thread * td,struct file * fp,struct ntsync_mutex_args * a,bool * doco)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
ntsync_mutex_ioctl(struct file * fp,u_long com,void * data,struct ucred * active_cred,struct thread * td)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
ntsync_mutex_stat(struct file * fp,struct stat * sbp,struct ucred * cred)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
ntsync_mutex_fill_kinfo(struct file * fp,struct kinfo_file * kif,struct filedesc * fdp)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
ntsync_create_mutex(struct ntsync_mutex_args * args,struct ntsync_priv * priv,struct thread * td)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
ntsync_event_is_signaled(struct ntsync_obj * obj,struct ntsync_wait_state * state,int index)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
ntsync_event_consume(struct ntsync_obj * obj,struct ntsync_wait_state * state,int index)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
ntsync_event_prepare(struct ntsync_obj * obj,struct ntsync_wait_state * state,int index,bool * stop)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
ntsync_event_commit(struct ntsync_obj * obj,struct ntsync_wait_state * state,int index)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
ntsync_event_post_commit(struct ntsync_obj * obj,struct ntsync_wait_state * state,int index)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
ntsync_event_close(struct file * fp,struct thread * td)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
ntsync_event_set(struct thread * td,struct file * fp,uint32_t * val)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
ntsync_event_reset(struct thread * td,struct file * fp,uint32_t * val)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
ntsync_event_pulse(struct thread * td,struct file * fp,uint32_t * val)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
ntsync_event_read(struct thread * td,struct file * fp,struct ntsync_event_args * a)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
ntsync_event_ioctl(struct file * fp,u_long com,void * data,struct ucred * active_cred,struct thread * td)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
ntsync_event_stat(struct file * fp,struct stat * sbp,struct ucred * cred)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
ntsync_event_fill_kinfo(struct file * fp,struct kinfo_file * kif,struct filedesc * fdp)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
ntsync_create_event(struct ntsync_event_args * args,struct ntsync_priv * priv,struct thread * td)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
ntsync_free_priv(struct ntsync_priv * priv)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
ntsync_priv_dtr(void * data)1156 ntsync_priv_dtr(void *data)
1157 {
1158 ntsync_free_priv(data);
1159 }
1160
1161 static int
ntsync_open(struct cdev * dev,int oflags,int devtype,struct thread * td)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
ntsync_close(struct cdev * dev,int fflag,int devtype,struct thread * td)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
ntsync_wait_state_get(struct ntsync_wait_args * nwa,u_long cmd,struct ntsync_priv * owner,struct ntsync_wait_state ** statep,struct thread * td)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
ntsync_wait_state_put(struct ntsync_wait_state * state,struct thread * td)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
ntsync_ioctl(struct cdev * dev,u_long cmd,caddr_t data,int fflag,struct thread * td)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
ntsync_modevent(module_t mod __unused,int type,void * data __unused)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