health.c (7298B)
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 11static int 12nsim_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 19static int 20nsim_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 27static const 28struct 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 34struct nsim_dev_dummy_reporter_ctx { 35 char *break_msg; 36}; 37 38static int 39nsim_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 63static 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 153static int 154nsim_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 171static int 172nsim_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 189static const 190struct 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 197static 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 218out: 219 kfree(break_msg); 220 return err ?: count; 221} 222 223static 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 230int 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 268err_dummy_reporter_destroy: 269 devlink_health_reporter_destroy(health->dummy_reporter); 270err_empty_reporter_destroy: 271 devlink_health_reporter_destroy(health->empty_reporter); 272 return err; 273} 274 275void 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}