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 /* 30 * This file contains the semaphore operations. 31 */ 32 33 #include <sys/param.h> 34 #include <sys/types.h> 35 #include <sys/systm.h> 36 #include <sys/schedctl.h> 37 #include <sys/semaphore.h> 38 #include <sys/sema_impl.h> 39 #include <sys/t_lock.h> 40 #include <sys/thread.h> 41 #include <sys/proc.h> 42 #include <sys/cmn_err.h> 43 #include <sys/debug.h> 44 #include <sys/disp.h> 45 #include <sys/sobject.h> 46 #include <sys/cpuvar.h> 47 #include <sys/sleepq.h> 48 #include <sys/sdt.h> 49 50 static void sema_unsleep(kthread_t *t); 51 static void sema_change_pri(kthread_t *t, pri_t pri, pri_t *t_prip); 52 static kthread_t *sema_owner(ksema_t *); 53 54 /* 55 * The sobj_ops vector exports a set of functions needed when a thread 56 * is asleep on a synchronization object of this type. 57 */ 58 static sobj_ops_t sema_sobj_ops = { 59 SOBJ_SEMA, sema_owner, sema_unsleep, sema_change_pri 60 }; 61 62 /* 63 * SEMA_BLOCK(sema_impl_t *s, disp_lock_t *lockp) 64 */ 65 #define SEMA_BLOCK(s, lockp) \ 66 { \ 67 kthread_t *tp; \ 68 kthread_t **tpp; \ 69 pri_t cpri; \ 70 klwp_t *lwp = ttolwp(curthread); \ 71 ASSERT(THREAD_LOCK_HELD(curthread)); \ 72 ASSERT(curthread != CPU->cpu_idle_thread); \ 73 ASSERT(CPU_ON_INTR(CPU) == 0); \ 74 ASSERT(curthread->t_wchan0 == NULL); \ 75 ASSERT(curthread->t_wchan == NULL); \ 76 ASSERT(curthread->t_state == TS_ONPROC); \ 77 CL_SLEEP(curthread); \ 78 THREAD_SLEEP(curthread, lockp); \ 79 curthread->t_wchan = (caddr_t)s; \ 80 curthread->t_sobj_ops = &sema_sobj_ops; \ 81 DTRACE_SCHED(sleep); \ 82 if (lwp != NULL) { \ 83 lwp->lwp_ru.nvcsw++; \ 84 (void) new_mstate(curthread, LMS_SLEEP); \ 85 } \ 86 cpri = DISP_PRIO(curthread); \ 87 tpp = &s->s_slpq; \ 88 while ((tp = *tpp) != NULL) { \ 89 if (cpri > DISP_PRIO(tp)) \ 90 break; \ 91 tpp = &tp->t_link; \ 92 } \ 93 *tpp = curthread; \ 94 curthread->t_link = tp; \ 95 ASSERT(s->s_slpq != NULL); \ 96 } 97 98 /* ARGSUSED */ 99 void 100 sema_init(ksema_t *sp, unsigned count, char *name, ksema_type_t type, void *arg) 101 { 102 ((sema_impl_t *)sp)->s_count = count; 103 ((sema_impl_t *)sp)->s_slpq = NULL; 104 } 105 106 void 107 sema_destroy(ksema_t *sp) 108 { 109 ASSERT(((sema_impl_t *)sp)->s_slpq == NULL); 110 } 111 112 /* 113 * Put a thread on the sleep queue for this semaphore. 114 */ 115 static void 116 sema_queue(ksema_t *sp, kthread_t *t) 117 { 118 kthread_t **tpp; 119 kthread_t *tp; 120 pri_t cpri; 121 sema_impl_t *s; 122 123 ASSERT(THREAD_LOCK_HELD(t)); 124 s = (sema_impl_t *)sp; 125 tpp = &s->s_slpq; 126 cpri = DISP_PRIO(t); 127 while ((tp = *tpp) != NULL) { 128 if (cpri > DISP_PRIO(tp)) 129 break; 130 tpp = &tp->t_link; 131 } 132 *tpp = t; 133 t->t_link = tp; 134 } 135 136 /* 137 * Remove a thread from the sleep queue for this 138 * semaphore. 139 */ 140 static void 141 sema_dequeue(ksema_t *sp, kthread_t *t) 142 { 143 kthread_t **tpp; 144 kthread_t *tp; 145 sema_impl_t *s; 146 147 ASSERT(THREAD_LOCK_HELD(t)); 148 s = (sema_impl_t *)sp; 149 tpp = &s->s_slpq; 150 while ((tp = *tpp) != NULL) { 151 if (tp == t) { 152 *tpp = t->t_link; 153 t->t_link = NULL; 154 return; 155 } 156 tpp = &tp->t_link; 157 } 158 } 159 160 /* ARGSUSED */ 161 static kthread_t * 162 sema_owner(ksema_t *sp) 163 { 164 return ((kthread_t *)NULL); 165 } 166 167 /* 168 * Wakeup a thread sleeping on a semaphore, and put it 169 * on the dispatch queue. 170 * Called via SOBJ_UNSLEEP(). 171 */ 172 static void 173 sema_unsleep(kthread_t *t) 174 { 175 kthread_t **tpp; 176 kthread_t *tp; 177 sema_impl_t *s; 178 179 ASSERT(THREAD_LOCK_HELD(t)); 180 s = (sema_impl_t *)t->t_wchan; 181 tpp = &s->s_slpq; 182 while ((tp = *tpp) != NULL) { 183 if (tp == t) { 184 *tpp = t->t_link; 185 t->t_link = NULL; 186 t->t_sobj_ops = NULL; 187 t->t_wchan = NULL; 188 t->t_wchan0 = NULL; 189 /* 190 * Change thread to transition state and 191 * drop the semaphore sleep queue lock. 192 */ 193 THREAD_TRANSITION(t); 194 CL_SETRUN(t); 195 return; 196 } 197 tpp = &tp->t_link; 198 } 199 } 200 201 /* 202 * operations to perform when changing the priority 203 * of a thread asleep on a semaphore. 204 * Called via SOBJ_CHANGE_PRI() and SOBJ_CHANGE_EPRI(). 205 */ 206 static void 207 sema_change_pri(kthread_t *t, pri_t pri, pri_t *t_prip) 208 { 209 ksema_t *sp; 210 211 if ((sp = (ksema_t *)t->t_wchan) != NULL) { 212 sema_dequeue(sp, t); 213 *t_prip = pri; 214 sema_queue(sp, t); 215 } else 216 panic("sema_change_pri: %p not on sleep queue", (void *)t); 217 } 218 219 /* 220 * the semaphore is granted when the semaphore's 221 * count is greater than zero and blocks when equal 222 * to zero. 223 */ 224 void 225 sema_p(ksema_t *sp) 226 { 227 sema_impl_t *s; 228 disp_lock_t *sqlp; 229 230 s = (sema_impl_t *)sp; 231 sqlp = &SQHASH(s)->sq_lock; 232 disp_lock_enter(sqlp); 233 ASSERT(s->s_count >= 0); 234 while (s->s_count == 0) { 235 if (panicstr) { 236 disp_lock_exit(sqlp); 237 return; 238 } 239 thread_lock_high(curthread); 240 SEMA_BLOCK(s, sqlp); 241 thread_unlock_nopreempt(curthread); 242 swtch(); 243 disp_lock_enter(sqlp); 244 } 245 s->s_count--; 246 disp_lock_exit(sqlp); 247 } 248 249 /* 250 * similiar to sema_p except that it blocks at an interruptible 251 * priority. if a signal is present then return 1 otherwise 0. 252 */ 253 int 254 sema_p_sig(ksema_t *sp) 255 { 256 kthread_t *t = curthread; 257 klwp_t *lwp = ttolwp(t); 258 int cancel_pending; 259 int cancelled = 0; 260 sema_impl_t *s; 261 disp_lock_t *sqlp; 262 263 if (lwp == NULL) { 264 sema_p(sp); 265 return (0); 266 } 267 268 cancel_pending = schedctl_cancel_pending(); 269 s = (sema_impl_t *)sp; 270 sqlp = &SQHASH(s)->sq_lock; 271 disp_lock_enter(sqlp); 272 ASSERT(s->s_count >= 0); 273 while (s->s_count == 0) { 274 proc_t *p = ttoproc(t); 275 thread_lock_high(t); 276 t->t_flag |= T_WAKEABLE; 277 SEMA_BLOCK(s, sqlp); 278 lwp->lwp_asleep = 1; 279 lwp->lwp_sysabort = 0; 280 thread_unlock_nopreempt(t); 281 if (ISSIG(t, JUSTLOOKING) || MUSTRETURN(p, t) || cancel_pending) 282 setrun(t); 283 swtch(); 284 t->t_flag &= ~T_WAKEABLE; 285 if (ISSIG(t, FORREAL) || lwp->lwp_sysabort || 286 MUSTRETURN(p, t) || (cancelled = cancel_pending) != 0) { 287 kthread_t *sq, *tp; 288 lwp->lwp_asleep = 0; 289 lwp->lwp_sysabort = 0; 290 disp_lock_enter(sqlp); 291 sq = s->s_slpq; 292 /* 293 * in case sema_v and interrupt happen 294 * at the same time, we need to pass the 295 * sema_v to the next thread. 296 */ 297 if ((sq != NULL) && (s->s_count > 0)) { 298 tp = sq; 299 ASSERT(THREAD_LOCK_HELD(tp)); 300 sq = sq->t_link; 301 tp->t_link = NULL; 302 DTRACE_SCHED1(wakeup, kthread_t *, tp); 303 tp->t_sobj_ops = NULL; 304 tp->t_wchan = NULL; 305 ASSERT(tp->t_state == TS_SLEEP); 306 CL_WAKEUP(tp); 307 s->s_slpq = sq; 308 disp_lock_exit_high(sqlp); 309 thread_unlock(tp); 310 } else { 311 disp_lock_exit(sqlp); 312 } 313 if (cancelled) 314 schedctl_cancel_eintr(); 315 return (1); 316 } 317 lwp->lwp_asleep = 0; 318 disp_lock_enter(sqlp); 319 } 320 s->s_count--; 321 disp_lock_exit(sqlp); 322 return (0); 323 } 324 325 /* 326 * the semaphore's count is incremented by one. a blocked thread 327 * is awakened and re-tries to acquire the semaphore. 328 */ 329 void 330 sema_v(ksema_t *sp) 331 { 332 sema_impl_t *s; 333 kthread_t *sq, *tp; 334 disp_lock_t *sqlp; 335 336 s = (sema_impl_t *)sp; 337 sqlp = &SQHASH(s)->sq_lock; 338 disp_lock_enter(sqlp); 339 if (panicstr) { 340 disp_lock_exit(sqlp); 341 return; 342 } 343 s->s_count++; 344 sq = s->s_slpq; 345 if (sq != NULL) { 346 tp = sq; 347 ASSERT(THREAD_LOCK_HELD(tp)); 348 sq = sq->t_link; 349 tp->t_link = NULL; 350 DTRACE_SCHED1(wakeup, kthread_t *, tp); 351 tp->t_sobj_ops = NULL; 352 tp->t_wchan = NULL; 353 ASSERT(tp->t_state == TS_SLEEP); 354 CL_WAKEUP(tp); 355 s->s_slpq = sq; 356 disp_lock_exit_high(sqlp); 357 thread_unlock(tp); 358 } else { 359 disp_lock_exit(sqlp); 360 } 361 } 362 363 /* 364 * try to acquire the semaphore. if the semaphore is greater than 365 * zero, then the semaphore is granted and returns 1. otherwise 366 * return 0. 367 */ 368 int 369 sema_tryp(ksema_t *sp) 370 { 371 sema_impl_t *s; 372 sleepq_head_t *sqh; 373 374 int gotit = 0; 375 376 s = (sema_impl_t *)sp; 377 sqh = SQHASH(s); 378 disp_lock_enter(&sqh->sq_lock); 379 if (s->s_count > 0) { 380 s->s_count--; 381 gotit = 1; 382 } 383 disp_lock_exit(&sqh->sq_lock); 384 return (gotit); 385 } 386 387 int 388 sema_held(ksema_t *sp) 389 { 390 sema_impl_t *s; 391 392 393 s = (sema_impl_t *)sp; 394 if (panicstr) 395 return (1); 396 else 397 return (s->s_count <= 0); 398 }