1
0
Fork 0

scsi: scsi_transport_sas: switch to bsg-lib for SMP passthrough

Simplify the SMP passthrough code by switching it to the generic bsg-lib
helpers that abstract away the details of the request code, and gets
drivers out of seeing struct scsi_request.

For the libsas host SMP code there is a small behavior difference in
that we now always clear the residual len for successful commands,
similar to the three other SMP handler implementations.  Given that
there is no partial command handling in the host SMP handler this should
not matter in practice.

[mkp: typos and checkpatch fixes]

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Johannes Thumshirn <jthumshirn@suse.de>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
hifive-unleashed-5.1
Christoph Hellwig 2017-08-25 17:37:41 +02:00 committed by Martin K. Petersen
parent eaa79a6cd7
commit 651a013649
9 changed files with 245 additions and 382 deletions

View File

@ -2210,33 +2210,26 @@ mptsas_get_bay_identifier(struct sas_rphy *rphy)
return rc; return rc;
} }
static int mptsas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, static void mptsas_smp_handler(struct bsg_job *job, struct Scsi_Host *shost,
struct request *req) struct sas_rphy *rphy)
{ {
MPT_ADAPTER *ioc = ((MPT_SCSI_HOST *) shost->hostdata)->ioc; MPT_ADAPTER *ioc = ((MPT_SCSI_HOST *) shost->hostdata)->ioc;
MPT_FRAME_HDR *mf; MPT_FRAME_HDR *mf;
SmpPassthroughRequest_t *smpreq; SmpPassthroughRequest_t *smpreq;
struct request *rsp = req->next_rq;
int ret;
int flagsLength; int flagsLength;
unsigned long timeleft; unsigned long timeleft;
char *psge; char *psge;
dma_addr_t dma_addr_in = 0;
dma_addr_t dma_addr_out = 0;
u64 sas_address = 0; u64 sas_address = 0;
unsigned int reslen = 0;
if (!rsp) { int ret = -EINVAL;
printk(MYIOC_s_ERR_FMT "%s: the smp response space is missing\n",
ioc->name, __func__);
return -EINVAL;
}
/* do we need to support multiple segments? */ /* do we need to support multiple segments? */
if (bio_multiple_segments(req->bio) || if (job->request_payload.sg_cnt > 1 ||
bio_multiple_segments(rsp->bio)) { job->reply_payload.sg_cnt > 1) {
printk(MYIOC_s_ERR_FMT "%s: multiple segments req %u, rsp %u\n", printk(MYIOC_s_ERR_FMT "%s: multiple segments req %u, rsp %u\n",
ioc->name, __func__, blk_rq_bytes(req), blk_rq_bytes(rsp)); ioc->name, __func__, job->request_payload.payload_len,
return -EINVAL; job->reply_payload.payload_len);
goto out;
} }
ret = mutex_lock_interruptible(&ioc->sas_mgmt.mutex); ret = mutex_lock_interruptible(&ioc->sas_mgmt.mutex);
@ -2252,7 +2245,8 @@ static int mptsas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
smpreq = (SmpPassthroughRequest_t *)mf; smpreq = (SmpPassthroughRequest_t *)mf;
memset(smpreq, 0, sizeof(*smpreq)); memset(smpreq, 0, sizeof(*smpreq));
smpreq->RequestDataLength = cpu_to_le16(blk_rq_bytes(req) - 4); smpreq->RequestDataLength =
cpu_to_le16(job->request_payload.payload_len - 4);
smpreq->Function = MPI_FUNCTION_SMP_PASSTHROUGH; smpreq->Function = MPI_FUNCTION_SMP_PASSTHROUGH;
if (rphy) if (rphy)
@ -2278,13 +2272,14 @@ static int mptsas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
MPI_SGE_FLAGS_END_OF_BUFFER | MPI_SGE_FLAGS_END_OF_BUFFER |
MPI_SGE_FLAGS_DIRECTION) MPI_SGE_FLAGS_DIRECTION)
<< MPI_SGE_FLAGS_SHIFT; << MPI_SGE_FLAGS_SHIFT;
flagsLength |= (blk_rq_bytes(req) - 4);
dma_addr_out = pci_map_single(ioc->pcidev, bio_data(req->bio), if (!dma_map_sg(&ioc->pcidev->dev, job->request_payload.sg_list,
blk_rq_bytes(req), PCI_DMA_BIDIRECTIONAL); 1, PCI_DMA_BIDIRECTIONAL))
if (pci_dma_mapping_error(ioc->pcidev, dma_addr_out))
goto put_mf; goto put_mf;
ioc->add_sge(psge, flagsLength, dma_addr_out);
flagsLength |= (sg_dma_len(job->request_payload.sg_list) - 4);
ioc->add_sge(psge, flagsLength,
sg_dma_address(job->request_payload.sg_list));
psge += ioc->SGE_size; psge += ioc->SGE_size;
/* response */ /* response */
@ -2294,12 +2289,13 @@ static int mptsas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
MPI_SGE_FLAGS_END_OF_BUFFER; MPI_SGE_FLAGS_END_OF_BUFFER;
flagsLength = flagsLength << MPI_SGE_FLAGS_SHIFT; flagsLength = flagsLength << MPI_SGE_FLAGS_SHIFT;
flagsLength |= blk_rq_bytes(rsp) + 4;
dma_addr_in = pci_map_single(ioc->pcidev, bio_data(rsp->bio), if (!dma_map_sg(&ioc->pcidev->dev, job->reply_payload.sg_list,
blk_rq_bytes(rsp), PCI_DMA_BIDIRECTIONAL); 1, PCI_DMA_BIDIRECTIONAL))
if (pci_dma_mapping_error(ioc->pcidev, dma_addr_in)) goto unmap_out;
goto unmap; flagsLength |= sg_dma_len(job->reply_payload.sg_list) + 4;
ioc->add_sge(psge, flagsLength, dma_addr_in); ioc->add_sge(psge, flagsLength,
sg_dma_address(job->reply_payload.sg_list));
INITIALIZE_MGMT_STATUS(ioc->sas_mgmt.status) INITIALIZE_MGMT_STATUS(ioc->sas_mgmt.status)
mpt_put_msg_frame(mptsasMgmtCtx, ioc, mf); mpt_put_msg_frame(mptsasMgmtCtx, ioc, mf);
@ -2310,10 +2306,10 @@ static int mptsas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
mpt_free_msg_frame(ioc, mf); mpt_free_msg_frame(ioc, mf);
mf = NULL; mf = NULL;
if (ioc->sas_mgmt.status & MPT_MGMT_STATUS_DID_IOCRESET) if (ioc->sas_mgmt.status & MPT_MGMT_STATUS_DID_IOCRESET)
goto unmap; goto unmap_in;
if (!timeleft) if (!timeleft)
mpt_Soft_Hard_ResetHandler(ioc, CAN_SLEEP); mpt_Soft_Hard_ResetHandler(ioc, CAN_SLEEP);
goto unmap; goto unmap_in;
} }
mf = NULL; mf = NULL;
@ -2321,23 +2317,22 @@ static int mptsas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
SmpPassthroughReply_t *smprep; SmpPassthroughReply_t *smprep;
smprep = (SmpPassthroughReply_t *)ioc->sas_mgmt.reply; smprep = (SmpPassthroughReply_t *)ioc->sas_mgmt.reply;
memcpy(scsi_req(req)->sense, smprep, sizeof(*smprep)); memcpy(job->reply, smprep, sizeof(*smprep));
scsi_req(req)->sense_len = sizeof(*smprep); job->reply_len = sizeof(*smprep);
scsi_req(req)->resid_len = 0; reslen = smprep->ResponseDataLength;
scsi_req(rsp)->resid_len -= smprep->ResponseDataLength;
} else { } else {
printk(MYIOC_s_ERR_FMT printk(MYIOC_s_ERR_FMT
"%s: smp passthru reply failed to be returned\n", "%s: smp passthru reply failed to be returned\n",
ioc->name, __func__); ioc->name, __func__);
ret = -ENXIO; ret = -ENXIO;
} }
unmap:
if (dma_addr_out) unmap_in:
pci_unmap_single(ioc->pcidev, dma_addr_out, blk_rq_bytes(req), dma_unmap_sg(&ioc->pcidev->dev, job->reply_payload.sg_list, 1,
PCI_DMA_BIDIRECTIONAL); PCI_DMA_BIDIRECTIONAL);
if (dma_addr_in) unmap_out:
pci_unmap_single(ioc->pcidev, dma_addr_in, blk_rq_bytes(rsp), dma_unmap_sg(&ioc->pcidev->dev, job->request_payload.sg_list, 1,
PCI_DMA_BIDIRECTIONAL); PCI_DMA_BIDIRECTIONAL);
put_mf: put_mf:
if (mf) if (mf)
mpt_free_msg_frame(ioc, mf); mpt_free_msg_frame(ioc, mf);
@ -2345,7 +2340,7 @@ out_unlock:
CLEAR_MGMT_STATUS(ioc->sas_mgmt.status) CLEAR_MGMT_STATUS(ioc->sas_mgmt.status)
mutex_unlock(&ioc->sas_mgmt.mutex); mutex_unlock(&ioc->sas_mgmt.mutex);
out: out:
return ret; bsg_job_done(job, ret, reslen);
} }
static struct sas_function_template mptsas_transport_functions = { static struct sas_function_template mptsas_transport_functions = {

View File

@ -26,6 +26,7 @@ config SCSI_SAS_LIBSAS
tristate "SAS Domain Transport Attributes" tristate "SAS Domain Transport Attributes"
depends on SCSI depends on SCSI
select SCSI_SAS_ATTRS select SCSI_SAS_ATTRS
select BLK_DEV_BSGLIB
help help
This provides transport specific helpers for SAS drivers which This provides transport specific helpers for SAS drivers which
use the domain device construct (like the aic94xxx). use the domain device construct (like the aic94xxx).

View File

@ -64,8 +64,8 @@ static void smp_task_done(struct sas_task *task)
/* Give it some long enough timeout. In seconds. */ /* Give it some long enough timeout. In seconds. */
#define SMP_TIMEOUT 10 #define SMP_TIMEOUT 10
static int smp_execute_task(struct domain_device *dev, void *req, int req_size, static int smp_execute_task_sg(struct domain_device *dev,
void *resp, int resp_size) struct scatterlist *req, struct scatterlist *resp)
{ {
int res, retry; int res, retry;
struct sas_task *task = NULL; struct sas_task *task = NULL;
@ -86,8 +86,8 @@ static int smp_execute_task(struct domain_device *dev, void *req, int req_size,
} }
task->dev = dev; task->dev = dev;
task->task_proto = dev->tproto; task->task_proto = dev->tproto;
sg_init_one(&task->smp_task.smp_req, req, req_size); task->smp_task.smp_req = *req;
sg_init_one(&task->smp_task.smp_resp, resp, resp_size); task->smp_task.smp_resp = *resp;
task->task_done = smp_task_done; task->task_done = smp_task_done;
@ -151,6 +151,17 @@ static int smp_execute_task(struct domain_device *dev, void *req, int req_size,
return res; return res;
} }
static int smp_execute_task(struct domain_device *dev, void *req, int req_size,
void *resp, int resp_size)
{
struct scatterlist req_sg;
struct scatterlist resp_sg;
sg_init_one(&req_sg, req, req_size);
sg_init_one(&resp_sg, resp, resp_size);
return smp_execute_task_sg(dev, &req_sg, &resp_sg);
}
/* ---------- Allocations ---------- */ /* ---------- Allocations ---------- */
static inline void *alloc_smp_req(int size) static inline void *alloc_smp_req(int size)
@ -2130,57 +2141,50 @@ int sas_ex_revalidate_domain(struct domain_device *port_dev)
return res; return res;
} }
int sas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, void sas_smp_handler(struct bsg_job *job, struct Scsi_Host *shost,
struct request *req) struct sas_rphy *rphy)
{ {
struct domain_device *dev; struct domain_device *dev;
int ret, type; unsigned int reslen = 0;
struct request *rsp = req->next_rq; int ret = -EINVAL;
if (!rsp) {
printk("%s: space for a smp response is missing\n",
__func__);
return -EINVAL;
}
/* no rphy means no smp target support (ie aic94xx host) */ /* no rphy means no smp target support (ie aic94xx host) */
if (!rphy) if (!rphy)
return sas_smp_host_handler(shost, req, rsp); return sas_smp_host_handler(job, shost);
type = rphy->identify.device_type; switch (rphy->identify.device_type) {
case SAS_EDGE_EXPANDER_DEVICE:
if (type != SAS_EDGE_EXPANDER_DEVICE && case SAS_FANOUT_EXPANDER_DEVICE:
type != SAS_FANOUT_EXPANDER_DEVICE) { break;
default:
printk("%s: can we send a smp request to a device?\n", printk("%s: can we send a smp request to a device?\n",
__func__); __func__);
return -EINVAL; goto out;
} }
dev = sas_find_dev_by_rphy(rphy); dev = sas_find_dev_by_rphy(rphy);
if (!dev) { if (!dev) {
printk("%s: fail to find a domain_device?\n", __func__); printk("%s: fail to find a domain_device?\n", __func__);
return -EINVAL; goto out;
} }
/* do we need to support multiple segments? */ /* do we need to support multiple segments? */
if (bio_multiple_segments(req->bio) || if (job->request_payload.sg_cnt > 1 ||
bio_multiple_segments(rsp->bio)) { job->reply_payload.sg_cnt > 1) {
printk("%s: multiple segments req %u, rsp %u\n", printk("%s: multiple segments req %u, rsp %u\n",
__func__, blk_rq_bytes(req), blk_rq_bytes(rsp)); __func__, job->request_payload.payload_len,
return -EINVAL; job->reply_payload.payload_len);
goto out;
} }
ret = smp_execute_task(dev, bio_data(req->bio), blk_rq_bytes(req), ret = smp_execute_task_sg(dev, job->request_payload.sg_list,
bio_data(rsp->bio), blk_rq_bytes(rsp)); job->reply_payload.sg_list);
if (ret > 0) { if (ret > 0) {
/* positive number is the untransferred residual */ /* positive number is the untransferred residual */
scsi_req(rsp)->resid_len = ret; reslen = ret;
scsi_req(req)->resid_len = 0;
ret = 0; ret = 0;
} else if (ret == 0) {
scsi_req(rsp)->resid_len = 0;
scsi_req(req)->resid_len = 0;
} }
return ret; out:
bsg_job_done(job, ret, reslen);
} }

