天天看点

Linux内核中VLAN的实现过程(3)-proc文件系统

Linux内核中VLAN的实现过程(3)

本节主要关注和解析vlan模块proc文件系统接口功能实现,代码位于

net/8021q/vlanproc.c

文件中。

接口原型

// 在proc文件系统中创建vlan条目(/proc/net/vlan)
int vlan_proc_init(struct net *net);

// 移除vlan设备节点
void vlan_proc_rem_dev(struct net_device *vlandev);

// 添加vlan设备节点
int vlan_proc_add_dev(struct net_device *vlandev);

// 从proc文件系统中删除vlan条目(/proc/net/vlan)
void vlan_proc_cleanup(struct net *net);
           

创建vlan条目

root@penyforever-pc:~# vconfig add docker0 1
root@penyforever-pc:~# vconfig add enp2s0 1
root@penyforever-pc:~# cd /proc/net/vlan
root@penyforever-pc:/proc/net/vlan# ls
config  docker0.1  enp2s0.1
root@penyforever-pc:/proc/net/vlan# cat config
VLAN Dev name	 | VLAN ID
Name-Type: VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD
enp2s0.1       | 1  | enp2s0
docker0.1      | 1  | docker0
root@penyforever-pc:/proc/net/vlan# cat docker0.1 
docker0.1  VID: 1	 REORDER_HDR: 1  dev->priv_flags: 1001
         total frames received            0
          total bytes received            0
      Broadcast/Multicast Rcvd            0

      total frames transmitted            0
       total bytes transmitted            0
Device: docker0
INGRESS priority mappings: 0:0  1:0  2:0  3:0  4:0  5:0  6:0 7:0
 EGRESS priority mappings: 
root@penyforever-pc:/proc/net/vlan# cat enp2s0.1 
enp2s0.1  VID: 1	 REORDER_HDR: 1  dev->priv_flags: 1021
         total frames received            0
          total bytes received            0
      Broadcast/Multicast Rcvd            0

      total frames transmitted            0
       total bytes transmitted            0
Device: enp2s0
INGRESS priority mappings: 0:0  1:0  2:0  3:0  4:0  5:0  6:0 7:0
 EGRESS priority mappings: 
           
/*
 *	Create /proc/net/vlan entries
 */

int __net_init vlan_proc_init(struct net *net)
{
    // 根据vlan命名空间id获取vlan proc文件系统信息
	struct vlan_net *vn = net_generic(net, vlan_net_id);

    // 在/proc/net/下创建vlan目录
	vn->proc_vlan_dir = proc_net_mkdir(net, name_root, net->proc_net);
	if (!vn->proc_vlan_dir)
		goto err;

    // 在/proc/net/vlan/下创建config文件,并设置文件权限和文件(seq fie)操作函数
	vn->proc_vlan_conf = proc_create_net(name_conf, S_IFREG | 0600,
			vn->proc_vlan_dir, &vlan_seq_ops,
			sizeof(struct seq_net_private));
	if (!vn->proc_vlan_conf)
		goto err;
	return 0;

err:
	pr_err("can't create entry in proc filesystem!\n");
    从proc文件系统中删除vlan条目(/proc/net/vlan)
	vlan_proc_cleanup(net);
	return -ENOBUFS;
}
           

删除vlan条目

/*
 *	Clean up /proc/net/vlan entries
 */

void vlan_proc_cleanup(struct net *net)
{
    // 根据vlan命名空间id获取vlan proc文件系统信息
	struct vlan_net *vn = net_generic(net, vlan_net_id);

    // 删除config文件
	if (vn->proc_vlan_conf)
		remove_proc_entry(name_conf, vn->proc_vlan_dir);

    // 删除vlan目录
	if (vn->proc_vlan_dir)
		remove_proc_entry(name_root, net->proc_net);

	/* Dynamically added entries should be cleaned up as their vlan_device
	 * is removed, so we should not have to take care of it here...
	 */
}

           

添加设备节点

/*
 *	Add directory entry for VLAN device.
 */

int vlan_proc_add_dev(struct net_device *vlandev)
{
    // 获取vlan设备私有信息
	struct vlan_dev_priv *vlan = vlan_dev_priv(vlandev);
    // 根据vlan命名空间id获取vlan proc文件系统信息
	struct vlan_net *vn = net_generic(dev_net(vlandev), vlan_net_id);

    // 校验,vlan设备名字不能是“config”
	if (!strcmp(vlandev->name, name_conf))
		return -EINVAL;
    // 在/proc/vlan/下创建设备节点文件,用来保存vlan设备信息,并设置文件权限和文件(seq file)操作函数
	vlan->dent = proc_create_single_data(vlandev->name, S_IFREG | 0600,
			vn->proc_vlan_dir, vlandev_seq_show, vlandev);
	if (!vlan->dent)
		return -ENOBUFS;
	return 0;
}
           

移除设备节点

/*
 *	Delete directory entry for VLAN device.
 */
