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