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); 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, false, 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, false, 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_OR_NULL(health->ddir)) { 289 err = PTR_ERR_OR_ZERO(health->ddir) ?: -EINVAL; 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 } 320