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 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_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 238*20fd4f42SWei Yongjun break_msg = memdup_user_nul(data, count); 239*20fd4f42SWei Yongjun if (IS_ERR(break_msg)) 240*20fd4f42SWei Yongjun return PTR_ERR(break_msg); 24182c93a87SJiri Pirko 24282c93a87SJiri Pirko if (break_msg[count - 1] == '\n') 24382c93a87SJiri Pirko break_msg[count - 1] = '\0'; 24482c93a87SJiri Pirko 24582c93a87SJiri Pirko ctx.break_msg = break_msg; 24682c93a87SJiri Pirko err = devlink_health_report(health->dummy_reporter, break_msg, &ctx); 24782c93a87SJiri Pirko if (err) 24882c93a87SJiri Pirko goto out; 24982c93a87SJiri Pirko 25082c93a87SJiri Pirko out: 25182c93a87SJiri Pirko kfree(break_msg); 25282c93a87SJiri Pirko return err ?: count; 25382c93a87SJiri Pirko } 25482c93a87SJiri Pirko 25582c93a87SJiri Pirko static const struct file_operations nsim_dev_health_break_fops = { 25682c93a87SJiri Pirko .open = simple_open, 25782c93a87SJiri Pirko .write = nsim_dev_health_break_write, 25882c93a87SJiri Pirko .llseek = generic_file_llseek, 259a5bbcbf2STaehee Yoo .owner = THIS_MODULE, 26082c93a87SJiri Pirko }; 26182c93a87SJiri Pirko 26282c93a87SJiri Pirko int nsim_dev_health_init(struct nsim_dev *nsim_dev, struct devlink *devlink) 26382c93a87SJiri Pirko { 26482c93a87SJiri Pirko struct nsim_dev_health *health = &nsim_dev->health; 26582c93a87SJiri Pirko int err; 26682c93a87SJiri Pirko 26782c93a87SJiri Pirko health->empty_reporter = 26882c93a87SJiri Pirko devlink_health_reporter_create(devlink, 26982c93a87SJiri Pirko &nsim_dev_empty_reporter_ops, 270ba7d16c7SEran Ben Elisha 0, health); 27182c93a87SJiri Pirko if (IS_ERR(health->empty_reporter)) 27282c93a87SJiri Pirko return PTR_ERR(health->empty_reporter); 27382c93a87SJiri Pirko 27482c93a87SJiri Pirko health->dummy_reporter = 27582c93a87SJiri Pirko devlink_health_reporter_create(devlink, 27682c93a87SJiri Pirko &nsim_dev_dummy_reporter_ops, 277ba7d16c7SEran Ben Elisha 0, health); 27882c93a87SJiri Pirko if (IS_ERR(health->dummy_reporter)) { 27982c93a87SJiri Pirko err = PTR_ERR(health->dummy_reporter); 28082c93a87SJiri Pirko goto err_empty_reporter_destroy; 28182c93a87SJiri Pirko } 28282c93a87SJiri Pirko 28382c93a87SJiri Pirko health->ddir = debugfs_create_dir("health", nsim_dev->ddir); 2846556ff32STaehee Yoo if (IS_ERR(health->ddir)) { 2856556ff32STaehee Yoo err = PTR_ERR(health->ddir); 28682c93a87SJiri Pirko goto err_dummy_reporter_destroy; 28782c93a87SJiri Pirko } 28882c93a87SJiri Pirko 28982c93a87SJiri Pirko health->recovered_break_msg = NULL; 29082c93a87SJiri Pirko debugfs_create_file("break_health", 0200, health->ddir, health, 29182c93a87SJiri Pirko &nsim_dev_health_break_fops); 29282c93a87SJiri Pirko health->binary_len = 16; 29382c93a87SJiri Pirko debugfs_create_u32("binary_len", 0600, health->ddir, 29482c93a87SJiri Pirko &health->binary_len); 29582c93a87SJiri Pirko health->fail_recover = false; 29682c93a87SJiri Pirko debugfs_create_bool("fail_recover", 0600, health->ddir, 29782c93a87SJiri Pirko &health->fail_recover); 29882c93a87SJiri Pirko return 0; 29982c93a87SJiri Pirko 30082c93a87SJiri Pirko err_dummy_reporter_destroy: 30182c93a87SJiri Pirko devlink_health_reporter_destroy(health->dummy_reporter); 30282c93a87SJiri Pirko err_empty_reporter_destroy: 30382c93a87SJiri Pirko devlink_health_reporter_destroy(health->empty_reporter); 30482c93a87SJiri Pirko return err; 30582c93a87SJiri Pirko } 30682c93a87SJiri Pirko 30782c93a87SJiri Pirko void nsim_dev_health_exit(struct nsim_dev *nsim_dev) 30882c93a87SJiri Pirko { 30982c93a87SJiri Pirko struct nsim_dev_health *health = &nsim_dev->health; 31082c93a87SJiri Pirko 31182c93a87SJiri Pirko debugfs_remove_recursive(health->ddir); 31282c93a87SJiri Pirko kfree(health->recovered_break_msg); 31382c93a87SJiri Pirko devlink_health_reporter_destroy(health->dummy_reporter); 31482c93a87SJiri Pirko devlink_health_reporter_destroy(health->empty_reporter); 31582c93a87SJiri Pirko } 316