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