diff --git a/vm/Makefile b/vm/Makefile index ee451889c..30f2b1a2b 100644 --- a/vm/Makefile +++ b/vm/Makefile @@ -33,7 +33,7 @@ all: libubpf.a test ubpf_jit_x86_64.o: ubpf_jit_x86_64.c ubpf_jit_x86_64.h -libubpf.a: ubpf_vm.o ubpf_jit_x86_64.o ubpf_loader.o +libubpf.a: ubpf_vm.o ubpf_jit_x86_64.o ubpf_loader.o ubpf_inst_cnt.o ar rc $@ $^ test: test.o libubpf.a diff --git a/vm/inc/ubpf.h b/vm/inc/ubpf.h index 3f31e4339..f28b94b21 100644 --- a/vm/inc/ubpf.h +++ b/vm/inc/ubpf.h @@ -17,6 +17,7 @@ #ifndef UBPF_H #define UBPF_H +#include #include #include #include @@ -36,6 +37,13 @@ typedef uint64_t (*ubpf_jit_fn)(void *mem, size_t mem_len); struct ubpf_vm *ubpf_create(void); void ubpf_destroy(struct ubpf_vm *vm); +struct ubpf_inst_cnt +{ + size_t inst; + size_t inst_cmpl; + size_t inst_vm; +}; + /* * Enable / disable bounds_check * @@ -132,4 +140,10 @@ int ubpf_translate(struct ubpf_vm *vm, uint8_t *buffer, size_t *size, char **err */ int ubpf_set_unwind_function_index(struct ubpf_vm *vm, unsigned int idx); +/* + * Populate the struct ubpf_inst_cnt * field in struct vm so we can + * enable instruction counting. + */ +void ubpf_set_inst_cnt(struct ubpf_vm *vm, struct ubpf_inst_cnt *inst_cnt); + #endif diff --git a/vm/ubpf_inst_cnt.c b/vm/ubpf_inst_cnt.c new file mode 100644 index 000000000..d3e7cac40 --- /dev/null +++ b/vm/ubpf_inst_cnt.c @@ -0,0 +1,94 @@ +/* + * Copyright 2021 Eideticom, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define _GNU_SOURCE +#include "ubpf_int.h" + +#include +#include +#include + +#ifdef __linux__ +#include + +void enable_instruction_count(int fd) +{ + ioctl(fd, PERF_EVENT_IOC_RESET, 0); + ioctl(fd, PERF_EVENT_IOC_ENABLE, 0); +} + +void disable_instruction_count(int fd) +{ + ioctl(fd, PERF_EVENT_IOC_DISABLE, 0); +} + +int setup_instruction_counter(void) +{ + int fd; + + struct perf_event_attr pe = { + .type = PERF_TYPE_HARDWARE, + .size = sizeof(struct perf_event_attr), + .config = PERF_COUNT_HW_INSTRUCTIONS, + .disabled = 1, + .exclude_kernel = 1, + .exclude_hv = 1, + }; + + fd = syscall(__NR_perf_event_open, &pe, 0, -1, -1, 0); + + return fd; +} + +long long get_instruction_count(int fd) +{ + long long count; + ssize_t rd; + + rd = read(fd, &count, sizeof(long long)); + if (rd != sizeof(long long)) + return -1; + + return count; +} + +#else /* __linux__ */ + +#include + +void disable_instruction_count(int fd) +{ + (void)fd; +} + +void enable_instruction_count(int fd) +{ + (void)fd; +} + +int setup_instruction_counter(void) +{ + errno = ENOTSUP; + return -1; +} + +long long get_instruction_count(int fd) +{ + (void)fd; + return -1; +} + +#endif /* __linux__ */ diff --git a/vm/ubpf_int.h b/vm/ubpf_int.h index 60153c0f2..a04bae7f7 100644 --- a/vm/ubpf_int.h +++ b/vm/ubpf_int.h @@ -33,9 +33,15 @@ struct ubpf_vm { bool bounds_check_enabled; int (*error_printf)(FILE* stream, const char* format, ...); int unwind_stack_extension_index; + struct ubpf_inst_cnt *inst_cnt; }; char *ubpf_error(const char *fmt, ...); unsigned int ubpf_lookup_registered_function(struct ubpf_vm *vm, const char *name); +void enable_instruction_count(int fd); +void disable_instruction_count(int fd); +int setup_instruction_counter(void); +long long get_instruction_count(int fd); + #endif diff --git a/vm/ubpf_vm.c b/vm/ubpf_vm.c index 0c701408d..dab5e9727 100644 --- a/vm/ubpf_vm.c +++ b/vm/ubpf_vm.c @@ -177,6 +177,10 @@ ubpf_exec(const struct ubpf_vm *vm, void *mem, size_t mem_len, uint64_t* bpf_ret const uint16_t cur_pc = pc; struct ebpf_inst inst = insts[pc++]; + if (vm->inst_cnt) { + vm->inst_cnt->inst_vm++; + } + switch (inst.opcode) { case EBPF_OP_ADD_IMM: reg[inst.dst] += inst.imm; @@ -595,20 +599,61 @@ int ubpf_run(struct ubpf_vm *vm, void *mem, size_t mem_len, bool jit, uint64_t *bpf_return_value, char **errmsg) { + int cntrfd = 0; + ubpf_jit_fn fn; + + if (vm->inst_cnt) { + cntrfd = setup_instruction_counter(); + if (cntrfd == -1) { + vm->error_printf(stderr, "uBPF warning: unable to setup instruction counter\n"); + } + } + if (jit) { - ubpf_jit_fn fn = ubpf_compile(vm, errmsg); + if (vm->inst_cnt) { + enable_instruction_count(cntrfd); + } + fn = ubpf_compile(vm, errmsg); + if (vm->inst_cnt) { + disable_instruction_count(cntrfd); + vm->inst_cnt->inst_cmpl = get_instruction_count(cntrfd); + if (vm->inst_cnt->inst_cmpl < 0) { + *errmsg = ubpf_error("Unable to get instruction count"); + return -1; + } + } if (fn == NULL) { return -1; } + } + + if (vm->inst_cnt) { + enable_instruction_count(cntrfd); + } + if (jit) { *bpf_return_value = fn(mem, mem_len); } else { if (ubpf_exec(vm, mem, mem_len, bpf_return_value) < 0) { *bpf_return_value = UINT64_MAX; } } + if (vm->inst_cnt) { + disable_instruction_count(cntrfd); + vm->inst_cnt->inst = get_instruction_count(cntrfd); + if (vm->inst_cnt->inst < 0) { + *errmsg = ubpf_error("Unable to get instruction count"); + return -1; + } + } return 0; } +void +ubpf_set_inst_cnt(struct ubpf_vm *vm, struct ubpf_inst_cnt *inst_cnt) +{ + vm->inst_cnt = inst_cnt; +} + static bool validate(const struct ubpf_vm *vm, const struct ebpf_inst *insts, uint32_t num_insts, char **errmsg) {