c5b309ff07
Main changes: - COW optimization for safecopy. - safemap, a grant-based interface for sharing memory regions between processes. - Integration with safemap and complete rework of DS, supporting new data types natively (labels, memory ranges, memory mapped ranges). - For further information: http://wiki.minix3.org/en/SummerOfCode2009/MemoryGrants Additional changes not included in the original Wu's branch: - Fixed unhandled case in VM when using COW optimization for safecopy in case of a block that has already been shared as SMAP. - Better interface and naming scheme for sys_saferevmap and ds_retrieve_map calls. - Better input checking in syslib: check for page alignment when creating memory mapping grants. - DS notifies subscribers when an entry is deleted. - Documented the behavior of indirect grants in case of memory mapping. - Test suite in /usr/src/test/safeperf|safecopy|safemap|ds/* reworked and extended. - Minor fixes and general cleanup. - TO-DO: Grant ids should be generated and managed the way endpoints are to make sure grant slots are never misreused.
213 lines
6.6 KiB
C
213 lines
6.6 KiB
C
/* This file contains memory management routines for RS.
|
|
*
|
|
* Changes:
|
|
* Nov 22, 2009: Created (Cristiano Giuffrida)
|
|
*/
|
|
|
|
#include "inc.h"
|
|
#include "../../kernel/const.h"
|
|
#include "../../kernel/type.h"
|
|
#include "../../kernel/proc.h"
|
|
|
|
EXTERN char *_brksize;
|
|
|
|
PRIVATE char * _rs_startbrksize = NULL;
|
|
PRIVATE char * _rs_endbrksize = NULL;
|
|
|
|
#define munmap _munmap
|
|
#define munmap_text _munmap_text
|
|
#include <sys/mman.h>
|
|
#undef munmap
|
|
#undef munmap_text
|
|
|
|
PUBLIC int unmap_ok = 0;
|
|
|
|
/*===========================================================================*
|
|
* check_mem_available *
|
|
*===========================================================================*/
|
|
PRIVATE int check_mem_available(char *new_brksize)
|
|
{
|
|
/* Check if enough memory is available to grow break size. */
|
|
register struct mem_map *mem_sp, *mem_dp;
|
|
vir_clicks sp_click, gap_base, sp_lower;
|
|
int s;
|
|
long base_of_stack, sp_delta; /* longs avoid certain problems */
|
|
vir_bytes sp;
|
|
struct proc proc;
|
|
vir_clicks data_clicks;
|
|
|
|
/* Get stack pointer and pointers to data/stack segment maps. */
|
|
if ((s=sys_getproc(&proc, SELF)) != OK) {
|
|
return(s);
|
|
}
|
|
sp = proc.p_reg.sp; /* stack pointer */
|
|
mem_dp = &proc.p_memmap[D]; /* pointer to data segment map */
|
|
mem_sp = &proc.p_memmap[S]; /* pointer to stack segment map */
|
|
|
|
/* Compute how many clicks the data segment is to become. */
|
|
data_clicks = (vir_clicks) (CLICK_CEIL(new_brksize) >> CLICK_SHIFT)
|
|
- mem_dp->mem_vir;
|
|
|
|
/* See if stack size has gone negative (i.e., sp too close to 0xFFFF...) */
|
|
base_of_stack = (long) mem_sp->mem_vir + (long) mem_sp->mem_len;
|
|
sp_click = sp >> CLICK_SHIFT; /* click containing sp */
|
|
if (sp_click >= base_of_stack)
|
|
{
|
|
return(ENOMEM); /* sp too high */
|
|
}
|
|
|
|
/* Compute size of gap between stack and data segments. */
|
|
sp_delta = (long) mem_sp->mem_vir - (long) sp_click;
|
|
sp_lower = (sp_delta > 0 ? sp_click : mem_sp->mem_vir);
|
|
|
|
/* Add a safety margin for future stack growth. Impossible to do right. */
|
|
#define SAFETY_BYTES (384 * sizeof(char *))
|
|
#define SAFETY_CLICKS ((vir_clicks) (CLICK_CEIL(SAFETY_BYTES) >> CLICK_SHIFT))
|
|
gap_base = mem_dp->mem_vir + data_clicks + SAFETY_CLICKS;
|
|
if (sp_lower < gap_base)
|
|
{
|
|
return(ENOMEM); /* data and stack collided */
|
|
}
|
|
|
|
return(OK);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* rs_startup_sbrk *
|
|
*===========================================================================*/
|
|
PUBLIC void* rs_startup_sbrk(size)
|
|
size_t size; /* the size to grow */
|
|
{
|
|
/* RS's own sbrk() used at startup. */
|
|
void* addr;
|
|
char* new_brksize;
|
|
|
|
/* Check input for non-positive size or size overflows. */
|
|
new_brksize = _brksize + size;
|
|
if (size <= 0 || new_brksize < _brksize) {
|
|
return( (char *) -1);
|
|
}
|
|
|
|
/* Check if enough memory is available. */
|
|
if(check_mem_available(new_brksize) != OK) {
|
|
return( (char *) -1);
|
|
}
|
|
|
|
/* Save initial break size. */
|
|
if(_rs_startbrksize == NULL) {
|
|
_rs_startbrksize = _brksize;
|
|
}
|
|
|
|
/* Set address and adjust break size. */
|
|
addr = _brksize;
|
|
_brksize = new_brksize;
|
|
_rs_endbrksize = _brksize;
|
|
|
|
return addr;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* rs_startup_sbrk_synch *
|
|
*===========================================================================*/
|
|
PUBLIC void* rs_startup_sbrk_synch(size)
|
|
size_t size; /* the size to grow */
|
|
{
|
|
/* Synchronize RS's own sbrk() with the rest of the system right after
|
|
* startup. We use the original sbrk() here.
|
|
*/
|
|
void* addr;
|
|
|
|
/* Restore original break size. */
|
|
_brksize = _rs_startbrksize;
|
|
|
|
/* Call original sbrk() and see if we observe the same effect. */
|
|
addr = (void*)sbrk(size);
|
|
if(_rs_startbrksize != addr) {
|
|
printf("Unable to synch rs_startup_sbrk() and sbrk(): addr 0x%x!=0x%x\n",
|
|
(int) _rs_startbrksize, (int) addr);
|
|
return( (char *) -1);
|
|
}
|
|
if(_rs_endbrksize != _brksize) {
|
|
printf("Unable to synch rs_startup_sbrk() and sbrk(): size 0x%x!=0x%x\n",
|
|
(int) _rs_endbrksize, (int) _brksize);
|
|
return( (char *) -1);
|
|
}
|
|
|
|
return addr;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* rs_startup_segcopy *
|
|
*===========================================================================*/
|
|
PUBLIC int rs_startup_segcopy(src_proc, src_seg, dst_seg, dst_vir, bytes)
|
|
endpoint_t src_proc; /* source process */
|
|
int src_seg; /* source memory segment */
|
|
int dst_seg; /* destination memory segment */
|
|
vir_bytes dst_vir; /* destination virtual address */
|
|
phys_bytes bytes; /* how many bytes */
|
|
{
|
|
/* Copy a process's T, D, S segment to RS's address space. Used at startup. */
|
|
struct proc src_p, dst_p;
|
|
phys_bytes src_phys, dst_phys;
|
|
int s;
|
|
|
|
/* Check input. */
|
|
if((src_seg != T && src_seg != D && src_seg != S) || bytes <= 0) {
|
|
return EINVAL;
|
|
}
|
|
|
|
/* We don't override normal behavior when not copying to our data segment. */
|
|
if(dst_seg != D) {
|
|
s = sys_vircopy(src_proc, src_seg, 0, SELF, dst_seg, dst_vir, bytes);
|
|
return(s);
|
|
}
|
|
|
|
/* Get kernel process slot for both source and destination. */
|
|
if ((s=sys_getproc(&src_p, src_proc)) != OK) {
|
|
return(s);
|
|
}
|
|
if ((s=sys_getproc(&dst_p, SELF)) != OK) {
|
|
return(s);
|
|
}
|
|
|
|
/* Map source address to physical address. */
|
|
src_phys = (phys_bytes) src_p.p_memmap[src_seg].mem_phys << CLICK_SHIFT;
|
|
|
|
/* Check if destination address is out of bounds or overflows. */
|
|
if(dst_vir+bytes > (vir_bytes)_rs_endbrksize
|
|
|| dst_vir < (vir_bytes)_rs_startbrksize || dst_vir+bytes < dst_vir) {
|
|
return EFAULT;
|
|
}
|
|
|
|
/* Map destination address to physical address. */
|
|
dst_phys = (phys_bytes) dst_p.p_memmap[D].mem_phys << CLICK_SHIFT;
|
|
dst_phys += dst_vir - (dst_p.p_memmap[D].mem_vir << CLICK_SHIFT);
|
|
|
|
/* Make a physical copy for the requested data. */
|
|
s = sys_abscopy(src_phys, dst_phys, bytes);
|
|
|
|
return(s);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* munmap *
|
|
*===========================================================================*/
|
|
PUBLIC int munmap(void *addrstart, vir_bytes len)
|
|
{
|
|
if(!unmap_ok)
|
|
return ENOSYS;
|
|
|
|
return _munmap(addrstart, len);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* munmap_text *
|
|
*===========================================================================*/
|
|
PUBLIC int munmap_text(void *addrstart, vir_bytes len)
|
|
{
|
|
if(!unmap_ok)
|
|
return ENOSYS;
|
|
|
|
return _munmap_text(addrstart, len);
|
|
}
|
|
|