Branch data 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 : : struct net *net = seq_file_net(seq);
19 : : struct net_device *dev;
20 : : struct hlist_head *h;
21 : 0 : unsigned int count = 0, offset = get_offset(*pos);
22 : :
23 : 0 : h = &net->dev_name_head[get_bucket(*pos)];
24 : 0 : hlist_for_each_entry_rcu(dev, h, name_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 : : struct net_device *dev;
35 : : unsigned int bucket;
36 : :
37 : : 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 : : 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 : : rcu_read_unlock();
76 : 0 : }
77 : :
78 : 0 : static void dev_seq_printf_stats(struct seq_file *seq, struct net_device *dev)
79 : : {
80 : : 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 struct softnet_data *softnet_get_online(loff_t *pos)
120 : : {
121 : : struct softnet_data *sd = NULL;
122 : :
123 : 0 : while (*pos < nr_cpu_ids)
124 : 0 : if (cpu_online(*pos)) {
125 : 0 : sd = &per_cpu(softnet_data, *pos);
126 : 0 : break;
127 : : } else
128 : 0 : ++*pos;
129 : 0 : return sd;
130 : : }
131 : :
132 : 0 : static void *softnet_seq_start(struct seq_file *seq, loff_t *pos)
133 : : {
134 : 0 : return softnet_get_online(pos);
135 : : }
136 : :
137 : 0 : static void *softnet_seq_next(struct seq_file *seq, void *v, loff_t *pos)
138 : : {
139 : 0 : ++*pos;
140 : 0 : return softnet_get_online(pos);
141 : : }
142 : :
143 : 0 : static void softnet_seq_stop(struct seq_file *seq, void *v)
144 : : {
145 : 0 : }
146 : :
147 : 0 : static int softnet_seq_show(struct seq_file *seq, void *v)
148 : : {
149 : : struct softnet_data *sd = v;
150 : : unsigned int flow_limit_count = 0;
151 : :
152 : : #ifdef CONFIG_NET_FLOW_LIMIT
153 : : struct sd_flow_limit *fl;
154 : :
155 : : rcu_read_lock();
156 : 0 : fl = rcu_dereference(sd->flow_limit);
157 : 0 : if (fl)
158 : 0 : flow_limit_count = fl->count;
159 : : rcu_read_unlock();
160 : : #endif
161 : :
162 : 0 : seq_printf(seq,
163 : : "%08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n",
164 : : sd->processed, sd->dropped, sd->time_squeeze, 0,
165 : : 0, 0, 0, 0, /* was fastroute */
166 : : 0, /* was cpu_collision */
167 : : sd->received_rps, flow_limit_count);
168 : 0 : return 0;
169 : : }
170 : :
171 : : static const struct seq_operations dev_seq_ops = {
172 : : .start = dev_seq_start,
173 : : .next = dev_seq_next,
174 : : .stop = dev_seq_stop,
175 : : .show = dev_seq_show,
176 : : };
177 : :
178 : : static const struct seq_operations softnet_seq_ops = {
179 : : .start = softnet_seq_start,
180 : : .next = softnet_seq_next,
181 : : .stop = softnet_seq_stop,
182 : : .show = softnet_seq_show,
183 : : };
184 : :
185 : 0 : static void *ptype_get_idx(loff_t pos)
186 : : {
187 : : struct packet_type *pt = NULL;
188 : : loff_t i = 0;
189 : : int t;
190 : :
191 : 0 : list_for_each_entry_rcu(pt, &ptype_all, list) {
192 : 0 : if (i == pos)
193 : 0 : return pt;
194 : 0 : ++i;
195 : : }
196 : :
197 : 0 : for (t = 0; t < PTYPE_HASH_SIZE; t++) {
198 : 0 : list_for_each_entry_rcu(pt, &ptype_base[t], list) {
199 : 0 : if (i == pos)
200 : 0 : return pt;
201 : 0 : ++i;
202 : : }
203 : : }
204 : : return NULL;
205 : : }
206 : :
207 : 0 : static void *ptype_seq_start(struct seq_file *seq, loff_t *pos)
208 : : __acquires(RCU)
209 : : {
210 : : rcu_read_lock();
211 : 0 : return *pos ? ptype_get_idx(*pos - 1) : SEQ_START_TOKEN;
212 : : }
213 : :
214 : 0 : static void *ptype_seq_next(struct seq_file *seq, void *v, loff_t *pos)
215 : : {
216 : : struct packet_type *pt;
217 : : struct list_head *nxt;
218 : : int hash;
219 : :
220 : 0 : ++*pos;
221 : 0 : if (v == SEQ_START_TOKEN)
222 : 0 : return ptype_get_idx(0);
223 : :
224 : : pt = v;
225 : 0 : nxt = pt->list.next;
226 : 0 : if (pt->type == htons(ETH_P_ALL)) {
227 : 0 : if (nxt != &ptype_all)
228 : : goto found;
229 : : hash = 0;
230 : 0 : nxt = ptype_base[0].next;
231 : : } else
232 : 0 : hash = ntohs(pt->type) & PTYPE_HASH_MASK;
233 : :
234 : 0 : while (nxt == &ptype_base[hash]) {
235 : 0 : if (++hash >= PTYPE_HASH_SIZE)
236 : : return NULL;
237 : 0 : nxt = ptype_base[hash].next;
238 : : }
239 : : found:
240 : 0 : return list_entry(nxt, struct packet_type, list);
241 : : }
242 : :
243 : 0 : static void ptype_seq_stop(struct seq_file *seq, void *v)
244 : : __releases(RCU)
245 : : {
246 : : rcu_read_unlock();
247 : 0 : }
248 : :
249 : 0 : static int ptype_seq_show(struct seq_file *seq, void *v)
250 : : {
251 : : struct packet_type *pt = v;
252 : :
253 : 0 : if (v == SEQ_START_TOKEN)
254 : 0 : seq_puts(seq, "Type Device Function\n");
255 : 0 : else if (pt->dev == NULL || dev_net(pt->dev) == seq_file_net(seq)) {
256 : 0 : if (pt->type == htons(ETH_P_ALL))
257 : 0 : seq_puts(seq, "ALL ");
258 : : else
259 : 0 : seq_printf(seq, "%04x", ntohs(pt->type));
260 : :
261 : 0 : seq_printf(seq, " %-8s %ps\n",
262 : 0 : pt->dev ? pt->dev->name : "", pt->func);
263 : : }
264 : :
265 : 0 : return 0;
266 : : }
267 : :
268 : : static const struct seq_operations ptype_seq_ops = {
269 : : .start = ptype_seq_start,
270 : : .next = ptype_seq_next,
271 : : .stop = ptype_seq_stop,
272 : : .show = ptype_seq_show,
273 : : };
274 : :
275 : 3 : static int __net_init dev_proc_net_init(struct net *net)
276 : : {
277 : : int rc = -ENOMEM;
278 : :
279 : 3 : if (!proc_create_net("dev", 0444, net->proc_net, &dev_seq_ops,
280 : : sizeof(struct seq_net_private)))
281 : : goto out;
282 : 3 : if (!proc_create_seq("softnet_stat", 0444, net->proc_net,
283 : : &softnet_seq_ops))
284 : : goto out_dev;
285 : 3 : if (!proc_create_net("ptype", 0444, net->proc_net, &ptype_seq_ops,
286 : : sizeof(struct seq_net_private)))
287 : : goto out_softnet;
288 : :
289 : 3 : if (wext_proc_init(net))
290 : : goto out_ptype;
291 : : rc = 0;
292 : : out:
293 : 3 : return rc;
294 : : out_ptype:
295 : 0 : remove_proc_entry("ptype", net->proc_net);
296 : : out_softnet:
297 : 0 : remove_proc_entry("softnet_stat", net->proc_net);
298 : : out_dev:
299 : 0 : remove_proc_entry("dev", net->proc_net);
300 : 0 : goto out;
301 : : }
302 : :
303 : 1 : static void __net_exit dev_proc_net_exit(struct net *net)
304 : : {
305 : 1 : wext_proc_exit(net);
306 : :
307 : 1 : remove_proc_entry("ptype", net->proc_net);
308 : 1 : remove_proc_entry("softnet_stat", net->proc_net);
309 : 1 : remove_proc_entry("dev", net->proc_net);
310 : 1 : }
311 : :
312 : : static struct pernet_operations __net_initdata dev_proc_ops = {
313 : : .init = dev_proc_net_init,
314 : : .exit = dev_proc_net_exit,
315 : : };
316 : :
317 : 0 : static int dev_mc_seq_show(struct seq_file *seq, void *v)
318 : : {
319 : : struct netdev_hw_addr *ha;
320 : : struct net_device *dev = v;
321 : :
322 : 0 : if (v == SEQ_START_TOKEN)
323 : : return 0;
324 : :
325 : : netif_addr_lock_bh(dev);
326 : 0 : netdev_for_each_mc_addr(ha, dev) {
327 : 0 : seq_printf(seq, "%-4d %-15s %-5d %-5d %*phN\n",
328 : 0 : dev->ifindex, dev->name,
329 : 0 : ha->refcount, ha->global_use,
330 : 0 : (int)dev->addr_len, ha->addr);
331 : : }
332 : : netif_addr_unlock_bh(dev);
333 : 0 : return 0;
334 : : }
335 : :
336 : : static const struct seq_operations dev_mc_seq_ops = {
337 : : .start = dev_seq_start,
338 : : .next = dev_seq_next,
339 : : .stop = dev_seq_stop,
340 : : .show = dev_mc_seq_show,
341 : : };
342 : :
343 : 3 : static int __net_init dev_mc_net_init(struct net *net)
344 : : {
345 : 3 : if (!proc_create_net("dev_mcast", 0, net->proc_net, &dev_mc_seq_ops,
346 : : sizeof(struct seq_net_private)))
347 : : return -ENOMEM;
348 : 3 : return 0;
349 : : }
350 : :
351 : 1 : static void __net_exit dev_mc_net_exit(struct net *net)
352 : : {
353 : 1 : remove_proc_entry("dev_mcast", net->proc_net);
354 : 1 : }
355 : :
356 : : static struct pernet_operations __net_initdata dev_mc_net_ops = {
357 : : .init = dev_mc_net_init,
358 : : .exit = dev_mc_net_exit,
359 : : };
360 : :
361 : 3 : int __init dev_proc_init(void)
362 : : {
363 : 3 : int ret = register_pernet_subsys(&dev_proc_ops);
364 : 3 : if (!ret)
365 : 3 : return register_pernet_subsys(&dev_mc_net_ops);
366 : : return ret;
367 : : }
|