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寄存器进行读写操作。