Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <linux/netdevice.h>
0003 #include <linux/proc_fs.h>
0004 #include <linux/seq_file.h>
0005 #include <net/wext.h>
0006 
0007 #include "dev.h"
0008 
0009 #define BUCKET_SPACE (32 - NETDEV_HASHBITS - 1)
0010 
0011 #define get_bucket(x) ((x) >> BUCKET_SPACE)
0012 #define get_offset(x) ((x) & ((1 << BUCKET_SPACE) - 1))
0013 #define set_bucket_offset(b, o) ((b) << BUCKET_SPACE | (o))
0014 
0015 static inline struct net_device *dev_from_same_bucket(struct seq_file *seq, loff_t *pos)
0016 {
0017     struct net *net = seq_file_net(seq);
0018     struct net_device *dev;
0019     struct hlist_head *h;
0020     unsigned int count = 0, offset = get_offset(*pos);
0021 
0022     h = &net->dev_index_head[get_bucket(*pos)];
0023     hlist_for_each_entry_rcu(dev, h, index_hlist) {
0024         if (++count == offset)
0025             return dev;
0026     }
0027 
0028     return NULL;
0029 }
0030 
0031 static inline struct net_device *dev_from_bucket(struct seq_file *seq, loff_t *pos)
0032 {
0033     struct net_device *dev;
0034     unsigned int bucket;
0035 
0036     do {
0037         dev = dev_from_same_bucket(seq, pos);
0038         if (dev)
0039             return dev;
0040 
0041         bucket = get_bucket(*pos) + 1;
0042         *pos = set_bucket_offset(bucket, 1);
0043     } while (bucket < NETDEV_HASHENTRIES);
0044 
0045     return NULL;
0046 }
0047 
0048 /*
0049  *  This is invoked by the /proc filesystem handler to display a device
0050  *  in detail.
0051  */
0052 static void *dev_seq_start(struct seq_file *seq, loff_t *pos)
0053     __acquires(RCU)
0054 {
0055     rcu_read_lock();
0056     if (!*pos)
0057         return SEQ_START_TOKEN;
0058 
0059     if (get_bucket(*pos) >= NETDEV_HASHENTRIES)
0060         return NULL;
0061 
0062     return dev_from_bucket(seq, pos);
0063 }
0064 
0065 static void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos)
0066 {
0067     ++*pos;
0068     return dev_from_bucket(seq, pos);
0069 }
0070 
0071 static void dev_seq_stop(struct seq_file *seq, void *v)
0072     __releases(RCU)
0073 {
0074     rcu_read_unlock();
0075 }
0076 
0077 static void dev_seq_printf_stats(struct seq_file *seq, struct net_device *dev)
0078 {
0079     struct rtnl_link_stats64 temp;
0080     const struct rtnl_link_stats64 *stats = dev_get_stats(dev, &temp);
0081 
0082     seq_printf(seq, "%6s: %7llu %7llu %4llu %4llu %4llu %5llu %10llu %9llu "
0083            "%8llu %7llu %4llu %4llu %4llu %5llu %7llu %10llu\n",
0084            dev->name, stats->rx_bytes, stats->rx_packets,
0085            stats->rx_errors,
0086            stats->rx_dropped + stats->rx_missed_errors,
0087            stats->rx_fifo_errors,
0088            stats->rx_length_errors + stats->rx_over_errors +
0089             stats->rx_crc_errors + stats->rx_frame_errors,
0090            stats->rx_compressed, stats->multicast,
0091            stats->tx_bytes, stats->tx_packets,
0092            stats->tx_errors, stats->tx_dropped,
0093            stats->tx_fifo_errors, stats->collisions,
0094            stats->tx_carrier_errors +
0095             stats->tx_aborted_errors +
0096             stats->tx_window_errors +
0097             stats->tx_heartbeat_errors,
0098            stats->tx_compressed);
0099 }
0100 
0101 /*
0102  *  Called from the PROCfs module. This now uses the new arbitrary sized
0103  *  /proc/net interface to create /proc/net/dev
0104  */
0105 static int dev_seq_show(struct seq_file *seq, void *v)
0106 {
0107     if (v == SEQ_START_TOKEN)
0108         seq_puts(seq, "Inter-|   Receive                            "
0109                   "                    |  Transmit\n"
0110                   " face |bytes    packets errs drop fifo frame "
0111                   "compressed multicast|bytes    packets errs "
0112                   "drop fifo colls carrier compressed\n");
0113     else
0114         dev_seq_printf_stats(seq, v);
0115     return 0;
0116 }
0117 
0118 static u32 softnet_backlog_len(struct softnet_data *sd)
0119 {
0120     return skb_queue_len_lockless(&sd->input_pkt_queue) +
0121            skb_queue_len_lockless(&sd->process_queue);
0122 }
0123 
0124 static struct softnet_data *softnet_get_online(loff_t *pos)
0125 {
0126     struct softnet_data *sd = NULL;
0127 
0128     while (*pos < nr_cpu_ids)
0129         if (cpu_online(*pos)) {
0130             sd = &per_cpu(softnet_data, *pos);
0131             break;
0132         } else
0133             ++*pos;
0134     return sd;
0135 }
0136 
0137 static void *softnet_seq_start(struct seq_file *seq, loff_t *pos)
0138 {
0139     return softnet_get_online(pos);
0140 }
0141 
0142 static void *softnet_seq_next(struct seq_file *seq, void *v, loff_t *pos)
0143 {
0144     ++*pos;
0145     return softnet_get_online(pos);
0146 }
0147 
0148 static void softnet_seq_stop(struct seq_file *seq, void *v)
0149 {
0150 }
0151 
0152 static int softnet_seq_show(struct seq_file *seq, void *v)
0153 {
0154     struct softnet_data *sd = v;
0155     unsigned int flow_limit_count = 0;
0156 
0157 #ifdef CONFIG_NET_FLOW_LIMIT
0158     struct sd_flow_limit *fl;
0159 
0160     rcu_read_lock();
0161     fl = rcu_dereference(sd->flow_limit);
0162     if (fl)
0163         flow_limit_count = fl->count;
0164     rcu_read_unlock();
0165 #endif
0166 
0167     /* the index is the CPU id owing this sd. Since offline CPUs are not
0168      * displayed, it would be othrwise not trivial for the user-space
0169      * mapping the data a specific CPU
0170      */
0171     seq_printf(seq,
0172            "%08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n",
0173            sd->processed, sd->dropped, sd->time_squeeze, 0,
0174            0, 0, 0, 0, /* was fastroute */
0175            0,   /* was cpu_collision */
0176            sd->received_rps, flow_limit_count,
0177            softnet_backlog_len(sd), (int)seq->index);
0178     return 0;
0179 }
0180 
0181 static const struct seq_operations dev_seq_ops = {
0182     .start = dev_seq_start,
0183     .next  = dev_seq_next,
0184     .stop  = dev_seq_stop,
0185     .show  = dev_seq_show,
0186 };
0187 
0188 static const struct seq_operations softnet_seq_ops = {
0189     .start = softnet_seq_start,
0190     .next  = softnet_seq_next,
0191     .stop  = softnet_seq_stop,
0192     .show  = softnet_seq_show,
0193 };
0194 
0195 static void *ptype_get_idx(struct seq_file *seq, loff_t pos)
0196 {
0197     struct list_head *ptype_list = NULL;
0198     struct packet_type *pt = NULL;
0199     struct net_device *dev;
0200     loff_t i = 0;
0201     int t;
0202 
0203     for_each_netdev_rcu(seq_file_net(seq), dev) {
0204         ptype_list = &dev->ptype_all;
0205         list_for_each_entry_rcu(pt, ptype_list, list) {
0206             if (i == pos)
0207                 return pt;
0208             ++i;
0209         }
0210     }
0211 
0212     list_for_each_entry_rcu(pt, &ptype_all, list) {
0213         if (i == pos)
0214             return pt;
0215         ++i;
0216     }
0217 
0218     for (t = 0; t < PTYPE_HASH_SIZE; t++) {
0219         list_for_each_entry_rcu(pt, &ptype_base[t], list) {
0220             if (i == pos)
0221                 return pt;
0222             ++i;
0223         }
0224     }
0225     return NULL;
0226 }
0227 
0228 static void *ptype_seq_start(struct seq_file *seq, loff_t *pos)
0229     __acquires(RCU)
0230 {
0231     rcu_read_lock();
0232     return *pos ? ptype_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;
0233 }
0234 
0235 static void *ptype_seq_next(struct seq_file *seq, void *v, loff_t *pos)
0236 {
0237     struct net_device *dev;
0238     struct packet_type *pt;
0239     struct list_head *nxt;
0240     int hash;
0241 
0242     ++*pos;
0243     if (v == SEQ_START_TOKEN)
0244         return ptype_get_idx(seq, 0);
0245 
0246     pt = v;
0247     nxt = pt->list.next;
0248     if (pt->dev) {
0249         if (nxt != &pt->dev->ptype_all)
0250             goto found;
0251 
0252         dev = pt->dev;
0253         for_each_netdev_continue_rcu(seq_file_net(seq), dev) {
0254             if (!list_empty(&dev->ptype_all)) {
0255                 nxt = dev->ptype_all.next;
0256                 goto found;
0257             }
0258         }
0259 
0260         nxt = ptype_all.next;
0261         goto ptype_all;
0262     }
0263 
0264     if (pt->type == htons(ETH_P_ALL)) {
0265 ptype_all:
0266         if (nxt != &ptype_all)
0267             goto found;
0268         hash = 0;
0269         nxt = ptype_base[0].next;
0270     } else
0271         hash = ntohs(pt->type) & PTYPE_HASH_MASK;
0272 
0273     while (nxt == &ptype_base[hash]) {
0274         if (++hash >= PTYPE_HASH_SIZE)
0275             return NULL;
0276         nxt = ptype_base[hash].next;
0277     }
0278 found:
0279     return list_entry(nxt, struct packet_type, list);
0280 }
0281 
0282 static void ptype_seq_stop(struct seq_file *seq, void *v)
0283     __releases(RCU)
0284 {
0285     rcu_read_unlock();
0286 }
0287 
0288 static int ptype_seq_show(struct seq_file *seq, void *v)
0289 {
0290     struct packet_type *pt = v;
0291 
0292     if (v == SEQ_START_TOKEN)
0293         seq_puts(seq, "Type Device      Function\n");
0294     else if ((!pt->af_packet_net || net_eq(pt->af_packet_net, seq_file_net(seq))) &&
0295          (!pt->dev || net_eq(dev_net(pt->dev), seq_file_net(seq)))) {
0296         if (pt->type == htons(ETH_P_ALL))
0297             seq_puts(seq, "ALL ");
0298         else
0299             seq_printf(seq, "%04x", ntohs(pt->type));
0300 
0301         seq_printf(seq, " %-8s %ps\n",
0302                pt->dev ? pt->dev->name : "", pt->func);
0303     }
0304 
0305     return 0;
0306 }
0307 
0308 static const struct seq_operations ptype_seq_ops = {
0309     .start = ptype_seq_start,
0310     .next  = ptype_seq_next,
0311     .stop  = ptype_seq_stop,
0312     .show  = ptype_seq_show,
0313 };
0314 
0315 static int __net_init dev_proc_net_init(struct net *net)
0316 {
0317     int rc = -ENOMEM;
0318 
0319     if (!proc_create_net("dev", 0444, net->proc_net, &dev_seq_ops,
0320             sizeof(struct seq_net_private)))
0321         goto out;
0322     if (!proc_create_seq("softnet_stat", 0444, net->proc_net,
0323              &softnet_seq_ops))
0324         goto out_dev;
0325     if (!proc_create_net("ptype", 0444, net->proc_net, &ptype_seq_ops,
0326             sizeof(struct seq_net_private)))
0327         goto out_softnet;
0328 
0329     if (wext_proc_init(net))
0330         goto out_ptype;
0331     rc = 0;
0332 out:
0333     return rc;
0334 out_ptype:
0335     remove_proc_entry("ptype", net->proc_net);
0336 out_softnet:
0337     remove_proc_entry("softnet_stat", net->proc_net);
0338 out_dev:
0339     remove_proc_entry("dev", net->proc_net);
0340     goto out;
0341 }
0342 
0343 static void __net_exit dev_proc_net_exit(struct net *net)
0344 {
0345     wext_proc_exit(net);
0346 
0347     remove_proc_entry("ptype", net->proc_net);
0348     remove_proc_entry("softnet_stat", net->proc_net);
0349     remove_proc_entry("dev", net->proc_net);
0350 }
0351 
0352 static struct pernet_operations __net_initdata dev_proc_ops = {
0353     .init = dev_proc_net_init,
0354     .exit = dev_proc_net_exit,
0355 };
0356 
0357 static int dev_mc_seq_show(struct seq_file *seq, void *v)
0358 {
0359     struct netdev_hw_addr *ha;
0360     struct net_device *dev = v;
0361 
0362     if (v == SEQ_START_TOKEN)
0363         return 0;
0364 
0365     netif_addr_lock_bh(dev);
0366     netdev_for_each_mc_addr(ha, dev) {
0367         seq_printf(seq, "%-4d %-15s %-5d %-5d %*phN\n",
0368                dev->ifindex, dev->name,
0369                ha->refcount, ha->global_use,
0370                (int)dev->addr_len, ha->addr);
0371     }
0372     netif_addr_unlock_bh(dev);
0373     return 0;
0374 }
0375 
0376 static const struct seq_operations dev_mc_seq_ops = {
0377     .start = dev_seq_start,
0378     .next  = dev_seq_next,
0379     .stop  = dev_seq_stop,
0380     .show  = dev_mc_seq_show,
0381 };
0382 
0383 static int __net_init dev_mc_net_init(struct net *net)
0384 {
0385     if (!proc_create_net("dev_mcast", 0, net->proc_net, &dev_mc_seq_ops,
0386             sizeof(struct seq_net_private)))
0387         return -ENOMEM;
0388     return 0;
0389 }
0390 
0391 static void __net_exit dev_mc_net_exit(struct net *net)
0392 {
0393     remove_proc_entry("dev_mcast", net->proc_net);
0394 }
0395 
0396 static struct pernet_operations __net_initdata dev_mc_net_ops = {
0397     .init = dev_mc_net_init,
0398     .exit = dev_mc_net_exit,
0399 };
0400 
0401 int __init dev_proc_init(void)
0402 {
0403     int ret = register_pernet_subsys(&dev_proc_ops);
0404     if (!ret)
0405         return register_pernet_subsys(&dev_mc_net_ops);
0406     return ret;
0407 }