網絡子係統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鐖嗘弧-鍗氬-浜戞爾紺懼尯-闃塊噷浜?