Skip to content

Commit 618bcd7

Browse files
xingxue-ibmchencha3
authored andcommitted
[OpenMP][AIX] Affinity implementation for AIX (llvm#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.
1 parent 7179756 commit 618bcd7

File tree

6 files changed

+233
-19
lines changed

6 files changed

+233
-19
lines changed

openmp/runtime/src/kmp.h

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

822+
#ifndef KMP_OS_AIX
822823
class kmp_affinity_raii_t {
823824
kmp_affin_mask_t *mask;
824825
bool restored;
@@ -843,6 +844,7 @@ class kmp_affinity_raii_t {
843844
}
844845
~kmp_affinity_raii_t() { restore(); }
845846
};
847+
#endif // !KMP_OS_AIX
846848

847849
// Declare local char buffers with this size for printing debug and info
848850
// messages, using __kmp_affinity_print_mask().
@@ -3910,7 +3912,8 @@ extern void __kmp_balanced_affinity(kmp_info_t *th, int team_size);
39103912
#if KMP_WEIGHTED_ITERATIONS_SUPPORTED
39113913
extern int __kmp_get_first_osid_with_ecore(void);
39123914
#endif
3913-
#if KMP_OS_LINUX || KMP_OS_FREEBSD || KMP_OS_NETBSD || KMP_OS_DRAGONFLY
3915+
#if KMP_OS_LINUX || KMP_OS_FREEBSD || KMP_OS_NETBSD || KMP_OS_DRAGONFLY || \
3916+
KMP_OS_AIX
39143917
extern int kmp_set_thread_affinity_mask_initial(void);
39153918
#endif
39163919
static inline void __kmp_assign_root_init_mask() {

openmp/runtime/src/kmp_affinity.cpp

+122-8
Original file line numberDiff line numberDiff line change
@@ -2910,12 +2910,17 @@ static inline const char *__kmp_cpuinfo_get_envvar() {
29102910
}
29112911

29122912
// Parse /proc/cpuinfo (or an alternate file in the same format) to obtain the
2913-
// affinity map.
2913+
// affinity map. On AIX, the map is obtained through system SRAD (Scheduler
2914+
// Resource Allocation Domain).
29142915
static bool __kmp_affinity_create_cpuinfo_map(int *line,
29152916
kmp_i18n_id_t *const msg_id) {
2917+
*msg_id = kmp_i18n_null;
2918+
2919+
#if KMP_OS_AIX
2920+
unsigned num_records = __kmp_xproc;
2921+
#else
29162922
const char *filename = __kmp_cpuinfo_get_filename();
29172923
const char *envvar = __kmp_cpuinfo_get_envvar();
2918-
*msg_id = kmp_i18n_null;
29192924

29202925
if (__kmp_affinity.flags.verbose) {
29212926
KMP_INFORM(AffParseFilename, "KMP_AFFINITY", filename);
@@ -2974,6 +2979,7 @@ static bool __kmp_affinity_create_cpuinfo_map(int *line,
29742979
*msg_id = kmp_i18n_str_CantRewindCpuinfo;
29752980
return false;
29762981
}
2982+
#endif // KMP_OS_AIX
29772983

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

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

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

4448-
#if KMP_OS_LINUX
4549+
#if KMP_OS_LINUX || KMP_OS_AIX
44494550
if (!success) {
44504551
int line = 0;
44514552
success = __kmp_affinity_create_cpuinfo_map(&line, &msg_id);
@@ -4841,7 +4942,12 @@ void __kmp_affinity_uninitialize(void) {
48414942
}
48424943
if (__kmp_affin_origMask != NULL) {
48434944
if (KMP_AFFINITY_CAPABLE()) {
4945+
#if KMP_OS_AIX
4946+
// Uninitialize by unbinding the thread.
4947+
bindprocessor(BINDTHREAD, thread_self(), PROCESSOR_CLASS_ANY);
4948+
#else
48444949
__kmp_set_system_affinity(__kmp_affin_origMask, FALSE);
4950+
#endif
48454951
}
48464952
KMP_CPU_FREE(__kmp_affin_origMask);
48474953
__kmp_affin_origMask = NULL;
@@ -5015,7 +5121,10 @@ void __kmp_affinity_bind_init_mask(int gtid) {
50155121
__kmp_set_system_affinity(th->th.th_affin_mask, FALSE);
50165122
} else
50175123
#endif
5124+
#ifndef KMP_OS_AIX
5125+
// Do not set the full mask as the init mask on AIX.
50185126
__kmp_set_system_affinity(th->th.th_affin_mask, TRUE);
5127+
#endif
50195128
}
50205129

50215130
void __kmp_affinity_bind_place(int gtid) {
@@ -5128,15 +5237,15 @@ int __kmp_aux_set_affinity(void **mask) {
51285237
int __kmp_aux_get_affinity(void **mask) {
51295238
int gtid;
51305239
int retval;
5131-
#if KMP_OS_WINDOWS || KMP_DEBUG
5240+
#if KMP_OS_WINDOWS || KMP_OS_AIX || KMP_DEBUG
51325241
kmp_info_t *th;
51335242
#endif
51345243
if (!KMP_AFFINITY_CAPABLE()) {
51355244
return -1;
51365245
}
51375246

51385247
gtid = __kmp_entry_gtid();
5139-
#if KMP_OS_WINDOWS || KMP_DEBUG
5248+
#if KMP_OS_WINDOWS || KMP_OS_AIX || KMP_DEBUG
51405249
th = __kmp_threads[gtid];
51415250
#else
51425251
(void)gtid; // unused variable
@@ -5159,7 +5268,7 @@ int __kmp_aux_get_affinity(void **mask) {
51595268
}
51605269
}
51615270

5162-
#if !KMP_OS_WINDOWS
5271+
#if !KMP_OS_WINDOWS && !KMP_OS_AIX
51635272

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

5182-
#endif /* KMP_OS_WINDOWS */
5291+
#endif /* !KMP_OS_WINDOWS && !KMP_OS_AIX */
51835292
}
51845293

51855294
int __kmp_aux_get_affinity_max_proc() {
@@ -5561,7 +5670,8 @@ void __kmp_balanced_affinity(kmp_info_t *th, int nthreads) {
55615670
}
55625671
}
55635672

5564-
#if KMP_OS_LINUX || KMP_OS_FREEBSD || KMP_OS_NETBSD || KMP_OS_DRAGONFLY
5673+
#if KMP_OS_LINUX || KMP_OS_FREEBSD || KMP_OS_NETBSD || KMP_OS_DRAGONFLY || \
5674+
KMP_OS_AIX
55655675
// We don't need this entry for Windows because
55665676
// there is GetProcessAffinityMask() api
55675677
//
@@ -5596,7 +5706,11 @@ extern "C"
55965706
"set full mask for thread %d\n",
55975707
gtid));
55985708
KMP_DEBUG_ASSERT(__kmp_affin_fullMask != NULL);
5709+
#if KMP_OS_AIX
5710+
return bindprocessor(BINDTHREAD, thread_self(), PROCESSOR_CLASS_ANY);
5711+
#else
55995712
return __kmp_set_system_affinity(__kmp_affin_fullMask, FALSE);
5713+
#endif
56005714
}
56015715
#endif
56025716

openmp/runtime/src/kmp_affinity.h

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

194-
#if KMP_OS_LINUX || KMP_OS_FREEBSD || KMP_OS_NETBSD || KMP_OS_DRAGONFLY
194+
#if KMP_OS_LINUX || KMP_OS_FREEBSD || KMP_OS_NETBSD || KMP_OS_DRAGONFLY || \
195+
KMP_OS_AIX
195196
#if KMP_OS_LINUX
196197
/* On some of the older OS's that we build on, these constants aren't present
197198
in <asm/unistd.h> #included from <sys.syscall.h>. They must be the same on
@@ -317,6 +318,10 @@ class KMPHwlocAffinity : public KMPAffinity {
317318
#elif KMP_OS_NETBSD
318319
#include <pthread.h>
319320
#include <sched.h>
321+
#elif KMP_OS_AIX
322+
#include <sys/dr.h>
323+
#include <sys/rset.h>
324+
#define VMI_MAXRADS 64 // Maximum number of RADs allowed by AIX.
320325
#endif
321326
class KMPNativeAffinity : public KMPAffinity {
322327
class Mask : public KMPAffinity::Mask {
@@ -404,6 +409,70 @@ class KMPNativeAffinity : public KMPAffinity {
404409
++retval;
405410
return retval;
406411
}
412+
#if KMP_OS_AIX
413+
// On AIX, we don't have a way to get CPU(s) a thread is bound to.
414+
// This routine is only used to get the full mask.
415+
int get_system_affinity(bool abort_on_error) override {
416+
KMP_ASSERT2(KMP_AFFINITY_CAPABLE(),
417+
"Illegal get affinity operation when not capable");
418+
419+
(void)abort_on_error;
420+
421+
// Set the mask with all CPUs that are available.
422+
for (int i = 0; i < __kmp_xproc; ++i)
423+
KMP_CPU_SET(i, this);
424+
return 0;
425+
}
426+
int set_system_affinity(bool abort_on_error) const override {
427+
KMP_ASSERT2(KMP_AFFINITY_CAPABLE(),
428+
429+
"Illegal set affinity operation when not capable");
430+
431+
int location;
432+
int gtid = __kmp_entry_gtid();
433+
int tid = thread_self();
434+
435+
// Unbind the thread if it was bound to any processors before so that
436+
// we can bind the thread to CPUs specified by the mask not others.
437+
int retval = bindprocessor(BINDTHREAD, tid, PROCESSOR_CLASS_ANY);
438+
439+
// On AIX, we can only bind to one instead of a set of CPUs with the
440+
// bindprocessor() system call.
441+
KMP_CPU_SET_ITERATE(location, this) {
442+
if (KMP_CPU_ISSET(location, this)) {
443+
retval = bindprocessor(BINDTHREAD, tid, location);
444+
if (retval == -1 && errno == 1) {
445+
rsid_t rsid;
446+
rsethandle_t rsh;
447+
// Put something in rsh to prevent compiler warning
448+
// about uninitalized use
449+
rsh = rs_alloc(RS_EMPTY);
450+
rsid.at_pid = getpid();
451+
if (RS_DEFAULT_RSET != ra_getrset(R_PROCESS, rsid, 0, rsh)) {
452+
retval = ra_detachrset(R_PROCESS, rsid, 0);
453+
retval = bindprocessor(BINDTHREAD, tid, location);
454+
}
455+
}
456+
if (retval == 0) {
457+
KA_TRACE(10, ("__kmp_set_system_affinity: Done binding "
458+
"T#%d to cpu=%d.\n",
459+
gtid, location));
460+
continue;
461+
}
462+
int error = errno;
463+
if (abort_on_error) {
464+
__kmp_fatal(KMP_MSG(FunctionError, "bindprocessor()"),
465+
KMP_ERR(error), __kmp_msg_null);
466+
KA_TRACE(10, ("__kmp_set_system_affinity: Error binding "
467+
"T#%d to cpu=%d, errno=%d.\n",
468+
gtid, location, error));
469+
return error;
470+
}
471+
}
472+
}
473+
return 0;
474+
}
475+
#else // !KMP_OS_AIX
407476
int get_system_affinity(bool abort_on_error) override {
408477
KMP_ASSERT2(KMP_AFFINITY_CAPABLE(),
409478
"Illegal get affinity operation when not capable");
@@ -446,6 +515,7 @@ class KMPNativeAffinity : public KMPAffinity {
446515
}
447516
return error;
448517
}
518+
#endif // KMP_OS_AIX
449519
};
450520
void determine_capable(const char *env_var) override {
451521
__kmp_affinity_determine_capable(env_var);
@@ -475,7 +545,7 @@ class KMPNativeAffinity : public KMPAffinity {
475545
api_type get_api_type() const override { return NATIVE_OS; }
476546
};
477547
#endif /* KMP_OS_LINUX || KMP_OS_FREEBSD || KMP_OS_NETBSD || KMP_OS_DRAGONFLY \
478-
*/
548+
|| KMP_OS_AIX */
479549

480550
#if KMP_OS_WINDOWS
481551
class KMPNativeAffinity : public KMPAffinity {

openmp/runtime/src/kmp_os.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@
7676
#endif
7777

7878
#if (KMP_OS_LINUX || KMP_OS_WINDOWS || KMP_OS_FREEBSD || KMP_OS_NETBSD || \
79-
KMP_OS_DRAGONFLY) && \
79+
KMP_OS_DRAGONFLY || KMP_OS_AIX) && \
8080
!KMP_OS_WASI
8181
#define KMP_AFFINITY_SUPPORTED 1
8282
#if KMP_OS_WINDOWS && KMP_ARCH_X86_64

0 commit comments

Comments
 (0)