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