13792Sakolb /* 23792Sakolb * CDDL HEADER START 33792Sakolb * 43792Sakolb * The contents of this file are subject to the terms of the 53792Sakolb * Common Development and Distribution License (the "License"). 63792Sakolb * You may not use this file except in compliance with the License. 73792Sakolb * 83792Sakolb * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 93792Sakolb * or http://www.opensolaris.org/os/licensing. 103792Sakolb * See the License for the specific language governing permissions 113792Sakolb * and limitations under the License. 123792Sakolb * 133792Sakolb * When distributing Covered Code, include this CDDL HEADER in each 143792Sakolb * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 153792Sakolb * If applicable, add the following below this CDDL HEADER, with the 163792Sakolb * fields enclosed by brackets "[]" replaced with your own identifying 173792Sakolb * information: Portions Copyright [yyyy] [name of copyright owner] 183792Sakolb * 193792Sakolb * CDDL HEADER END 203792Sakolb */ 213792Sakolb /* 223792Sakolb * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 233792Sakolb * Use is subject to license terms. 243792Sakolb */ 253792Sakolb 263792Sakolb #pragma ident "%Z%%M% %I% %E% SMI" 273792Sakolb 283792Sakolb #include <sys/param.h> 293792Sakolb #include <sys/systm.h> 303792Sakolb #include <sys/thread.h> 313792Sakolb #include <sys/class.h> 323792Sakolb #include <sys/debug.h> 333792Sakolb #include <sys/cpuvar.h> 343792Sakolb #include <sys/waitq.h> 353792Sakolb #include <sys/cmn_err.h> 363792Sakolb #include <sys/time.h> 373792Sakolb #include <sys/dtrace.h> 383792Sakolb #include <sys/sdt.h> 393792Sakolb #include <sys/zone.h> 403792Sakolb 413792Sakolb /* 423792Sakolb * Wait queue implementation. 433792Sakolb */ 443792Sakolb 453792Sakolb void 463792Sakolb waitq_init(waitq_t *wq) 473792Sakolb { 483792Sakolb DISP_LOCK_INIT(&wq->wq_lock); 493792Sakolb wq->wq_first = NULL; 503792Sakolb wq->wq_count = 0; 513792Sakolb wq->wq_blocked = B_TRUE; 523792Sakolb } 533792Sakolb 543792Sakolb void 553792Sakolb waitq_fini(waitq_t *wq) 563792Sakolb { 573792Sakolb ASSERT(wq->wq_count == 0); 583792Sakolb ASSERT(wq->wq_first == NULL); 593792Sakolb ASSERT(wq->wq_blocked == B_TRUE); 603792Sakolb ASSERT(!DISP_LOCK_HELD(&wq->wq_lock)); 613792Sakolb 623792Sakolb DISP_LOCK_DESTROY(&wq->wq_lock); 633792Sakolb } 643792Sakolb 653792Sakolb /* 663792Sakolb * Operations on waitq_t structures. 673792Sakolb * 683792Sakolb * A wait queue is a singly linked NULL-terminated list with doubly 693792Sakolb * linked circular sublists. The singly linked list is in descending 703792Sakolb * priority order and FIFO for threads of the same priority. It links 713792Sakolb * through the t_link field of the thread structure. The doubly linked 723792Sakolb * sublists link threads of the same priority. They use the t_priforw 733792Sakolb * and t_priback fields of the thread structure. 743792Sakolb * 753792Sakolb * Graphically (with priorities in parens): 763792Sakolb * 773792Sakolb * ________________ _______ _______ 783792Sakolb * / \ / \ / \ 793792Sakolb * | | | | | | 803792Sakolb * v v v v v v 813792Sakolb * t1(60)-->t2(60)-->t3(60)-->t4(50)-->t5(50)-->t6(30)-->t7(0)-->t8(0) 823792Sakolb * ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ 833792Sakolb * | | | | | | | | | | 843792Sakolb * \______/ \______/ \_______/ \__/ \_______/ 853792Sakolb * 863792Sakolb * There are three interesting operations on a waitq list: inserting 873792Sakolb * a thread into the proper position according to priority; removing a 883792Sakolb * thread given a pointer to it; and walking the list, possibly 893792Sakolb * removing threads along the way. This design allows all three 903792Sakolb * operations to be performed efficiently and easily. 913792Sakolb * 923792Sakolb * To insert a thread, traverse the list looking for the sublist of 933792Sakolb * the same priority as the thread (or one of a lower priority, 943792Sakolb * meaning there are no other threads in the list of the same 953792Sakolb * priority). This can be done without touching all threads in the 963792Sakolb * list by following the links between the first threads in each 973792Sakolb * sublist. Given a thread t that is the head of a sublist (the first 983792Sakolb * thread of that priority found when following the t_link pointers), 993792Sakolb * t->t_priback->t_link points to the head of the next sublist. It's 1003792Sakolb * important to do this since a waitq may contain thousands of 1013792Sakolb * threads. 1023792Sakolb * 1033792Sakolb * Removing a thread from the list is also efficient. First, the 1043792Sakolb * t_waitq field contains a pointer to the waitq on which a thread 1053792Sakolb * is waiting (or NULL if it's not on a waitq). This is used to 1063792Sakolb * determine if the given thread is on the given waitq without 1073792Sakolb * searching the list. Assuming it is, if it's not the head of a 1083792Sakolb * sublist, just remove it from the sublist and use the t_priback 1093792Sakolb * pointer to find the thread that points to it with t_link. If it is 1103792Sakolb * the head of a sublist, search for it by walking the sublist heads, 1113792Sakolb * similar to searching for a given priority level when inserting a 1123792Sakolb * thread. 1133792Sakolb * 1143792Sakolb * To walk the list, simply follow the t_link pointers. Removing 1153792Sakolb * threads along the way can be done easily if the code maintains a 1163792Sakolb * pointer to the t_link field that pointed to the thread being 1173792Sakolb * removed. 1183792Sakolb */ 1193792Sakolb 1203792Sakolb static void 1213792Sakolb waitq_link(waitq_t *wq, kthread_t *t) 1223792Sakolb { 1233792Sakolb kthread_t *next_tp; 1243792Sakolb kthread_t *last_tp; 1253792Sakolb kthread_t **tpp; 1263792Sakolb pri_t tpri, next_pri, last_pri = -1; 1273792Sakolb 1283792Sakolb ASSERT(DISP_LOCK_HELD(&wq->wq_lock)); 1293792Sakolb 1303792Sakolb tpri = DISP_PRIO(t); 1313792Sakolb tpp = &wq->wq_first; 1323792Sakolb while ((next_tp = *tpp) != NULL) { 1333792Sakolb next_pri = DISP_PRIO(next_tp); 1343792Sakolb if (tpri > next_pri) 1353792Sakolb break; 1363792Sakolb last_tp = next_tp->t_priback; 1373792Sakolb last_pri = next_pri; 1383792Sakolb tpp = &last_tp->t_link; 1393792Sakolb } 1403792Sakolb *tpp = t; 1413792Sakolb t->t_link = next_tp; 1423792Sakolb if (last_pri == tpri) { 1433792Sakolb /* last_tp points to the last thread of this priority */ 1443792Sakolb t->t_priback = last_tp; 1453792Sakolb t->t_priforw = last_tp->t_priforw; 1463792Sakolb last_tp->t_priforw->t_priback = t; 1473792Sakolb last_tp->t_priforw = t; 1483792Sakolb } else { 1493792Sakolb t->t_priback = t->t_priforw = t; 1503792Sakolb } 1513792Sakolb wq->wq_count++; 1523792Sakolb t->t_waitq = wq; 1533792Sakolb } 1543792Sakolb 1553792Sakolb static void 1563792Sakolb waitq_unlink(waitq_t *wq, kthread_t *t) 1573792Sakolb { 1583792Sakolb kthread_t *nt; 1593792Sakolb kthread_t **ptl; 1603792Sakolb 1613792Sakolb ASSERT(THREAD_LOCK_HELD(t)); 1623792Sakolb ASSERT(DISP_LOCK_HELD(&wq->wq_lock)); 1633792Sakolb ASSERT(t->t_waitq == wq); 1643792Sakolb 1653792Sakolb ptl = &t->t_priback->t_link; 1663792Sakolb /* 1673792Sakolb * Is it the head of a priority sublist? If so, need to walk 1683792Sakolb * the priorities to find the t_link pointer that points to it. 1693792Sakolb */ 1703792Sakolb if (*ptl != t) { 1713792Sakolb /* 1723792Sakolb * Find the right priority level. 1733792Sakolb */ 1743792Sakolb ptl = &t->t_waitq->wq_first; 1753792Sakolb while ((nt = *ptl) != t) 1763792Sakolb ptl = &nt->t_priback->t_link; 1773792Sakolb } 1783792Sakolb /* 1793792Sakolb * Remove thread from the t_link list. 1803792Sakolb */ 1813792Sakolb *ptl = t->t_link; 1823792Sakolb 1833792Sakolb /* 1843792Sakolb * Take it off the priority sublist if there's more than one 1853792Sakolb * thread there. 1863792Sakolb */ 1873792Sakolb if (t->t_priforw != t) { 1883792Sakolb t->t_priback->t_priforw = t->t_priforw; 1893792Sakolb t->t_priforw->t_priback = t->t_priback; 1903792Sakolb } 1913792Sakolb t->t_link = NULL; 1923792Sakolb 1933792Sakolb wq->wq_count--; 1943792Sakolb t->t_waitq = NULL; 1953792Sakolb t->t_priforw = NULL; 1963792Sakolb t->t_priback = NULL; 1973792Sakolb } 1983792Sakolb 1993792Sakolb /* 2003792Sakolb * Put specified thread to specified wait queue without dropping thread's lock. 2013792Sakolb * Returns 1 if thread was successfully placed on project's wait queue, or 2023792Sakolb * 0 if wait queue is blocked. 2033792Sakolb */ 2043792Sakolb int 2053792Sakolb waitq_enqueue(waitq_t *wq, kthread_t *t) 2063792Sakolb { 2073792Sakolb ASSERT(THREAD_LOCK_HELD(t)); 2083792Sakolb ASSERT(t->t_sleepq == NULL); 2093792Sakolb ASSERT(t->t_waitq == NULL); 2103792Sakolb ASSERT(t->t_link == NULL); 2113792Sakolb 2123792Sakolb disp_lock_enter_high(&wq->wq_lock); 2133792Sakolb 2143792Sakolb /* 2153792Sakolb * Can't enqueue anything on a blocked wait queue 2163792Sakolb */ 2173792Sakolb if (wq->wq_blocked) { 2183792Sakolb disp_lock_exit_high(&wq->wq_lock); 2193792Sakolb return (0); 2203792Sakolb } 2213792Sakolb 2223792Sakolb /* 2233792Sakolb * Mark the time when thread is placed on wait queue. The microstate 2243792Sakolb * accounting code uses this timestamp to determine wait times. 2253792Sakolb */ 2263792Sakolb t->t_waitrq = gethrtime_unscaled(); 2273792Sakolb 2283792Sakolb /* 2293792Sakolb * Mark thread as not swappable. If necessary, it will get 2303792Sakolb * swapped out when it returns to the userland. 2313792Sakolb */ 2323792Sakolb t->t_schedflag |= TS_DONT_SWAP; 2333792Sakolb DTRACE_SCHED1(cpucaps__sleep, kthread_t *, t); 2343792Sakolb waitq_link(wq, t); 2353792Sakolb 2363792Sakolb THREAD_WAIT(t, &wq->wq_lock); 2373792Sakolb return (1); 2383792Sakolb } 2393792Sakolb 2403792Sakolb /* 2413792Sakolb * Change thread's priority while on the wait queue. 2423792Sakolb * Dequeue and equeue it again so that it gets placed in the right place. 2433792Sakolb */ 2443792Sakolb void 2453792Sakolb waitq_change_pri(kthread_t *t, pri_t new_pri) 2463792Sakolb { 2473792Sakolb waitq_t *wq = t->t_waitq; 2483792Sakolb 2493792Sakolb ASSERT(THREAD_LOCK_HELD(t)); 2503792Sakolb ASSERT(ISWAITING(t)); 2513792Sakolb ASSERT(wq != NULL); 2523792Sakolb 2533792Sakolb waitq_unlink(wq, t); 2543792Sakolb t->t_pri = new_pri; 2553792Sakolb waitq_link(wq, t); 2563792Sakolb } 2573792Sakolb 2583792Sakolb static void 2593792Sakolb waitq_dequeue(waitq_t *wq, kthread_t *t) 2603792Sakolb { 2613792Sakolb ASSERT(THREAD_LOCK_HELD(t)); 2623792Sakolb ASSERT(t->t_waitq == wq); 2633792Sakolb ASSERT(ISWAITING(t)); 2643792Sakolb 2653792Sakolb waitq_unlink(wq, t); 2663792Sakolb DTRACE_SCHED1(cpucaps__wakeup, kthread_t *, t); 2673792Sakolb 2683792Sakolb /* 269*4994Sakolb * Change thread to transition state and drop the wait queue lock. The 270*4994Sakolb * thread will remain locked since its t_lockp points to the 271*4994Sakolb * transition_lock. 2723792Sakolb */ 273*4994Sakolb THREAD_TRANSITION(t); 2743792Sakolb } 2753792Sakolb 2763792Sakolb /* 2773792Sakolb * Return True iff there are any threads on the specified wait queue. 2783792Sakolb * The check is done **without holding any locks**. 2793792Sakolb */ 2803792Sakolb boolean_t 2813792Sakolb waitq_isempty(waitq_t *wq) 2823792Sakolb { 2833792Sakolb return (wq->wq_count == 0); 2843792Sakolb } 2853792Sakolb 2863792Sakolb /* 2873792Sakolb * Take thread off its wait queue and make it runnable. 2883792Sakolb * Returns with thread lock held. 2893792Sakolb */ 2903792Sakolb void 2913792Sakolb waitq_setrun(kthread_t *t) 2923792Sakolb { 2933792Sakolb waitq_t *wq = t->t_waitq; 2943792Sakolb 2953792Sakolb ASSERT(THREAD_LOCK_HELD(t)); 2963792Sakolb 2973792Sakolb ASSERT(ISWAITING(t)); 2983792Sakolb if (wq == NULL) 2993792Sakolb panic("waitq_setrun: thread %p is not on waitq", t); 3003792Sakolb waitq_dequeue(wq, t); 3013792Sakolb CL_SETRUN(t); 3023792Sakolb } 3033792Sakolb 3043792Sakolb /* 3053792Sakolb * Take the first thread off the wait queue and return pointer to it. 3063792Sakolb */ 3073792Sakolb static kthread_t * 3083792Sakolb waitq_takeone(waitq_t *wq) 3093792Sakolb { 3103792Sakolb kthread_t *t; 3113792Sakolb 3123792Sakolb disp_lock_enter(&wq->wq_lock); 313*4994Sakolb /* 314*4994Sakolb * waitq_dequeue drops wait queue lock but leaves the CPU at high PIL. 315*4994Sakolb */ 3163792Sakolb if ((t = wq->wq_first) != NULL) 3173792Sakolb waitq_dequeue(wq, wq->wq_first); 318*4994Sakolb else 319*4994Sakolb disp_lock_exit(&wq->wq_lock); 3203792Sakolb return (t); 3213792Sakolb } 3223792Sakolb 3233792Sakolb /* 3243792Sakolb * Take the first thread off the wait queue and make it runnable. 3253792Sakolb * Return the pointer to the thread or NULL if waitq is empty 3263792Sakolb */ 3273792Sakolb static kthread_t * 3283792Sakolb waitq_runfirst(waitq_t *wq) 3293792Sakolb { 3303792Sakolb kthread_t *t; 3313792Sakolb 3323792Sakolb t = waitq_takeone(wq); 3333792Sakolb if (t != NULL) { 334*4994Sakolb /* 335*4994Sakolb * t should have transition lock held. 336*4994Sakolb * CL_SETRUN() will replace it with dispq lock and keep it held. 337*4994Sakolb * thread_unlock() will drop dispq lock and restore PIL. 338*4994Sakolb */ 339*4994Sakolb ASSERT(THREAD_LOCK_HELD(t)); 3403792Sakolb CL_SETRUN(t); 341*4994Sakolb thread_unlock(t); 3423792Sakolb } 3433792Sakolb return (t); 3443792Sakolb } 3453792Sakolb 3463792Sakolb /* 3473792Sakolb * Take the first thread off the wait queue and make it runnable. 3483792Sakolb */ 3493792Sakolb void 3503792Sakolb waitq_runone(waitq_t *wq) 3513792Sakolb { 3523792Sakolb (void) waitq_runfirst(wq); 3533792Sakolb } 3543792Sakolb 3553792Sakolb /* 3563792Sakolb * Take all threads off the wait queue and make them runnable. 3573792Sakolb */ 3583792Sakolb static void 3593792Sakolb waitq_runall(waitq_t *wq) 3603792Sakolb { 3613792Sakolb while (waitq_runfirst(wq) != NULL) 3623792Sakolb ; 3633792Sakolb } 3643792Sakolb 3653792Sakolb /* 3663792Sakolb * Prevent any new threads from entering wait queue and make all threads 3673792Sakolb * currently on the wait queue runnable. After waitq_block() completion, no 3683792Sakolb * threads should ever appear on the wait queue untill it is unblocked. 3693792Sakolb */ 3703792Sakolb void 3713792Sakolb waitq_block(waitq_t *wq) 3723792Sakolb { 3733792Sakolb ASSERT(!wq->wq_blocked); 3743792Sakolb disp_lock_enter(&wq->wq_lock); 3753792Sakolb wq->wq_blocked = B_TRUE; 3763792Sakolb disp_lock_exit(&wq->wq_lock); 3773792Sakolb waitq_runall(wq); 3783792Sakolb ASSERT(waitq_isempty(wq)); 3793792Sakolb } 3803792Sakolb 3813792Sakolb /* 3823792Sakolb * Allow threads to be placed on the wait queue. 3833792Sakolb */ 3843792Sakolb void 3853792Sakolb waitq_unblock(waitq_t *wq) 3863792Sakolb { 3873792Sakolb disp_lock_enter(&wq->wq_lock); 3883792Sakolb 3893792Sakolb ASSERT(waitq_isempty(wq)); 3903792Sakolb ASSERT(wq->wq_blocked); 3913792Sakolb 3923792Sakolb wq->wq_blocked = B_FALSE; 3933792Sakolb 3943792Sakolb disp_lock_exit(&wq->wq_lock); 3953792Sakolb } 396