日期:2014-05-16 浏览次数:21005 次
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()函数