Skip to content

Commit

Permalink
kvm_watcher项目:记录mmio page fault处理延时等信息,添加README.md,优化代码 (#636)
Browse files Browse the repository at this point in the history
* 记录mmio page fault 延时,地址等信息

* 修改action

* 格式化代码

* 添加kvm_watcher项目README.md

* Update README.md

* Update README.md

* update readme

* update readme
  • Loading branch information
nanshuaibo authored Jan 4, 2024
1 parent f538d1d commit 2bc72c7
Show file tree
Hide file tree
Showing 7 changed files with 248 additions and 33 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/kvm_watcher.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,6 @@ jobs:
sudo ./kvm_watcher -e -t 10 -s
sudo ./kvm_watcher -n -t 10
sudo ./kvm_watcher -d -t 10
sudo ./kvm_watcher -f -t 10
sudo ./kvm_watcher -f -m -t 10
make clean
142 changes: 142 additions & 0 deletions eBPF_Supermarket/kvm_watcher/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# kvm_watcher项目

## 一、项目简介

`kvm_watcher` 是一个基于 eBPF 技术的项目,旨在在宿主机侧监控和提取 KVM 虚拟机的性能指标,同时对宿主机性能影响较小。该项目基于 eBPF 的实时监控方案,通过在宿主机中执行eBPF程序,实时捕获有关 KVM 虚拟机的关键性能数据和性能事件,提供全面的性能数据,帮助管理员优化虚拟化环境,改善虚拟机的运行效率和响应性,并且允许用户根据实际需求选择监控的指标和事件,实现个性化配置。

## 二、功能介绍

`kvm_watcher`是一款基于eBPF的kvm检测工具,其旨在使用户方便快捷在宿主机侧获取kvm虚拟机中的各种信息。

目前,其实现的功能主要包括:

- **VM Exit 事件分析:**
- 捕获 VM Exit 事件,包括发生的时间戳、原因、次数以及处理时延等信息。
- **KVM mmu事件分析:**
- 监控 KVM 中的 mmu page fault 和mmio page fault 事件,记录gva、hva、pfn、错误类型和处理时延等关键信息。
- 实时监控kvm虚拟机中产生的dirty page,记录脏页地址、变脏时间、变脏次数和memslot等相关信息。
- **vCPU相关指标分析:**
- 记录有关vCPU的性能指标,包括唤醒时的时间戳,halt持续时间,vCPU id等相关信息。
- 实时监控vCPU的halt-polling时间的变化信息,包括vCPU的线程tid,变化类型,变化前后的halt-polling时间等信息。

## 三、使用方法

> 测试环境:
>
> Kernel: Linux6.2
>
> OS: Ubuntu 23.04
**安装依赖:**

```
sudo apt install clang libelf1 libelf-dev zlib1g-dev libbpf-dev linux-tools-$(uname -r) linux-cloud-tools-$(uname -r)
sudo modprobe kvm && sudo modprobe kvm-intel //加载kvm模块
```

**编译运行:**

```
make
sudo ./kvm_watcher [options]
make clean
```

**参数介绍:**

`kvm_watcher`通过一系列命令参数来控制其具体行为:
```
Usage: kvm_watcher [OPTION...]
BPF program used for monitoring KVM event
-d, --mark_page_dirty Monitor virtual machine dirty page information.
-e, --vm_exit Monitoring the event of vm exit.
-f, --kvmmmu_page_fault Monitoring the data of kvmmmu page fault.
-m, --mmio Monitoring the data of mmio page fault..(The -f option must be specified.)
-n, --halt_poll_ns Monitoring the variation in vCPU halt-polling time.
-p, --vm_pid=PID Specify the virtual machine pid to monitor.
-s, --stat Display statistical data.(The -e option must be specified.)
-t, --monitoring_time=SEC Time for monitoring.
-w, --vcpu_wakeup Monitoring the wakeup of vcpu.
-?, --help Give this help list
--usage Give a short usage message
-V, --version Print program version
```

`-e`:记录vm exit事件信息

`-s`:输出最后的vm exit事件统计信息(需要和`-e`一同使用)

`-f`:记录kvmmmu缺页信息

`-m`:记录mmio缺页信息(需要和`-f`一同使用)

`-d`:记录kvm脏页信息

`-n`:记录vcpu的halt-polling相关信息

`-w`:记录vcpu唤醒时的相关信息

`-p`:指定kvm虚拟机进程pid(必须为虚拟机进程,否则会报错)

`-t`:监控时间

## 四、代码结构

```
├── include
│ ├── kvm_exits.h //vm exit事件相关的内核bpf程序
│ ├── kvm_mmu.h //kvmmmu相关的内核bpf程序
│ ├── kvm_vcpu.h //vcpu相关内核bpf程序
│ └── kvm_watcher.h //项目公用头文件
├── Makefile //编译脚本
├── src
│ ├── kvm_watcher.bpf.c //内核态bpf入口程序
│ └── kvm_watcher.c //用户态bpf程序
└── temp
└── dirty_temp //脏页临时文件
```

## 五、测试

可以按照如下流程测试程序输出:

- **安装依赖**

```
sudo apt install clang libelf1 libelf-dev zlib1g-dev libbpf-dev linux-tools-$(uname -r) linux-cloud-tools-$(uname -r)
```

- **加载KVM模块**

```
sudo modprobe kvm && sudo modprobe kvm-intel
```

- **下载CirrOs镜像**

> CirrOS 是一个专门设计用于在云环境中运行的轻量级 Linux 发行版,特别适用于测试和虚拟机环境,[cirros官网](https://download.cirros-cloud.net/)
```
wget http://download.cirros-cloud.net/0.5.1/cirros-0.5.1-x86_64-disk.img //Download Cirros image
```

- **使用QEMU启动虚拟机**

```
sudo qemu-system-x86_64 -enable-kvm -cpu host -m 2048 -drive file=cirros-0.5.1-x86_64-disk.img,format=qcow2 -boot c -nographic
```

- **编译&&运行程序**

```
make
sudo ./kvm_watcher -w -t 10
sudo ./kvm_watcher -e -t 10 -s
sudo ./kvm_watcher -n -t 10
sudo ./kvm_watcher -d -t 10
make clean
```



43 changes: 43 additions & 0 deletions eBPF_Supermarket/kvm_watcher/include/kvm_mmu.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,47 @@ static int trace_direct_page_fault(struct kvm_vcpu *vcpu,
}
return 0;
}

static int trace_kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa,
u64 error_code, pid_t vm_pid) {
CHECK_PID(vm_pid) {
if (error_code & PFERR_RSVD_MASK) {
u64 ts = bpf_ktime_get_ns();
u64 addr = cr2_or_gpa;
bpf_map_update_elem(&pf_delay, &addr, &ts, BPF_ANY);
}
}
return 0;
}

