1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1985-2013 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Eclipse Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.eclipse.org/org/documents/epl-v10.html *
11 * (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12 * *
13 * Information and Software Systems Research *
14 * AT&T Research *
15 * Florham Park NJ *
16 * *
17 * Glenn Fowler <glenn.s.fowler@gmail.com> *
18 * David Korn <dgkorn@gmail.com> *
19 * Phong Vo <phongvo@gmail.com> *
20 * *
21 ***********************************************************************/
22 #if defined(_UWIN) && defined(_BLD_ast)
23
_STUB_vmdcshare()24 void _STUB_vmdcshare(){}
25
26 #else
27
28 #include "vmhdr.h"
29 #include <sys/types.h>
30 #include <string.h>
31 #if _hdr_unistd
32 #include <unistd.h>
33 #endif
34
35 #include <sys/mman.h> /* mmap() headers */
36 #include <sys/file.h>
37 #include <sys/stat.h>
38 #include <fcntl.h>
39
40 #include <sys/shm.h> /* shm headers */
41 #include <sys/ipc.h>
42
43 /* Create a discipline to allocate based on mmap() or shmget().
44 ** Both ways can be used for allocating shared memory across processes.
45 ** However, mmap() also allows for allocating persistent memory.
46 **
47 ** Written by Kiem-Phong Vo, phongvo@gmail.com
48 */
49
50 /* magic word signaling region is being initialized */
51 #define MM_JUST4US ((unsigned int)(('P'<<24) | ('N'<<16) | ('B'<<8) | ('I')) ) /* 1347306057 */
52
53 /* magic word signaling file/segment is ready */
54 #define MM_MAGIC ((unsigned int)(('P'<<24) | ('&'<<16) | ('N'<<8) | ('8')) ) /* 1344687672 */
55
56 /* default mimimum region size */
57 #define MM_MINSIZE (64*_Vmpagesize)
58
59 /* flags for actions on region closing */
60 #define MM_DETACH 01 /* detach all attached memory */
61 #define MM_REMOVE 02 /* remove files/segments */
62
63 /* macros to get the data section and size */
64 #define MMHEAD(name) ROUND(sizeof(Mmvm_t)+strlen(name), MEM_ALIGN)
65 #define MMDATA(mmvm) ((Vmuchar_t*)(mmvm)->base + MMHEAD(mmvm->name))
66 #define MMSIZE(mmvm) ((mmvm)->size - MMHEAD(mmvm->name))
67
68 #ifdef S_IRUSR
69 #define FILE_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
70 #else
71 #define FILE_MODE 0644
72 #endif
73
74 typedef struct _mmvm_s
75 { unsigned int magic; /* magic bytes */
76 Void_t* base; /* address to map to */
77 ssize_t size; /* total data size */
78 ssize_t busy; /* amount in use */
79 key_t shmkey; /* shared segment's key */
80 int shmid; /* shared segment's ID */
81 int proj; /* project number */
82 char name[1];/* file or shm name */
83 } Mmvm_t;
84
85 typedef struct _mmdisc_s
86 { Vmdisc_t disc; /* Vmalloc discipline */
87 int init; /* initializing state */
88 int mode; /* closing modes */
89 Mmvm_t* mmvm; /* shared memory data */
90 ssize_t size; /* desired memory size */
91 key_t shmkey; /* shared segment's key */
92 int shmid; /* shared segment's ID */
93 int proj; /* shm project ID */
94 char name[1];/* backing store/strID */
95 } Mmdisc_t;
96
97 #if DEBUG
98 #include <stdio.h>
99 #include <string.h>
_vmmdump(Vmalloc_t * vm,int fd)100 int _vmmdump(Vmalloc_t* vm, int fd)
101 {
102 char mesg[1024];
103 Mmdisc_t *mmdc = (Mmdisc_t*)vm->disc;
104
105 fd = fd < 0 ? 2 : fd;
106 sprintf(mesg, "File: %s\n", mmdc->name ); write(fd, mesg, strlen(mesg));
107 sprintf(mesg, "Project: %10d\n", mmdc->proj); write(fd, mesg, strlen(mesg));
108 sprintf(mesg, "Memory: %#010lx\n", mmdc->mmvm); write(fd, mesg, strlen(mesg));
109 sprintf(mesg, "Size: %10d\n", mmdc->size); write(fd, mesg, strlen(mesg));
110 sprintf(mesg, "Shmid: %10d\n", mmdc->shmid); write(fd, mesg, strlen(mesg));
111
112 sprintf(mesg, "File header:\n"); write(fd, mesg, strlen(mesg));
113 sprintf(mesg, "Magic: %10d\n", mmdc->mmvm->magic); write(fd, mesg, strlen(mesg));
114 sprintf(mesg, "Base: %#010lx\n", mmdc->mmvm->base); write(fd, mesg, strlen(mesg));
115 sprintf(mesg, "Size: %10d\n", mmdc->mmvm->size); write(fd, mesg, strlen(mesg));
116 sprintf(mesg, "Busy: %10d\n", mmdc->mmvm->busy); write(fd, mesg, strlen(mesg));
117 return 0;
118 }
119 #endif /*DEBUG*/
120
121 /* make a key from a string and a project number -- this is like ftok() */
mmkey(char * str,int proj)122 static key_t mmkey(char* str, int proj)
123 {
124 unsigned int hash;
125 key_t key;
126
127 /* Fowler-Knoll-Vo hash function */
128 #if _ast_sizeof_int == 8 /* 64-bit hash */
129 #define FNV_PRIME ((1<<40) + (1<<8) + 0xb3)
130 #define FNV_OFFSET 14695981039346656037
131 #else /* 32-bit hash */
132 #define FNV_PRIME ((1<<24) + (1<<8) + 0x93)
133 #define FNV_OFFSET 2166136261
134 #endif
135 for(hash = FNV_OFFSET; *str; ++str)
136 hash = (hash ^ str[0]) * FNV_PRIME;
137 hash = (hash ^ proj) * FNV_PRIME; /* hash project number */
138
139 return (key = (key_t)hash) <= 0 ? -key : key;
140 }
141
142 /* fix the mapped address for a region */
mmfix(Mmvm_t * mmvm,Mmdisc_t * mmdc,int fd)143 static Mmvm_t* mmfix(Mmvm_t* mmvm, Mmdisc_t* mmdc, int fd)
144 {
145 Void_t *base = mmvm->base;
146 ssize_t size = mmvm->size;
147
148 if(base != (Void_t*)mmvm) /* mmvm is not right yet */
149 { /**/DEBUG_ASSERT(!base || (base && (VMLONG(base)%_Vmpagesize) == 0) );
150 if(mmdc->proj < 0)
151 { munmap((Void_t*)mmvm, size);
152 mmvm = (Mmvm_t*)mmap(base, size, (PROT_READ|PROT_WRITE), (MAP_FIXED|MAP_SHARED), fd, (off_t)0);
153 }
154 else
155 { shmdt((Void_t*)mmvm);
156 mmvm = (Mmvm_t*)shmat(mmdc->shmid, base, 0);
157 }
158 if(!mmvm || mmvm == (Mmvm_t*)(-1) )
159 mmvm = NIL(Mmvm_t*);
160 }
161
162 return mmvm;
163 }
164
165 /* initialize region data */
mminit(Mmdisc_t * mmdc)166 static int mminit(Mmdisc_t* mmdc)
167 {
168 Void_t *base;
169 int try;
170 int fd = -1;
171 ssize_t extent, size = 0;
172 Mmvm_t *mmvm = NIL(Mmvm_t*);
173 int rv = -1;
174
175 if(mmdc->mmvm) /* already done this */
176 return 0;
177
178 /* fixed size region so make it reasonably large */
179 if((size = mmdc->size) < MM_MINSIZE )
180 size = MM_MINSIZE;
181 size += MMHEAD(mmdc->name) + MEM_ALIGN;
182 size = ROUND(size, _Vmpagesize);
183
184 /* get/create the initial segment of data */
185 if(mmdc->proj < 0 ) /* proj < 0 means doing mmap() */
186 { /* this can be done in multiple processes */
187 if((fd = open(mmdc->name, O_RDWR|O_CREAT, FILE_MODE)) < 0)
188 { /**/DEBUG_MESSAGE("vmdcshare: open() failed");
189 goto done;
190 }
191
192 /* Note that the location being written to is always zero! */
193 if((extent = (ssize_t)lseek(fd, (off_t)0, SEEK_END)) < 0)
194 { /**/DEBUG_MESSAGE("vmdcshare: lseek() to get file size failed");
195 goto done;
196 }
197
198 if(extent < size) /* make the file size large enough */
199 { if(lseek(fd, (off_t)size, 0) != (off_t)size || write(fd, "", 1) != 1 )
200 { /**/DEBUG_MESSAGE("vmdcshare: attempt to extend file failed");
201 goto done;
202 }
203 }
204
205 /* map the file into memory */
206 mmvm = (Mmvm_t*)mmap(NIL(Void_t*), size, (PROT_READ|PROT_WRITE), MAP_SHARED, fd, (off_t)0);
207 if(!mmvm || mmvm == (Mmvm_t*)(-1) ) /* initial mapping failed */
208 { /**/DEBUG_MESSAGE("vmdcshare: mmap() failed");
209 goto done;
210 }
211 }
212 else
213 { /* make the key and get/create an id for the share mem segment */
214 if((mmdc->shmkey = mmkey(mmdc->name, mmdc->proj)) < 0 )
215 goto done;
216 if((mmdc->shmid = shmget(mmdc->shmkey, size, IPC_CREAT|FILE_MODE)) < 0 )
217 { /**/DEBUG_MESSAGE("vmdcshare: shmget() failed");
218 goto done;
219 }
220
221 /* map the data segment into memory */
222 mmvm = (Mmvm_t*)shmat(mmdc->shmid, NIL(Void_t*), 0);
223 if(!mmvm || mmvm == (Mmvm_t*)(-1) ) /* initial mapping failed */
224 { /**/DEBUG_MESSAGE("vmdcshare: attempt to attach memory failed");
225 shmctl(mmdc->shmid, IPC_RMID, 0);
226 goto done;
227 }
228 }
229
230 #if 0
231 switch (mmvm->magic)
232 {
233 case 0: write(2, "vmalloc: mmvm->magic=0\n", 23); break;
234 case MM_JUST4US: write(2, "vmalloc: mmvm->magic=MM_JUST4US\n", 32); break;
235 case MM_MAGIC: write(2, "vmalloc: mmvm->magic=MM_MAGIC\n", 29); break;
236 }
237 #endif
238 /* all processes compete for the chore to initialize data */
239 if(asocasint(&mmvm->magic, 0, MM_JUST4US) == 0 ) /* lucky winner: us! */
240 { if((base = vmmaddress(size)) != NIL(Void_t*))
241 { mmvm->base = base; /* this will be the base of the map */
242 mmvm->size = size;
243 }
244 if(!base || !(mmvm = mmfix(mmvm, mmdc, fd)) )
245 { /* remove any resource just created */
246 if(mmdc->proj >= 0)
247 shmctl(mmdc->shmid, IPC_RMID, 0);
248 else unlink(mmdc->name);
249 goto done;
250 }
251
252 mmdc->init = 1;
253 mmvm->base = base;
254 mmvm->size = size;
255 mmvm->busy = 0;
256 mmvm->shmkey = mmdc->shmkey;
257 mmvm->shmid = mmdc->shmid;
258 mmvm->proj = mmdc->proj;
259 strcpy(mmvm->name, mmdc->name);
260 if(mmdc->proj < 0 ) /* flush to file */
261 msync((Void_t*)mmvm, MMHEAD(mmvm->name), MS_SYNC);
262
263 rv = 0; /* success, return this value to indicate a new map */
264 }
265 else /* wait for someone else to finish initialization */
266 { /**/DEBUG_ASSERT(mmdc->init == 0);
267 if(mmvm->magic != MM_JUST4US && mmvm->magic != MM_MAGIC)
268 { /**/DEBUG_MESSAGE("vmdcshare: bad magic numbers");
269 goto done;
270 }
271
272 for(try = 0;; asospinrest() ) /* wait for region completion */
273 { if(asogetint(&mmvm->magic) == MM_MAGIC )
274 break;
275 else if((try += 1) <= 0 ) /* too many tries */
276 { /**/DEBUG_MESSAGE("vmdcshare: waiting time exhausted");
277 goto done;
278 }
279 }
280
281 /* mapped the wrong memory */
282 if(mmvm->proj != mmdc->proj || strcmp(mmvm->name, mmdc->name) != 0 )
283 { /**/DEBUG_MESSAGE("vmdcshare: wrong initialization parameters");
284 goto done;
285 }
286
287 if(mmvm->base != (Void_t*)mmvm) /* not yet at the right address */
288 { if(!(mmvm = mmfix(mmvm, mmdc, fd)) )
289 { /**/DEBUG_MESSAGE("vmdcshare: Can't fix address");
290 goto done;
291 }
292 }
293
294 rv = 1; /* success, return this value to indicate a finished map */
295 }
296
297 done: if(fd >= 0)
298 (void)close(fd);
299
300 if(rv >= 0 ) /* successful construction of region */
301 { /**/DEBUG_ASSERT(mmvm && mmvm != (Mmvm_t*)(-1));
302 mmdc->mmvm = mmvm;
303 }
304 else if(mmvm && mmvm != (Mmvm_t*)(-1)) /* error, remove map */
305 { /**/DEBUG_MESSAGE("vmdcshare: error during opening region");
306 if(mmdc->proj < 0)
307 (void)munmap((Void_t*)mmvm, size);
308 else (void)shmdt((Void_t*)mmvm);
309 }
310
311 return rv;
312 }
313
314 #if __STD_C /* end a file mapping */
mmend(Mmdisc_t * mmdc)315 static int mmend(Mmdisc_t* mmdc)
316 #else
317 static int mmend(mmdc)
318 Mmdisc_t* mmdc;
319 #endif
320 {
321 Mmvm_t *mmvm;
322 struct shmid_ds shmds;
323
324 if(!(mmvm = mmdc->mmvm) )
325 return 0;
326
327 if(mmdc->proj < 0 )
328 { (void)msync(mmvm->base, mmvm->size, MS_ASYNC);
329 if(mmdc->mode&MM_DETACH)
330 { if(mmvm->base )
331 (void)munmap(mmvm->base, mmvm->size);
332 }
333 if(mmdc->mode&MM_REMOVE)
334 (void)unlink(mmdc->name);
335 }
336 else
337 { if(mmdc->mode&MM_DETACH)
338 { if(mmvm->base )
339 (void)shmdt(mmvm->base);
340 }
341 if(mmdc->mode&MM_REMOVE)
342 { if(mmdc->shmid >= 0 )
343 (void)shmctl(mmdc->shmid, IPC_RMID, &shmds);
344 }
345 }
346
347 mmdc->mmvm = NIL(Mmvm_t*);
348 return 0;
349 }
350
351 #if __STD_C
mmgetmem(Vmalloc_t * vm,Void_t * caddr,size_t csize,size_t nsize,Vmdisc_t * disc)352 static Void_t* mmgetmem(Vmalloc_t* vm, Void_t* caddr,
353 size_t csize, size_t nsize, Vmdisc_t* disc)
354 #else
355 static Void_t* mmgetmem(vm, caddr, csize, nsize, disc)
356 Vmalloc_t* vm;
357 Void_t* caddr;
358 size_t csize;
359 size_t nsize;
360 Vmdisc_t* disc;
361 #endif
362 {
363 Mmvm_t *mmvm;
364 Mmdisc_t *mmdc = (Mmdisc_t*)disc;
365
366 if(!(mmvm = mmdc->mmvm) ) /* bad data */
367 return NIL(Void_t*);
368
369 /* this region allows only a single busy block! */
370 if(caddr) /* resizing/freeing an existing block */
371 { if(caddr == MMDATA(mmvm) && nsize <= MMSIZE(mmvm) )
372 { mmvm->busy = nsize;
373 return MMDATA(mmvm);
374 }
375 else return NIL(Void_t*);
376 }
377 else /* requesting a new block */
378 { if(mmvm->busy == 0 )
379 { mmvm->busy = nsize;
380 return MMDATA(mmvm);
381 }
382 else return NIL(Void_t*);
383 }
384 }
385
386 #if __STD_C
mmexcept(Vmalloc_t * vm,int type,Void_t * data,Vmdisc_t * disc)387 static int mmexcept(Vmalloc_t* vm, int type, Void_t* data, Vmdisc_t* disc)
388 #else
389 static int mmexcept(vm, type, data, disc)
390 Vmalloc_t* vm;
391 int type;
392 Void_t* data;
393 Vmdisc_t* disc;
394 #endif
395 {
396 int rv;
397 Mmdisc_t *mmdc = (Mmdisc_t*)disc;
398
399 if(type == VM_OPEN)
400 { if(data) /* VM_OPEN event at start of vmopen() */
401 { if((rv = mminit(mmdc)) < 0 ) /* initialization failed */
402 return -1;
403 else if(rv == 0) /* just started a new map */
404 { /**/DEBUG_ASSERT(mmdc->init == 1);
405 /**/DEBUG_ASSERT(mmdc->mmvm->magic == MM_JUST4US);
406 return 0;
407 }
408 else /* an existing map was reconstructed */
409 { /**/DEBUG_ASSERT(mmdc->init == 0);
410 /**/DEBUG_ASSERT(mmdc->mmvm->magic == MM_MAGIC);
411 *((Void_t**)data) = MMDATA(mmdc->mmvm);
412 return 1;
413 }
414 }
415 else return 0;
416 }
417 else if(type == VM_ENDOPEN) /* at end of vmopen() */
418 { if(mmdc->init) /* this is the initializing process! */
419 { /**/DEBUG_ASSERT(mmdc->mmvm->magic == MM_JUST4US);
420 asocasint(&mmdc->mmvm->magic, MM_JUST4US, MM_MAGIC);
421
422 if(mmdc->proj < 0) /* sync data to file now */
423 msync((Void_t*)mmdc->mmvm, MMHEAD(mmdc->name), MS_SYNC);
424 } /**/DEBUG_ASSERT(mmdc->mmvm->magic == MM_MAGIC);
425 return 0;
426 }
427 else if(type == VM_CLOSE)
428 return 1; /* tell vmclose not to free memory segments */
429 else if(type == VM_ENDCLOSE) /* this is the final closing event */
430 { (void)mmend(mmdc);
431 (void)vmfree(Vmheap, mmdc);
432 return 0; /* all done */
433 }
434 else if(type == VM_DISC)
435 return -1;
436 else return 0;
437 }
438
439 #if __STD_C
vmdcshare(char * name,int proj,ssize_t size,int mode)440 Vmdisc_t* vmdcshare(char* name, int proj, ssize_t size, int mode )
441 #else
442 Vmdisc_t* vmdcshare(name, proj, size, mode )
443 char* name; /* key or file persistent store */
444 int proj; /* project ID, < 0 for doing mmap */
445 ssize_t size; /* desired size for memory segment */
446 int mode; /* 1: keep memory segments */
447 /* 0: release memory segments */
448 /* -1: like 0 plus removing files/shms */
449 #endif
450 {
451 Mmdisc_t *mmdc;
452
453 VMPAGESIZE();
454
455 if(!name || !name[0] )
456 { /**/DEBUG_MESSAGE("vmdcshare: store name not given");
457 return NIL(Vmdisc_t*);
458 }
459 if(size <= 0 )
460 { /**/DEBUG_MESSAGE("vmdcshare: store size <= 0");
461 return NIL(Vmdisc_t*);
462 }
463
464 /* create discipline structure for getting memory from mmap */
465 if(!(mmdc = vmalloc(Vmheap, sizeof(Mmdisc_t)+strlen(name))) )
466 { /**/DEBUG_MESSAGE("vmdcshare: failed to allocate discipline ");
467 return NIL(Vmdisc_t*);
468 }
469
470 memset(mmdc, 0, sizeof(Mmdisc_t));
471 mmdc->disc.memoryf = mmgetmem;
472 mmdc->disc.exceptf = mmexcept;
473 mmdc->disc.round = (size/4) > _Vmsegsize ? _Vmsegsize : size/4;
474 mmdc->disc.round = ROUND(mmdc->disc.round, _Vmpagesize);
475 mmdc->mmvm = NIL(Mmvm_t*);
476 mmdc->size = size;
477 mmdc->shmkey = -1;
478 mmdc->shmid = -1;
479 mmdc->init = 0;
480 mmdc->mode = mode > 0 ? 0 : mode == 0 ? MM_DETACH : (MM_DETACH|MM_REMOVE);
481 mmdc->proj = proj;
482 strcpy(mmdc->name, name);
483
484 return (Vmdisc_t*)mmdc;
485 }
486
487 #endif
488