日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux内核中流量控制(4)

發布時間:2025/3/15 linux 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux内核中流量控制(4) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
本文檔的Copyleft歸yfydz所有,使用GPL發布,可以自由拷貝,轉載,轉載時請保持文檔的完整性, 嚴禁用于任何商業用途。
msn: yfydz_no1@hotmail.com
來源:http://yfydz.cublog.cn
5.4 PRIO(priority)
PRIO是PFIFO_FAST算法的擴展,PFIFO_FAST中一共是3個隊列, 而PRIO最多可設置16個帶(band),每 個帶都相當于是一個PFIFO_FAST, 因此可以進行更細粒度地分類然后進行排隊, 在 net/sched/sch_prio.c中定義。
5.4.1 操作結構定義 // 最大帶數
#define TCQ_PRIO_BANDS?16
// 最小帶數
#define TCQ_MIN_PRIO_BANDS 2
// PRIO私有數據結構
struct prio_sched_data
{
// 有效帶數, 不超過16
?int bands;
// 協議過濾器鏈表
?struct tcf_proto *filter_list;
// 優先權轉帶值的轉換數組, 數組是16個元素
?u8? prio2band[TC_PRIO_MAX+1];
// 16個qdisc指針的數組
?struct Qdisc *queues[TCQ_PRIO_BANDS];
};
// PRIO流控算法操作結構
static struct Qdisc_ops prio_qdisc_ops = {
?.next??=?NULL,
?.cl_ops??=?&prio_class_ops,
?.id??=?"prio",
?.priv_size?=?sizeof(struct prio_sched_data),
?.enqueue?=?prio_enqueue,
?.dequeue?=?prio_dequeue,
?.requeue?=?prio_requeue,
?.drop??=?prio_drop,
?.init??=?prio_init,
?.reset??=?prio_reset,
?.destroy?=?prio_destroy,
?.change??=?prio_tune,
?.dump??=?prio_dump,
?.owner??=?THIS_MODULE,
}; // PRIO類別操作結構
static struct Qdisc_class_ops prio_class_ops = {
?.graft??=?prio_graft,
?.leaf??=?prio_leaf,
?.get??=?prio_get,
?.put??=?prio_put,
?.change??=?prio_change,
?.delete??=?prio_delete,
?.walk??=?prio_walk,
?.tcf_chain?=?prio_find_tcf,
?.bind_tcf?=?prio_bind,
?.unbind_tcf?=?prio_put,
?.dump??=?prio_dump_class,
};
5.4.2 初始化
static int prio_init(struct Qdisc *sch, struct rtattr *opt)
{
// PRIO私有數據
?struct prio_sched_data *q = qdisc_priv(sch);
?int i; // 16個Qdisc都初始化為noop_qdisc
?for (i=0; i<TCQ_PRIO_BANDS; i++)
??q->queues[i] = &noop_qdisc; if (opt == NULL) {
??return -EINVAL;
?} else {
??int err;
// 根據參數選項設置PRIO算法內部參數
??if ((err= prio_tune(sch, opt)) != 0)
???return err;
?}
?return 0;
}
// 算法參數調整, 同時也是prio_qdisc_ops結構的change成員函數
// 指定有多少個帶, 每個帶對應一個pfifo_fast的流控節點
static int prio_tune(struct Qdisc *sch, struct rtattr *opt)
{
// PRIO私有數據
?struct prio_sched_data *q = qdisc_priv(sch);
// TC的PRIO的參數, 包括帶數和優先權值到帶值的轉換數組
?struct tc_prio_qopt *qopt = RTA_DATA(opt);
?int i; // 長度檢查
?if (opt->rta_len < RTA_LENGTH(sizeof(*qopt)))
??return -EINVAL;
// 帶數為2~16個
?if (qopt->bands > TCQ_PRIO_BANDS || qopt->bands < 2)
??return -EINVAL; // 檢查轉換數組中的值是否都不超過帶數, 否則非法
?for (i=0; i<=TC_PRIO_MAX; i++) {
??if (qopt->priomap[i] >= qopt->bands)
???return -EINVAL;
?} sch_tree_lock(sch);
// 有效帶數
?q->bands = qopt->bands;
// 映射數組: 優先權值 -> 帶值
?memcpy(q->prio2band, qopt->priomap, TC_PRIO_MAX+1); // 將大于等于帶值的的Qdisc數組項都釋放掉, 指向noop_qdisc
?for (i=q->bands; i<TCQ_PRIO_BANDS; i++) {
??struct Qdisc *child = xchg(&q->queues[i], &noop_qdisc);
??if (child != &noop_qdisc)
???qdisc_destroy(child);
?}
?sch_tree_unlock(sch); // 設置有效的Qdisc數組, 數量為指定的帶數
?for (i=0; i<q->bands; i++) {
// 為noop_qdisc表示該qdisc數組項可用
??if (q->queues[i] == &noop_qdisc) {
???struct Qdisc *child;
// 創建一個pfifo_fast的Qdisc
???child = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops);
???if (child) {
????sch_tree_lock(sch);
// 將生成的PFIFO_FAST的Qdisc賦給PRIO的Qdisc中的一個數組元素
????child = xchg(&q->queues[i], child);
// 這個判斷應該是unlikely的, 結果應該是假
????if (child != &noop_qdisc)
?????qdisc_destroy(child);
????sch_tree_unlock(sch);
???}
??}
?}
?return 0;
}
值得注意的是在初始化賦值函數中沒有設置過濾器鏈表q->filter_list, 應該是后續執行單獨命令進 行綁定的。 5.4.3 入隊
static int
prio_enqueue(struct sk_buff *skb, struct Qdisc *sch)
{
?struct Qdisc *qdisc;
?int ret; // 根據skb數據包的優先權值(priority)確定帶值, 返回該帶值對應的Qdisc
?qdisc = prio_classify(skb, sch, &ret);
#ifdef CONFIG_NET_CLS_ACT
?if (qdisc == NULL) {
// 該處的qdisc為空, 丟包
??if (ret == NET_XMIT_BYPASS)
???sch->qstats.drops++;
??kfree_skb(skb);
??return ret;
?}
#endif
// 調用該qdisc的入隊函數, 正常就是pfifo_fast流控算法的入隊函數
?if ((ret = qdisc->enqueue(skb, qdisc)) == NET_XMIT_SUCCESS) {
// 入隊成功, 統計值更新
??sch->bstats.bytes += skb->len;
??sch->bstats.packets++;
??sch->q.qlen++;
??return NET_XMIT_SUCCESS;
?}
?sch->qstats.drops++;
?return ret;
}
// PRIO分類操作
static struct Qdisc *
prio_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr)
{
// PRIO私有數據
?struct prio_sched_data *q = qdisc_priv(sch);
// 帶值初始化為數據包優先權值
?u32 band = skb->priority;
?struct tcf_result res; // 缺省返回錯誤值: 旁路
?*qerr = NET_XMIT_BYPASS;
// 優先權的低16位清零后不等于Qdisc的句柄值的情況
?if (TC_H_MAJ(skb->priority) != sch->handle) { #ifdef CONFIG_NET_CLS_ACT
// 該內核選項表示Qdisc可以對數據包進行最終動作,如發送, 丟棄等
// TC分類
??switch (tc_classify(skb, q->filter_list, &res)) {
// STOLEN或QUEUED都表示成功
??case TC_ACT_STOLEN:
??case TC_ACT_QUEUED:
???*qerr = NET_XMIT_SUCCESS;
??case TC_ACT_SHOT:
???return NULL;
??};
// 沒有過濾表
??if (!q->filter_list ) {
#else
// 沒有過濾表或者分類不成功
??if (!q->filter_list || tc_classify(skb, q->filter_list, &res)) {
#endif
// 如果帶值高16位非0, 帶值取為0
???if (TC_H_MAJ(band))
????band = 0;
// 用帶值的最低4位作為轉換數組的索引返回相應的Qdisc流控結構
???return q->queues[q->prio2band[band&TC_PRIO_MAX]];
??}
// 分類成功, 將返回的類別值賦值為帶值
??band = res.classid;
?} // 優先權的低16位清零后等于Qdisc的句柄值的情況
// 帶值為取priority的低16位
?band = TC_H_MIN(band) - 1;
// 如果超過Qdisc中的有效帶數, 取0號優先權對應的帶值對應的Qdisc數組項
?if (band > q->bands)
??return q->queues[q->prio2band[0]];
// 取帶值對應的Qdisc數組項
?return q->queues[band];
}
/* net/sched/sch_api.c */
/* Main classifier routine: scans classifier chain attached
?? to this qdisc, (optionally) tests for protocol and asks
?? specific classifiers.
?*/
// TC分類, 返回0表示分類成功, 負數表示沒有合適的類, 正數是各種重新操作方法
int tc_classify(struct sk_buff *skb, struct tcf_proto *tp,
?struct tcf_result *res)
{
?int err = 0;
// 數據包協議, 是以太頭中的協議類型
?u32 protocol = skb->protocol;
#ifdef CONFIG_NET_CLS_ACT
?struct tcf_proto *otp = tp;
reclassify:
#endif
?protocol = skb->protocol;
// 循環tcf_proto鏈表
?for ( ; tp; tp = tp->next) {
??if ((tp->protocol == protocol ||
???tp->protocol == __constant_htons(ETH_P_ALL)) &&
// 調用tcf_proto的分類算法
???(err = tp->classify(skb, tp, res)) >= 0) {
// 協議符合的情況
#ifdef CONFIG_NET_CLS_ACT
// 需要重新分類
???if ( TC_ACT_RECLASSIFY == err) {
// verdict: 對數據包的處理結果
????__u32 verd = (__u32) G_TC_VERD(skb->tc_verd);
????tp = otp; if (MAX_REC_LOOP < verd++) {
?????printk("rule prio %d protocol %02x reclassify is buggy packet dropped\n",
??????tp->prio&0xffff, ntohs(tp->protocol));
?????return TC_ACT_SHOT;
????}
//
????skb->tc_verd = SET_TC_VERD(skb->tc_verd,verd);
????goto reclassify;
???} else {
// 分類成功
// 設置數據包的TC處理結果 if (skb->tc_verd)
?????skb->tc_verd = SET_TC_VERD(skb->tc_verd,0);
// 返回分類結果
????return err;
???}
#else
如果內核沒定義CONFIG_NET_CLS_ACT, 直接返回
???return err;
#endif
??} }
?return -1;
}
5.4.4 出隊 static struct sk_buff *
prio_dequeue(struct Qdisc* sch)
{
?struct sk_buff *skb;
// PRIO私有數據
?struct prio_sched_data *q = qdisc_priv(sch);
?int prio;
?struct Qdisc *qdisc; // 從0號帶開始循環
?for (prio = 0; prio < q->bands; prio++) {
// 該帶的Qdisc
??qdisc = q->queues[prio];
// 執行該qdisc的出隊操作, 應該就是pfifo_fast的出隊操作
??skb = qdisc->dequeue(qdisc);
??if (skb) {
// 取得數據包, prio隊列數減一
???sch->q.qlen--;
???return skb;
??}
?}
?return NULL; }
由此可見, 0號帶優先權最高, 15號帶最低, 總是高優先級的帶中的數據隊列都清空后才發送低優先 級的帶, 同時每個帶實際有3個隊列(見pfifo_fast), 因此最多可有48個隊列,這樣數據粒度就可以 比較細了,高優先權數據總是在低優先權數據之前發送。
5.4.5 重入隊
static int
prio_requeue(struct sk_buff *skb, struct Qdisc* sch)
{
?struct Qdisc *qdisc;
?int ret; // 查找該skb對應的qdisc
?qdisc = prio_classify(skb, sch, &ret);
#ifdef CONFIG_NET_CLS_ACT
?if (qdisc == NULL) {
// 查找失敗丟包
??if (ret == NET_XMIT_BYPASS)
???sch->qstats.drops++;
??kfree_skb(skb);
??return ret;
?}
#endif
// 執行該qdisc的重入隊操作, 就是pfifo_fast的requeue
?if ((ret = qdisc->ops->requeue(skb, qdisc)) == NET_XMIT_SUCCESS) {
// 統計數更新
??sch->q.qlen++;
??sch->qstats.requeues++;
??return 0;
?}
// 失敗, 丟包
?sch->qstats.drops++;
?return NET_XMIT_DROP;
} 5.4.6 復位 static void
prio_reset(struct Qdisc* sch)
{
?int prio;
?struct prio_sched_data *q = qdisc_priv(sch); // 循環有效帶數, 不是所有帶
?for (prio=0; prio<q->bands; prio++)
// 標準的qdisc復位操作
??qdisc_reset(q->queues[prio]);
?sch->q.qlen = 0;
} 5.4.7 輸出
static int prio_dump(struct Qdisc *sch, struct sk_buff *skb)
{
?struct prio_sched_data *q = qdisc_priv(sch);
?unsigned char? *b = skb->tail;
?struct tc_prio_qopt opt; // 輸出當前的帶數和優先權值到帶值的轉換數組
?opt.bands = q->bands;
?memcpy(&opt.priomap, q->prio2band, TC_PRIO_MAX+1);
?RTA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt);
?return skb->len; rtattr_failure:
?skb_trim(skb, b - skb->data);
?return -1;
}
5.4.8 丟包
static unsigned int prio_drop(struct Qdisc* sch)
{
?struct prio_sched_data *q = qdisc_priv(sch);
?int prio;
?unsigned int len;
?struct Qdisc *qdisc; // 倒序操作, 先丟優先權最低的
?for (prio = q->bands-1; prio >= 0; prio--) {
// 該帶的qdisc
??qdisc = q->queues[prio];
// 調用該qdisc的drop函數, 問題是pfifo_fast算法中是沒有drop函數的
??if (qdisc->ops->drop && (len = qdisc->ops->drop(qdisc)) != 0) {
???sch->q.qlen--;
???return len;
??}
?}
?return 0;
}
由于PFIFO_FAST中沒有drop成員函數, 使得這個函數似乎沒意義, 除非進行了嫁接操作, 使用了其他 類型的qdisc作為數組元素
5.4.9 PRIO類別操作
// 嫁接, 替換prio_qdisc中的內部qdisc數組中的qdisc元素
static int prio_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
??????? struct Qdisc **old)
{
// PRIO私有數據
?struct prio_sched_data *q = qdisc_priv(sch);
?unsigned long band = arg - 1; // 數組位置超過prio_qidsc中的帶數, 錯誤
?if (band >= q->bands)
??return -EINVAL; // 如果新qdisc為空,設為noop_qdisc
?if (new == NULL)
??new = &noop_qdisc; sch_tree_lock(sch);
// 老的qdisc
?*old = q->queues[band];
// 將該數組位置的qdisc設置為新的qdisc
?q->queues[band] = new;
// 將老qdisc的排隊數去除
?sch->q.qlen -= (*old)->q.qlen;
// 復位老qdisc
?qdisc_reset(*old);
?sch_tree_unlock(sch); return 0;
}
// 返回葉子qdisc
static struct Qdisc *
prio_leaf(struct Qdisc *sch, unsigned long arg)
{
// prio私有數組
?struct prio_sched_data *q = qdisc_priv(sch);
?unsigned long band = arg - 1; // 數組位置超過prio_qidsc中的帶數, 錯誤
?if (band >= q->bands)
??return NULL; // 返回指定位置的qdisc數組元素
?return q->queues[band];
} // 將類別ID轉換為帶號
static unsigned long prio_get(struct Qdisc *sch, u32 classid)
{
?struct prio_sched_data *q = qdisc_priv(sch);
// 取類別ID的低16位
?unsigned long band = TC_H_MIN(classid); // 如果超過了當前帶數, 返回0
?if (band - 1 >= q->bands)
??return 0;
// 否則作為有效帶值返回
?return band;
} // 綁定, 獲取與類別ID相關的帶值
static unsigned long prio_bind(struct Qdisc *sch, unsigned long parent, u32 classid)
{
?return prio_get(sch, classid);
}
// 釋放, 空函數
static void prio_put(struct Qdisc *q, unsigned long cl)
{
?return;
} // 修改, 基本是空函數, 沒進行任何修改
static int prio_change(struct Qdisc *sch, u32 handle, u32 parent, struct rtattr **tca, unsigned long *arg)
{
?unsigned long cl = *arg;
?struct prio_sched_data *q = qdisc_priv(sch); if (cl - 1 > q->bands)
??return -ENOENT;
?return 0;
} // 刪除操作, 基本是空函數, 但沒進行任何實際刪除操作
static int prio_delete(struct Qdisc *sch, unsigned long cl)
{
?struct prio_sched_data *q = qdisc_priv(sch);
?if (cl - 1 > q->bands)
??return -ENOENT;
?return 0;
}
// 輸出類別, cl指定類別
static int prio_dump_class(struct Qdisc *sch, unsigned long cl, struct sk_buff *skb,
????? struct tcmsg *tcm)
{
// PRIO私有數據
?struct prio_sched_data *q = qdisc_priv(sch); // 檢查cl是否合法
?if (cl - 1 > q->bands)
??return -ENOENT;
// tc句柄或cl的低16位
?tcm->tcm_handle |= TC_H_MIN(cl);
// 如果Qdisc數組項非空(應該是非空的, 即使不用的也指向noop_qdisc), 保存其句柄值
?if (q->queues[cl-1])
??tcm->tcm_info = q->queues[cl-1]->handle;
?return 0;
}
// PRIO節點遍歷進行某種操作
static void prio_walk(struct Qdisc *sch, struct qdisc_walker *arg)
{
// 私有數據
?struct prio_sched_data *q = qdisc_priv(sch);
?int prio; // 是否設置了停止標志
?if (arg->stop)
??return; // 遍歷有效的帶值
?for (prio = 0; prio < q->bands; prio++) {
// 可以忽略一些元素
??if (arg->count < arg->skip) {
???arg->count++;
???continue;
??}
// 調用指定的操作
??if (arg->fn(sch, prio+1, arg) < 0) {
???arg->stop = 1;
???break;
??}
??arg->count++;
?}
}
// 查找協議分類, 返回結果是指針的指針
static struct tcf_proto ** prio_find_tcf(struct Qdisc *sch, unsigned long cl)
{
// PRIO私有數據
?struct prio_sched_data *q = qdisc_priv(sch); // 定義了cl的話返回空
?if (cl)
??return NULL;
// 返回filter_list的地址
?return &q->filter_list;
} ...... 待續 ......
發表于: 2007-08-02,修改于: 2007-08-02 22:08,已瀏覽1881次,有評論3條 推薦 投訴
網友: 本站網友 時間:2007-08-20 13:53:14 IP地址:222.68.182.★
請問能具體解釋一下tc_classify返回正數時代表什么意思嗎?謝謝
網友: yfydz 時間:2007-08-25 21:38:33 IP地址:123.116.96.★
分類操作成功
網友: 本站網友 時間:2008-11-16 16:19:21 IP地址:114.47.104.★
priority?qdisc看起來是不錯?不過單獨使用的話
在大頻帶往小頻帶使用上?只要像ADSL還有一個ATUR的裝置在那邊的話
就算做了traffic?shaping後的priority?qdisc還是會在ATUR那邊形成新的對列
所以單獨使用priority?qdisc的話?除非有總速限制?不然效果還是有限?還是會lag
只是這我就不知道怎弄了?有人寫過的話可以參考看看

轉載于:https://blog.51cto.com/enchen/158026

總結

以上是生活随笔為你收集整理的Linux内核中流量控制(4)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。