aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/lustre/include/linux/libcfs/libcfs_fail.h
blob: d6fc3164e7e7cd2923ed1c7177af515ffa2da388 (plain) (blame)
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
// SPDX-License-Identifier: GPL-2.0
/*
 * GPL HEADER START
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 only,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License version 2 for more details (a copy is included
 * in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU General Public License
 * version 2 along with this program; If not, see http://www.gnu.org/licenses
 *
 * GPL HEADER END
 */
/*
 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
 * Use is subject to license terms.
 *
 * Copyright (c) 2011, 2012, Intel Corporation.
 */
/*
 * This file is part of Lustre, http://www.lustre.org/
 * Lustre is a trademark of Oracle Corporation, Inc.
 */

#ifndef _LIBCFS_FAIL_H
#define _LIBCFS_FAIL_H

extern unsigned long cfs_fail_loc;
extern unsigned int cfs_fail_val;
extern int cfs_fail_err;

extern wait_queue_head_t cfs_race_waitq;
extern int cfs_race_state;

int __cfs_fail_check_set(u32 id, u32 value, int set);
int __cfs_fail_timeout_set(u32 id, u32 value, int ms, int set);

enum {
	CFS_FAIL_LOC_NOSET      = 0,
	CFS_FAIL_LOC_ORSET      = 1,
	CFS_FAIL_LOC_RESET      = 2,
	CFS_FAIL_LOC_VALUE      = 3
};

/* Failure injection control */
#define CFS_FAIL_MASK_SYS    0x0000FF00
#define CFS_FAIL_MASK_LOC   (0x000000FF | CFS_FAIL_MASK_SYS)

#define CFS_FAILED_BIT       30
/* CFS_FAILED is 0x40000000 */
#define CFS_FAILED		BIT(CFS_FAILED_BIT)

#define CFS_FAIL_ONCE_BIT    31
/* CFS_FAIL_ONCE is 0x80000000 */
#define CFS_FAIL_ONCE		BIT(CFS_FAIL_ONCE_BIT)

/* The following flags aren't made to be combined */
#define CFS_FAIL_SKIP	0x20000000 /* skip N times then fail */
#define CFS_FAIL_SOME	0x10000000 /* only fail N times */
#define CFS_FAIL_RAND	0x08000000 /* fail 1/N of the times */
#define CFS_FAIL_USR1	0x04000000 /* user flag */

#define CFS_FAULT	0x02000000 /* match any CFS_FAULT_CHECK */

static inline bool CFS_FAIL_PRECHECK(u32 id)
{
	return cfs_fail_loc &&
	       ((cfs_fail_loc & CFS_FAIL_MASK_LOC) == (id & CFS_FAIL_MASK_LOC) ||
		(cfs_fail_loc & id & CFS_FAULT));
}

static inline int cfs_fail_check_set(u32 id, u32 value,
				     int set, int quiet)
{
	int ret = 0;

	if (unlikely(CFS_FAIL_PRECHECK(id))) {
		ret = __cfs_fail_check_set(id, value, set);
		if (ret) {
			if (quiet) {
				CDEBUG(D_INFO, "*** cfs_fail_loc=%x, val=%u***\n",
				       id, value);
			} else {
				LCONSOLE_INFO("*** cfs_fail_loc=%x, val=%u***\n",
					      id, value);
			}
		}
	}

	return ret;
}

/* If id hit cfs_fail_loc, return 1, otherwise return 0 */
#define CFS_FAIL_CHECK(id) \
	cfs_fail_check_set(id, 0, CFS_FAIL_LOC_NOSET, 0)
#define CFS_FAIL_CHECK_QUIET(id) \
	cfs_fail_check_set(id, 0, CFS_FAIL_LOC_NOSET, 1)

/*
 * If id hit cfs_fail_loc and cfs_fail_val == (-1 or value) return 1,
 * otherwise return 0
 */
#define CFS_FAIL_CHECK_VALUE(id, value) \
	cfs_fail_check_set(id, value, CFS_FAIL_LOC_VALUE, 0)
#define CFS_FAIL_CHECK_VALUE_QUIET(id, value) \
	cfs_fail_check_set(id, value, CFS_FAIL_LOC_VALUE, 1)

/*
 * If id hit cfs_fail_loc, cfs_fail_loc |= value and return 1,
 * otherwise return 0
 */
#define CFS_FAIL_CHECK_ORSET(id, value) \
	cfs_fail_check_set(id, value, CFS_FAIL_LOC_ORSET, 0)
#define CFS_FAIL_CHECK_ORSET_QUIET(id, value) \
	cfs_fail_check_set(id, value, CFS_FAIL_LOC_ORSET, 1)

/*
 * If id hit cfs_fail_loc, cfs_fail_loc = value and return 1,
 * otherwise return 0
 */
#define CFS_FAIL_CHECK_RESET(id, value) \
	cfs_fail_check_set(id, value, CFS_FAIL_LOC_RESET, 0)
#define CFS_FAIL_CHECK_RESET_QUIET(id, value) \
	cfs_fail_check_set(id, value, CFS_FAIL_LOC_RESET, 1)

static inline int cfs_fail_timeout_set(u32 id, u32 value, int ms, int set)
{
	if (unlikely(CFS_FAIL_PRECHECK(id)))
		return __cfs_fail_timeout_set(id, value, ms, set);
	return 0;
}

/* If id hit cfs_fail_loc, sleep for seconds or milliseconds */
#define CFS_FAIL_TIMEOUT(id, secs) \
	cfs_fail_timeout_set(id, 0, secs * 1000, CFS_FAIL_LOC_NOSET)

#define CFS_FAIL_TIMEOUT_MS(id, ms) \
	cfs_fail_timeout_set(id, 0, ms, CFS_FAIL_LOC_NOSET)

/*
 * If id hit cfs_fail_loc, cfs_fail_loc |= value and
 * sleep seconds or milliseconds
 */
#define CFS_FAIL_TIMEOUT_ORSET(id, value, secs) \
	cfs_fail_timeout_set(id, value, secs * 1000, CFS_FAIL_LOC_ORSET)

#define CFS_FAIL_TIMEOUT_RESET(id, value, secs) \
	cfs_fail_timeout_set(id, value, secs * 1000, CFS_FAIL_LOC_RESET)

#define CFS_FAIL_TIMEOUT_MS_ORSET(id, value, ms) \
	cfs_fail_timeout_set(id, value, ms, CFS_FAIL_LOC_ORSET)

#define CFS_FAULT_CHECK(id)			\
	CFS_FAIL_CHECK(CFS_FAULT | (id))

/*
 * The idea here is to synchronise two threads to force a race. The
 * first thread that calls this with a matching fail_loc is put to
 * sleep. The next thread that calls with the same fail_loc wakes up
 * the first and continues.
 */
static inline void cfs_race(u32 id)
{
	if (CFS_FAIL_PRECHECK(id)) {
		if (unlikely(__cfs_fail_check_set(id, 0, CFS_FAIL_LOC_NOSET))) {
			int rc;

			cfs_race_state = 0;
			CERROR("cfs_race id %x sleeping\n", id);
			rc = wait_event_interruptible(cfs_race_waitq,
						      !!cfs_race_state);
			CERROR("cfs_fail_race id %x awake, rc=%d\n", id, rc);
		} else {
			CERROR("cfs_fail_race id %x waking\n", id);
			cfs_race_state = 1;
			wake_up(&cfs_race_waitq);
		}
	}
}

#define CFS_RACE(id) cfs_race(id)

#endif /* _LIBCFS_FAIL_H */