linux怎么删除我添加的qdisc,qdisc_restart分析

qdisc_restart的执行流程:

qdisc_restart

--->sch_direct_xmit

--->dev_hard_start_xmit(skb, dev, txq)

---->ops->ndo_start_xmit(skb, dev)

/*

* NOTE: Called under qdisc_lock(q) with locally disabled BH.

*

* __QDISC_STATE_RUNNING guarantees only one CPU can process

* this qdisc at a time. qdisc_lock(q) serializes queue accesses for

* this queue.

*

*  netif_tx_lock serializes accesses to device driver.

*

*  qdisc_lock(q) and netif_tx_lock are mutually exclusive,

*  if one is grabbed, another must be free.

*

* Note, that this procedure can be called by a watchdog timer

*

* Returns to the caller:

*    0  - queue is empty or throttled.

*    >0 - queue is not empty.

*

*/

static inline int qdisc_restart(struct Qdisc *q)

{

struct netdev_queue *txq;

struct net_device *dev;

spinlock_t *root_lock;

struct sk_buff *skb;

/* Dequeue packet */

skb = dequeue_skb(q);

if (unlikely(!skb))

return 0;

WARN_ON_ONCE(skb_dst_is_noref(skb));

// 因为sch_direct_xmit里面在调用设备的发送函数前要释放掉root_lcok, 这里准备作为一个参数传递进去

root_lock = qdisc_lock(q);

dev = qdisc_dev(q);

txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb));// 这里通过qdisc找到txq不行吗?为什么非要通过skb来找到?

// 如果sch_direct_xmit返回值大于0,表示队列非空;返回0表示队列空了或者发送失败

return sch_direct_xmit(skb, q, dev, txq, root_lock);

}

/*

* Transmit one skb, and handle the return status as required. Holding the

* __QDISC_STATE_RUNNING bit guarantees that only one CPU can execute this

* function.

*

* Returns to the caller:

*    0  - queue is empty or throttled.

*    >0 - queue is not empty.

*/

int sch_direct_xmit(struct sk_buff *skb, struct Qdisc *q,

struct net_device *dev, struct netdev_queue *txq,

spinlock_t *root_lock)

{

int ret = NETDEV_TX_BUSY;

/* And release qdisc */

spin_unlock(root_lock);

// 可以看到这里把root_lock释放掉了,也就是说另外一个等待root_lock的进程或softirq可以操作此qdisc的队列了, 即enqueue/dequeue 操作可以工作了。

// 这样可以使得enqueue/dequeue 的操作与硬件BD的操作并行。因为enqueue/dequeue 的操作与硬件BD的操作是互不关联的动作,可以并行

// 但是对于同一个工作队列来说,BD的操作应当互斥,所以HARD_TX_LOCK考虑保护同一个工作队列内部的BD操作。

// 如果硬件上是多队列的,队列之间不存在互相干扰的关系, 因为队列之间的硬件寄存器本来就是多套。而假如硬件上是单队列,软件上想实现多队列,这里就

// 需要在driver的transimit函数里加一把自旋锁防止多cpu同时访问同一队列的BD。

/*

static inline void __netif_tx_lock(struct netdev_queue *txq, int cpu)

{

spin_lock(&txq->_xmit_lock);

txq->xmit_lock_owner = cpu;

}

#define HARD_TX_LOCK(dev, txq, cpu) {   \

if ((dev->features & NETIF_F_LLTX) == 0) { \

__netif_tx_lock(txq, cpu);  \

}      \

}

#define HARD_TX_UNLOCK(dev, txq) {   \

if ((dev->features & NETIF_F_LLTX) == 0) { \

__netif_tx_unlock(txq);   \

}      \

}

如果当前设备没有NETIF_F_LLTX特性,则取txq->_xmit_lock这个spin_lock,

如果当前设备有NETIF_F_LLTX特性, 表示设备驱动里面会实现一个锁机制,这个框架就不要担心了

*/

HARD_TX_LOCK(dev, txq, smp_processor_id());

if (!netif_xmit_frozen_or_stopped(txq))

ret = dev_hard_start_xmit(skb, dev, txq);

HARD_TX_UNLOCK(dev, txq);

spin_lock(root_lock);

if (dev_xmit_complete(ret)) {//发送成功,返回当前队列的长度

/* Driver sent out skb successfully or skb was consumed */

ret = qdisc_qlen(q);

} else if (ret == NETDEV_TX_LOCKED) { //只有使能NETIF_F_LLTX feature时,才会返回该值,表示无法获得驱动锁

/* Driver try lock failed */

ret = handle_dev_cpu_collision(skb, txq, q);

// 如果不能获得驱动锁,表示其他cpu在访问这个发送驱动锁

// a. 如果是当前cpu已经获得了这个锁,有可能递归了。 b. 如果其他cpu获得了这个锁,把skb重新放回队列

} else { // NETDEV_TX_BUSY,表示dev的发送缓冲区不够,需要把skb重新放回队列

/* Driver returned NETDEV_TX_BUSY - requeue skb */

if (unlikely (ret != NETDEV_TX_BUSY && net_ratelimit()))

pr_warning("BUG %s code %d qlen %d\n",

dev->name, ret, q->q.qlen);

ret = dev_requeue_skb(skb, q);

}

if (ret && netif_xmit_frozen_or_stopped(txq))

ret = 0;

return ret;

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值