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.
This commit is contained in:
drochner 2012-10-19 17:09:06 +00:00
parent ba854cbddd
commit 5724e77fda
12 changed files with 351 additions and 33 deletions

View File

@ -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 }

View File

@ -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;
}

View File

@ -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 recreate the file system from scratch.
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.

View File

@ -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, 1994\
#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,
};

View File

@ -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 */

View File

@ -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 *addr, int flag, struct lwp *l)
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 flags)
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)
{

View File

@ -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_ */

View File

@ -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" \

View File

@ -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, daddr_t bno, long size)
*
* => 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 *devvp, daddr_t bno, long size,
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 *devvp, daddr_t bno, long size,
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.
*

View File

@ -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 *, daddr_t, int, int, int64_t *);
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);

View File

@ -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 mount *mp, struct lwp *l)
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 mntflags)
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;

View File

@ -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 {