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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 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 /* ARGSUSED2 */ 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 return (0); 59 } 60 61 #pragma weak sema_destroy = _sema_destroy 62 int 63 _sema_destroy(sema_t *sp) 64 { 65 sp->magic = 0; 66 tdb_sync_obj_deregister(sp); 67 return (0); 68 } 69 70 static int 71 sema_wait_impl(sema_t *sp, timespec_t *tsp) 72 { 73 lwp_sema_t *lsp = (lwp_sema_t *)sp; 74 ulwp_t *self = curthread; 75 uberdata_t *udp = self->ul_uberdata; 76 tdb_sema_stats_t *ssp = SEMA_STATS(sp, udp); 77 hrtime_t begin_sleep = 0; 78 uint_t count; 79 int error = 0; 80 81 /* 82 * All variations of sema_wait() are cancellation points. 83 */ 84 _cancelon(); 85 86 if (ssp) 87 tdb_incr(ssp->sema_wait); 88 89 self->ul_sp = stkptr(); 90 self->ul_wchan = lsp; 91 if (__td_event_report(self, TD_SLEEP, udp)) { 92 self->ul_td_evbuf.eventnum = TD_SLEEP; 93 self->ul_td_evbuf.eventdata = lsp; 94 tdb_event(TD_SLEEP, udp); 95 } 96 /* just a guess, but it looks like we will sleep */ 97 if (ssp && lsp->count == 0) { 98 begin_sleep = gethrtime(); 99 if (lsp->count == 0) /* still looks like sleep */ 100 tdb_incr(ssp->sema_wait_sleep); 101 else /* we changed our mind */ 102 begin_sleep = 0; 103 } 104 105 if (lsp->type == USYNC_PROCESS) { /* kernel-level */ 106 set_parking_flag(self, 1); 107 if (self->ul_cursig != 0 || 108 (self->ul_cancelable && self->ul_cancel_pending)) 109 set_parking_flag(self, 0); 110 /* the kernel always does FIFO queueing */ 111 error = ___lwp_sema_timedwait(lsp, tsp, 1); 112 set_parking_flag(self, 0); 113 } else if (!udp->uberflags.uf_mt && /* single threaded */ 114 lsp->count != 0) { /* and non-blocking */ 115 /* 116 * Since we are single-threaded, we don't need the 117 * protection of queue_lock(). However, we do need 118 * to block signals while modifying the count. 119 */ 120 sigoff(self); 121 lsp->count--; 122 sigon(self); 123 } else { /* multithreaded or blocking */ 124 queue_head_t *qp; 125 ulwp_t *ulwp; 126 int more; 127 lwpid_t lwpid = 0; 128 129 qp = queue_lock(lsp, CV); 130 while (error == 0 && lsp->count == 0) { 131 /* 132 * SUSV3 requires FIFO queueing for semaphores, 133 * at least for SCHED_FIFO and SCHED_RR scheduling. 134 */ 135 enqueue(qp, self, lsp, CV | FIFOQ); 136 lsp->sema_waiters = 1; 137 set_parking_flag(self, 1); 138 queue_unlock(qp); 139 /* 140 * We may have received SIGCANCEL before we 141 * called queue_lock(). If so and we are 142 * cancelable we should return EINTR. 143 */ 144 if (self->ul_cursig != 0 || 145 (self->ul_cancelable && self->ul_cancel_pending)) 146 set_parking_flag(self, 0); 147 error = __lwp_park(tsp, 0); 148 set_parking_flag(self, 0); 149 qp = queue_lock(lsp, CV); 150 if (self->ul_sleepq) /* timeout or spurious wakeup */ 151 lsp->sema_waiters = dequeue_self(qp, lsp); 152 } 153 if (error == 0) 154 lsp->count--; 155 if (lsp->count != 0 && lsp->sema_waiters) { 156 if ((ulwp = dequeue(qp, lsp, &more)) == NULL) 157 lsp->sema_waiters = 0; 158 else { 159 no_preempt(self); 160 lwpid = ulwp->ul_lwpid; 161 lsp->sema_waiters = (more? 1 : 0); 162 } 163 } 164 queue_unlock(qp); 165 if (lwpid) { 166 (void) __lwp_unpark(lwpid); 167 preempt(self); 168 } 169 } 170 171 self->ul_wchan = NULL; 172 self->ul_sp = 0; 173 if (ssp) { 174 if (error == 0) { 175 /* we just decremented the count */ 176 count = lsp->count; 177 if (ssp->sema_min_count > count) 178 ssp->sema_min_count = count; 179 } 180 if (begin_sleep) 181 ssp->sema_wait_sleep_time += gethrtime() - begin_sleep; 182 } 183 184 if (error == EINTR) 185 _canceloff(); 186 else 187 _canceloff_nocancel(); 188 return (error); 189 } 190 191 #pragma weak sema_wait = _sema_wait 192 int 193 _sema_wait(sema_t *sp) 194 { 195 ASSERT(!curthread->ul_critical || curthread->ul_bindflags); 196 return (sema_wait_impl(sp, NULL)); 197 } 198 199 #pragma weak sema_reltimedwait = _sema_reltimedwait 200 int 201 _sema_reltimedwait(sema_t *sp, timespec_t *reltime) 202 { 203 timespec_t tslocal = *reltime; 204 205 ASSERT(!curthread->ul_critical || curthread->ul_bindflags); 206 return (sema_wait_impl(sp, &tslocal)); 207 } 208 209 #pragma weak sema_timedwait = _sema_timedwait 210 int 211 _sema_timedwait(sema_t *sp, timespec_t *abstime) 212 { 213 timespec_t tslocal; 214 215 ASSERT(!curthread->ul_critical || curthread->ul_bindflags); 216 abstime_to_reltime(CLOCK_REALTIME, abstime, &tslocal); 217 return (sema_wait_impl(sp, &tslocal)); 218 } 219 220 #pragma weak sema_trywait = _sema_trywait 221 int 222 _sema_trywait(sema_t *sp) 223 { 224 lwp_sema_t *lsp = (lwp_sema_t *)sp; 225 ulwp_t *self = curthread; 226 uberdata_t *udp = self->ul_uberdata; 227 tdb_sema_stats_t *ssp = SEMA_STATS(sp, udp); 228 uint_t count; 229 int error = 0; 230 231 ASSERT(!curthread->ul_critical || curthread->ul_bindflags); 232 233 if (ssp) 234 tdb_incr(ssp->sema_trywait); 235 236 if (lsp->type == USYNC_PROCESS) { /* kernel-level */ 237 error = __lwp_sema_trywait(lsp); 238 } else if (!udp->uberflags.uf_mt) { /* single threaded */ 239 sigoff(self); 240 if (lsp->count == 0) 241 error = EBUSY; 242 else 243 lsp->count--; 244 sigon(self); 245 } else { /* multithreaded */ 246 queue_head_t *qp; 247 ulwp_t *ulwp; 248 int more; 249 lwpid_t lwpid = 0; 250 251 qp = queue_lock(lsp, CV); 252 if (lsp->count == 0) 253 error = EBUSY; 254 else if (--lsp->count != 0 && lsp->sema_waiters) { 255 if ((ulwp = dequeue(qp, lsp, &more)) == NULL) 256 lsp->sema_waiters = 0; 257 else { 258 no_preempt(self); 259 lwpid = ulwp->ul_lwpid; 260 lsp->sema_waiters = (more? 1 : 0); 261 } 262 } 263 queue_unlock(qp); 264 if (lwpid) { 265 (void) __lwp_unpark(lwpid); 266 preempt(self); 267 } 268 } 269 270 if (error == 0) { 271 if (ssp) { 272 /* we just decremented the count */ 273 count = lsp->count; 274 if (ssp->sema_min_count > count) 275 ssp->sema_min_count = count; 276 } 277 } else { 278 if (ssp) 279 tdb_incr(ssp->sema_trywait_fail); 280 if (__td_event_report(self, TD_LOCK_TRY, udp)) { 281 self->ul_td_evbuf.eventnum = TD_LOCK_TRY; 282 tdb_event(TD_LOCK_TRY, udp); 283 } 284 } 285 286 return (error); 287 } 288 289 #pragma weak sema_post = _sema_post 290 int 291 _sema_post(sema_t *sp) 292 { 293 lwp_sema_t *lsp = (lwp_sema_t *)sp; 294 ulwp_t *self = curthread; 295 uberdata_t *udp = self->ul_uberdata; 296 tdb_sema_stats_t *ssp = SEMA_STATS(sp, udp); 297 uint_t count; 298 int error = 0; 299 300 if (ssp) 301 tdb_incr(ssp->sema_post); 302 if (_semvaluemax == 0) 303 _semvaluemax = (uint32_t)_sysconf(_SC_SEM_VALUE_MAX); 304 305 if (lsp->type == USYNC_PROCESS) { /* kernel-level */ 306 error = __lwp_sema_post(lsp); 307 } else if (!udp->uberflags.uf_mt) { /* single threaded */ 308 sigoff(self); 309 if (lsp->count >= _semvaluemax) 310 error = EOVERFLOW; 311 else 312 lsp->count++; 313 sigon(self); 314 } else { /* multithreaded */ 315 queue_head_t *qp; 316 ulwp_t *ulwp; 317 int more; 318 lwpid_t lwpid = 0; 319 320 qp = queue_lock(lsp, CV); 321 if (lsp->count >= _semvaluemax) 322 error = EOVERFLOW; 323 else if (lsp->count++ == 0 && lsp->sema_waiters) { 324 if ((ulwp = dequeue(qp, lsp, &more)) == NULL) 325 lsp->sema_waiters = 0; 326 else { 327 no_preempt(self); 328 lwpid = ulwp->ul_lwpid; 329 lsp->sema_waiters = (more? 1 : 0); 330 } 331 } 332 queue_unlock(qp); 333 if (lwpid) { 334 (void) __lwp_unpark(lwpid); 335 preempt(self); 336 } 337 } 338 339 if (error == 0) { 340 if (ssp) { 341 /* we just incremented the count */ 342 count = lsp->count; 343 if (ssp->sema_max_count < count) 344 ssp->sema_max_count = count; 345 } 346 } 347 348 return (error); 349 } 350