Skip to content

Commit 7ff70d2

Browse files
committed
Add pipe benchmark program
1 parent ad1e9e9 commit 7ff70d2

File tree

3 files changed

+242
-0
lines changed

3 files changed

+242
-0
lines changed

CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,8 @@ add_executable(storagekvm
3939
src/storage.cpp
4040
)
4141
target_link_libraries(storagekvm tinykvm)
42+
43+
add_executable(pipekvm
44+
src/pipe.cpp
45+
)
46+
target_link_libraries(pipekvm tinykvm)

guest/pipe/pipe.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
int main() {
2+
}
3+
asm(".global sys_send\n"
4+
".type sys_send, @function\n"
5+
"sys_send:\n"
6+
" mov $0x10000, %eax\n"
7+
" out %eax, $0\n"
8+
" ret\n");
9+
asm(".global sys_recv\n"
10+
".type sys_recv, @function\n"
11+
"sys_recv:\n"
12+
" mov $0x10001, %eax\n"
13+
" out %eax, $0\n"
14+
" ret\n");
15+
extern void sys_send(const void* buf, unsigned len);
16+
extern void sys_recv(void* buf, unsigned len);
17+
18+
void caller() {
19+
char buf[4096];
20+
sys_send(buf, sizeof(buf));
21+
}
22+
void resumer() {
23+
char buf[4096];
24+
sys_recv(buf, sizeof(buf));
25+
}

