閱讀1002 返回首頁    go 技術社區[雲棲]


網絡子係統29_橋接轉發數據庫

//	轉發數據庫
//		1.容量
//			net_bridge->hash[BR_HASH_SIZE],其中BR_HASH_SIZE為(1<<8),有256個bucket
//		2.老化機製,
//			2.1 gc函數,到期時會順序遍曆age_list上的轉發項,修改gc函數下一次到期時間為age_list中第一個沒有過期的轉發項的剩餘時間。
//			2.2 age_list鏈表,保存除本機l2地址以外的所有轉發項,按照到期時間進行排序,新添加或更新的轉發項保存在age_list尾部。


//	網橋轉發數據庫初始化
//	調用路徑:br_init->br_fdb_init
1.1 void __init br_fdb_init(void)
{
	//創建轉發項的SLAB cache
	br_fdb_cache = kmem_cache_create("bridge_fdb_cache",
					 sizeof(struct net_bridge_fdb_entry),
					 0,
					 SLAB_HWCACHE_ALIGN, NULL, NULL);
}


//	查詢轉發項
//		__br_fdb_xxx不會遞增net_bridge_fdb_entry的引用計數,br_fdb_xxx會遞增net_bridge_fdb_entry的引用計數		
//	參數:
//		addr,目的l2地址
//	函數主要任務:
//		hash l2地址到正確的bucket,遍曆bucket查找具有相同l2地址的轉發項
2.1 struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br,
					  const unsigned char *addr)
{
	struct hlist_node *h;
	struct net_bridge_fdb_entry *fdb;
	//net_bridge->hash為轉發數據庫的hash表
	hlist_for_each_entry_rcu(fdb, h, &br->hash[br_mac_hash(addr)], hlist) {//遍曆對應的bucket的鏈表
		if (!memcmp(fdb->addr.addr, addr, ETH_ALEN)) {//比較轉發項的地址與查詢的地址
			if (unlikely(has_expired(br, fdb)))
				break;
			return fdb;//找到返回轉發項
		}
	}

	return NULL;//否則返回null
}

//	添加轉發項
//		轉發數據庫由hash_lock保護
2.2 int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
		  const unsigned char *addr, int is_local)
{
	int ret;

	spin_lock_bh(&br->hash_lock);//獲取hash表的鎖,關下半部中斷
	ret = fdb_insert(br, source, addr, is_local);//
	spin_unlock_bh(&br->hash_lock);
	return ret;
}
//	調用路徑:br_fdb_insert->fdb_insert
//	函數主要任務:
//		1.遍曆對應的bucket
//		2.如果對應的轉發項已經存在
//			2.1 轉發項為本機地址,添加的地址為本機地址,返回
//			2.2 轉發項非本機,添加的地址為本機,更新已有的轉發項
//			2.3 轉發項非本機,添加的地址非本機
//				2.3.1 如果轉發項靜態配置,則不更新已有轉發項
//				2.3.2 否則從age_list上刪除轉發項,更新已有轉發項
//		3.轉發項不存在,分配新的轉發項,更新轉發項,添加到hash表

