diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ca0923..51ff90b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,14 @@ endif() include(GNUInstallDirs) set(CMAKE_INSTALL_LIBDIR lib) +# Options to enable/disable library support (on by default) +option(DTO_ACCEL_CONFIG_SUPPORT "Enable accel-config for automatic DSA discovery (requires libaccel-config)" ON) +option(DTO_NUMA_SUPPORT "Enable NUMA awareness support (requires libnuma)" ON) + +# Options to statically link dependencies into libdto.so +option(DTO_STATIC_ACCEL_CONFIG "Statically link accel-config into libdto.so" OFF) +option(DTO_STATIC_NUMA "Statically link numa into libdto.so (requires PIC-compiled libnuma.a)" OFF) + # Build the shared library add_library(dto SHARED dto.c) add_library(DTO::dto ALIAS dto) @@ -29,10 +37,62 @@ add_compile_definitions(_GNU_SOURCE) # Add the -DDTO_STATS_SUPPORT preprocessor definition target_compile_definitions(dto PRIVATE DTO_STATS_SUPPORT) -# Link libraries. Use PRIVATE so accel-config/numa/dl are the shared library's -# own dependencies (resolved via its DT_NEEDED) rather than INTERFACE +# Link libraries +set(DTO_STATIC_LIBS "") +set(DTO_DYNAMIC_LIBS dl) + +# Handle accel-config support +if(DTO_ACCEL_CONFIG_SUPPORT) + target_compile_definitions(dto PRIVATE DTO_ACCEL_CONFIG_SUPPORT) + message(STATUS "accel-config support: enabled") + + if(DTO_STATIC_ACCEL_CONFIG) + find_library(ACCEL_CONFIG_STATIC NAMES libaccel-config.a PATHS /usr/lib /usr/local/lib /usr/lib/x86_64-linux-gnu) + if(NOT ACCEL_CONFIG_STATIC) + message(FATAL_ERROR "libaccel-config.a not found. Install static accel-config or disable DTO_STATIC_ACCEL_CONFIG.") + endif() + message(STATUS "Statically linking accel-config: ${ACCEL_CONFIG_STATIC}") + list(APPEND DTO_STATIC_LIBS ${ACCEL_CONFIG_STATIC}) + else() + list(APPEND DTO_DYNAMIC_LIBS accel-config) + endif() +else() + message(STATUS "accel-config support: disabled (DTO_WQ_LIST env var required at runtime)") +endif() + +# Handle numa support +if(DTO_NUMA_SUPPORT) + target_compile_definitions(dto PRIVATE DTO_NUMA_SUPPORT) + message(STATUS "NUMA support: enabled") + + if(DTO_STATIC_NUMA) + find_library(NUMA_STATIC NAMES libnuma.a PATHS /usr/lib /usr/local/lib /usr/lib/x86_64-linux-gnu) + if(NOT NUMA_STATIC) + message(FATAL_ERROR "libnuma.a not found. Install static numa or disable DTO_STATIC_NUMA.") + endif() + message(STATUS "Statically linking numa: ${NUMA_STATIC}") + message(STATUS " (Note: libnuma.a must be compiled with -fPIC)") + list(APPEND DTO_STATIC_LIBS ${NUMA_STATIC}) + else() + list(APPEND DTO_DYNAMIC_LIBS numa) + endif() +else() + message(STATUS "NUMA support: disabled") +endif() + +# Link everything together. Use PRIVATE so accel-config/numa/dl are the shared +# library's own dependencies (resolved via its DT_NEEDED) rather than INTERFACE # requirements that would be forced onto every downstream consumer of DTO::dto. -target_link_libraries(dto PRIVATE dl accel-config numa) +if(DTO_STATIC_LIBS) + target_link_libraries(dto PRIVATE + -Wl,--whole-archive + ${DTO_STATIC_LIBS} + -Wl,--no-whole-archive + ${DTO_DYNAMIC_LIBS} + ) +else() + target_link_libraries(dto PRIVATE ${DTO_DYNAMIC_LIBS}) +endif() include(CheckCCompilerFlag) check_c_compiler_flag("-mwaitpkg" HAS_WAITPKG) diff --git a/Makefile b/Makefile index 95af413..06cc2e1 100644 --- a/Makefile +++ b/Makefile @@ -7,10 +7,10 @@ all: libdto dto-test-wodto DML_LIB_CXX=-D_GNU_SOURCE libdto: dto.c - gcc -shared -fPIC -Wl,-soname,libdto.so dto.c $(DML_LIB_CXX) -DDTO_STATS_SUPPORT -o libdto.so.1.0 -laccel-config -ldl -lnuma -mwaitpkg + gcc -shared -fPIC -Wl,-soname,libdto.so dto.c $(DML_LIB_CXX) -DDTO_STATS_SUPPORT -DDTO_ACCEL_CONFIG_SUPPORT -DDTO_NUMA_SUPPORT -o libdto.so.1.0 -laccel-config -ldl -lnuma -mwaitpkg libdto_nostats: dto.c - gcc -shared -fPIC -Wl,-soname,libdto.so dto.c $(DML_LIB_CXX) -o libdto.so.1.0 -laccel-config -ldl -lnuma -mwaitpkg + gcc -shared -fPIC -Wl,-soname,libdto.so dto.c $(DML_LIB_CXX) -DDTO_ACCEL_CONFIG_SUPPORT -DDTO_NUMA_SUPPORT -o libdto.so.1.0 -laccel-config -ldl -lnuma -mwaitpkg install: cp libdto.so.1.0 /usr/lib64/ diff --git a/README.md b/README.md index 6328f64..bd3e239 100644 --- a/README.md +++ b/README.md @@ -58,8 +58,10 @@ Following environment variables control the behavior of DTO library: DTO_CPU_SIZE_FRACTION=0.xx (specifies fraction of job performed by CPU, in parallel to DSA). Default is 0.00 DTO_AUTO_ADJUST_KNOBS=0/1 (disables/enables auto tuning of cpu_size_fraction and dsa_min_bytes parameters. 0 -- disable, 1 -- enable (default)) DTO_IS_NUMA_AWARE=0/1/2 (disables/buffer-centric/cpu-centric numa awareness. 0 -- disable (default), 1 -- buffer-centric, 2 - cpu-centric) + Only available when DTO is built with NUMA support (see Build options). DTO_WQ_LIST="semi-colon(;) separated list of DSA WQs to use". The WQ names should match their names in /dev/dsa/ directory (see example below). - If not specified, DTO will try to auto-discover and use all available WQs. + If not specified, DTO will try to auto-discover and use all available WQs. Auto-discovery requires DTO to be + built with accel-config support (see Build options); otherwise DTO_WQ_LIST is mandatory. DTO_DSA_MEMCPY=0/1, 1 (default) - DTO uses DSA to process memcpy, 0 - DTO uses system memcpy DTO_DSA_MEMMOVE=0/1, 1 (default) - DTO uses DSA to process memmove, 0 - DTO uses system memmove DTO_DSA_MEMSET=0/1, 1 (default) - DTO uses DSA to process memset, 0 - DTO use system memset @@ -103,6 +105,8 @@ On Fedora/CentOS/Rhel: kernel-headers, accel-config-devel, libuuid-devel, libnum On Ubuntu/Debian: linux-libc-dev, libaccel-config-dev, uuid-dev, libnuma-dev +The accel-config and numa packages are only required when the corresponding build options are enabled (they are enabled by default, see [Build options](#build-options) below). + ### CMake ### ```bash cmake -B build @@ -110,6 +114,27 @@ cmake --build build -j sudo cmake --install build ``` +#### Build options #### + +The accel-config and libnuma dependencies are optional and can be disabled at configure time: + +| Option | Default | Description | +| --- | --- | --- | +| `DTO_ACCEL_CONFIG_SUPPORT` | `ON` | Use libaccel-config to auto-discover configured DSA work queues. When disabled, libdto does not link against libaccel-config, and the `DTO_WQ_LIST` environment variable **must** be set at runtime (e.g., `export DTO_WQ_LIST="wq0.0;wq1.0"`) since auto-discovery is unavailable. | +| `DTO_NUMA_SUPPORT` | `ON` | Use libnuma for NUMA-aware work queue selection. When disabled, libdto does not link against libnuma and the `DTO_IS_NUMA_AWARE` environment variable has no effect (work queues are used in round-robin order). | +| `DTO_STATIC_ACCEL_CONFIG` | `OFF` | Statically link libaccel-config into libdto.so instead of depending on the shared library at runtime. Requires `libaccel-config.a` to be installed. | +| `DTO_STATIC_NUMA` | `OFF` | Statically link libnuma into libdto.so instead of depending on the shared library at runtime. Requires a `libnuma.a` compiled with `-fPIC`. | + +Example: build without either dependency: +```bash +cmake -B build -DDTO_ACCEL_CONFIG_SUPPORT=OFF -DDTO_NUMA_SUPPORT=OFF +cmake --build build -j +``` + +The deprecated Makefile always builds with both features enabled (it passes +`-DDTO_ACCEL_CONFIG_SUPPORT -DDTO_NUMA_SUPPORT` to the compiler); use CMake if +you need to build without them. + The CMake build also produces the test applications in the build directory: `dto-test` (for the -ldto method) and `dto-test-wodto` (for the LD_PRELOAD method). diff --git a/dto.c b/dto.c index 48ef3a7..dab4642 100644 --- a/dto.c +++ b/dto.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -22,9 +23,13 @@ #include #include #include +#ifdef DTO_ACCEL_CONFIG_SUPPORT #include +#endif +#ifdef DTO_NUMA_SUPPORT #include #include +#endif #include #define likely(x) __builtin_expect((x), 1) @@ -70,7 +75,9 @@ static void * (*orig_memmove)(void *dest, const void *src, size_t n); static int (*orig_memcmp)(const void *s1, const void *s2, size_t n); struct dto_wq { +#ifdef DTO_ACCEL_CONFIG_SUPPORT struct accfg_wq *acc_wq; +#endif char wq_path[PATH_MAX]; uint64_t dsa_gencap; int wq_size; @@ -80,11 +87,13 @@ struct dto_wq { bool wq_mmapped; }; +#ifdef DTO_NUMA_SUPPORT struct dto_device { struct dto_wq* wqs[MAX_WQS]; uint8_t num_wqs; atomic_uchar next_wq; }; +#endif enum wait_options { WAIT_BUSYPOLL = 0, @@ -114,7 +123,9 @@ static const char * const numa_aware_names[] = { // global workqueue variables static struct dto_wq wqs[MAX_WQS]; +#ifdef DTO_NUMA_SUPPORT static struct dto_device* devices[MAX_NUMA_NODES]; +#endif static uint8_t num_wqs; static atomic_uchar next_wq; static atomic_uchar dto_initialized; @@ -822,6 +833,7 @@ static unsigned long long dto_get_param_ullong(int dir_fd, char *path, int *err) return val; } +#ifdef DTO_NUMA_SUPPORT static struct dto_device* get_dto_device(int dev_numa_node) { struct dto_device* dev = NULL; @@ -845,7 +857,9 @@ static void correct_devices_list() { } } } +#endif +#ifdef DTO_NUMA_SUPPORT static __always_inline int get_numa_node(void* buf) { int numa_node = -1; @@ -886,7 +900,9 @@ static __always_inline int get_numa_node(void* buf) { return numa_node; } +#endif +#ifdef DTO_NUMA_SUPPORT static void cleanup_devices() { struct dto_device* dev = NULL; for (uint i = 0; i < MAX_NUMA_NODES; i++) { @@ -897,6 +913,7 @@ static void cleanup_devices() { devices[i] = NULL; } } +#endif static bool test_write_syscall(struct dto_wq *wq) { @@ -956,11 +973,13 @@ static int dsa_init_from_wq_list(char *wq_list) goto fail_wq; } +#ifdef DTO_NUMA_SUPPORT const int dev_numa_node = (int)dto_get_param_ullong(dir_fd, "numa_node", &rc); if (rc) { close(dir_fd); goto fail_wq; } +#endif close(dir_fd); @@ -1029,6 +1048,7 @@ static int dsa_init_from_wq_list(char *wq_list) close(wqs[num_wqs].wq_fd); } +#ifdef DTO_NUMA_SUPPORT if (is_numa_aware) { struct dto_device* dev = get_dto_device(dev_numa_node); if (dev != NULL && @@ -1036,6 +1056,7 @@ static int dsa_init_from_wq_list(char *wq_list) dev->wqs[dev->num_wqs++] = &wqs[num_wqs]; } } +#endif ++num_wqs; if (num_wqs == MAX_WQS) @@ -1049,9 +1070,11 @@ static int dsa_init_from_wq_list(char *wq_list) goto fail; } +#ifdef DTO_NUMA_SUPPORT if (is_numa_aware) { correct_devices_list(); } +#endif return 0; @@ -1060,12 +1083,15 @@ static int dsa_init_from_wq_list(char *wq_list) munmap(wqs[j].wq_portal, 0x1000); num_wqs = 0; +#ifdef DTO_NUMA_SUPPORT cleanup_devices(); +#endif fail: return rc; } +#ifdef DTO_ACCEL_CONFIG_SUPPORT static int dsa_init_from_accfg(void) { int used_devids[MAX_WQS]; @@ -1104,12 +1130,14 @@ static int dsa_init_from_accfg(void) if (i != num_wqs) continue; +#ifdef DTO_NUMA_SUPPORT struct dto_device* dev = NULL; if (is_numa_aware) { const int dev_numa_node = accfg_device_get_numa_node(device); dev = get_dto_device(dev_numa_node); } +#endif accfg_wq_foreach(device, wq) { enum accfg_wq_state wstate; @@ -1139,11 +1167,13 @@ static int dsa_init_from_accfg(void) used_devids[num_wqs] = accfg_device_get_id(device); +#ifdef DTO_NUMA_SUPPORT if (is_numa_aware && dev != NULL && dev->num_wqs < MAX_WQS) { dev->wqs[dev->num_wqs++] = &wqs[num_wqs]; } +#endif num_wqs++; } @@ -1195,9 +1225,11 @@ static int dsa_init_from_accfg(void) } } +#ifdef DTO_NUMA_SUPPORT if (is_numa_aware) { correct_devices_list(); } +#endif accfg_unref(dto_ctx); return 0; @@ -1207,11 +1239,14 @@ static int dsa_init_from_accfg(void) munmap(wqs[j].wq_portal, 0x1000); num_wqs = 0; +#ifdef DTO_NUMA_SUPPORT cleanup_devices(); +#endif fail: accfg_unref(dto_ctx); return rc; } +#endif /* DTO_ACCEL_CONFIG_SUPPORT */ static int dsa_init(void) { @@ -1255,8 +1290,15 @@ static int dsa_init(void) } env_str = getenv("DTO_WQ_LIST"); - if (env_str == NULL) + if (env_str == NULL) { +#ifdef DTO_ACCEL_CONFIG_SUPPORT return dsa_init_from_accfg(); +#else + LOG_ERROR("DTO_WQ_LIST environment variable must be set when accel-config support is disabled.\n"); + LOG_ERROR("Example: export DTO_WQ_LIST=\"wq0.0;wq1.0\"\n"); + return -EINVAL; +#endif + } strncpy(wq_list, env_str, sizeof(wq_list) - 1); /* ensure wq_list is null terminated */ @@ -1470,6 +1512,7 @@ static int init_dto(void) auto_adjust_knobs = !!auto_adjust_knobs; } +#ifdef DTO_NUMA_SUPPORT if (numa_available() != -1) { env_str = getenv("DTO_IS_NUMA_AWARE"); if (env_str != NULL) { @@ -1480,6 +1523,7 @@ static int init_dto(void) } } } +#endif env_str = getenv("DTO_UMWAIT_DELAY"); @@ -1554,13 +1598,16 @@ static void cleanup_dto(void) stats_fd = -1; } +#ifdef DTO_NUMA_SUPPORT cleanup_devices(); +#endif } static __always_inline struct dto_wq *get_wq(void* buf) { struct dto_wq* wq = NULL; +#ifdef DTO_NUMA_SUPPORT if (is_numa_aware) { int status[1] = {-1}; @@ -1574,6 +1621,7 @@ static __always_inline struct dto_wq *get_wq(void* buf) } } } +#endif if (wq == NULL) { wq = &wqs[next_wq++ % num_wqs];