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