1/****************************************************************************** 2 * vlanproc.c VLAN Module. /proc filesystem interface. 3 * 4 * This module is completely hardware-independent and provides 5 * access to the router using Linux /proc filesystem. 6 * 7 * Author: Ben Greear, <greearb@candelatech.com> coppied from wanproc.c 8 * by: Gene Kozin <genek@compuserve.com> 9 * 10 * Copyright: (c) 1998 Ben Greear 11 * 12 * This program is free software; you can redistribute it and/or 13 * modify it under the terms of the GNU General Public License 14 * as published by the Free Software Foundation; either version 15 * 2 of the License, or (at your option) any later version. 16 * ============================================================================ 17 * Jan 20, 1998 Ben Greear Initial Version 18 *****************************************************************************/ 19 20#include <linux/module.h> 21#include <linux/errno.h> 22#include <linux/kernel.h> 23#include <linux/string.h> 24#include <linux/proc_fs.h> 25#include <linux/seq_file.h> 26#include <linux/fs.h> 27#include <linux/netdevice.h> 28#include <linux/if_vlan.h> 29#include <net/net_namespace.h> 30#include <net/netns/generic.h> 31#include "vlanproc.h" 32#include "vlan.h" 33 34/****** Function Prototypes *************************************************/ 35 36/* Methods for preparing data for reading proc entries */ 37static int vlan_seq_show(struct seq_file *seq, void *v); 38static void *vlan_seq_start(struct seq_file *seq, loff_t *pos); 39static void *vlan_seq_next(struct seq_file *seq, void *v, loff_t *pos); 40static void vlan_seq_stop(struct seq_file *seq, void *); 41static int vlandev_seq_show(struct seq_file *seq, void *v); 42#ifdef CONFIG_INET_GRO 43static int gro_write(struct file *file, const char __user *buf, size_t size, loff_t *ppos); 44#endif /* CONFIG_INET_GRO */ 45 46/* 47 * Global Data 48 */ 49 50 51/* 52 * Names of the proc directory entries 53 */ 54 55static const char name_root[] = "vlan"; 56static const char name_conf[] = "config"; 57 58/* 59 * Structures for interfacing with the /proc filesystem. 60 * VLAN creates its own directory /proc/net/vlan with the folowing 61 * entries: 62 * config device status/configuration 63 * <device> entry for each device 64 */ 65 66/* 67 * Generic /proc/net/vlan/<file> file and inode operations 68 */ 69 70static const struct seq_operations vlan_seq_ops = { 71 .start = vlan_seq_start, 72 .next = vlan_seq_next, 73 .stop = vlan_seq_stop, 74 .show = vlan_seq_show, 75}; 76 77static int vlan_seq_open(struct inode *inode, struct file *file) 78{ 79 return seq_open_net(inode, file, &vlan_seq_ops, 80 sizeof(struct seq_net_private)); 81} 82 83static const struct file_operations vlan_fops = { 84 .owner = THIS_MODULE, 85 .open = vlan_seq_open, 86 .read = seq_read, 87 .llseek = seq_lseek, 88 .release = seq_release_net, 89}; 90 91/* 92 * /proc/net/vlan/<device> file and inode operations 93 */ 94 95static int vlandev_seq_open(struct inode *inode, struct file *file) 96{ 97 return single_open(file, vlandev_seq_show, PDE(inode)->data); 98} 99 100#ifdef CONFIG_INET_GRO 101extern atomic_t gro_timer_init; 102static struct timer_list gro_timer; 103spinlock_t gro_lock; 104static int gro_timer_interval; 105extern struct napi_struct gro_napi; 106 107static void gro_watchdog(ulong data) 108{ 109 struct net_device *gro_dev = (struct net_device *)data; 110 111 if (gro_dev->features & NETIF_F_GRO) { 112 gro_timer.expires = jiffies + gro_timer_interval; 113 add_timer(&gro_timer); 114 } 115 116 spin_lock_bh(&gro_lock); 117 generic_napi_gro_flush(&gro_napi); 118 spin_unlock_bh(&gro_lock); 119} 120 121static int gro_write(struct file *file, const char __user *buf, size_t size, loff_t *ppos) 122{ 123 struct seq_file *seq = (struct seq_file *)file->private_data; 124 struct net_device *gro_dev = seq->private; 125 126 if (!atomic_read(&gro_timer_init)) { 127 spin_lock_init(&gro_lock); 128 129 init_timer(&gro_timer); 130 gro_timer.function = gro_watchdog; 131 atomic_set(&gro_timer_init, 1); 132 } 133 134 if (size < 5 || size > 8) 135 return -EINVAL; 136 137 if (strncmp(buf, "-gro", 4)) 138 return -EINVAL; 139 140 sscanf(buf, "-gro %d", &gro_timer_interval); 141 142 if (gro_timer_interval > 0) { 143 gro_dev->features |= NETIF_F_GRO; 144 gro_timer.data = (ulong)gro_dev; 145 gro_timer.expires = jiffies + gro_timer_interval; 146 mod_timer(&gro_timer, jiffies + gro_timer_interval); 147 printk("\ngro enabled with interval %d\n", gro_timer_interval); 148 } 149 else { 150 gro_dev->features &= ~NETIF_F_GRO; 151 del_timer(&gro_timer); 152 153 /* flush packet */ 154 gro_watchdog((ulong)gro_dev); 155 printk("\ngro disabled\n"); 156 } 157 158 return size; 159} 160#endif /* CONFIG_INET_GRO */ 161 162static const struct file_operations vlandev_fops = { 163 .owner = THIS_MODULE, 164 .open = vlandev_seq_open, 165 .read = seq_read, 166#ifdef CONFIG_INET_GRO 167 .write = gro_write, 168#endif /* CONFIG_INET_GRO */ 169 .llseek = seq_lseek, 170 .release = single_release, 171}; 172 173/* 174 * Proc filesystem derectory entries. 175 */ 176 177/* Strings */ 178static const char *const vlan_name_type_str[VLAN_NAME_TYPE_HIGHEST] = { 179 [VLAN_NAME_TYPE_RAW_PLUS_VID] = "VLAN_NAME_TYPE_RAW_PLUS_VID", 180 [VLAN_NAME_TYPE_PLUS_VID_NO_PAD] = "VLAN_NAME_TYPE_PLUS_VID_NO_PAD", 181 [VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD] = "VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD", 182 [VLAN_NAME_TYPE_PLUS_VID] = "VLAN_NAME_TYPE_PLUS_VID", 183}; 184/* 185 * Interface functions 186 */ 187 188/* 189 * Clean up /proc/net/vlan entries 190 */ 191 192void vlan_proc_cleanup(struct net *net) 193{ 194 struct vlan_net *vn = net_generic(net, vlan_net_id); 195 196 if (vn->proc_vlan_conf) 197 remove_proc_entry(name_conf, vn->proc_vlan_dir); 198 199 if (vn->proc_vlan_dir) 200 proc_net_remove(net, name_root); 201 202 /* Dynamically added entries should be cleaned up as their vlan_device 203 * is removed, so we should not have to take care of it here... 204 */ 205} 206 207/* 208 * Create /proc/net/vlan entries 209 */ 210 211int __net_init vlan_proc_init(struct net *net) 212{ 213 struct vlan_net *vn = net_generic(net, vlan_net_id); 214 215 vn->proc_vlan_dir = proc_net_mkdir(net, name_root, net->proc_net); 216 if (!vn->proc_vlan_dir) 217 goto err; 218 219 vn->proc_vlan_conf = proc_create(name_conf, S_IFREG|S_IRUSR|S_IWUSR, 220 vn->proc_vlan_dir, &vlan_fops); 221 if (!vn->proc_vlan_conf) 222 goto err; 223 return 0; 224 225err: 226 pr_err("%s: can't create entry in proc filesystem!\n", __func__); 227 vlan_proc_cleanup(net); 228 return -ENOBUFS; 229} 230 231/* 232 * Add directory entry for VLAN device. 233 */ 234 235int vlan_proc_add_dev(struct net_device *vlandev) 236{ 237 struct vlan_dev_info *dev_info = vlan_dev_info(vlandev); 238 struct vlan_net *vn = net_generic(dev_net(vlandev), vlan_net_id); 239 240 dev_info->dent = 241 proc_create_data(vlandev->name, S_IFREG|S_IRUSR|S_IWUSR, 242 vn->proc_vlan_dir, &vlandev_fops, vlandev); 243 if (!dev_info->dent) 244 return -ENOBUFS; 245 return 0; 246} 247 248/* 249 * Delete directory entry for VLAN device. 250 */ 251int vlan_proc_rem_dev(struct net_device *vlandev) 252{ 253 struct vlan_net *vn = net_generic(dev_net(vlandev), vlan_net_id); 254 255 /** NOTE: This will consume the memory pointed to by dent, it seems. */ 256 if (vlan_dev_info(vlandev)->dent) { 257 remove_proc_entry(vlan_dev_info(vlandev)->dent->name, 258 vn->proc_vlan_dir); 259 vlan_dev_info(vlandev)->dent = NULL; 260 } 261 return 0; 262} 263 264/****** Proc filesystem entry points ****************************************/ 265 266/* 267 * The following few functions build the content of /proc/net/vlan/config 268 */ 269 270/* start read of /proc/net/vlan/config */ 271static void *vlan_seq_start(struct seq_file *seq, loff_t *pos) 272 __acquires(rcu) 273{ 274 struct net_device *dev; 275 struct net *net = seq_file_net(seq); 276 loff_t i = 1; 277 278 rcu_read_lock(); 279 if (*pos == 0) 280 return SEQ_START_TOKEN; 281 282 for_each_netdev_rcu(net, dev) { 283 if (!is_vlan_dev(dev)) 284 continue; 285 286 if (i++ == *pos) 287 return dev; 288 } 289 290 return NULL; 291} 292 293static void *vlan_seq_next(struct seq_file *seq, void *v, loff_t *pos) 294{ 295 struct net_device *dev; 296 struct net *net = seq_file_net(seq); 297 298 ++*pos; 299 300 dev = (struct net_device *)v; 301 if (v == SEQ_START_TOKEN) 302 dev = net_device_entry(&net->dev_base_head); 303 304 for_each_netdev_continue_rcu(net, dev) { 305 if (!is_vlan_dev(dev)) 306 continue; 307 308 return dev; 309 } 310 311 return NULL; 312} 313 314static void vlan_seq_stop(struct seq_file *seq, void *v) 315 __releases(rcu) 316{ 317 rcu_read_unlock(); 318} 319 320static int vlan_seq_show(struct seq_file *seq, void *v) 321{ 322 struct net *net = seq_file_net(seq); 323 struct vlan_net *vn = net_generic(net, vlan_net_id); 324 325 if (v == SEQ_START_TOKEN) { 326 const char *nmtype = NULL; 327 328 seq_puts(seq, "VLAN Dev name | VLAN ID\n"); 329 330 if (vn->name_type < ARRAY_SIZE(vlan_name_type_str)) 331 nmtype = vlan_name_type_str[vn->name_type]; 332 333 seq_printf(seq, "Name-Type: %s\n", 334 nmtype ? nmtype : "UNKNOWN"); 335 } else { 336 const struct net_device *vlandev = v; 337 const struct vlan_dev_info *dev_info = vlan_dev_info(vlandev); 338 339 seq_printf(seq, "%-15s| %d | %s\n", vlandev->name, 340 dev_info->vlan_id, dev_info->real_dev->name); 341 } 342 return 0; 343} 344 345static int vlandev_seq_show(struct seq_file *seq, void *offset) 346{ 347 struct net_device *vlandev = (struct net_device *) seq->private; 348 const struct vlan_dev_info *dev_info = vlan_dev_info(vlandev); 349 struct rtnl_link_stats64 temp; 350 const struct rtnl_link_stats64 *stats; 351 static const char fmt[] = "%30s %12lu\n"; 352 static const char fmt64[] = "%30s %12llu\n"; 353 int i; 354 355 if (!is_vlan_dev(vlandev)) 356 return 0; 357 358 stats = dev_get_stats(vlandev, &temp); 359 seq_printf(seq, 360 "%s VID: %d REORDER_HDR: %i dev->priv_flags: %hx\n", 361 vlandev->name, dev_info->vlan_id, 362 (int)(dev_info->flags & 1), vlandev->priv_flags); 363 364 seq_printf(seq, fmt64, "total frames received", stats->rx_packets); 365 seq_printf(seq, fmt64, "total bytes received", stats->rx_bytes); 366 seq_printf(seq, fmt64, "Broadcast/Multicast Rcvd", stats->multicast); 367 seq_puts(seq, "\n"); 368 seq_printf(seq, fmt64, "total frames transmitted", stats->tx_packets); 369 seq_printf(seq, fmt64, "total bytes transmitted", stats->tx_bytes); 370 seq_printf(seq, fmt, "total headroom inc", 371 dev_info->cnt_inc_headroom_on_tx); 372 seq_printf(seq, fmt, "total encap on xmit", 373 dev_info->cnt_encap_on_xmit); 374 seq_printf(seq, "Device: %s", dev_info->real_dev->name); 375 /* now show all PRIORITY mappings relating to this VLAN */ 376 seq_printf(seq, "\nINGRESS priority mappings: " 377 "0:%u 1:%u 2:%u 3:%u 4:%u 5:%u 6:%u 7:%u\n", 378 dev_info->ingress_priority_map[0], 379 dev_info->ingress_priority_map[1], 380 dev_info->ingress_priority_map[2], 381 dev_info->ingress_priority_map[3], 382 dev_info->ingress_priority_map[4], 383 dev_info->ingress_priority_map[5], 384 dev_info->ingress_priority_map[6], 385 dev_info->ingress_priority_map[7]); 386 387 seq_printf(seq, " EGRESS priority mappings: "); 388 for (i = 0; i < 16; i++) { 389 const struct vlan_priority_tci_mapping *mp 390 = dev_info->egress_priority_map[i]; 391 while (mp) { 392 seq_printf(seq, "%u:%hu ", 393 mp->priority, ((mp->vlan_qos >> 13) & 0x7)); 394 mp = mp->next; 395 } 396 } 397 seq_puts(seq, "\n"); 398 399 return 0; 400} 401