void vlan_proc_rem_dev(struct net_device *vlandev)
{
    // 从/proc/net/vlan/移除vlan设备信息文件
	/** NOTE:  This will consume the memory pointed to by dent, it seems. */
	proc_remove(vlan_dev_priv(vlandev)->dent);
    // vlan设备私有信息置为空
	vlan_dev_priv(vlandev)->dent = NULL;
}
           

seq文件操作函数

/****** Proc filesystem entry points ****************************************/

/*
 * The following few functions build the content of /proc/net/vlan/config
 */

// 主要实现初始化工作,在遍历一个链接对象开始时调用,返回一个链接对象的偏移或者SEQ_START_TOKEN(表征这是所有循环的开始)
/* start read of /proc/net/vlan/config */
static void *vlan_seq_start(struct seq_file *seq, loff_t *pos)
	__acquires(rcu)
{
	struct net_device *dev;
	struct net *net = seq_file_net(seq);
	loff_t i = 1;

    // 加rcu读锁
	rcu_read_lock();
	if (*pos == 0)
		return SEQ_START_TOKEN;

    // 遍历网络命名空间下的所有设备,找到偏移pos指向的vlan设备
	for_each_netdev_rcu(net, dev) {
		if (!is_vlan_dev(dev))
			continue;

		if (i++ == *pos)
			return dev;
	}

	return  NULL;
}

// 用来在遍历中寻找下一个链接对象,返回下一个链接对象或者NULL(遍历结束)
static void *vlan_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
	struct net_device *dev;
	struct net *net = seq_file_net(seq);

	++*pos;

    // 找到下一个vlan设备
	dev = v;
	if (v == SEQ_START_TOKEN)
		dev = net_device_entry(&net->dev_base_head);

	for_each_netdev_continue_rcu(net, dev) {
		if (!is_vlan_dev(dev))
			continue;

		return dev;
	}

	return NULL;
}

// 当所有链接对象遍历结束时调用,主要完成一些清理工作
static void vlan_seq_stop(struct seq_file *seq, void *v)
	__releases(rcu)
{
    // 释放rcu读锁
	rcu_read_unlock();
}

// 对遍历对象进行查看操作的函数,主要是调用seq_printf(), seq_puts()之类的函数,打印出这个对象节点的信息
static int vlandev_seq_show(struct seq_file *seq, void *offset)
{
    // 获取vlan设备信息
	struct net_device *vlandev = (struct net_device *) seq->private;

    // 获取vlan设备私有信息
	const struct vlan_dev_priv *vlan = vlan_dev_priv(vlandev);
	struct rtnl_link_stats64 temp;
	const struct rtnl_link_stats64 *stats;
	static const char fmt64[] = "%30s %12llu\n";
	int i;

	if (!is_vlan_dev(vlandev))
		return 0;

    // 获取vlan设备接口统计信息
	stats = dev_get_stats(vlandev, &temp);
    // 输出设备设备名称,vlan id,网络设备接口的标识符(其状态类型被定义在<linux/if.h>之中),网络设备接口的标识符(但对用户空间不可见)
	seq_printf(seq,
		   "%s  VID: %d	 REORDER_HDR: %i  dev->priv_flags: %llx\n",
		   vlandev->name, vlan->vlan_id,
		   (int)(vlan->flags & 1), vlandev->priv_flags);

    // 输出接口收到的数据包个数
	seq_printf(seq, fmt64, "total frames received", stats->rx_packets);
    // 输出接口收到的字节数
	seq_printf(seq, fmt64, "total bytes received", stats->rx_bytes);
    // 输出接口收到的广播包和组播包个数
	seq_printf(seq, fmt64, "Broadcast/Multicast Rcvd", stats->multicast);
	seq_puts(seq, "\n");
    // 输出接口发送的数据包个数
	seq_printf(seq, fmt64, "total frames transmitted", stats->tx_packets);
    // 输出接口发送的字节数
	seq_printf(seq, fmt64, "total bytes transmitted", stats->tx_bytes);
    // 输出vlan设备宿主设备名称
	seq_printf(seq, "Device: %s", vlan->real_dev->name);
    // 输出入口vlan优先级信息
	/* now show all PRIORITY mappings relating to this VLAN */
	seq_printf(seq, "\nINGRESS priority mappings: "
			"0:%u  1:%u  2:%u  3:%u  4:%u  5:%u  6:%u 7:%u\n",
		   vlan->ingress_priority_map[0],
		   vlan->ingress_priority_map[1],
		   vlan->ingress_priority_map[2],
		   vlan->ingress_priority_map[3],
		   vlan->ingress_priority_map[4],
		   vlan->ingress_priority_map[5],
		   vlan->ingress_priority_map[6],
		   vlan->ingress_priority_map[7]);

    // 输出出口vlan优先级信息
	seq_printf(seq, " EGRESS priority mappings: ");
	for (i = 0; i < 16; i++) {
		const struct vlan_priority_tci_mapping *mp
			= vlan->egress_priority_map[i];
		while (mp) {
			seq_printf(seq, "%u:%d ",
				   mp->priority, ((mp->vlan_qos >> 13) & 0x7));
			mp = mp->next;
		}
	}
	seq_puts(seq, "\n");

	return 0;
}
           

继续阅读