linux 队列 发送堆积,发送队列的默认队列策略 (linux网络子系统学习 第十一节 )...

如果网络设备发送队列没有配置发送策略,内核就会使用默认的队列策略来进行报文的发送。

内核中定义实现了两种默认的队列策略,一种是给队列长度为零的队列使用的。一种是给队列长度不为0,但没配置队列策略的队列使用的。

初始化时使用的默认队列策略 noop_qdisc:

/*入队操作直接丢弃报文*/

static int noop_enqueue(struct sk_buff *skb, struct Qdisc * qdisc)

{

kfree_skb(skb);

return NET_XMIT_CN;

}

/*出队操作直接返回NULL*/

static struct sk_buff *noop_dequeue(struct Qdisc * qdisc)

{

return NULL;

}

struct Qdisc_ops noop_qdisc_ops __read_mostly = {

.id = "noop",

.priv_size = 0,

.enqueue = noop_enqueue,

.dequeue = noop_dequeue,

.peek = noop_dequeue,

.owner = THIS_MODULE,

};

static struct netdev_queue noop_netdev_queue = {

.qdisc = &noop_qdisc,

.qdisc_sleeping = &noop_qdisc,

};

struct Qdisc noop_qdisc = {

.enqueue = noop_enqueue,

.dequeue = noop_dequeue,

.flags = TCQ_F_BUILTIN,

.ops = &noop_qdisc_ops,

.list = LIST_HEAD_INIT(noop_qdisc.list),

.dev_queue = &noop_netdev_queue,

};

EXPORT_SYMBOL(noop_qdisc);

零长度队列的默认队列策略 noqueue_qdisc:

static struct Qdisc_ops noqueue_qdisc_ops __read_mostly = {

.id = "noqueue",

.priv_size = 0,

.enqueue = noop_enqueue,

.dequeue = noop_dequeue,

.peek = noop_dequeue,

.owner = THIS_MODULE,

};

static struct netdev_queue noqueue_netdev_queue = {

.qdisc = &noqueue_qdisc,

.qdisc_sleeping = &noqueue_qdisc,

};

static struct Qdisc noqueue_qdisc = {

/*队列长度为零,入队函数置为空,

*发送函数根据该字段是否为空来判断是否需要缓存报文

*/

.enqueue = NULL,

.dequeue = noop_dequeue,

.flags = TCQ_F_BUILTIN,

.ops = &noqueue_qdisc_ops,

.list = LIST_HEAD_INIT(noqueue_qdisc.list),

.q.lock = __SPIN_LOCK_UNLOCKED(noqueue_qdisc.q.lock),

.dev_queue = &noqueue_netdev_queue,

};

单队列时使用的默认队列策略 pfifo_fast:

该默认队列策略使用三个优先级队列来管理报文的发送。根据skb->priority字段设置的优先级来决定报文的发送优先级。

/*默认报文优先级队列个数为 3 个*/

#define PFIFO_FAST_BANDS 3

/*队列策略的私有数据结构

* q:三个不同优先级的报文队列,数组下标越小优先级越高

* bitmap:记录三个优先级队列中哪些有报文需要发送

*/

struct pfifo_fast_priv

{

u32 bitmap;

struct sk_buff_head q[PFIFO_FAST_BANDS];

};

/*根据priv->bitmap的值取出有报文要发送的队列号,

*如果有多个队列都有报文要发送,返回优先级最高的队列号

*/

static const int bitmap2band[] = {-1, 0, 1, 0, 2, 0, 1, 0};

struct sk_buff_head *band2list(struct pfifo_fast_priv *priv,

int band)

{

return priv->q + band;

}

/*根据skb->priority字段往队列策略优先级队列中的映射表*/

static const u8 prio2band[TC_PRIO_MAX+1] =

{ 1, 2, 2, 2, 1, 2, 0, 0 , 1, 1, 1, 1, 1, 1, 1, 1 };

入队和出队的操作:

static int pfifo_fast_enqueue(struct sk_buff *skb,

struct Qdisc* qdisc)