src/pipe.cpp

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
#include <tinykvm/machine.hpp>
2+
#include <algorithm>
3+
#include <cstring>
4+
#include <cstdio>
5+
#include <fcntl.h>
6+
#include <sys/uio.h>
7+
#include "assert.hpp"
8+
#include "load_file.hpp"
9+
10+
#define GUEST_MEMORY 0x80000000 /* 2GB memory */
11+
#define GUEST_WORK_MEM 1024UL * 1024*1024 /* MB working mem */
12+
13+
inline timespec time_now();
14+
inline long nanodiff(timespec start_time, timespec end_time);
15+
16+
static void do_benchmark(tinykvm::Machine& sender_vm, tinykvm::Machine& receiver_vm,
17+
int pipefd[2],
18+
uint64_t& vmsplice_addr, uint64_t& vmsplice_size,
19+
uint64_t& input_addr, uint64_t& input_size,
20+
uint64_t receiver_addr, uint64_t sender_addr)
21+
{
22+
// Receiver VM will do a "blocking" syscall (pause)
23+
receiver_vm.vmcall(receiver_addr);
24+
if (input_addr == 0 || input_size == 0) {
25+
fprintf(stderr, "Error: input_addr == 0 || input_size == 0\n");
26+
exit(1);
27+
}
28+
// Sender VM will do a syscall that starts a vmsplice()
29+
// and then we resume receiver_vm while sender_vm is "doing this"
30+
sender_vm.vmcall(sender_addr);
31+
if (vmsplice_addr == 0 || vmsplice_size == 0) {
32+
fprintf(stderr, "Error: vmsplice_addr == 0 || vmsplice_size == 0\n");
33+
exit(1);
34+
}
35+
// Map the pipe from source data
36+
std::vector<tinykvm::Machine::Buffer> bufs;
37+
sender_vm.gather_buffers_from_range(bufs, vmsplice_addr, vmsplice_size);
38+
vmsplice(pipefd[1], (const struct iovec *)bufs.data(), bufs.size(), SPLICE_F_MOVE);
39+
40+
// Resume receiver_vm which now has data
41+
std::vector<tinykvm::Machine::WrBuffer> wbufs;
42+
receiver_vm.writable_buffers_from_range(wbufs, input_addr, input_size);
43+
// Do the actual vmsplice() into the pipe
44+
readv(pipefd[0], (const struct iovec *)wbufs.data(), wbufs.size());
45+
receiver_vm.vmresume();
46+
}
47+
48+
int main(int argc, char** argv)
49+
{
50+
if (argc < 2) {
51+
fprintf(stderr, "Missing argument: 64-bit ELF binary\n");
52+
exit(1);
53+
}
54+
std::vector<uint8_t> binary;
55+
std::vector<std::string> args;
56+
std::string filename = argv[1];
57+
binary = load_file(filename);
58+
59+
const tinykvm::DynamicElf dyn_elf = tinykvm::is_dynamic_elf(
60+
std::string_view{(const char*)binary.data(), binary.size()});
61+
if (dyn_elf.is_dynamic) {
62+
// Add ld-linux.so.2 as first argument
63+
static const std::string ld_linux_so = "/lib64/ld-linux-x86-64.so.2";
64+
binary = load_file(ld_linux_so);
65+
args.push_back(ld_linux_so);
66+
}
67+
68+
for (int i = 1; i < argc; i++) {
69+
args.push_back(argv[i]);
70+
}
71+
72+
tinykvm::Machine::init();
73+
74+
/* Setup */
75+
const tinykvm::MachineOptions options {
76+
.max_mem = GUEST_MEMORY,
77+
.max_cow_mem = GUEST_WORK_MEM,
78+
.reset_free_work_mem = 0,
79+
.verbose_loader = true,
80+
.executable_heap = dyn_elf.is_dynamic,
81+
};
82+
tinykvm::Machine master_vm {binary, options};
83+
//master_vm.print_pagetables();
84+
if (dyn_elf.is_dynamic) {
85+
static const std::vector<std::string> allowed_readable_paths({
86+
argv[1],
87+
// Add all common standard libraries to the list of allowed readable paths
88+
"/lib64/ld-linux-x86-64.so.2",
89+
"/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2",
90+
"/lib/x86_64-linux-gnu/libgcc_s.so.1",
91+
"/lib/x86_64-linux-gnu/libc.so.6",
92+
"/lib/x86_64-linux-gnu/libm.so.6",
93+
"/lib/x86_64-linux-gnu/libpthread.so.0",
94+
"/lib/x86_64-linux-gnu/libdl.so.2",
95+
"/lib/x86_64-linux-gnu/libstdc++.so.6",
96+
"/lib/x86_64-linux-gnu/librt.so.1",
97+
"/lib/x86_64-linux-gnu/libz.so.1",
98+
"/lib/x86_64-linux-gnu/libexpat.so.1",
99+
"/lib/x86_64-linux-gnu/glibc-hwcaps/x86-64-v2/libstdc++.so.6",
100+
"/lib/x86_64-linux-gnu/glibc-hwcaps/x86-64-v3/libstdc++.so.6",
101+
"/lib/x86_64-linux-gnu/glibc-hwcaps/x86-64-v4/libstdc++.so.6",
102+
});
103+
master_vm.fds().set_open_readable_callback(
104+
[&] (std::string& path) -> bool {
105+
return std::find(allowed_readable_paths.begin(),
106+
allowed_readable_paths.end(), path) != allowed_readable_paths.end();
107+
}
108+
);
109+
}
110+
111+
master_vm.setup_linux(
112+
args,
113+
{"LC_TYPE=C", "LC_ALL=C", "USER=root"});
114+
115+
/* Normal execution of _start -> main() */
116+
try {
117+
master_vm.run();
118+
} catch (const tinykvm::MachineException& me) {
119+
master_vm.print_registers();
120+
fprintf(stderr, "Machine exception: %s Data: 0x%lX\n", me.what(), me.data());
121+
throw;
122+
} catch (...) {
123+
master_vm.print_registers();
124+
throw;
125+
}
126+
127+
/* Fork master VM */
128+
master_vm.prepare_copy_on_write();
129+
130+
static uint64_t vmsplice_addr = 0;
131+
static uint64_t vmsplice_size = 0;
132+
static uint64_t input_addr = 0;
133+
static uint64_t input_size = 0;
134+
int pipefd[2];
135+
// Create a pipe to do the vmsplice() into
136+
if (pipe(pipefd) == -1) {
137+
perror("pipe");
138+
exit(1);
139+
}
140+
tinykvm::Machine::install_unhandled_syscall_handler(
141+
[] (tinykvm::vCPU& cpu, unsigned scall) {
142+
switch (scall) {
143+
case 0x10000:
144+
// A "blocking" syscall that does a vmsplice()
145+
vmsplice_addr = cpu.registers().rdi;
146+
vmsplice_size = cpu.registers().rsi;
147+
cpu.stop();
148+
break;
149+
case 0x10001:
150+
// The input buffer address
151+
// now blocking, waiting for a resume
152+
input_addr = cpu.registers().rdi;
153+
input_size = cpu.registers().rsi;
154+
cpu.stop();
155+
break;
156+
case 0x10707:
157+
throw "Unimplemented";
158+
default:
159+
printf("Unhandled system call: %u\n", scall);
160+
auto regs = cpu.registers();
161+
regs.rax = -ENOSYS;
162+
cpu.set_registers(regs);
163+
}
164+
});
165+
166+
const uint64_t sender_addr = master_vm.address_of("caller");
167+
const uint64_t receiver_addr = master_vm.address_of("resumer");
168+
169+
tinykvm::Machine vm1{master_vm, options};
170+
tinykvm::Machine vm2{master_vm, options};
171+
172+
// Warmup run
173+
do_benchmark(vm1, vm2, pipefd,
174+
vmsplice_addr, vmsplice_size,
175+
input_addr, input_size,
176+
receiver_addr, sender_addr);
177+
178+
asm("" ::: "memory");
179+
auto t0 = time_now();
180+
asm("" ::: "memory");
181+
182+
// Benchmark the two VMs
183+
for (int i = 0; i < 1000; i++) {
184+
do_benchmark(vm1, vm2, pipefd,
185+
vmsplice_addr, vmsplice_size,
186+
input_addr, input_size,
187+
receiver_addr, sender_addr);
188+
}
189+
190+
asm("" ::: "memory");
191+
auto t1 = time_now();
192+
asm("" ::: "memory");
193+
194+
close(pipefd[0]);
195+
close(pipefd[1]);
196+
197+
// Results
198+
const double total_us = nanodiff(t0, t1) * 1e-3;
199+
const double avg_us = (double)total_us / 1000.0;
200+
printf("Average time for vmsplice() between two VMs: %.3f us\n", avg_us);
201+
}
202+
203+
timespec time_now()
204+
{
205+
timespec t;
206+
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &t);
207+
return t;
208+
}
209+
long nanodiff(timespec start_time, timespec end_time)
210+
{
211+
return (end_time.tv_sec - start_time.tv_sec) * (long)1e9 + (end_time.tv_nsec - start_time.tv_nsec);
212+
}

0 commit comments

Comments
 (0)