//	對已有選項的更新:
//		1.設置轉發項是否為本機
//		2.本機轉發項設置為static
//		3.非本機轉發項添加到age_list上	
2.3 static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
		  const unsigned char *addr, int is_local)
{
	struct hlist_node *h;
	struct net_bridge_fdb_entry *fdb;
	int hash = br_mac_hash(addr);

	if (!is_valid_ether_addr(addr))//非全0,非廣播或多播地址
		return -EADDRNOTAVAIL;

	hlist_for_each_entry(fdb, h, &br->hash[hash], hlist) {//遍曆對應的bucket
		if (!memcmp(fdb->addr.addr, addr, ETH_ALEN)) {//已存在l2地址對應的轉發項
			if (fdb->is_local) {//轉發項為本機地址
				if (is_local) //要添加的本機地址已經存在,直接返回
					return 0;

				return -EEXIST;//返回錯誤
			}

			if (is_local) {//轉發項非本機地址,將此轉發項修改為本機地址
				printk(KERN_WARNING "%s adding interface with same address "
				       "as a received packet\n",
				       source->dev->name);
				goto update;
			}

			if (fdb->is_static)//轉發項靜態配置
				return 0;

			list_del(&fdb->u.age_list);//將fdb從br->age_list鏈表上刪除
			goto update;
		}
	}

	fdb = kmem_cache_alloc(br_fdb_cache, GFP_ATOMIC);//分配新的轉發項
	if (!fdb)
		return ENOMEM;

	memcpy(fdb->addr.addr, addr, ETH_ALEN);//複製l2地址
	atomic_set(&fdb->use_count, 1);
	hlist_add_head_rcu(&fdb->hlist, &br->hash[hash]);//添加到對應的bucket

	if (!timer_pending(&br->gc_timer)) {
		br->gc_timer.expires = jiffies + hold_time(br);
		add_timer(&br->gc_timer);//啟動垃圾回收定時器
	}

 update:
	fdb->dst = source;//到達此l2地址的端口
	fdb->is_local = is_local;//指示是否為本機地址
	fdb->is_static = is_local;//本機地址為靜態配置地址
	fdb->ageing_timer = jiffies;//最近一次被使用的時間
	if (!is_local) 
		list_add_tail(&fdb->u.age_list, &br->age_list);//對於非本機地址的轉發項,添加到br->age_list鏈表,執行垃圾回收機製

	return 0;
}

//	垃圾回收定時器
//		除本機地址外的所有轉發項,鏈接在lru鏈表中,遍曆鏈表,直到最後一個沒有過期的轉發項。
//	轉發項到期時間:
//		1.正常情況下,300HZ
//		2.拓撲發生變化時,short aging機製,過期時間為15HZ

//	函數主要任務:
//		1.遍曆age_list,釋放過期的轉發項
//		2.修改gc下一次到期時間為最近到期的鄰居項的到期時間。
2.5 void br_fdb_cleanup(unsigned long _data)
{
	struct net_bridge *br = (struct net_bridge *)_data;
	struct list_head *l, *n;
	unsigned long delay;
	//在獲取轉發數據庫的鎖之後進行清理操作
	spin_lock_bh(&br->hash_lock);
	//1.當拓撲發生改變時,啟動short aging機製,過期時間為15HZ,轉發數據庫中的項很快過期
	//2.在正常情況下,過期時間為300HZ
	delay = hold_time(br);

	list_for_each_safe(l, n, &br->age_list) {//安全方式遍曆age_list
		struct net_bridge_fdb_entry *f;
		unsigned long expires;

		f = list_entry(l, struct net_bridge_fdb_entry, u.age_list);
		expires = f->ageing_timer + delay;//轉發項的到期時間

		if (time_before_eq(expires, jiffies)) {//如果轉發項到期
			WARN_ON(f->is_static);
			pr_debug("expire age %lu jiffies %lu\n",
				 f->ageing_timer, jiffies);
			fdb_delete(f);
		} else {
			mod_timer(&br->gc_timer, expires);//第一個沒有過期的轉發項,之後的所有轉發項都不會過期,則調整gc_timer的下一次運行時間,返回
			break;
		}
	}
	spin_unlock_bh(&br->hash_lock);
}

//	調用路徑br_fdb_cleanup->fdb_delete
2.6 static __inline__ void fdb_delete(struct net_bridge_fdb_entry *f)
{
	hlist_del_rcu(&f->hlist);//從hash表上刪除此轉發項
	if (!f->is_static)//is_static=1,說明為本機地址,本機地址不會出現在age_list上
		list_del(&f->u.age_list);//從age_list上刪除此轉發項

	br_fdb_put(f);//遞減引用計數,為0時,直接釋放
}

最後更新:2017-04-03 15:22:11

  上一篇:go 2014百度校招筆試題之動態鏈接庫&amp;靜態鏈接庫詳解
  下一篇:go 網絡子係統28_橋接ioctl