{

/*如果队列没满,就缓存报文*/

if (skb_queue_len(&qdisc->q)

< qdisc_dev(qdisc)->tx_queue_len)

{

/*根据skb的优先级找到队列策略对应的优先级队列*/

int band = prio2band[skb->priority & TC_PRIO_MAX];

struct pfifo_fast_priv *priv = qdisc_priv(qdisc);

struct sk_buff_head *list = band2list(priv, band);

/*置位优先级队列对应的bitmap位*/

priv->bitmap |= (1 << band);

qdisc->q.qlen++;

/*把报文加入队列*/

return __qdisc_enqueue_tail(skb, qdisc, list);

}

/*如果队列已经满了,丢弃报文*/

return qdisc_drop(skb, qdisc);

}

static struct sk_buff *pfifo_fast_dequeue(struct Qdisc* qdisc)

{

struct pfifo_fast_priv *priv = qdisc_priv(qdisc);

/*找到有报文要发送的优先级最高的队列*/

int band = bitmap2band[priv->bitmap];

if (likely(band >= 0))

{

struct sk_buff_head *list = band2list(priv, band);

/*从队列中取一个报文*/

struct sk_buff *skb =

__qdisc_dequeue_head(qdisc, list);

qdisc->q.qlen--;

/*如果队列为空,清除bitmap位*/

if (skb_queue_empty(list))

{

priv->bitmap &= ~(1 << band);

}

return skb;

}

/*没有队列有报文要发送,返回NULL*/

return NULL;

}

struct Qdisc_ops pfifo_fast_ops __read_mostly = {

.id = "pfifo_fast",

.priv_size = sizeof(struct pfifo_fast_priv),

.enqueue = pfifo_fast_enqueue,

.dequeue = pfifo_fast_dequeue,

.owner = THIS_MODULE,

};

队列策略的初始化:

在设备创建时会使用noop_qdisc来初始化发送队列的队列策略:

int register_netdevice(struct net_device *dev)

{

。。。。。。

dev_init_scheduler(dev);

。。。。。。

}

void dev_init_scheduler(struct net_device *dev)

{

dev->qdisc = &noop_qdisc;

netdev_for_each_tx_queue(dev, dev_init_scheduler_queue,

&noop_qdisc);

}

当打开设备时,如果没创建队列策略,就会给创建一个默认的队列策略。

dev_open()

{

。。。。。。

dev_activate(dev);

。。。。。。

}

void dev_activate(struct net_device *dev)

{

/*如果dev使用的默认的qisc noop_qdisc,

*创建一个新的qdisc*/

if (dev->qdisc == &noop_qdisc)

{

attach_default_qdiscs(dev);

}

}

static void attach_one_default_qdisc(struct net_device *dev,

struct netdev_queue *dev_queue,

void *_unused)

{

struct Qdisc *qdisc;

/*如果队列长度不为0,就创建一个发送队列策略pfifo_fast*/

if (dev->tx_queue_len)

{

qdisc = qdisc_create_dflt(dev, dev_queue,

&pfifo_fast_ops, TC_H_ROOT);

if (!qdisc)

{

printk(KERN_INFO "%s: activation failed\n", dev->name);

return;

}

/* Can by-pass the queue discipline for default qdisc */

qdisc->flags |= TCQ_F_CAN_BYPASS;

}

/*发送队列长度为0,就使用noqueue_qdisc*/

else

{

qdisc = &noqueue_qdisc;

}

dev_queue->qdisc_sleeping = qdisc;

}

static void attach_default_qdiscs(struct net_device *dev)

{

struct netdev_queue *txq;

struct Qdisc *qdisc;

/*取得设备的第一个发送队列*/

txq = netdev_get_tx_queue(dev, 0);

/*如果设备只有一个发送队列或者发送队列长度为0,

*调用attach_one_default_qdisc创建一个默认队列策略

*/

if (!netif_is_multiqueue(dev)

|| dev->tx_queue_len == 0)

{

netdev_for_each_tx_queue(dev,

attach_one_default_qdisc, NULL);

dev->qdisc = txq->qdisc_sleeping;

atomic_inc(&dev->qdisc->refcnt);

}

else

{

qdisc = qdisc_create_dflt(dev, txq,

&mq_qdisc_ops, TC_H_ROOT);

if (qdisc)

{

qdisc->ops->attach(qdisc);

dev->qdisc = qdisc;

}

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值