diff options
| author | drochner <drochner@NetBSD.org> | 2012-10-19 17:09:06 +0000 |
|---|---|---|
| committer | drochner <drochner@NetBSD.org> | 2012-10-19 17:09:06 +0000 |
| commit | 8feb2e9e9cc5d2370fbfecead7298554ce2ac952 (patch) | |
| tree | d6e4e4794437f8aea6e5790fb05fc34425ff0f16 /sys/dev/ata | |
| parent | 0b5f293a141d215016838bb5f18cc625fbdb80c5 (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.h | 12 | ||||
| -rw-r--r-- | sys/dev/ata/wd.c | 70 |
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) { |
