From 8d3dfc2691ee934dfd7c17235c0f10a395525eed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dag-Erling=20Sm=C3=B8rgrav?= Date: Fri, 29 Apr 2011 23:00:23 +0000 Subject: [PATCH] Add an -E option to mirror newfs's. The idea is that if you have a system that was built before ffs grew support for TRIM, your filesystem will have plenty of free blocks that the flash chip doesn't know are free, so it can't take advantage of them for wear leveling. Once you've upgraded your kernel, you enable TRIM on the filesystem (tunefs -t enable), then run fsck_ffs -E on it before mounting it. I tested this patch by half-filling an mdconfig'ed filesystem image, running fsck_ffs -E on it, then verifying that the contents were not damaged by comparing them to a pristine copy using rsync's checksum functionality. There is no reliable way to test it on real hardware. Many thanks to mckusick@, who provided the tricky parts of this patch and reviewed the final version. Reviewed by: mckusick@ MFC after: 3 weeks --- sbin/fsck_ffs/fsck.h | 2 ++ sbin/fsck_ffs/fsck_ffs.8 | 22 ++++++++++++++++++++-- sbin/fsck_ffs/fsutil.c | 17 ++++++++++++++++- sbin/fsck_ffs/main.c | 8 ++++++-- sbin/fsck_ffs/pass5.c | 25 +++++++++++++++++++++++-- 5 files changed, 67 insertions(+), 7 deletions(-) diff --git a/sbin/fsck_ffs/fsck.h b/sbin/fsck_ffs/fsck.h index 342b95f138e6..250fc1f9890f 100644 --- a/sbin/fsck_ffs/fsck.h +++ b/sbin/fsck_ffs/fsck.h @@ -274,6 +274,7 @@ char yflag; /* assume a yes response */ int bkgrdflag; /* use a snapshot to run on an active system */ int bflag; /* location of alternate super block */ int debug; /* output debugging info */ +int Eflag; /* zero out empty data blocks */ int inoopt; /* trim out unused inodes */ char ckclean; /* only do work if not cleanly unmounted */ int cvtlevel; /* convert to newer file system format */ @@ -337,6 +338,7 @@ char *blockcheck(char *name); int blread(int fd, char *buf, ufs2_daddr_t blk, long size); void bufinit(void); void blwrite(int fd, char *buf, ufs2_daddr_t blk, long size); +void blerase(int fd, ufs2_daddr_t blk, long size); void cacheino(union dinode *dp, ino_t inumber); void catch(int); void catchquit(int); diff --git a/sbin/fsck_ffs/fsck_ffs.8 b/sbin/fsck_ffs/fsck_ffs.8 index 2834149f97c8..523cb6543ed8 100644 --- a/sbin/fsck_ffs/fsck_ffs.8 +++ b/sbin/fsck_ffs/fsck_ffs.8 @@ -29,7 +29,7 @@ .\" @(#)fsck.8 8.4 (Berkeley) 5/9/95 .\" $FreeBSD$ .\" -.Dd January 25, 2009 +.Dd April 27, 2011 .Dt FSCK_FFS 8 .Os .Sh NAME @@ -38,7 +38,7 @@ .Nd file system consistency check and interactive repair .Sh SYNOPSIS .Nm -.Op Fl BFfnpry +.Op Fl BEFfnpry .Op Fl b Ar block .Op Fl c Ar level .Op Fl m Ar mode @@ -149,6 +149,24 @@ If unexpected errors are found, the file system is marked as needing a foreground check and .Nm exits without attempting any further cleaning. +.It Fl E +Clear unallocated blocks, notifying the underlying device that they +are not used and that their contents may be discarded. +This is useful for filesystems which have been mounted on systems +without TRIM support, or with TRIM support disabled, as well as +filesystems which have been copied from one device to another. +.Pp +See the +.Fl E +and +.Fl t +flags of +.Xr newfs 8 , +and +the +.Fl t +flag of +.Xr tunefs 8 . .It Fl F Determine whether the file system needs to be cleaned immediately in foreground, or if its cleaning can be deferred to background. diff --git a/sbin/fsck_ffs/fsutil.c b/sbin/fsck_ffs/fsutil.c index 68d113a3f618..85ea03352685 100644 --- a/sbin/fsck_ffs/fsutil.c +++ b/sbin/fsck_ffs/fsutil.c @@ -39,9 +39,10 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include +#include #include -#include #include #include @@ -421,6 +422,20 @@ blwrite(int fd, char *buf, ufs2_daddr_t blk, long size) return; } +void +blerase(int fd, ufs2_daddr_t blk, long size) +{ + off_t ioarg[2]; + + if (fd < 0) + return; + ioarg[0] = blk * dev_bsize; + ioarg[1] = size; + ioctl(fd, DIOCGDELETE, ioarg); + /* we don't really care if we succeed or not */ + return; +} + /* * Verify cylinder group's magic number and other parameters. If the * test fails, offer an option to rebuild the whole cylinder group. diff --git a/sbin/fsck_ffs/main.c b/sbin/fsck_ffs/main.c index 127a612cae91..e26494d50574 100644 --- a/sbin/fsck_ffs/main.c +++ b/sbin/fsck_ffs/main.c @@ -82,7 +82,7 @@ main(int argc, char *argv[]) sync(); skipclean = 1; inoopt = 0; - while ((ch = getopt(argc, argv, "b:Bc:CdfFm:npry")) != -1) { + while ((ch = getopt(argc, argv, "b:Bc:CdEfFm:npry")) != -1) { switch (ch) { case 'b': skipclean = 0; @@ -106,6 +106,10 @@ main(int argc, char *argv[]) debug++; break; + case 'E': + Eflag++; + break; + case 'f': skipclean = 0; break; @@ -632,7 +636,7 @@ static void usage(void) { (void) fprintf(stderr, - "usage: %s [-BFprfny] [-b block] [-c level] [-m mode] " + "usage: %s [-BEFprfny] [-b block] [-c level] [-m mode] " "filesystem ...\n", getprogname()); exit(1); diff --git a/sbin/fsck_ffs/pass5.c b/sbin/fsck_ffs/pass5.c index 639ce0f8abdc..01ed8a54052d 100644 --- a/sbin/fsck_ffs/pass5.c +++ b/sbin/fsck_ffs/pass5.c @@ -51,6 +51,8 @@ __FBSDID("$FreeBSD$"); static void check_maps(u_char *, u_char *, int, ufs2_daddr_t, const char *, int *, int, int); +static void clear_blocks(ufs2_daddr_t start, ufs2_daddr_t end); + void pass5(void) { @@ -58,7 +60,7 @@ pass5(void) int inomapsize, blkmapsize; struct fs *fs = &sblock; struct cg *cg = &cgrp; - ufs2_daddr_t d, dbase, dmax; + ufs2_daddr_t d, dbase, dmax, start; int excessdirs, rewritecg = 0; struct csum *cs; struct csum_total cstotal; @@ -242,13 +244,21 @@ pass5(void) setbit(cg_inosused(newcg), i); newcg->cg_cs.cs_nifree--; } + start = -1; for (i = 0, d = dbase; d < dmax; d += fs->fs_frag, i += fs->fs_frag) { frags = 0; for (j = 0; j < fs->fs_frag; j++) { - if (testbmap(d + j)) + if (testbmap(d + j)) { + if (Eflag && start != -1) { + clear_blocks(start, d + j - 1); + start = -1; + } continue; + } + if (start == -1) + start = d + j; setbit(cg_blksfree(newcg), i + j); frags++; } @@ -263,6 +273,8 @@ pass5(void) ffs_fragacct(fs, blk, newcg->cg_frsum, 1); } } + if (Eflag && start != -1) + clear_blocks(start, d - 1); if (fs->fs_contigsumsize > 0) { int32_t *sump = cg_clustersum(newcg); u_char *mapp = cg_clustersfree(newcg); @@ -551,3 +563,12 @@ check_maps( } } } + +static void clear_blocks(ufs2_daddr_t start, ufs2_daddr_t end) +{ + + if (debug) + printf("Zero frags %jd to %jd\n", start, end); + blerase(fswritefd, fsbtodb(&sblock, start), + lfragtosize(&sblock, end - start + 1)); +}