diff options
author | kib <kib@FreeBSD.org> | 2010-12-29 12:25:28 +0000 |
---|---|---|
committer | kib <kib@FreeBSD.org> | 2010-12-29 12:25:28 +0000 |
commit | 17dccd1898b6a0ddd49fcd77d7314ddc494cbac8 (patch) | |
tree | caf44cb53e3be97531f2a851ce0a0a25b34c7ac1 /sys/ufs/ffs/ffs_alloc.c | |
parent | e8862739b90ac36ae90c4552837b4959cbd54410 (diff) | |
download | FreeBSD-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/ffs/ffs_alloc.c')
-rw-r--r-- | sys/ufs/ffs/ffs_alloc.c | 102 |
1 files changed, 100 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 |