Merge tag 'iomap-6.0-merge-2' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux
[linux-2.6-microblaze.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_end(fmsg);
114         if (err)
115                 return err;
116
117         err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_u32_array");
118         if (err)
119                 return err;
120         for (i = 0; i < 10; i++) {
121                 err = devlink_fmsg_u32_put(fmsg, i);
122                 if (err)
123                         return err;
124         }
125         err = devlink_fmsg_arr_pair_nest_end(fmsg);
126         if (err)
127                 return err;
128
129         err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_array_of_objects");
130         if (err)
131                 return err;
132         for (i = 0; i < 10; i++) {
133                 err = devlink_fmsg_obj_nest_start(fmsg);
134                 if (err)
135                         return err;
136                 err = devlink_fmsg_bool_pair_put(fmsg,
137                                                  "in_array_nested_test_bool",
138                                                  false);
139                 if (err)
140                         return err;
141                 err = devlink_fmsg_u8_pair_put(fmsg,
142                                                "in_array_nested_test_u8",
143                                                i);
144                 if (err)
145                         return err;
146                 err = devlink_fmsg_obj_nest_end(fmsg);
147                 if (err)
148                         return err;
149         }
150         return devlink_fmsg_arr_pair_nest_end(fmsg);
151 }
152
153 static int
154 nsim_dev_dummy_reporter_dump(struct devlink_health_reporter *reporter,
155                              struct devlink_fmsg *fmsg, void *priv_ctx,
156                              struct netlink_ext_ack *extack)
157 {
158         struct nsim_dev_health *health = devlink_health_reporter_priv(reporter);
159         struct nsim_dev_dummy_reporter_ctx *ctx = priv_ctx;
160         int err;
161
162         if (ctx) {
163                 err = devlink_fmsg_string_pair_put(fmsg, "break_message",
164                                                    ctx->break_msg);
165                 if (err)
166                         return err;
167         }
168         return nsim_dev_dummy_fmsg_put(fmsg, health->binary_len);
169 }
170
171 static int
172 nsim_dev_dummy_reporter_diagnose(struct devlink_health_reporter *reporter,
173                                  struct devlink_fmsg *fmsg,
174                                  struct netlink_ext_ack *extack)
175 {
176         struct nsim_dev_health *health = devlink_health_reporter_priv(reporter);
177         int err;
178
179         if (health->recovered_break_msg) {
180                 err = devlink_fmsg_string_pair_put(fmsg,
181                                                    "recovered_break_message",
182                                                    health->recovered_break_msg);
183                 if (err)
184                         return err;
185         }
186         return nsim_dev_dummy_fmsg_put(fmsg, health->binary_len);
187 }
188
189 static const
190 struct devlink_health_reporter_ops nsim_dev_dummy_reporter_ops = {
191         .name = "dummy",
192         .recover = nsim_dev_dummy_reporter_recover,
193         .dump = nsim_dev_dummy_reporter_dump,
194         .diagnose = nsim_dev_dummy_reporter_diagnose,
195 };
196
197 static ssize_t nsim_dev_health_break_write(struct file *file,
198                                            const char __user *data,
199                                            size_t count, loff_t *ppos)
200 {
201         struct nsim_dev_health *health = file->private_data;
202         struct nsim_dev_dummy_reporter_ctx ctx;
203         char *break_msg;
204         int err;
205
206         break_msg = memdup_user_nul(data, count);
207         if (IS_ERR(break_msg))
208                 return PTR_ERR(break_msg);
209
210         if (break_msg[count - 1] == '\n')
211                 break_msg[count - 1] = '\0';
212
213         ctx.break_msg = break_msg;
214         err = devlink_health_report(health->dummy_reporter, break_msg, &ctx);
215         if (err)
216                 goto out;
217
218 out:
219         kfree(break_msg);
220         return err ?: count;
221 }
222
223 static const struct file_operations nsim_dev_health_break_fops = {
224         .open = simple_open,
225         .write = nsim_dev_health_break_write,
226         .llseek = generic_file_llseek,
227         .owner = THIS_MODULE,
228 };
229
230 int nsim_dev_health_init(struct nsim_dev *nsim_dev, struct devlink *devlink)
231 {
232         struct nsim_dev_health *health = &nsim_dev->health;
233         int err;
234
235         health->empty_reporter =
236                 devlink_health_reporter_create(devlink,
237                                                &nsim_dev_empty_reporter_ops,
238                                                0, health);
239         if (IS_ERR(health->empty_reporter))
240                 return PTR_ERR(health->empty_reporter);
241
242         health->dummy_reporter =
243                 devlink_health_reporter_create(devlink,
244                                                &nsim_dev_dummy_reporter_ops,
245                                                0, health);
246         if (IS_ERR(health->dummy_reporter)) {
247                 err = PTR_ERR(health->dummy_reporter);
248                 goto err_empty_reporter_destroy;
249         }
250
251         health->ddir = debugfs_create_dir("health", nsim_dev->ddir);
252         if (IS_ERR(health->ddir)) {
253                 err = PTR_ERR(health->ddir);
254                 goto err_dummy_reporter_destroy;
255         }
256
257         health->recovered_break_msg = NULL;
258         debugfs_create_file("break_health", 0200, health->ddir, health,
259                             &nsim_dev_health_break_fops);
260         health->binary_len = 16;
261         debugfs_create_u32("binary_len", 0600, health->ddir,
262                            &health->binary_len);
263         health->fail_recover = false;
264         debugfs_create_bool("fail_recover", 0600, health->ddir,
265                             &health->fail_recover);
266         return 0;
267
268 err_dummy_reporter_destroy:
269         devlink_health_reporter_destroy(health->dummy_reporter);
270 err_empty_reporter_destroy:
271         devlink_health_reporter_destroy(health->empty_reporter);
272         return err;
273 }
274
275 void nsim_dev_health_exit(struct nsim_dev *nsim_dev)
276 {
277         struct nsim_dev_health *health = &nsim_dev->health;
278
279         debugfs_remove_recursive(health->ddir);
280         kfree(health->recovered_break_msg);
281         devlink_health_reporter_destroy(health->dummy_reporter);
282         devlink_health_reporter_destroy(health->empty_reporter);
283 }