1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 * Copyright 2024 Oxide Computer Company
26 */
27
28 #include "lint.h"
29 #include "thr_uberdata.h"
30
31 static uint32_t _semvaluemax;
32
33 /*
34 * Check to see if anyone is waiting for this semaphore.
35 */
36 #pragma weak _sema_held = sema_held
37 int
sema_held(sema_t * sp)38 sema_held(sema_t *sp)
39 {
40 return (sp->count == 0);
41 }
42
43 #pragma weak _sema_init = sema_init
44 int
sema_init(sema_t * sp,unsigned int count,int type,void * arg __unused)45 sema_init(sema_t *sp, unsigned int count, int type, void *arg __unused)
46 {
47 if (_semvaluemax == 0)
48 _semvaluemax = (uint32_t)_sysconf(_SC_SEM_VALUE_MAX);
49 if ((type != USYNC_THREAD && type != USYNC_PROCESS) ||
50 (count > _semvaluemax))
51 return (EINVAL);
52 (void) memset(sp, 0, sizeof (*sp));
53 sp->count = count;
54 sp->type = (uint16_t)type;
55 sp->magic = SEMA_MAGIC;
56
57 /*
58 * This should be at the beginning of the function,
59 * but for the sake of old broken applications that
60 * do not have proper alignment for their semaphores
61 * (and don't check the return code from sema_init),
62 * we put it here, after initializing the semaphore regardless.
63 */
64 if (((uintptr_t)sp & (_LONG_LONG_ALIGNMENT - 1)) &&
65 curthread->ul_misaligned == 0)
66 return (EINVAL);
67
68 return (0);
69 }
70
71 #pragma weak _sema_destroy = sema_destroy
72 int
sema_destroy(sema_t * sp)73 sema_destroy(sema_t *sp)
74 {
75 sp->magic = 0;
76 tdb_sync_obj_deregister(sp);
77 return (0);
78 }
79
80 static int
sema_wait_impl(sema_t * sp,timespec_t * tsp)81 sema_wait_impl(sema_t *sp, timespec_t *tsp)
82 {
83 lwp_sema_t *lsp = (lwp_sema_t *)sp;
84 ulwp_t *self = curthread;
85 uberdata_t *udp = self->ul_uberdata;
86 tdb_sema_stats_t *ssp = SEMA_STATS(sp, udp);
87 hrtime_t begin_sleep = 0;
88 uint_t count;
89 int error = 0;
90
91 /*
92 * All variations of sema_wait() are cancellation points.
93 */
94 _cancelon();
95
96 if (ssp)
97 tdb_incr(ssp->sema_wait);
98
99 self->ul_sp = stkptr();
100 self->ul_wchan = lsp;
101 if (__td_event_report(self, TD_SLEEP, udp)) {
102 self->ul_td_evbuf.eventnum = TD_SLEEP;
103 self->ul_td_evbuf.eventdata = lsp;
104 tdb_event(TD_SLEEP, udp);
105 }
106 /* just a guess, but it looks like we will sleep */
107 if (ssp && lsp->count == 0) {
108 begin_sleep = gethrtime();
109 if (lsp->count == 0) /* still looks like sleep */
110 tdb_incr(ssp->sema_wait_sleep);
111 else /* we changed our mind */
112 begin_sleep = 0;
113 }
114
115 if (lsp->type == USYNC_PROCESS) { /* kernel-level */
116 set_parking_flag(self, 1);
117 if (self->ul_cursig != 0 ||
118 (self->ul_cancelable && self->ul_cancel_pending))
119 set_parking_flag(self, 0);
120 /* the kernel always does FIFO queueing */
121 error = ___lwp_sema_timedwait(lsp, tsp, 1);
122 set_parking_flag(self, 0);
123 } else if (!udp->uberflags.uf_mt && /* single threaded */
124 lsp->count != 0) { /* and non-blocking */
125 /*
126 * Since we are single-threaded, we don't need the
127 * protection of queue_lock(). However, we do need
128 * to block signals while modifying the count.
129 */
130 sigoff(self);
131 lsp->count--;
132 sigon(self);
133 } else { /* multithreaded or blocking */
134 queue_head_t *qp;
135 ulwp_t *ulwp;
136 lwpid_t lwpid = 0;
137
138 qp = queue_lock(lsp, CV);
139 while (error == 0 && lsp->count == 0) {
140 /*
141 * SUSV3 requires FIFO queueing for semaphores,
142 * at least for SCHED_FIFO and SCHED_RR scheduling.
143 */
144 enqueue(qp, self, 1);
145 lsp->sema_waiters = 1;
146 set_parking_flag(self, 1);
147 queue_unlock(qp);
148 /*
149 * We may have received SIGCANCEL before we
150 * called queue_lock(). If so and we are
151 * cancelable we should return EINTR.
152 */
153 if (self->ul_cursig != 0 ||
154 (self->ul_cancelable && self->ul_cancel_pending))
155 set_parking_flag(self, 0);
156 error = __lwp_park(tsp, 0);
157 set_parking_flag(self, 0);
158 qp = queue_lock(lsp, CV);
159 if (self->ul_sleepq) /* timeout or spurious wakeup */
160 lsp->sema_waiters = dequeue_self(qp);
161 }
162 if (error == 0)
163 lsp->count--;
164 if (lsp->count != 0 && lsp->sema_waiters) {
165 int more;
166 if ((ulwp = dequeue(qp, &more)) != NULL) {
167 no_preempt(self);
168 lwpid = ulwp->ul_lwpid;
169 }
170 lsp->sema_waiters = more;
171 }
172 queue_unlock(qp);
173 if (lwpid) {
174 (void) __lwp_unpark(lwpid);
175 preempt(self);
176 }
177 }
178
179 self->ul_wchan = NULL;
180 self->ul_sp = 0;
181 if (ssp) {
182 if (error == 0) {
183 /* we just decremented the count */
184 count = lsp->count;
185 if (ssp->sema_min_count > count)
186 ssp->sema_min_count = count;
187 }
188 if (begin_sleep)
189 ssp->sema_wait_sleep_time += gethrtime() - begin_sleep;
190 }
191
192 if (error == EINTR)
193 _canceloff();
194 else
195 _canceloff_nocancel();
196 return (error);
197 }
198
199 #pragma weak _sema_wait = sema_wait
200 int
sema_wait(sema_t * sp)201 sema_wait(sema_t *sp)
202 {
203 ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
204 return (sema_wait_impl(sp, NULL));
205 }
206
207 /*
208 * sema_relcockwait() and sema_clockwait() are currently only internal to libc
209 * to aid with implementing the POSIX versions of these functions.
210 */
211 int
sema_relclockwait(sema_t * sp,clockid_t clock,const timespec_t * reltime)212 sema_relclockwait(sema_t *sp, clockid_t clock, const timespec_t *reltime)
213 {
214 timespec_t tslocal = *reltime;
215
216 ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
217 switch (clock) {
218 case CLOCK_REALTIME:
219 case CLOCK_HIGHRES:
220 break;
221 default:
222 return (EINVAL);
223 }
224 return (sema_wait_impl(sp, &tslocal));
225 }
226
227 int
sema_clockwait(sema_t * sp,clockid_t clock,const timespec_t * abstime)228 sema_clockwait(sema_t *sp, clockid_t clock, const timespec_t *abstime)
229 {
230 timespec_t tslocal;
231
232 ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
233 switch (clock) {
234 case CLOCK_REALTIME:
235 case CLOCK_HIGHRES:
236 break;
237 default:
238 return (EINVAL);
239 }
240 abstime_to_reltime(clock, abstime, &tslocal);
241 return (sema_wait_impl(sp, &tslocal));
242 }
243
244 int
sema_reltimedwait(sema_t * sp,const timespec_t * reltime)245 sema_reltimedwait(sema_t *sp, const timespec_t *reltime)
246 {
247 return (sema_relclockwait(sp, CLOCK_REALTIME, reltime));
248 }
249
250 int
sema_timedwait(sema_t * sp,const timespec_t * abstime)251 sema_timedwait(sema_t *sp, const timespec_t *abstime)
252 {
253 return (sema_clockwait(sp, CLOCK_REALTIME, abstime));
254 }
255
256 #pragma weak _sema_trywait = sema_trywait
257 int
sema_trywait(sema_t * sp)258 sema_trywait(sema_t *sp)
259 {
260 lwp_sema_t *lsp = (lwp_sema_t *)sp;
261 ulwp_t *self = curthread;
262 uberdata_t *udp = self->ul_uberdata;
263 tdb_sema_stats_t *ssp = SEMA_STATS(sp, udp);
264 uint_t count;
265 int error = 0;
266
267 ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
268
269 if (ssp)
270 tdb_incr(ssp->sema_trywait);
271
272 if (lsp->type == USYNC_PROCESS) { /* kernel-level */
273 error = _lwp_sema_trywait(lsp);
274 } else if (!udp->uberflags.uf_mt) { /* single threaded */
275 sigoff(self);
276 if (lsp->count == 0)
277 error = EBUSY;
278 else
279 lsp->count--;
280 sigon(self);
281 } else { /* multithreaded */
282 queue_head_t *qp;
283 ulwp_t *ulwp;
284 lwpid_t lwpid = 0;
285
286 qp = queue_lock(lsp, CV);
287 if (lsp->count == 0)
288 error = EBUSY;
289 else if (--lsp->count != 0 && lsp->sema_waiters) {
290 int more;
291 if ((ulwp = dequeue(qp, &more)) != NULL) {
292 no_preempt(self);
293 lwpid = ulwp->ul_lwpid;
294 }
295 lsp->sema_waiters = more;
296 }
297 queue_unlock(qp);
298 if (lwpid) {
299 (void) __lwp_unpark(lwpid);
300 preempt(self);
301 }
302 }
303
304 if (error == 0) {
305 if (ssp) {
306 /* we just decremented the count */
307 count = lsp->count;
308 if (ssp->sema_min_count > count)
309 ssp->sema_min_count = count;
310 }
311 } else {
312 if (ssp)
313 tdb_incr(ssp->sema_trywait_fail);
314 if (__td_event_report(self, TD_LOCK_TRY, udp)) {
315 self->ul_td_evbuf.eventnum = TD_LOCK_TRY;
316 tdb_event(TD_LOCK_TRY, udp);
317 }
318 }
319
320 return (error);
321 }
322
323 #pragma weak _sema_post = sema_post
324 int
sema_post(sema_t * sp)325 sema_post(sema_t *sp)
326 {
327 lwp_sema_t *lsp = (lwp_sema_t *)sp;
328 ulwp_t *self = curthread;
329 uberdata_t *udp = self->ul_uberdata;
330 tdb_sema_stats_t *ssp = SEMA_STATS(sp, udp);
331 uint_t count;
332 int error = 0;
333
334 if (ssp)
335 tdb_incr(ssp->sema_post);
336 if (_semvaluemax == 0)
337 _semvaluemax = (uint32_t)_sysconf(_SC_SEM_VALUE_MAX);
338
339 if (lsp->type == USYNC_PROCESS) { /* kernel-level */
340 error = _lwp_sema_post(lsp);
341 } else if (!udp->uberflags.uf_mt) { /* single threaded */
342 sigoff(self);
343 if (lsp->count >= _semvaluemax)
344 error = EOVERFLOW;
345 else
346 lsp->count++;
347 sigon(self);
348 } else { /* multithreaded */
349 queue_head_t *qp;
350 ulwp_t *ulwp;
351 lwpid_t lwpid = 0;
352
353 qp = queue_lock(lsp, CV);
354 if (lsp->count >= _semvaluemax)
355 error = EOVERFLOW;
356 else if (lsp->count++ == 0 && lsp->sema_waiters) {
357 int more;
358 if ((ulwp = dequeue(qp, &more)) != NULL) {
359 no_preempt(self);
360 lwpid = ulwp->ul_lwpid;
361 }
362 lsp->sema_waiters = more;
363 }
364 queue_unlock(qp);
365 if (lwpid) {
366 (void) __lwp_unpark(lwpid);
367 preempt(self);
368 }
369 }
370
371 if (error == 0) {
372 if (ssp) {
373 /* we just incremented the count */
374 count = lsp->count;
375 if (ssp->sema_max_count < count)
376 ssp->sema_max_count = count;
377 }
378 }
379
380 return (error);
381 }
382