summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/s390/scsi/zfcp_dbf.c2
-rw-r--r--drivers/s390/scsi/zfcp_dbf.h1
-rw-r--r--drivers/s390/scsi/zfcp_def.h9
-rw-r--r--drivers/s390/scsi/zfcp_fc.c55
-rw-r--r--drivers/s390/scsi/zfcp_fsf.c32
-rw-r--r--drivers/s390/scsi/zfcp_fsf.h2
6 files changed, 61 insertions, 40 deletions
diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c
index bf691fbc4d29..150bd5390b1d 100644
--- a/drivers/s390/scsi/zfcp_dbf.c
+++ b/drivers/s390/scsi/zfcp_dbf.c
@@ -936,6 +936,7 @@ void zfcp_san_dbf_event_ct_response(struct zfcp_fsf_req *fsf_req)
rct->reason_code = hdr->reason_code;
rct->expl = hdr->reason_code_expl;
rct->vendor_unique = hdr->vendor_unique;
+ rct->max_res_size = hdr->max_res_size;
rct->len = min((int)ct->resp->length - (int)sizeof(struct ct_hdr),
ZFCP_DBF_SAN_MAX_PAYLOAD);
debug_event(adapter->san_dbf, level, r, sizeof(*r));
@@ -1043,6 +1044,7 @@ static int zfcp_san_dbf_view_format(debug_info_t *id, struct debug_view *view,
zfcp_dbf_out(&p, "reason_code", "0x%02x", ct->reason_code);
zfcp_dbf_out(&p, "reason_code_expl", "0x%02x", ct->expl);
zfcp_dbf_out(&p, "vendor_unique", "0x%02x", ct->vendor_unique);
+ zfcp_dbf_out(&p, "max_res_size", "0x%04x", ct->max_res_size);
} else if (strncmp(r->tag, "oels", ZFCP_DBF_TAG_SIZE) == 0 ||
strncmp(r->tag, "rels", ZFCP_DBF_TAG_SIZE) == 0 ||
strncmp(r->tag, "iels", ZFCP_DBF_TAG_SIZE) == 0) {
diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h
index 5d6b2dff855b..74998ff88e57 100644
--- a/drivers/s390/scsi/zfcp_dbf.h
+++ b/drivers/s390/scsi/zfcp_dbf.h
@@ -171,6 +171,7 @@ struct zfcp_san_dbf_record_ct_response {
u8 reason_code;
u8 expl;
u8 vendor_unique;
+ u16 max_res_size;
u32 len;
} __attribute__ ((packed));
diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h
index ce4094966236..510662783a6f 100644
--- a/drivers/s390/scsi/zfcp_def.h
+++ b/drivers/s390/scsi/zfcp_def.h
@@ -197,7 +197,6 @@ struct zfcp_ls_adisc {
#define ZFCP_CT_UNABLE_TO_PERFORM_CMD 0x09
#define ZFCP_CT_GID_PN 0x0121
#define ZFCP_CT_GPN_FT 0x0172
-#define ZFCP_CT_MAX_SIZE 0x1020
#define ZFCP_CT_ACCEPT 0x8002
#define ZFCP_CT_REJECT 0x8001
@@ -325,8 +324,6 @@ struct ct_iu_gid_pn_resp {
* @wka_port: port where the request is sent to
* @req: scatter-gather list for request
* @resp: scatter-gather list for response
- * @req_count: number of elements in request scatter-gather list
- * @resp_count: number of elements in response scatter-gather list
* @handler: handler function (called for response to the request)
* @handler_data: data passed to handler function
* @timeout: FSF timeout for this request
@@ -337,8 +334,6 @@ struct zfcp_send_ct {
struct zfcp_wka_port *wka_port;
struct scatterlist *req;
struct scatterlist *resp;
- unsigned int req_count;
- unsigned int resp_count;
void (*handler)(unsigned long);
unsigned long handler_data;
int timeout;
@@ -363,8 +358,6 @@ struct zfcp_gid_pn_data {
* @d_id: destiniation id of port where request is sent to
* @req: scatter-gather list for request
* @resp: scatter-gather list for response
- * @req_count: number of elements in request scatter-gather list
- * @resp_count: number of elements in response scatter-gather list
* @handler: handler function (called for response to the request)
* @handler_data: data passed to handler function
* @completion: completion for synchronization purposes
@@ -377,8 +370,6 @@ struct zfcp_send_els {
u32 d_id;
struct scatterlist *req;
struct scatterlist *resp;
- unsigned int req_count;
- unsigned int resp_count;
void (*handler)(unsigned long);
unsigned long handler_data;
struct completion *completion;
diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c
index 217c3b04fd01..eabdfe24456e 100644
--- a/drivers/s390/scsi/zfcp_fc.c
+++ b/drivers/s390/scsi/zfcp_fc.c
@@ -40,9 +40,12 @@ struct gpn_ft_resp_acc {
u64 wwpn;
} __attribute__ ((packed));
-#define ZFCP_GPN_FT_ENTRIES ((PAGE_SIZE - sizeof(struct ct_hdr)) \
- / sizeof(struct gpn_ft_resp_acc))
+#define ZFCP_CT_SIZE_ONE_PAGE (PAGE_SIZE - sizeof(struct ct_hdr))
+#define ZFCP_GPN_FT_ENTRIES (ZFCP_CT_SIZE_ONE_PAGE \
+ / sizeof(struct gpn_ft_resp_acc))
#define ZFCP_GPN_FT_BUFFERS 4
+#define ZFCP_GPN_FT_MAX_SIZE (ZFCP_GPN_FT_BUFFERS * PAGE_SIZE \
+ - sizeof(struct ct_hdr))
#define ZFCP_GPN_FT_MAX_ENTRIES ZFCP_GPN_FT_BUFFERS * (ZFCP_GPN_FT_ENTRIES + 1)
struct ct_iu_gpn_ft_resp {
@@ -282,8 +285,6 @@ int static zfcp_fc_ns_gid_pn_request(struct zfcp_erp_action *erp_action,
gid_pn->ct.timeout = ZFCP_NS_GID_PN_TIMEOUT;
gid_pn->ct.req = &gid_pn->req;
gid_pn->ct.resp = &gid_pn->resp;
- gid_pn->ct.req_count = 1;
- gid_pn->ct.resp_count = 1;
sg_init_one(&gid_pn->req, &gid_pn->ct_iu_req,
sizeof(struct ct_iu_gid_pn_req));
sg_init_one(&gid_pn->resp, &gid_pn->ct_iu_resp,
@@ -295,7 +296,7 @@ int static zfcp_fc_ns_gid_pn_request(struct zfcp_erp_action *erp_action,
gid_pn->ct_iu_req.header.gs_subtype = ZFCP_CT_NAME_SERVER;
gid_pn->ct_iu_req.header.options = ZFCP_CT_SYNCHRONOUS;
gid_pn->ct_iu_req.header.cmd_rsp_code = ZFCP_CT_GID_PN;
- gid_pn->ct_iu_req.header.max_res_size = ZFCP_CT_MAX_SIZE;
+ gid_pn->ct_iu_req.header.max_res_size = ZFCP_CT_SIZE_ONE_PAGE / 4;
gid_pn->ct_iu_req.wwpn = erp_action->port->wwpn;
init_completion(&compl_rec.done);
@@ -405,8 +406,6 @@ static int zfcp_fc_adisc(struct zfcp_port *port)
sg_init_one(adisc->els.resp, &adisc->ls_adisc_acc,
sizeof(struct zfcp_ls_adisc));
- adisc->els.req_count = 1;
- adisc->els.resp_count = 1;
adisc->els.adapter = adapter;
adisc->els.port = port;
adisc->els.d_id = port->d_id;
@@ -446,17 +445,17 @@ void zfcp_test_link(struct zfcp_port *port)
zfcp_erp_port_forced_reopen(port, 0, 65, NULL);
}
-static void zfcp_free_sg_env(struct zfcp_gpn_ft *gpn_ft)
+static void zfcp_free_sg_env(struct zfcp_gpn_ft *gpn_ft, int buf_num)
{
struct scatterlist *sg = &gpn_ft->sg_req;
kfree(sg_virt(sg)); /* free request buffer */
- zfcp_sg_free_table(gpn_ft->sg_resp, ZFCP_GPN_FT_BUFFERS);
+ zfcp_sg_free_table(gpn_ft->sg_resp, buf_num);
kfree(gpn_ft);
}
-static struct zfcp_gpn_ft *zfcp_alloc_sg_env(void)
+static struct zfcp_gpn_ft *zfcp_alloc_sg_env(int buf_num)
{
struct zfcp_gpn_ft *gpn_ft;
struct ct_iu_gpn_ft_req *req;
@@ -473,8 +472,8 @@ static struct zfcp_gpn_ft *zfcp_alloc_sg_env(void)
}
sg_init_one(&gpn_ft->sg_req, req, sizeof(*req));
- if (zfcp_sg_setup_table(gpn_ft->sg_resp, ZFCP_GPN_FT_BUFFERS)) {
- zfcp_free_sg_env(gpn_ft);
+ if (zfcp_sg_setup_table(gpn_ft->sg_resp, buf_num)) {
+ zfcp_free_sg_env(gpn_ft, buf_num);
gpn_ft = NULL;
}
out:
@@ -483,7 +482,8 @@ out:
static int zfcp_scan_issue_gpn_ft(struct zfcp_gpn_ft *gpn_ft,
- struct zfcp_adapter *adapter)
+ struct zfcp_adapter *adapter,
+ int max_bytes)
{
struct zfcp_send_ct *ct = &gpn_ft->ct;
struct ct_iu_gpn_ft_req *req = sg_virt(&gpn_ft->sg_req);
@@ -496,8 +496,7 @@ static int zfcp_scan_issue_gpn_ft(struct zfcp_gpn_ft *gpn_ft,
req->header.gs_subtype = ZFCP_CT_NAME_SERVER;
req->header.options = ZFCP_CT_SYNCHRONOUS;
req->header.cmd_rsp_code = ZFCP_CT_GPN_FT;
- req->header.max_res_size = (sizeof(struct gpn_ft_resp_acc) *
- (ZFCP_GPN_FT_MAX_ENTRIES - 1)) >> 2;
+ req->header.max_res_size = max_bytes / 4;
req->flags = 0;
req->domain_id_scope = 0;
req->area_id_scope = 0;
@@ -510,8 +509,6 @@ static int zfcp_scan_issue_gpn_ft(struct zfcp_gpn_ft *gpn_ft,
ct->timeout = 10;
ct->req = &gpn_ft->sg_req;
ct->resp = gpn_ft->sg_resp;
- ct->req_count = 1;
- ct->resp_count = ZFCP_GPN_FT_BUFFERS;
init_completion(&compl_rec.done);
compl_rec.handler = NULL;
@@ -538,7 +535,7 @@ static void zfcp_validate_port(struct zfcp_port *port)
zfcp_port_dequeue(port);
}
-static int zfcp_scan_eval_gpn_ft(struct zfcp_gpn_ft *gpn_ft)
+static int zfcp_scan_eval_gpn_ft(struct zfcp_gpn_ft *gpn_ft, int max_entries)
{
struct zfcp_send_ct *ct = &gpn_ft->ct;
struct scatterlist *sg = gpn_ft->sg_resp;
@@ -558,13 +555,17 @@ static int zfcp_scan_eval_gpn_ft(struct zfcp_gpn_ft *gpn_ft)
return -EIO;
}
- if (hdr->max_res_size)
+ if (hdr->max_res_size) {
+ dev_warn(&adapter->ccw_device->dev,
+ "The name server reported %d words residual data\n",
+ hdr->max_res_size);
return -E2BIG;
+ }
down(&zfcp_data.config_sema);
/* first entry is the header */
- for (x = 1; x < ZFCP_GPN_FT_MAX_ENTRIES && !last; x++) {
+ for (x = 1; x < max_entries && !last; x++) {
if (x % (ZFCP_GPN_FT_ENTRIES + 1))
acc++;
else
@@ -609,6 +610,12 @@ int zfcp_scan_ports(struct zfcp_adapter *adapter)
{
int ret, i;
struct zfcp_gpn_ft *gpn_ft;
+ int chain, max_entries, buf_num, max_bytes;
+
+ chain = adapter->adapter_features & FSF_FEATURE_ELS_CT_CHAINED_SBALS;
+ buf_num = chain ? ZFCP_GPN_FT_BUFFERS : 1;
+ max_entries = chain ? ZFCP_GPN_FT_MAX_ENTRIES : ZFCP_GPN_FT_ENTRIES;
+ max_bytes = chain ? ZFCP_GPN_FT_MAX_SIZE : ZFCP_CT_SIZE_ONE_PAGE;
if (fc_host_port_type(adapter->scsi_host) != FC_PORTTYPE_NPORT)
return 0;
@@ -617,23 +624,23 @@ int zfcp_scan_ports(struct zfcp_adapter *adapter)
if (ret)
return ret;
- gpn_ft = zfcp_alloc_sg_env();
+ gpn_ft = zfcp_alloc_sg_env(buf_num);
if (!gpn_ft) {
ret = -ENOMEM;
goto out;
}
for (i = 0; i < 3; i++) {
- ret = zfcp_scan_issue_gpn_ft(gpn_ft, adapter);
+ ret = zfcp_scan_issue_gpn_ft(gpn_ft, adapter, max_bytes);
if (!ret) {
- ret = zfcp_scan_eval_gpn_ft(gpn_ft);
+ ret = zfcp_scan_eval_gpn_ft(gpn_ft, max_entries);
if (ret == -EAGAIN)
ssleep(1);
else
break;
}
}
- zfcp_free_sg_env(gpn_ft);
+ zfcp_free_sg_env(gpn_ft, buf_num);
out:
zfcp_wka_port_put(&adapter->nsp);
return ret;
diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c
index 17620ecda335..9bba56b16831 100644
--- a/drivers/s390/scsi/zfcp_fsf.c
+++ b/drivers/s390/scsi/zfcp_fsf.c
@@ -1013,12 +1013,29 @@ skip_fsfstatus:
send_ct->handler(send_ct->handler_data);
}
-static int zfcp_fsf_setup_sbals(struct zfcp_fsf_req *req,
- struct scatterlist *sg_req,
- struct scatterlist *sg_resp, int max_sbals)
+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 qdio_buffer_element *sbale = zfcp_qdio_sbale_req(req);
+ u32 feat = req->adapter->adapter_features;
int bytes;
+ if (!(feat & FSF_FEATURE_ELS_CT_CHAINED_SBALS)) {
+ if (sg_req->length > PAGE_SIZE || sg_resp->length > PAGE_SIZE ||
+ !sg_is_last(sg_req) || !sg_is_last(sg_resp))
+ return -EOPNOTSUPP;
+
+ sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE_READ;
+ sbale[2].addr = sg_virt(sg_req);
+ sbale[2].length = sg_req->length;
+ sbale[3].addr = sg_virt(sg_resp);
+ sbale[3].length = sg_resp->length;
+ sbale[3].flags |= SBAL_FLAGS_LAST_ENTRY;
+ return 0;
+ }
+
bytes = zfcp_qdio_sbals_from_sg(req, SBAL_FLAGS0_TYPE_WRITE_READ,
sg_req, max_sbals);
if (bytes <= 0)
@@ -1060,8 +1077,8 @@ int zfcp_fsf_send_ct(struct zfcp_send_ct *ct, mempool_t *pool,
goto out;
}
- ret = zfcp_fsf_setup_sbals(req, ct->req, ct->resp,
- FSF_MAX_SBALS_PER_REQ);
+ ret = zfcp_fsf_setup_ct_els_sbals(req, ct->req, ct->resp,
+ FSF_MAX_SBALS_PER_REQ);
if (ret)
goto failed_send;
@@ -1171,7 +1188,7 @@ int zfcp_fsf_send_els(struct zfcp_send_els *els)
goto out;
}
- ret = zfcp_fsf_setup_sbals(req, els->req, els->resp, 2);
+ ret = zfcp_fsf_setup_ct_els_sbals(req, els->req, els->resp, 2);
if (ret)
goto failed_send;
@@ -1440,7 +1457,8 @@ static void zfcp_fsf_open_port_handler(struct zfcp_fsf_req *req)
* Alternately, an ADISC/PDISC ELS should suffice, as well.
*/
plogi = (struct fsf_plogi *) req->qtcb->bottom.support.els;
- if (req->qtcb->bottom.support.els1_length >= sizeof(*plogi)) {
+ if (req->qtcb->bottom.support.els1_length >=
+ FSF_PLOGI_MIN_LEN) {
if (plogi->serv_param.wwpn != port->wwpn)
port->d_id = 0;
else {
diff --git a/drivers/s390/scsi/zfcp_fsf.h b/drivers/s390/scsi/zfcp_fsf.h
index fa2a31780611..8bb200252347 100644
--- a/drivers/s390/scsi/zfcp_fsf.h
+++ b/drivers/s390/scsi/zfcp_fsf.h
@@ -164,6 +164,7 @@
#define FSF_FEATURE_LUN_SHARING 0x00000004
#define FSF_FEATURE_NOTIFICATION_LOST 0x00000008
#define FSF_FEATURE_HBAAPI_MANAGEMENT 0x00000010
+#define FSF_FEATURE_ELS_CT_CHAINED_SBALS 0x00000020
#define FSF_FEATURE_UPDATE_ALERT 0x00000100
#define FSF_FEATURE_MEASUREMENT_DATA 0x00000200
@@ -322,6 +323,7 @@ struct fsf_nport_serv_param {
u8 vendor_version_level[16];
} __attribute__ ((packed));
+#define FSF_PLOGI_MIN_LEN 112
struct fsf_plogi {
u32 code;
struct fsf_nport_serv_param serv_param;