Merge tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
[linux-2.6-block.git] / drivers / scsi / lpfc / lpfc_attr.c
index 065c526218b2715433ad9bbe3717cb5b3fc816d2..ea62322ffe2bb7e711bc87d521903573550be282 100644 (file)
@@ -176,6 +176,7 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr,
        int i;
        int len = 0;
        char tmp[LPFC_MAX_NVME_INFO_TMP_LEN] = {0};
+       unsigned long iflags = 0;
 
        if (!(vport->cfg_enable_fc4_type & LPFC_ENABLE_NVME)) {
                len = scnprintf(buf, PAGE_SIZE, "NVME Disabled\n");
@@ -354,7 +355,7 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr,
                  phba->sli4_hba.io_xri_max,
                  lpfc_sli4_get_els_iocb_cnt(phba));
        if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
-               goto buffer_done;
+               goto rcu_unlock_buf_done;
 
        /* Port state is only one of two values for now. */
        if (localport->port_id)
@@ -370,15 +371,15 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr,
                  wwn_to_u64(vport->fc_nodename.u.wwn),
                  localport->port_id, statep);
        if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
-               goto buffer_done;
+               goto rcu_unlock_buf_done;
 
        list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) {
                nrport = NULL;
-               spin_lock(&vport->phba->hbalock);
+               spin_lock_irqsave(&vport->phba->hbalock, iflags);
                rport = lpfc_ndlp_get_nrport(ndlp);
                if (rport)
                        nrport = rport->remoteport;
-               spin_unlock(&vport->phba->hbalock);
+               spin_unlock_irqrestore(&vport->phba->hbalock, iflags);
                if (!nrport)
                        continue;
 
@@ -397,39 +398,39 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr,
 
                /* Tab in to show lport ownership. */
                if (strlcat(buf, "NVME RPORT       ", PAGE_SIZE) >= PAGE_SIZE)
-                       goto buffer_done;
+                       goto rcu_unlock_buf_done;
                if (phba->brd_no >= 10) {
                        if (strlcat(buf, " ", PAGE_SIZE) >= PAGE_SIZE)
-                               goto buffer_done;
+                               goto rcu_unlock_buf_done;
                }
 
                scnprintf(tmp, sizeof(tmp), "WWPN x%llx ",
                          nrport->port_name);
                if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
-                       goto buffer_done;
+                       goto rcu_unlock_buf_done;
 
                scnprintf(tmp, sizeof(tmp), "WWNN x%llx ",
                          nrport->node_name);
                if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
-                       goto buffer_done;
+                       goto rcu_unlock_buf_done;
 
                scnprintf(tmp, sizeof(tmp), "DID x%06x ",
                          nrport->port_id);
                if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
-                       goto buffer_done;
+                       goto rcu_unlock_buf_done;
 
                /* An NVME rport can have multiple roles. */
                if (nrport->port_role & FC_PORT_ROLE_NVME_INITIATOR) {
                        if (strlcat(buf, "INITIATOR ", PAGE_SIZE) >= PAGE_SIZE)
-                               goto buffer_done;
+                               goto rcu_unlock_buf_done;
                }
                if (nrport->port_role & FC_PORT_ROLE_NVME_TARGET) {
                        if (strlcat(buf, "TARGET ", PAGE_SIZE) >= PAGE_SIZE)
-                               goto buffer_done;
+                               goto rcu_unlock_buf_done;
                }
                if (nrport->port_role & FC_PORT_ROLE_NVME_DISCOVERY) {
                        if (strlcat(buf, "DISCSRVC ", PAGE_SIZE) >= PAGE_SIZE)
-                               goto buffer_done;
+                               goto rcu_unlock_buf_done;
                }
                if (nrport->port_role & ~(FC_PORT_ROLE_NVME_INITIATOR |
                                          FC_PORT_ROLE_NVME_TARGET |
@@ -437,12 +438,12 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr,
                        scnprintf(tmp, sizeof(tmp), "UNKNOWN ROLE x%x",
                                  nrport->port_role);
                        if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
-                               goto buffer_done;
+                               goto rcu_unlock_buf_done;
                }
 
                scnprintf(tmp, sizeof(tmp), "%s\n", statep);
                if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
-                       goto buffer_done;
+                       goto rcu_unlock_buf_done;
        }
        rcu_read_unlock();
 
@@ -504,7 +505,13 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr,
                  atomic_read(&lport->cmpl_fcp_err));
        strlcat(buf, tmp, PAGE_SIZE);
 
