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 /* 28 * This file contains the semaphore operations. 29 */ 30 31 #include <sys/param.h> 32 #include <sys/types.h> 33 #include <sys/systm.h> 34 #include <sys/schedctl.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 if (panicstr) { 233 disp_lock_exit(sqlp); 234 return; 235 } 236 while (s->s_count == 0) { 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 int cancel_pending; 257 int cancelled = 0; 258 sema_impl_t *s; 259 disp_lock_t *sqlp; 260 261 if (lwp == NULL) { 262 sema_p(sp); 263 return (0); 264 } 265 266 cancel_pending = schedctl_cancel_pending(); 267 s = (sema_impl_t *)sp; 268 sqlp = &SQHASH(s)->sq_lock; 269 disp_lock_enter(sqlp); 270 ASSERT(s->s_count >= 0); 271 while (s->s_count == 0) { 272 proc_t *p = ttoproc(t); 273 thread_lock_high(t); 274 t->t_flag |= T_WAKEABLE; 275 SEMA_BLOCK(s, sqlp); 276 lwp->lwp_asleep = 1; 277 lwp->lwp_sysabort = 0; 278 thread_unlock_nopreempt(t); 279 if (ISSIG(t, JUSTLOOKING) || MUSTRETURN(p, t) || cancel_pending) 280 setrun(t); 281 swtch(); 282 t->t_flag &= ~T_WAKEABLE; 283 if (ISSIG(t, FORREAL) || lwp->lwp_sysabort || 284 MUSTRETURN(p, t) || (cancelled = cancel_pending) != 0) { 285 kthread_t *sq, *tp; 286 lwp->lwp_asleep = 0; 287 lwp->lwp_sysabort = 0; 288 disp_lock_enter(sqlp); 289 sq = s->s_slpq; 290 /* 291 * in case sema_v and interrupt happen 292 * at the same time, we need to pass the 293 * sema_v to the next thread. 294 */ 295 if ((sq != NULL) && (s->s_count > 0)) { 296 tp = sq; 297 ASSERT(THREAD_LOCK_HELD(tp)); 298 sq = sq->t_link; 299 tp->t_link = NULL; 300 DTRACE_SCHED1(wakeup, kthread_t *, tp); 301 tp->t_sobj_ops = NULL; 302 tp->t_wchan = NULL; 303 ASSERT(tp->t_state == TS_SLEEP); 304 CL_WAKEUP(tp); 305 s->s_slpq = sq; 306 disp_lock_exit_high(sqlp); 307 thread_unlock(tp); 308 } else { 309 disp_lock_exit(sqlp); 310 } 311 if (cancelled) 312 schedctl_cancel_eintr(); 313 return (1); 314 } 315 lwp->lwp_asleep = 0; 316 disp_lock_enter(sqlp); 317 } 318 s->s_count--; 319 disp_lock_exit(sqlp); 320 return (0); 321 } 322 323 /* 324 * the semaphore's count is incremented by one. a blocked thread 325 * is awakened and re-tries to acquire the semaphore. 326 */ 327 void 328 sema_v(ksema_t *sp) 329 { 330 sema_impl_t *s; 331 kthread_t *sq, *tp; 332 disp_lock_t *sqlp; 333 334 s = (sema_impl_t *)sp; 335 sqlp = &SQHASH(s)->sq_lock; 336 disp_lock_enter(sqlp); 337 if (panicstr) { 338 disp_lock_exit(sqlp); 339 return; 340 } 341 s->s_count++; 342 sq = s->s_slpq; 343 if (sq != NULL) { 344 tp = sq; 345 ASSERT(THREAD_LOCK_HELD(tp)); 346 sq = sq->t_link; 347 tp->t_link = NULL; 348 DTRACE_SCHED1(wakeup, kthread_t *, tp); 349 tp->t_sobj_ops = NULL; 350 tp->t_wchan = NULL; 351 ASSERT(tp->t_state == TS_SLEEP); 352 CL_WAKEUP(tp); 353 s->s_slpq = sq; 354 disp_lock_exit_high(sqlp); 355 thread_unlock(tp); 356 } else { 357 disp_lock_exit(sqlp); 358 } 359 } 360 361 /* 362 * try to acquire the semaphore. if the semaphore is greater than 363 * zero, then the semaphore is granted and returns 1. otherwise 364 * return 0. 365 */ 366 int 367 sema_tryp(ksema_t *sp) 368 { 369 sema_impl_t *s; 370 sleepq_head_t *sqh; 371 372 int gotit = 0; 373 374 s = (sema_impl_t *)sp; 375 sqh = SQHASH(s); 376 disp_lock_enter(&sqh->sq_lock); 377 if (panicstr) { 378 disp_lock_exit(sqlp); 379 return (1); 380 } 381 if (s->s_count > 0) { 382 s->s_count--; 383 gotit = 1; 384 } 385 disp_lock_exit(&sqh->sq_lock); 386 return (gotit); 387 } 388 389 int 390 sema_held(ksema_t *sp) 391 { 392 sema_impl_t *s; 393 394 395 s = (sema_impl_t *)sp; 396 if (panicstr) 397 return (1); 398 else 399 return (s->s_count <= 0); 400 }