xref: /kernel/linux/linux-6.6/drivers/hv/hv_debugfs.c (revision 62306a36)
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