/* Add/remove paging devices Copyright (C) 1997,98,99,2000,01,02 Free Software Foundation, Inc. Written by Miles Bader This file is part of the GNU Hurd. The GNU Hurd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. The GNU Hurd is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "default_pager_U.h" #ifdef SWAPOFF const char *argp_program_version = STANDARD_HURD_VERSION (swapoff); #else const char *argp_program_version = STANDARD_HURD_VERSION (swapon); #endif static int ignore_signature, require_signature, quiet; static struct argp_option options[] = { {"standard", 'a', 0, 0, "Use all devices marked as `swap' in " _PATH_MNTTAB}, {"no-signature",'n', 0, 0, "Do not check for a Linux swap signature page"}, {"require-signature", 's', 0, 0, "Require a Linux swap signature page"}, {"silent", 'q', 0, 0, "Print only diagnostic messages"}, {"quiet", 'q', 0, OPTION_ALIAS | OPTION_HIDDEN }, {0, 0} }; static char *args_doc = "DEVICE..."; #ifdef SWAPOFF static char *doc = "Stop paging on DEVICE..."; #else static char *doc = "Start paging onto DEVICE..."; #endif #define verbose(fmt, arg...) \ if (quiet_now) ((void)0); else error (0, 0, fmt ,##arg) #define inform_2_0(fmt, arg...) \ verbose ("%s: Linux 2.0 swap signature, " fmt, name ,##arg) #define inform_2_2(fmt, arg...) \ verbose ("%s: Linux 2.2 swap signature v1, %zuk swap-space" fmt, \ name, freepages * (LINUX_PAGE_SIZE / 1024) ,##arg) /* Examine the store in *STOREP to see if it has a Linux-compatible swap signature page as created by the Linux `mkswap' utility. If we find such a signature, it indicates some subset of the store that should actually be used for paging; return zero after consuming *STOREP and replacing it by using store_remap to get just the indicated subset (unless NO_REMAP is nonzero). If we get an error reading the store, or find a signature but have some problem with it, return some error code. If the store has no signature at all, and if --require-signature was given, then that is an error. This function prints diagnostics for all those errors. Otherwise (the store has no signature) we return EFTYPE and print nothing. */ static error_t check_signature (const char *name, struct store **storep, int no_remap, int quiet_now) { struct store *const store = *storep; #define LINUX_PAGE_SIZE 4096 /* size of pages in Linux swap partitions */ #define LINUX_PAGE_SHIFT 12 /* RUNS points to latest run (highest value of start). Each time we remove a bad page from the set, we either adjust the latest run or add a new one and point RUNS at it. */ struct run { struct run *next; size_t start, limit; /* in units of LINUX_PAGE_SIZE */ }; size_t freepages = store->size / LINUX_PAGE_SIZE; struct run first_run = { NULL, 0, freepages }, *runs = &first_run; size_t nruns = 1; /* This is always called with increasing page numbers. */ #define BAD_PAGE(pageno) \ ({ \ size_t page = (pageno); \ if (page == runs->start) \ runs->start = page + 1; \ else \ { \ runs->next = alloca (sizeof *runs); \ runs->next->start = page + 1; \ runs->next->limit = runs->limit; \ runs->limit = page; \ ++nruns; \ } \ }) /* Read the first page, which contains the signature. */ void *buf = 0; size_t len = 0; error_t err = store_read (store, 0, LINUX_PAGE_SIZE, &buf, &len); if (err) { error (0, err, "%s: cannot read Linux swap signature page", name); return err; } if (len < LINUX_PAGE_SIZE) { error (0, 0, "%s: short read %zu reading Linux swap signature page", name, len); return EINVAL; } quiet_now |= quiet; /* Check for Linux 2.0 format. */ if (!memcmp ("SWAP-SPACE", buf + LINUX_PAGE_SIZE-10, 10)) { /* The partition's first page has a Linux swap signature. This means the beginning of the page contains a bitmap of good pages, and all others are bad. */ size_t i, bad, max; int waste; /* The first page, and the pages corresponding to the bits occupied by the signature in the final 10 bytes of the page, are always unavailable ("bad"). */ *(uint32_t *) buf &= ~(u_int32_t) 1; memset (buf + LINUX_PAGE_SIZE-10, 0, 10); max = LINUX_PAGE_SIZE / sizeof (uint32_t); if (max > (store->size + 31) / 32) max = (store->size + 31) / 32; /* Search the page for zero bits, which indicate unusable pages. */ bad = 0; for (i = 0; i < max; ++i) { size_t p = i*32; uint32_t bm = ~((uint32_t *) buf)[i]; while (bm != 0) /* inverted so unusable pages are one bits */ { /* Find the first bit set in this word. */ int bit = ffs (bm); bm >>= bit; /* Next time look at the rest of the word. */ p += bit - 1; /* Corresponding page. */ if (p >= runs->limit) break; ++bad; BAD_PAGE (p); } } freepages -= bad; --bad; /* Don't complain about first page. */ waste = (store->size >> LINUX_PAGE_SHIFT) - (8 * (LINUX_PAGE_SIZE-10)); if (waste > 0) { /* The wasted pages were already marked "bad". */ bad -= waste; if (bad > 0) inform_2_0 ("%zdk swap-space (%zdk bad, %dk wasted at end)", freepages * (LINUX_PAGE_SIZE / 1024), bad * (LINUX_PAGE_SIZE / 1024), waste * (LINUX_PAGE_SIZE / 1024)); else inform_2_0 ("%zdk swap-space (%dk wasted at end)", freepages * (LINUX_PAGE_SIZE / 1024), waste * (LINUX_PAGE_SIZE / 1024)); } else if (bad > 0) inform_2_0 ("%zdk swap-space (excludes %zdk marked bad)", freepages * (LINUX_PAGE_SIZE / 1024), bad * (LINUX_PAGE_SIZE / 1024)); else inform_2_0 ("%zdk swap-space", freepages * (LINUX_PAGE_SIZE / 1024)); } /* Check for Linux 2.2 format. */ else if (!memcmp ("SWAPSPACE2", buf + LINUX_PAGE_SIZE-10, 10)) { struct { u_int8_t bootbits[1024]; u_int32_t version; u_int32_t last_page; u_int32_t nr_badpages; u_int32_t padding[125]; u_int32_t badpages[1]; } *hdr = buf; ++first_run.start; /* first page unusable */ --freepages; switch (hdr->version) { default: error (0, 0, "%s: Linux 2.2 swap signature with unknown version %u", name, hdr->version); munmap (buf, len); if (require_signature) { error (0, 0, "%s: will not use without valid signature page", name); return EINVAL; } error (0, 0, "WARNING: ignoring unrecognized signature page"); return EFTYPE; case 1: { unsigned int waste, i; if (hdr->last_page >= first_run.limit) { error (0, 0, "%s: signature says %uk, partition has only %uk!", name, hdr->last_page * (LINUX_PAGE_SIZE / 1024), (unsigned int) (store->size / 1024)); waste = 0; } else { waste = first_run.limit + 1 - hdr->last_page; freepages = first_run.limit - first_run.start; first_run.limit = hdr->last_page + 1; } for (i = 0; i < hdr->nr_badpages; ++i) { BAD_PAGE (hdr->badpages[i]); --freepages; } { size_t badk = hdr->nr_badpages * (LINUX_PAGE_SIZE / 1024); size_t wastek = waste * (LINUX_PAGE_SIZE / 1024); if (badk && wastek) inform_2_2 ("\ (excludes %zuk marked bad and %zuk at end of partition)", badk, wastek); else if (badk) inform_2_2 (" (excludes %zuk marked bad)", badk); else if (wastek) inform_2_2 (" (excludes %zuk at end of partition)", wastek); else inform_2_2 (""); } } } } /* There does not appear to be any signature page here. */ else if (require_signature) { error (0, 0, "%s: will not use without Linux swap signature", name); return EINVAL; } else /* We use this error code to tell our caller that we found nothing. */ return EFTYPE; /* Now that we have collected the runs of LINUX_PAGE_SIZE we will use, convert those into store_run's in the store's block size. */ { const int scale = LINUX_PAGE_SHIFT - store->log2_block_size; struct store_run store_runs[nruns]; size_t i = 0; struct run *r = &first_run; do { struct store_run *sr = &store_runs[i++]; sr->start = (store_offset_t) r->start << scale; sr->length = (r->limit - r->start) << scale; do r = r->next; while (r != 0 && r->start == r->limit); /* skip empty runs */ } while (r != 0); /* Give us a new store that uses only the good pages. */ return store_remap (store, store_runs, i, storep); } } /* Process a single argument file. */ static int swaponoff (const char *file, int add) { error_t err; struct store *store; static mach_port_t def_pager = MACH_PORT_NULL; static mach_port_t dev_master = MACH_PORT_NULL; static int old_protocol; int quiet_now = 0; try_again: err = store_open (file, 0, 0, &store); if (err) { error (0, err, "%s", file); return err; } /* Let's see what we've got. */ if (old_protocol) { /* The default pager only lets us give a whole partition, and it will read the signature page (but not insist on it). */ if (! (store->flags & STORE_ENFORCED)) { error (0, 0, "%s: Can only page to the entire device", file); return EINVAL; } /* If we want to require the signature, we can check that it is actually there even though we won't be the one interpreting it. */ if (require_signature && check_signature (file, &store, 1, quiet_now) != 0) return EINVAL; } else if (ignore_signature) verbose ("%s: %uk swap space", file, (unsigned int) (store->size / 1024)); else { /* Adjust the store according to the Linux signature. */ err = check_signature (file, &store, 0, 0); if (err == EFTYPE) verbose ("%s: %uk swap space (no Linux signature page)", file, (unsigned int) (store->size / 1024)); else if (err) { store_free (store); return err; } /* Otherwise check_signature printed something out. */ } if (store->class != &store_device_class) { error (0, 0, "%s: Can't get underlying device", file); store_free (store); return EINVAL; } if (def_pager == MACH_PORT_NULL) { mach_port_t host; err = get_privileged_ports (&host, &dev_master); if (err) error (12, err, "Cannot get host port"); err = vm_set_default_memory_manager (host, &def_pager); mach_port_deallocate (mach_task_self (), host); if (err) error (13, err, "Cannot get default pager port"); if (def_pager == MACH_PORT_NULL) error (14, 0, "No default pager (memory manager) is running!"); } if (old_protocol) { /* The default pager does not support the new protocol. We tried it in a previous call (below) and got MIG_BAD_ID. */ char pname[sizeof "/dev/" + strlen (store->name) + 1]; strcpy (stpcpy (pname, "/dev/"), store->name); err = default_pager_paging_file (def_pager, dev_master, pname, add); } else { /* Try the new protocol, which will take our list of runs. */ recnum_t runs[store->num_runs * 2]; size_t i, j; for (i = j = 0; i < store->num_runs; ++i) { runs[j++] = store->runs[i].start; runs[j++] = store->runs[i].length; } err = default_pager_paging_storage (def_pager, store->port, runs, j, store->name, add); if (err == MIG_BAD_ID) { /* The default pager does not support the new protocol. We'll do the whole thing over again, since we have different requirements now. */ old_protocol = 1; store_free (store); if (! ignore_signature) error (0, 0, "\ default pager uses old protocol, does its own signature checking"); quiet_now = 1; goto try_again; } } store_free (store); if (err) error (0, err, "%s", file); return err; } #undef inform_2_0 #undef inform_2_2 #undef verbose static int do_all; int main (int argc, char *argv[]) { /* Parse our options... */ error_t parse_opt (int key, char *arg, struct argp_state *state) { switch (key) { case 'a': do_all = 1; break; case 'n': ignore_signature = 1; break; case 's': require_signature = 1; ignore_signature = 0; break; case 'q': quiet = 1; break; case ARGP_KEY_ARG: #ifdef SWAPOFF #define ONOFF 0 #else #define ONOFF 1 #endif swaponoff (arg, ONOFF); break; default: return ARGP_ERR_UNKNOWN; } return 0; } struct argp argp = {options, parse_opt, args_doc, doc}; argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0); if (do_all) { struct mntent *me; FILE *f; f = setmntent (_PATH_MNTTAB, "r"); if (f == NULL) error (1, errno, "Cannot read %s", _PATH_MNTTAB); else { int done = 0, err = 0; while ((me = getmntent (f)) != NULL) if (!strcmp (me->mnt_type, MNTTYPE_SWAP)) { done = 1; err |= swaponoff (me->mnt_fsname, ONOFF); } if (done == 0) error (2, 0, "No swap partitions found in %s", _PATH_MNTTAB); else if (err) return 1; } } return 0; }