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 } 284