xref: /openbmc/linux/drivers/net/netdevsim/health.c (revision dfdfd130)
182c93a87SJiri Pirko // SPDX-License-Identifier: GPL-2.0
282c93a87SJiri Pirko /* Copyright (c) 2019 Mellanox Technologies. All rights reserved */
382c93a87SJiri Pirko 
482c93a87SJiri Pirko #include <linux/debugfs.h>
582c93a87SJiri Pirko #include <linux/err.h>
682c93a87SJiri Pirko #include <linux/kernel.h>
782c93a87SJiri Pirko #include <linux/slab.h>
882c93a87SJiri Pirko 
982c93a87SJiri Pirko #include "netdevsim.h"
1082c93a87SJiri Pirko 
1182c93a87SJiri Pirko static int
nsim_dev_empty_reporter_dump(struct devlink_health_reporter * reporter,struct devlink_fmsg * fmsg,void * priv_ctx,struct netlink_ext_ack * extack)1282c93a87SJiri Pirko nsim_dev_empty_reporter_dump(struct devlink_health_reporter *reporter,
1382c93a87SJiri Pirko 			     struct devlink_fmsg *fmsg, void *priv_ctx,
1482c93a87SJiri Pirko 			     struct netlink_ext_ack *extack)
1582c93a87SJiri Pirko {
1682c93a87SJiri Pirko 	return 0;
1782c93a87SJiri Pirko }
1882c93a87SJiri Pirko 
1982c93a87SJiri Pirko static int
nsim_dev_empty_reporter_diagnose(struct devlink_health_reporter * reporter,struct devlink_fmsg * fmsg,struct netlink_ext_ack * extack)2082c93a87SJiri Pirko nsim_dev_empty_reporter_diagnose(struct devlink_health_reporter *reporter,
2182c93a87SJiri Pirko 				 struct devlink_fmsg *fmsg,
2282c93a87SJiri Pirko 				 struct netlink_ext_ack *extack)
2382c93a87SJiri Pirko {
2482c93a87SJiri Pirko 	return 0;
2582c93a87SJiri Pirko }
2682c93a87SJiri Pirko 
2782c93a87SJiri Pirko static const
2882c93a87SJiri Pirko struct devlink_health_reporter_ops nsim_dev_empty_reporter_ops = {
2982c93a87SJiri Pirko 	.name = "empty",
3082c93a87SJiri Pirko 	.dump = nsim_dev_empty_reporter_dump,
3182c93a87SJiri Pirko 	.diagnose = nsim_dev_empty_reporter_diagnose,
3282c93a87SJiri Pirko };
3382c93a87SJiri Pirko 
3482c93a87SJiri Pirko struct nsim_dev_dummy_reporter_ctx {
3582c93a87SJiri Pirko 	char *break_msg;
3682c93a87SJiri Pirko };
3782c93a87SJiri Pirko 
3882c93a87SJiri Pirko static int
nsim_dev_dummy_reporter_recover(struct devlink_health_reporter * reporter,void * priv_ctx,struct netlink_ext_ack * extack)3982c93a87SJiri Pirko nsim_dev_dummy_reporter_recover(struct devlink_health_reporter *reporter,
4082c93a87SJiri Pirko 				void *priv_ctx,
4182c93a87SJiri Pirko 				struct netlink_ext_ack *extack)
4282c93a87SJiri Pirko {
4382c93a87SJiri Pirko 	struct nsim_dev_health *health = devlink_health_reporter_priv(reporter);
4482c93a87SJiri Pirko 	struct nsim_dev_dummy_reporter_ctx *ctx = priv_ctx;
4582c93a87SJiri Pirko 
4682c93a87SJiri Pirko 	if (health->fail_recover) {
4782c93a87SJiri Pirko 		/* For testing purposes, user set debugfs fail_recover
4882c93a87SJiri Pirko 		 * value to true. Fail right away.
4982c93a87SJiri Pirko 		 */
5082c93a87SJiri Pirko 		NL_SET_ERR_MSG_MOD(extack, "User setup the recover to fail for testing purposes");
5182c93a87SJiri Pirko 		return -EINVAL;
5282c93a87SJiri Pirko 	}
5382c93a87SJiri Pirko 	if (ctx) {
5482c93a87SJiri Pirko 		kfree(health->recovered_break_msg);
5582c93a87SJiri Pirko 		health->recovered_break_msg = kstrdup(ctx->break_msg,
5682c93a87SJiri Pirko 						      GFP_KERNEL);
5782c93a87SJiri Pirko 		if (!health->recovered_break_msg)
5882c93a87SJiri Pirko 			return -ENOMEM;
5982c93a87SJiri Pirko 	}
6082c93a87SJiri Pirko 	return 0;
6182c93a87SJiri Pirko }
6282c93a87SJiri Pirko 
nsim_dev_dummy_fmsg_put(struct devlink_fmsg * fmsg,u32 binary_len)6382c93a87SJiri Pirko static int nsim_dev_dummy_fmsg_put(struct devlink_fmsg *fmsg, u32 binary_len)
6482c93a87SJiri Pirko {
6582c93a87SJiri Pirko 	char *binary;
6682c93a87SJiri Pirko 	int err;
6782c93a87SJiri Pirko 	int i;
6882c93a87SJiri Pirko 
6982c93a87SJiri Pirko 	err = devlink_fmsg_bool_pair_put(fmsg, "test_bool", true);
7082c93a87SJiri Pirko 	if (err)
7182c93a87SJiri Pirko 		return err;
7282c93a87SJiri Pirko 	err = devlink_fmsg_u8_pair_put(fmsg, "test_u8", 1);
7382c93a87SJiri Pirko 	if (err)
7482c93a87SJiri Pirko 		return err;
7582c93a87SJiri Pirko 	err = devlink_fmsg_u32_pair_put(fmsg, "test_u32", 3);
7682c93a87SJiri Pirko 	if (err)
7782c93a87SJiri Pirko 		return err;
7882c93a87SJiri Pirko 	err = devlink_fmsg_u64_pair_put(fmsg, "test_u64", 4);
7982c93a87SJiri Pirko 	if (err)
8082c93a87SJiri Pirko 		return err;
8182c93a87SJiri Pirko 	err = devlink_fmsg_string_pair_put(fmsg, "test_string", "somestring");
8282c93a87SJiri Pirko 	if (err)
8382c93a87SJiri Pirko 		return err;
8482c93a87SJiri Pirko 
8583cf4213STaehee Yoo 	binary = kmalloc(binary_len, GFP_KERNEL | __GFP_NOWARN);
8682c93a87SJiri Pirko 	if (!binary)
8782c93a87SJiri Pirko 		return -ENOMEM;
8882c93a87SJiri Pirko 	get_random_bytes(binary, binary_len);
898fdcd8fbSAya Levin 	err = devlink_fmsg_binary_pair_put(fmsg, "test_binary", binary, binary_len);
9082c93a87SJiri Pirko 	kfree(binary);
9182c93a87SJiri Pirko 	if (err)
9282c93a87SJiri Pirko 		return err;
9382c93a87SJiri Pirko 
9482c93a87SJiri Pirko 	err = devlink_fmsg_pair_nest_start(fmsg, "test_nest");
9582c93a87SJiri Pirko 	if (err)
9682c93a87SJiri Pirko 		return err;
9782c93a87SJiri Pirko 	err = devlink_fmsg_obj_nest_start(fmsg);
9882c93a87SJiri Pirko 	if (err)
9982c93a87SJiri Pirko 		return err;
10082c93a87SJiri Pirko 	err = devlink_fmsg_bool_pair_put(fmsg, "nested_test_bool", false);
10182c93a87SJiri Pirko 	if (err)
10282c93a87SJiri Pirko 		return err;
10382c93a87SJiri Pirko 	err = devlink_fmsg_u8_pair_put(fmsg, "nested_test_u8", false);
10482c93a87SJiri Pirko 	if (err)
10582c93a87SJiri Pirko 		return err;
10682c93a87SJiri Pirko 	err = devlink_fmsg_obj_nest_end(fmsg);
10782c93a87SJiri Pirko 	if (err)
10882c93a87SJiri Pirko 		return err;
10982c93a87SJiri Pirko 	err = devlink_fmsg_pair_nest_end(fmsg);
11082c93a87SJiri Pirko 	if (err)
11182c93a87SJiri Pirko 		return err;
11282c93a87SJiri Pirko 
11382c93a87SJiri Pirko 	err = devlink_fmsg_arr_pair_nest_end(fmsg);
11482c93a87SJiri Pirko 	if (err)
11582c93a87SJiri Pirko 		return err;
11682c93a87SJiri Pirko 
11782c93a87SJiri Pirko 	err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_u32_array");
11882c93a87SJiri Pirko 	if (err)
11982c93a87SJiri Pirko 		return err;
12082c93a87SJiri Pirko 	for (i = 0; i < 10; i++) {
12182c93a87SJiri Pirko 		err = devlink_fmsg_u32_put(fmsg, i);
12282c93a87SJiri Pirko 		if (err)
12382c93a87SJiri Pirko 			return err;
12482c93a87SJiri Pirko 	}
12582c93a87SJiri Pirko 	err = devlink_fmsg_arr_pair_nest_end(fmsg);
12682c93a87SJiri Pirko 	if (err)
12782c93a87SJiri Pirko 		return err;
12882c93a87SJiri Pirko 
12982c93a87SJiri Pirko 	err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_array_of_objects");
13082c93a87SJiri Pirko 	if (err)
13182c93a87SJiri Pirko 		return err;
13282c93a87SJiri Pirko 	for (i = 0; i < 10; i++) {
13382c93a87SJiri Pirko 		err = devlink_fmsg_obj_nest_start(fmsg);
13482c93a87SJiri Pirko 		if (err)
13582c93a87SJiri Pirko 			return err;
13682c93a87SJiri Pirko 		err = devlink_fmsg_bool_pair_put(fmsg,
13782c93a87SJiri Pirko 						 "in_array_nested_test_bool",
13882c93a87SJiri Pirko 						 false);
13982c93a87SJiri Pirko 		if (err)
14082c93a87SJiri Pirko 			return err;
14182c93a87SJiri Pirko 		err = devlink_fmsg_u8_pair_put(fmsg,
14282c93a87SJiri Pirko 					       "in_array_nested_test_u8",
14382c93a87SJiri Pirko 					       i);
14482c93a87SJiri Pirko 		if (err)
14582c93a87SJiri Pirko 			return err;
14682c93a87SJiri Pirko 		err = devlink_fmsg_obj_nest_end(fmsg);
14782c93a87SJiri Pirko 		if (err)
14882c93a87SJiri Pirko 			return err;
14982c93a87SJiri Pirko 	}
15082c93a87SJiri Pirko 	return devlink_fmsg_arr_pair_nest_end(fmsg);
15182c93a87SJiri Pirko }
15282c93a87SJiri Pirko 
15382c93a87SJiri Pirko static int
nsim_dev_dummy_reporter_dump(struct devlink_health_reporter * reporter,struct devlink_fmsg * fmsg,void * priv_ctx,struct netlink_ext_ack * extack)15482c93a87SJiri Pirko nsim_dev_dummy_reporter_dump(struct devlink_health_reporter *reporter,
15582c93a87SJiri Pirko 			     struct devlink_fmsg *fmsg, void *priv_ctx,
15682c93a87SJiri Pirko 			     struct netlink_ext_ack *extack)
15782c93a87SJiri Pirko {
15882c93a87SJiri Pirko 	struct nsim_dev_health *health = devlink_health_reporter_priv(reporter);
15982c93a87SJiri Pirko 	struct nsim_dev_dummy_reporter_ctx *ctx = priv_ctx;
16082c93a87SJiri Pirko 	int err;
16182c93a87SJiri Pirko 
16282c93a87SJiri Pirko 	if (ctx) {
16382c93a87SJiri Pirko 		err = devlink_fmsg_string_pair_put(fmsg, "break_message",
16482c93a87SJiri Pirko 						   ctx->break_msg);
16582c93a87SJiri Pirko 		if (err)
16682c93a87SJiri Pirko 			return err;
16782c93a87SJiri Pirko 	}
16882c93a87SJiri Pirko 	return nsim_dev_dummy_fmsg_put(fmsg, health->binary_len);
16982c93a87SJiri Pirko }
17082c93a87SJiri Pirko 
17182c93a87SJiri Pirko static int
nsim_dev_dummy_reporter_diagnose(struct devlink_health_reporter * reporter,struct devlink_fmsg * fmsg,struct netlink_ext_ack * extack)17282c93a87SJiri Pirko nsim_dev_dummy_reporter_diagnose(struct devlink_health_reporter *reporter,
17382c93a87SJiri Pirko 				 struct devlink_fmsg *fmsg,
17482c93a87SJiri Pirko 				 struct netlink_ext_ack *extack)
17582c93a87SJiri Pirko {
17682c93a87SJiri Pirko 	struct nsim_dev_health *health = devlink_health_reporter_priv(reporter);
17782c93a87SJiri Pirko 	int err;
17882c93a87SJiri Pirko 
17982c93a87SJiri Pirko 	if (health->recovered_break_msg) {
18082c93a87SJiri Pirko 		err = devlink_fmsg_string_pair_put(fmsg,
18182c93a87SJiri Pirko 						   "recovered_break_message",
18282c93a87SJiri Pirko 						   health->recovered_break_msg);
18382c93a87SJiri Pirko 		if (err)
18482c93a87SJiri Pirko 			return err;
18582c93a87SJiri Pirko 	}
18682c93a87SJiri Pirko 	return nsim_dev_dummy_fmsg_put(fmsg, health->binary_len);
18782c93a87SJiri Pirko }
18882c93a87SJiri Pirko 
18982c93a87SJiri Pirko static const
19082c93a87SJiri Pirko struct devlink_health_reporter_ops nsim_dev_dummy_reporter_ops = {
19182c93a87SJiri Pirko 	.name = "dummy",
19282c93a87SJiri Pirko 	.recover = nsim_dev_dummy_reporter_recover,
19382c93a87SJiri Pirko 	.dump = nsim_dev_dummy_reporter_dump,
19482c93a87SJiri Pirko 	.diagnose = nsim_dev_dummy_reporter_diagnose,
19582c93a87SJiri Pirko };
19682c93a87SJiri Pirko 
nsim_dev_health_break_write(struct file * file,const char __user * data,size_t count,loff_t * ppos)19782c93a87SJiri Pirko static ssize_t nsim_dev_health_break_write(struct file *file,
19882c93a87SJiri Pirko 					   const char __user *data,
19982c93a87SJiri Pirko 					   size_t count, loff_t *ppos)
20082c93a87SJiri Pirko {
20182c93a87SJiri Pirko 	struct nsim_dev_health *health = file->private_data;
20282c93a87SJiri Pirko 	struct nsim_dev_dummy_reporter_ctx ctx;
20382c93a87SJiri Pirko 	char *break_msg;
20482c93a87SJiri Pirko 	int err;
20582c93a87SJiri Pirko 
20620fd4f42SWei Yongjun 	break_msg = memdup_user_nul(data, count);
20720fd4f42SWei Yongjun 	if (IS_ERR(break_msg))
20820fd4f42SWei Yongjun 		return PTR_ERR(break_msg);
20982c93a87SJiri Pirko 
21082c93a87SJiri Pirko 	if (break_msg[count - 1] == '\n')
21182c93a87SJiri Pirko 		break_msg[count - 1] = '\0';
21282c93a87SJiri Pirko 
21382c93a87SJiri Pirko 	ctx.break_msg = break_msg;
21482c93a87SJiri Pirko 	err = devlink_health_report(health->dummy_reporter, break_msg, &ctx);
21582c93a87SJiri Pirko 	if (err)
21682c93a87SJiri Pirko 		goto out;
21782c93a87SJiri Pirko 
21882c93a87SJiri Pirko out:
21982c93a87SJiri Pirko 	kfree(break_msg);
22082c93a87SJiri Pirko 	return err ?: count;
22182c93a87SJiri Pirko }
22282c93a87SJiri Pirko 
22382c93a87SJiri Pirko static const struct file_operations nsim_dev_health_break_fops = {
22482c93a87SJiri Pirko 	.open = simple_open,
22582c93a87SJiri Pirko 	.write = nsim_dev_health_break_write,
22682c93a87SJiri Pirko 	.llseek = generic_file_llseek,
227a5bbcbf2STaehee Yoo 	.owner = THIS_MODULE,
22882c93a87SJiri Pirko };
22982c93a87SJiri Pirko 
nsim_dev_health_init(struct nsim_dev * nsim_dev,struct devlink * devlink)23082c93a87SJiri Pirko int nsim_dev_health_init(struct nsim_dev *nsim_dev, struct devlink *devlink)
23182c93a87SJiri Pirko {
23282c93a87SJiri Pirko 	struct nsim_dev_health *health = &nsim_dev->health;
23382c93a87SJiri Pirko 	int err;
23482c93a87SJiri Pirko 
23582c93a87SJiri Pirko 	health->empty_reporter =
236*dfdfd130SJiri Pirko 		devl_health_reporter_create(devlink,
23782c93a87SJiri Pirko 					    &nsim_dev_empty_reporter_ops,
238ba7d16c7SEran Ben Elisha 					    0, health);
23982c93a87SJiri Pirko 	if (IS_ERR(health->empty_reporter))
24082c93a87SJiri Pirko 		return PTR_ERR(health->empty_reporter);
24182c93a87SJiri Pirko 
24282c93a87SJiri Pirko 	health->dummy_reporter =
243*dfdfd130SJiri Pirko 		devl_health_reporter_create(devlink,
24482c93a87SJiri Pirko 					    &nsim_dev_dummy_reporter_ops,
245ba7d16c7SEran Ben Elisha 					    0, health);
24682c93a87SJiri Pirko 	if (IS_ERR(health->dummy_reporter)) {
24782c93a87SJiri Pirko 		err = PTR_ERR(health->dummy_reporter);
24882c93a87SJiri Pirko 		goto err_empty_reporter_destroy;
24982c93a87SJiri Pirko 	}
25082c93a87SJiri Pirko 
25182c93a87SJiri Pirko 	health->ddir = debugfs_create_dir("health", nsim_dev->ddir);
2526556ff32STaehee Yoo 	if (IS_ERR(health->ddir)) {
2536556ff32STaehee Yoo 		err = PTR_ERR(health->ddir);
25482c93a87SJiri Pirko 		goto err_dummy_reporter_destroy;
25582c93a87SJiri Pirko 	}
25682c93a87SJiri Pirko 
25782c93a87SJiri Pirko 	health->recovered_break_msg = NULL;
25882c93a87SJiri Pirko 	debugfs_create_file("break_health", 0200, health->ddir, health,
25982c93a87SJiri Pirko 			    &nsim_dev_health_break_fops);
26082c93a87SJiri Pirko 	health->binary_len = 16;
26182c93a87SJiri Pirko 	debugfs_create_u32("binary_len", 0600, health->ddir,
26282c93a87SJiri Pirko 			   &health->binary_len);
26382c93a87SJiri Pirko 	health->fail_recover = false;
26482c93a87SJiri Pirko 	debugfs_create_bool("fail_recover", 0600, health->ddir,
26582c93a87SJiri Pirko 			    &health->fail_recover);
26682c93a87SJiri Pirko 	return 0;
26782c93a87SJiri Pirko 
26882c93a87SJiri Pirko err_dummy_reporter_destroy:
269*dfdfd130SJiri Pirko 	devl_health_reporter_destroy(health->dummy_reporter);
27082c93a87SJiri Pirko err_empty_reporter_destroy:
271*dfdfd130SJiri Pirko 	devl_health_reporter_destroy(health->empty_reporter);
27282c93a87SJiri Pirko 	return err;
27382c93a87SJiri Pirko }
27482c93a87SJiri Pirko 
nsim_dev_health_exit(struct nsim_dev * nsim_dev)27582c93a87SJiri Pirko void nsim_dev_health_exit(struct nsim_dev *nsim_dev)
27682c93a87SJiri Pirko {
27782c93a87SJiri Pirko 	struct nsim_dev_health *health = &nsim_dev->health;
27882c93a87SJiri Pirko 
27982c93a87SJiri Pirko 	debugfs_remove_recursive(health->ddir);
28082c93a87SJiri Pirko 	kfree(health->recovered_break_msg);
281*dfdfd130SJiri Pirko 	devl_health_reporter_destroy(health->dummy_reporter);
282*dfdfd130SJiri Pirko 	devl_health_reporter_destroy(health->empty_reporter);
28382c93a87SJiri Pirko }
284