Linux协议栈处理发送流程时,在发送驱动函数前会通过__netdev_pick_tx选择一个发送队列,当选择好一个发送队列后,协议栈会将其设置到sk->sk_tx_queue_mapping,这样后续的发送流程就不需要再去选择。
static u16 __netdev_pick_tx(struct net_device *dev, struct sk_buff *skb) { struct sock *sk = skb->sk; int queue_index = sk_tx_queue_get(sk); //skb->ooo_okay对于udp没有意义,针对tcp,当tcp层的发送端检测到所有 //发送出去的skb都已经被ack时,就会对ooo_okay置1,这时候这里就允许重新 //选择一个不一样的队列了,否则会沿用上一次选择的队列 if (queue_index < 0 || skb->ooo_okay || queue_index >= dev->real_num_tx_queues) { int new_index = get_xps_queue(dev, skb); if (new_index < 0) new_index = skb_tx_hash(dev, skb); if (queue_index != new_index && sk && sk_fullsock(sk) && rcu_access_pointer(sk->sk_dst_cache)) sk_tx_queue_set(sk, new_index); queue_index = new_index; } return queue_index; }下面主要是使用retkprobe探测tcp流选择的队列信息:
#include <linux/kernel.h> #include <linux/module.h> #include <linux/kprobes.h> #include <linux/ktime.h> #include <linux/limits.h> #include <linux/sched.h> #include <uapi/linux/tcp.h> #include <uapi/linux/if_ether.h> #include <uapi/linux/if_packet.h> #include <linux/ip.h> #include <linux/socket.h> #include <linux/net.h> #include <net/sock.h> //需要探测的函数名称 static char func_name[NAME_MAX] = "__netdev_pick_tx"; struct my_data { ktime_t entry_stamp; }; static int count=0; static long int queue_counts[32] = {}; static __be32 cpu_src[32] = {}; static __be32 cpu_dst[32] = {}; static int entry_handler(struct kretprobe_instance *ri, struct pt_regs *regs) { //regs->si为__netdev_pick_tx的第二个参数(skb) struct sk_buff *skb = regs->si; const struct iphdr *iph; iph = ip_hdr(skb); //按cpu_id为索引,将ip的源、宿信息保存到对应的数组里 int cpu_id = raw_smp_processor_id(); cpu_src[cpu_id] = iph->saddr; cpu_dst[cpu_id] = iph->daddr; int ooo_okay = skb->ooo_okay; struct sock *sk = skb->sk; int queue_index = sk_tx_queue_get(sk); if (cpu_src[cpu_id] == 2196511856 && cpu_dst[cpu_id] == 2347506800) { net_info_ratelimited("entry handle ooo_okay: %d, queue_index:%d, sk: %p \n", ooo_okay,queue_index, sk); } //net_info_ratelimited("netdev_cap_txqueue %pI4 -> %pI4 %u ~ %u cpu: %d %u ~ %u \n", &iph->saddr,&iph->daddr, iph->saddr, iph->daddr, cpu_id, cpu_src[cpu_id], cpu_dst[cpu_id]); return 0; } static int ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs) { //获取函数的返回值 int queue_index = regs_return_value(regs); //将当前选择的队列计数加1 int cpu_id = raw_smp_processor_id(); if (cpu_src[cpu_id] == 2196511856 && cpu_dst[cpu_id] == 2347506800) { queue_counts[queue_index]++; } net_info_ratelimited("per cpu packets %ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld \n %ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld \n %ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld,\n %ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld \n", queue_counts[0],queue_counts[1],queue_counts[2],queue_counts[3],queue_counts[4],queue_counts[5],queue_counts[6],queue_counts[7], queue_counts[8],queue_counts[9],queue_counts[10],queue_counts[11],queue_counts[12],queue_counts[13],queue_counts[14],queue_counts[15], queue_counts[16],queue_counts[17],queue_counts[18],queue_counts[19],queue_counts[20],queue_counts[21],queue_counts[22],queue_counts[23], queue_counts[24],queue_counts[25],queue_counts[26],queue_counts[27],queue_counts[28],queue_counts[29],queue_counts[30],queue_counts[31]); } static struct kretprobe my_kretprobe = { //handler退出函数时执行 .handler = ret_handler, //entry_handle进入函数时执行 .entry_handler = entry_handler, .data_size = sizeof(struct my_data), /* Probe up to 20 instances concurrently. */ .maxactive = 20, }; static int __init kretprobe_init(void) { int ret; //注册retkprobe my_kretprobe.kp.symbol_name = func_name; ret = register_kretprobe(&my_kretprobe); if (ret < 0) { printk(KERN_INFO "register_kretprobe failed, returned %d\n", ret); return -1; } printk(KERN_INFO "Planted return probe at %s: %p\n", my_kretprobe.kp.symbol_name, my_kretprobe.kp.addr); return 0; } static void __exit kretprobe_exit(void) { unregister_kretprobe(&my_kretprobe); printk(KERN_INFO "kretprobe at %p unregistered\n", my_kretprobe.kp.addr); } module_init(kretprobe_init) module_exit(kretprobe_exit) MODULE_LICENSE("GPL");