Mercurial > illumos > illumos-gate
changeset 12950:d560524b6bb6
6866610 Add SATA TRIM support
6971542 Add support for TPRZ and TPE bits in READ CAPACITY (16)
author | Phi Tran <Phi.Tran@Sun.COM> |
---|---|
date | Wed, 28 Jul 2010 12:45:59 -0700 |
parents | dd7214c03e0d |
children | 21b9be7f3041 |
files | usr/src/uts/common/io/sata/adapters/nv_sata/nv_sata.c usr/src/uts/common/io/sata/impl/sata.c usr/src/uts/common/sys/sata/sata_defs.h usr/src/uts/common/sys/scsi/impl/spc3_types.h |
diffstat | 4 files changed, 325 insertions(+), 18 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/uts/common/io/sata/adapters/nv_sata/nv_sata.c Wed Jul 28 12:51:51 2010 -0600 +++ b/usr/src/uts/common/io/sata/adapters/nv_sata/nv_sata.c Wed Jul 28 12:45:59 2010 -0700 @@ -1981,6 +1981,7 @@ case SATAC_WRITE_DMA_QUEUED_EXT: case SATAC_READ_FPDMA_QUEUED: case SATAC_WRITE_FPDMA_QUEUED: + case SATAC_DSM: dma_cmd = B_TRUE; break; default:
--- a/usr/src/uts/common/io/sata/impl/sata.c Wed Jul 28 12:51:51 2010 -0600 +++ b/usr/src/uts/common/io/sata/impl/sata.c Wed Jul 28 12:45:59 2010 -0700 @@ -209,6 +209,7 @@ static int sata_txlt_start_stop_unit(sata_pkt_txlate_t *); static int sata_txlt_read_capacity(sata_pkt_txlate_t *); static int sata_txlt_read_capacity16(sata_pkt_txlate_t *); +static int sata_txlt_unmap(sata_pkt_txlate_t *); static int sata_txlt_request_sense(sata_pkt_txlate_t *); static int sata_txlt_read(sata_pkt_txlate_t *); static int sata_txlt_write(sata_pkt_txlate_t *); @@ -226,9 +227,11 @@ static int sata_txlt_check_condition(sata_pkt_txlate_t *, uchar_t, uchar_t); static int sata_txlt_lba_out_of_range(sata_pkt_txlate_t *); static int sata_txlt_ata_pass_thru_illegal_cmd(sata_pkt_txlate_t *); +static int sata_txlt_unmap_nodata_cmd(sata_pkt_txlate_t *); static void sata_txlt_rw_completion(sata_pkt_t *); static void sata_txlt_nodata_cmd_completion(sata_pkt_t *); static void sata_txlt_apt_completion(sata_pkt_t *sata_pkt); +static void sata_txlt_unmap_completion(sata_pkt_t *sata_pkt); static void sata_txlt_download_mcode_cmd_completion(sata_pkt_t *); static int sata_emul_rw_completion(sata_pkt_txlate_t *); static void sata_fill_ata_return_desc(sata_pkt_t *, uint8_t, uint8_t, @@ -2628,6 +2631,11 @@ /* Other cases will be filed later */ /* postponed until phase 2 of the development */ + case SPC3_CMD_UNMAP: + if (bp != NULL && (bp->b_flags & (B_PHYS | B_PAGEIO))) + bp_mapin(bp); + rval = sata_txlt_unmap(spx); + break; default: rval = sata_txlt_invalid_command(spx); break; @@ -4500,6 +4508,8 @@ uint16_t l2p_exp; uchar_t *rbuf; int rval, reason; +#define TPE 0x80 +#define TPRZ 0x40 SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst, "sata_txlt_read_capacity: ", NULL); @@ -4606,9 +4616,17 @@ /* logical blocks per physical block exponent */ rbuf[13] = l2p_exp; - /* tpe, tprz, undefined by SAT-2 */ /* lowest aligned logical block address = 0 (for now) */ - /* rbuf[14] = 0; */ + /* tpe and tprz as defined in T10/10-079 r0 */ + if (sdinfo->satadrv_id.ai_addsupported & + SATA_DETERMINISTIC_READ) { + if (sdinfo->satadrv_id.ai_addsupported & + SATA_READ_ZERO) { + rbuf[14] |= TPRZ; + } else { + rbuf[14] |= TPE; + } + } /* rbuf[15] = 0; */ scsipkt->pkt_state |= STATE_XFERRED_DATA; @@ -4647,6 +4665,167 @@ } /* + * Translate command: UNMAP + * + * The function cannot be called in interrupt context since it may sleep. + */ +static int +sata_txlt_unmap(sata_pkt_txlate_t *spx) +{ + struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt; + sata_cmd_t *scmd = &spx->txlt_sata_pkt->satapkt_cmd; + struct buf *bp = spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp; + uint16_t count = 0; + int synch; + int rval, reason; + int i, x; + int bdlen = 0; + int ranges = 0; + int paramlen = 8; + uint8_t *data, *tmpbd; + sata_drive_info_t *sdinfo; +#define TRIM 0x1 + + SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst, + "sata_txlt_unmap: ", NULL); + + mutex_enter(&(SATA_TXLT_CPORT_MUTEX(spx))); + + sdinfo = sata_get_device_info(spx->txlt_sata_hba_inst, + &spx->txlt_sata_pkt->satapkt_device); + if (sdinfo != NULL) { + SATADBG2(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst, + "DSM support 0x%x, max number of 512 byte blocks of LBA " + "range entries 0x%x\n", sdinfo->satadrv_id.ai_dsm, + sdinfo->satadrv_id.ai_maxcount); + } + + rval = sata_txlt_generic_pkt_info(spx, &reason, 1); + if ((rval != TRAN_ACCEPT) || (reason == CMD_DEV_GONE)) { + mutex_exit(&(SATA_TXLT_CPORT_MUTEX(spx))); + return (rval); + } + + /* + * Need to modify bp to have TRIM data instead of UNMAP data. + * Start by getting the block descriptor data length by subtracting + * the 8 byte parameter list header from the parameter list length. + * The block descriptor size has to be a multiple of 16 bytes. + */ + bdlen = scsipkt->pkt_cdbp[7]; + bdlen = (bdlen << 8) + scsipkt->pkt_cdbp[8] - paramlen; + if ((bdlen < 0) || ((bdlen % 16) != 0) || + (bdlen > (bp->b_bcount - paramlen))) { + SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst, + "sata_txlt_unmap: invalid block descriptor length", NULL); + mutex_exit(&(SATA_TXLT_CPORT_MUTEX(spx))); + return ((sata_txlt_check_condition(spx, KEY_ILLEGAL_REQUEST, + SD_SCSI_ASC_INVALID_FIELD_IN_CDB))); + } + /* + * If there are no parameter data or block descriptors, it is not + * considered an error so just complete the command without sending + * TRIM. + */ + if ((bdlen == 0) || (bp == NULL) || (bp->b_un.b_addr == NULL) || + (bp->b_bcount == 0)) { + SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst, + "sata_txlt_unmap: no parameter data or block descriptors", + NULL); + mutex_exit(&(SATA_TXLT_CPORT_MUTEX(spx))); + return (sata_txlt_unmap_nodata_cmd(spx)); + } + tmpbd = (uint8_t *)bp->b_un.b_addr + paramlen; + data = kmem_zalloc(bdlen, KM_SLEEP); + + /* + * Loop through all the UNMAP block descriptors and convert the data + * into TRIM format. + */ + for (i = 0, x = 0; i < bdlen; i += 16, x += 8) { + /* get range length */ + data[x] = tmpbd[i+7]; + data[x+1] = tmpbd[i+6]; + /* get LBA */ + data[x+2] = tmpbd[i+5]; + data[x+3] = tmpbd[i+4]; + data[x+4] = tmpbd[i+3]; + data[x+5] = tmpbd[i+2]; + data[x+6] = tmpbd[i+11]; + data[x+7] = tmpbd[i+10]; + + ranges++; + } + + /* + * The TRIM command expects the data buffer to be a multiple of + * 512-byte blocks of range entries. This means that the UNMAP buffer + * may be too small. Free the original DMA resources and create a + * local buffer. + */ + sata_common_free_dma_rsrcs(spx); + + /* + * Get count of 512-byte blocks of range entries. The length + * of a range entry is 8 bytes which means one count has 64 range + * entries. + */ + count = (ranges + 63)/64; + + /* Allocate a buffer that is a multiple of 512 bytes. */ + mutex_exit(&(SATA_TXLT_CPORT_MUTEX(spx))); + bp = sata_alloc_local_buffer(spx, count * 512); + if (bp == NULL) { + SATADBG1(SATA_DBG_ATAPI, spx->txlt_sata_hba_inst, + "sata_txlt_unmap: " + "cannot allocate buffer for TRIM command", NULL); + kmem_free(data, bdlen); + return (TRAN_BUSY); + } + bp_mapin(bp); /* make data buffer accessible */ + mutex_enter(&(SATA_TXLT_CPORT_MUTEX(spx))); + + bzero(bp->b_un.b_addr, bp->b_bcount); + bcopy(data, bp->b_un.b_addr, x); + kmem_free(data, bdlen); + rval = ddi_dma_sync(spx->txlt_buf_dma_handle, 0, 0, + DDI_DMA_SYNC_FORDEV); + ASSERT(rval == DDI_SUCCESS); + + scmd->satacmd_flags.sata_data_direction = SATA_DIR_WRITE; + scmd->satacmd_addr_type = ATA_ADDR_LBA48; + scmd->satacmd_cmd_reg = SATAC_DSM; + scmd->satacmd_sec_count_msb = (count >> 8) & 0xff; + scmd->satacmd_sec_count_lsb = count & 0xff; + scmd->satacmd_features_reg = TRIM; + scmd->satacmd_device_reg = SATA_ADH_LBA; + scmd->satacmd_status_reg = 0; + scmd->satacmd_error_reg = 0; + + /* Start processing command */ + if (!(spx->txlt_sata_pkt->satapkt_op_mode & SATA_OPMODE_SYNCH)) { + spx->txlt_sata_pkt->satapkt_comp = + sata_txlt_unmap_completion; + synch = FALSE; + } else { + synch = TRUE; + } + + if (sata_hba_start(spx, &rval) != 0) { + mutex_exit(&(SATA_TXLT_CPORT_MUTEX(spx))); + return (rval); + } + + mutex_exit(&(SATA_TXLT_CPORT_MUTEX(spx))); + + if (synch) { + sata_txlt_unmap_completion(spx->txlt_sata_pkt); + } + + return (TRAN_ACCEPT); +} + +/* * SATA translate command: Mode Sense. * Translated into appropriate SATA command or emulated. * Saved Values Page Control (03) are not supported. @@ -6720,7 +6899,6 @@ stat = (*SATA_START_FUNC(sata_hba_inst))(SATA_DIP(sata_hba_inst), spx->txlt_sata_pkt); - mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst, cport))); /* * If sata pkt was accepted and executed in asynchronous mode, i.e. @@ -7067,6 +7245,35 @@ } /* + * The UNMAP command considers it not to be an error if the parameter length + * or block descriptor length is 0. For this case, there is nothing for TRIM + * to do so just complete the command. + */ +static int +sata_txlt_unmap_nodata_cmd(sata_pkt_txlate_t *spx) +{ + struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt; + + scsipkt->pkt_reason = CMD_CMPLT; + *scsipkt->pkt_scbp = STATUS_GOOD; + scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET | + STATE_SENT_CMD | STATE_GOT_STATUS; + + if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0 && + scsipkt->pkt_comp != NULL) { + /* scsi callback required */ + if (taskq_dispatch(SATA_TXLT_TASKQ(spx), + (task_func_t *)spx->txlt_scsi_pkt->pkt_comp, + (void *)spx->txlt_scsi_pkt, TQ_SLEEP) == NULL) { + /* Scheduling the callback failed */ + return (TRAN_BUSY); + } + } + + return (TRAN_ACCEPT); +} + +/* * Emulated SATA Read/Write command completion for zero-length requests. * This request always succedes, so in synchronous mode it always returns * TRAN_ACCEPT, and in non-synchronous mode it may return TRAN_BUSY if the @@ -7392,7 +7599,99 @@ } /* - * j + * Completion handler for unmap translation command + */ +static void +sata_txlt_unmap_completion(sata_pkt_t *sata_pkt) +{ + sata_pkt_txlate_t *spx = + (sata_pkt_txlate_t *)sata_pkt->satapkt_framework_private; + sata_cmd_t *scmd = &sata_pkt->satapkt_cmd; + struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt; + struct buf *bp; + uint8_t sense_key = 0, addl_sense_code = 0, addl_sense_qual = 0; + + if (sata_pkt->satapkt_reason == SATA_PKT_COMPLETED) { + /* Normal completion */ + scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET | + STATE_SENT_CMD | STATE_XFERRED_DATA | STATE_GOT_STATUS; + scsipkt->pkt_reason = CMD_CMPLT; + *scsipkt->pkt_scbp = STATUS_GOOD; + + if (spx->txlt_tmp_buf != NULL) { + /* Temporary buffer was used */ + bp = spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp; + if (bp->b_flags & B_READ) { + bcopy(spx->txlt_tmp_buf, bp->b_un.b_addr, + bp->b_bcount); + } + } + } else { + scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET | + STATE_SENT_CMD | STATE_GOT_STATUS; + scsipkt->pkt_reason = CMD_INCOMPLETE; + *scsipkt->pkt_scbp = STATUS_CHECK; + + /* + * If DF or ERR was set, the HBA should have copied out the + * status and error registers to the satacmd structure. + */ + if (scmd->satacmd_status_reg & SATA_STATUS_DF) { + sense_key = KEY_HARDWARE_ERROR; + addl_sense_code = SD_SCSI_ASC_INTERNAL_TARGET_FAILURE; + addl_sense_qual = 0; + } else if (scmd->satacmd_status_reg & SATA_STATUS_ERR) { + if (scmd->satacmd_error_reg & SATA_ERROR_NM) { + sense_key = KEY_NOT_READY; + addl_sense_code = + SD_SCSI_ASC_MEDIUM_NOT_PRESENT; + addl_sense_qual = 0; + } else if (scmd->satacmd_error_reg & SATA_ERROR_UNC) { + sense_key = KEY_MEDIUM_ERROR; + addl_sense_code = SD_SCSI_ASC_WRITE_ERR; + addl_sense_qual = 0; + } else if (scmd->satacmd_error_reg & SATA_ERROR_ILI) { + sense_key = KEY_DATA_PROTECT; + addl_sense_code = SD_SCSI_ASC_WRITE_PROTECTED; + addl_sense_qual = 0; + } else if (scmd->satacmd_error_reg & SATA_ERROR_IDNF) { + sense_key = KEY_ILLEGAL_REQUEST; + addl_sense_code = SD_SCSI_ASC_LBA_OUT_OF_RANGE; + addl_sense_qual = 0; + } else if (scmd->satacmd_error_reg & SATA_ERROR_ABORT) { + sense_key = KEY_ABORTED_COMMAND; + addl_sense_code = SD_SCSI_ASC_NO_ADD_SENSE; + addl_sense_qual = 0; + } else if (scmd->satacmd_error_reg & SATA_ERROR_MC) { + sense_key = KEY_UNIT_ATTENTION; + addl_sense_code = + SD_SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED; + addl_sense_qual = 0; + } else if (scmd->satacmd_error_reg & SATA_ERROR_MCR) { + sense_key = KEY_UNIT_ATTENTION; + addl_sense_code = SD_SCSI_ASC_OP_MEDIUM_REM_REQ; + addl_sense_qual = 0; + } else if (scmd->satacmd_error_reg & SATA_ERROR_ICRC) { + sense_key = KEY_ABORTED_COMMAND; + addl_sense_code = + SD_SCSI_ASC_INFO_UNIT_IUCRC_ERR; + addl_sense_qual = 0; + } + } + + sata_fill_ata_return_desc(sata_pkt, sense_key, addl_sense_code, + addl_sense_qual); + } + + sata_free_local_buffer(spx); + + if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0) + /* scsi callback required */ + scsi_hba_pkt_comp(scsipkt); +} + +/* + * */ static void sata_fill_ata_return_desc(sata_pkt_t *sata_pkt, uint8_t sense_key, @@ -7749,7 +8048,7 @@ * if these features are supported by the device. If these features are not * supported, the command will be terminated with STATUS_CHECK. * This function fails only if the SET FEATURE command sent to - * the device fails. The page format is not varified, assuming that the + * the device fails. The page format is not verified, assuming that the * target driver operates correctly - if parameters length is too short, * we just drop the page. * Two command may be sent if both Read Cache/Read Ahead and Write Cache
--- a/usr/src/uts/common/sys/sata/sata_defs.h Wed Jul 28 12:51:51 2010 -0600 +++ b/usr/src/uts/common/sys/sata/sata_defs.h Wed Jul 28 12:45:59 2010 -0700 @@ -20,8 +20,7 @@ */ /* - * Copyright 2010 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _SATA_DEFS_H @@ -50,6 +49,7 @@ /* * ATA/ATAPI disk commands (subset) */ +#define SATAC_DSM 0x06 /* Data Set Management */ #define SATAC_DEVICE_RESET 0x08 /* ATAPI device reset */ #define SATAC_DOWNLOAD_MICROCODE 0x92 /* Download microcode */ #define SATAC_EJECT 0xed /* media eject */ @@ -197,7 +197,8 @@ ushort_t ai_recmwdma; /* 66 rec multi-word dma cycle info */ ushort_t ai_minpio; /* 67 min PIO cycle info */ ushort_t ai_minpioflow; /* 68 min PIO cycle info w/flow ctl */ - ushort_t ai_resv3[2]; /* 69,70 reserved */ + ushort_t ai_addsupported; /* 69 additional supported */ + ushort_t ai_resv3; /* 70 reserved */ ushort_t ai_typtime[2]; /* 71-72 timing */ ushort_t ai_resv4[2]; /* 73-74 reserved */ ushort_t ai_qdepth; /* 75 queue depth */ @@ -226,7 +227,8 @@ ushort_t ai_streamperf[2]; /* 98-99 streaming performance gran. */ ushort_t ai_addrsecxt[4]; /* 100 extended max LBA sector */ ushort_t ai_stream_xfer_p; /* 104 streaming transfer time (PIO) */ - ushort_t ai_padding1; /* 105 pad */ + ushort_t ai_maxcount; /* 105 max count of 512-byte blocks of */ + /* LBA range entries */ ushort_t ai_phys_sect_sz; /* 106 physical sector size */ ushort_t ai_seek_delay; /* 107 inter-seek delay time (usecs) */ ushort_t ai_naa_ieee_oui; /* 108 NAA/IEEE OUI */ @@ -238,18 +240,19 @@ ushort_t ai_words_lsec[2]; /* 117-118 words per logical sector */ ushort_t ai_cmdset119; /* 119 more command sets supported */ ushort_t ai_features120; /* 120 enabled features */ - ushort_t ai_padding2[6]; /* pad to 126 */ + ushort_t ai_padding1[6]; /* pad to 126 */ ushort_t ai_rmsn; /* 127 removable media notification */ ushort_t ai_securestatus; /* 128 security status */ ushort_t ai_vendor[31]; /* 129-159 vendor specific */ - ushort_t ai_padding3[8]; /* 160 pad to 168 */ + ushort_t ai_padding2[8]; /* 160 pad to 168 */ ushort_t ai_nomformfactor; /* 168 nominal form factor */ - ushort_t ai_padding4[7]; /* 169 pad to 176 */ + ushort_t ai_dsm; /* 169 data set management */ + ushort_t ai_padding3[6]; /* 170 pad to 176 */ ushort_t ai_curmedser[30]; /* 176-205 current media serial # */ ushort_t ai_sctsupport; /* 206 SCT command transport */ - ushort_t ai_padding5[10]; /* 207 pad to 217 */ + ushort_t ai_padding4[10]; /* 207 pad to 217 */ ushort_t ai_medrotrate; /* 217 nominal media rotation rate */ - ushort_t ai_padding6[37]; /* 218 pad to 255 */ + ushort_t ai_padding5[37]; /* 218 pad to 255 */ ushort_t ai_integrity; /* 255 integrity word */ } sata_id_t; @@ -282,6 +285,11 @@ #define SATA_VALIDINFO_88 0x0004 /* word 88 supported fields valid */ #define SATA_VALIDINFO_70_64 0x0004 /* words 70-64 fields valid */ +/* Identify Device: ai_addsupported (word 69) */ + +#define SATA_DETERMINISTIC_READ 0x4000 /* word 69 deterministic read supp. */ +#define SATA_READ_ZERO 0x0020 /* word 69 read zero after TRIM supp. */ + /* Identify Device: ai_majorversion (word 80) */ #define SATA_MAJVER_7 0x0080 /* ATA/ATAPI-7 version supported */
--- a/usr/src/uts/common/sys/scsi/impl/spc3_types.h Wed Jul 28 12:51:51 2010 -0600 +++ b/usr/src/uts/common/sys/scsi/impl/spc3_types.h Wed Jul 28 12:45:59 2010 -0700 @@ -20,15 +20,12 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _SPC3_TYPES_H #define _SPC3_TYPES_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -161,6 +158,8 @@ SPC3_CMD_CHANGE_DEFINITION = 0x40, SPC3_CMD_WRITE_SAME = 0x41, SPC3_CMD_WRITE_SAME10 = 0x41, + SPC3_CMD_UNMAP = 0x42, + SPC3_CMD_UNMAP10 = 0x42, SPC3_CMD_READ_SUBCHANNEL = SCMD_READ_SUBCHANNEL, SPC3_CMD_READ_TOC = SCMD_READ_TOC, SPC3_CMD_REPORT_DENSITY_SUPPORT = SCMD_REPORT_DENSITIES,