xref: /openbmc/linux/net/core/net-procfs.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2900ff8c6SCong Wang #include <linux/netdevice.h>
3900ff8c6SCong Wang #include <linux/proc_fs.h>
4900ff8c6SCong Wang #include <linux/seq_file.h>
5900ff8c6SCong Wang #include <net/wext.h>
6900ff8c6SCong Wang 
76264f58cSJakub Kicinski #include "dev.h"
86264f58cSJakub Kicinski 
9900ff8c6SCong Wang #define BUCKET_SPACE (32 - NETDEV_HASHBITS - 1)
10900ff8c6SCong Wang 
11900ff8c6SCong Wang #define get_bucket(x) ((x) >> BUCKET_SPACE)
12900ff8c6SCong Wang #define get_offset(x) ((x) & ((1 << BUCKET_SPACE) - 1))
13900ff8c6SCong Wang #define set_bucket_offset(b, o) ((b) << BUCKET_SPACE | (o))
14900ff8c6SCong Wang 
dev_from_same_bucket(struct seq_file * seq,loff_t * pos)15900ff8c6SCong Wang static inline struct net_device *dev_from_same_bucket(struct seq_file *seq, loff_t *pos)
16900ff8c6SCong Wang {
17900ff8c6SCong Wang 	struct net *net = seq_file_net(seq);
18900ff8c6SCong Wang 	struct net_device *dev;
19900ff8c6SCong Wang 	struct hlist_head *h;
20900ff8c6SCong Wang 	unsigned int count = 0, offset = get_offset(*pos);
21900ff8c6SCong Wang 
226958c97aSJiri Pirko 	h = &net->dev_index_head[get_bucket(*pos)];
236958c97aSJiri Pirko 	hlist_for_each_entry_rcu(dev, h, index_hlist) {
24900ff8c6SCong Wang 		if (++count == offset)
25900ff8c6SCong Wang 			return dev;
26900ff8c6SCong Wang 	}
27900ff8c6SCong Wang 
28900ff8c6SCong Wang 	return NULL;
29900ff8c6SCong Wang }
30900ff8c6SCong Wang 
dev_from_bucket(struct seq_file * seq,loff_t * pos)31900ff8c6SCong Wang static inline struct net_device *dev_from_bucket(struct seq_file *seq, loff_t *pos)
32900ff8c6SCong Wang {
33900ff8c6SCong Wang 	struct net_device *dev;
34900ff8c6SCong Wang 	unsigned int bucket;
35900ff8c6SCong Wang 
36900ff8c6SCong Wang 	do {
37900ff8c6SCong Wang 		dev = dev_from_same_bucket(seq, pos);
38900ff8c6SCong Wang 		if (dev)
39900ff8c6SCong Wang 			return dev;
40900ff8c6SCong Wang 
41900ff8c6SCong Wang 		bucket = get_bucket(*pos) + 1;
42900ff8c6SCong Wang 		*pos = set_bucket_offset(bucket, 1);
43900ff8c6SCong Wang 	} while (bucket < NETDEV_HASHENTRIES);
44900ff8c6SCong Wang 
45900ff8c6SCong Wang 	return NULL;
46900ff8c6SCong Wang }
47900ff8c6SCong Wang 
48900ff8c6SCong Wang /*
49900ff8c6SCong Wang  *	This is invoked by the /proc filesystem handler to display a device
50900ff8c6SCong Wang  *	in detail.
51900ff8c6SCong Wang  */
dev_seq_start(struct seq_file * seq,loff_t * pos)52900ff8c6SCong Wang static void *dev_seq_start(struct seq_file *seq, loff_t *pos)
53900ff8c6SCong Wang 	__acquires(RCU)
54900ff8c6SCong Wang {
55900ff8c6SCong Wang 	rcu_read_lock();
56900ff8c6SCong Wang 	if (!*pos)
57900ff8c6SCong Wang 		return SEQ_START_TOKEN;
58900ff8c6SCong Wang 
59900ff8c6SCong Wang 	if (get_bucket(*pos) >= NETDEV_HASHENTRIES)
60900ff8c6SCong Wang 		return NULL;
61900ff8c6SCong Wang 
62900ff8c6SCong Wang 	return dev_from_bucket(seq, pos);
63900ff8c6SCong Wang }
64900ff8c6SCong Wang 
dev_seq_next(struct seq_file * seq,void * v,loff_t * pos)65900ff8c6SCong Wang static void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos)
66900ff8c6SCong Wang {
67900ff8c6SCong Wang 	++*pos;
68900ff8c6SCong Wang 	return dev_from_bucket(seq, pos);
69900ff8c6SCong Wang }
70900ff8c6SCong Wang 
dev_seq_stop(struct seq_file * seq,void * v)71900ff8c6SCong Wang static void dev_seq_stop(struct seq_file *seq, void *v)
72900ff8c6SCong Wang 	__releases(RCU)
73900ff8c6SCong Wang {
74900ff8c6SCong Wang 	rcu_read_unlock();
75900ff8c6SCong Wang }
76900ff8c6SCong Wang 
dev_seq_printf_stats(struct seq_file * seq,struct net_device * dev)77900ff8c6SCong Wang static void dev_seq_printf_stats(struct seq_file *seq, struct net_device *dev)
78900ff8c6SCong Wang {
79900ff8c6SCong Wang 	struct rtnl_link_stats64 temp;
80900ff8c6SCong Wang 	const struct rtnl_link_stats64 *stats = dev_get_stats(dev, &temp);
81900ff8c6SCong Wang 
821f922d9eSVladimir Oltean 	seq_printf(seq, "%6s: %7llu %7llu %4llu %4llu %4llu %5llu %10llu %9llu "
831f922d9eSVladimir Oltean 		   "%8llu %7llu %4llu %4llu %4llu %5llu %7llu %10llu\n",
84900ff8c6SCong Wang 		   dev->name, stats->rx_bytes, stats->rx_packets,
85900ff8c6SCong Wang 		   stats->rx_errors,
86900ff8c6SCong Wang 		   stats->rx_dropped + stats->rx_missed_errors,
87900ff8c6SCong Wang 		   stats->rx_fifo_errors,
88900ff8c6SCong Wang 		   stats->rx_length_errors + stats->rx_over_errors +
89900ff8c6SCong Wang 		    stats->rx_crc_errors + stats->rx_frame_errors,
90900ff8c6SCong Wang 		   stats->rx_compressed, stats->multicast,
91900ff8c6SCong Wang 		   stats->tx_bytes, stats->tx_packets,
92900ff8c6SCong Wang 		   stats->tx_errors, stats->tx_dropped,
93900ff8c6SCong Wang 		   stats->tx_fifo_errors, stats->collisions,
94900ff8c6SCong Wang 		   stats->tx_carrier_errors +
95900ff8c6SCong Wang 		    stats->tx_aborted_errors +
96900ff8c6SCong Wang 		    stats->tx_window_errors +
97900ff8c6SCong Wang 		    stats->tx_heartbeat_errors,
98900ff8c6SCong Wang 		   stats->tx_compressed);
99900ff8c6SCong Wang }
100900ff8c6SCong Wang 
101900ff8c6SCong Wang /*
102900ff8c6SCong Wang  *	Called from the PROCfs module. This now uses the new arbitrary sized
103900ff8c6SCong Wang  *	/proc/net interface to create /proc/net/dev
104900ff8c6SCong Wang  */
dev_seq_show(struct seq_file * seq,void * v)105900ff8c6SCong Wang static int dev_seq_show(struct seq_file *seq, void *v)
106900ff8c6SCong Wang {
107900ff8c6SCong Wang 	if (v == SEQ_START_TOKEN)
1081f922d9eSVladimir Oltean 		seq_puts(seq, "Inter-|   Receive                            "
109900ff8c6SCong Wang 			      "                    |  Transmit\n"
1101f922d9eSVladimir Oltean 			      " face |bytes    packets errs drop fifo frame "
111900ff8c6SCong Wang 			      "compressed multicast|bytes    packets errs "
112900ff8c6SCong Wang 			      "drop fifo colls carrier compressed\n");
113900ff8c6SCong Wang 	else
114900ff8c6SCong Wang 		dev_seq_printf_stats(seq, v);
115900ff8c6SCong Wang 	return 0;
116900ff8c6SCong Wang }
117900ff8c6SCong Wang 
softnet_input_pkt_queue_len(struct softnet_data * sd)118*59da2d7bSJason Xing static u32 softnet_input_pkt_queue_len(struct softnet_data *sd)
1197d58e655SPaolo Abeni {
120*59da2d7bSJason Xing 	return skb_queue_len_lockless(&sd->input_pkt_queue);
121*59da2d7bSJason Xing }
122*59da2d7bSJason Xing 
softnet_process_queue_len(struct softnet_data * sd)123*59da2d7bSJason Xing static u32 softnet_process_queue_len(struct softnet_data *sd)
124*59da2d7bSJason Xing {
125*59da2d7bSJason Xing 	return skb_queue_len_lockless(&sd->process_queue);
1267d58e655SPaolo Abeni }
1277d58e655SPaolo Abeni 
softnet_get_online(loff_t * pos)128900ff8c6SCong Wang static struct softnet_data *softnet_get_online(loff_t *pos)
129900ff8c6SCong Wang {
130900ff8c6SCong Wang 	struct softnet_data *sd = NULL;
131900ff8c6SCong Wang 
132900ff8c6SCong Wang 	while (*pos < nr_cpu_ids)
133900ff8c6SCong Wang 		if (cpu_online(*pos)) {
134900ff8c6SCong Wang 			sd = &per_cpu(softnet_data, *pos);
135900ff8c6SCong Wang 			break;
136900ff8c6SCong Wang 		} else
137900ff8c6SCong Wang 			++*pos;
138900ff8c6SCong Wang 	return sd;
139900ff8c6SCong Wang }
140900ff8c6SCong Wang 
softnet_seq_start(struct seq_file * seq,loff_t * pos)141900ff8c6SCong Wang static void *softnet_seq_start(struct seq_file *seq, loff_t *pos)
142900ff8c6SCong Wang {
143900ff8c6SCong Wang 	return softnet_get_online(pos);
144900ff8c6SCong Wang }
145900ff8c6SCong Wang 
softnet_seq_next(struct seq_file * seq,void * v,loff_t * pos)146900ff8c6SCong Wang static void *softnet_seq_next(struct seq_file *seq, void *v, loff_t *pos)
147900ff8c6SCong Wang {
148900ff8c6SCong Wang 	++*pos;
149900ff8c6SCong Wang 	return softnet_get_online(pos);
150900ff8c6SCong Wang }
151900ff8c6SCong Wang 
softnet_seq_stop(struct seq_file * seq,void * v)152900ff8c6SCong Wang static void softnet_seq_stop(struct seq_file *seq, void *v)
153900ff8c6SCong Wang {
154900ff8c6SCong Wang }
155900ff8c6SCong Wang 
softnet_seq_show(struct seq_file * seq,void * v)156900ff8c6SCong Wang static int softnet_seq_show(struct seq_file *seq, void *v)
157900ff8c6SCong Wang {
158900ff8c6SCong Wang 	struct softnet_data *sd = v;
159*59da2d7bSJason Xing 	u32 input_qlen = softnet_input_pkt_queue_len(sd);
160*59da2d7bSJason Xing 	u32 process_qlen = softnet_process_queue_len(sd);
16199bbc707SWillem de Bruijn 	unsigned int flow_limit_count = 0;
162900ff8c6SCong Wang 
16399bbc707SWillem de Bruijn #ifdef CONFIG_NET_FLOW_LIMIT
16499bbc707SWillem de Bruijn 	struct sd_flow_limit *fl;
16599bbc707SWillem de Bruijn 
16699bbc707SWillem de Bruijn 	rcu_read_lock();
16799bbc707SWillem de Bruijn 	fl = rcu_dereference(sd->flow_limit);
16899bbc707SWillem de Bruijn 	if (fl)
16999bbc707SWillem de Bruijn 		flow_limit_count = fl->count;
17099bbc707SWillem de Bruijn 	rcu_read_unlock();
17199bbc707SWillem de Bruijn #endif
17299bbc707SWillem de Bruijn 
1737d58e655SPaolo Abeni 	/* the index is the CPU id owing this sd. Since offline CPUs are not
1747d58e655SPaolo Abeni 	 * displayed, it would be othrwise not trivial for the user-space
1757d58e655SPaolo Abeni 	 * mapping the data a specific CPU
1767d58e655SPaolo Abeni 	 */
17799bbc707SWillem de Bruijn 	seq_printf(seq,
178*59da2d7bSJason Xing 		   "%08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x "
179*59da2d7bSJason Xing 		   "%08x %08x\n",
180900ff8c6SCong Wang 		   sd->processed, sd->dropped, sd->time_squeeze, 0,
181900ff8c6SCong Wang 		   0, 0, 0, 0, /* was fastroute */
182f0cdf76cSFlorian Westphal 		   0,	/* was cpu_collision */
1837d58e655SPaolo Abeni 		   sd->received_rps, flow_limit_count,
184*59da2d7bSJason Xing 		   input_qlen + process_qlen, (int)seq->index,
185*59da2d7bSJason Xing 		   input_qlen, process_qlen);
186900ff8c6SCong Wang 	return 0;
187900ff8c6SCong Wang }
188900ff8c6SCong Wang 
189900ff8c6SCong Wang static const struct seq_operations dev_seq_ops = {
190900ff8c6SCong Wang 	.start = dev_seq_start,
191900ff8c6SCong Wang 	.next  = dev_seq_next,
192900ff8c6SCong Wang 	.stop  = dev_seq_stop,
193900ff8c6SCong Wang 	.show  = dev_seq_show,
194900ff8c6SCong Wang };
195900ff8c6SCong Wang 
196900ff8c6SCong Wang static const struct seq_operations softnet_seq_ops = {
197900ff8c6SCong Wang 	.start = softnet_seq_start,
198900ff8c6SCong Wang 	.next  = softnet_seq_next,
199900ff8c6SCong Wang 	.stop  = softnet_seq_stop,
200900ff8c6SCong Wang 	.show  = softnet_seq_show,
201900ff8c6SCong Wang };
202900ff8c6SCong Wang 
ptype_get_idx(struct seq_file * seq,loff_t pos)2031d10f8a1SJianguo Wu static void *ptype_get_idx(struct seq_file *seq, loff_t pos)
204900ff8c6SCong Wang {
2051d10f8a1SJianguo Wu 	struct list_head *ptype_list = NULL;
206900ff8c6SCong Wang 	struct packet_type *pt = NULL;
2071d10f8a1SJianguo Wu 	struct net_device *dev;
208900ff8c6SCong Wang 	loff_t i = 0;
209900ff8c6SCong Wang 	int t;
210900ff8c6SCong Wang 
2111d10f8a1SJianguo Wu 	for_each_netdev_rcu(seq_file_net(seq), dev) {
2121d10f8a1SJianguo Wu 		ptype_list = &dev->ptype_all;
2131d10f8a1SJianguo Wu 		list_for_each_entry_rcu(pt, ptype_list, list) {
2141d10f8a1SJianguo Wu 			if (i == pos)
2151d10f8a1SJianguo Wu 				return pt;
2161d10f8a1SJianguo Wu 			++i;
2171d10f8a1SJianguo Wu 		}
2181d10f8a1SJianguo Wu 	}
2191d10f8a1SJianguo Wu 
220900ff8c6SCong Wang 	list_for_each_entry_rcu(pt, &ptype_all, list) {
221900ff8c6SCong Wang 		if (i == pos)
222900ff8c6SCong Wang 			return pt;
223900ff8c6SCong Wang 		++i;
224900ff8c6SCong Wang 	}
225900ff8c6SCong Wang 
226900ff8c6SCong Wang 	for (t = 0; t < PTYPE_HASH_SIZE; t++) {
227900ff8c6SCong Wang 		list_for_each_entry_rcu(pt, &ptype_base[t], list) {
228900ff8c6SCong Wang 			if (i == pos)
229900ff8c6SCong Wang 				return pt;
230900ff8c6SCong Wang 			++i;
231900ff8c6SCong Wang 		}
232900ff8c6SCong Wang 	}
233900ff8c6SCong Wang 	return NULL;
234900ff8c6SCong Wang }
235900ff8c6SCong Wang 
ptype_seq_start(struct seq_file * seq,loff_t * pos)236900ff8c6SCong Wang static void *ptype_seq_start(struct seq_file *seq, loff_t *pos)
237900ff8c6SCong Wang 	__acquires(RCU)
238900ff8c6SCong Wang {
239900ff8c6SCong Wang 	rcu_read_lock();
2401d10f8a1SJianguo Wu 	return *pos ? ptype_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;
241900ff8c6SCong Wang }
242900ff8c6SCong Wang 
ptype_seq_next(struct seq_file * seq,void * v,loff_t * pos)243900ff8c6SCong Wang static void *ptype_seq_next(struct seq_file *seq, void *v, loff_t *pos)
244900ff8c6SCong Wang {
2451d10f8a1SJianguo Wu 	struct net_device *dev;
246900ff8c6SCong Wang 	struct packet_type *pt;
247900ff8c6SCong Wang 	struct list_head *nxt;
248900ff8c6SCong Wang 	int hash;
249900ff8c6SCong Wang 
250900ff8c6SCong Wang 	++*pos;
251900ff8c6SCong Wang 	if (v == SEQ_START_TOKEN)
2521d10f8a1SJianguo Wu 		return ptype_get_idx(seq, 0);
253900ff8c6SCong Wang 
254900ff8c6SCong Wang 	pt = v;
255900ff8c6SCong Wang 	nxt = pt->list.next;
2561d10f8a1SJianguo Wu 	if (pt->dev) {
2571d10f8a1SJianguo Wu 		if (nxt != &pt->dev->ptype_all)
2581d10f8a1SJianguo Wu 			goto found;
2591d10f8a1SJianguo Wu 
2601d10f8a1SJianguo Wu 		dev = pt->dev;
2611d10f8a1SJianguo Wu 		for_each_netdev_continue_rcu(seq_file_net(seq), dev) {
2621d10f8a1SJianguo Wu 			if (!list_empty(&dev->ptype_all)) {
2631d10f8a1SJianguo Wu 				nxt = dev->ptype_all.next;
2641d10f8a1SJianguo Wu 				goto found;
2651d10f8a1SJianguo Wu 			}
2661d10f8a1SJianguo Wu 		}
2671d10f8a1SJianguo Wu 
2681d10f8a1SJianguo Wu 		nxt = ptype_all.next;
2691d10f8a1SJianguo Wu 		goto ptype_all;
2701d10f8a1SJianguo Wu 	}
2711d10f8a1SJianguo Wu 
272900ff8c6SCong Wang 	if (pt->type == htons(ETH_P_ALL)) {
2731d10f8a1SJianguo Wu ptype_all:
274900ff8c6SCong Wang 		if (nxt != &ptype_all)
275900ff8c6SCong Wang 			goto found;
276900ff8c6SCong Wang 		hash = 0;
277900ff8c6SCong Wang 		nxt = ptype_base[0].next;
278900ff8c6SCong Wang 	} else
279900ff8c6SCong Wang 		hash = ntohs(pt->type) & PTYPE_HASH_MASK;
280900ff8c6SCong Wang 
281900ff8c6SCong Wang 	while (nxt == &ptype_base[hash]) {
282900ff8c6SCong Wang 		if (++hash >= PTYPE_HASH_SIZE)
283900ff8c6SCong Wang 			return NULL;
284900ff8c6SCong Wang 		nxt = ptype_base[hash].next;
285900ff8c6SCong Wang 	}
286900ff8c6SCong Wang found:
287900ff8c6SCong Wang 	return list_entry(nxt, struct packet_type, list);
288900ff8c6SCong Wang }
289900ff8c6SCong Wang 
ptype_seq_stop(struct seq_file * seq,void * v)290900ff8c6SCong Wang static void ptype_seq_stop(struct seq_file *seq, void *v)
291900ff8c6SCong Wang 	__releases(RCU)
292900ff8c6SCong Wang {
293900ff8c6SCong Wang 	rcu_read_unlock();
294900ff8c6SCong Wang }
295900ff8c6SCong Wang 
ptype_seq_show(struct seq_file * seq,void * v)296900ff8c6SCong Wang static int ptype_seq_show(struct seq_file *seq, void *v)
297900ff8c6SCong Wang {
298900ff8c6SCong Wang 	struct packet_type *pt = v;
299900ff8c6SCong Wang 
300900ff8c6SCong Wang 	if (v == SEQ_START_TOKEN)
301900ff8c6SCong Wang 		seq_puts(seq, "Type Device      Function\n");
30247934e06SCongyu Liu 	else if ((!pt->af_packet_net || net_eq(pt->af_packet_net, seq_file_net(seq))) &&
30347934e06SCongyu Liu 		 (!pt->dev || net_eq(dev_net(pt->dev), seq_file_net(seq)))) {
304900ff8c6SCong Wang 		if (pt->type == htons(ETH_P_ALL))
305900ff8c6SCong Wang 			seq_puts(seq, "ALL ");
306900ff8c6SCong Wang 		else
307900ff8c6SCong Wang 			seq_printf(seq, "%04x", ntohs(pt->type));
308900ff8c6SCong Wang 
3091f922d9eSVladimir Oltean 		seq_printf(seq, " %-8s %ps\n",
310900ff8c6SCong Wang 			   pt->dev ? pt->dev->name : "", pt->func);
311900ff8c6SCong Wang 	}
312900ff8c6SCong Wang 
313900ff8c6SCong Wang 	return 0;
314900ff8c6SCong Wang }
315900ff8c6SCong Wang 
316900ff8c6SCong Wang static const struct seq_operations ptype_seq_ops = {
317900ff8c6SCong Wang 	.start = ptype_seq_start,
318900ff8c6SCong Wang 	.next  = ptype_seq_next,
319900ff8c6SCong Wang 	.stop  = ptype_seq_stop,
320900ff8c6SCong Wang 	.show  = ptype_seq_show,
321900ff8c6SCong Wang };
322900ff8c6SCong Wang 
dev_proc_net_init(struct net * net)323900ff8c6SCong Wang static int __net_init dev_proc_net_init(struct net *net)
324900ff8c6SCong Wang {
325900ff8c6SCong Wang 	int rc = -ENOMEM;
326900ff8c6SCong Wang 
327c3506372SChristoph Hellwig 	if (!proc_create_net("dev", 0444, net->proc_net, &dev_seq_ops,
328c3506372SChristoph Hellwig 			sizeof(struct seq_net_private)))
329900ff8c6SCong Wang 		goto out;
330fddda2b7SChristoph Hellwig 	if (!proc_create_seq("softnet_stat", 0444, net->proc_net,
331fddda2b7SChristoph Hellwig 			 &softnet_seq_ops))
332900ff8c6SCong Wang 		goto out_dev;
333c3506372SChristoph Hellwig 	if (!proc_create_net("ptype", 0444, net->proc_net, &ptype_seq_ops,
334c3506372SChristoph Hellwig 			sizeof(struct seq_net_private)))
335900ff8c6SCong Wang 		goto out_softnet;
336900ff8c6SCong Wang 
337900ff8c6SCong Wang 	if (wext_proc_init(net))
338900ff8c6SCong Wang 		goto out_ptype;
339900ff8c6SCong Wang 	rc = 0;
340900ff8c6SCong Wang out:
341900ff8c6SCong Wang 	return rc;
342900ff8c6SCong Wang out_ptype:
343900ff8c6SCong Wang 	remove_proc_entry("ptype", net->proc_net);
344900ff8c6SCong Wang out_softnet:
345900ff8c6SCong Wang 	remove_proc_entry("softnet_stat", net->proc_net);
346900ff8c6SCong Wang out_dev:
347900ff8c6SCong Wang 	remove_proc_entry("dev", net->proc_net);
348900ff8c6SCong Wang 	goto out;
349900ff8c6SCong Wang }
350900ff8c6SCong Wang 
dev_proc_net_exit(struct net * net)351900ff8c6SCong Wang static void __net_exit dev_proc_net_exit(struct net *net)
352900ff8c6SCong Wang {
353900ff8c6SCong Wang 	wext_proc_exit(net);
354900ff8c6SCong Wang 
355900ff8c6SCong Wang 	remove_proc_entry("ptype", net->proc_net);
356900ff8c6SCong Wang 	remove_proc_entry("softnet_stat", net->proc_net);
357900ff8c6SCong Wang 	remove_proc_entry("dev", net->proc_net);
358900ff8c6SCong Wang }
359900ff8c6SCong Wang 
360900ff8c6SCong Wang static struct pernet_operations __net_initdata dev_proc_ops = {
361900ff8c6SCong Wang 	.init = dev_proc_net_init,
362900ff8c6SCong Wang 	.exit = dev_proc_net_exit,
363900ff8c6SCong Wang };
364900ff8c6SCong Wang 
dev_mc_seq_show(struct seq_file * seq,void * v)365900ff8c6SCong Wang static int dev_mc_seq_show(struct seq_file *seq, void *v)
366900ff8c6SCong Wang {
367900ff8c6SCong Wang 	struct netdev_hw_addr *ha;
368900ff8c6SCong Wang 	struct net_device *dev = v;
369900ff8c6SCong Wang 
3701f922d9eSVladimir Oltean 	if (v == SEQ_START_TOKEN)
371900ff8c6SCong Wang 		return 0;
372900ff8c6SCong Wang 
373900ff8c6SCong Wang 	netif_addr_lock_bh(dev);
374900ff8c6SCong Wang 	netdev_for_each_mc_addr(ha, dev) {
3751f922d9eSVladimir Oltean 		seq_printf(seq, "%-4d %-15s %-5d %-5d %*phN\n",
376fbd0ac60SJoe Perches 			   dev->ifindex, dev->name,
377fbd0ac60SJoe Perches 			   ha->refcount, ha->global_use,
378fbd0ac60SJoe Perches 			   (int)dev->addr_len, ha->addr);
379900ff8c6SCong Wang 	}
380900ff8c6SCong Wang 	netif_addr_unlock_bh(dev);
381900ff8c6SCong Wang 	return 0;
382900ff8c6SCong Wang }
383900ff8c6SCong Wang 
384900ff8c6SCong Wang static const struct seq_operations dev_mc_seq_ops = {
385900ff8c6SCong Wang 	.start = dev_seq_start,
386900ff8c6SCong Wang 	.next  = dev_seq_next,
387900ff8c6SCong Wang 	.stop  = dev_seq_stop,
388900ff8c6SCong Wang 	.show  = dev_mc_seq_show,
389900ff8c6SCong Wang };
390900ff8c6SCong Wang 
dev_mc_net_init(struct net * net)391900ff8c6SCong Wang static int __net_init dev_mc_net_init(struct net *net)
392900ff8c6SCong Wang {
393c3506372SChristoph Hellwig 	if (!proc_create_net("dev_mcast", 0, net->proc_net, &dev_mc_seq_ops,
394c3506372SChristoph Hellwig 			sizeof(struct seq_net_private)))
395900ff8c6SCong Wang 		return -ENOMEM;
396900ff8c6SCong Wang 	return 0;
397900ff8c6SCong Wang }
398900ff8c6SCong Wang 
dev_mc_net_exit(struct net * net)399900ff8c6SCong Wang static void __net_exit dev_mc_net_exit(struct net *net)
400900ff8c6SCong Wang {
401900ff8c6SCong Wang 	remove_proc_entry("dev_mcast", net->proc_net);
402900ff8c6SCong Wang }
403900ff8c6SCong Wang 
404900ff8c6SCong Wang static struct pernet_operations __net_initdata dev_mc_net_ops = {
405900ff8c6SCong Wang 	.init = dev_mc_net_init,
406900ff8c6SCong Wang 	.exit = dev_mc_net_exit,
407900ff8c6SCong Wang };
408900ff8c6SCong Wang 
dev_proc_init(void)409cd061574SCong Wang int __init dev_proc_init(void)
410900ff8c6SCong Wang {
411cd061574SCong Wang 	int ret = register_pernet_subsys(&dev_proc_ops);
412cd061574SCong Wang 	if (!ret)
413cd061574SCong Wang 		return register_pernet_subsys(&dev_mc_net_ops);
414cd061574SCong Wang 	return ret;
415900ff8c6SCong Wang }
416