Fix a bug in the block number calculation for VN disks with a sector

size != 512 that are configured without a label.  The bug should only
    have effected swap-backed VN mounts without a label.

    Add several major features to VN.  In the kernel we add a swap
    pre-reservation capability, which can be used to guarentee seek
    consistency for swap-backed VN nodes.  This also incidently allows
    a swap-backed VN filesystem to be recovered after a crash in some
    cases (if the same swap blocks happen to be reserved).

    We also add a number of new options to vnconfig which do the work
    of pre-zeroing or creating/truncating/extending a file which greatly
    simplifies using VN in a file-backed configuration.

    Add FreeBSD CVS label to sys/sys/vnioctl.h, as well as a new ioctl
    flag for the swap pre-reservation feature.

Reviewed by:	Alan Cox <alc@cs.rice.edu>, David Greenman <dg@root.com>
This commit is contained in:
Matthew Dillon 1999-09-17 05:34:00 +00:00
parent 24579ca1d7
commit 447deba3da
5 changed files with 173 additions and 21 deletions

View File

@ -157,6 +157,7 @@ static SLIST_HEAD(, vn_softc) vn_list;
static u_long vn_options;
#define IFOPT(vn,opt) if (((vn)->sc_options|vn_options) & (opt))
#define TESTOPT(vn,opt) (((vn)->sc_options|vn_options) & (opt))
static int vnsetcred (struct vn_softc *vn, struct ucred *cred);
static void vnclear (struct vn_softc *vn);
@ -208,14 +209,23 @@ vnfindvn(dev_t dev)
static int
vnopen(dev_t dev, int flags, int mode, struct proc *p)
{
int unit;
struct vn_softc *vn;
unit = dkunit(dev);
vn = dev->si_drv1;
if (!vn)
/*
* Locate preexisting device
*/
if ((vn = dev->si_drv1) == NULL)
vn = vnfindvn(dev);
/*
* Update si_bsize fields for device. This data will be overriden by
* the slice/parition code for vn accesses through partitions, and
* used directly if you open the 'whole disk' device.
*/
dev->si_bsize_phys = vn->sc_secsize;
dev->si_bsize_best = vn->sc_secsize;
if (flags & FWRITE && vn->sc_flags & VNF_READONLY)
return (EACCES);
@ -223,6 +233,10 @@ vnopen(dev_t dev, int flags, int mode, struct proc *p)
printf("vnopen(%s, 0x%x, 0x%x, %p)\n",
devtoname(dev), flags, mode, (void *)p);
/*
* Initialize label
*/
IFOPT(vn, VN_LABELS) {
if (vn->sc_flags & VNF_INITED) {
struct disklabel label;
@ -241,8 +255,9 @@ vnopen(dev_t dev, int flags, int mode, struct proc *p)
}
if (dkslice(dev) != WHOLE_DISK_SLICE ||
dkpart(dev) != RAW_PART ||
mode != S_IFCHR)
mode != S_IFCHR) {
return (ENXIO);
}
}
return(0);
}
@ -295,9 +310,9 @@ vnstrategy(struct buf *bp)
return;
}
} else {
int pbn;
int pbn; /* in sc_secsize chunks */
pbn = bp->b_blkno * (vn->sc_secsize / DEV_BSIZE);
pbn = bp->b_blkno / (vn->sc_secsize / DEV_BSIZE);
sz = howmany(bp->b_bcount, vn->sc_secsize);
if (pbn < 0 || pbn + sz > vn->sc_size) {
@ -356,8 +371,17 @@ vnstrategy(struct buf *bp)
* OBJT_SWAP I/O
*
* ( handles read, write, freebuf )
*
* Note: if we pre-reserved swap, B_FREEBUF is disabled
*/
vm_pager_strategy(vn->sc_object, bp);
KASSERT((bp->b_bufsize & (vn->sc_secsize - 1)) == 0,
("vnstrategy: buffer %p to small for physio", bp));
if ((bp->b_flags & B_FREEBUF) && TESTOPT(vn, VN_RESERVE)) {
biodone(bp);
} else {
vm_pager_strategy(vn->sc_object, bp);
}
} else {
bp->b_flags |= B_ERROR;
bp->b_error = EINVAL;
@ -504,13 +528,20 @@ vniocattach_file(vn, vio, dev, flag, p)
VOP_UNLOCK(nd.ni_vp, 0, p);
vn->sc_secsize = DEV_BSIZE;
vn->sc_vp = nd.ni_vp;
vn->sc_size = vattr.va_size / vn->sc_secsize; /* note truncation */
/*
* If the size is specified, override the file attributes. Note that
* the vn_size argument is in PAGE_SIZE sized blocks.
*/
if (vio->vn_size)
vn->sc_size = (quad_t)vio->vn_size * PAGE_SIZE / vn->sc_secsize;
else
vn->sc_size = vattr.va_size / vn->sc_secsize;
error = vnsetcred(vn, p->p_ucred);
if (error) {
(void) vn_close(nd.ni_vp, flags, p->p_ucred, p);
return(error);
}
dev->si_bsize_phys = vn->sc_secsize;
vn->sc_flags |= VNF_INITED;
if (flags == FREAD)
vn->sc_flags |= VNF_READONLY;
@ -571,6 +602,13 @@ vniocattach_swap(vn, vio, dev, flag, p)
vn->sc_size = vio->vn_size;
vn->sc_object =
vm_pager_allocate(OBJT_SWAP, NULL, vn->sc_secsize * (vm_ooffset_t)vio->vn_size, VM_PROT_DEFAULT, 0);
IFOPT(vn, VN_RESERVE) {
if (swap_pager_reserve(vn->sc_object, 0, vn->sc_size) < 0) {
vm_pager_deallocate(vn->sc_object);
vn->sc_object = NULL;
return(EDOM);
}
}
vn->sc_flags |= VNF_INITED;
error = vnsetcred(vn, p->p_ucred);

View File

@ -38,6 +38,8 @@
* from: Utah $Hdr: fdioctl.h 1.1 90/07/09$
*
* @(#)vnioctl.h 8.1 (Berkeley) 6/10/93
*
* $FreeBSD$
*/
#ifndef _SYS_VNIOCTL_H_
@ -72,5 +74,6 @@ struct vn_ioctl {
#define VN_DEBUG 0x4 /* Debug data in vn driver */
#define VN_IO 0x8 /* Debug I/O in vn driver */
#define VN_DONTCLUSTER 0x10 /* Don't cluster */
#define VN_RESERVE 0x20 /* Pre-reserve swap */
#endif /* _SYS_VNIOCTL_H_*/

View File

@ -38,6 +38,8 @@
* from: Utah $Hdr: fdioctl.h 1.1 90/07/09$
*
* @(#)vnioctl.h 8.1 (Berkeley) 6/10/93
*
* $FreeBSD$
*/
#ifndef _SYS_VNIOCTL_H_
@ -72,5 +74,6 @@ struct vn_ioctl {
#define VN_DEBUG 0x4 /* Debug data in vn driver */
#define VN_IO 0x8 /* Debug I/O in vn driver */
#define VN_DONTCLUSTER 0x10 /* Don't cluster */
#define VN_RESERVE 0x20 /* Pre-reserve swap */
#endif /* _SYS_VNIOCTL_H_*/

View File

@ -46,8 +46,8 @@
.Sh SYNOPSIS
.Nm vnconfig
.Op Fl cdeguv
.Op Fl s Ar option
.Op Fl r Ar option
.Op Fl s Ar option[,option...]
.Op Fl r Ar option[,option...]
.Op Fl S Ar value
.Ar special_file Ar [regular_file]
.Op Ar feature
@ -106,6 +106,15 @@ The list of allowed flags and their meanings are:
.Bl -tag -width "follow"
.It Ar labels
use disk/slice labels.
.It Ar reserve
Pre-reserve the blocks underlying the file or swap backing store. Currently only
works for swap backing store. This option also disables on-the-fly freeing of
the underlying backing store (for example, when you remove a large file).
Use this option if you wish to avoid long-term fragmentation of the backing
store. Also note that when this option is used, the initial contents of the
backing store may contain garbage rather then zeros. It may even be possible to
recover the prior contents of a swap-backed VN across a reboot if the VN device
is configured before any swap is allocated by the system.
.It Ar follow
debug flow in the
.Xr vn 4
@ -133,7 +142,18 @@ option.
If no regular file is specified, VN will use swap for backing store.
This option specifies the size of the device. For example, '23m' for
23 megabytes. The VN device will round the size up to a machine page boundry.
Filesystems up to 7.9 terrabytes are supported.
Filesystems up to 7.9 terrabytes are supported. When specified along with
a regular file, this option overrides the regular file's size insofar as
VN is concerned.
.It Fl T
When a regular file is specified, VN will ftruncate() the file to 0 first.
Normally you should also specify the -S option to set the size of the file.
This option also creates the file if it did not previously exist.
This option is only meaningful if the -S option has been specified.
.It Fl Z
When a regular file is specified, VN will zero the contents of the file to
ensure that all blocks have been allocated by the filesystem. This option is
only meaningful if the -S option has been specified.
.It Fl u
Disable and ``unconfigure'' the device.
.It Fl v

View File

@ -51,6 +51,7 @@ static const char rcsid[] =
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/param.h>
@ -64,6 +65,7 @@ static const char rcsid[] =
#define MAXVNDISK 16
#define LINESIZE 1024
#define ZBUFSIZE 32768
struct vndisk {
char *dev;
@ -84,6 +86,8 @@ struct vndisk {
#define VN_IGNORE 0x80
#define VN_SET 0x100
#define VN_RESET 0x200
#define VN_TRUNCATE 0x400
#define VN_ZERO 0x800
int nvndisks;
@ -111,9 +115,10 @@ main(argc, argv)
int flags = 0;
int size = 0;
char *autolabel = NULL;
char *s;
configfile = _PATH_VNTAB;
while ((i = getopt(argc, argv, "acdef:gr:s:S:L:uv")) != -1)
while ((i = getopt(argc, argv, "acdef:gr:s:S:TZL:uv")) != -1)
switch (i) {
/* all -- use config file */
@ -151,15 +156,19 @@ main(argc, argv)
/* reset options */
case 'r':
if (what_opt(optarg,&resetopt))
errx(1, "invalid options '%s'", optarg);
for (s = strtok(optarg, ","); s; s = strtok(NULL, ",")) {
if (what_opt(s, &resetopt))
errx(1, "invalid options '%s'", s);
}
flags |= VN_RESET;
break;
/* set options */
case 's':
if (what_opt(optarg,&setopt))
errx(1, "invalid options '%s'", optarg);
for (s = strtok(optarg, ","); s; s = strtok(NULL, ",")) {
if (what_opt(s, &setopt))
errx(1, "invalid options '%s'", s);
}
flags |= VN_SET;
break;
@ -178,6 +187,14 @@ main(argc, argv)
size = getsize(optarg);
break;
case 'T':
flags |= VN_TRUNCATE;
break;
case 'Z':
flags |= VN_ZERO;
break;
case 'L':
autolabel = optarg;
break;
@ -192,13 +209,13 @@ main(argc, argv)
if (flags == 0)
flags = VN_CONFIG;
if (all)
if (all) {
readconfig(flags);
else {
} else {
if (argc < optind + 1)
usage();
vndisks[0].dev = argv[optind++];
vndisks[0].file = argv[optind++];
vndisks[0].file = argv[optind++]; /* may be NULL */
vndisks[0].flags = flags;
vndisks[0].size = size;
vndisks[0].autolabel = autolabel;
@ -217,6 +234,7 @@ what_opt(str,p)
char *str;
u_long *p;
{
if (!strcmp(str,"reserve")) { *p |= VN_RESERVE; return 0; }
if (!strcmp(str,"labels")) { *p |= VN_LABELS; return 0; }
if (!strcmp(str,"follow")) { *p |= VN_FOLLOW; return 0; }
if (!strcmp(str,"debug")) { *p |= VN_DEBUG; return 0; }
@ -237,6 +255,7 @@ config(vnp)
char *rdev;
FILE *f;
u_long l;
int pgsize = getpagesize();
rv = 0;
@ -254,6 +273,47 @@ config(vnp)
if (flags & VN_IGNORE)
return(0);
/*
* When a regular file has been specified, do any requested setup
* of the file. Truncation (also creates the file if necessary),
* sizing, and zeroing.
*/
if (file && vnp->size != 0 && (flags & VN_CONFIG)) {
int fd;
struct stat st;
if (flags & VN_TRUNCATE)
fd = open(file, O_RDWR|O_CREAT|O_TRUNC);
else
fd = open(file, O_RDWR);
if (fd >= 0 && fstat(fd, &st) == 0 && S_ISREG(st.st_mode)) {
if (st.st_size < (off_t)vnp->size * pgsize)
ftruncate(fd, (off_t)vnp->size * pgsize);
if (vnp->size != 0)
st.st_size = (off_t)vnp->size * pgsize;
if (flags & VN_ZERO) {
char *buf = malloc(ZBUFSIZE);
bzero(buf, ZBUFSIZE);
while (st.st_size > 0) {
int n = (st.st_size > ZBUFSIZE) ?
ZBUFSIZE : (int)st.st_size;
if (write(fd, buf, n) != n) {
ftruncate(fd, 0);
printf("Unable to ZERO file %s\n", file);
return(0);
}
st.st_size -= (off_t)n;
}
}
close(fd);
} else {
printf("Unable to open file %s\n", file);
return(0);
}
}
rdev = rawdevice(dev);
f = fopen(rdev, "rw");
if (f == NULL) {
@ -293,6 +353,34 @@ config(vnp)
} else if (verbose)
printf("%s: cleared\n", dev);
}
/*
* Set specified options
*/
if (flags & VN_SET) {
l = setopt;
if (global)
rv = ioctl(fileno(f), VNIOCGSET, &l);
else
rv = ioctl(fileno(f), VNIOCUSET, &l);
if (rv) {
warn("VNIO[GU]SET");
} else if (verbose)
printf("%s: flags now=%08x\n",dev,l);
}
/*
* Reset specified options
*/
if (flags & VN_RESET) {
l = resetopt;
if (global)
rv = ioctl(fileno(f), VNIOCGCLEAR, &l);
else
rv = ioctl(fileno(f), VNIOCUCLEAR, &l);
if (rv) {
warn("VNIO[GU]CLEAR");
} else if (verbose)
printf("%s: flags now=%08x\n",dev,l);
}
/*
* Configure the device
*/