diff options
Diffstat (limited to 'drivers/scsi/libsas')
-rw-r--r-- | drivers/scsi/libsas/Kconfig | 1 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_expander.c | 70 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_host_smp.c | 106 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_internal.h | 12 |
4 files changed, 84 insertions, 105 deletions
diff --git a/drivers/scsi/libsas/Kconfig b/drivers/scsi/libsas/Kconfig index 9dafe64e7c7a..13739bfacc67 100644 --- a/drivers/scsi/libsas/Kconfig +++ b/drivers/scsi/libsas/Kconfig @@ -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). diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 570b2cb2da43..6b4fd2375178 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -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); - - type = rphy->identify.device_type; + return sas_smp_host_handler(job, shost); - 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); } diff --git a/drivers/scsi/libsas/sas_host_smp.c b/drivers/scsi/libsas/sas_host_smp.c index 45cbbc44f4d7..9ead93df3a6e 100644 --- a/drivers/scsi/libsas/sas_host_smp.c +++ b/drivers/scsi/libsas/sas_host_smp.c @@ -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); } diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index a216c957b639..c07e08136491 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -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 |