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