View File

@ -225,47 +225,36 @@ static void sas_phy_control(struct sas_ha_struct *sas_ha, u8 phy_id,
resp_data[2] = SMP_RESP_FUNC_ACC; resp_data[2] = SMP_RESP_FUNC_ACC;
} }
int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req, void sas_smp_host_handler(struct bsg_job *job, struct Scsi_Host *shost)
struct request *rsp)
{ {
u8 *req_data = NULL, *resp_data = NULL, *buf;
struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
u8 *req_data, *resp_data;
unsigned int reslen = 0;
int error = -EINVAL; int error = -EINVAL;
/* eight is the minimum size for request and response frames */ /* eight is the minimum size for request and response frames */
if (blk_rq_bytes(req) < 8 || blk_rq_bytes(rsp) < 8) if (job->request_payload.payload_len < 8 ||
job->reply_payload.payload_len < 8)
goto out; goto out;
if (bio_offset(req->bio) + blk_rq_bytes(req) > PAGE_SIZE || error = -ENOMEM;
bio_offset(rsp->bio) + blk_rq_bytes(rsp) > PAGE_SIZE) { req_data = kzalloc(job->request_payload.payload_len, GFP_KERNEL);
shost_printk(KERN_ERR, shost, if (!req_data)
"SMP request/response frame crosses page boundary");
goto out; goto out;
} sg_copy_to_buffer(job->request_payload.sg_list,
job->request_payload.sg_cnt, req_data,
req_data = kzalloc(blk_rq_bytes(req), GFP_KERNEL); job->request_payload.payload_len);
/* make sure frame can always be built ... we copy /* make sure frame can always be built ... we copy
* back only the requested length */ * back only the requested length */
resp_data = kzalloc(max(blk_rq_bytes(rsp), 128U), GFP_KERNEL); resp_data = kzalloc(max(job->reply_payload.payload_len, 128U),
GFP_KERNEL);
if (!req_data || !resp_data) { if (!resp_data)
error = -ENOMEM; goto out_free_req;
goto out;
}
local_irq_disable();
buf = kmap_atomic(bio_page(req->bio));
memcpy(req_data, buf, blk_rq_bytes(req));
kunmap_atomic(buf - bio_offset(req->bio));
local_irq_enable();
error = -EINVAL;
if (req_data[0] != SMP_REQUEST) if (req_data[0] != SMP_REQUEST)
goto out; goto out_free_resp;
/* always succeeds ... even if we can't process the request
* the result is in the response frame */
error = 0;
/* set up default don't know response */ /* set up default don't know response */
resp_data[0] = SMP_RESPONSE; resp_data[0] = SMP_RESPONSE;
@ -274,20 +263,18 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,
switch (req_data[1]) { switch (req_data[1]) {
case SMP_REPORT_GENERAL: case SMP_REPORT_GENERAL:
scsi_req(req)->resid_len -= 8;
scsi_req(rsp)->resid_len -= 32;
resp_data[2] = SMP_RESP_FUNC_ACC; resp_data[2] = SMP_RESP_FUNC_ACC;
resp_data[9] = sas_ha->num_phys; resp_data[9] = sas_ha->num_phys;
reslen = 32;
break; break;
case SMP_REPORT_MANUF_INFO: case SMP_REPORT_MANUF_INFO:
scsi_req(req)->resid_len -= 8;
scsi_req(rsp)->resid_len -= 64;
resp_data[2] = SMP_RESP_FUNC_ACC; resp_data[2] = SMP_RESP_FUNC_ACC;
memcpy(resp_data + 12, shost->hostt->name, memcpy(resp_data + 12, shost->hostt->name,
SAS_EXPANDER_VENDOR_ID_LEN); SAS_EXPANDER_VENDOR_ID_LEN);
memcpy(resp_data + 20, "libsas virt phy", memcpy(resp_data + 20, "libsas virt phy",
SAS_EXPANDER_PRODUCT_ID_LEN); SAS_EXPANDER_PRODUCT_ID_LEN);
reslen = 64;
break; break;
case SMP_READ_GPIO_REG: case SMP_READ_GPIO_REG:
@ -295,14 +282,10 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,
break; break;
case SMP_DISCOVER: case SMP_DISCOVER:
scsi_req(req)->resid_len -= 16; if (job->request_payload.payload_len < 16)
if ((int)scsi_req(req)->resid_len < 0) { goto out_free_resp;
scsi_req(req)->resid_len = 0;
error = -EINVAL;
goto out;
}
scsi_req(rsp)->resid_len -= 56;
sas_host_smp_discover(sas_ha, resp_data, req_data[9]); sas_host_smp_discover(sas_ha, resp_data, req_data[9]);
reslen = 56;
break; break;
case SMP_REPORT_PHY_ERR_LOG: case SMP_REPORT_PHY_ERR_LOG:
@ -311,14 +294,10 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,
break; break;
case SMP_REPORT_PHY_SATA: case SMP_REPORT_PHY_SATA:
scsi_req(req)->resid_len -= 16; if (job->request_payload.payload_len < 16)
if ((int)scsi_req(req)->resid_len < 0) { goto out_free_resp;
scsi_req(req)->resid_len = 0;
error = -EINVAL;
goto out;
}
scsi_req(rsp)->resid_len -= 60;
sas_report_phy_sata(sas_ha, resp_data, req_data[9]); sas_report_phy_sata(sas_ha, resp_data, req_data[9]);
reslen = 60;
break; break;
case SMP_REPORT_ROUTE_INFO: case SMP_REPORT_ROUTE_INFO:
@ -330,16 +309,15 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,
const int base_frame_size = 11; const int base_frame_size = 11;
int to_write = req_data[4]; int to_write = req_data[4];
if (blk_rq_bytes(req) < base_frame_size + to_write * 4 || if (job->request_payload.payload_len <
scsi_req(req)->resid_len < base_frame_size + to_write * 4) { base_frame_size + to_write * 4) {
resp_data[2] = SMP_RESP_INV_FRM_LEN; resp_data[2] = SMP_RESP_INV_FRM_LEN;
break; break;
} }
to_write = sas_host_smp_write_gpio(sas_ha, resp_data, req_data[2], to_write = sas_host_smp_write_gpio(sas_ha, resp_data, req_data[2],
req_data[3], to_write, &req_data[8]); req_data[3], to_write, &req_data[8]);
scsi_req(req)->resid_len -= base_frame_size + to_write * 4; reslen = 8;
scsi_req(rsp)->resid_len -= 8;
break; break;
} }
@ -348,16 +326,12 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,
break; break;
case SMP_PHY_CONTROL: case SMP_PHY_CONTROL:
scsi_req(req)->resid_len -= 44; if (job->request_payload.payload_len < 44)
if ((int)scsi_req(req)->resid_len < 0) { goto out_free_resp;
scsi_req(req)->resid_len = 0;
error = -EINVAL;
goto out;
}
scsi_req(rsp)->resid_len -= 8;
sas_phy_control(sas_ha, req_data[9], req_data[10], sas_phy_control(sas_ha, req_data[9], req_data[10],
req_data[32] >> 4, req_data[33] >> 4, req_data[32] >> 4, req_data[33] >> 4,
resp_data); resp_data);
reslen = 8;
break; break;
case SMP_PHY_TEST_FUNCTION: case SMP_PHY_TEST_FUNCTION:
@ -369,15 +343,15 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,
break; break;
} }
local_irq_disable(); sg_copy_from_buffer(job->reply_payload.sg_list,
buf = kmap_atomic(bio_page(rsp->bio)); job->reply_payload.sg_cnt, resp_data,
memcpy(buf, resp_data, blk_rq_bytes(rsp)); job->reply_payload.payload_len);
flush_kernel_dcache_page(bio_page(rsp->bio));
kunmap_atomic(buf - bio_offset(rsp->bio));
local_irq_enable();
out: error = 0;
kfree(req_data); out_free_resp:
kfree(resp_data); kfree(resp_data);
return error; out_free_req:
kfree(req_data);
out:
bsg_job_done(job, error, reslen);
} }