-buffer_done:
+       /* RCU is already unlocked. */
+       goto buffer_done;
+
+ rcu_unlock_buf_done:
+       rcu_read_unlock();
+
+ buffer_done:
        len = strnlen(buf, PAGE_SIZE);
 
        if (unlikely(len >= (PAGE_SIZE - 1))) {
@@ -4951,6 +4958,64 @@ static DEVICE_ATTR(lpfc_req_fw_upgrade, S_IRUGO | S_IWUSR,
                   lpfc_request_firmware_upgrade_show,
                   lpfc_request_firmware_upgrade_store);
 
+/**
+ * lpfc_force_rscn_store
+ *
+ * @dev: class device that is converted into a Scsi_host.
+ * @attr: device attribute, not used.
+ * @buf: unused string
+ * @count: unused variable.
+ *
+ * Description:
+ * Force the switch to send a RSCN to all other NPorts in our zone
+ * If we are direct connect pt2pt, build the RSCN command ourself
+ * and send to the other NPort. Not supported for private loop.
+ *
+ * Returns:
+ * 0      - on success
+ * -EIO   - if command is not sent
+ **/
+static ssize_t
+lpfc_force_rscn_store(struct device *dev, struct device_attribute *attr,
+                     const char *buf, size_t count)
+{
+       struct Scsi_Host *shost = class_to_shost(dev);
+       struct lpfc_vport *vport = (struct lpfc_vport *)shost->hostdata;
+       int i;
+
+       i = lpfc_issue_els_rscn(vport, 0);
+       if (i)
+               return -EIO;
+       return strlen(buf);
+}
+
+/*
+ * lpfc_force_rscn: Force an RSCN to be sent to all remote NPorts
+ * connected to  the HBA.
+ *
+ * Value range is any ascii value
+ */
+static int lpfc_force_rscn;
+module_param(lpfc_force_rscn, int, 0644);
+MODULE_PARM_DESC(lpfc_force_rscn,
+                "Force an RSCN to be sent to all remote NPorts");
+lpfc_param_show(force_rscn)
+
+/**
+ * lpfc_force_rscn_init - Force an RSCN to be sent to all remote NPorts
+ * @phba: lpfc_hba pointer.
+ * @val: unused value.
+ *
+ * Returns:
+ * zero if val saved.
+ **/
+static int
+lpfc_force_rscn_init(struct lpfc_hba *phba, int val)
+{
+       return 0;
+}
+static DEVICE_ATTR_RW(lpfc_force_rscn);
+
 /**
  * lpfc_fcp_imax_store
  *
@@ -5961,6 +6026,7 @@ struct device_attribute *lpfc_hba_attrs[] = {
        &dev_attr_lpfc_nvme_oas,
        &dev_attr_lpfc_nvme_embed_cmd,
        &dev_attr_lpfc_fcp_imax,
+       &dev_attr_lpfc_force_rscn,
        &dev_attr_lpfc_cq_poll_threshold,
        &dev_attr_lpfc_cq_max_proc_limit,
        &dev_attr_lpfc_fcp_cpu_map,
@@ -7008,6 +7074,7 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
        lpfc_nvme_oas_init(phba, lpfc_nvme_oas);
        lpfc_nvme_embed_cmd_init(phba, lpfc_nvme_embed_cmd);
        lpfc_fcp_imax_init(phba, lpfc_fcp_imax);
+       lpfc_force_rscn_init(phba, lpfc_force_rscn);
        lpfc_cq_poll_threshold_init(phba, lpfc_cq_poll_threshold);
        lpfc_cq_max_proc_limit_init(phba, lpfc_cq_max_proc_limit);
        lpfc_fcp_cpu_map_init(phba, lpfc_fcp_cpu_map);