[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH 3/5] qmp: Added the helper stamp check.
From: |
Andrew Melnychenko |
Subject: |
[PATCH 3/5] qmp: Added the helper stamp check. |
Date: |
Sun, 19 Feb 2023 18:20:58 +0200 |
Added a function to check the stamp in the helper.
eBPF helper should have a special symbol that generates during the build.
QEMU checks the helper and determines that it fits, so the helper
will produce proper output.
Signed-off-by: Andrew Melnychenko <andrew@daynix.com>
---
meson.build | 10 +
monitor/meson.build | 1 +
monitor/qemu-ebpf-rss-helper-stamp-utils.c | 322 +++++++++++++++++++++
monitor/qemu-ebpf-rss-helper-stamp-utils.h | 39 +++
4 files changed, 372 insertions(+)
create mode 100644 monitor/qemu-ebpf-rss-helper-stamp-utils.c
create mode 100644 monitor/qemu-ebpf-rss-helper-stamp-utils.h
diff --git a/meson.build b/meson.build
index a76c855312..b409912aed 100644
--- a/meson.build
+++ b/meson.build
@@ -2868,6 +2868,16 @@ foreach d : hx_headers
endforeach
genh += hxdep
+ebpf_rss_helper_stamp = custom_target(
+ 'qemu-ebpf-rss-helper-stamp.h',
+ output : 'qemu-ebpf-rss-helper-stamp.h',
+ input : 'ebpf/rss.bpf.skeleton.h',
+ command : [python, '-c', 'import hashlib; print(\'#define
QEMU_EBPF_RSS_HELPER_STAMP
qemuEbpfRssHelperStamp_{}\'.format(hashlib.sha1(open(\'@INPUT@\',
\'rb\').read()).hexdigest()))'],
+ capture: true,
+)
+
+genh += ebpf_rss_helper_stamp
+
###################
# Collect sources #
###################
diff --git a/monitor/meson.build b/monitor/meson.build
index ccb4d1a8e6..36de73414b 100644
--- a/monitor/meson.build
+++ b/monitor/meson.build
@@ -6,6 +6,7 @@ softmmu_ss.add(files(
'hmp.c',
))
softmmu_ss.add([spice_headers, files('qmp-cmds.c')])
+softmmu_ss.add(files('qemu-ebpf-rss-helper-stamp-utils.c'))
specific_ss.add(when: 'CONFIG_SOFTMMU',
if_true: [files( 'hmp-cmds-target.c', 'hmp-target.c'), spice])
diff --git a/monitor/qemu-ebpf-rss-helper-stamp-utils.c
b/monitor/qemu-ebpf-rss-helper-stamp-utils.c
new file mode 100644
index 0000000000..23efc36ef0
--- /dev/null
+++ b/monitor/qemu-ebpf-rss-helper-stamp-utils.c
@@ -0,0 +1,322 @@
+/*
+ * QEMU helper stamp check utils.
+ *
+ * Developed by Daynix Computing LTD (http://www.daynix.com)
+ *
+ * Authors:
+ * Andrew Melnychenko <andrew@daynix.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ * Description: This file mostly implements helper stamp checking.
+ * The stamp is implemented in a similar way as in qemu modules.
+ * The helper should contain a specific symbol.
+ * Not in a similar way is symbol checking - here we parse
+ * the ELF file. For now, only eBPF helper contains
+ * the stamp, and the stamp is generated from
+ * sha1 ebpf/rss.bpf.skeleton.h (see meson.build).
+ */
+
+#include "qemu/osdep.h"
+#include "elf.h"
+#include "qemu-ebpf-rss-helper-stamp-utils.h"
+
+#include <glib/gstdio.h>
+
+#ifdef CONFIG_LINUX
+
+static void *file_allocate_and_read(int fd, off_t off, size_t size)
+{
+ void *data;
+ int err;
+
+ if (fd < 0) {
+ return NULL;
+ }
+
+ err = lseek(fd, off, SEEK_SET);
+ if (err < 0) {
+ return NULL;
+ }
+
+ data = g_new0(char, size);
+ if (data == NULL) {
+ return NULL;
+ }
+
+ err = read(fd, data, size);
+ if (err < 0) {
+ g_free(data);
+ return NULL;
+ }
+
+ return data;
+}
+
+static Elf64_Shdr *elf64_get_section_table(int fd, Elf64_Ehdr *elf_header)
+{
+ if (elf_header == NULL) {
+ return NULL;
+ }
+ return (Elf64_Shdr *)file_allocate_and_read(fd, elf_header->e_shoff,
+ elf_header->e_shnum * elf_header->e_shentsize);
+}
+
+static Elf32_Shdr *elf32_get_section_table(int fd, Elf32_Ehdr *elf_header)
+{
+ if (elf_header == NULL) {
+ return NULL;
+ }
+ return (Elf32_Shdr *)file_allocate_and_read(fd, elf_header->e_shoff,
+ elf_header->e_shnum * elf_header->e_shentsize);
+}
+
+static void *elf64_get_section_data(int fd, const Elf64_Shdr* section_header)
+{
+ if (fd < 0 || section_header == NULL) {
+ return NULL;
+ }
+ return file_allocate_and_read(fd, section_header->sh_offset,
+ section_header->sh_size);
+}
+
+static void *elf32_get_section_data(int fd, const Elf32_Shdr* section_header)
+{
+ if (fd < 0 || section_header == NULL) {
+ return NULL;
+ }
+ return file_allocate_and_read(fd, section_header->sh_offset,
+ section_header->sh_size);
+}
+
+static bool elf64_check_symbol_in_symbol_table(int fd,
+ Elf64_Shdr *section_table,
+ Elf64_Shdr *symbol_section,
+ const char *symbol)
+{
+ Elf64_Sym *symbol_table;
+ char *string_table;
+ uint32_t i;
+ bool ret = false;
+
+ symbol_table = (Elf64_Sym *) elf64_get_section_data(fd, symbol_section);
+ if (symbol_table == NULL) {
+ return false;
+ }
+
+ string_table = (char *) elf64_get_section_data(
+ fd, section_table + symbol_section->sh_link);
+ if (string_table == NULL) {
+ g_free(symbol_table);
+ return false;
+ }
+
+ for (i = 0; i < (symbol_section->sh_size / sizeof(Elf64_Sym)); ++i) {
+ if (strncmp((string_table + symbol_table[i].st_name),
+ symbol, strlen(symbol)) == 0)
+ {
+ ret = true;
+ break;
+ }
+ }
+
+ g_free(string_table);
+ g_free(symbol_table);
+ return ret;
+}
+
+static bool elf32_check_symbol_in_symbol_table(int fd,
+ Elf32_Shdr *section_table,
+ Elf32_Shdr *symbol_section,
+ const char *symbol)
+{
+ Elf32_Sym *symbol_table;
+ char *string_table;
+ uint32_t i;
+ bool ret = false;
+
+ symbol_table = (Elf32_Sym *) elf32_get_section_data(fd, symbol_section);
+ if (symbol_table == NULL) {
+ return false;
+ }
+
+ string_table = (char *) elf32_get_section_data(fd,
+ section_table +
symbol_section->sh_link);
+ if (string_table == NULL) {
+ g_free(symbol_table);
+ return false;
+ }
+
+ for (i = 0; i < (symbol_section->sh_size / sizeof(Elf32_Sym)); ++i) {
+ if (strncmp((string_table + symbol_table[i].st_name),
+ symbol, strlen(symbol)) == 0)
+ {
+ ret = true;
+ break;
+ }
+ }
+
+ g_free(string_table);
+ g_free(symbol_table);
+ return ret;
+}
+
+static bool elf64_check_stamp(int fd, Elf64_Ehdr *elf_header, const char
*stamp)
+{
+ Elf64_Shdr *section_table;
+ size_t i;
+ bool ret = false;
+
+ section_table = elf64_get_section_table(fd, elf_header);
+ if (section_table == NULL) {
+ return false;
+ }
+
+ for (i = 0; i < elf_header->e_shnum; ++i) {
+ if ((section_table[i].sh_type == SHT_SYMTAB)
+ || (section_table[i].sh_type == SHT_DYNSYM)) {
+ if (elf64_check_symbol_in_symbol_table(fd, section_table,
+ section_table + i, stamp)) {
+ ret = true;
+ break;
+ }
+ }
+ }
+
+ g_free(section_table);
+ return ret;
+}
+
+static bool elf32_check_stamp(int fd, Elf32_Ehdr *elf_header, const char
*stamp)
+{
+ Elf32_Shdr *section_table;
+ size_t i;
+ bool ret = false;
+
+ section_table = elf32_get_section_table(fd, elf_header);
+ if (section_table == NULL) {
+ return false;
+ }
+
+ for (i = 0; i < elf_header->e_shnum; ++i) {
+ if ((section_table[i].sh_type == SHT_SYMTAB)
+ || (section_table[i].sh_type == SHT_DYNSYM)) {
+ if (elf32_check_symbol_in_symbol_table(fd, section_table,
+ section_table + i, stamp)) {
+ ret = true;
+ break;
+ }
+ }
+ }
+
+ g_free(section_table);
+ return ret;
+}
+
+static bool qemu_check_helper_stamp(const char *path, const char *stamp)
+{
+ int fd;
+ bool ret = false;
+ Elf64_Ehdr *elf_header;
+
+ fd = open(path, O_RDONLY | O_SYNC);
+ if (fd < 0) {
+ return false;
+ }
+
+ elf_header = (Elf64_Ehdr *)file_allocate_and_read(
+ fd, 0, sizeof(Elf64_Ehdr));
+ if (elf_header == NULL) {
+ goto error;
+ }
+
+ if (strncmp((char *)elf_header->e_ident, ELFMAG, SELFMAG)) {
+ g_free(elf_header);
+ goto error;
+ }
+
+ if (elf_header->e_ident[EI_CLASS] == ELFCLASS64) {
+ ret = elf64_check_stamp(fd, elf_header, stamp);
+ } else if (elf_header->e_ident[EI_CLASS] == ELFCLASS32) {
+ ret = elf32_check_stamp(fd, (Elf32_Ehdr *)elf_header, stamp);
+ }
+
+ g_free(elf_header);
+error:
+ close(fd);
+ return ret;
+}
+
+#else
+
+static bool qemu_check_helper_stamp(const char *path, const char *stamp)
+{
+ return false;
+}
+
+#endif
+
+char *qemu_find_default_ebpf_rss_helper(void)
+{
+ char *qemu_exec = NULL;
+ char *qemu_dir = NULL;
+ char *helper = NULL;
+
+ helper = g_build_filename(CONFIG_QEMU_HELPERDIR,
+ QEMU_DEFAULT_EBPF_RSS_HELPER_BIN_NAME, NULL);
+ if (g_access(helper, X_OK) == 0
+ && qemu_check_helper_stamp(helper, QEMU_EBPF_RSS_HELPER_STAMP_STR)) {
+ return helper;
+ }
+ g_free(helper);
+
+#ifdef CONFIG_LINUX
+ qemu_exec = g_file_read_link("/proc/self/exe", NULL);
+#else
+ qemu_exec = NULL;
+#endif
+ if (qemu_exec != NULL) {
+ qemu_dir = g_path_get_dirname(qemu_exec);
+ g_free(qemu_exec);
+ helper = g_build_filename(qemu_dir,
+ QEMU_DEFAULT_EBPF_RSS_HELPER_BIN_NAME, NULL);
+ g_free(qemu_dir);
+ if (g_access(helper, X_OK) == 0
+ && qemu_check_helper_stamp(helper, QEMU_EBPF_RSS_HELPER_STAMP_STR))
{
+ return helper;
+ }
+ g_free(helper);
+ }
+
+ return NULL;
+}
+
+char *qemu_check_suggested_ebpf_rss_helper(const char *path)
+{
+ char *helperbin = NULL;
+ struct stat statbuf; /* NOTE: use GStatBuf? */
+
+ /* check is dir or file */
+ if (g_stat(path, &statbuf) < 0) {
+ return NULL;
+ }
+
+ if (statbuf.st_mode & S_IFDIR) {
+ /* is dir */
+ helperbin = g_build_filename(path,
+ QEMU_DEFAULT_EBPF_RSS_HELPER_BIN_NAME, NULL);
+
+ } else if (statbuf.st_mode & S_IFREG) {
+ /* is file */
+ helperbin = g_strdup(path);
+ }
+
+ if (qemu_check_helper_stamp(helperbin, QEMU_EBPF_RSS_HELPER_STAMP_STR)) {
+ return helperbin;
+ }
+
+ g_free(helperbin);
+
+ return NULL;
+}
diff --git a/monitor/qemu-ebpf-rss-helper-stamp-utils.h
b/monitor/qemu-ebpf-rss-helper-stamp-utils.h
new file mode 100644
index 0000000000..7490568aa1
--- /dev/null
+++ b/monitor/qemu-ebpf-rss-helper-stamp-utils.h
@@ -0,0 +1,39 @@
+/*
+ * QEMU helper stamp check utils.
+ *
+ * Developed by Daynix Computing LTD (http://www.daynix.com)
+ *
+ * Authors:
+ * Andrew Melnychenko <andrew@daynix.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#ifndef QEMU_QEMU_HELPER_STAMP_UTILS_H
+#define QEMU_QEMU_HELPER_STAMP_UTILS_H
+
+#include "qemu-ebpf-rss-helper-stamp.h" /* generated stamp per build */
+
+#define QEMU_EBPF_RSS_HELPER_STAMP_STR
stringify(QEMU_EBPF_RSS_HELPER_STAMP)
+
+#define QEMU_DEFAULT_EBPF_RSS_HELPER_BIN_NAME "qemu-ebpf-rss-helper"
+
+/**
+ * Trying to find the helper with a valid stamp in HELPERDIR
+ * or next to the QEMU binary.
+ * @return path to the eBPF RSS helper bin or NULL(helper not found).
+ */
+char *qemu_find_default_ebpf_rss_helper(void);
+
+/**
+ * Check the helper by the suggested path. The helper should have a valid
stamp.
+ * @param path - it can be either a file or directory path.
+ * For the file - checks the stamp of the file.
+ * For the directory - looks for QEMU_DEFAULT_EBPF_RSS_HELPER_BIN_NAME
+ * and checks the stamp of that file.
+ * @return path to valid eBPF RSS helper bin or NULL.
+ */
+char *qemu_check_suggested_ebpf_rss_helper(const char *path);
+
+#endif /* QEMU_QEMU_HELPER_STAMP_UTILS_H */
--
2.39.1
- [PATCH 0/5] eBPF RSS Helper support., Andrew Melnychenko, 2023/02/19
- [PATCH 1/5] ebpf: Added eBPF initialization by fds and map update., Andrew Melnychenko, 2023/02/19
- [PATCH 2/5] virtio-net: Added property to load eBPF RSS with fds., Andrew Melnychenko, 2023/02/19
- [PATCH 3/5] qmp: Added the helper stamp check.,
Andrew Melnychenko <=
- Re: [PATCH 3/5] qmp: Added the helper stamp check., Daniel P . Berrangé, 2023/02/20
- Re: [PATCH 3/5] qmp: Added the helper stamp check., Yuri Benditovich, 2023/02/28
- Re: [PATCH 3/5] qmp: Added the helper stamp check., Daniel P . Berrangé, 2023/02/28
- Re: [PATCH 3/5] qmp: Added the helper stamp check., Toke Høiland-Jørgensen, 2023/02/28
- Re: [PATCH 3/5] qmp: Added the helper stamp check., Daniel P . Berrangé, 2023/02/28
- Re: [PATCH 3/5] qmp: Added the helper stamp check., Toke Høiland-Jørgensen, 2023/02/28
- Re: [PATCH 3/5] qmp: Added the helper stamp check., Daniel P . Berrangé, 2023/02/28
[PATCH 4/5] ebpf_rss_helper: Added helper for eBPF RSS., Andrew Melnychenko, 2023/02/19