iproute2 用户空间
Sched 与 iproute2 的通信,是典型的 Linux 内核模块和用户空间的进程之间的通信,这 种通信一般由 Netlink Socket 来提供这种双向的通信连接。这种连接由标准的提供给用户进程的 socket 和提供给内核模块的 API 组成,用户空间的接口简单的说就是创建一个 family 为 AF_NETLINK 的 socket,然后使用这个 socket 进行通信。
rtnl_open() 函数的作用是打开一个AF_NETLINK 的 socket,rtnl_close() 函数的作用是关闭一个AF_NETLINK 的 socket。

用户空间通信前的准备:填充 netlink 包;然后把 netlink 包发送到内核空间去。
tc.c

通过cbs_qdisc_util→cbs_parse_opt调用 q_cbs.c 中的cbs_parse_opt()函数。

q_cbs.c

最后通过rtnl_talk(&rth, &req.n, NULL)把 netlink 包发送到内核空间去,rtnl_talk()发送过程包括 sendmsg 和 recvmsg。

内核模块的初始化
内核模块的初始化:在 net/sched/sch_api.c 文件中的 void __init pktsched_init (void)函数中,初始化了 link_rtnetlink_table 表,link_rtnetlink_table 是一张 struct rtnetlink_link的表。
1 | struct rtnetlink_link { |
1 | static int __init pktsched_init(void) |
struct rtnetlink_link 由函数指针 doit 和 dumpit 组成,这张表可以由需要执行的动作的宏定义 (例如:RTM_NEWQDISC,RTM_DELQDISC)来索引,以使得能通过这张表调动相应的函数。内核模块从用户空间收到的就是这些索引和参数,以此调用注册在此表中的函数。
在qdisc_create()中的opt→init调用sch_cbs.c的cbs_init()初始化函数。
1 | static struct Qdisc *qdisc_create(struct net_device *dev, |
sch_cbs.c

gdb调试过程
tc部分分析
1 | /bin/tc qdisc replace dev eth1 parent 6666:2 handle 7777 cbs \ |
设置断点。main()—>do_cmd()—>do_qdisc()—>tc_qdisc_modify()

tc_qdisc_modify() 首先解析参数eth1 …… cbs

找到cbs_qdisc_util

通过addttr_l()添加k到NETLINK包里。

执行cbs_qdisc_util→parse_qopt

通过cbs_parse_opt()解析参数idleslope …… offload 1

通过addattr_l()添加到NETLINK包里。

与内核通信。

此时内核运行完打印消息

rtnl_close()结束与内核的通信

内核部分 net/sched/sch_api.c
断到tc_modify_qdisc()函数。

进入ops→init

解析到cbs_init()函数,初始化。

进入cbs_change() —> csb_enable_offload()


ops→ndo_setup_tc()开始进入驱动部分。

gmac网卡驱动部分
调用stmmac_tc_setup_cbs()

在stmmac_config_cbs()中对寄存器进行读写配置。

对CBS相关的DMA寄存器进行读写操作。
