1 /*
2 * Copyright © 2011-2018 Inria. All rights reserved.
3 * See COPYING in top-level directory.
4 */
5
6 #include "hwloc.h"
7 #include "hwloc/shmem.h"
8 #include "private/misc.h" /* for hwloc_getpagesize() */
9
10 #include <stdlib.h>
11 #include <fcntl.h>
12 #include <stdio.h>
13 #ifdef HAVE_UNISTD_H
14 #include <unistd.h>
15 #endif
16 #include <sys/mman.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <sys/wait.h>
20 #include <string.h>
21 #include <stdint.h>
22 #include <assert.h>
23
24 #define EXIT_SKIP 77
25
26 /* must match the header defined in hwloc/shmem.c */
27 struct header {
28 uint32_t header_version;
29 uint32_t header_length;
30 uint64_t mmap_address;
31 uint64_t mmap_length;
32 };
33
adopt(int fd,unsigned long fileoffset,unsigned long mmap_address,unsigned long mmap_length,int synthetic_with_distances)34 static int adopt(int fd, unsigned long fileoffset, unsigned long mmap_address, unsigned long mmap_length, int synthetic_with_distances)
35 {
36 static hwloc_topology_t adopted;
37 char *xmlbuf;
38 int xmlbuflen;
39 char *origxmlbuf;
40 struct hwloc_distances_s *distances;
41 unsigned nr = 1;
42 int err;
43 int ret = EXIT_SKIP;
44
45 err = lseek(fd, 0, SEEK_SET);
46 assert(!err);
47
48 printf(" reading XML dump\n");
49 origxmlbuf = malloc(fileoffset);
50 assert(origxmlbuf);
51 err = read(fd, origxmlbuf, fileoffset);
52 assert(err > 0);
53
54 printf(" adopting from file at offset %lu with address 0x%lx len %lu\n", fileoffset, mmap_address, mmap_length);
55
56 err = hwloc_shmem_topology_adopt(&adopted, fd, fileoffset, (void*)(uintptr_t)mmap_address, mmap_length, 0);
57 if (err == -1 && errno == EBUSY) {
58 /* may fail on 32bits and on some OS (e.g. darwin from time to time), and even on Linux/64bits if unlucky */
59 fprintf(stderr, "Failed to shmem adopt, requested mapping is busy\n");
60 goto out_with_origxmlbuf;
61 }
62 assert(!err);
63 printf(" adopted OK\n");
64
65 err = hwloc_distances_get_by_type(adopted, HWLOC_OBJ_NUMANODE, &nr, &distances, 0, 0);
66 assert(!err);
67 if (synthetic_with_distances) {
68 assert(nr == 1);
69 assert(distances->nbobjs == 3);
70 assert(distances->kind == (HWLOC_DISTANCES_KIND_MEANS_LATENCY|HWLOC_DISTANCES_KIND_FROM_USER));
71 hwloc_distances_release(adopted, distances);
72 printf(" distances OK\n");
73 }
74
75 err = hwloc_topology_export_xmlbuffer(adopted, &xmlbuf, &xmlbuflen, 0);
76 assert(!err);
77 printf(" XML export %d bytes\n", xmlbuflen);
78 assert((unsigned long) xmlbuflen < fileoffset);
79 assert(!memcmp(origxmlbuf, xmlbuf, xmlbuflen));
80 hwloc_free_xmlbuffer(adopted, xmlbuf);
81 printf(" XML export is identical to original\n");
82
83 hwloc_topology_destroy(adopted);
84 printf(" destroyed\n");
85
86 ret = EXIT_SUCCESS;
87
88 out_with_origxmlbuf:
89 free(origxmlbuf);
90 return ret;
91 }
92
93 static unsigned long
find_mmap_addr(unsigned long length)94 find_mmap_addr(unsigned long length)
95 {
96 unsigned long addr;
97 void *tmp_mmap;
98 int err;
99
100 /* try to find a good address starting from something in the middle of the entire/full address space */
101 #if SIZEOF_VOID_P == 8
102 addr = 0x8000000000000000UL;
103 #else
104 addr = 0x80000000UL;
105 #endif
106 printf("testing mmaps to find room for length %lu\n", length);
107
108 again:
109 tmp_mmap = mmap((void*)(uintptr_t)addr, length, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_SHARED, -1, 0);
110 if (tmp_mmap != MAP_FAILED) {
111 err = munmap((void*)(uintptr_t)tmp_mmap, length);
112 assert(!err);
113 if (tmp_mmap == (void*)(uintptr_t) addr) {
114 /* worked! */
115 printf(" test mmap at 0x%lx succeeded, let's use that!\n", addr);
116 return addr;
117 }
118 printf(" test mmap at 0x%lx returned another address\n", addr);
119 } else
120 printf(" test mmap at 0x%lx failed (errno %d)\n", addr, errno);
121 /* couldn't map there, try again with a smaller address */
122 addr >>= 3;
123 if (addr)
124 goto again;
125
126 return 0;
127 }
128
test(hwloc_topology_t orig,const char * callname)129 static int test(hwloc_topology_t orig, const char *callname) {
130 unsigned long forced_addr;
131 unsigned long fileoffset;
132 size_t shmem_length;
133 int synthetic_with_distances = (hwloc_obj_get_info_by_name(hwloc_get_root_obj(orig), "SyntheticDescription") != NULL);
134 char tmpname[] = "/tmp/hwloc_test_shmem.XXXXXX";
135 char cmd[512];
136 struct stat st;
137 int fd, err;
138 int ret = EXIT_SKIP;
139
140 printf("opening temporary file\n");
141 fd = mkstemp(tmpname);
142 if (fd < 0) {
143 perror("mkstemp");
144 goto out;
145 }
146 printf("opened %s\n", tmpname);
147
148 printf("exporting XML\n");
149 err = hwloc_topology_export_xml(orig, tmpname, 0);
150 assert(!err);
151 err = stat(tmpname, &st);
152 assert(!err);
153 printf("exported %lu bytes\n", (unsigned long) st.st_size);
154 fileoffset = st.st_size+1; /* skip a couple bytes to make sure the XML is don" */
155 fileoffset = (fileoffset + hwloc_getpagesize() - 1) &~(hwloc_getpagesize() - 1);
156 printf("will mmap at file offset %lu\n", fileoffset);
157
158 err = hwloc_shmem_topology_get_length(orig, &shmem_length, 0);
159 assert(!err);
160 printf("need mmap length %lu\n", (unsigned long) shmem_length);
161
162 forced_addr = find_mmap_addr((unsigned long) shmem_length);
163 if (!forced_addr)
164 goto out_with_fd;
165
166 printf("write to shmem at address 0x%lx in file %s offset %lu\n", forced_addr, tmpname, fileoffset);
167 err = hwloc_shmem_topology_write(orig, fd, fileoffset, (void*)(uintptr_t)forced_addr, shmem_length, 0);
168 if (err == -1 && errno == EBUSY) {
169 fprintf(stderr, "Failed to shmem write, requested mapping is busy\n");
170 goto out_with_fd;
171 }
172 assert(!err);
173 printf("wrote length %lu\n", (unsigned long) shmem_length);
174
175 printf("adopting locally\n");
176 ret = adopt(fd, fileoffset, forced_addr, shmem_length, synthetic_with_distances);
177 assert(ret == EXIT_SUCCESS || ret == EXIT_SKIP);
178
179 printf("adopting in other child process\n");
180 snprintf(cmd, sizeof(cmd), "%s %s %lu 0x%lx %lu %d", callname, tmpname, fileoffset, forced_addr, (unsigned long) shmem_length, synthetic_with_distances);
181 printf("running command %s\n", cmd);
182 err = system(cmd);
183 assert(WIFEXITED(err));
184 printf("child process returned %d\n", WEXITSTATUS(err));
185 assert(WEXITSTATUS(err) == EXIT_SUCCESS || WEXITSTATUS(err) == EXIT_SKIP);
186
187 /* we caught errors above.
188 * return SKIP if both returned SKIP. otherwise SUCCESS
189 */
190 if (WEXITSTATUS(err) == EXIT_SKIP && ret == EXIT_SKIP)
191 ret = EXIT_SKIP;
192 else
193 ret = EXIT_SUCCESS;
194
195 out_with_fd:
196 close(fd);
197 unlink(tmpname);
198 out:
199 return ret;
200 }
201
main(int argc,char * argv[])202 int main(int argc, char *argv[])
203 {
204 static hwloc_topology_t orig;
205 hwloc_obj_t nodes[3];
206 uint64_t node_distances[9];
207 unsigned i,j;
208 int err, ret, ret2;
209
210 if (argc > 1) {
211 int fd;
212 unsigned long forced_addr;
213 unsigned long fileoffset;
214 size_t shmem_length;
215 int synthetic_with_distances;
216
217 if (argc < 6) {
218 printf("needs 5 arguments\n");
219 return EXIT_FAILURE;
220 }
221
222 printf(" opening %s\n", argv[1]);
223 fd = open(argv[1], O_RDONLY);
224 if (fd < 0) {
225 perror("open");
226 return EXIT_FAILURE;
227 }
228
229 fileoffset = strtoul(argv[2], NULL, 0);
230 forced_addr = strtoul(argv[3], NULL, 0);
231 shmem_length = strtoul(argv[4], NULL, 0);
232 synthetic_with_distances = atoi(argv[5]);
233
234 ret = adopt(fd, fileoffset, forced_addr, shmem_length, synthetic_with_distances);
235 close(fd);
236 exit(ret);
237 }
238
239 printf("########################\n");
240 printf("creating native topology\n");
241 err = hwloc_topology_init(&orig);
242 assert(!err);
243 err = hwloc_topology_set_all_types_filter(orig, HWLOC_TYPE_FILTER_KEEP_ALL);
244 assert(!err);
245 err = hwloc_topology_load(orig);
246 assert(!err);
247
248 ret = test(orig, argv[0]);
249
250 printf("destroying original\n");
251 hwloc_topology_destroy(orig);
252
253 printf("###############################################\n");
254 printf("creating synthetic topo with distances topology\n");
255 err = hwloc_topology_init(&orig);
256 assert(!err);
257 err = hwloc_topology_set_synthetic(orig, "node:3 core:2 pu:4");
258 assert(!err);
259 err = hwloc_topology_load(orig);
260 assert(!err);
261
262 printf("adding distance matrix\n");
263 for(i=0; i<3; i++) {
264 nodes[i] = hwloc_get_obj_by_type(orig, HWLOC_OBJ_NUMANODE, i);
265 for(j=0; j<3; j++)
266 node_distances[i*3+j] = (i == j ? 10 : 20);
267 }
268 err = hwloc_distances_add(orig, 3, nodes, node_distances,
269 HWLOC_DISTANCES_KIND_MEANS_LATENCY|HWLOC_DISTANCES_KIND_FROM_USER,
270 HWLOC_DISTANCES_ADD_FLAG_GROUP);
271 assert(!err);
272
273 ret2 = test(orig, argv[0]);
274
275 printf("destroying original\n");
276 hwloc_topology_destroy(orig);
277
278 /* we caught errors above.
279 * return SKIP if both returned SKIP. otherwise SUCCESS
280 */
281 if (ret == EXIT_SKIP && ret2 == EXIT_SKIP)
282 ret = EXIT_SKIP;
283 else
284 ret = EXIT_SUCCESS;
285
286 return ret;
287 }
288