1*3792Sakolb /* 2*3792Sakolb * CDDL HEADER START 3*3792Sakolb * 4*3792Sakolb * The contents of this file are subject to the terms of the 5*3792Sakolb * Common Development and Distribution License (the "License"). 6*3792Sakolb * You may not use this file except in compliance with the License. 7*3792Sakolb * 8*3792Sakolb * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*3792Sakolb * or http://www.opensolaris.org/os/licensing. 10*3792Sakolb * See the License for the specific language governing permissions 11*3792Sakolb * and limitations under the License. 12*3792Sakolb * 13*3792Sakolb * When distributing Covered Code, include this CDDL HEADER in each 14*3792Sakolb * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*3792Sakolb * If applicable, add the following below this CDDL HEADER, with the 16*3792Sakolb * fields enclosed by brackets "[]" replaced with your own identifying 17*3792Sakolb * information: Portions Copyright [yyyy] [name of copyright owner] 18*3792Sakolb * 19*3792Sakolb * CDDL HEADER END 20*3792Sakolb */ 21*3792Sakolb /* 22*3792Sakolb * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23*3792Sakolb * Use is subject to license terms. 24*3792Sakolb */ 25*3792Sakolb 26*3792Sakolb #pragma ident "%Z%%M% %I% %E% SMI" 27*3792Sakolb 28*3792Sakolb #include <sys/param.h> 29*3792Sakolb #include <sys/systm.h> 30*3792Sakolb #include <sys/thread.h> 31*3792Sakolb #include <sys/class.h> 32*3792Sakolb #include <sys/debug.h> 33*3792Sakolb #include <sys/cpuvar.h> 34*3792Sakolb #include <sys/waitq.h> 35*3792Sakolb #include <sys/cmn_err.h> 36*3792Sakolb #include <sys/time.h> 37*3792Sakolb #include <sys/dtrace.h> 38*3792Sakolb #include <sys/sdt.h> 39*3792Sakolb #include <sys/zone.h> 40*3792Sakolb 41*3792Sakolb /* 42*3792Sakolb * Wait queue implementation. 43*3792Sakolb */ 44*3792Sakolb 45*3792Sakolb void 46*3792Sakolb waitq_init(waitq_t *wq) 47*3792Sakolb { 48*3792Sakolb DISP_LOCK_INIT(&wq->wq_lock); 49*3792Sakolb wq->wq_first = NULL; 50*3792Sakolb wq->wq_count = 0; 51*3792Sakolb wq->wq_blocked = B_TRUE; 52*3792Sakolb } 53*3792Sakolb 54*3792Sakolb void 55*3792Sakolb waitq_fini(waitq_t *wq) 56*3792Sakolb { 57*3792Sakolb ASSERT(wq->wq_count == 0); 58*3792Sakolb ASSERT(wq->wq_first == NULL); 59*3792Sakolb ASSERT(wq->wq_blocked == B_TRUE); 60*3792Sakolb ASSERT(!DISP_LOCK_HELD(&wq->wq_lock)); 61*3792Sakolb 62*3792Sakolb DISP_LOCK_DESTROY(&wq->wq_lock); 63*3792Sakolb } 64*3792Sakolb 65*3792Sakolb /* 66*3792Sakolb * Operations on waitq_t structures. 67*3792Sakolb * 68*3792Sakolb * A wait queue is a singly linked NULL-terminated list with doubly 69*3792Sakolb * linked circular sublists. The singly linked list is in descending 70*3792Sakolb * priority order and FIFO for threads of the same priority. It links 71*3792Sakolb * through the t_link field of the thread structure. The doubly linked 72*3792Sakolb * sublists link threads of the same priority. They use the t_priforw 73*3792Sakolb * and t_priback fields of the thread structure. 74*3792Sakolb * 75*3792Sakolb * Graphically (with priorities in parens): 76*3792Sakolb * 77*3792Sakolb * ________________ _______ _______ 78*3792Sakolb * / \ / \ / \ 79*3792Sakolb * | | | | | | 80*3792Sakolb * v v v v v v 81*3792Sakolb * t1(60)-->t2(60)-->t3(60)-->t4(50)-->t5(50)-->t6(30)-->t7(0)-->t8(0) 82*3792Sakolb * ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ 83*3792Sakolb * | | | | | | | | | | 84*3792Sakolb * \______/ \______/ \_______/ \__/ \_______/ 85*3792Sakolb * 86*3792Sakolb * There are three interesting operations on a waitq list: inserting 87*3792Sakolb * a thread into the proper position according to priority; removing a 88*3792Sakolb * thread given a pointer to it; and walking the list, possibly 89*3792Sakolb * removing threads along the way. This design allows all three 90*3792Sakolb * operations to be performed efficiently and easily. 91*3792Sakolb * 92*3792Sakolb * To insert a thread, traverse the list looking for the sublist of 93*3792Sakolb * the same priority as the thread (or one of a lower priority, 94*3792Sakolb * meaning there are no other threads in the list of the same 95*3792Sakolb * priority). This can be done without touching all threads in the 96*3792Sakolb * list by following the links between the first threads in each 97*3792Sakolb * sublist. Given a thread t that is the head of a sublist (the first 98*3792Sakolb * thread of that priority found when following the t_link pointers), 99*3792Sakolb * t->t_priback->t_link points to the head of the next sublist. It's 100*3792Sakolb * important to do this since a waitq may contain thousands of 101*3792Sakolb * threads. 102*3792Sakolb * 103*3792Sakolb * Removing a thread from the list is also efficient. First, the 104*3792Sakolb * t_waitq field contains a pointer to the waitq on which a thread 105*3792Sakolb * is waiting (or NULL if it's not on a waitq). This is used to 106*3792Sakolb * determine if the given thread is on the given waitq without 107*3792Sakolb * searching the list. Assuming it is, if it's not the head of a 108*3792Sakolb * sublist, just remove it from the sublist and use the t_priback 109*3792Sakolb * pointer to find the thread that points to it with t_link. If it is 110*3792Sakolb * the head of a sublist, search for it by walking the sublist heads, 111*3792Sakolb * similar to searching for a given priority level when inserting a 112*3792Sakolb * thread. 113*3792Sakolb * 114*3792Sakolb * To walk the list, simply follow the t_link pointers. Removing 115*3792Sakolb * threads along the way can be done easily if the code maintains a 116*3792Sakolb * pointer to the t_link field that pointed to the thread being 117*3792Sakolb * removed. 118*3792Sakolb */ 119*3792Sakolb 120*3792Sakolb static void 121*3792Sakolb waitq_link(waitq_t *wq, kthread_t *t) 122*3792Sakolb { 123*3792Sakolb kthread_t *next_tp; 124*3792Sakolb kthread_t *last_tp; 125*3792Sakolb kthread_t **tpp; 126*3792Sakolb pri_t tpri, next_pri, last_pri = -1; 127*3792Sakolb 128*3792Sakolb ASSERT(DISP_LOCK_HELD(&wq->wq_lock)); 129*3792Sakolb 130*3792Sakolb tpri = DISP_PRIO(t); 131*3792Sakolb tpp = &wq->wq_first; 132*3792Sakolb while ((next_tp = *tpp) != NULL) { 133*3792Sakolb next_pri = DISP_PRIO(next_tp); 134*3792Sakolb if (tpri > next_pri) 135*3792Sakolb break; 136*3792Sakolb last_tp = next_tp->t_priback; 137*3792Sakolb last_pri = next_pri; 138*3792Sakolb tpp = &last_tp->t_link; 139*3792Sakolb } 140*3792Sakolb *tpp = t; 141*3792Sakolb t->t_link = next_tp; 142*3792Sakolb if (last_pri == tpri) { 143*3792Sakolb /* last_tp points to the last thread of this priority */ 144*3792Sakolb t->t_priback = last_tp; 145*3792Sakolb t->t_priforw = last_tp->t_priforw; 146*3792Sakolb last_tp->t_priforw->t_priback = t; 147*3792Sakolb last_tp->t_priforw = t; 148*3792Sakolb } else { 149*3792Sakolb t->t_priback = t->t_priforw = t; 150*3792Sakolb } 151*3792Sakolb wq->wq_count++; 152*3792Sakolb t->t_waitq = wq; 153*3792Sakolb } 154*3792Sakolb 155*3792Sakolb static void 156*3792Sakolb waitq_unlink(waitq_t *wq, kthread_t *t) 157*3792Sakolb { 158*3792Sakolb kthread_t *nt; 159*3792Sakolb kthread_t **ptl; 160*3792Sakolb 161*3792Sakolb ASSERT(THREAD_LOCK_HELD(t)); 162*3792Sakolb ASSERT(DISP_LOCK_HELD(&wq->wq_lock)); 163*3792Sakolb ASSERT(t->t_waitq == wq); 164*3792Sakolb 165*3792Sakolb ptl = &t->t_priback->t_link; 166*3792Sakolb /* 167*3792Sakolb * Is it the head of a priority sublist? If so, need to walk 168*3792Sakolb * the priorities to find the t_link pointer that points to it. 169*3792Sakolb */ 170*3792Sakolb if (*ptl != t) { 171*3792Sakolb /* 172*3792Sakolb * Find the right priority level. 173*3792Sakolb */ 174*3792Sakolb ptl = &t->t_waitq->wq_first; 175*3792Sakolb while ((nt = *ptl) != t) 176*3792Sakolb ptl = &nt->t_priback->t_link; 177*3792Sakolb } 178*3792Sakolb /* 179*3792Sakolb * Remove thread from the t_link list. 180*3792Sakolb */ 181*3792Sakolb *ptl = t->t_link; 182*3792Sakolb 183*3792Sakolb /* 184*3792Sakolb * Take it off the priority sublist if there's more than one 185*3792Sakolb * thread there. 186*3792Sakolb */ 187*3792Sakolb if (t->t_priforw != t) { 188*3792Sakolb t->t_priback->t_priforw = t->t_priforw; 189*3792Sakolb t->t_priforw->t_priback = t->t_priback; 190*3792Sakolb } 191*3792Sakolb t->t_link = NULL; 192*3792Sakolb 193*3792Sakolb wq->wq_count--; 194*3792Sakolb t->t_waitq = NULL; 195*3792Sakolb t->t_priforw = NULL; 196*3792Sakolb t->t_priback = NULL; 197*3792Sakolb } 198*3792Sakolb 199*3792Sakolb /* 200*3792Sakolb * Put specified thread to specified wait queue without dropping thread's lock. 201*3792Sakolb * Returns 1 if thread was successfully placed on project's wait queue, or 202*3792Sakolb * 0 if wait queue is blocked. 203*3792Sakolb */ 204*3792Sakolb int 205*3792Sakolb waitq_enqueue(waitq_t *wq, kthread_t *t) 206*3792Sakolb { 207*3792Sakolb ASSERT(THREAD_LOCK_HELD(t)); 208*3792Sakolb ASSERT(t->t_sleepq == NULL); 209*3792Sakolb ASSERT(t->t_waitq == NULL); 210*3792Sakolb ASSERT(t->t_link == NULL); 211*3792Sakolb 212*3792Sakolb disp_lock_enter_high(&wq->wq_lock); 213*3792Sakolb 214*3792Sakolb /* 215*3792Sakolb * Can't enqueue anything on a blocked wait queue 216*3792Sakolb */ 217*3792Sakolb if (wq->wq_blocked) { 218*3792Sakolb disp_lock_exit_high(&wq->wq_lock); 219*3792Sakolb return (0); 220*3792Sakolb } 221*3792Sakolb 222*3792Sakolb /* 223*3792Sakolb * Mark the time when thread is placed on wait queue. The microstate 224*3792Sakolb * accounting code uses this timestamp to determine wait times. 225*3792Sakolb */ 226*3792Sakolb t->t_waitrq = gethrtime_unscaled(); 227*3792Sakolb 228*3792Sakolb /* 229*3792Sakolb * Mark thread as not swappable. If necessary, it will get 230*3792Sakolb * swapped out when it returns to the userland. 231*3792Sakolb */ 232*3792Sakolb t->t_schedflag |= TS_DONT_SWAP; 233*3792Sakolb DTRACE_SCHED1(cpucaps__sleep, kthread_t *, t); 234*3792Sakolb waitq_link(wq, t); 235*3792Sakolb 236*3792Sakolb THREAD_WAIT(t, &wq->wq_lock); 237*3792Sakolb return (1); 238*3792Sakolb } 239*3792Sakolb 240*3792Sakolb /* 241*3792Sakolb * Change thread's priority while on the wait queue. 242*3792Sakolb * Dequeue and equeue it again so that it gets placed in the right place. 243*3792Sakolb */ 244*3792Sakolb void 245*3792Sakolb waitq_change_pri(kthread_t *t, pri_t new_pri) 246*3792Sakolb { 247*3792Sakolb waitq_t *wq = t->t_waitq; 248*3792Sakolb 249*3792Sakolb ASSERT(THREAD_LOCK_HELD(t)); 250*3792Sakolb ASSERT(ISWAITING(t)); 251*3792Sakolb ASSERT(wq != NULL); 252*3792Sakolb 253*3792Sakolb waitq_unlink(wq, t); 254*3792Sakolb t->t_pri = new_pri; 255*3792Sakolb waitq_link(wq, t); 256*3792Sakolb } 257*3792Sakolb 258*3792Sakolb static void 259*3792Sakolb waitq_dequeue(waitq_t *wq, kthread_t *t) 260*3792Sakolb { 261*3792Sakolb ASSERT(THREAD_LOCK_HELD(t)); 262*3792Sakolb ASSERT(t->t_waitq == wq); 263*3792Sakolb ASSERT(ISWAITING(t)); 264*3792Sakolb 265*3792Sakolb waitq_unlink(wq, t); 266*3792Sakolb DTRACE_SCHED1(cpucaps__wakeup, kthread_t *, t); 267*3792Sakolb 268*3792Sakolb /* 269*3792Sakolb * Change thread to transition state without dropping 270*3792Sakolb * the wait queue lock. 271*3792Sakolb */ 272*3792Sakolb THREAD_TRANSITION_NOLOCK(t); 273*3792Sakolb } 274*3792Sakolb 275*3792Sakolb /* 276*3792Sakolb * Return True iff there are any threads on the specified wait queue. 277*3792Sakolb * The check is done **without holding any locks**. 278*3792Sakolb */ 279*3792Sakolb boolean_t 280*3792Sakolb waitq_isempty(waitq_t *wq) 281*3792Sakolb { 282*3792Sakolb return (wq->wq_count == 0); 283*3792Sakolb } 284*3792Sakolb 285*3792Sakolb /* 286*3792Sakolb * Take thread off its wait queue and make it runnable. 287*3792Sakolb * Returns with thread lock held. 288*3792Sakolb */ 289*3792Sakolb void 290*3792Sakolb waitq_setrun(kthread_t *t) 291*3792Sakolb { 292*3792Sakolb waitq_t *wq = t->t_waitq; 293*3792Sakolb 294*3792Sakolb ASSERT(THREAD_LOCK_HELD(t)); 295*3792Sakolb 296*3792Sakolb ASSERT(ISWAITING(t)); 297*3792Sakolb if (wq == NULL) 298*3792Sakolb panic("waitq_setrun: thread %p is not on waitq", t); 299*3792Sakolb waitq_dequeue(wq, t); 300*3792Sakolb 301*3792Sakolb disp_lock_exit_high(&wq->wq_lock); 302*3792Sakolb CL_SETRUN(t); 303*3792Sakolb } 304*3792Sakolb 305*3792Sakolb /* 306*3792Sakolb * Take the first thread off the wait queue and return pointer to it. 307*3792Sakolb */ 308*3792Sakolb static kthread_t * 309*3792Sakolb waitq_takeone(waitq_t *wq) 310*3792Sakolb { 311*3792Sakolb kthread_t *t; 312*3792Sakolb 313*3792Sakolb disp_lock_enter(&wq->wq_lock); 314*3792Sakolb if ((t = wq->wq_first) != NULL) 315*3792Sakolb waitq_dequeue(wq, wq->wq_first); 316*3792Sakolb disp_lock_exit(&wq->wq_lock); 317*3792Sakolb return (t); 318*3792Sakolb } 319*3792Sakolb 320*3792Sakolb /* 321*3792Sakolb * Take the first thread off the wait queue and make it runnable. 322*3792Sakolb * Return the pointer to the thread or NULL if waitq is empty 323*3792Sakolb */ 324*3792Sakolb static kthread_t * 325*3792Sakolb waitq_runfirst(waitq_t *wq) 326*3792Sakolb { 327*3792Sakolb kthread_t *t; 328*3792Sakolb 329*3792Sakolb t = waitq_takeone(wq); 330*3792Sakolb if (t != NULL) { 331*3792Sakolb CL_SETRUN(t); 332*3792Sakolb thread_unlock(t); /* drops dispq lock */ 333*3792Sakolb } 334*3792Sakolb return (t); 335*3792Sakolb } 336*3792Sakolb 337*3792Sakolb /* 338*3792Sakolb * Take the first thread off the wait queue and make it runnable. 339*3792Sakolb */ 340*3792Sakolb void 341*3792Sakolb waitq_runone(waitq_t *wq) 342*3792Sakolb { 343*3792Sakolb (void) waitq_runfirst(wq); 344*3792Sakolb } 345*3792Sakolb 346*3792Sakolb /* 347*3792Sakolb * Take all threads off the wait queue and make them runnable. 348*3792Sakolb */ 349*3792Sakolb static void 350*3792Sakolb waitq_runall(waitq_t *wq) 351*3792Sakolb { 352*3792Sakolb while (waitq_runfirst(wq) != NULL) 353*3792Sakolb ; 354*3792Sakolb } 355*3792Sakolb 356*3792Sakolb /* 357*3792Sakolb * Prevent any new threads from entering wait queue and make all threads 358*3792Sakolb * currently on the wait queue runnable. After waitq_block() completion, no 359*3792Sakolb * threads should ever appear on the wait queue untill it is unblocked. 360*3792Sakolb */ 361*3792Sakolb void 362*3792Sakolb waitq_block(waitq_t *wq) 363*3792Sakolb { 364*3792Sakolb ASSERT(!wq->wq_blocked); 365*3792Sakolb disp_lock_enter(&wq->wq_lock); 366*3792Sakolb wq->wq_blocked = B_TRUE; 367*3792Sakolb disp_lock_exit(&wq->wq_lock); 368*3792Sakolb waitq_runall(wq); 369*3792Sakolb ASSERT(waitq_isempty(wq)); 370*3792Sakolb } 371*3792Sakolb 372*3792Sakolb /* 373*3792Sakolb * Allow threads to be placed on the wait queue. 374*3792Sakolb */ 375*3792Sakolb void 376*3792Sakolb waitq_unblock(waitq_t *wq) 377*3792Sakolb { 378*3792Sakolb disp_lock_enter(&wq->wq_lock); 379*3792Sakolb 380*3792Sakolb ASSERT(waitq_isempty(wq)); 381*3792Sakolb ASSERT(wq->wq_blocked); 382*3792Sakolb 383*3792Sakolb wq->wq_blocked = B_FALSE; 384*3792Sakolb 385*3792Sakolb disp_lock_exit(&wq->wq_lock); 386*3792Sakolb } 387