From d2285fb8304c82fd87de483845d1c12b06927479 Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Wed, 4 Sep 2024 21:30:22 +0200 Subject: [PATCH 1/7] MDEV-34859: Failed to initialise non-blocking API on OpenBSD arm64 Implement native my_context for arm64 (aarch64). This is more efficient than ucontext, and also makes the non-blocking API available on arm64 platforms that do not have ucontext such as OpenBSD. Tested-by: Brad Smith Signed-off-by: Kristian Nielsen --- include/ma_context.h | 19 +++ libmariadb/ma_context.c | 341 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 352 insertions(+), 8 deletions(-) diff --git a/include/ma_context.h b/include/ma_context.h index 0ad7f68b..2c4ab10a 100644 --- a/include/ma_context.h +++ b/include/ma_context.h @@ -32,6 +32,8 @@ #define MY_CONTEXT_USE_X86_64_GCC_ASM #elif defined(__GNUC__) && __GNUC__ >= 3 && defined(__i386__) #define MY_CONTEXT_USE_I386_GCC_ASM +#elif defined(__GNUC__) && __GNUC__ >= 3 && defined(__aarch64__) +#define MY_CONTEXT_USE_AARCH64_GCC_ASM #elif defined(HAVE_UCONTEXT_H) #define MY_CONTEXT_USE_UCONTEXT #else @@ -107,6 +109,23 @@ struct my_context { #endif +#ifdef MY_CONTEXT_USE_AARCH64_GCC_ASM +#include + +struct my_context { + uint64_t save[22]; + void *stack_top; + void *stack_bot; +#ifdef HAVE_VALGRIND + unsigned int valgrind_stack_id; +#endif +#ifndef DBUG_OFF + void *dbug_state; +#endif +}; +#endif + + #ifdef MY_CONTEXT_DISABLE struct my_context { int dummy; diff --git a/libmariadb/ma_context.c b/libmariadb/ma_context.c index 222dbe28..3edef568 100644 --- a/libmariadb/ma_context.c +++ b/libmariadb/ma_context.c @@ -21,6 +21,9 @@ swapcontext(). */ +#include +#include + #include "ma_global.h" #include "ma_string.h" #include "ma_context.h" @@ -167,9 +170,6 @@ my_context_destroy(struct my_context *c) save them as we cannot know if we will yield or not in advance). */ -#include -#include - /* Layout of saved registers etc. Since this is accessed through gcc inline assembler, it is simpler to just @@ -204,7 +204,7 @@ my_context_spawn(struct my_context *c, void (*f)(void *), void *d) ( "movq %%rsp, (%[save])\n\t" "movq %[stack], %%rsp\n\t" -#if __GNUC__ >= 4 && __GNUC_MINOR__ >= 4 && !defined(__INTEL_COMPILER) +#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4) || __clang__) && !defined(__INTEL_COMPILER) /* This emits a DWARF DW_CFA_undefined directive to make the return address undefined. This indicates that this is the top of the stack frame, and @@ -408,9 +408,6 @@ my_context_destroy(struct my_context *c) save them as we cannot know if we will yield or not in advance). */ -#include -#include - /* Layout of saved registers etc. Since this is accessed through gcc inline assembler, it is simpler to just @@ -443,7 +440,7 @@ my_context_spawn(struct my_context *c, void (*f)(void *), void *d) ( "movl %%esp, (%[save])\n\t" "movl %[stack], %%esp\n\t" -#if __GNUC__ >= 4 && __GNUC_MINOR__ >= 4 && !defined(__INTEL_COMPILER) +#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4) || __clang__) && !defined(__INTEL_COMPILER) /* This emits a DWARF DW_CFA_undefined directive to make the return address undefined. This indicates that this is the top of the stack frame, and @@ -625,6 +622,334 @@ my_context_destroy(struct my_context *c) #endif /* MY_CONTEXT_USE_I386_GCC_ASM */ +#ifdef MY_CONTEXT_USE_AARCH64_GCC_ASM +/* + GCC-aarch64 (arm64) implementation of my_context. + + This is slightly optimized in the common case where we never yield + (eg. fetch next row and it is already fully received in buffer). In this + case we do not need to restore registers at return (though we still need to + save them as we cannot know if we will yield or not in advance). +*/ + +/* + Layout of saved registers etc. + Since this is accessed through gcc inline assembler, it is simpler to just + use numbers than to try to define nice constants or structs. + + 0 0 x19 + 1 8 x20 + 2 16 x21 + ... + 9 72 x28 + 10 80 x29 (frame pointer) + 11 88 sp + 12 96 d8 + 13 104 d9 + ... + 19 152 d15 + 20 160 pc for done + 21 168 pc for yield/continue +*/ + +int +my_context_spawn(struct my_context *c, void (*f)(void *), void *d) +{ + register int ret asm("w0"); + register void (*f_reg)(void *) asm("x1") = f; + register void *d_reg asm("x2") = d; + register void *stack asm("x13") = c->stack_top; + /* Need this in callee-save register to preserve in function call. */ + register const uint64_t *save asm("x19") = &c->save[0]; + + /* + There are a total of 20 callee-save registers (including frame pointer and + link register) we need to save and restore when suspending and continuing, + plus stack pointer sp and program counter pc. + + However, if we never suspend, the user-supplied function will in any case + restore the callee-save registers, so we can avoid restoring them in this + case. + */ + __asm__ __volatile__ + ( + "mov x10, sp\n\t" + "mov sp, %[stack]\n\t" +#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4) || __clang__) && !defined(__INTEL_COMPILER) + /* + This emits a DWARF DW_CFA_undefined directive to make the return address + (UNW_AARCH64_X30) undefined. This indicates that this is the top of the + stack frame, and helps tools that use DWARF stack unwinding to obtain + stack traces. (I use numeric constant to avoid a dependency on libdwarf + includes). + */ + ".cfi_escape 0x07, 30\n\t" +#endif + "stp x19, x20, [%[save], #0]\n\t" + "stp x21, x22, [%[save], #16]\n\t" + "stp x23, x24, [%[save], #32]\n\t" + "stp x25, x26, [%[save], #48]\n\t" + "stp x27, x28, [%[save], #64]\n\t" + "stp x29, x10, [%[save], #80]\n\t" + "stp d8, d9, [%[save], #96]\n\t" + "stp d10, d11, [%[save], #112]\n\t" + "stp d12, d13, [%[save], #128]\n\t" + "stp d14, d15, [%[save], #144]\n\t" + "adr x10, 1f\n\t" + "adr x11, 2f\n\t" + "stp x10, x11, [%[save], #160]\n\t" + + /* Need this in x0 to follow calling convention. */ + "mov x0, %[d]\n\t" + "blr %[f]\n\t" + "ldr x11, [%[save], #160]\n\t" + "br x11\n" + /* + Come here when operation is done. + We do not need to restore callee-save registers, as the called function + will do this for us if needed. + */ + "1:\n\t" + "ldr x10, [%[save], #88]\n\t" + "mov sp, x10\n\t" + "mov %w[ret], #0\n\t" + "b 3f\n" + /* Come here when operation was suspended. */ + "2:\n\t" + "mov %w[ret], #1\n" + "3:\n" + : [ret] "=r" (ret), + [f] "+r" (f_reg), + [d] "+r" (d_reg), + [stack] "+r" (stack) + : [save] "r" (save) + : "x3", "x4", "x5", "x6", "x7", + "x9", "x10", "x11", "x14", "x15", "x18", "x30", + "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", + "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23", + "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31", + "memory", "cc" + ); + + return ret; +} + +int +my_context_continue(struct my_context *c) +{ + register int ret asm("w0"); + /* Need this in callee-save register to preserve in function call. */ + register const uint64_t *save asm("x19") = &c->save[0]; + + __asm__ __volatile__ + ( + "ldp x13, x11, [%[save], #0]\n\t" + "stp x19, x20, [%[save], #0]\n\t" + /* x19 is %[save], delay restoring it until %[save] is no longer needed. */ + "mov x20, x11\n\t" + + "ldp x10, x11, [%[save], #16]\n\t" + "stp x21, x22, [%[save], #16]\n\t" + "mov x21, x10\n\t" + "mov x22, x11\n\t" + + "ldp x10, x11, [%[save], #32]\n\t" + "stp x23, x24, [%[save], #32]\n\t" + "mov x23, x10\n\t" + "mov x24, x11\n\t" + + "ldp x10, x11, [%[save], #48]\n\t" + "stp x25, x26, [%[save], #48]\n\t" + "mov x25, x10\n\t" + "mov x26, x11\n\t" + + "ldp x10, x11, [%[save], #64]\n\t" + "stp x27, x28, [%[save], #64]\n\t" + "mov x27, x10\n\t" + "mov x28, x11\n\t" + + "ldp x10, x11, [%[save], #80]\n\t" + "mov x14, sp\n\t" + "stp x29, x14, [%[save], #80]\n\t" + "mov x29, x10\n\t" + "mov sp, x11\n\t" + + "ldp d0, d1, [%[save], #96]\n\t" + "stp d8, d9, [%[save], #96]\n\t" + "fmov d8, d0\n\t" + "fmov d9, d1\n\t" + + "ldp d0, d1, [%[save], #112]\n\t" + "stp d10, d11, [%[save], #112]\n\t" + "fmov d10, d0\n\t" + "fmov d11, d1\n\t" + + "ldp d0, d1, [%[save], #128]\n\t" + "stp d12, d13, [%[save], #128]\n\t" + "fmov d12, d0\n\t" + "fmov d13, d1\n\t" + + "ldp d0, d1, [%[save], #144]\n\t" + "stp d14, d15, [%[save], #144]\n\t" + "fmov d14, d0\n\t" + "fmov d15, d1\n\t" + + "adr x10, 1f\n\t" + "adr x11, 2f\n\t" + "ldr x15, [%[save], #168]\n\t" + "stp x10, x11, [%[save], #160]\n\t" + "mov x19, x13\n\t" + "br x15\n" + /* + Come here when operation is done. + Be sure to use the same callee-save register for %[save] here and in + my_context_spawn(), so we preserve the value correctly at this point. + */ + "1:\n\t" + /* x19 (aka %[save]) is preserved from my_context_spawn() in this case. */ + "ldr x20, [%[save], #8]\n\t" + "ldp x21, x22, [%[save], #16]\n\t" + "ldp x21, x22, [%[save], #32]\n\t" + "ldp x21, x22, [%[save], #48]\n\t" + "ldp x21, x22, [%[save], #64]\n\t" + "ldp x29, x10, [%[save], #80]\n\t" + "mov sp, x10\n\t" + "ldp d8, d9, [%[save], #96]\n\t" + "ldp d10, d11, [%[save], #112]\n\t" + "ldp d12, d13, [%[save], #128]\n\t" + "ldp d14, d15, [%[save], #144]\n\t" + "mov %w[ret], #0\n\t" + "b 3f\n" + /* Come here when operation is suspended. */ + "2:\n\t" + "mov %w[ret], #1\n" + "3:\n" + : [ret] "=r" (ret) + : [save] "r" (save) + : "x1", "x2", "x3", "x4", "x5", "x6", "x7", + "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x18", "x30", + "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", + "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23", + "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31", + "memory", "cc" + ); + + return ret; +} + +int +my_context_yield(struct my_context *c) +{ + register const uint64_t *save asm("x19") = &c->save[0]; + __asm__ __volatile__ + ( + "ldp x13, x11, [%[save], #0]\n\t" + "stp x19, x20, [%[save], #0]\n\t" + /* x19 is %[save], delay restoring it until %[save] is no longer needed. */ + "mov x20, x11\n\t" + + "ldp x10, x11, [%[save], #16]\n\t" + "stp x21, x22, [%[save], #16]\n\t" + "mov x21, x10\n\t" + "mov x22, x11\n\t" + + "ldp x10, x11, [%[save], #32]\n\t" + "stp x23, x24, [%[save], #32]\n\t" + "mov x23, x10\n\t" + "mov x24, x11\n\t" + + "ldp x10, x11, [%[save], #48]\n\t" + "stp x25, x26, [%[save], #48]\n\t" + "mov x25, x10\n\t" + "mov x26, x11\n\t" + + "ldp x10, x11, [%[save], #64]\n\t" + "stp x27, x28, [%[save], #64]\n\t" + "mov x27, x10\n\t" + "mov x28, x11\n\t" + + "ldp x10, x11, [%[save], #80]\n\t" + "mov x14, sp\n\t" + "stp x29, x14, [%[save], #80]\n\t" + "mov x29, x10\n\t" + "mov sp, x11\n\t" + + "ldp d0, d1, [%[save], #96]\n\t" + "stp d8, d9, [%[save], #96]\n\t" + "fmov d8, d0\n\t" + "fmov d9, d1\n\t" + + "ldp d0, d1, [%[save], #112]\n\t" + "stp d10, d11, [%[save], #112]\n\t" + "fmov d10, d0\n\t" + "fmov d11, d1\n\t" + + "ldp d0, d1, [%[save], #128]\n\t" + "stp d12, d13, [%[save], #128]\n\t" + "fmov d12, d0\n\t" + "fmov d13, d1\n\t" + + "ldp d0, d1, [%[save], #144]\n\t" + "stp d14, d15, [%[save], #144]\n\t" + "fmov d14, d0\n\t" + "fmov d15, d1\n\t" + + "ldr x11, [%[save], #168]\n\t" + "adr x10, 1f\n\t" + "str x10, [%[save], #168]\n\t" + "mov x19, x13\n\t" + "br x11\n" + + "1:\n" + : + : [save] "r" (save) + : "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", + "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x18", "x30", + "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", + "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23", + "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31", + "memory", "cc" + ); + return 0; +} + +int +my_context_init(struct my_context *c, size_t stack_size) +{ + memset(c, 0, sizeof(*c)); + + if (!(c->stack_bot= malloc(stack_size))) + return -1; /* Out of memory */ + /* + Align stack to 16-byte boundary. + Also put two zero words at the top of the stack. + */ + c->stack_top= (void *) + (( ((intptr)c->stack_bot + stack_size) & ~(intptr)0xf) - 16); + memset(c->stack_top, 0, 16); + +#ifdef HAVE_VALGRIND + c->valgrind_stack_id= + VALGRIND_STACK_REGISTER(c->stack_bot, c->stack_top); +#endif + return 0; +} + +void +my_context_destroy(struct my_context *c) +{ + if (c->stack_bot) + { + free(c->stack_bot); +#ifdef HAVE_VALGRIND + VALGRIND_STACK_DEREGISTER(c->valgrind_stack_id); +#endif + } +} + +#endif /* MY_CONTEXT_USE_AARCH64_GCC_ASM */ + + #ifdef MY_CONTEXT_USE_WIN32_FIBERS int my_context_yield(struct my_context *c) From da0a01367c74ddddb3ee26718f074c9e91901994 Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Thu, 5 Sep 2024 13:56:44 +0200 Subject: [PATCH 2/7] Implement boost::context as a fallback for non-blocking API support The non-blocking API has native (assembler) implementations for x86_64, i386, and (with recent patch) aarch64; these implementations are the most efficient. For other architectures, a fallback to ucontext is supported. But ucontext is not the most efficient, and it is not available on all platforms (it has been deprecated in POSIX). The boost::context library provides an alternative fallback that is available on more architectures and should be more efficient than ucontext (if still not quite as fast as the native support). This patch adds a CMake option -DWITH_BOOST_CONTEXT=ON that adds boost::context as a dependency of libmariadb to provide a fallback on non-natively supported architectures. Boost::context is preferred over ucontext when both are available. The option is off by default and must be explicitly enabled by the user. This avoids introducing a C++ dependency (including dependency on a C++ compiler and on libstdc++) unless explicitly requested by the user (libmariadb is otherwise C-only). Tested-by: Brad Smith Signed-off-by: Kristian Nielsen --- CMakeLists.txt | 35 +++++++--- cmake/check_include_files.cmake | 3 + include/ma_config.h.in | 1 + include/ma_context.h | 36 ++++++++++ libmariadb/CMakeLists.txt | 3 + libmariadb/ma_boost_context.cc | 117 ++++++++++++++++++++++++++++++++ libmariadb/ma_context.c | 2 +- 7 files changed, 185 insertions(+), 12 deletions(-) create mode 100644 libmariadb/ma_boost_context.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f43faa2..616dd702 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,8 +16,6 @@ IF(COMMAND CMAKE_POLICY) ENDIF() -PROJECT(mariadb-connector-c C) - # Is C/C built as subproject? get_directory_property(IS_SUBPROJECT PARENT_DIRECTORY) @@ -25,10 +23,28 @@ get_directory_property(IS_SUBPROJECT PARENT_DIRECTORY) SET_PROPERTY(DIRECTORY PROPERTY INCLUDE_DIRECTORIES) FOREACH(V WITH_MYSQLCOMPAT WITH_MSI WITH_SIGNCODE WITH_RTC WITH_UNIT_TESTS WITH_DYNCOL WITH_EXTERNAL_ZLIB WITH_CURL WITH_SQLITE WITH_SSL WITH_ICONV - DEFAULT_CHARSET INSTALL_LAYOUT WITH_TEST_SRCPKG) + DEFAULT_CHARSET INSTALL_LAYOUT WITH_TEST_SRCPKG WITH_BOOST_CONTEXT) SET(${V} ${${OPT}${V}}) ENDFOREACH() +MACRO(ADD_OPTION _name _text _default) + IF(NOT DEFINED ${_name}) + OPTION(${OPT}${_name} "${_text}" "${_default}") + ELSE() + OPTION(${OPT}${_name} "${_text}" "${${_name}}") + ENDIF() +ENDMACRO() + +ADD_OPTION(WITH_BOOST_CONTEXT + "Use Boost::Context for the non-blocking API on platforms without native implementation" + OFF) + +IF(WITH_BOOST_CONTEXT) + PROJECT(mariadb-connector-c LANGUAGES C CXX) +ELSE() + PROJECT(mariadb-connector-c C) +ENDIF() + # SET(PACKAGE_STATUS_SUFFIX "rc1") SET(CC_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) @@ -42,14 +58,6 @@ MATH(EXPR MARIADB_PACKAGE_VERSION_ID "${CPACK_PACKAGE_VERSION_MAJOR} * 10000 + ${CPACK_PACKAGE_VERSION_MINOR} * 100 + ${CPACK_PACKAGE_VERSION_PATCH}") -MACRO(ADD_OPTION _name _text _default) - IF(NOT DEFINED ${_name}) - OPTION(${OPT}${_name} "${_text}" "${_default}") - ELSE() - OPTION(${OPT}${_name} "${_text}" "${${_name}}") - ENDIF() -ENDMACRO() - ### Options ### IF(NOT WIN32) ADD_OPTION(WITH_MYSQLCOMPAT "creates libmysql* symbolic links" OFF) @@ -361,6 +369,11 @@ IF(NOT WITH_SSL STREQUAL "OFF") MARK_AS_ADVANCED(SSL_SOURCES) ENDIF() +IF(WITH_BOOST_CONTEXT) + FIND_PACKAGE(Boost 1.40 COMPONENTS context REQUIRED) + SET(SYSTEM_LIBS ${SYSTEM_LIBS} ${Boost_LIBRARIES}) +ENDIF() + SET(ENABLED_LOCAL_INFILE "AUTO" CACHE STRING "If we should should enable LOAD DATA LOCAL by default (OFF/ON/AUTO)") MARK_AS_ADVANCED(ENABLED_LOCAL_INFILE) IF (ENABLED_LOCAL_INFILE MATCHES "^(0|FALSE)$") diff --git a/cmake/check_include_files.cmake b/cmake/check_include_files.cmake index d8bbffcd..50e289fa 100644 --- a/cmake/check_include_files.cmake +++ b/cmake/check_include_files.cmake @@ -43,6 +43,9 @@ CHECK_INCLUDE_FILES (sys/types.h HAVE_SYS_TYPES_H) CHECK_INCLUDE_FILES (sys/stat.h HAVE_SYS_STAT_H) CHECK_INCLUDE_FILES (sys/un.h HAVE_SYS_UN_H) CHECK_INCLUDE_FILES (unistd.h HAVE_UNISTD_H) +IF(WITH_BOOST_CONTEXT) + CHECK_INCLUDE_FILE_CXX (boost/fiber/context.hpp HAVE_BOOST_CONTEXT_H) +ENDIF() IF(APPLE) SET(CMAKE_REQUIRED_DEFINITIONS -D_XOPEN_SOURCE=600) diff --git a/include/ma_config.h.in b/include/ma_config.h.in index c5c758dc..ff825491 100644 --- a/include/ma_config.h.in +++ b/include/ma_config.h.in @@ -28,6 +28,7 @@ #cmakedefine HAVE_SYS_UN_H 1 #cmakedefine HAVE_UNISTD_H 1 #cmakedefine HAVE_UCONTEXT_H 1 +#cmakedefine HAVE_BOOST_CONTEXT_H 1 /* * function definitions - processed in LibmysqlFunctions.txt diff --git a/include/ma_context.h b/include/ma_context.h index 2c4ab10a..4c3f0d8d 100644 --- a/include/ma_context.h +++ b/include/ma_context.h @@ -34,12 +34,18 @@ #define MY_CONTEXT_USE_I386_GCC_ASM #elif defined(__GNUC__) && __GNUC__ >= 3 && defined(__aarch64__) #define MY_CONTEXT_USE_AARCH64_GCC_ASM +#elif defined(HAVE_BOOST_CONTEXT_H) +#define MY_CONTEXT_USE_BOOST_CONTEXT #elif defined(HAVE_UCONTEXT_H) #define MY_CONTEXT_USE_UCONTEXT #else #define MY_CONTEXT_DISABLE #endif +#ifdef __cplusplus +extern "C" { +#endif + #ifdef MY_CONTEXT_USE_WIN32_FIBERS struct my_context { void (*user_func)(void *); @@ -126,6 +132,32 @@ struct my_context { #endif +#ifdef MY_CONTEXT_USE_BOOST_CONTEXT +/* + boost::context is a C++-library that provides a portable co-routine fallback + for architectures that lack a native my_context implementation, and which is + available on some platforms where ucontext is not (ucontext has been + deprecated in Posix). + + Since boost::context is in C++, the implementation details must be put into + a separate source file ma_boost_context.cc and hidden in an opaque void *. +*/ +struct my_context { + /* Pointer to state that uses C++-types and cannot be compiled in C. */ + void *internal_context; + void *stack; + size_t stack_size; +#ifndef DBUG_OFF + void *dbug_state; +#endif + int active; +#ifdef HAVE_VALGRIND + unsigned int valgrind_stack_id; +#endif +}; +#endif /* MY_CONTEXT_USE_BOOST_CONTEXT */ + + #ifdef MY_CONTEXT_DISABLE struct my_context { int dummy; @@ -259,3 +291,7 @@ struct mysql_async_context { */ struct my_context async_context; }; + +#ifdef __cplusplus +} +#endif diff --git a/libmariadb/CMakeLists.txt b/libmariadb/CMakeLists.txt index 43ed67b4..1ce0d4c6 100644 --- a/libmariadb/CMakeLists.txt +++ b/libmariadb/CMakeLists.txt @@ -390,6 +390,9 @@ IF(WITH_DYNCOL) ENDIF() SET(LIBMARIADB_SOURCES ${LIBMARIADB_SOURCES} mariadb_async.c ma_context.c) +IF(WITH_BOOST_CONTEXT) + SET(LIBMARIADB_SOURCES ${LIBMARIADB_SOURCES} ma_boost_context.cc) +ENDIF() SET(MARIADB_LIB_SYMBOLS ${MARIADB_LIB_SYMBOLS} ${MARIADB_NONBLOCK_SYMBOLS}) INCLUDE(${CC_SOURCE_DIR}/cmake/export.cmake) diff --git a/libmariadb/ma_boost_context.cc b/libmariadb/ma_boost_context.cc new file mode 100644 index 00000000..cb7625d9 --- /dev/null +++ b/libmariadb/ma_boost_context.cc @@ -0,0 +1,117 @@ +#include "ma_config.h" +#include "mysql.h" +#include "ma_context.h" + +#ifdef MY_CONTEXT_USE_BOOST_CONTEXT + +#include + +namespace ctx=boost::context; + +struct my_context_intern { + ctx::fiber parent, coro; + + void *stack_top(const my_context *c) { + return (unsigned char *)(c->stack) + c->stack_size; + } + + /* A StackAlloc for ctx::fiber that reuses our stack. */ + struct my_stack_alloc { + typedef ctx::stack_traits traits_type; + my_context *c; + my_stack_alloc(my_context *c_arg) : c(c_arg) { }; + ctx::stack_context allocate() { + ctx::stack_context sctx; + sctx.size= c->stack_size; + sctx.sp= ((my_context_intern *)c->internal_context)->stack_top(c); +#if defined(BOOST_USE_VALGRIND) && defined(HAVE_VALGRIND) + sctx.valgrind_stack_id= c->valgrind_stack_id; +#endif + return sctx; + } + void deallocate(ctx::stack_context & sctx) { + /* Empty, we will re-use the stack. */ + } + }; +}; + + +extern "C" +int +my_context_spawn(struct my_context *c, void (*f)(void *), void *d) +{ + my_context_intern *ci= (my_context_intern *)c->internal_context; + ci->coro= ctx::fiber(std::allocator_arg, my_context_intern::my_stack_alloc(c), + [c, f, d](ctx::fiber && parent) { + my_context_intern *ci= (my_context_intern *)c->internal_context; + ci->parent= std::move(parent); + (*f)(d); + c->active= 0; + return std::move(ci->parent); + }); + c->active= 1; + ci->coro= std::move(ci->coro).resume(); + return c->active; +} + + +extern "C" +int +my_context_continue(struct my_context *c) +{ + if (!c->active) + return 0; + my_context_intern *ci= (my_context_intern *)c->internal_context; + ci->coro= std::move(ci->coro).resume(); + return c->active; +} + + +extern "C" +int +my_context_yield(struct my_context *c) +{ + if (!c->active) + return -1; + my_context_intern *ci= (my_context_intern *)c->internal_context; + ci->parent= std::move(ci->parent).resume(); + return 0; +} + + +extern "C" +int +my_context_init(struct my_context *c, size_t stack_size) +{ + memset(c, 0, sizeof(*c)); + if (!(c->stack= malloc(stack_size))) + return -1; /* Out of memory */ + if (!(c->internal_context= new my_context_intern)) + { + free(c->stack); + return -1; + } + c->stack_size= stack_size; +#ifdef HAVE_VALGRIND + c->valgrind_stack_id= + VALGRIND_STACK_REGISTER(c->stack, ((unsigned char *)(c->stack))+stack_size); +#endif + return 0; +} + + +extern "C" +void +my_context_destroy(struct my_context *c) +{ + delete (my_context_intern *)c->internal_context; + if (c->stack) + { +#ifdef HAVE_VALGRIND + VALGRIND_STACK_DEREGISTER(c->valgrind_stack_id); +#endif + free(c->stack); + } +} + +#endif /* MY_CONTEXT_USE_BOOST_CONTEXT */ diff --git a/libmariadb/ma_context.c b/libmariadb/ma_context.c index 3edef568..203796c5 100644 --- a/libmariadb/ma_context.c +++ b/libmariadb/ma_context.c @@ -675,7 +675,7 @@ my_context_spawn(struct my_context *c, void (*f)(void *), void *d) ( "mov x10, sp\n\t" "mov sp, %[stack]\n\t" -#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4) || __clang__) && !defined(__INTEL_COMPILER) +#if !defined(__INTEL_COMPILER) /* This emits a DWARF DW_CFA_undefined directive to make the return address (UNW_AARCH64_X30) undefined. This indicates that this is the top of the From 8ace383fa0635d737be4d003d004e52a77a24b7a Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Sun, 5 Nov 2023 12:31:13 +0100 Subject: [PATCH 3/7] fix meaningless code in mariadb_time_to_string() which was apparently supposed to print microseconds --- libmariadb/ma_time.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/libmariadb/ma_time.c b/libmariadb/ma_time.c index 460c32d4..b2d70152 100644 --- a/libmariadb/ma_time.c +++ b/libmariadb/ma_time.c @@ -54,12 +54,9 @@ size_t mariadb_time_to_string(const MYSQL_TIME *tm, char *time_str, size_t len, return 0; break; } - if (digits && (len < length)) - { - char helper[16]; - snprintf(helper, 16, ".%%0%du", digits); - length+= snprintf(time_str + length, len - length, helper, digits); - } + if (digits && len > length + 1) + length+= snprintf(time_str + length, len - length, ".%0*lu", digits, + tm->second_part); return length; } From 61ef765c43156466d651ffc6e96aaf25a3efce05 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Sun, 5 Nov 2023 12:32:35 +0100 Subject: [PATCH 4/7] change plugin TYPE in the REGISTER_PLUGIN to something useful it's currently unused --- plugins/auth/CMakeLists.txt | 16 ++++++++-------- plugins/compress/CMakeLists.txt | 4 ++-- plugins/connection/CMakeLists.txt | 2 +- plugins/io/CMakeLists.txt | 2 +- plugins/pvio/CMakeLists.txt | 6 +++--- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/plugins/auth/CMakeLists.txt b/plugins/auth/CMakeLists.txt index fc581a0b..36876cfd 100644 --- a/plugins/auth/CMakeLists.txt +++ b/plugins/auth/CMakeLists.txt @@ -22,14 +22,14 @@ ENDIF() #native password REGISTER_PLUGIN(TARGET mysql_native_password - TYPE MARIADB_CLIENT_PLUGIN_AUTH + TYPE AUTH CONFIGURATIONS STATIC DEFAULT STATIC SOURCES ${CC_SOURCE_DIR}/plugins/auth/my_auth.c) #Dialog client authentication plugin REGISTER_PLUGIN(TARGET dialog - TYPE MARIADB_CLIENT_PLUGIN_AUTH + TYPE AUTH CONFIGURATIONS DYNAMIC STATIC OFF DEFAULT DYNAMIC SOURCES ${CC_SOURCE_DIR}/plugins/auth/dialog.c @@ -53,7 +53,7 @@ IF(CRYPTO_PLUGIN) ${REF10_DIR}/ge_msub.c ${REF10_DIR}/ge_p3_tobytes.c ${REF10_DIR}/keypair.c) REGISTER_PLUGIN(TARGET client_ed25519 - TYPE MARIADB_CLIENT_PLUGIN_AUTH + TYPE AUTH CONFIGURATIONS DYNAMIC STATIC OFF DEFAULT DYNAMIC SOURCES ${CC_SOURCE_DIR}/plugins/auth/ed25519.c @@ -75,7 +75,7 @@ IF(CRYPTO_PLUGIN) # SHA256 caching plugin for MySQL 8.0 connection REGISTER_PLUGIN(TARGET caching_sha2_password - TYPE MARIADB_CLIENT_PLUGIN_AUTH + TYPE AUTH CONFIGURATIONS DYNAMIC STATIC OFF DEFAULT DYNAMIC SOURCES ${CC_SOURCE_DIR}/plugins/auth/caching_sha2_pw.c @@ -86,7 +86,7 @@ IF(CRYPTO_PLUGIN) MESSAGE1(STATUS "sha256_password not supported by GnuTLS due to missing OAEP padding") ELSE() REGISTER_PLUGIN(TARGET sha256_password - TYPE MARIADB_CLIENT_PLUGIN_AUTH + TYPE AUTH CONFIGURATIONS DYNAMIC STATIC OFF DEFAULT DYNAMIC SOURCES ${AUTH_DIR}/sha256_pw.c @@ -112,7 +112,7 @@ ELSE() ENDIF() IF(GSSAPI_SOURCES) REGISTER_PLUGIN(TARGET auth_gssapi_client - TYPE MARIADB_CLIENT_PLUGIN_AUTH + TYPE AUTH CONFIGURATIONS DYNAMIC STATIC OFF DYNAMIC_AND_STATIC DEFAULT ${AUTH_GSSAPI_DEFAULT_CONFIG} SOURCES ${GSSAPI_SOURCES} @@ -128,14 +128,14 @@ ENDIF() # old_password plugin REGISTER_PLUGIN(TARGET mysql_old_password - TYPE MARIADB_CLIENT_PLUGIN_AUTH + TYPE AUTH CONFIGURATIONS STATIC DYNAMIC OFF DEFAULT STATIC SOURCES ${AUTH_DIR}/old_password.c) # Cleartext REGISTER_PLUGIN(TARGET mysql_clear_password - TYPE MARIADB_CLIENT_PLUGIN_AUTH + TYPE AUTH CONFIGURATIONS DYNAMIC STATIC OFF DEFAULT DYNAMIC SOURCES ${AUTH_DIR}/mariadb_cleartext.c) diff --git a/plugins/compress/CMakeLists.txt b/plugins/compress/CMakeLists.txt index f6dab85d..ed04a658 100644 --- a/plugins/compress/CMakeLists.txt +++ b/plugins/compress/CMakeLists.txt @@ -6,7 +6,7 @@ INCLUDE_DIRECTORIES(${CC_SOURCE_DIR}/include) #zlib compression REGISTER_PLUGIN(TARGET zlib - TYPE MARIADB_CLIENT_COMPRESSION_PLUGIN + TYPE COMPRESSION CONFIGURATIONS STATIC DEFAULT STATIC SOURCES ${COMPRESS_PLUGIN_DIR}/c_zlib.c) @@ -15,7 +15,7 @@ REGISTER_PLUGIN(TARGET zlib IF(${ZSTD_FOUND}) INCLUDE_DIRECTORIES(${ZSTD_INCLUDE_DIRS}) REGISTER_PLUGIN(TARGET zstd - TYPE MARIADB_CLIENT_COMPRESSION_PLUGIN + TYPE COMPRESSION CONFIGURATIONS DYNAMIC STATIC OFF DEFAULT DYNAMIC SOURCES ${COMPRESS_PLUGIN_DIR}/c_zstd.c diff --git a/plugins/connection/CMakeLists.txt b/plugins/connection/CMakeLists.txt index 743cf2b0..47665c6d 100644 --- a/plugins/connection/CMakeLists.txt +++ b/plugins/connection/CMakeLists.txt @@ -1,6 +1,6 @@ # Replication REGISTER_PLUGIN(TARGET replication - TYPE MARIADB_CLIENT_PLUGIN_CONNECTION + TYPE CONNECTION CONFIGURATIONS STATIC DYNAMIC OFF DEFAULT OFF SOURCES ${CC_SOURCE_DIR}/plugins/connection/replication.c) diff --git a/plugins/io/CMakeLists.txt b/plugins/io/CMakeLists.txt index 8c304c99..42a09300 100644 --- a/plugins/io/CMakeLists.txt +++ b/plugins/io/CMakeLists.txt @@ -5,7 +5,7 @@ IF (WITH_CURL) ADD_DEFINITIONS(-DHAVE_REMOTEIO=1) #remote io plugin REGISTER_PLUGIN(TARGET remote_io - TYPE MARIADB_CLIENT_PLUGIN_IO + TYPE REMOTEIO CONFIGURATIONS DYNAMIC STATIC OFF DEFAULT DYNAMIC SOURCES ${CC_SOURCE_DIR}/plugins/io/remote_io.c diff --git a/plugins/pvio/CMakeLists.txt b/plugins/pvio/CMakeLists.txt index b74407a6..c59a50f6 100644 --- a/plugins/pvio/CMakeLists.txt +++ b/plugins/pvio/CMakeLists.txt @@ -5,7 +5,7 @@ INCLUDE_DIRECTORIES(${CC_SOURCE_DIR}/include) #native password REGISTER_PLUGIN(TARGET pvio_socket - TYPE MARIADB_CLIENT_PLUGIN_PVIO + TYPE PVIO CONFIGURATIONS STATIC DYNAMIC DEFAULT DEFAULT STATIC SOURCES ${CC_SOURCE_DIR}/plugins/pvio/pvio_socket.c) @@ -13,14 +13,14 @@ REGISTER_PLUGIN(TARGET pvio_socket IF(WIN32) # named pipe REGISTER_PLUGIN(TARGET pvio_npipe - TYPE MARIADB_CLIENT_PLUGIN_PVIO + TYPE PVIO CONFIGURATIONS STATIC DYNAMIC DEFAULT DEFAULT STATIC SOURCES ${CC_SOURCE_DIR}/plugins/pvio/pvio_npipe.c) # shared memory REGISTER_PLUGIN(TARGET pvio_shmem - TYPE MARIADB_CLIENT_PLUGIN_PVIO + TYPE PVIO CONFIGURATIONS STATIC DYNAMIC DEFAULT DEFAULT DYNAMIC SOURCES ${CC_SOURCE_DIR}/plugins/pvio/pvio_shmem.c) From 6c0e755edf742c362d4e3b7397dc4e7a96e1470c Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Sun, 5 Nov 2023 12:34:37 +0100 Subject: [PATCH 5/7] fix compilation errors with -flto MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit declare plugin structures using their correct type. Fixes errors like ./libmariadb/libmariadb/ma_client_plugin.c:86:39: error: type of ‘pvio_socket_client_plugin’ does not match original declaration [-Werror=lto-type-mismatch] ./libmariadb/plugins/pvio/pvio_socket.c:141:21: note: type ‘struct MARIADB_PVIO_PLUGIN’ should match type ‘struct st_mysql_client_plugin’ Approved by: Georg Richter --- cmake/plugins.cmake | 2 +- libmariadb/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/plugins.cmake b/cmake/plugins.cmake index adb48126..e77e6adb 100644 --- a/cmake/plugins.cmake +++ b/cmake/plugins.cmake @@ -44,7 +44,7 @@ FUNCTION(REGISTER_PLUGIN) endif() if(NOT ${CC_PLUGIN_DEFAULT} STREQUAL "OFF") - set(PLUGIN_${CC_PLUGIN_TARGET}_TYPE ${CC_PLUGIN_TYPE}) + set(PLUGIN_${CC_PLUGIN_TARGET}_TYPE ${CC_PLUGIN_TYPE} PARENT_SCOPE) if(${CC_PLUGIN_DEFAULT} MATCHES "DYNAMIC") diff --git a/libmariadb/CMakeLists.txt b/libmariadb/CMakeLists.txt index 1ce0d4c6..67c803c2 100644 --- a/libmariadb/CMakeLists.txt +++ b/libmariadb/CMakeLists.txt @@ -298,7 +298,7 @@ MESSAGE(STATUS "SYSTEM_LIBS: ${SYSTEM_LIBS}") INCLUDE_DIRECTORIES(${LIBMARIADB_PLUGIN_INCLUDES}) ADD_DEFINITIONS(${LIBMARIADB_PLUGIN_DEFS}) FOREACH(plugin ${PLUGINS_STATIC}) - SET(EXTERNAL_PLUGINS "${EXTERNAL_PLUGINS} extern struct st_mysql_client_plugin ${plugin}_client_plugin;\n") + SET(EXTERNAL_PLUGINS "${EXTERNAL_PLUGINS} extern struct st_mysql_client_plugin_${PLUGIN_${plugin}_TYPE} ${plugin}_client_plugin;\n") SET(BUILTIN_PLUGINS "${BUILTIN_PLUGINS} (struct st_mysql_client_plugin *)&${plugin}_client_plugin,\n") ENDFOREACH() CONFIGURE_FILE(${CC_SOURCE_DIR}/libmariadb/ma_client_plugin.c.in From 3b29ff9c91344263183fcb2aa2ac4d634e501a7f Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Fri, 18 Oct 2024 13:51:52 +0200 Subject: [PATCH 6/7] MDEV-34859: Failed to initialise non-blocking API on OpenBSD arm64 Follow-up patch to fix copy-paste error that causes incorrect restore of registers in my_context_continue which can cause crashes on arm64. Signed-off-by: Kristian Nielsen --- libmariadb/ma_context.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libmariadb/ma_context.c b/libmariadb/ma_context.c index 203796c5..f9431d04 100644 --- a/libmariadb/ma_context.c +++ b/libmariadb/ma_context.c @@ -809,9 +809,9 @@ my_context_continue(struct my_context *c) /* x19 (aka %[save]) is preserved from my_context_spawn() in this case. */ "ldr x20, [%[save], #8]\n\t" "ldp x21, x22, [%[save], #16]\n\t" - "ldp x21, x22, [%[save], #32]\n\t" - "ldp x21, x22, [%[save], #48]\n\t" - "ldp x21, x22, [%[save], #64]\n\t" + "ldp x23, x24, [%[save], #32]\n\t" + "ldp x25, x26, [%[save], #48]\n\t" + "ldp x27, x28, [%[save], #64]\n\t" "ldp x29, x10, [%[save], #80]\n\t" "mov sp, x10\n\t" "ldp d8, d9, [%[save], #96]\n\t" From 6635e4bdd6bf7086e4e1685f28c6030b22e9ead1 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Tue, 22 Oct 2024 13:26:50 +0200 Subject: [PATCH 7/7] Fix for CONC-735 If a reconnect occurs, we need to check if mysql->options.host was already set and pass NULL instead of the previous host name. --- libmariadb/mariadb_lib.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libmariadb/mariadb_lib.c b/libmariadb/mariadb_lib.c index 6f5ec07e..7d5f6241 100644 --- a/libmariadb/mariadb_lib.c +++ b/libmariadb/mariadb_lib.c @@ -1452,8 +1452,8 @@ mysql_real_connect(MYSQL *mysql, const char *host, const char *user, if (!mysql->options.extension || !mysql->options.extension->status_callback) mysql_optionsv(mysql, MARIADB_OPT_STATUS_CALLBACK, NULL, NULL); - /* if host contains a semicolon, we need to parse connection string */ - if (host && strchr(host, ';')) + /* if host contains a semicolon or equal sign, we need to parse connection string */ + if (host && (strchr(host, ';') || strchr(host, '='))) { if (parse_connection_string(mysql, NULL, host, strlen(host))) return NULL; @@ -2109,7 +2109,9 @@ my_bool STDCALL mariadb_reconnect(MYSQL *mysql) my_context_install_suspend_resume_hook(ctxt, my_suspend_hook, &hook_data); } - if (!mysql_real_connect(&tmp_mysql,mysql->host,mysql->user,mysql->passwd, + if (!mysql_real_connect(&tmp_mysql, + mysql->options.host ? NULL : mysql->host, + mysql->user,mysql->passwd, mysql->db, mysql->port, mysql->unix_socket, mysql->client_flag | CLIENT_REMEMBER_OPTIONS) || mysql_set_character_set(&tmp_mysql, mysql->charset->csname))