Print this page
7127 remove -Wno-missing-braces from Makefile.uts
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/uts/common/io/timerfd.c
+++ new/usr/src/uts/common/io/timerfd.c
1 1 /*
2 2 * This file and its contents are supplied under the terms of the
3 3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 4 * You may only use this file in accordance with the terms of version
5 5 * 1.0 of the CDDL.
6 6 *
7 7 * A full copy of the text of the CDDL should have accompanied this
8 8 * source. A copy of the CDDL is also available via the Internet at
9 9 * http://www.illumos.org/license/CDDL.
10 10 */
11 11
12 12 /*
13 13 * Copyright (c) 2015 Joyent, Inc. All rights reserved.
14 14 */
15 15
16 16 /*
17 17 * Support for the timerfd facility, a Linux-borne facility that allows
18 18 * POSIX.1b timers to be created and manipulated via a file descriptor
19 19 * interface.
20 20 */
21 21
22 22 #include <sys/ddi.h>
23 23 #include <sys/sunddi.h>
24 24 #include <sys/timerfd.h>
25 25 #include <sys/conf.h>
26 26 #include <sys/vmem.h>
27 27 #include <sys/sysmacros.h>
28 28 #include <sys/filio.h>
29 29 #include <sys/stat.h>
30 30 #include <sys/file.h>
31 31 #include <sys/timer.h>
32 32
33 33 struct timerfd_state;
34 34 typedef struct timerfd_state timerfd_state_t;
35 35
36 36 struct timerfd_state {
37 37 kmutex_t tfd_lock; /* lock protecting state */
38 38 kcondvar_t tfd_cv; /* condvar */
39 39 pollhead_t tfd_pollhd; /* poll head */
40 40 uint64_t tfd_fired; /* # of times fired */
41 41 itimer_t tfd_itimer; /* underlying itimer */
42 42 timerfd_state_t *tfd_next; /* next state on global list */
43 43 };
44 44
45 45 /*
46 46 * Internal global variables.
47 47 */
48 48 static kmutex_t timerfd_lock; /* lock protecting state */
49 49 static dev_info_t *timerfd_devi; /* device info */
50 50 static vmem_t *timerfd_minor; /* minor number arena */
51 51 static void *timerfd_softstate; /* softstate pointer */
52 52 static timerfd_state_t *timerfd_state; /* global list of state */
53 53
54 54 static itimer_t *
55 55 timerfd_itimer_lock(timerfd_state_t *state)
56 56 {
57 57 itimer_t *it = &state->tfd_itimer;
58 58
59 59 mutex_enter(&state->tfd_lock);
60 60
61 61 while (it->it_lock & ITLK_LOCKED) {
62 62 it->it_blockers++;
63 63 cv_wait(&it->it_cv, &state->tfd_lock);
64 64 it->it_blockers--;
65 65 }
66 66
67 67 it->it_lock |= ITLK_LOCKED;
68 68
69 69 mutex_exit(&state->tfd_lock);
70 70
71 71 return (it);
72 72 }
73 73
74 74 static void
75 75 timerfd_itimer_unlock(timerfd_state_t *state, itimer_t *it)
76 76 {
77 77 VERIFY(it == &state->tfd_itimer);
78 78 VERIFY(it->it_lock & ITLK_LOCKED);
79 79
80 80 mutex_enter(&state->tfd_lock);
81 81
82 82 it->it_lock &= ~ITLK_LOCKED;
83 83
84 84 if (it->it_blockers)
85 85 cv_signal(&it->it_cv);
86 86
87 87 mutex_exit(&state->tfd_lock);
88 88 }
89 89
90 90 static void
91 91 timerfd_fire(itimer_t *it)
92 92 {
93 93 timerfd_state_t *state = it->it_frontend;
94 94 uint64_t oval;
95 95
96 96 mutex_enter(&state->tfd_lock);
97 97 oval = state->tfd_fired++;
98 98 mutex_exit(&state->tfd_lock);
99 99
100 100 if (oval == 0) {
101 101 cv_broadcast(&state->tfd_cv);
102 102 pollwakeup(&state->tfd_pollhd, POLLRDNORM | POLLIN);
103 103 }
104 104 }
105 105
106 106 /*ARGSUSED*/
107 107 static int
108 108 timerfd_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
109 109 {
110 110 timerfd_state_t *state;
111 111 major_t major = getemajor(*devp);
112 112 minor_t minor = getminor(*devp);
113 113
114 114 if (minor != TIMERFDMNRN_TIMERFD)
115 115 return (ENXIO);
116 116
117 117 mutex_enter(&timerfd_lock);
118 118
119 119 minor = (minor_t)(uintptr_t)vmem_alloc(timerfd_minor, 1,
120 120 VM_BESTFIT | VM_SLEEP);
121 121
122 122 if (ddi_soft_state_zalloc(timerfd_softstate, minor) != DDI_SUCCESS) {
123 123 vmem_free(timerfd_minor, (void *)(uintptr_t)minor, 1);
124 124 mutex_exit(&timerfd_lock);
125 125 return (NULL);
126 126 }
127 127
128 128 state = ddi_get_soft_state(timerfd_softstate, minor);
129 129 *devp = makedevice(major, minor);
130 130
131 131 state->tfd_next = timerfd_state;
132 132 timerfd_state = state;
133 133
134 134 mutex_exit(&timerfd_lock);
135 135
136 136 return (0);
137 137 }
138 138
139 139 /*ARGSUSED*/
140 140 static int
141 141 timerfd_read(dev_t dev, uio_t *uio, cred_t *cr)
142 142 {
143 143 timerfd_state_t *state;
144 144 minor_t minor = getminor(dev);
145 145 uint64_t val;
146 146 int err;
147 147
148 148 if (uio->uio_resid < sizeof (val))
149 149 return (EINVAL);
150 150
151 151 state = ddi_get_soft_state(timerfd_softstate, minor);
152 152
153 153 mutex_enter(&state->tfd_lock);
154 154
155 155 while (state->tfd_fired == 0) {
156 156 if (uio->uio_fmode & (FNDELAY|FNONBLOCK)) {
157 157 mutex_exit(&state->tfd_lock);
158 158 return (EAGAIN);
159 159 }
160 160
161 161 if (!cv_wait_sig_swap(&state->tfd_cv, &state->tfd_lock)) {
162 162 mutex_exit(&state->tfd_lock);
163 163 return (EINTR);
164 164 }
165 165 }
166 166
167 167 /*
168 168 * Our tfd_fired is non-zero; slurp its value and then clear it.
169 169 */
170 170 val = state->tfd_fired;
171 171 state->tfd_fired = 0;
172 172 mutex_exit(&state->tfd_lock);
173 173
174 174 err = uiomove(&val, sizeof (val), UIO_READ, uio);
175 175
176 176 return (err);
177 177 }
178 178
179 179 /*ARGSUSED*/
180 180 static int
181 181 timerfd_poll(dev_t dev, short events, int anyyet, short *reventsp,
182 182 struct pollhead **phpp)
183 183 {
184 184 timerfd_state_t *state;
185 185 minor_t minor = getminor(dev);
186 186 short revents = 0;
187 187
188 188 state = ddi_get_soft_state(timerfd_softstate, minor);
189 189
190 190 mutex_enter(&state->tfd_lock);
191 191
192 192 if (state->tfd_fired > 0)
193 193 revents |= POLLRDNORM | POLLIN;
194 194
195 195 if (!(*reventsp = revents & events) && !anyyet)
196 196 *phpp = &state->tfd_pollhd;
197 197
198 198 mutex_exit(&state->tfd_lock);
199 199
200 200 return (0);
201 201 }
202 202
203 203 static int
204 204 timerfd_copyin(uintptr_t addr, itimerspec_t *dest)
205 205 {
206 206 if (get_udatamodel() == DATAMODEL_NATIVE) {
207 207 if (copyin((void *)addr, dest, sizeof (itimerspec_t)) != 0)
208 208 return (EFAULT);
209 209 } else {
210 210 itimerspec32_t dest32;
211 211
212 212 if (copyin((void *)addr, &dest32, sizeof (itimerspec32_t)) != 0)
213 213 return (EFAULT);
214 214
215 215 ITIMERSPEC32_TO_ITIMERSPEC(dest, &dest32);
216 216 }
217 217
218 218 if (itimerspecfix(&dest->it_value) ||
219 219 (itimerspecfix(&dest->it_interval) &&
220 220 timerspecisset(&dest->it_value))) {
221 221 return (EINVAL);
222 222 }
223 223
224 224 return (0);
225 225 }
226 226
227 227 static int
228 228 timerfd_copyout(itimerspec_t *src, uintptr_t addr)
229 229 {
230 230 if (get_udatamodel() == DATAMODEL_NATIVE) {
231 231 if (copyout(src, (void *)addr, sizeof (itimerspec_t)) != 0)
232 232 return (EFAULT);
233 233 } else {
234 234 itimerspec32_t src32;
235 235
236 236 if (ITIMERSPEC_OVERFLOW(src))
237 237 return (EOVERFLOW);
238 238
239 239 ITIMERSPEC_TO_ITIMERSPEC32(&src32, src);
240 240
241 241 if (copyout(&src32, (void *)addr, sizeof (itimerspec32_t)) != 0)
242 242 return (EFAULT);
243 243 }
244 244
245 245 return (0);
246 246 }
247 247
248 248 /*ARGSUSED*/
249 249 static int
250 250 timerfd_ioctl(dev_t dev, int cmd, intptr_t arg, int md, cred_t *cr, int *rv)
251 251 {
252 252 itimerspec_t when, oval;
253 253 timerfd_state_t *state;
254 254 minor_t minor = getminor(dev);
255 255 int err;
256 256 itimer_t *it;
257 257
258 258 state = ddi_get_soft_state(timerfd_softstate, minor);
259 259
260 260 switch (cmd) {
261 261 case TIMERFDIOC_CREATE: {
262 262 if (arg == TIMERFD_MONOTONIC)
263 263 arg = CLOCK_MONOTONIC;
264 264
265 265 it = timerfd_itimer_lock(state);
266 266
267 267 if (it->it_backend != NULL) {
268 268 timerfd_itimer_unlock(state, it);
269 269 return (EEXIST);
270 270 }
271 271
272 272 if ((it->it_backend = clock_get_backend(arg)) == NULL) {
273 273 timerfd_itimer_unlock(state, it);
274 274 return (EINVAL);
275 275 }
276 276
277 277 /*
278 278 * We need to provide a proc structure only for purposes
279 279 * of locking CLOCK_REALTIME-based timers -- it is safe to
280 280 * provide p0 here.
281 281 */
282 282 it->it_proc = &p0;
283 283
284 284 err = it->it_backend->clk_timer_create(it, timerfd_fire);
285 285
286 286 if (err != 0) {
287 287 it->it_backend = NULL;
288 288 timerfd_itimer_unlock(state, it);
289 289 return (err);
290 290 }
291 291
292 292 it->it_frontend = state;
293 293 timerfd_itimer_unlock(state, it);
294 294
295 295 return (0);
296 296 }
297 297
298 298 case TIMERFDIOC_GETTIME: {
299 299 it = timerfd_itimer_lock(state);
300 300
301 301 if (it->it_backend == NULL) {
302 302 timerfd_itimer_unlock(state, it);
303 303 return (ENODEV);
304 304 }
305 305
306 306 err = it->it_backend->clk_timer_gettime(it, &when);
307 307 timerfd_itimer_unlock(state, it);
308 308
309 309 if (err != 0)
310 310 return (err);
311 311
312 312 if ((err = timerfd_copyout(&when, arg)) != 0)
313 313 return (err);
314 314
315 315 return (0);
316 316 }
317 317
318 318 case TIMERFDIOC_SETTIME: {
319 319 timerfd_settime_t st;
320 320
321 321 if (copyin((void *)arg, &st, sizeof (st)) != 0)
322 322 return (EFAULT);
323 323
324 324 if ((err = timerfd_copyin(st.tfd_settime_value, &when)) != 0)
325 325 return (err);
326 326
327 327 it = timerfd_itimer_lock(state);
328 328
329 329 if (it->it_backend == NULL) {
330 330 timerfd_itimer_unlock(state, it);
331 331 return (ENODEV);
332 332 }
333 333
334 334 if (st.tfd_settime_ovalue != NULL) {
335 335 err = it->it_backend->clk_timer_gettime(it, &oval);
336 336
337 337 if (err != 0) {
338 338 timerfd_itimer_unlock(state, it);
339 339 return (err);
340 340 }
341 341 }
342 342
343 343 /*
344 344 * Before we set the time, we're going to clear tfd_fired.
345 345 * This can potentially race with the (old) timer firing, but
346 346 * the window is deceptively difficult to close: if we were
347 347 * to simply clear tfd_fired after the call to the backend
348 348 * returned, we would run the risk of plowing a firing of the
349 349 * new timer. Ultimately, the race can only be resolved by
350 350 * the backend, which would likely need to be extended with a
351 351 * function to call back into when the timer is between states
352 352 * (that is, after the timer can no longer fire with the old
353 353 * timer value, but before it can fire with the new one).
354 354 * This is straightforward enough for backends that set a
355 355 * timer's value by deleting the old one and adding the new
356 356 * one, but for those that modify the timer value in place
357 357 * (e.g., cyclics), the required serialization is necessarily
358 358 * delicate: the function would have to be callable from
359 359 * arbitrary interrupt context. While implementing all of
360 360 * this is possible, it does not (for the moment) seem worth
361 361 * it: if the timer is firing at essentially the same moment
362 362 * that it's being reprogrammed, there is a higher-level race
363 363 * with respect to timerfd usage that the progam itself will
364 364 * have to properly resolve -- and it seems reasonable to
365 365 * simply allow the program to resolve it in this case.
366 366 */
367 367 mutex_enter(&state->tfd_lock);
368 368 state->tfd_fired = 0;
369 369 mutex_exit(&state->tfd_lock);
370 370
371 371 err = it->it_backend->clk_timer_settime(it,
372 372 st.tfd_settime_flags & TFD_TIMER_ABSTIME ?
373 373 TIMER_ABSTIME : TIMER_RELTIME, &when);
374 374 timerfd_itimer_unlock(state, it);
375 375
376 376 if (err != 0 || st.tfd_settime_ovalue == NULL)
377 377 return (err);
378 378
379 379 if ((err = timerfd_copyout(&oval, st.tfd_settime_ovalue)) != 0)
380 380 return (err);
381 381
382 382 return (0);
383 383 }
384 384
385 385 default:
386 386 break;
387 387 }
388 388
389 389 return (ENOTTY);
390 390 }
391 391
392 392 /*ARGSUSED*/
393 393 static int
394 394 timerfd_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
395 395 {
396 396 timerfd_state_t *state, **sp;
397 397 itimer_t *it;
398 398 minor_t minor = getminor(dev);
399 399
400 400 state = ddi_get_soft_state(timerfd_softstate, minor);
401 401
402 402 if (state->tfd_pollhd.ph_list != NULL) {
403 403 pollwakeup(&state->tfd_pollhd, POLLERR);
404 404 pollhead_clean(&state->tfd_pollhd);
405 405 }
406 406
407 407 /*
408 408 * No one can get to this timer; we don't need to lock it -- we can
409 409 * just call on the backend to delete it.
410 410 */
411 411 it = &state->tfd_itimer;
412 412
413 413 if (it->it_backend != NULL)
414 414 it->it_backend->clk_timer_delete(it);
415 415
416 416 mutex_enter(&timerfd_lock);
417 417
418 418 /*
419 419 * Remove our state from our global list.
420 420 */
421 421 for (sp = &timerfd_state; *sp != state; sp = &((*sp)->tfd_next))
422 422 VERIFY(*sp != NULL);
423 423
424 424 *sp = (*sp)->tfd_next;
425 425
426 426 ddi_soft_state_free(timerfd_softstate, minor);
427 427 vmem_free(timerfd_minor, (void *)(uintptr_t)minor, 1);
428 428
429 429 mutex_exit(&timerfd_lock);
430 430
431 431 return (0);
432 432 }
433 433
434 434 static int
435 435 timerfd_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
436 436 {
437 437 switch (cmd) {
438 438 case DDI_ATTACH:
439 439 break;
440 440
441 441 case DDI_RESUME:
442 442 return (DDI_SUCCESS);
443 443
444 444 default:
445 445 return (DDI_FAILURE);
446 446 }
447 447
448 448 mutex_enter(&timerfd_lock);
449 449
450 450 if (ddi_soft_state_init(&timerfd_softstate,
451 451 sizeof (timerfd_state_t), 0) != 0) {
452 452 cmn_err(CE_NOTE, "/dev/timerfd failed to create soft state");
453 453 mutex_exit(&timerfd_lock);
454 454 return (DDI_FAILURE);
455 455 }
456 456
457 457 if (ddi_create_minor_node(devi, "timerfd", S_IFCHR,
458 458 TIMERFDMNRN_TIMERFD, DDI_PSEUDO, NULL) == DDI_FAILURE) {
459 459 cmn_err(CE_NOTE, "/dev/timerfd couldn't create minor node");
460 460 ddi_soft_state_fini(&timerfd_softstate);
461 461 mutex_exit(&timerfd_lock);
462 462 return (DDI_FAILURE);
463 463 }
464 464
465 465 ddi_report_dev(devi);
466 466 timerfd_devi = devi;
467 467
468 468 timerfd_minor = vmem_create("timerfd_minor", (void *)TIMERFDMNRN_CLONE,
469 469 UINT32_MAX - TIMERFDMNRN_CLONE, 1, NULL, NULL, NULL, 0,
470 470 VM_SLEEP | VMC_IDENTIFIER);
471 471
472 472 mutex_exit(&timerfd_lock);
473 473
474 474 return (DDI_SUCCESS);
475 475 }
476 476
477 477 /*ARGSUSED*/
478 478 static int
479 479 timerfd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
480 480 {
481 481 switch (cmd) {
482 482 case DDI_DETACH:
483 483 break;
484 484
485 485 case DDI_SUSPEND:
486 486 return (DDI_SUCCESS);
487 487
488 488 default:
489 489 return (DDI_FAILURE);
490 490 }
491 491
492 492 mutex_enter(&timerfd_lock);
493 493 vmem_destroy(timerfd_minor);
494 494
495 495 ddi_remove_minor_node(timerfd_devi, NULL);
496 496 timerfd_devi = NULL;
497 497
498 498 ddi_soft_state_fini(&timerfd_softstate);
499 499 mutex_exit(&timerfd_lock);
500 500
501 501 return (DDI_SUCCESS);
502 502 }
503 503
504 504 /*ARGSUSED*/
505 505 static int
506 506 timerfd_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
507 507 {
508 508 int error;
509 509
510 510 switch (infocmd) {
511 511 case DDI_INFO_DEVT2DEVINFO:
512 512 *result = (void *)timerfd_devi;
513 513 error = DDI_SUCCESS;
514 514 break;
515 515 case DDI_INFO_DEVT2INSTANCE:
516 516 *result = (void *)0;
517 517 error = DDI_SUCCESS;
518 518 break;
519 519 default:
520 520 error = DDI_FAILURE;
521 521 }
522 522 return (error);
523 523 }
524 524
525 525 static struct cb_ops timerfd_cb_ops = {
526 526 timerfd_open, /* open */
527 527 timerfd_close, /* close */
528 528 nulldev, /* strategy */
529 529 nulldev, /* print */
530 530 nodev, /* dump */
531 531 timerfd_read, /* read */
532 532 nodev, /* write */
533 533 timerfd_ioctl, /* ioctl */
534 534 nodev, /* devmap */
535 535 nodev, /* mmap */
536 536 nodev, /* segmap */
537 537 timerfd_poll, /* poll */
538 538 ddi_prop_op, /* cb_prop_op */
539 539 0, /* streamtab */
540 540 D_NEW | D_MP /* Driver compatibility flag */
541 541 };
542 542
543 543 static struct dev_ops timerfd_ops = {
544 544 DEVO_REV, /* devo_rev */
545 545 0, /* refcnt */
546 546 timerfd_info, /* get_dev_info */
547 547 nulldev, /* identify */
548 548 nulldev, /* probe */
549 549 timerfd_attach, /* attach */
550 550 timerfd_detach, /* detach */
551 551 nodev, /* reset */
552 552 &timerfd_cb_ops, /* driver operations */
553 553 NULL, /* bus operations */
554 554 nodev, /* dev power */
555 555 ddi_quiesce_not_needed, /* quiesce */
↓ open down ↓ |
555 lines elided |
↑ open up ↑ |
556 556 };
557 557
558 558 static struct modldrv modldrv = {
559 559 &mod_driverops, /* module type (this is a pseudo driver) */
560 560 "timerfd support", /* name of module */
561 561 &timerfd_ops, /* driver ops */
562 562 };
563 563
564 564 static struct modlinkage modlinkage = {
565 565 MODREV_1,
566 - (void *)&modldrv,
567 - NULL
566 + { (void *)&modldrv, NULL }
568 567 };
569 568
570 569 int
571 570 _init(void)
572 571 {
573 572 return (mod_install(&modlinkage));
574 573 }
575 574
576 575 int
577 576 _info(struct modinfo *modinfop)
578 577 {
579 578 return (mod_info(&modlinkage, modinfop));
580 579 }
581 580
582 581 int
583 582 _fini(void)
584 583 {
585 584 return (mod_remove(&modlinkage));
586 585 }
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX