#include "std.h"
#include "swap.h"
#include "file_incl.h"
#include "simul_efun.h"
#include "master.h"
#include "comm.h"
#include "md.h"
#include "file.h"
#include "port.h"
/*
* Swap out programs from objects.
*
* Todo:
* Separate objects/programs, so that they can swapped out
* independently. This way even inherited programs could
* be swapped, at the expense of not swapping entire program.
*/
static int num_swapped;
static int total_bytes_swapped;
static int line_num_bytes_swapped;
static char file_name_buf[100];
static char *file_name = file_name_buf;
/* The swap file is opened once */
#ifdef SWAP_USE_FD
static int swap_file;
#else
static FILE *swap_file;
#endif
#define DIFF(x, y) ((char *)(x) - (char *)(y))
#define ADD(x, y) (&(((char *)(y))[(POINTER_INT)x]))
typedef struct sw_block_s {
int start;
int length;
struct sw_block_s *next;
} sw_block_t;
static sw_block_t *swap_free;
static int last_data;
static int assert_swap_file PROT((void));
static int alloc_swap PROT((int));
static void free_swap PROT((int start, int length));
static int swap_in PROT((char **, int));
static int swap_out PROT((char *, int, int *));
/**
** General swapping routines.
**/
/*
* Make sure swap file is opened.
*/
static int assert_swap_file()
{
if (swap_file == NULL) {
#ifdef SWAP_USE_FD
char host[50];
gethostname(host, sizeof host);
sprintf(file_name_buf, "%s.%s.%d", SWAP_FILE, host,
external_port[0].port);
file_name = file_name_buf;
if (file_name[0] == '/')
file_name++;
swap_file = open(file_name, O_RDWR | O_CREAT | O_TRUNC);
#else
char host[50];
gethostname(host, sizeof host);
sprintf(file_name_buf, "%s.%s.%d", SWAP_FILE, host,
external_port[0].port);
file_name = file_name_buf;
if (file_name[0] == '/')
file_name++;
swap_file = fopen(file_name, "w+b");
#endif
swap_free = 0;
last_data = 0;
/* Leave this file pointer open ! */
if (swap_file == 0)
return 0;
}
return 1;
}
/*
* Seek the swap file
*/
static void
swap_seek P2(long, offset, int, flag) {
int ret;
do {
#ifdef SWAP_USE_FD
ret = lseek(swap_file, offset, flag);
#else
ret = fseek(swap_file, offset, flag);
#endif
} while (ret == -1 && errno == EINTR);
if (ret == -1)
fatal("Couldn't seek the swap file, error %s, offset %d.\n",
port_strerror(errno), offset);
}
/*
* Find a position to swap to, using free blocks if possible.
* 'length' is the size we need.
*
* Todo - think about better free block allocation methods
*/
static int alloc_swap P1(int, length)
{
sw_block_t *ptr, *prev;
int ret;
/*
* Doing first fit. (next fit might work nicely if next pointer is reset
* when a block is freed, anticipating a new allocation of the same size)
*/
for (ptr = swap_free, prev = 0; ptr; prev = ptr, ptr = ptr->next) {
if (ptr->length < length)
continue;
/*
* found a block, update free list
*/
ret = ptr->start;
ptr->start += length;
ptr->length -= length;
if (ptr->length == 0) {
/*
* exact fit, remove free block from list
*/
if (!prev)
swap_free = ptr->next;
else
prev->next = ptr->next;
FREE(ptr);
}
return ret;
}
/*
* no appropriate blocks found, go to end of file
*/
return last_data;
}
/*
* Free up a chunk of swap space, coalescing free blocks if necessary.
*
* Todo - think about tradeoff of storing the free block
* info in the swap file itself.
*/
static void free_swap P2(int, start, int, length)
{
sw_block_t *m, *ptr, *prev;
length += sizeof(int); /* extend with size of hidden information */
/*
* Construct and insert new free block
*/
m = (sw_block_t *) DXALLOC(sizeof(sw_block_t), TAG_SWAP, "free_swap");
m->start = start;
m->length = length;
for (ptr = swap_free, prev = 0; ptr; prev = ptr, ptr = ptr->next) {
if (start < ptr->start)
break;
}
if (!prev) {
swap_free = m;
} else {
prev->next = m;
}
m->next = ptr;
/*
* Combine adjacent blocks
*/
if (ptr && m->start + m->length == ptr->start) {
m->length += ptr->length;
m->next = ptr->next;
FREE(ptr);
}
if (prev && prev->start + prev->length == m->start) {
prev->length += m->length;
prev->next = m->next;
FREE(m);
m = prev;
}
/*
* There is an implicit infinite block at the end making life hard Can't
* do this earlier, since m and prev could have combined, so prev must be
* found again (or use doubly linked list, etc).
*/
if (m->start + m->length == last_data) {
DEBUG_CHECK(m->next, "extraneous free swap blocks!\n");
/* find prev pointer again *sigh* */
for (ptr = swap_free, prev = 0; ptr != m; prev = ptr, ptr = ptr->next);
last_data = m->start;
FREE(m);
if (!prev)
swap_free = 0;
else
prev->next = 0;
}
}
/*
* Actually swap something out.
* block - the memory to swap out
* size - how big it is
* locp - the swap location is written to the int this points to
*/
static int
swap_out P3(char *, block, int, size, int *, locp)
{
if (!block || time_to_swap == 0)
return 0;
if (!assert_swap_file())
return 0;
if (*locp == -1) { /* needs data written out */
*locp = alloc_swap(size + sizeof size);
swap_seek(*locp, 0);
#ifdef SWAP_USE_FD
if ((write(swap_file, &size, sizeof size) != sizeof size) ||
write(swap_file, block, size) != size) {
debug_perror("swap_out", swap_file);
*locp = -1;
return 0;
}
#else
if (fwrite((char *) &size, sizeof size, 1, swap_file) != 1 ||
fwrite(block, size, 1, swap_file) != 1) {
debug_perror("swap_out:swap file", 0);
*locp = -1;
return 0;
}
#endif
if (*locp >= last_data)
last_data = *locp + sizeof size + size;
}
total_bytes_swapped += size;/* also count sizeof int?? */
return 1;
}
/*
* Read something back in from swap. Return the size.
* blockp - a pointer to what will hold the block read in
* loc - position in the swap file to read from
*/
static int
swap_in P2(char **, blockp, int, loc)
{
int size;
DEBUG_CHECK(!blockp, "blockp null in swap_in()\n");
if (loc == -1)
return 0;
swap_seek(loc, 0);
#ifdef SWAP_USE_FD
/* find out size */
if (read(swap_file, &size, sizeof size) == -1)
fatal("Couldn't read the swap file.\n");
DEBUG_CHECK(size <= 0, "Illegal size read from swap file.\n");
*blockp = DXALLOC(size, TAG_SWAP, "swap_in");
if (read(swap_file, *blockp, size) == -1)
fatal("Couldn't read the swap file.\n");
#else
/* find out size */
if (fread((char *) &size, sizeof size, 1, swap_file) == -1)
fatal("Couldn't read the swap file.\n");
DEBUG_CHECK(size <= 0, "Illegal size read from swap file.\n");
*blockp = DXALLOC(size, TAG_SWAP, "swap_in");
if (fread(*blockp, size, 1, swap_file) == -1)
fatal("Couldn't read the swap file.\n");
#endif
total_bytes_swapped -= size;
return size;
}
/**
** Routines to swap/load specific things.
**/
/*
* marion - adjust pointers for swap out and later relocate on swap in
* program
* functions
* strings
* variable_names
* inherit
* argument_types
* type_start
*/
int
locate_out P1(program_t *, prog)
{
if (!prog)
return 0;
debug(d_flag, ("locate_out: %p %p %p %p %p %p %p\n",
prog->program, prog->function_table,
prog->strings, prog->variable_table, prog->inherit,
prog->argument_types, prog->type_start));
prog->program = (char *)DIFF(prog->program, prog);
prog->function_table = (function_t *)DIFF(prog->function_table, prog);
prog->function_flags = (unsigned short *)DIFF(prog->function_flags, prog);
prog->strings = (char **)DIFF(prog->strings, prog);
prog->variable_table = (char **)DIFF(prog->variable_table, prog);
prog->variable_types = (unsigned short *)DIFF(prog->variable_types, prog);
prog->inherit = (inherit_t *)DIFF(prog->inherit, prog);
prog->classes = (class_def_t *)DIFF(prog->classes, prog);
prog->class_members = (class_member_entry_t *)DIFF(prog->class_members, prog);
if (prog->type_start) {
prog->argument_types = (unsigned short *)DIFF(prog->argument_types, prog);
prog->type_start = (unsigned short *)DIFF(prog->type_start, prog);
}
return 1;
}
/*
* marion - relocate pointers after swap in
* program
* functions
* strings
* variable_names
* inherit
* argument_types
* type_start
*/
int
locate_in P1(program_t *, prog)
{
if (!prog)
return 0;
prog->program = ADD(prog->program, prog);
prog->function_table = (function_t *)ADD(prog->function_table, prog);
prog->function_flags = (unsigned short *)ADD(prog->function_flags, prog);
prog->strings = (char **)ADD(prog->strings, prog);
prog->variable_table = (char **)ADD(prog->variable_table, prog);
prog->variable_types = (unsigned short *)ADD(prog->variable_types, prog);
prog->inherit = (inherit_t *)ADD(prog->inherit, prog);
prog->classes = (class_def_t *)ADD(prog->classes, prog);
prog->class_members = (class_member_entry_t *)ADD(prog->class_members, prog);
if (prog->type_start) {
prog->argument_types = (unsigned short *)ADD(prog->argument_types, prog);
prog->type_start = (unsigned short *)ADD(prog->type_start, prog);
}
debug(d_flag, ("locate_in: %p %p %p %p %p %p %p\n",
prog->program, prog->function_table,
prog->strings, prog->variable_table, prog->inherit,
prog->argument_types, prog->type_start));
return 1;
}
/*
* Swap out an object. Only the program is swapped, not the 'object_t'.
*
* marion - the swap seems to corrupt the function table
*/
int swap P1(object_t *, ob)
{
/* the simuls[] table uses pointers to the functions so the simul_efun
* program cannot be relocated. locate_in() could be changed to
* correct this or simuls[] could use offsets, but it doesn't seem
* worth it just to get the simul_efun object to swap. Maybe later.
*
* Ditto the master object and master_applies[]. Mudlibs that have
* a period TIME_TO_SWAP between successive master applies must be
* extremely rare ...
*/
if (ob == simul_efun_ob || ob == master_ob) return 0;
if (ob->flags & O_DESTRUCTED)
return 0;
debug(d_flag, ("Swap object /%s (ref %d)", ob->name, ob->ref));
if (ob->prog->line_info)
swap_line_numbers(ob->prog); /* not always done before we get here */
if ((ob->flags & O_HEART_BEAT) || (ob->flags & O_CLONE)) {
debug(d_flag, (" object not swapped - heart beat or cloned."));
return 0;
}
if (ob->prog->ref > 1 || ob->interactive) {
debug(d_flag, (" object not swapped - inherited or interactive or in apply_low() cache."));
return 0;
}
if (ob->prog->func_ref > 0) {
debug(d_flag, (" object not swapped - referenced by functions."));
return 0;
}
locate_out(ob->prog); /* relocate the internal pointers */
if (swap_out((char *) ob->prog, ob->prog->total_size, (int *) &ob->swap_num)) {
num_swapped++;
free_prog(ob->prog, 0); /* Do not free the strings */
ob->prog = 0;
ob->flags |= O_SWAPPED;
return 1;
} else {
locate_in(ob->prog);
return 0;
}
}
void load_ob_from_swap P1(object_t *, ob)
{
if (ob->swap_num == -1)
fatal("Loading not swapped object.\n");
debug(d_flag, ("Unswap object /%s (ref %d)", ob->name, ob->ref));
swap_in((char **) &ob->prog, ob->swap_num);
SET_TAG(ob->prog, TAG_PROGRAM);
/*
* to be relocated: program functions strings variable_names inherit
* argument_types type_start
*/
locate_in(ob->prog); /* relocate the internal pointers */
/* The reference count will already be 1 ! */
ob->flags &= ~O_SWAPPED;
num_swapped--;
total_prog_block_size += ob->prog->total_size;
total_num_prog_blocks += 1;
}
/*
* Swap out line number information.
*/
int
swap_line_numbers P1(program_t *, prog)
{
int size;
if (!prog || !prog->line_info)
return 0;
debug(d_flag, ("Swap line numbers for /%s", prog->name));
size = prog->file_info[0];
if (swap_out((char *) prog->file_info, size,
&prog->line_swap_index)) {
line_num_bytes_swapped += size;
FREE(prog->file_info);
prog->file_info = 0;
prog->line_info = 0;
return 1;
}
return 0;
}
/*
* Reload line number information from swap.
*/
void load_line_numbers P1(program_t *, prog)
{
int size;
if (prog->line_info)
return;
debug(d_flag, ("Unswap line numbers for /%s\n", prog->name));
size = swap_in((char **) &prog->file_info, prog->line_swap_index);
SET_TAG(prog->file_info, TAG_LINENUMBERS);
prog->line_info = (unsigned char *)&prog->file_info[prog->file_info[1]];
line_num_bytes_swapped -= size;
}
/**
** Misc. routines.
**/
/*
* Remove the swap space associated with this object.
*/
void remove_swap_file P1(object_t *, ob)
{
if (!ob)
return;
/* may be swapped out, so swap in to get size, update stats, etc */
if (ob->flags & O_SWAPPED)
load_ob_from_swap(ob);
if (ob->prog)
free_swap(ob->swap_num, ob->prog->total_size);
ob->swap_num = -1;
}
/*
* Same as above, but to remove line_number swap space.
*/
void
remove_line_swap P1(program_t *, prog)
{
if (!prog->line_info)
load_line_numbers(prog);
if (prog->line_swap_index != -1 && prog->line_info)
free_swap(prog->line_swap_index,
prog->file_info[0]);
prog->line_swap_index = -1;
}
void print_swap_stats P1(outbuffer_t *, out)
{
int size, cnt, end;
sw_block_t *m;
outbuf_add(out, "Swap information:\n");
outbuf_add(out, "-------------------------\n");
outbuf_addv(out, "Progs swapped: %10lu\n", num_swapped);
outbuf_addv(out, "Linenum bytes: %10lu\n", line_num_bytes_swapped);
outbuf_addv(out, "Total bytes swapped: %10lu\n", total_bytes_swapped);
if (!swap_file) {
outbuf_add(out, "No swap file\n");
return;
}
size = cnt = 0;
for (m = swap_free; m; size += m->length, cnt++, m = m->next);
swap_seek(0, 2);
#ifdef SWAP_USE_FD
end = tell(swap_file) - last_data;
#else
end = ftell(swap_file) - last_data;
#endif
if (end) {
size += end;
cnt++;
}
outbuf_addv(out, "Freed bytes: %10lu (%d chunks)\n", size, cnt);
}
/*
* This one is called at shutdown. Remove the swap file.
*/
void unlink_swap_file()
{
if (swap_file == 0)
return;
#ifdef SWAP_USE_FD
close(swap_file);
unlink(file_name);
#else
unlink(file_name); /* why is this backwards ? */
fclose(swap_file);
#endif
}