]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/s390/scsi/zfcp_fc.c
Merge commit 'v2.6.27-rc7' into x86/microcode
[linux-2.6-omap-h63xx.git] / drivers / s390 / scsi / zfcp_fc.c
index bb07c3bf2258af13555e6c48d933cf9580e38228..56196c98c07b417f060e44dfb6c8a2dce2543e89 100644 (file)
@@ -8,6 +8,37 @@
 
 #include "zfcp_ext.h"
 
+struct ct_iu_gpn_ft_req {
+       struct ct_hdr header;
+       u8 flags;
+       u8 domain_id_scope;
+       u8 area_id_scope;
+       u8 fc4_type;
+} __attribute__ ((packed));
+
+struct gpn_ft_resp_acc {
+       u8 control;
+       u8 port_id[3];
+       u8 reserved[4];
+       u64 wwpn;
+} __attribute__ ((packed));
+
+#define ZFCP_GPN_FT_ENTRIES ((PAGE_SIZE - sizeof(struct ct_hdr)) \
+                               / sizeof(struct gpn_ft_resp_acc))
+#define ZFCP_GPN_FT_BUFFERS 4
+#define ZFCP_GPN_FT_MAX_ENTRIES ZFCP_GPN_FT_BUFFERS * (ZFCP_GPN_FT_ENTRIES + 1)
+
+struct ct_iu_gpn_ft_resp {
+       struct ct_hdr header;
+       struct gpn_ft_resp_acc accept[ZFCP_GPN_FT_ENTRIES];
+} __attribute__ ((packed));
+
+struct zfcp_gpn_ft {
+       struct zfcp_send_ct ct;
+       struct scatterlist sg_req;
+       struct scatterlist sg_resp[ZFCP_GPN_FT_BUFFERS];
+};
+
 static void _zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req, u32 range,
                                   struct fcp_rscn_element *elem)
 {
@@ -40,8 +71,8 @@ static void zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req)
        u16 no_entries;
        u32 range_mask;
 
-       fcp_rscn_head = (struct fcp_rscn_head *) status_buffer->payload;
-       fcp_rscn_element = (struct fcp_rscn_element *) status_buffer->payload;
+       fcp_rscn_head = (struct fcp_rscn_head *) status_buffer->payload.data;
+       fcp_rscn_element = (struct fcp_rscn_element *) fcp_rscn_head;
 
        /* see FC-FS */
        no_entries = fcp_rscn_head->payload_len /
@@ -68,6 +99,7 @@ static void zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req)
                }
                _zfcp_fc_incoming_rscn(fsf_req, range_mask, fcp_rscn_element);
        }
+       schedule_work(&fsf_req->adapter->scan_work);
 }
 
 static void zfcp_fc_incoming_wwpn(struct zfcp_fsf_req *req, wwn_t wwpn)
@@ -91,7 +123,7 @@ static void zfcp_fc_incoming_plogi(struct zfcp_fsf_req *req)
        struct fsf_status_read_buffer *status_buffer =
                (struct fsf_status_read_buffer *)req->data;
        struct fsf_plogi *els_plogi =
-               (struct fsf_plogi *) status_buffer->payload;
+               (struct fsf_plogi *) status_buffer->payload.data;
 
        zfcp_fc_incoming_wwpn(req, els_plogi->serv_param.wwpn);
 }
@@ -100,7 +132,8 @@ static void zfcp_fc_incoming_logo(struct zfcp_fsf_req *req)
 {
        struct fsf_status_read_buffer *status_buffer =
                (struct fsf_status_read_buffer *)req->data;
-       struct fcp_logo *els_logo = (struct fcp_logo *) status_buffer->payload;
+       struct fcp_logo *els_logo =
+               (struct fcp_logo *) status_buffer->payload.data;
 
        zfcp_fc_incoming_wwpn(req, els_logo->nport_wwpn);
 }
