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>
This commit is contained in:
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

@@ -26,6 +26,7 @@ config SCSI_SAS_LIBSAS
tristate "SAS Domain Transport Attributes"
depends on SCSI
select SCSI_SAS_ATTRS
select BLK_DEV_BSGLIB
help
This provides transport specific helpers for SAS drivers which
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. */
#define SMP_TIMEOUT 10
static int smp_execute_task(struct domain_device *dev, void *req, int req_size,
void *resp, int resp_size)
static int smp_execute_task_sg(struct domain_device *dev,
struct scatterlist *req, struct scatterlist *resp)
{
int res, retry;
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->task_proto = dev->tproto;
sg_init_one(&task->smp_task.smp_req, req, req_size);
sg_init_one(&task->smp_task.smp_resp, resp, resp_size);
task->smp_task.smp_req = *req;
task->smp_task.smp_resp = *resp;
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;
}
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 ---------- */
static inline void *alloc_smp_req(int size)
@@ -2130,57 +2141,50 @@ int sas_ex_revalidate_domain(struct domain_device *port_dev)
return res;
}
int sas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
struct request *req)
void sas_smp_handler(struct bsg_job *job, struct Scsi_Host *shost,
struct sas_rphy *rphy)
{
struct domain_device *dev;
int ret, type;
struct request *rsp = req->next_rq;
if (!rsp) {
printk("%s: space for a smp response is missing\n",
__func__);
return -EINVAL;
}
unsigned int reslen = 0;
int ret = -EINVAL;
/* no rphy means no smp target support (ie aic94xx host) */
if (!rphy)
return sas_smp_host_handler(shost, req, rsp);
return sas_smp_host_handler(job, shost);
type = rphy->identify.device_type;
if (type != SAS_EDGE_EXPANDER_DEVICE &&
type != SAS_FANOUT_EXPANDER_DEVICE) {
switch (rphy->identify.device_type) {
case SAS_EDGE_EXPANDER_DEVICE:
case SAS_FANOUT_EXPANDER_DEVICE:
break;
default:
printk("%s: can we send a smp request to a device?\n",
__func__);
return -EINVAL;
goto out;
}
dev = sas_find_dev_by_rphy(rphy);
if (!dev) {
printk("%s: fail to find a domain_device?\n", __func__);
return -EINVAL;
goto out;
}
/* do we need to support multiple segments? */
if (bio_multiple_segments(req->bio) ||
bio_multiple_segments(rsp->bio)) {
if (job->request_payload.sg_cnt > 1 ||
job->reply_payload.sg_cnt > 1) {
printk("%s: multiple segments req %u, rsp %u\n",
__func__, blk_rq_bytes(req), blk_rq_bytes(rsp));
return -EINVAL;
__func__, job->request_payload.payload_len,
job->reply_payload.payload_len);
goto out;
}
ret = smp_execute_task(dev, bio_data(req->bio), blk_rq_bytes(req),
bio_data(rsp->bio), blk_rq_bytes(rsp));
ret = smp_execute_task_sg(dev, job->request_payload.sg_list,
job->reply_payload.sg_list);
if (ret > 0) {
/* positive number is the untransferred residual */
scsi_req(rsp)->resid_len = ret;
scsi_req(req)->resid_len = 0;
reslen = ret;
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;
}
int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,
struct request *rsp)
void sas_smp_host_handler(struct bsg_job *job, struct Scsi_Host *shost)
{
u8 *req_data = NULL, *resp_data = NULL, *buf;
struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
u8 *req_data, *resp_data;
unsigned int reslen = 0;
int error = -EINVAL;
/* 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;
if (bio_offset(req->bio) + blk_rq_bytes(req) > PAGE_SIZE ||
bio_offset(rsp->bio) + blk_rq_bytes(rsp) > PAGE_SIZE) {
shost_printk(KERN_ERR, shost,
"SMP request/response frame crosses page boundary");
error = -ENOMEM;
req_data = kzalloc(job->request_payload.payload_len, GFP_KERNEL);
if (!req_data)
goto out;
}
req_data = kzalloc(blk_rq_bytes(req), GFP_KERNEL);
sg_copy_to_buffer(job->request_payload.sg_list,
job->request_payload.sg_cnt, req_data,
job->request_payload.payload_len);
/* make sure frame can always be built ... we copy
* back only the requested length */
resp_data = kzalloc(max(blk_rq_bytes(rsp), 128U), GFP_KERNEL);
if (!req_data || !resp_data) {
error = -ENOMEM;
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();
resp_data = kzalloc(max(job->reply_payload.payload_len, 128U),
GFP_KERNEL);
if (!resp_data)
goto out_free_req;
error = -EINVAL;
if (req_data[0] != SMP_REQUEST)
goto out;
/* always succeeds ... even if we can't process the request
* the result is in the response frame */
error = 0;
goto out_free_resp;
/* set up default don't know 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]) {
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[9] = sas_ha->num_phys;
reslen = 32;
break;
case SMP_REPORT_MANUF_INFO:
scsi_req(req)->resid_len -= 8;
scsi_req(rsp)->resid_len -= 64;
resp_data[2] = SMP_RESP_FUNC_ACC;
memcpy(resp_data + 12, shost->hostt->name,
SAS_EXPANDER_VENDOR_ID_LEN);
memcpy(resp_data + 20, "libsas virt phy",
SAS_EXPANDER_PRODUCT_ID_LEN);
reslen = 64;
break;
case SMP_READ_GPIO_REG:
@@ -295,14 +282,10 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,
break;
case SMP_DISCOVER:
scsi_req(req)->resid_len -= 16;
if ((int)scsi_req(req)->resid_len < 0) {
scsi_req(req)->resid_len = 0;
error = -EINVAL;
goto out;
}
scsi_req(rsp)->resid_len -= 56;
if (job->request_payload.payload_len < 16)
goto out_free_resp;
sas_host_smp_discover(sas_ha, resp_data, req_data[9]);
reslen = 56;
break;
case SMP_REPORT_PHY_ERR_LOG:
@@ -311,14 +294,10 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,
break;
case SMP_REPORT_PHY_SATA:
scsi_req(req)->resid_len -= 16;
if ((int)scsi_req(req)->resid_len < 0) {
scsi_req(req)->resid_len = 0;
error = -EINVAL;
goto out;
}
scsi_req(rsp)->resid_len -= 60;
if (job->request_payload.payload_len < 16)
goto out_free_resp;
sas_report_phy_sata(sas_ha, resp_data, req_data[9]);
reslen = 60;
break;
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;
int to_write = req_data[4];
if (blk_rq_bytes(req) < base_frame_size + to_write * 4 ||
scsi_req(req)->resid_len < base_frame_size + to_write * 4) {
if (job->request_payload.payload_len <
base_frame_size + to_write * 4) {
resp_data[2] = SMP_RESP_INV_FRM_LEN;
break;
}
to_write = sas_host_smp_write_gpio(sas_ha, resp_data, req_data[2],
req_data[3], to_write, &req_data[8]);
scsi_req(req)->resid_len -= base_frame_size + to_write * 4;
scsi_req(rsp)->resid_len -= 8;
reslen = 8;
break;
}
@@ -348,16 +326,12 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,
break;
case SMP_PHY_CONTROL:
scsi_req(req)->resid_len -= 44;
if ((int)scsi_req(req)->resid_len < 0) {
scsi_req(req)->resid_len = 0;
error = -EINVAL;
goto out;
}
scsi_req(rsp)->resid_len -= 8;
if (job->request_payload.payload_len < 44)
goto out_free_resp;
sas_phy_control(sas_ha, req_data[9], req_data[10],
req_data[32] >> 4, req_data[33] >> 4,
resp_data);
reslen = 8;
break;
case SMP_PHY_TEST_FUNCTION:
@@ -369,15 +343,15 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,
break;
}
local_irq_disable();
buf = kmap_atomic(bio_page(rsp->bio));
memcpy(buf, resp_data, blk_rq_bytes(rsp));
flush_kernel_dcache_page(bio_page(rsp->bio));
kunmap_atomic(buf - bio_offset(rsp->bio));
local_irq_enable();
sg_copy_from_buffer(job->reply_payload.sg_list,
job->reply_payload.sg_cnt, resp_data,
job->reply_payload.payload_len);
out:
kfree(req_data);
error = 0;
out_free_resp:
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 *);
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,
enum phy_func phy_func, struct sas_phy_linkrates *);
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);
#ifdef CONFIG_SCSI_SAS_HOST_SMP
extern int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,
struct request *rsp);
extern void sas_smp_host_handler(struct bsg_job *job, struct Scsi_Host *shost);
#else
static inline int sas_smp_host_handler(struct Scsi_Host *shost,
struct request *req,
struct request *rsp)
static inline void sas_smp_host_handler(struct bsg_job *job,
struct Scsi_Host *shost)
{
shost_printk(KERN_ERR, shost,
"Cannot send SMP to a sas host (not enabled in CONFIG)\n");
return -EINVAL;
bsg_job_done(job, -EINVAL, 0);
}
#endif