#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 }