Message ID | 193e1a53ba530e4a7ec909a11662ec733196d659.1712312063.git.fweimer@redhat.com |
---|---|
State | New |
Headers | show |
Series | Enhanced CPU diagnostics for ld.so | expand |
On Fri, Apr 5, 2024 at 3:17 AM Florian Weimer <fweimer@redhat.com> wrote: > > --- > elf/dl-iterate_cpu.h | 135 ++++++++++++++++++++++++++ > sysdeps/generic/dl-affinity.h | 54 +++++++++++ > sysdeps/unix/sysv/linux/dl-affinity.h | 46 +++++++++ > 3 files changed, 235 insertions(+) > create mode 100644 elf/dl-iterate_cpu.h > create mode 100644 sysdeps/generic/dl-affinity.h > create mode 100644 sysdeps/unix/sysv/linux/dl-affinity.h > > diff --git a/elf/dl-iterate_cpu.h b/elf/dl-iterate_cpu.h > new file mode 100644 > index 0000000000..772efffae1 > --- /dev/null > +++ b/elf/dl-iterate_cpu.h > @@ -0,0 +1,135 @@ > +/* Iterate over all CPUs, for CPU-specific diagnostics. > + Copyright (C) 2024 Free Software Foundation, Inc. > + This file is part of the GNU C Library. > + > + The GNU C Library is free software; you can redistribute it and/or > + modify it under the terms of the GNU Lesser General Public > + License as published by the Free Software Foundation; either > + version 2.1 of the License, or (at your option) any later version. > + > + The GNU C Library is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + Lesser General Public License for more details. > + > + You should have received a copy of the GNU Lesser General Public > + License along with the GNU C Library; if not, see > + <https://www.gnu.org/licenses/>. */ > + > +#ifndef DL_ITERATE_CPU_H > +#define DL_ITERATE_CPU_H > + > +#include <dl-affinity.h> > + > +struct dl_iterate_cpu > +{ > + /* Sequential iteration count, starting at 0. */ > + unsigned int processor_index; > + > + /* Requested CPU. Can be -1 if affinity could not be set. */ > + int requested_cpu; > + > + /* Observed current CPU. -1 if unavailable. */ > + int actual_cpu; > + > + /* Observed node ID for the CPU. -1 if unavailable. */ > + int actual_node; > + > + /* Internal fields to implement the iteration. */ > + > + /* Affinity as obtained by _dl_iterate_cpu_init, using > + _dl_getaffinity. Space for 8,192 CPUs. */ > + unsigned long int mask_reference[8192 / sizeof (unsigned long int) / 8]; > + > + /* This array is used by _dl_setaffinity calls. */ > + unsigned long int mask_request[8192 / sizeof (unsigned long int) / 8]; > + > + /* Return value from the initial _dl_getaffinity call. */ > + int length_reference; > +}; > + > +static void > +_dl_iterate_cpu_init (struct dl_iterate_cpu *dic) > +{ > + dic->length_reference > + = _dl_getaffinity (dic->mask_reference, sizeof (dic->mask_reference)); > + /* Prepare for the first _dl_iterate_cpu_next call. */ > + dic->processor_index = -1; > + dic->requested_cpu = -1; > +} > + > +static bool > +_dl_iterate_cpu_next (struct dl_iterate_cpu *dic) > +{ > + ++dic->processor_index; > + > + if (dic->length_reference > 0) > + { > + /* Search for the next CPU to switch to. */ > + while (true) > + { > + ++dic->requested_cpu; > + > + /* Array index and bit number within the array. */ > + unsigned int long_index > + = dic->requested_cpu / sizeof (unsigned long int) / 8; > + unsigned int bit_index > + = dic->requested_cpu % (sizeof (unsigned long int) * 8); > + > + if (long_index * sizeof (unsigned long int) >= dic->length_reference) > + /* All possible CPUs have been covered. */ > + return false; > + > + unsigned long int bit = 1UL << bit_index; > + if (dic->mask_reference[long_index] & bit) > + { > + /* The CPU is available. Try to select it. */ > + dic->mask_request[long_index] = bit; > + if (_dl_setaffinity (dic->mask_request, > + (long_index + 1) > + * sizeof (unsigned long int)) < 0) > + { > + /* Record that we could not perform a CPU request. */ > + dic->length_reference = -1; > + > + if (dic->processor_index > 0) > + /* We already reported something. There is no need to > + continue because the new data is probably not useful. */ > + return false; > + } > + > + /* Clear the bit in case the next iteration switches to the > + next long value. */ > + dic->mask_request[long_index] = 0; > + > + /* We found a CPU to run on. */ > + break; > + } > + } > + } > + else > + { > + /* No way to set CPU affinity. Iterate just once. */ > + if (dic->processor_index > 0) > + return false; > + } > + > + /* Fill in the actual CPU information. CPU pinning may not actually > + be effective, depending on the container host. */ > + unsigned int cpu, node; > + if (_dl_getcpu (&cpu, &node) < 0) > + { > + /* No CPU information available. */ > + dic->actual_cpu = -1; > + dic->actual_node = -1; > + } > + else > + { > + dic->actual_cpu = cpu; > + dic->actual_node = node; > + } > + > + return true; > +} > + > +#endif /* DL_ITERATE_CPU_H */ > diff --git a/sysdeps/generic/dl-affinity.h b/sysdeps/generic/dl-affinity.h > new file mode 100644 > index 0000000000..d117f737e9 > --- /dev/null > +++ b/sysdeps/generic/dl-affinity.h > @@ -0,0 +1,54 @@ > +/* CPU affinity handling for the dynamic linker. Stub version. > + Copyright (C) 2024 Free Software Foundation, Inc. > + This file is part of the GNU C Library. > + > + The GNU C Library is free software; you can redistribute it and/or > + modify it under the terms of the GNU Lesser General Public > + License as published by the Free Software Foundation; either > + version 2.1 of the License, or (at your option) any later version. > + > + The GNU C Library is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + Lesser General Public License for more details. > + > + You should have received a copy of the GNU Lesser General Public > + License along with the GNU C Library; if not, see > + <https://www.gnu.org/licenses/>. */ > + > +#ifndef DL_AFFINITY_H > +#define DL_AFFINITY_H > + > +#include <errno.h> > +#include <stddef.h> > + > +/* On success, write the current CPU ID to *CPU, and the current node > + ID to *NODE, and return 0. Return a negative error code on > + failure. */ > +static inline int > +_dl_getcpu (unsigned int *cpu, unsigned int *node) > +{ > + return -ENOSYS; > +} > + > +/* On success, write CPU ID affinity bits for the current thread to > + *BITS, which must be SIZE bytes long, and return the number of > + bytes updated, a multiple of sizeof (unsigned long int). On > + failure, return a negative error code. */ > +static int > +_dl_getaffinity (unsigned long int *bits, size_t size) > +{ > + return -ENOSYS; > +} > + > +/* Set the CPU affinity mask for the current thread to *BITS, using > + the SIZE bytes from that array, which should be a multiple of > + sizeof (unsigned long int). Return 0 on success, and a negative > + error code on failure. */ > +static int > +_dl_setaffinity (const unsigned long int *bits, size_t size) > +{ > + return -ENOSYS; > +} > + > +#endif /* DL_AFFINITY_H */ > diff --git a/sysdeps/unix/sysv/linux/dl-affinity.h b/sysdeps/unix/sysv/linux/dl-affinity.h > new file mode 100644 > index 0000000000..bbfede7750 > --- /dev/null > +++ b/sysdeps/unix/sysv/linux/dl-affinity.h > @@ -0,0 +1,46 @@ > +/* CPU affinity handling for the dynamic linker. Linux version. > + Copyright (C) 2024 Free Software Foundation, Inc. > + This file is part of the GNU C Library. > + > + The GNU C Library is free software; you can redistribute it and/or > + modify it under the terms of the GNU Lesser General Public > + License as published by the Free Software Foundation; either > + version 2.1 of the License, or (at your option) any later version. > + > + The GNU C Library is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + Lesser General Public License for more details. > + > + You should have received a copy of the GNU Lesser General Public > + License along with the GNU C Library; if not, see > + <https://www.gnu.org/licenses/>. */ > + > +/* See sysdeps/generic/dl-affinity.h for documentation of these interfaces. */ > + > +#ifndef DL_AFFINITY_H > +#define DL_AFFINITY_H > + > +#include <sysdep.h> > +#include <stddef.h> > +#include <unistd.h> > + > +static inline int > +_dl_getcpu (unsigned int *cpu, unsigned int *node) > +{ > + return INTERNAL_SYSCALL_CALL (getcpu, cpu, node); > +} > + > +static int > +_dl_getaffinity (unsigned long int *bits, size_t size) > +{ > + return INTERNAL_SYSCALL_CALL (sched_getaffinity, /* TID */ 0, size, bits); > +} > + > +static int > +_dl_setaffinity (const unsigned long int *bits, size_t size) > +{ > + return INTERNAL_SYSCALL_CALL (sched_setaffinity, /* TID */ 0, size, bits); > +} > + > +#endif /* DL_AFFINITY_H */ > -- > 2.44.0 > > LGTM. Reviewed-by: H.J. Lu <hjl.tools@gmail.com> Thanks.
diff --git a/elf/dl-iterate_cpu.h b/elf/dl-iterate_cpu.h new file mode 100644 index 0000000000..772efffae1 --- /dev/null +++ b/elf/dl-iterate_cpu.h @@ -0,0 +1,135 @@ +/* Iterate over all CPUs, for CPU-specific diagnostics. + Copyright (C) 2024 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <https://www.gnu.org/licenses/>. */ + +#ifndef DL_ITERATE_CPU_H +#define DL_ITERATE_CPU_H + +#include <dl-affinity.h> + +struct dl_iterate_cpu +{ + /* Sequential iteration count, starting at 0. */ + unsigned int processor_index; + + /* Requested CPU. Can be -1 if affinity could not be set. */ + int requested_cpu; + + /* Observed current CPU. -1 if unavailable. */ + int actual_cpu; + + /* Observed node ID for the CPU. -1 if unavailable. */ + int actual_node; + + /* Internal fields to implement the iteration. */ + + /* Affinity as obtained by _dl_iterate_cpu_init, using + _dl_getaffinity. Space for 8,192 CPUs. */ + unsigned long int mask_reference[8192 / sizeof (unsigned long int) / 8]; + + /* This array is used by _dl_setaffinity calls. */ + unsigned long int mask_request[8192 / sizeof (unsigned long int) / 8]; + + /* Return value from the initial _dl_getaffinity call. */ + int length_reference; +}; + +static void +_dl_iterate_cpu_init (struct dl_iterate_cpu *dic) +{ + dic->length_reference + = _dl_getaffinity (dic->mask_reference, sizeof (dic->mask_reference)); + /* Prepare for the first _dl_iterate_cpu_next call. */ + dic->processor_index = -1; + dic->requested_cpu = -1; +} + +static bool +_dl_iterate_cpu_next (struct dl_iterate_cpu *dic) +{ + ++dic->processor_index; + + if (dic->length_reference > 0) + { + /* Search for the next CPU to switch to. */ + while (true) + { + ++dic->requested_cpu; + + /* Array index and bit number within the array. */ + unsigned int long_index + = dic->requested_cpu / sizeof (unsigned long int) / 8; + unsigned int bit_index + = dic->requested_cpu % (sizeof (unsigned long int) * 8); + + if (long_index * sizeof (unsigned long int) >= dic->length_reference) + /* All possible CPUs have been covered. */ + return false; + + unsigned long int bit = 1UL << bit_index; + if (dic->mask_reference[long_index] & bit) + { + /* The CPU is available. Try to select it. */ + dic->mask_request[long_index] = bit; + if (_dl_setaffinity (dic->mask_request, + (long_index + 1) + * sizeof (unsigned long int)) < 0) + { + /* Record that we could not perform a CPU request. */ + dic->length_reference = -1; + + if (dic->processor_index > 0) + /* We already reported something. There is no need to + continue because the new data is probably not useful. */ + return false; + } + + /* Clear the bit in case the next iteration switches to the + next long value. */ + dic->mask_request[long_index] = 0; + + /* We found a CPU to run on. */ + break; + } + } + } + else + { + /* No way to set CPU affinity. Iterate just once. */ + if (dic->processor_index > 0) + return false; + } + + /* Fill in the actual CPU information. CPU pinning may not actually + be effective, depending on the container host. */ + unsigned int cpu, node; + if (_dl_getcpu (&cpu, &node) < 0) + { + /* No CPU information available. */ + dic->actual_cpu = -1; + dic->actual_node = -1; + } + else + { + dic->actual_cpu = cpu; + dic->actual_node = node; + } + + return true; +} + +#endif /* DL_ITERATE_CPU_H */ diff --git a/sysdeps/generic/dl-affinity.h b/sysdeps/generic/dl-affinity.h new file mode 100644 index 0000000000..d117f737e9 --- /dev/null +++ b/sysdeps/generic/dl-affinity.h @@ -0,0 +1,54 @@ +/* CPU affinity handling for the dynamic linker. Stub version. + Copyright (C) 2024 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <https://www.gnu.org/licenses/>. */ + +#ifndef DL_AFFINITY_H +#define DL_AFFINITY_H + +#include <errno.h> +#include <stddef.h> + +/* On success, write the current CPU ID to *CPU, and the current node + ID to *NODE, and return 0. Return a negative error code on + failure. */ +static inline int +_dl_getcpu (unsigned int *cpu, unsigned int *node) +{ + return -ENOSYS; +} + +/* On success, write CPU ID affinity bits for the current thread to + *BITS, which must be SIZE bytes long, and return the number of + bytes updated, a multiple of sizeof (unsigned long int). On + failure, return a negative error code. */ +static int +_dl_getaffinity (unsigned long int *bits, size_t size) +{ + return -ENOSYS; +} + +/* Set the CPU affinity mask for the current thread to *BITS, using + the SIZE bytes from that array, which should be a multiple of + sizeof (unsigned long int). Return 0 on success, and a negative + error code on failure. */ +static int +_dl_setaffinity (const unsigned long int *bits, size_t size) +{ + return -ENOSYS; +} + +#endif /* DL_AFFINITY_H */ diff --git a/sysdeps/unix/sysv/linux/dl-affinity.h b/sysdeps/unix/sysv/linux/dl-affinity.h new file mode 100644 index 0000000000..bbfede7750 --- /dev/null +++ b/sysdeps/unix/sysv/linux/dl-affinity.h @@ -0,0 +1,46 @@ +/* CPU affinity handling for the dynamic linker. Linux version. + Copyright (C) 2024 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <https://www.gnu.org/licenses/>. */ + +/* See sysdeps/generic/dl-affinity.h for documentation of these interfaces. */ + +#ifndef DL_AFFINITY_H +#define DL_AFFINITY_H + +#include <sysdep.h> +#include <stddef.h> +#include <unistd.h> + +static inline int +_dl_getcpu (unsigned int *cpu, unsigned int *node) +{ + return INTERNAL_SYSCALL_CALL (getcpu, cpu, node); +} + +static int +_dl_getaffinity (unsigned long int *bits, size_t size) +{ + return INTERNAL_SYSCALL_CALL (sched_getaffinity, /* TID */ 0, size, bits); +} + +static int +_dl_setaffinity (const unsigned long int *bits, size_t size) +{ + return INTERNAL_SYSCALL_CALL (sched_setaffinity, /* TID */ 0, size, bits); +} + +#endif /* DL_AFFINITY_H */