網絡子係統5_設備隊列規則
// 1.設備描述符與隊列規則相關的字段: // 1.1 dev->tx_queue_len 指示驅動程序規則隊列的隊列長度,在注冊設備時使用,通知核心是否為設備提供隊列規則機製. // 1.1.1 不適用隊列規則=0 // 1.1.2 使用隊列規則>0 // 1.2 dev->qdisc,執行設備傳輸時,qdisc_run,dev_queue_xmit始終通過該字段獲取設備當前使用的隊列規則。 // 1.3 dev->qdisc_sleep, 保存設備具備傳輸能力時,使用的設備隊列。 // 2.設備隊列規則設置的時機: // 2.1 注冊設備(register_netdevice)時,設置為noop_qdisc // 2.2 開啟設備(dev_open)時,創建新的隊列規則。 // 2.3 關閉設備(dev_close)時,設置dev->qdisc為noop_qdisc,表示在設備關閉的過程中,任何的傳輸都會被丟棄 // 初始化設備的隊列規則 // 調用路徑:register_netdevice->dev_init_scheduler 1.1 void dev_init_scheduler(struct net_device *dev) { //獲取qdisc_tree_lock,dev->queue_lock,並關軟中斷 qdisc_lock_tree(dev); //設置dev的隊列規則 dev->qdisc = &noop_qdisc; dev->qdisc_sleeping = &noop_qdisc; INIT_LIST_HEAD(&dev->qdisc_list); //開鎖 qdisc_unlock_tree(dev); //隊列看門狗 dev_watchdog_init(dev); } //初始化看門狗 1.2 static void dev_watchdog_init(struct net_device *dev) { //初始化定時器 init_timer(&dev->watchdog_timer); dev->watchdog_timer.data = (unsigned long)dev; //定時器函數 dev->watchdog_timer.function = dev_watchdog; } // 隊列規則使用的看門狗定時器 // 看門狗函數執行的條件: // 1.設備在係統中 // 2.設備處於IFF_UP狀態 // 3.設備有載波 // 4.傳輸沒有被關閉 // 5,上一次傳輸距離現在已經超過了到期時間 1.3 static void dev_watchdog(unsigned long arg) { struct net_device *dev = (struct net_device *)arg; //持有hard_start_xmit的保護鎖 spin_lock(&dev->xmit_lock); //判斷如果設備的隊列規則不是noop_qdisc if (dev->qdisc != &noop_qdisc) { //檢查設備是否存在,_PRESENT標誌 if (netif_device_present(dev) && netif_running(dev) && netif_carrier_ok(dev)) { if (netif_queue_stopped(dev) && (jiffies - dev->trans_start) > dev->watchdog_timeo) { printk(KERN_INFO "NETDEV WATCHDOG: %s: transmit timed out\n", dev->name); //執行注冊設備時提供的time_out函數 dev->tx_timeout(dev); } //修改定時器的下一次到期時間 if (!mod_timer(&dev->watchdog_timer, jiffies + dev->watchdog_timeo)) //對非活躍的定時器修改到期時間,則同時增加對dev的引用計數 dev_hold(dev); } } spin_unlock(&dev->xmit_lock); //釋放對dev的引用計數 dev_put(dev); } // noop隊列規則 // 所有操作,均將skb釋放掉 2.1 struct Qdisc noop_qdisc = { .enqueue = noop_enqueue,//skb入隊操作 .dequeue = noop_dequeue,//skb出隊操作 .flags = TCQ_F_BUILTIN,//表示內建的隊列規則 .ops = &noop_qdisc_ops,//規則操作 .list = LIST_HEAD_INIT(noop_qdisc.list),//鏈表頭 }; // 分配隊列規則: // 當設備第一次被開啟式時,為設備創建隊列規則 // 參數: // ops為pfifo_fast_ops // 調用路徑:dev_open->dev_activate->qdisc_create_dflt 3.1 struct Qdisc * qdisc_create_dflt(struct net_device *dev, struct Qdisc_ops *ops) { void *p; struct Qdisc *sch; int size; //隊列規則對齊到32字節 size = ((sizeof(*sch) + QDISC_ALIGN_CONST) & ~QDISC_ALIGN_CONST); size += ops->priv_size + QDISC_ALIGN_CONST; //分配內存 p = kmalloc(size, GFP_KERNEL); if (!p) return NULL; memset(p, 0, size); //對齊到32字節 sch = (struct Qdisc *)(((unsigned long)p + QDISC_ALIGN_CONST) & ~QDISC_ALIGN_CONST); //保存為對齊而浪費的字節 sch->padded = (char *)sch - (char *)p; //初始化規則隊列鏈表頭,用於鏈接到dev->qdisc_list INIT_LIST_HEAD(&sch->list); //初始化設備的傳輸隊列 skb_queue_head_init(&sch->q); //隊列操作 sch->ops = ops; sch->enqueue = ops->enqueue; sch->dequeue = ops->dequeue; //此隊列關聯的設備 sch->dev = dev; //增加設備引用計數 dev_hold(dev); //隊列規則的統計變量鎖為設備的傳輸鎖 //說明隊列的統計數據更新依賴於傳輸隊列 sch->stats_lock = &dev->queue_lock; //隊列引用規則為1 atomic_set(&sch->refcnt, 1); if (!ops->init || ops->init(sch, NULL) == 0) return sch; // dev_put(dev); kfree(p); return NULL; } // 關閉隊列規則 // dev_close->dev_deactivate 3.2 void dev_deactivate(struct net_device *dev) { struct Qdisc *qdisc; //關中斷,獲取隊列鎖 spin_lock_bh(&dev->queue_lock); qdisc = dev->qdisc; dev->qdisc = &noop_qdisc;//將隊列規則修改為noop_qdisc,丟棄所有的傳輸 //調用qdisc->reset操作,將隊列規則中所有未傳輸的skb丟棄掉 qdisc_reset(qdisc); spin_unlock_bh(&dev->queue_lock); //關閉看門狗 dev_watchdog_down(dev); //檢查設備是否被調度 while (test_bit(__LINK_STATE_SCHED, &dev->state)) yield();//將當前進程放到就緒隊列,切換到其他進程執行 //等待dev->xmit_lock沒有被任何cpu占用,從而保證在dev_deactivate返回後,沒有任何cpu在dev上執行傳輸 spin_unlock_wait(&dev->xmit_lock); } // fifo隊列規則 4.1 static struct Qdisc_ops pfifo_fast_ops = { .next = NULL, .cl_ops = NULL, .id = "pfifo_fast",//規則id .priv_size = 3 * sizeof(struct sk_buff_head),//三個skb鏈表頭 .enqueue = pfifo_fast_enqueue,//入隊 .dequeue = pfifo_fast_dequeue,//出隊 .requeue = pfifo_fast_requeue,//重新入隊 .init = pfifo_fast_init,//初始化 .reset = pfifo_fast_reset,//重置 .dump = pfifo_fast_dump,//dump .owner = THIS_MODULE, }; // fifo的入隊操作: 4.2 static int pfifo_fast_enqueue(struct sk_buff *skb, struct Qdisc* qdisc) { //規則隊列中的隊列頭 struct sk_buff_head *list = qdisc_priv(qdisc); //根據skb的優先級,計算skb應該進入的list頭 list += prio2band[skb->priority&TC_PRIO_MAX]; //如果當前列表頭中鏈接的skb個數<設備傳輸隊列的長度 if (list->qlen < qdisc->dev->tx_queue_len) { //將skb掛在到此list最後 __skb_queue_tail(list, skb); //增加隊列長度、隊列中數據byte數、輸出封包個數 qdisc->q.qlen++; qdisc->bstats.bytes += skb->len; qdisc->bstats.packets++; return 0; } //該skb應該進入的隊列已滿,丟掉skb qdisc->qstats.drops++; kfree_skb(skb); return NET_XMIT_DROP; } // fifo出隊操作: 4.3 static struct sk_buff *pfifo_fast_dequeue(struct Qdisc* qdisc) { int prio; struct sk_buff_head *list = qdisc_priv(qdisc); struct sk_buff *skb; //3個sk_buff_head,0-3優先級遞減 for (prio = 0; prio < 3; prio++, list++) { //從最高優先級的sk_buff_head出隊一個skb skb = __skb_dequeue(list); if (skb) { //遞減規則隊列的包個數 qdisc->q.qlen--; return skb; } } return NULL; } // fifo重新入隊操作: // 調用時機: // 出隊skb後,發現hard_start_xmit的鎖被獲取,則重新入隊skb 4.4 static int pfifo_fast_requeue(struct sk_buff *skb, struct Qdisc* qdisc) { struct sk_buff_head *list = qdisc_priv(qdisc); //計算skb優先級對應的head list += prio2band[skb->priority&TC_PRIO_MAX]; //入隊 __skb_queue_head(list, skb); //更新長度與統計變量 qdisc->q.qlen++; qdisc->qstats.requeues++; return 0; } // fifo 複位隊列 // 清空隊列中的所有skb 4.5 static void pfifo_fast_reset(struct Qdisc* qdisc) { int prio; struct sk_buff_head *list = qdisc_priv(qdisc); //0-3優先級依次清空隊列頭 for (prio=0; prio < 3; prio++) skb_queue_purge(list+prio); qdisc->q.qlen = 0; } // 清空鏈表 // pfifo_fast_reset->skb_queue_purge 4.6 void skb_queue_purge(struct sk_buff_head *list) { struct sk_buff *skb; //出鏈表後釋放 while ((skb = skb_dequeue(list)) != NULL) kfree_skb(skb); } //規則隊列初始化 4.7 static int pfifo_fast_init(struct Qdisc *qdisc, struct rtattr *opt) { int i; struct sk_buff_head *list = qdisc_priv(qdisc); //初始化3個sk_buff_head for (i=0; i<3; i++) skb_queue_head_init(list+i); return 0; } //skb->priority -> sk_buff_head的映射 4.8 static const u8 prio2band[TC_PRIO_MAX+1] = { 1, 2, 2, 2, 1, 2, 0, 0 , 1, 1, 1, 1, 1, 1, 1, 1 }; //刪除隊列規則 //調用路徑unregister_netdevice->dev_shutdown->qdisc_destroy void qdisc_destroy(struct Qdisc *qdisc) { struct list_head cql = LIST_HEAD_INIT(cql); struct Qdisc *cq, *q, *n; if (qdisc->flags & TCQ_F_BUILTIN ||//noop_qdisc 設置此標識 !atomic_dec_and_test(&qdisc->refcnt))//pfifo_fast_ops 在初始化時,設置引用計數為1 return; if (!list_empty(&qdisc->list)) {//由於將qdisc鏈接到dev->qdisc_list,因此此判斷失敗 if (qdisc->ops->cl_ops == NULL)//pfifo_fast_ops 的此字段為null list_del(&qdisc->list); else list_move(&qdisc->list, &cql); } list_for_each_entry(cq, &cql, list)//由於pfifo_fast_ops->cl_ops為null,所以cql為空,此循環不執行 list_for_each_entry_safe(q, n, &qdisc->dev->qdisc_list, list) if (TC_H_MAJ(q->parent) == TC_H_MAJ(cq->handle)) { if (q->ops->cl_ops == NULL) list_del_init(&q->list); else list_move_tail(&q->list, &cql); } list_for_each_entry_safe(cq, n, &cql, list)//不執行 list_del_init(&cq->list); call_rcu(&qdisc->q_rcu, __qdisc_destroy);//直接調用__qdisc_destroy,kfree(qdisc) }
最後更新:2017-04-03 15:21:55
上一篇:
linux下自動同步internet時間
下一篇:
使用WinDbg內核調試
Vega數據可視化工具——教你輕鬆玩轉大數據可視化 | 附代碼
android 反編譯 apk 分享 smali2java 1.0.0.558
DirectSound中關於IID_IDirectSound無法解析的問題
數據挖掘工程師發展方向淺談
AKKA文檔(java版)—什麼是角色
javascript中函數(function)的用法
GestureDetector和SimpleOnGestureListener的使用教程
《雲周刊》第123期:來了,致未來開發者,525首屆互聯網新兵進階在線峰會
C# 下利用ICSharpCode.SharpZipLib.dll實現文件/文件夾壓縮、解壓縮
Bug:StampedLock鐨勪腑鏂棶棰樺鑷碈PU鐖嗘弧-鍗氬-浜戞爾紺懼尯-闃塊噷浜?