Line data Source code
1 : // SPDX-License-Identifier: GPL-2.0
2 : #include <linux/netdevice.h>
3 : #include <linux/proc_fs.h>
4 : #include <linux/seq_file.h>
5 : #include <net/wext.h>
6 :
7 : #define BUCKET_SPACE (32 - NETDEV_HASHBITS - 1)
8 :
9 : #define get_bucket(x) ((x) >> BUCKET_SPACE)
10 : #define get_offset(x) ((x) & ((1 << BUCKET_SPACE) - 1))
11 : #define set_bucket_offset(b, o) ((b) << BUCKET_SPACE | (o))
12 :
13 : extern struct list_head ptype_all __read_mostly;
14 : extern struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly;
15 :
16 0 : static inline struct net_device *dev_from_same_bucket(struct seq_file *seq, loff_t *pos)
17 : {
18 0 : struct net *net = seq_file_net(seq);
19 0 : struct net_device *dev;
20 0 : struct hlist_head *h;
21 0 : unsigned int count = 0, offset = get_offset(*pos);
22 :
23 0 : h = &net->dev_index_head[get_bucket(*pos)];
24 0 : hlist_for_each_entry_rcu(dev, h, index_hlist) {
25 0 : if (++count == offset)
26 0 : return dev;
27 : }
28 :
29 : return NULL;
30 : }
31 :
32 0 : static inline struct net_device *dev_from_bucket(struct seq_file *seq, loff_t *pos)
33 : {
34 0 : struct net_device *dev;
35 0 : unsigned int bucket;
36 :
37 0 : do {
38 0 : dev = dev_from_same_bucket(seq, pos);
39 0 : if (dev)
40 0 : return dev;
41 :
42 0 : bucket = get_bucket(*pos) + 1;
43 0 : *pos = set_bucket_offset(bucket, 1);
44 0 : } while (bucket < NETDEV_HASHENTRIES);
45 :
46 : return NULL;
47 : }
48 :
49 : /*
50 : * This is invoked by the /proc filesystem handler to display a device
51 : * in detail.
52 : */
53 0 : static void *dev_seq_start(struct seq_file *seq, loff_t *pos)
54 : __acquires(RCU)
55 : {
56 0 : rcu_read_lock();
57 0 : if (!*pos)
58 : return SEQ_START_TOKEN;
59 :
60 0 : if (get_bucket(*pos) >= NETDEV_HASHENTRIES)
61 : return NULL;
62 :
63 0 : return dev_from_bucket(seq, pos);
64 : }
65 :
66 0 : static void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos)
67 : {
68 0 : ++*pos;
69 0 : return dev_from_bucket(seq, pos);
70 : }
71 :
72 0 : static void dev_seq_stop(struct seq_file *seq, void *v)
73 : __releases(RCU)
74 : {
75 0 : rcu_read_unlock();
76 0 : }
77 :
78 0 : static void dev_seq_printf_stats(struct seq_file *seq, struct net_device *dev)
79 : {
80 0 : struct rtnl_link_stats64 temp;
81 0 : const struct rtnl_link_stats64 *stats = dev_get_stats(dev, &temp);
82 :
83 0 : seq_printf(seq, "%6s: %7llu %7llu %4llu %4llu %4llu %5llu %10llu %9llu "
84 : "%8llu %7llu %4llu %4llu %4llu %5llu %7llu %10llu\n",
85 0 : dev->name, stats->rx_bytes, stats->rx_packets,
86 : stats->rx_errors,
87 0 : stats->rx_dropped + stats->rx_missed_errors,
88 : stats->rx_fifo_errors,
89 0 : stats->rx_length_errors + stats->rx_over_errors +
90 0 : stats->rx_crc_errors + stats->rx_frame_errors,
91 : stats->rx_compressed, stats->multicast,
92 : stats->tx_bytes, stats->tx_packets,
93 : stats->tx_errors, stats->tx_dropped,
94 : stats->tx_fifo_errors, stats->collisions,
95 0 : stats->tx_carrier_errors +
96 0 : stats->tx_aborted_errors +
97 0 : stats->tx_window_errors +
98 0 : stats->tx_heartbeat_errors,
99 : stats->tx_compressed);
100 0 : }
101 :
102 : /*
103 : * Called from the PROCfs module. This now uses the new arbitrary sized
104 : * /proc/net interface to create /proc/net/dev
105 : */
106 0 : static int dev_seq_show(struct seq_file *seq, void *v)
107 : {
108 0 : if (v == SEQ_START_TOKEN)
109 0 : seq_puts(seq, "Inter-| Receive "
110 : " | Transmit\n"
111 : " face |bytes packets errs drop fifo frame "
112 : "compressed multicast|bytes packets errs "
113 : "drop fifo colls carrier compressed\n");
114 : else
115 0 : dev_seq_printf_stats(seq, v);
116 0 : return 0;
117 : }
118 :
119 0 : static u32 softnet_backlog_len(struct softnet_data *sd)
120 : {
121 0 : return skb_queue_len_lockless(&sd->input_pkt_queue) +
122 0 : skb_queue_len_lockless(&sd->process_queue);
123 : }
124 :
125 0 : static struct softnet_data *softnet_get_online(loff_t *pos)
126 : {
127 0 : struct softnet_data *sd = NULL;
128 :
129 0 : while (*pos < nr_cpu_ids)
130 0 : if (cpu_online(*pos)) {
131 0 : sd = &per_cpu(softnet_data, *pos);
132 0 : break;
133 : } else
134 0 : ++*pos;
135 0 : return sd;
136 : }
137 :
138 0 : static void *softnet_seq_start(struct seq_file *seq, loff_t *pos)
139 : {
140 0 : return softnet_get_online(pos);
141 : }
142 :
143 0 : static void *softnet_seq_next(struct seq_file *seq, void *v, loff_t *pos)
144 : {
145 0 : ++*pos;
146 0 : return softnet_get_online(pos);
147 : }
148 :
149 0 : static void softnet_seq_stop(struct seq_file *seq, void *v)
150 : {
151 0 : }
152 :
153 0 : static int softnet_seq_show(struct seq_file *seq, void *v)
154 : {
155 0 : struct softnet_data *sd = v;
156 0 : unsigned int flow_limit_count = 0;
157 :
158 : #ifdef CONFIG_NET_FLOW_LIMIT
159 0 : struct sd_flow_limit *fl;
160 :
161 0 : rcu_read_lock();
162 0 : fl = rcu_dereference(sd->flow_limit);
163 0 : if (fl)
164 0 : flow_limit_count = fl->count;
165 0 : rcu_read_unlock();
166 : #endif
167 :
168 : /* the index is the CPU id owing this sd. Since offline CPUs are not
169 : * displayed, it would be othrwise not trivial for the user-space
170 : * mapping the data a specific CPU
171 : */
172 0 : seq_printf(seq,
173 : "%08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n",
174 : sd->processed, sd->dropped, sd->time_squeeze, 0,
175 : 0, 0, 0, 0, /* was fastroute */
176 : 0, /* was cpu_collision */
177 : sd->received_rps, flow_limit_count,
178 0 : softnet_backlog_len(sd), (int)seq->index);
179 0 : return 0;
180 : }
181 :
182 : static const struct seq_operations dev_seq_ops = {
183 : .start = dev_seq_start,
184 : .next = dev_seq_next,
185 : .stop = dev_seq_stop,
186 : .show = dev_seq_show,
187 : };
188 :
189 : static const struct seq_operations softnet_seq_ops = {
190 : .start = softnet_seq_start,
191 : .next = softnet_seq_next,
192 : .stop = softnet_seq_stop,
193 : .show = softnet_seq_show,
194 : };
195 :
196 0 : static void *ptype_get_idx(loff_t pos)
197 : {
198 0 : struct packet_type *pt = NULL;
199 0 : loff_t i = 0;
200 0 : int t;
201 :
202 0 : list_for_each_entry_rcu(pt, &ptype_all, list) {
203 0 : if (i == pos)
204 0 : return pt;
205 0 : ++i;
206 : }
207 :
208 0 : for (t = 0; t < PTYPE_HASH_SIZE; t++) {
209 0 : list_for_each_entry_rcu(pt, &ptype_base[t], list) {
210 0 : if (i == pos)
211 0 : return pt;
212 0 : ++i;
213 : }
214 : }
215 : return NULL;
216 : }
217 :
218 0 : static void *ptype_seq_start(struct seq_file *seq, loff_t *pos)
219 : __acquires(RCU)
220 : {
221 0 : rcu_read_lock();
222 0 : return *pos ? ptype_get_idx(*pos - 1) : SEQ_START_TOKEN;
223 : }
224 :
225 0 : static void *ptype_seq_next(struct seq_file *seq, void *v, loff_t *pos)
226 : {
227 0 : struct packet_type *pt;
228 0 : struct list_head *nxt;
229 0 : int hash;
230 :
231 0 : ++*pos;
232 0 : if (v == SEQ_START_TOKEN)
233 0 : return ptype_get_idx(0);
234 :
235 0 : pt = v;
236 0 : nxt = pt->list.next;
237 0 : if (pt->type == htons(ETH_P_ALL)) {
238 0 : if (nxt != &ptype_all)
239 0 : goto found;
240 0 : hash = 0;
241 0 : nxt = ptype_base[0].next;
242 : } else
243 0 : hash = ntohs(pt->type) & PTYPE_HASH_MASK;
244 :
245 0 : while (nxt == &ptype_base[hash]) {
246 0 : if (++hash >= PTYPE_HASH_SIZE)
247 : return NULL;
248 0 : nxt = ptype_base[hash].next;
249 : }
250 0 : found:
251 0 : return list_entry(nxt, struct packet_type, list);
252 : }
253 :
254 0 : static void ptype_seq_stop(struct seq_file *seq, void *v)
255 : __releases(RCU)
256 : {
257 0 : rcu_read_unlock();
258 0 : }
259 :
260 0 : static int ptype_seq_show(struct seq_file *seq, void *v)
261 : {
262 0 : struct packet_type *pt = v;
263 :
264 0 : if (v == SEQ_START_TOKEN)
265 0 : seq_puts(seq, "Type Device Function\n");
266 0 : else if (pt->dev == NULL || dev_net(pt->dev) == seq_file_net(seq)) {
267 0 : if (pt->type == htons(ETH_P_ALL))
268 0 : seq_puts(seq, "ALL ");
269 : else
270 0 : seq_printf(seq, "%04x", ntohs(pt->type));
271 :
272 0 : seq_printf(seq, " %-8s %ps\n",
273 0 : pt->dev ? pt->dev->name : "", pt->func);
274 : }
275 :
276 0 : return 0;
277 : }
278 :
279 : static const struct seq_operations ptype_seq_ops = {
280 : .start = ptype_seq_start,
281 : .next = ptype_seq_next,
282 : .stop = ptype_seq_stop,
283 : .show = ptype_seq_show,
284 : };
285 :
286 1 : static int __net_init dev_proc_net_init(struct net *net)
287 : {
288 1 : int rc = -ENOMEM;
289 :
290 1 : if (!proc_create_net("dev", 0444, net->proc_net, &dev_seq_ops,
291 : sizeof(struct seq_net_private)))
292 0 : goto out;
293 1 : if (!proc_create_seq("softnet_stat", 0444, net->proc_net,
294 : &softnet_seq_ops))
295 0 : goto out_dev;
296 1 : if (!proc_create_net("ptype", 0444, net->proc_net, &ptype_seq_ops,
297 : sizeof(struct seq_net_private)))
298 0 : goto out_softnet;
299 :
300 1 : if (wext_proc_init(net))
301 : goto out_ptype;
302 : rc = 0;
303 1 : out:
304 1 : return rc;
305 : out_ptype:
306 : remove_proc_entry("ptype", net->proc_net);
307 0 : out_softnet:
308 0 : remove_proc_entry("softnet_stat", net->proc_net);
309 0 : out_dev:
310 0 : remove_proc_entry("dev", net->proc_net);
311 0 : goto out;
312 : }
313 :
314 0 : static void __net_exit dev_proc_net_exit(struct net *net)
315 : {
316 0 : wext_proc_exit(net);
317 :
318 0 : remove_proc_entry("ptype", net->proc_net);
319 0 : remove_proc_entry("softnet_stat", net->proc_net);
320 0 : remove_proc_entry("dev", net->proc_net);
321 0 : }
322 :
323 : static struct pernet_operations __net_initdata dev_proc_ops = {
324 : .init = dev_proc_net_init,
325 : .exit = dev_proc_net_exit,
326 : };
327 :
328 0 : static int dev_mc_seq_show(struct seq_file *seq, void *v)
329 : {
330 0 : struct netdev_hw_addr *ha;
331 0 : struct net_device *dev = v;
332 :
333 0 : if (v == SEQ_START_TOKEN)
334 : return 0;
335 :
336 0 : netif_addr_lock_bh(dev);
337 0 : netdev_for_each_mc_addr(ha, dev) {
338 0 : seq_printf(seq, "%-4d %-15s %-5d %-5d %*phN\n",
339 0 : dev->ifindex, dev->name,
340 0 : ha->refcount, ha->global_use,
341 0 : (int)dev->addr_len, ha->addr);
342 : }
343 0 : netif_addr_unlock_bh(dev);
344 0 : return 0;
345 : }
346 :
347 : static const struct seq_operations dev_mc_seq_ops = {
348 : .start = dev_seq_start,
349 : .next = dev_seq_next,
350 : .stop = dev_seq_stop,
351 : .show = dev_mc_seq_show,
352 : };
353 :
354 1 : static int __net_init dev_mc_net_init(struct net *net)
355 : {
356 1 : if (!proc_create_net("dev_mcast", 0, net->proc_net, &dev_mc_seq_ops,
357 : sizeof(struct seq_net_private)))
358 0 : return -ENOMEM;
359 : return 0;
360 : }
361 :
362 0 : static void __net_exit dev_mc_net_exit(struct net *net)
363 : {
364 0 : remove_proc_entry("dev_mcast", net->proc_net);
365 0 : }
366 :
367 : static struct pernet_operations __net_initdata dev_mc_net_ops = {
368 : .init = dev_mc_net_init,
369 : .exit = dev_mc_net_exit,
370 : };
371 :
372 1 : int __init dev_proc_init(void)
373 : {
374 1 : int ret = register_pernet_subsys(&dev_proc_ops);
375 1 : if (!ret)
376 1 : return register_pernet_subsys(&dev_mc_net_ops);
377 : return ret;
378 : }
|