From 8b7c6f8b9052b17e4c2392e1ac559ee9e9f31a49 Mon Sep 17 00:00:00 2001 From: Tomas Bzatek Date: Mon, 2 Dec 2024 14:52:54 +0100 Subject: [PATCH] initrd: avoid dynamic linking of libnvme, use dlopen() instead As suggested during the review process, NBFT is niche and most users won't need it. So keep the initrd generator light and only open libnvme when any NBFT table is found. In a typical dracut host-only scenario the nbft dracut module will be pulled in only when NBFT is present in the system, packing in nvme-cli and libnvme in the initramfs image. Signed-off-by: Tomas Bzatek --- config.h.meson | 3 ++ meson.build | 3 ++ src/nm-initrd-generator/meson.build | 18 +++----- src/nm-initrd-generator/nmi-nbft-reader.c | 53 +++++++++++++++++++---- 4 files changed, 57 insertions(+), 20 deletions(-) diff --git a/config.h.meson b/config.h.meson index 2bd93ce22..d41304db6 100644 --- a/config.h.meson +++ b/config.h.meson @@ -282,3 +282,6 @@ /* Define if NBFT support is enabled */ #mesondefine WITH_NBFT + +/* Define to 1 if dlvsym() is available */ +#mesondefine HAVE_DLVSYM diff --git a/meson.build b/meson.build index a88012c6b..e13cd9da0 100644 --- a/meson.build +++ b/meson.build @@ -137,6 +137,9 @@ config_h.set10('HAVE_DECL_REALLOCARRAY', cc.has_function('reallocarray', prefix: config_h.set10('HAVE_DECL_EXPLICIT_BZERO', cc.has_function('explicit_bzero', prefix: '#include ')) config_h.set10('HAVE_DECL_MEMFD_CREATE', cc.has_function('memfd_create', prefix: '#include ')) +config_h.set10('HAVE_DLVSYM', cc.has_function('dlvsym', prefix: '''#define _GNU_SOURCE + #include ''')) + # types config_h.set('SIZEOF_PID_T', cc.sizeof('pid_t', prefix : '#include ')) config_h.set('SIZEOF_UID_T', cc.sizeof('uid_t', prefix : '#include ')) diff --git a/src/nm-initrd-generator/meson.build b/src/nm-initrd-generator/meson.build index 0e9689f32..6b02e0668 100644 --- a/src/nm-initrd-generator/meson.build +++ b/src/nm-initrd-generator/meson.build @@ -1,15 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -libnmi_deps = [ - libnm_core_public_dep, -] - -if enable_nbft - libnmi_deps += [ - libnvme_dep - ] -endif - libnmi_core = static_library( 'nmi-core', sources: files( @@ -22,7 +12,9 @@ libnmi_core = static_library( src_inc, top_inc, ], - dependencies: libnmi_deps, + dependencies: [ + libnm_core_public_dep, + ], ) executable( @@ -32,7 +24,9 @@ executable( src_inc, top_inc, ], - dependencies: libnmi_deps, + dependencies: [ + libnm_core_public_dep, + ], link_with: [ libnmi_core, libnm_core_aux_intern, diff --git a/src/nm-initrd-generator/nmi-nbft-reader.c b/src/nm-initrd-generator/nmi-nbft-reader.c index 782c6acca..09e5eb12d 100644 --- a/src/nm-initrd-generator/nmi-nbft-reader.c +++ b/src/nm-initrd-generator/nmi-nbft-reader.c @@ -10,6 +10,7 @@ #if WITH_NBFT #include +#include #include "libnm-log-core/nm-logging.h" #include "libnm-core-intern/nm-core-internal.h" @@ -32,6 +33,34 @@ is_valid_addr(int family, const char *addr) && nm_utils_ipaddr_valid(family, addr)); } +static int (*_nvme_nbft_read)(struct nbft_info **nbft, const char *filename); +static void (*_nvme_nbft_free)(struct nbft_info *nbft); + +static void * +load_libnvme(void) +{ + void *handle; + + handle = dlopen("libnvme.so.1", RTLD_LAZY); + if (!handle) + return NULL; + +#if HAVE_DLVSYM + _nvme_nbft_read = dlvsym(handle, "nvme_nbft_read", "LIBNVME_1_5"); + _nvme_nbft_free = dlvsym(handle, "nvme_nbft_free", "LIBNVME_1_5"); +#else + /* no dlvsym() in musl */ + _nvme_nbft_read = dlsym(handle, "nvme_nbft_read"); + _nvme_nbft_free = dlsym(handle, "nvme_nbft_free"); +#endif + + if (!_nvme_nbft_read || !_nvme_nbft_free) { + dlclose(handle); + return NULL; + } + return handle; +} + static NMConnection * parse_hfi(struct nbft_info_hfi *hfi, int iface_idx, char **hostname) { @@ -225,12 +254,13 @@ parse_hfi(struct nbft_info_hfi *hfi, int iface_idx, char **hostname) NMConnection ** nmi_nbft_reader_parse(const char *sysfs_dir, char **hostname) { - GPtrArray *a; - gs_free char *path = NULL; - gs_free_error GError *error = NULL; - GDir *dir; - const char *entry_name; - int idx = 1; + nm_auto_unref_ptrarray GPtrArray *a = NULL; + gs_free char *path = NULL; + gs_free_error GError *error = NULL; + GDir *dir; + void *libnvme_handle = NULL; + const char *entry_name; + int idx = 1; g_return_val_if_fail(sysfs_dir != NULL, NULL); path = g_build_filename(sysfs_dir, "firmware", "acpi", "tables", NULL); @@ -250,8 +280,14 @@ nmi_nbft_reader_parse(const char *sysfs_dir, char **hostname) if (!g_str_has_prefix(entry_name, "NBFT")) continue; + /* attempt to load libnvme only on the first table match, saving some I/O */ + if (!libnvme_handle && !(libnvme_handle = load_libnvme())) { + g_dir_close(dir); + return NULL; + } + entry_path = g_build_filename(path, entry_name, NULL); - ret = nvme_nbft_read(&nbft, entry_path); + ret = _nvme_nbft_read(&nbft, entry_path); if (ret) { _LOGW(LOGD_CORE, "Error parsing NBFT table %s: %m", entry_path); continue; @@ -274,10 +310,11 @@ nmi_nbft_reader_parse(const char *sysfs_dir, char **hostname) g_ptr_array_add(a, connection); } - nvme_nbft_free(nbft); + _nvme_nbft_free(nbft); } g_dir_close(dir); + dlclose(libnvme_handle); g_ptr_array_add(a, NULL); /* trailing NULL-delimiter */ return (NMConnection **) g_ptr_array_free(a, FALSE); }