summaryrefslogtreecommitdiff
path: root/sys/dev/ata
diff options
context:
space:
mode:
authordrochner <drochner@NetBSD.org>2012-10-19 17:09:06 +0000
committerdrochner <drochner@NetBSD.org>2012-10-19 17:09:06 +0000
commit8feb2e9e9cc5d2370fbfecead7298554ce2ac952 (patch)
treed6e4e4794437f8aea6e5790fb05fc34425ff0f16 /sys/dev/ata
parent0b5f293a141d215016838bb5f18cc625fbdb80c5 (diff)
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.
Diffstat (limited to 'sys/dev/ata')
-rw-r--r--sys/dev/ata/atareg.h12
-rw-r--r--sys/dev/ata/wd.c70
2 files changed, 77 insertions, 5 deletions
diff --git a/sys/dev/ata/atareg.h b/sys/dev/ata/atareg.h
index 5b7954bacc6..b7a3a2ec304 100644
--- a/sys/dev/ata/atareg.h
+++ b/sys/dev/ata/atareg.h
@@ -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 */
diff --git a/sys/dev/ata/wd.c b/sys/dev/ata/wd.c
index 1e381bebe7b..131f5404dd8 100644
--- a/sys/dev/ata/wd.c
+++ b/sys/dev/ata/wd.c
@@ -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)
{