本文共 16470 字,大约阅读时间需要 54 分钟。
// 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_scheduler1.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_dflt3.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_deactivate3.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的锁被获取,则重新入队skb4.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 复位队列// 清空队列中的所有skb4.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_purge4.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_destroyvoid 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)} 设备传输队列的长度>
转载地址:http://qsvci.baihongyu.com/