0001
0002
0003
0004 #include <linux/debugfs.h>
0005 #include <linux/err.h>
0006 #include <linux/kernel.h>
0007 #include <linux/slab.h>
0008
0009 #include "netdevsim.h"
0010
0011 static int
0012 nsim_dev_empty_reporter_dump(struct devlink_health_reporter *reporter,
0013 struct devlink_fmsg *fmsg, void *priv_ctx,
0014 struct netlink_ext_ack *extack)
0015 {
0016 return 0;
0017 }
0018
0019 static int
0020 nsim_dev_empty_reporter_diagnose(struct devlink_health_reporter *reporter,
0021 struct devlink_fmsg *fmsg,
0022 struct netlink_ext_ack *extack)
0023 {
0024 return 0;
0025 }
0026
0027 static const
0028 struct devlink_health_reporter_ops nsim_dev_empty_reporter_ops = {
0029 .name = "empty",
0030 .dump = nsim_dev_empty_reporter_dump,
0031 .diagnose = nsim_dev_empty_reporter_diagnose,
0032 };
0033
0034 struct nsim_dev_dummy_reporter_ctx {
0035 char *break_msg;
0036 };
0037
0038 static int
0039 nsim_dev_dummy_reporter_recover(struct devlink_health_reporter *reporter,
0040 void *priv_ctx,
0041 struct netlink_ext_ack *extack)
0042 {
0043 struct nsim_dev_health *health = devlink_health_reporter_priv(reporter);
0044 struct nsim_dev_dummy_reporter_ctx *ctx = priv_ctx;
0045
0046 if (health->fail_recover) {
0047
0048
0049
0050 NL_SET_ERR_MSG_MOD(extack, "User setup the recover to fail for testing purposes");
0051 return -EINVAL;
0052 }
0053 if (ctx) {
0054 kfree(health->recovered_break_msg);
0055 health->recovered_break_msg = kstrdup(ctx->break_msg,
0056 GFP_KERNEL);
0057 if (!health->recovered_break_msg)
0058 return -ENOMEM;
0059 }
0060 return 0;
0061 }
0062
0063 static int nsim_dev_dummy_fmsg_put(struct devlink_fmsg *fmsg, u32 binary_len)
0064 {
0065 char *binary;
0066 int err;
0067 int i;
0068
0069 err = devlink_fmsg_bool_pair_put(fmsg, "test_bool", true);
0070 if (err)
0071 return err;
0072 err = devlink_fmsg_u8_pair_put(fmsg, "test_u8", 1);
0073 if (err)
0074 return err;
0075 err = devlink_fmsg_u32_pair_put(fmsg, "test_u32", 3);
0076 if (err)
0077 return err;
0078 err = devlink_fmsg_u64_pair_put(fmsg, "test_u64", 4);
0079 if (err)
0080 return err;
0081 err = devlink_fmsg_string_pair_put(fmsg, "test_string", "somestring");
0082 if (err)
0083 return err;
0084
0085 binary = kmalloc(binary_len, GFP_KERNEL | __GFP_NOWARN);
0086 if (!binary)
0087 return -ENOMEM;
0088 get_random_bytes(binary, binary_len);
0089 err = devlink_fmsg_binary_pair_put(fmsg, "test_binary", binary, binary_len);
0090 kfree(binary);
0091 if (err)
0092 return err;
0093
0094 err = devlink_fmsg_pair_nest_start(fmsg, "test_nest");
0095 if (err)
0096 return err;
0097 err = devlink_fmsg_obj_nest_start(fmsg);
0098 if (err)
0099 return err;
0100 err = devlink_fmsg_bool_pair_put(fmsg, "nested_test_bool", false);
0101 if (err)
0102 return err;
0103 err = devlink_fmsg_u8_pair_put(fmsg, "nested_test_u8", false);
0104 if (err)
0105 return err;
0106 err = devlink_fmsg_obj_nest_end(fmsg);
0107 if (err)
0108 return err;
0109 err = devlink_fmsg_pair_nest_end(fmsg);
0110 if (err)
0111 return err;
0112
0113 err = devlink_fmsg_arr_pair_nest_end(fmsg);
0114 if (err)
0115 return err;
0116
0117 err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_u32_array");
0118 if (err)
0119 return err;
0120 for (i = 0; i < 10; i++) {
0121 err = devlink_fmsg_u32_put(fmsg, i);
0122 if (err)
0123 return err;
0124 }
0125 err = devlink_fmsg_arr_pair_nest_end(fmsg);
0126 if (err)
0127 return err;
0128
0129 err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_array_of_objects");
0130 if (err)
0131 return err;
0132 for (i = 0; i < 10; i++) {
0133 err = devlink_fmsg_obj_nest_start(fmsg);
0134 if (err)
0135 return err;
0136 err = devlink_fmsg_bool_pair_put(fmsg,
0137 "in_array_nested_test_bool",
0138 false);
0139 if (err)
0140 return err;
0141 err = devlink_fmsg_u8_pair_put(fmsg,
0142 "in_array_nested_test_u8",
0143 i);
0144 if (err)
0145 return err;
0146 err = devlink_fmsg_obj_nest_end(fmsg);
0147 if (err)
0148 return err;
0149 }
0150 return devlink_fmsg_arr_pair_nest_end(fmsg);
0151 }
0152
0153 static int
0154 nsim_dev_dummy_reporter_dump(struct devlink_health_reporter *reporter,
0155 struct devlink_fmsg *fmsg, void *priv_ctx,
0156 struct netlink_ext_ack *extack)
0157 {
0158 struct nsim_dev_health *health = devlink_health_reporter_priv(reporter);
0159 struct nsim_dev_dummy_reporter_ctx *ctx = priv_ctx;
0160 int err;
0161
0162 if (ctx) {
0163 err = devlink_fmsg_string_pair_put(fmsg, "break_message",
0164 ctx->break_msg);
0165 if (err)
0166 return err;
0167 }
0168 return nsim_dev_dummy_fmsg_put(fmsg, health->binary_len);
0169 }
0170
0171 static int
0172 nsim_dev_dummy_reporter_diagnose(struct devlink_health_reporter *reporter,
0173 struct devlink_fmsg *fmsg,
0174 struct netlink_ext_ack *extack)
0175 {
0176 struct nsim_dev_health *health = devlink_health_reporter_priv(reporter);
0177 int err;
0178
0179 if (health->recovered_break_msg) {
0180 err = devlink_fmsg_string_pair_put(fmsg,
0181 "recovered_break_message",
0182 health->recovered_break_msg);
0183 if (err)
0184 return err;
0185 }
0186 return nsim_dev_dummy_fmsg_put(fmsg, health->binary_len);
0187 }
0188
0189 static const
0190 struct devlink_health_reporter_ops nsim_dev_dummy_reporter_ops = {
0191 .name = "dummy",
0192 .recover = nsim_dev_dummy_reporter_recover,
0193 .dump = nsim_dev_dummy_reporter_dump,
0194 .diagnose = nsim_dev_dummy_reporter_diagnose,
0195 };
0196
0197 static ssize_t nsim_dev_health_break_write(struct file *file,
0198 const char __user *data,
0199 size_t count, loff_t *ppos)
0200 {
0201 struct nsim_dev_health *health = file->private_data;
0202 struct nsim_dev_dummy_reporter_ctx ctx;
0203 char *break_msg;
0204 int err;
0205
0206 break_msg = memdup_user_nul(data, count);
0207 if (IS_ERR(break_msg))
0208 return PTR_ERR(break_msg);
0209
0210 if (break_msg[count - 1] == '\n')
0211 break_msg[count - 1] = '\0';
0212
0213 ctx.break_msg = break_msg;
0214 err = devlink_health_report(health->dummy_reporter, break_msg, &ctx);
0215 if (err)
0216 goto out;
0217
0218 out:
0219 kfree(break_msg);
0220 return err ?: count;
0221 }
0222
0223 static const struct file_operations nsim_dev_health_break_fops = {
0224 .open = simple_open,
0225 .write = nsim_dev_health_break_write,
0226 .llseek = generic_file_llseek,
0227 .owner = THIS_MODULE,
0228 };
0229
0230 int nsim_dev_health_init(struct nsim_dev *nsim_dev, struct devlink *devlink)
0231 {
0232 struct nsim_dev_health *health = &nsim_dev->health;
0233 int err;
0234
0235 health->empty_reporter =
0236 devlink_health_reporter_create(devlink,
0237 &nsim_dev_empty_reporter_ops,
0238 0, health);
0239 if (IS_ERR(health->empty_reporter))
0240 return PTR_ERR(health->empty_reporter);
0241
0242 health->dummy_reporter =
0243 devlink_health_reporter_create(devlink,
0244 &nsim_dev_dummy_reporter_ops,
0245 0, health);
0246 if (IS_ERR(health->dummy_reporter)) {
0247 err = PTR_ERR(health->dummy_reporter);
0248 goto err_empty_reporter_destroy;
0249 }
0250
0251 health->ddir = debugfs_create_dir("health", nsim_dev->ddir);
0252 if (IS_ERR(health->ddir)) {
0253 err = PTR_ERR(health->ddir);
0254 goto err_dummy_reporter_destroy;
0255 }
0256
0257 health->recovered_break_msg = NULL;
0258 debugfs_create_file("break_health", 0200, health->ddir, health,
0259 &nsim_dev_health_break_fops);
0260 health->binary_len = 16;
0261 debugfs_create_u32("binary_len", 0600, health->ddir,
0262 &health->binary_len);
0263 health->fail_recover = false;
0264 debugfs_create_bool("fail_recover", 0600, health->ddir,
0265 &health->fail_recover);
0266 return 0;
0267
0268 err_dummy_reporter_destroy:
0269 devlink_health_reporter_destroy(health->dummy_reporter);
0270 err_empty_reporter_destroy:
0271 devlink_health_reporter_destroy(health->empty_reporter);
0272 return err;
0273 }
0274
0275 void nsim_dev_health_exit(struct nsim_dev *nsim_dev)
0276 {
0277 struct nsim_dev_health *health = &nsim_dev->health;
0278
0279 debugfs_remove_recursive(health->ddir);
0280 kfree(health->recovered_break_msg);
0281 devlink_health_reporter_destroy(health->dummy_reporter);
0282 devlink_health_reporter_destroy(health->empty_reporter);
0283 }