diff options
Diffstat (limited to 'drivers/scsi/lpfc/lpfc_attr.c')
-rw-r--r-- | drivers/scsi/lpfc/lpfc_attr.c | 80 |
1 files changed, 54 insertions, 26 deletions
diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 2542f1f8bf8..d0ebaeb7ef6 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -23,6 +23,7 @@ #include <linux/delay.h> #include <linux/pci.h> #include <linux/interrupt.h> +#include <linux/module.h> #include <linux/aer.h> #include <linux/gfp.h> #include <linux/kernel.h> @@ -52,6 +53,13 @@ #define LPFC_MIN_DEVLOSS_TMO 1 #define LPFC_MAX_DEVLOSS_TMO 255 +/* + * Write key size should be multiple of 4. If write key is changed + * make sure that library write key is also changed. + */ +#define LPFC_REG_WRITE_KEY_SIZE 4 +#define LPFC_REG_WRITE_KEY "EMLX" + /** * lpfc_jedec_to_ascii - Hex to ascii convertor according to JEDEC rules * @incr: integer to convert. @@ -693,7 +701,7 @@ lpfc_selective_reset(struct lpfc_hba *phba) int rc; if (!phba->cfg_enable_hba_reset) - return -EIO; + return -EACCES; status = lpfc_do_offline(phba, LPFC_EVT_OFFLINE); @@ -742,9 +750,11 @@ lpfc_issue_reset(struct device *dev, struct device_attribute *attr, struct Scsi_Host *shost = class_to_shost(dev); struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata; struct lpfc_hba *phba = vport->phba; - int status = -EINVAL; + if (!phba->cfg_enable_hba_reset) + return -EACCES; + if (strncmp(buf, "selective", sizeof("selective") - 1) == 0) status = phba->lpfc_selective_reset(phba); @@ -765,16 +775,21 @@ lpfc_issue_reset(struct device *dev, struct device_attribute *attr, * Returns: * zero for success **/ -static int +int lpfc_sli4_pdev_status_reg_wait(struct lpfc_hba *phba) { - struct lpfc_register portstat_reg; + struct lpfc_register portstat_reg = {0}; int i; - + msleep(100); lpfc_readl(phba->sli4_hba.u.if_type2.STATUSregaddr, &portstat_reg.word0); + /* verify if privilaged for the request operation */ + if (!bf_get(lpfc_sliport_status_rn, &portstat_reg) && + !bf_get(lpfc_sliport_status_err, &portstat_reg)) + return -EPERM; + /* wait for the SLI port firmware ready after firmware reset */ for (i = 0; i < LPFC_FW_RESET_MAXIMUM_WAIT_10MS_CNT; i++) { msleep(10); @@ -816,16 +831,13 @@ lpfc_sli4_pdev_reg_request(struct lpfc_hba *phba, uint32_t opcode) int rc; if (!phba->cfg_enable_hba_reset) - return -EIO; + return -EACCES; if ((phba->sli_rev < LPFC_SLI_REV4) || (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) != LPFC_SLI_INTF_IF_TYPE_2)) return -EPERM; - if (!pdev->is_physfn) - return -EPERM; - /* Disable SR-IOV virtual functions if enabled */ if (phba->cfg_sriov_nr_virtfn) { pci_disable_sriov(pdev); @@ -858,7 +870,7 @@ lpfc_sli4_pdev_reg_request(struct lpfc_hba *phba, uint32_t opcode) rc = lpfc_sli4_pdev_status_reg_wait(phba); if (rc) - return -EIO; + return rc; init_completion(&online_compl); rc = lpfc_workq_post_event(phba, &status, &online_compl, @@ -984,7 +996,7 @@ lpfc_board_mode_store(struct device *dev, struct device_attribute *attr, if (!status) return strlen(buf); else - return -EIO; + return status; } /** @@ -3885,18 +3897,23 @@ sysfs_ctlreg_write(struct file *filp, struct kobject *kobj, if ((off + count) > FF_REG_AREA_SIZE) return -ERANGE; - if (count == 0) return 0; + if (count <= LPFC_REG_WRITE_KEY_SIZE) + return 0; if (off % 4 || count % 4 || (unsigned long)buf % 4) return -EINVAL; - if (!(vport->fc_flag & FC_OFFLINE_MODE)) { + /* This is to protect HBA registers from accidental writes. */ + if (memcmp(buf, LPFC_REG_WRITE_KEY, LPFC_REG_WRITE_KEY_SIZE)) + return -EINVAL; + + if (!(vport->fc_flag & FC_OFFLINE_MODE)) return -EPERM; - } spin_lock_irq(&phba->hbalock); - for (buf_off = 0; buf_off < count; buf_off += sizeof(uint32_t)) - writel(*((uint32_t *)(buf + buf_off)), + for (buf_off = 0; buf_off < count - LPFC_REG_WRITE_KEY_SIZE; + buf_off += sizeof(uint32_t)) + writel(*((uint32_t *)(buf + buf_off + LPFC_REG_WRITE_KEY_SIZE)), phba->ctrl_regs_memmap_p + off + buf_off); spin_unlock_irq(&phba->hbalock); @@ -4097,8 +4114,10 @@ sysfs_mbox_read(struct file *filp, struct kobject *kobj, struct Scsi_Host *shost = class_to_shost(dev); struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata; struct lpfc_hba *phba = vport->phba; - int rc; + LPFC_MBOXQ_t *mboxq; MAILBOX_t *pmb; + uint32_t mbox_tmo; + int rc; if (off > MAILBOX_CMD_SIZE) return -ERANGE; @@ -4123,7 +4142,8 @@ sysfs_mbox_read(struct file *filp, struct kobject *kobj, if (off == 0 && phba->sysfs_mbox.state == SMBOX_WRITING && phba->sysfs_mbox.offset >= 2 * sizeof(uint32_t)) { - pmb = &phba->sysfs_mbox.mbox->u.mb; + mboxq = (LPFC_MBOXQ_t *)&phba->sysfs_mbox.mbox; + pmb = &mboxq->u.mb; switch (pmb->mbxCommand) { /* Offline only */ case MBX_INIT_LINK: @@ -4233,9 +4253,8 @@ sysfs_mbox_read(struct file *filp, struct kobject *kobj, } else { spin_unlock_irq(&phba->hbalock); - rc = lpfc_sli_issue_mbox_wait (phba, - phba->sysfs_mbox.mbox, - lpfc_mbox_tmo_val(phba, pmb->mbxCommand) * HZ); + mbox_tmo = lpfc_mbox_tmo_val(phba, mboxq); + rc = lpfc_sli_issue_mbox_wait(phba, mboxq, mbox_tmo); spin_lock_irq(&phba->hbalock); } @@ -4480,9 +4499,10 @@ lpfc_get_host_fabric_name (struct Scsi_Host *shost) spin_lock_irq(shost->host_lock); - if ((vport->fc_flag & FC_FABRIC) || - ((phba->fc_topology == LPFC_TOPOLOGY_LOOP) && - (vport->fc_flag & FC_PUBLIC_LOOP))) + if ((vport->port_state > LPFC_FLOGI) && + ((vport->fc_flag & FC_FABRIC) || + ((phba->fc_topology == LPFC_TOPOLOGY_LOOP) && + (vport->fc_flag & FC_PUBLIC_LOOP)))) node_name = wwn_to_u64(phba->fc_fabparam.nodeName.u.wwn); else /* fabric is local port if there is no F/FL_Port */ @@ -4555,9 +4575,17 @@ lpfc_get_stats(struct Scsi_Host *shost) memset(hs, 0, sizeof (struct fc_host_statistics)); hs->tx_frames = pmb->un.varRdStatus.xmitFrameCnt; - hs->tx_words = (pmb->un.varRdStatus.xmitByteCnt * 256); + /* + * The MBX_READ_STATUS returns tx_k_bytes which has to + * converted to words + */ + hs->tx_words = (uint64_t) + ((uint64_t)pmb->un.varRdStatus.xmitByteCnt + * (uint64_t)256); hs->rx_frames = pmb->un.varRdStatus.rcvFrameCnt; - hs->rx_words = (pmb->un.varRdStatus.rcvByteCnt * 256); + hs->rx_words = (uint64_t) + ((uint64_t)pmb->un.varRdStatus.rcvByteCnt + * (uint64_t)256); memset(pmboxq, 0, sizeof (LPFC_MBOXQ_t)); pmb->mbxCommand = MBX_READ_LNK_STAT; |