minix/servers/pm/break.c

182 lines
6.7 KiB
C
Raw Normal View History

/* The MINIX model of memory allocation reserves a fixed amount of memory for
* the combined text, data, and stack segments. The amount used for a child
* process created by FORK is the same as the parent had. If the child does
* an EXEC later, the new size is taken from the header of the file EXEC'ed.
*
* The layout in memory consists of the text segment, followed by the data
* segment, followed by a gap (unused memory), followed by the stack segment.
* The data segment grows upward and the stack grows downward, so each can
* take memory from the gap. If they meet, the process must be killed. The
* procedures in this file deal with the growth of the data and stack segments.
*
* The entry points into this file are:
* do_brk: BRK/SBRK system calls to grow or shrink the data segment
* adjust: see if a proposed segment adjustment is allowed
2005-09-23 22:46:17 +02:00
* size_ok: see if the segment sizes are feasible (i86 only)
*/
#include "pm.h"
#include <signal.h>
#include "mproc.h"
#include "param.h"
#define DATA_CHANGED 1 /* flag value when data segment size changed */
#define STACK_CHANGED 2 /* flag value when stack size changed */
/*===========================================================================*
2006-05-11 16:57:23 +02:00
* do_brk *
*===========================================================================*/
PUBLIC int do_brk()
{
/* Perform the brk(addr) system call.
*
* The call is complicated by the fact that on some machines (e.g., 8088),
* the stack pointer can grow beyond the base of the stack segment without
* anybody noticing it.
* The parameter, 'addr' is the new virtual address in D space.
*/
register struct mproc *rmp;
int r;
vir_bytes v, new_sp;
vir_clicks new_clicks;
rmp = mp;
v = (vir_bytes) m_in.addr;
new_clicks = (vir_clicks) ( ((long) v + CLICK_SIZE - 1) >> CLICK_SHIFT);
if (new_clicks < rmp->mp_seg[D].mem_vir) {
rmp->mp_reply.reply_ptr = (char *) -1;
return(ENOMEM);
}
new_clicks -= rmp->mp_seg[D].mem_vir;
endpoint-aware conversion of servers. 'who', indicating caller number in pm and fs and some other servers, has been removed in favour of 'who_e' (endpoint) and 'who_p' (proc nr.). In both PM and FS, isokendpt() convert endpoints to process slot numbers, returning OK if it was a valid and consistent endpoint number. okendpt() does the same but panic()s if it doesn't succeed. (In PM, this is pm_isok..) pm and fs keep their own records of process endpoints in their proc tables, which are needed to make kernel calls about those processes. message field names have changed. fs drivers are endpoints. fs now doesn't try to get out of driver deadlock, as the protocol isn't supposed to let that happen any more. (A warning is printed if ELOCKED is detected though.) fproc[].fp_task (indicating which driver the process is suspended on) became an int. PM and FS now get endpoint numbers of initial boot processes from the kernel. These happen to be the same as the old proc numbers, to let user processes reach them with the old numbers, but FS and PM don't know that. All new processes after INIT, even after the generation number wraps around, get endpoint numbers with generation 1 and higher, so the first instances of the boot processes are the only processes ever to have endpoint numbers in the old proc number range. More return code checks of sys_* functions have been added. IS has become endpoint-aware. Ditched the 'text' and 'data' fields in the kernel dump (which show locations, not sizes, so aren't terribly useful) in favour of the endpoint number. Proc number is still visible. Some other dumps (e.g. dmap, rs) show endpoint numbers now too which got the formatting changed. PM reading segments using rw_seg() has changed - it uses other fields in the message now instead of encoding the segment and process number and fd in the fd field. For that it uses _read_pm() and _write_pm() which to _taskcall()s directly in pm/misc.c. PM now sys_exit()s itself on panic(), instead of sys_abort(). RS also talks in endpoints instead of process numbers.
2006-03-03 11:20:58 +01:00
if ((r=get_stack_ptr(who_e, &new_sp)) != OK) /* ask kernel for sp value */
panic(__FILE__,"couldn't get stack pointer", r);
r = adjust(rmp, new_clicks, new_sp);
rmp->mp_reply.reply_ptr = (r == OK ? m_in.addr : (char *) -1);
return(r); /* return new address or -1 */
}
/*===========================================================================*
2006-05-11 16:57:23 +02:00
* adjust *
*===========================================================================*/
PUBLIC int adjust(rmp, data_clicks, sp)
register struct mproc *rmp; /* whose memory is being adjusted? */
vir_clicks data_clicks; /* how big is data segment to become? */
vir_bytes sp; /* new value of sp */
{
/* See if data and stack segments can coexist, adjusting them if need be.
* Memory is never allocated or freed. Instead it is added or removed from the
* gap between data segment and stack segment. If the gap size becomes
* negative, the adjustment of data or stack fails and ENOMEM is returned.
*/
register struct mem_map *mem_sp, *mem_dp;
vir_clicks sp_click, gap_base, lower, old_clicks;
int changed, r, ft;
long base_of_stack, delta; /* longs avoid certain problems */
mem_dp = &rmp->mp_seg[D]; /* pointer to data segment map */
mem_sp = &rmp->mp_seg[S]; /* pointer to stack segment map */
changed = 0; /* set when either segment changed */
/* 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 */
2006-05-11 16:57:23 +02:00
if (sp_click >= base_of_stack)
{
return(ENOMEM); /* sp too high */
}
/* Compute size of gap between stack and data segments. */
delta = (long) mem_sp->mem_vir - (long) sp_click;
lower = (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 ((SAFETY_BYTES + CLICK_SIZE - 1) / CLICK_SIZE)
gap_base = mem_dp->mem_vir + data_clicks + SAFETY_CLICKS;
2006-05-11 16:57:23 +02:00
if (lower < gap_base)
{
return(ENOMEM); /* data and stack collided */
}
/* Update data length (but not data orgin) on behalf of brk() system call. */
old_clicks = mem_dp->mem_len;
if (data_clicks != mem_dp->mem_len) {
mem_dp->mem_len = data_clicks;
changed |= DATA_CHANGED;
}
/* Update stack length and origin due to change in stack pointer. */
if (delta > 0) {
mem_sp->mem_vir -= delta;
mem_sp->mem_phys -= delta;
mem_sp->mem_len += delta;
changed |= STACK_CHANGED;
}
/* Do the new data and stack segment sizes fit in the address space? */
ft = (rmp->mp_flags & SEPARATE);
2005-08-05 18:56:02 +02:00
#if (CHIP == INTEL && _WORD_SIZE == 2)
r = size_ok(ft, rmp->mp_seg[T].mem_len, rmp->mp_seg[D].mem_len,
rmp->mp_seg[S].mem_len, rmp->mp_seg[D].mem_vir, rmp->mp_seg[S].mem_vir);
2005-08-05 18:56:02 +02:00
#else
r = (rmp->mp_seg[D].mem_vir + rmp->mp_seg[D].mem_len >
rmp->mp_seg[S].mem_vir) ? ENOMEM : OK;
#endif
if (r == OK) {
endpoint-aware conversion of servers. 'who', indicating caller number in pm and fs and some other servers, has been removed in favour of 'who_e' (endpoint) and 'who_p' (proc nr.). In both PM and FS, isokendpt() convert endpoints to process slot numbers, returning OK if it was a valid and consistent endpoint number. okendpt() does the same but panic()s if it doesn't succeed. (In PM, this is pm_isok..) pm and fs keep their own records of process endpoints in their proc tables, which are needed to make kernel calls about those processes. message field names have changed. fs drivers are endpoints. fs now doesn't try to get out of driver deadlock, as the protocol isn't supposed to let that happen any more. (A warning is printed if ELOCKED is detected though.) fproc[].fp_task (indicating which driver the process is suspended on) became an int. PM and FS now get endpoint numbers of initial boot processes from the kernel. These happen to be the same as the old proc numbers, to let user processes reach them with the old numbers, but FS and PM don't know that. All new processes after INIT, even after the generation number wraps around, get endpoint numbers with generation 1 and higher, so the first instances of the boot processes are the only processes ever to have endpoint numbers in the old proc number range. More return code checks of sys_* functions have been added. IS has become endpoint-aware. Ditched the 'text' and 'data' fields in the kernel dump (which show locations, not sizes, so aren't terribly useful) in favour of the endpoint number. Proc number is still visible. Some other dumps (e.g. dmap, rs) show endpoint numbers now too which got the formatting changed. PM reading segments using rw_seg() has changed - it uses other fields in the message now instead of encoding the segment and process number and fd in the fd field. For that it uses _read_pm() and _write_pm() which to _taskcall()s directly in pm/misc.c. PM now sys_exit()s itself on panic(), instead of sys_abort(). RS also talks in endpoints instead of process numbers.
2006-03-03 11:20:58 +01:00
int r2;
if (changed && (r2=sys_newmap(rmp->mp_endpoint, rmp->mp_seg)) != OK)
panic(__FILE__,"couldn't sys_newmap in adjust", r2);
return(OK);
}
/* New sizes don't fit or require too many page/segment registers. Restore.*/
if (changed & DATA_CHANGED) mem_dp->mem_len = old_clicks;
if (changed & STACK_CHANGED) {
mem_sp->mem_vir += delta;
mem_sp->mem_phys += delta;
mem_sp->mem_len -= delta;
}
return(ENOMEM);
}
2005-08-05 18:56:02 +02:00
#if (CHIP == INTEL && _WORD_SIZE == 2)
/*===========================================================================*
* size_ok *
*===========================================================================*/
PUBLIC int size_ok(file_type, tc, dc, sc, dvir, s_vir)
int file_type; /* SEPARATE or 0 */
vir_clicks tc; /* text size in clicks */
vir_clicks dc; /* data size in clicks */
vir_clicks sc; /* stack size in clicks */
vir_clicks dvir; /* virtual address for start of data seg */
vir_clicks s_vir; /* virtual address for start of stack seg */
{
/* Check to see if the sizes are feasible and enough segmentation registers
* exist. On a machine with eight 8K pages, text, data, stack sizes of
* (32K, 16K, 16K) will fit, but (33K, 17K, 13K) will not, even though the
* former is bigger (64K) than the latter (63K). Even on the 8088 this test
* is needed, since the data and stack may not exceed 4096 clicks.
2005-09-23 22:46:17 +02:00
* Note this is not used for 32-bit Intel Minix, the test is done in-line.
*/
int pt, pd, ps; /* segment sizes in pages */
pt = ( (tc << CLICK_SHIFT) + PAGE_SIZE - 1)/PAGE_SIZE;
pd = ( (dc << CLICK_SHIFT) + PAGE_SIZE - 1)/PAGE_SIZE;
ps = ( (sc << CLICK_SHIFT) + PAGE_SIZE - 1)/PAGE_SIZE;
if (file_type == SEPARATE) {
if (pt > MAX_PAGES || pd + ps > MAX_PAGES) return(ENOMEM);
} else {
if (pt + pd + ps > MAX_PAGES) return(ENOMEM);
}
if (dvir + dc > s_vir) return(ENOMEM);
return(OK);
}
2005-08-05 18:56:02 +02:00
#endif