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 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 int 198 sema_reltimedwait(sema_t *sp, const timespec_t *reltime) 199 { 200 timespec_t tslocal = *reltime; 201 202 ASSERT(!curthread->ul_critical || curthread->ul_bindflags); 203 return (sema_wait_impl(sp, &tslocal)); 204 } 205 206 int 207 sema_timedwait(sema_t *sp, const timespec_t *abstime) 208 { 209 timespec_t tslocal; 210 211 ASSERT(!curthread->ul_critical || curthread->ul_bindflags); 212 abstime_to_reltime(CLOCK_REALTIME, abstime, &tslocal); 213 return (sema_wait_impl(sp, &tslocal)); 214 } 215 216 #pragma weak _sema_trywait = sema_trywait 217 int 218 sema_trywait(sema_t *sp) 219 { 220 lwp_sema_t *lsp = (lwp_sema_t *)sp; 221 ulwp_t *self = curthread; 222 uberdata_t *udp = self->ul_uberdata; 223 tdb_sema_stats_t *ssp = SEMA_STATS(sp, udp); 224 uint_t count; 225 int error = 0; 226 227 ASSERT(!curthread->ul_critical || curthread->ul_bindflags); 228 229 if (ssp) 230 tdb_incr(ssp->sema_trywait); 231 232 if (lsp->type == USYNC_PROCESS) { /* kernel-level */ 233 error = _lwp_sema_trywait(lsp); 234 } else if (!udp->uberflags.uf_mt) { /* single threaded */ 235 sigoff(self); 236 if (lsp->count == 0) 237 error = EBUSY; 238 else 239 lsp->count--; 240 sigon(self); 241 } else { /* multithreaded */ 242 queue_head_t *qp; 243 ulwp_t *ulwp; 244 lwpid_t lwpid = 0; 245 246 qp = queue_lock(lsp, CV); 247 if (lsp->count == 0) 248 error = EBUSY; 249 else if (--lsp->count != 0 && lsp->sema_waiters) { 250 int more; 251 if ((ulwp = dequeue(qp, &more)) != NULL) { 252 no_preempt(self); 253 lwpid = ulwp->ul_lwpid; 254 } 255 lsp->sema_waiters = more; 256 } 257 queue_unlock(qp); 258 if (lwpid) { 259 (void) __lwp_unpark(lwpid); 260 preempt(self); 261 } 262 } 263 264 if (error == 0) { 265 if (ssp) { 266 /* we just decremented the count */ 267 count = lsp->count; 268 if (ssp->sema_min_count > count) 269 ssp->sema_min_count = count; 270 } 271 } else { 272 if (ssp) 273 tdb_incr(ssp->sema_trywait_fail); 274 if (__td_event_report(self, TD_LOCK_TRY, udp)) { 275 self->ul_td_evbuf.eventnum = TD_LOCK_TRY; 276 tdb_event(TD_LOCK_TRY, udp); 277 } 278 } 279 280 return (error); 281 } 282 283 #pragma weak _sema_post = sema_post 284 int 285 sema_post(sema_t *sp) 286 { 287 lwp_sema_t *lsp = (lwp_sema_t *)sp; 288 ulwp_t *self = curthread; 289 uberdata_t *udp = self->ul_uberdata; 290 tdb_sema_stats_t *ssp = SEMA_STATS(sp, udp); 291 uint_t count; 292 int error = 0; 293 294 if (ssp) 295 tdb_incr(ssp->sema_post); 296 if (_semvaluemax == 0) 297 _semvaluemax = (uint32_t)_sysconf(_SC_SEM_VALUE_MAX); 298 299 if (lsp->type == USYNC_PROCESS) { /* kernel-level */ 300 error = _lwp_sema_post(lsp); 301 } else if (!udp->uberflags.uf_mt) { /* single threaded */ 302 sigoff(self); 303 if (lsp->count >= _semvaluemax) 304 error = EOVERFLOW; 305 else 306 lsp->count++; 307 sigon(self); 308 } else { /* multithreaded */ 309 queue_head_t *qp; 310 ulwp_t *ulwp; 311 lwpid_t lwpid = 0; 312 313 qp = queue_lock(lsp, CV); 314 if (lsp->count >= _semvaluemax) 315 error = EOVERFLOW; 316 else if (lsp->count++ == 0 && lsp->sema_waiters) { 317 int more; 318 if ((ulwp = dequeue(qp, &more)) != NULL) { 319 no_preempt(self); 320 lwpid = ulwp->ul_lwpid; 321 } 322 lsp->sema_waiters = more; 323 } 324 queue_unlock(qp); 325 if (lwpid) { 326 (void) __lwp_unpark(lwpid); 327 preempt(self); 328 } 329 } 330 331 if (error == 0) { 332 if (ssp) { 333 /* we just incremented the count */ 334 count = lsp->count; 335 if (ssp->sema_max_count < count) 336 ssp->sema_max_count = count; 337 } 338 } 339 340 return (error); 341 } 342