1*fb435c17SZeeshan Lakhani /*
2*fb435c17SZeeshan Lakhani * This file and its contents are supplied under the terms of the
3*fb435c17SZeeshan Lakhani * Common Development and Distribution License ("CDDL"), version 1.0.
4*fb435c17SZeeshan Lakhani * You may only use this file in accordance with the terms of version 1.0
5*fb435c17SZeeshan Lakhani * (the "License").
6*fb435c17SZeeshan Lakhani *
7*fb435c17SZeeshan Lakhani * A full copy of the text of the CDDL should have accompanied this
8*fb435c17SZeeshan Lakhani * source. A copy of the CDDL is also available via the Internet at
9*fb435c17SZeeshan Lakhani * http://www.illumos.org/license/CDDL.
10*fb435c17SZeeshan Lakhani */
11*fb435c17SZeeshan Lakhani
12*fb435c17SZeeshan Lakhani /*
13*fb435c17SZeeshan Lakhani * Copyright 2026 Oxide Computer Company
14*fb435c17SZeeshan Lakhani */
15*fb435c17SZeeshan Lakhani
16*fb435c17SZeeshan Lakhani /*
17*fb435c17SZeeshan Lakhani * Verify that closing one of two descriptors for the same STREAMS vnode does
18*fb435c17SZeeshan Lakhani * not confuse poll cache state belonging to the other descriptor. On DEBUG
19*fb435c17SZeeshan Lakhani * kernels, checkwfdlist() used to assert that every same-process entry on the
20*fb435c17SZeeshan Lakhani * vnode's pollhead appears on the closing descriptor's per-fd fpollinfo list,
21*fb435c17SZeeshan Lakhani * panicking on this pattern. Non-DEBUG builds compile the check out. Keeping
22*fb435c17SZeeshan Lakhani * both polling threads alive while one descriptor is closed exercises the
23*fb435c17SZeeshan Lakhani * necessary check.
24*fb435c17SZeeshan Lakhani */
25*fb435c17SZeeshan Lakhani
26*fb435c17SZeeshan Lakhani #include <poll.h>
27*fb435c17SZeeshan Lakhani #include <pthread.h>
28*fb435c17SZeeshan Lakhani #include <stdlib.h>
29*fb435c17SZeeshan Lakhani #include <sys/debug.h>
30*fb435c17SZeeshan Lakhani #include <unistd.h>
31*fb435c17SZeeshan Lakhani
32*fb435c17SZeeshan Lakhani typedef struct poll_arg {
33*fb435c17SZeeshan Lakhani int pa_fd;
34*fb435c17SZeeshan Lakhani pthread_barrier_t *pa_ready;
35*fb435c17SZeeshan Lakhani pthread_barrier_t *pa_done;
36*fb435c17SZeeshan Lakhani } poll_arg_t;
37*fb435c17SZeeshan Lakhani
38*fb435c17SZeeshan Lakhani static void
barrier_wait(pthread_barrier_t * barrier)39*fb435c17SZeeshan Lakhani barrier_wait(pthread_barrier_t *barrier)
40*fb435c17SZeeshan Lakhani {
41*fb435c17SZeeshan Lakhani int ret = pthread_barrier_wait(barrier);
42*fb435c17SZeeshan Lakhani
43*fb435c17SZeeshan Lakhani VERIFY(ret == 0 || ret == PTHREAD_BARRIER_SERIAL_THREAD);
44*fb435c17SZeeshan Lakhani }
45*fb435c17SZeeshan Lakhani
46*fb435c17SZeeshan Lakhani static void *
poller(void * arg)47*fb435c17SZeeshan Lakhani poller(void *arg)
48*fb435c17SZeeshan Lakhani {
49*fb435c17SZeeshan Lakhani poll_arg_t *pa = arg;
50*fb435c17SZeeshan Lakhani struct pollfd pfd = {
51*fb435c17SZeeshan Lakhani .fd = pa->pa_fd,
52*fb435c17SZeeshan Lakhani .events = POLLIN
53*fb435c17SZeeshan Lakhani };
54*fb435c17SZeeshan Lakhani
55*fb435c17SZeeshan Lakhani VERIFY0(poll(&pfd, 1, 0));
56*fb435c17SZeeshan Lakhani VERIFY0(pfd.revents);
57*fb435c17SZeeshan Lakhani
58*fb435c17SZeeshan Lakhani barrier_wait(pa->pa_ready);
59*fb435c17SZeeshan Lakhani barrier_wait(pa->pa_done);
60*fb435c17SZeeshan Lakhani
61*fb435c17SZeeshan Lakhani return (NULL);
62*fb435c17SZeeshan Lakhani }
63*fb435c17SZeeshan Lakhani
64*fb435c17SZeeshan Lakhani int
main(void)65*fb435c17SZeeshan Lakhani main(void)
66*fb435c17SZeeshan Lakhani {
67*fb435c17SZeeshan Lakhani pthread_barrier_t ready, done;
68*fb435c17SZeeshan Lakhani pthread_t threads[2];
69*fb435c17SZeeshan Lakhani poll_arg_t args[2];
70*fb435c17SZeeshan Lakhani int pipefds[2];
71*fb435c17SZeeshan Lakhani
72*fb435c17SZeeshan Lakhani VERIFY0(pipe(pipefds));
73*fb435c17SZeeshan Lakhani args[0].pa_fd = pipefds[0];
74*fb435c17SZeeshan Lakhani args[1].pa_fd = dup(pipefds[0]);
75*fb435c17SZeeshan Lakhani VERIFY3S(args[1].pa_fd, >=, 0);
76*fb435c17SZeeshan Lakhani
77*fb435c17SZeeshan Lakhani VERIFY0(pthread_barrier_init(&ready, NULL, 3));
78*fb435c17SZeeshan Lakhani VERIFY0(pthread_barrier_init(&done, NULL, 3));
79*fb435c17SZeeshan Lakhani
80*fb435c17SZeeshan Lakhani for (uint_t i = 0; i < 2; i++) {
81*fb435c17SZeeshan Lakhani args[i].pa_ready = &ready;
82*fb435c17SZeeshan Lakhani args[i].pa_done = &done;
83*fb435c17SZeeshan Lakhani VERIFY0(pthread_create(&threads[i], NULL, poller, &args[i]));
84*fb435c17SZeeshan Lakhani }
85*fb435c17SZeeshan Lakhani
86*fb435c17SZeeshan Lakhani barrier_wait(&ready);
87*fb435c17SZeeshan Lakhani VERIFY0(close(args[0].pa_fd));
88*fb435c17SZeeshan Lakhani barrier_wait(&done);
89*fb435c17SZeeshan Lakhani
90*fb435c17SZeeshan Lakhani for (uint_t i = 0; i < 2; i++)
91*fb435c17SZeeshan Lakhani VERIFY0(pthread_join(threads[i], NULL));
92*fb435c17SZeeshan Lakhani
93*fb435c17SZeeshan Lakhani VERIFY0(pthread_barrier_destroy(&ready));
94*fb435c17SZeeshan Lakhani VERIFY0(pthread_barrier_destroy(&done));
95*fb435c17SZeeshan Lakhani VERIFY0(close(args[1].pa_fd));
96*fb435c17SZeeshan Lakhani VERIFY0(close(pipefds[1]));
97*fb435c17SZeeshan Lakhani
98*fb435c17SZeeshan Lakhani return (EXIT_SUCCESS);
99*fb435c17SZeeshan Lakhani }
100