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