1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1985-2012 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 <gsf@research.att.com> *
18 * David Korn <dgk@research.att.com> *
19 * Phong Vo <kpv@research.att.com> *
20 * *
21 ***********************************************************************/
22 #if defined(_UWIN) && defined(_BLD_ast)
23
_STUB_vmmapopen()24 void _STUB_vmmapopen(){}
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 #undef ALIGN /* some sys/param.h define this */
36
37 #include <sys/mman.h> /* mmap() headers */
38 #include <sys/file.h>
39 #include <sys/stat.h>
40 #include <fcntl.h>
41
42 #include <sys/shm.h> /* shm headers */
43 #include <sys/ipc.h>
44
45 #undef ALIGN
46 #define ALIGN sizeof(struct _align_s)
47
48 /* Create a region to allocate based on mmap() or shmget().
49 ** Both ways provide for share memory allocation.
50 ** mmap() also allows for allocating persistent data.
51 **
52 ** Written by Kiem-Phong Vo (kpv@research.att.com)
53 */
54
55 #define MM_INIT 001 /* initialization mode */
56
57 #define MM_RELEASE 010 /* release share mem */
58 #define MM_CLEANUP 020 /* clean up resources */
59
60 /* magic word signaling region is being initialized */
61 #define MM_LETMEDOIT ((unsigned int)(('N'<<24) | ('B'<<16) | ('&'<<8) | ('I')) )
62
63 /* magic word signaling file/segment is ready */
64 #define MM_MAGIC ((unsigned int)(('P'<<24) | ('&'<<16) | ('N'<<8) | ('8')) )
65
66 /* default mimimum region size */
67 #define MM_MINSIZE (64*_Vmpagesize)
68
69 /* macros to get the data section and size */
70 #define MMHEAD(file) ROUND(sizeof(Mmvm_t)+strlen(file), ALIGN)
71 #define MMDATA(mmvm) ((Vmuchar_t*)(mmvm)->base + MMHEAD(mmvm->file))
72 #define MMSIZE(mmvm) ((mmvm)->size - MMHEAD(mmvm->file))
73
74 #ifdef S_IRUSR
75 #define FILE_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
76 #else
77 #define FILE_MODE 0644
78 #endif
79
80 /* to store key/value pairs for application */
81 typedef struct _mmuser_s Mmuser_t;
82 struct _mmuser_s
83 { Mmuser_t* next; /* link list */
84 int key; /* identifying key */
85 Void_t* val; /* associated value */
86 };
87
88 typedef struct _mmvm_s
89 { unsigned int magic; /* magic bytes */
90 Void_t* base; /* address to map to */
91 ssize_t size; /* total data size */
92 ssize_t busy; /* amount in use */
93 Mmuser_t* user; /* stored (key,val)'s */
94 int proj; /* project number */
95 char file[1];/* file name */
96 } Mmvm_t;
97
98 typedef struct _mmdisc_s
99 { Vmdisc_t disc; /* Vmalloc discipline */
100 int flag; /* various modes */
101 Mmvm_t* mmvm; /* shared memory data */
102 ssize_t size; /* desired file size */
103 int shmid; /* ID of the shared mem */
104 int proj; /* shm project ID */
105 char file[1];/* backing store/ftok() */
106 } Mmdisc_t;
107
108 #if DEBUG
109 #include <stdio.h>
110 #include <string.h>
_vmmdump(Vmalloc_t * vm,int fd)111 int _vmmdump(Vmalloc_t* vm, int fd)
112 {
113 char mesg[1024];
114 Mmdisc_t *mmdc = (Mmdisc_t*)vm->disc;
115
116 fd = fd < 0 ? 2 : fd;
117 sprintf(mesg, "File: %s\n", mmdc->file ); write(fd, mesg, strlen(mesg));
118 sprintf(mesg, "Project: %10d\n", mmdc->proj); write(fd, mesg, strlen(mesg));
119 sprintf(mesg, "Memory: %#010lx\n", mmdc->mmvm); write(fd, mesg, strlen(mesg));
120 sprintf(mesg, "Size: %10d\n", mmdc->size); write(fd, mesg, strlen(mesg));
121 sprintf(mesg, "Shmid: %10d\n", mmdc->shmid); write(fd, mesg, strlen(mesg));
122
123 sprintf(mesg, "File header:\n"); write(fd, mesg, strlen(mesg));
124 sprintf(mesg, "Magic: %10d\n", mmdc->mmvm->magic); write(fd, mesg, strlen(mesg));
125 sprintf(mesg, "Base: %#010lx\n", mmdc->mmvm->base); write(fd, mesg, strlen(mesg));
126 sprintf(mesg, "Size: %10d\n", mmdc->mmvm->size); write(fd, mesg, strlen(mesg));
127 sprintf(mesg, "Busy: %10d\n", mmdc->mmvm->busy); write(fd, mesg, strlen(mesg));
128 return 0;
129 }
130 #endif /*DEBUG*/
131
132 /* fix the mapped address for a region */
mmfix(Mmvm_t * mmvm,Mmdisc_t * mmdc,int fd)133 static Mmvm_t* mmfix(Mmvm_t* mmvm, Mmdisc_t* mmdc, int fd)
134 {
135 Void_t *base = mmvm->base;
136 ssize_t size = mmvm->size;
137
138 if(base != (Void_t*)mmvm) /* mmvm is not right yet */
139 { /**/ASSERT(!base || (base && (VLONG(base)%_Vmpagesize) == 0) );
140 if(mmdc->proj < 0)
141 { munmap((Void_t*)mmvm, size);
142 mmvm = (Mmvm_t*)mmap(base, size, (PROT_READ|PROT_WRITE),
143 (MAP_FIXED|MAP_SHARED), fd, (off_t)0 );
144 }
145 else
146 { shmdt((Void_t*)mmvm);
147 mmvm = (Mmvm_t*)shmat(mmdc->shmid, base, 0);
148 }
149 if(!mmvm || mmvm == (Mmvm_t*)(-1) )
150 mmvm = NIL(Mmvm_t*);
151 }
152
153 return mmvm;
154 }
155
156 /* initialize region data */
mminit(Mmdisc_t * mmdc)157 static int mminit(Mmdisc_t* mmdc)
158 {
159 Void_t *base;
160 int try, k;
161 int fd = -1;
162 key_t key = -1;
163 ssize_t extent, size = 0;
164 Mmvm_t *mmvm = NIL(Mmvm_t*);
165 int rv = -1;
166
167 if(mmdc->mmvm) /* already done this */
168 return 0;
169
170 /* fixed size region so make it reasonably large */
171 if((size = mmdc->size) < MM_MINSIZE )
172 size = MM_MINSIZE;
173 size += MMHEAD(mmdc->file) + ALIGN;
174 size = ROUND(size, _Vmpagesize);
175
176 /* this op can happen simultaneously in different processes */
177 if((fd = open(mmdc->file, O_RDWR|O_CREAT, FILE_MODE)) < 0)
178 return -1;
179
180 /* get/create the initial segment of data */
181 if(mmdc->proj < 0 ) /* proj < 0 means doing mmap() */
182 { /* Note that the location being written to is always zero! */
183 if((extent = (ssize_t)lseek(fd, (off_t)0, SEEK_END)) < 0)
184 goto done;
185 if(extent < size) /* make the file size large enough */
186 if(lseek(fd, (off_t)size, 0) != (off_t)size || write(fd, "", 1) != 1 )
187 goto done;
188
189 /* map the file into memory */
190 mmvm = (Mmvm_t*)mmap(NIL(Void_t*), size, (PROT_READ|PROT_WRITE),
191 MAP_SHARED, fd, (off_t)0 );
192 }
193 else
194 { /* make the key and get/create an id for the share mem segment */
195 if((key = ftok(mmdc->file, mmdc->proj)) < 0 )
196 goto done;
197 if((mmdc->shmid = shmget(key, size, IPC_CREAT|FILE_MODE)) < 0 )
198 goto done;
199
200 /* map the data segment into memory */
201 mmvm = (Mmvm_t*)shmat(mmdc->shmid, NIL(Void_t*), 0);
202 }
203
204 if(!mmvm || mmvm == (Mmvm_t*)(-1) ) /* initial mapping failed */
205 goto done;
206
207 /* all processes compete for the chore to initialize data */
208 if(asocasint(&mmvm->magic, 0, MM_LETMEDOIT) == 0 ) /* lucky winner: us! */
209 { if(!(base = vmmaddress(size)) ) /* get a suitable base for the map */
210 base = (Void_t*)mmvm;
211 mmdc->flag |= MM_INIT;
212 mmvm->base = base;
213 mmvm->size = size;
214 mmvm->busy = 0;
215 mmvm->proj = mmdc->proj;
216 strcpy(mmvm->file, mmdc->file);
217 if(mmdc->proj < 0 ) /* flush to file */
218 msync((Void_t*)mmvm, MMHEAD(mmvm->file), MS_SYNC);
219
220 if(mmvm->base != (Void_t*)mmvm) /* not yet at the right address */
221 if(!(mmvm = mmfix(mmvm, mmdc, fd)) )
222 goto done;
223 rv = 0; /* success, return this value to indicate a new map */
224 }
225 else /* wait for someone else to finish initialization */
226 { /**/ASSERT(!(mmdc->flag&MM_INIT));
227 if(mmvm->magic != MM_LETMEDOIT && mmvm->magic != MM_MAGIC)
228 goto done;
229
230 for(try = 0, k = 0;; ASOLOOP(k) ) /* waiting */
231 { if(asocasint(&mmvm->magic, MM_MAGIC, MM_MAGIC) == MM_MAGIC )
232 break;
233 else if((try += 1) <= 0 ) /* too many tries */
234 goto done;
235 }
236
237 /* mapped the wrong memory */
238 if(mmvm->proj != mmdc->proj || strcmp(mmvm->file, mmdc->file) != 0 )
239 goto done;
240
241 if(mmvm->base != (Void_t*)mmvm) /* not yet at the right address */
242 if(!(mmvm = mmfix(mmvm, mmdc, fd)) )
243 goto done;
244 rv = 1; /* success, return this value to indicate a finished map */
245 }
246
247 done: (void)close(fd);
248
249 if(rv >= 0 ) /* successful construction of region */
250 { /**/ASSERT(mmvm && mmvm != (Mmvm_t*)(-1));
251 mmdc->mmvm = mmvm;
252 }
253 else if(mmvm && mmvm != (Mmvm_t*)(-1)) /* error, remove map */
254 { if(mmdc->proj < 0)
255 (void)munmap((Void_t*)mmvm, size);
256 else (void)shmdt((Void_t*)mmvm);
257 }
258
259 return rv;
260 }
261
262 #if __STD_C /* end a file mapping */
mmend(Mmdisc_t * mmdc)263 static int mmend(Mmdisc_t* mmdc)
264 #else
265 static int mmend(mmdc)
266 Mmdisc_t* mmdc;
267 #endif
268 {
269 Mmvm_t *mmvm;
270 struct shmid_ds shmds;
271
272 if(!(mmvm = mmdc->mmvm) )
273 return 0;
274
275 if(mmdc->proj < 0 )
276 { (void)msync(mmvm->base, mmvm->size, MS_ASYNC);
277 if(mmdc->flag&MM_RELEASE)
278 { if(mmvm->base )
279 (void)munmap(mmvm->base, mmvm->size);
280 }
281 if(mmdc->flag&MM_CLEANUP)
282 (void)unlink(mmdc->file);
283 }
284 else
285 { if(mmdc->flag&MM_RELEASE)
286 { if(mmvm->base )
287 (void)shmdt(mmvm->base);
288 }
289 if(mmdc->flag&MM_CLEANUP)
290 { if(mmdc->shmid >= 0 )
291 (void)shmctl(mmdc->shmid, IPC_RMID, &shmds);
292 }
293 }
294
295 mmdc->mmvm = NIL(Mmvm_t*);
296 return 0;
297 }
298
299 #if __STD_C
mmgetmem(Vmalloc_t * vm,Void_t * caddr,size_t csize,size_t nsize,Vmdisc_t * disc)300 static Void_t* mmgetmem(Vmalloc_t* vm, Void_t* caddr,
301 size_t csize, size_t nsize, Vmdisc_t* disc)
302 #else
303 static Void_t* mmgetmem(vm, caddr, csize, nsize, disc)
304 Vmalloc_t* vm;
305 Void_t* caddr;
306 size_t csize;
307 size_t nsize;
308 Vmdisc_t* disc;
309 #endif
310 {
311 Mmvm_t *mmvm;
312 Mmdisc_t *mmdc = (Mmdisc_t*)disc;
313
314 if(!(mmvm = mmdc->mmvm) ) /* bad data */
315 return NIL(Void_t*);
316
317 /* this region allows only a single busy block! */
318 if(caddr) /* resizing/freeing an existing block */
319 { if(caddr == MMDATA(mmvm) && nsize <= MMSIZE(mmvm) )
320 { mmvm->busy = nsize;
321 return MMDATA(mmvm);
322 }
323 else return NIL(Void_t*);
324 }
325 else /* requesting a new block */
326 { if(mmvm->busy == 0 )
327 { mmvm->busy = nsize;
328 return MMDATA(mmvm);
329 }
330 else return NIL(Void_t*);
331 }
332 }
333
334 #if __STD_C
mmexcept(Vmalloc_t * vm,int type,Void_t * data,Vmdisc_t * disc)335 static int mmexcept(Vmalloc_t* vm, int type, Void_t* data, Vmdisc_t* disc)
336 #else
337 static int mmexcept(vm, type, data, disc)
338 Vmalloc_t* vm;
339 int type;
340 Void_t* data;
341 Vmdisc_t* disc;
342 #endif
343 {
344 int rv;
345 Void_t *base;
346 Mmdisc_t *mmdc = (Mmdisc_t*)disc;
347
348 if(type == VM_OPEN)
349 { if(data) /* VM_OPEN event at start of vmopen() */
350 { if((rv = mminit(mmdc)) < 0 ) /* initialization failed */
351 return -1;
352 else if(rv == 0) /* just started a new map */
353 { /**/ASSERT(mmdc->flag&MM_INIT);
354 /**/ASSERT(mmdc->mmvm->magic == MM_LETMEDOIT);
355 return 0;
356 }
357 else /* an existing map was reconstructed */
358 { /**/ASSERT(!(mmdc->flag&MM_INIT));
359 /**/ASSERT(mmdc->mmvm->magic == MM_MAGIC);
360 *((Void_t**)data) = MMDATA(mmdc->mmvm);
361 return 1;
362 }
363 }
364 else return 0;
365 }
366 else if(type == VM_ENDOPEN) /* at end of vmopen() */
367 { if(mmdc->flag&MM_INIT) /* this is the initializing process! */
368 { /**/ASSERT(mmdc->mmvm->magic == MM_LETMEDOIT);
369 asocasint(&mmdc->mmvm->magic, MM_LETMEDOIT, MM_MAGIC);
370
371 if(mmdc->proj < 0) /* sync data to file now */
372 msync((Void_t*)mmdc->mmvm, MMHEAD(mmdc->file), MS_SYNC);
373 } /**/ASSERT(mmdc->mmvm->magic == MM_MAGIC);
374 return 0;
375 }
376 else if(type == VM_CLOSE)
377 return 1; /* tell vmclose not to free memory segments */
378 else if(type == VM_ENDCLOSE) /* this is the final closing event */
379 { (void)mmend(mmdc);
380 (void)vmfree(Vmheap, mmdc);
381 return 0; /* all done */
382 }
383 else return 0;
384 }
385
386 #if __STD_C
vmmopen(char * file,int proj,ssize_t size)387 Vmalloc_t* vmmopen(char* file, int proj, ssize_t size )
388 #else
389 Vmalloc_t* vmmopen(file, proj, size )
390 char* file; /* file for key or data backing */
391 int proj; /* project ID, < 0 doing mmap */
392 ssize_t size; /* desired size for mem segment */
393 #endif
394 {
395 Vmalloc_t *vm;
396 Mmdisc_t *mmdc;
397
398 GETPAGESIZE(_Vmpagesize);
399
400 if(!file || !file[0] )
401 return NIL(Vmalloc_t*);
402
403 /* create discipline structure for getting memory from mmap */
404 if(!(mmdc = vmalloc(Vmheap, sizeof(Mmdisc_t)+strlen(file))) )
405 return NIL(Vmalloc_t*);
406 memset(mmdc, 0, sizeof(Mmdisc_t));
407 mmdc->disc.memoryf = mmgetmem;
408 mmdc->disc.exceptf = mmexcept;
409 mmdc->disc.round = _Vmpagesize; /* round request to this size */
410 mmdc->mmvm = NIL(Mmvm_t*);
411 mmdc->size = size;
412 mmdc->shmid = -1;
413 mmdc->flag = 0;
414 mmdc->proj = proj;
415 strcpy(mmdc->file, file);
416
417 /* now open the Vmalloc_t handle to return to application */
418 if(!(vm = vmopen(&mmdc->disc, Vmbest, VM_SHARE)) )
419 { (void)mmend(mmdc);
420 (void)vmfree(Vmheap, mmdc);
421 return NIL(Vmalloc_t*);
422 }
423 else
424 { /**/ASSERT(mmdc->mmvm && mmdc->mmvm->magic == MM_MAGIC);
425 return vm;
426 }
427 }
428
429 /* to store (key,value) data in the map */
430 #if __STD_C
vmmvalue(Vmalloc_t * vm,int key,Void_t * val,int oper)431 Void_t* vmmvalue(Vmalloc_t* vm, int key, Void_t* val, int oper)
432 #else
433 Void_t* vmmvalue(vm, key, val, oper)
434 Vmalloc_t* vm; /* a region based on vmmapopen */
435 int key; /* key of data to be set */
436 Void_t* val; /* data to be set */
437 int oper; /* operation type */
438 #endif
439 {
440 Mmuser_t *u;
441 Mmdisc_t *mmdc = (Mmdisc_t*)vm->disc;
442 Mmvm_t *mmvm = mmdc->mmvm;
443
444 /* check to see if operation is well-defined */
445 if(oper != VM_MMGET && oper != VM_MMSET && oper != VM_MMADD)
446 return NIL(Void_t*);
447
448 SETLOCK(vm, 0);
449
450 /* find the key */
451 for(u = mmvm->user; u; u = u->next)
452 if(u->key == key)
453 break;
454
455 if(!u && (oper == VM_MMSET || oper == VM_MMADD) )
456 { if((u = KPVALLOC(vm, sizeof(Mmuser_t), vm->meth.allocf)) )
457 { u->val = NIL(Void_t*);
458 u->key = key;
459 u->next = mmvm->user;
460 mmvm->user = u;
461 }
462 }
463
464 if(u) /* update data and set value to return */
465 { if(oper == VM_MMSET)
466 u->val = val;
467 else if(oper == VM_MMADD)
468 u->val = (Void_t*)((long)(u->val) + (long)(val));
469 val = u->val;
470 }
471 else val = NIL(Void_t*);
472
473 CLRLOCK(vm, 0);
474
475 return val;
476 }
477
vmmrelease(Vmalloc_t * vm,int type)478 void vmmrelease(Vmalloc_t* vm, int type)
479 {
480 Mmdisc_t *mmdc = (Mmdisc_t*)vm->disc;
481
482 mmdc->flag |= MM_RELEASE;
483 if(type > 0)
484 mmdc->flag |= MM_CLEANUP;
485 }
486
487 /* suggest an address usable for mapping memory */
vmmaddress(size_t size)488 Void_t* vmmaddress(size_t size)
489 {
490 #if !defined(_map_min) || !defined(_map_max) || !defined(_map_dir)
491 return NIL(Void_t*);
492 #else
493 Void_t *avail;
494 static Vmuchar_t *min = (Vmuchar_t*)_map_min;
495 static Vmuchar_t *max = (Vmuchar_t*)_map_max;
496
497 GETPAGESIZE(_Vmpagesize);
498 size = ROUND(size, _Vmpagesize);
499
500 if(_map_dir == 0 || (min+size) > max)
501 avail = NIL(Void_t*);
502 else if(_map_dir > 0)
503 { avail = (Void_t*)min;
504 min += size;
505 }
506 else
507 { max -= size;
508 avail = (Void_t*)max;
509 }
510
511 return avail;
512 #endif
513 }
514
515 #endif
516