@@ -113,7 +146,7 @@ void zfcp_fc_incoming_els(struct zfcp_fsf_req *fsf_req)
 {
        struct fsf_status_read_buffer *status_buffer =
                (struct fsf_status_read_buffer *) fsf_req->data;
-       unsigned int els_type = status_buffer->payload[0];
+       unsigned int els_type = status_buffer->payload.data[0];
 
        zfcp_san_dbf_event_incoming_els(fsf_req);
        if (els_type == LS_PLOGI)
@@ -231,7 +264,7 @@ static void zfcp_fc_adisc_handler(unsigned long data)
        struct zfcp_port *port = adisc->els.port;
        struct zfcp_ls_adisc_acc *ls_adisc = &adisc->ls_adisc_acc;
 
-       if (!adisc->els.status) {
+       if (adisc->els.status) {
                /* request rejected or timed out */
                zfcp_erp_port_forced_reopen(port, 0, 63, NULL);
                goto out;
@@ -296,10 +329,230 @@ void zfcp_test_link(struct zfcp_port *port)
 
        zfcp_port_get(port);
        retval = zfcp_fc_adisc(port);
-       if (retval == 0 || retval == -EBUSY)
+       if (retval == 0)
                return;
 
        /* send of ADISC was not possible */
        zfcp_port_put(port);
-       zfcp_erp_port_forced_reopen(port, 0, 65, NULL);
+       if (retval != -EBUSY)
+               zfcp_erp_port_forced_reopen(port, 0, 65, NULL);
+}
+
+static int zfcp_scan_get_nameserver(struct zfcp_adapter *adapter)
+{
+       int ret;
+
+       if (!adapter->nameserver_port)
+               return -EINTR;
+
+       if (!atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED,
+                              &adapter->nameserver_port->status)) {
+               ret = zfcp_erp_port_reopen(adapter->nameserver_port, 0, 148,
+                                          NULL);
+               if (ret)
+                       return ret;
+               zfcp_erp_wait(adapter);
+       }
+       return !atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED,
+                                 &adapter->nameserver_port->status);
+}
+
+static void zfcp_gpn_ft_handler(unsigned long _done)
+{
+       complete((struct completion *)_done);
+}
+
+static void zfcp_free_sg_env(struct zfcp_gpn_ft *gpn_ft)
+{
+       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);
+
+       kfree(gpn_ft);
+}
+
+static struct zfcp_gpn_ft *zfcp_alloc_sg_env(void)
+{
+       struct zfcp_gpn_ft *gpn_ft;
+       struct ct_iu_gpn_ft_req *req;
+
+       gpn_ft = kzalloc(sizeof(*gpn_ft), GFP_KERNEL);
+       if (!gpn_ft)
+               return NULL;
+
+       req = kzalloc(sizeof(struct ct_iu_gpn_ft_req), GFP_KERNEL);
+       if (!req) {
+               kfree(gpn_ft);
+               gpn_ft = NULL;
+               goto out;
+       }
+       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);
+               gpn_ft = NULL;
+       }
+out:
+       return gpn_ft;
+}
+
+
+static int zfcp_scan_issue_gpn_ft(struct zfcp_gpn_ft *gpn_ft,
+                                 struct zfcp_adapter *adapter)
+{
+       struct zfcp_send_ct *ct = &gpn_ft->ct;
+       struct ct_iu_gpn_ft_req *req = sg_virt(&gpn_ft->sg_req);
+       struct completion done;
+       int ret;
+
+       /* prepare CT IU for GPN_FT */
+       req->header.revision = ZFCP_CT_REVISION;
+       req->header.gs_type = ZFCP_CT_DIRECTORY_SERVICE;
+       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->flags = 0;
+       req->domain_id_scope = 0;
+       req->area_id_scope = 0;
+       req->fc4_type = ZFCP_CT_SCSI_FCP;
+
+       /* prepare zfcp_send_ct */
+       ct->port = adapter->nameserver_port;
+       ct->handler = zfcp_gpn_ft_handler;
+       ct->handler_data = (unsigned long)&done;
+       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(&done);
+       ret = zfcp_fsf_send_ct(ct, NULL, NULL);
+       if (!ret)
+               wait_for_completion(&done);
+       return ret;
+}
+
+static void zfcp_validate_port(struct zfcp_port *port)
+{
+       struct zfcp_adapter *adapter = port->adapter;
+
+       atomic_clear_mask(ZFCP_STATUS_COMMON_NOESC, &port->status);
+
+       if (port == adapter->nameserver_port)
+               return;
+       if ((port->supported_classes != 0) || (port->units != 0)) {
+               zfcp_port_put(port);
+               return;
+       }
+       zfcp_erp_port_shutdown(port, 0, 151, NULL);
+       zfcp_erp_wait(adapter);
+       zfcp_port_put(port);
+       zfcp_port_dequeue(port);
+}
+
+static int zfcp_scan_eval_gpn_ft(struct zfcp_gpn_ft *gpn_ft)
+{
+       struct zfcp_send_ct *ct = &gpn_ft->ct;
+       struct scatterlist *sg = gpn_ft->sg_resp;
+       struct ct_hdr *hdr = sg_virt(sg);
+       struct gpn_ft_resp_acc *acc = sg_virt(sg);
+       struct zfcp_adapter *adapter = ct->port->adapter;
+       struct zfcp_port *port, *tmp;
+       u32 d_id;
+       int ret = 0, x, last = 0;
+
+       if (ct->status)
+               return -EIO;
+
+       if (hdr->cmd_rsp_code != ZFCP_CT_ACCEPT) {
+               if (hdr->reason_code == ZFCP_CT_UNABLE_TO_PERFORM_CMD)
+                       return -EAGAIN; /* might be a temporary condition */
+               return -EIO;
+       }
+
+       if (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++) {
+               if (x % (ZFCP_GPN_FT_ENTRIES + 1))
+                       acc++;
+               else
+                       acc = sg_virt(++sg);
+
+               last = acc->control & 0x80;
+               d_id = acc->port_id[0] << 16 | acc->port_id[1] << 8 |
+                      acc->port_id[2];
+
+               /* skip the adapter's port and known remote ports */
+               if (acc->wwpn == fc_host_port_name(adapter->scsi_host))
+                       continue;
+               port = zfcp_get_port_by_wwpn(adapter, acc->wwpn);
+               if (port) {
+                       zfcp_port_get(port);
+                       continue;
+               }
+
+               port = zfcp_port_enqueue(adapter, acc->wwpn,
+                                        ZFCP_STATUS_PORT_DID_DID |
+                                        ZFCP_STATUS_COMMON_NOESC, d_id);
+               if (IS_ERR(port))
+                       ret = PTR_ERR(port);
+               else
+                       zfcp_erp_port_reopen(port, 0, 149, NULL);
+       }
+
+       zfcp_erp_wait(adapter);
+       list_for_each_entry_safe(port, tmp, &adapter->port_list_head, list)
+               zfcp_validate_port(port);
+       up(&zfcp_data.config_sema);
+       return ret;
+}
+
+/**
+ * zfcp_scan_ports - scan remote ports and attach new ports
+ * @adapter: pointer to struct zfcp_adapter
+ */
+int zfcp_scan_ports(struct zfcp_adapter *adapter)
+{
+       int ret, i;
+       struct zfcp_gpn_ft *gpn_ft;
+
+       zfcp_erp_wait(adapter); /* wait until adapter is finished with ERP */
+       if (fc_host_port_type(adapter->scsi_host) != FC_PORTTYPE_NPORT)
+               return 0;
+
+       ret = zfcp_scan_get_nameserver(adapter);
+       if (ret)
+               return ret;
+
+       gpn_ft = zfcp_alloc_sg_env();
+       if (!gpn_ft)
+               return -ENOMEM;
+
+       for (i = 0; i < 3; i++) {
+               ret = zfcp_scan_issue_gpn_ft(gpn_ft, adapter);
+               if (!ret) {
+                       ret = zfcp_scan_eval_gpn_ft(gpn_ft);
+                       if (ret == -EAGAIN)
+                               ssleep(1);
+                       else
+                               break;
+               }
+       }
+       zfcp_free_sg_env(gpn_ft);
+
+       return ret;
+}
+
+
+void _zfcp_scan_ports_later(struct work_struct *work)
+{
+       zfcp_scan_ports(container_of(work, struct zfcp_adapter, scan_work));
 }