18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Authors: 48c2ecf20Sopenharmony_ci * Branden Bonaby <brandonbonaby94@gmail.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/hyperv.h> 88c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 98c2ecf20Sopenharmony_ci#include <linux/delay.h> 108c2ecf20Sopenharmony_ci#include <linux/err.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "hyperv_vmbus.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_cistatic struct dentry *hv_debug_root; 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistatic int hv_debugfs_delay_get(void *data, u64 *val) 178c2ecf20Sopenharmony_ci{ 188c2ecf20Sopenharmony_ci *val = *(u32 *)data; 198c2ecf20Sopenharmony_ci return 0; 208c2ecf20Sopenharmony_ci} 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic int hv_debugfs_delay_set(void *data, u64 val) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci if (val > 1000) 258c2ecf20Sopenharmony_ci return -EINVAL; 268c2ecf20Sopenharmony_ci *(u32 *)data = val; 278c2ecf20Sopenharmony_ci return 0; 288c2ecf20Sopenharmony_ci} 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(hv_debugfs_delay_fops, hv_debugfs_delay_get, 318c2ecf20Sopenharmony_ci hv_debugfs_delay_set, "%llu\n"); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic int hv_debugfs_state_get(void *data, u64 *val) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci *val = *(bool *)data; 368c2ecf20Sopenharmony_ci return 0; 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic int hv_debugfs_state_set(void *data, u64 val) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci if (val == 1) 428c2ecf20Sopenharmony_ci *(bool *)data = true; 438c2ecf20Sopenharmony_ci else if (val == 0) 448c2ecf20Sopenharmony_ci *(bool *)data = false; 458c2ecf20Sopenharmony_ci else 468c2ecf20Sopenharmony_ci return -EINVAL; 478c2ecf20Sopenharmony_ci return 0; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(hv_debugfs_state_fops, hv_debugfs_state_get, 518c2ecf20Sopenharmony_ci hv_debugfs_state_set, "%llu\n"); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* Setup delay files to store test values */ 548c2ecf20Sopenharmony_cistatic int hv_debug_delay_files(struct hv_device *dev, struct dentry *root) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci struct vmbus_channel *channel = dev->channel; 578c2ecf20Sopenharmony_ci char *buffer = "fuzz_test_buffer_interrupt_delay"; 588c2ecf20Sopenharmony_ci char *message = "fuzz_test_message_delay"; 598c2ecf20Sopenharmony_ci int *buffer_val = &channel->fuzz_testing_interrupt_delay; 608c2ecf20Sopenharmony_ci int *message_val = &channel->fuzz_testing_message_delay; 618c2ecf20Sopenharmony_ci struct dentry *buffer_file, *message_file; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci buffer_file = debugfs_create_file(buffer, 0644, root, 648c2ecf20Sopenharmony_ci buffer_val, 658c2ecf20Sopenharmony_ci &hv_debugfs_delay_fops); 668c2ecf20Sopenharmony_ci if (IS_ERR(buffer_file)) { 678c2ecf20Sopenharmony_ci pr_debug("debugfs_hyperv: file %s not created\n", buffer); 688c2ecf20Sopenharmony_ci return PTR_ERR(buffer_file); 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci message_file = debugfs_create_file(message, 0644, root, 728c2ecf20Sopenharmony_ci message_val, 738c2ecf20Sopenharmony_ci &hv_debugfs_delay_fops); 748c2ecf20Sopenharmony_ci if (IS_ERR(message_file)) { 758c2ecf20Sopenharmony_ci pr_debug("debugfs_hyperv: file %s not created\n", message); 768c2ecf20Sopenharmony_ci return PTR_ERR(message_file); 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci return 0; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci/* Setup test state value for vmbus device */ 838c2ecf20Sopenharmony_cistatic int hv_debug_set_test_state(struct hv_device *dev, struct dentry *root) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct vmbus_channel *channel = dev->channel; 868c2ecf20Sopenharmony_ci bool *state = &channel->fuzz_testing_state; 878c2ecf20Sopenharmony_ci char *status = "fuzz_test_state"; 888c2ecf20Sopenharmony_ci struct dentry *test_state; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci test_state = debugfs_create_file(status, 0644, root, 918c2ecf20Sopenharmony_ci state, 928c2ecf20Sopenharmony_ci &hv_debugfs_state_fops); 938c2ecf20Sopenharmony_ci if (IS_ERR(test_state)) { 948c2ecf20Sopenharmony_ci pr_debug("debugfs_hyperv: file %s not created\n", status); 958c2ecf20Sopenharmony_ci return PTR_ERR(test_state); 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci return 0; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci/* Bind hv device to a dentry for debugfs */ 1028c2ecf20Sopenharmony_cistatic void hv_debug_set_dir_dentry(struct hv_device *dev, struct dentry *root) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci if (hv_debug_root) 1058c2ecf20Sopenharmony_ci dev->debug_dir = root; 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci/* Create all test dentry's and names for fuzz testing */ 1098c2ecf20Sopenharmony_ciint hv_debug_add_dev_dir(struct hv_device *dev) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci const char *device = dev_name(&dev->device); 1128c2ecf20Sopenharmony_ci char *delay_name = "delay"; 1138c2ecf20Sopenharmony_ci struct dentry *delay, *dev_root; 1148c2ecf20Sopenharmony_ci int ret; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (!IS_ERR(hv_debug_root)) { 1178c2ecf20Sopenharmony_ci dev_root = debugfs_create_dir(device, hv_debug_root); 1188c2ecf20Sopenharmony_ci if (IS_ERR(dev_root)) { 1198c2ecf20Sopenharmony_ci pr_debug("debugfs_hyperv: hyperv/%s/ not created\n", 1208c2ecf20Sopenharmony_ci device); 1218c2ecf20Sopenharmony_ci return PTR_ERR(dev_root); 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci hv_debug_set_test_state(dev, dev_root); 1248c2ecf20Sopenharmony_ci hv_debug_set_dir_dentry(dev, dev_root); 1258c2ecf20Sopenharmony_ci delay = debugfs_create_dir(delay_name, dev_root); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (IS_ERR(delay)) { 1288c2ecf20Sopenharmony_ci pr_debug("debugfs_hyperv: hyperv/%s/%s/ not created\n", 1298c2ecf20Sopenharmony_ci device, delay_name); 1308c2ecf20Sopenharmony_ci return PTR_ERR(delay); 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci ret = hv_debug_delay_files(dev, delay); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci return ret; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci pr_debug("debugfs_hyperv: hyperv/ not in root debugfs path\n"); 1378c2ecf20Sopenharmony_ci return PTR_ERR(hv_debug_root); 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci/* Remove dentry associated with released hv device */ 1418c2ecf20Sopenharmony_civoid hv_debug_rm_dev_dir(struct hv_device *dev) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci if (!IS_ERR(hv_debug_root)) 1448c2ecf20Sopenharmony_ci debugfs_remove_recursive(dev->debug_dir); 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci/* Remove all dentrys associated with vmbus testing */ 1488c2ecf20Sopenharmony_civoid hv_debug_rm_all_dir(void) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci debugfs_remove_recursive(hv_debug_root); 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci/* Delay buffer/message reads on a vmbus channel */ 1548c2ecf20Sopenharmony_civoid hv_debug_delay_test(struct vmbus_channel *channel, enum delay delay_type) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci struct vmbus_channel *test_channel = channel->primary_channel ? 1578c2ecf20Sopenharmony_ci channel->primary_channel : 1588c2ecf20Sopenharmony_ci channel; 1598c2ecf20Sopenharmony_ci bool state = test_channel->fuzz_testing_state; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci if (state) { 1628c2ecf20Sopenharmony_ci if (delay_type == 0) 1638c2ecf20Sopenharmony_ci udelay(test_channel->fuzz_testing_interrupt_delay); 1648c2ecf20Sopenharmony_ci else 1658c2ecf20Sopenharmony_ci udelay(test_channel->fuzz_testing_message_delay); 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci/* Initialize top dentry for vmbus testing */ 1708c2ecf20Sopenharmony_ciint hv_debug_init(void) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci hv_debug_root = debugfs_create_dir("hyperv", NULL); 1738c2ecf20Sopenharmony_ci if (IS_ERR(hv_debug_root)) { 1748c2ecf20Sopenharmony_ci pr_debug("debugfs_hyperv: hyperv/ not created\n"); 1758c2ecf20Sopenharmony_ci return PTR_ERR(hv_debug_root); 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci return 0; 1788c2ecf20Sopenharmony_ci} 179