Chinaunix首页 | 论坛 | 博客
  • 博客访问: 435848
  • 博文数量: 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 10:36:11

__dev_xmit_skb函数工作的环境应当是进程的上下文

_dev_xmit_skb函数主要做两件事情:

(1) 如果qdisc的队列为空,则试图直接发送数据包
(2)如果qdisc的队列不空,将数据包加入qdisc的队列,并运行qdisc

当设备进入调度队列准备传输时,qdisc_run函数就会选出下一个要传输的帧,而该函数会间接的调用相关联的队列规则dequeue函数,从对了中取出数据进行传输。

有两个时机将会调用__qdisc_run()
1.__dev_xmit_skb()
2. 软中断NET_TX_SOFTIRQ对应的服务函数


static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q,
     struct net_device *dev,
     struct netdev_queue *txq)
{
 spinlock_t *root_lock = qdisc_lock(q);
 bool contended;
 int rc;

 qdisc_skb_cb(skb)->pkt_len = skb->len;
 qdisc_calculate_pkt_len(skb, q);
 /*
  * Heuristic to force contended enqueues to serialize on a
  * separate lock before trying to get qdisc main lock.
  * This permits __QDISC_STATE_RUNNING owner to get the lock more often
  * and dequeue packets faster.
  */
/*  这里的busylock,查了很多资料都没有介绍,自己琢磨了一下,大概是这样,不知道对不对
busylock只会对进程有效,不会对softirq运行qdisc有什么作用
假如A进程首先访问这个qdisc,肯定不会启用busylock,直接获得root_lcok,但假如这时B进程也要访问这个qdisc,B肯定需要获得busylock,等待在rootlock上,C进程假如也要访问该qdisc,此时C必须等待busy_lock.
当A进程结束Qdisc的运行时,由于B进程已经获得了busylock,这时B进程优先获得qdisc的root lock, C还需要等待busylcok。
这样B和C进程的访问顺序就体现出来了。
另外由于A进程如果在qdisc的运行中, 即使释放了rootlock(sch_direct_xmit会释放root_lock), 但由于B进程要判断running state,所以只能插入队列后结束, A进程又可以重新获取 root_lock */


 contended = qdisc_is_running(q);
 if (unlikely(contended))
  spin_lock(&q->busylock);

 spin_lock(root_lock);  // a. 调用 __qdisc_run/sch_direct_xmit 时需要获得qdisc_lock, 以保护对&qdisc->q的操作
 if (unlikely(test_bit(__QDISC_STATE_DEACTIVATED, &q->state))) {
  kfree_skb(skb);
  rc = NET_XMIT_DROP;
 } else if ((q->flags & TCQ_F_CAN_BYPASS) && !qdisc_qlen(q) &&
      qdisc_run_begin(q)) {
    // 如果qdisc中skb队列的长度为0,并且可以忽略q规则(pfifo_fast设置有这个标志), 尝试直接发送
  /*
   * This is a work-conserving queue; there are no old skbs
   * waiting to be sent out; and the qdisc is not running -
   * xmit the skb directly.
   */
  if (!(dev->priv_flags & IFF_XMIT_DST_RELEASE))
   skb_dst_force(skb);

  qdisc_bstats_update(q, skb); // 更新统计数据

  if (sch_direct_xmit(skb, q, dev, txq, root_lock)) {
   // 直接发送该skb,而不是插入队列,再通过qdisc_run的方式
   // sch_direct_xmit返回值非0,表示队列非空,需要强行继续运行调度__qdisc_run
   if (unlikely(contended)) {
    spin_unlock(&q->busylock);
    contended = false;
   }
   __qdisc_run(q);
  } else // sch_direct_xmit返回值0,表示队列空了或者某种原因不能发了,结束
   qdisc_run_end(q);

  rc = NET_XMIT_SUCCESS;
 } else {   //插入队列,调度qdisc
  skb_dst_force(skb);
  rc = q->enqueue(skb, q) & NET_XMIT_MASK;
  if (qdisc_run_begin(q)) {
   if (unlikely(contended)) {
    spin_unlock(&q->busylock);  //运行 __qdisc_run之前要给busy_lock解锁
     contended = false;
   }
   __qdisc_run(q);
  }
 }
 spin_unlock(root_lock);// b. 与a处对应,释放qdisc_lock
 if (unlikely(contended))
  spin_unlock(&q->busylock);
 return rc;
}

 

阅读(3359) | 评论(0) | 转发(0) |
0

上一篇:net_tx_action

下一篇:dev_kfree_skb_irq

给主人留下些什么吧!~~