1/*-
2 * Copyright (c) 2009 Isilon Inc http://www.isilon.com/
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 *
25 * $FreeBSD$
26 */
27/**
28 * @file
29 *
30 * Main header for failpoint facility.
31 */
32#ifndef _SYS_FAIL_H_
33#define _SYS_FAIL_H_
34
35#include <sys/param.h>
36#include <sys/cdefs.h>
37#include <sys/linker_set.h>
38#include <sys/queue.h>
39#include <sys/sysctl.h>
40
41/**
42 * Failpoint return codes, used internally.
43 * @ingroup failpoint_private
44 */
45enum fail_point_return_code {
46	FAIL_POINT_RC_CONTINUE = 0,	/**< Continue with normal execution */
47	FAIL_POINT_RC_RETURN,		/**< FP evaluated to 'return' */
48	FAIL_POINT_RC_QUEUED,		/**< sleep_fn will be called */
49};
50
51struct fail_point_entry;
52TAILQ_HEAD(fail_point_entries, fail_point_entry);
53/**
54 * Internal failpoint structure, tracking all the current details of the
55 * failpoint.  This structure is the core component shared between the
56 * failure-injection code and the user-interface.
57 * @ingroup failpoint_private
58 */
59struct fail_point {
60	const char *fp_name;		/**< name of fail point */
61	const char *fp_location;	/**< file:line of fail point */
62	struct fail_point_entries fp_entries;	/**< list of entries */
63	int fp_flags;
64	void (*fp_sleep_fn)(void *);	/**< Function to call at end of
65					 * sleep for sleep failpoints */
66	void *fp_sleep_arg;		/**< Arg for sleep_fn */
67};
68
69#define	FAIL_POINT_DYNAMIC_NAME	0x01	/**< Must free name on destroy */
70
71__BEGIN_DECLS
72
73/* Private failpoint eval function -- use fail_point_eval() instead. */
74enum fail_point_return_code fail_point_eval_nontrivial(struct fail_point *,
75	int *ret);
76
77/**
78 * @addtogroup failpoint
79 * @{
80 */
81/*
82 * Initialize a fail-point.  The name is formed in printf-like fashion
83 * from "fmt" and the subsequent arguments.
84 * Pair with fail_point_destroy().
85 */
86void fail_point_init(struct fail_point *, const char *fmt, ...)
87    __printflike(2, 3);
88
89/**
90 * Set the sleep function for a fail point
91 * If sleep_fn is specified, then FAIL_POINT_SLEEP will result in a
92 * (*fp->sleep_fn)(fp->sleep_arg) call by the timer thread.  Otherwise,
93 * if sleep_fn is NULL (default), then FAIL_POINT_SLEEP will result in the
94 * fail_point_eval() call sleeping.
95 */
96static __inline void
97fail_point_sleep_set_func(struct fail_point *fp, void (*sleep_fn)(void *))
98{
99	fp->fp_sleep_fn = sleep_fn;
100}
101
102/**
103 * Set the argument for the sleep function for a fail point
104 */
105static __inline void
106fail_point_sleep_set_arg(struct fail_point *fp, void *sleep_arg)
107{
108	fp->fp_sleep_arg = sleep_arg;
109}
110
111/**
112 * Free the resources used by a fail-point.  Pair with fail_point_init().
113 */
114void fail_point_destroy(struct fail_point *);
115
116/**
117 * Evaluate a failpoint.
118 */
119static __inline enum fail_point_return_code
120fail_point_eval(struct fail_point *fp, int *ret)
121{
122	if (TAILQ_EMPTY(&fp->fp_entries)) {
123		return (FAIL_POINT_RC_CONTINUE);
124	}
125	return (fail_point_eval_nontrivial(fp, ret));
126}
127
128__END_DECLS
129
130/* Declare a fail_point and its sysctl in a function. */
131#define	_FAIL_POINT_NAME(name)	_fail_point_##name
132#define	_FAIL_POINT_LOCATION()	"(" __FILE__ ":" __XSTRING(__LINE__) ")"
133
134/**
135 * Instantiate a failpoint which returns "value" from the function when triggered.
136 * @param parent  The parent sysctl under which to locate the sysctl
137 * @param name    The name of the failpoint in the sysctl tree (and printouts)
138 * @return        Instantly returns the return("value") specified in the
139 *                failpoint, if triggered.
140 */
141#define KFAIL_POINT_RETURN(parent, name) \
142	KFAIL_POINT_CODE(parent, name, return RETURN_VALUE)
143
144/**
145 * Instantiate a failpoint which returns (void) from the function when triggered.
146 * @param parent  The parent sysctl under which to locate the sysctl
147 * @param name    The name of the failpoint in the sysctl tree (and printouts)
148 * @return        Instantly returns void, if triggered in the failpoint.
149 */
150#define KFAIL_POINT_RETURN_VOID(parent, name) \
151	KFAIL_POINT_CODE(parent, name, return)
152
153/**
154 * Instantiate a failpoint which sets an error when triggered.
155 * @param parent     The parent sysctl under which to locate the sysctl
156 * @param name       The name of the failpoint in the sysctl tree (and printouts)
157 * @param error_var  A variable to set to the failpoint's specified
158 *                   return-value when triggered
159 */
160#define KFAIL_POINT_ERROR(parent, name, error_var) \
161	KFAIL_POINT_CODE(parent, name, (error_var) = RETURN_VALUE)
162
163/**
164 * Instantiate a failpoint which sets an error and then goes to a
165 * specified label in the function when triggered.
166 * @param parent     The parent sysctl under which to locate the sysctl
167 * @param name       The name of the failpoint in the sysctl tree (and printouts)
168 * @param error_var  A variable to set to the failpoint's specified
169 *                   return-value when triggered
170 * @param label      The location to goto when triggered.
171 */
172#define KFAIL_POINT_GOTO(parent, name, error_var, label) \
173	KFAIL_POINT_CODE(parent, name, (error_var) = RETURN_VALUE; goto label)
174
175/**
176 * Instantiate a failpoint which runs arbitrary code when triggered.
177 * @param parent     The parent sysctl under which to locate the sysctl
178 * @param name       The name of the failpoint in the sysctl tree
179 *		     (and printouts)
180 * @param code       The arbitrary code to run when triggered.  Can reference
181 *                   "RETURN_VALUE" if desired to extract the specified
182 *                   user return-value when triggered.  Note that this is
183 *                   implemented with a do-while loop so be careful of
184 *                   break and continue statements.
185 */
186#define KFAIL_POINT_CODE(parent, name, code)				\
187do {									\
188	int RETURN_VALUE;						\
189	static struct fail_point _FAIL_POINT_NAME(name) = {		\
190		#name,							\
191		_FAIL_POINT_LOCATION(),					\
192		TAILQ_HEAD_INITIALIZER(_FAIL_POINT_NAME(name).fp_entries), \
193		0,							\
194		NULL, NULL,						\
195	};								\
196	SYSCTL_OID(parent, OID_AUTO, name,				\
197	    CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,		\
198	    &_FAIL_POINT_NAME(name), 0, fail_point_sysctl,		\
199	    "A", "");							\
200									\
201	if (__predict_false(						\
202	    fail_point_eval(&_FAIL_POINT_NAME(name), &RETURN_VALUE))) {	\
203									\
204		code;							\
205									\
206	}								\
207} while (0)
208
209
210/**
211 * @}
212 * (end group failpoint)
213 */
214
215#ifdef _KERNEL
216int fail_point_sysctl(SYSCTL_HANDLER_ARGS);
217
218/* The fail point sysctl tree. */
219SYSCTL_DECL(_debug_fail_point);
220#define	DEBUG_FP	_debug_fail_point
221#endif
222
223#endif /* _SYS_FAIL_H_ */
224