aboutsummaryrefslogtreecommitdiffstats
path: root/include/os/freebsd/spl/sys/callb.h
blob: cc67b0263c51bff15f95b2079605f8554ddf798e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#ifndef	_SYS_CALLB_H
#define	_SYS_CALLB_H

#include <sys/condvar.h>

#ifdef	__cplusplus
extern "C" {
#endif

/*
 * definitions of callback classes (c_class)
 *
 * Callbacks belong in the same class if (1) their callback routines
 * do the same kind of processing (ideally, using the same callback function)
 * and (2) they can/should be executed at the same time in a cpr
 * suspend/resume operation.
 *
 * Note: The DAEMON class, in particular, is for stopping kernel threads
 * and nothing else.  The CALLB_* macros below should be used to deal
 * with kernel threads, and the callback function should be callb_generic_cpr.
 * Another idiosyncrasy of the DAEMON class is that if a suspend operation
 * fails, some of the callback functions may be called with the RESUME
 * code which were never called with SUSPEND.  Not a problem currently,
 * but see bug 4201851.
 */
#define	CB_CL_CPR_DAEMON	0
#define	CB_CL_CPR_VM		1
#define	CB_CL_CPR_CALLOUT	2
#define	CB_CL_CPR_OBP		3
#define	CB_CL_CPR_FB		4
#define	CB_CL_PANIC		5
#define	CB_CL_CPR_RPC		6
#define	CB_CL_CPR_PROMPRINTF	7
#define	CB_CL_UADMIN		8
#define	CB_CL_CPR_PM		9
#define	CB_CL_HALT		10
#define	CB_CL_CPR_DMA		11
#define	CB_CL_CPR_POST_USER	12
#define	CB_CL_UADMIN_PRE_VFS    13
#define	CB_CL_MDBOOT		CB_CL_UADMIN
#define	CB_CL_ENTER_DEBUGGER	14
#define	CB_CL_CPR_POST_KERNEL	15
#define	CB_CL_CPU_DEEP_IDLE	16
#define	NCBCLASS		17 /* CHANGE ME if classes are added/removed */

/*
 * CB_CL_CPR_DAEMON class specific definitions are given below:
 */

/*
 * code for CPR callb_execute_class
 */
#define	CB_CODE_CPR_CHKPT	0
#define	CB_CODE_CPR_RESUME	1

typedef	void *		callb_id_t;
/*
 * Per kernel thread structure for CPR daemon callbacks.
 * Must be protected by either a existing lock in the daemon or
 * a new lock created for such a purpose.
 */
typedef struct callb_cpr {
	kmutex_t	*cc_lockp;	/* lock to protect this struct */
	char		cc_events;	/* various events for CPR */
	callb_id_t	cc_id;		/* callb id address */
	kcondvar_t	cc_callb_cv;	/* cv for callback waiting */
	kcondvar_t	cc_stop_cv;	/* cv to checkpoint block */
} callb_cpr_t;

/*
 * cc_events definitions
 */
#define	CALLB_CPR_START		1	/* a checkpoint request's started */
#define	CALLB_CPR_SAFE		2	/* thread is safe for CPR */
#define	CALLB_CPR_ALWAYS_SAFE	4	/* thread is ALWAYS safe for CPR */

/*
 * Used when checking that all kernel threads are stopped.
 */
#define	CALLB_MAX_RETRY		3	/* when waiting for kthread to sleep */
#define	CALLB_THREAD_DELAY	10	/* ticks allowed to reach sleep */
#define	CPR_KTHREAD_TIMEOUT_SEC	90	/* secs before callback times out -- */
					/* due to pwr mgmt of disks, make -- */
					/* big enough for worst spinup time */

/*
 *
 * CALLB_CPR_INIT macro is used by kernel threads to add their entry to
 * the callback table and perform other initialization.  It automatically
 * adds the thread as being in the callback class CB_CL_CPR_DAEMON.
 *
 *	cp    - ptr to the callb_cpr_t structure for this kernel thread
 *
 *	lockp - pointer to mutex protecting the callb_cpr_t struct
 *
 *	func  - pointer to the callback function for this kernel thread.
 *		It has the prototype boolean_t <func>(void *arg, int code)
 *		where: arg	- ptr to the callb_cpr_t structure
 *		       code	- not used for this type of callback
 *		returns: B_TRUE if successful; B_FALSE if unsuccessful.
 *
 *	name  - a string giving the name of the kernel thread
 *
 * Note: lockp is the lock to protect the callb_cpr_t (cp) structure
 * later on.  No lock held is needed for this initialization.
 */
#define	CALLB_CPR_INIT(cp, lockp, func, name)	{			\
		strlcpy(curthread->td_name, (name),			\
		    sizeof (curthread->td_name));			\
		bzero((caddr_t)(cp), sizeof (callb_cpr_t));		\
		(cp)->cc_lockp = lockp;					\
		(cp)->cc_id = callb_add(func, (void *)(cp),		\
			CB_CL_CPR_DAEMON, name);			\
		cv_init(&(cp)->cc_callb_cv, NULL, CV_DEFAULT, NULL);	\
		cv_init(&(cp)->cc_stop_cv, NULL, CV_DEFAULT, NULL);	\
	}

