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 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include "lint.h" 30 #include "thr_uberdata.h" 31 32 static uint32_t _semvaluemax; 33 34 /* 35 * Check to see if anyone is waiting for this semaphore. 36 */ 37 #pragma weak _sema_held = sema_held 38 int 39 sema_held(sema_t *sp) 40 { 41 return (sp->count == 0); 42 } 43 44 #pragma weak _sema_init = sema_init 45 /* ARGSUSED3 */ 46 int 47 sema_init(sema_t *sp, unsigned int count, int type, void *arg) 48 { 49 if (_semvaluemax == 0) 50 _semvaluemax = (uint32_t)_sysconf(_SC_SEM_VALUE_MAX); 51 if ((type != USYNC_THREAD && type != USYNC_PROCESS) || 52 (count > _semvaluemax)) 53 return (EINVAL); 54 (void) memset(sp, 0, sizeof (*sp)); 55 sp->count = count; 56 sp->type = (uint16_t)type; 57 sp->magic = SEMA_MAGIC; 58 59 /* 60 * This should be at the beginning of the function, 61 * but for the sake of old broken applications that 62 * do not have proper alignment for their semaphores 63 * (and don't check the return code from sema_init), 64 * we put it here, after initializing the semaphore regardless. 65 */ 66 if (((uintptr_t)sp & (_LONG_LONG_ALIGNMENT - 1)) && 67 curthread->ul_misaligned == 0) 68 return (EINVAL); 69 70 return (0); 71 } 72 73 #pragma weak _sema_destroy = sema_destroy 74 int 75 sema_destroy(sema_t *sp) 76 { 77 sp->magic = 0; 78 tdb_sync_obj_deregister(sp); 79 return (0); 80 } 81 82 static int 83 sema_wait_impl(sema_t *sp, timespec_t *tsp) 84 { 85 lwp_sema_t *lsp = (lwp_sema_t *)sp; 86 ulwp_t *self = curthread; 87 uberdata_t *udp = self->ul_uberdata; 88 tdb_sema_stats_t *ssp = SEMA_STATS(sp, udp); 89 hrtime_t begin_sleep = 0; 90 uint_t count; 91 int error = 0; 92 93 /* 94 * All variations of sema_wait() are cancellation points. 95 */ 96 _cancelon(); 97 98 if (ssp) 99 tdb_incr(ssp->sema_wait); 100 101 self->ul_sp = stkptr(); 102 self->ul_wchan = lsp; 103 if (__td_event_report(self, TD_SLEEP, udp)) { 104 self->ul_td_evbuf.eventnum = TD_SLEEP; 105 self->ul_td_evbuf.eventdata = lsp; 106 tdb_event(TD_SLEEP, udp); 107 } 108 /* just a guess, but it looks like we will sleep */ 109 if (ssp && lsp->count == 0) { 110 begin_sleep = gethrtime(); 111 if (lsp->count == 0) /* still looks like sleep */ 112 tdb_incr(ssp->sema_wait_sleep); 113 else /* we changed our mind */ 114 begin_sleep = 0; 115 } 116 117 if (lsp->type == USYNC_PROCESS) { /* kernel-level */ 118 set_parking_flag(self, 1); 119 if (self->ul_cursig != 0 || 120 (self->ul_cancelable && self->ul_cancel_pending)) 121 set_parking_flag(self, 0); 122 /* the kernel always does FIFO queueing */ 123 error = ___lwp_sema_timedwait(lsp, tsp, 1); 124 set_parking_flag(self, 0); 125 } else if (!udp->uberflags.uf_mt && /* single threaded */ 126 lsp->count != 0) { /* and non-blocking */ 127 /* 128 * Since we are single-threaded, we don't need the 129 * protection of queue_lock(). However, we do need 130 * to block signals while modifying the count. 131 */ 132 sigoff(self); 133 lsp->count--; 134 sigon(self); 135 } else { /* multithreaded or blocking */ 136 queue_head_t *qp; 137 ulwp_t *ulwp; 138 lwpid_t lwpid = 0; 139 140 qp = queue_lock(lsp, CV); 141 while (error == 0 && lsp->count == 0) { 142 /* 143 * SUSV3 requires FIFO queueing for semaphores, 144 * at least for SCHED_FIFO and SCHED_RR scheduling. 145 */ 146 enqueue(qp, self, 1); 147 lsp->sema_waiters = 1; 148 set_parking_flag(self, 1); 149 queue_unlock(qp); 150 /* 151 * We may have received SIGCANCEL before we 152 * called queue_lock(). If so and we are 153 * cancelable we should return EINTR. 154 */ 155 if (self->ul_cursig != 0 || 156 (self->ul_cancelable && self->ul_cancel_pending)) 157 set_parking_flag(self, 0); 158 error = __lwp_park(tsp, 0); 159 set_parking_flag(self, 0); 160 qp = queue_lock(lsp, CV); 161 if (self->ul_sleepq) /* timeout or spurious wakeup */ 162 lsp->sema_waiters = dequeue_self(qp); 163 } 164 if (error == 0) 165 lsp->count--; 166 if (lsp->count != 0 && lsp->sema_waiters) { 167 int more; 168 if ((ulwp = dequeue(qp, &more)) != NULL) { 169 no_preempt(self); 170 lwpid = ulwp->ul_lwpid; 171 } 172 lsp->sema_waiters = more; 173 } 174 queue_unlock(qp); 175 if (lwpid) { 176 (void) __lwp_unpark(lwpid); 177 preempt(self); 178 } 179 } 180 181 self->ul_wchan = NULL; 182 self->ul_sp = 0; 183 if (ssp) { 184 if (error == 0) { 185 /* we just decremented the count */ 186 count = lsp->count; 187 if (ssp->sema_min_count > count) 188 ssp->sema_min_count = count; 189 } 190 if (begin_sleep) 191 ssp->sema_wait_sleep_time += gethrtime() - begin_sleep; 192 } 193 194 if (error == EINTR) 195 _canceloff(); 196 else 197 _canceloff_nocancel(); 198 return (error); 199 } 200 201 #pragma weak _sema_wait = sema_wait 202 int 203 sema_wait(sema_t *sp) 204 { 205 ASSERT(!curthread->ul_critical || curthread->ul_bindflags); 206 return (sema_wait_impl(sp, NULL)); 207 } 208 209 int 210 sema_reltimedwait(sema_t *sp, const timespec_t *reltime) 211 { 212 timespec_t tslocal = *reltime; 213 214 ASSERT(!curthread->ul_critical || curthread->ul_bindflags); 215 return (sema_wait_impl(sp, &tslocal)); 216 } 217 218 int 219 sema_timedwait(sema_t *sp, const timespec_t *abstime) 220 { 221 timespec_t tslocal; 222 223 ASSERT(!curthread->ul_critical || curthread->ul_bindflags); 224 abstime_to_reltime(CLOCK_REALTIME, abstime, &tslocal); 225 return (sema_wait_impl(sp, &tslocal)); 226 } 227 228 #pragma weak _sema_trywait = sema_trywait 229 int 230 sema_trywait(sema_t *sp) 231 { 232 lwp_sema_t *lsp = (lwp_sema_t *)sp; 233 ulwp_t *self = curthread; 234 uberdata_t *udp = self->ul_uberdata; 235 tdb_sema_stats_t *ssp = SEMA_STATS(sp, udp); 236 uint_t count; 237 int error = 0; 238 239 ASSERT(!curthread->ul_critical || curthread->ul_bindflags); 240 241 if (ssp) 242 tdb_incr(ssp->sema_trywait); 243 244 if (lsp->type == USYNC_PROCESS) { /* kernel-level */ 245 error = _lwp_sema_trywait(lsp); 246 } else if (!udp->uberflags.uf_mt) { /* single threaded */ 247 sigoff(self); 248 if (lsp->count == 0) 249 error = EBUSY; 250 else 251 lsp->count--; 252 sigon(self); 253 } else { /* multithreaded */ 254 queue_head_t *qp; 255 ulwp_t *ulwp; 256 lwpid_t lwpid = 0; 257 258 qp = queue_lock(lsp, CV); 259 if (lsp->count == 0) 260 error = EBUSY; 261 else if (--lsp->count != 0 && lsp->sema_waiters) { 262 int more; 263 if ((ulwp = dequeue(qp, &more)) != NULL) { 264 no_preempt(self); 265 lwpid = ulwp->ul_lwpid; 266 } 267 lsp->sema_waiters = more; 268 } 269 queue_unlock(qp); 270 if (lwpid) { 271 (void) __lwp_unpark(lwpid); 272 preempt(self); 273 } 274 } 275 276 if (error == 0) { 277 if (ssp) { 278 /* we just decremented the count */ 279 count = lsp->count; 280 if (ssp->sema_min_count > count) 281 ssp->sema_min_count = count; 282 } 283 } else { 284 if (ssp) 285 tdb_incr(ssp->sema_trywait_fail); 286 if (__td_event_report(self, TD_LOCK_TRY, udp)) { 287 self->ul_td_evbuf.eventnum = TD_LOCK_TRY; 288 tdb_event(TD_LOCK_TRY, udp); 289 } 290 } 291 292 return (error); 293 } 294 295 #pragma weak _sema_post = sema_post 296 int 297 sema_post(sema_t *sp) 298 { 299 lwp_sema_t *lsp = (lwp_sema_t *)sp; 300 ulwp_t *self = curthread; 301 uberdata_t *udp = self->ul_uberdata; 302 tdb_sema_stats_t *ssp = SEMA_STATS(sp, udp); 303 uint_t count; 304 int error = 0; 305 306 if (ssp) 307 tdb_incr(ssp->sema_post); 308 if (_semvaluemax == 0) 309 _semvaluemax = (uint32_t)_sysconf(_SC_SEM_VALUE_MAX); 310 311 if (lsp->type == USYNC_PROCESS) { /* kernel-level */ 312 error = _lwp_sema_post(lsp); 313 } else if (!udp->uberflags.uf_mt) { /* single threaded */ 314 sigoff(self); 315 if (lsp->count >= _semvaluemax) 316 error = EOVERFLOW; 317 else 318 lsp->count++; 319 sigon(self); 320 } else { /* multithreaded */ 321 queue_head_t *qp; 322 ulwp_t *ulwp; 323 lwpid_t lwpid = 0; 324 325 qp = queue_lock(lsp, CV); 326 if (lsp->count >= _semvaluemax) 327 error = EOVERFLOW; 328 else if (lsp->count++ == 0 && lsp->sema_waiters) { 329 int more; 330 if ((ulwp = dequeue(qp, &more)) != NULL) { 331 no_preempt(self); 332 lwpid = ulwp->ul_lwpid; 333 } 334 lsp->sema_waiters = more; 335 } 336 queue_unlock(qp); 337 if (lwpid) { 338 (void) __lwp_unpark(lwpid); 339 preempt(self); 340 } 341 } 342 343 if (error == 0) { 344 if (ssp) { 345 /* we just incremented the count */ 346 count = lsp->count; 347 if (ssp->sema_max_count < count) 348 ssp->sema_max_count = count; 349 } 350 } 351 352 return (error); 353 } 354