xref: /aosp_15_r20/external/bcc/tools/netqtop.c (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1 
2 #include <linux/netdevice.h>
3 #include <linux/ethtool.h>
4 #if IFNAMSIZ != 16
5 #error "IFNAMSIZ != 16 is not supported"
6 #endif
7 #define MAX_QUEUE_NUM 1024
8 
9 /**
10 * This union is use to store name of the specified interface
11 * and read it as two different data types
12 */
13 union name_buf{
14     char name[IFNAMSIZ];
15     struct {
16         u64 hi;
17         u64 lo;
18     }name_int;
19 };
20 
21 /* data retrieved in tracepoints */
22 struct queue_data{
23     u64 total_pkt_len;
24     u32 num_pkt;
25     u32 size_64B;
26     u32 size_512B;
27     u32 size_2K;
28     u32 size_16K;
29     u32 size_64K;
30 };
31 
32 /* array of length 1 for device name */
33 BPF_ARRAY(name_map, union name_buf, 1);
34 /* table for transmit & receive packets */
35 BPF_HASH(tx_q, u16, struct queue_data, MAX_QUEUE_NUM);
36 BPF_HASH(rx_q, u16, struct queue_data, MAX_QUEUE_NUM);
37 
name_filter(struct sk_buff * skb)38 static inline int name_filter(struct sk_buff* skb){
39     /* get device name from skb */
40     union name_buf real_devname;
41     struct net_device *dev;
42     bpf_probe_read(&dev, sizeof(skb->dev), ((char *)skb + offsetof(struct sk_buff, dev)));
43     bpf_probe_read(&real_devname, IFNAMSIZ, dev->name);
44 
45     int key=0;
46     union name_buf *leaf = name_map.lookup(&key);
47     if(!leaf){
48         return 0;
49     }
50     if((leaf->name_int).hi != real_devname.name_int.hi || (leaf->name_int).lo != real_devname.name_int.lo){
51         return 0;
52     }
53 
54     return 1;
55 }
56 
updata_data(struct queue_data * data,u64 len)57 static void updata_data(struct queue_data *data, u64 len){
58     data->total_pkt_len += len;
59     data->num_pkt ++;
60     if(len / 64 == 0){
61         data->size_64B ++;
62     }
63     else if(len / 512 == 0){
64         data->size_512B ++;
65     }
66     else if(len / 2048 == 0){
67         data->size_2K ++;
68     }
69     else if(len / 16384 == 0){
70         data->size_16K ++;
71     }
72     else if(len / 65536 == 0){
73         data->size_64K ++;
74     }
75 }
76 
TRACEPOINT_PROBE(net,net_dev_start_xmit)77 TRACEPOINT_PROBE(net, net_dev_start_xmit){
78     /* read device name */
79     struct sk_buff* skb = (struct sk_buff*)args->skbaddr;
80     if(!name_filter(skb)){
81         return 0;
82     }
83 
84     /* update table */
85     u16 qid = skb->queue_mapping;
86     struct queue_data newdata;
87     __builtin_memset(&newdata, 0, sizeof(newdata));
88     struct queue_data *data = tx_q.lookup_or_try_init(&qid, &newdata);
89     if(!data){
90         return 0;
91     }
92     updata_data(data, skb->len);
93 
94     return 0;
95 }
96 
TRACEPOINT_PROBE(net,netif_receive_skb)97 TRACEPOINT_PROBE(net, netif_receive_skb){
98     struct sk_buff skb;
99 
100     bpf_probe_read(&skb, sizeof(skb), args->skbaddr);
101     if(!name_filter(&skb)){
102         return 0;
103     }
104 
105     /* case 1: if the NIC does not support multi-queue feature, there is only
106      *         one queue(qid is always 0).
107      * case 2: if the NIC supports multi-queue feature, there are several queues
108      *         with different qid(from 0 to n-1).
109      * The net device driver should mark queue id by API 'skb_record_rx_queue'
110      * for a recieved skb, otherwise it should be a BUG(all of the packets are
111      * reported as queue 0). For example, virtio net driver is fixed for linux:
112      * commit: 133bbb18ab1a2("virtio-net: per-queue RPS config")
113      */
114     u16 qid = 0;
115     if (skb_rx_queue_recorded(&skb))
116         qid = skb_get_rx_queue(&skb);
117 
118     struct queue_data newdata;
119     __builtin_memset(&newdata, 0, sizeof(newdata));
120     struct queue_data *data = rx_q.lookup_or_try_init(&qid, &newdata);
121     if(!data){
122         return 0;
123     }
124     updata_data(data, skb.len);
125 
126     return 0;
127 }
128