Skip to content

Commit

Permalink
[OpenMP][AIX] Affinity implementation for AIX (llvm#84984)
Browse files Browse the repository at this point in the history
This patch implements `affinity` for AIX, which is quite different from
platforms such as Linux.
- Setting CPU affinity through masks and related functions are not
supported. System call `bindprocessor()` is used to bind a thread to one
CPU per call.
- There are no system routines to get the affinity info of a thread. The
implementation of `get_system_affinity()` for AIX gets the mask of all
available CPUs, to be used as the full mask only.
- Topology is not available from the file system. It is obtained through
system SRAD (Scheduler Resource Allocation Domain).

This patch has run through the libomp LIT tests successfully with
`affinity` enabled.
  • Loading branch information
xingxue-ibm authored and chencha3 committed Mar 22, 2024
1 parent 7179756 commit 618bcd7
Show file tree
Hide file tree
Showing 6 changed files with 233 additions and 19 deletions.
5 changes: 4 additions & 1 deletion openmp/runtime/src/kmp.h
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,7 @@ class KMPAffinity {
typedef KMPAffinity::Mask kmp_affin_mask_t;
extern KMPAffinity *__kmp_affinity_dispatch;

#ifndef KMP_OS_AIX
class kmp_affinity_raii_t {
kmp_affin_mask_t *mask;
bool restored;
Expand All @@ -843,6 +844,7 @@ class kmp_affinity_raii_t {
}
~kmp_affinity_raii_t() { restore(); }
};
#endif // !KMP_OS_AIX

// Declare local char buffers with this size for printing debug and info
// messages, using __kmp_affinity_print_mask().
Expand Down Expand Up @@ -3910,7 +3912,8 @@ extern void __kmp_balanced_affinity(kmp_info_t *th, int team_size);
#if KMP_WEIGHTED_ITERATIONS_SUPPORTED
extern int __kmp_get_first_osid_with_ecore(void);
#endif
#if KMP_OS_LINUX || KMP_OS_FREEBSD || KMP_OS_NETBSD || KMP_OS_DRAGONFLY
#if KMP_OS_LINUX || KMP_OS_FREEBSD || KMP_OS_NETBSD || KMP_OS_DRAGONFLY || \
KMP_OS_AIX
extern int kmp_set_thread_affinity_mask_initial(void);
#endif
static inline void __kmp_assign_root_init_mask() {
Expand Down
130 changes: 122 additions & 8 deletions openmp/runtime/src/kmp_affinity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2910,12 +2910,17 @@ static inline const char *__kmp_cpuinfo_get_envvar() {
}

// Parse /proc/cpuinfo (or an alternate file in the same format) to obtain the
// affinity map.
// affinity map. On AIX, the map is obtained through system SRAD (Scheduler
// Resource Allocation Domain).
static bool __kmp_affinity_create_cpuinfo_map(int *line,
kmp_i18n_id_t *const msg_id) {
*msg_id = kmp_i18n_null;

#if KMP_OS_AIX
unsigned num_records = __kmp_xproc;
#else
const char *filename = __kmp_cpuinfo_get_filename();
const char *envvar = __kmp_cpuinfo_get_envvar();
*msg_id = kmp_i18n_null;

if (__kmp_affinity.flags.verbose) {
KMP_INFORM(AffParseFilename, "KMP_AFFINITY", filename);
Expand Down Expand Up @@ -2974,6 +2979,7 @@ static bool __kmp_affinity_create_cpuinfo_map(int *line,
*msg_id = kmp_i18n_str_CantRewindCpuinfo;
return false;
}
#endif // KMP_OS_AIX

// Allocate the array of records to store the proc info in. The dummy
// element at the end makes the logic in filling them out easier to code.
Expand Down Expand Up @@ -3003,6 +3009,99 @@ static bool __kmp_affinity_create_cpuinfo_map(int *line,
INIT_PROC_INFO(threadInfo[i]);
}

#if KMP_OS_AIX
int smt_threads;
lpar_info_format1_t cpuinfo;
unsigned num_avail = __kmp_xproc;

if (__kmp_affinity.flags.verbose)
KMP_INFORM(AffParseFilename, "KMP_AFFINITY", "system info for topology");

// Get the number of SMT threads per core.
int retval =
lpar_get_info(LPAR_INFO_FORMAT1, &cpuinfo, sizeof(lpar_info_format1_t));
if (!retval)
smt_threads = cpuinfo.smt_threads;
else {
CLEANUP_THREAD_INFO;
*msg_id = kmp_i18n_str_UnknownTopology;
return false;
}

// Allocate a resource set containing available system resourses.
rsethandle_t sys_rset = rs_alloc(RS_SYSTEM);
if (sys_rset == NULL) {
CLEANUP_THREAD_INFO;
*msg_id = kmp_i18n_str_UnknownTopology;
return false;
}
// Allocate a resource set for the SRAD info.
rsethandle_t srad = rs_alloc(RS_EMPTY);
if (srad == NULL) {
rs_free(sys_rset);
CLEANUP_THREAD_INFO;
*msg_id = kmp_i18n_str_UnknownTopology;
return false;
}

// Get the SRAD system detail level.
int sradsdl = rs_getinfo(NULL, R_SRADSDL, 0);
if (sradsdl < 0) {
rs_free(sys_rset);
rs_free(srad);
CLEANUP_THREAD_INFO;
*msg_id = kmp_i18n_str_UnknownTopology;
return false;
}
// Get the number of RADs at that SRAD SDL.
int num_rads = rs_numrads(sys_rset, sradsdl, 0);
if (num_rads < 0) {
rs_free(sys_rset);
rs_free(srad);
CLEANUP_THREAD_INFO;
*msg_id = kmp_i18n_str_UnknownTopology;
return false;
}

// Get the maximum number of procs that may be contained in a resource set.
int max_procs = rs_getinfo(NULL, R_MAXPROCS, 0);
if (max_procs < 0) {
rs_free(sys_rset);
rs_free(srad);
CLEANUP_THREAD_INFO;
*msg_id = kmp_i18n_str_UnknownTopology;
return false;
}

int cur_rad = 0;
int num_set = 0;
for (int srad_idx = 0; cur_rad < num_rads && srad_idx < VMI_MAXRADS;
++srad_idx) {
// Check if the SRAD is available in the RSET.
if (rs_getrad(sys_rset, srad, sradsdl, srad_idx, 0) < 0)
continue;

for (int cpu = 0; cpu < max_procs; cpu++) {
// Set the info for the cpu if it is in the SRAD.
if (rs_op(RS_TESTRESOURCE, srad, NULL, R_PROCS, cpu)) {
threadInfo[cpu][osIdIndex] = cpu;
threadInfo[cpu][pkgIdIndex] = cur_rad;
threadInfo[cpu][coreIdIndex] = cpu / smt_threads;
++num_set;
if (num_set >= num_avail) {
// Done if all available CPUs have been set.
break;
}
}
}
++cur_rad;
}
rs_free(sys_rset);
rs_free(srad);

// The topology is already sorted.

#else // !KMP_OS_AIX
unsigned num_avail = 0;
*line = 0;
#if KMP_ARCH_S390X
Expand Down Expand Up @@ -3250,6 +3349,8 @@ static bool __kmp_affinity_create_cpuinfo_map(int *line,
qsort(threadInfo, num_avail, sizeof(*threadInfo),
__kmp_affinity_cmp_ProcCpuInfo_phys_id);

#endif // KMP_OS_AIX

// The table is now sorted by pkgId / coreId / threadId, but we really don't
// know the radix of any of the fields. pkgId's may be sparsely assigned among
// the chips on a system. Although coreId's are usually assigned
Expand Down Expand Up @@ -4445,7 +4546,7 @@ static bool __kmp_aux_affinity_initialize_topology(kmp_affinity_t &affinity) {
}
#endif /* KMP_ARCH_X86 || KMP_ARCH_X86_64 */

#if KMP_OS_LINUX
#if KMP_OS_LINUX || KMP_OS_AIX
if (!success) {
int line = 0;
success = __kmp_affinity_create_cpuinfo_map(&line, &msg_id);
Expand Down Expand Up @@ -4841,7 +4942,12 @@ void __kmp_affinity_uninitialize(void) {
}
if (__kmp_affin_origMask != NULL) {
if (KMP_AFFINITY_CAPABLE()) {
#if KMP_OS_AIX
// Uninitialize by unbinding the thread.
bindprocessor(BINDTHREAD, thread_self(), PROCESSOR_CLASS_ANY);
#else
__kmp_set_system_affinity(__kmp_affin_origMask, FALSE);
#endif
}
KMP_CPU_FREE(__kmp_affin_origMask);
__kmp_affin_origMask = NULL;
Expand Down Expand Up @@ -5015,7 +5121,10 @@ void __kmp_affinity_bind_init_mask(int gtid) {
__kmp_set_system_affinity(th->th.th_affin_mask, FALSE);
} else
#endif
#ifndef KMP_OS_AIX
// Do not set the full mask as the init mask on AIX.
__kmp_set_system_affinity(th->th.th_affin_mask, TRUE);
#endif
}

void __kmp_affinity_bind_place(int gtid) {
Expand Down Expand Up @@ -5128,15 +5237,15 @@ int __kmp_aux_set_affinity(void **mask) {
int __kmp_aux_get_affinity(void **mask) {
int gtid;
int retval;
#if KMP_OS_WINDOWS || KMP_DEBUG
#if KMP_OS_WINDOWS || KMP_OS_AIX || KMP_DEBUG
kmp_info_t *th;
#endif
if (!KMP_AFFINITY_CAPABLE()) {
return -1;
}

gtid = __kmp_entry_gtid();
#if KMP_OS_WINDOWS || KMP_DEBUG
#if KMP_OS_WINDOWS || KMP_OS_AIX || KMP_DEBUG
th = __kmp_threads[gtid];
#else
(void)gtid; // unused variable
Expand All @@ -5159,7 +5268,7 @@ int __kmp_aux_get_affinity(void **mask) {
}
}

#if !KMP_OS_WINDOWS
#if !KMP_OS_WINDOWS && !KMP_OS_AIX

retval = __kmp_get_system_affinity((kmp_affin_mask_t *)(*mask), FALSE);
KA_TRACE(
Expand All @@ -5179,7 +5288,7 @@ int __kmp_aux_get_affinity(void **mask) {
KMP_CPU_COPY((kmp_affin_mask_t *)(*mask), th->th.th_affin_mask);
return 0;

#endif /* KMP_OS_WINDOWS */
#endif /* !KMP_OS_WINDOWS && !KMP_OS_AIX */
}

int __kmp_aux_get_affinity_max_proc() {
Expand Down Expand Up @@ -5561,7 +5670,8 @@ void __kmp_balanced_affinity(kmp_info_t *th, int nthreads) {
}
}

#if KMP_OS_LINUX || KMP_OS_FREEBSD || KMP_OS_NETBSD || KMP_OS_DRAGONFLY
#if KMP_OS_LINUX || KMP_OS_FREEBSD || KMP_OS_NETBSD || KMP_OS_DRAGONFLY || \
KMP_OS_AIX
// We don't need this entry for Windows because
// there is GetProcessAffinityMask() api
//
Expand Down Expand Up @@ -5596,7 +5706,11 @@ extern "C"
"set full mask for thread %d\n",
gtid));
KMP_DEBUG_ASSERT(__kmp_affin_fullMask != NULL);
#if KMP_OS_AIX
return bindprocessor(BINDTHREAD, thread_self(), PROCESSOR_CLASS_ANY);
#else
return __kmp_set_system_affinity(__kmp_affin_fullMask, FALSE);
#endif
}
#endif

Expand Down
74 changes: 72 additions & 2 deletions openmp/runtime/src/kmp_affinity.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,8 @@ class KMPHwlocAffinity : public KMPAffinity {
};
#endif /* KMP_USE_HWLOC */

#if KMP_OS_LINUX || KMP_OS_FREEBSD || KMP_OS_NETBSD || KMP_OS_DRAGONFLY
#if KMP_OS_LINUX || KMP_OS_FREEBSD || KMP_OS_NETBSD || KMP_OS_DRAGONFLY || \
KMP_OS_AIX
#if KMP_OS_LINUX
/* On some of the older OS's that we build on, these constants aren't present
in <asm/unistd.h> #included from <sys.syscall.h>. They must be the same on
Expand Down Expand Up @@ -317,6 +318,10 @@ class KMPHwlocAffinity : public KMPAffinity {
#elif KMP_OS_NETBSD
#include <pthread.h>
#include <sched.h>
#elif KMP_OS_AIX
#include <sys/dr.h>
#include <sys/rset.h>
#define VMI_MAXRADS 64 // Maximum number of RADs allowed by AIX.
#endif
class KMPNativeAffinity : public KMPAffinity {
class Mask : public KMPAffinity::Mask {
Expand Down Expand Up @@ -404,6 +409,70 @@ class KMPNativeAffinity : public KMPAffinity {
++retval;
return retval;
}
#if KMP_OS_AIX
// On AIX, we don't have a way to get CPU(s) a thread is bound to.
// This routine is only used to get the full mask.
int get_system_affinity(bool abort_on_error) override {
KMP_ASSERT2(KMP_AFFINITY_CAPABLE(),
"Illegal get affinity operation when not capable");

(void)abort_on_error;

// Set the mask with all CPUs that are available.
for (int i = 0; i < __kmp_xproc; ++i)
KMP_CPU_SET(i, this);
return 0;
}
int set_system_affinity(bool abort_on_error) const override {
KMP_ASSERT2(KMP_AFFINITY_CAPABLE(),

"Illegal set affinity operation when not capable");

int location;
int gtid = __kmp_entry_gtid();
int tid = thread_self();

// Unbind the thread if it was bound to any processors before so that
// we can bind the thread to CPUs specified by the mask not others.
int retval = bindprocessor(BINDTHREAD, tid, PROCESSOR_CLASS_ANY);

// On AIX, we can only bind to one instead of a set of CPUs with the
// bindprocessor() system call.
KMP_CPU_SET_ITERATE(location, this) {
if (KMP_CPU_ISSET(location, this)) {
retval = bindprocessor(BINDTHREAD, tid, location);
if (retval == -1 && errno == 1) {
rsid_t rsid;
rsethandle_t rsh;
// Put something in rsh to prevent compiler warning
// about uninitalized use
rsh = rs_alloc(RS_EMPTY);
rsid.at_pid = getpid();
if (RS_DEFAULT_RSET != ra_getrset(R_PROCESS, rsid, 0, rsh)) {
retval = ra_detachrset(R_PROCESS, rsid, 0);
retval = bindprocessor(BINDTHREAD, tid, location);
}
}
if (retval == 0) {
KA_TRACE(10, ("__kmp_set_system_affinity: Done binding "
"T#%d to cpu=%d.\n",
gtid, location));
continue;
}
int error = errno;
if (abort_on_error) {
__kmp_fatal(KMP_MSG(FunctionError, "bindprocessor()"),
KMP_ERR(error), __kmp_msg_null);
KA_TRACE(10, ("__kmp_set_system_affinity: Error binding "
"T#%d to cpu=%d, errno=%d.\n",
gtid, location, error));
return error;
}
}
}
return 0;
}
#else // !KMP_OS_AIX
int get_system_affinity(bool abort_on_error) override {
KMP_ASSERT2(KMP_AFFINITY_CAPABLE(),
"Illegal get affinity operation when not capable");
Expand Down Expand Up @@ -446,6 +515,7 @@ class KMPNativeAffinity : public KMPAffinity {
}
return error;
}
#endif // KMP_OS_AIX
};
void determine_capable(const char *env_var) override {
__kmp_affinity_determine_capable(env_var);
Expand Down Expand Up @@ -475,7 +545,7 @@ class KMPNativeAffinity : public KMPAffinity {
api_type get_api_type() const override { return NATIVE_OS; }
};
#endif /* KMP_OS_LINUX || KMP_OS_FREEBSD || KMP_OS_NETBSD || KMP_OS_DRAGONFLY \
*/
|| KMP_OS_AIX */

#if KMP_OS_WINDOWS
class KMPNativeAffinity : public KMPAffinity {
Expand Down
2 changes: 1 addition & 1 deletion openmp/runtime/src/kmp_os.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
#endif

#if (KMP_OS_LINUX || KMP_OS_WINDOWS || KMP_OS_FREEBSD || KMP_OS_NETBSD || \
KMP_OS_DRAGONFLY) && \
KMP_OS_DRAGONFLY || KMP_OS_AIX) && \
!KMP_OS_WASI
#define KMP_AFFINITY_SUPPORTED 1
#if KMP_OS_WINDOWS && KMP_ARCH_X86_64
Expand Down
Loading

0 comments on commit 618bcd7

Please sign in to comment.