From b1d8a9aa0d80fced013dc216f20f725e570256f9 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Wed, 28 Feb 2001 08:33:40 +0000 Subject: 2001-02-27 Roland McGrath Use a new, more flexible RPC interface to the default pager. This allows swapon to do its own interpretation of Linux swap signature pages, and to support partial-device stores such as store_open will return for regular files on disk filesystems. * swapon.c: Include , not . (ignore_signature, require_signature, quiet): New variables. (options): Add --silent, --no-signature, --require-signature. (main: parse_opt): Parse them. (check_signature): New function. (swaponoff): Use it unless --no-signature set, to get a remapped store using the pages indicated by the Linux signature page. Try new default_pager_paging_storage RPC first, using a possibly-remapped device store. If the default pager does not support that, fall back to the old protocol that only supports whole devices (and wherein the default pager itself interprets a Linux signature page). * Makefile (swapon, swapoff): Depend on default_pagerUser.o. --- sutils/Makefile | 4 +- sutils/swapon.c | 373 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 358 insertions(+), 19 deletions(-) diff --git a/sutils/Makefile b/sutils/Makefile index 5b5c2110..be2da539 100644 --- a/sutils/Makefile +++ b/sutils/Makefile @@ -1,6 +1,6 @@ # Makefile for sutils # -# Copyright (C) 1996,97,99 Free Software Foundation, Inc. +# Copyright (C) 1996,97,99,2000 Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as @@ -34,7 +34,7 @@ HURDLIBS = store shouldbeinlibc include ../Makeconf fsck: fstab.o clookup.o -swapon swapoff: ../libstore/libstore.a +swapon swapoff: ../libstore/libstore.a default_pagerUser.o $(progs): %: %.o ../libshouldbeinlibc/libshouldbeinlibc.a install: $(prefix)/dev $(prefix)/dev/MAKEDEV diff --git a/sutils/swapon.c b/sutils/swapon.c index fb264bc6..a1fc001a 100644 --- a/sutils/swapon.c +++ b/sutils/swapon.c @@ -1,6 +1,6 @@ /* Add/remove paging devices - Copyright (C) 1997,98,99 Free Software Foundation, Inc. + Copyright (C) 1997,98,99,2000,2001 Free Software Foundation, Inc. Written by Miles Bader This file is part of the GNU Hurd. @@ -22,12 +22,15 @@ #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); @@ -35,10 +38,18 @@ const char *argp_program_version = STANDARD_HURD_VERSION (swapoff); 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..."; @@ -49,6 +60,254 @@ static char *doc = "Stop paging on DEVICE..."; 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, %uk 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 %u 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 ("%dk swap-space (%dk bad, %dk wasted at end)", + freepages * (LINUX_PAGE_SIZE / 1024), + bad * (LINUX_PAGE_SIZE / 1024), + waste * (LINUX_PAGE_SIZE / 1024)); + else + inform_2_0 ("%dk swap-space (%dk wasted at end)", + freepages * (LINUX_PAGE_SIZE / 1024), + waste * (LINUX_PAGE_SIZE / 1024)); + } + else if (bad > 0) + inform_2_0 ("%dk swap-space (excludes %dk marked bad)", + freepages * (LINUX_PAGE_SIZE / 1024), + bad * (LINUX_PAGE_SIZE / 1024)); + else + inform_2_0 ("%dk 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 %uk marked bad and %uk at end of partition)", + badk, wastek); + else if (badk) + inform_2_2 (" (excludes %uk marked bad)", badk); + else if (wastek) + inform_2_2 (" (excludes %uk 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) { @@ -56,7 +315,10 @@ swaponoff (const char *file, int add) 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) { @@ -64,15 +326,45 @@ swaponoff (const char *file, int add) return err; } - if (store->class != &store_device_class) + /* Let's see what we've got. */ + if (old_protocol) { - error (0, 0, "%s: Can't get device", file); - return err; + /* 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; } - if (! (store->flags & STORE_ENFORCED)) + else if (ignore_signature) + verbose ("%s: %uk swap space", + file, (unsigned int) (store->size / 1024)); + else { - error (0, 0, "%s: Can only page to the entire device", file); - return err; + /* 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) @@ -91,23 +383,57 @@ swaponoff (const char *file, int add) error (14, 0, "No default pager (memory manager) is running!"); } - { - char pname[sizeof "/dev/" + strlen (store->name) + 1]; - snprintf (pname, sizeof pname, "/dev/%s", store->name); - err = default_pager_paging_file (def_pager, dev_master, pname, add); - if (err) - error (0, err, "%s", file); - } + 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[]) +main (int argc, char *argv[]) { /* Parse our options... */ error_t parse_opt (int key, char *arg, struct argp_state *state) @@ -118,6 +444,19 @@ main(int argc, char *argv[]) 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 @@ -134,7 +473,7 @@ main(int argc, char *argv[]) } struct argp argp = {options, parse_opt, args_doc, doc}; - argp_parse (&argp, argc, argv, 0, 0, 0); + argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0); if (do_all) { -- cgit v1.2.3