天天看點

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;
}
           

繼續閱讀