Hi! On 4/7/25 11:27 PM, Tomas Vondra wrote:
I've pushed all three parts of v29, with some additional corrections (picked lower OIDs, bumped catversion, fixed commit messages).
While building the PG18 beta1/2 packages I noticed that in our build containers the selftest for pg_buffercache_numa and numa failed. It seems that libnuma was available and pg_numa_init/numa_available returns no errors, we still fail in pg_numa_query_pages/move_pages with EPERM yielding the following error when accessing pg_buffercache_numa/pg_shmem_allocations_numa:
ERROR: failed NUMA pages inquiry: Operation not permittedThe man-page of move_pages lead me to believe that this is because of the missing capability CAP_SYS_NICE on the process but I couldn't prove that theory with the attached patch. The patch did make the tests pass but also disabled NUMA permanently on a vanilla Debian VM and that is certainly not wanted. It may well be that my understanding of checking capabilities and how they work is incomplete. I also think that adding a new dependency for the reason of just checking the capability is probably a bit of an overkill, maybe we can check if we can access move_pages once without an error before treating it as one?
I'd be happy to debug this further but I have limited access to our build-infra, I should be able to sneak in commands during the build though.
Thanks, Patrick
From 5ddfa2184b85d76e95cbe2bc00991cad6b154fa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20St=C3=A4hlin?= <m...@packi.ch> Date: Thu, 17 Jul 2025 19:04:25 +0200 Subject: [PATCH] Check for CAP_SYS_NICE in pg_numa_init Make sure we have CAP_SYS_NICE set for our process to see if we're actually able to use any of the libnuma functions. If this is not set the selftest will fail in some build environments where we compile with libnuma but are then not able to use it. Failing test is pg_buffercache_numa with the following message: ERROR: failed NUMA pages inquiry: Operation not permitted --- configure | 187 +++++++++++++++++++++++++++++++++ configure.ac | 16 +++ doc/src/sgml/installation.sgml | 20 ++++ meson.build | 13 +++ meson_options.txt | 3 + src/include/pg_config.h.in | 6 ++ src/port/pg_numa.c | 35 +++++- 7 files changed, 279 insertions(+), 1 deletion(-) diff --git a/configure b/configure index 1b9980226c5..0126a4991a4 100755 --- a/configure +++ b/configure @@ -717,6 +717,9 @@ LIBCURL_CPPFLAGS LIBCURL_LIBS LIBCURL_CFLAGS with_libcurl +LIBCAP_LIBS +LIBCAP_CFLAGS +with_libcap with_uuid LIBURING_LIBS LIBURING_CFLAGS @@ -877,6 +880,7 @@ with_libedit_preferred with_liburing with_uuid with_ossp_uuid +with_libcap with_libcurl with_libnuma with_libxml @@ -911,6 +915,8 @@ ICU_CFLAGS ICU_LIBS LIBURING_CFLAGS LIBURING_LIBS +LIBCAP_CFLAGS +LIBCAP_LIBS LIBCURL_CFLAGS LIBCURL_LIBS LIBNUMA_CFLAGS @@ -1596,6 +1602,7 @@ Optional Packages: --with-liburing build with io_uring support, for asynchronous I/O --with-uuid=LIB build contrib/uuid-ossp using LIB (bsd,e2fs,ossp) --with-ossp-uuid obsolete spelling of --with-uuid=ossp + --with-libcap build with libcap support --with-libcurl build with libcurl support --with-libnuma build with libnuma support --with-libxml build with XML support @@ -1635,6 +1642,9 @@ Some influential environment variables: C compiler flags for LIBURING, overriding pkg-config LIBURING_LIBS linker flags for LIBURING, overriding pkg-config + LIBCAP_CFLAGS + C compiler flags for LIBCAP, overriding pkg-config + LIBCAP_LIBS linker flags for LIBCAP, overriding pkg-config LIBCURL_CFLAGS C compiler flags for LIBCURL, overriding pkg-config LIBCURL_LIBS @@ -8926,6 +8936,183 @@ fi +# +# libcap +# +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build with libcap support" >&5 +$as_echo_n "checking whether to build with libcap support... " >&6; } + + + +# Check whether --with-libcap was given. +if test "${with_libcap+set}" = set; then : + withval=$with_libcap; + case $withval in + yes) + +$as_echo "#define USE_LIBCAP 1" >>confdefs.h + + ;; + no) + : + ;; + *) + as_fn_error $? "no argument expected for --with-libcap option" "$LINENO" 5 + ;; + esac + +else + with_libcap=no + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_libcap" >&5 +$as_echo "$with_libcap" >&6; } + + +if test "$with_libcap" = yes ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for cap_get_proc in -lcap" >&5 +$as_echo_n "checking for cap_get_proc in -lcap... " >&6; } +if ${ac_cv_lib_cap_cap_get_proc+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lcap $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char cap_get_proc (); +int +main () +{ +return cap_get_proc (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_cap_cap_get_proc=yes +else + ac_cv_lib_cap_cap_get_proc=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_cap_cap_get_proc" >&5 +$as_echo "$ac_cv_lib_cap_cap_get_proc" >&6; } +if test "x$ac_cv_lib_cap_cap_get_proc" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBCAP 1 +_ACEOF + + LIBS="-lcap $LIBS" + +else + as_fn_error $? "library 'libcap' is required for capablities support" "$LINENO" 5 +fi + + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libcap" >&5 +$as_echo_n "checking for libcap... " >&6; } + +if test -n "$LIBCAP_CFLAGS"; then + pkg_cv_LIBCAP_CFLAGS="$LIBCAP_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libcap\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libcap") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_LIBCAP_CFLAGS=`$PKG_CONFIG --cflags "libcap" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$LIBCAP_LIBS"; then + pkg_cv_LIBCAP_LIBS="$LIBCAP_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libcap\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libcap") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_LIBCAP_LIBS=`$PKG_CONFIG --libs "libcap" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + LIBCAP_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libcap" 2>&1` + else + LIBCAP_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libcap" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$LIBCAP_PKG_ERRORS" >&5 + + as_fn_error $? "Package requirements (libcap) were not met: + +$LIBCAP_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + +Alternatively, you may set the environment variables LIBCAP_CFLAGS +and LIBCAP_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details." "$LINENO" 5 +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it +is in your PATH or set the PKG_CONFIG environment variable to the full +path to pkg-config. + +Alternatively, you may set the environment variables LIBCAP_CFLAGS +and LIBCAP_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details. + +To get pkg-config, see <http://pkg-config.freedesktop.org/>. +See \`config.log' for more details" "$LINENO" 5; } +else + LIBCAP_CFLAGS=$pkg_cv_LIBCAP_CFLAGS + LIBCAP_LIBS=$pkg_cv_LIBCAP_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + +fi +fi + + # # libcurl # diff --git a/configure.ac b/configure.ac index 3e3fcfa9831..3c595212b59 100644 --- a/configure.ac +++ b/configure.ac @@ -1019,6 +1019,21 @@ fi AC_SUBST(with_uuid) +# +# libcap +# +AC_MSG_CHECKING([whether to build with libcap support]) +PGAC_ARG_BOOL(with, libcap, no, [build with libcap support], + [AC_DEFINE([USE_LIBCAP], 1, [Define to build with libcap support. (--with-libcap)])]) +AC_MSG_RESULT([$with_libcap]) +AC_SUBST(with_libcap) + +if test "$with_libcap" = yes ; then + AC_CHECK_LIB(cap, cap_get_proc, [], [AC_MSG_ERROR([library 'libcap' is required for capablities support])]) + PKG_CHECK_MODULES(LIBCAP, libcap) +fi + + # # libcurl # @@ -1075,6 +1090,7 @@ if test "$with_libnuma" = yes ; then PKG_CHECK_MODULES(LIBNUMA, numa) fi + # # XML # diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml index de19f3ad929..3696ac64eed 100644 --- a/doc/src/sgml/installation.sgml +++ b/doc/src/sgml/installation.sgml @@ -1151,6 +1151,16 @@ build-postgresql: </listitem> </varlistentry> + <varlistentry id="configure-option-with-libnuma"> + <term><option>--with-libcap</option></term> + <listitem> + <para> + Build with libcap support for detecting that NUMA can work on the machine. + This is only used if the build also uses <literal>--with-libnuma</literal>. + </para> + </listitem> + </varlistentry> + <varlistentry id="configure-option-with-libcurl"> <term><option>--with-libcurl</option></term> <listitem> @@ -2664,6 +2674,16 @@ ninja install </listitem> </varlistentry> + <varlistentry id="configure-with-libcap-meson"> + <term><option>-Dlibcap={ auto | enabled | disabled }</option></term> + <listitem> + <para> + Build with libcap support for detecting that NUMA can work on the machine. + This is only used if the build also uses <productname>libnuma</productname>. + </para> + </listitem> + </varlistentry> + <varlistentry id="configure-with-libnuma-meson"> <term><option>-Dlibnuma={ auto | enabled | disabled }</option></term> <listitem> diff --git a/meson.build b/meson.build index 21c31f05f75..01676ba5958 100644 --- a/meson.build +++ b/meson.build @@ -960,6 +960,17 @@ else endif +############################################################### +# Library: libcap +############################################################### + +libcapopt = get_option('libcap') +libcap = dependency('libcap', required: libcapopt) +if libcap.found() + cdata.set('USE_LIBCAP', 1) +endif + + ############################################################### # Library: libnuma ############################################################### @@ -3328,6 +3339,7 @@ backend_both_deps += [ ldap, libintl, libnuma, + libcap, liburing, libxml, lz4, @@ -3983,6 +3995,7 @@ if meson.version().version_compare('>=0.57') 'gss': gssapi, 'icu': icu, 'ldap': ldap, + 'libcap': libcap, 'libcurl': libcurl, 'libnuma': libnuma, 'liburing': liburing, diff --git a/meson_options.txt b/meson_options.txt index 06bf5627d3c..fef0f58f82c 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -100,6 +100,9 @@ option('icu', type: 'feature', value: 'auto', option('ldap', type: 'feature', value: 'auto', description: 'LDAP support') +option('libcap', type: 'feature', value: 'auto', + description: 'libcap support') + option('libcurl', type : 'feature', value: 'auto', description: 'libcurl support') diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index c4dc5d72bdb..53338b9f8f5 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in @@ -244,6 +244,9 @@ /* Define to 1 if you have the `crypto' library (-lcrypto). */ #undef HAVE_LIBCRYPTO +/* Define to 1 if you have the `cap' library (-lcap). */ +#undef HAVE_LIBCAP + /* Define to 1 if you have the `curl' library (-lcurl). */ #undef HAVE_LIBCURL @@ -693,6 +696,9 @@ /* Define to 1 to build with LDAP support. (--with-ldap) */ #undef USE_LDAP +/* Define to 1 to build with libcap support. (--with-libcap) */ +#undef USE_LIBCAP + /* Define to 1 to build with libcurl support. (--with-libcurl) */ #undef USE_LIBCURL diff --git a/src/port/pg_numa.c b/src/port/pg_numa.c index 3368a43a338..87c076d0cdf 100644 --- a/src/port/pg_numa.c +++ b/src/port/pg_numa.c @@ -30,6 +30,10 @@ #include <numa.h> #include <numaif.h> +#ifdef USE_LIBCAP +#include <sys/capability.h> +#endif + /* * numa_move_pages() chunk size, has to be <= 16 to work around a kernel bug * in do_pages_stat() (chunked by DO_PAGES_STAT_CHUNK_NR). By using the same @@ -47,9 +51,38 @@ int pg_numa_init(void) { - int r = numa_available(); + int r; +#ifdef USE_LIBCAP + cap_t cap; + cap_flag_value_t on; +#endif + + r = numa_available(); + +#ifdef USE_LIBCAP + if (r == -1) + return r; + + /* + * Check if we have CAP_SYS_NICE set, which is required by NUMA function + * calls. + */ + cap = cap_get_proc(); + if (cap == NULL) + return r; + + if (cap_get_flag(cap, CAP_SYS_NICE, CAP_PERMITTED, &on) != 0) + on = CAP_CLEAR; + + cap_free(cap); + + if (on == CAP_SET) + return r; + return -1; +#else return r; +#endif } /* -- 2.48.1