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 /* 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 lwpid_t lwpid = 0; 127 128 qp = queue_lock(lsp, CV); 129 while (error == 0 && lsp->count == 0) { 130 /* 131 * SUSV3 requires FIFO queueing for semaphores, 132 * at least for SCHED_FIFO and SCHED_RR scheduling. 133 */ 134 enqueue(qp, self, 1); 135 lsp->sema_waiters = 1; 136 set_parking_flag(self, 1); 137 queue_unlock(qp); 138 /* 139 * We may have received SIGCANCEL before we 140 * called queue_lock(). If so and we are 141 * cancelable we should return EINTR. 142 */ 143 if (self->ul_cursig != 0 || 144 (self->ul_cancelable && self->ul_cancel_pending)) 145 set_parking_flag(self, 0); 146 error = __lwp_park(tsp, 0); 147 set_parking_flag(self, 0); 148 qp = queue_lock(lsp, CV); 149 if (self->ul_sleepq) /* timeout or spurious wakeup */ 150 lsp->sema_waiters = dequeue_self(qp); 151 } 152 if (error == 0) 153 lsp->count--; 154 if (lsp->count != 0 && lsp->sema_waiters) { 155 int more; 156 if ((ulwp = dequeue(qp, &more)) != NULL) { 157 no_preempt(self); 158 lwpid = ulwp->ul_lwpid; 159 } 160 lsp->sema_waiters = more; 161 } 162 queue_unlock(qp); 163 if (lwpid) { 164 (void) __lwp_unpark(lwpid); 165 preempt(self); 166 } 167 } 168 169 self->ul_wchan = NULL; 170 self->ul_sp = 0; 171 if (ssp) { 172 if (error == 0) { 173 /* we just decremented the count */ 174 count = lsp->count; 175 if (ssp->sema_min_count > count) 176 ssp->sema_min_count = count; 177 } 178 if (begin_sleep) 179 ssp->sema_wait_sleep_time += gethrtime() - begin_sleep; 180 } 181 182 if (error == EINTR) 183 _canceloff(); 184 else 185 _canceloff_nocancel(); 186 return (error); 187 } 188 189 #pragma weak sema_wait = _sema_wait 190 int 191 _sema_wait(sema_t *sp) 192 { 193 ASSERT(!curthread->ul_critical || curthread->ul_bindflags); 194 return (sema_wait_impl(sp, NULL)); 195 } 196 197 #pragma weak sema_reltimedwait = _sema_reltimedwait 198 int 199 _sema_reltimedwait(sema_t *sp, timespec_t *reltime) 200 { 201 timespec_t tslocal = *reltime; 202 203 ASSERT(!curthread->ul_critical || curthread->ul_bindflags); 204 return (sema_wait_impl(sp, &tslocal)); 205 } 206 207 #pragma weak sema_timedwait = _sema_timedwait 208 int 209 _sema_timedwait(sema_t *sp, timespec_t *abstime) 210 { 211 timespec_t tslocal; 212 213 ASSERT(!curthread->ul_critical || curthread->ul_bindflags); 214 abstime_to_reltime(CLOCK_REALTIME, abstime, &tslocal); 215 return (sema_wait_impl(sp, &tslocal)); 216 } 217 218 #pragma weak sema_trywait = _sema_trywait 219 int 220 _sema_trywait(sema_t *sp) 221 { 222 lwp_sema_t *lsp = (lwp_sema_t *)sp; 223 ulwp_t *self = curthread; 224 uberdata_t *udp = self->ul_uberdata; 225 tdb_sema_stats_t *ssp = SEMA_STATS(sp, udp); 226 uint_t count; 227 int error = 0; 228 229 ASSERT(!curthread->ul_critical || curthread->ul_bindflags); 230 231 if (ssp) 232 tdb_incr(ssp->sema_trywait); 233 234 if (lsp->type == USYNC_PROCESS) { /* kernel-level */ 235 error = __lwp_sema_trywait(lsp); 236 } else if (!udp->uberflags.uf_mt) { /* single threaded */ 237 sigoff(self); 238 if (lsp->count == 0) 239 error = EBUSY; 240 else 241 lsp->count--; 242 sigon(self); 243 } else { /* multithreaded */ 244 queue_head_t *qp; 245 ulwp_t *ulwp; 246 lwpid_t lwpid = 0; 247 248 qp = queue_lock(lsp, CV); 249 if (lsp->count == 0) 250 error = EBUSY; 251 else if (--lsp->count != 0 && lsp->sema_waiters) { 252 int more; 253 if ((ulwp = dequeue(qp, &more)) != NULL) { 254 no_preempt(self); 255 lwpid = ulwp->ul_lwpid; 256 } 257 lsp->sema_waiters = more; 258 } 259 queue_unlock(qp); 260 if (lwpid) { 261 (void) __lwp_unpark(lwpid); 262 preempt(self); 263 } 264 } 265 266 if (error == 0) { 267 if (ssp) { 268 /* we just decremented the count */ 269 count = lsp->count; 270 if (ssp->sema_min_count > count) 271 ssp->sema_min_count = count; 272 } 273 } else { 274 if (ssp) 275 tdb_incr(ssp->sema_trywait_fail); 276 if (__td_event_report(self, TD_LOCK_TRY, udp)) { 277 self->ul_td_evbuf.eventnum = TD_LOCK_TRY; 278 tdb_event(TD_LOCK_TRY, udp); 279 } 280 } 281 282 return (error); 283 } 284 285 #pragma weak sema_post = _sema_post 286 int 287 _sema_post(sema_t *sp) 288 { 289 lwp_sema_t *lsp = (lwp_sema_t *)sp; 290 ulwp_t *self = curthread; 291 uberdata_t *udp = self->ul_uberdata; 292 tdb_sema_stats_t *ssp = SEMA_STATS(sp, udp); 293 uint_t count; 294 int error = 0; 295 296 if (ssp) 297 tdb_incr(ssp->sema_post); 298 if (_semvaluemax == 0) 299 _semvaluemax = (uint32_t)_sysconf(_SC_SEM_VALUE_MAX); 300 301 if (lsp->type == USYNC_PROCESS) { /* kernel-level */ 302 error = __lwp_sema_post(lsp); 303 } else if (!udp->uberflags.uf_mt) { /* single threaded */ 304 sigoff(self); 305 if (lsp->count >= _semvaluemax) 306 error = EOVERFLOW; 307 else 308 lsp->count++; 309 sigon(self); 310 } else { /* multithreaded */ 311 queue_head_t *qp; 312 ulwp_t *ulwp; 313 lwpid_t lwpid = 0; 314 315 qp = queue_lock(lsp, CV); 316 if (lsp->count >= _semvaluemax) 317 error = EOVERFLOW; 318 else if (lsp->count++ == 0 && lsp->sema_waiters) { 319 int more; 320 if ((ulwp = dequeue(qp, &more)) != NULL) { 321 no_preempt(self); 322 lwpid = ulwp->ul_lwpid; 323 } 324 lsp->sema_waiters = more; 325 } 326 queue_unlock(qp); 327 if (lwpid) { 328 (void) __lwp_unpark(lwpid); 329 preempt(self); 330 } 331 } 332 333 if (error == 0) { 334 if (ssp) { 335 /* we just incremented the count */ 336 count = lsp->count; 337 if (ssp->sema_max_count < count) 338 ssp->sema_max_count = count; 339 } 340 } 341 342 return (error); 343 } 344