1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright(c) 1999 - 2018 Intel Corporation. */ |
3 | |
4 | #include <linux/debugfs.h> |
5 | #include <linux/module.h> |
6 | |
7 | #include "ixgbe.h" |
8 | |
9 | static struct dentry *ixgbe_dbg_root; |
10 | |
11 | static char ixgbe_dbg_reg_ops_buf[256] = "" ; |
12 | |
13 | static ssize_t ixgbe_dbg_common_ops_read(struct file *filp, char __user *buffer, |
14 | size_t count, loff_t *ppos, |
15 | char *dbg_buf) |
16 | { |
17 | struct ixgbe_adapter *adapter = filp->private_data; |
18 | char *buf; |
19 | int len; |
20 | |
21 | /* don't allow partial reads */ |
22 | if (*ppos != 0) |
23 | return 0; |
24 | |
25 | buf = kasprintf(GFP_KERNEL, fmt: "%s: %s\n" , |
26 | adapter->netdev->name, dbg_buf); |
27 | if (!buf) |
28 | return -ENOMEM; |
29 | |
30 | if (count < strlen(buf)) { |
31 | kfree(objp: buf); |
32 | return -ENOSPC; |
33 | } |
34 | |
35 | len = simple_read_from_buffer(to: buffer, count, ppos, from: buf, strlen(buf)); |
36 | |
37 | kfree(objp: buf); |
38 | return len; |
39 | } |
40 | |
41 | /** |
42 | * ixgbe_dbg_reg_ops_read - read for reg_ops datum |
43 | * @filp: the opened file |
44 | * @buffer: where to write the data for the user to read |
45 | * @count: the size of the user's buffer |
46 | * @ppos: file position offset |
47 | **/ |
48 | static ssize_t ixgbe_dbg_reg_ops_read(struct file *filp, char __user *buffer, |
49 | size_t count, loff_t *ppos) |
50 | { |
51 | return ixgbe_dbg_common_ops_read(filp, buffer, count, ppos, |
52 | dbg_buf: ixgbe_dbg_reg_ops_buf); |
53 | } |
54 | |
55 | /** |
56 | * ixgbe_dbg_reg_ops_write - write into reg_ops datum |
57 | * @filp: the opened file |
58 | * @buffer: where to find the user's data |
59 | * @count: the length of the user's data |
60 | * @ppos: file position offset |
61 | **/ |
62 | static ssize_t ixgbe_dbg_reg_ops_write(struct file *filp, |
63 | const char __user *buffer, |
64 | size_t count, loff_t *ppos) |
65 | { |
66 | struct ixgbe_adapter *adapter = filp->private_data; |
67 | int len; |
68 | |
69 | /* don't allow partial writes */ |
70 | if (*ppos != 0) |
71 | return 0; |
72 | if (count >= sizeof(ixgbe_dbg_reg_ops_buf)) |
73 | return -ENOSPC; |
74 | |
75 | len = simple_write_to_buffer(to: ixgbe_dbg_reg_ops_buf, |
76 | available: sizeof(ixgbe_dbg_reg_ops_buf)-1, |
77 | ppos, |
78 | from: buffer, |
79 | count); |
80 | if (len < 0) |
81 | return len; |
82 | |
83 | ixgbe_dbg_reg_ops_buf[len] = '\0'; |
84 | |
85 | if (strncmp(ixgbe_dbg_reg_ops_buf, "write" , 5) == 0) { |
86 | u32 reg, value; |
87 | int cnt; |
88 | cnt = sscanf(&ixgbe_dbg_reg_ops_buf[5], "%x %x" , ®, &value); |
89 | if (cnt == 2) { |
90 | IXGBE_WRITE_REG(&adapter->hw, reg, value); |
91 | value = IXGBE_READ_REG(&adapter->hw, reg); |
92 | e_dev_info("write: 0x%08x = 0x%08x\n" , reg, value); |
93 | } else { |
94 | e_dev_info("write <reg> <value>\n" ); |
95 | } |
96 | } else if (strncmp(ixgbe_dbg_reg_ops_buf, "read" , 4) == 0) { |
97 | u32 reg, value; |
98 | int cnt; |
99 | cnt = sscanf(&ixgbe_dbg_reg_ops_buf[4], "%x" , ®); |
100 | if (cnt == 1) { |
101 | value = IXGBE_READ_REG(&adapter->hw, reg); |
102 | e_dev_info("read 0x%08x = 0x%08x\n" , reg, value); |
103 | } else { |
104 | e_dev_info("read <reg>\n" ); |
105 | } |
106 | } else { |
107 | e_dev_info("Unknown command %s\n" , ixgbe_dbg_reg_ops_buf); |
108 | e_dev_info("Available commands:\n" ); |
109 | e_dev_info(" read <reg>\n" ); |
110 | e_dev_info(" write <reg> <value>\n" ); |
111 | } |
112 | return count; |
113 | } |
114 | |
115 | static const struct file_operations ixgbe_dbg_reg_ops_fops = { |
116 | .owner = THIS_MODULE, |
117 | .open = simple_open, |
118 | .read = ixgbe_dbg_reg_ops_read, |
119 | .write = ixgbe_dbg_reg_ops_write, |
120 | }; |
121 | |
122 | static char ixgbe_dbg_netdev_ops_buf[256] = "" ; |
123 | |
124 | /** |
125 | * ixgbe_dbg_netdev_ops_read - read for netdev_ops datum |
126 | * @filp: the opened file |
127 | * @buffer: where to write the data for the user to read |
128 | * @count: the size of the user's buffer |
129 | * @ppos: file position offset |
130 | **/ |
131 | static ssize_t ixgbe_dbg_netdev_ops_read(struct file *filp, char __user *buffer, |
132 | size_t count, loff_t *ppos) |
133 | { |
134 | return ixgbe_dbg_common_ops_read(filp, buffer, count, ppos, |
135 | dbg_buf: ixgbe_dbg_netdev_ops_buf); |
136 | } |
137 | |
138 | /** |
139 | * ixgbe_dbg_netdev_ops_write - write into netdev_ops datum |
140 | * @filp: the opened file |
141 | * @buffer: where to find the user's data |
142 | * @count: the length of the user's data |
143 | * @ppos: file position offset |
144 | **/ |
145 | static ssize_t ixgbe_dbg_netdev_ops_write(struct file *filp, |
146 | const char __user *buffer, |
147 | size_t count, loff_t *ppos) |
148 | { |
149 | struct ixgbe_adapter *adapter = filp->private_data; |
150 | int len; |
151 | |
152 | /* don't allow partial writes */ |
153 | if (*ppos != 0) |
154 | return 0; |
155 | if (count >= sizeof(ixgbe_dbg_netdev_ops_buf)) |
156 | return -ENOSPC; |
157 | |
158 | len = simple_write_to_buffer(to: ixgbe_dbg_netdev_ops_buf, |
159 | available: sizeof(ixgbe_dbg_netdev_ops_buf)-1, |
160 | ppos, |
161 | from: buffer, |
162 | count); |
163 | if (len < 0) |
164 | return len; |
165 | |
166 | ixgbe_dbg_netdev_ops_buf[len] = '\0'; |
167 | |
168 | if (strncmp(ixgbe_dbg_netdev_ops_buf, "tx_timeout" , 10) == 0) { |
169 | /* TX Queue number below is wrong, but ixgbe does not use it */ |
170 | adapter->netdev->netdev_ops->ndo_tx_timeout(adapter->netdev, |
171 | UINT_MAX); |
172 | e_dev_info("tx_timeout called\n" ); |
173 | } else { |
174 | e_dev_info("Unknown command: %s\n" , ixgbe_dbg_netdev_ops_buf); |
175 | e_dev_info("Available commands:\n" ); |
176 | e_dev_info(" tx_timeout\n" ); |
177 | } |
178 | return count; |
179 | } |
180 | |
181 | static const struct file_operations ixgbe_dbg_netdev_ops_fops = { |
182 | .owner = THIS_MODULE, |
183 | .open = simple_open, |
184 | .read = ixgbe_dbg_netdev_ops_read, |
185 | .write = ixgbe_dbg_netdev_ops_write, |
186 | }; |
187 | |
188 | /** |
189 | * ixgbe_dbg_adapter_init - setup the debugfs directory for the adapter |
190 | * @adapter: the adapter that is starting up |
191 | **/ |
192 | void ixgbe_dbg_adapter_init(struct ixgbe_adapter *adapter) |
193 | { |
194 | const char *name = pci_name(pdev: adapter->pdev); |
195 | |
196 | adapter->ixgbe_dbg_adapter = debugfs_create_dir(name, parent: ixgbe_dbg_root); |
197 | debugfs_create_file(name: "reg_ops" , mode: 0600, parent: adapter->ixgbe_dbg_adapter, |
198 | data: adapter, fops: &ixgbe_dbg_reg_ops_fops); |
199 | debugfs_create_file(name: "netdev_ops" , mode: 0600, parent: adapter->ixgbe_dbg_adapter, |
200 | data: adapter, fops: &ixgbe_dbg_netdev_ops_fops); |
201 | } |
202 | |
203 | /** |
204 | * ixgbe_dbg_adapter_exit - clear out the adapter's debugfs entries |
205 | * @adapter: the adapter that is exiting |
206 | **/ |
207 | void ixgbe_dbg_adapter_exit(struct ixgbe_adapter *adapter) |
208 | { |
209 | debugfs_remove_recursive(dentry: adapter->ixgbe_dbg_adapter); |
210 | adapter->ixgbe_dbg_adapter = NULL; |
211 | } |
212 | |
213 | /** |
214 | * ixgbe_dbg_init - start up debugfs for the driver |
215 | **/ |
216 | void ixgbe_dbg_init(void) |
217 | { |
218 | ixgbe_dbg_root = debugfs_create_dir(name: ixgbe_driver_name, NULL); |
219 | } |
220 | |
221 | /** |
222 | * ixgbe_dbg_exit - clean out the driver's debugfs entries |
223 | **/ |
224 | void ixgbe_dbg_exit(void) |
225 | { |
226 | debugfs_remove_recursive(dentry: ixgbe_dbg_root); |
227 | } |
228 | |