Commit af9ca6f9 authored by Branden Bonaby's avatar Branden Bonaby Committed by Sasha Levin
Browse files

drivers: hv: vmbus: Introduce latency testing



Introduce user specified latency in the packet reception path
By exposing the test parameters as part of the debugfs channel
attributes. We will control the testing state via these attributes.

Signed-off-by: default avatarBranden Bonaby <brandonbonaby94@gmail.com>
Reviewed-by: default avatarMichael Kelley <mikelley@microsoft.com>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent d21987d7
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
What:           /sys/kernel/debug/hyperv/<UUID>/fuzz_test_state
Date:           October 2019
KernelVersion:  5.5
Contact:        Branden Bonaby <brandonbonaby94@gmail.com>
Description:    Fuzz testing status of a vmbus device, whether its in an ON
                state or a OFF state
Users:          Debugging tools

What:           /sys/kernel/debug/hyperv/<UUID>/delay/fuzz_test_buffer_interrupt_delay
Date:           October 2019
KernelVersion:  5.5
Contact:        Branden Bonaby <brandonbonaby94@gmail.com>
Description:    Fuzz testing buffer interrupt delay value between 0 - 1000
		 microseconds (inclusive).
Users:          Debugging tools

What:           /sys/kernel/debug/hyperv/<UUID>/delay/fuzz_test_message_delay
Date:           October 2019
KernelVersion:  5.5
Contact:        Branden Bonaby <brandonbonaby94@gmail.com>
Description:    Fuzz testing message delay value between 0 - 1000 microseconds
		 (inclusive).
Users:          Debugging tools
+1 −0
Original line number Diff line number Diff line
@@ -7578,6 +7578,7 @@ F: include/uapi/linux/hyperv.h
F:	include/asm-generic/mshyperv.h
F:	tools/hv/
F:	Documentation/ABI/stable/sysfs-bus-vmbus
F:	Documentation/ABI/testing/debugfs-hyperv
HYPERBUS SUPPORT
M:	Vignesh Raghavendra <vigneshr@ti.com>
+1 −0
Original line number Diff line number Diff line
@@ -9,4 +9,5 @@ CFLAGS_hv_balloon.o = -I$(src)
hv_vmbus-y := vmbus_drv.o \
		 hv.o connection.o channel.o \
		 channel_mgmt.o ring_buffer.o hv_trace.o
