162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Authors: 462306a36Sopenharmony_ci * Branden Bonaby <brandonbonaby94@gmail.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/hyperv.h> 862306a36Sopenharmony_ci#include <linux/debugfs.h> 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/err.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "hyperv_vmbus.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_cistatic struct dentry *hv_debug_root; 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistatic int hv_debugfs_delay_get(void *data, u64 *val) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci *val = *(u32 *)data; 1962306a36Sopenharmony_ci return 0; 2062306a36Sopenharmony_ci} 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic int hv_debugfs_delay_set(void *data, u64 val) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci if (val > 1000) 2562306a36Sopenharmony_ci return -EINVAL; 2662306a36Sopenharmony_ci *(u32 *)data = val; 2762306a36Sopenharmony_ci return 0; 2862306a36Sopenharmony_ci} 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(hv_debugfs_delay_fops, hv_debugfs_delay_get, 3162306a36Sopenharmony_ci hv_debugfs_delay_set, "%llu\n"); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic int hv_debugfs_state_get(void *data, u64 *val) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci *val = *(bool *)data; 3662306a36Sopenharmony_ci return 0; 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic int hv_debugfs_state_set(void *data, u64 val) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci if (val == 1) 4262306a36Sopenharmony_ci *(bool *)data = true; 4362306a36Sopenharmony_ci else if (val == 0) 4462306a36Sopenharmony_ci *(bool *)data = false; 4562306a36Sopenharmony_ci else 4662306a36Sopenharmony_ci return -EINVAL; 4762306a36Sopenharmony_ci return 0; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(hv_debugfs_state_fops, hv_debugfs_state_get, 5162306a36Sopenharmony_ci hv_debugfs_state_set, "%llu\n"); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* Setup delay files to store test values */ 5462306a36Sopenharmony_cistatic int hv_debug_delay_files(struct hv_device *dev, struct dentry *root) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci struct vmbus_channel *channel = dev->channel; 5762306a36Sopenharmony_ci char *buffer = "fuzz_test_buffer_interrupt_delay"; 5862306a36Sopenharmony_ci char *message = "fuzz_test_message_delay"; 5962306a36Sopenharmony_ci int *buffer_val = &channel->fuzz_testing_interrupt_delay; 6062306a36Sopenharmony_ci int *message_val = &channel->fuzz_testing_message_delay; 6162306a36Sopenharmony_ci struct dentry *buffer_file, *message_file; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci buffer_file = debugfs_create_file(buffer, 0644, root, 6462306a36Sopenharmony_ci buffer_val, 6562306a36Sopenharmony_ci &hv_debugfs_delay_fops); 6662306a36Sopenharmony_ci if (IS_ERR(buffer_file)) { 6762306a36Sopenharmony_ci pr_debug("debugfs_hyperv: file %s not created\n", buffer); 6862306a36Sopenharmony_ci return PTR_ERR(buffer_file); 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci message_file = debugfs_create_file(message, 0644, root, 7262306a36Sopenharmony_ci message_val, 7362306a36Sopenharmony_ci &hv_debugfs_delay_fops); 7462306a36Sopenharmony_ci if (IS_ERR(message_file)) { 7562306a36Sopenharmony_ci pr_debug("debugfs_hyperv: file %s not created\n", message); 7662306a36Sopenharmony_ci return PTR_ERR(message_file); 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci return 0; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* Setup test state value for vmbus device */ 8362306a36Sopenharmony_cistatic int hv_debug_set_test_state(struct hv_device *dev, struct dentry *root) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci struct vmbus_channel *channel = dev->channel; 8662306a36Sopenharmony_ci bool *state = &channel->fuzz_testing_state; 8762306a36Sopenharmony_ci char *status = "fuzz_test_state"; 8862306a36Sopenharmony_ci struct dentry *test_state; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci test_state = debugfs_create_file(status, 0644, root, 9162306a36Sopenharmony_ci state, 9262306a36Sopenharmony_ci &hv_debugfs_state_fops); 9362306a36Sopenharmony_ci if (IS_ERR(test_state)) { 9462306a36Sopenharmony_ci pr_debug("debugfs_hyperv: file %s not created\n", status); 9562306a36Sopenharmony_ci return PTR_ERR(test_state); 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci return 0; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci/* Bind hv device to a dentry for debugfs */ 10262306a36Sopenharmony_cistatic void hv_debug_set_dir_dentry(struct hv_device *dev, struct dentry *root) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci if (hv_debug_root) 10562306a36Sopenharmony_ci dev->debug_dir = root; 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci/* Create all test dentry's and names for fuzz testing */ 10962306a36Sopenharmony_ciint hv_debug_add_dev_dir(struct hv_device *dev) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci const char *device = dev_name(&dev->device); 11262306a36Sopenharmony_ci char *delay_name = "delay"; 11362306a36Sopenharmony_ci struct dentry *delay, *dev_root; 11462306a36Sopenharmony_ci int ret; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (!IS_ERR(hv_debug_root)) { 11762306a36Sopenharmony_ci dev_root = debugfs_create_dir(device, hv_debug_root); 11862306a36Sopenharmony_ci if (IS_ERR(dev_root)) { 11962306a36Sopenharmony_ci pr_debug("debugfs_hyperv: hyperv/%s/ not created\n", 12062306a36Sopenharmony_ci device); 12162306a36Sopenharmony_ci return PTR_ERR(dev_root); 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci hv_debug_set_test_state(dev, dev_root); 12462306a36Sopenharmony_ci hv_debug_set_dir_dentry(dev, dev_root); 12562306a36Sopenharmony_ci delay = debugfs_create_dir(delay_name, dev_root); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (IS_ERR(delay)) { 12862306a36Sopenharmony_ci pr_debug("debugfs_hyperv: hyperv/%s/%s/ not created\n", 12962306a36Sopenharmony_ci device, delay_name); 13062306a36Sopenharmony_ci return PTR_ERR(delay); 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci ret = hv_debug_delay_files(dev, delay); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci return ret; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci pr_debug("debugfs_hyperv: hyperv/ not in root debugfs path\n"); 13762306a36Sopenharmony_ci return PTR_ERR(hv_debug_root); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci/* Remove dentry associated with released hv device */ 14162306a36Sopenharmony_civoid hv_debug_rm_dev_dir(struct hv_device *dev) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci if (!IS_ERR(hv_debug_root)) 14462306a36Sopenharmony_ci debugfs_remove_recursive(dev->debug_dir); 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci/* Remove all dentrys associated with vmbus testing */ 14862306a36Sopenharmony_civoid hv_debug_rm_all_dir(void) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci debugfs_remove_recursive(hv_debug_root); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/* Delay buffer/message reads on a vmbus channel */ 15462306a36Sopenharmony_civoid hv_debug_delay_test(struct vmbus_channel *channel, enum delay delay_type) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct vmbus_channel *test_channel = channel->primary_channel ? 15762306a36Sopenharmony_ci channel->primary_channel : 15862306a36Sopenharmony_ci channel; 15962306a36Sopenharmony_ci bool state = test_channel->fuzz_testing_state; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci if (state) { 16262306a36Sopenharmony_ci if (delay_type == 0) 16362306a36Sopenharmony_ci udelay(test_channel->fuzz_testing_interrupt_delay); 16462306a36Sopenharmony_ci else 16562306a36Sopenharmony_ci udelay(test_channel->fuzz_testing_message_delay); 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci/* Initialize top dentry for vmbus testing */ 17062306a36Sopenharmony_ciint hv_debug_init(void) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci hv_debug_root = debugfs_create_dir("hyperv", NULL); 17362306a36Sopenharmony_ci if (IS_ERR(hv_debug_root)) { 17462306a36Sopenharmony_ci pr_debug("debugfs_hyperv: hyperv/ not created\n"); 17562306a36Sopenharmony_ci return PTR_ERR(hv_debug_root); 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci return 0; 17862306a36Sopenharmony_ci} 179