| 1 | // RUN: %libomp-compile -D_GNU_SOURCE |
| 2 | // RUN: env OMP_PLACES=threads %libomp-run |
| 3 | // RUN: env OMP_PLACES=cores %libomp-run |
| 4 | // RUN: env OMP_PLACES=sockets %libomp-run |
| 5 | // RUN: env OMP_PLACES=cores RUN_OUT_OF_ORDER=1 %libomp-run |
| 6 | // REQUIRES: linux |
| 7 | |
| 8 | #include <stdio.h> |
| 9 | #include <stdlib.h> |
| 10 | #include <string.h> |
| 11 | #include "libomp_test_affinity.h" |
| 12 | #include "libomp_test_topology.h" |
| 13 | |
| 14 | // Check openmp place list to make sure it follow KMP_HW_SUBSET restriction |
| 15 | static int compare_hw_subset_places(const place_list_t *openmp_places, |
| 16 | topology_obj_type_t type, int nsockets, |
| 17 | int ncores_per_socket, |
| 18 | int nthreads_per_core) { |
| 19 | int i, j, expected_total, expected_per_place; |
| 20 | if (type == TOPOLOGY_OBJ_THREAD) { |
| 21 | expected_total = nsockets * ncores_per_socket * nthreads_per_core; |
| 22 | expected_per_place = 1; |
| 23 | } else if (type == TOPOLOGY_OBJ_CORE) { |
| 24 | expected_total = nsockets * ncores_per_socket; |
| 25 | expected_per_place = nthreads_per_core; |
| 26 | } else { |
| 27 | expected_total = nsockets; |
| 28 | expected_per_place = ncores_per_socket * nthreads_per_core; |
| 29 | } |
| 30 | if (openmp_places->num_places != expected_total) { |
| 31 | fprintf(stderr, format: "error: KMP_HW_SUBSET did not half each resource layer!\n" ); |
| 32 | printf(format: "openmp_places places:\n" ); |
| 33 | topology_print_places(p: openmp_places); |
| 34 | printf(format: "\n" ); |
| 35 | return EXIT_FAILURE; |
| 36 | } |
| 37 | for (i = 0; i < openmp_places->num_places; ++i) { |
| 38 | int count = affinity_mask_count(mask: openmp_places->masks[i]); |
| 39 | if (count != expected_per_place) { |
| 40 | fprintf(stderr, format: "error: place %d has %d OS procs instead of %d\n" , i, |
| 41 | count, expected_per_place); |
| 42 | return EXIT_FAILURE; |
| 43 | } |
| 44 | } |
| 45 | return EXIT_SUCCESS; |
| 46 | } |
| 47 | |
| 48 | static int check_places() { |
| 49 | char buf[100]; |
| 50 | topology_obj_type_t type; |
| 51 | const char *value; |
| 52 | int status = EXIT_SUCCESS; |
| 53 | place_list_t *threads, *cores, *sockets, *openmp_places; |
| 54 | threads = topology_alloc_type_places(type: TOPOLOGY_OBJ_THREAD); |
| 55 | cores = topology_alloc_type_places(type: TOPOLOGY_OBJ_CORE); |
| 56 | sockets = topology_alloc_type_places(type: TOPOLOGY_OBJ_SOCKET); |
| 57 | |
| 58 | if (threads->num_places <= 1) { |
| 59 | printf(format: "Only one hardware thread to execute on. Skipping test.\n" ); |
| 60 | return status; |
| 61 | } |
| 62 | |
| 63 | value = getenv(name: "OMP_PLACES" ); |
| 64 | if (!value) { |
| 65 | fprintf(stderr, |
| 66 | format: "error: OMP_PLACES must be set to one of threads,cores,sockets!\n" ); |
| 67 | return EXIT_FAILURE; |
| 68 | } |
| 69 | if (strcmp(s1: value, s2: "threads" ) == 0) |
| 70 | type = TOPOLOGY_OBJ_THREAD; |
| 71 | else if (strcmp(s1: value, s2: "cores" ) == 0) |
| 72 | type = TOPOLOGY_OBJ_CORE; |
| 73 | else if (strcmp(s1: value, s2: "sockets" ) == 0) |
| 74 | type = TOPOLOGY_OBJ_SOCKET; |
| 75 | else { |
| 76 | fprintf(stderr, |
| 77 | format: "error: OMP_PLACES must be one of threads,cores,sockets!\n" ); |
| 78 | return EXIT_FAILURE; |
| 79 | } |
| 80 | |
| 81 | // Calculate of num threads per core, num cores per socket, & num sockets |
| 82 | if (cores->num_places <= 0) { |
| 83 | printf(format: "Invalid number of cores (%d). Skipping test.\n" , cores->num_places); |
| 84 | return status; |
| 85 | } else if (sockets->num_places <= 0) { |
| 86 | printf(format: "Invalid number of sockets (%d). Skipping test.\n" , |
| 87 | cores->num_places); |
| 88 | return status; |
| 89 | } |
| 90 | int nthreads_per_core = threads->num_places / cores->num_places; |
| 91 | int ncores_per_socket = cores->num_places / sockets->num_places; |
| 92 | int nsockets = sockets->num_places; |
| 93 | |
| 94 | if (nsockets * ncores_per_socket * nthreads_per_core != threads->num_places) { |
| 95 | printf(format: "Only uniform topologies can be tested. Skipping test.\n" ); |
| 96 | return status; |
| 97 | } |
| 98 | |
| 99 | // Use half the resources of every level |
| 100 | if (nthreads_per_core > 1) |
| 101 | nthreads_per_core /= 2; |
| 102 | if (ncores_per_socket > 1) |
| 103 | ncores_per_socket /= 2; |
| 104 | if (nsockets > 1) |
| 105 | nsockets /= 2; |
| 106 | |
| 107 | if (getenv(name: "RUN_OUT_OF_ORDER" )) { |
| 108 | snprintf(s: buf, maxlen: sizeof(buf), format: "%dt,%ds,%dc" , nthreads_per_core, nsockets, |
| 109 | ncores_per_socket); |
| 110 | } else { |
| 111 | snprintf(s: buf, maxlen: sizeof(buf), format: "%ds,%dc,%dt" , nsockets, ncores_per_socket, |
| 112 | nthreads_per_core); |
| 113 | } |
| 114 | setenv(name: "KMP_HW_SUBSET" , value: buf, replace: 1); |
| 115 | |
| 116 | openmp_places = topology_alloc_openmp_places(); |
| 117 | status = compare_hw_subset_places(openmp_places, type, nsockets, |
| 118 | ncores_per_socket, nthreads_per_core); |
| 119 | topology_free_places(places: threads); |
| 120 | topology_free_places(places: cores); |
| 121 | topology_free_places(places: sockets); |
| 122 | topology_free_places(places: openmp_places); |
| 123 | return status; |
| 124 | } |
| 125 | |
| 126 | int main() { |
| 127 | if (!topology_using_full_mask()) { |
| 128 | printf(format: "Thread does not have access to all logical processors. Skipping " |
| 129 | "test.\n" ); |
| 130 | return EXIT_SUCCESS; |
| 131 | } |
| 132 | return check_places(); |
| 133 | } |
| 134 | |