- // 1.设备描述符与队列规则相关的字段:
- // 1.1 dev->tx_queue_len 指示驱动程序规则队列的队列长度,在注册设备时使用,通知核心是否为设备提供队列规则机制.
- // 1.1.1 不适用队列规则=0
- // 1.1.2 使用队列规则>0
- // 1.2 dev->qdisc,执行设备传输时,qdisc_run,dev_queue_xmit始终通过该字段获取设备当前使用的队列规则。
- // 1.3 dev->qdisc_sleep, 保存设备具备传输能力时,使用的设备队列。
-
- // 2.设备队列规则设置的时机:
- // 2.1 注册设备(register_netdevice)时,设置为noop_qdisc
- // 2.2 开启设备(dev_open)时,创建新的队列规则。
- // 2.3 关闭设备(dev_close)时,设置dev->qdisc为noop_qdisc,表示在设备关闭的过程中,任何的传输都会被丢弃
-
-
- // 初始化设备的队列规则
- // 调用路径:register_netdevice->dev_init_scheduler
- 1.1 void dev_init_scheduler(struct net_device *dev)
- {
- //获取qdisc_tree_lock,dev->queue_lock,并关软中断
- qdisc_lock_tree(dev);
- //设置dev的队列规则
- dev->qdisc = &noop_qdisc;
- dev->qdisc_sleeping = &noop_qdisc;
- INIT_LIST_HEAD(&dev->qdisc_list);
- //开锁
- qdisc_unlock_tree(dev);
- //队列看门狗
- dev_watchdog_init(dev);
- }
-
- //初始化看门狗
- 1.2 static void dev_watchdog_init(struct net_device *dev)
- {
- //初始化定时器
- init_timer(&dev->watchdog_timer);
- dev->watchdog_timer.data = (unsigned long)dev;
- //定时器函数
- dev->watchdog_timer.function = dev_watchdog;
- }
-
- // 队列规则使用的看门狗定时器
- // 看门狗函数执行的条件:
- // 1.设备在系统中
- // 2.设备处于IFF_UP状态
- // 3.设备有载波
- // 4.传输没有被关闭
- // 5,上一次传输距离现在已经超过了到期时间
- 1.3 static void dev_watchdog(unsigned long arg)
- {
- struct net_device *dev = (struct net_device *)arg;
-
- //持有hard_start_xmit的保护锁
- spin_lock(&dev->xmit_lock);
- //判断如果设备的队列规则不是noop_qdisc
- if (dev->qdisc != &noop_qdisc) {
- //检查设备是否存在,_PRESENT标志
- if (netif_device_present(dev) && netif_running(dev) && netif_carrier_ok(dev)) {
- if (netif_queue_stopped(dev) && (jiffies - dev->trans_start) > dev->watchdog_timeo) {
- printk(KERN_INFO "NETDEV WATCHDOG: %s: transmit timed out\n", dev->name);
- //执行注册设备时提供的time_out函数
- dev->tx_timeout(dev);
- }
- //修改定时器的下一次到期时间
- if (!mod_timer(&dev->watchdog_timer, jiffies + dev->watchdog_timeo))
- //对非活跃的定时器修改到期时间,则同时增加对dev的引用计数
- dev_hold(dev);
- }
- }
- spin_unlock(&dev->xmit_lock);
- //释放对dev的引用计数
- dev_put(dev);
- }
-
- // noop队列规则
- // 所有操作,均将skb释放掉
- 2.1 struct Qdisc noop_qdisc = {
- .enqueue = noop_enqueue,//skb入队操作
- .dequeue = noop_dequeue,//skb出队操作
- .flags = TCQ_F_BUILTIN,//表示内建的队列规则
- .ops = &noop_qdisc_ops,//规则操作
- .list = LIST_HEAD_INIT(noop_qdisc.list),//链表头
- };
-
-
-
- // 分配队列规则:
- // 当设备第一次被开启式时,为设备创建队列规则
- // 参数:
- // ops为pfifo_fast_ops
-
- // 调用路径:dev_open->dev_activate->qdisc_create_dflt
- 3.1 struct Qdisc * qdisc_create_dflt(struct net_device *dev, struct Qdisc_ops *ops)
- {
- void *p;
- struct Qdisc *sch;
- int size;
- //队列规则对齐到32字节
- size = ((sizeof(*sch) + QDISC_ALIGN_CONST) & ~QDISC_ALIGN_CONST);
- size += ops->priv_size + QDISC_ALIGN_CONST;
- //分配内存
- p = kmalloc(size, GFP_KERNEL);
- if (!p)
- return NULL;
- memset(p, 0, size);
- //对齐到32字节
- sch = (struct Qdisc *)(((unsigned long)p + QDISC_ALIGN_CONST)
- & ~QDISC_ALIGN_CONST);
- //保存为对齐而浪费的字节
- sch->padded = (char *)sch - (char *)p;
-
- //初始化规则队列链表头,用于链接到dev->qdisc_list
- INIT_LIST_HEAD(&sch->list);
- //初始化设备的传输队列
- skb_queue_head_init(&sch->q);
- //队列操作
- sch->ops = ops;
- sch->enqueue = ops->enqueue;
- sch->dequeue = ops->dequeue;
- //此队列关联的设备
- sch->dev = dev;
- //增加设备引用计数
- dev_hold(dev);
- //队列规则的统计变量锁为设备的传输锁
- //说明队列的统计数据更新依赖于传输队列
- sch->stats_lock = &dev->queue_lock;
- //队列引用规则为1
- atomic_set(&sch->refcnt, 1);
- if (!ops->init || ops->init(sch, NULL) == 0)
- return sch;
- //
- dev_put(dev);
- kfree(p);
- return NULL;
- }
-
-
-
- // 关闭队列规则
- // dev_close->dev_deactivate
- 3.2 void dev_deactivate(struct net_device *dev)
- {
- struct Qdisc *qdisc;
- //关中断,获取队列锁
- spin_lock_bh(&dev->queue_lock);
- qdisc = dev->qdisc;
- dev->qdisc = &noop_qdisc;//将队列规则修改为noop_qdisc,丢弃所有的传输
- //调用qdisc->reset操作,将队列规则中所有未传输的skb丢弃掉
- qdisc_reset(qdisc);
-
- spin_unlock_bh(&dev->queue_lock);
- //关闭看门狗
- dev_watchdog_down(dev);
- //检查设备是否被调度
- while (test_bit(__LINK_STATE_SCHED, &dev->state))
- yield();//将当前进程放到就绪队列,切换到其他进程执行
-
- //等待dev->xmit_lock没有被任何cpu占用,从而保证在dev_deactivate返回后,没有任何cpu在dev上执行传输
- spin_unlock_wait(&dev->xmit_lock);
- }
-
-
-
- // fifo队列规则
- 4.1 static struct Qdisc_ops pfifo_fast_ops = {
- .next = NULL,
- .cl_ops = NULL,
- .id = "pfifo_fast",//规则id
- .priv_size = 3 * sizeof(struct sk_buff_head),//三个skb链表头
- .enqueue = pfifo_fast_enqueue,//入队
- .dequeue = pfifo_fast_dequeue,//出队
- .requeue = pfifo_fast_requeue,//重新入队
- .init = pfifo_fast_init,//初始化
- .reset = pfifo_fast_reset,//重置
- .dump = pfifo_fast_dump,//dump
- .owner = THIS_MODULE,
- };
-
- // fifo的入队操作:
- 4.2 static int pfifo_fast_enqueue(struct sk_buff *skb, struct Qdisc* qdisc)
- {
- //规则队列中的队列头
- struct sk_buff_head *list = qdisc_priv(qdisc);
- //根据skb的优先级,计算skb应该进入的list头
- list += prio2band[skb->priority&TC_PRIO_MAX];
- //如果当前列表头中链接的skb个数<设备传输队列的长度
- if (list->qlen < qdisc->dev->tx_queue_len) {
- //将skb挂在到此list最后
- __skb_queue_tail(list, skb);
- //增加队列长度、队列中数据byte数、输出封包个数
- qdisc->q.qlen++;
- qdisc->bstats.bytes += skb->len;
- qdisc->bstats.packets++;
- return 0;
- }
- //该skb应该进入的队列已满,丢掉skb
- qdisc->qstats.drops++;
- kfree_skb(skb);
- return NET_XMIT_DROP;
- }
-
- // fifo出队操作:
- 4.3 static struct sk_buff *pfifo_fast_dequeue(struct Qdisc* qdisc)
- {
- int prio;
- struct sk_buff_head *list = qdisc_priv(qdisc);
- struct sk_buff *skb;
-
- //3个sk_buff_head,0-3优先级递减
- for (prio = 0; prio < 3; prio++, list++) {
- //从最高优先级的sk_buff_head出队一个skb
- skb = __skb_dequeue(list);
- if (skb) {
- //递减规则队列的包个数
- qdisc->q.qlen--;
- return skb;
- }
- }
- return NULL;
- }
-
- // fifo重新入队操作:
- // 调用时机:
- // 出队skb后,发现hard_start_xmit的锁被获取,则重新入队skb
- 4.4 static int pfifo_fast_requeue(struct sk_buff *skb, struct Qdisc* qdisc)
- {
- struct sk_buff_head *list = qdisc_priv(qdisc);
- //计算skb优先级对应的head
- list += prio2band[skb->priority&TC_PRIO_MAX];
- //入队
- __skb_queue_head(list, skb);
- //更新长度与统计变量
- qdisc->q.qlen++;
- qdisc->qstats.requeues++;
- return 0;
- }
-
- // fifo 复位队列
- // 清空队列中的所有skb
- 4.5 static void pfifo_fast_reset(struct Qdisc* qdisc)
- {
- int prio;
- struct sk_buff_head *list = qdisc_priv(qdisc);
- //0-3优先级依次清空队列头
- for (prio=0; prio < 3; prio++)
- skb_queue_purge(list+prio);
- qdisc->q.qlen = 0;
- }
-
- // 清空链表
- // pfifo_fast_reset->skb_queue_purge
- 4.6 void skb_queue_purge(struct sk_buff_head *list)
- {
- struct sk_buff *skb;
- //出链表后释放
- while ((skb = skb_dequeue(list)) != NULL)
- kfree_skb(skb);
- }
-
-
- //规则队列初始化
- 4.7 static int pfifo_fast_init(struct Qdisc *qdisc, struct rtattr *opt)
- {
- int i;
- struct sk_buff_head *list = qdisc_priv(qdisc);
- //初始化3个sk_buff_head
- for (i=0; i<3; i++)
- skb_queue_head_init(list+i);
-
- return 0;
- }
-
- //skb->priority -> sk_buff_head的映射
- 4.8 static const u8 prio2band[TC_PRIO_MAX+1] =
- { 1, 2, 2, 2, 1, 2, 0, 0 , 1, 1, 1, 1, 1, 1, 1, 1 };
-
- //删除队列规则
- //调用路径unregister_netdevice->dev_shutdown->qdisc_destroy
- void qdisc_destroy(struct Qdisc *qdisc)
- {
- struct list_head cql = LIST_HEAD_INIT(cql);
- struct Qdisc *cq, *q, *n;
-
- if (qdisc->flags & TCQ_F_BUILTIN ||//noop_qdisc 设置此标识
- !atomic_dec_and_test(&qdisc->refcnt))//pfifo_fast_ops 在初始化时,设置引用计数为1
- return;
-
- if (!list_empty(&qdisc->list)) {//由于将qdisc链接到dev->qdisc_list,因此此判断失败
- if (qdisc->ops->cl_ops == NULL)//pfifo_fast_ops 的此字段为null
- list_del(&qdisc->list);
- else
- list_move(&qdisc->list, &cql);
- }
-
- list_for_each_entry(cq, &cql, list)//由于pfifo_fast_ops->cl_ops为null,所以cql为空,此循环不执行
- list_for_each_entry_safe(q, n, &qdisc->dev->qdisc_list, list)
- if (TC_H_MAJ(q->parent) == TC_H_MAJ(cq->handle)) {
- if (q->ops->cl_ops == NULL)
- list_del_init(&q->list);
- else
- list_move_tail(&q->list, &cql);
- }
- list_for_each_entry_safe(cq, n, &cql, list)//不执行
- list_del_init(&cq->list);
-
- call_rcu(&qdisc->q_rcu, __qdisc_destroy);//直接调用__qdisc_destroy,kfree(qdisc)
- }
// 1.设备描述符与队列规则相关的字段:
// 1.1 dev->tx_queue_len 指示驱动程序规则队列的队列长度,在注册设备时使用,通知核心是否为设备提供队列规则机制.
// 1.1.1 不适用队列规则=0
// 1.1.2 使用队列规则>0
// 1.2 dev->qdisc,执行设备传输时,qdisc_run,dev_queue_xmit始终通过该字段获取设备当前使用的队列规则。
// 1.3 dev->qdisc_sleep, 保存设备具备传输能力时,使用的设备队列。
// 2.设备队列规则设置的时机:
// 2.1 注册设备(register_netdevice)时,设置为noop_qdisc
// 2.2 开启设备(dev_open)时,创建新的队列规则。
// 2.3 关闭设备(dev_close)时,设置dev->qdisc为noop_qdisc,表示在设备关闭的过程中,任何的传输都会被丢弃
// 初始化设备的队列规则
// 调用路径:register_netdevice->dev_init_scheduler
1.1 void dev_init_scheduler(struct net_device *dev)
{
//获取qdisc_tree_lock,dev->queue_lock,并关软中断
qdisc_lock_tree(dev);
//设置dev的队列规则
dev->qdisc = &noop_qdisc;
dev->qdisc_sleeping = &noop_qdisc;
INIT_LIST_HEAD(&dev->qdisc_list);
//开锁
qdisc_unlock_tree(dev);
//队列看门狗
dev_watchdog_init(dev);
}
//初始化看门狗
1.2 static void dev_watchdog_init(struct net_device *dev)
{
//初始化定时器
init_timer(&dev->watchdog_timer);
dev->watchdog_timer.data = (unsigned long)dev;
//定时器函数
dev->watchdog_timer.function = dev_watchdog;
}
// 队列规则使用的看门狗定时器
// 看门狗函数执行的条件:
// 1.设备在系统中
// 2.设备处于IFF_UP状态
// 3.设备有载波
// 4.传输没有被关闭
// 5,上一次传输距离现在已经超过了到期时间
1.3 static void dev_watchdog(unsigned long arg)
{
struct net_device *dev = (struct net_device *)arg;
//持有hard_start_xmit的保护锁
spin_lock(&dev->xmit_lock);
//判断如果设备的队列规则不是noop_qdisc
if (dev->qdisc != &noop_qdisc) {
//检查设备是否存在,_PRESENT标志
if (netif_device_present(dev) && netif_running(dev) && netif_carrier_ok(dev)) {
if (netif_queue_stopped(dev) && (jiffies - dev->trans_start) > dev->watchdog_timeo) {
printk(KERN_INFO "NETDEV WATCHDOG: %s: transmit timed out\n", dev->name);
//执行注册设备时提供的time_out函数
dev->tx_timeout(dev);
}
//修改定时器的下一次到期时间
if (!mod_timer(&dev->watchdog_timer, jiffies + dev->watchdog_timeo))
//对非活跃的定时器修改到期时间,则同时增加对dev的引用计数
dev_hold(dev);
}
}
spin_unlock(&dev->xmit_lock);
//释放对dev的引用计数
dev_put(dev);
}
// noop队列规则
// 所有操作,均将skb释放掉
2.1 struct Qdisc noop_qdisc = {
.enqueue = noop_enqueue,//skb入队操作
.dequeue = noop_dequeue,//skb出队操作
.flags = TCQ_F_BUILTIN,//表示内建的队列规则
.ops = &noop_qdisc_ops,//规则操作
.list = LIST_HEAD_INIT(noop_qdisc.list),//链表头
};
// 分配队列规则:
// 当设备第一次被开启式时,为设备创建队列规则
// 参数:
// ops为pfifo_fast_ops
// 调用路径:dev_open->dev_activate->qdisc_create_dflt
3.1 struct Qdisc * qdisc_create_dflt(struct net_device *dev, struct Qdisc_ops *ops)
{
void *p;
struct Qdisc *sch;
int size;
//队列规则对齐到32字节
size = ((sizeof(*sch) + QDISC_ALIGN_CONST) & ~QDISC_ALIGN_CONST);
size += ops->priv_size + QDISC_ALIGN_CONST;
//分配内存
p = kmalloc(size, GFP_KERNEL);
if (!p)
return NULL;
memset(p, 0, size);
//对齐到32字节
sch = (struct Qdisc *)(((unsigned long)p + QDISC_ALIGN_CONST)
& ~QDISC_ALIGN_CONST);
//保存为对齐而浪费的字节
sch->padded = (char *)sch - (char *)p;
//初始化规则队列链表头,用于链接到dev->qdisc_list
INIT_LIST_HEAD(&sch->list);
//初始化设备的传输队列
skb_queue_head_init(&sch->q);
//队列操作
sch->ops = ops;
sch->enqueue = ops->enqueue;
sch->dequeue = ops->dequeue;
//此队列关联的设备
sch->dev = dev;
//增加设备引用计数
dev_hold(dev);
//队列规则的统计变量锁为设备的传输锁
//说明队列的统计数据更新依赖于传输队列
sch->stats_lock = &dev->queue_lock;
//队列引用规则为1
atomic_set(&sch->refcnt, 1);
if (!ops->init || ops->init(sch, NULL) == 0)
return sch;
//
dev_put(dev);
kfree(p);
return NULL;
}
// 关闭队列规则
// dev_close->dev_deactivate
3.2 void dev_deactivate(struct net_device *dev)
{
struct Qdisc *qdisc;
//关中断,获取队列锁
spin_lock_bh(&dev->queue_lock);
qdisc = dev->qdisc;
dev->qdisc = &noop_qdisc;//将队列规则修改为noop_qdisc,丢弃所有的传输
//调用qdisc->reset操作,将队列规则中所有未传输的skb丢弃掉
qdisc_reset(qdisc);
spin_unlock_bh(&dev->queue_lock);
//关闭看门狗
dev_watchdog_down(dev);
//检查设备是否被调度
while (test_bit(__LINK_STATE_SCHED, &dev->state))
yield();//将当前进程放到就绪队列,切换到其他进程执行
//等待dev->xmit_lock没有被任何cpu占用,从而保证在dev_deactivate返回后,没有任何cpu在dev上执行传输
spin_unlock_wait(&dev->xmit_lock);
}
// fifo队列规则
4.1 static struct Qdisc_ops pfifo_fast_ops = {
.next = NULL,
.cl_ops = NULL,
.id = "pfifo_fast",//规则id
.priv_size = 3 * sizeof(struct sk_buff_head),//三个skb链表头
.enqueue = pfifo_fast_enqueue,//入队
.dequeue = pfifo_fast_dequeue,//出队
.requeue = pfifo_fast_requeue,//重新入队
.init = pfifo_fast_init,//初始化
.reset = pfifo_fast_reset,//重置
.dump = pfifo_fast_dump,//dump
.owner = THIS_MODULE,
};
// fifo的入队操作:
4.2 static int pfifo_fast_enqueue(struct sk_buff *skb, struct Qdisc* qdisc)
{
//规则队列中的队列头
struct sk_buff_head *list = qdisc_priv(qdisc);
//根据skb的优先级,计算skb应该进入的list头
list += prio2band[skb->priority&TC_PRIO_MAX];
//如果当前列表头中链接的skb个数<设备传输队列的长度
if (list->qlen < qdisc->dev->tx_queue_len) {
//将skb挂在到此list最后
__skb_queue_tail(list, skb);
//增加队列长度、队列中数据byte数、输出封包个数
qdisc->q.qlen++;
qdisc->bstats.bytes += skb->len;
qdisc->bstats.packets++;
return 0;
}
//该skb应该进入的队列已满,丢掉skb
qdisc->qstats.drops++;
kfree_skb(skb);
return NET_XMIT_DROP;
}
// fifo出队操作:
4.3 static struct sk_buff *pfifo_fast_dequeue(struct Qdisc* qdisc)
{
int prio;
struct sk_buff_head *list = qdisc_priv(qdisc);
struct sk_buff *skb;
//3个sk_buff_head,0-3优先级递减
for (prio = 0; prio < 3; prio++, list++) {
//从最高优先级的sk_buff_head出队一个skb
skb = __skb_dequeue(list);
if (skb) {
//递减规则队列的包个数
qdisc->q.qlen--;
return skb;
}
}
return NULL;
}
// fifo重新入队操作:
// 调用时机:
// 出队skb后,发现hard_start_xmit的锁被获取,则重新入队skb
4.4 static int pfifo_fast_requeue(struct sk_buff *skb, struct Qdisc* qdisc)
{
struct sk_buff_head *list = qdisc_priv(qdisc);
//计算skb优先级对应的head
list += prio2band[skb->priority&TC_PRIO_MAX];
//入队
__skb_queue_head(list, skb);
//更新长度与统计变量
qdisc->q.qlen++;
qdisc->qstats.requeues++;
return 0;
}
// fifo 复位队列
// 清空队列中的所有skb
4.5 static void pfifo_fast_reset(struct Qdisc* qdisc)
{
int prio;
struct sk_buff_head *list = qdisc_priv(qdisc);
//0-3优先级依次清空队列头
for (prio=0; prio < 3; prio++)
skb_queue_purge(list+prio);
qdisc->q.qlen = 0;
}
// 清空链表
// pfifo_fast_reset->skb_queue_purge
4.6 void skb_queue_purge(struct sk_buff_head *list)
{
struct sk_buff *skb;
//出链表后释放
while ((skb = skb_dequeue(list)) != NULL)
kfree_skb(skb);
}
//规则队列初始化
4.7 static int pfifo_fast_init(struct Qdisc *qdisc, struct rtattr *opt)
{
int i;
struct sk_buff_head *list = qdisc_priv(qdisc);
//初始化3个sk_buff_head
for (i=0; i<3; i++)
skb_queue_head_init(list+i);
return 0;
}
//skb->priority -> sk_buff_head的映射
4.8 static const u8 prio2band[TC_PRIO_MAX+1] =
{ 1, 2, 2, 2, 1, 2, 0, 0 , 1, 1, 1, 1, 1, 1, 1, 1 };
//删除队列规则
//调用路径unregister_netdevice->dev_shutdown->qdisc_destroy
void qdisc_destroy(struct Qdisc *qdisc)
{
struct list_head cql = LIST_HEAD_INIT(cql);
struct Qdisc *cq, *q, *n;
if (qdisc->flags & TCQ_F_BUILTIN ||//noop_qdisc 设置此标识
!atomic_dec_and_test(&qdisc->refcnt))//pfifo_fast_ops 在初始化时,设置引用计数为1
return;
if (!list_empty(&qdisc->list)) {//由于将qdisc链接到dev->qdisc_list,因此此判断失败
if (qdisc->ops->cl_ops == NULL)//pfifo_fast_ops 的此字段为null
list_del(&qdisc->list);
else
list_move(&qdisc->list, &cql);
}
list_for_each_entry(cq, &cql, list)//由于pfifo_fast_ops->cl_ops为null,所以cql为空,此循环不执行
list_for_each_entry_safe(q, n, &qdisc->dev->qdisc_list, list)
if (TC_H_MAJ(q->parent) == TC_H_MAJ(cq->handle)) {
if (q->ops->cl_ops == NULL)
list_del_init(&q->list);
else
list_move_tail(&q->list, &cql);
}
list_for_each_entry_safe(cq, n, &cql, list)//不执行
list_del_init(&cq->list);
call_rcu(&qdisc->q_rcu, __qdisc_destroy);//直接调用__qdisc_destroy,kfree(qdisc)
}