162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/netdevice.h> 362306a36Sopenharmony_ci#include <linux/proc_fs.h> 462306a36Sopenharmony_ci#include <linux/seq_file.h> 562306a36Sopenharmony_ci#include <net/wext.h> 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include "dev.h" 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#define BUCKET_SPACE (32 - NETDEV_HASHBITS - 1) 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#define get_bucket(x) ((x) >> BUCKET_SPACE) 1262306a36Sopenharmony_ci#define get_offset(x) ((x) & ((1 << BUCKET_SPACE) - 1)) 1362306a36Sopenharmony_ci#define set_bucket_offset(b, o) ((b) << BUCKET_SPACE | (o)) 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistatic inline struct net_device *dev_from_same_bucket(struct seq_file *seq, loff_t *pos) 1662306a36Sopenharmony_ci{ 1762306a36Sopenharmony_ci struct net *net = seq_file_net(seq); 1862306a36Sopenharmony_ci struct net_device *dev; 1962306a36Sopenharmony_ci struct hlist_head *h; 2062306a36Sopenharmony_ci unsigned int count = 0, offset = get_offset(*pos); 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci h = &net->dev_index_head[get_bucket(*pos)]; 2362306a36Sopenharmony_ci hlist_for_each_entry_rcu(dev, h, index_hlist) { 2462306a36Sopenharmony_ci if (++count == offset) 2562306a36Sopenharmony_ci return dev; 2662306a36Sopenharmony_ci } 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci return NULL; 2962306a36Sopenharmony_ci} 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic inline struct net_device *dev_from_bucket(struct seq_file *seq, loff_t *pos) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci struct net_device *dev; 3462306a36Sopenharmony_ci unsigned int bucket; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci do { 3762306a36Sopenharmony_ci dev = dev_from_same_bucket(seq, pos); 3862306a36Sopenharmony_ci if (dev) 3962306a36Sopenharmony_ci return dev; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci bucket = get_bucket(*pos) + 1; 4262306a36Sopenharmony_ci *pos = set_bucket_offset(bucket, 1); 4362306a36Sopenharmony_ci } while (bucket < NETDEV_HASHENTRIES); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci return NULL; 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* 4962306a36Sopenharmony_ci * This is invoked by the /proc filesystem handler to display a device 5062306a36Sopenharmony_ci * in detail. 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_cistatic void *dev_seq_start(struct seq_file *seq, loff_t *pos) 5362306a36Sopenharmony_ci __acquires(RCU) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci rcu_read_lock(); 5662306a36Sopenharmony_ci if (!*pos) 5762306a36Sopenharmony_ci return SEQ_START_TOKEN; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci if (get_bucket(*pos) >= NETDEV_HASHENTRIES) 6062306a36Sopenharmony_ci return NULL; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci return dev_from_bucket(seq, pos); 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci ++*pos; 6862306a36Sopenharmony_ci return dev_from_bucket(seq, pos); 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic void dev_seq_stop(struct seq_file *seq, void *v) 7262306a36Sopenharmony_ci __releases(RCU) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci rcu_read_unlock(); 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic void dev_seq_printf_stats(struct seq_file *seq, struct net_device *dev) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci struct rtnl_link_stats64 temp; 8062306a36Sopenharmony_ci const struct rtnl_link_stats64 *stats = dev_get_stats(dev, &temp); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci seq_printf(seq, "%6s: %7llu %7llu %4llu %4llu %4llu %5llu %10llu %9llu " 8362306a36Sopenharmony_ci "%8llu %7llu %4llu %4llu %4llu %5llu %7llu %10llu\n", 8462306a36Sopenharmony_ci dev->name, stats->rx_bytes, stats->rx_packets, 8562306a36Sopenharmony_ci stats->rx_errors, 8662306a36Sopenharmony_ci stats->rx_dropped + stats->rx_missed_errors, 8762306a36Sopenharmony_ci stats->rx_fifo_errors, 8862306a36Sopenharmony_ci stats->rx_length_errors + stats->rx_over_errors + 8962306a36Sopenharmony_ci stats->rx_crc_errors + stats->rx_frame_errors, 9062306a36Sopenharmony_ci stats->rx_compressed, stats->multicast, 9162306a36Sopenharmony_ci stats->tx_bytes, stats->tx_packets, 9262306a36Sopenharmony_ci stats->tx_errors, stats->tx_dropped, 9362306a36Sopenharmony_ci stats->tx_fifo_errors, stats->collisions, 9462306a36Sopenharmony_ci stats->tx_carrier_errors + 9562306a36Sopenharmony_ci stats->tx_aborted_errors + 9662306a36Sopenharmony_ci stats->tx_window_errors + 9762306a36Sopenharmony_ci stats->tx_heartbeat_errors, 9862306a36Sopenharmony_ci stats->tx_compressed); 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci/* 10262306a36Sopenharmony_ci * Called from the PROCfs module. This now uses the new arbitrary sized 10362306a36Sopenharmony_ci * /proc/net interface to create /proc/net/dev 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_cistatic int dev_seq_show(struct seq_file *seq, void *v) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci if (v == SEQ_START_TOKEN) 10862306a36Sopenharmony_ci seq_puts(seq, "Inter-| Receive " 10962306a36Sopenharmony_ci " | Transmit\n" 11062306a36Sopenharmony_ci " face |bytes packets errs drop fifo frame " 11162306a36Sopenharmony_ci "compressed multicast|bytes packets errs " 11262306a36Sopenharmony_ci "drop fifo colls carrier compressed\n"); 11362306a36Sopenharmony_ci else 11462306a36Sopenharmony_ci dev_seq_printf_stats(seq, v); 11562306a36Sopenharmony_ci return 0; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic u32 softnet_input_pkt_queue_len(struct softnet_data *sd) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci return skb_queue_len_lockless(&sd->input_pkt_queue); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic u32 softnet_process_queue_len(struct softnet_data *sd) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci return skb_queue_len_lockless(&sd->process_queue); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic struct softnet_data *softnet_get_online(loff_t *pos) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct softnet_data *sd = NULL; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci while (*pos < nr_cpu_ids) 13362306a36Sopenharmony_ci if (cpu_online(*pos)) { 13462306a36Sopenharmony_ci sd = &per_cpu(softnet_data, *pos); 13562306a36Sopenharmony_ci break; 13662306a36Sopenharmony_ci } else 13762306a36Sopenharmony_ci ++*pos; 13862306a36Sopenharmony_ci return sd; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic void *softnet_seq_start(struct seq_file *seq, loff_t *pos) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci return softnet_get_online(pos); 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic void *softnet_seq_next(struct seq_file *seq, void *v, loff_t *pos) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci ++*pos; 14962306a36Sopenharmony_ci return softnet_get_online(pos); 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic void softnet_seq_stop(struct seq_file *seq, void *v) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic int softnet_seq_show(struct seq_file *seq, void *v) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci struct softnet_data *sd = v; 15962306a36Sopenharmony_ci u32 input_qlen = softnet_input_pkt_queue_len(sd); 16062306a36Sopenharmony_ci u32 process_qlen = softnet_process_queue_len(sd); 16162306a36Sopenharmony_ci unsigned int flow_limit_count = 0; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci#ifdef CONFIG_NET_FLOW_LIMIT 16462306a36Sopenharmony_ci struct sd_flow_limit *fl; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci rcu_read_lock(); 16762306a36Sopenharmony_ci fl = rcu_dereference(sd->flow_limit); 16862306a36Sopenharmony_ci if (fl) 16962306a36Sopenharmony_ci flow_limit_count = fl->count; 17062306a36Sopenharmony_ci rcu_read_unlock(); 17162306a36Sopenharmony_ci#endif 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* the index is the CPU id owing this sd. Since offline CPUs are not 17462306a36Sopenharmony_ci * displayed, it would be othrwise not trivial for the user-space 17562306a36Sopenharmony_ci * mapping the data a specific CPU 17662306a36Sopenharmony_ci */ 17762306a36Sopenharmony_ci seq_printf(seq, 17862306a36Sopenharmony_ci "%08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x " 17962306a36Sopenharmony_ci "%08x %08x\n", 18062306a36Sopenharmony_ci sd->processed, sd->dropped, sd->time_squeeze, 0, 18162306a36Sopenharmony_ci 0, 0, 0, 0, /* was fastroute */ 18262306a36Sopenharmony_ci 0, /* was cpu_collision */ 18362306a36Sopenharmony_ci sd->received_rps, flow_limit_count, 18462306a36Sopenharmony_ci input_qlen + process_qlen, (int)seq->index, 18562306a36Sopenharmony_ci input_qlen, process_qlen); 18662306a36Sopenharmony_ci return 0; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic const struct seq_operations dev_seq_ops = { 19062306a36Sopenharmony_ci .start = dev_seq_start, 19162306a36Sopenharmony_ci .next = dev_seq_next, 19262306a36Sopenharmony_ci .stop = dev_seq_stop, 19362306a36Sopenharmony_ci .show = dev_seq_show, 19462306a36Sopenharmony_ci}; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic const struct seq_operations softnet_seq_ops = { 19762306a36Sopenharmony_ci .start = softnet_seq_start, 19862306a36Sopenharmony_ci .next = softnet_seq_next, 19962306a36Sopenharmony_ci .stop = softnet_seq_stop, 20062306a36Sopenharmony_ci .show = softnet_seq_show, 20162306a36Sopenharmony_ci}; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic void *ptype_get_idx(struct seq_file *seq, loff_t pos) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci struct list_head *ptype_list = NULL; 20662306a36Sopenharmony_ci struct packet_type *pt = NULL; 20762306a36Sopenharmony_ci struct net_device *dev; 20862306a36Sopenharmony_ci loff_t i = 0; 20962306a36Sopenharmony_ci int t; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci for_each_netdev_rcu(seq_file_net(seq), dev) { 21262306a36Sopenharmony_ci ptype_list = &dev->ptype_all; 21362306a36Sopenharmony_ci list_for_each_entry_rcu(pt, ptype_list, list) { 21462306a36Sopenharmony_ci if (i == pos) 21562306a36Sopenharmony_ci return pt; 21662306a36Sopenharmony_ci ++i; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci list_for_each_entry_rcu(pt, &ptype_all, list) { 22162306a36Sopenharmony_ci if (i == pos) 22262306a36Sopenharmony_ci return pt; 22362306a36Sopenharmony_ci ++i; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci for (t = 0; t < PTYPE_HASH_SIZE; t++) { 22762306a36Sopenharmony_ci list_for_each_entry_rcu(pt, &ptype_base[t], list) { 22862306a36Sopenharmony_ci if (i == pos) 22962306a36Sopenharmony_ci return pt; 23062306a36Sopenharmony_ci ++i; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci return NULL; 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic void *ptype_seq_start(struct seq_file *seq, loff_t *pos) 23762306a36Sopenharmony_ci __acquires(RCU) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci rcu_read_lock(); 24062306a36Sopenharmony_ci return *pos ? ptype_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic void *ptype_seq_next(struct seq_file *seq, void *v, loff_t *pos) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci struct net_device *dev; 24662306a36Sopenharmony_ci struct packet_type *pt; 24762306a36Sopenharmony_ci struct list_head *nxt; 24862306a36Sopenharmony_ci int hash; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci ++*pos; 25162306a36Sopenharmony_ci if (v == SEQ_START_TOKEN) 25262306a36Sopenharmony_ci return ptype_get_idx(seq, 0); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci pt = v; 25562306a36Sopenharmony_ci nxt = pt->list.next; 25662306a36Sopenharmony_ci if (pt->dev) { 25762306a36Sopenharmony_ci if (nxt != &pt->dev->ptype_all) 25862306a36Sopenharmony_ci goto found; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci dev = pt->dev; 26162306a36Sopenharmony_ci for_each_netdev_continue_rcu(seq_file_net(seq), dev) { 26262306a36Sopenharmony_ci if (!list_empty(&dev->ptype_all)) { 26362306a36Sopenharmony_ci nxt = dev->ptype_all.next; 26462306a36Sopenharmony_ci goto found; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci nxt = ptype_all.next; 26962306a36Sopenharmony_ci goto ptype_all; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (pt->type == htons(ETH_P_ALL)) { 27362306a36Sopenharmony_ciptype_all: 27462306a36Sopenharmony_ci if (nxt != &ptype_all) 27562306a36Sopenharmony_ci goto found; 27662306a36Sopenharmony_ci hash = 0; 27762306a36Sopenharmony_ci nxt = ptype_base[0].next; 27862306a36Sopenharmony_ci } else 27962306a36Sopenharmony_ci hash = ntohs(pt->type) & PTYPE_HASH_MASK; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci while (nxt == &ptype_base[hash]) { 28262306a36Sopenharmony_ci if (++hash >= PTYPE_HASH_SIZE) 28362306a36Sopenharmony_ci return NULL; 28462306a36Sopenharmony_ci nxt = ptype_base[hash].next; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_cifound: 28762306a36Sopenharmony_ci return list_entry(nxt, struct packet_type, list); 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic void ptype_seq_stop(struct seq_file *seq, void *v) 29162306a36Sopenharmony_ci __releases(RCU) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci rcu_read_unlock(); 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic int ptype_seq_show(struct seq_file *seq, void *v) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci struct packet_type *pt = v; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (v == SEQ_START_TOKEN) 30162306a36Sopenharmony_ci seq_puts(seq, "Type Device Function\n"); 30262306a36Sopenharmony_ci else if ((!pt->af_packet_net || net_eq(pt->af_packet_net, seq_file_net(seq))) && 30362306a36Sopenharmony_ci (!pt->dev || net_eq(dev_net(pt->dev), seq_file_net(seq)))) { 30462306a36Sopenharmony_ci if (pt->type == htons(ETH_P_ALL)) 30562306a36Sopenharmony_ci seq_puts(seq, "ALL "); 30662306a36Sopenharmony_ci else 30762306a36Sopenharmony_ci seq_printf(seq, "%04x", ntohs(pt->type)); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci seq_printf(seq, " %-8s %ps\n", 31062306a36Sopenharmony_ci pt->dev ? pt->dev->name : "", pt->func); 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci return 0; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic const struct seq_operations ptype_seq_ops = { 31762306a36Sopenharmony_ci .start = ptype_seq_start, 31862306a36Sopenharmony_ci .next = ptype_seq_next, 31962306a36Sopenharmony_ci .stop = ptype_seq_stop, 32062306a36Sopenharmony_ci .show = ptype_seq_show, 32162306a36Sopenharmony_ci}; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic int __net_init dev_proc_net_init(struct net *net) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci int rc = -ENOMEM; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (!proc_create_net("dev", 0444, net->proc_net, &dev_seq_ops, 32862306a36Sopenharmony_ci sizeof(struct seq_net_private))) 32962306a36Sopenharmony_ci goto out; 33062306a36Sopenharmony_ci if (!proc_create_seq("softnet_stat", 0444, net->proc_net, 33162306a36Sopenharmony_ci &softnet_seq_ops)) 33262306a36Sopenharmony_ci goto out_dev; 33362306a36Sopenharmony_ci if (!proc_create_net("ptype", 0444, net->proc_net, &ptype_seq_ops, 33462306a36Sopenharmony_ci sizeof(struct seq_net_private))) 33562306a36Sopenharmony_ci goto out_softnet; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (wext_proc_init(net)) 33862306a36Sopenharmony_ci goto out_ptype; 33962306a36Sopenharmony_ci rc = 0; 34062306a36Sopenharmony_ciout: 34162306a36Sopenharmony_ci return rc; 34262306a36Sopenharmony_ciout_ptype: 34362306a36Sopenharmony_ci remove_proc_entry("ptype", net->proc_net); 34462306a36Sopenharmony_ciout_softnet: 34562306a36Sopenharmony_ci remove_proc_entry("softnet_stat", net->proc_net); 34662306a36Sopenharmony_ciout_dev: 34762306a36Sopenharmony_ci remove_proc_entry("dev", net->proc_net); 34862306a36Sopenharmony_ci goto out; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic void __net_exit dev_proc_net_exit(struct net *net) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci wext_proc_exit(net); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci remove_proc_entry("ptype", net->proc_net); 35662306a36Sopenharmony_ci remove_proc_entry("softnet_stat", net->proc_net); 35762306a36Sopenharmony_ci remove_proc_entry("dev", net->proc_net); 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic struct pernet_operations __net_initdata dev_proc_ops = { 36162306a36Sopenharmony_ci .init = dev_proc_net_init, 36262306a36Sopenharmony_ci .exit = dev_proc_net_exit, 36362306a36Sopenharmony_ci}; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic int dev_mc_seq_show(struct seq_file *seq, void *v) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci struct netdev_hw_addr *ha; 36862306a36Sopenharmony_ci struct net_device *dev = v; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (v == SEQ_START_TOKEN) 37162306a36Sopenharmony_ci return 0; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci netif_addr_lock_bh(dev); 37462306a36Sopenharmony_ci netdev_for_each_mc_addr(ha, dev) { 37562306a36Sopenharmony_ci seq_printf(seq, "%-4d %-15s %-5d %-5d %*phN\n", 37662306a36Sopenharmony_ci dev->ifindex, dev->name, 37762306a36Sopenharmony_ci ha->refcount, ha->global_use, 37862306a36Sopenharmony_ci (int)dev->addr_len, ha->addr); 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci netif_addr_unlock_bh(dev); 38162306a36Sopenharmony_ci return 0; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic const struct seq_operations dev_mc_seq_ops = { 38562306a36Sopenharmony_ci .start = dev_seq_start, 38662306a36Sopenharmony_ci .next = dev_seq_next, 38762306a36Sopenharmony_ci .stop = dev_seq_stop, 38862306a36Sopenharmony_ci .show = dev_mc_seq_show, 38962306a36Sopenharmony_ci}; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic int __net_init dev_mc_net_init(struct net *net) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci if (!proc_create_net("dev_mcast", 0, net->proc_net, &dev_mc_seq_ops, 39462306a36Sopenharmony_ci sizeof(struct seq_net_private))) 39562306a36Sopenharmony_ci return -ENOMEM; 39662306a36Sopenharmony_ci return 0; 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic void __net_exit dev_mc_net_exit(struct net *net) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci remove_proc_entry("dev_mcast", net->proc_net); 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_cistatic struct pernet_operations __net_initdata dev_mc_net_ops = { 40562306a36Sopenharmony_ci .init = dev_mc_net_init, 40662306a36Sopenharmony_ci .exit = dev_mc_net_exit, 40762306a36Sopenharmony_ci}; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ciint __init dev_proc_init(void) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci int ret = register_pernet_subsys(&dev_proc_ops); 41262306a36Sopenharmony_ci if (!ret) 41362306a36Sopenharmony_ci return register_pernet_subsys(&dev_mc_net_ops); 41462306a36Sopenharmony_ci return ret; 41562306a36Sopenharmony_ci} 416