1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright(c) 2013 - 2018 Intel Corporation. */ |
3 | |
4 | #include "fm10k.h" |
5 | |
6 | #include <linux/debugfs.h> |
7 | #include <linux/seq_file.h> |
8 | |
9 | static struct dentry *dbg_root; |
10 | |
11 | /* Descriptor Seq Functions */ |
12 | |
13 | static void *fm10k_dbg_desc_seq_start(struct seq_file *s, loff_t *pos) |
14 | { |
15 | struct fm10k_ring *ring = s->private; |
16 | |
17 | return (*pos < ring->count) ? pos : NULL; |
18 | } |
19 | |
20 | static void *fm10k_dbg_desc_seq_next(struct seq_file *s, |
21 | void __always_unused *v, |
22 | loff_t *pos) |
23 | { |
24 | struct fm10k_ring *ring = s->private; |
25 | |
26 | return (++(*pos) < ring->count) ? pos : NULL; |
27 | } |
28 | |
29 | static void fm10k_dbg_desc_seq_stop(struct seq_file __always_unused *s, |
30 | void __always_unused *v) |
31 | { |
32 | /* Do nothing. */ |
33 | } |
34 | |
35 | static void fm10k_dbg_desc_break(struct seq_file *s, int i) |
36 | { |
37 | while (i--) |
38 | seq_putc(m: s, c: '-'); |
39 | |
40 | seq_putc(m: s, c: '\n'); |
41 | } |
42 | |
43 | static int fm10k_dbg_tx_desc_seq_show(struct seq_file *s, void *v) |
44 | { |
45 | struct fm10k_ring *ring = s->private; |
46 | int i = *(loff_t *)v; |
47 | static const char tx_desc_hdr[] = |
48 | "DES BUFFER_ADDRESS LENGTH VLAN MSS HDRLEN FLAGS\n" ; |
49 | |
50 | /* Generate header */ |
51 | if (!i) { |
52 | seq_printf(m: s, fmt: tx_desc_hdr); |
53 | fm10k_dbg_desc_break(s, i: sizeof(tx_desc_hdr) - 1); |
54 | } |
55 | |
56 | /* Validate descriptor allocation */ |
57 | if (!ring->desc) { |
58 | seq_printf(m: s, fmt: "%03X Descriptor ring not allocated.\n" , i); |
59 | } else { |
60 | struct fm10k_tx_desc *txd = FM10K_TX_DESC(ring, i); |
61 | |
62 | seq_printf(m: s, fmt: "%03X %#018llx %#06x %#06x %#06x %#06x %#04x\n" , |
63 | i, txd->buffer_addr, txd->buflen, txd->vlan, |
64 | txd->mss, txd->hdrlen, txd->flags); |
65 | } |
66 | |
67 | return 0; |
68 | } |
69 | |
70 | static int fm10k_dbg_rx_desc_seq_show(struct seq_file *s, void *v) |
71 | { |
72 | struct fm10k_ring *ring = s->private; |
73 | int i = *(loff_t *)v; |
74 | static const char rx_desc_hdr[] = |
75 | "DES DATA RSS STATERR LENGTH VLAN DGLORT SGLORT TIMESTAMP\n" ; |
76 | |
77 | /* Generate header */ |
78 | if (!i) { |
79 | seq_printf(m: s, fmt: rx_desc_hdr); |
80 | fm10k_dbg_desc_break(s, i: sizeof(rx_desc_hdr) - 1); |
81 | } |
82 | |
83 | /* Validate descriptor allocation */ |
84 | if (!ring->desc) { |
85 | seq_printf(m: s, fmt: "%03X Descriptor ring not allocated.\n" , i); |
86 | } else { |
87 | union fm10k_rx_desc *rxd = FM10K_RX_DESC(ring, i); |
88 | |
89 | seq_printf(m: s, |
90 | fmt: "%03X %#010x %#010x %#010x %#06x %#06x %#06x %#06x %#018llx\n" , |
91 | i, rxd->d.data, rxd->d.rss, rxd->d.staterr, |
92 | rxd->w.length, rxd->w.vlan, rxd->w.dglort, |
93 | rxd->w.sglort, rxd->q.timestamp); |
94 | } |
95 | |
96 | return 0; |
97 | } |
98 | |
99 | static const struct seq_operations fm10k_dbg_tx_desc_seq_ops = { |
100 | .start = fm10k_dbg_desc_seq_start, |
101 | .next = fm10k_dbg_desc_seq_next, |
102 | .stop = fm10k_dbg_desc_seq_stop, |
103 | .show = fm10k_dbg_tx_desc_seq_show, |
104 | }; |
105 | |
106 | static const struct seq_operations fm10k_dbg_rx_desc_seq_ops = { |
107 | .start = fm10k_dbg_desc_seq_start, |
108 | .next = fm10k_dbg_desc_seq_next, |
109 | .stop = fm10k_dbg_desc_seq_stop, |
110 | .show = fm10k_dbg_rx_desc_seq_show, |
111 | }; |
112 | |
113 | static int fm10k_dbg_desc_open(struct inode *inode, struct file *filep) |
114 | { |
115 | struct fm10k_ring *ring = inode->i_private; |
116 | struct fm10k_q_vector *q_vector = ring->q_vector; |
117 | const struct seq_operations *desc_seq_ops; |
118 | int err; |
119 | |
120 | if (ring < q_vector->rx.ring) |
121 | desc_seq_ops = &fm10k_dbg_tx_desc_seq_ops; |
122 | else |
123 | desc_seq_ops = &fm10k_dbg_rx_desc_seq_ops; |
124 | |
125 | err = seq_open(filep, desc_seq_ops); |
126 | if (err) |
127 | return err; |
128 | |
129 | ((struct seq_file *)filep->private_data)->private = ring; |
130 | |
131 | return 0; |
132 | } |
133 | |
134 | static const struct file_operations fm10k_dbg_desc_fops = { |
135 | .owner = THIS_MODULE, |
136 | .open = fm10k_dbg_desc_open, |
137 | .read = seq_read, |
138 | .llseek = seq_lseek, |
139 | .release = seq_release, |
140 | }; |
141 | |
142 | /** |
143 | * fm10k_dbg_q_vector_init - setup debugfs for the q_vectors |
144 | * @q_vector: q_vector to allocate directories for |
145 | * |
146 | * A folder is created for each q_vector found. In each q_vector |
147 | * folder, a debugfs file is created for each tx and rx ring |
148 | * allocated to the q_vector. |
149 | **/ |
150 | void fm10k_dbg_q_vector_init(struct fm10k_q_vector *q_vector) |
151 | { |
152 | struct fm10k_intfc *interface = q_vector->interface; |
153 | char name[16]; |
154 | int i; |
155 | |
156 | if (!interface->dbg_intfc) |
157 | return; |
158 | |
159 | /* Generate a folder for each q_vector */ |
160 | snprintf(buf: name, size: sizeof(name), fmt: "q_vector.%03d" , q_vector->v_idx); |
161 | |
162 | q_vector->dbg_q_vector = debugfs_create_dir(name, parent: interface->dbg_intfc); |
163 | |
164 | /* Generate a file for each rx ring in the q_vector */ |
165 | for (i = 0; i < q_vector->tx.count; i++) { |
166 | struct fm10k_ring *ring = &q_vector->tx.ring[i]; |
167 | |
168 | snprintf(buf: name, size: sizeof(name), fmt: "tx_ring.%03d" , ring->queue_index); |
169 | |
170 | debugfs_create_file(name, mode: 0600, |
171 | parent: q_vector->dbg_q_vector, data: ring, |
172 | fops: &fm10k_dbg_desc_fops); |
173 | } |
174 | |
175 | /* Generate a file for each rx ring in the q_vector */ |
176 | for (i = 0; i < q_vector->rx.count; i++) { |
177 | struct fm10k_ring *ring = &q_vector->rx.ring[i]; |
178 | |
179 | snprintf(buf: name, size: sizeof(name), fmt: "rx_ring.%03d" , ring->queue_index); |
180 | |
181 | debugfs_create_file(name, mode: 0600, |
182 | parent: q_vector->dbg_q_vector, data: ring, |
183 | fops: &fm10k_dbg_desc_fops); |
184 | } |
185 | } |
186 | |
187 | /** |
188 | * fm10k_dbg_q_vector_exit - setup debugfs for the q_vectors |
189 | * @q_vector: q_vector to allocate directories for |
190 | **/ |
191 | void fm10k_dbg_q_vector_exit(struct fm10k_q_vector *q_vector) |
192 | { |
193 | struct fm10k_intfc *interface = q_vector->interface; |
194 | |
195 | if (interface->dbg_intfc) |
196 | debugfs_remove_recursive(dentry: q_vector->dbg_q_vector); |
197 | q_vector->dbg_q_vector = NULL; |
198 | } |
199 | |
200 | /** |
201 | * fm10k_dbg_intfc_init - setup the debugfs directory for the intferface |
202 | * @interface: the interface that is starting up |
203 | **/ |
204 | |
205 | void fm10k_dbg_intfc_init(struct fm10k_intfc *interface) |
206 | { |
207 | const char *name = pci_name(pdev: interface->pdev); |
208 | |
209 | if (dbg_root) |
210 | interface->dbg_intfc = debugfs_create_dir(name, parent: dbg_root); |
211 | } |
212 | |
213 | /** |
214 | * fm10k_dbg_intfc_exit - clean out the interface's debugfs entries |
215 | * @interface: the interface that is stopping |
216 | **/ |
217 | void fm10k_dbg_intfc_exit(struct fm10k_intfc *interface) |
218 | { |
219 | if (dbg_root) |
220 | debugfs_remove_recursive(dentry: interface->dbg_intfc); |
221 | interface->dbg_intfc = NULL; |
222 | } |
223 | |
224 | /** |
225 | * fm10k_dbg_init - start up debugfs for the driver |
226 | **/ |
227 | void fm10k_dbg_init(void) |
228 | { |
229 | dbg_root = debugfs_create_dir(name: fm10k_driver_name, NULL); |
230 | } |
231 | |
232 | /** |
233 | * fm10k_dbg_exit - clean out the driver's debugfs entries |
234 | **/ |
235 | void fm10k_dbg_exit(void) |
236 | { |
237 | debugfs_remove_recursive(dentry: dbg_root); |
238 | dbg_root = NULL; |
239 | } |
240 | |