EDAC/igen6: ecclog_llist can be static
[sfrench/cifs-2.6.git] / drivers / net / netdevsim / health.c
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         binary = kmalloc(binary_len, GFP_KERNEL | __GFP_NOWARN);
86         if (!binary)
87                 return -ENOMEM;
88         get_random_bytes(binary, binary_len);
89         err = devlink_fmsg_binary_pair_put(fmsg, "test_binary", binary, binary_len);
90         kfree(binary);
91         if (err)
92                 return err;
93
94         err = devlink_fmsg_pair_nest_start(fmsg, "test_nest");
95         if (err)
96                 return err;
97         err = devlink_fmsg_obj_nest_start(fmsg);
98         if (err)
99                 return err;
100         err = devlink_fmsg_bool_pair_put(fmsg, "nested_test_bool", false);
101         if (err)
102                 return err;
103         err = devlink_fmsg_u8_pair_put(fmsg, "nested_test_u8", false);
104         if (err)
105                 return err;
106         err = devlink_fmsg_obj_nest_end(fmsg);
107         if (err)
108                 return err;
109         err = devlink_fmsg_pair_nest_end(fmsg);
110         if (err)
111                 return err;
112
113         err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_bool_array");
114         if (err)
115                 return err;
116         for (i = 0; i < 10; i++) {
117                 err = devlink_fmsg_bool_put(fmsg, true);
118                 if (err)
119                         return err;
120         }
121         err = devlink_fmsg_arr_pair_nest_end(fmsg);
122         if (err)
123                 return err;
124
125         err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_u8_array");
126         if (err)
127                 return err;
128         for (i = 0; i < 10; i++) {
129                 err = devlink_fmsg_u8_put(fmsg, i);
130                 if (err)
131                         return err;
132         }
133         err = devlink_fmsg_arr_pair_nest_end(fmsg);
134         if (err)
135                 return err;
136
137         err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_u32_array");
138         if (err)
139                 return err;
140         for (i = 0; i < 10; i++) {
141                 err = devlink_fmsg_u32_put(fmsg, i);
142                 if (err)
143                         return err;
144         }
145         err = devlink_fmsg_arr_pair_nest_end(fmsg);
146         if (err)
147                 return err;
148
149         err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_u64_array");
150         if (err)
151                 return err;
152         for (i = 0; i < 10; i++) {
153                 err = devlink_fmsg_u64_put(fmsg, i);
154                 if (err)
155                         return err;
156         }
157         err = devlink_fmsg_arr_pair_nest_end(fmsg);
158         if (err)
159                 return err;
160
161         err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_array_of_objects");
162         if (err)
163                 return err;
164         for (i = 0; i < 10; i++) {
165                 err = devlink_fmsg_obj_nest_start(fmsg);
166                 if (err)
167                         return err;
168                 err = devlink_fmsg_bool_pair_put(fmsg,
169                                                  "in_array_nested_test_bool",
170                                                  false);
171                 if (err)
172                         return err;
173                 err = devlink_fmsg_u8_pair_put(fmsg,
174                                                "in_array_nested_test_u8",
175                                                i);
176                 if (err)
177                         return err;
178                 err = devlink_fmsg_obj_nest_end(fmsg);
179                 if (err)
180                         return err;
181         }
182         return devlink_fmsg_arr_pair_nest_end(fmsg);
183 }
184
185 static int
186 nsim_dev_dummy_reporter_dump(struct devlink_health_reporter *reporter,
187                              struct devlink_fmsg *fmsg, void *priv_ctx,
188                              struct netlink_ext_ack *extack)
189 {
190         struct nsim_dev_health *health = devlink_health_reporter_priv(reporter);
191         struct nsim_dev_dummy_reporter_ctx *ctx = priv_ctx;
192         int err;
193
194         if (ctx) {
195                 err = devlink_fmsg_string_pair_put(fmsg, "break_message",
196                                                    ctx->break_msg);
197                 if (err)
198                         return err;
199         }
200         return nsim_dev_dummy_fmsg_put(fmsg, health->binary_len);
201 }
202
203 static int
204 nsim_dev_dummy_reporter_diagnose(struct devlink_health_reporter *reporter,
205                                  struct devlink_fmsg *fmsg,
206                                  struct netlink_ext_ack *extack)
207 {
208         struct nsim_dev_health *health = devlink_health_reporter_priv(reporter);
209         int err;
210
211         if (health->recovered_break_msg) {
212                 err = devlink_fmsg_string_pair_put(fmsg,
213                                                    "recovered_break_message",
214                                                    health->recovered_break_msg);
215                 if (err)
216                         return err;
217         }
218         return nsim_dev_dummy_fmsg_put(fmsg, health->binary_len);
219 }
220
221 static const
222 struct devlink_health_reporter_ops nsim_dev_dummy_reporter_ops = {
223         .name = "dummy",
224         .recover = nsim_dev_dummy_reporter_recover,
225         .dump = nsim_dev_dummy_reporter_dump,
226         .diagnose = nsim_dev_dummy_reporter_diagnose,
227 };
228
229 static ssize_t nsim_dev_health_break_write(struct file *file,
230                                            const char __user *data,
231                                            size_t count, loff_t *ppos)
232 {
233         struct nsim_dev_health *health = file->private_data;
234         struct nsim_dev_dummy_reporter_ctx ctx;
235         char *break_msg;
236         int err;
237
238         break_msg = kmalloc(count + 1, GFP_KERNEL);
239         if (!break_msg)
240                 return -ENOMEM;
241
242         if (copy_from_user(break_msg, data, count)) {
243                 err = -EFAULT;
244                 goto out;
245         }
246         break_msg[count] = '\0';
247         if (break_msg[count - 1] == '\n')
248                 break_msg[count - 1] = '\0';
249
250         ctx.break_msg = break_msg;
251         err = devlink_health_report(health->dummy_reporter, break_msg, &ctx);
252         if (err)
253                 goto out;
254
255 out:
256         kfree(break_msg);
257         return err ?: count;
258 }
259
260 static const struct file_operations nsim_dev_health_break_fops = {
261         .open = simple_open,
262         .write = nsim_dev_health_break_write,
263         .llseek = generic_file_llseek,
264 };
265
266 int nsim_dev_health_init(struct nsim_dev *nsim_dev, struct devlink *devlink)
267 {
268         struct nsim_dev_health *health = &nsim_dev->health;
269         int err;
270
271         health->empty_reporter =
272                 devlink_health_reporter_create(devlink,
273                                                &nsim_dev_empty_reporter_ops,
274                                                0, health);
275         if (IS_ERR(health->empty_reporter))
276                 return PTR_ERR(health->empty_reporter);
277
278         health->dummy_reporter =
279                 devlink_health_reporter_create(devlink,
280                                                &nsim_dev_dummy_reporter_ops,
281                                                0, health);
282         if (IS_ERR(health->dummy_reporter)) {
283                 err = PTR_ERR(health->dummy_reporter);
284                 goto err_empty_reporter_destroy;
285         }
286
287         health->ddir = debugfs_create_dir("health", nsim_dev->ddir);
288         if (IS_ERR(health->ddir)) {
289                 err = PTR_ERR(health->ddir);
290                 goto err_dummy_reporter_destroy;
291         }
292
293         health->recovered_break_msg = NULL;
294         debugfs_create_file("break_health", 0200, health->ddir, health,
295                             &nsim_dev_health_break_fops);
296         health->binary_len = 16;
297         debugfs_create_u32("binary_len", 0600, health->ddir,
298                            &health->binary_len);
299         health->fail_recover = false;
300         debugfs_create_bool("fail_recover", 0600, health->ddir,
301                             &health->fail_recover);
302         return 0;
303
304 err_dummy_reporter_destroy:
305         devlink_health_reporter_destroy(health->dummy_reporter);
306 err_empty_reporter_destroy:
307         devlink_health_reporter_destroy(health->empty_reporter);
308         return err;
309 }
310
311 void nsim_dev_health_exit(struct nsim_dev *nsim_dev)
312 {
313         struct nsim_dev_health *health = &nsim_dev->health;
314
315         debugfs_remove_recursive(health->ddir);
316         kfree(health->recovered_break_msg);
317         devlink_health_reporter_destroy(health->dummy_reporter);
318         devlink_health_reporter_destroy(health->empty_reporter);
319 }