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