linux kernel 工程师
全部博文(99)
分类: 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;
}