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