#ifndef __lock_lint
#define	CALLB_CPR_ASSERT(cp)	ASSERT(MUTEX_HELD((cp)->cc_lockp));
#else
#define	CALLB_CPR_ASSERT(cp)
#endif
/*
 * Some threads (like the idle threads) do not adhere to the callback
 * protocol and are always considered safe.  Such threads must never exit.
 * They register their presence by calling this macro during their
 * initialization.
 *
 * Args:
 *	t	- thread pointer of the client kernel thread
 *	name	- a string giving the name of the kernel thread
 */
#define	CALLB_CPR_INIT_SAFE(t, name) {					\
		(void) callb_add_thread(callb_generic_cpr_safe,		\
		(void *) &callb_cprinfo_safe, CB_CL_CPR_DAEMON,		\
		    name, t);						\
	}
/*
 * The lock to protect cp's content must be held before
 * calling the following two macros.
 *
 * Any code region between CALLB_CPR_SAFE_BEGIN and CALLB_CPR_SAFE_END
 * is safe for checkpoint/resume.
 */
#define	CALLB_CPR_SAFE_BEGIN(cp) { 			\
		CALLB_CPR_ASSERT(cp)			\
		(cp)->cc_events |= CALLB_CPR_SAFE;	\
		if ((cp)->cc_events & CALLB_CPR_START)	\
			cv_signal(&(cp)->cc_callb_cv);	\
	}
#define	CALLB_CPR_SAFE_END(cp, lockp) {				\
		CALLB_CPR_ASSERT(cp)				\
		while ((cp)->cc_events & CALLB_CPR_START)	\
			cv_wait(&(cp)->cc_stop_cv, lockp);	\
		(cp)->cc_events &= ~CALLB_CPR_SAFE;		\
	}
/*
 * cv_destroy is nop right now but may be needed in the future.
 */
#define	CALLB_CPR_EXIT(cp) {				\
		CALLB_CPR_ASSERT(cp)			\
		(cp)->cc_events |= CALLB_CPR_SAFE;	\
		if ((cp)->cc_events & CALLB_CPR_START)	\
			cv_signal(&(cp)->cc_callb_cv);	\
		mutex_exit((cp)->cc_lockp);		\
		(void) callb_delete((cp)->cc_id);	\
		cv_destroy(&(cp)->cc_callb_cv);		\
		cv_destroy(&(cp)->cc_stop_cv);		\
	}

extern callb_cpr_t callb_cprinfo_safe;
extern callb_id_t callb_add(boolean_t  (*)(void *, int), void *, int, char *);
extern callb_id_t callb_add_thread(boolean_t (*)(void *, int),
    void *, int, char *, kthread_id_t);
extern int	callb_delete(callb_id_t);
extern void	callb_execute(callb_id_t, int);
extern void	*callb_execute_class(int, int);
extern boolean_t callb_generic_cpr(void *, int);
extern boolean_t callb_generic_cpr_safe(void *, int);
extern boolean_t callb_is_stopped(kthread_id_t, caddr_t *);
extern void	callb_lock_table(void);
extern void	callb_unlock_table(void);

#ifdef	__cplusplus
}
#endif

#endif	/* _SYS_CALLB_H */