View File

@ -81,6 +81,8 @@ int sas_queue_work(struct sas_ha_struct *ha, struct sas_work *sw);
int sas_notify_lldd_dev_found(struct domain_device *); int sas_notify_lldd_dev_found(struct domain_device *);
void sas_notify_lldd_dev_gone(struct domain_device *); void sas_notify_lldd_dev_gone(struct domain_device *);
void sas_smp_handler(struct bsg_job *job, struct Scsi_Host *shost,
struct sas_rphy *rphy);
int sas_smp_phy_control(struct domain_device *dev, int phy_id, int sas_smp_phy_control(struct domain_device *dev, int phy_id,
enum phy_func phy_func, struct sas_phy_linkrates *); enum phy_func phy_func, struct sas_phy_linkrates *);
int sas_smp_get_phy_events(struct sas_phy *phy); int sas_smp_get_phy_events(struct sas_phy *phy);
@ -98,16 +100,14 @@ void sas_hae_reset(struct work_struct *work);
void sas_free_device(struct kref *kref); void sas_free_device(struct kref *kref);
#ifdef CONFIG_SCSI_SAS_HOST_SMP #ifdef CONFIG_SCSI_SAS_HOST_SMP
extern int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req, extern void sas_smp_host_handler(struct bsg_job *job, struct Scsi_Host *shost);
struct request *rsp);
#else #else
static inline int sas_smp_host_handler(struct Scsi_Host *shost, static inline void sas_smp_host_handler(struct bsg_job *job,
struct request *req, struct Scsi_Host *shost)
struct request *rsp)
{ {
shost_printk(KERN_ERR, shost, shost_printk(KERN_ERR, shost,
"Cannot send SMP to a sas host (not enabled in CONFIG)\n"); "Cannot send SMP to a sas host (not enabled in CONFIG)\n");
return -EINVAL; bsg_job_done(job, -EINVAL, 0);
} }
#endif #endif

