On Sun, Jun 21, 2020 at 03:35:21PM +0200, Otto Moerbeek wrote:
> Hi,
>
> both phase 1 and phase 5 need cylinder group metadata. This diff
> keeps the cg data read in phase 1 in memory to be used by phase 5 if
> possible. From FreeBSD.
>
> -Otto
>
> On an empty 30T fileystem:
>
> $ time obj/fsck_ffs -f /dev/sd3a
> 2m44.10s real 0m13.21s user 0m07.38s system
>
> $ time doas obj/fsck_ffs -f /dev/sd3a
> 1m32.81s real 0m12.86s user 0m05.25s system
>
> The difference will be less if a fileystem is filled up, but still nice.
Any takers?
-Otto
>
> Index: fsck.h
> ===================================================================
> RCS file: /cvs/src/sbin/fsck_ffs/fsck.h,v
> retrieving revision 1.32
> diff -u -p -r1.32 fsck.h
> --- fsck.h 5 Jan 2018 09:33:47 -0000 1.32
> +++ fsck.h 21 Jun 2020 12:48:50 -0000
> @@ -136,7 +136,6 @@ struct bufarea {
> struct bufarea bufhead; /* head of list of other blks in
> filesys */
> struct bufarea sblk; /* file system superblock */
> struct bufarea asblk; /* alternate file system superblock */
> -struct bufarea cgblk; /* cylinder group blocks */
> struct bufarea *pdirbp; /* current directory contents */
> struct bufarea *pbp; /* current inode block */
> struct bufarea *getdatablk(daddr_t, long);
> @@ -148,9 +147,7 @@ struct bufarea *getdatablk(daddr_t, long
> (bp)->b_flags = 0;
>
> #define sbdirty() sblk.b_dirty = 1
> -#define cgdirty() cgblk.b_dirty = 1
> #define sblock (*sblk.b_un.b_fs)
> -#define cgrp (*cgblk.b_un.b_cg)
>
> enum fixstate {DONTKNOW, NOFIX, FIX, IGNORE};
>
> @@ -275,9 +272,13 @@ struct ufs2_dinode ufs2_zino;
> #define FOUND 0x10
>
> union dinode *ginode(ino_t);
> +struct bufarea *cglookup(u_int cg);
> struct inoinfo *getinoinfo(ino_t);
> void getblk(struct bufarea *, daddr_t, long);
> ino_t allocino(ino_t, int);
> +void *Malloc(size_t);
> +void *Calloc(size_t, size_t);
> +void *Reallocarray(void *, size_t, size_t);
>
> int (*info_fn)(char *, size_t);
> char *info_filesys;
> Index: inode.c
> ===================================================================
> RCS file: /cvs/src/sbin/fsck_ffs/inode.c,v
> retrieving revision 1.49
> diff -u -p -r1.49 inode.c
> --- inode.c 16 Sep 2018 02:43:11 -0000 1.49
> +++ inode.c 21 Jun 2020 12:48:50 -0000
> @@ -370,7 +370,7 @@ setinodebuf(ino_t inum)
> partialsize = inobufsize;
> }
> if (inodebuf == NULL &&
> - (inodebuf = malloc((unsigned)inobufsize)) == NULL)
> + (inodebuf = Malloc((unsigned)inobufsize)) == NULL)
> errexit("Cannot allocate space for inode buffer\n");
> }
>
> @@ -401,7 +401,7 @@ cacheino(union dinode *dp, ino_t inumber
> blks = howmany(DIP(dp, di_size), sblock.fs_bsize);
> if (blks > NDADDR)
> blks = NDADDR + NIADDR;
> - inp = malloc(sizeof(*inp) + (blks ? blks - 1 : 0) * sizeof(daddr_t));
> + inp = Malloc(sizeof(*inp) + (blks ? blks - 1 : 0) * sizeof(daddr_t));
> if (inp == NULL)
> errexit("cannot allocate memory for inode cache\n");
> inpp = &inphead[inumber % numdirs];
> @@ -423,10 +423,10 @@ cacheino(union dinode *dp, ino_t inumber
> inp->i_blks[NDADDR + i] = DIP(dp, di_ib[i]);
> if (inplast == listmax) {
> newlistmax = listmax + 100;
> - newinpsort = reallocarray(inpsort,
> + newinpsort = Reallocarray(inpsort,
> (unsigned)newlistmax, sizeof(struct inoinfo *));
> if (newinpsort == NULL)
> - errexit("cannot increase directory list");
> + errexit("cannot increase directory list\n");
> inpsort = newinpsort;
> listmax = newlistmax;
> }
> @@ -582,7 +582,8 @@ allocino(ino_t request, int type)
> {
> ino_t ino;
> union dinode *dp;
> - struct cg *cgp = &cgrp;
> + struct bufarea *cgbp;
> + struct cg *cgp;
> int cg;
> time_t t;
> struct inostat *info;
> @@ -602,7 +603,7 @@ allocino(ino_t request, int type)
> unsigned long newalloced, i;
> newalloced = MINIMUM(sblock.fs_ipg,
> MAXIMUM(2 * inostathead[cg].il_numalloced, 10));
> - info = calloc(newalloced, sizeof(struct inostat));
> + info = Calloc(newalloced, sizeof(struct inostat));
> if (info == NULL) {
> pwarn("cannot alloc %zu bytes to extend inoinfo\n",
> sizeof(struct inostat) * newalloced);
> @@ -619,7 +620,8 @@ allocino(ino_t request, int type)
> inostathead[cg].il_numalloced = newalloced;
> info = inoinfo(ino);
> }
> - getblk(&cgblk, cgtod(&sblock, cg), sblock.fs_cgsize);
> + cgbp = cglookup(cg);
> + cgp = cgbp->b_un.b_cg;
> if (!cg_chkmagic(cgp))
> pfatal("CG %d: BAD MAGIC NUMBER\n", cg);
> setbit(cg_inosused(cgp), ino % sblock.fs_ipg);
> @@ -637,7 +639,7 @@ allocino(ino_t request, int type)
> default:
> return (0);
> }
> - cgdirty();
> + dirty(cgbp);
> dp = ginode(ino);
> DIP_SET(dp, di_db[0], allocblk(1));
> if (DIP(dp, di_db[0]) == 0) {
> Index: pass1.c
> ===================================================================
> RCS file: /cvs/src/sbin/fsck_ffs/pass1.c,v
> retrieving revision 1.46
> diff -u -p -r1.46 pass1.c
> --- pass1.c 20 Jun 2020 07:49:04 -0000 1.46
> +++ pass1.c 21 Jun 2020 12:48:50 -0000
> @@ -66,6 +66,8 @@ pass1(void)
> ino_t inumber, inosused, ninosused;
> size_t inospace;
> struct inostat *info;
> + struct bufarea *cgbp;
> + struct cg *cgp;
> u_int c;
> struct inodesc idesc;
> daddr_t i, cgd;
> @@ -99,9 +101,10 @@ pass1(void)
> for (c = 0; c < sblock.fs_ncg; c++) {
> inumber = c * sblock.fs_ipg;
> setinodebuf(inumber);
> - getblk(&cgblk, cgtod(&sblock, c), sblock.fs_cgsize);
> + cgbp = cglookup(c);
> + cgp = cgbp->b_un.b_cg;
> if (sblock.fs_magic == FS_UFS2_MAGIC) {
> - inosused = cgrp.cg_initediblk;
> + inosused = cgp->cg_initediblk;
> if (inosused > sblock.fs_ipg)
> inosused = sblock.fs_ipg;
> } else
> @@ -115,7 +118,7 @@ pass1(void)
> * read only those inodes in from disk.
> */
> if (preen && usedsoftdep) {
> - cp = &cg_inosused(&cgrp)[(inosused - 1) / CHAR_BIT];
> + cp = &cg_inosused(cgp)[(inosused - 1) / CHAR_BIT];
> for ( ; inosused != 0; cp--) {
> if (*cp == 0) {
> if (inosused > CHAR_BIT)
> @@ -140,10 +143,10 @@ pass1(void)
> inostathead[c].il_stat = 0;
> continue;
> }
> - info = calloc((unsigned)inosused, sizeof(struct inostat));
> + info = Calloc((unsigned)inosused, sizeof(struct inostat));
> inospace = (unsigned)inosused * sizeof(struct inostat);
> if (info == NULL)
> - errexit("cannot alloc %zu bytes for inoinfo", inospace);
> + errexit("cannot alloc %zu bytes for inoinfo\n",
> inospace);
> inostathead[c].il_stat = info;
> /*
> * Scan the allocated inodes.
> @@ -179,7 +182,7 @@ pass1(void)
> struct inostat *ninfo;
> size_t ninospace;
>
> - ninfo = reallocarray(info, ninosused, sizeof(*ninfo));
> + ninfo = Reallocarray(info, ninosused, sizeof(*ninfo));
> if (ninfo == NULL) {
> pfatal("too many inodes %llu, or out of
> memory\n",
> (unsigned long long)ninosused);
> @@ -300,7 +303,7 @@ checkinode(ino_t inumber, struct inodesc
> n_files++;
> ILNCOUNT(inumber) = DIP(dp, di_nlink);
> if (DIP(dp, di_nlink) <= 0) {
> - zlnp = malloc(sizeof *zlnp);
> + zlnp = Malloc(sizeof *zlnp);
> if (zlnp == NULL) {
> pfatal("LINK COUNT TABLE OVERFLOW");
> if (reply("CONTINUE") == 0) {
> @@ -392,7 +395,7 @@ pass1check(struct inodesc *idesc)
> }
> return (STOP);
> }
> - new = malloc(sizeof(struct dups));
> + new = Malloc(sizeof(struct dups));
> if (new == NULL) {
> pfatal("DUP TABLE OVERFLOW.");
> if (reply("CONTINUE") == 0) {
> Index: pass5.c
> ===================================================================
> RCS file: /cvs/src/sbin/fsck_ffs/pass5.c,v
> retrieving revision 1.49
> diff -u -p -r1.49 pass5.c
> --- pass5.c 20 Jun 2020 07:49:04 -0000 1.49
> +++ pass5.c 21 Jun 2020 12:48:50 -0000
> @@ -65,7 +65,6 @@ pass5(void)
> u_int c;
> int inomapsize, blkmapsize;
> struct fs *fs = &sblock;
> - struct cg *cg = &cgrp;
> daddr_t dbase, dmax;
> daddr_t d;
> long i, k, rewritecg = 0;
> @@ -76,6 +75,8 @@ pass5(void)
> char buf[MAXBSIZE];
> struct cg *newcg = (struct cg *)buf;
> struct ocg *ocg = (struct ocg *)buf;
> + struct cg *cg;
> + struct bufarea *cgbp;
>
> memset(newcg, 0, (size_t)fs->fs_cgsize);
> if (cvtlevel >= 3) {
> @@ -179,7 +180,8 @@ pass5(void)
> info_fn = pass5_info;
> for (c = 0; c < fs->fs_ncg; c++) {
> info_cg = c;
> - getblk(&cgblk, cgtod(fs, c), fs->fs_cgsize);
> + cgbp = cglookup(c);
> + cg = cgbp->b_un.b_cg;
> if (!cg_chkmagic(cg))
> pfatal("CG %u: BAD MAGIC NUMBER\n", c);
> dbase = cgbase(fs, c);
> @@ -323,13 +325,13 @@ pass5(void)
> }
> if (rewritecg) {
> memcpy(cg, newcg, (size_t)fs->fs_cgsize);
> - cgdirty();
> + dirty(cgbp);
> continue;
> }
> if (memcmp(newcg, cg, basesize) &&
> dofix(&idesc[2], "SUMMARY INFORMATION BAD")) {
> memcpy(cg, newcg, (size_t)basesize);
> - cgdirty();
> + dirty(cgbp);
> }
> if (usedsoftdep) {
> for (i = 0; i < inomapsize; i++) {
> @@ -364,7 +366,7 @@ pass5(void)
> dofix(&idesc[1], "BLK(S) MISSING IN BIT MAPS")) {
> memmove(cg_inosused(cg), cg_inosused(newcg),
> (size_t)mapsize);
> - cgdirty();
> + dirty(cgbp);
> }
> }
> info_fn = NULL;
> Index: utilities.c
> ===================================================================
> RCS file: /cvs/src/sbin/fsck_ffs/utilities.c,v
> retrieving revision 1.53
> diff -u -p -r1.53 utilities.c
> --- utilities.c 20 Jun 2020 07:49:04 -0000 1.53
> +++ utilities.c 21 Jun 2020 12:48:50 -0000
> @@ -51,7 +51,8 @@
> #include "fsck.h"
> #include "extern.h"
>
> -long diskreads, totalreads; /* Disk cache statistics */
> +long diskreads, totalreads; /* Disk cache
> statistics */
> +static struct bufarea cgblk; /* backup
> buffer for cylinder group blocks */
>
> static void rwerror(char *, daddr_t);
>
> @@ -176,6 +177,39 @@ bufinit(void)
> }
>
> /*
> + * Manage cylinder group buffers.
> + */
> +static struct bufarea *cgbufs; /* header for cylinder group cache */
> +static int flushtries; /* number of tries to reclaim memory */
> +struct bufarea *
> +cglookup(u_int cg)
> +{
> + struct bufarea *cgbp;
> + struct cg *cgp;
> +
> + if (cgbufs == NULL) {
> + cgbufs = calloc(sblock.fs_ncg, sizeof(struct bufarea));
> + if (cgbufs == NULL)
> + errexit("cannot allocate cylinder group buffers");
> + }
> + cgbp = &cgbufs[cg];
> + if (cgbp->b_un.b_cg != NULL)
> + return (cgbp);
> + cgp = NULL;
> + if (flushtries == 0)
> + cgp = malloc((unsigned int)sblock.fs_cgsize);
> + if (cgp == NULL) {
> + getblk(&cgblk, cgtod(&sblock, cg), sblock.fs_cgsize);
> + return (&cgblk);
> + }
> + cgbp->b_un.b_cg = cgp;
> + initbarea(cgbp);
> + getblk(cgbp, cgtod(&sblock, cg), sblock.fs_cgsize);
> + return (cgbp);
> +}
> +
> +
> +/*
> * Manage a cache of directory blocks.
> */
> struct bufarea *
> @@ -305,6 +339,15 @@ ckfini(int markclean)
> }
> if (bufhead.b_size != cnt)
> errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt);
> + if (cgbufs != NULL) {
> + for (cnt = 0; cnt < sblock.fs_ncg; cnt++) {
> + if (cgbufs[cnt].b_un.b_cg == NULL)
> + continue;
> + flush(fswritefd, &cgbufs[cnt]);
> + free(cgbufs[cnt].b_un.b_cg);
> + }
> + free(cgbufs);
> + }
> pbp = pdirbp = NULL;
> if (markclean && (sblock.fs_clean & FS_ISCLEAN) == 0) {
> /*
> @@ -400,7 +443,8 @@ allocblk(int frags)
> {
> daddr_t i, baseblk;
> int j, k, cg;
> - struct cg *cgp = &cgrp;
> + struct bufarea *cgbp;
> + struct cg *cgp;
>
> if (frags <= 0 || frags > sblock.fs_frag)
> return (0);
> @@ -416,7 +460,8 @@ allocblk(int frags)
> continue;
> }
> cg = dtog(&sblock, i + j);
> - getblk(&cgblk, cgtod(&sblock, cg), sblock.fs_cgsize);
> + cgbp = cglookup(cg);
> + cgp = cgbp->b_un.b_cg;
> if (!cg_chkmagic(cgp))
> pfatal("CG %d: BAD MAGIC NUMBER\n", cg);
> baseblk = dtogd(&sblock, i + j);
> @@ -614,4 +659,68 @@ catchinfo(int signo)
> writev(info_fd, iov, 4);
> }
> errno = save_errno;
> +}
> +/*
> + * Attempt to flush a cylinder group cache entry.
> + * Return whether the flush was successful.
> + */
> +static int
> +flushentry(void)
> +{
> + struct bufarea *cgbp;
> +
> + if (flushtries == sblock.fs_ncg || cgbufs == NULL)
> + return (0);
> + cgbp = &cgbufs[flushtries++];
> + if (cgbp->b_un.b_cg == NULL)
> + return (0);
> + flush(fswritefd, cgbp);
> + free(cgbp->b_un.b_buf);
> + cgbp->b_un.b_buf = NULL;
> + return (1);
> +}
> +
> +/*
> + * Wrapper for malloc() that flushes the cylinder group cache to try
> + * to get space.
> + */
> +void *
> +Malloc(size_t size)
> +{
> + void *retval;
> +
> + while ((retval = malloc(size)) == NULL)
> + if (flushentry() == 0)
> + break;
> + return (retval);
> +}
> +
> +/*
> + * Wrapper for calloc() that flushes the cylinder group cache to try
> + * to get space.
> + */
> +void*
> +Calloc(size_t cnt, size_t size)
> +{
> + void *retval;
> +
> + while ((retval = calloc(cnt, size)) == NULL)
> + if (flushentry() == 0)
> + break;
> + return (retval);
> +}
> +
> +/*
> + * Wrapper for reallocarray() that flushes the cylinder group cache to try
> + * to get space.
> + */
> +void*
> +Reallocarray(void *p, size_t cnt, size_t size)
> +{
> + void *retval;
> +
> + while ((retval = reallocarray(p, cnt, size)) == NULL)
> + if (flushentry() == 0)
> + break;
> + return (retval);
> }
>
>