Chinaunix首页 | 论坛 | 博客
  • 博客访问: 418777
  • 博文数量: 99
  • 博客积分: 65
  • 博客等级: 民兵
  • 技术积分: 1012
  • 用 户 组: 普通用户
  • 注册时间: 2012-04-20 16:30
个人简介

linux kernel 工程师

文章分类

全部博文(99)

文章存档

2018年(5)

2017年(12)

2016年(27)

2015年(10)

2014年(43)

2012年(2)

我的朋友

分类: LINUX

2014-02-14 09:06:34

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;
}

 

阅读(1479) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~