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