GNU Linux-libre 5.19-rc6-gnu
[releases.git] / drivers / hv / hv_debugfs.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Authors:
4  *   Branden Bonaby <brandonbonaby94@gmail.com>
5  */
6
7 #include <linux/hyperv.h>
8 #include <linux/debugfs.h>
9 #include <linux/delay.h>
10 #include <linux/err.h>
11
12 #include "hyperv_vmbus.h"
13
14 static struct dentry *hv_debug_root;
15
16 static int hv_debugfs_delay_get(void *data, u64 *val)
17 {
18         *val = *(u32 *)data;
19         return 0;
20 }
21
22 static int hv_debugfs_delay_set(void *data, u64 val)
23 {
24         if (val > 1000)
25                 return -EINVAL;
26         *(u32 *)data = val;
27         return 0;
28 }
29
30 DEFINE_DEBUGFS_ATTRIBUTE(hv_debugfs_delay_fops, hv_debugfs_delay_get,
31                          hv_debugfs_delay_set, "%llu\n");
32
33 static int hv_debugfs_state_get(void *data, u64 *val)
34 {
35         *val = *(bool *)data;
36         return 0;
37 }
38
39 static int hv_debugfs_state_set(void *data, u64 val)
40 {
41         if (val == 1)
42                 *(bool *)data = true;
43         else if (val == 0)
44                 *(bool *)data = false;
45         else
46                 return -EINVAL;
47         return 0;
48 }
49
50 DEFINE_DEBUGFS_ATTRIBUTE(hv_debugfs_state_fops, hv_debugfs_state_get,
51                          hv_debugfs_state_set, "%llu\n");
52
53 /* Setup delay files to store test values */
54 static int hv_debug_delay_files(struct hv_device *dev, struct dentry *root)
55 {
56         struct vmbus_channel *channel = dev->channel;
57         char *buffer = "fuzz_test_buffer_interrupt_delay";
58         char *message = "fuzz_test_message_delay";
59         int *buffer_val = &channel->fuzz_testing_interrupt_delay;
60         int *message_val = &channel->fuzz_testing_message_delay;
61         struct dentry *buffer_file, *message_file;
62
63         buffer_file = debugfs_create_file(buffer, 0644, root,
64                                           buffer_val,
65                                           &hv_debugfs_delay_fops);
66         if (IS_ERR(buffer_file)) {
67                 pr_debug("debugfs_hyperv: file %s not created\n", buffer);
68                 return PTR_ERR(buffer_file);
69         }
70
71         message_file = debugfs_create_file(message, 0644, root,
72                                            message_val,
73                                            &hv_debugfs_delay_fops);
74         if (IS_ERR(message_file)) {
75                 pr_debug("debugfs_hyperv: file %s not created\n", message);
76                 return PTR_ERR(message_file);
77         }
78
79         return 0;
80 }
81
82 /* Setup test state value for vmbus device */
83 static int hv_debug_set_test_state(struct hv_device *dev, struct dentry *root)
84 {
85         struct vmbus_channel *channel = dev->channel;
86         bool *state = &channel->fuzz_testing_state;
87         char *status = "fuzz_test_state";
88         struct dentry *test_state;
89
90         test_state = debugfs_create_file(status, 0644, root,
91                                          state,
92                                          &hv_debugfs_state_fops);
93         if (IS_ERR(test_state)) {
94                 pr_debug("debugfs_hyperv: file %s not created\n", status);
95                 return PTR_ERR(test_state);
96         }
97
98         return 0;
99 }
100
101 /* Bind hv device to a dentry for debugfs */
102 static void hv_debug_set_dir_dentry(struct hv_device *dev, struct dentry *root)
103 {
104         if (hv_debug_root)
105                 dev->debug_dir = root;
106 }
107
108 /* Create all test dentry's and names for fuzz testing */
109 int hv_debug_add_dev_dir(struct hv_device *dev)
110 {
111         const char *device = dev_name(&dev->device);
112         char *delay_name = "delay";
113         struct dentry *delay, *dev_root;
114         int ret;
115
116         if (!IS_ERR(hv_debug_root)) {
117                 dev_root = debugfs_create_dir(device, hv_debug_root);
118                 if (IS_ERR(dev_root)) {
119                         pr_debug("debugfs_hyperv: hyperv/%s/ not created\n",
120                                  device);
121                         return PTR_ERR(dev_root);
122                 }
123                 hv_debug_set_test_state(dev, dev_root);
124                 hv_debug_set_dir_dentry(dev, dev_root);
125                 delay = debugfs_create_dir(delay_name, dev_root);
126
127                 if (IS_ERR(delay)) {
128                         pr_debug("debugfs_hyperv: hyperv/%s/%s/ not created\n",
129                                  device, delay_name);
130                         return PTR_ERR(delay);
131                 }
132                 ret = hv_debug_delay_files(dev, delay);
133
134                 return ret;
135         }
136         pr_debug("debugfs_hyperv: hyperv/ not in root debugfs path\n");
137         return PTR_ERR(hv_debug_root);
138 }
139
140 /* Remove dentry associated with released hv device */
141 void hv_debug_rm_dev_dir(struct hv_device *dev)
142 {
143         if (!IS_ERR(hv_debug_root))
144                 debugfs_remove_recursive(dev->debug_dir);
145 }
146
147 /* Remove all dentrys associated with vmbus testing */
148 void hv_debug_rm_all_dir(void)
149 {
150         debugfs_remove_recursive(hv_debug_root);
151 }
152
153 /* Delay buffer/message reads on a vmbus channel */
154 void hv_debug_delay_test(struct vmbus_channel *channel, enum delay delay_type)
155 {
156         struct vmbus_channel *test_channel =    channel->primary_channel ?
157                                                 channel->primary_channel :
158                                                 channel;
159         bool state = test_channel->fuzz_testing_state;
160
161         if (state) {
162                 if (delay_type == 0)
163                         udelay(test_channel->fuzz_testing_interrupt_delay);
164                 else
165                         udelay(test_channel->fuzz_testing_message_delay);
166         }
167 }
168
169 /* Initialize top dentry for vmbus testing */
170 int hv_debug_init(void)
171 {
172         hv_debug_root = debugfs_create_dir("hyperv", NULL);
173         if (IS_ERR(hv_debug_root)) {
174                 pr_debug("debugfs_hyperv: hyperv/ not created\n");
175                 return PTR_ERR(hv_debug_root);
176         }
177         return 0;
178 }