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