From 447deba3dadf866766beba0745bb6868241eaef6 Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Fri, 17 Sep 1999 05:34:00 +0000 Subject: [PATCH] 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 , David Greenman --- sys/dev/vn/vn.c | 58 +++++++++++++++---- sys/sys/mdioctl.h | 3 + sys/sys/vnioctl.h | 3 + usr.sbin/vnconfig/vnconfig.8 | 26 ++++++++- usr.sbin/vnconfig/vnconfig.c | 104 ++++++++++++++++++++++++++++++++--- 5 files changed, 173 insertions(+), 21 deletions(-) diff --git a/sys/dev/vn/vn.c b/sys/dev/vn/vn.c index 199c8046d46..54914b24f79 100644 --- a/sys/dev/vn/vn.c +++ b/sys/dev/vn/vn.c @@ -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); diff --git a/sys/sys/mdioctl.h b/sys/sys/mdioctl.h index 4f2e9371ce9..b2837418282 100644 --- a/sys/sys/mdioctl.h +++ b/sys/sys/mdioctl.h @@ -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_*/ diff --git a/sys/sys/vnioctl.h b/sys/sys/vnioctl.h index 4f2e9371ce9..b2837418282 100644 --- a/sys/sys/vnioctl.h +++ b/sys/sys/vnioctl.h @@ -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_*/ diff --git a/usr.sbin/vnconfig/vnconfig.8 b/usr.sbin/vnconfig/vnconfig.8 index 85ced187e91..7cfe517026c 100644 --- a/usr.sbin/vnconfig/vnconfig.8 +++ b/usr.sbin/vnconfig/vnconfig.8 @@ -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 diff --git a/usr.sbin/vnconfig/vnconfig.c b/usr.sbin/vnconfig/vnconfig.c index ce1f7eaae3d..c9f6e5e976d 100644 --- a/usr.sbin/vnconfig/vnconfig.c +++ b/usr.sbin/vnconfig/vnconfig.c @@ -51,6 +51,7 @@ static const char rcsid[] = #include #include #include +#include #include #include #include @@ -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 */