diff options
author | Dave Jiang <dave.jiang@intel.com> | 2012-02-10 01:18:34 -0800 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2012-02-19 09:14:24 -0600 |
commit | 3d2d752549150c2706f6bf8d8a2cceb89ef9f42e (patch) | |
tree | b50821eef1f21902c5d22263001e202b65c3d680 /drivers/scsi | |
parent | a6fe35c052c4fdd1e8e21251f14eea0bd4bbd25b (diff) | |
download | linux-3d2d752549150c2706f6bf8d8a2cceb89ef9f42e.tar.gz linux-3d2d752549150c2706f6bf8d8a2cceb89ef9f42e.tar.bz2 linux-3d2d752549150c2706f6bf8d8a2cceb89ef9f42e.zip |
[SCSI] isci: T10 DIF support
This allows the controller to do WRITE_INSERT and READ_STRIP for SAS
disks that support protection information. SAS disks must be formatted
with protection information to use this feature via sg_format.
sg3_utils-1.32 -- sg_format version 1.19 20110730
sg_format usage:
sg_format --format --verbose --pinfo /dev/sda
Acked-by: Martin K. Petersen <martin.petersen@oracle.com>
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers/scsi')
-rw-r--r-- | drivers/scsi/isci/init.c | 7 | ||||
-rw-r--r-- | drivers/scsi/isci/request.c | 147 | ||||
-rw-r--r-- | drivers/scsi/isci/scu_task_context.h | 55 |
3 files changed, 193 insertions, 16 deletions
diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c index 17c4c2c89c2e..6b911e0aea3f 100644 --- a/drivers/scsi/isci/init.c +++ b/drivers/scsi/isci/init.c @@ -528,6 +528,13 @@ static int __devinit isci_pci_probe(struct pci_dev *pdev, const struct pci_devic goto err_host_alloc; } pci_info->hosts[i] = h; + + /* turn on DIF support */ + scsi_host_set_prot(h->shost, + SHOST_DIF_TYPE1_PROTECTION | + SHOST_DIF_TYPE2_PROTECTION | + SHOST_DIF_TYPE3_PROTECTION); + scsi_host_set_guard(h->shost, SHOST_DIX_GUARD_CRC); } err = isci_setup_interrupts(pdev); diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c index 192cb48d849a..751368b46b44 100644 --- a/drivers/scsi/isci/request.c +++ b/drivers/scsi/isci/request.c @@ -53,6 +53,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include <scsi/scsi_cmnd.h> #include "isci.h" #include "task.h" #include "request.h" @@ -264,6 +265,141 @@ static void scu_ssp_reqeust_construct_task_context( task_context->response_iu_lower = lower_32_bits(dma_addr); } +static u8 scu_bg_blk_size(struct scsi_device *sdp) +{ + switch (sdp->sector_size) { + case 512: + return 0; + case 1024: + return 1; + case 4096: + return 3; + default: + return 0xff; + } +} + +static u32 scu_dif_bytes(u32 len, u32 sector_size) +{ + return (len >> ilog2(sector_size)) * 8; +} + +static void scu_ssp_ireq_dif_insert(struct isci_request *ireq, u8 type, u8 op) +{ + struct scu_task_context *tc = ireq->tc; + struct scsi_cmnd *scmd = ireq->ttype_ptr.io_task_ptr->uldd_task; + u8 blk_sz = scu_bg_blk_size(scmd->device); + + tc->block_guard_enable = 1; + tc->blk_prot_en = 1; + tc->blk_sz = blk_sz; + /* DIF write insert */ + tc->blk_prot_func = 0x2; + + tc->transfer_length_bytes += scu_dif_bytes(tc->transfer_length_bytes, + scmd->device->sector_size); + + /* always init to 0, used by hw */ + tc->interm_crc_val = 0; + + tc->init_crc_seed = 0; + tc->app_tag_verify = 0; + tc->app_tag_gen = 0; + tc->ref_tag_seed_verify = 0; + + /* always init to same as bg_blk_sz */ + tc->UD_bytes_immed_val = scmd->device->sector_size; + + tc->reserved_DC_0 = 0; + + /* always init to 8 */ + tc->DIF_bytes_immed_val = 8; + + tc->reserved_DC_1 = 0; + tc->bgc_blk_sz = scmd->device->sector_size; + tc->reserved_E0_0 = 0; + tc->app_tag_gen_mask = 0; + + /** setup block guard control **/ + tc->bgctl = 0; + + /* DIF write insert */ + tc->bgctl_f.op = 0x2; + + tc->app_tag_verify_mask = 0; + + /* must init to 0 for hw */ + tc->blk_guard_err = 0; + + tc->reserved_E8_0 = 0; + + if ((type & SCSI_PROT_DIF_TYPE1) || (type & SCSI_PROT_DIF_TYPE2)) + tc->ref_tag_seed_gen = scsi_get_lba(scmd) & 0xffffffff; + else if (type & SCSI_PROT_DIF_TYPE3) + tc->ref_tag_seed_gen = 0; +} + +static void scu_ssp_ireq_dif_strip(struct isci_request *ireq, u8 type, u8 op) +{ + struct scu_task_context *tc = ireq->tc; + struct scsi_cmnd *scmd = ireq->ttype_ptr.io_task_ptr->uldd_task; + u8 blk_sz = scu_bg_blk_size(scmd->device); + + tc->block_guard_enable = 1; + tc->blk_prot_en = 1; + tc->blk_sz = blk_sz; + /* DIF read strip */ + tc->blk_prot_func = 0x1; + + tc->transfer_length_bytes += scu_dif_bytes(tc->transfer_length_bytes, + scmd->device->sector_size); + + /* always init to 0, used by hw */ + tc->interm_crc_val = 0; + + tc->init_crc_seed = 0; + tc->app_tag_verify = 0; + tc->app_tag_gen = 0; + + if ((type & SCSI_PROT_DIF_TYPE1) || (type & SCSI_PROT_DIF_TYPE2)) + tc->ref_tag_seed_verify = scsi_get_lba(scmd) & 0xffffffff; + else if (type & SCSI_PROT_DIF_TYPE3) + tc->ref_tag_seed_verify = 0; + + /* always init to same as bg_blk_sz */ + tc->UD_bytes_immed_val = scmd->device->sector_size; + + tc->reserved_DC_0 = 0; + + /* always init to 8 */ + tc->DIF_bytes_immed_val = 8; + + tc->reserved_DC_1 = 0; + tc->bgc_blk_sz = scmd->device->sector_size; + tc->reserved_E0_0 = 0; + tc->app_tag_gen_mask = 0; + + /** setup block guard control **/ + tc->bgctl = 0; + + /* DIF read strip */ + tc->bgctl_f.crc_verify = 1; + tc->bgctl_f.op = 0x1; + if ((type & SCSI_PROT_DIF_TYPE1) || (type & SCSI_PROT_DIF_TYPE2)) { + tc->bgctl_f.ref_tag_chk = 1; + tc->bgctl_f.app_f_detect = 1; + } else if (type & SCSI_PROT_DIF_TYPE3) + tc->bgctl_f.app_ref_f_detect = 1; + + tc->app_tag_verify_mask = 0; + + /* must init to 0 for hw */ + tc->blk_guard_err = 0; + + tc->reserved_E8_0 = 0; + tc->ref_tag_seed_gen = 0; +} + /** * This method is will fill in the SCU Task Context for a SSP IO request. * @sci_req: @@ -274,6 +410,10 @@ static void scu_ssp_io_request_construct_task_context(struct isci_request *ireq, u32 len) { struct scu_task_context *task_context = ireq->tc; + struct sas_task *sas_task = ireq->ttype_ptr.io_task_ptr; + struct scsi_cmnd *scmd = sas_task->uldd_task; + u8 prot_type = scsi_get_prot_type(scmd); + u8 prot_op = scsi_get_prot_op(scmd); scu_ssp_reqeust_construct_task_context(ireq, task_context); @@ -296,6 +436,13 @@ static void scu_ssp_io_request_construct_task_context(struct isci_request *ireq, if (task_context->transfer_length_bytes > 0) sci_request_build_sgl(ireq); + + if (prot_type != SCSI_PROT_DIF_TYPE0) { + if (prot_op == SCSI_PROT_READ_STRIP) + scu_ssp_ireq_dif_strip(ireq, prot_type, prot_op); + else if (prot_op == SCSI_PROT_WRITE_INSERT) + scu_ssp_ireq_dif_insert(ireq, prot_type, prot_op); + } } /** diff --git a/drivers/scsi/isci/scu_task_context.h b/drivers/scsi/isci/scu_task_context.h index 7df87d923285..869a979eb5b2 100644 --- a/drivers/scsi/isci/scu_task_context.h +++ b/drivers/scsi/isci/scu_task_context.h @@ -866,9 +866,9 @@ struct scu_task_context { struct transport_snapshot snapshot; /* read only set to 0 */ /* OFFSET 0x5C */ - u32 block_protection_enable:1; - u32 block_size:2; - u32 block_protection_function:2; + u32 blk_prot_en:1; + u32 blk_sz:2; + u32 blk_prot_func:2; u32 reserved_5C_0:9; u32 active_sgl_element:2; /* read only set to 0 */ u32 sgl_exhausted:1; /* read only set to 0 */ @@ -896,33 +896,56 @@ struct scu_task_context { u32 reserved_C4_CC[3]; /* OFFSET 0xD0 */ - u32 intermediate_crc_value:16; - u32 initial_crc_seed:16; + u32 interm_crc_val:16; + u32 init_crc_seed:16; /* OFFSET 0xD4 */ - u32 application_tag_for_verify:16; - u32 application_tag_for_generate:16; + u32 app_tag_verify:16; + u32 app_tag_gen:16; /* OFFSET 0xD8 */ - u32 reference_tag_seed_for_verify_function; + u32 ref_tag_seed_verify; /* OFFSET 0xDC */ - u32 reserved_DC; + u32 UD_bytes_immed_val:13; + u32 reserved_DC_0:3; + u32 DIF_bytes_immed_val:4; + u32 reserved_DC_1:12; /* OFFSET 0xE0 */ - u32 reserved_E0_0:16; - u32 application_tag_mask_for_generate:16; + u32 bgc_blk_sz:13; + u32 reserved_E0_0:3; + u32 app_tag_gen_mask:16; /* OFFSET 0xE4 */ - u32 block_protection_control:16; - u32 application_tag_mask_for_verify:16; + union { + u16 bgctl; + struct { + u16 crc_verify:1; + u16 app_tag_chk:1; + u16 ref_tag_chk:1; + u16 op:2; + u16 legacy:1; + u16 invert_crc_seed:1; + u16 ref_tag_gen:1; + u16 fixed_ref_tag:1; + u16 invert_crc:1; + u16 app_ref_f_detect:1; + u16 uninit_dif_check_err:1; + u16 uninit_dif_bypass:1; + u16 app_f_detect:1; + u16 reserved_0:2; + } bgctl_f; + }; + + u16 app_tag_verify_mask; /* OFFSET 0xE8 */ - u32 block_protection_error:8; + u32 blk_guard_err:8; u32 reserved_E8_0:24; /* OFFSET 0xEC */ - u32 reference_tag_seed_for_verify; + u32 ref_tag_seed_gen; /* OFFSET 0xF0 */ u32 intermediate_crc_valid_snapshot:16; @@ -937,6 +960,6 @@ struct scu_task_context { /* OFFSET 0xFC */ u32 reference_tag_seed_for_generate_function_snapshot; -}; +} __packed; #endif /* _SCU_TASK_CONTEXT_H_ */ |