View File

@ -1870,6 +1870,38 @@ _transport_phy_speed(struct sas_phy *phy, struct sas_phy_linkrates *rates)
return rc; return rc;
} }
static int
_transport_map_smp_buffer(struct device *dev, struct bsg_buffer *buf,
dma_addr_t *dma_addr, size_t *dma_len, void **p)
{
/* Check if the request is split across multiple segments */
if (buf->sg_cnt > 1) {
*p = dma_alloc_coherent(dev, buf->payload_len, dma_addr,
GFP_KERNEL);
if (!*p)
return -ENOMEM;
*dma_len = buf->payload_len;
} else {
if (!dma_map_sg(dev, buf->sg_list, 1, DMA_BIDIRECTIONAL))
return -ENOMEM;
*dma_addr = sg_dma_address(buf->sg_list);
*dma_len = sg_dma_len(buf->sg_list);
*p = NULL;
}
return 0;
}
static void
_transport_unmap_smp_buffer(struct device *dev, struct bsg_buffer *buf,
dma_addr_t dma_addr, void *p)
{
if (p)
dma_free_coherent(dev, buf->payload_len, p, dma_addr);
else
dma_unmap_sg(dev, buf->sg_list, 1, DMA_BIDIRECTIONAL);
}
/** /**
* _transport_smp_handler - transport portal for smp passthru * _transport_smp_handler - transport portal for smp passthru
* @shost: shost object * @shost: shost object
@ -1880,9 +1912,9 @@ _transport_phy_speed(struct sas_phy *phy, struct sas_phy_linkrates *rates)
* Example: * Example:
* smp_rep_general /sys/class/bsg/expander-5:0 * smp_rep_general /sys/class/bsg/expander-5:0
*/ */
static int static void
_transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, _transport_smp_handler(struct bsg_job *job, struct Scsi_Host *shost,
struct request *req) struct sas_rphy *rphy)
{ {
struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
Mpi2SmpPassthroughRequest_t *mpi_request; Mpi2SmpPassthroughRequest_t *mpi_request;
@ -1891,33 +1923,25 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
u16 smid; u16 smid;
u32 ioc_state; u32 ioc_state;
void *psge; void *psge;
u8 issue_reset = 0; dma_addr_t dma_addr_in;
dma_addr_t dma_addr_in = 0; dma_addr_t dma_addr_out;
dma_addr_t dma_addr_out = 0; void *addr_in = NULL;
dma_addr_t pci_dma_in = 0; void *addr_out = NULL;
dma_addr_t pci_dma_out = 0; size_t dma_len_in;
void *pci_addr_in = NULL; size_t dma_len_out;
void *pci_addr_out = NULL;
u16 wait_state_count; u16 wait_state_count;
struct request *rsp = req->next_rq; unsigned int reslen = 0;
struct bio_vec bvec;
struct bvec_iter iter;
if (!rsp) {
pr_err(MPT3SAS_FMT "%s: the smp response space is missing\n",
ioc->name, __func__);
return -EINVAL;
}
if (ioc->shost_recovery || ioc->pci_error_recovery) { if (ioc->shost_recovery || ioc->pci_error_recovery) {
pr_info(MPT3SAS_FMT "%s: host reset in progress!\n", pr_info(MPT3SAS_FMT "%s: host reset in progress!\n",
__func__, ioc->name); __func__, ioc->name);
return -EFAULT; rc = -EFAULT;
goto out;
} }
rc = mutex_lock_interruptible(&ioc->transport_cmds.mutex); rc = mutex_lock_interruptible(&ioc->transport_cmds.mutex);
if (rc) if (rc)
return rc; goto out;
if (ioc->transport_cmds.status != MPT3_CMD_NOT_USED) { if (ioc->transport_cmds.status != MPT3_CMD_NOT_USED) {
pr_err(MPT3SAS_FMT "%s: transport_cmds in use\n", ioc->name, pr_err(MPT3SAS_FMT "%s: transport_cmds in use\n", ioc->name,
@ -1927,58 +1951,20 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
} }
ioc->transport_cmds.status = MPT3_CMD_PENDING; ioc->transport_cmds.status = MPT3_CMD_PENDING;
/* Check if the request is split across multiple segments */ rc = _transport_map_smp_buffer(&ioc->pdev->dev, &job->request_payload,
if (bio_multiple_segments(req->bio)) { &dma_addr_out, &dma_len_out, &addr_out);
u32 offset = 0; if (rc)
goto out;
/* Allocate memory and copy the request */ if (addr_out) {
pci_addr_out = pci_alloc_consistent(ioc->pdev, sg_copy_to_buffer(job->request_payload.sg_list,
blk_rq_bytes(req), &pci_dma_out); job->request_payload.sg_cnt, addr_out,
if (!pci_addr_out) { job->request_payload.payload_len);
pr_info(MPT3SAS_FMT "%s(): PCI Addr out = NULL\n",
ioc->name, __func__);
rc = -ENOMEM;
goto out;
}
bio_for_each_segment(bvec, req->bio, iter) {
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 (pci_dma_mapping_error(ioc->pdev, dma_addr_out)) {
pr_info(MPT3SAS_FMT "%s(): DMA Addr out = NULL\n",
ioc->name, __func__);
rc = -ENOMEM;
goto free_pci;
}
} }
/* Check if the response needs to be populated across rc = _transport_map_smp_buffer(&ioc->pdev->dev, &job->reply_payload,
* multiple segments */ &dma_addr_in, &dma_len_in, &addr_in);
if (bio_multiple_segments(rsp->bio)) { if (rc)
pci_addr_in = pci_alloc_consistent(ioc->pdev, blk_rq_bytes(rsp), goto unmap_out;
&pci_dma_in);
if (!pci_addr_in) {
pr_info(MPT3SAS_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 (pci_dma_mapping_error(ioc->pdev, dma_addr_in)) {
pr_info(MPT3SAS_FMT "%s(): DMA Addr in = NULL\n",
ioc->name, __func__);
rc = -ENOMEM;
goto unmap;
}
}
wait_state_count = 0; wait_state_count = 0;
ioc_state = mpt3sas_base_get_iocstate(ioc, 1); ioc_state = mpt3sas_base_get_iocstate(ioc, 1);
@ -1988,7 +1974,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 unmap; goto unmap_in;
} }
ssleep(1); ssleep(1);
ioc_state = mpt3sas_base_get_iocstate(ioc, 1); ioc_state = mpt3sas_base_get_iocstate(ioc, 1);
@ -2005,7 +1991,7 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n",
ioc->name, __func__); ioc->name, __func__);
rc = -EAGAIN; rc = -EAGAIN;
goto unmap; goto unmap_in;
} }
rc = 0; rc = 0;
@ -2018,15 +2004,11 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
mpi_request->SASAddress = (rphy) ? mpi_request->SASAddress = (rphy) ?
cpu_to_le64(rphy->identify.sas_address) : cpu_to_le64(rphy->identify.sas_address) :
cpu_to_le64(ioc->sas_hba.sas_address); cpu_to_le64(ioc->sas_hba.sas_address);
mpi_request->RequestDataLength = cpu_to_le16(blk_rq_bytes(req) - 4); mpi_request->RequestDataLength = cpu_to_le16(dma_len_out - 4);
psge = &mpi_request->SGL; psge = &mpi_request->SGL;
if (bio_multiple_segments(req->bio)) ioc->build_sg(ioc, psge, dma_addr_out, dma_len_out - 4, dma_addr_in,
ioc->build_sg(ioc, psge, pci_dma_out, (blk_rq_bytes(req) - 4), dma_len_in - 4);
pci_dma_in, (blk_rq_bytes(rsp) + 4));
else
ioc->build_sg(ioc, psge, dma_addr_out, (blk_rq_bytes(req) - 4),
dma_addr_in, (blk_rq_bytes(rsp) + 4));
dtransportprintk(ioc, pr_info(MPT3SAS_FMT dtransportprintk(ioc, pr_info(MPT3SAS_FMT
"%s - sending smp request\n", ioc->name, __func__)); "%s - sending smp request\n", ioc->name, __func__));
@ -2040,83 +2022,51 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
__func__, ioc->name); __func__, ioc->name);
_debug_dump_mf(mpi_request, _debug_dump_mf(mpi_request,
sizeof(Mpi2SmpPassthroughRequest_t)/4); sizeof(Mpi2SmpPassthroughRequest_t)/4);
if (!(ioc->transport_cmds.status & MPT3_CMD_RESET)) if (!(ioc->transport_cmds.status & MPT3_CMD_RESET)) {
issue_reset = 1; mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
goto issue_host_reset; rc = -ETIMEDOUT;
goto unmap_in;
}
} }
dtransportprintk(ioc, pr_info(MPT3SAS_FMT dtransportprintk(ioc, pr_info(MPT3SAS_FMT
"%s - complete\n", ioc->name, __func__)); "%s - complete\n", ioc->name, __func__));
if (ioc->transport_cmds.status & MPT3_CMD_REPLY_VALID) { if (!(ioc->transport_cmds.status & MPT3_CMD_REPLY_VALID)) {
mpi_reply = ioc->transport_cmds.reply;
dtransportprintk(ioc, pr_info(MPT3SAS_FMT
"%s - reply data transfer size(%d)\n",
ioc->name, __func__,
le16_to_cpu(mpi_reply->ResponseDataLength)));
memcpy(scsi_req(req)->sense, mpi_reply, sizeof(*mpi_reply));
scsi_req(req)->sense_len = sizeof(*mpi_reply);
scsi_req(req)->resid_len = 0;
scsi_req(rsp)->resid_len -=
le16_to_cpu(mpi_reply->ResponseDataLength);
/* check if the resp needs to be copied from the allocated
* pci mem */
if (bio_multiple_segments(rsp->bio)) {
u32 offset = 0;
u32 bytes_to_copy =
le16_to_cpu(mpi_reply->ResponseDataLength);
bio_for_each_segment(bvec, rsp->bio, iter) {
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 {
dtransportprintk(ioc, pr_info(MPT3SAS_FMT dtransportprintk(ioc, pr_info(MPT3SAS_FMT
"%s - no reply\n", ioc->name, __func__)); "%s - no reply\n", ioc->name, __func__));
rc = -ENXIO; rc = -ENXIO;
goto unmap_in;
} }
issue_host_reset: mpi_reply = ioc->transport_cmds.reply;
if (issue_reset) {
mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); dtransportprintk(ioc,
rc = -ETIMEDOUT; pr_info(MPT3SAS_FMT "%s - reply data transfer size(%d)\n",
ioc->name, __func__,
le16_to_cpu(mpi_reply->ResponseDataLength)));
memcpy(job->reply, mpi_reply, sizeof(*mpi_reply));
job->reply_len = sizeof(*mpi_reply);
reslen = le16_to_cpu(mpi_reply->ResponseDataLength);
if (addr_in) {
sg_copy_to_buffer(job->reply_payload.sg_list,
job->reply_payload.sg_cnt, addr_in,
job->reply_payload.payload_len);
} }
unmap: rc = 0;
if (dma_addr_out) unmap_in:
pci_unmap_single(ioc->pdev, dma_addr_out, blk_rq_bytes(req), _transport_unmap_smp_buffer(&ioc->pdev->dev, &job->reply_payload,
PCI_DMA_BIDIRECTIONAL); dma_addr_in, addr_in);
if (dma_addr_in) unmap_out:
pci_unmap_single(ioc->pdev, dma_addr_in, blk_rq_bytes(rsp), _transport_unmap_smp_buffer(&ioc->pdev->dev, &job->request_payload,
PCI_DMA_BIDIRECTIONAL); dma_addr_out, addr_out);
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 = MPT3_CMD_NOT_USED; ioc->transport_cmds.status = MPT3_CMD_NOT_USED;
mutex_unlock(&ioc->transport_cmds.mutex); mutex_unlock(&ioc->transport_cmds.mutex);
return rc; bsg_job_done(job, rc, reslen);
} }
struct sas_function_template mpt3sas_transport_functions = { struct sas_function_template mpt3sas_transport_functions = {

View File

@ -169,39 +169,22 @@ static struct sas_end_device *sas_sdev_to_rdev(struct scsi_device *sdev)
return rdev; return rdev;
} }
static void sas_smp_request(struct request_queue *q, struct Scsi_Host *shost, static int sas_smp_dispatch(struct bsg_job *job)
struct sas_rphy *rphy)
{ {
struct request *req; struct Scsi_Host *shost = dev_to_shost(job->dev);
blk_status_t ret; struct sas_rphy *rphy = NULL;
int (*handler)(struct Scsi_Host *, struct sas_rphy *, struct request *);
while ((req = blk_fetch_request(q)) != NULL) { if (!scsi_is_host_device(job->dev))
spin_unlock_irq(q->queue_lock); rphy = dev_to_rphy(job->dev);
scsi_req(req)->resid_len = blk_rq_bytes(req); if (!job->req->next_rq) {
if (req->next_rq) dev_warn(job->dev, "space for a smp response is missing\n");
scsi_req(req->next_rq)->resid_len = bsg_job_done(job, -EINVAL, 0);
blk_rq_bytes(req->next_rq); return 0;
handler = to_sas_internal(shost->transportt)->f->smp_handler;
ret = handler(shost, rphy, req);
scsi_req(req)->result = ret;
blk_end_request_all(req, 0);
spin_lock_irq(q->queue_lock);
} }
}
static void sas_host_smp_request(struct request_queue *q) to_sas_internal(shost->transportt)->f->smp_handler(job, shost, rphy);
{ return 0;
sas_smp_request(q, (struct Scsi_Host *)q->queuedata, NULL);
}
static void sas_non_host_smp_request(struct request_queue *q)
{
struct sas_rphy *rphy = q->queuedata;
sas_smp_request(q, rphy_to_shost(rphy), rphy);
} }
static void sas_host_release(struct device *dev) static void sas_host_release(struct device *dev)
@ -217,81 +200,36 @@ static void sas_host_release(struct device *dev)
static int sas_bsg_initialize(struct Scsi_Host *shost, struct sas_rphy *rphy) static int sas_bsg_initialize(struct Scsi_Host *shost, struct sas_rphy *rphy)
{ {
struct request_queue *q; struct request_queue *q;
int error;
struct device *dev;
char namebuf[20];
const char *name;
void (*release)(struct device *);
if (!to_sas_internal(shost->transportt)->f->smp_handler) { if (!to_sas_internal(shost->transportt)->f->smp_handler) {
printk("%s can't handle SMP requests\n", shost->hostt->name); printk("%s can't handle SMP requests\n", shost->hostt->name);
return 0; return 0;
} }
q = blk_alloc_queue(GFP_KERNEL);
if (!q)
return -ENOMEM;
q->initialize_rq_fn = scsi_initialize_rq;
q->cmd_size = sizeof(struct scsi_request);
if (rphy) { if (rphy) {
q->request_fn = sas_non_host_smp_request; q = bsg_setup_queue(&rphy->dev, dev_name(&rphy->dev),
dev = &rphy->dev; sas_smp_dispatch, 0, NULL);
name = dev_name(dev); if (IS_ERR(q))
release = NULL; return PTR_ERR(q);
rphy->q = q;
} else { } else {
q->request_fn = sas_host_smp_request; char name[20];
dev = &shost->shost_gendev;
snprintf(namebuf, sizeof(namebuf), snprintf(name, sizeof(name), "sas_host%d", shost->host_no);
"sas_host%d", shost->host_no); q = bsg_setup_queue(&shost->shost_gendev, name,
name = namebuf; sas_smp_dispatch, 0, sas_host_release);
release = sas_host_release; if (IS_ERR(q))
return PTR_ERR(q);
to_sas_host_attrs(shost)->q = q;
} }
error = blk_init_allocated_queue(q);
if (error)
goto out_cleanup_queue;
/* /*
* by default assume old behaviour and bounce for any highmem page * by default assume old behaviour and bounce for any highmem page
*/ */
blk_queue_bounce_limit(q, BLK_BOUNCE_HIGH); blk_queue_bounce_limit(q, BLK_BOUNCE_HIGH);
error = bsg_register_queue(q, dev, name, release);
if (error)
goto out_cleanup_queue;
if (rphy)
rphy->q = q;
else
to_sas_host_attrs(shost)->q = q;
if (rphy)
q->queuedata = rphy;
else
q->queuedata = shost;
queue_flag_set_unlocked(QUEUE_FLAG_BIDI, q); queue_flag_set_unlocked(QUEUE_FLAG_BIDI, q);
queue_flag_set_unlocked(QUEUE_FLAG_SCSI_PASSTHROUGH, q); queue_flag_set_unlocked(QUEUE_FLAG_SCSI_PASSTHROUGH, q);
return 0; return 0;
out_cleanup_queue:
blk_cleanup_queue(q);
return error;
}
static void sas_bsg_remove(struct Scsi_Host *shost, struct sas_rphy *rphy)
{
struct request_queue *q;
if (rphy)
q = rphy->q;
else
q = to_sas_host_attrs(shost)->q;
if (!q)
return;
bsg_unregister_queue(q);
} }
/* /*
@ -321,9 +259,10 @@ static int sas_host_remove(struct transport_container *tc, struct device *dev,
struct device *cdev) struct device *cdev)
{ {
struct Scsi_Host *shost = dev_to_shost(dev); struct Scsi_Host *shost = dev_to_shost(dev);
struct request_queue *q = to_sas_host_attrs(shost)->q;
sas_bsg_remove(shost, NULL); if (q)
bsg_unregister_queue(q);
return 0; return 0;
} }
@ -1713,7 +1652,8 @@ sas_rphy_remove(struct sas_rphy *rphy)
} }
sas_rphy_unlink(rphy); sas_rphy_unlink(rphy);
sas_bsg_remove(NULL, rphy); if (rphy->q)
bsg_unregister_queue(rphy->q);
transport_remove_device(dev); transport_remove_device(dev);
device_del(dev); device_del(dev);
} }

View File

@ -721,9 +721,6 @@ extern int sas_slave_alloc(struct scsi_device *);
extern int sas_ioctl(struct scsi_device *sdev, int cmd, void __user *arg); extern int sas_ioctl(struct scsi_device *sdev, int cmd, void __user *arg);
extern int sas_drain_work(struct sas_ha_struct *ha); extern int sas_drain_work(struct sas_ha_struct *ha);
extern int sas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
struct request *req);
extern void sas_ssp_task_response(struct device *dev, struct sas_task *task, extern void sas_ssp_task_response(struct device *dev, struct sas_task *task,
struct ssp_response_iu *iu); struct ssp_response_iu *iu);
struct sas_phy *sas_get_local_phy(struct domain_device *dev); struct sas_phy *sas_get_local_phy(struct domain_device *dev);

View File

@ -5,6 +5,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <scsi/sas.h> #include <scsi/sas.h>
#include <linux/bsg-lib.h>
struct scsi_transport_template; struct scsi_transport_template;
struct sas_rphy; struct sas_rphy;
@ -176,7 +177,8 @@ struct sas_function_template {
int (*phy_setup)(struct sas_phy *); int (*phy_setup)(struct sas_phy *);
void (*phy_release)(struct sas_phy *); void (*phy_release)(struct sas_phy *);
int (*set_phy_speed)(struct sas_phy *, struct sas_phy_linkrates *); int (*set_phy_speed)(struct sas_phy *, struct sas_phy_linkrates *);
int (*smp_handler)(struct Scsi_Host *, struct sas_rphy *, struct request *); void (*smp_handler)(struct bsg_job *, struct Scsi_Host *,
struct sas_rphy *);
}; };