0001
0002
0003 #include <linux/ptrace.h>
0004 #include <stddef.h>
0005 #include <linux/bpf.h>
0006 #include <bpf/bpf_helpers.h>
0007 #include <bpf/bpf_tracing.h>
0008
0009 char _license[] SEC("license") = "GPL";
0010
0011
0012 #define VIRTIO_MAX_SGS 6
0013
0014
0015
0016
0017 #define WORKAROUND
0018 #ifdef WORKAROUND
0019 #define SG_MAX 10
0020 #else
0021
0022 #define SG_MAX 128
0023 #endif
0024
0025 #define SG_CHAIN 0x01UL
0026 #define SG_END 0x02UL
0027
0028 struct scatterlist {
0029 unsigned long page_link;
0030 unsigned int offset;
0031 unsigned int length;
0032 };
0033
0034 #define sg_is_chain(sg) ((sg)->page_link & SG_CHAIN)
0035 #define sg_is_last(sg) ((sg)->page_link & SG_END)
0036 #define sg_chain_ptr(sg) \
0037 ((struct scatterlist *) ((sg)->page_link & ~(SG_CHAIN | SG_END)))
0038
0039 static inline struct scatterlist *__sg_next(struct scatterlist *sgp)
0040 {
0041 struct scatterlist sg;
0042
0043 bpf_probe_read_kernel(&sg, sizeof(sg), sgp);
0044 if (sg_is_last(&sg))
0045 return NULL;
0046
0047 sgp++;
0048
0049 bpf_probe_read_kernel(&sg, sizeof(sg), sgp);
0050 if (sg_is_chain(&sg))
0051 sgp = sg_chain_ptr(&sg);
0052
0053 return sgp;
0054 }
0055
0056 static inline struct scatterlist *get_sgp(struct scatterlist **sgs, int i)
0057 {
0058 struct scatterlist *sgp;
0059
0060 bpf_probe_read_kernel(&sgp, sizeof(sgp), sgs + i);
0061 return sgp;
0062 }
0063
0064 int config = 0;
0065 int result = 0;
0066
0067 SEC("kprobe/virtqueue_add_sgs")
0068 int BPF_KPROBE(trace_virtqueue_add_sgs, void *unused, struct scatterlist **sgs,
0069 unsigned int out_sgs, unsigned int in_sgs)
0070 {
0071 struct scatterlist *sgp = NULL;
0072 __u64 length1 = 0, length2 = 0;
0073 unsigned int i, n, len;
0074
0075 if (config != 0)
0076 return 0;
0077
0078 for (i = 0; (i < VIRTIO_MAX_SGS) && (i < out_sgs); i++) {
0079 for (n = 0, sgp = get_sgp(sgs, i); sgp && (n < SG_MAX);
0080 sgp = __sg_next(sgp)) {
0081 bpf_probe_read_kernel(&len, sizeof(len), &sgp->length);
0082 length1 += len;
0083 n++;
0084 }
0085 }
0086
0087 for (i = 0; (i < VIRTIO_MAX_SGS) && (i < in_sgs); i++) {
0088 for (n = 0, sgp = get_sgp(sgs, i); sgp && (n < SG_MAX);
0089 sgp = __sg_next(sgp)) {
0090 bpf_probe_read_kernel(&len, sizeof(len), &sgp->length);
0091 length2 += len;
0092 n++;
0093 }
0094 }
0095
0096 config = 1;
0097 result = length2 - length1;
0098 return 0;
0099 }