[SCSI] pm80xx: 4G boundary fix.

Firmware is having an issue.  When a single IO request crosses
4G boundary, system will crash.  To avoid the issue single sg is
converted into extended sg.

Signed-off-by: Anandkumar.Santhanam@pmcs.com
Reviewed-by: Jack Wang <jinpu.wang@profitbricks.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
This commit is contained in:
Anand Kumar Santhanam 2013-09-18 11:14:54 +05:30 committed by James Bottomley
parent f9cd6cbd80
commit 0ecdf00ba6
1 changed files with 101 additions and 2 deletions

View File

@ -3610,7 +3610,8 @@ static int pm80xx_chip_ssp_io_req(struct pm8001_hba_info *pm8001_ha,
struct ssp_ini_io_start_req ssp_cmd;
u32 tag = ccb->ccb_tag;
int ret;
u64 phys_addr;
u64 phys_addr, start_addr, end_addr;
u32 end_addr_high, end_addr_low;
struct inbound_queue_table *circularQ;
u32 q_index;
u32 opc = OPC_INB_SSPINIIOSTART;
@ -3664,6 +3665,30 @@ static int pm80xx_chip_ssp_io_req(struct pm8001_hba_info *pm8001_ha,
cpu_to_le32(upper_32_bits(dma_addr));
ssp_cmd.enc_len = cpu_to_le32(task->total_xfer_len);
ssp_cmd.enc_esgl = 0;
/* Check 4G Boundary */
start_addr = cpu_to_le64(dma_addr);
end_addr = (start_addr + ssp_cmd.enc_len) - 1;
end_addr_low = cpu_to_le32(lower_32_bits(end_addr));
end_addr_high = cpu_to_le32(upper_32_bits(end_addr));
if (end_addr_high != ssp_cmd.enc_addr_high) {
PM8001_FAIL_DBG(pm8001_ha,
pm8001_printk("The sg list address "
"start_addr=0x%016llx data_len=0x%x "
"end_addr_high=0x%08x end_addr_low="
"0x%08x has crossed 4G boundary\n",
start_addr, ssp_cmd.enc_len,
end_addr_high, end_addr_low));
pm8001_chip_make_sg(task->scatter, 1,
ccb->buf_prd);
phys_addr = ccb->ccb_dma_handle +
offsetof(struct pm8001_ccb_info,
buf_prd[0]);
ssp_cmd.enc_addr_low =
cpu_to_le32(lower_32_bits(phys_addr));
ssp_cmd.enc_addr_high =
cpu_to_le32(upper_32_bits(phys_addr));
ssp_cmd.enc_esgl = cpu_to_le32(1<<31);
}
} else if (task->num_scatter == 0) {
ssp_cmd.enc_addr_low = 0;
ssp_cmd.enc_addr_high = 0;
@ -3699,6 +3724,30 @@ static int pm80xx_chip_ssp_io_req(struct pm8001_hba_info *pm8001_ha,
cpu_to_le32(upper_32_bits(dma_addr));
ssp_cmd.len = cpu_to_le32(task->total_xfer_len);
ssp_cmd.esgl = 0;
/* Check 4G Boundary */
start_addr = cpu_to_le64(dma_addr);
end_addr = (start_addr + ssp_cmd.len) - 1;
end_addr_low = cpu_to_le32(lower_32_bits(end_addr));
end_addr_high = cpu_to_le32(upper_32_bits(end_addr));
if (end_addr_high != ssp_cmd.addr_high) {
PM8001_FAIL_DBG(pm8001_ha,
pm8001_printk("The sg list address "
"start_addr=0x%016llx data_len=0x%x "
"end_addr_high=0x%08x end_addr_low="
"0x%08x has crossed 4G boundary\n",
start_addr, ssp_cmd.len,
end_addr_high, end_addr_low));
pm8001_chip_make_sg(task->scatter, 1,
ccb->buf_prd);
phys_addr = ccb->ccb_dma_handle +
offsetof(struct pm8001_ccb_info,
buf_prd[0]);
ssp_cmd.addr_low =
cpu_to_le32(lower_32_bits(phys_addr));
ssp_cmd.addr_high =
cpu_to_le32(upper_32_bits(phys_addr));
ssp_cmd.esgl = cpu_to_le32(1<<31);
}
} else if (task->num_scatter == 0) {
ssp_cmd.addr_low = 0;
ssp_cmd.addr_high = 0;
@ -3723,7 +3772,8 @@ static int pm80xx_chip_sata_req(struct pm8001_hba_info *pm8001_ha,
u32 q_index;
struct sata_start_req sata_cmd;
u32 hdr_tag, ncg_tag = 0;
u64 phys_addr;
u64 phys_addr, start_addr, end_addr;
u32 end_addr_high, end_addr_low;
u32 ATAP = 0x0;
u32 dir;
struct inbound_queue_table *circularQ;
@ -3792,6 +3842,31 @@ static int pm80xx_chip_sata_req(struct pm8001_hba_info *pm8001_ha,
sata_cmd.enc_addr_high = upper_32_bits(dma_addr);
sata_cmd.enc_len = cpu_to_le32(task->total_xfer_len);
sata_cmd.enc_esgl = 0;
/* Check 4G Boundary */
start_addr = cpu_to_le64(dma_addr);
end_addr = (start_addr + sata_cmd.enc_len) - 1;
end_addr_low = cpu_to_le32(lower_32_bits(end_addr));
end_addr_high = cpu_to_le32(upper_32_bits(end_addr));
if (end_addr_high != sata_cmd.enc_addr_high) {
PM8001_FAIL_DBG(pm8001_ha,
pm8001_printk("The sg list address "
"start_addr=0x%016llx data_len=0x%x "
"end_addr_high=0x%08x end_addr_low"
"=0x%08x has crossed 4G boundary\n",
start_addr, sata_cmd.enc_len,
end_addr_high, end_addr_low));
pm8001_chip_make_sg(task->scatter, 1,
ccb->buf_prd);
phys_addr = ccb->ccb_dma_handle +
offsetof(struct pm8001_ccb_info,
buf_prd[0]);
sata_cmd.enc_addr_low =
lower_32_bits(phys_addr);
sata_cmd.enc_addr_high =
upper_32_bits(phys_addr);
sata_cmd.enc_esgl =
cpu_to_le32(1 << 31);
}
} else if (task->num_scatter == 0) {
sata_cmd.enc_addr_low = 0;
sata_cmd.enc_addr_high = 0;
@ -3833,6 +3908,30 @@ static int pm80xx_chip_sata_req(struct pm8001_hba_info *pm8001_ha,
sata_cmd.addr_high = upper_32_bits(dma_addr);
sata_cmd.len = cpu_to_le32(task->total_xfer_len);
sata_cmd.esgl = 0;
/* Check 4G Boundary */
start_addr = cpu_to_le64(dma_addr);
end_addr = (start_addr + sata_cmd.len) - 1;
end_addr_low = cpu_to_le32(lower_32_bits(end_addr));
end_addr_high = cpu_to_le32(upper_32_bits(end_addr));
if (end_addr_high != sata_cmd.addr_high) {
PM8001_FAIL_DBG(pm8001_ha,
pm8001_printk("The sg list address "
"start_addr=0x%016llx data_len=0x%x"
"end_addr_high=0x%08x end_addr_low="
"0x%08x has crossed 4G boundary\n",
start_addr, sata_cmd.len,
end_addr_high, end_addr_low));
pm8001_chip_make_sg(task->scatter, 1,
ccb->buf_prd);
phys_addr = ccb->ccb_dma_handle +
offsetof(struct pm8001_ccb_info,
buf_prd[0]);
sata_cmd.addr_low =
lower_32_bits(phys_addr);
sata_cmd.addr_high =
upper_32_bits(phys_addr);
sata_cmd.esgl = cpu_to_le32(1 << 31);
}
} else if (task->num_scatter == 0) {
sata_cmd.addr_low = 0;
sata_cmd.addr_high = 0;