日期:2014-05-16 浏览次数:20800 次
5.15. Qdisc的netlink控制 各网卡的Qdisc的用户层操作控制是通过rtnetlink接口实现用户空间和内核之间的通信的: rtnetlink_link, rtnetlink是专门针对路由控制的netlink接口. /* include/linux/rtnetlink.h */ struct rtnetlink_link { // 就两个成员函数, 操作和输出 int (*doit)(struct sk_buff *, struct nlmsghdr*, void *attr); int (*dumpit)(struct sk_buff *, struct netlink_callback *cb); }; // 全局数组, 具体在 net/core/rtnetlink.c中定义 extern struct rtnetlink_link * rtnetlink_links[NPROTO]; 其中的两个成员定义如下: /* net/core/rtnetlink.c */ void __init rtnetlink_init(void) { ...... rtnetlink_links[PF_UNSPEC] = link_rtnetlink_table; rtnetlink_links[PF_PACKET] = link_rtnetlink_table; ...... } 其中link_rtnetlink_table是一个数组, 定义为: static struct rtnetlink_link link_rtnetlink_table[RTM_NR_MSGTYPES] = { // 数组基本元素, 处理路由, 邻居ARP等相关信息, 这些不是本文重点, // 只是Qdisc的相关操作也是定义在这个数组中 [RTM_GETLINK - RTM_BASE] = { .doit = rtnl_getlink, .dumpit = rtnl_dump_ifinfo }, [RTM_SETLINK - RTM_BASE] = { .doit = rtnl_setlink }, [RTM_GETADDR - RTM_BASE] = { .dumpit = rtnl_dump_all }, [RTM_GETROUTE - RTM_BASE] = { .dumpit = rtnl_dump_all }, [RTM_NEWNEIGH - RTM_BASE] = { .doit = neigh_add }, [RTM_DELNEIGH - RTM_BASE] = { .doit = neigh_delete }, [RTM_GETNEIGH - RTM_BASE] = { .dumpit = neigh_dump_info }, #ifdef CONFIG_FIB_RULES [RTM_NEWRULE - RTM_BASE] = { .doit = fib_nl_newrule }, [RTM_DELRULE - RTM_BASE] = { .doit = fib_nl_delrule }, #endif [RTM_GETRULE - RTM_BASE] = { .dumpit = rtnl_dump_all }, [RTM_GETNEIGHTBL - RTM_BASE] = { .dumpit = neightbl_dump_info }, [RTM_SETNEIGHTBL - RTM_BASE] = { .doit = neightbl_set }, }; 5.15.1 初始化 初始化过程是定义对应tc的qdisc和class的操作命令的处理函数: /* net/sched/sch_api.c */ static int __init pktsched_init(void) { struct rtnetlink_link *link_p; // 流控调度的时钟初始化 #ifdef CONFIG_NET_SCH_CLK_CPU if (psched_calibrate_clock() < 0) return -1; #elif defined(CONFIG_NET_SCH_CLK_JIFFIES) psched_tick_per_us = HZ<<PSCHED_JSCALE; psched_us_per_tick = 1000000; #endif // 使用PF_UNSPEC(0)号rtnetlink_links元素用来作为QDISC操作的接口 link_p = rtnetlink_links[PF_UNSPEC]; /* Setup rtnetlink links. It is made here to avoid exporting large number of public symbols. */ // link_p将指向link_rtnetlink_table数组 if (link_p) { // 对数组中流控相关元素进行赋值 // Qdisc操作, 也就是对应tc qdisc add/modify等操作 link_p[RTM_NEWQDISC-RTM_BASE].doit = tc_modify_qdisc; // 删除/获取Qdisc操作 link_p[RTM_DELQDISC-RTM_BASE].doit = tc_get_qdisc; link_p[RTM_GETQDISC-RTM_BASE].doit = tc_get_qdisc; // 获取Qdisc信息, 也就是对应tc qdisc show link_p[RTM_GETQDISC-RTM_BASE].dumpit = tc_dump_qdisc; // class操作, 也就是对应tc class add/delete/modify/get等操作, 在后续文章中分析 link_p[RTM_NEWTCLASS-RTM_BASE].doit = tc_ctl_tclass; link_p[RTM_DELTCLASS-RTM_BASE].doit = tc_ctl_tclass; link_p[RTM_GETTCLASS-RTM_BASE].doit = tc_ctl_tclass; link_p[RTM_GETTCLASS-RTM_BASE].dumpit = tc_dump_tclass; } // 登记FIFO流控处理, 这是网卡设备基本流控方法, 缺省必有的 register_qdisc(&pfifo_qdisc_ops); register_qdisc(&bfifo_qdisc_ops); proc_net_fops_create("psched", 0, &psched_fops); return 0; } 5.15.2 相关操作 以下函数中用到的Qdisc操作函数可见本系列第一篇, 第4节 5.15.2.1 创建/修改qdisc /* Create/change qdisc. */ static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg) { struct tcmsg *tcm; struct rtattr **tca; struct net_device *dev; u32 clid; struct Qdisc *q, *p; int err; replay: /* Reinit, just in case something touches this. */ // tc消息指针 tcm = NLMSG_DATA(n); tca = arg; // class id clid = tcm->tcm_parent; q = p = NULL; // 该tc命令针对的网卡 if ((dev = __dev_get_by_index(tcm->tcm_ifindex)) == NULL) return -ENODEV; if (clid) { // 指定了类别ID的情况 if (clid != TC_H_ROOT) { // 如果不是根节点 if (clid != TC_H_INGRESS) { // 非ingress节点时, 根据类别ID的高16位查找Qdisc节点 if ((p = qdisc_lookup(dev, TC_H_MAJ(clid))) == NULL) return -ENOENT; // 获取p节点的叶子节点, 将调用p->ops->cl_ops->leaf()函数