summaryrefslogtreecommitdiffstats
path: root/sys/ufs
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2010-12-29 12:25:28 +0000
committerkib <kib@FreeBSD.org>2010-12-29 12:25:28 +0000
commit17dccd1898b6a0ddd49fcd77d7314ddc494cbac8 (patch)
treecaf44cb53e3be97531f2a851ce0a0a25b34c7ac1 /sys/ufs
parente8862739b90ac36ae90c4552837b4959cbd54410 (diff)
downloadFreeBSD-src-17dccd1898b6a0ddd49fcd77d7314ddc494cbac8.zip
FreeBSD-src-17dccd1898b6a0ddd49fcd77d7314ddc494cbac8.tar.gz
Add kernel side support for BIO_DELETE/TRIM on UFS.
The FS_TRIM fs flag indicates that administrator requested issuing of TRIM commands for the volume. UFS will only send the command to disk if the disk reports GEOM::candelete attribute. Since disk queue is reordered, data block is marked as free in the bitmap only after TRIM command completed. Due to need to sleep waiting for i/o to finish, TRIM bio_done routine schedules taskqueue to set the bitmap bit. Based on the patch by: mckusick Reviewed by: mckusick, pjd Tested by: pho MFC after: 1 month
Diffstat (limited to 'sys/ufs')
-rw-r--r--sys/ufs/ffs/ffs_alloc.c102
-rw-r--r--sys/ufs/ffs/ffs_vfsops.c15
-rw-r--r--sys/ufs/ffs/fs.h1
-rw-r--r--sys/ufs/ufs/ufsmount.h1
4 files changed, 117 insertions, 2 deletions
diff --git a/sys/ufs/ffs/ffs_alloc.c b/sys/ufs/ffs/ffs_alloc.c
index b740bbb..392c150 100644
--- a/sys/ufs/ffs/ffs_alloc.c
+++ b/sys/ufs/ffs/ffs_alloc.c
@@ -80,9 +80,12 @@ __FBSDID("$FreeBSD$");
#include <sys/syscallsubr.h>
#include <sys/sysctl.h>
#include <sys/syslog.h>
+#include <sys/taskqueue.h>
#include <security/audit/audit.h>
+#include <geom/geom.h>
+
#include <ufs/ufs/dir.h>
#include <ufs/ufs/extattr.h>
#include <ufs/ufs/quota.h>
@@ -92,6 +95,7 @@ __FBSDID("$FreeBSD$");
#include <ufs/ffs/fs.h>
#include <ufs/ffs/ffs_extern.h>
+#include <ufs/ffs/softdep.h>
typedef ufs2_daddr_t allocfcn_t(struct inode *ip, u_int cg, ufs2_daddr_t bpref,
int size, int rsize);
@@ -99,6 +103,11 @@ typedef ufs2_daddr_t allocfcn_t(struct inode *ip, u_int cg, ufs2_daddr_t bpref,
static ufs2_daddr_t ffs_alloccg(struct inode *, u_int, ufs2_daddr_t, int, int);
static ufs2_daddr_t
ffs_alloccgblk(struct inode *, struct buf *, ufs2_daddr_t, int);
+static void ffs_blkfree_cg(struct ufsmount *, struct fs *,
+ struct vnode *, ufs2_daddr_t, long, ino_t,
+ struct workhead *);
+static void ffs_blkfree_trim_completed(struct bio *);
+static void ffs_blkfree_trim_task(void *ctx, int pending __unused);
#ifdef INVARIANTS
static int ffs_checkblk(struct inode *, ufs2_daddr_t, long);
#endif
@@ -1831,8 +1840,8 @@ gotit:
* free map. If a fragment is deallocated, a possible
* block reassembly is checked.
*/
-void
-ffs_blkfree(ump, fs, devvp, bno, size, inum, dephd)
+static void
+ffs_blkfree_cg(ump, fs, devvp, bno, size, inum, dephd)
struct ufsmount *ump;
struct fs *fs;
struct vnode *devvp;
@@ -1964,6 +1973,95 @@ ffs_blkfree(ump, fs, devvp, bno, size, inum, dephd)
bdwrite(bp);
}
+TASKQUEUE_DEFINE_THREAD(ffs_trim);
+
+struct ffs_blkfree_trim_params {
+ struct task task;
+ struct ufsmount *ump;
+ struct vnode *devvp;
+ ufs2_daddr_t bno;
+ long size;
+ ino_t inum;
+ struct workhead *pdephd;
+ struct workhead dephd;
+};
+
+static void
+ffs_blkfree_trim_task(ctx, pending)
+ void *ctx;
+ int pending;
+{
+ struct ffs_blkfree_trim_params *tp;
+
+ tp = ctx;
+ ffs_blkfree_cg(tp->ump, tp->ump->um_fs, tp->devvp, tp->bno, tp->size,
+ tp->inum, tp->pdephd);
+ vn_finished_secondary_write(UFSTOVFS(tp->ump));
+ free(tp, M_TEMP);
+}
+
+static void
+ffs_blkfree_trim_completed(bip)
+ struct bio *bip;
+{
+ struct ffs_blkfree_trim_params *tp;
+
+ tp = bip->bio_caller2;
+ g_destroy_bio(bip);
+ TASK_INIT(&tp->task, 0, ffs_blkfree_trim_task, tp);
+ taskqueue_enqueue(taskqueue_ffs_trim, &tp->task);
+}
+
+void
+ffs_blkfree(ump, fs, devvp, bno, size, inum, dephd)
+ struct ufsmount *ump;
+ struct fs *fs;
+ struct vnode *devvp;
+ ufs2_daddr_t bno;
+ long size;
+ ino_t inum;
+ struct workhead *dephd;
+{
+ struct mount *mp;
+ struct bio *bip;
+ struct ffs_blkfree_trim_params *tp;
+
+ if (!ump->um_candelete) {
+ ffs_blkfree_cg(ump, fs, devvp, bno, size, inum, dephd);
+ return;
+ }
+
+ /*
+ * Postpone the set of the free bit in the cg bitmap until the
+ * BIO_DELETE is completed. Otherwise, due to disk queue
+ * reordering, TRIM might be issued after we reuse the block
+ * and write some new data into it.
+ */
+ tp = malloc(sizeof(struct ffs_blkfree_trim_params), M_TEMP, M_WAITOK);
+ tp->ump = ump;
+ tp->devvp = devvp;
+ tp->bno = bno;
+ tp->size = size;
+ tp->inum = inum;
+ if (dephd != NULL) {
+ LIST_INIT(&tp->dephd);
+ LIST_SWAP(dephd, &tp->dephd, worklist, wk_list);
+ tp->pdephd = &tp->dephd;
+ } else
+ tp->pdephd = NULL;
+
+ bip = g_alloc_bio();
+ bip->bio_cmd = BIO_DELETE;
+ bip->bio_offset = dbtob(fsbtodb(fs, bno));
+ bip->bio_done = ffs_blkfree_trim_completed;
+ bip->bio_length = size;
+ bip->bio_caller2 = tp;
+
+ mp = UFSTOVFS(ump);
+ vn_start_secondary_write(NULL, &mp, 0);
+ g_io_request(bip, (struct g_consumer *)devvp->v_bufobj.bo_private);
+}
+
#ifdef INVARIANTS
/*
* Verify allocation of a block or fragment. Returns true if block or
diff --git a/sys/ufs/ffs/ffs_vfsops.c b/sys/ufs/ffs/ffs_vfsops.c
index 72f40da..f578382 100644
--- a/sys/ufs/ffs/ffs_vfsops.c
+++ b/sys/ufs/ffs/ffs_vfsops.c
@@ -895,6 +895,21 @@ ffs_mountfs(devvp, mp, td)
mp->mnt_stat.f_mntonname);
#endif
}
+ if ((fs->fs_flags & FS_TRIM) != 0) {
+ size = sizeof(int);
+ if (g_io_getattr("GEOM::candelete", cp, &size,
+ &ump->um_candelete) == 0) {
+ if (!ump->um_candelete)
+ printf(
+"WARNING: %s: TRIM flag on fs but disk does not support TRIM\n",
+ mp->mnt_stat.f_mntonname);
+ } else {
+ printf(
+"WARNING: %s: TRIM flag on fs but cannot get whether disk supports TRIM\n",
+ mp->mnt_stat.f_mntonname);
+ ump->um_candelete = 0;
+ }
+ }
ump->um_mountp = mp;
ump->um_dev = dev;
diff --git a/sys/ufs/ffs/fs.h b/sys/ufs/ffs/fs.h
index ba98ed3..13d9ede 100644
--- a/sys/ufs/ffs/fs.h
+++ b/sys/ufs/ffs/fs.h
@@ -417,6 +417,7 @@ CTASSERT(sizeof(struct fs) == 1376);
#define FS_FLAGS_UPDATED 0x0080 /* flags have been moved to new location */
#define FS_NFS4ACLS 0x0100 /* file system has NFSv4 ACLs enabled */
#define FS_INDEXDIRS 0x0200 /* kernel supports indexed directories */
+#define FS_TRIM 0x0400 /* issue BIO_DELETE for deleted blocks */
/*
* Macros to access bits in the fs_active array.
diff --git a/sys/ufs/ufs/ufsmount.h b/sys/ufs/ufs/ufsmount.h
index 03edd73..b13db40 100644
--- a/sys/ufs/ufs/ufsmount.h
+++ b/sys/ufs/ufs/ufsmount.h
@@ -95,6 +95,7 @@ struct ufsmount {
time_t um_itime[MAXQUOTAS]; /* inode quota time limit */
char um_qflags[MAXQUOTAS]; /* quota specific flags */
int64_t um_savedmaxfilesize; /* XXX - limit maxfilesize */
+ int um_candelete; /* devvp supports TRIM */
int (*um_balloc)(struct vnode *, off_t, int, struct ucred *, int, struct buf **);
int (*um_blkatoff)(struct vnode *, off_t, char **, struct buf **);
int (*um_truncate)(struct vnode *, off_t, int, struct ucred *, struct thread *);
OpenPOWER on IntegriCloud