[SCSI] mpt2sas: Added multisegment mode support for Linux BSG Driver

Added support for Block IO requests with multiple segments (vectors) in
the SMP handler of the SAS Transport Class. This is required by the
BSG driver. Multisegment support added for both, Request and Response.

Signed-off-by: Nagalakshmi Nandigama <nagalakshmi.nandigama@lsi.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
This commit is contained in:
nagalakshmi.nandigama@lsi.com 2012-03-20 12:04:43 +05:30 committed by James Bottomley
parent 913809f6e7
commit 77a6edffdf
1 changed files with 104 additions and 29 deletions

View File

@ -1828,7 +1828,7 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
Mpi2SmpPassthroughRequest_t *mpi_request; Mpi2SmpPassthroughRequest_t *mpi_request;
Mpi2SmpPassthroughReply_t *mpi_reply; Mpi2SmpPassthroughReply_t *mpi_reply;
int rc; int rc, i;
u16 smid; u16 smid;
u32 ioc_state; u32 ioc_state;
unsigned long timeleft; unsigned long timeleft;
@ -1837,24 +1837,20 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
u8 issue_reset = 0; u8 issue_reset = 0;
dma_addr_t dma_addr_in = 0; dma_addr_t dma_addr_in = 0;
dma_addr_t dma_addr_out = 0; dma_addr_t dma_addr_out = 0;
dma_addr_t pci_dma_in = 0;
dma_addr_t pci_dma_out = 0;
void *pci_addr_in = NULL;
void *pci_addr_out = NULL;
u16 wait_state_count; u16 wait_state_count;
struct request *rsp = req->next_rq; struct request *rsp = req->next_rq;
struct bio_vec *bvec = NULL;
if (!rsp) { if (!rsp) {
printk(MPT2SAS_ERR_FMT "%s: the smp response space is " printk(MPT2SAS_ERR_FMT "%s: the smp response space is "
"missing\n", ioc->name, __func__); "missing\n", ioc->name, __func__);
return -EINVAL; return -EINVAL;
} }
if (ioc->shost_recovery || ioc->pci_error_recovery) {
/* do we need to support multiple segments? */
if (req->bio->bi_vcnt > 1 || rsp->bio->bi_vcnt > 1) {
printk(MPT2SAS_ERR_FMT "%s: multiple segments req %u %u, "
"rsp %u %u\n", ioc->name, __func__, req->bio->bi_vcnt,
blk_rq_bytes(req), rsp->bio->bi_vcnt, blk_rq_bytes(rsp));
return -EINVAL;
}
if (ioc->shost_recovery) {
printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n", printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n",
__func__, ioc->name); __func__, ioc->name);
return -EFAULT; return -EFAULT;
@ -1872,6 +1868,59 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
} }
ioc->transport_cmds.status = MPT2_CMD_PENDING; ioc->transport_cmds.status = MPT2_CMD_PENDING;
/* Check if the request is split across multiple segments */
if (req->bio->bi_vcnt > 1) {
u32 offset = 0;
/* Allocate memory and copy the request */
pci_addr_out = pci_alloc_consistent(ioc->pdev,
blk_rq_bytes(req), &pci_dma_out);
if (!pci_addr_out) {
printk(MPT2SAS_INFO_FMT "%s(): PCI Addr out = NULL\n",
ioc->name, __func__);
rc = -ENOMEM;
goto out;
}
bio_for_each_segment(bvec, req->bio, i) {
memcpy(pci_addr_out + offset,
page_address(bvec->bv_page) + bvec->bv_offset,
bvec->bv_len);
offset += bvec->bv_len;
}
} else {
dma_addr_out = pci_map_single(ioc->pdev, bio_data(req->bio),
blk_rq_bytes(req), PCI_DMA_BIDIRECTIONAL);
if (!dma_addr_out) {
printk(MPT2SAS_INFO_FMT "%s(): DMA Addr out = NULL\n",
ioc->name, __func__);
rc = -ENOMEM;
goto free_pci;
}
}
/* Check if the response needs to be populated across
* multiple segments */
if (rsp->bio->bi_vcnt > 1) {
pci_addr_in = pci_alloc_consistent(ioc->pdev, blk_rq_bytes(rsp),
&pci_dma_in);
if (!pci_addr_in) {
printk(MPT2SAS_INFO_FMT "%s(): PCI Addr in = NULL\n",
ioc->name, __func__);
rc = -ENOMEM;
goto unmap;
}
} else {
dma_addr_in = pci_map_single(ioc->pdev, bio_data(rsp->bio),
blk_rq_bytes(rsp), PCI_DMA_BIDIRECTIONAL);
if (!dma_addr_in) {
printk(MPT2SAS_INFO_FMT "%s(): DMA Addr in = NULL\n",
ioc->name, __func__);
rc = -ENOMEM;
goto unmap;
}
}
wait_state_count = 0; wait_state_count = 0;
ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
@ -1880,7 +1929,7 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
"%s: failed due to ioc not operational\n", "%s: failed due to ioc not operational\n",
ioc->name, __func__); ioc->name, __func__);
rc = -EFAULT; rc = -EFAULT;
goto out; goto unmap;
} }
ssleep(1); ssleep(1);
ioc_state = mpt2sas_base_get_iocstate(ioc, 1); ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
@ -1897,7 +1946,7 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
ioc->name, __func__); ioc->name, __func__);
rc = -EAGAIN; rc = -EAGAIN;
goto out; goto unmap;
} }
rc = 0; rc = 0;
@ -1919,16 +1968,14 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_HOST_TO_IOC); MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_HOST_TO_IOC);
sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
dma_addr_out = pci_map_single(ioc->pdev, bio_data(req->bio), if (req->bio->bi_vcnt > 1) {
blk_rq_bytes(req), PCI_DMA_BIDIRECTIONAL); ioc->base_add_sg_single(psge, sgl_flags |
if (!dma_addr_out) { (blk_rq_bytes(req) - 4), pci_dma_out);
mpt2sas_base_free_smid(ioc, smid); } else {
goto unmap; ioc->base_add_sg_single(psge, sgl_flags |
(blk_rq_bytes(req) - 4), dma_addr_out);
} }
ioc->base_add_sg_single(psge, sgl_flags | (blk_rq_bytes(req) - 4),
dma_addr_out);
/* incr sgel */ /* incr sgel */
psge += ioc->sge_size; psge += ioc->sge_size;
@ -1937,16 +1984,14 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER |
MPI2_SGE_FLAGS_END_OF_LIST); MPI2_SGE_FLAGS_END_OF_LIST);
sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
dma_addr_in = pci_map_single(ioc->pdev, bio_data(rsp->bio), if (rsp->bio->bi_vcnt > 1) {
blk_rq_bytes(rsp), PCI_DMA_BIDIRECTIONAL); ioc->base_add_sg_single(psge, sgl_flags |
if (!dma_addr_in) { (blk_rq_bytes(rsp) + 4), pci_dma_in);
mpt2sas_base_free_smid(ioc, smid); } else {
goto unmap; ioc->base_add_sg_single(psge, sgl_flags |
(blk_rq_bytes(rsp) + 4), dma_addr_in);
} }
ioc->base_add_sg_single(psge, sgl_flags | (blk_rq_bytes(rsp) + 4),
dma_addr_in);
dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT "%s - " dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT "%s - "
"sending smp request\n", ioc->name, __func__)); "sending smp request\n", ioc->name, __func__));
@ -1982,6 +2027,27 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
req->resid_len = 0; req->resid_len = 0;
rsp->resid_len -= rsp->resid_len -=
le16_to_cpu(mpi_reply->ResponseDataLength); le16_to_cpu(mpi_reply->ResponseDataLength);
/* check if the resp needs to be copied from the allocated
* pci mem */
if (rsp->bio->bi_vcnt > 1) {
u32 offset = 0;
u32 bytes_to_copy =
le16_to_cpu(mpi_reply->ResponseDataLength);
bio_for_each_segment(bvec, rsp->bio, i) {
if (bytes_to_copy <= bvec->bv_len) {
memcpy(page_address(bvec->bv_page) +
bvec->bv_offset, pci_addr_in +
offset, bytes_to_copy);
break;
} else {
memcpy(page_address(bvec->bv_page) +
bvec->bv_offset, pci_addr_in +
offset, bvec->bv_len);
bytes_to_copy -= bvec->bv_len;
}
offset += bvec->bv_len;
}
}
} else { } else {
dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT
"%s - no reply\n", ioc->name, __func__)); "%s - no reply\n", ioc->name, __func__));
@ -2003,6 +2069,15 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
pci_unmap_single(ioc->pdev, dma_addr_in, blk_rq_bytes(rsp), pci_unmap_single(ioc->pdev, dma_addr_in, blk_rq_bytes(rsp),
PCI_DMA_BIDIRECTIONAL); PCI_DMA_BIDIRECTIONAL);
free_pci:
if (pci_addr_out)
pci_free_consistent(ioc->pdev, blk_rq_bytes(req), pci_addr_out,
pci_dma_out);
if (pci_addr_in)
pci_free_consistent(ioc->pdev, blk_rq_bytes(rsp), pci_addr_in,
pci_dma_in);
out: out:
ioc->transport_cmds.status = MPT2_CMD_NOT_USED; ioc->transport_cmds.status = MPT2_CMD_NOT_USED;
mutex_unlock(&ioc->transport_cmds.mutex); mutex_unlock(&ioc->transport_cmds.mutex);