static int trace_handle_mmio_page_fault(struct kvm_vcpu *vcpu, u64 addr,
bool direct, void *rb) {
u64 *ts;
ts = bpf_map_lookup_elem(&pf_delay, &addr);
if (ts) {
u32 *count;
u32 new_count = 1;
u64 delay = bpf_ktime_get_ns() - *ts;
bpf_map_delete_elem(&pf_delay, &addr);
struct page_fault_event *e;
RESERVE_RINGBUF_ENTRY(rb, e);
count = bpf_map_lookup_elem(&pf_count, &addr);
if (count) {
(*count)++;
e->count = *count;
bpf_map_update_elem(&pf_count, &addr, count, BPF_ANY);
} else {
e->count = 1;
bpf_map_update_elem(&pf_count, &addr, &new_count, BPF_ANY);
}
e->delay = delay;
e->addr = addr;
e->error_code = PFERR_RSVD_MASK;
e->process.pid = bpf_get_current_pid_tgid() >> 32;
bpf_get_current_comm(&e->process.comm, sizeof(e->process.comm));
e->time = *ts;
bpf_ringbuf_submit(e, 0);
}
return 0;
}
#endif /* __KVM_MMU_H */
4 changes: 3 additions & 1 deletion eBPF_Supermarket/kvm_watcher/include/kvm_vcpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,9 @@ static int trace_mark_page_dirty_in_slot(struct kvm *kvm,
struct kvm_memory_slot *slot;
bpf_probe_read_kernel(&slot, sizeof(memslot), &memslot);
bpf_probe_read_kernel(&flags, sizeof(memslot->flags), &memslot->flags);
if (slot && (flags & KVM_MEM_LOG_DIRTY_PAGES)) { // 检查memslot是否启用了脏页追踪
if (slot &&
(flags &
KVM_MEM_LOG_DIRTY_PAGES)) { // 检查memslot是否启用了脏页追踪
gfn_t gfnum = gfn;
u32 *count = bpf_map_lookup_elem(&count_dirty_map, &gfnum);
if (count) {
Expand Down
2 changes: 2 additions & 0 deletions eBPF_Supermarket/kvm_watcher/include/kvm_watcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
#define PFERR_PK_BIT 5
#define PFERR_SGX_BIT 15

#define PFERR_RSVD_MASK (1UL << 3) // mmio

#define PRINT_USAGE_ERR() \
do { \
fprintf(stderr, "Use either the -w, -p, -d,-f or -e option.\n"); \
Expand Down
33 changes: 19 additions & 14 deletions eBPF_Supermarket/kvm_watcher/src/kvm_watcher.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,44 +36,49 @@ struct {

SEC("tp/kvm/kvm_vcpu_wakeup")
int tp_vcpu_wakeup(struct vcpu_wakeup *ctx) {
trace_kvm_vcpu_wakeup(ctx, &rb, vm_pid);
return 0;
return trace_kvm_vcpu_wakeup(ctx, &rb, vm_pid);
}

SEC("tp/kvm/kvm_halt_poll_ns")
int tp_kvm_halt_poll_ns(struct halt_poll_ns *ctx) {
trace_kvm_halt_poll_ns(ctx, &rb, vm_pid);
return 0;
return trace_kvm_halt_poll_ns(ctx, &rb, vm_pid);
}

SEC("tp/kvm/kvm_exit")
int tp_exit(struct exit *ctx) {
trace_kvm_exit(ctx, vm_pid);
return 0;
return trace_kvm_exit(ctx, vm_pid);
}

SEC("tp/kvm/kvm_entry")
int tp_entry(struct exit *ctx) {
trace_kvm_entry(&rb);
return 0;
return trace_kvm_entry(&rb);
}

SEC("kprobe/mark_page_dirty_in_slot")
int BPF_KPROBE(kp_mark_page_dirty_in_slot, struct kvm *kvm,
const struct kvm_memory_slot *memslot, gfn_t gfn) {
trace_mark_page_dirty_in_slot(kvm, memslot, gfn, &rb, vm_pid);
return 0;
return trace_mark_page_dirty_in_slot(kvm, memslot, gfn, &rb, vm_pid);
}

SEC("tp/kvm/kvm_page_fault")
int tp_page_fault(struct trace_event_raw_kvm_page_fault *ctx) {
trace_page_fault(ctx, vm_pid);
return 0;
return trace_page_fault(ctx, vm_pid);
}

SEC("fexit/direct_page_fault")
int BPF_PROG(fexit_direct_page_fault, struct kvm_vcpu *vcpu,
struct kvm_page_fault *fault) {
trace_direct_page_fault(vcpu, fault, &rb);
return 0;
return trace_direct_page_fault(vcpu, fault, &rb);
}

SEC("fentry/kvm_mmu_page_fault")
int BPF_PROG(fentry_kvm_mmu_page_fault, struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa,
u64 error_code) {
return trace_kvm_mmu_page_fault(vcpu, cr2_or_gpa, error_code, vm_pid);
}

SEC("fexit/handle_mmio_page_fault")
int BPF_PROG(fexit_handle_mmio_page_fault, struct kvm_vcpu *vcpu, u64 addr,
bool direct) {
return trace_handle_mmio_page_fault(vcpu, addr, direct, &rb);
}
Loading

0 comments on commit 2bc72c7

Please sign in to comment.