hv_vmbus-$(CONFIG_HYPERV_TESTING)	+= hv_debugfs.o
hv_utils-y := hv_util.o hv_kvp.o hv_snapshot.o hv_fcopy.o hv_utils_transport.o
+1 −0
Original line number Diff line number Diff line
@@ -363,6 +363,7 @@ void vmbus_on_event(unsigned long data)

	trace_vmbus_on_event(channel);

	hv_debug_delay_test(channel, INTERRUPT_DELAY);
	do {
		void (*callback_fn)(void *);

+178 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Authors:
 *   Branden Bonaby <brandonbonaby94@gmail.com>
 */

#include <linux/hyperv.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/err.h>

#include "hyperv_vmbus.h"

struct dentry *hv_debug_root;

static int hv_debugfs_delay_get(void *data, u64 *val)
{
	*val = *(u32 *)data;
	return 0;
}

static int hv_debugfs_delay_set(void *data, u64 val)
{
	if (val > 1000)
		return -EINVAL;
	*(u32 *)data = val;
	return 0;
}

DEFINE_DEBUGFS_ATTRIBUTE(hv_debugfs_delay_fops, hv_debugfs_delay_get,
			 hv_debugfs_delay_set, "%llu\n");

static int hv_debugfs_state_get(void *data, u64 *val)
{
	*val = *(bool *)data;
	return 0;
}

static int hv_debugfs_state_set(void *data, u64 val)
{
	if (val == 1)
		*(bool *)data = true;
	else if (val == 0)
		*(bool *)data = false;
	else
		return -EINVAL;
	return 0;
}

DEFINE_DEBUGFS_ATTRIBUTE(hv_debugfs_state_fops, hv_debugfs_state_get,
			 hv_debugfs_state_set, "%llu\n");

/* Setup delay files to store test values */
static int hv_debug_delay_files(struct hv_device *dev, struct dentry *root)
{
	struct vmbus_channel *channel = dev->channel;
	char *buffer = "fuzz_test_buffer_interrupt_delay";
	char *message = "fuzz_test_message_delay";
	int *buffer_val = &channel->fuzz_testing_interrupt_delay;
	int *message_val = &channel->fuzz_testing_message_delay;
	struct dentry *buffer_file, *message_file;

	buffer_file = debugfs_create_file(buffer, 0644, root,
					  buffer_val,
					  &hv_debugfs_delay_fops);
	if (IS_ERR(buffer_file)) {
		pr_debug("debugfs_hyperv: file %s not created\n", buffer);
		return PTR_ERR(buffer_file);
	}

	message_file = debugfs_create_file(message, 0644, root,
					   message_val,
					   &hv_debugfs_delay_fops);
	if (IS_ERR(message_file)) {
		pr_debug("debugfs_hyperv: file %s not created\n", message);
		return PTR_ERR(message_file);
	}

	return 0;
}

/* Setup test state value for vmbus device */
static int hv_debug_set_test_state(struct hv_device *dev, struct dentry *root)
{
	struct vmbus_channel *channel = dev->channel;
	bool *state = &channel->fuzz_testing_state;
	char *status = "fuzz_test_state";
	struct dentry *test_state;

	test_state = debugfs_create_file(status, 0644, root,
					 state,
					 &hv_debugfs_state_fops);
	if (IS_ERR(test_state)) {
		pr_debug("debugfs_hyperv: file %s not created\n", status);
		return PTR_ERR(test_state);
	}

	return 0;
}

/* Bind hv device to a dentry for debugfs */
static void hv_debug_set_dir_dentry(struct hv_device *dev, struct dentry *root)
{
	if (hv_debug_root)
		dev->debug_dir = root;
}

/* Create all test dentry's and names for fuzz testing */
int hv_debug_add_dev_dir(struct hv_device *dev)
{
	const char *device = dev_name(&dev->device);
	char *delay_name = "delay";
	struct dentry *delay, *dev_root;
	int ret;

	if (!IS_ERR(hv_debug_root)) {
		dev_root = debugfs_create_dir(device, hv_debug_root);
		if (IS_ERR(dev_root)) {
			pr_debug("debugfs_hyperv: hyperv/%s/ not created\n",
				 device);
			return PTR_ERR(dev_root);
		}
		hv_debug_set_test_state(dev, dev_root);
		hv_debug_set_dir_dentry(dev, dev_root);
		delay = debugfs_create_dir(delay_name, dev_root);

		if (IS_ERR(delay)) {
			pr_debug("debugfs_hyperv: hyperv/%s/%s/ not created\n",
				 device, delay_name);
			return PTR_ERR(delay);
		}
		ret = hv_debug_delay_files(dev, delay);

		return ret;
	}
	pr_debug("debugfs_hyperv: hyperv/ not in root debugfs path\n");
	return PTR_ERR(hv_debug_root);
}

/* Remove dentry associated with released hv device */
void hv_debug_rm_dev_dir(struct hv_device *dev)
{
	if (!IS_ERR(hv_debug_root))
		debugfs_remove_recursive(dev->debug_dir);
}

/* Remove all dentrys associated with vmbus testing */
void hv_debug_rm_all_dir(void)
{
	debugfs_remove_recursive(hv_debug_root);
}

/* Delay buffer/message reads on a vmbus channel */
void hv_debug_delay_test(struct vmbus_channel *channel, enum delay delay_type)
{
	struct vmbus_channel *test_channel =    channel->primary_channel ?
						channel->primary_channel :
						channel;
	bool state = test_channel->fuzz_testing_state;

	if (state) {
		if (delay_type == 0)
			udelay(test_channel->fuzz_testing_interrupt_delay);
		else
			udelay(test_channel->fuzz_testing_message_delay);
	}
}

/* Initialize top dentry for vmbus testing */
int hv_debug_init(void)
{
	hv_debug_root = debugfs_create_dir("hyperv", NULL);
	if (IS_ERR(hv_debug_root)) {
		pr_debug("debugfs_hyperv: hyperv/ not created\n");
		return PTR_ERR(hv_debug_root);
	}
	return 0;
}
Loading