Skip to content

Commit

Permalink
[SCSI] qla2xxx: T10 DIF - Handle uninitalized sectors.
Browse files Browse the repository at this point in the history
Driver needs to update protection bytes for uninitialized sectors as they are
not DMA-d.

Signed-off-by: Arun Easi <arun.easi@qlogic.com>
Reviewed-by: Andrew Vasquez <andrew.vasquez@qlogic.com>
Signed-off-by: Chad Dupuis <chad.dupuis@qlogic.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
  • Loading branch information
Arun Easi authored and James Bottomley committed Aug 27, 2011
1 parent 01350d0 commit 8cb2049
Show file tree
Hide file tree
Showing 6 changed files with 335 additions and 38 deletions.
5 changes: 4 additions & 1 deletion drivers/scsi/qla2xxx/qla_attr.c
Original file line number Diff line number Diff line change
Expand Up @@ -1788,11 +1788,14 @@ qla24xx_vport_create(struct fc_vport *fc_vport, bool disable)

if ((IS_QLA25XX(ha) || IS_QLA81XX(ha)) && ql2xenabledif) {
if (ha->fw_attributes & BIT_4) {
int prot = 0;
vha->flags.difdix_supported = 1;
ql_dbg(ql_dbg_user, vha, 0x7082,
"Registered for DIF/DIX type 1 and 3 protection.\n");
if (ql2xenabledif == 1)
prot = SHOST_DIX_TYPE0_PROTECTION;
scsi_host_set_prot(vha->host,
SHOST_DIF_TYPE1_PROTECTION
prot | SHOST_DIF_TYPE1_PROTECTION
| SHOST_DIF_TYPE2_PROTECTION
| SHOST_DIF_TYPE3_PROTECTION
| SHOST_DIX_TYPE1_PROTECTION
Expand Down
5 changes: 5 additions & 0 deletions drivers/scsi/qla2xxx/qla_fw.h
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,11 @@ struct sts_entry_24xx {
/*
* If DIF Error is set in comp_status, these additional fields are
* defined:
*
* !!! NOTE: Firmware sends expected/actual DIF data in big endian
* format; but all of the "data" field gets swab32-d in the beginning
* of qla2x00_status_entry().
*
* &data[10] : uint8_t report_runt_bg[2]; - computed guard
* &data[12] : uint8_t actual_dif[8]; - DIF Data received
* &data[20] : uint8_t expected_dif[8]; - DIF Data computed
Expand Down
21 changes: 21 additions & 0 deletions drivers/scsi/qla2xxx/qla_inline.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,24 @@ qla2x00_set_fcport_state(fc_port_t *fcport, int state)
fcport->d_id.b.al_pa);
}
}

static inline int
qla2x00_hba_err_chk_enabled(unsigned char op)
{
switch (op) {
case SCSI_PROT_READ_STRIP:
case SCSI_PROT_WRITE_INSERT:
if (ql2xenablehba_err_chk >= 1)
return 1;
break;
case SCSI_PROT_READ_PASS:
case SCSI_PROT_WRITE_PASS:
if (ql2xenablehba_err_chk >= 2)
return 1;
break;
case SCSI_PROT_READ_INSERT:
case SCSI_PROT_WRITE_STRIP:
return 1;
}
return 0;
}
233 changes: 217 additions & 16 deletions drivers/scsi/qla2xxx/qla_iocb.c
Original file line number Diff line number Diff line change
Expand Up @@ -717,20 +717,25 @@ qla24xx_set_t10dif_tags(struct scsi_cmnd *cmd, struct fw_dif_context *pkt,
unsigned char op = scsi_get_prot_op(cmd);

switch (scsi_get_prot_type(cmd)) {
/* For TYPE 0 protection: no checking */
case SCSI_PROT_DIF_TYPE0:
pkt->ref_tag_mask[0] = 0x00;
pkt->ref_tag_mask[1] = 0x00;
pkt->ref_tag_mask[2] = 0x00;
pkt->ref_tag_mask[3] = 0x00;
/*
* No check for ql2xenablehba_err_chk, as it would be an
* I/O error if hba tag generation is not done.
*/
pkt->ref_tag = cpu_to_le32((uint32_t)
(0xffffffff & scsi_get_lba(cmd)));
pkt->ref_tag_mask[0] = 0xff;
pkt->ref_tag_mask[1] = 0xff;
pkt->ref_tag_mask[2] = 0xff;
pkt->ref_tag_mask[3] = 0xff;
break;

/*
* For TYPE 2 protection: 16 bit GUARD + 32 bit REF tag has to
* match LBA in CDB + N
*/
case SCSI_PROT_DIF_TYPE2:
if (!ql2xenablehba_err_chk)
if (!qla2x00_hba_err_chk_enabled(op))
break;

if (scsi_prot_sg_count(cmd)) {
Expand Down Expand Up @@ -763,7 +768,7 @@ qla24xx_set_t10dif_tags(struct scsi_cmnd *cmd, struct fw_dif_context *pkt,
* 16 bit app tag.
*/
case SCSI_PROT_DIF_TYPE1:
if (!ql2xenablehba_err_chk)
if (!qla2x00_hba_err_chk_enabled(op))
break;

if (protcnt && (op == SCSI_PROT_WRITE_STRIP ||
Expand Down Expand Up @@ -798,7 +803,161 @@ qla24xx_set_t10dif_tags(struct scsi_cmnd *cmd, struct fw_dif_context *pkt,
scsi_get_prot_type(cmd), cmd);
}

struct qla2_sgx {
dma_addr_t dma_addr; /* OUT */
uint32_t dma_len; /* OUT */

uint32_t tot_bytes; /* IN */
struct scatterlist *cur_sg; /* IN */

/* for book keeping, bzero on initial invocation */
uint32_t bytes_consumed;
uint32_t num_bytes;
uint32_t tot_partial;

/* for debugging */
uint32_t num_sg;
srb_t *sp;
};

static int
qla24xx_get_one_block_sg(uint32_t blk_sz, struct qla2_sgx *sgx,
uint32_t *partial)
{
struct scatterlist *sg;
uint32_t cumulative_partial, sg_len;
dma_addr_t sg_dma_addr;

if (sgx->num_bytes == sgx->tot_bytes)
return 0;

sg = sgx->cur_sg;
cumulative_partial = sgx->tot_partial;

sg_dma_addr = sg_dma_address(sg);
sg_len = sg_dma_len(sg);

sgx->dma_addr = sg_dma_addr + sgx->bytes_consumed;

if ((cumulative_partial + (sg_len - sgx->bytes_consumed)) >= blk_sz) {
sgx->dma_len = (blk_sz - cumulative_partial);
sgx->tot_partial = 0;
sgx->num_bytes += blk_sz;
*partial = 0;
} else {
sgx->dma_len = sg_len - sgx->bytes_consumed;
sgx->tot_partial += sgx->dma_len;
*partial = 1;
}

sgx->bytes_consumed += sgx->dma_len;

if (sg_len == sgx->bytes_consumed) {
sg = sg_next(sg);
sgx->num_sg++;
sgx->cur_sg = sg;
sgx->bytes_consumed = 0;
}

return 1;
}

static int
qla24xx_walk_and_build_sglist_no_difb(struct qla_hw_data *ha, srb_t *sp,
uint32_t *dsd, uint16_t tot_dsds)
{
void *next_dsd;
uint8_t avail_dsds = 0;
uint32_t dsd_list_len;
struct dsd_dma *dsd_ptr;
struct scatterlist *sg_prot;
uint32_t *cur_dsd = dsd;
uint16_t used_dsds = tot_dsds;

uint32_t prot_int;
uint32_t partial;
struct qla2_sgx sgx;
dma_addr_t sle_dma;
uint32_t sle_dma_len, tot_prot_dma_len = 0;
struct scsi_cmnd *cmd = sp->cmd;

prot_int = cmd->device->sector_size;

memset(&sgx, 0, sizeof(struct qla2_sgx));
sgx.tot_bytes = scsi_bufflen(sp->cmd);
sgx.cur_sg = scsi_sglist(sp->cmd);
sgx.sp = sp;

sg_prot = scsi_prot_sglist(sp->cmd);

while (qla24xx_get_one_block_sg(prot_int, &sgx, &partial)) {

sle_dma = sgx.dma_addr;
sle_dma_len = sgx.dma_len;
alloc_and_fill:
/* Allocate additional continuation packets? */
if (avail_dsds == 0) {
avail_dsds = (used_dsds > QLA_DSDS_PER_IOCB) ?
QLA_DSDS_PER_IOCB : used_dsds;
dsd_list_len = (avail_dsds + 1) * 12;
used_dsds -= avail_dsds;

/* allocate tracking DS */
dsd_ptr = kzalloc(sizeof(struct dsd_dma), GFP_ATOMIC);
if (!dsd_ptr)
return 1;

/* allocate new list */
dsd_ptr->dsd_addr = next_dsd =
dma_pool_alloc(ha->dl_dma_pool, GFP_ATOMIC,
&dsd_ptr->dsd_list_dma);

if (!next_dsd) {
/*
* Need to cleanup only this dsd_ptr, rest
* will be done by sp_free_dma()
*/
kfree(dsd_ptr);
return 1;
}

list_add_tail(&dsd_ptr->list,
&((struct crc_context *)sp->ctx)->dsd_list);

sp->flags |= SRB_CRC_CTX_DSD_VALID;

/* add new list to cmd iocb or last list */
*cur_dsd++ = cpu_to_le32(LSD(dsd_ptr->dsd_list_dma));
*cur_dsd++ = cpu_to_le32(MSD(dsd_ptr->dsd_list_dma));
*cur_dsd++ = dsd_list_len;
cur_dsd = (uint32_t *)next_dsd;
}
*cur_dsd++ = cpu_to_le32(LSD(sle_dma));
*cur_dsd++ = cpu_to_le32(MSD(sle_dma));
*cur_dsd++ = cpu_to_le32(sle_dma_len);
avail_dsds--;

if (partial == 0) {
/* Got a full protection interval */
sle_dma = sg_dma_address(sg_prot) + tot_prot_dma_len;
sle_dma_len = 8;

tot_prot_dma_len += sle_dma_len;
if (tot_prot_dma_len == sg_dma_len(sg_prot)) {
tot_prot_dma_len = 0;
sg_prot = sg_next(sg_prot);
}

partial = 1; /* So as to not re-enter this block */
goto alloc_and_fill;
}
}
/* Null termination */
*cur_dsd++ = 0;
*cur_dsd++ = 0;
*cur_dsd++ = 0;
return 0;
}
static int
qla24xx_walk_and_build_sglist(struct qla_hw_data *ha, srb_t *sp, uint32_t *dsd,
uint16_t tot_dsds)
Expand Down Expand Up @@ -981,7 +1140,7 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt,
struct scsi_cmnd *cmd;
struct scatterlist *cur_seg;
int sgc;
uint32_t total_bytes;
uint32_t total_bytes = 0;
uint32_t data_bytes;
uint32_t dif_bytes;
uint8_t bundling = 1;
Expand Down Expand Up @@ -1023,8 +1182,10 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt,
__constant_cpu_to_le16(CF_READ_DATA);
}

tot_prot_dsds = scsi_prot_sg_count(cmd);
if (!tot_prot_dsds)
if ((scsi_get_prot_op(sp->cmd) == SCSI_PROT_READ_INSERT) ||
(scsi_get_prot_op(sp->cmd) == SCSI_PROT_WRITE_STRIP) ||
(scsi_get_prot_op(sp->cmd) == SCSI_PROT_READ_STRIP) ||
(scsi_get_prot_op(sp->cmd) == SCSI_PROT_WRITE_INSERT))
bundling = 0;

/* Allocate CRC context from global pool */
Expand Down Expand Up @@ -1107,15 +1268,28 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt,
cmd_pkt->fcp_rsp_dseg_len = 0; /* Let response come in status iocb */

/* Compute dif len and adjust data len to incude protection */
total_bytes = data_bytes;
dif_bytes = 0;
blk_size = cmd->device->sector_size;
if (scsi_get_prot_op(cmd) != SCSI_PROT_NORMAL) {
dif_bytes = (data_bytes / blk_size) * 8;
total_bytes += dif_bytes;
dif_bytes = (data_bytes / blk_size) * 8;

switch (scsi_get_prot_op(sp->cmd)) {
case SCSI_PROT_READ_INSERT:
case SCSI_PROT_WRITE_STRIP:
total_bytes = data_bytes;
data_bytes += dif_bytes;
break;

case SCSI_PROT_READ_STRIP:
case SCSI_PROT_WRITE_INSERT:
case SCSI_PROT_READ_PASS:
case SCSI_PROT_WRITE_PASS:
total_bytes = data_bytes + dif_bytes;
break;
default:
BUG();
}

if (!ql2xenablehba_err_chk)
if (!qla2x00_hba_err_chk_enabled(scsi_get_prot_op(cmd)))
fw_prot_opts |= 0x10; /* Disable Guard tag checking */

if (!bundling) {
Expand Down Expand Up @@ -1151,7 +1325,12 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt,

cmd_pkt->control_flags |=
__constant_cpu_to_le16(CF_DATA_SEG_DESCR_ENABLE);
if (qla24xx_walk_and_build_sglist(ha, sp, cur_dsd,

if (!bundling && tot_prot_dsds) {
if (qla24xx_walk_and_build_sglist_no_difb(ha, sp,
cur_dsd, tot_dsds))
goto crc_queuing_error;
} else if (qla24xx_walk_and_build_sglist(ha, sp, cur_dsd,
(tot_dsds - tot_prot_dsds)))
goto crc_queuing_error;

Expand Down Expand Up @@ -1414,6 +1593,22 @@ qla24xx_dif_start_scsi(srb_t *sp)
goto queuing_error;
else
sp->flags |= SRB_DMA_VALID;

if ((scsi_get_prot_op(cmd) == SCSI_PROT_READ_INSERT) ||
(scsi_get_prot_op(cmd) == SCSI_PROT_WRITE_STRIP)) {
struct qla2_sgx sgx;
uint32_t partial;

memset(&sgx, 0, sizeof(struct qla2_sgx));
sgx.tot_bytes = scsi_bufflen(cmd);
sgx.cur_sg = scsi_sglist(cmd);
sgx.sp = sp;

nseg = 0;
while (qla24xx_get_one_block_sg(
cmd->device->sector_size, &sgx, &partial))
nseg++;
}
} else
nseg = 0;

Expand All @@ -1428,6 +1623,11 @@ qla24xx_dif_start_scsi(srb_t *sp)
goto queuing_error;
else
sp->flags |= SRB_CRC_PROT_DMA_VALID;

if ((scsi_get_prot_op(cmd) == SCSI_PROT_READ_INSERT) ||
(scsi_get_prot_op(cmd) == SCSI_PROT_WRITE_STRIP)) {
nseg = scsi_bufflen(cmd) / cmd->device->sector_size;
}
} else {
nseg = 0;
}
Expand All @@ -1454,6 +1654,7 @@ qla24xx_dif_start_scsi(srb_t *sp)
/* Build header part of command packet (excluding the OPCODE). */
req->current_outstanding_cmd = handle;
req->outstanding_cmds[handle] = sp;
sp->handle = handle;
sp->cmd->host_scribble = (unsigned char *)(unsigned long)handle;
req->cnt -= req_cnt;

Expand Down
Loading

0 comments on commit 8cb2049

Please sign in to comment.