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