Module Name: src
Committed By: drochner
Date: Fri Oct 19 17:09:08 UTC 2012
Modified Files:
src/include: mntopts.h
src/sbin/atactl: atactl.c
src/sbin/mount: mount.8
src/sbin/mount_ffs: mount_ffs.c
src/sys/dev/ata: atareg.h wd.c
src/sys/sys: dkio.h fstypes.h
src/sys/ufs/ffs: ffs_alloc.c ffs_extern.h ffs_vfsops.c
src/sys/ufs/ufs: ufsmount.h
Log Message:
Implement experimental support to pass notifications that a file
was deleted from the filesystem to the disk driver, commonly
known as "discard" or "trim".
fs/driver support is in ffs and ata wd for now.
This is what was posted here:
http://mail-index.netbsd.org/tech-kern/2012/02/28/msg012813.html
with minor cleanup, and the global switch replaced by a mount option.
To generate a diff of this commit:
cvs rdiff -u -r1.14 -r1.15 src/include/mntopts.h
cvs rdiff -u -r1.66 -r1.67 src/sbin/atactl/atactl.c
cvs rdiff -u -r1.77 -r1.78 src/sbin/mount/mount.8
cvs rdiff -u -r1.27 -r1.28 src/sbin/mount_ffs/mount_ffs.c
cvs rdiff -u -r1.40 -r1.41 src/sys/dev/ata/atareg.h
cvs rdiff -u -r1.400 -r1.401 src/sys/dev/ata/wd.c
cvs rdiff -u -r1.17 -r1.18 src/sys/sys/dkio.h
cvs rdiff -u -r1.30 -r1.31 src/sys/sys/fstypes.h
cvs rdiff -u -r1.130 -r1.131 src/sys/ufs/ffs/ffs_alloc.c
cvs rdiff -u -r1.78 -r1.79 src/sys/ufs/ffs/ffs_extern.h
cvs rdiff -u -r1.278 -r1.279 src/sys/ufs/ffs/ffs_vfsops.c
cvs rdiff -u -r1.38 -r1.39 src/sys/ufs/ufs/ufsmount.h
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/include/mntopts.h
diff -u src/include/mntopts.h:1.14 src/include/mntopts.h:1.15
--- src/include/mntopts.h:1.14 Fri Jun 17 14:23:50 2011
+++ src/include/mntopts.h Fri Oct 19 17:09:06 2012
@@ -1,4 +1,4 @@
-/* $NetBSD: mntopts.h,v 1.14 2011/06/17 14:23:50 manu Exp $ */
+/* $NetBSD: mntopts.h,v 1.15 2012/10/19 17:09:06 drochner Exp $ */
/*-
* Copyright (c) 1994
@@ -58,6 +58,7 @@ struct mntopt {
#define MOPT_LOG { "log", 0, MNT_LOG, 0 }
#define MOPT_IGNORE { "hidden", 0, MNT_IGNORE, 0 }
#define MOPT_EXTATTR { "extattr", 0, MNT_EXTATTR, 0 }
+#define MOPT_DISCARD { "discard", 0, MNT_DISCARD, 0 }
/* Control flags. */
#define MOPT_FORCE { "force", 0, MNT_FORCE, 0 }
Index: src/sbin/atactl/atactl.c
diff -u src/sbin/atactl/atactl.c:1.66 src/sbin/atactl/atactl.c:1.67
--- src/sbin/atactl/atactl.c:1.66 Mon Oct 31 15:26:11 2011
+++ src/sbin/atactl/atactl.c Fri Oct 19 17:09:07 2012
@@ -1,4 +1,4 @@
-/* $NetBSD: atactl.c,v 1.66 2011/10/31 15:26:11 jakllsch Exp $ */
+/* $NetBSD: atactl.c,v 1.67 2012/10/19 17:09:07 drochner Exp $ */
/*-
* Copyright (c) 1998 The NetBSD Foundation, Inc.
@@ -35,7 +35,7 @@
#include <sys/cdefs.h>
#ifndef lint
-__RCSID("$NetBSD: atactl.c,v 1.66 2011/10/31 15:26:11 jakllsch Exp $");
+__RCSID("$NetBSD: atactl.c,v 1.67 2012/10/19 17:09:07 drochner Exp $");
#endif
@@ -177,6 +177,7 @@ static const struct bitinfo ata_vers[] =
{ WDC_VER_ATA5, "ATA-5" },
{ WDC_VER_ATA6, "ATA-6" },
{ WDC_VER_ATA7, "ATA-7" },
+ { WDC_VER_ATA8, "ATA-8" },
{ 0, NULL },
};
@@ -1041,6 +1042,10 @@ device_identify(int argc, char *argv[])
inqbuf->atap_sata_features_supp, ata_sata_feat);
}
+ if ((inqbuf->atap_ata_major & WDC_VER_ATA8) &&
+ (inqbuf->support_dsm & ATA_SUPPORT_DSM_TRIM))
+ printf("TRIM supported\n");
+
return;
}
Index: src/sbin/mount/mount.8
diff -u src/sbin/mount/mount.8:1.77 src/sbin/mount/mount.8:1.78
--- src/sbin/mount/mount.8:1.77 Wed Oct 3 19:36:11 2012
+++ src/sbin/mount/mount.8 Fri Oct 19 17:09:07 2012
@@ -1,4 +1,4 @@
-.\" $NetBSD: mount.8,v 1.77 2012/10/03 19:36:11 wiz Exp $
+.\" $NetBSD: mount.8,v 1.78 2012/10/19 17:09:07 drochner Exp $
.\"
.\" Copyright (c) 1980, 1989, 1991, 1993
.\" The Regents of the University of California. All rights reserved.
@@ -190,6 +190,9 @@ recovery mechanism, or are willing to re
Clear
.Cm async
mode.
+.It Cm discard
+Use DISCARD/TRIM commands if disk and driver support it.
+EXPERIMENTAL!
.It Cm extattr
Enable extended attributes, if the filesystem supports them and
does not enable them by default.
Index: src/sbin/mount_ffs/mount_ffs.c
diff -u src/sbin/mount_ffs/mount_ffs.c:1.27 src/sbin/mount_ffs/mount_ffs.c:1.28
--- src/sbin/mount_ffs/mount_ffs.c:1.27 Mon Aug 29 14:35:01 2011
+++ src/sbin/mount_ffs/mount_ffs.c Fri Oct 19 17:09:07 2012
@@ -1,4 +1,4 @@
-/* $NetBSD: mount_ffs.c,v 1.27 2011/08/29 14:35:01 joerg Exp $ */
+/* $NetBSD: mount_ffs.c,v 1.28 2012/10/19 17:09:07 drochner Exp $ */
/*-
* Copyright (c) 1993, 1994
@@ -39,7 +39,7 @@ __COPYRIGHT("@(#) Copyright (c) 1993, 19
#if 0
static char sccsid[] = "@(#)mount_ufs.c 8.4 (Berkeley) 4/26/95";
#else
-__RCSID("$NetBSD: mount_ffs.c,v 1.27 2011/08/29 14:35:01 joerg Exp $");
+__RCSID("$NetBSD: mount_ffs.c,v 1.28 2012/10/19 17:09:07 drochner Exp $");
#endif
#endif /* not lint */
@@ -75,6 +75,7 @@ static const struct mntopt mopts[] = {
MOPT_LOG,
MOPT_GETARGS,
MOPT_EXTATTR,
+ MOPT_DISCARD,
MOPT_NULL,
};
Index: src/sys/dev/ata/atareg.h
diff -u src/sys/dev/ata/atareg.h:1.40 src/sys/dev/ata/atareg.h:1.41
--- src/sys/dev/ata/atareg.h:1.40 Mon Oct 24 20:52:34 2011
+++ src/sys/dev/ata/atareg.h Fri Oct 19 17:09:07 2012
@@ -1,4 +1,4 @@
-/* $NetBSD: atareg.h,v 1.40 2011/10/24 20:52:34 jakllsch Exp $ */
+/* $NetBSD: atareg.h,v 1.41 2012/10/19 17:09:07 drochner Exp $ */
/*
* Copyright (c) 1998, 2001 Manuel Bouyer.
@@ -90,6 +90,7 @@
/* Commands for Disk Controller. */
#define WDCC_NOP 0x00 /* Always fail with "aborted command" */
+#define ATA_DATA_SET_MANAGEMENT 0x06
#define WDCC_RECAL 0x10 /* disk restore code -- resets cntlr */
#define WDCC_READ 0x20 /* disk read code */
@@ -387,6 +388,7 @@ struct ataparams {
#define WDC_VER_ATA5 0x0020
#define WDC_VER_ATA6 0x0040
#define WDC_VER_ATA7 0x0080
+#define WDC_VER_ATA8 0x0100
uint16_t atap_ata_minor; /* 81: Minor version number */
uint16_t atap_cmd_set1; /* 82: command set supported */
#define WDC_CMD1_NOP 0x4000 /* NOP */
@@ -451,7 +453,8 @@ struct ataparams {
uint16_t atap_apm_val; /* 91: current APM value */
uint16_t __reserved5[8]; /* 92-99: reserved */
uint16_t atap_max_lba[4]; /* 100-103: Max. user LBA addr */
- uint16_t __reserved6[2]; /* 104-105: reserved */
+ uint16_t __reserved6; /* 104: reserved */
+ uint16_t max_dsm_blocks; /* 105: DSM (ATA-8/ACS-2) */
uint16_t atap_secsz; /* 106: physical/logical sector size */
#define ATA_SECSZ_VALID_MASK 0xc000
#define ATA_SECSZ_VALID 0x4000
@@ -480,7 +483,10 @@ struct ataparams {
#define ATA_CFA_MODE1_DIS 0x1000 /* CFA Mode 1 Disabled */
#define ATA_CFA_MODE1_REQ 0x2000 /* CFA Mode 1 Required */
#define ATA_CFA_WORD160 0x8000 /* Word 160 supported */
- uint16_t __reserved10[15]; /* 161-175: reserved for CFA */
+ uint16_t __reserved10[8]; /* 161-168: reserved for CFA */
+ uint16_t support_dsm; /* 169: DSM (ATA-8/ACS-2) */
+#define ATA_SUPPORT_DSM_TRIM 0x0001
+ uint16_t __reserved10a[6]; /* 170-175: reserved for CFA */
uint8_t atap_media_serial[60]; /* 176-205: media serial number */
uint16_t __reserved11[3]; /* 206-208: */
uint16_t atap_logical_align; /* 209: logical/physical alignment */
Index: src/sys/dev/ata/wd.c
diff -u src/sys/dev/ata/wd.c:1.400 src/sys/dev/ata/wd.c:1.401
--- src/sys/dev/ata/wd.c:1.400 Tue Jul 31 15:50:34 2012
+++ src/sys/dev/ata/wd.c Fri Oct 19 17:09:07 2012
@@ -1,4 +1,4 @@
-/* $NetBSD: wd.c,v 1.400 2012/07/31 15:50:34 bouyer Exp $ */
+/* $NetBSD: wd.c,v 1.401 2012/10/19 17:09:07 drochner Exp $ */
/*
* Copyright (c) 1998, 2001 Manuel Bouyer. All rights reserved.
@@ -54,7 +54,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: wd.c,v 1.400 2012/07/31 15:50:34 bouyer Exp $");
+__KERNEL_RCSID(0, "$NetBSD: wd.c,v 1.401 2012/10/19 17:09:07 drochner Exp $");
#include "opt_ata.h"
@@ -178,6 +178,7 @@ void wdrestart(void *);
void wddone(void *);
int wd_get_params(struct wd_softc *, u_int8_t, struct ataparams *);
int wd_flushcache(struct wd_softc *, int);
+int wd_trim(struct wd_softc *, int, struct disk_discard_range *);
bool wd_shutdown(device_t, int);
int wd_getcache(struct wd_softc *, int *);
@@ -1526,6 +1527,20 @@ wdioctl(dev_t dev, u_long xfer, void *ad
return 0;
}
+ case DIOCGDISCARDPARAMS: {
+ struct disk_discard_params * tp;
+
+ if (!(wd->sc_params.atap_ata_major & WDC_VER_ATA8)
+ || !(wd->sc_params.support_dsm & ATA_SUPPORT_DSM_TRIM))
+ return ENOTTY;
+ tp = (struct disk_discard_params *)addr;
+ tp->maxsize = 0xffff; /*wd->sc_params.max_dsm_blocks*/
+ printf("wd: maxtrimsize %ld\n", tp->maxsize);
+ return 0;
+ }
+ case DIOCDISCARD:
+ return wd_trim(wd, WDPART(dev), (struct disk_discard_range *)addr);
+
default:
return ENOTTY;
}
@@ -1934,6 +1949,57 @@ wd_flushcache(struct wd_softc *wd, int f
return 0;
}
+int
+wd_trim(struct wd_softc *wd, int part, struct disk_discard_range *tr)
+{
+ struct ata_command ata_c;
+ unsigned char *req;
+ daddr_t bno = tr->bno;
+
+ if (part != RAW_PART)
+ bno += wd->sc_dk.dk_label->d_partitions[part].p_offset;;
+
+ req = kmem_zalloc(512, KM_SLEEP);
+ req[0] = bno & 0xff;
+ req[1] = (bno >> 8) & 0xff;
+ req[2] = (bno >> 16) & 0xff;
+ req[3] = (bno >> 24) & 0xff;
+ req[4] = (bno >> 32) & 0xff;
+ req[5] = (bno >> 40) & 0xff;
+ req[6] = tr->size & 0xff;
+ req[7] = (tr->size >> 8) & 0xff;
+
+ memset(&ata_c, 0, sizeof(struct ata_command));
+ ata_c.r_command = ATA_DATA_SET_MANAGEMENT;
+ ata_c.r_count = 1;
+ ata_c.r_features = ATA_SUPPORT_DSM_TRIM;
+ ata_c.r_st_bmask = WDCS_DRDY;
+ ata_c.r_st_pmask = WDCS_DRDY;
+ ata_c.timeout = 30000; /* 30s timeout */
+ ata_c.data = req;
+ ata_c.bcount = 512;
+ ata_c.flags |= AT_WRITE | AT_WAIT;
+ if (wd->atabus->ata_exec_command(wd->drvp, &ata_c) != ATACMD_COMPLETE) {
+ aprint_error_dev(wd->sc_dev,
+ "trim command didn't complete\n");
+ kmem_free(req, 512);
+ return EIO;
+ }
+ kmem_free(req, 512);
+ if (ata_c.flags & AT_ERROR) {
+ if (ata_c.r_error == WDCE_ABRT) /* command not supported */
+ return ENODEV;
+ }
+ if (ata_c.flags & (AT_ERROR | AT_TIMEOU | AT_DF)) {
+ char sbuf[sizeof(at_errbits) + 64];
+ snprintb(sbuf, sizeof(sbuf), at_errbits, ata_c.flags);
+ aprint_error_dev(wd->sc_dev, "wd_trim: status=%s\n",
+ sbuf);
+ return EIO;
+ }
+ return 0;
+}
+
bool
wd_shutdown(device_t dev, int how)
{
Index: src/sys/sys/dkio.h
diff -u src/sys/sys/dkio.h:1.17 src/sys/sys/dkio.h:1.18
--- src/sys/sys/dkio.h:1.17 Tue Jan 18 19:52:24 2011
+++ src/sys/sys/dkio.h Fri Oct 19 17:09:07 2012
@@ -1,4 +1,4 @@
-/* $NetBSD: dkio.h,v 1.17 2011/01/18 19:52:24 matt Exp $ */
+/* $NetBSD: dkio.h,v 1.18 2012/10/19 17:09:07 drochner Exp $ */
/*
* Copyright (c) 1987, 1988, 1993
@@ -109,4 +109,15 @@
#define DIOCTUR _IOR('d', 128, int) /* test unit ready */
+struct disk_discard_params {
+ long maxsize; /* in DEV_BSIZE units */
+};
+#define DIOCGDISCARDPARAMS _IOR('d', 129, struct disk_discard_params)
+
+struct disk_discard_range {
+ daddr_t bno;
+ long size;
+};
+#define DIOCDISCARD _IOW('d', 130, struct disk_discard_range)
+
#endif /* _SYS_DKIO_H_ */
Index: src/sys/sys/fstypes.h
diff -u src/sys/sys/fstypes.h:1.30 src/sys/sys/fstypes.h:1.31
--- src/sys/sys/fstypes.h:1.30 Fri Nov 18 21:17:45 2011
+++ src/sys/sys/fstypes.h Fri Oct 19 17:09:07 2012
@@ -1,4 +1,4 @@
-/* $NetBSD: fstypes.h,v 1.30 2011/11/18 21:17:45 christos Exp $ */
+/* $NetBSD: fstypes.h,v 1.31 2012/10/19 17:09:07 drochner Exp $ */
/*
* Copyright (c) 1989, 1991, 1993
@@ -84,7 +84,6 @@ typedef struct fhandle fhandle_t;
*/
#define __MNT_UNUSED1 0x00200000
-#define __MNT_UNUSED2 0x00800000
#define MNT_RDONLY 0x00000001 /* read only filesystem */
#define MNT_SYNCHRONOUS 0x00000002 /* file system written synchronously */
@@ -96,6 +95,7 @@ typedef struct fhandle fhandle_t;
#define MNT_NOCOREDUMP 0x00008000 /* don't write core dumps to this FS */
#define MNT_RELATIME 0x00020000 /* only update access time if mod/ch */
#define MNT_IGNORE 0x00100000 /* don't show entry in df */
+#define MNT_DISCARD 0x00800000 /* use DISCARD/TRIM if supported */
#define MNT_EXTATTR 0x01000000 /* enable extended attributes */
#define MNT_LOG 0x02000000 /* Use logging */
#define MNT_NOATIME 0x04000000 /* Never update access times in fs */
@@ -105,6 +105,7 @@ typedef struct fhandle fhandle_t;
#define __MNT_BASIC_FLAGS \
{ MNT_ASYNC, 0, "asynchronous" }, \
+ { MNT_DISCARD, 0, "discard" }, \
{ MNT_EXTATTR, 0, "extattr" }, \
{ MNT_IGNORE, 0, "hidden" }, \
{ MNT_LOG, 0, "log" }, \
@@ -121,9 +122,9 @@ typedef struct fhandle fhandle_t;
{ MNT_SYNCHRONOUS, 0, "synchronous" }, \
{ MNT_UNION, 0, "union" }, \
-#define MNT_BASIC_FLAGS (MNT_ASYNC | MNT_EXTATTR | MNT_LOG | MNT_NOATIME | \
- MNT_NOCOREDUMP | MNT_NODEV | MNT_NODEVMTIME | MNT_NOEXEC | MNT_NOSUID | \
- MNT_RDONLY | MNT_RELATIME | MNT_SOFTDEP | MNT_SYMPERM | \
+#define MNT_BASIC_FLAGS (MNT_ASYNC | MNT_DISCARD | MNT_EXTATTR | MNT_LOG | \
+ MNT_NOATIME | MNT_NOCOREDUMP | MNT_NODEV | MNT_NODEVMTIME | MNT_NOEXEC | \
+ MNT_NOSUID | MNT_RDONLY | MNT_RELATIME | MNT_SOFTDEP | MNT_SYMPERM | \
MNT_SYNCHRONOUS | MNT_UNION)
/*
* exported mount flags.
@@ -235,7 +236,7 @@ typedef struct fhandle fhandle_t;
"\33MNT_NOATIME" \
"\32MNT_LOG" \
"\31MNT_EXTATTR" \
- "\30MNT_UNUSED" \
+ "\30MNT_DISCARD" \
"\27MNT_GETARGS" \
"\26MNT_UNUSED" \
"\25MNT_IGNORE" \
Index: src/sys/ufs/ffs/ffs_alloc.c
diff -u src/sys/ufs/ffs/ffs_alloc.c:1.130 src/sys/ufs/ffs/ffs_alloc.c:1.131
--- src/sys/ufs/ffs/ffs_alloc.c:1.130 Mon Nov 28 08:05:07 2011
+++ src/sys/ufs/ffs/ffs_alloc.c Fri Oct 19 17:09:08 2012
@@ -1,4 +1,4 @@
-/* $NetBSD: ffs_alloc.c,v 1.130 2011/11/28 08:05:07 tls Exp $ */
+/* $NetBSD: ffs_alloc.c,v 1.131 2012/10/19 17:09:08 drochner Exp $ */
/*-
* Copyright (c) 2008, 2009 The NetBSD Foundation, Inc.
@@ -70,7 +70,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ffs_alloc.c,v 1.130 2011/11/28 08:05:07 tls Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ffs_alloc.c,v 1.131 2012/10/19 17:09:08 drochner Exp $");
#if defined(_KERNEL_OPT)
#include "opt_ffs.h"
@@ -1552,9 +1552,8 @@ ffs_blkalloc_ump(struct ufsmount *ump, d
*
* => um_lock not held on entry or exit
*/
-void
-ffs_blkfree(struct fs *fs, struct vnode *devvp, daddr_t bno, long size,
- ino_t inum)
+static void
+ffs_blkfree_cg(struct fs *fs, struct vnode *devvp, daddr_t bno, long size)
{
struct cg *cgp;
struct buf *bp;
@@ -1574,12 +1573,6 @@ ffs_blkfree(struct fs *fs, struct vnode
ump = VFSTOUFS(devvp->v_specmountpoint);
KASSERT(fs == ump->um_fs);
cgblkno = fsbtodb(fs, cgtod(fs, cg));
- if (ffs_snapblkfree(fs, devvp, bno, size, inum))
- return;
-
- error = ffs_check_bad_allocation(__func__, fs, bno, size, dev, inum);
- if (error)
- return;
error = bread(devvp, cgblkno, (int)fs->fs_cgsize,
NOCRED, B_MODIFY, &bp);
@@ -1598,6 +1591,225 @@ ffs_blkfree(struct fs *fs, struct vnode
bdwrite(bp);
}
+struct discardopdata {
+ struct work wk; /* must be first */
+ struct vnode *devvp;
+ daddr_t bno;
+ long size;
+};
+
+struct discarddata {
+ struct fs *fs;
+ struct discardopdata *entry;
+ long maxsize;
+ kmutex_t entrylk;
+ struct workqueue *wq;
+ int wqcnt, wqdraining;
+ kmutex_t wqlk;
+ kcondvar_t wqcv;
+ /* timer for flush? */
+};
+
+static void
+ffs_blkfree_td(struct fs *fs, struct discardopdata *td)
+{
+ long todo;
+
+ while (td->size) {
+ todo = min(td->size,
+ lfragtosize(fs, (fs->fs_frag - fragnum(fs, td->bno))));
+ ffs_blkfree_cg(fs, td->devvp, td->bno, todo);
+ td->bno += numfrags(fs, todo);
+ td->size -= todo;
+ }
+}
+
+static void
+ffs_discardcb(struct work *wk, void *arg)
+{
+ struct discardopdata *td = (void *)wk;
+ struct discarddata *ts = arg;
+ struct fs *fs = ts->fs;
+ struct disk_discard_range ta;
+ int error;
+
+ ta.bno = fsbtodb(fs, td->bno);
+ ta.size = td->size >> DEV_BSHIFT;
+ error = VOP_IOCTL(td->devvp, DIOCDISCARD, &ta, FWRITE, FSCRED);
+#ifdef TRIMDEBUG
+ printf("trim(%" PRId64 ",%ld):%d\n", td->bno, td->size, error);
+#endif
+
+ ffs_blkfree_td(fs, td);
+ kmem_free(td, sizeof(*td));
+ mutex_enter(&ts->wqlk);
+ ts->wqcnt--;
+ if (ts->wqdraining && !ts->wqcnt)
+ cv_signal(&ts->wqcv);
+ mutex_exit(&ts->wqlk);
+}
+
+void *
+ffs_discard_init(struct vnode *devvp, struct fs *fs)
+{
+ struct disk_discard_params tp;
+ struct discarddata *ts;
+ int error;
+
+ error = VOP_IOCTL(devvp, DIOCGDISCARDPARAMS, &tp, FREAD, FSCRED);
+ if (error) {
+ printf("DIOCGDISCARDPARAMS: %d\n", error);
+ return NULL;
+ }
+ if (tp.maxsize * DEV_BSIZE < fs->fs_bsize) {
+ printf("tp.maxsize=%ld, fs_bsize=%d\n", tp.maxsize, fs->fs_bsize);
+ return NULL;
+ }
+
+ ts = kmem_zalloc(sizeof (*ts), KM_SLEEP);
+ error = workqueue_create(&ts->wq, "trimwq", ffs_discardcb, ts,
+ 0, 0, 0);
+ if (error) {
+ kmem_free(ts, sizeof (*ts));
+ return NULL;
+ }
+ mutex_init(&ts->entrylk, MUTEX_DEFAULT, IPL_NONE);
+ mutex_init(&ts->wqlk, MUTEX_DEFAULT, IPL_NONE);
+ cv_init(&ts->wqcv, "trimwqcv");
+ ts->maxsize = max(tp.maxsize * DEV_BSIZE, 100*1024); /* XXX */
+ ts->fs = fs;
+ return ts;
+}
+
+void
+ffs_discard_finish(void *vts, int flags)
+{
+ struct discarddata *ts = vts;
+ struct discardopdata *td = NULL;
+ int res = 0;
+
+ /* wait for workqueue to drain */
+ mutex_enter(&ts->wqlk);
+ if (ts->wqcnt) {
+ ts->wqdraining = 1;
+ res = cv_timedwait(&ts->wqcv, &ts->wqlk, mstohz(5000));
+ }
+ mutex_exit(&ts->wqlk);
+ if (res)
+ printf("ffs_discarddata drain timeout\n");
+
+ mutex_enter(&ts->entrylk);
+ if (ts->entry) {
+ td = ts->entry;
+ ts->entry = NULL;
+ }
+ mutex_exit(&ts->entrylk);
+ if (td) {
+ /* XXX don't tell disk, its optional */
+ ffs_blkfree_td(ts->fs, td);
+#ifdef TRIMDEBUG
+ printf("finish(%" PRId64 ",%ld)\n", td->bno, td->size);
+#endif
+ kmem_free(td, sizeof(*td));
+ }
+
+ cv_destroy(&ts->wqcv);
+ mutex_destroy(&ts->entrylk);
+ mutex_destroy(&ts->wqlk);
+ workqueue_destroy(ts->wq);
+ kmem_free(ts, sizeof(*ts));
+}
+
+void
+ffs_blkfree(struct fs *fs, struct vnode *devvp, daddr_t bno, long size,
+ ino_t inum)
+{
+ struct ufsmount *ump;
+ int error;
+ dev_t dev;
+ struct discarddata *ts;
+ struct discardopdata *td;
+
+ dev = devvp->v_rdev;
+ ump = VFSTOUFS(devvp->v_specmountpoint);
+ if (ffs_snapblkfree(fs, devvp, bno, size, inum))
+ return;
+
+ error = ffs_check_bad_allocation(__func__, fs, bno, size, dev, inum);
+ if (error)
+ return;
+
+ if (!ump->um_discarddata) {
+ ffs_blkfree_cg(fs, devvp, bno, size);
+ return;
+ }
+
+#ifdef TRIMDEBUG
+ printf("blkfree(%" PRId64 ",%ld)\n", bno, size);
+#endif
+ ts = ump->um_discarddata;
+ td = NULL;
+
+ mutex_enter(&ts->entrylk);
+ if (ts->entry) {
+ td = ts->entry;
+ /* ffs deallocs backwards, check for prepend only */
+ if (td->bno == bno + numfrags(fs, size)
+ && td->size + size <= ts->maxsize) {
+ td->bno = bno;
+ td->size += size;
+ if (td->size < ts->maxsize) {
+#ifdef TRIMDEBUG
+ printf("defer(%" PRId64 ",%ld)\n", td->bno, td->size);
+#endif
+ mutex_exit(&ts->entrylk);
+ return;
+ }
+ size = 0; /* mark done */
+ }
+ ts->entry = NULL;
+ }
+ mutex_exit(&ts->entrylk);
+
+ if (td) {
+#ifdef TRIMDEBUG
+ printf("enq old(%" PRId64 ",%ld)\n", td->bno, td->size);
+#endif
+ mutex_enter(&ts->wqlk);
+ ts->wqcnt++;
+ mutex_exit(&ts->wqlk);
+ workqueue_enqueue(ts->wq, &td->wk, NULL);
+ }
+ if (!size)
+ return;
+
+ td = kmem_alloc(sizeof(*td), KM_SLEEP);
+ td->devvp = devvp;
+ td->bno = bno;
+ td->size = size;
+
+ if (td->size < ts->maxsize) { /* XXX always the case */
+ mutex_enter(&ts->entrylk);
+ if (!ts->entry) { /* possible race? */
+#ifdef TRIMDEBUG
+ printf("defer(%" PRId64 ",%ld)\n", td->bno, td->size);
+#endif
+ ts->entry = td;
+ td = NULL;
+ }
+ mutex_exit(&ts->entrylk);
+ }
+ if (td) {
+#ifdef TRIMDEBUG
+ printf("enq new(%" PRId64 ",%ld)\n", td->bno, td->size);
+#endif
+ mutex_enter(&ts->wqlk);
+ ts->wqcnt++;
+ mutex_exit(&ts->wqlk);
+ workqueue_enqueue(ts->wq, &td->wk, NULL);
+ }
+}
+
/*
* Free a block or fragment from a snapshot cg copy.
*
Index: src/sys/ufs/ffs/ffs_extern.h
diff -u src/sys/ufs/ffs/ffs_extern.h:1.78 src/sys/ufs/ffs/ffs_extern.h:1.79
--- src/sys/ufs/ffs/ffs_extern.h:1.78 Fri Jun 17 14:23:52 2011
+++ src/sys/ufs/ffs/ffs_extern.h Fri Oct 19 17:09:08 2012
@@ -1,4 +1,4 @@
-/* $NetBSD: ffs_extern.h,v 1.78 2011/06/17 14:23:52 manu Exp $ */
+/* $NetBSD: ffs_extern.h,v 1.79 2012/10/19 17:09:08 drochner Exp $ */
/*-
* Copyright (c) 1991, 1993, 1994
@@ -98,6 +98,8 @@ daddr_t ffs_blkpref_ufs2(struct inode *,
int ffs_blkalloc(struct inode *, daddr_t, long);
int ffs_blkalloc_ump(struct ufsmount *, daddr_t, long);
void ffs_blkfree(struct fs *, struct vnode *, daddr_t, long, ino_t);
+void *ffs_discard_init(struct vnode *, struct fs *);
+void ffs_discard_finish(void *, int);
void ffs_blkfree_snap(struct fs *, struct vnode *, daddr_t, long, ino_t);
int ffs_vfree(struct vnode *, ino_t, int);
int ffs_checkfreefile(struct fs *, struct vnode *, ino_t);
Index: src/sys/ufs/ffs/ffs_vfsops.c
diff -u src/sys/ufs/ffs/ffs_vfsops.c:1.278 src/sys/ufs/ffs/ffs_vfsops.c:1.279
--- src/sys/ufs/ffs/ffs_vfsops.c:1.278 Mon Sep 10 07:57:50 2012
+++ src/sys/ufs/ffs/ffs_vfsops.c Fri Oct 19 17:09:08 2012
@@ -1,4 +1,4 @@
-/* $NetBSD: ffs_vfsops.c,v 1.278 2012/09/10 07:57:50 manu Exp $ */
+/* $NetBSD: ffs_vfsops.c,v 1.279 2012/10/19 17:09:08 drochner Exp $ */
/*-
* Copyright (c) 2008, 2009 The NetBSD Foundation, Inc.
@@ -61,7 +61,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ffs_vfsops.c,v 1.278 2012/09/10 07:57:50 manu Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ffs_vfsops.c,v 1.279 2012/10/19 17:09:08 drochner Exp $");
#if defined(_KERNEL_OPT)
#include "opt_ffs.h"
@@ -1306,6 +1306,9 @@ ffs_mountfs(struct vnode *devvp, struct
ufs_extattr_uepm_init(&ump->um_extattr);
#endif /* UFS_EXTATTR */
+ if (mp->mnt_flag & MNT_DISCARD)
+ ump->um_discarddata = ffs_discard_init(devvp, fs);
+
return (0);
out:
#ifdef WAPBL
@@ -1462,6 +1465,11 @@ ffs_unmount(struct mount *mp, int mntfla
extern int doforce;
#endif
+ if (ump->um_discarddata) {
+ ffs_discard_finish(ump->um_discarddata, mntflags);
+ ump->um_discarddata = NULL;
+ }
+
flags = 0;
if (mntflags & MNT_FORCE)
flags |= FORCECLOSE;
Index: src/sys/ufs/ufs/ufsmount.h
diff -u src/sys/ufs/ufs/ufsmount.h:1.38 src/sys/ufs/ufs/ufsmount.h:1.39
--- src/sys/ufs/ufs/ufsmount.h:1.38 Wed May 9 00:21:18 2012
+++ src/sys/ufs/ufs/ufsmount.h Fri Oct 19 17:09:08 2012
@@ -1,4 +1,4 @@
-/* $NetBSD: ufsmount.h,v 1.38 2012/05/09 00:21:18 riastradh Exp $ */
+/* $NetBSD: ufsmount.h,v 1.39 2012/10/19 17:09:08 drochner Exp $ */
/*
* Copyright (c) 1982, 1986, 1989, 1993
@@ -124,6 +124,8 @@ struct ufsmount {
void *um_snapinfo; /* snapshot private data */
const struct ufs_ops *um_ops;
+
+ void *um_discarddata;
};
struct ufs_ops {