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 38 sema_held(sema_t *sp) 39 { 40 return (sp->count == 0); 41 } 42 43 #pragma weak _sema_init = sema_init 44 int 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 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 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 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 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 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 245 sema_reltimedwait(sema_t *sp, const timespec_t *reltime) 246 { 247 return (sema_relclockwait(sp, CLOCK_REALTIME, reltime)); 248 } 249 250 int 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 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 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