From 5a7de559b4e0169ff4cfca654b4e4f0014996e57 Mon Sep 17 00:00:00 2001 From: Christof Schmitt Date: Thu, 8 Jul 2010 09:53:07 +0200 Subject: [SCSI] zfcp: Register SCSI devices after successful fc_remote_port_add When the successful return of an adisc is the final step to set the port online, the registration of SCSI devices might be omitted. SCSI devices that have been removed before (due to a short dev_loss_tmo setting) might not be attached again. The problem is that the registration of SCSI devices is done only after erp has finished. The correct place would be after the call to fc_remote_port_add to mimick the scan in the FC transport class. Change the registration of SCSI devices to be triggered after the fc_remote_port_add call. For the initial inquiry command to succeed, the unit must also be open. If the unit reopen is still pending, the inquiry command to the LUN will be deferred with DID_IMM_RETRY, so there is no harm from this approach. Reviewed-by: Swen Schillig Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_scsi.c | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) (limited to 'drivers/s390/scsi/zfcp_scsi.c') diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index be5d2c60453d..153f69b26e78 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -524,6 +524,20 @@ static void zfcp_scsi_terminate_rport_io(struct fc_rport *rport) } } +static void zfcp_scsi_queue_unit_register(struct zfcp_port *port) +{ + struct zfcp_unit *unit; + + read_lock_irq(&port->unit_list_lock); + list_for_each_entry(unit, &port->unit_list, list) { + get_device(&unit->dev); + if (scsi_queue_work(port->adapter->scsi_host, + &unit->scsi_work) <= 0) + put_device(&unit->dev); + } + read_unlock_irq(&port->unit_list_lock); +} + static void zfcp_scsi_rport_register(struct zfcp_port *port) { struct fc_rport_identifiers ids; @@ -548,6 +562,8 @@ static void zfcp_scsi_rport_register(struct zfcp_port *port) rport->maxframe_size = port->maxframe_size; rport->supported_classes = port->supported_classes; port->rport = rport; + + zfcp_scsi_queue_unit_register(port); } static void zfcp_scsi_rport_block(struct zfcp_port *port) @@ -610,21 +626,26 @@ void zfcp_scsi_rport_work(struct work_struct *work) put_device(&port->dev); } - -void zfcp_scsi_scan(struct work_struct *work) +/** + * zfcp_scsi_scan - Register LUN with SCSI midlayer + * @unit: The LUN/unit to register + */ +void zfcp_scsi_scan(struct zfcp_unit *unit) { - struct zfcp_unit *unit = container_of(work, struct zfcp_unit, - scsi_work); - struct fc_rport *rport; - - flush_work(&unit->port->rport_work); - rport = unit->port->rport; + struct fc_rport *rport = unit->port->rport; if (rport && rport->port_state == FC_PORTSTATE_ONLINE) scsi_scan_target(&rport->dev, 0, rport->scsi_target_id, scsilun_to_int((struct scsi_lun *) &unit->fcp_lun), 0); +} + +void zfcp_scsi_scan_work(struct work_struct *work) +{ + struct zfcp_unit *unit = container_of(work, struct zfcp_unit, + scsi_work); + zfcp_scsi_scan(unit); put_device(&unit->dev); } -- cgit v1.2.3 From 835dc29887073eec7817559a07558f955383d099 Mon Sep 17 00:00:00 2001 From: Christof Schmitt Date: Thu, 8 Jul 2010 09:53:08 +0200 Subject: [SCSI] zfcp: Use forced_reopen in terminate_rport_io callback When running in non-NPIV mode, the port_reopen in terminate_rport_io might succeed even though the remote port is not available. If the same port connection is held open from another operating system, the reopen is only a virtual operation and might not hit the SAN. Fix this by changing the call to forced_reopen that forces a logout/login operation in the SAN. Reviewed-by: Swen Schillig Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_scsi.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/s390/scsi/zfcp_scsi.c') diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 153f69b26e78..4cab33a2f7be 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -506,8 +506,10 @@ static void zfcp_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout) * @rport: The FC rport where to teminate I/O * * Abort all pending SCSI commands for a port by closing the - * port. Using a reopen avoiding a conflict with a shutdown - * overwriting a reopen. + * port. Using a reopen avoids a conflict with a shutdown + * overwriting a reopen. The "forced" ensures that a disappeared port + * is not opened again as valid due to the cached plogi data in + * non-NPIV mode. */ static void zfcp_scsi_terminate_rport_io(struct fc_rport *rport) { @@ -519,7 +521,7 @@ static void zfcp_scsi_terminate_rport_io(struct fc_rport *rport) port = zfcp_get_port_by_wwpn(adapter, rport->port_name); if (port) { - zfcp_erp_port_reopen(port, 0, "sctrpi1", NULL); + zfcp_erp_port_forced_reopen(port, 0, "sctrpi1", NULL); put_device(&port->dev); } } -- cgit v1.2.3 From 1bf3ff02ca6247b2d7c9ebda93002392bf60a61d Mon Sep 17 00:00:00 2001 From: Christof Schmitt Date: Fri, 16 Jul 2010 15:37:35 +0200 Subject: [SCSI] zfcp: Remove SCSI device when removing unit Configuring a LUN in zfcp, also creates a SCSI device. For consistency, it makes sense to remove the SCSI device when the LUN is deconfigured. Replace the flush_work with the call to scsi_remove_device: scsi_remove_device also takes the scan_mutex that synchronizes itself with any long running device discovery. Reviewed-by: Swen Schillig Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_def.h | 1 + drivers/s390/scsi/zfcp_scsi.c | 1 + drivers/s390/scsi/zfcp_sysfs.c | 10 ++++++++-- 3 files changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers/s390/scsi/zfcp_scsi.c') diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h index 9fa1b064893e..86a8725430a4 100644 --- a/drivers/s390/scsi/zfcp_def.h +++ b/drivers/s390/scsi/zfcp_def.h @@ -212,6 +212,7 @@ struct zfcp_port { struct work_struct test_link_work; struct work_struct rport_work; enum { RPORT_NONE, RPORT_ADD, RPORT_DEL } rport_task; + unsigned int starget_id; }; struct zfcp_unit { diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 4cab33a2f7be..9d117ee7159a 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -564,6 +564,7 @@ static void zfcp_scsi_rport_register(struct zfcp_port *port) rport->maxframe_size = port->maxframe_size; rport->supported_classes = port->supported_classes; port->rport = rport; + port->starget_id = rport->scsi_target_id; zfcp_scsi_queue_unit_register(port); } diff --git a/drivers/s390/scsi/zfcp_sysfs.c b/drivers/s390/scsi/zfcp_sysfs.c index 205b9f8056e2..b4561c86e230 100644 --- a/drivers/s390/scsi/zfcp_sysfs.c +++ b/drivers/s390/scsi/zfcp_sysfs.c @@ -290,6 +290,7 @@ static ssize_t zfcp_sysfs_unit_remove_store(struct device *dev, struct zfcp_unit *unit; u64 fcp_lun; int retval = -EINVAL; + struct scsi_device *sdev; if (!(port && get_device(&port->dev))) return -EBUSY; @@ -303,8 +304,13 @@ static ssize_t zfcp_sysfs_unit_remove_store(struct device *dev, else retval = 0; - /* wait for possible timeout during SCSI probe */ - flush_work(&unit->scsi_work); + sdev = scsi_device_lookup(port->adapter->scsi_host, 0, + port->starget_id, + scsilun_to_int((struct scsi_lun *)&fcp_lun)); + if (sdev) { + scsi_remove_device(sdev); + scsi_device_put(sdev); + } write_lock_irq(&port->unit_list_lock); list_del(&unit->list); -- cgit v1.2.3 From 01b047599ade30051bf6c14fbe64181d1fec3dfa Mon Sep 17 00:00:00 2001 From: Swen Schillig Date: Fri, 16 Jul 2010 15:37:37 +0200 Subject: [SCSI] zfcp: Cleanup function parameters for sbal value. A lot of functions require the amount of SBALs as one of their parameter which is most times invariable. Therefore remove this parameter and set the SBAL value explicitly if a non standard value is required. In addition the warning message "oversized data" is replaced with a BUG_ON() statement assuring the limits defined and requested by zfcp. Signed-off-by: Swen Schillig Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_ext.h | 2 +- drivers/s390/scsi/zfcp_fsf.c | 40 ++++++++++++++-------------------------- drivers/s390/scsi/zfcp_fsf.h | 8 -------- drivers/s390/scsi/zfcp_qdio.c | 15 ++------------- drivers/s390/scsi/zfcp_qdio.h | 28 ++++++++++++++++++++++++++++ drivers/s390/scsi/zfcp_scsi.c | 4 ++-- 6 files changed, 47 insertions(+), 50 deletions(-) (limited to 'drivers/s390/scsi/zfcp_scsi.c') diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index 6abe2909e75b..3aab0f5544d4 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -146,7 +146,7 @@ extern void zfcp_qdio_destroy(struct zfcp_qdio *); extern int zfcp_qdio_sbal_get(struct zfcp_qdio *); extern int zfcp_qdio_send(struct zfcp_qdio *, struct zfcp_qdio_req *); extern int zfcp_qdio_sbals_from_sg(struct zfcp_qdio *, struct zfcp_qdio_req *, - struct scatterlist *, int); + struct scatterlist *); extern int zfcp_qdio_open(struct zfcp_qdio *); extern void zfcp_qdio_close(struct zfcp_qdio *); diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index ee0c1df8a6d2..5f502c9cb067 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -959,8 +959,7 @@ static void zfcp_fsf_setup_ct_els_unchained(struct zfcp_qdio *qdio, static int zfcp_fsf_setup_ct_els_sbals(struct zfcp_fsf_req *req, struct scatterlist *sg_req, - struct scatterlist *sg_resp, - int max_sbals) + struct scatterlist *sg_resp) { struct zfcp_adapter *adapter = req->adapter; u32 feat = adapter->adapter_features; @@ -983,15 +982,14 @@ static int zfcp_fsf_setup_ct_els_sbals(struct zfcp_fsf_req *req, return 0; } - bytes = zfcp_qdio_sbals_from_sg(adapter->qdio, &req->qdio_req, - sg_req, max_sbals); + bytes = zfcp_qdio_sbals_from_sg(adapter->qdio, &req->qdio_req, sg_req); if (bytes <= 0) return -EIO; req->qtcb->bottom.support.req_buf_length = bytes; zfcp_qdio_skip_to_last_sbale(&req->qdio_req); bytes = zfcp_qdio_sbals_from_sg(adapter->qdio, &req->qdio_req, - sg_resp, max_sbals); + sg_resp); req->qtcb->bottom.support.resp_buf_length = bytes; if (bytes <= 0) return -EIO; @@ -1002,11 +1000,11 @@ static int zfcp_fsf_setup_ct_els_sbals(struct zfcp_fsf_req *req, static int zfcp_fsf_setup_ct_els(struct zfcp_fsf_req *req, struct scatterlist *sg_req, struct scatterlist *sg_resp, - int max_sbals, unsigned int timeout) + unsigned int timeout) { int ret; - ret = zfcp_fsf_setup_ct_els_sbals(req, sg_req, sg_resp, max_sbals); + ret = zfcp_fsf_setup_ct_els_sbals(req, sg_req, sg_resp); if (ret) return ret; @@ -1046,8 +1044,7 @@ int zfcp_fsf_send_ct(struct zfcp_fc_wka_port *wka_port, } req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; - ret = zfcp_fsf_setup_ct_els(req, ct->req, ct->resp, - ZFCP_FSF_MAX_SBALS_PER_REQ, timeout); + ret = zfcp_fsf_setup_ct_els(req, ct->req, ct->resp, timeout); if (ret) goto failed_send; @@ -1143,7 +1140,10 @@ int zfcp_fsf_send_els(struct zfcp_adapter *adapter, u32 d_id, } req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; - ret = zfcp_fsf_setup_ct_els(req, els->req, els->resp, 2, timeout); + + zfcp_qdio_sbal_limit(qdio, &req->qdio_req, 2); + + ret = zfcp_fsf_setup_ct_els(req, els->req, els->resp, timeout); if (ret) goto failed_send; @@ -2259,20 +2259,9 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit, zfcp_fc_scsi_to_fcp(fcp_cmnd, scsi_cmnd); real_bytes = zfcp_qdio_sbals_from_sg(qdio, &req->qdio_req, - scsi_sglist(scsi_cmnd), - ZFCP_FSF_MAX_SBALS_PER_REQ); - if (unlikely(real_bytes < 0)) { - if (req->qdio_req.sbal_number >= ZFCP_FSF_MAX_SBALS_PER_REQ) { - dev_err(&adapter->ccw_device->dev, - "Oversize data package, unit 0x%016Lx " - "on port 0x%016Lx closed\n", - (unsigned long long)unit->fcp_lun, - (unsigned long long)unit->port->wwpn); - zfcp_erp_unit_shutdown(unit, 0, "fssfct1", req); - retval = -EINVAL; - } + scsi_sglist(scsi_cmnd)); + if (unlikely(real_bytes < 0)) goto failed_scsi_cmnd; - } retval = zfcp_fsf_req_send(req); if (unlikely(retval)) @@ -2391,9 +2380,8 @@ struct zfcp_fsf_req *zfcp_fsf_control_file(struct zfcp_adapter *adapter, bottom->operation_subtype = FSF_CFDC_OPERATION_SUBTYPE; bottom->option = fsf_cfdc->option; - bytes = zfcp_qdio_sbals_from_sg(qdio, &req->qdio_req, - fsf_cfdc->sg, - ZFCP_FSF_MAX_SBALS_PER_REQ); + bytes = zfcp_qdio_sbals_from_sg(qdio, &req->qdio_req, fsf_cfdc->sg); + if (bytes != ZFCP_CFDC_MAX_SIZE) { zfcp_fsf_req_free(req); goto out; diff --git a/drivers/s390/scsi/zfcp_fsf.h b/drivers/s390/scsi/zfcp_fsf.h index ca45e32d6b6f..ca110e386761 100644 --- a/drivers/s390/scsi/zfcp_fsf.h +++ b/drivers/s390/scsi/zfcp_fsf.h @@ -151,14 +151,6 @@ /* fc service class */ #define FSF_CLASS_3 0x00000003 -/* SBAL chaining */ -#define ZFCP_FSF_MAX_SBALS_PER_REQ 36 - -/* max. number of (data buffer) SBALEs in largest SBAL chain - * request ID + QTCB in SBALE 0 + 1 of first SBAL in chain */ -#define ZFCP_FSF_MAX_SBALES_PER_REQ \ - (ZFCP_FSF_MAX_SBALS_PER_REQ * ZFCP_QDIO_MAX_SBALES_PER_SBAL - 2) - /* logging space behind QTCB */ #define FSF_QTCB_LOG_SIZE 1024 diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c index 6fa5e0453176..7ab1ac16a11f 100644 --- a/drivers/s390/scsi/zfcp_qdio.c +++ b/drivers/s390/scsi/zfcp_qdio.c @@ -141,15 +141,6 @@ static void zfcp_qdio_int_resp(struct ccw_device *cdev, unsigned int qdio_err, zfcp_qdio_resp_put_back(qdio, count); } -static void zfcp_qdio_sbal_limit(struct zfcp_qdio *qdio, - struct zfcp_qdio_req *q_req, int max_sbals) -{ - int count = atomic_read(&qdio->req_q.count); - count = min(count, max_sbals); - q_req->sbal_limit = (q_req->sbal_first + count - 1) - % QDIO_MAX_BUFFERS_PER_Q; -} - static struct qdio_buffer_element * zfcp_qdio_sbal_chain(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req) { @@ -173,6 +164,7 @@ zfcp_qdio_sbal_chain(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req) /* keep this requests number of SBALs up-to-date */ q_req->sbal_number++; + BUG_ON(q_req->sbal_number > ZFCP_QDIO_MAX_SBALS_PER_REQ); /* start at first SBALE of new SBAL */ q_req->sbale_curr = 0; @@ -213,14 +205,11 @@ static void zfcp_qdio_undo_sbals(struct zfcp_qdio *qdio, * Returns: number of bytes, or error (negativ) */ int zfcp_qdio_sbals_from_sg(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req, - struct scatterlist *sg, int max_sbals) + struct scatterlist *sg) { struct qdio_buffer_element *sbale; int bytes = 0; - /* figure out last allowed SBAL */ - zfcp_qdio_sbal_limit(qdio, q_req, max_sbals); - /* set storage-block type for this request */ sbale = zfcp_qdio_sbale_req(qdio, q_req); sbale->flags |= q_req->sbtype; diff --git a/drivers/s390/scsi/zfcp_qdio.h b/drivers/s390/scsi/zfcp_qdio.h index 138fba577b48..8bb00545f19c 100644 --- a/drivers/s390/scsi/zfcp_qdio.h +++ b/drivers/s390/scsi/zfcp_qdio.h @@ -19,6 +19,14 @@ /* index of last SBALE (with respect to DMQ bug workaround) */ #define ZFCP_QDIO_LAST_SBALE_PER_SBAL (ZFCP_QDIO_MAX_SBALES_PER_SBAL - 1) +/* Max SBALS for chaining */ +#define ZFCP_QDIO_MAX_SBALS_PER_REQ 36 + +/* max. number of (data buffer) SBALEs in largest SBAL chain + * request ID + QTCB in SBALE 0 + 1 of first SBAL in chain */ +#define ZFCP_QDIO_MAX_SBALES_PER_REQ \ + (ZFCP_QDIO_MAX_SBALS_PER_REQ * ZFCP_QDIO_MAX_SBALES_PER_SBAL - 2) + /** * struct zfcp_qdio_queue - qdio queue buffer, zfcp index and free count * @sbal: qdio buffers @@ -134,10 +142,14 @@ void zfcp_qdio_req_init(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req, unsigned long req_id, u32 sbtype, void *data, u32 len) { struct qdio_buffer_element *sbale; + int count = min(atomic_read(&qdio->req_q.count), + ZFCP_QDIO_MAX_SBALS_PER_REQ); q_req->sbal_first = q_req->sbal_last = qdio->req_q.first; q_req->sbal_number = 1; q_req->sbtype = sbtype; + q_req->sbal_limit = (q_req->sbal_first + count - 1) + % QDIO_MAX_BUFFERS_PER_Q; sbale = zfcp_qdio_sbale_req(qdio, q_req); sbale->addr = (void *) req_id; @@ -210,4 +222,20 @@ void zfcp_qdio_skip_to_last_sbale(struct zfcp_qdio_req *q_req) q_req->sbale_curr = ZFCP_QDIO_LAST_SBALE_PER_SBAL; } +/** + * zfcp_qdio_sbal_limit - set the sbal limit for a request in q_req + * @qdio: pointer to struct zfcp_qdio + * @q_req: The current zfcp_qdio_req + * @max_sbals: maximum number of SBALs allowed + */ +static inline +void zfcp_qdio_sbal_limit(struct zfcp_qdio *qdio, + struct zfcp_qdio_req *q_req, int max_sbals) +{ + int count = min(atomic_read(&qdio->req_q.count), max_sbals); + + q_req->sbal_limit = (q_req->sbal_first + count - 1) % + QDIO_MAX_BUFFERS_PER_Q; +} + #endif /* ZFCP_QDIO_H */ diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 9d117ee7159a..eb471a1723cd 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -701,11 +701,11 @@ struct zfcp_data zfcp_data = { .eh_host_reset_handler = zfcp_scsi_eh_host_reset_handler, .can_queue = 4096, .this_id = -1, - .sg_tablesize = ZFCP_FSF_MAX_SBALES_PER_REQ, + .sg_tablesize = ZFCP_QDIO_MAX_SBALES_PER_REQ, .cmd_per_lun = 1, .use_clustering = 1, .sdev_attrs = zfcp_sysfs_sdev_attrs, - .max_sectors = (ZFCP_FSF_MAX_SBALES_PER_REQ * 8), + .max_sectors = (ZFCP_QDIO_MAX_SBALES_PER_REQ * 8), .dma_boundary = ZFCP_QDIO_SBALE_LEN - 1, .shost_attrs = zfcp_sysfs_shost_attrs, }, -- cgit v1.2.3 From ef3eb71d8ba4fd9d48c5f9310bc9d90ca00323b4 Mon Sep 17 00:00:00 2001 From: Felix Beck Date: Fri, 16 Jul 2010 15:37:42 +0200 Subject: [SCSI] zfcp: Introduce experimental support for DIF/DIX Introduce support for DIF/DIX in zfcp: Report the capabilities for the Scsi_host, map the protection data when issuing I/O requests and handle the new error codes. Also add the fsf data_direction field to the hba trace, it is useful information for debugging in that area. This is an EXPERIMENTAL feature for now. Signed-off-by: Felix Beck Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_dbf.c | 3 ++ drivers/s390/scsi/zfcp_dbf.h | 1 + drivers/s390/scsi/zfcp_ext.h | 2 + drivers/s390/scsi/zfcp_fc.h | 3 ++ drivers/s390/scsi/zfcp_fsf.c | 109 +++++++++++++++++++++++++++++++++--------- drivers/s390/scsi/zfcp_fsf.h | 24 ++++++++-- drivers/s390/scsi/zfcp_qdio.c | 4 -- drivers/s390/scsi/zfcp_qdio.h | 16 +++++++ drivers/s390/scsi/zfcp_scsi.c | 53 ++++++++++++++++++++ drivers/scsi/Kconfig | 4 ++ 10 files changed, 189 insertions(+), 30 deletions(-) (limited to 'drivers/s390/scsi/zfcp_scsi.c') diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c index a08d33a96ec9..a86117b0d6e1 100644 --- a/drivers/s390/scsi/zfcp_dbf.c +++ b/drivers/s390/scsi/zfcp_dbf.c @@ -155,6 +155,8 @@ void _zfcp_dbf_hba_fsf_response(const char *tag2, int level, if (scsi_cmnd) { response->u.fcp.cmnd = (unsigned long)scsi_cmnd; response->u.fcp.serial = scsi_cmnd->serial_number; + response->u.fcp.data_dir = + qtcb->bottom.io.data_direction; } break; @@ -326,6 +328,7 @@ static void zfcp_dbf_hba_view_response(char **p, case FSF_QTCB_FCP_CMND: if (r->fsf_req_status & ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT) break; + zfcp_dbf_out(p, "data_direction", "0x%04x", r->u.fcp.data_dir); zfcp_dbf_out(p, "scsi_cmnd", "0x%0Lx", r->u.fcp.cmnd); zfcp_dbf_out(p, "scsi_serial", "0x%016Lx", r->u.fcp.serial); *p += sprintf(*p, "\n"); diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h index 457e046f2d28..2bcc3403126a 100644 --- a/drivers/s390/scsi/zfcp_dbf.h +++ b/drivers/s390/scsi/zfcp_dbf.h @@ -111,6 +111,7 @@ struct zfcp_dbf_hba_record_response { struct { u64 cmnd; u64 serial; + u32 data_dir; } fcp; struct { u64 wwpn; diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index a8bb7488dc98..de0925f25dcc 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -164,6 +164,8 @@ extern void zfcp_scsi_schedule_rport_block(struct zfcp_port *); extern void zfcp_scsi_schedule_rports_block(struct zfcp_adapter *); extern void zfcp_scsi_scan(struct zfcp_unit *); extern void zfcp_scsi_scan_work(struct work_struct *); +extern void zfcp_scsi_set_prot(struct zfcp_adapter *); +extern void zfcp_scsi_dif_sense_error(struct scsi_cmnd *, int); /* zfcp_sysfs.c */ extern struct attribute_group zfcp_sysfs_unit_attrs; diff --git a/drivers/s390/scsi/zfcp_fc.h b/drivers/s390/scsi/zfcp_fc.h index 85c37d2b82c2..938d50360166 100644 --- a/drivers/s390/scsi/zfcp_fc.h +++ b/drivers/s390/scsi/zfcp_fc.h @@ -220,6 +220,9 @@ void zfcp_fc_scsi_to_fcp(struct fcp_cmnd *fcp, struct scsi_cmnd *scsi) memcpy(fcp->fc_cdb, scsi->cmnd, scsi->cmd_len); fcp->fc_dl = scsi_bufflen(scsi); + + if (scsi_get_prot_type(scsi) == SCSI_PROT_DIF_TYPE1) + fcp->fc_dl += fcp->fc_dl / scsi->device->sector_size * 8; } /** diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 63402fd5f9ae..f9be5d60d92a 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -526,6 +526,8 @@ static int zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *req) return -EIO; } + zfcp_scsi_set_prot(adapter); + return 0; } @@ -988,6 +990,7 @@ static int zfcp_fsf_setup_ct_els_sbals(struct zfcp_fsf_req *req, bytes = zfcp_qdio_sbals_from_sg(adapter->qdio, &req->qdio_req, sg_req); if (bytes <= 0) return -EIO; + zfcp_qdio_set_sbale_last(adapter->qdio, &req->qdio_req); req->qtcb->bottom.support.req_buf_length = bytes; zfcp_qdio_skip_to_last_sbale(&req->qdio_req); @@ -996,6 +999,7 @@ static int zfcp_fsf_setup_ct_els_sbals(struct zfcp_fsf_req *req, req->qtcb->bottom.support.resp_buf_length = bytes; if (bytes <= 0) return -EIO; + zfcp_qdio_set_sbale_last(adapter->qdio, &req->qdio_req); return 0; } @@ -2038,9 +2042,13 @@ static void zfcp_fsf_req_trace(struct zfcp_fsf_req *req, struct scsi_cmnd *scsi) blktrc.fabric_lat = lat_in->fabric_lat * ticks; switch (req->qtcb->bottom.io.data_direction) { + case FSF_DATADIR_DIF_READ_STRIP: + case FSF_DATADIR_DIF_READ_CONVERT: case FSF_DATADIR_READ: lat = &unit->latencies.read; break; + case FSF_DATADIR_DIF_WRITE_INSERT: + case FSF_DATADIR_DIF_WRITE_CONVERT: case FSF_DATADIR_WRITE: lat = &unit->latencies.write; break; @@ -2081,6 +2089,21 @@ static void zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *req) goto skip_fsfstatus; } + switch (req->qtcb->header.fsf_status) { + case FSF_INCONSISTENT_PROT_DATA: + case FSF_INVALID_PROT_PARM: + set_host_byte(scpnt, DID_ERROR); + goto skip_fsfstatus; + case FSF_BLOCK_GUARD_CHECK_FAILURE: + zfcp_scsi_dif_sense_error(scpnt, 0x1); + goto skip_fsfstatus; + case FSF_APP_TAG_CHECK_FAILURE: + zfcp_scsi_dif_sense_error(scpnt, 0x2); + goto skip_fsfstatus; + case FSF_REF_TAG_CHECK_FAILURE: + zfcp_scsi_dif_sense_error(scpnt, 0x3); + goto skip_fsfstatus; + } fcp_rsp = (struct fcp_resp_with_ext *) &req->qtcb->bottom.io.fcp_rsp; zfcp_fc_eval_fcp_rsp(fcp_rsp, scpnt); @@ -2190,6 +2213,44 @@ skip_fsfstatus: } } +static int zfcp_fsf_set_data_dir(struct scsi_cmnd *scsi_cmnd, u32 *data_dir) +{ + switch (scsi_get_prot_op(scsi_cmnd)) { + case SCSI_PROT_NORMAL: + switch (scsi_cmnd->sc_data_direction) { + case DMA_NONE: + *data_dir = FSF_DATADIR_CMND; + break; + case DMA_FROM_DEVICE: + *data_dir = FSF_DATADIR_READ; + break; + case DMA_TO_DEVICE: + *data_dir = FSF_DATADIR_WRITE; + break; + case DMA_BIDIRECTIONAL: + return -EINVAL; + } + break; + + case SCSI_PROT_READ_STRIP: + *data_dir = FSF_DATADIR_DIF_READ_STRIP; + break; + case SCSI_PROT_WRITE_INSERT: + *data_dir = FSF_DATADIR_DIF_WRITE_INSERT; + break; + case SCSI_PROT_READ_PASS: + *data_dir = FSF_DATADIR_DIF_READ_CONVERT; + break; + case SCSI_PROT_WRITE_PASS: + *data_dir = FSF_DATADIR_DIF_WRITE_CONVERT; + break; + default: + return -EINVAL; + } + + return 0; +} + /** * zfcp_fsf_send_fcp_command_task - initiate an FCP command (for a SCSI command) * @unit: unit where command is sent to @@ -2201,9 +2262,10 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit, struct zfcp_fsf_req *req; struct fcp_cmnd *fcp_cmnd; unsigned int sbtype = SBAL_FLAGS0_TYPE_READ; - int real_bytes, retval = -EIO; + int real_bytes, retval = -EIO, dix_bytes = 0; struct zfcp_adapter *adapter = unit->port->adapter; struct zfcp_qdio *qdio = adapter->qdio; + struct fsf_qtcb_bottom_io *io; if (unlikely(!(atomic_read(&unit->status) & ZFCP_STATUS_COMMON_UNBLOCKED))) @@ -2226,46 +2288,46 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit, goto out; } + scsi_cmnd->host_scribble = (unsigned char *) req->req_id; + + io = &req->qtcb->bottom.io; req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; req->unit = unit; req->data = scsi_cmnd; req->handler = zfcp_fsf_send_fcp_command_handler; req->qtcb->header.lun_handle = unit->handle; req->qtcb->header.port_handle = unit->port->handle; - req->qtcb->bottom.io.service_class = FSF_CLASS_3; - req->qtcb->bottom.io.fcp_cmnd_length = FCP_CMND_LEN; + io->service_class = FSF_CLASS_3; + io->fcp_cmnd_length = FCP_CMND_LEN; - scsi_cmnd->host_scribble = (unsigned char *) req->req_id; - - /* - * set depending on data direction: - * data direction bits in SBALE (SB Type) - * data direction bits in QTCB - */ - switch (scsi_cmnd->sc_data_direction) { - case DMA_NONE: - req->qtcb->bottom.io.data_direction = FSF_DATADIR_CMND; - break; - case DMA_FROM_DEVICE: - req->qtcb->bottom.io.data_direction = FSF_DATADIR_READ; - break; - case DMA_TO_DEVICE: - req->qtcb->bottom.io.data_direction = FSF_DATADIR_WRITE; - break; - case DMA_BIDIRECTIONAL: - goto failed_scsi_cmnd; + if (scsi_get_prot_op(scsi_cmnd) != SCSI_PROT_NORMAL) { + io->data_block_length = scsi_cmnd->device->sector_size; + io->ref_tag_value = scsi_get_lba(scsi_cmnd) & 0xFFFFFFFF; } + zfcp_fsf_set_data_dir(scsi_cmnd, &io->data_direction); + get_device(&unit->dev); fcp_cmnd = (struct fcp_cmnd *) &req->qtcb->bottom.io.fcp_cmnd; zfcp_fc_scsi_to_fcp(fcp_cmnd, scsi_cmnd); + if (scsi_prot_sg_count(scsi_cmnd)) { + zfcp_qdio_set_data_div(qdio, &req->qdio_req, + scsi_prot_sg_count(scsi_cmnd)); + dix_bytes = zfcp_qdio_sbals_from_sg(qdio, &req->qdio_req, + scsi_prot_sglist(scsi_cmnd)); + io->prot_data_length = dix_bytes; + } + real_bytes = zfcp_qdio_sbals_from_sg(qdio, &req->qdio_req, scsi_sglist(scsi_cmnd)); - if (unlikely(real_bytes < 0)) + + if (unlikely(real_bytes < 0) || unlikely(dix_bytes < 0)) goto failed_scsi_cmnd; + zfcp_qdio_set_sbale_last(adapter->qdio, &req->qdio_req); + retval = zfcp_fsf_req_send(req); if (unlikely(retval)) goto failed_scsi_cmnd; @@ -2389,6 +2451,7 @@ struct zfcp_fsf_req *zfcp_fsf_control_file(struct zfcp_adapter *adapter, zfcp_fsf_req_free(req); goto out; } + zfcp_qdio_set_sbale_last(adapter->qdio, &req->qdio_req); zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT); retval = zfcp_fsf_req_send(req); diff --git a/drivers/s390/scsi/zfcp_fsf.h b/drivers/s390/scsi/zfcp_fsf.h index ca110e386761..db8c85382dca 100644 --- a/drivers/s390/scsi/zfcp_fsf.h +++ b/drivers/s390/scsi/zfcp_fsf.h @@ -80,11 +80,15 @@ #define FSF_REQUEST_SIZE_TOO_LARGE 0x00000061 #define FSF_RESPONSE_SIZE_TOO_LARGE 0x00000062 #define FSF_SBAL_MISMATCH 0x00000063 +#define FSF_INCONSISTENT_PROT_DATA 0x00000070 +#define FSF_INVALID_PROT_PARM 0x00000071 +#define FSF_BLOCK_GUARD_CHECK_FAILURE 0x00000081 +#define FSF_APP_TAG_CHECK_FAILURE 0x00000082 +#define FSF_REF_TAG_CHECK_FAILURE 0x00000083 #define FSF_ADAPTER_STATUS_AVAILABLE 0x000000AD #define FSF_UNKNOWN_COMMAND 0x000000E2 #define FSF_UNKNOWN_OP_SUBTYPE 0x000000E3 #define FSF_INVALID_COMMAND_OPTION 0x000000E5 -/* #define FSF_ERROR 0x000000FF */ #define FSF_PROT_STATUS_QUAL_SIZE 16 #define FSF_STATUS_QUALIFIER_SIZE 16 @@ -147,6 +151,13 @@ #define FSF_DATADIR_WRITE 0x00000001 #define FSF_DATADIR_READ 0x00000002 #define FSF_DATADIR_CMND 0x00000004 +#define FSF_DATADIR_DIF_WRITE_INSERT 0x00000009 +#define FSF_DATADIR_DIF_READ_STRIP 0x0000000a +#define FSF_DATADIR_DIF_WRITE_CONVERT 0x0000000b +#define FSF_DATADIR_DIF_READ_CONVERT 0X0000000c + +/* data protection control flags */ +#define FSF_APP_TAG_CHECK_ENABLE 0x10 /* fc service class */ #define FSF_CLASS_3 0x00000003 @@ -162,6 +173,8 @@ #define FSF_FEATURE_ELS_CT_CHAINED_SBALS 0x00000020 #define FSF_FEATURE_UPDATE_ALERT 0x00000100 #define FSF_FEATURE_MEASUREMENT_DATA 0x00000200 +#define FSF_FEATURE_DIF_PROT_TYPE1 0x00010000 +#define FSF_FEATURE_DIX_PROT_TCPIP 0x00020000 /* host connection features */ #define FSF_FEATURE_NPIV_MODE 0x00000001 @@ -316,9 +329,14 @@ struct fsf_qtcb_header { struct fsf_qtcb_bottom_io { u32 data_direction; u32 service_class; - u8 res1[8]; + u8 res1; + u8 data_prot_flags; + u16 app_tag_value; + u32 ref_tag_value; u32 fcp_cmnd_length; - u8 res2[12]; + u32 data_block_length; + u32 prot_data_length; + u8 res2[4]; u8 fcp_cmnd[FSF_FCP_CMND_SIZE]; u8 fcp_rsp[FSF_FCP_RSP_SIZE]; u8 res3[64]; diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c index c4559b29bebc..aceced8ec7e4 100644 --- a/drivers/s390/scsi/zfcp_qdio.c +++ b/drivers/s390/scsi/zfcp_qdio.c @@ -193,10 +193,6 @@ int zfcp_qdio_sbals_from_sg(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req, bytes += sg->length; } - /* assume that no other SBALEs are to follow in the same SBAL */ - sbale = zfcp_qdio_sbale_curr(qdio, q_req); - sbale->flags |= SBAL_FLAGS_LAST_ENTRY; - return bytes; } diff --git a/drivers/s390/scsi/zfcp_qdio.h b/drivers/s390/scsi/zfcp_qdio.h index 10d0df99dbf4..2297d8d3e947 100644 --- a/drivers/s390/scsi/zfcp_qdio.h +++ b/drivers/s390/scsi/zfcp_qdio.h @@ -215,4 +215,20 @@ void zfcp_qdio_sbal_limit(struct zfcp_qdio *qdio, QDIO_MAX_BUFFERS_PER_Q; } +/** + * zfcp_qdio_set_data_div - set data division count + * @qdio: pointer to struct zfcp_qdio + * @q_req: The current zfcp_qdio_req + * @count: The data division count + */ +static inline +void zfcp_qdio_set_data_div(struct zfcp_qdio *qdio, + struct zfcp_qdio_req *q_req, u32 count) +{ + struct qdio_buffer_element *sbale; + + sbale = &qdio->req_q[q_req->sbal_first]->element[0]; + sbale->length = count; +} + #endif /* ZFCP_QDIO_H */ diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index eb471a1723cd..cb000c9833bb 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include "zfcp_ext.h" #include "zfcp_dbf.h" @@ -22,6 +23,13 @@ static unsigned int default_depth = 32; module_param_named(queue_depth, default_depth, uint, 0600); MODULE_PARM_DESC(queue_depth, "Default queue depth for new SCSI devices"); +static bool enable_dif; + +#ifdef CONFIG_ZFCP_DIF +module_param_named(dif, enable_dif, bool, 0600); +MODULE_PARM_DESC(dif, "Enable DIF/DIX data integrity support"); +#endif + static int zfcp_scsi_change_queue_depth(struct scsi_device *sdev, int depth, int reason) { @@ -652,6 +660,51 @@ void zfcp_scsi_scan_work(struct work_struct *work) put_device(&unit->dev); } +/** + * zfcp_scsi_set_prot - Configure DIF/DIX support in scsi_host + * @adapter: The adapter where to configure DIF/DIX for the SCSI host + */ +void zfcp_scsi_set_prot(struct zfcp_adapter *adapter) +{ + unsigned int mask = 0; + unsigned int data_div; + struct Scsi_Host *shost = adapter->scsi_host; + + data_div = atomic_read(&adapter->status) & + ZFCP_STATUS_ADAPTER_DATA_DIV_ENABLED; + + if (enable_dif && + adapter->adapter_features & FSF_FEATURE_DIF_PROT_TYPE1) + mask |= SHOST_DIF_TYPE1_PROTECTION; + + if (enable_dif && data_div && + adapter->adapter_features & FSF_FEATURE_DIX_PROT_TCPIP) { + mask |= SHOST_DIX_TYPE1_PROTECTION; + scsi_host_set_guard(shost, SHOST_DIX_GUARD_IP); + shost->sg_tablesize = ZFCP_QDIO_MAX_SBALES_PER_REQ / 2; + shost->max_sectors = ZFCP_QDIO_MAX_SBALES_PER_REQ * 8 / 2; + } + + scsi_host_set_prot(shost, mask); +} + +/** + * zfcp_scsi_dif_sense_error - Report DIF/DIX error as driver sense error + * @scmd: The SCSI command to report the error for + * @ascq: The ASCQ to put in the sense buffer + * + * See the error handling in sd_done for the sense codes used here. + * Set DID_SOFT_ERROR to retry the request, if possible. + */ +void zfcp_scsi_dif_sense_error(struct scsi_cmnd *scmd, int ascq) +{ + scsi_build_sense_buffer(1, scmd->sense_buffer, + ILLEGAL_REQUEST, 0x10, ascq); + set_driver_byte(scmd, DRIVER_SENSE); + scmd->result |= SAM_STAT_CHECK_CONDITION; + set_host_byte(scmd, DID_SOFT_ERROR); +} + struct fc_function_template zfcp_transport_functions = { .show_starget_port_id = 1, .show_starget_port_name = 1, diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 75f2336807cb..158284f05732 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -1847,6 +1847,10 @@ config ZFCP called zfcp. If you want to compile it as a module, say M here and read . +config ZFCP_DIF + tristate "T10 DIF/DIX support for the zfcp driver (EXPERIMENTAL)" + depends on ZFCP && EXPERIMENTAL + config SCSI_PMCRAID tristate "PMC SIERRA Linux MaxRAID adapter support" depends on PCI && SCSI -- cgit v1.2.3