xref: /illumos-gate/usr/src/uts/common/os/callb.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*7c478bd9Sstevel@tonic-gate 
29*7c478bd9Sstevel@tonic-gate #include <sys/param.h>
30*7c478bd9Sstevel@tonic-gate #include <sys/t_lock.h>
31*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
32*7c478bd9Sstevel@tonic-gate #include <sys/time.h>
33*7c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
34*7c478bd9Sstevel@tonic-gate #include <sys/systm.h>
35*7c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h>
36*7c478bd9Sstevel@tonic-gate #include <sys/user.h>
37*7c478bd9Sstevel@tonic-gate #include <sys/proc.h>
38*7c478bd9Sstevel@tonic-gate #include <sys/callb.h>
39*7c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
40*7c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
41*7c478bd9Sstevel@tonic-gate #include <sys/swap.h>
42*7c478bd9Sstevel@tonic-gate #include <sys/vmsystm.h>
43*7c478bd9Sstevel@tonic-gate #include <sys/class.h>
44*7c478bd9Sstevel@tonic-gate #include <sys/debug.h>
45*7c478bd9Sstevel@tonic-gate #include <sys/thread.h>
46*7c478bd9Sstevel@tonic-gate #include <sys/kobj.h>
47*7c478bd9Sstevel@tonic-gate #include <sys/ddi.h>	/* for delay() */
48*7c478bd9Sstevel@tonic-gate #include <sys/taskq.h>  /* For TASKQ_NAMELEN */
49*7c478bd9Sstevel@tonic-gate 
50*7c478bd9Sstevel@tonic-gate #define	CB_MAXNAME	TASKQ_NAMELEN
51*7c478bd9Sstevel@tonic-gate 
52*7c478bd9Sstevel@tonic-gate /*
53*7c478bd9Sstevel@tonic-gate  * The callb mechanism provides generic event scheduling/echoing.
54*7c478bd9Sstevel@tonic-gate  * A callb function is registered and called on behalf of the event.
55*7c478bd9Sstevel@tonic-gate  */
56*7c478bd9Sstevel@tonic-gate typedef struct callb {
57*7c478bd9Sstevel@tonic-gate 	struct callb	*c_next; 	/* next in class or on freelist */
58*7c478bd9Sstevel@tonic-gate 	kthread_id_t	c_thread;	/* ptr to caller's thread struct */
59*7c478bd9Sstevel@tonic-gate 	char		c_flag;		/* info about the callb state */
60*7c478bd9Sstevel@tonic-gate 	uchar_t		c_class;	/* this callb's class */
61*7c478bd9Sstevel@tonic-gate 	kcondvar_t	c_done_cv;	/* signal callb completion */
62*7c478bd9Sstevel@tonic-gate 	boolean_t	(*c_func)();	/* cb function: returns true if ok */
63*7c478bd9Sstevel@tonic-gate 	void		*c_arg;		/* arg to c_func */
64*7c478bd9Sstevel@tonic-gate 	char		c_name[CB_MAXNAME+1]; /* debug:max func name length */
65*7c478bd9Sstevel@tonic-gate } callb_t;
66*7c478bd9Sstevel@tonic-gate 
67*7c478bd9Sstevel@tonic-gate /*
68*7c478bd9Sstevel@tonic-gate  * callb c_flag bitmap definitions
69*7c478bd9Sstevel@tonic-gate  */
70*7c478bd9Sstevel@tonic-gate #define	CALLB_FREE		0x0
71*7c478bd9Sstevel@tonic-gate #define	CALLB_TAKEN		0x1
72*7c478bd9Sstevel@tonic-gate #define	CALLB_EXECUTING		0x2
73*7c478bd9Sstevel@tonic-gate 
74*7c478bd9Sstevel@tonic-gate /*
75*7c478bd9Sstevel@tonic-gate  * Basic structure for a callb table.
76*7c478bd9Sstevel@tonic-gate  * All callbs are organized into different class groups described
77*7c478bd9Sstevel@tonic-gate  * by ct_class array.
78*7c478bd9Sstevel@tonic-gate  * The callbs within a class are single-linked and normally run by a
79*7c478bd9Sstevel@tonic-gate  * serial execution.
80*7c478bd9Sstevel@tonic-gate  */
81*7c478bd9Sstevel@tonic-gate typedef struct callb_table {
82*7c478bd9Sstevel@tonic-gate 	kmutex_t ct_lock;		/* protect all callb states */
83*7c478bd9Sstevel@tonic-gate 	callb_t	*ct_freelist; 		/* free callb structures */
84*7c478bd9Sstevel@tonic-gate 	int	ct_busy;		/* != 0 prevents additions */
85*7c478bd9Sstevel@tonic-gate 	kcondvar_t ct_busy_cv;		/* to wait for not busy    */
86*7c478bd9Sstevel@tonic-gate 	int	ct_ncallb; 		/* num of callbs allocated */
87*7c478bd9Sstevel@tonic-gate 	callb_t	*ct_first_cb[NCBCLASS];	/* ptr to 1st callb in a class */
88*7c478bd9Sstevel@tonic-gate } callb_table_t;
89*7c478bd9Sstevel@tonic-gate 
90*7c478bd9Sstevel@tonic-gate int callb_timeout_sec = CPR_KTHREAD_TIMEOUT_SEC;
91*7c478bd9Sstevel@tonic-gate 
92*7c478bd9Sstevel@tonic-gate static callb_id_t callb_add_common(boolean_t (*)(void *, int),
93*7c478bd9Sstevel@tonic-gate     void *, int, char *, kthread_id_t);
94*7c478bd9Sstevel@tonic-gate 
95*7c478bd9Sstevel@tonic-gate static callb_table_t callb_table;	/* system level callback table */
96*7c478bd9Sstevel@tonic-gate static callb_table_t *ct = &callb_table;
97*7c478bd9Sstevel@tonic-gate static kmutex_t	callb_safe_mutex;
98*7c478bd9Sstevel@tonic-gate callb_cpr_t	callb_cprinfo_safe = {
99*7c478bd9Sstevel@tonic-gate 	&callb_safe_mutex, CALLB_CPR_ALWAYS_SAFE, 0, 0, 0 };
100*7c478bd9Sstevel@tonic-gate 
101*7c478bd9Sstevel@tonic-gate /*
102*7c478bd9Sstevel@tonic-gate  * Init all callb tables in the system.
103*7c478bd9Sstevel@tonic-gate  */
104*7c478bd9Sstevel@tonic-gate void
105*7c478bd9Sstevel@tonic-gate callb_init()
106*7c478bd9Sstevel@tonic-gate {
107*7c478bd9Sstevel@tonic-gate 	callb_table.ct_busy = 0;	/* mark table open for additions */
108*7c478bd9Sstevel@tonic-gate 	mutex_init(&callb_safe_mutex, NULL, MUTEX_DEFAULT, NULL);
109*7c478bd9Sstevel@tonic-gate 	mutex_init(&callb_table.ct_lock, NULL, MUTEX_DEFAULT, NULL);
110*7c478bd9Sstevel@tonic-gate }
111*7c478bd9Sstevel@tonic-gate 
112*7c478bd9Sstevel@tonic-gate /*
113*7c478bd9Sstevel@tonic-gate  * callout_add() is called to register func() be called later.
114*7c478bd9Sstevel@tonic-gate  */
115*7c478bd9Sstevel@tonic-gate static callb_id_t
116*7c478bd9Sstevel@tonic-gate callb_add_common(boolean_t (*func)(void *arg, int code),
117*7c478bd9Sstevel@tonic-gate     void *arg, int class, char *name, kthread_id_t t)
118*7c478bd9Sstevel@tonic-gate {
119*7c478bd9Sstevel@tonic-gate 	callb_t *cp;
120*7c478bd9Sstevel@tonic-gate 
121*7c478bd9Sstevel@tonic-gate 	ASSERT(class < NCBCLASS);
122*7c478bd9Sstevel@tonic-gate 
123*7c478bd9Sstevel@tonic-gate 	mutex_enter(&ct->ct_lock);
124*7c478bd9Sstevel@tonic-gate 	while (ct->ct_busy)
125*7c478bd9Sstevel@tonic-gate 		cv_wait(&ct->ct_busy_cv, &ct->ct_lock);
126*7c478bd9Sstevel@tonic-gate 	if ((cp = ct->ct_freelist) == NULL) {
127*7c478bd9Sstevel@tonic-gate 		ct->ct_ncallb++;
128*7c478bd9Sstevel@tonic-gate 		cp = (callb_t *)kmem_zalloc(sizeof (callb_t), KM_SLEEP);
129*7c478bd9Sstevel@tonic-gate 	}
130*7c478bd9Sstevel@tonic-gate 	ct->ct_freelist = cp->c_next;
131*7c478bd9Sstevel@tonic-gate 	cp->c_thread = t;
132*7c478bd9Sstevel@tonic-gate 	cp->c_func = func;
133*7c478bd9Sstevel@tonic-gate 	cp->c_arg = arg;
134*7c478bd9Sstevel@tonic-gate 	cp->c_class = (uchar_t)class;
135*7c478bd9Sstevel@tonic-gate 	cp->c_flag |= CALLB_TAKEN;
136*7c478bd9Sstevel@tonic-gate #ifdef DEBUG
137*7c478bd9Sstevel@tonic-gate 	if (strlen(name) > CB_MAXNAME)
138*7c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "callb_add: name of callback function '%s' "
139*7c478bd9Sstevel@tonic-gate 		    "too long -- truncated to %d chars",
140*7c478bd9Sstevel@tonic-gate 		    name, CB_MAXNAME);
141*7c478bd9Sstevel@tonic-gate #endif
142*7c478bd9Sstevel@tonic-gate 	(void) strncpy(cp->c_name, name, CB_MAXNAME);
143*7c478bd9Sstevel@tonic-gate 	cp->c_name[CB_MAXNAME] = '\0';
144*7c478bd9Sstevel@tonic-gate 
145*7c478bd9Sstevel@tonic-gate 	/*
146*7c478bd9Sstevel@tonic-gate 	 * Insert the new callb at the head of its class list.
147*7c478bd9Sstevel@tonic-gate 	 */
148*7c478bd9Sstevel@tonic-gate 	cp->c_next = ct->ct_first_cb[class];
149*7c478bd9Sstevel@tonic-gate 	ct->ct_first_cb[class] = cp;
150*7c478bd9Sstevel@tonic-gate 
151*7c478bd9Sstevel@tonic-gate 	mutex_exit(&ct->ct_lock);
152*7c478bd9Sstevel@tonic-gate 	return ((callb_id_t)cp);
153*7c478bd9Sstevel@tonic-gate }
154*7c478bd9Sstevel@tonic-gate 
155*7c478bd9Sstevel@tonic-gate /*
156*7c478bd9Sstevel@tonic-gate  * The default function to add an entry to the callback table.  Since
157*7c478bd9Sstevel@tonic-gate  * it uses curthread as the thread identifier to store in the table,
158*7c478bd9Sstevel@tonic-gate  * it should be used for the normal case of a thread which is calling
159*7c478bd9Sstevel@tonic-gate  * to add ITSELF to the table.
160*7c478bd9Sstevel@tonic-gate  */
161*7c478bd9Sstevel@tonic-gate callb_id_t
162*7c478bd9Sstevel@tonic-gate callb_add(boolean_t (*func)(void *arg, int code),
163*7c478bd9Sstevel@tonic-gate     void *arg, int class, char *name)
164*7c478bd9Sstevel@tonic-gate {
165*7c478bd9Sstevel@tonic-gate 	return (callb_add_common(func, arg, class, name, curthread));
166*7c478bd9Sstevel@tonic-gate }
167*7c478bd9Sstevel@tonic-gate 
168*7c478bd9Sstevel@tonic-gate /*
169*7c478bd9Sstevel@tonic-gate  * A special version of callb_add() above for use by threads which
170*7c478bd9Sstevel@tonic-gate  * might be adding an entry to the table on behalf of some other
171*7c478bd9Sstevel@tonic-gate  * thread (for example, one which is constructed but not yet running).
172*7c478bd9Sstevel@tonic-gate  * In this version the thread id is an argument.
173*7c478bd9Sstevel@tonic-gate  */
174*7c478bd9Sstevel@tonic-gate callb_id_t
175*7c478bd9Sstevel@tonic-gate callb_add_thread(boolean_t (*func)(void *arg, int code),
176*7c478bd9Sstevel@tonic-gate     void *arg, int class, char *name, kthread_id_t t)
177*7c478bd9Sstevel@tonic-gate {
178*7c478bd9Sstevel@tonic-gate 	return (callb_add_common(func, arg, class, name, t));
179*7c478bd9Sstevel@tonic-gate }
180*7c478bd9Sstevel@tonic-gate 
181*7c478bd9Sstevel@tonic-gate /*
182*7c478bd9Sstevel@tonic-gate  * callout_delete() is called to remove an entry identified by id
183*7c478bd9Sstevel@tonic-gate  * that was originally placed there by a call to callout_add().
184*7c478bd9Sstevel@tonic-gate  * return -1 if fail to delete a callb entry otherwise return 0.
185*7c478bd9Sstevel@tonic-gate  */
186*7c478bd9Sstevel@tonic-gate int
187*7c478bd9Sstevel@tonic-gate callb_delete(callb_id_t id)
188*7c478bd9Sstevel@tonic-gate {
189*7c478bd9Sstevel@tonic-gate 	callb_t **pp;
190*7c478bd9Sstevel@tonic-gate 	callb_t *me = (callb_t *)id;
191*7c478bd9Sstevel@tonic-gate 
192*7c478bd9Sstevel@tonic-gate 	mutex_enter(&ct->ct_lock);
193*7c478bd9Sstevel@tonic-gate 
194*7c478bd9Sstevel@tonic-gate 	for (;;) {
195*7c478bd9Sstevel@tonic-gate 		pp = &ct->ct_first_cb[me->c_class];
196*7c478bd9Sstevel@tonic-gate 		while (*pp != NULL && *pp != me)
197*7c478bd9Sstevel@tonic-gate 			pp = &(*pp)->c_next;
198*7c478bd9Sstevel@tonic-gate 
199*7c478bd9Sstevel@tonic-gate #ifdef DEBUG
200*7c478bd9Sstevel@tonic-gate 		if (*pp != me) {
201*7c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "callb delete bogus entry 0x%p",
202*7c478bd9Sstevel@tonic-gate 			    (void *)me);
203*7c478bd9Sstevel@tonic-gate 			mutex_exit(&ct->ct_lock);
204*7c478bd9Sstevel@tonic-gate 			return (-1);
205*7c478bd9Sstevel@tonic-gate 		}
206*7c478bd9Sstevel@tonic-gate #endif /* DEBUG */
207*7c478bd9Sstevel@tonic-gate 
208*7c478bd9Sstevel@tonic-gate 		/*
209*7c478bd9Sstevel@tonic-gate 		 * It is not allowed to delete a callb in the middle of
210*7c478bd9Sstevel@tonic-gate 		 * executing otherwise, the callb_execute() will be confused.
211*7c478bd9Sstevel@tonic-gate 		 */
212*7c478bd9Sstevel@tonic-gate 		if (!(me->c_flag & CALLB_EXECUTING))
213*7c478bd9Sstevel@tonic-gate 			break;
214*7c478bd9Sstevel@tonic-gate 
215*7c478bd9Sstevel@tonic-gate 		cv_wait(&me->c_done_cv, &ct->ct_lock);
216*7c478bd9Sstevel@tonic-gate 	}
217*7c478bd9Sstevel@tonic-gate 	/* relink the class list */
218*7c478bd9Sstevel@tonic-gate 	*pp = me->c_next;
219*7c478bd9Sstevel@tonic-gate 
220*7c478bd9Sstevel@tonic-gate 	/* clean up myself and return the free callb to the head of freelist */
221*7c478bd9Sstevel@tonic-gate 	me->c_flag = CALLB_FREE;
222*7c478bd9Sstevel@tonic-gate 	me->c_next = ct->ct_freelist;
223*7c478bd9Sstevel@tonic-gate 	ct->ct_freelist = me;
224*7c478bd9Sstevel@tonic-gate 
225*7c478bd9Sstevel@tonic-gate 	mutex_exit(&ct->ct_lock);
226*7c478bd9Sstevel@tonic-gate 	return (0);
227*7c478bd9Sstevel@tonic-gate }
228*7c478bd9Sstevel@tonic-gate 
229*7c478bd9Sstevel@tonic-gate /*
230*7c478bd9Sstevel@tonic-gate  * class:	indicates to execute all callbs in the same class;
231*7c478bd9Sstevel@tonic-gate  * code:	optional argument for the callb functions.
232*7c478bd9Sstevel@tonic-gate  * return:	 = 0: success
233*7c478bd9Sstevel@tonic-gate  *		!= 0: ptr to string supplied when callback was registered
234*7c478bd9Sstevel@tonic-gate  */
235*7c478bd9Sstevel@tonic-gate void *
236*7c478bd9Sstevel@tonic-gate callb_execute_class(int class, int code)
237*7c478bd9Sstevel@tonic-gate {
238*7c478bd9Sstevel@tonic-gate 	callb_t *cp;
239*7c478bd9Sstevel@tonic-gate 	void *ret = NULL;
240*7c478bd9Sstevel@tonic-gate 
241*7c478bd9Sstevel@tonic-gate 	ASSERT(class < NCBCLASS);
242*7c478bd9Sstevel@tonic-gate 
243*7c478bd9Sstevel@tonic-gate 	mutex_enter(&ct->ct_lock);
244*7c478bd9Sstevel@tonic-gate 
245*7c478bd9Sstevel@tonic-gate 	for (cp = ct->ct_first_cb[class];
246*7c478bd9Sstevel@tonic-gate 	    cp != NULL && ret == 0; cp = cp->c_next) {
247*7c478bd9Sstevel@tonic-gate 		while (cp->c_flag & CALLB_EXECUTING)
248*7c478bd9Sstevel@tonic-gate 			cv_wait(&cp->c_done_cv, &ct->ct_lock);
249*7c478bd9Sstevel@tonic-gate 		/*
250*7c478bd9Sstevel@tonic-gate 		 * cont if the callb is deleted while we're sleeping
251*7c478bd9Sstevel@tonic-gate 		 */
252*7c478bd9Sstevel@tonic-gate 		if (cp->c_flag == CALLB_FREE)
253*7c478bd9Sstevel@tonic-gate 			continue;
254*7c478bd9Sstevel@tonic-gate 		cp->c_flag |= CALLB_EXECUTING;
255*7c478bd9Sstevel@tonic-gate 
256*7c478bd9Sstevel@tonic-gate #ifdef CALLB_DEBUG
257*7c478bd9Sstevel@tonic-gate 		printf("callb_execute: name=%s func=%p arg=%p\n",
258*7c478bd9Sstevel@tonic-gate 			cp->c_name, (void *)cp->c_func, (void *)cp->c_arg);
259*7c478bd9Sstevel@tonic-gate #endif /* CALLB_DEBUG */
260*7c478bd9Sstevel@tonic-gate 
261*7c478bd9Sstevel@tonic-gate 		mutex_exit(&ct->ct_lock);
262*7c478bd9Sstevel@tonic-gate 		/* If callback function fails, pass back client's name */
263*7c478bd9Sstevel@tonic-gate 		if (!(*cp->c_func)(cp->c_arg, code))
264*7c478bd9Sstevel@tonic-gate 			ret = cp->c_name;
265*7c478bd9Sstevel@tonic-gate 		mutex_enter(&ct->ct_lock);
266*7c478bd9Sstevel@tonic-gate 
267*7c478bd9Sstevel@tonic-gate 		cp->c_flag &= ~CALLB_EXECUTING;
268*7c478bd9Sstevel@tonic-gate 		cv_broadcast(&cp->c_done_cv);
269*7c478bd9Sstevel@tonic-gate 	}
270*7c478bd9Sstevel@tonic-gate 	mutex_exit(&ct->ct_lock);
271*7c478bd9Sstevel@tonic-gate 	return (ret);
272*7c478bd9Sstevel@tonic-gate }
273*7c478bd9Sstevel@tonic-gate 
274*7c478bd9Sstevel@tonic-gate /*
275*7c478bd9Sstevel@tonic-gate  * callers make sure no recursive entries to this func.
276*7c478bd9Sstevel@tonic-gate  * dp->cc_lockp is registered by callb_add to protect callb_cpr_t structure.
277*7c478bd9Sstevel@tonic-gate  *
278*7c478bd9Sstevel@tonic-gate  * When calling to stop a kernel thread (code == CB_CODE_CPR_CHKPT) we
279*7c478bd9Sstevel@tonic-gate  * use a cv_timedwait() in case the kernel thread is blocked.
280*7c478bd9Sstevel@tonic-gate  *
281*7c478bd9Sstevel@tonic-gate  * Note that this is a generic callback handler for daemon CPR and
282*7c478bd9Sstevel@tonic-gate  * should NOT be changed to accommodate any specific requirement in a daemon.
283*7c478bd9Sstevel@tonic-gate  * Individual daemons that require changes to the handler shall write
284*7c478bd9Sstevel@tonic-gate  * callback routines in their own daemon modules.
285*7c478bd9Sstevel@tonic-gate  */
286*7c478bd9Sstevel@tonic-gate boolean_t
287*7c478bd9Sstevel@tonic-gate callb_generic_cpr(void *arg, int code)
288*7c478bd9Sstevel@tonic-gate {
289*7c478bd9Sstevel@tonic-gate 	callb_cpr_t *cp = (callb_cpr_t *)arg;
290*7c478bd9Sstevel@tonic-gate 	clock_t ret = 0;			/* assume success */
291*7c478bd9Sstevel@tonic-gate 
292*7c478bd9Sstevel@tonic-gate 	mutex_enter(cp->cc_lockp);
293*7c478bd9Sstevel@tonic-gate 
294*7c478bd9Sstevel@tonic-gate 	switch (code) {
295*7c478bd9Sstevel@tonic-gate 	case CB_CODE_CPR_CHKPT:
296*7c478bd9Sstevel@tonic-gate 		cp->cc_events |= CALLB_CPR_START;
297*7c478bd9Sstevel@tonic-gate 		while (!(cp->cc_events & CALLB_CPR_SAFE))
298*7c478bd9Sstevel@tonic-gate 			/* cv_timedwait() returns -1 if it times out. */
299*7c478bd9Sstevel@tonic-gate 			if ((ret = cv_timedwait(&cp->cc_callb_cv,
300*7c478bd9Sstevel@tonic-gate 			    cp->cc_lockp,
301*7c478bd9Sstevel@tonic-gate 			    lbolt + callb_timeout_sec * hz)) == -1)
302*7c478bd9Sstevel@tonic-gate 				break;
303*7c478bd9Sstevel@tonic-gate 		break;
304*7c478bd9Sstevel@tonic-gate 
305*7c478bd9Sstevel@tonic-gate 	case CB_CODE_CPR_RESUME:
306*7c478bd9Sstevel@tonic-gate 		cp->cc_events &= ~CALLB_CPR_START;
307*7c478bd9Sstevel@tonic-gate 		cv_signal(&cp->cc_stop_cv);
308*7c478bd9Sstevel@tonic-gate 		break;
309*7c478bd9Sstevel@tonic-gate 	}
310*7c478bd9Sstevel@tonic-gate 	mutex_exit(cp->cc_lockp);
311*7c478bd9Sstevel@tonic-gate 	return (ret != -1);
312*7c478bd9Sstevel@tonic-gate }
313*7c478bd9Sstevel@tonic-gate 
314*7c478bd9Sstevel@tonic-gate /*
315*7c478bd9Sstevel@tonic-gate  * The generic callback function associated with kernel threads which
316*7c478bd9Sstevel@tonic-gate  * are always considered safe.
317*7c478bd9Sstevel@tonic-gate  */
318*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
319*7c478bd9Sstevel@tonic-gate boolean_t
320*7c478bd9Sstevel@tonic-gate callb_generic_cpr_safe(void *arg, int code)
321*7c478bd9Sstevel@tonic-gate {
322*7c478bd9Sstevel@tonic-gate 	return (B_TRUE);
323*7c478bd9Sstevel@tonic-gate }
324*7c478bd9Sstevel@tonic-gate /*
325*7c478bd9Sstevel@tonic-gate  * Prevent additions to callback table.
326*7c478bd9Sstevel@tonic-gate  */
327*7c478bd9Sstevel@tonic-gate void
328*7c478bd9Sstevel@tonic-gate callb_lock_table(void)
329*7c478bd9Sstevel@tonic-gate {
330*7c478bd9Sstevel@tonic-gate 	mutex_enter(&ct->ct_lock);
331*7c478bd9Sstevel@tonic-gate 	ASSERT(ct->ct_busy == 0);
332*7c478bd9Sstevel@tonic-gate 	ct->ct_busy = 1;
333*7c478bd9Sstevel@tonic-gate 	mutex_exit(&ct->ct_lock);
334*7c478bd9Sstevel@tonic-gate }
335*7c478bd9Sstevel@tonic-gate 
336*7c478bd9Sstevel@tonic-gate /*
337*7c478bd9Sstevel@tonic-gate  * Allow additions to callback table.
338*7c478bd9Sstevel@tonic-gate  */
339*7c478bd9Sstevel@tonic-gate void
340*7c478bd9Sstevel@tonic-gate callb_unlock_table(void)
341*7c478bd9Sstevel@tonic-gate {
342*7c478bd9Sstevel@tonic-gate 	mutex_enter(&ct->ct_lock);
343*7c478bd9Sstevel@tonic-gate 	ASSERT(ct->ct_busy != 0);
344*7c478bd9Sstevel@tonic-gate 	ct->ct_busy = 0;
345*7c478bd9Sstevel@tonic-gate 	cv_broadcast(&ct->ct_busy_cv);
346*7c478bd9Sstevel@tonic-gate 	mutex_exit(&ct->ct_lock);
347*7c478bd9Sstevel@tonic-gate }
348*7c478bd9Sstevel@tonic-gate 
349*7c478bd9Sstevel@tonic-gate /*
350*7c478bd9Sstevel@tonic-gate  * Return a boolean value indicating whether a particular kernel thread is
351*7c478bd9Sstevel@tonic-gate  * stopped in accordance with the cpr callback protocol.  If returning
352*7c478bd9Sstevel@tonic-gate  * false, also return a pointer to the thread name via the 2nd argument.
353*7c478bd9Sstevel@tonic-gate  */
354*7c478bd9Sstevel@tonic-gate boolean_t
355*7c478bd9Sstevel@tonic-gate callb_is_stopped(kthread_id_t tp, caddr_t *thread_name)
356*7c478bd9Sstevel@tonic-gate {
357*7c478bd9Sstevel@tonic-gate 	callb_t *cp;
358*7c478bd9Sstevel@tonic-gate 	boolean_t ret_val;
359*7c478bd9Sstevel@tonic-gate 
360*7c478bd9Sstevel@tonic-gate 	mutex_enter(&ct->ct_lock);
361*7c478bd9Sstevel@tonic-gate 
362*7c478bd9Sstevel@tonic-gate 	for (cp = ct->ct_first_cb[CB_CL_CPR_DAEMON];
363*7c478bd9Sstevel@tonic-gate 	    cp != NULL && tp != cp->c_thread; cp = cp->c_next)
364*7c478bd9Sstevel@tonic-gate 		;
365*7c478bd9Sstevel@tonic-gate 
366*7c478bd9Sstevel@tonic-gate 	ret_val = (cp != NULL);
367*7c478bd9Sstevel@tonic-gate 	if (ret_val) {
368*7c478bd9Sstevel@tonic-gate 		/*
369*7c478bd9Sstevel@tonic-gate 		 * We found the thread in the callback table and have
370*7c478bd9Sstevel@tonic-gate 		 * provisionally set the return value to true.  Now
371*7c478bd9Sstevel@tonic-gate 		 * see if it is marked "safe" and is sleeping or stopped.
372*7c478bd9Sstevel@tonic-gate 		 */
373*7c478bd9Sstevel@tonic-gate 		callb_cpr_t *ccp = (callb_cpr_t *)cp->c_arg;
374*7c478bd9Sstevel@tonic-gate 
375*7c478bd9Sstevel@tonic-gate 		*thread_name = cp->c_name;	/* in case not stopped */
376*7c478bd9Sstevel@tonic-gate 		mutex_enter(ccp->cc_lockp);
377*7c478bd9Sstevel@tonic-gate 
378*7c478bd9Sstevel@tonic-gate 		if (ccp->cc_events & CALLB_CPR_SAFE) {
379*7c478bd9Sstevel@tonic-gate 			int retry;
380*7c478bd9Sstevel@tonic-gate 
381*7c478bd9Sstevel@tonic-gate 			mutex_exit(ccp->cc_lockp);
382*7c478bd9Sstevel@tonic-gate 			for (retry = 0; retry < CALLB_MAX_RETRY; retry++) {
383*7c478bd9Sstevel@tonic-gate 				thread_lock(tp);
384*7c478bd9Sstevel@tonic-gate 				if (tp->t_state & (TS_SLEEP | TS_STOPPED)) {
385*7c478bd9Sstevel@tonic-gate 					thread_unlock(tp);
386*7c478bd9Sstevel@tonic-gate 					break;
387*7c478bd9Sstevel@tonic-gate 				}
388*7c478bd9Sstevel@tonic-gate 				thread_unlock(tp);
389*7c478bd9Sstevel@tonic-gate 				delay(CALLB_THREAD_DELAY);
390*7c478bd9Sstevel@tonic-gate 			}
391*7c478bd9Sstevel@tonic-gate 			ret_val = retry < CALLB_MAX_RETRY;
392*7c478bd9Sstevel@tonic-gate 		} else {
393*7c478bd9Sstevel@tonic-gate 			ret_val =
394*7c478bd9Sstevel@tonic-gate 			    (ccp->cc_events & CALLB_CPR_ALWAYS_SAFE) != 0;
395*7c478bd9Sstevel@tonic-gate 			mutex_exit(ccp->cc_lockp);
396*7c478bd9Sstevel@tonic-gate 		}
397*7c478bd9Sstevel@tonic-gate 	} else {
398*7c478bd9Sstevel@tonic-gate 		/*
399*7c478bd9Sstevel@tonic-gate 		 * Thread not found in callback table.  Make the best
400*7c478bd9Sstevel@tonic-gate 		 * attempt to identify the thread in the error message.
401*7c478bd9Sstevel@tonic-gate 		 */
402*7c478bd9Sstevel@tonic-gate 		ulong_t offset;
403*7c478bd9Sstevel@tonic-gate 		char *sym = kobj_getsymname((uintptr_t)tp->t_startpc,
404*7c478bd9Sstevel@tonic-gate 		    &offset);
405*7c478bd9Sstevel@tonic-gate 
406*7c478bd9Sstevel@tonic-gate 		*thread_name = sym ? sym : "*unknown*";
407*7c478bd9Sstevel@tonic-gate 	}
408*7c478bd9Sstevel@tonic-gate 
409*7c478bd9Sstevel@tonic-gate 	mutex_exit(&ct->ct_lock);
410*7c478bd9Sstevel@tonic-gate 	return (ret_val);
411*7c478bd9Sstevel@tonic-gate }
412