vmmopen.c revision da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968
/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1985-2007 AT&T Knowledge Ventures *
* and is licensed under the *
* Common Public License, Version 1.0 *
* by AT&T Knowledge Ventures *
* *
* A copy of the License is available at *
* http://www.opensource.org/licenses/cpl1.0.txt *
* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
* *
* Information and Software Systems Research *
* AT&T Research *
* Florham Park NJ *
* *
* Glenn Fowler <gsf@research.att.com> *
* David Korn <dgk@research.att.com> *
* Phong Vo <kpv@research.att.com> *
* *
***********************************************************************/
#if defined(_UWIN) && defined(_BLD_ast)
void _STUB_vmmopen(){}
#else
#include "vmhdr.h"
#if _sys_stat
#include <sys/stat.h>
#endif
#include <fcntl.h>
#ifdef S_IRUSR
#define CREAT_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
#else
#define CREAT_MODE 0644
#endif
#if _lib_mmap
#include <sys/mman.h>
#else
#define mmap(a,b,c,d,e,f) MAP_FAILED
#define munmap(a,b) MAP_FAILED
#endif
/* Create a region to allocate based on mmap()
**
** Written by Kiem-Phong Vo (kpv@research.att.com)
*/
#ifndef MAP_FAILED
#define MAP_FAILED (void*)(-1)
#endif
#define MM_MAGIC (('V'<<24) | ('M'<<16) | ('A'<<8) | ('P'))
#define MM_ROUND (64*1024)
#define MM_START ROUND(sizeof(Mmvm_t),ALIGN)
typedef struct _user_s
{ struct _user_s* next; /* link list */
int key; /* identifying key */
Void_t* data; /* data to be returned */
} User_t;
typedef struct _mmvm_s
{
Vmulong_t magic; /* magic bytes */
Void_t* base; /* base of the map */
size_t size; /* current size */
size_t busy; /* amount in use */
size_t round; /* amount to round to */
User_t* user; /* some user data */
} Mmvm_t;
typedef struct _mmvmdisc_s
{
Vmdisc_t disc; /* Vmalloc discipline */
int fd; /* file descriptor */
Mmvm_t* mm; /* mmap data */
} Mmvmdisc_t;
#if __STD_C
static int mmvminit(char* file, Void_t* addr, size_t round, Mmvm_t* mm)
#else
static int mmvminit(file, addr, round, mm)
char* file; /* file to map data from */
Void_t* addr; /* desired starting address */
size_t round; /* amount to round requests */
Mmvm_t* mm; /* to return some mapped info */
#endif
{
int fd;
off_t size;
Void_t *base;
Mmvm_t *hdr;
base = NIL(Void_t*);
if((fd = open(file, O_RDWR, CREAT_MODE)) >= 0)
{ if((size = lseek(fd, (off_t)0, 2)) < 0)
goto done;
else if(size == 0)
goto new_f;
/* read the header */
if(lseek(fd, (off_t)0, 0) != (off_t)0)
goto done;
if(read(fd, mm, sizeof(Mmvm_t)) != sizeof(Mmvm_t))
goto done;
if(mm->magic != MM_MAGIC || !mm->base ||
(off_t)mm->size != size || mm->busy > mm->size )
goto done;
base = (Void_t*)mmap(mm->base, mm->size, PROT_READ|PROT_WRITE,
MAP_FIXED|MAP_SHARED, fd, (off_t)0 );
if(base == (Void_t*)MAP_FAILED)
base = NIL(Void_t*);
}
else
{ if((fd = open(file, O_RDWR|O_CREAT, CREAT_MODE)) < 0)
goto done;
new_f: /* create an initial set of data */
size = round;
if(lseek(fd, size-1, 0) != (size-1) || write(fd, "", 1) != 1 )
goto done;
base = (Void_t*)mmap(addr, (size_t)size, PROT_READ|PROT_WRITE,
(addr ? MAP_FIXED : 0)|MAP_SHARED, fd, (off_t)0 );
if(base == (Void_t*)MAP_FAILED)
base = NIL(Void_t*);
if(!base)
goto done;
/* write magic number */
hdr = (Mmvm_t*)base;
hdr->magic = MM_MAGIC;
hdr->base = base;
hdr->size = size;
hdr->busy = MM_START;
hdr->round = round;
hdr->user = NIL(User_t*);
memcpy(mm, hdr, sizeof(Mmvm_t));
}
done:
if(!base)
{ if(fd >= 0)
close(fd);
fd = -1;
}
return fd;
}
#if __STD_C
static Void_t* mmvmmemory(Vmalloc_t* vm, Void_t* caddr,
size_t csize, size_t nsize, Vmdisc_t* disc)
#else
static Void_t* mmvmmemory(vm, caddr, csize, nsize, disc)
Vmalloc_t* vm;
Void_t* caddr;
size_t csize;
size_t nsize;
Vmdisc_t* disc;
#endif
{
Mmvmdisc_t *mmdc = (Mmvmdisc_t*)disc;
if(mmdc->fd < 0 || !mmdc->mm)
return NIL(Void_t*);
#define MMADDR(b) ((Void_t*)(((Vmuchar_t*)b) + MM_START) )
if(caddr && caddr != MMADDR(mmdc->mm->base) )
return NIL(Void_t*);
if(nsize < csize)
return NIL(Void_t*);
if(nsize > mmdc->mm->size-MM_START)
{ /* base and size of new map */
caddr = mmdc->mm->base;
csize = MM_START + nsize +
((nsize % disc->round) < (disc->round/2) ? disc->round/2 : 0);
csize = ROUND(csize, disc->round);
/* make room for new space */
if(lseek(mmdc->fd, (off_t)(csize-1), 0) != (off_t)(csize-1) ||
write(mmdc->fd, "", 1) != 1 )
return NIL(Void_t*);
/* remap the space */
(void)munmap(caddr, mmdc->mm->size);
caddr = (Void_t*)mmap(caddr, csize, PROT_READ|PROT_WRITE,
MAP_FIXED|MAP_SHARED, mmdc->fd, (off_t)0 );
if(caddr == (Void_t*)MAP_FAILED)
caddr = NIL(Void_t*);
if(caddr)
mmdc->mm->size = csize;
else /* bad problem */
{ close(mmdc->fd);
mmdc->fd = -1;
mmdc->mm = NIL(Mmvm_t*);
return NIL(Void_t*);
}
}
mmdc->mm->busy = nsize+MM_START;
return (Void_t*)(((Vmuchar_t*)mmdc->mm->base) + MM_START);
}
#if __STD_C
static int mmvmexcept(Vmalloc_t* vm, int type, Void_t* data, Vmdisc_t* disc)
#else
static int mmvmexcept(vm, type, data, disc)
Vmalloc_t* vm;
int type;
Void_t* data;
Vmdisc_t* disc;
#endif
{
Mmvmdisc_t *mmdc = (Mmvmdisc_t*)disc;
Vmuchar_t *base;
if(type == VM_OPEN)
{ if(mmdc->mm->busy > MM_START)
{ base = ((Vmuchar_t*)mmdc->mm->base) + MM_START;
*((Void_t**)data) = (Void_t*)base;
return 1;
}
else return 0;
}
else if(type == VM_CLOSE)
{ (void)munmap(mmdc->mm->base, mmdc->mm->size);
(void)close(mmdc->fd);
vmfree(Vmheap, mmdc);
return 1; /* freeing of mapped data is already done */
}
else return 0;
}
#if __STD_C
Vmalloc_t* vmmopen(char* file, Void_t* base, size_t round)
#else
Vmalloc_t* vmmopen(file, base, round)
char* file; /* file mapping data from */
Void_t* base; /* desired starting address */
size_t round; /* amount to round requests */
#endif
{
Vmalloc_t *vm;
Mmvmdisc_t *mmdc;
Mmvm_t mm;
int fd;
if(!file)
return NIL(Vmalloc_t*);
/* set the amount to round up to on each memory request */
GETPAGESIZE(_Vmpagesize);
if(round < MM_ROUND)
round = MM_ROUND;
round = ROUND(round, _Vmpagesize);
if((fd = mmvminit(file, base, round, &mm)) < 0)
return NIL(Vmalloc_t*);
if(!(mmdc = (Mmvmdisc_t*)vmalloc(Vmheap, sizeof(Mmvmdisc_t))) )
{ close(fd);
return NIL(Vmalloc_t*);
}
mmdc->disc.memoryf = mmvmmemory;
mmdc->disc.exceptf = mmvmexcept;
mmdc->disc.round = mm.round;
mmdc->fd = fd;
mmdc->mm = (Mmvm_t*)mm.base;
if(!(vm = vmopen(&mmdc->disc, Vmbest, VM_TRUST)) )
{ mmvmexcept(NIL(Vmalloc_t*), VM_CLOSE, NIL(Void_t*), &mmdc->disc);
return NIL(Vmalloc_t*);
}
return vm;
}
#if __STD_C
Void_t* vmmset(Vmalloc_t* vm, int key, Void_t* data, int set)
#else
Void_t* vmmset(vm, data, key, set)
Vmalloc_t* vm; /* a region based on vmmmopen */
int key; /* key of data to be set */
Void_t* data; /* data to be set */
int set; /* 1 for setting, 0 for getting */
#endif
{
User_t *u;
Mmvm_t *mmvm = ((Mmvmdisc_t*)vm->disc)->mm;
for(u = mmvm->user; u; u = u->next)
if(u->key == key)
break;
if(!set)
return u ? u->data : NIL(Void_t*);
else if(u)
{ Void_t* old = u->data;
u->data = data;
return old;
}
else if(!(u = (User_t*)vmalloc(vm, sizeof(User_t))) )
return NIL(Void_t*);
else
{ u->data = data;
u->key = key;
u->next = mmvm->user;
mmvm->user = u;
return data;
}
}
#endif