1 #ifndef LIBOMP_TEST_TOPOLOGY_H
2 #define LIBOMP_TEST_TOPOLOGY_H
3 
4 #include "libomp_test_affinity.h"
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <dirent.h>
8 #include <errno.h>
9 #include <ctype.h>
10 #include <omp.h>
11 
12 typedef enum topology_obj_type_t {
13   TOPOLOGY_OBJ_THREAD,
14   TOPOLOGY_OBJ_CORE,
15   TOPOLOGY_OBJ_SOCKET,
16   TOPOLOGY_OBJ_MAX
17 } topology_obj_type_t;
18 
19 typedef struct place_list_t {
20   int num_places;
21   affinity_mask_t **masks;
22 } place_list_t;
23 
24 // Return the first character in file 'f' that is not a whitespace character
25 // including newlines and carriage returns
get_first_nonspace_from_file(FILE * f)26 static int get_first_nonspace_from_file(FILE *f) {
27   int c;
28   do {
29     c = fgetc(f);
30   } while (c != EOF && (isspace(c) || c == '\n' || c == '\r'));
31   return c;
32 }
33 
34 // Read an integer from file 'f' into 'number'
35 // Return 1 on successful read of integer,
36 //        0 on unsuccessful read of integer,
37 //        EOF on end of file.
get_integer_from_file(FILE * f,int * number)38 static int get_integer_from_file(FILE *f, int *number) {
39   int n;
40   n = fscanf(f, "%d", number);
41   if (feof(f))
42     return EOF;
43   if (n != 1)
44     return 0;
45   return 1;
46 }
47 
48 // Read a siblings list file from Linux /sys/devices/system/cpu/cpu?/topology/*
topology_get_mask_from_file(const char * filename)49 static affinity_mask_t *topology_get_mask_from_file(const char *filename) {
50   int status = EXIT_SUCCESS;
51   FILE *f = fopen(filename, "r");
52   if (!f) {
53     perror(filename);
54     exit(EXIT_FAILURE);
55   }
56   affinity_mask_t *mask = affinity_mask_alloc();
57   while (1) {
58     int c, i, n, lower, upper;
59     // Read the first integer
60     n = get_integer_from_file(f, &lower);
61     if (n == EOF) {
62       break;
63     } else if (n == 0) {
64       fprintf(stderr, "syntax error: expected integer\n");
65       status = EXIT_FAILURE;
66       break;
67     }
68 
69     // Now either a , or -
70     c = get_first_nonspace_from_file(f);
71     if (c == EOF || c == ',') {
72       affinity_mask_set(mask, lower);
73       if (c == EOF)
74         break;
75     } else if (c == '-') {
76       n = get_integer_from_file(f, &upper);
77       if (n == EOF || n == 0) {
78         fprintf(stderr, "syntax error: expected integer\n");
79         status = EXIT_FAILURE;
80         break;
81       }
82       for (i = lower; i <= upper; ++i)
83         affinity_mask_set(mask, i);
84       c = get_first_nonspace_from_file(f);
85       if (c == EOF) {
86         break;
87       } else if (c == ',') {
88         continue;
89       } else {
90         fprintf(stderr, "syntax error: unexpected character: '%c (%d)'\n", c,
91                 c);
92         status = EXIT_FAILURE;
93         break;
94       }
95     } else {
96       fprintf(stderr, "syntax error: unexpected character: '%c (%d)'\n", c, c);
97       status = EXIT_FAILURE;
98       break;
99     }
100   }
101   fclose(f);
102   if (status == EXIT_FAILURE) {
103     affinity_mask_free(mask);
104     mask = NULL;
105   }
106   return mask;
107 }
108 
topology_get_num_cpus()109 static int topology_get_num_cpus() {
110   char buf[1024];
111   // Count the number of cpus
112   int cpu = 0;
113   while (1) {
114     snprintf(buf, sizeof(buf), "/sys/devices/system/cpu/cpu%d", cpu);
115     DIR *dir = opendir(buf);
116     if (dir) {
117       closedir(dir);
118       cpu++;
119     } else {
120       break;
121     }
122   }
123   if (cpu == 0)
124     cpu = 1;
125   return cpu;
126 }
127 
128 // Return whether the current thread has access to all logical processors
topology_using_full_mask()129 static int topology_using_full_mask() {
130   int cpu;
131   int has_all = 1;
132   int num_cpus = topology_get_num_cpus();
133   affinity_mask_t *mask = affinity_mask_alloc();
134   get_thread_affinity(mask);
135   for (cpu = 0; cpu < num_cpus; ++cpu) {
136     if (!affinity_mask_isset(mask, cpu)) {
137       has_all = 0;
138       break;
139     }
140   }
141   affinity_mask_free(mask);
142   return has_all;
143 }
144 
145 // Return array of masks representing OMP_PLACES keyword (e.g., sockets, cores,
146 // threads)
topology_alloc_type_places(topology_obj_type_t type)147 static place_list_t *topology_alloc_type_places(topology_obj_type_t type) {
148   char buf[1024];
149   int i, cpu, num_places, num_unique;
150   int num_cpus = topology_get_num_cpus();
151   place_list_t *places = (place_list_t *)malloc(sizeof(place_list_t));
152   affinity_mask_t **masks =
153       (affinity_mask_t **)malloc(sizeof(affinity_mask_t *) * num_cpus);
154   num_unique = 0;
155   for (cpu = 0; cpu < num_cpus; ++cpu) {
156     affinity_mask_t *mask;
157     if (type == TOPOLOGY_OBJ_CORE) {
158       snprintf(buf, sizeof(buf),
159                "/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list",
160                cpu);
161       mask = topology_get_mask_from_file(buf);
162     } else if (type == TOPOLOGY_OBJ_SOCKET) {
163       snprintf(buf, sizeof(buf),
164                "/sys/devices/system/cpu/cpu%d/topology/core_siblings_list",
165                cpu);
166       mask = topology_get_mask_from_file(buf);
167     } else if (type == TOPOLOGY_OBJ_THREAD) {
168       mask = affinity_mask_alloc();
169       affinity_mask_set(mask, cpu);
170     } else {
171       fprintf(stderr, "Unknown topology type (%d)\n", (int)type);
172       exit(EXIT_FAILURE);
173     }
174     // Check for unique topology objects above the thread level
175     if (type != TOPOLOGY_OBJ_THREAD) {
176       for (i = 0; i < num_unique; ++i) {
177         if (affinity_mask_equal(masks[i], mask)) {
178           affinity_mask_free(mask);
179           mask = NULL;
180           break;
181         }
182       }
183     }
184     if (mask)
185       masks[num_unique++] = mask;
186   }
187   places->num_places = num_unique;
188   places->masks = masks;
189   return places;
190 }
191 
topology_alloc_openmp_places()192 static place_list_t *topology_alloc_openmp_places() {
193   int place, i;
194   int num_places = omp_get_num_places();
195   place_list_t *places = (place_list_t *)malloc(sizeof(place_list_t));
196   affinity_mask_t **masks =
197       (affinity_mask_t **)malloc(sizeof(affinity_mask_t *) * num_places);
198   for (place = 0; place < num_places; ++place) {
199     int num_procs = omp_get_place_num_procs(place);
200     int *ids = (int *)malloc(sizeof(int) * num_procs);
201     omp_get_place_proc_ids(place, ids);
202     affinity_mask_t *mask = affinity_mask_alloc();
203     for (i = 0; i < num_procs; ++i)
204       affinity_mask_set(mask, ids[i]);
205     masks[place] = mask;
206   }
207   places->num_places = num_places;
208   places->masks = masks;
209   return places;
210 }
211 
212 // Free the array of masks from one of: topology_alloc_type_masks()
213 // or topology_alloc_openmp_masks()
topology_free_places(place_list_t * places)214 static void topology_free_places(place_list_t *places) {
215   int i;
216   for (i = 0; i < places->num_places; ++i)
217     affinity_mask_free(places->masks[i]);
218   free(places->masks);
219   free(places);
220 }
221 
topology_print_places(const place_list_t * p)222 static void topology_print_places(const place_list_t *p) {
223   int i;
224   char buf[1024];
225   for (i = 0; i < p->num_places; ++i) {
226     affinity_mask_snprintf(buf, sizeof(buf), p->masks[i]);
227     printf("Place %d: %s\n", i, buf);
228   }
229 }
230 
231 #endif
232