xref: /openbmc/linux/drivers/net/netdevsim/health.c (revision 8fdcd8fb)
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
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
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
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 
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 
8582c93a87SJiri Pirko 	binary = kmalloc(binary_len, GFP_KERNEL);
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_start(fmsg, "test_bool_array");
11482c93a87SJiri Pirko 	if (err)
11582c93a87SJiri Pirko 		return err;
11682c93a87SJiri Pirko 	for (i = 0; i < 10; i++) {
11782c93a87SJiri Pirko 		err = devlink_fmsg_bool_put(fmsg, true);
11882c93a87SJiri Pirko 		if (err)
11982c93a87SJiri Pirko 			return err;
12082c93a87SJiri Pirko 	}
12182c93a87SJiri Pirko 	err = devlink_fmsg_arr_pair_nest_end(fmsg);
12282c93a87SJiri Pirko 	if (err)
12382c93a87SJiri Pirko 		return err;
12482c93a87SJiri Pirko 
12582c93a87SJiri Pirko 	err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_u8_array");
12682c93a87SJiri Pirko 	if (err)
12782c93a87SJiri Pirko 		return err;
12882c93a87SJiri Pirko 	for (i = 0; i < 10; i++) {
12982c93a87SJiri Pirko 		err = devlink_fmsg_u8_put(fmsg, i);
13082c93a87SJiri Pirko 		if (err)
13182c93a87SJiri Pirko 			return err;
13282c93a87SJiri Pirko 	}
13382c93a87SJiri Pirko 	err = devlink_fmsg_arr_pair_nest_end(fmsg);
13482c93a87SJiri Pirko 	if (err)
13582c93a87SJiri Pirko 		return err;
13682c93a87SJiri Pirko 
13782c93a87SJiri Pirko 	err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_u32_array");
13882c93a87SJiri Pirko 	if (err)
13982c93a87SJiri Pirko 		return err;
14082c93a87SJiri Pirko 	for (i = 0; i < 10; i++) {
14182c93a87SJiri Pirko 		err = devlink_fmsg_u32_put(fmsg, i);
14282c93a87SJiri Pirko 		if (err)
14382c93a87SJiri Pirko 			return err;
14482c93a87SJiri Pirko 	}
14582c93a87SJiri Pirko 	err = devlink_fmsg_arr_pair_nest_end(fmsg);
14682c93a87SJiri Pirko 	if (err)
14782c93a87SJiri Pirko 		return err;
14882c93a87SJiri Pirko 
14982c93a87SJiri Pirko 	err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_u64_array");
15082c93a87SJiri Pirko 	if (err)
15182c93a87SJiri Pirko 		return err;
15282c93a87SJiri Pirko 	for (i = 0; i < 10; i++) {
15382c93a87SJiri Pirko 		err = devlink_fmsg_u64_put(fmsg, i);
15482c93a87SJiri Pirko 		if (err)
15582c93a87SJiri Pirko 			return err;
15682c93a87SJiri Pirko 	}
15782c93a87SJiri Pirko 	err = devlink_fmsg_arr_pair_nest_end(fmsg);
15882c93a87SJiri Pirko 	if (err)
15982c93a87SJiri Pirko 		return err;
16082c93a87SJiri Pirko 
16182c93a87SJiri Pirko 	err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_array_of_objects");
16282c93a87SJiri Pirko 	if (err)
16382c93a87SJiri Pirko 		return err;
16482c93a87SJiri Pirko 	for (i = 0; i < 10; i++) {
16582c93a87SJiri Pirko 		err = devlink_fmsg_obj_nest_start(fmsg);
16682c93a87SJiri Pirko 		if (err)
16782c93a87SJiri Pirko 			return err;
16882c93a87SJiri Pirko 		err = devlink_fmsg_bool_pair_put(fmsg,
16982c93a87SJiri Pirko 						 "in_array_nested_test_bool",
17082c93a87SJiri Pirko 						 false);
17182c93a87SJiri Pirko 		if (err)
17282c93a87SJiri Pirko 			return err;
17382c93a87SJiri Pirko 		err = devlink_fmsg_u8_pair_put(fmsg,
17482c93a87SJiri Pirko 					       "in_array_nested_test_u8",
17582c93a87SJiri Pirko 					       i);
17682c93a87SJiri Pirko 		if (err)
17782c93a87SJiri Pirko 			return err;
17882c93a87SJiri Pirko 		err = devlink_fmsg_obj_nest_end(fmsg);
17982c93a87SJiri Pirko 		if (err)
18082c93a87SJiri Pirko 			return err;
18182c93a87SJiri Pirko 	}
18282c93a87SJiri Pirko 	return devlink_fmsg_arr_pair_nest_end(fmsg);
18382c93a87SJiri Pirko }
18482c93a87SJiri Pirko 
18582c93a87SJiri Pirko static int
18682c93a87SJiri Pirko nsim_dev_dummy_reporter_dump(struct devlink_health_reporter *reporter,
18782c93a87SJiri Pirko 			     struct devlink_fmsg *fmsg, void *priv_ctx,
18882c93a87SJiri Pirko 			     struct netlink_ext_ack *extack)
18982c93a87SJiri Pirko {
19082c93a87SJiri Pirko 	struct nsim_dev_health *health = devlink_health_reporter_priv(reporter);
19182c93a87SJiri Pirko 	struct nsim_dev_dummy_reporter_ctx *ctx = priv_ctx;
19282c93a87SJiri Pirko 	int err;
19382c93a87SJiri Pirko 
19482c93a87SJiri Pirko 	if (ctx) {
19582c93a87SJiri Pirko 		err = devlink_fmsg_string_pair_put(fmsg, "break_message",
19682c93a87SJiri Pirko 						   ctx->break_msg);
19782c93a87SJiri Pirko 		if (err)
19882c93a87SJiri Pirko 			return err;
19982c93a87SJiri Pirko 	}
20082c93a87SJiri Pirko 	return nsim_dev_dummy_fmsg_put(fmsg, health->binary_len);
20182c93a87SJiri Pirko }
20282c93a87SJiri Pirko 
20382c93a87SJiri Pirko static int
20482c93a87SJiri Pirko nsim_dev_dummy_reporter_diagnose(struct devlink_health_reporter *reporter,
20582c93a87SJiri Pirko 				 struct devlink_fmsg *fmsg,
20682c93a87SJiri Pirko 				 struct netlink_ext_ack *extack)
20782c93a87SJiri Pirko {
20882c93a87SJiri Pirko 	struct nsim_dev_health *health = devlink_health_reporter_priv(reporter);
20982c93a87SJiri Pirko 	int err;
21082c93a87SJiri Pirko 
21182c93a87SJiri Pirko 	if (health->recovered_break_msg) {
21282c93a87SJiri Pirko 		err = devlink_fmsg_string_pair_put(fmsg,
21382c93a87SJiri Pirko 						   "recovered_break_message",
21482c93a87SJiri Pirko 						   health->recovered_break_msg);
21582c93a87SJiri Pirko 		if (err)
21682c93a87SJiri Pirko 			return err;
21782c93a87SJiri Pirko 	}
21882c93a87SJiri Pirko 	return nsim_dev_dummy_fmsg_put(fmsg, health->binary_len);
21982c93a87SJiri Pirko }
22082c93a87SJiri Pirko 
22182c93a87SJiri Pirko static const
22282c93a87SJiri Pirko struct devlink_health_reporter_ops nsim_dev_dummy_reporter_ops = {
22382c93a87SJiri Pirko 	.name = "dummy",
22482c93a87SJiri Pirko 	.recover = nsim_dev_dummy_reporter_recover,
22582c93a87SJiri Pirko 	.dump = nsim_dev_dummy_reporter_dump,
22682c93a87SJiri Pirko 	.diagnose = nsim_dev_dummy_reporter_diagnose,
22782c93a87SJiri Pirko };
22882c93a87SJiri Pirko 
22982c93a87SJiri Pirko static ssize_t nsim_dev_health_break_write(struct file *file,
23082c93a87SJiri Pirko 					   const char __user *data,
23182c93a87SJiri Pirko 					   size_t count, loff_t *ppos)
23282c93a87SJiri Pirko {
23382c93a87SJiri Pirko 	struct nsim_dev_health *health = file->private_data;
23482c93a87SJiri Pirko 	struct nsim_dev_dummy_reporter_ctx ctx;
23582c93a87SJiri Pirko 	char *break_msg;
23682c93a87SJiri Pirko 	int err;
23782c93a87SJiri Pirko 
23882c93a87SJiri Pirko 	break_msg = kmalloc(count + 1, GFP_KERNEL);
23982c93a87SJiri Pirko 	if (!break_msg)
24082c93a87SJiri Pirko 		return -ENOMEM;
24182c93a87SJiri Pirko 
24282c93a87SJiri Pirko 	if (copy_from_user(break_msg, data, count)) {
24382c93a87SJiri Pirko 		err = -EFAULT;
24482c93a87SJiri Pirko 		goto out;
24582c93a87SJiri Pirko 	}
24682c93a87SJiri Pirko 	break_msg[count] = '\0';
24782c93a87SJiri Pirko 	if (break_msg[count - 1] == '\n')
24882c93a87SJiri Pirko 		break_msg[count - 1] = '\0';
24982c93a87SJiri Pirko 
25082c93a87SJiri Pirko 	ctx.break_msg = break_msg;
25182c93a87SJiri Pirko 	err = devlink_health_report(health->dummy_reporter, break_msg, &ctx);
25282c93a87SJiri Pirko 	if (err)
25382c93a87SJiri Pirko 		goto out;
25482c93a87SJiri Pirko 
25582c93a87SJiri Pirko out:
25682c93a87SJiri Pirko 	kfree(break_msg);
25782c93a87SJiri Pirko 	return err ?: count;
25882c93a87SJiri Pirko }
25982c93a87SJiri Pirko 
26082c93a87SJiri Pirko static const struct file_operations nsim_dev_health_break_fops = {
26182c93a87SJiri Pirko 	.open = simple_open,
26282c93a87SJiri Pirko 	.write = nsim_dev_health_break_write,
26382c93a87SJiri Pirko 	.llseek = generic_file_llseek,
26482c93a87SJiri Pirko };
26582c93a87SJiri Pirko 
26682c93a87SJiri Pirko int nsim_dev_health_init(struct nsim_dev *nsim_dev, struct devlink *devlink)
26782c93a87SJiri Pirko {
26882c93a87SJiri Pirko 	struct nsim_dev_health *health = &nsim_dev->health;
26982c93a87SJiri Pirko 	int err;
27082c93a87SJiri Pirko 
27182c93a87SJiri Pirko 	health->empty_reporter =
27282c93a87SJiri Pirko 		devlink_health_reporter_create(devlink,
27382c93a87SJiri Pirko 					       &nsim_dev_empty_reporter_ops,
27482c93a87SJiri Pirko 					       0, false, health);
27582c93a87SJiri Pirko 	if (IS_ERR(health->empty_reporter))
27682c93a87SJiri Pirko 		return PTR_ERR(health->empty_reporter);
27782c93a87SJiri Pirko 
27882c93a87SJiri Pirko 	health->dummy_reporter =
27982c93a87SJiri Pirko 		devlink_health_reporter_create(devlink,
28082c93a87SJiri Pirko 					       &nsim_dev_dummy_reporter_ops,
28182c93a87SJiri Pirko 					       0, false, health);
28282c93a87SJiri Pirko 	if (IS_ERR(health->dummy_reporter)) {
28382c93a87SJiri Pirko 		err = PTR_ERR(health->dummy_reporter);
28482c93a87SJiri Pirko 		goto err_empty_reporter_destroy;
28582c93a87SJiri Pirko 	}
28682c93a87SJiri Pirko 
28782c93a87SJiri Pirko 	health->ddir = debugfs_create_dir("health", nsim_dev->ddir);
28882c93a87SJiri Pirko 	if (IS_ERR_OR_NULL(health->ddir)) {
28982c93a87SJiri Pirko 		err = PTR_ERR_OR_ZERO(health->ddir) ?: -EINVAL;
29082c93a87SJiri Pirko 		goto err_dummy_reporter_destroy;
29182c93a87SJiri Pirko 	}
29282c93a87SJiri Pirko 
29382c93a87SJiri Pirko 	health->recovered_break_msg = NULL;
29482c93a87SJiri Pirko 	debugfs_create_file("break_health", 0200, health->ddir, health,
29582c93a87SJiri Pirko 			    &nsim_dev_health_break_fops);
29682c93a87SJiri Pirko 	health->binary_len = 16;
29782c93a87SJiri Pirko 	debugfs_create_u32("binary_len", 0600, health->ddir,
29882c93a87SJiri Pirko 			   &health->binary_len);
29982c93a87SJiri Pirko 	health->fail_recover = false;
30082c93a87SJiri Pirko 	debugfs_create_bool("fail_recover", 0600, health->ddir,
30182c93a87SJiri Pirko 			    &health->fail_recover);
30282c93a87SJiri Pirko 	return 0;
30382c93a87SJiri Pirko 
30482c93a87SJiri Pirko err_dummy_reporter_destroy:
30582c93a87SJiri Pirko 	devlink_health_reporter_destroy(health->dummy_reporter);
30682c93a87SJiri Pirko err_empty_reporter_destroy:
30782c93a87SJiri Pirko 	devlink_health_reporter_destroy(health->empty_reporter);
30882c93a87SJiri Pirko 	return err;
30982c93a87SJiri Pirko }
31082c93a87SJiri Pirko 
31182c93a87SJiri Pirko void nsim_dev_health_exit(struct nsim_dev *nsim_dev)
31282c93a87SJiri Pirko {
31382c93a87SJiri Pirko 	struct nsim_dev_health *health = &nsim_dev->health;
31482c93a87SJiri Pirko 
31582c93a87SJiri Pirko 	debugfs_remove_recursive(health->ddir);
31682c93a87SJiri Pirko 	kfree(health->recovered_break_msg);
31782c93a87SJiri Pirko 	devlink_health_reporter_destroy(health->dummy_reporter);
31882c93a87SJiri Pirko 	devlink_health_reporter_destroy(health->empty_reporter);
31982c93a87SJiri Pirko }
320