128 lines
3.2 KiB
C
128 lines
3.2 KiB
C
|
|
#include <linux/netdevice.h>
|
|
#include <linux/ethtool.h>
|
|
#if IFNAMSIZ != 16
|
|
#error "IFNAMSIZ != 16 is not supported"
|
|
#endif
|
|
#define MAX_QUEUE_NUM 1024
|
|
|
|
/**
|
|
* This union is use to store name of the specified interface
|
|
* and read it as two different data types
|
|
*/
|
|
union name_buf{
|
|
char name[IFNAMSIZ];
|
|
struct {
|
|
u64 hi;
|
|
u64 lo;
|
|
}name_int;
|
|
};
|
|
|
|
/* data retrieved in tracepoints */
|
|
struct queue_data{
|
|
u64 total_pkt_len;
|
|
u32 num_pkt;
|
|
u32 size_64B;
|
|
u32 size_512B;
|
|
u32 size_2K;
|
|
u32 size_16K;
|
|
u32 size_64K;
|
|
};
|
|
|
|
/* array of length 1 for device name */
|
|
BPF_ARRAY(name_map, union name_buf, 1);
|
|
/* table for transmit & receive packets */
|
|
BPF_HASH(tx_q, u16, struct queue_data, MAX_QUEUE_NUM);
|
|
BPF_HASH(rx_q, u16, struct queue_data, MAX_QUEUE_NUM);
|
|
|
|
static inline int name_filter(struct sk_buff* skb){
|
|
/* get device name from skb */
|
|
union name_buf real_devname;
|
|
struct net_device *dev;
|
|
bpf_probe_read(&dev, sizeof(skb->dev), ((char *)skb + offsetof(struct sk_buff, dev)));
|
|
bpf_probe_read(&real_devname, IFNAMSIZ, dev->name);
|
|
|
|
int key=0;
|
|
union name_buf *leaf = name_map.lookup(&key);
|
|
if(!leaf){
|
|
return 0;
|
|
}
|
|
if((leaf->name_int).hi != real_devname.name_int.hi || (leaf->name_int).lo != real_devname.name_int.lo){
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void updata_data(struct queue_data *data, u64 len){
|
|
data->total_pkt_len += len;
|
|
data->num_pkt ++;
|
|
if(len / 64 == 0){
|
|
data->size_64B ++;
|
|
}
|
|
else if(len / 512 == 0){
|
|
data->size_512B ++;
|
|
}
|
|
else if(len / 2048 == 0){
|
|
data->size_2K ++;
|
|
}
|
|
else if(len / 16384 == 0){
|
|
data->size_16K ++;
|
|
}
|
|
else if(len / 65536 == 0){
|
|
data->size_64K ++;
|
|
}
|
|
}
|
|
|
|
TRACEPOINT_PROBE(net, net_dev_start_xmit){
|
|
/* read device name */
|
|
struct sk_buff* skb = (struct sk_buff*)args->skbaddr;
|
|
if(!name_filter(skb)){
|
|
return 0;
|
|
}
|
|
|
|
/* update table */
|
|
u16 qid = skb->queue_mapping;
|
|
struct queue_data newdata;
|
|
__builtin_memset(&newdata, 0, sizeof(newdata));
|
|
struct queue_data *data = tx_q.lookup_or_try_init(&qid, &newdata);
|
|
if(!data){
|
|
return 0;
|
|
}
|
|
updata_data(data, skb->len);
|
|
|
|
return 0;
|
|
}
|
|
|
|
TRACEPOINT_PROBE(net, netif_receive_skb){
|
|
struct sk_buff skb;
|
|
|
|
bpf_probe_read(&skb, sizeof(skb), args->skbaddr);
|
|
if(!name_filter(&skb)){
|
|
return 0;
|
|
}
|
|
|
|
/* case 1: if the NIC does not support multi-queue feature, there is only
|
|
* one queue(qid is always 0).
|
|
* case 2: if the NIC supports multi-queue feature, there are several queues
|
|
* with different qid(from 0 to n-1).
|
|
* The net device driver should mark queue id by API 'skb_record_rx_queue'
|
|
* for a recieved skb, otherwise it should be a BUG(all of the packets are
|
|
* reported as queue 0). For example, virtio net driver is fixed for linux:
|
|
* commit: 133bbb18ab1a2("virtio-net: per-queue RPS config")
|
|
*/
|
|
u16 qid = 0;
|
|
if (skb_rx_queue_recorded(&skb))
|
|
qid = skb_get_rx_queue(&skb);
|
|
|
|
struct queue_data newdata;
|
|
__builtin_memset(&newdata, 0, sizeof(newdata));
|
|
struct queue_data *data = rx_q.lookup_or_try_init(&qid, &newdata);
|
|
if(!data){
|
|
return 0;
|
|
}
|
|
updata_data(data, skb.len);
|
|
|
|
return 0;
|
|
}
|