18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright (c) 2019 Mellanox Technologies. All rights reserved */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 58c2ecf20Sopenharmony_ci#include <linux/err.h> 68c2ecf20Sopenharmony_ci#include <linux/kernel.h> 78c2ecf20Sopenharmony_ci#include <linux/slab.h> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include "netdevsim.h" 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_cistatic int 128c2ecf20Sopenharmony_cinsim_dev_empty_reporter_dump(struct devlink_health_reporter *reporter, 138c2ecf20Sopenharmony_ci struct devlink_fmsg *fmsg, void *priv_ctx, 148c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 158c2ecf20Sopenharmony_ci{ 168c2ecf20Sopenharmony_ci return 0; 178c2ecf20Sopenharmony_ci} 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic int 208c2ecf20Sopenharmony_cinsim_dev_empty_reporter_diagnose(struct devlink_health_reporter *reporter, 218c2ecf20Sopenharmony_ci struct devlink_fmsg *fmsg, 228c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci return 0; 258c2ecf20Sopenharmony_ci} 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic const 288c2ecf20Sopenharmony_cistruct devlink_health_reporter_ops nsim_dev_empty_reporter_ops = { 298c2ecf20Sopenharmony_ci .name = "empty", 308c2ecf20Sopenharmony_ci .dump = nsim_dev_empty_reporter_dump, 318c2ecf20Sopenharmony_ci .diagnose = nsim_dev_empty_reporter_diagnose, 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistruct nsim_dev_dummy_reporter_ctx { 358c2ecf20Sopenharmony_ci char *break_msg; 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic int 398c2ecf20Sopenharmony_cinsim_dev_dummy_reporter_recover(struct devlink_health_reporter *reporter, 408c2ecf20Sopenharmony_ci void *priv_ctx, 418c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci struct nsim_dev_health *health = devlink_health_reporter_priv(reporter); 448c2ecf20Sopenharmony_ci struct nsim_dev_dummy_reporter_ctx *ctx = priv_ctx; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci if (health->fail_recover) { 478c2ecf20Sopenharmony_ci /* For testing purposes, user set debugfs fail_recover 488c2ecf20Sopenharmony_ci * value to true. Fail right away. 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "User setup the recover to fail for testing purposes"); 518c2ecf20Sopenharmony_ci return -EINVAL; 528c2ecf20Sopenharmony_ci } 538c2ecf20Sopenharmony_ci if (ctx) { 548c2ecf20Sopenharmony_ci kfree(health->recovered_break_msg); 558c2ecf20Sopenharmony_ci health->recovered_break_msg = kstrdup(ctx->break_msg, 568c2ecf20Sopenharmony_ci GFP_KERNEL); 578c2ecf20Sopenharmony_ci if (!health->recovered_break_msg) 588c2ecf20Sopenharmony_ci return -ENOMEM; 598c2ecf20Sopenharmony_ci } 608c2ecf20Sopenharmony_ci return 0; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic int nsim_dev_dummy_fmsg_put(struct devlink_fmsg *fmsg, u32 binary_len) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci char *binary; 668c2ecf20Sopenharmony_ci int err; 678c2ecf20Sopenharmony_ci int i; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci err = devlink_fmsg_bool_pair_put(fmsg, "test_bool", true); 708c2ecf20Sopenharmony_ci if (err) 718c2ecf20Sopenharmony_ci return err; 728c2ecf20Sopenharmony_ci err = devlink_fmsg_u8_pair_put(fmsg, "test_u8", 1); 738c2ecf20Sopenharmony_ci if (err) 748c2ecf20Sopenharmony_ci return err; 758c2ecf20Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "test_u32", 3); 768c2ecf20Sopenharmony_ci if (err) 778c2ecf20Sopenharmony_ci return err; 788c2ecf20Sopenharmony_ci err = devlink_fmsg_u64_pair_put(fmsg, "test_u64", 4); 798c2ecf20Sopenharmony_ci if (err) 808c2ecf20Sopenharmony_ci return err; 818c2ecf20Sopenharmony_ci err = devlink_fmsg_string_pair_put(fmsg, "test_string", "somestring"); 828c2ecf20Sopenharmony_ci if (err) 838c2ecf20Sopenharmony_ci return err; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci binary = kmalloc(binary_len, GFP_KERNEL | __GFP_NOWARN); 868c2ecf20Sopenharmony_ci if (!binary) 878c2ecf20Sopenharmony_ci return -ENOMEM; 888c2ecf20Sopenharmony_ci get_random_bytes(binary, binary_len); 898c2ecf20Sopenharmony_ci err = devlink_fmsg_binary_pair_put(fmsg, "test_binary", binary, binary_len); 908c2ecf20Sopenharmony_ci kfree(binary); 918c2ecf20Sopenharmony_ci if (err) 928c2ecf20Sopenharmony_ci return err; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci err = devlink_fmsg_pair_nest_start(fmsg, "test_nest"); 958c2ecf20Sopenharmony_ci if (err) 968c2ecf20Sopenharmony_ci return err; 978c2ecf20Sopenharmony_ci err = devlink_fmsg_obj_nest_start(fmsg); 988c2ecf20Sopenharmony_ci if (err) 998c2ecf20Sopenharmony_ci return err; 1008c2ecf20Sopenharmony_ci err = devlink_fmsg_bool_pair_put(fmsg, "nested_test_bool", false); 1018c2ecf20Sopenharmony_ci if (err) 1028c2ecf20Sopenharmony_ci return err; 1038c2ecf20Sopenharmony_ci err = devlink_fmsg_u8_pair_put(fmsg, "nested_test_u8", false); 1048c2ecf20Sopenharmony_ci if (err) 1058c2ecf20Sopenharmony_ci return err; 1068c2ecf20Sopenharmony_ci err = devlink_fmsg_obj_nest_end(fmsg); 1078c2ecf20Sopenharmony_ci if (err) 1088c2ecf20Sopenharmony_ci return err; 1098c2ecf20Sopenharmony_ci err = devlink_fmsg_pair_nest_end(fmsg); 1108c2ecf20Sopenharmony_ci if (err) 1118c2ecf20Sopenharmony_ci return err; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_bool_array"); 1148c2ecf20Sopenharmony_ci if (err) 1158c2ecf20Sopenharmony_ci return err; 1168c2ecf20Sopenharmony_ci for (i = 0; i < 10; i++) { 1178c2ecf20Sopenharmony_ci err = devlink_fmsg_bool_put(fmsg, true); 1188c2ecf20Sopenharmony_ci if (err) 1198c2ecf20Sopenharmony_ci return err; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci err = devlink_fmsg_arr_pair_nest_end(fmsg); 1228c2ecf20Sopenharmony_ci if (err) 1238c2ecf20Sopenharmony_ci return err; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_u8_array"); 1268c2ecf20Sopenharmony_ci if (err) 1278c2ecf20Sopenharmony_ci return err; 1288c2ecf20Sopenharmony_ci for (i = 0; i < 10; i++) { 1298c2ecf20Sopenharmony_ci err = devlink_fmsg_u8_put(fmsg, i); 1308c2ecf20Sopenharmony_ci if (err) 1318c2ecf20Sopenharmony_ci return err; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci err = devlink_fmsg_arr_pair_nest_end(fmsg); 1348c2ecf20Sopenharmony_ci if (err) 1358c2ecf20Sopenharmony_ci return err; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_u32_array"); 1388c2ecf20Sopenharmony_ci if (err) 1398c2ecf20Sopenharmony_ci return err; 1408c2ecf20Sopenharmony_ci for (i = 0; i < 10; i++) { 1418c2ecf20Sopenharmony_ci err = devlink_fmsg_u32_put(fmsg, i); 1428c2ecf20Sopenharmony_ci if (err) 1438c2ecf20Sopenharmony_ci return err; 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci err = devlink_fmsg_arr_pair_nest_end(fmsg); 1468c2ecf20Sopenharmony_ci if (err) 1478c2ecf20Sopenharmony_ci return err; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_u64_array"); 1508c2ecf20Sopenharmony_ci if (err) 1518c2ecf20Sopenharmony_ci return err; 1528c2ecf20Sopenharmony_ci for (i = 0; i < 10; i++) { 1538c2ecf20Sopenharmony_ci err = devlink_fmsg_u64_put(fmsg, i); 1548c2ecf20Sopenharmony_ci if (err) 1558c2ecf20Sopenharmony_ci return err; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci err = devlink_fmsg_arr_pair_nest_end(fmsg); 1588c2ecf20Sopenharmony_ci if (err) 1598c2ecf20Sopenharmony_ci return err; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_array_of_objects"); 1628c2ecf20Sopenharmony_ci if (err) 1638c2ecf20Sopenharmony_ci return err; 1648c2ecf20Sopenharmony_ci for (i = 0; i < 10; i++) { 1658c2ecf20Sopenharmony_ci err = devlink_fmsg_obj_nest_start(fmsg); 1668c2ecf20Sopenharmony_ci if (err) 1678c2ecf20Sopenharmony_ci return err; 1688c2ecf20Sopenharmony_ci err = devlink_fmsg_bool_pair_put(fmsg, 1698c2ecf20Sopenharmony_ci "in_array_nested_test_bool", 1708c2ecf20Sopenharmony_ci false); 1718c2ecf20Sopenharmony_ci if (err) 1728c2ecf20Sopenharmony_ci return err; 1738c2ecf20Sopenharmony_ci err = devlink_fmsg_u8_pair_put(fmsg, 1748c2ecf20Sopenharmony_ci "in_array_nested_test_u8", 1758c2ecf20Sopenharmony_ci i); 1768c2ecf20Sopenharmony_ci if (err) 1778c2ecf20Sopenharmony_ci return err; 1788c2ecf20Sopenharmony_ci err = devlink_fmsg_obj_nest_end(fmsg); 1798c2ecf20Sopenharmony_ci if (err) 1808c2ecf20Sopenharmony_ci return err; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci return devlink_fmsg_arr_pair_nest_end(fmsg); 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int 1868c2ecf20Sopenharmony_cinsim_dev_dummy_reporter_dump(struct devlink_health_reporter *reporter, 1878c2ecf20Sopenharmony_ci struct devlink_fmsg *fmsg, void *priv_ctx, 1888c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci struct nsim_dev_health *health = devlink_health_reporter_priv(reporter); 1918c2ecf20Sopenharmony_ci struct nsim_dev_dummy_reporter_ctx *ctx = priv_ctx; 1928c2ecf20Sopenharmony_ci int err; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (ctx) { 1958c2ecf20Sopenharmony_ci err = devlink_fmsg_string_pair_put(fmsg, "break_message", 1968c2ecf20Sopenharmony_ci ctx->break_msg); 1978c2ecf20Sopenharmony_ci if (err) 1988c2ecf20Sopenharmony_ci return err; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci return nsim_dev_dummy_fmsg_put(fmsg, health->binary_len); 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic int 2048c2ecf20Sopenharmony_cinsim_dev_dummy_reporter_diagnose(struct devlink_health_reporter *reporter, 2058c2ecf20Sopenharmony_ci struct devlink_fmsg *fmsg, 2068c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci struct nsim_dev_health *health = devlink_health_reporter_priv(reporter); 2098c2ecf20Sopenharmony_ci int err; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci if (health->recovered_break_msg) { 2128c2ecf20Sopenharmony_ci err = devlink_fmsg_string_pair_put(fmsg, 2138c2ecf20Sopenharmony_ci "recovered_break_message", 2148c2ecf20Sopenharmony_ci health->recovered_break_msg); 2158c2ecf20Sopenharmony_ci if (err) 2168c2ecf20Sopenharmony_ci return err; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci return nsim_dev_dummy_fmsg_put(fmsg, health->binary_len); 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic const 2228c2ecf20Sopenharmony_cistruct devlink_health_reporter_ops nsim_dev_dummy_reporter_ops = { 2238c2ecf20Sopenharmony_ci .name = "dummy", 2248c2ecf20Sopenharmony_ci .recover = nsim_dev_dummy_reporter_recover, 2258c2ecf20Sopenharmony_ci .dump = nsim_dev_dummy_reporter_dump, 2268c2ecf20Sopenharmony_ci .diagnose = nsim_dev_dummy_reporter_diagnose, 2278c2ecf20Sopenharmony_ci}; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic ssize_t nsim_dev_health_break_write(struct file *file, 2308c2ecf20Sopenharmony_ci const char __user *data, 2318c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci struct nsim_dev_health *health = file->private_data; 2348c2ecf20Sopenharmony_ci struct nsim_dev_dummy_reporter_ctx ctx; 2358c2ecf20Sopenharmony_ci char *break_msg; 2368c2ecf20Sopenharmony_ci int err; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci break_msg = kmalloc(count + 1, GFP_KERNEL); 2398c2ecf20Sopenharmony_ci if (!break_msg) 2408c2ecf20Sopenharmony_ci return -ENOMEM; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (copy_from_user(break_msg, data, count)) { 2438c2ecf20Sopenharmony_ci err = -EFAULT; 2448c2ecf20Sopenharmony_ci goto out; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci break_msg[count] = '\0'; 2478c2ecf20Sopenharmony_ci if (break_msg[count - 1] == '\n') 2488c2ecf20Sopenharmony_ci break_msg[count - 1] = '\0'; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci ctx.break_msg = break_msg; 2518c2ecf20Sopenharmony_ci err = devlink_health_report(health->dummy_reporter, break_msg, &ctx); 2528c2ecf20Sopenharmony_ci if (err) 2538c2ecf20Sopenharmony_ci goto out; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ciout: 2568c2ecf20Sopenharmony_ci kfree(break_msg); 2578c2ecf20Sopenharmony_ci return err ?: count; 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic const struct file_operations nsim_dev_health_break_fops = { 2618c2ecf20Sopenharmony_ci .open = simple_open, 2628c2ecf20Sopenharmony_ci .write = nsim_dev_health_break_write, 2638c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 2648c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2658c2ecf20Sopenharmony_ci}; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ciint nsim_dev_health_init(struct nsim_dev *nsim_dev, struct devlink *devlink) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci struct nsim_dev_health *health = &nsim_dev->health; 2708c2ecf20Sopenharmony_ci int err; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci health->empty_reporter = 2738c2ecf20Sopenharmony_ci devlink_health_reporter_create(devlink, 2748c2ecf20Sopenharmony_ci &nsim_dev_empty_reporter_ops, 2758c2ecf20Sopenharmony_ci 0, health); 2768c2ecf20Sopenharmony_ci if (IS_ERR(health->empty_reporter)) 2778c2ecf20Sopenharmony_ci return PTR_ERR(health->empty_reporter); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci health->dummy_reporter = 2808c2ecf20Sopenharmony_ci devlink_health_reporter_create(devlink, 2818c2ecf20Sopenharmony_ci &nsim_dev_dummy_reporter_ops, 2828c2ecf20Sopenharmony_ci 0, health); 2838c2ecf20Sopenharmony_ci if (IS_ERR(health->dummy_reporter)) { 2848c2ecf20Sopenharmony_ci err = PTR_ERR(health->dummy_reporter); 2858c2ecf20Sopenharmony_ci goto err_empty_reporter_destroy; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci health->ddir = debugfs_create_dir("health", nsim_dev->ddir); 2898c2ecf20Sopenharmony_ci if (IS_ERR(health->ddir)) { 2908c2ecf20Sopenharmony_ci err = PTR_ERR(health->ddir); 2918c2ecf20Sopenharmony_ci goto err_dummy_reporter_destroy; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci health->recovered_break_msg = NULL; 2958c2ecf20Sopenharmony_ci debugfs_create_file("break_health", 0200, health->ddir, health, 2968c2ecf20Sopenharmony_ci &nsim_dev_health_break_fops); 2978c2ecf20Sopenharmony_ci health->binary_len = 16; 2988c2ecf20Sopenharmony_ci debugfs_create_u32("binary_len", 0600, health->ddir, 2998c2ecf20Sopenharmony_ci &health->binary_len); 3008c2ecf20Sopenharmony_ci health->fail_recover = false; 3018c2ecf20Sopenharmony_ci debugfs_create_bool("fail_recover", 0600, health->ddir, 3028c2ecf20Sopenharmony_ci &health->fail_recover); 3038c2ecf20Sopenharmony_ci return 0; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cierr_dummy_reporter_destroy: 3068c2ecf20Sopenharmony_ci devlink_health_reporter_destroy(health->dummy_reporter); 3078c2ecf20Sopenharmony_cierr_empty_reporter_destroy: 3088c2ecf20Sopenharmony_ci devlink_health_reporter_destroy(health->empty_reporter); 3098c2ecf20Sopenharmony_ci return err; 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_civoid nsim_dev_health_exit(struct nsim_dev *nsim_dev) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci struct nsim_dev_health *health = &nsim_dev->health; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci debugfs_remove_recursive(health->ddir); 3178c2ecf20Sopenharmony_ci kfree(health->recovered_break_msg); 3188c2ecf20Sopenharmony_ci devlink_health_reporter_destroy(health->dummy_reporter); 3198c2ecf20Sopenharmony_ci devlink_health_reporter_destroy(health->empty_reporter); 3208c2ecf20Sopenharmony_ci} 321