Skip to content

Commit 47aac49

Browse files
committed
[OpenMP][AIX] Affinity implementation for AIX (#84984)
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. (cherry picked from commit d394f3a)
1 parent a385a91 commit 47aac49

File tree

6 files changed

+229
-19
lines changed

6 files changed

+229
-19
lines changed

openmp/runtime/src/kmp.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,7 @@ class KMPAffinity {
818818
typedef KMPAffinity::Mask kmp_affin_mask_t;
819819
extern KMPAffinity *__kmp_affinity_dispatch;
820820

821+
#ifndef KMP_OS_AIX
821822
class kmp_affinity_raii_t {
822823
kmp_affin_mask_t *mask;
823824
bool restored;
@@ -842,6 +843,7 @@ class kmp_affinity_raii_t {
842843
}
843844
~kmp_affinity_raii_t() { restore(); }
844845
};
846+
#endif // !KMP_OS_AIX
845847

846848
// Declare local char buffers with this size for printing debug and info
847849
// messages, using __kmp_affinity_print_mask().
@@ -3911,7 +3913,7 @@ extern void __kmp_balanced_affinity(kmp_info_t *th, int team_size);
39113913
#if KMP_WEIGHTED_ITERATIONS_SUPPORTED
39123914
extern int __kmp_get_first_osid_with_ecore(void);
39133915
#endif
3914-
#if KMP_OS_LINUX || KMP_OS_FREEBSD
3916+
#if KMP_OS_LINUX || KMP_OS_FREEBSD || KMP_OS_AIX
39153917
extern int kmp_set_thread_affinity_mask_initial(void);
39163918
#endif
39173919
static inline void __kmp_assign_root_init_mask() {

openmp/runtime/src/kmp_affinity.cpp

+121-8
Original file line numberDiff line numberDiff line change
@@ -2906,12 +2906,17 @@ static inline const char *__kmp_cpuinfo_get_envvar() {
29062906
}
29072907

29082908
// Parse /proc/cpuinfo (or an alternate file in the same format) to obtain the
2909-
// affinity map.
2909+
// affinity map. On AIX, the map is obtained through system SRAD (Scheduler
2910+
// Resource Allocation Domain).
29102911
static bool __kmp_affinity_create_cpuinfo_map(int *line,
29112912
kmp_i18n_id_t *const msg_id) {
2913+
*msg_id = kmp_i18n_null;
2914+
2915+
#if KMP_OS_AIX
2916+
unsigned num_records = __kmp_xproc;
2917+
#else
29122918
const char *filename = __kmp_cpuinfo_get_filename();
29132919
const char *envvar = __kmp_cpuinfo_get_envvar();
2914-
*msg_id = kmp_i18n_null;
29152920

29162921
if (__kmp_affinity.flags.verbose) {
29172922
KMP_INFORM(AffParseFilename, "KMP_AFFINITY", filename);
@@ -2970,6 +2975,7 @@ static bool __kmp_affinity_create_cpuinfo_map(int *line,
29702975
*msg_id = kmp_i18n_str_CantRewindCpuinfo;
29712976
return false;
29722977
}
2978+
#endif // KMP_OS_AIX
29732979

29742980
// Allocate the array of records to store the proc info in. The dummy
29752981
// element at the end makes the logic in filling them out easier to code.
@@ -2999,6 +3005,99 @@ static bool __kmp_affinity_create_cpuinfo_map(int *line,
29993005
INIT_PROC_INFO(threadInfo[i]);
30003006
}
30013007

3008+
#if KMP_OS_AIX
3009+
int smt_threads;
3010+
lpar_info_format1_t cpuinfo;
3011+
unsigned num_avail = __kmp_xproc;
3012+
3013+
if (__kmp_affinity.flags.verbose)
3014+
KMP_INFORM(AffParseFilename, "KMP_AFFINITY", "system info for topology");
3015+
3016+
// Get the number of SMT threads per core.
3017+
int retval =
3018+
lpar_get_info(LPAR_INFO_FORMAT1, &cpuinfo, sizeof(lpar_info_format1_t));
3019+
if (!retval)
3020+
smt_threads = cpuinfo.smt_threads;
3021+
else {
3022+
CLEANUP_THREAD_INFO;
3023+
*msg_id = kmp_i18n_str_UnknownTopology;
3024+
return false;
3025+
}
3026+
3027+
// Allocate a resource set containing available system resourses.
3028+
rsethandle_t sys_rset = rs_alloc(RS_SYSTEM);
3029+
if (sys_rset == NULL) {
3030+
CLEANUP_THREAD_INFO;
3031+
*msg_id = kmp_i18n_str_UnknownTopology;
3032+
return false;
3033+
}
3034+
// Allocate a resource set for the SRAD info.
3035+
rsethandle_t srad = rs_alloc(RS_EMPTY);
3036+
if (srad == NULL) {
3037+
rs_free(sys_rset);
3038+
CLEANUP_THREAD_INFO;
3039+
*msg_id = kmp_i18n_str_UnknownTopology;
3040+
return false;
3041+
}
3042+
3043+
// Get the SRAD system detail level.
3044+
int sradsdl = rs_getinfo(NULL, R_SRADSDL, 0);
3045+
if (sradsdl < 0) {
3046+
rs_free(sys_rset);
3047+
rs_free(srad);
3048+
CLEANUP_THREAD_INFO;
3049+
*msg_id = kmp_i18n_str_UnknownTopology;
3050+
return false;
3051+
}
3052+
// Get the number of RADs at that SRAD SDL.
3053+
int num_rads = rs_numrads(sys_rset, sradsdl, 0);
3054+
if (num_rads < 0) {
3055+
rs_free(sys_rset);
3056+
rs_free(srad);
3057+
CLEANUP_THREAD_INFO;
3058+
*msg_id = kmp_i18n_str_UnknownTopology;
3059+
return false;
3060+
}
3061+
3062+
// Get the maximum number of procs that may be contained in a resource set.
3063+
int max_procs = rs_getinfo(NULL, R_MAXPROCS, 0);
3064+
if (max_procs < 0) {
3065+
rs_free(sys_rset);
3066+
rs_free(srad);
3067+
CLEANUP_THREAD_INFO;
3068+
*msg_id = kmp_i18n_str_UnknownTopology;
3069+
return false;
3070+
}
3071+
3072+
int cur_rad = 0;
3073+
int num_set = 0;
3074+
for (int srad_idx = 0; cur_rad < num_rads && srad_idx < VMI_MAXRADS;
3075+
++srad_idx) {
3076+
// Check if the SRAD is available in the RSET.
3077+
if (rs_getrad(sys_rset, srad, sradsdl, srad_idx, 0) < 0)
3078+
continue;
3079+
3080+
for (int cpu = 0; cpu < max_procs; cpu++) {
3081+
// Set the info for the cpu if it is in the SRAD.
3082+
if (rs_op(RS_TESTRESOURCE, srad, NULL, R_PROCS, cpu)) {
3083+
threadInfo[cpu][osIdIndex] = cpu;
3084+
threadInfo[cpu][pkgIdIndex] = cur_rad;
3085+
threadInfo[cpu][coreIdIndex] = cpu / smt_threads;
3086+
++num_set;
3087+
if (num_set >= num_avail) {
3088+
// Done if all available CPUs have been set.
3089+
break;
3090+
}
3091+
}
3092+
}
3093+
++cur_rad;
3094+
}
3095+
rs_free(sys_rset);
3096+
rs_free(srad);
3097+
3098+
// The topology is already sorted.
3099+
3100+
#else // !KMP_OS_AIX
30023101
unsigned num_avail = 0;
30033102
*line = 0;
30043103
#if KMP_ARCH_S390X
@@ -3246,6 +3345,8 @@ static bool __kmp_affinity_create_cpuinfo_map(int *line,
32463345
qsort(threadInfo, num_avail, sizeof(*threadInfo),
32473346
__kmp_affinity_cmp_ProcCpuInfo_phys_id);
32483347

3348+
#endif // KMP_OS_AIX
3349+
32493350
// The table is now sorted by pkgId / coreId / threadId, but we really don't
32503351
// know the radix of any of the fields. pkgId's may be sparsely assigned among
32513352
// the chips on a system. Although coreId's are usually assigned
@@ -4441,7 +4542,7 @@ static bool __kmp_aux_affinity_initialize_topology(kmp_affinity_t &affinity) {
44414542
}
44424543
#endif /* KMP_ARCH_X86 || KMP_ARCH_X86_64 */
44434544

4444-
#if KMP_OS_LINUX
4545+
#if KMP_OS_LINUX || KMP_OS_AIX
44454546
if (!success) {
44464547
int line = 0;
44474548
success = __kmp_affinity_create_cpuinfo_map(&line, &msg_id);
@@ -4837,7 +4938,12 @@ void __kmp_affinity_uninitialize(void) {
48374938
}
48384939
if (__kmp_affin_origMask != NULL) {
48394940
if (KMP_AFFINITY_CAPABLE()) {
4941+
#if KMP_OS_AIX
4942+
// Uninitialize by unbinding the thread.
4943+
bindprocessor(BINDTHREAD, thread_self(), PROCESSOR_CLASS_ANY);
4944+
#else
48404945
__kmp_set_system_affinity(__kmp_affin_origMask, FALSE);
4946+
#endif
48414947
}
48424948
KMP_CPU_FREE(__kmp_affin_origMask);
48434949
__kmp_affin_origMask = NULL;
@@ -5011,7 +5117,10 @@ void __kmp_affinity_bind_init_mask(int gtid) {
50115117
__kmp_set_system_affinity(th->th.th_affin_mask, FALSE);
50125118
} else
50135119
#endif
5120+
#ifndef KMP_OS_AIX
5121+
// Do not set the full mask as the init mask on AIX.
50145122
__kmp_set_system_affinity(th->th.th_affin_mask, TRUE);
5123+
#endif
50155124
}
50165125

50175126
void __kmp_affinity_bind_place(int gtid) {
@@ -5124,15 +5233,15 @@ int __kmp_aux_set_affinity(void **mask) {
51245233
int __kmp_aux_get_affinity(void **mask) {
51255234
int gtid;
51265235
int retval;
5127-
#if KMP_OS_WINDOWS || KMP_DEBUG
5236+
#if KMP_OS_WINDOWS || KMP_OS_AIX || KMP_DEBUG
51285237
kmp_info_t *th;
51295238
#endif
51305239
if (!KMP_AFFINITY_CAPABLE()) {
51315240
return -1;
51325241
}
51335242

51345243
gtid = __kmp_entry_gtid();
5135-
#if KMP_OS_WINDOWS || KMP_DEBUG
5244+
#if KMP_OS_WINDOWS || KMP_OS_AIX || KMP_DEBUG
51365245
th = __kmp_threads[gtid];
51375246
#else
51385247
(void)gtid; // unused variable
@@ -5155,7 +5264,7 @@ int __kmp_aux_get_affinity(void **mask) {
51555264
}
51565265
}
51575266

5158-
#if !KMP_OS_WINDOWS
5267+
#if !KMP_OS_WINDOWS && !KMP_OS_AIX
51595268

51605269
retval = __kmp_get_system_affinity((kmp_affin_mask_t *)(*mask), FALSE);
51615270
KA_TRACE(
@@ -5175,7 +5284,7 @@ int __kmp_aux_get_affinity(void **mask) {
51755284
KMP_CPU_COPY((kmp_affin_mask_t *)(*mask), th->th.th_affin_mask);
51765285
return 0;
51775286

5178-
#endif /* KMP_OS_WINDOWS */
5287+
#endif /* !KMP_OS_WINDOWS && !KMP_OS_AIX */
51795288
}
51805289

51815290
int __kmp_aux_get_affinity_max_proc() {
@@ -5557,7 +5666,7 @@ void __kmp_balanced_affinity(kmp_info_t *th, int nthreads) {
55575666
}
55585667
}
55595668

5560-
#if KMP_OS_LINUX || KMP_OS_FREEBSD
5669+
#if KMP_OS_LINUX || KMP_OS_FREEBSD || KMP_OS_AIX
55615670
// We don't need this entry for Windows because
55625671
// there is GetProcessAffinityMask() api
55635672
//
@@ -5592,7 +5701,11 @@ extern "C"
55925701
"set full mask for thread %d\n",
55935702
gtid));
55945703
KMP_DEBUG_ASSERT(__kmp_affin_fullMask != NULL);
5704+
#if KMP_OS_AIX
5705+
return bindprocessor(BINDTHREAD, thread_self(), PROCESSOR_CLASS_ANY);
5706+
#else
55955707
return __kmp_set_system_affinity(__kmp_affin_fullMask, FALSE);
5708+
#endif
55965709
}
55975710
#endif
55985711

openmp/runtime/src/kmp_affinity.h

+71-2
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ class KMPHwlocAffinity : public KMPAffinity {
191191
};
192192
#endif /* KMP_USE_HWLOC */
193193

194-
#if KMP_OS_LINUX || KMP_OS_FREEBSD
194+
#if KMP_OS_LINUX || KMP_OS_FREEBSD || KMP_OS_AIX
195195
#if KMP_OS_LINUX
196196
/* On some of the older OS's that we build on, these constants aren't present
197197
in <asm/unistd.h> #included from <sys.syscall.h>. They must be the same on
@@ -314,6 +314,10 @@ class KMPHwlocAffinity : public KMPAffinity {
314314
#elif KMP_OS_FREEBSD
315315
#include <pthread.h>
316316
#include <pthread_np.h>
317+
#elif KMP_OS_AIX
318+
#include <sys/dr.h>
319+
#include <sys/rset.h>
320+
#define VMI_MAXRADS 64 // Maximum number of RADs allowed by AIX.
317321
#endif
318322
class KMPNativeAffinity : public KMPAffinity {
319323
class Mask : public KMPAffinity::Mask {
@@ -401,6 +405,70 @@ class KMPNativeAffinity : public KMPAffinity {
401405
++retval;
402406
return retval;
403407
}
408+
#if KMP_OS_AIX
409+
// On AIX, we don't have a way to get CPU(s) a thread is bound to.
410+
// This routine is only used to get the full mask.
411+
int get_system_affinity(bool abort_on_error) override {
412+
KMP_ASSERT2(KMP_AFFINITY_CAPABLE(),
413+
"Illegal get affinity operation when not capable");
414+
415+
(void)abort_on_error;
416+
417+
// Set the mask with all CPUs that are available.
418+
for (int i = 0; i < __kmp_xproc; ++i)
419+
KMP_CPU_SET(i, this);
420+
return 0;
421+
}
422+
int set_system_affinity(bool abort_on_error) const override {
423+
KMP_ASSERT2(KMP_AFFINITY_CAPABLE(),
424+
425+
"Illegal set affinity operation when not capable");
426+
427+
int location;
428+
int gtid = __kmp_entry_gtid();
429+
int tid = thread_self();
430+
431+
// Unbind the thread if it was bound to any processors before so that
432+
// we can bind the thread to CPUs specified by the mask not others.
433+
int retval = bindprocessor(BINDTHREAD, tid, PROCESSOR_CLASS_ANY);
434+
435+
// On AIX, we can only bind to one instead of a set of CPUs with the
436+
// bindprocessor() system call.
437+
KMP_CPU_SET_ITERATE(location, this) {
438+
if (KMP_CPU_ISSET(location, this)) {
439+
retval = bindprocessor(BINDTHREAD, tid, location);
440+
if (retval == -1 && errno == 1) {
441+
rsid_t rsid;
442+
rsethandle_t rsh;
443+
// Put something in rsh to prevent compiler warning
444+
// about uninitalized use
445+
rsh = rs_alloc(RS_EMPTY);
446+
rsid.at_pid = getpid();
447+
if (RS_DEFAULT_RSET != ra_getrset(R_PROCESS, rsid, 0, rsh)) {
448+
retval = ra_detachrset(R_PROCESS, rsid, 0);
449+
retval = bindprocessor(BINDTHREAD, tid, location);
450+
}
451+
}
452+
if (retval == 0) {
453+
KA_TRACE(10, ("__kmp_set_system_affinity: Done binding "
454+
"T#%d to cpu=%d.\n",
455+
gtid, location));
456+
continue;
457+
}
458+
int error = errno;
459+
if (abort_on_error) {
460+
__kmp_fatal(KMP_MSG(FunctionError, "bindprocessor()"),
461+
KMP_ERR(error), __kmp_msg_null);
462+
KA_TRACE(10, ("__kmp_set_system_affinity: Error binding "
463+
"T#%d to cpu=%d, errno=%d.\n",
464+
gtid, location, error));
465+
return error;
466+
}
467+
}
468+
}
469+
return 0;
470+
}
471+
#else // !KMP_OS_AIX
404472
int get_system_affinity(bool abort_on_error) override {
405473
KMP_ASSERT2(KMP_AFFINITY_CAPABLE(),
406474
"Illegal get affinity operation when not capable");
@@ -443,6 +511,7 @@ class KMPNativeAffinity : public KMPAffinity {
443511
}
444512
return error;
445513
}
514+
#endif // KMP_OS_AIX
446515
};
447516
void determine_capable(const char *env_var) override {
448517
__kmp_affinity_determine_capable(env_var);
@@ -471,7 +540,7 @@ class KMPNativeAffinity : public KMPAffinity {
471540
}
472541
api_type get_api_type() const override { return NATIVE_OS; }
473542
};
474-
#endif /* KMP_OS_LINUX || KMP_OS_FREEBSD */
543+
#endif /* KMP_OS_LINUX || KMP_OS_FREEBSD || KMP_OS_AIX */
475544

476545
#if KMP_OS_WINDOWS
477546
class KMPNativeAffinity : public KMPAffinity {

openmp/runtime/src/kmp_os.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@
7575
#error Unknown compiler
7676
#endif
7777

78-
#if (KMP_OS_LINUX || KMP_OS_WINDOWS || KMP_OS_FREEBSD) && !KMP_OS_WASI
78+
#if (KMP_OS_LINUX || KMP_OS_WINDOWS || KMP_OS_FREEBSD || KMP_OS_AIX) && \
79+
!KMP_OS_WASI
7980
#define KMP_AFFINITY_SUPPORTED 1
8081
#if KMP_OS_WINDOWS && KMP_ARCH_X86_64
8182
#define KMP_GROUP_AFFINITY 1

0 commit comments

Comments
 (0)