xref: /openbmc/linux/drivers/net/netdevsim/health.c (revision 82c93a87)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2019 Mellanox Technologies. All rights reserved */
3 
4 #include <linux/debugfs.h>
5 #include <linux/err.h>
6 #include <linux/kernel.h>
7 #include <linux/slab.h>
8 
9 #include "netdevsim.h"
10 
11 static int
12 nsim_dev_empty_reporter_dump(struct devlink_health_reporter *reporter,
13 			     struct devlink_fmsg *fmsg, void *priv_ctx,
14 			     struct netlink_ext_ack *extack)
15 {
16 	return 0;
17 }
18 
19 static int
20 nsim_dev_empty_reporter_diagnose(struct devlink_health_reporter *reporter,
21 				 struct devlink_fmsg *fmsg,
22 				 struct netlink_ext_ack *extack)
23 {
24 	return 0;
25 }
26 
27 static const
28 struct devlink_health_reporter_ops nsim_dev_empty_reporter_ops = {
29 	.name = "empty",
30 	.dump = nsim_dev_empty_reporter_dump,
31 	.diagnose = nsim_dev_empty_reporter_diagnose,
32 };
33 
34 struct nsim_dev_dummy_reporter_ctx {
35 	char *break_msg;
36 };
37 
38 static int
39 nsim_dev_dummy_reporter_recover(struct devlink_health_reporter *reporter,
40 				void *priv_ctx,
41 				struct netlink_ext_ack *extack)
42 {
43 	struct nsim_dev_health *health = devlink_health_reporter_priv(reporter);
44 	struct nsim_dev_dummy_reporter_ctx *ctx = priv_ctx;
45 
46 	if (health->fail_recover) {
47 		/* For testing purposes, user set debugfs fail_recover
48 		 * value to true. Fail right away.
49 		 */
50 		NL_SET_ERR_MSG_MOD(extack, "User setup the recover to fail for testing purposes");
51 		return -EINVAL;
52 	}
53 	if (ctx) {
54 		kfree(health->recovered_break_msg);
55 		health->recovered_break_msg = kstrdup(ctx->break_msg,
56 						      GFP_KERNEL);
57 		if (!health->recovered_break_msg)
58 			return -ENOMEM;
59 	}
60 	return 0;
61 }
62 
63 static int nsim_dev_dummy_fmsg_put(struct devlink_fmsg *fmsg, u32 binary_len)
64 {
65 	char *binary;
66 	int err;
67 	int i;
68 
69 	err = devlink_fmsg_bool_pair_put(fmsg, "test_bool", true);
70 	if (err)
71 		return err;
72 	err = devlink_fmsg_u8_pair_put(fmsg, "test_u8", 1);
73 	if (err)
74 		return err;
75 	err = devlink_fmsg_u32_pair_put(fmsg, "test_u32", 3);
76 	if (err)
77 		return err;
78 	err = devlink_fmsg_u64_pair_put(fmsg, "test_u64", 4);
79 	if (err)
80 		return err;
81 	err = devlink_fmsg_string_pair_put(fmsg, "test_string", "somestring");
82 	if (err)
83 		return err;
84 
85 	err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_binary");
86 	if (err)
87 		return err;
88 	binary = kmalloc(binary_len, GFP_KERNEL);
89 	if (!binary)
90 		return -ENOMEM;
91 	get_random_bytes(binary, binary_len);
92 	err = devlink_fmsg_binary_put(fmsg, binary, binary_len);
93 	kfree(binary);
94 	if (err)
95 		return err;
96 	err = devlink_fmsg_arr_pair_nest_end(fmsg);
97 	if (err)
98 		return err;
99 
100 	err = devlink_fmsg_pair_nest_start(fmsg, "test_nest");
101 	if (err)
102 		return err;
103 	err = devlink_fmsg_obj_nest_start(fmsg);
104 	if (err)
105 		return err;
106 	err = devlink_fmsg_bool_pair_put(fmsg, "nested_test_bool", false);
107 	if (err)
108 		return err;
109 	err = devlink_fmsg_u8_pair_put(fmsg, "nested_test_u8", false);
110 	if (err)
111 		return err;
112 	err = devlink_fmsg_obj_nest_end(fmsg);
113 	if (err)
114 		return err;
115 	err = devlink_fmsg_pair_nest_end(fmsg);
116 	if (err)
117 		return err;
118 
119 	err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_bool_array");
120 	if (err)
121 		return err;
122 	for (i = 0; i < 10; i++) {
123 		err = devlink_fmsg_bool_put(fmsg, true);
124 		if (err)
125 			return err;
126 	}
127 	err = devlink_fmsg_arr_pair_nest_end(fmsg);
128 	if (err)
129 		return err;
130 
131 	err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_u8_array");
132 	if (err)
133 		return err;
134 	for (i = 0; i < 10; i++) {
135 		err = devlink_fmsg_u8_put(fmsg, i);
136 		if (err)
137 			return err;
138 	}
139 	err = devlink_fmsg_arr_pair_nest_end(fmsg);
140 	if (err)
141 		return err;
142 
143 	err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_u32_array");
144 	if (err)
145 		return err;
146 	for (i = 0; i < 10; i++) {
147 		err = devlink_fmsg_u32_put(fmsg, i);
148 		if (err)
149 			return err;
150 	}
151 	err = devlink_fmsg_arr_pair_nest_end(fmsg);
152 	if (err)
153 		return err;
154 
155 	err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_u64_array");
156 	if (err)
157 		return err;
158 	for (i = 0; i < 10; i++) {
159 		err = devlink_fmsg_u64_put(fmsg, i);
160 		if (err)
161 			return err;
162 	}
163 	err = devlink_fmsg_arr_pair_nest_end(fmsg);
164 	if (err)
165 		return err;
166 
167 	err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_array_of_objects");
168 	if (err)
169 		return err;
170 	for (i = 0; i < 10; i++) {
171 		err = devlink_fmsg_obj_nest_start(fmsg);
172 		if (err)
173 			return err;
174 		err = devlink_fmsg_bool_pair_put(fmsg,
175 						 "in_array_nested_test_bool",
176 						 false);
177 		if (err)
178 			return err;
179 		err = devlink_fmsg_u8_pair_put(fmsg,
180 					       "in_array_nested_test_u8",
181 					       i);
182 		if (err)
183 			return err;
184 		err = devlink_fmsg_obj_nest_end(fmsg);
185 		if (err)
186 			return err;
187 	}
188 	return devlink_fmsg_arr_pair_nest_end(fmsg);
189 }
190 
191 static int
192 nsim_dev_dummy_reporter_dump(struct devlink_health_reporter *reporter,
193 			     struct devlink_fmsg *fmsg, void *priv_ctx,
194 			     struct netlink_ext_ack *extack)
195 {
196 	struct nsim_dev_health *health = devlink_health_reporter_priv(reporter);
197 	struct nsim_dev_dummy_reporter_ctx *ctx = priv_ctx;
198 	int err;
199 
200 	if (ctx) {
201 		err = devlink_fmsg_string_pair_put(fmsg, "break_message",
202 						   ctx->break_msg);
203 		if (err)
204 			return err;
205 	}
206 	return nsim_dev_dummy_fmsg_put(fmsg, health->binary_len);
207 }
208 
209 static int
210 nsim_dev_dummy_reporter_diagnose(struct devlink_health_reporter *reporter,
211 				 struct devlink_fmsg *fmsg,
212 				 struct netlink_ext_ack *extack)
213 {
214 	struct nsim_dev_health *health = devlink_health_reporter_priv(reporter);
215 	int err;
216 
217 	if (health->recovered_break_msg) {
218 		err = devlink_fmsg_string_pair_put(fmsg,
219 						   "recovered_break_message",
220 						   health->recovered_break_msg);
221 		if (err)
222 			return err;
223 	}
224 	return nsim_dev_dummy_fmsg_put(fmsg, health->binary_len);
225 }
226 
227 static const
228 struct devlink_health_reporter_ops nsim_dev_dummy_reporter_ops = {
229 	.name = "dummy",
230 	.recover = nsim_dev_dummy_reporter_recover,
231 	.dump = nsim_dev_dummy_reporter_dump,
232 	.diagnose = nsim_dev_dummy_reporter_diagnose,
233 };
234 
235 static ssize_t nsim_dev_health_break_write(struct file *file,
236 					   const char __user *data,
237 					   size_t count, loff_t *ppos)
238 {
239 	struct nsim_dev_health *health = file->private_data;
240 	struct nsim_dev_dummy_reporter_ctx ctx;
241 	char *break_msg;
242 	int err;
243 
244 	break_msg = kmalloc(count + 1, GFP_KERNEL);
245 	if (!break_msg)
246 		return -ENOMEM;
247 
248 	if (copy_from_user(break_msg, data, count)) {
249 		err = -EFAULT;
250 		goto out;
251 	}
252 	break_msg[count] = '\0';
253 	if (break_msg[count - 1] == '\n')
254 		break_msg[count - 1] = '\0';
255 
256 	ctx.break_msg = break_msg;
257 	err = devlink_health_report(health->dummy_reporter, break_msg, &ctx);
258 	if (err)
259 		goto out;
260 
261 out:
262 	kfree(break_msg);
263 	return err ?: count;
264 }
265 
266 static const struct file_operations nsim_dev_health_break_fops = {
267 	.open = simple_open,
268 	.write = nsim_dev_health_break_write,
269 	.llseek = generic_file_llseek,
270 };
271 
272 int nsim_dev_health_init(struct nsim_dev *nsim_dev, struct devlink *devlink)
273 {
274 	struct nsim_dev_health *health = &nsim_dev->health;
275 	int err;
276 
277 	health->empty_reporter =
278 		devlink_health_reporter_create(devlink,
279 					       &nsim_dev_empty_reporter_ops,
280 					       0, false, health);
281 	if (IS_ERR(health->empty_reporter))
282 		return PTR_ERR(health->empty_reporter);
283 
284 	health->dummy_reporter =
285 		devlink_health_reporter_create(devlink,
286 					       &nsim_dev_dummy_reporter_ops,
287 					       0, false, health);
288 	if (IS_ERR(health->dummy_reporter)) {
289 		err = PTR_ERR(health->dummy_reporter);
290 		goto err_empty_reporter_destroy;
291 	}
292 
293 	health->ddir = debugfs_create_dir("health", nsim_dev->ddir);
294 	if (IS_ERR_OR_NULL(health->ddir)) {
295 		err = PTR_ERR_OR_ZERO(health->ddir) ?: -EINVAL;
296 		goto err_dummy_reporter_destroy;
297 	}
298 
299 	health->recovered_break_msg = NULL;
300 	debugfs_create_file("break_health", 0200, health->ddir, health,
301 			    &nsim_dev_health_break_fops);
302 	health->binary_len = 16;
303 	debugfs_create_u32("binary_len", 0600, health->ddir,
304 			   &health->binary_len);
305 	health->fail_recover = false;
306 	debugfs_create_bool("fail_recover", 0600, health->ddir,
307 			    &health->fail_recover);
308 	return 0;
309 
310 err_dummy_reporter_destroy:
311 	devlink_health_reporter_destroy(health->dummy_reporter);
312 err_empty_reporter_destroy:
313 	devlink_health_reporter_destroy(health->empty_reporter);
314 	return err;
315 }
316 
317 void nsim_dev_health_exit(struct nsim_dev *nsim_dev)
318 {
319 	struct nsim_dev_health *health = &nsim_dev->health;
320 
321 	debugfs_remove_recursive(health->ddir);
322 	kfree(health->recovered_break_msg);
323 	devlink_health_reporter_destroy(health->dummy_reporter);
324 	devlink_health_reporter_destroy(health->empty_reporter);
325 }
326