diff options
Diffstat (limited to 'drivers/scsi/lpfc')
-rw-r--r-- | drivers/scsi/lpfc/lpfc.h | 14 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_attr.c | 118 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_bsg.c | 2473 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_bsg.h | 98 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_crtn.h | 22 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_ct.c | 15 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_els.c | 145 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_hbadisc.c | 735 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_hw.h | 23 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_hw4.h | 265 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_init.c | 547 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_mbox.c | 111 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_nl.h | 22 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_nportdisc.c | 85 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_scsi.c | 46 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_scsi.h | 1 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_sli.c | 329 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_sli.h | 10 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_sli4.h | 82 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_version.h | 4 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_vport.c | 7 |
21 files changed, 4263 insertions, 889 deletions
diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index 1cc23a69db5..84b696463a5 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2009 Emulex. All rights reserved. * + * Copyright (C) 2004-2010 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * @@ -315,6 +315,9 @@ struct lpfc_vport { #define FC_VPORT_NEEDS_REG_VPI 0x80000 /* Needs to have its vpi registered */ #define FC_RSCN_DEFERRED 0x100000 /* A deferred RSCN being processed */ #define FC_VPORT_NEEDS_INIT_VPI 0x200000 /* Need to INIT_VPI before FDISC */ +#define FC_VPORT_CVL_RCVD 0x400000 /* VLink failed due to CVL */ +#define FC_VFI_REGISTERED 0x800000 /* VFI is registered */ +#define FC_FDISC_COMPLETED 0x1000000/* FDISC completed */ uint32_t ct_flags; #define FC_CT_RFF_ID 0x1 /* RFF_ID accepted by switch */ @@ -448,6 +451,8 @@ struct unsol_rcv_ct_ctx { uint32_t ctxt_id; uint32_t SID; uint32_t oxid; + uint32_t flags; +#define UNSOL_VALID 0x00000001 }; struct lpfc_hba { @@ -499,6 +504,10 @@ struct lpfc_hba { (struct lpfc_hba *); void (*lpfc_stop_port) (struct lpfc_hba *); + int (*lpfc_hba_init_link) + (struct lpfc_hba *); + int (*lpfc_hba_down_link) + (struct lpfc_hba *); /* SLI4 specific HBA data structure */ @@ -613,6 +622,7 @@ struct lpfc_hba { uint32_t cfg_enable_bg; uint32_t cfg_log_verbose; uint32_t cfg_aer_support; + uint32_t cfg_suppress_link_up; lpfc_vpd_t vpd; /* vital product data */ @@ -790,7 +800,7 @@ struct lpfc_hba { uint16_t vlan_id; struct list_head fcf_conn_rec_list; - struct mutex ct_event_mutex; /* synchronize access to ct_ev_waiters */ + spinlock_t ct_ev_lock; /* synchronize access to ct_ev_waiters */ struct list_head ct_ev_waiters; struct unsol_rcv_ct_ctx ct_ctx[64]; uint32_t ctx_idx; diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 91542f786ed..c992e8328f9 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -482,6 +482,41 @@ lpfc_link_state_show(struct device *dev, struct device_attribute *attr, } /** + * lpfc_link_state_store - Transition the link_state on an HBA port + * @dev: class device that is converted into a Scsi_host. + * @attr: device attribute, not used. + * @buf: one or more lpfc_polling_flags values. + * @count: not used. + * + * Returns: + * -EINVAL if the buffer is not "up" or "down" + * return from link state change function if non-zero + * length of the buf on success + **/ +static ssize_t +lpfc_link_state_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; + struct lpfc_hba *phba = vport->phba; + + int status = -EINVAL; + + if ((strncmp(buf, "up", sizeof("up") - 1) == 0) && + (phba->link_state == LPFC_LINK_DOWN)) + status = phba->lpfc_hba_init_link(phba); + else if ((strncmp(buf, "down", sizeof("down") - 1) == 0) && + (phba->link_state >= LPFC_LINK_UP)) + status = phba->lpfc_hba_down_link(phba); + + if (status == 0) + return strlen(buf); + else + return status; +} + +/** * lpfc_num_discovered_ports_show - Return sum of mapped and unmapped vports * @dev: class device that is converted into a Scsi_host. * @attr: device attribute, not used. @@ -1219,7 +1254,7 @@ lpfc_##attr##_show(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 val = 0;\ + uint val = 0;\ val = phba->cfg_##attr;\ return snprintf(buf, PAGE_SIZE, "%d\n",\ phba->cfg_##attr);\ @@ -1247,7 +1282,7 @@ lpfc_##attr##_show(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 val = 0;\ + uint val = 0;\ val = phba->cfg_##attr;\ return snprintf(buf, PAGE_SIZE, "%#x\n",\ phba->cfg_##attr);\ @@ -1274,7 +1309,7 @@ lpfc_##attr##_show(struct device *dev, struct device_attribute *attr, \ **/ #define lpfc_param_init(attr, default, minval, maxval) \ static int \ -lpfc_##attr##_init(struct lpfc_hba *phba, int val) \ +lpfc_##attr##_init(struct lpfc_hba *phba, uint val) \ { \ if (val >= minval && val <= maxval) {\ phba->cfg_##attr = val;\ @@ -1309,7 +1344,7 @@ lpfc_##attr##_init(struct lpfc_hba *phba, int val) \ **/ #define lpfc_param_set(attr, default, minval, maxval) \ static int \ -lpfc_##attr##_set(struct lpfc_hba *phba, int val) \ +lpfc_##attr##_set(struct lpfc_hba *phba, uint val) \ { \ if (val >= minval && val <= maxval) {\ phba->cfg_##attr = val;\ @@ -1350,7 +1385,7 @@ lpfc_##attr##_store(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 val=0;\ + uint val = 0;\ if (!isdigit(buf[0]))\ return -EINVAL;\ if (sscanf(buf, "%i", &val) != 1)\ @@ -1382,7 +1417,7 @@ lpfc_##attr##_show(struct device *dev, struct device_attribute *attr, \ { \ struct Scsi_Host *shost = class_to_shost(dev);\ struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;\ - int val = 0;\ + uint val = 0;\ val = vport->cfg_##attr;\ return snprintf(buf, PAGE_SIZE, "%d\n", vport->cfg_##attr);\ } @@ -1409,7 +1444,7 @@ lpfc_##attr##_show(struct device *dev, struct device_attribute *attr, \ { \ struct Scsi_Host *shost = class_to_shost(dev);\ struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;\ - int val = 0;\ + uint val = 0;\ val = vport->cfg_##attr;\ return snprintf(buf, PAGE_SIZE, "%#x\n", vport->cfg_##attr);\ } @@ -1434,7 +1469,7 @@ lpfc_##attr##_show(struct device *dev, struct device_attribute *attr, \ **/ #define lpfc_vport_param_init(attr, default, minval, maxval) \ static int \ -lpfc_##attr##_init(struct lpfc_vport *vport, int val) \ +lpfc_##attr##_init(struct lpfc_vport *vport, uint val) \ { \ if (val >= minval && val <= maxval) {\ vport->cfg_##attr = val;\ @@ -1466,7 +1501,7 @@ lpfc_##attr##_init(struct lpfc_vport *vport, int val) \ **/ #define lpfc_vport_param_set(attr, default, minval, maxval) \ static int \ -lpfc_##attr##_set(struct lpfc_vport *vport, int val) \ +lpfc_##attr##_set(struct lpfc_vport *vport, uint val) \ { \ if (val >= minval && val <= maxval) {\ vport->cfg_##attr = val;\ @@ -1502,7 +1537,7 @@ lpfc_##attr##_store(struct device *dev, struct device_attribute *attr, \ { \ struct Scsi_Host *shost = class_to_shost(dev);\ struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;\ - int val=0;\ + uint val = 0;\ if (!isdigit(buf[0]))\ return -EINVAL;\ if (sscanf(buf, "%i", &val) != 1)\ @@ -1515,22 +1550,22 @@ lpfc_##attr##_store(struct device *dev, struct device_attribute *attr, \ #define LPFC_ATTR(name, defval, minval, maxval, desc) \ -static int lpfc_##name = defval;\ -module_param(lpfc_##name, int, 0);\ +static uint lpfc_##name = defval;\ +module_param(lpfc_##name, uint, 0);\ MODULE_PARM_DESC(lpfc_##name, desc);\ lpfc_param_init(name, defval, minval, maxval) #define LPFC_ATTR_R(name, defval, minval, maxval, desc) \ -static int lpfc_##name = defval;\ -module_param(lpfc_##name, int, 0);\ +static uint lpfc_##name = defval;\ +module_param(lpfc_##name, uint, 0);\ MODULE_PARM_DESC(lpfc_##name, desc);\ lpfc_param_show(name)\ lpfc_param_init(name, defval, minval, maxval)\ static DEVICE_ATTR(lpfc_##name, S_IRUGO , lpfc_##name##_show, NULL) #define LPFC_ATTR_RW(name, defval, minval, maxval, desc) \ -static int lpfc_##name = defval;\ -module_param(lpfc_##name, int, 0);\ +static uint lpfc_##name = defval;\ +module_param(lpfc_##name, uint, 0);\ MODULE_PARM_DESC(lpfc_##name, desc);\ lpfc_param_show(name)\ lpfc_param_init(name, defval, minval, maxval)\ @@ -1540,16 +1575,16 @@ static DEVICE_ATTR(lpfc_##name, S_IRUGO | S_IWUSR,\ lpfc_##name##_show, lpfc_##name##_store) #define LPFC_ATTR_HEX_R(name, defval, minval, maxval, desc) \ -static int lpfc_##name = defval;\ -module_param(lpfc_##name, int, 0);\ +static uint lpfc_##name = defval;\ +module_param(lpfc_##name, uint, 0);\ MODULE_PARM_DESC(lpfc_##name, desc);\ lpfc_param_hex_show(name)\ lpfc_param_init(name, defval, minval, maxval)\ static DEVICE_ATTR(lpfc_##name, S_IRUGO , lpfc_##name##_show, NULL) #define LPFC_ATTR_HEX_RW(name, defval, minval, maxval, desc) \ -static int lpfc_##name = defval;\ -module_param(lpfc_##name, int, 0);\ +static uint lpfc_##name = defval;\ +module_param(lpfc_##name, uint, 0);\ MODULE_PARM_DESC(lpfc_##name, desc);\ lpfc_param_hex_show(name)\ lpfc_param_init(name, defval, minval, maxval)\ @@ -1559,22 +1594,22 @@ static DEVICE_ATTR(lpfc_##name, S_IRUGO | S_IWUSR,\ lpfc_##name##_show, lpfc_##name##_store) #define LPFC_VPORT_ATTR(name, defval, minval, maxval, desc) \ -static int lpfc_##name = defval;\ -module_param(lpfc_##name, int, 0);\ +static uint lpfc_##name = defval;\ +module_param(lpfc_##name, uint, 0);\ MODULE_PARM_DESC(lpfc_##name, desc);\ lpfc_vport_param_init(name, defval, minval, maxval) #define LPFC_VPORT_ATTR_R(name, defval, minval, maxval, desc) \ -static int lpfc_##name = defval;\ -module_param(lpfc_##name, int, 0);\ +static uint lpfc_##name = defval;\ +module_param(lpfc_##name, uint, 0);\ MODULE_PARM_DESC(lpfc_##name, desc);\ lpfc_vport_param_show(name)\ lpfc_vport_param_init(name, defval, minval, maxval)\ static DEVICE_ATTR(lpfc_##name, S_IRUGO , lpfc_##name##_show, NULL) #define LPFC_VPORT_ATTR_RW(name, defval, minval, maxval, desc) \ -static int lpfc_##name = defval;\ -module_param(lpfc_##name, int, 0);\ +static uint lpfc_##name = defval;\ +module_param(lpfc_##name, uint, 0);\ MODULE_PARM_DESC(lpfc_##name, desc);\ lpfc_vport_param_show(name)\ lpfc_vport_param_init(name, defval, minval, maxval)\ @@ -1584,16 +1619,16 @@ static DEVICE_ATTR(lpfc_##name, S_IRUGO | S_IWUSR,\ lpfc_##name##_show, lpfc_##name##_store) #define LPFC_VPORT_ATTR_HEX_R(name, defval, minval, maxval, desc) \ -static int lpfc_##name = defval;\ -module_param(lpfc_##name, int, 0);\ +static uint lpfc_##name = defval;\ +module_param(lpfc_##name, uint, 0);\ MODULE_PARM_DESC(lpfc_##name, desc);\ lpfc_vport_param_hex_show(name)\ lpfc_vport_param_init(name, defval, minval, maxval)\ static DEVICE_ATTR(lpfc_##name, S_IRUGO , lpfc_##name##_show, NULL) #define LPFC_VPORT_ATTR_HEX_RW(name, defval, minval, maxval, desc) \ -static int lpfc_##name = defval;\ -module_param(lpfc_##name, int, 0);\ +static uint lpfc_##name = defval;\ +module_param(lpfc_##name, uint, 0);\ MODULE_PARM_DESC(lpfc_##name, desc);\ lpfc_vport_param_hex_show(name)\ lpfc_vport_param_init(name, defval, minval, maxval)\ @@ -1614,7 +1649,8 @@ static DEVICE_ATTR(programtype, S_IRUGO, lpfc_programtype_show, NULL); static DEVICE_ATTR(portnum, S_IRUGO, lpfc_vportnum_show, NULL); static DEVICE_ATTR(fwrev, S_IRUGO, lpfc_fwrev_show, NULL); static DEVICE_ATTR(hdw, S_IRUGO, lpfc_hdw_show, NULL); -static DEVICE_ATTR(link_state, S_IRUGO, lpfc_link_state_show, NULL); +static DEVICE_ATTR(link_state, S_IRUGO | S_IWUSR, lpfc_link_state_show, + lpfc_link_state_store); static DEVICE_ATTR(option_rom_version, S_IRUGO, lpfc_option_rom_version_show, NULL); static DEVICE_ATTR(num_discovered_ports, S_IRUGO, @@ -1897,6 +1933,15 @@ static DEVICE_ATTR(lpfc_enable_npiv, S_IRUGO, lpfc_enable_npiv_show, NULL); /* +# lpfc_suppress_link_up: Bring link up at initialization +# 0x0 = bring link up (issue MBX_INIT_LINK) +# 0x1 = do NOT bring link up at initialization(MBX_INIT_LINK) +# 0x2 = never bring up link +# Default value is 0. +*/ +LPFC_ATTR_R(suppress_link_up, 0, 0, 2, "Suppress Link Up at initialization"); + +/* # lpfc_nodev_tmo: If set, it will hold all I/O errors on devices that disappear # until the timer expires. Value range is [0,255]. Default value is 30. */ @@ -3114,12 +3159,12 @@ LPFC_ATTR_RW(poll_tmo, 10, 1, 255, /* # lpfc_use_msi: Use MSI (Message Signaled Interrupts) in systems that # support this feature -# 0 = MSI disabled (default) +# 0 = MSI disabled # 1 = MSI enabled -# 2 = MSI-X enabled -# Value range is [0,2]. Default value is 0. +# 2 = MSI-X enabled (default) +# Value range is [0,2]. Default value is 2. */ -LPFC_ATTR_R(use_msi, 0, 0, 2, "Use Message Signaled Interrupts (1) or " +LPFC_ATTR_R(use_msi, 2, 0, 2, "Use Message Signaled Interrupts (1) or " "MSI-X (2), if possible"); /* @@ -3278,6 +3323,7 @@ struct device_attribute *lpfc_hba_attrs[] = { &dev_attr_lpfc_prot_sg_seg_cnt, &dev_attr_lpfc_aer_support, &dev_attr_lpfc_aer_state_cleanup, + &dev_attr_lpfc_suppress_link_up, NULL, }; @@ -4456,7 +4502,7 @@ lpfc_get_cfgparam(struct lpfc_hba *phba) lpfc_hba_queue_depth_init(phba, lpfc_hba_queue_depth); lpfc_hba_log_verbose_init(phba, lpfc_log_verbose); lpfc_aer_support_init(phba, lpfc_aer_support); - + lpfc_suppress_link_up_init(phba, lpfc_suppress_link_up); return; } diff --git a/drivers/scsi/lpfc/lpfc_bsg.c b/drivers/scsi/lpfc/lpfc_bsg.c index a5d9048235d..f3f1bf1a0a7 100644 --- a/drivers/scsi/lpfc/lpfc_bsg.c +++ b/drivers/scsi/lpfc/lpfc_bsg.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2009 Emulex. All rights reserved. * + * Copyright (C) 2009-2010 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * * @@ -21,6 +21,7 @@ #include <linux/interrupt.h> #include <linux/mempool.h> #include <linux/pci.h> +#include <linux/delay.h> #include <scsi/scsi.h> #include <scsi/scsi_host.h> @@ -33,6 +34,7 @@ #include "lpfc_sli.h" #include "lpfc_sli4.h" #include "lpfc_nl.h" +#include "lpfc_bsg.h" #include "lpfc_disc.h" #include "lpfc_scsi.h" #include "lpfc.h" @@ -41,14 +43,183 @@ #include "lpfc_vport.h" #include "lpfc_version.h" +struct lpfc_bsg_event { + struct list_head node; + struct kref kref; + wait_queue_head_t wq; + + /* Event type and waiter identifiers */ + uint32_t type_mask; + uint32_t req_id; + uint32_t reg_id; + + /* next two flags are here for the auto-delete logic */ + unsigned long wait_time_stamp; + int waiting; + + /* seen and not seen events */ + struct list_head events_to_get; + struct list_head events_to_see; + + /* job waiting for this event to finish */ + struct fc_bsg_job *set_job; +}; + +struct lpfc_bsg_iocb { + struct lpfc_iocbq *cmdiocbq; + struct lpfc_iocbq *rspiocbq; + struct lpfc_dmabuf *bmp; + struct lpfc_nodelist *ndlp; + + /* job waiting for this iocb to finish */ + struct fc_bsg_job *set_job; +}; + +struct lpfc_bsg_mbox { + LPFC_MBOXQ_t *pmboxq; + MAILBOX_t *mb; + + /* job waiting for this mbox command to finish */ + struct fc_bsg_job *set_job; +}; + +#define TYPE_EVT 1 +#define TYPE_IOCB 2 +#define TYPE_MBOX 3 +struct bsg_job_data { + uint32_t type; + union { + struct lpfc_bsg_event *evt; + struct lpfc_bsg_iocb iocb; + struct lpfc_bsg_mbox mbox; + } context_un; +}; + +struct event_data { + struct list_head node; + uint32_t type; + uint32_t immed_dat; + void *data; + uint32_t len; +}; + +#define BUF_SZ_4K 4096 +#define SLI_CT_ELX_LOOPBACK 0x10 + +enum ELX_LOOPBACK_CMD { + ELX_LOOPBACK_XRI_SETUP, + ELX_LOOPBACK_DATA, +}; + +#define ELX_LOOPBACK_HEADER_SZ \ + (size_t)(&((struct lpfc_sli_ct_request *)NULL)->un) + +struct lpfc_dmabufext { + struct lpfc_dmabuf dma; + uint32_t size; + uint32_t flag; +}; + +/** + * lpfc_bsg_send_mgmt_cmd_cmp - lpfc_bsg_send_mgmt_cmd's completion handler + * @phba: Pointer to HBA context object. + * @cmdiocbq: Pointer to command iocb. + * @rspiocbq: Pointer to response iocb. + * + * This function is the completion handler for iocbs issued using + * lpfc_bsg_send_mgmt_cmd function. This function is called by the + * ring event handler function without any lock held. This function + * can be called from both worker thread context and interrupt + * context. This function also can be called from another thread which + * cleans up the SLI layer objects. + * This function copies the contents of the response iocb to the + * response iocb memory object provided by the caller of + * lpfc_sli_issue_iocb_wait and then wakes up the thread which + * sleeps for the iocb completion. + **/ +static void +lpfc_bsg_send_mgmt_cmd_cmp(struct lpfc_hba *phba, + struct lpfc_iocbq *cmdiocbq, + struct lpfc_iocbq *rspiocbq) +{ + unsigned long iflags; + struct bsg_job_data *dd_data; + struct fc_bsg_job *job; + IOCB_t *rsp; + struct lpfc_dmabuf *bmp; + struct lpfc_nodelist *ndlp; + struct lpfc_bsg_iocb *iocb; + unsigned long flags; + int rc = 0; + + spin_lock_irqsave(&phba->ct_ev_lock, flags); + dd_data = cmdiocbq->context1; + if (!dd_data) { + spin_unlock_irqrestore(&phba->ct_ev_lock, flags); + return; + } + + iocb = &dd_data->context_un.iocb; + job = iocb->set_job; + job->dd_data = NULL; /* so timeout handler does not reply */ + + spin_lock_irqsave(&phba->hbalock, iflags); + cmdiocbq->iocb_flag |= LPFC_IO_WAKE; + if (cmdiocbq->context2 && rspiocbq) + memcpy(&((struct lpfc_iocbq *)cmdiocbq->context2)->iocb, + &rspiocbq->iocb, sizeof(IOCB_t)); + spin_unlock_irqrestore(&phba->hbalock, iflags); + + bmp = iocb->bmp; + rspiocbq = iocb->rspiocbq; + rsp = &rspiocbq->iocb; + ndlp = iocb->ndlp; + + pci_unmap_sg(phba->pcidev, job->request_payload.sg_list, + job->request_payload.sg_cnt, DMA_TO_DEVICE); + pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list, + job->reply_payload.sg_cnt, DMA_FROM_DEVICE); + + if (rsp->ulpStatus) { + if (rsp->ulpStatus == IOSTAT_LOCAL_REJECT) { + switch (rsp->un.ulpWord[4] & 0xff) { + case IOERR_SEQUENCE_TIMEOUT: + rc = -ETIMEDOUT; + break; + case IOERR_INVALID_RPI: + rc = -EFAULT; + break; + default: + rc = -EACCES; + break; + } + } else + rc = -EACCES; + } else + job->reply->reply_payload_rcv_len = + rsp->un.genreq64.bdl.bdeSize; + + lpfc_mbuf_free(phba, bmp->virt, bmp->phys); + lpfc_sli_release_iocbq(phba, rspiocbq); + lpfc_sli_release_iocbq(phba, cmdiocbq); + lpfc_nlp_put(ndlp); + kfree(bmp); + kfree(dd_data); + /* make error code available to userspace */ + job->reply->result = rc; + /* complete the job back to userspace */ + job->job_done(job); + spin_unlock_irqrestore(&phba->ct_ev_lock, flags); + return; +} + /** - * lpfc_bsg_rport_ct - send a CT command from a bsg request + * lpfc_bsg_send_mgmt_cmd - send a CT command from a bsg request * @job: fc_bsg_job to handle - */ + **/ static int -lpfc_bsg_rport_ct(struct fc_bsg_job *job) +lpfc_bsg_send_mgmt_cmd(struct fc_bsg_job *job) { - struct Scsi_Host *shost = job->shost; struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata; struct lpfc_hba *phba = vport->phba; struct lpfc_rport_data *rdata = job->rport->dd_data; @@ -65,57 +236,60 @@ lpfc_bsg_rport_ct(struct fc_bsg_job *job) struct scatterlist *sgel = NULL; int numbde; dma_addr_t busaddr; + struct bsg_job_data *dd_data; + uint32_t creg_val; int rc = 0; /* in case no data is transferred */ job->reply->reply_payload_rcv_len = 0; + /* allocate our bsg tracking structure */ + dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL); + if (!dd_data) { + lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, + "2733 Failed allocation of dd_data\n"); + rc = -ENOMEM; + goto no_dd_data; + } + if (!lpfc_nlp_get(ndlp)) { - job->reply->result = -ENODEV; - return 0; + rc = -ENODEV; + goto no_ndlp; + } + + bmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL); + if (!bmp) { + rc = -ENOMEM; + goto free_ndlp; } if (ndlp->nlp_flag & NLP_ELS_SND_MASK) { rc = -ENODEV; - goto free_ndlp_exit; + goto free_bmp; } - spin_lock_irq(shost->host_lock); cmdiocbq = lpfc_sli_get_iocbq(phba); if (!cmdiocbq) { rc = -ENOMEM; - spin_unlock_irq(shost->host_lock); - goto free_ndlp_exit; + goto free_bmp; } - cmd = &cmdiocbq->iocb; + cmd = &cmdiocbq->iocb; rspiocbq = lpfc_sli_get_iocbq(phba); if (!rspiocbq) { rc = -ENOMEM; goto free_cmdiocbq; } - spin_unlock_irq(shost->host_lock); rsp = &rspiocbq->iocb; - - bmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL); - if (!bmp) { - rc = -ENOMEM; - spin_lock_irq(shost->host_lock); - goto free_rspiocbq; - } - - spin_lock_irq(shost->host_lock); bmp->virt = lpfc_mbuf_alloc(phba, 0, &bmp->phys); if (!bmp->virt) { rc = -ENOMEM; - goto free_bmp; + goto free_rspiocbq; } - spin_unlock_irq(shost->host_lock); INIT_LIST_HEAD(&bmp->list); bpl = (struct ulp_bde64 *) bmp->virt; - request_nseg = pci_map_sg(phba->pcidev, job->request_payload.sg_list, job->request_payload.sg_cnt, DMA_TO_DEVICE); for_each_sg(job->request_payload.sg_list, sgel, request_nseg, numbde) { @@ -157,78 +331,152 @@ lpfc_bsg_rport_ct(struct fc_bsg_job *job) cmd->ulpContext = ndlp->nlp_rpi; cmd->ulpOwner = OWN_CHIP; cmdiocbq->vport = phba->pport; - cmdiocbq->context1 = NULL; - cmdiocbq->context2 = NULL; + cmdiocbq->context3 = bmp; cmdiocbq->iocb_flag |= LPFC_IO_LIBDFC; - timeout = phba->fc_ratov * 2; - job->dd_data = cmdiocbq; - - rc = lpfc_sli_issue_iocb_wait(phba, LPFC_ELS_RING, cmdiocbq, rspiocbq, - timeout + LPFC_DRVR_TIMEOUT); - - if (rc != IOCB_TIMEDOUT) { - pci_unmap_sg(phba->pcidev, job->request_payload.sg_list, - job->request_payload.sg_cnt, DMA_TO_DEVICE); - pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list, - job->reply_payload.sg_cnt, DMA_FROM_DEVICE); + cmd->ulpTimeout = timeout; + + cmdiocbq->iocb_cmpl = lpfc_bsg_send_mgmt_cmd_cmp; + cmdiocbq->context1 = dd_data; + cmdiocbq->context2 = rspiocbq; + dd_data->type = TYPE_IOCB; + dd_data->context_un.iocb.cmdiocbq = cmdiocbq; + dd_data->context_un.iocb.rspiocbq = rspiocbq; + dd_data->context_un.iocb.set_job = job; + dd_data->context_un.iocb.bmp = bmp; + dd_data->context_un.iocb.ndlp = ndlp; + + if (phba->cfg_poll & DISABLE_FCP_RING_INT) { + creg_val = readl(phba->HCregaddr); + creg_val |= (HC_R0INT_ENA << LPFC_FCP_RING); + writel(creg_val, phba->HCregaddr); + readl(phba->HCregaddr); /* flush */ } - if (rc == IOCB_TIMEDOUT) { - lpfc_sli_release_iocbq(phba, rspiocbq); - rc = -EACCES; - goto free_ndlp_exit; - } + rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, cmdiocbq, 0); - if (rc != IOCB_SUCCESS) { - rc = -EACCES; - goto free_outdmp; - } + if (rc == IOCB_SUCCESS) + return 0; /* done for now */ - if (rsp->ulpStatus) { - if (rsp->ulpStatus == IOSTAT_LOCAL_REJECT) { - switch (rsp->un.ulpWord[4] & 0xff) { - case IOERR_SEQUENCE_TIMEOUT: - rc = -ETIMEDOUT; - break; - case IOERR_INVALID_RPI: - rc = -EFAULT; - break; - default: - rc = -EACCES; - break; - } - goto free_outdmp; - } - } else - job->reply->reply_payload_rcv_len = - rsp->un.genreq64.bdl.bdeSize; + /* iocb failed so cleanup */ + pci_unmap_sg(phba->pcidev, job->request_payload.sg_list, + job->request_payload.sg_cnt, DMA_TO_DEVICE); + pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list, + job->reply_payload.sg_cnt, DMA_FROM_DEVICE); -free_outdmp: - spin_lock_irq(shost->host_lock); lpfc_mbuf_free(phba, bmp->virt, bmp->phys); -free_bmp: - kfree(bmp); + free_rspiocbq: lpfc_sli_release_iocbq(phba, rspiocbq); free_cmdiocbq: lpfc_sli_release_iocbq(phba, cmdiocbq); - spin_unlock_irq(shost->host_lock); -free_ndlp_exit: +free_bmp: + kfree(bmp); +free_ndlp: lpfc_nlp_put(ndlp); +no_ndlp: + kfree(dd_data); +no_dd_data: + /* make error code available to userspace */ + job->reply->result = rc; + job->dd_data = NULL; + return rc; +} + +/** + * lpfc_bsg_rport_els_cmp - lpfc_bsg_rport_els's completion handler + * @phba: Pointer to HBA context object. + * @cmdiocbq: Pointer to command iocb. + * @rspiocbq: Pointer to response iocb. + * + * This function is the completion handler for iocbs issued using + * lpfc_bsg_rport_els_cmp function. This function is called by the + * ring event handler function without any lock held. This function + * can be called from both worker thread context and interrupt + * context. This function also can be called from other thread which + * cleans up the SLI layer objects. + * This function copies the contents of the response iocb to the + * response iocb memory object provided by the caller of + * lpfc_sli_issue_iocb_wait and then wakes up the thread which + * sleeps for the iocb completion. + **/ +static void +lpfc_bsg_rport_els_cmp(struct lpfc_hba *phba, + struct lpfc_iocbq *cmdiocbq, + struct lpfc_iocbq *rspiocbq) +{ + struct bsg_job_data *dd_data; + struct fc_bsg_job *job; + IOCB_t *rsp; + struct lpfc_nodelist *ndlp; + struct lpfc_dmabuf *pbuflist = NULL; + struct fc_bsg_ctels_reply *els_reply; + uint8_t *rjt_data; + unsigned long flags; + int rc = 0; + + spin_lock_irqsave(&phba->ct_ev_lock, flags); + dd_data = cmdiocbq->context1; + /* normal completion and timeout crossed paths, already done */ + if (!dd_data) { + spin_unlock_irqrestore(&phba->hbalock, flags); + return; + } + + cmdiocbq->iocb_flag |= LPFC_IO_WAKE; + if (cmdiocbq->context2 && rspiocbq) + memcpy(&((struct lpfc_iocbq *)cmdiocbq->context2)->iocb, + &rspiocbq->iocb, sizeof(IOCB_t)); + + job = dd_data->context_un.iocb.set_job; + cmdiocbq = dd_data->context_un.iocb.cmdiocbq; + rspiocbq = dd_data->context_un.iocb.rspiocbq; + rsp = &rspiocbq->iocb; + ndlp = dd_data->context_un.iocb.ndlp; + + pci_unmap_sg(phba->pcidev, job->request_payload.sg_list, + job->request_payload.sg_cnt, DMA_TO_DEVICE); + pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list, + job->reply_payload.sg_cnt, DMA_FROM_DEVICE); + if (job->reply->result == -EAGAIN) + rc = -EAGAIN; + else if (rsp->ulpStatus == IOSTAT_SUCCESS) + job->reply->reply_payload_rcv_len = + rsp->un.elsreq64.bdl.bdeSize; + else if (rsp->ulpStatus == IOSTAT_LS_RJT) { + job->reply->reply_payload_rcv_len = + sizeof(struct fc_bsg_ctels_reply); + /* LS_RJT data returned in word 4 */ + rjt_data = (uint8_t *)&rsp->un.ulpWord[4]; + els_reply = &job->reply->reply_data.ctels_reply; + els_reply->status = FC_CTELS_STATUS_REJECT; + els_reply->rjt_data.action = rjt_data[3]; + els_reply->rjt_data.reason_code = rjt_data[2]; + els_reply->rjt_data.reason_explanation = rjt_data[1]; + els_reply->rjt_data.vendor_unique = rjt_data[0]; + } else + rc = -EIO; + + pbuflist = (struct lpfc_dmabuf *) cmdiocbq->context3; + lpfc_mbuf_free(phba, pbuflist->virt, pbuflist->phys); + lpfc_sli_release_iocbq(phba, rspiocbq); + lpfc_sli_release_iocbq(phba, cmdiocbq); + lpfc_nlp_put(ndlp); + kfree(dd_data); /* make error code available to userspace */ job->reply->result = rc; + job->dd_data = NULL; /* complete the job back to userspace */ job->job_done(job); - - return 0; + spin_unlock_irqrestore(&phba->ct_ev_lock, flags); + return; } /** * lpfc_bsg_rport_els - send an ELS command from a bsg request * @job: fc_bsg_job to handle - */ + **/ static int lpfc_bsg_rport_els(struct fc_bsg_job *job) { @@ -236,7 +484,6 @@ lpfc_bsg_rport_els(struct fc_bsg_job *job) struct lpfc_hba *phba = vport->phba; struct lpfc_rport_data *rdata = job->rport->dd_data; struct lpfc_nodelist *ndlp = rdata->pnode; - uint32_t elscmd; uint32_t cmdsize; uint32_t rspsize; @@ -248,20 +495,30 @@ lpfc_bsg_rport_els(struct fc_bsg_job *job) struct lpfc_dmabuf *prsp; struct lpfc_dmabuf *pbuflist = NULL; struct ulp_bde64 *bpl; - int iocb_status; int request_nseg; int reply_nseg; struct scatterlist *sgel = NULL; int numbde; dma_addr_t busaddr; + struct bsg_job_data *dd_data; + uint32_t creg_val; int rc = 0; /* in case no data is transferred */ job->reply->reply_payload_rcv_len = 0; + /* allocate our bsg tracking structure */ + dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL); + if (!dd_data) { + lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, + "2735 Failed allocation of dd_data\n"); + rc = -ENOMEM; + goto no_dd_data; + } + if (!lpfc_nlp_get(ndlp)) { rc = -ENODEV; - goto out; + goto free_dd_data; } elscmd = job->request->rqst_data.r_els.els_code; @@ -271,24 +528,24 @@ lpfc_bsg_rport_els(struct fc_bsg_job *job) if (!rspiocbq) { lpfc_nlp_put(ndlp); rc = -ENOMEM; - goto out; + goto free_dd_data; } rsp = &rspiocbq->iocb; rpi = ndlp->nlp_rpi; - cmdiocbq = lpfc_prep_els_iocb(phba->pport, 1, cmdsize, 0, ndlp, + cmdiocbq = lpfc_prep_els_iocb(vport, 1, cmdsize, 0, ndlp, ndlp->nlp_DID, elscmd); - if (!cmdiocbq) { - lpfc_sli_release_iocbq(phba, rspiocbq); - return -EIO; + rc = -EIO; + goto free_rspiocbq; } - job->dd_data = cmdiocbq; + /* prep els iocb set context1 to the ndlp, context2 to the command + * dmabuf, context3 holds the data dmabuf + */ pcmd = (struct lpfc_dmabuf *) cmdiocbq->context2; prsp = (struct lpfc_dmabuf *) pcmd->list.next; - lpfc_mbuf_free(phba, pcmd->virt, pcmd->phys); kfree(pcmd); lpfc_mbuf_free(phba, prsp->virt, prsp->phys); @@ -300,7 +557,6 @@ lpfc_bsg_rport_els(struct fc_bsg_job *job) request_nseg = pci_map_sg(phba->pcidev, job->request_payload.sg_list, job->request_payload.sg_cnt, DMA_TO_DEVICE); - for_each_sg(job->request_payload.sg_list, sgel, request_nseg, numbde) { busaddr = sg_dma_address(sgel); bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64; @@ -322,7 +578,6 @@ lpfc_bsg_rport_els(struct fc_bsg_job *job) bpl->addrHigh = cpu_to_le32(putPaddrHigh(busaddr)); bpl++; } - cmdiocbq->iocb.un.elsreq64.bdl.bdeSize = (request_nseg + reply_nseg) * sizeof(struct ulp_bde64); cmdiocbq->iocb.ulpContext = rpi; @@ -330,102 +585,62 @@ lpfc_bsg_rport_els(struct fc_bsg_job *job) cmdiocbq->context1 = NULL; cmdiocbq->context2 = NULL; - iocb_status = lpfc_sli_issue_iocb_wait(phba, LPFC_ELS_RING, cmdiocbq, - rspiocbq, (phba->fc_ratov * 2) - + LPFC_DRVR_TIMEOUT); - - /* release the new ndlp once the iocb completes */ - lpfc_nlp_put(ndlp); - if (iocb_status != IOCB_TIMEDOUT) { - pci_unmap_sg(phba->pcidev, job->request_payload.sg_list, - job->request_payload.sg_cnt, DMA_TO_DEVICE); - pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list, - job->reply_payload.sg_cnt, DMA_FROM_DEVICE); + cmdiocbq->iocb_cmpl = lpfc_bsg_rport_els_cmp; + cmdiocbq->context1 = dd_data; + cmdiocbq->context2 = rspiocbq; + dd_data->type = TYPE_IOCB; + dd_data->context_un.iocb.cmdiocbq = cmdiocbq; + dd_data->context_un.iocb.rspiocbq = rspiocbq; + dd_data->context_un.iocb.set_job = job; + dd_data->context_un.iocb.bmp = NULL;; + dd_data->context_un.iocb.ndlp = ndlp; + + if (phba->cfg_poll & DISABLE_FCP_RING_INT) { + creg_val = readl(phba->HCregaddr); + creg_val |= (HC_R0INT_ENA << LPFC_FCP_RING); + writel(creg_val, phba->HCregaddr); + readl(phba->HCregaddr); /* flush */ } + rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, cmdiocbq, 0); + lpfc_nlp_put(ndlp); + if (rc == IOCB_SUCCESS) + return 0; /* done for now */ - if (iocb_status == IOCB_SUCCESS) { - if (rsp->ulpStatus == IOSTAT_SUCCESS) { - job->reply->reply_payload_rcv_len = - rsp->un.elsreq64.bdl.bdeSize; - rc = 0; - } else if (rsp->ulpStatus == IOSTAT_LS_RJT) { - struct fc_bsg_ctels_reply *els_reply; - /* LS_RJT data returned in word 4 */ - uint8_t *rjt_data = (uint8_t *)&rsp->un.ulpWord[4]; - - els_reply = &job->reply->reply_data.ctels_reply; - job->reply->result = 0; - els_reply->status = FC_CTELS_STATUS_REJECT; - els_reply->rjt_data.action = rjt_data[0]; - els_reply->rjt_data.reason_code = rjt_data[1]; - els_reply->rjt_data.reason_explanation = rjt_data[2]; - els_reply->rjt_data.vendor_unique = rjt_data[3]; - } else - rc = -EIO; - } else - rc = -EIO; + pci_unmap_sg(phba->pcidev, job->request_payload.sg_list, + job->request_payload.sg_cnt, DMA_TO_DEVICE); + pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list, + job->reply_payload.sg_cnt, DMA_FROM_DEVICE); + + lpfc_mbuf_free(phba, pbuflist->virt, pbuflist->phys); - if (iocb_status != IOCB_TIMEDOUT) - lpfc_els_free_iocb(phba, cmdiocbq); + lpfc_sli_release_iocbq(phba, cmdiocbq); +free_rspiocbq: lpfc_sli_release_iocbq(phba, rspiocbq); -out: +free_dd_data: + kfree(dd_data); + +no_dd_data: /* make error code available to userspace */ job->reply->result = rc; - /* complete the job back to userspace */ - job->job_done(job); - - return 0; -} - -struct lpfc_ct_event { - struct list_head node; - int ref; - wait_queue_head_t wq; - - /* Event type and waiter identifiers */ - uint32_t type_mask; - uint32_t req_id; - uint32_t reg_id; - - /* next two flags are here for the auto-delete logic */ - unsigned long wait_time_stamp; - int waiting; - - /* seen and not seen events */ - struct list_head events_to_get; - struct list_head events_to_see; -}; - -struct event_data { - struct list_head node; - uint32_t type; - uint32_t immed_dat; - void *data; - uint32_t len; -}; - -static struct lpfc_ct_event * -lpfc_ct_event_new(int ev_reg_id, uint32_t ev_req_id) -{ - struct lpfc_ct_event *evt = kzalloc(sizeof(*evt), GFP_KERNEL); - if (!evt) - return NULL; - - INIT_LIST_HEAD(&evt->events_to_get); - INIT_LIST_HEAD(&evt->events_to_see); - evt->req_id = ev_req_id; - evt->reg_id = ev_reg_id; - evt->wait_time_stamp = jiffies; - init_waitqueue_head(&evt->wq); - - return evt; + job->dd_data = NULL; + return rc; } +/** + * lpfc_bsg_event_free - frees an allocated event structure + * @kref: Pointer to a kref. + * + * Called from kref_put. Back cast the kref into an event structure address. + * Free any events to get, delete associated nodes, free any events to see, + * free any data then free the event itself. + **/ static void -lpfc_ct_event_free(struct lpfc_ct_event *evt) +lpfc_bsg_event_free(struct kref *kref) { + struct lpfc_bsg_event *evt = container_of(kref, struct lpfc_bsg_event, + kref); struct event_data *ed; list_del(&evt->node); @@ -447,25 +662,82 @@ lpfc_ct_event_free(struct lpfc_ct_event *evt) kfree(evt); } +/** + * lpfc_bsg_event_ref - increments the kref for an event + * @evt: Pointer to an event structure. + **/ static inline void -lpfc_ct_event_ref(struct lpfc_ct_event *evt) +lpfc_bsg_event_ref(struct lpfc_bsg_event *evt) { - evt->ref++; + kref_get(&evt->kref); } +/** + * lpfc_bsg_event_unref - Uses kref_put to free an event structure + * @evt: Pointer to an event structure. + **/ static inline void -lpfc_ct_event_unref(struct lpfc_ct_event *evt) +lpfc_bsg_event_unref(struct lpfc_bsg_event *evt) { - if (--evt->ref < 0) - lpfc_ct_event_free(evt); + kref_put(&evt->kref, lpfc_bsg_event_free); } -#define SLI_CT_ELX_LOOPBACK 0x10 +/** + * lpfc_bsg_event_new - allocate and initialize a event structure + * @ev_mask: Mask of events. + * @ev_reg_id: Event reg id. + * @ev_req_id: Event request id. + **/ +static struct lpfc_bsg_event * +lpfc_bsg_event_new(uint32_t ev_mask, int ev_reg_id, uint32_t ev_req_id) +{ + struct lpfc_bsg_event *evt = kzalloc(sizeof(*evt), GFP_KERNEL); -enum ELX_LOOPBACK_CMD { - ELX_LOOPBACK_XRI_SETUP, - ELX_LOOPBACK_DATA, -}; + if (!evt) + return NULL; + + INIT_LIST_HEAD(&evt->events_to_get); + INIT_LIST_HEAD(&evt->events_to_see); + evt->type_mask = ev_mask; + evt->req_id = ev_req_id; + evt->reg_id = ev_reg_id; + evt->wait_time_stamp = jiffies; + init_waitqueue_head(&evt->wq); + kref_init(&evt->kref); + return evt; +} + +/** + * diag_cmd_data_free - Frees an lpfc dma buffer extension + * @phba: Pointer to HBA context object. + * @mlist: Pointer to an lpfc dma buffer extension. + **/ +static int +diag_cmd_data_free(struct lpfc_hba *phba, struct lpfc_dmabufext *mlist) +{ + struct lpfc_dmabufext *mlast; + struct pci_dev *pcidev; + struct list_head head, *curr, *next; + + if ((!mlist) || (!lpfc_is_link_up(phba) && + (phba->link_flag & LS_LOOPBACK_MODE))) { + return 0; + } + + pcidev = phba->pcidev; + list_add_tail(&head, &mlist->dma.list); + + list_for_each_safe(curr, next, &head) { + mlast = list_entry(curr, struct lpfc_dmabufext , dma.list); + if (mlast->dma.virt) + dma_free_coherent(&pcidev->dev, + mlast->size, + mlast->dma.virt, + mlast->dma.phys); + kfree(mlast); + } + return 0; +} /** * lpfc_bsg_ct_unsol_event - process an unsolicited CT command @@ -474,9 +746,9 @@ enum ELX_LOOPBACK_CMD { * @piocbq: * * This function is called when an unsolicited CT command is received. It - * forwards the event to any processes registerd to receive CT events. - */ -void + * forwards the event to any processes registered to receive CT events. + **/ +int lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, struct lpfc_iocbq *piocbq) { @@ -484,7 +756,7 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, uint32_t cmd; uint32_t len; struct lpfc_dmabuf *dmabuf = NULL; - struct lpfc_ct_event *evt; + struct lpfc_bsg_event *evt; struct event_data *evt_dat = NULL; struct lpfc_iocbq *iocbq; size_t offset = 0; @@ -496,6 +768,9 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, struct lpfc_dmabuf *bdeBuf2 = piocbq->context3; struct lpfc_hbq_entry *hbqe; struct lpfc_sli_ct_request *ct_req; + struct fc_bsg_job *job = NULL; + unsigned long flags; + int size = 0; INIT_LIST_HEAD(&head); list_add_tail(&head, &piocbq->list); @@ -504,6 +779,10 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, piocbq->iocb.un.cont64[0].tus.f.bdeSize == 0) goto error_ct_unsol_exit; + if (phba->link_state == LPFC_HBA_ERROR || + (!(phba->sli.sli_flag & LPFC_SLI_ACTIVE))) + goto error_ct_unsol_exit; + if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) dmabuf = bdeBuf1; else { @@ -511,7 +790,8 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, piocbq->iocb.un.cont64[0].addrLow); dmabuf = lpfc_sli_ringpostbuf_get(phba, pring, dma_addr); } - + if (dmabuf == NULL) + goto error_ct_unsol_exit; ct_req = (struct lpfc_sli_ct_request *)dmabuf->virt; evt_req_id = ct_req->FsType; cmd = ct_req->CommandResponse.bits.CmdRsp; @@ -519,24 +799,24 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, if (!(phba->sli3_options & LPFC_SLI3_HBQ_ENABLED)) lpfc_sli_ringpostbuf_put(phba, pring, dmabuf); - mutex_lock(&phba->ct_event_mutex); + spin_lock_irqsave(&phba->ct_ev_lock, flags); list_for_each_entry(evt, &phba->ct_ev_waiters, node) { - if (evt->req_id != evt_req_id) + if (!(evt->type_mask & FC_REG_CT_EVENT) || + evt->req_id != evt_req_id) continue; - lpfc_ct_event_ref(evt); - + lpfc_bsg_event_ref(evt); + spin_unlock_irqrestore(&phba->ct_ev_lock, flags); evt_dat = kzalloc(sizeof(*evt_dat), GFP_KERNEL); - if (!evt_dat) { - lpfc_ct_event_unref(evt); + if (evt_dat == NULL) { + spin_lock_irqsave(&phba->ct_ev_lock, flags); + lpfc_bsg_event_unref(evt); lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, "2614 Memory allocation failed for " "CT event\n"); break; } - mutex_unlock(&phba->ct_event_mutex); - if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) { /* take accumulated byte count from the last iocbq */ iocbq = list_entry(head.prev, typeof(*iocbq), list); @@ -550,25 +830,25 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, } evt_dat->data = kzalloc(evt_dat->len, GFP_KERNEL); - if (!evt_dat->data) { + if (evt_dat->data == NULL) { lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, "2615 Memory allocation failed for " "CT event data, size %d\n", evt_dat->len); kfree(evt_dat); - mutex_lock(&phba->ct_event_mutex); - lpfc_ct_event_unref(evt); - mutex_unlock(&phba->ct_event_mutex); + spin_lock_irqsave(&phba->ct_ev_lock, flags); + lpfc_bsg_event_unref(evt); + spin_unlock_irqrestore(&phba->ct_ev_lock, flags); goto error_ct_unsol_exit; } list_for_each_entry(iocbq, &head, list) { + size = 0; if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) { bdeBuf1 = iocbq->context2; bdeBuf2 = iocbq->context3; } for (i = 0; i < iocbq->iocb.ulpBdeCount; i++) { - int size = 0; if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) { if (i == 0) { @@ -601,9 +881,11 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, iocbq); kfree(evt_dat->data); kfree(evt_dat); - mutex_lock(&phba->ct_event_mutex); - lpfc_ct_event_unref(evt); - mutex_unlock(&phba->ct_event_mutex); + spin_lock_irqsave(&phba->ct_ev_lock, + flags); + lpfc_bsg_event_unref(evt); + spin_unlock_irqrestore( + &phba->ct_ev_lock, flags); goto error_ct_unsol_exit; } memcpy((char *)(evt_dat->data) + offset, @@ -616,15 +898,24 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, dmabuf); } else { switch (cmd) { + case ELX_LOOPBACK_DATA: + diag_cmd_data_free(phba, + (struct lpfc_dmabufext *) + dmabuf); + break; case ELX_LOOPBACK_XRI_SETUP: - if (!(phba->sli3_options & - LPFC_SLI3_HBQ_ENABLED)) + if ((phba->sli_rev == + LPFC_SLI_REV2) || + (phba->sli3_options & + LPFC_SLI3_HBQ_ENABLED + )) { + lpfc_in_buf_free(phba, + dmabuf); + } else { lpfc_post_buffer(phba, pring, 1); - else - lpfc_in_buf_free(phba, - dmabuf); + } break; default: if (!(phba->sli3_options & @@ -638,7 +929,7 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, } } - mutex_lock(&phba->ct_event_mutex); + spin_lock_irqsave(&phba->ct_ev_lock, flags); if (phba->sli_rev == LPFC_SLI_REV4) { evt_dat->immed_dat = phba->ctx_idx; phba->ctx_idx = (phba->ctx_idx + 1) % 64; @@ -651,122 +942,144 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, evt_dat->type = FC_REG_CT_EVENT; list_add(&evt_dat->node, &evt->events_to_see); - wake_up_interruptible(&evt->wq); - lpfc_ct_event_unref(evt); - if (evt_req_id == SLI_CT_ELX_LOOPBACK) + if (evt_req_id == SLI_CT_ELX_LOOPBACK) { + wake_up_interruptible(&evt->wq); + lpfc_bsg_event_unref(evt); break; + } + + list_move(evt->events_to_see.prev, &evt->events_to_get); + lpfc_bsg_event_unref(evt); + + job = evt->set_job; + evt->set_job = NULL; + if (job) { + job->reply->reply_payload_rcv_len = size; + /* make error code available to userspace */ + job->reply->result = 0; + job->dd_data = NULL; + /* complete the job back to userspace */ + spin_unlock_irqrestore(&phba->ct_ev_lock, flags); + job->job_done(job); + spin_lock_irqsave(&phba->ct_ev_lock, flags); + } } - mutex_unlock(&phba->ct_event_mutex); + spin_unlock_irqrestore(&phba->ct_ev_lock, flags); error_ct_unsol_exit: if (!list_empty(&head)) list_del(&head); - - return; + if (evt_req_id == SLI_CT_ELX_LOOPBACK) + return 0; + return 1; } /** - * lpfc_bsg_set_event - process a SET_EVENT bsg vendor command + * lpfc_bsg_hba_set_event - process a SET_EVENT bsg vendor command * @job: SET_EVENT fc_bsg_job - */ + **/ static int -lpfc_bsg_set_event(struct fc_bsg_job *job) +lpfc_bsg_hba_set_event(struct fc_bsg_job *job) { struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata; struct lpfc_hba *phba = vport->phba; struct set_ct_event *event_req; - struct lpfc_ct_event *evt; + struct lpfc_bsg_event *evt; int rc = 0; + struct bsg_job_data *dd_data = NULL; + uint32_t ev_mask; + unsigned long flags; if (job->request_len < sizeof(struct fc_bsg_request) + sizeof(struct set_ct_event)) { lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, "2612 Received SET_CT_EVENT below minimum " "size\n"); - return -EINVAL; + rc = -EINVAL; + goto job_error; + } + + dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL); + if (dd_data == NULL) { + lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, + "2734 Failed allocation of dd_data\n"); + rc = -ENOMEM; + goto job_error; } event_req = (struct set_ct_event *) job->request->rqst_data.h_vendor.vendor_cmd; - - mutex_lock(&phba->ct_event_mutex); + ev_mask = ((uint32_t)(unsigned long)event_req->type_mask & + FC_REG_EVENT_MASK); + spin_lock_irqsave(&phba->ct_ev_lock, flags); list_for_each_entry(evt, &phba->ct_ev_waiters, node) { if (evt->reg_id == event_req->ev_reg_id) { - lpfc_ct_event_ref(evt); + lpfc_bsg_event_ref(evt); evt->wait_time_stamp = jiffies; break; } } - mutex_unlock(&phba->ct_event_mutex); + spin_unlock_irqrestore(&phba->ct_ev_lock, flags); if (&evt->node == &phba->ct_ev_waiters) { /* no event waiting struct yet - first call */ - evt = lpfc_ct_event_new(event_req->ev_reg_id, + evt = lpfc_bsg_event_new(ev_mask, event_req->ev_reg_id, event_req->ev_req_id); if (!evt) { lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, "2617 Failed allocation of event " "waiter\n"); - return -ENOMEM; + rc = -ENOMEM; + goto job_error; } - mutex_lock(&phba->ct_event_mutex); + spin_lock_irqsave(&phba->ct_ev_lock, flags); list_add(&evt->node, &phba->ct_ev_waiters); - lpfc_ct_event_ref(evt); - mutex_unlock(&phba->ct_event_mutex); + lpfc_bsg_event_ref(evt); + evt->wait_time_stamp = jiffies; + spin_unlock_irqrestore(&phba->ct_ev_lock, flags); } + spin_lock_irqsave(&phba->ct_ev_lock, flags); evt->waiting = 1; - if (wait_event_interruptible(evt->wq, - !list_empty(&evt->events_to_see))) { - mutex_lock(&phba->ct_event_mutex); - lpfc_ct_event_unref(evt); /* release ref */ - lpfc_ct_event_unref(evt); /* delete */ - mutex_unlock(&phba->ct_event_mutex); - rc = -EINTR; - goto set_event_out; - } - - evt->wait_time_stamp = jiffies; - evt->waiting = 0; - - mutex_lock(&phba->ct_event_mutex); - list_move(evt->events_to_see.prev, &evt->events_to_get); - lpfc_ct_event_unref(evt); /* release ref */ - mutex_unlock(&phba->ct_event_mutex); - -set_event_out: - /* set_event carries no reply payload */ - job->reply->reply_payload_rcv_len = 0; - /* make error code available to userspace */ - job->reply->result = rc; - /* complete the job back to userspace */ - job->job_done(job); - - return 0; + dd_data->type = TYPE_EVT; + dd_data->context_un.evt = evt; + evt->set_job = job; /* for unsolicited command */ + job->dd_data = dd_data; /* for fc transport timeout callback*/ + spin_unlock_irqrestore(&phba->ct_ev_lock, flags); + return 0; /* call job done later */ + +job_error: + if (dd_data != NULL) + kfree(dd_data); + + job->dd_data = NULL; + return rc; } /** - * lpfc_bsg_get_event - process a GET_EVENT bsg vendor command + * lpfc_bsg_hba_get_event - process a GET_EVENT bsg vendor command * @job: GET_EVENT fc_bsg_job - */ + **/ static int -lpfc_bsg_get_event(struct fc_bsg_job *job) +lpfc_bsg_hba_get_event(struct fc_bsg_job *job) { struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata; struct lpfc_hba *phba = vport->phba; struct get_ct_event *event_req; struct get_ct_event_reply *event_reply; - struct lpfc_ct_event *evt; + struct lpfc_bsg_event *evt; struct event_data *evt_dat = NULL; - int rc = 0; + unsigned long flags; + uint32_t rc = 0; if (job->request_len < sizeof(struct fc_bsg_request) + sizeof(struct get_ct_event)) { lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, "2613 Received GET_CT_EVENT request below " "minimum size\n"); - return -EINVAL; + rc = -EINVAL; + goto job_error; } event_req = (struct get_ct_event *) @@ -774,13 +1087,12 @@ lpfc_bsg_get_event(struct fc_bsg_job *job) event_reply = (struct get_ct_event_reply *) job->reply->reply_data.vendor_reply.vendor_rsp; - - mutex_lock(&phba->ct_event_mutex); + spin_lock_irqsave(&phba->ct_ev_lock, flags); list_for_each_entry(evt, &phba->ct_ev_waiters, node) { if (evt->reg_id == event_req->ev_reg_id) { if (list_empty(&evt->events_to_get)) break; - lpfc_ct_event_ref(evt); + lpfc_bsg_event_ref(evt); evt->wait_time_stamp = jiffies; evt_dat = list_entry(evt->events_to_get.prev, struct event_data, node); @@ -788,45 +1100,1539 @@ lpfc_bsg_get_event(struct fc_bsg_job *job) break; } } - mutex_unlock(&phba->ct_event_mutex); + spin_unlock_irqrestore(&phba->ct_ev_lock, flags); - if (!evt_dat) { + /* The app may continue to ask for event data until it gets + * an error indicating that there isn't anymore + */ + if (evt_dat == NULL) { job->reply->reply_payload_rcv_len = 0; rc = -ENOENT; - goto error_get_event_exit; + goto job_error; } - if (evt_dat->len > job->reply_payload.payload_len) { - evt_dat->len = job->reply_payload.payload_len; - lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, - "2618 Truncated event data at %d " - "bytes\n", - job->reply_payload.payload_len); + if (evt_dat->len > job->request_payload.payload_len) { + evt_dat->len = job->request_payload.payload_len; + lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, + "2618 Truncated event data at %d " + "bytes\n", + job->request_payload.payload_len); } + event_reply->type = evt_dat->type; event_reply->immed_data = evt_dat->immed_dat; - if (evt_dat->len > 0) job->reply->reply_payload_rcv_len = - sg_copy_from_buffer(job->reply_payload.sg_list, - job->reply_payload.sg_cnt, + sg_copy_from_buffer(job->request_payload.sg_list, + job->request_payload.sg_cnt, evt_dat->data, evt_dat->len); else job->reply->reply_payload_rcv_len = 0; - rc = 0; - if (evt_dat) + if (evt_dat) { kfree(evt_dat->data); - kfree(evt_dat); - mutex_lock(&phba->ct_event_mutex); - lpfc_ct_event_unref(evt); - mutex_unlock(&phba->ct_event_mutex); + kfree(evt_dat); + } + + spin_lock_irqsave(&phba->ct_ev_lock, flags); + lpfc_bsg_event_unref(evt); + spin_unlock_irqrestore(&phba->ct_ev_lock, flags); + job->dd_data = NULL; + job->reply->result = 0; + job->job_done(job); + return 0; + +job_error: + job->dd_data = NULL; + job->reply->result = rc; + return rc; +} + +/** + * lpfc_issue_ct_rsp_cmp - lpfc_issue_ct_rsp's completion handler + * @phba: Pointer to HBA context object. + * @cmdiocbq: Pointer to command iocb. + * @rspiocbq: Pointer to response iocb. + * + * This function is the completion handler for iocbs issued using + * lpfc_issue_ct_rsp_cmp function. This function is called by the + * ring event handler function without any lock held. This function + * can be called from both worker thread context and interrupt + * context. This function also can be called from other thread which + * cleans up the SLI layer objects. + * This function copy the contents of the response iocb to the + * response iocb memory object provided by the caller of + * lpfc_sli_issue_iocb_wait and then wakes up the thread which + * sleeps for the iocb completion. + **/ +static void +lpfc_issue_ct_rsp_cmp(struct lpfc_hba *phba, + struct lpfc_iocbq *cmdiocbq, + struct lpfc_iocbq *rspiocbq) +{ + struct bsg_job_data *dd_data; + struct fc_bsg_job *job; + IOCB_t *rsp; + struct lpfc_dmabuf *bmp; + struct lpfc_nodelist *ndlp; + unsigned long flags; + int rc = 0; + + spin_lock_irqsave(&phba->ct_ev_lock, flags); + dd_data = cmdiocbq->context1; + /* normal completion and timeout crossed paths, already done */ + if (!dd_data) { + spin_unlock_irqrestore(&phba->hbalock, flags); + return; + } -error_get_event_exit: + job = dd_data->context_un.iocb.set_job; + bmp = dd_data->context_un.iocb.bmp; + rsp = &rspiocbq->iocb; + ndlp = dd_data->context_un.iocb.ndlp; + + pci_unmap_sg(phba->pcidev, job->request_payload.sg_list, + job->request_payload.sg_cnt, DMA_TO_DEVICE); + + if (rsp->ulpStatus) { + if (rsp->ulpStatus == IOSTAT_LOCAL_REJECT) { + switch (rsp->un.ulpWord[4] & 0xff) { + case IOERR_SEQUENCE_TIMEOUT: + rc = -ETIMEDOUT; + break; + case IOERR_INVALID_RPI: + rc = -EFAULT; + break; + default: + rc = -EACCES; + break; + } + } else + rc = -EACCES; + } else + job->reply->reply_payload_rcv_len = + rsp->un.genreq64.bdl.bdeSize; + + lpfc_mbuf_free(phba, bmp->virt, bmp->phys); + lpfc_sli_release_iocbq(phba, cmdiocbq); + lpfc_nlp_put(ndlp); + kfree(bmp); + kfree(dd_data); /* make error code available to userspace */ job->reply->result = rc; + job->dd_data = NULL; /* complete the job back to userspace */ job->job_done(job); + spin_unlock_irqrestore(&phba->ct_ev_lock, flags); + return; +} + +/** + * lpfc_issue_ct_rsp - issue a ct response + * @phba: Pointer to HBA context object. + * @job: Pointer to the job object. + * @tag: tag index value into the ports context exchange array. + * @bmp: Pointer to a dma buffer descriptor. + * @num_entry: Number of enties in the bde. + **/ +static int +lpfc_issue_ct_rsp(struct lpfc_hba *phba, struct fc_bsg_job *job, uint32_t tag, + struct lpfc_dmabuf *bmp, int num_entry) +{ + IOCB_t *icmd; + struct lpfc_iocbq *ctiocb = NULL; + int rc = 0; + struct lpfc_nodelist *ndlp = NULL; + struct bsg_job_data *dd_data; + uint32_t creg_val; + + /* allocate our bsg tracking structure */ + dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL); + if (!dd_data) { + lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, + "2736 Failed allocation of dd_data\n"); + rc = -ENOMEM; + goto no_dd_data; + } + + /* Allocate buffer for command iocb */ + ctiocb = lpfc_sli_get_iocbq(phba); + if (!ctiocb) { + rc = ENOMEM; + goto no_ctiocb; + } + + icmd = &ctiocb->iocb; + icmd->un.xseq64.bdl.ulpIoTag32 = 0; + icmd->un.xseq64.bdl.addrHigh = putPaddrHigh(bmp->phys); + icmd->un.xseq64.bdl.addrLow = putPaddrLow(bmp->phys); + icmd->un.xseq64.bdl.bdeFlags = BUFF_TYPE_BLP_64; + icmd->un.xseq64.bdl.bdeSize = (num_entry * sizeof(struct ulp_bde64)); + icmd->un.xseq64.w5.hcsw.Fctl = (LS | LA); + icmd->un.xseq64.w5.hcsw.Dfctl = 0; + icmd->un.xseq64.w5.hcsw.Rctl = FC_RCTL_DD_SOL_CTL; + icmd->un.xseq64.w5.hcsw.Type = FC_TYPE_CT; + + /* Fill in rest of iocb */ + icmd->ulpCommand = CMD_XMIT_SEQUENCE64_CX; + icmd->ulpBdeCount = 1; + icmd->ulpLe = 1; + icmd->ulpClass = CLASS3; + if (phba->sli_rev == LPFC_SLI_REV4) { + /* Do not issue unsol response if oxid not marked as valid */ + if (!(phba->ct_ctx[tag].flags & UNSOL_VALID)) { + rc = IOCB_ERROR; + goto issue_ct_rsp_exit; + } + icmd->ulpContext = phba->ct_ctx[tag].oxid; + ndlp = lpfc_findnode_did(phba->pport, phba->ct_ctx[tag].SID); + if (!ndlp) { + lpfc_printf_log(phba, KERN_WARNING, LOG_ELS, + "2721 ndlp null for oxid %x SID %x\n", + icmd->ulpContext, + phba->ct_ctx[tag].SID); + rc = IOCB_ERROR; + goto issue_ct_rsp_exit; + } + icmd->un.ulpWord[3] = ndlp->nlp_rpi; + /* The exchange is done, mark the entry as invalid */ + phba->ct_ctx[tag].flags &= ~UNSOL_VALID; + } else + icmd->ulpContext = (ushort) tag; + + icmd->ulpTimeout = phba->fc_ratov * 2; + + /* Xmit CT response on exchange <xid> */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "2722 Xmit CT response on exchange x%x Data: x%x x%x\n", + icmd->ulpContext, icmd->ulpIoTag, phba->link_state); + + ctiocb->iocb_cmpl = NULL; + ctiocb->iocb_flag |= LPFC_IO_LIBDFC; + ctiocb->vport = phba->pport; + ctiocb->context3 = bmp; + + ctiocb->iocb_cmpl = lpfc_issue_ct_rsp_cmp; + ctiocb->context1 = dd_data; + ctiocb->context2 = NULL; + dd_data->type = TYPE_IOCB; + dd_data->context_un.iocb.cmdiocbq = ctiocb; + dd_data->context_un.iocb.rspiocbq = NULL; + dd_data->context_un.iocb.set_job = job; + dd_data->context_un.iocb.bmp = bmp; + dd_data->context_un.iocb.ndlp = ndlp; + + if (phba->cfg_poll & DISABLE_FCP_RING_INT) { + creg_val = readl(phba->HCregaddr); + creg_val |= (HC_R0INT_ENA << LPFC_FCP_RING); + writel(creg_val, phba->HCregaddr); + readl(phba->HCregaddr); /* flush */ + } + + rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, ctiocb, 0); + + if (rc == IOCB_SUCCESS) + return 0; /* done for now */ + +issue_ct_rsp_exit: + lpfc_sli_release_iocbq(phba, ctiocb); +no_ctiocb: + kfree(dd_data); +no_dd_data: + return rc; +} + +/** + * lpfc_bsg_send_mgmt_rsp - process a SEND_MGMT_RESP bsg vendor command + * @job: SEND_MGMT_RESP fc_bsg_job + **/ +static int +lpfc_bsg_send_mgmt_rsp(struct fc_bsg_job *job) +{ + struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata; + struct lpfc_hba *phba = vport->phba; + struct send_mgmt_resp *mgmt_resp = (struct send_mgmt_resp *) + job->request->rqst_data.h_vendor.vendor_cmd; + struct ulp_bde64 *bpl; + struct lpfc_dmabuf *bmp = NULL; + struct scatterlist *sgel = NULL; + int request_nseg; + int numbde; + dma_addr_t busaddr; + uint32_t tag = mgmt_resp->tag; + unsigned long reqbfrcnt = + (unsigned long)job->request_payload.payload_len; + int rc = 0; + + /* in case no data is transferred */ + job->reply->reply_payload_rcv_len = 0; + + if (!reqbfrcnt || (reqbfrcnt > (80 * BUF_SZ_4K))) { + rc = -ERANGE; + goto send_mgmt_rsp_exit; + } + + bmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL); + if (!bmp) { + rc = -ENOMEM; + goto send_mgmt_rsp_exit; + } + + bmp->virt = lpfc_mbuf_alloc(phba, 0, &bmp->phys); + if (!bmp->virt) { + rc = -ENOMEM; + goto send_mgmt_rsp_free_bmp; + } + + INIT_LIST_HEAD(&bmp->list); + bpl = (struct ulp_bde64 *) bmp->virt; + request_nseg = pci_map_sg(phba->pcidev, job->request_payload.sg_list, + job->request_payload.sg_cnt, DMA_TO_DEVICE); + for_each_sg(job->request_payload.sg_list, sgel, request_nseg, numbde) { + busaddr = sg_dma_address(sgel); + bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64; + bpl->tus.f.bdeSize = sg_dma_len(sgel); + bpl->tus.w = cpu_to_le32(bpl->tus.w); + bpl->addrLow = cpu_to_le32(putPaddrLow(busaddr)); + bpl->addrHigh = cpu_to_le32(putPaddrHigh(busaddr)); + bpl++; + } + + rc = lpfc_issue_ct_rsp(phba, job, tag, bmp, request_nseg); + + if (rc == IOCB_SUCCESS) + return 0; /* done for now */ + + /* TBD need to handle a timeout */ + pci_unmap_sg(phba->pcidev, job->request_payload.sg_list, + job->request_payload.sg_cnt, DMA_TO_DEVICE); + rc = -EACCES; + lpfc_mbuf_free(phba, bmp->virt, bmp->phys); + +send_mgmt_rsp_free_bmp: + kfree(bmp); +send_mgmt_rsp_exit: + /* make error code available to userspace */ + job->reply->result = rc; + job->dd_data = NULL; + return rc; +} + +/** + * lpfc_bsg_diag_mode - process a LPFC_BSG_VENDOR_DIAG_MODE bsg vendor command + * @job: LPFC_BSG_VENDOR_DIAG_MODE + * + * This function is responsible for placing a port into diagnostic loopback + * mode in order to perform a diagnostic loopback test. + * All new scsi requests are blocked, a small delay is used to allow the + * scsi requests to complete then the link is brought down. If the link is + * is placed in loopback mode then scsi requests are again allowed + * so the scsi mid-layer doesn't give up on the port. + * All of this is done in-line. + */ +static int +lpfc_bsg_diag_mode(struct fc_bsg_job *job) +{ + struct Scsi_Host *shost = job->shost; + struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata; + struct lpfc_hba *phba = vport->phba; + struct diag_mode_set *loopback_mode; + struct lpfc_sli *psli = &phba->sli; + struct lpfc_sli_ring *pring = &psli->ring[LPFC_FCP_RING]; + uint32_t link_flags; + uint32_t timeout; + struct lpfc_vport **vports; + LPFC_MBOXQ_t *pmboxq; + int mbxstatus; + int i = 0; + int rc = 0; + + /* no data to return just the return code */ + job->reply->reply_payload_rcv_len = 0; + + if (job->request_len < + sizeof(struct fc_bsg_request) + sizeof(struct diag_mode_set)) { + lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, + "2738 Received DIAG MODE request below minimum " + "size\n"); + rc = -EINVAL; + goto job_error; + } + + loopback_mode = (struct diag_mode_set *) + job->request->rqst_data.h_vendor.vendor_cmd; + link_flags = loopback_mode->type; + timeout = loopback_mode->timeout; + + if ((phba->link_state == LPFC_HBA_ERROR) || + (psli->sli_flag & LPFC_BLOCK_MGMT_IO) || + (!(psli->sli_flag & LPFC_SLI_ACTIVE))) { + rc = -EACCES; + goto job_error; + } + + pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); + if (!pmboxq) { + rc = -ENOMEM; + goto job_error; + } + + vports = lpfc_create_vport_work_array(phba); + if (vports) { + for (i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) { + shost = lpfc_shost_from_vport(vports[i]); + scsi_block_requests(shost); + } + + lpfc_destroy_vport_work_array(phba, vports); + } else { + shost = lpfc_shost_from_vport(phba->pport); + scsi_block_requests(shost); + } + + while (pring->txcmplq_cnt) { + if (i++ > 500) /* wait up to 5 seconds */ + break; + + msleep(10); + } + + memset((void *)pmboxq, 0, sizeof(LPFC_MBOXQ_t)); + pmboxq->u.mb.mbxCommand = MBX_DOWN_LINK; + pmboxq->u.mb.mbxOwner = OWN_HOST; + + mbxstatus = lpfc_sli_issue_mbox_wait(phba, pmboxq, LPFC_MBOX_TMO); + + if ((mbxstatus == MBX_SUCCESS) && (pmboxq->u.mb.mbxStatus == 0)) { + /* wait for link down before proceeding */ + i = 0; + while (phba->link_state != LPFC_LINK_DOWN) { + if (i++ > timeout) { + rc = -ETIMEDOUT; + goto loopback_mode_exit; + } + + msleep(10); + } + + memset((void *)pmboxq, 0, sizeof(LPFC_MBOXQ_t)); + if (link_flags == INTERNAL_LOOP_BACK) + pmboxq->u.mb.un.varInitLnk.link_flags = FLAGS_LOCAL_LB; + else + pmboxq->u.mb.un.varInitLnk.link_flags = + FLAGS_TOPOLOGY_MODE_LOOP; + + pmboxq->u.mb.mbxCommand = MBX_INIT_LINK; + pmboxq->u.mb.mbxOwner = OWN_HOST; + + mbxstatus = lpfc_sli_issue_mbox_wait(phba, pmboxq, + LPFC_MBOX_TMO); + + if ((mbxstatus != MBX_SUCCESS) || (pmboxq->u.mb.mbxStatus)) + rc = -ENODEV; + else { + phba->link_flag |= LS_LOOPBACK_MODE; + /* wait for the link attention interrupt */ + msleep(100); + + i = 0; + while (phba->link_state != LPFC_HBA_READY) { + if (i++ > timeout) { + rc = -ETIMEDOUT; + break; + } + + msleep(10); + } + } + + } else + rc = -ENODEV; + +loopback_mode_exit: + vports = lpfc_create_vport_work_array(phba); + if (vports) { + for (i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) { + shost = lpfc_shost_from_vport(vports[i]); + scsi_unblock_requests(shost); + } + lpfc_destroy_vport_work_array(phba, vports); + } else { + shost = lpfc_shost_from_vport(phba->pport); + scsi_unblock_requests(shost); + } + + /* + * Let SLI layer release mboxq if mbox command completed after timeout. + */ + if (mbxstatus != MBX_TIMEOUT) + mempool_free(pmboxq, phba->mbox_mem_pool); + +job_error: + /* make error code available to userspace */ + job->reply->result = rc; + /* complete the job back to userspace if no error */ + if (rc == 0) + job->job_done(job); + return rc; +} + +/** + * lpfcdiag_loop_self_reg - obtains a remote port login id + * @phba: Pointer to HBA context object + * @rpi: Pointer to a remote port login id + * + * This function obtains a remote port login id so the diag loopback test + * can send and receive its own unsolicited CT command. + **/ +static int lpfcdiag_loop_self_reg(struct lpfc_hba *phba, uint16_t * rpi) +{ + LPFC_MBOXQ_t *mbox; + struct lpfc_dmabuf *dmabuff; + int status; + + mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); + if (!mbox) + return ENOMEM; + + status = lpfc_reg_rpi(phba, 0, phba->pport->fc_myDID, + (uint8_t *)&phba->pport->fc_sparam, mbox, 0); + if (status) { + mempool_free(mbox, phba->mbox_mem_pool); + return ENOMEM; + } + + dmabuff = (struct lpfc_dmabuf *) mbox->context1; + mbox->context1 = NULL; + status = lpfc_sli_issue_mbox_wait(phba, mbox, LPFC_MBOX_TMO); + + if ((status != MBX_SUCCESS) || (mbox->u.mb.mbxStatus)) { + lpfc_mbuf_free(phba, dmabuff->virt, dmabuff->phys); + kfree(dmabuff); + if (status != MBX_TIMEOUT) + mempool_free(mbox, phba->mbox_mem_pool); + return ENODEV; + } + + *rpi = mbox->u.mb.un.varWords[0]; + + lpfc_mbuf_free(phba, dmabuff->virt, dmabuff->phys); + kfree(dmabuff); + mempool_free(mbox, phba->mbox_mem_pool); + return 0; +} + +/** + * lpfcdiag_loop_self_unreg - unregs from the rpi + * @phba: Pointer to HBA context object + * @rpi: Remote port login id + * + * This function unregisters the rpi obtained in lpfcdiag_loop_self_reg + **/ +static int lpfcdiag_loop_self_unreg(struct lpfc_hba *phba, uint16_t rpi) +{ + LPFC_MBOXQ_t *mbox; + int status; + + /* Allocate mboxq structure */ + mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); + if (mbox == NULL) + return ENOMEM; + + lpfc_unreg_login(phba, 0, rpi, mbox); + status = lpfc_sli_issue_mbox_wait(phba, mbox, LPFC_MBOX_TMO); + + if ((status != MBX_SUCCESS) || (mbox->u.mb.mbxStatus)) { + if (status != MBX_TIMEOUT) + mempool_free(mbox, phba->mbox_mem_pool); + return EIO; + } + + mempool_free(mbox, phba->mbox_mem_pool); + return 0; +} + +/** + * lpfcdiag_loop_get_xri - obtains the transmit and receive ids + * @phba: Pointer to HBA context object + * @rpi: Remote port login id + * @txxri: Pointer to transmit exchange id + * @rxxri: Pointer to response exchabge id + * + * This function obtains the transmit and receive ids required to send + * an unsolicited ct command with a payload. A special lpfc FsType and CmdRsp + * flags are used to the unsolicted response handler is able to process + * the ct command sent on the same port. + **/ +static int lpfcdiag_loop_get_xri(struct lpfc_hba *phba, uint16_t rpi, + uint16_t *txxri, uint16_t * rxxri) +{ + struct lpfc_bsg_event *evt; + struct lpfc_iocbq *cmdiocbq, *rspiocbq; + IOCB_t *cmd, *rsp; + struct lpfc_dmabuf *dmabuf; + struct ulp_bde64 *bpl = NULL; + struct lpfc_sli_ct_request *ctreq = NULL; + int ret_val = 0; + unsigned long flags; + + *txxri = 0; + *rxxri = 0; + evt = lpfc_bsg_event_new(FC_REG_CT_EVENT, current->pid, + SLI_CT_ELX_LOOPBACK); + if (!evt) + return ENOMEM; + + spin_lock_irqsave(&phba->ct_ev_lock, flags); + list_add(&evt->node, &phba->ct_ev_waiters); + lpfc_bsg_event_ref(evt); + spin_unlock_irqrestore(&phba->ct_ev_lock, flags); + + cmdiocbq = lpfc_sli_get_iocbq(phba); + rspiocbq = lpfc_sli_get_iocbq(phba); + + dmabuf = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL); + if (dmabuf) { + dmabuf->virt = lpfc_mbuf_alloc(phba, 0, &dmabuf->phys); + INIT_LIST_HEAD(&dmabuf->list); + bpl = (struct ulp_bde64 *) dmabuf->virt; + memset(bpl, 0, sizeof(*bpl)); + ctreq = (struct lpfc_sli_ct_request *)(bpl + 1); + bpl->addrHigh = + le32_to_cpu(putPaddrHigh(dmabuf->phys + sizeof(*bpl))); + bpl->addrLow = + le32_to_cpu(putPaddrLow(dmabuf->phys + sizeof(*bpl))); + bpl->tus.f.bdeFlags = 0; + bpl->tus.f.bdeSize = ELX_LOOPBACK_HEADER_SZ; + bpl->tus.w = le32_to_cpu(bpl->tus.w); + } + + if (cmdiocbq == NULL || rspiocbq == NULL || + dmabuf == NULL || bpl == NULL || ctreq == NULL) { + ret_val = ENOMEM; + goto err_get_xri_exit; + } + + cmd = &cmdiocbq->iocb; + rsp = &rspiocbq->iocb; + + memset(ctreq, 0, ELX_LOOPBACK_HEADER_SZ); + + ctreq->RevisionId.bits.Revision = SLI_CT_REVISION; + ctreq->RevisionId.bits.InId = 0; + ctreq->FsType = SLI_CT_ELX_LOOPBACK; + ctreq->FsSubType = 0; + ctreq->CommandResponse.bits.CmdRsp = ELX_LOOPBACK_XRI_SETUP; + ctreq->CommandResponse.bits.Size = 0; + + + cmd->un.xseq64.bdl.addrHigh = putPaddrHigh(dmabuf->phys); + cmd->un.xseq64.bdl.addrLow = putPaddrLow(dmabuf->phys); + cmd->un.xseq64.bdl.bdeFlags = BUFF_TYPE_BLP_64; + cmd->un.xseq64.bdl.bdeSize = sizeof(*bpl); + + cmd->un.xseq64.w5.hcsw.Fctl = LA; + cmd->un.xseq64.w5.hcsw.Dfctl = 0; + cmd->un.xseq64.w5.hcsw.Rctl = FC_RCTL_DD_UNSOL_CTL; + cmd->un.xseq64.w5.hcsw.Type = FC_TYPE_CT; + + cmd->ulpCommand = CMD_XMIT_SEQUENCE64_CR; + cmd->ulpBdeCount = 1; + cmd->ulpLe = 1; + cmd->ulpClass = CLASS3; + cmd->ulpContext = rpi; + + cmdiocbq->iocb_flag |= LPFC_IO_LIBDFC; + cmdiocbq->vport = phba->pport; + + ret_val = lpfc_sli_issue_iocb_wait(phba, LPFC_ELS_RING, cmdiocbq, + rspiocbq, + (phba->fc_ratov * 2) + + LPFC_DRVR_TIMEOUT); + if (ret_val) + goto err_get_xri_exit; + + *txxri = rsp->ulpContext; + + evt->waiting = 1; + evt->wait_time_stamp = jiffies; + ret_val = wait_event_interruptible_timeout( + evt->wq, !list_empty(&evt->events_to_see), + ((phba->fc_ratov * 2) + LPFC_DRVR_TIMEOUT) * HZ); + if (list_empty(&evt->events_to_see)) + ret_val = (ret_val) ? EINTR : ETIMEDOUT; + else { + ret_val = IOCB_SUCCESS; + spin_lock_irqsave(&phba->ct_ev_lock, flags); + list_move(evt->events_to_see.prev, &evt->events_to_get); + spin_unlock_irqrestore(&phba->ct_ev_lock, flags); + *rxxri = (list_entry(evt->events_to_get.prev, + typeof(struct event_data), + node))->immed_dat; + } + evt->waiting = 0; + +err_get_xri_exit: + spin_lock_irqsave(&phba->ct_ev_lock, flags); + lpfc_bsg_event_unref(evt); /* release ref */ + lpfc_bsg_event_unref(evt); /* delete */ + spin_unlock_irqrestore(&phba->ct_ev_lock, flags); + + if (dmabuf) { + if (dmabuf->virt) + lpfc_mbuf_free(phba, dmabuf->virt, dmabuf->phys); + kfree(dmabuf); + } + + if (cmdiocbq && (ret_val != IOCB_TIMEDOUT)) + lpfc_sli_release_iocbq(phba, cmdiocbq); + if (rspiocbq) + lpfc_sli_release_iocbq(phba, rspiocbq); + return ret_val; +} + +/** + * diag_cmd_data_alloc - fills in a bde struct with dma buffers + * @phba: Pointer to HBA context object + * @bpl: Pointer to 64 bit bde structure + * @size: Number of bytes to process + * @nocopydata: Flag to copy user data into the allocated buffer + * + * This function allocates page size buffers and populates an lpfc_dmabufext. + * If allowed the user data pointed to with indataptr is copied into the kernel + * memory. The chained list of page size buffers is returned. + **/ +static struct lpfc_dmabufext * +diag_cmd_data_alloc(struct lpfc_hba *phba, + struct ulp_bde64 *bpl, uint32_t size, + int nocopydata) +{ + struct lpfc_dmabufext *mlist = NULL; + struct lpfc_dmabufext *dmp; + int cnt, offset = 0, i = 0; + struct pci_dev *pcidev; + + pcidev = phba->pcidev; + + while (size) { + /* We get chunks of 4K */ + if (size > BUF_SZ_4K) + cnt = BUF_SZ_4K; + else + cnt = size; + + /* allocate struct lpfc_dmabufext buffer header */ + dmp = kmalloc(sizeof(struct lpfc_dmabufext), GFP_KERNEL); + if (!dmp) + goto out; + + INIT_LIST_HEAD(&dmp->dma.list); + + /* Queue it to a linked list */ + if (mlist) + list_add_tail(&dmp->dma.list, &mlist->dma.list); + else + mlist = dmp; + + /* allocate buffer */ + dmp->dma.virt = dma_alloc_coherent(&pcidev->dev, + cnt, + &(dmp->dma.phys), + GFP_KERNEL); + + if (!dmp->dma.virt) + goto out; + + dmp->size = cnt; + + if (nocopydata) { + bpl->tus.f.bdeFlags = 0; + pci_dma_sync_single_for_device(phba->pcidev, + dmp->dma.phys, LPFC_BPL_SIZE, PCI_DMA_TODEVICE); + + } else { + memset((uint8_t *)dmp->dma.virt, 0, cnt); + bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I; + } + + /* build buffer ptr list for IOCB */ + bpl->addrLow = le32_to_cpu(putPaddrLow(dmp->dma.phys)); + bpl->addrHigh = le32_to_cpu(putPaddrHigh(dmp->dma.phys)); + bpl->tus.f.bdeSize = (ushort) cnt; + bpl->tus.w = le32_to_cpu(bpl->tus.w); + bpl++; + + i++; + offset += cnt; + size -= cnt; + } + + mlist->flag = i; + return mlist; +out: + diag_cmd_data_free(phba, mlist); + return NULL; +} + +/** + * lpfcdiag_loop_post_rxbufs - post the receive buffers for an unsol CT cmd + * @phba: Pointer to HBA context object + * @rxxri: Receive exchange id + * @len: Number of data bytes + * + * This function allocates and posts a data buffer of sufficient size to recieve + * an unsolicted CT command. + **/ +static int lpfcdiag_loop_post_rxbufs(struct lpfc_hba *phba, uint16_t rxxri, + size_t len) +{ + struct lpfc_sli *psli = &phba->sli; + struct lpfc_sli_ring *pring = &psli->ring[LPFC_ELS_RING]; + struct lpfc_iocbq *cmdiocbq; + IOCB_t *cmd = NULL; + struct list_head head, *curr, *next; + struct lpfc_dmabuf *rxbmp; + struct lpfc_dmabuf *dmp; + struct lpfc_dmabuf *mp[2] = {NULL, NULL}; + struct ulp_bde64 *rxbpl = NULL; + uint32_t num_bde; + struct lpfc_dmabufext *rxbuffer = NULL; + int ret_val = 0; + int i = 0; + + cmdiocbq = lpfc_sli_get_iocbq(phba); + rxbmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL); + if (rxbmp != NULL) { + rxbmp->virt = lpfc_mbuf_alloc(phba, 0, &rxbmp->phys); + INIT_LIST_HEAD(&rxbmp->list); + rxbpl = (struct ulp_bde64 *) rxbmp->virt; + rxbuffer = diag_cmd_data_alloc(phba, rxbpl, len, 0); + } + + if (!cmdiocbq || !rxbmp || !rxbpl || !rxbuffer) { + ret_val = ENOMEM; + goto err_post_rxbufs_exit; + } + + /* Queue buffers for the receive exchange */ + num_bde = (uint32_t)rxbuffer->flag; + dmp = &rxbuffer->dma; + + cmd = &cmdiocbq->iocb; + i = 0; + + INIT_LIST_HEAD(&head); + list_add_tail(&head, &dmp->list); + list_for_each_safe(curr, next, &head) { + mp[i] = list_entry(curr, struct lpfc_dmabuf, list); + list_del(curr); + + if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) { + mp[i]->buffer_tag = lpfc_sli_get_buffer_tag(phba); + cmd->un.quexri64cx.buff.bde.addrHigh = + putPaddrHigh(mp[i]->phys); + cmd->un.quexri64cx.buff.bde.addrLow = + putPaddrLow(mp[i]->phys); + cmd->un.quexri64cx.buff.bde.tus.f.bdeSize = + ((struct lpfc_dmabufext *)mp[i])->size; + cmd->un.quexri64cx.buff.buffer_tag = mp[i]->buffer_tag; + cmd->ulpCommand = CMD_QUE_XRI64_CX; + cmd->ulpPU = 0; + cmd->ulpLe = 1; + cmd->ulpBdeCount = 1; + cmd->unsli3.que_xri64cx_ext_words.ebde_count = 0; + + } else { + cmd->un.cont64[i].addrHigh = putPaddrHigh(mp[i]->phys); + cmd->un.cont64[i].addrLow = putPaddrLow(mp[i]->phys); + cmd->un.cont64[i].tus.f.bdeSize = + ((struct lpfc_dmabufext *)mp[i])->size; + cmd->ulpBdeCount = ++i; + + if ((--num_bde > 0) && (i < 2)) + continue; + + cmd->ulpCommand = CMD_QUE_XRI_BUF64_CX; + cmd->ulpLe = 1; + } + + cmd->ulpClass = CLASS3; + cmd->ulpContext = rxxri; + + ret_val = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, cmdiocbq, 0); + + if (ret_val == IOCB_ERROR) { + diag_cmd_data_free(phba, + (struct lpfc_dmabufext *)mp[0]); + if (mp[1]) + diag_cmd_data_free(phba, + (struct lpfc_dmabufext *)mp[1]); + dmp = list_entry(next, struct lpfc_dmabuf, list); + ret_val = EIO; + goto err_post_rxbufs_exit; + } + + lpfc_sli_ringpostbuf_put(phba, pring, mp[0]); + if (mp[1]) { + lpfc_sli_ringpostbuf_put(phba, pring, mp[1]); + mp[1] = NULL; + } + + /* The iocb was freed by lpfc_sli_issue_iocb */ + cmdiocbq = lpfc_sli_get_iocbq(phba); + if (!cmdiocbq) { + dmp = list_entry(next, struct lpfc_dmabuf, list); + ret_val = EIO; + goto err_post_rxbufs_exit; + } + + cmd = &cmdiocbq->iocb; + i = 0; + } + list_del(&head); + +err_post_rxbufs_exit: + + if (rxbmp) { + if (rxbmp->virt) + lpfc_mbuf_free(phba, rxbmp->virt, rxbmp->phys); + kfree(rxbmp); + } + + if (cmdiocbq) + lpfc_sli_release_iocbq(phba, cmdiocbq); + return ret_val; +} + +/** + * lpfc_bsg_diag_test - with a port in loopback issues a Ct cmd to itself + * @job: LPFC_BSG_VENDOR_DIAG_TEST fc_bsg_job + * + * This function receives a user data buffer to be transmitted and received on + * the same port, the link must be up and in loopback mode prior + * to being called. + * 1. A kernel buffer is allocated to copy the user data into. + * 2. The port registers with "itself". + * 3. The transmit and receive exchange ids are obtained. + * 4. The receive exchange id is posted. + * 5. A new els loopback event is created. + * 6. The command and response iocbs are allocated. + * 7. The cmd iocb FsType is set to elx loopback and the CmdRsp to looppback. + * + * This function is meant to be called n times while the port is in loopback + * so it is the apps responsibility to issue a reset to take the port out + * of loopback mode. + **/ +static int +lpfc_bsg_diag_test(struct fc_bsg_job *job) +{ + struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata; + struct lpfc_hba *phba = vport->phba; + struct diag_mode_test *diag_mode; + struct lpfc_bsg_event *evt; + struct event_data *evdat; + struct lpfc_sli *psli = &phba->sli; + uint32_t size; + uint32_t full_size; + size_t segment_len = 0, segment_offset = 0, current_offset = 0; + uint16_t rpi; + struct lpfc_iocbq *cmdiocbq, *rspiocbq; + IOCB_t *cmd, *rsp; + struct lpfc_sli_ct_request *ctreq; + struct lpfc_dmabuf *txbmp; + struct ulp_bde64 *txbpl = NULL; + struct lpfc_dmabufext *txbuffer = NULL; + struct list_head head; + struct lpfc_dmabuf *curr; + uint16_t txxri, rxxri; + uint32_t num_bde; + uint8_t *ptr = NULL, *rx_databuf = NULL; + int rc = 0; + unsigned long flags; + void *dataout = NULL; + uint32_t total_mem; + + /* in case no data is returned return just the return code */ + job->reply->reply_payload_rcv_len = 0; + + if (job->request_len < + sizeof(struct fc_bsg_request) + sizeof(struct diag_mode_test)) { + lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, + "2739 Received DIAG TEST request below minimum " + "size\n"); + rc = -EINVAL; + goto loopback_test_exit; + } + + if (job->request_payload.payload_len != + job->reply_payload.payload_len) { + rc = -EINVAL; + goto loopback_test_exit; + } + + diag_mode = (struct diag_mode_test *) + job->request->rqst_data.h_vendor.vendor_cmd; + + if ((phba->link_state == LPFC_HBA_ERROR) || + (psli->sli_flag & LPFC_BLOCK_MGMT_IO) || + (!(psli->sli_flag & LPFC_SLI_ACTIVE))) { + rc = -EACCES; + goto loopback_test_exit; + } + + if (!lpfc_is_link_up(phba) || !(phba->link_flag & LS_LOOPBACK_MODE)) { + rc = -EACCES; + goto loopback_test_exit; + } + + size = job->request_payload.payload_len; + full_size = size + ELX_LOOPBACK_HEADER_SZ; /* plus the header */ + + if ((size == 0) || (size > 80 * BUF_SZ_4K)) { + rc = -ERANGE; + goto loopback_test_exit; + } + + if (size >= BUF_SZ_4K) { + /* + * Allocate memory for ioctl data. If buffer is bigger than 64k, + * then we allocate 64k and re-use that buffer over and over to + * xfer the whole block. This is because Linux kernel has a + * problem allocating more than 120k of kernel space memory. Saw + * problem with GET_FCPTARGETMAPPING... + */ + if (size <= (64 * 1024)) + total_mem = size; + else + total_mem = 64 * 1024; + } else + /* Allocate memory for ioctl data */ + total_mem = BUF_SZ_4K; + + dataout = kmalloc(total_mem, GFP_KERNEL); + if (dataout == NULL) { + rc = -ENOMEM; + goto loopback_test_exit; + } + + ptr = dataout; + ptr += ELX_LOOPBACK_HEADER_SZ; + sg_copy_to_buffer(job->request_payload.sg_list, + job->request_payload.sg_cnt, + ptr, size); + + rc = lpfcdiag_loop_self_reg(phba, &rpi); + if (rc) { + rc = -ENOMEM; + goto loopback_test_exit; + } + + rc = lpfcdiag_loop_get_xri(phba, rpi, &txxri, &rxxri); + if (rc) { + lpfcdiag_loop_self_unreg(phba, rpi); + rc = -ENOMEM; + goto loopback_test_exit; + } + + rc = lpfcdiag_loop_post_rxbufs(phba, rxxri, full_size); + if (rc) { + lpfcdiag_loop_self_unreg(phba, rpi); + rc = -ENOMEM; + goto loopback_test_exit; + } + + evt = lpfc_bsg_event_new(FC_REG_CT_EVENT, current->pid, + SLI_CT_ELX_LOOPBACK); + if (!evt) { + lpfcdiag_loop_self_unreg(phba, rpi); + rc = -ENOMEM; + goto loopback_test_exit; + } + + spin_lock_irqsave(&phba->ct_ev_lock, flags); + list_add(&evt->node, &phba->ct_ev_waiters); + lpfc_bsg_event_ref(evt); + spin_unlock_irqrestore(&phba->ct_ev_lock, flags); + + cmdiocbq = lpfc_sli_get_iocbq(phba); + rspiocbq = lpfc_sli_get_iocbq(phba); + txbmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL); + + if (txbmp) { + txbmp->virt = lpfc_mbuf_alloc(phba, 0, &txbmp->phys); + INIT_LIST_HEAD(&txbmp->list); + txbpl = (struct ulp_bde64 *) txbmp->virt; + if (txbpl) + txbuffer = diag_cmd_data_alloc(phba, + txbpl, full_size, 0); + } + + if (!cmdiocbq || !rspiocbq || !txbmp || !txbpl || !txbuffer) { + rc = -ENOMEM; + goto err_loopback_test_exit; + } + + cmd = &cmdiocbq->iocb; + rsp = &rspiocbq->iocb; + + INIT_LIST_HEAD(&head); + list_add_tail(&head, &txbuffer->dma.list); + list_for_each_entry(curr, &head, list) { + segment_len = ((struct lpfc_dmabufext *)curr)->size; + if (current_offset == 0) { + ctreq = curr->virt; + memset(ctreq, 0, ELX_LOOPBACK_HEADER_SZ); + ctreq->RevisionId.bits.Revision = SLI_CT_REVISION; + ctreq->RevisionId.bits.InId = 0; + ctreq->FsType = SLI_CT_ELX_LOOPBACK; + ctreq->FsSubType = 0; + ctreq->CommandResponse.bits.CmdRsp = ELX_LOOPBACK_DATA; + ctreq->CommandResponse.bits.Size = size; + segment_offset = ELX_LOOPBACK_HEADER_SZ; + } else + segment_offset = 0; + + BUG_ON(segment_offset >= segment_len); + memcpy(curr->virt + segment_offset, + ptr + current_offset, + segment_len - segment_offset); + + current_offset += segment_len - segment_offset; + BUG_ON(current_offset > size); + } + list_del(&head); + + /* Build the XMIT_SEQUENCE iocb */ + + num_bde = (uint32_t)txbuffer->flag; + + cmd->un.xseq64.bdl.addrHigh = putPaddrHigh(txbmp->phys); + cmd->un.xseq64.bdl.addrLow = putPaddrLow(txbmp->phys); + cmd->un.xseq64.bdl.bdeFlags = BUFF_TYPE_BLP_64; + cmd->un.xseq64.bdl.bdeSize = (num_bde * sizeof(struct ulp_bde64)); + + cmd->un.xseq64.w5.hcsw.Fctl = (LS | LA); + cmd->un.xseq64.w5.hcsw.Dfctl = 0; + cmd->un.xseq64.w5.hcsw.Rctl = FC_RCTL_DD_UNSOL_CTL; + cmd->un.xseq64.w5.hcsw.Type = FC_TYPE_CT; + + cmd->ulpCommand = CMD_XMIT_SEQUENCE64_CX; + cmd->ulpBdeCount = 1; + cmd->ulpLe = 1; + cmd->ulpClass = CLASS3; + cmd->ulpContext = txxri; + + cmdiocbq->iocb_flag |= LPFC_IO_LIBDFC; + cmdiocbq->vport = phba->pport; + + rc = lpfc_sli_issue_iocb_wait(phba, LPFC_ELS_RING, cmdiocbq, rspiocbq, + (phba->fc_ratov * 2) + LPFC_DRVR_TIMEOUT); + + if ((rc != IOCB_SUCCESS) || (rsp->ulpStatus != IOCB_SUCCESS)) { + rc = -EIO; + goto err_loopback_test_exit; + } + + evt->waiting = 1; + rc = wait_event_interruptible_timeout( + evt->wq, !list_empty(&evt->events_to_see), + ((phba->fc_ratov * 2) + LPFC_DRVR_TIMEOUT) * HZ); + evt->waiting = 0; + if (list_empty(&evt->events_to_see)) + rc = (rc) ? -EINTR : -ETIMEDOUT; + else { + spin_lock_irqsave(&phba->ct_ev_lock, flags); + list_move(evt->events_to_see.prev, &evt->events_to_get); + evdat = list_entry(evt->events_to_get.prev, + typeof(*evdat), node); + spin_unlock_irqrestore(&phba->ct_ev_lock, flags); + rx_databuf = evdat->data; + if (evdat->len != full_size) { + lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC, + "1603 Loopback test did not receive expected " + "data length. actual length 0x%x expected " + "length 0x%x\n", + evdat->len, full_size); + rc = -EIO; + } else if (rx_databuf == NULL) + rc = -EIO; + else { + rc = IOCB_SUCCESS; + /* skip over elx loopback header */ + rx_databuf += ELX_LOOPBACK_HEADER_SZ; + job->reply->reply_payload_rcv_len = + sg_copy_from_buffer(job->reply_payload.sg_list, + job->reply_payload.sg_cnt, + rx_databuf, size); + job->reply->reply_payload_rcv_len = size; + } + } + +err_loopback_test_exit: + lpfcdiag_loop_self_unreg(phba, rpi); + + spin_lock_irqsave(&phba->ct_ev_lock, flags); + lpfc_bsg_event_unref(evt); /* release ref */ + lpfc_bsg_event_unref(evt); /* delete */ + spin_unlock_irqrestore(&phba->ct_ev_lock, flags); + + if (cmdiocbq != NULL) + lpfc_sli_release_iocbq(phba, cmdiocbq); + + if (rspiocbq != NULL) + lpfc_sli_release_iocbq(phba, rspiocbq); + + if (txbmp != NULL) { + if (txbpl != NULL) { + if (txbuffer != NULL) + diag_cmd_data_free(phba, txbuffer); + lpfc_mbuf_free(phba, txbmp->virt, txbmp->phys); + } + kfree(txbmp); + } + +loopback_test_exit: + kfree(dataout); + /* make error code available to userspace */ + job->reply->result = rc; + job->dd_data = NULL; + /* complete the job back to userspace if no error */ + if (rc == 0) + job->job_done(job); + return rc; +} + +/** + * lpfc_bsg_get_dfc_rev - process a GET_DFC_REV bsg vendor command + * @job: GET_DFC_REV fc_bsg_job + **/ +static int +lpfc_bsg_get_dfc_rev(struct fc_bsg_job *job) +{ + struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata; + struct lpfc_hba *phba = vport->phba; + struct get_mgmt_rev *event_req; + struct get_mgmt_rev_reply *event_reply; + int rc = 0; + + if (job->request_len < + sizeof(struct fc_bsg_request) + sizeof(struct get_mgmt_rev)) { + lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, + "2740 Received GET_DFC_REV request below " + "minimum size\n"); + rc = -EINVAL; + goto job_error; + } + + event_req = (struct get_mgmt_rev *) + job->request->rqst_data.h_vendor.vendor_cmd; + + event_reply = (struct get_mgmt_rev_reply *) + job->reply->reply_data.vendor_reply.vendor_rsp; + + if (job->reply_len < + sizeof(struct fc_bsg_request) + sizeof(struct get_mgmt_rev_reply)) { + lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, + "2741 Received GET_DFC_REV reply below " + "minimum size\n"); + rc = -EINVAL; + goto job_error; + } + + event_reply->info.a_Major = MANAGEMENT_MAJOR_REV; + event_reply->info.a_Minor = MANAGEMENT_MINOR_REV; +job_error: + job->reply->result = rc; + if (rc == 0) + job->job_done(job); + return rc; +} + +/** + * lpfc_bsg_wake_mbox_wait - lpfc_bsg_issue_mbox mbox completion handler + * @phba: Pointer to HBA context object. + * @pmboxq: Pointer to mailbox command. + * + * This is completion handler function for mailbox commands issued from + * lpfc_bsg_issue_mbox function. This function is called by the + * mailbox event handler function with no lock held. This function + * will wake up thread waiting on the wait queue pointed by context1 + * of the mailbox. + **/ +void +lpfc_bsg_wake_mbox_wait(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq) +{ + struct bsg_job_data *dd_data; + MAILBOX_t *pmb; + MAILBOX_t *mb; + struct fc_bsg_job *job; + uint32_t size; + unsigned long flags; + + spin_lock_irqsave(&phba->ct_ev_lock, flags); + dd_data = pmboxq->context1; + if (!dd_data) { + spin_unlock_irqrestore(&phba->ct_ev_lock, flags); + return; + } + + pmb = &dd_data->context_un.mbox.pmboxq->u.mb; + mb = dd_data->context_un.mbox.mb; + job = dd_data->context_un.mbox.set_job; + memcpy(mb, pmb, sizeof(*pmb)); + size = job->request_payload.payload_len; + job->reply->reply_payload_rcv_len = + sg_copy_from_buffer(job->reply_payload.sg_list, + job->reply_payload.sg_cnt, + mb, size); + job->reply->result = 0; + dd_data->context_un.mbox.set_job = NULL; + job->dd_data = NULL; + job->job_done(job); + spin_unlock_irqrestore(&phba->ct_ev_lock, flags); + mempool_free(dd_data->context_un.mbox.pmboxq, phba->mbox_mem_pool); + kfree(mb); + kfree(dd_data); + return; +} + +/** + * lpfc_bsg_check_cmd_access - test for a supported mailbox command + * @phba: Pointer to HBA context object. + * @mb: Pointer to a mailbox object. + * @vport: Pointer to a vport object. + * + * Some commands require the port to be offline, some may not be called from + * the application. + **/ +static int lpfc_bsg_check_cmd_access(struct lpfc_hba *phba, + MAILBOX_t *mb, struct lpfc_vport *vport) +{ + /* return negative error values for bsg job */ + switch (mb->mbxCommand) { + /* Offline only */ + case MBX_INIT_LINK: + case MBX_DOWN_LINK: + case MBX_CONFIG_LINK: + case MBX_CONFIG_RING: + case MBX_RESET_RING: + case MBX_UNREG_LOGIN: + case MBX_CLEAR_LA: + case MBX_DUMP_CONTEXT: + case MBX_RUN_DIAGS: + case MBX_RESTART: + case MBX_SET_MASK: + if (!(vport->fc_flag & FC_OFFLINE_MODE)) { + lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, + "2743 Command 0x%x is illegal in on-line " + "state\n", + mb->mbxCommand); + return -EPERM; + } + case MBX_WRITE_NV: + case MBX_WRITE_VPARMS: + case MBX_LOAD_SM: + case MBX_READ_NV: + case MBX_READ_CONFIG: + case MBX_READ_RCONFIG: + case MBX_READ_STATUS: + case MBX_READ_XRI: + case MBX_READ_REV: + case MBX_READ_LNK_STAT: + case MBX_DUMP_MEMORY: + case MBX_DOWN_LOAD: + case MBX_UPDATE_CFG: + case MBX_KILL_BOARD: + case MBX_LOAD_AREA: + case MBX_LOAD_EXP_ROM: + case MBX_BEACON: + case MBX_DEL_LD_ENTRY: + case MBX_SET_DEBUG: + case MBX_WRITE_WWN: + case MBX_SLI4_CONFIG: + case MBX_READ_EVENT_LOG_STATUS: + case MBX_WRITE_EVENT_LOG: + case MBX_PORT_CAPABILITIES: + case MBX_PORT_IOV_CONTROL: + break; + case MBX_SET_VARIABLE: + case MBX_RUN_BIU_DIAG64: + case MBX_READ_EVENT_LOG: + case MBX_READ_SPARM64: + case MBX_READ_LA: + case MBX_READ_LA64: + case MBX_REG_LOGIN: + case MBX_REG_LOGIN64: + case MBX_CONFIG_PORT: + case MBX_RUN_BIU_DIAG: + default: + lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, + "2742 Unknown Command 0x%x\n", + mb->mbxCommand); + return -EPERM; + } + + return 0; /* ok */ +} + +/** + * lpfc_bsg_issue_mbox - issues a mailbox command on behalf of an app + * @phba: Pointer to HBA context object. + * @mb: Pointer to a mailbox object. + * @vport: Pointer to a vport object. + * + * Allocate a tracking object, mailbox command memory, get a mailbox + * from the mailbox pool, copy the caller mailbox command. + * + * If offline and the sli is active we need to poll for the command (port is + * being reset) and com-plete the job, otherwise issue the mailbox command and + * let our completion handler finish the command. + **/ +static uint32_t +lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job, + struct lpfc_vport *vport) +{ + LPFC_MBOXQ_t *pmboxq; + MAILBOX_t *pmb; + MAILBOX_t *mb; + struct bsg_job_data *dd_data; + uint32_t size; + int rc = 0; + + /* allocate our bsg tracking structure */ + dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL); + if (!dd_data) { + lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, + "2727 Failed allocation of dd_data\n"); + return -ENOMEM; + } + + mb = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!mb) { + kfree(dd_data); + return -ENOMEM; + } + + pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); + if (!pmboxq) { + kfree(dd_data); + kfree(mb); + return -ENOMEM; + } + + size = job->request_payload.payload_len; + job->reply->reply_payload_rcv_len = + sg_copy_to_buffer(job->request_payload.sg_list, + job->request_payload.sg_cnt, + mb, size); + + rc = lpfc_bsg_check_cmd_access(phba, mb, vport); + if (rc != 0) { + kfree(dd_data); + kfree(mb); + mempool_free(pmboxq, phba->mbox_mem_pool); + return rc; /* must be negative */ + } + + memset(pmboxq, 0, sizeof(LPFC_MBOXQ_t)); + pmb = &pmboxq->u.mb; + memcpy(pmb, mb, sizeof(*pmb)); + pmb->mbxOwner = OWN_HOST; + pmboxq->context1 = NULL; + pmboxq->vport = vport; + + if ((vport->fc_flag & FC_OFFLINE_MODE) || + (!(phba->sli.sli_flag & LPFC_SLI_ACTIVE))) { + rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_POLL); + if (rc != MBX_SUCCESS) { + if (rc != MBX_TIMEOUT) { + kfree(dd_data); + kfree(mb); + mempool_free(pmboxq, phba->mbox_mem_pool); + } + return (rc == MBX_TIMEOUT) ? -ETIME : -ENODEV; + } + + memcpy(mb, pmb, sizeof(*pmb)); + job->reply->reply_payload_rcv_len = + sg_copy_from_buffer(job->reply_payload.sg_list, + job->reply_payload.sg_cnt, + mb, size); + kfree(dd_data); + kfree(mb); + mempool_free(pmboxq, phba->mbox_mem_pool); + /* not waiting mbox already done */ + return 0; + } + + /* setup wake call as IOCB callback */ + pmboxq->mbox_cmpl = lpfc_bsg_wake_mbox_wait; + /* setup context field to pass wait_queue pointer to wake function */ + pmboxq->context1 = dd_data; + dd_data->type = TYPE_MBOX; + dd_data->context_un.mbox.pmboxq = pmboxq; + dd_data->context_un.mbox.mb = mb; + dd_data->context_un.mbox.set_job = job; + job->dd_data = dd_data; + rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_NOWAIT); + if ((rc != MBX_SUCCESS) && (rc != MBX_BUSY)) { + kfree(dd_data); + kfree(mb); + mempool_free(pmboxq, phba->mbox_mem_pool); + return -EIO; + } + + return 1; +} + +/** + * lpfc_bsg_mbox_cmd - process an fc bsg LPFC_BSG_VENDOR_MBOX command + * @job: MBOX fc_bsg_job for LPFC_BSG_VENDOR_MBOX. + **/ +static int +lpfc_bsg_mbox_cmd(struct fc_bsg_job *job) +{ + struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata; + struct lpfc_hba *phba = vport->phba; + int rc = 0; + + /* in case no data is transferred */ + job->reply->reply_payload_rcv_len = 0; + if (job->request_len < + sizeof(struct fc_bsg_request) + sizeof(struct dfc_mbox_req)) { + lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, + "2737 Received MBOX_REQ request below " + "minimum size\n"); + rc = -EINVAL; + goto job_error; + } + + if (job->request_payload.payload_len != PAGE_SIZE) { + rc = -EINVAL; + goto job_error; + } + + if (phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO) { + rc = -EAGAIN; + goto job_error; + } + + rc = lpfc_bsg_issue_mbox(phba, job, vport); + +job_error: + if (rc == 0) { + /* job done */ + job->reply->result = 0; + job->dd_data = NULL; + job->job_done(job); + } else if (rc == 1) + /* job submitted, will complete later*/ + rc = 0; /* return zero, no error */ + else { + /* some error occurred */ + job->reply->result = rc; + job->dd_data = NULL; + } return rc; } @@ -834,38 +2640,57 @@ error_get_event_exit: /** * lpfc_bsg_hst_vendor - process a vendor-specific fc_bsg_job * @job: fc_bsg_job to handle - */ + **/ static int lpfc_bsg_hst_vendor(struct fc_bsg_job *job) { int command = job->request->rqst_data.h_vendor.vendor_cmd[0]; + int rc; switch (command) { case LPFC_BSG_VENDOR_SET_CT_EVENT: - return lpfc_bsg_set_event(job); + rc = lpfc_bsg_hba_set_event(job); break; - case LPFC_BSG_VENDOR_GET_CT_EVENT: - return lpfc_bsg_get_event(job); + rc = lpfc_bsg_hba_get_event(job); + break; + case LPFC_BSG_VENDOR_SEND_MGMT_RESP: + rc = lpfc_bsg_send_mgmt_rsp(job); + break; + case LPFC_BSG_VENDOR_DIAG_MODE: + rc = lpfc_bsg_diag_mode(job); + break; + case LPFC_BSG_VENDOR_DIAG_TEST: + rc = lpfc_bsg_diag_test(job); + break; + case LPFC_BSG_VENDOR_GET_MGMT_REV: + rc = lpfc_bsg_get_dfc_rev(job); + break; + case LPFC_BSG_VENDOR_MBOX: + rc = lpfc_bsg_mbox_cmd(job); break; - default: - return -EINVAL; + rc = -EINVAL; + job->reply->reply_payload_rcv_len = 0; + /* make error code available to userspace */ + job->reply->result = rc; + break; } + + return rc; } /** * lpfc_bsg_request - handle a bsg request from the FC transport * @job: fc_bsg_job to handle - */ + **/ int lpfc_bsg_request(struct fc_bsg_job *job) { uint32_t msgcode; - int rc = -EINVAL; + int rc; msgcode = job->request->msgcode; - switch (msgcode) { case FC_BSG_HST_VENDOR: rc = lpfc_bsg_hst_vendor(job); @@ -874,9 +2699,13 @@ lpfc_bsg_request(struct fc_bsg_job *job) rc = lpfc_bsg_rport_els(job); break; case FC_BSG_RPT_CT: - rc = lpfc_bsg_rport_ct(job); + rc = lpfc_bsg_send_mgmt_cmd(job); break; default: + rc = -EINVAL; + job->reply->reply_payload_rcv_len = 0; + /* make error code available to userspace */ + job->reply->result = rc; break; } @@ -889,17 +2718,71 @@ lpfc_bsg_request(struct fc_bsg_job *job) * * This function just aborts the job's IOCB. The aborted IOCB will return to * the waiting function which will handle passing the error back to userspace - */ + **/ int lpfc_bsg_timeout(struct fc_bsg_job *job) { struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata; struct lpfc_hba *phba = vport->phba; - struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *)job->dd_data; + struct lpfc_iocbq *cmdiocb; + struct lpfc_bsg_event *evt; + struct lpfc_bsg_iocb *iocb; + struct lpfc_bsg_mbox *mbox; struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING]; + struct bsg_job_data *dd_data; + unsigned long flags; + + spin_lock_irqsave(&phba->ct_ev_lock, flags); + dd_data = (struct bsg_job_data *)job->dd_data; + /* timeout and completion crossed paths if no dd_data */ + if (!dd_data) { + spin_unlock_irqrestore(&phba->ct_ev_lock, flags); + return 0; + } - if (cmdiocb) + switch (dd_data->type) { + case TYPE_IOCB: + iocb = &dd_data->context_un.iocb; + cmdiocb = iocb->cmdiocbq; + /* hint to completion handler that the job timed out */ + job->reply->result = -EAGAIN; + spin_unlock_irqrestore(&phba->ct_ev_lock, flags); + /* this will call our completion handler */ + spin_lock_irq(&phba->hbalock); lpfc_sli_issue_abort_iotag(phba, pring, cmdiocb); + spin_unlock_irq(&phba->hbalock); + break; + case TYPE_EVT: + evt = dd_data->context_un.evt; + /* this event has no job anymore */ + evt->set_job = NULL; + job->dd_data = NULL; + job->reply->reply_payload_rcv_len = 0; + /* Return -EAGAIN which is our way of signallying the + * app to retry. + */ + job->reply->result = -EAGAIN; + spin_unlock_irqrestore(&phba->ct_ev_lock, flags); + job->job_done(job); + break; + case TYPE_MBOX: + mbox = &dd_data->context_un.mbox; + /* this mbox has no job anymore */ + mbox->set_job = NULL; + job->dd_data = NULL; + job->reply->reply_payload_rcv_len = 0; + job->reply->result = -EAGAIN; + spin_unlock_irqrestore(&phba->ct_ev_lock, flags); + job->job_done(job); + break; + default: + spin_unlock_irqrestore(&phba->ct_ev_lock, flags); + break; + } + /* scsi transport fc fc_bsg_job_timeout expects a zero return code, + * otherwise an error message will be displayed on the console + * so always return success (zero) + */ return 0; } diff --git a/drivers/scsi/lpfc/lpfc_bsg.h b/drivers/scsi/lpfc/lpfc_bsg.h new file mode 100644 index 00000000000..6c8f87e39b9 --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_bsg.h @@ -0,0 +1,98 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2010 Emulex. All rights reserved. * + * EMULEX and SLI are trademarks of Emulex. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of version 2 of the GNU General * + * Public License as published by the Free Software Foundation. * + * This program is distributed in the hope that it will be useful. * + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * + * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * + * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * + * TO BE LEGALLY INVALID. See the GNU General Public License for * + * more details, a copy of which can be found in the file COPYING * + * included with this package. * + *******************************************************************/ +/* bsg definitions + * No pointers to user data are allowed, all application buffers and sizes will + * derived through the bsg interface. + * + * These are the vendor unique structures passed in using the bsg + * FC_BSG_HST_VENDOR message code type. + */ +#define LPFC_BSG_VENDOR_SET_CT_EVENT 1 +#define LPFC_BSG_VENDOR_GET_CT_EVENT 2 +#define LPFC_BSG_VENDOR_SEND_MGMT_RESP 3 +#define LPFC_BSG_VENDOR_DIAG_MODE 4 +#define LPFC_BSG_VENDOR_DIAG_TEST 5 +#define LPFC_BSG_VENDOR_GET_MGMT_REV 6 +#define LPFC_BSG_VENDOR_MBOX 7 + +struct set_ct_event { + uint32_t command; + uint32_t type_mask; + uint32_t ev_req_id; + uint32_t ev_reg_id; +}; + +struct get_ct_event { + uint32_t command; + uint32_t ev_reg_id; + uint32_t ev_req_id; +}; + +struct get_ct_event_reply { + uint32_t immed_data; + uint32_t type; +}; + +struct send_mgmt_resp { + uint32_t command; + uint32_t tag; +}; + + +#define INTERNAL_LOOP_BACK 0x1 /* adapter short cuts the loop internally */ +#define EXTERNAL_LOOP_BACK 0x2 /* requires an external loopback plug */ + +struct diag_mode_set { + uint32_t command; + uint32_t type; + uint32_t timeout; +}; + +struct diag_mode_test { + uint32_t command; +}; + +#define LPFC_WWNN_TYPE 0 +#define LPFC_WWPN_TYPE 1 + +struct get_mgmt_rev { + uint32_t command; +}; + +#define MANAGEMENT_MAJOR_REV 1 +#define MANAGEMENT_MINOR_REV 0 + +/* the MgmtRevInfo structure */ +struct MgmtRevInfo { + uint32_t a_Major; + uint32_t a_Minor; +}; + +struct get_mgmt_rev_reply { + struct MgmtRevInfo info; +}; + +struct dfc_mbox_req { + uint32_t command; + uint32_t inExtWLen; + uint32_t outExtWLen; + uint8_t mbOffset; +}; + diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h index 650494d622c..6f0fb51eb46 100644 --- a/drivers/scsi/lpfc/lpfc_crtn.h +++ b/drivers/scsi/lpfc/lpfc_crtn.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2008 Emulex. All rights reserved. * + * Copyright (C) 2004-2010 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * * @@ -44,18 +44,26 @@ int lpfc_reg_rpi(struct lpfc_hba *, uint16_t, uint32_t, uint8_t *, void lpfc_unreg_login(struct lpfc_hba *, uint16_t, uint32_t, LPFC_MBOXQ_t *); void lpfc_unreg_did(struct lpfc_hba *, uint16_t, uint32_t, LPFC_MBOXQ_t *); void lpfc_reg_vpi(struct lpfc_vport *, LPFC_MBOXQ_t *); +void lpfc_register_new_vport(struct lpfc_hba *, struct lpfc_vport *, + struct lpfc_nodelist *); void lpfc_unreg_vpi(struct lpfc_hba *, uint16_t, LPFC_MBOXQ_t *); void lpfc_init_link(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t, uint32_t); void lpfc_request_features(struct lpfc_hba *, struct lpfcMboxq *); +void lpfc_supported_pages(struct lpfcMboxq *); +void lpfc_sli4_params(struct lpfcMboxq *); +int lpfc_pc_sli4_params_get(struct lpfc_hba *, LPFC_MBOXQ_t *); struct lpfc_vport *lpfc_find_vport_by_did(struct lpfc_hba *, uint32_t); void lpfc_cleanup_rcv_buffers(struct lpfc_vport *); void lpfc_rcv_seq_check_edtov(struct lpfc_vport *); void lpfc_cleanup_rpis(struct lpfc_vport *, int); +void lpfc_cleanup_pending_mbox(struct lpfc_vport *); int lpfc_linkdown(struct lpfc_hba *); void lpfc_linkdown_port(struct lpfc_vport *); void lpfc_port_link_failure(struct lpfc_vport *); void lpfc_mbx_cmpl_read_la(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_init_vpi_cmpl(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_retry_pport_discovery(struct lpfc_hba *); void lpfc_mbx_cmpl_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *); void lpfc_mbx_cmpl_dflt_rpi(struct lpfc_hba *, LPFC_MBOXQ_t *); @@ -73,6 +81,7 @@ void lpfc_set_disctmo(struct lpfc_vport *); int lpfc_can_disctmo(struct lpfc_vport *); int lpfc_unreg_rpi(struct lpfc_vport *, struct lpfc_nodelist *); void lpfc_unreg_all_rpis(struct lpfc_vport *); +void lpfc_unreg_hba_rpis(struct lpfc_hba *); void lpfc_unreg_default_rpis(struct lpfc_vport *); void lpfc_issue_reg_vpi(struct lpfc_hba *, struct lpfc_vport *); @@ -99,7 +108,7 @@ int lpfc_disc_state_machine(struct lpfc_vport *, struct lpfc_nodelist *, void *, void lpfc_do_scr_ns_plogi(struct lpfc_hba *, struct lpfc_vport *); int lpfc_check_sparm(struct lpfc_vport *, struct lpfc_nodelist *, - struct serv_parm *, uint32_t); + struct serv_parm *, uint32_t, int); int lpfc_els_abort(struct lpfc_hba *, struct lpfc_nodelist *); void lpfc_more_plogi(struct lpfc_vport *); void lpfc_more_adisc(struct lpfc_vport *); @@ -197,6 +206,7 @@ void lpfc_reg_fcfi(struct lpfc_hba *, struct lpfcMboxq *); void lpfc_unreg_fcfi(struct lpfcMboxq *, uint16_t); void lpfc_resume_rpi(struct lpfcMboxq *, struct lpfc_nodelist *); int lpfc_check_pending_fcoe_event(struct lpfc_hba *, uint8_t); +void lpfc_issue_init_vpi(struct lpfc_vport *); void lpfc_config_hbq(struct lpfc_hba *, uint32_t, struct lpfc_hbq_init *, uint32_t , LPFC_MBOXQ_t *); @@ -206,7 +216,11 @@ struct hbq_dmabuf *lpfc_sli4_rb_alloc(struct lpfc_hba *); void lpfc_sli4_rb_free(struct lpfc_hba *, struct hbq_dmabuf *); void lpfc_sli4_build_dflt_fcf_record(struct lpfc_hba *, struct fcf_record *, uint16_t); +void lpfc_unregister_fcf(struct lpfc_hba *); +void lpfc_unregister_fcf_rescan(struct lpfc_hba *); void lpfc_unregister_unused_fcf(struct lpfc_hba *); +int lpfc_sli4_redisc_fcf_table(struct lpfc_hba *); +void lpfc_fcf_redisc_wait_start_timer(struct lpfc_hba *); int lpfc_mem_alloc(struct lpfc_hba *, int align); void lpfc_mem_free(struct lpfc_hba *); @@ -365,6 +379,8 @@ void lpfc_free_fast_evt(struct lpfc_hba *, struct lpfc_fast_path_event *); void lpfc_create_static_vport(struct lpfc_hba *); void lpfc_stop_hba_timers(struct lpfc_hba *); void lpfc_stop_port(struct lpfc_hba *); +void __lpfc_sli4_stop_fcf_redisc_wait_timer(struct lpfc_hba *); +void lpfc_sli4_stop_fcf_redisc_wait_timer(struct lpfc_hba *); void lpfc_parse_fcoe_conf(struct lpfc_hba *, uint8_t *, uint32_t); int lpfc_parse_vpd(struct lpfc_hba *, uint8_t *, int); void lpfc_start_fdiscs(struct lpfc_hba *phba); @@ -378,5 +394,5 @@ struct lpfc_vport *lpfc_find_vport_by_vpid(struct lpfc_hba *, uint16_t); /* functions to support SGIOv4/bsg interface */ int lpfc_bsg_request(struct fc_bsg_job *); int lpfc_bsg_timeout(struct fc_bsg_job *); -void lpfc_bsg_ct_unsol_event(struct lpfc_hba *, struct lpfc_sli_ring *, +int lpfc_bsg_ct_unsol_event(struct lpfc_hba *, struct lpfc_sli_ring *, struct lpfc_iocbq *); diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c index 0ebcd9baca7..c7e921973f6 100644 --- a/drivers/scsi/lpfc/lpfc_ct.c +++ b/drivers/scsi/lpfc/lpfc_ct.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2009 Emulex. All rights reserved. * + * Copyright (C) 2004-2010 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * * @@ -97,7 +97,8 @@ lpfc_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, struct list_head head; struct lpfc_dmabuf *bdeBuf; - lpfc_bsg_ct_unsol_event(phba, pring, piocbq); + if (lpfc_bsg_ct_unsol_event(phba, pring, piocbq) == 0) + return; if (unlikely(icmd->ulpStatus == IOSTAT_NEED_BUFFER)) { lpfc_sli_hbqbuf_add_hbqs(phba, LPFC_ELS_HBQ); @@ -181,7 +182,8 @@ lpfc_sli4_ct_abort_unsol_event(struct lpfc_hba *phba, uint32_t size; /* Forward abort event to any process registered to receive ct event */ - lpfc_bsg_ct_unsol_event(phba, pring, piocbq); + if (lpfc_bsg_ct_unsol_event(phba, pring, piocbq) == 0) + return; /* If there is no BDE associated with IOCB, there is nothing to do */ if (icmd->ulpBdeCount == 0) @@ -1843,12 +1845,7 @@ lpfc_decode_firmware_rev(struct lpfc_hba *phba, char *fwrevision, int flag) c = (rev & 0x0000ff00) >> 8; b4 = (rev & 0x000000ff); - if (flag) - sprintf(fwrevision, "%d.%d%d%c%d ", b1, - b2, b3, c, b4); - else - sprintf(fwrevision, "%d.%d%d%c%d ", b1, - b2, b3, c, b4); + sprintf(fwrevision, "%d.%d%d%c%d", b1, b2, b3, c, b4); } return; } diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index 2cc39684ce9..08b6634cb99 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -50,9 +50,6 @@ static int lpfc_issue_els_fdisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, uint8_t retry); static int lpfc_issue_fabric_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *iocb); -static void lpfc_register_new_vport(struct lpfc_hba *phba, - struct lpfc_vport *vport, - struct lpfc_nodelist *ndlp); static int lpfc_max_els_tries = 3; @@ -592,6 +589,15 @@ lpfc_cmpl_els_flogi_fabric(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, vport->fc_flag |= FC_VPORT_NEEDS_REG_VPI; spin_unlock_irq(shost->host_lock); } + /* + * If VPI is unreged, driver need to do INIT_VPI + * before re-registering + */ + if (phba->sli_rev == LPFC_SLI_REV4) { + spin_lock_irq(shost->host_lock); + vport->fc_flag |= FC_VPORT_NEEDS_INIT_VPI; + spin_unlock_irq(shost->host_lock); + } } if (phba->sli_rev < LPFC_SLI_REV4) { @@ -604,10 +610,13 @@ lpfc_cmpl_els_flogi_fabric(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, } else { ndlp->nlp_type |= NLP_FABRIC; lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE); - if (vport->vpi_state & LPFC_VPI_REGISTERED) { + if ((!(vport->fc_flag & FC_VPORT_NEEDS_REG_VPI)) && + (vport->vpi_state & LPFC_VPI_REGISTERED)) { lpfc_start_fdiscs(phba); lpfc_do_scr_ns_plogi(phba, vport); - } else + } else if (vport->fc_flag & FC_VFI_REGISTERED) + lpfc_issue_init_vpi(vport); + else lpfc_issue_reg_vfi(vport); } return 0; @@ -804,6 +813,9 @@ lpfc_cmpl_els_flogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, irsp->ulpTimeout); goto flogifail; } + spin_lock_irq(shost->host_lock); + vport->fc_flag &= ~FC_VPORT_CVL_RCVD; + spin_unlock_irq(shost->host_lock); /* * The FLogI succeeded. Sync the data for the CPU before @@ -2720,7 +2732,7 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, if (did == FDMI_DID) retry = 1; - if ((cmd == ELS_CMD_FLOGI) && + if (((cmd == ELS_CMD_FLOGI) || (cmd == ELS_CMD_FDISC)) && (phba->fc_topology != TOPOLOGY_LOOP) && !lpfc_error_lost_link(irsp)) { /* FLOGI retry policy */ @@ -4385,7 +4397,7 @@ lpfc_els_rcv_flogi(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, did = Fabric_DID; - if ((lpfc_check_sparm(vport, ndlp, sp, CLASS3))) { + if ((lpfc_check_sparm(vport, ndlp, sp, CLASS3, 1))) { /* For a FLOGI we accept, then if our portname is greater * then the remote portname we initiate Nport login. */ @@ -5915,6 +5927,7 @@ lpfc_cmpl_reg_new_vport(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) struct Scsi_Host *shost = lpfc_shost_from_vport(vport); struct lpfc_nodelist *ndlp = (struct lpfc_nodelist *) pmb->context2; MAILBOX_t *mb = &pmb->u.mb; + int rc; spin_lock_irq(shost->host_lock); vport->fc_flag &= ~FC_VPORT_NEEDS_REG_VPI; @@ -5936,6 +5949,26 @@ lpfc_cmpl_reg_new_vport(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) spin_unlock_irq(shost->host_lock); lpfc_can_disctmo(vport); break; + /* If reg_vpi fail with invalid VPI status, re-init VPI */ + case 0x20: + spin_lock_irq(shost->host_lock); + vport->fc_flag |= FC_VPORT_NEEDS_REG_VPI; + spin_unlock_irq(shost->host_lock); + lpfc_init_vpi(phba, pmb, vport->vpi); + pmb->vport = vport; + pmb->mbox_cmpl = lpfc_init_vpi_cmpl; + rc = lpfc_sli_issue_mbox(phba, pmb, + MBX_NOWAIT); + if (rc == MBX_NOT_FINISHED) { + lpfc_printf_vlog(vport, + KERN_ERR, LOG_MBOX, + "2732 Failed to issue INIT_VPI" + " mailbox command\n"); + } else { + lpfc_nlp_put(ndlp); + return; + } + default: /* Try to recover from this error */ lpfc_mbx_unreg_vpi(vport); @@ -5949,13 +5982,17 @@ lpfc_cmpl_reg_new_vport(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) break; } } else { + spin_lock_irq(shost->host_lock); vport->vpi_state |= LPFC_VPI_REGISTERED; - if (vport == phba->pport) + spin_unlock_irq(shost->host_lock); + if (vport == phba->pport) { if (phba->sli_rev < LPFC_SLI_REV4) lpfc_issue_fabric_reglogin(vport); - else - lpfc_issue_reg_vfi(vport); - else + else { + lpfc_start_fdiscs(phba); + lpfc_do_scr_ns_plogi(phba, vport); + } + } else lpfc_do_scr_ns_plogi(phba, vport); } @@ -5977,7 +6014,7 @@ lpfc_cmpl_reg_new_vport(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) * This routine registers the @vport as a new virtual port with a HBA. * It is done through a registering vpi mailbox command. **/ -static void +void lpfc_register_new_vport(struct lpfc_hba *phba, struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) { @@ -6018,6 +6055,78 @@ mbox_err_exit: } /** + * lpfc_retry_pport_discovery - Start timer to retry FLOGI. + * @phba: pointer to lpfc hba data structure. + * + * This routine abort all pending discovery commands and + * start a timer to retry FLOGI for the physical port + * discovery. + **/ +void +lpfc_retry_pport_discovery(struct lpfc_hba *phba) +{ + struct lpfc_vport **vports; + struct lpfc_nodelist *ndlp; + struct Scsi_Host *shost; + int i; + uint32_t link_state; + + /* Treat this failure as linkdown for all vports */ + link_state = phba->link_state; + lpfc_linkdown(phba); + phba->link_state = link_state; + + vports = lpfc_create_vport_work_array(phba); + + if (vports) { + for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) { + ndlp = lpfc_findnode_did(vports[i], Fabric_DID); + if (ndlp) + lpfc_cancel_retry_delay_tmo(vports[i], ndlp); + lpfc_els_flush_cmd(vports[i]); + } + lpfc_destroy_vport_work_array(phba, vports); + } + + /* If fabric require FLOGI, then re-instantiate physical login */ + ndlp = lpfc_findnode_did(phba->pport, Fabric_DID); + if (!ndlp) + return; + + + shost = lpfc_shost_from_vport(phba->pport); + mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ); + spin_lock_irq(shost->host_lock); + ndlp->nlp_flag |= NLP_DELAY_TMO; + spin_unlock_irq(shost->host_lock); + ndlp->nlp_last_elscmd = ELS_CMD_FLOGI; + phba->pport->port_state = LPFC_FLOGI; + return; +} + +/** + * lpfc_fabric_login_reqd - Check if FLOGI required. + * @phba: pointer to lpfc hba data structure. + * @cmdiocb: pointer to FDISC command iocb. + * @rspiocb: pointer to FDISC response iocb. + * + * This routine checks if a FLOGI is reguired for FDISC + * to succeed. + **/ +static int +lpfc_fabric_login_reqd(struct lpfc_hba *phba, + struct lpfc_iocbq *cmdiocb, + struct lpfc_iocbq *rspiocb) +{ + + if ((rspiocb->iocb.ulpStatus != IOSTAT_FABRIC_RJT) || + (rspiocb->iocb.un.ulpWord[4] != RJT_LOGIN_REQUIRED)) + return 0; + else + return 1; +} + +/** * lpfc_cmpl_els_fdisc - Completion function for fdisc iocb command * @phba: pointer to lpfc hba data structure. * @cmdiocb: pointer to lpfc command iocb data structure. @@ -6066,6 +6175,12 @@ lpfc_cmpl_els_fdisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, irsp->ulpStatus, irsp->un.ulpWord[4], vport->fc_prevDID); if (irsp->ulpStatus) { + + if (lpfc_fabric_login_reqd(phba, cmdiocb, rspiocb)) { + lpfc_retry_pport_discovery(phba); + goto out; + } + /* Check for retry */ if (lpfc_els_retry(phba, cmdiocb, rspiocb)) goto out; @@ -6076,6 +6191,7 @@ lpfc_cmpl_els_fdisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, goto fdisc_failed; } spin_lock_irq(shost->host_lock); + vport->fc_flag &= ~FC_VPORT_CVL_RCVD; vport->fc_flag |= FC_FABRIC; if (vport->phba->fc_topology == TOPOLOGY_LOOP) vport->fc_flag |= FC_PUBLIC_LOOP; @@ -6103,10 +6219,13 @@ lpfc_cmpl_els_fdisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, lpfc_mbx_unreg_vpi(vport); spin_lock_irq(shost->host_lock); vport->fc_flag |= FC_VPORT_NEEDS_REG_VPI; + vport->fc_flag |= FC_VPORT_NEEDS_INIT_VPI; spin_unlock_irq(shost->host_lock); } - if (vport->fc_flag & FC_VPORT_NEEDS_REG_VPI) + if (vport->fc_flag & FC_VPORT_NEEDS_INIT_VPI) + lpfc_issue_init_vpi(vport); + else if (vport->fc_flag & FC_VPORT_NEEDS_REG_VPI) lpfc_register_new_vport(phba, vport, ndlp); else lpfc_do_scr_ns_plogi(phba, vport); diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index 2445e399fd6..2359d0bfb73 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -525,6 +525,8 @@ lpfc_work_done(struct lpfc_hba *phba) spin_unlock_irq(&phba->hbalock); lpfc_sli_hbqbuf_add_hbqs(phba, LPFC_ELS_HBQ); } + if (phba->fcf.fcf_flag & FCF_REDISC_EVT) + lpfc_sli4_fcf_redisc_event_proc(phba); } vports = lpfc_create_vport_work_array(phba); @@ -706,6 +708,8 @@ lpfc_cleanup_rpis(struct lpfc_vport *vport, int remove) void lpfc_port_link_failure(struct lpfc_vport *vport) { + lpfc_vport_set_state(vport, FC_VPORT_LINKDOWN); + /* Cleanup any outstanding received buffers */ lpfc_cleanup_rcv_buffers(vport); @@ -752,12 +756,14 @@ lpfc_linkdown(struct lpfc_hba *phba) lpfc_scsi_dev_block(phba); spin_lock_irq(&phba->hbalock); - phba->fcf.fcf_flag &= ~(FCF_AVAILABLE | FCF_DISCOVERED); + phba->fcf.fcf_flag &= ~(FCF_AVAILABLE | FCF_SCAN_DONE); + spin_unlock_irq(&phba->hbalock); if (phba->link_state > LPFC_LINK_DOWN) { phba->link_state = LPFC_LINK_DOWN; + spin_lock_irq(shost->host_lock); phba->pport->fc_flag &= ~FC_LBIT; + spin_unlock_irq(shost->host_lock); } - spin_unlock_irq(&phba->hbalock); vports = lpfc_create_vport_work_array(phba); if (vports != NULL) for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) { @@ -1023,7 +1029,7 @@ lpfc_mbx_cmpl_reg_fcfi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) return; } spin_lock_irqsave(&phba->hbalock, flags); - phba->fcf.fcf_flag |= (FCF_DISCOVERED | FCF_IN_USE); + phba->fcf.fcf_flag |= (FCF_SCAN_DONE | FCF_IN_USE); phba->hba_flag &= ~FCF_DISC_INPROGRESS; spin_unlock_irqrestore(&phba->hbalock, flags); if (vport->port_state != LPFC_FLOGI) @@ -1045,25 +1051,23 @@ lpfc_mbx_cmpl_reg_fcfi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) static uint32_t lpfc_fab_name_match(uint8_t *fab_name, struct fcf_record *new_fcf_record) { - if ((fab_name[0] == - bf_get(lpfc_fcf_record_fab_name_0, new_fcf_record)) && - (fab_name[1] == - bf_get(lpfc_fcf_record_fab_name_1, new_fcf_record)) && - (fab_name[2] == - bf_get(lpfc_fcf_record_fab_name_2, new_fcf_record)) && - (fab_name[3] == - bf_get(lpfc_fcf_record_fab_name_3, new_fcf_record)) && - (fab_name[4] == - bf_get(lpfc_fcf_record_fab_name_4, new_fcf_record)) && - (fab_name[5] == - bf_get(lpfc_fcf_record_fab_name_5, new_fcf_record)) && - (fab_name[6] == - bf_get(lpfc_fcf_record_fab_name_6, new_fcf_record)) && - (fab_name[7] == - bf_get(lpfc_fcf_record_fab_name_7, new_fcf_record))) - return 1; - else + if (fab_name[0] != bf_get(lpfc_fcf_record_fab_name_0, new_fcf_record)) + return 0; + if (fab_name[1] != bf_get(lpfc_fcf_record_fab_name_1, new_fcf_record)) + return 0; + if (fab_name[2] != bf_get(lpfc_fcf_record_fab_name_2, new_fcf_record)) return 0; + if (fab_name[3] != bf_get(lpfc_fcf_record_fab_name_3, new_fcf_record)) + return 0; + if (fab_name[4] != bf_get(lpfc_fcf_record_fab_name_4, new_fcf_record)) + return 0; + if (fab_name[5] != bf_get(lpfc_fcf_record_fab_name_5, new_fcf_record)) + return 0; + if (fab_name[6] != bf_get(lpfc_fcf_record_fab_name_6, new_fcf_record)) + return 0; + if (fab_name[7] != bf_get(lpfc_fcf_record_fab_name_7, new_fcf_record)) + return 0; + return 1; } /** @@ -1078,30 +1082,28 @@ lpfc_fab_name_match(uint8_t *fab_name, struct fcf_record *new_fcf_record) static uint32_t lpfc_sw_name_match(uint8_t *sw_name, struct fcf_record *new_fcf_record) { - if ((sw_name[0] == - bf_get(lpfc_fcf_record_switch_name_0, new_fcf_record)) && - (sw_name[1] == - bf_get(lpfc_fcf_record_switch_name_1, new_fcf_record)) && - (sw_name[2] == - bf_get(lpfc_fcf_record_switch_name_2, new_fcf_record)) && - (sw_name[3] == - bf_get(lpfc_fcf_record_switch_name_3, new_fcf_record)) && - (sw_name[4] == - bf_get(lpfc_fcf_record_switch_name_4, new_fcf_record)) && - (sw_name[5] == - bf_get(lpfc_fcf_record_switch_name_5, new_fcf_record)) && - (sw_name[6] == - bf_get(lpfc_fcf_record_switch_name_6, new_fcf_record)) && - (sw_name[7] == - bf_get(lpfc_fcf_record_switch_name_7, new_fcf_record))) - return 1; - else + if (sw_name[0] != bf_get(lpfc_fcf_record_switch_name_0, new_fcf_record)) return 0; + if (sw_name[1] != bf_get(lpfc_fcf_record_switch_name_1, new_fcf_record)) + return 0; + if (sw_name[2] != bf_get(lpfc_fcf_record_switch_name_2, new_fcf_record)) + return 0; + if (sw_name[3] != bf_get(lpfc_fcf_record_switch_name_3, new_fcf_record)) + return 0; + if (sw_name[4] != bf_get(lpfc_fcf_record_switch_name_4, new_fcf_record)) + return 0; + if (sw_name[5] != bf_get(lpfc_fcf_record_switch_name_5, new_fcf_record)) + return 0; + if (sw_name[6] != bf_get(lpfc_fcf_record_switch_name_6, new_fcf_record)) + return 0; + if (sw_name[7] != bf_get(lpfc_fcf_record_switch_name_7, new_fcf_record)) + return 0; + return 1; } /** * lpfc_mac_addr_match - Check if the fcf mac address match. - * @phba: pointer to lpfc hba data structure. + * @mac_addr: pointer to mac address. * @new_fcf_record: pointer to fcf record. * * This routine compare the fcf record's mac address with HBA's @@ -1109,85 +1111,115 @@ lpfc_sw_name_match(uint8_t *sw_name, struct fcf_record *new_fcf_record) * returns 1 else return 0. **/ static uint32_t -lpfc_mac_addr_match(struct lpfc_hba *phba, struct fcf_record *new_fcf_record) +lpfc_mac_addr_match(uint8_t *mac_addr, struct fcf_record *new_fcf_record) { - if ((phba->fcf.mac_addr[0] == - bf_get(lpfc_fcf_record_mac_0, new_fcf_record)) && - (phba->fcf.mac_addr[1] == - bf_get(lpfc_fcf_record_mac_1, new_fcf_record)) && - (phba->fcf.mac_addr[2] == - bf_get(lpfc_fcf_record_mac_2, new_fcf_record)) && - (phba->fcf.mac_addr[3] == - bf_get(lpfc_fcf_record_mac_3, new_fcf_record)) && - (phba->fcf.mac_addr[4] == - bf_get(lpfc_fcf_record_mac_4, new_fcf_record)) && - (phba->fcf.mac_addr[5] == - bf_get(lpfc_fcf_record_mac_5, new_fcf_record))) - return 1; - else + if (mac_addr[0] != bf_get(lpfc_fcf_record_mac_0, new_fcf_record)) + return 0; + if (mac_addr[1] != bf_get(lpfc_fcf_record_mac_1, new_fcf_record)) + return 0; + if (mac_addr[2] != bf_get(lpfc_fcf_record_mac_2, new_fcf_record)) return 0; + if (mac_addr[3] != bf_get(lpfc_fcf_record_mac_3, new_fcf_record)) + return 0; + if (mac_addr[4] != bf_get(lpfc_fcf_record_mac_4, new_fcf_record)) + return 0; + if (mac_addr[5] != bf_get(lpfc_fcf_record_mac_5, new_fcf_record)) + return 0; + return 1; +} + +static bool +lpfc_vlan_id_match(uint16_t curr_vlan_id, uint16_t new_vlan_id) +{ + return (curr_vlan_id == new_vlan_id); } /** * lpfc_copy_fcf_record - Copy fcf information to lpfc_hba. - * @phba: pointer to lpfc hba data structure. + * @fcf: pointer to driver fcf record. * @new_fcf_record: pointer to fcf record. * * This routine copies the FCF information from the FCF * record to lpfc_hba data structure. **/ static void -lpfc_copy_fcf_record(struct lpfc_hba *phba, struct fcf_record *new_fcf_record) +lpfc_copy_fcf_record(struct lpfc_fcf_rec *fcf_rec, + struct fcf_record *new_fcf_record) { - phba->fcf.fabric_name[0] = + /* Fabric name */ + fcf_rec->fabric_name[0] = bf_get(lpfc_fcf_record_fab_name_0, new_fcf_record); - phba->fcf.fabric_name[1] = + fcf_rec->fabric_name[1] = bf_get(lpfc_fcf_record_fab_name_1, new_fcf_record); - phba->fcf.fabric_name[2] = + fcf_rec->fabric_name[2] = bf_get(lpfc_fcf_record_fab_name_2, new_fcf_record); - phba->fcf.fabric_name[3] = + fcf_rec->fabric_name[3] = bf_get(lpfc_fcf_record_fab_name_3, new_fcf_record); - phba->fcf.fabric_name[4] = + fcf_rec->fabric_name[4] = bf_get(lpfc_fcf_record_fab_name_4, new_fcf_record); - phba->fcf.fabric_name[5] = + fcf_rec->fabric_name[5] = bf_get(lpfc_fcf_record_fab_name_5, new_fcf_record); - phba->fcf.fabric_name[6] = + fcf_rec->fabric_name[6] = bf_get(lpfc_fcf_record_fab_name_6, new_fcf_record); - phba->fcf.fabric_name[7] = + fcf_rec->fabric_name[7] = bf_get(lpfc_fcf_record_fab_name_7, new_fcf_record); - phba->fcf.mac_addr[0] = - bf_get(lpfc_fcf_record_mac_0, new_fcf_record); - phba->fcf.mac_addr[1] = - bf_get(lpfc_fcf_record_mac_1, new_fcf_record); - phba->fcf.mac_addr[2] = - bf_get(lpfc_fcf_record_mac_2, new_fcf_record); - phba->fcf.mac_addr[3] = - bf_get(lpfc_fcf_record_mac_3, new_fcf_record); - phba->fcf.mac_addr[4] = - bf_get(lpfc_fcf_record_mac_4, new_fcf_record); - phba->fcf.mac_addr[5] = - bf_get(lpfc_fcf_record_mac_5, new_fcf_record); - phba->fcf.fcf_indx = bf_get(lpfc_fcf_record_fcf_index, new_fcf_record); - phba->fcf.priority = new_fcf_record->fip_priority; - phba->fcf.switch_name[0] = + /* Mac address */ + fcf_rec->mac_addr[0] = bf_get(lpfc_fcf_record_mac_0, new_fcf_record); + fcf_rec->mac_addr[1] = bf_get(lpfc_fcf_record_mac_1, new_fcf_record); + fcf_rec->mac_addr[2] = bf_get(lpfc_fcf_record_mac_2, new_fcf_record); + fcf_rec->mac_addr[3] = bf_get(lpfc_fcf_record_mac_3, new_fcf_record); + fcf_rec->mac_addr[4] = bf_get(lpfc_fcf_record_mac_4, new_fcf_record); + fcf_rec->mac_addr[5] = bf_get(lpfc_fcf_record_mac_5, new_fcf_record); + /* FCF record index */ + fcf_rec->fcf_indx = bf_get(lpfc_fcf_record_fcf_index, new_fcf_record); + /* FCF record priority */ + fcf_rec->priority = new_fcf_record->fip_priority; + /* Switch name */ + fcf_rec->switch_name[0] = bf_get(lpfc_fcf_record_switch_name_0, new_fcf_record); - phba->fcf.switch_name[1] = + fcf_rec->switch_name[1] = bf_get(lpfc_fcf_record_switch_name_1, new_fcf_record); - phba->fcf.switch_name[2] = + fcf_rec->switch_name[2] = bf_get(lpfc_fcf_record_switch_name_2, new_fcf_record); - phba->fcf.switch_name[3] = + fcf_rec->switch_name[3] = bf_get(lpfc_fcf_record_switch_name_3, new_fcf_record); - phba->fcf.switch_name[4] = + fcf_rec->switch_name[4] = bf_get(lpfc_fcf_record_switch_name_4, new_fcf_record); - phba->fcf.switch_name[5] = + fcf_rec->switch_name[5] = bf_get(lpfc_fcf_record_switch_name_5, new_fcf_record); - phba->fcf.switch_name[6] = + fcf_rec->switch_name[6] = bf_get(lpfc_fcf_record_switch_name_6, new_fcf_record); - phba->fcf.switch_name[7] = + fcf_rec->switch_name[7] = bf_get(lpfc_fcf_record_switch_name_7, new_fcf_record); } /** + * lpfc_update_fcf_record - Update driver fcf record + * @phba: pointer to lpfc hba data structure. + * @fcf_rec: pointer to driver fcf record. + * @new_fcf_record: pointer to hba fcf record. + * @addr_mode: address mode to be set to the driver fcf record. + * @vlan_id: vlan tag to be set to the driver fcf record. + * @flag: flag bits to be set to the driver fcf record. + * + * This routine updates the driver FCF record from the new HBA FCF record + * together with the address mode, vlan_id, and other informations. This + * routine is called with the host lock held. + **/ +static void +__lpfc_update_fcf_record(struct lpfc_hba *phba, struct lpfc_fcf_rec *fcf_rec, + struct fcf_record *new_fcf_record, uint32_t addr_mode, + uint16_t vlan_id, uint32_t flag) +{ + /* Copy the fields from the HBA's FCF record */ + lpfc_copy_fcf_record(fcf_rec, new_fcf_record); + /* Update other fields of driver FCF record */ + fcf_rec->addr_mode = addr_mode; + fcf_rec->vlan_id = vlan_id; + fcf_rec->flag |= (flag | RECORD_VALID); +} + +/** * lpfc_register_fcf - Register the FCF with hba. * @phba: pointer to lpfc hba data structure. * @@ -1212,7 +1244,7 @@ lpfc_register_fcf(struct lpfc_hba *phba) /* The FCF is already registered, start discovery */ if (phba->fcf.fcf_flag & FCF_REGISTERED) { - phba->fcf.fcf_flag |= (FCF_DISCOVERED | FCF_IN_USE); + phba->fcf.fcf_flag |= (FCF_SCAN_DONE | FCF_IN_USE); phba->hba_flag &= ~FCF_DISC_INPROGRESS; spin_unlock_irqrestore(&phba->hbalock, flags); if (phba->pport->port_state != LPFC_FLOGI) @@ -1250,6 +1282,7 @@ lpfc_register_fcf(struct lpfc_hba *phba) * @new_fcf_record: pointer to fcf record. * @boot_flag: Indicates if this record used by boot bios. * @addr_mode: The address mode to be used by this FCF + * @vlan_id: The vlan id to be used as vlan tagging by this FCF. * * This routine compare the fcf record with connect list obtained from the * config region to decide if this FCF can be used for SAN discovery. It returns @@ -1323,7 +1356,8 @@ lpfc_match_fcf_conn_list(struct lpfc_hba *phba, return 1; } - list_for_each_entry(conn_entry, &phba->fcf_conn_rec_list, list) { + list_for_each_entry(conn_entry, + &phba->fcf_conn_rec_list, list) { if (!(conn_entry->conn_rec.flags & FCFCNCT_VALID)) continue; @@ -1470,6 +1504,7 @@ lpfc_check_pending_fcoe_event(struct lpfc_hba *phba, uint8_t unreg_fcf) */ spin_lock_irq(&phba->hbalock); phba->hba_flag &= ~FCF_DISC_INPROGRESS; + phba->fcf.fcf_flag &= ~FCF_REDISC_FOV; spin_unlock_irq(&phba->hbalock); } @@ -1524,11 +1559,12 @@ lpfc_mbx_cmpl_read_fcf_record(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) uint32_t shdr_status, shdr_add_status; union lpfc_sli4_cfg_shdr *shdr; struct fcf_record *new_fcf_record; - int rc; uint32_t boot_flag, addr_mode; uint32_t next_fcf_index; - unsigned long flags; + struct lpfc_fcf_rec *fcf_rec = NULL; + unsigned long iflags; uint16_t vlan_id; + int rc; /* If there is pending FCoE event restart FCF table scan */ if (lpfc_check_pending_fcoe_event(phba, 0)) { @@ -1583,9 +1619,8 @@ lpfc_mbx_cmpl_read_fcf_record(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) sizeof(struct fcf_record)); bytep = virt_addr + sizeof(union lpfc_sli4_cfg_shdr); - rc = lpfc_match_fcf_conn_list(phba, new_fcf_record, - &boot_flag, &addr_mode, - &vlan_id); + rc = lpfc_match_fcf_conn_list(phba, new_fcf_record, &boot_flag, + &addr_mode, &vlan_id); /* * If the fcf record does not match with connect list entries * read the next entry. @@ -1594,90 +1629,159 @@ lpfc_mbx_cmpl_read_fcf_record(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) goto read_next_fcf; /* * If this is not the first FCF discovery of the HBA, use last - * FCF record for the discovery. + * FCF record for the discovery. The condition that a rescan + * matches the in-use FCF record: fabric name, switch name, mac + * address, and vlan_id. */ - spin_lock_irqsave(&phba->hbalock, flags); + spin_lock_irqsave(&phba->hbalock, iflags); if (phba->fcf.fcf_flag & FCF_IN_USE) { - if (lpfc_fab_name_match(phba->fcf.fabric_name, + if (lpfc_fab_name_match(phba->fcf.current_rec.fabric_name, new_fcf_record) && - lpfc_sw_name_match(phba->fcf.switch_name, + lpfc_sw_name_match(phba->fcf.current_rec.switch_name, new_fcf_record) && - lpfc_mac_addr_match(phba, new_fcf_record)) { + lpfc_mac_addr_match(phba->fcf.current_rec.mac_addr, + new_fcf_record) && + lpfc_vlan_id_match(phba->fcf.current_rec.vlan_id, + vlan_id)) { phba->fcf.fcf_flag |= FCF_AVAILABLE; - spin_unlock_irqrestore(&phba->hbalock, flags); + if (phba->fcf.fcf_flag & FCF_REDISC_PEND) + /* Stop FCF redisc wait timer if pending */ + __lpfc_sli4_stop_fcf_redisc_wait_timer(phba); + else if (phba->fcf.fcf_flag & FCF_REDISC_FOV) + /* If in fast failover, mark it's completed */ + phba->fcf.fcf_flag &= ~FCF_REDISC_FOV; + spin_unlock_irqrestore(&phba->hbalock, iflags); goto out; } - spin_unlock_irqrestore(&phba->hbalock, flags); - goto read_next_fcf; + /* + * Read next FCF record from HBA searching for the matching + * with in-use record only if not during the fast failover + * period. In case of fast failover period, it shall try to + * determine whether the FCF record just read should be the + * next candidate. + */ + if (!(phba->fcf.fcf_flag & FCF_REDISC_FOV)) { + spin_unlock_irqrestore(&phba->hbalock, iflags); + goto read_next_fcf; + } } + /* + * Update on failover FCF record only if it's in FCF fast-failover + * period; otherwise, update on current FCF record. + */ + if (phba->fcf.fcf_flag & FCF_REDISC_FOV) { + /* Fast FCF failover only to the same fabric name */ + if (lpfc_fab_name_match(phba->fcf.current_rec.fabric_name, + new_fcf_record)) + fcf_rec = &phba->fcf.failover_rec; + else + goto read_next_fcf; + } else + fcf_rec = &phba->fcf.current_rec; + if (phba->fcf.fcf_flag & FCF_AVAILABLE) { /* - * If the current FCF record does not have boot flag - * set and new fcf record has boot flag set, use the - * new fcf record. + * If the driver FCF record does not have boot flag + * set and new hba fcf record has boot flag set, use + * the new hba fcf record. */ - if (boot_flag && !(phba->fcf.fcf_flag & FCF_BOOT_ENABLE)) { - /* Use this FCF record */ - lpfc_copy_fcf_record(phba, new_fcf_record); - phba->fcf.addr_mode = addr_mode; - phba->fcf.fcf_flag |= FCF_BOOT_ENABLE; - if (vlan_id != 0xFFFF) { - phba->fcf.fcf_flag |= FCF_VALID_VLAN; - phba->fcf.vlan_id = vlan_id; - } - spin_unlock_irqrestore(&phba->hbalock, flags); + if (boot_flag && !(fcf_rec->flag & BOOT_ENABLE)) { + /* Choose this FCF record */ + __lpfc_update_fcf_record(phba, fcf_rec, new_fcf_record, + addr_mode, vlan_id, BOOT_ENABLE); + spin_unlock_irqrestore(&phba->hbalock, iflags); goto read_next_fcf; } /* - * If the current FCF record has boot flag set and the - * new FCF record does not have boot flag, read the next - * FCF record. + * If the driver FCF record has boot flag set and the + * new hba FCF record does not have boot flag, read + * the next FCF record. */ - if (!boot_flag && (phba->fcf.fcf_flag & FCF_BOOT_ENABLE)) { - spin_unlock_irqrestore(&phba->hbalock, flags); + if (!boot_flag && (fcf_rec->flag & BOOT_ENABLE)) { + spin_unlock_irqrestore(&phba->hbalock, iflags); goto read_next_fcf; } /* - * If there is a record with lower priority value for - * the current FCF, use that record. + * If the new hba FCF record has lower priority value + * than the driver FCF record, use the new record. */ - if (lpfc_fab_name_match(phba->fcf.fabric_name, - new_fcf_record) && - (new_fcf_record->fip_priority < phba->fcf.priority)) { - /* Use this FCF record */ - lpfc_copy_fcf_record(phba, new_fcf_record); - phba->fcf.addr_mode = addr_mode; - if (vlan_id != 0xFFFF) { - phba->fcf.fcf_flag |= FCF_VALID_VLAN; - phba->fcf.vlan_id = vlan_id; - } - spin_unlock_irqrestore(&phba->hbalock, flags); - goto read_next_fcf; + if (lpfc_fab_name_match(fcf_rec->fabric_name, new_fcf_record) && + (new_fcf_record->fip_priority < fcf_rec->priority)) { + /* Choose this FCF record */ + __lpfc_update_fcf_record(phba, fcf_rec, new_fcf_record, + addr_mode, vlan_id, 0); } - spin_unlock_irqrestore(&phba->hbalock, flags); + spin_unlock_irqrestore(&phba->hbalock, iflags); goto read_next_fcf; } /* - * This is the first available FCF record, use this - * record. + * This is the first suitable FCF record, choose this record for + * initial best-fit FCF. */ - lpfc_copy_fcf_record(phba, new_fcf_record); - phba->fcf.addr_mode = addr_mode; - if (boot_flag) - phba->fcf.fcf_flag |= FCF_BOOT_ENABLE; - phba->fcf.fcf_flag |= FCF_AVAILABLE; - if (vlan_id != 0xFFFF) { - phba->fcf.fcf_flag |= FCF_VALID_VLAN; - phba->fcf.vlan_id = vlan_id; + if (fcf_rec) { + __lpfc_update_fcf_record(phba, fcf_rec, new_fcf_record, + addr_mode, vlan_id, (boot_flag ? + BOOT_ENABLE : 0)); + phba->fcf.fcf_flag |= FCF_AVAILABLE; } - spin_unlock_irqrestore(&phba->hbalock, flags); + spin_unlock_irqrestore(&phba->hbalock, iflags); goto read_next_fcf; read_next_fcf: lpfc_sli4_mbox_cmd_free(phba, mboxq); - if (next_fcf_index == LPFC_FCOE_FCF_NEXT_NONE || next_fcf_index == 0) - lpfc_register_fcf(phba); - else + if (next_fcf_index == LPFC_FCOE_FCF_NEXT_NONE || next_fcf_index == 0) { + if (phba->fcf.fcf_flag & FCF_REDISC_FOV) { + /* + * Case of FCF fast failover scan + */ + + /* + * It has not found any suitable FCF record, cancel + * FCF scan inprogress, and do nothing + */ + if (!(phba->fcf.failover_rec.flag & RECORD_VALID)) { + spin_lock_irqsave(&phba->hbalock, iflags); + phba->hba_flag &= ~FCF_DISC_INPROGRESS; + spin_unlock_irqrestore(&phba->hbalock, iflags); + return; + } + /* + * It has found a suitable FCF record that is not + * the same as in-use FCF record, unregister the + * in-use FCF record, replace the in-use FCF record + * with the new FCF record, mark FCF fast failover + * completed, and then start register the new FCF + * record. + */ + + /* unregister the current in-use FCF record */ + lpfc_unregister_fcf(phba); + /* replace in-use record with the new record */ + memcpy(&phba->fcf.current_rec, + &phba->fcf.failover_rec, + sizeof(struct lpfc_fcf_rec)); + /* mark the FCF fast failover completed */ + spin_lock_irqsave(&phba->hbalock, iflags); + phba->fcf.fcf_flag &= ~FCF_REDISC_FOV; + spin_unlock_irqrestore(&phba->hbalock, iflags); + /* Register to the new FCF record */ + lpfc_register_fcf(phba); + } else { + /* + * In case of transaction period to fast FCF failover, + * do nothing when search to the end of the FCF table. + */ + if ((phba->fcf.fcf_flag & FCF_REDISC_EVT) || + (phba->fcf.fcf_flag & FCF_REDISC_PEND)) + return; + /* + * Otherwise, initial scan or post linkdown rescan, + * register with the best fit FCF record found so + * far through the scanning process. + */ + lpfc_register_fcf(phba); + } + } else lpfc_sli4_read_fcf_record(phba, next_fcf_index); return; @@ -1695,10 +1799,13 @@ out: * * This function handles completion of init vpi mailbox command. */ -static void +void lpfc_init_vpi_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) { struct lpfc_vport *vport = mboxq->vport; + struct lpfc_nodelist *ndlp; + struct Scsi_Host *shost = lpfc_shost_from_vport(vport); + if (mboxq->u.mb.mbxStatus) { lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX, @@ -1708,9 +1815,23 @@ lpfc_init_vpi_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) lpfc_vport_set_state(vport, FC_VPORT_FAILED); return; } - spin_lock_irq(&phba->hbalock); + spin_lock_irq(shost->host_lock); vport->fc_flag &= ~FC_VPORT_NEEDS_INIT_VPI; - spin_unlock_irq(&phba->hbalock); + spin_unlock_irq(shost->host_lock); + + /* If this port is physical port or FDISC is done, do reg_vpi */ + if ((phba->pport == vport) || (vport->port_state == LPFC_FDISC)) { + ndlp = lpfc_findnode_did(vport, Fabric_DID); + if (!ndlp) + lpfc_printf_vlog(vport, KERN_ERR, + LOG_DISCOVERY, + "2731 Cannot find fabric " + "controller node\n"); + else + lpfc_register_new_vport(phba, vport, ndlp); + mempool_free(mboxq, phba->mbox_mem_pool); + return; + } if (phba->link_flag & LS_NPIV_FAB_SUPPORTED) lpfc_initial_fdisc(vport); @@ -1719,10 +1840,42 @@ lpfc_init_vpi_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS, "2606 No NPIV Fabric support\n"); } + mempool_free(mboxq, phba->mbox_mem_pool); return; } /** + * lpfc_issue_init_vpi - Issue init_vpi mailbox command. + * @vport: pointer to lpfc_vport data structure. + * + * This function issue a init_vpi mailbox command to initialize + * VPI for the vport. + */ +void +lpfc_issue_init_vpi(struct lpfc_vport *vport) +{ + LPFC_MBOXQ_t *mboxq; + int rc; + + mboxq = mempool_alloc(vport->phba->mbox_mem_pool, GFP_KERNEL); + if (!mboxq) { + lpfc_printf_vlog(vport, KERN_ERR, + LOG_MBOX, "2607 Failed to allocate " + "init_vpi mailbox\n"); + return; + } + lpfc_init_vpi(vport->phba, mboxq, vport->vpi); + mboxq->vport = vport; + mboxq->mbox_cmpl = lpfc_init_vpi_cmpl; + rc = lpfc_sli_issue_mbox(vport->phba, mboxq, MBX_NOWAIT); + if (rc == MBX_NOT_FINISHED) { + lpfc_printf_vlog(vport, KERN_ERR, + LOG_MBOX, "2608 Failed to issue init_vpi mailbox\n"); + mempool_free(mboxq, vport->phba->mbox_mem_pool); + } +} + +/** * lpfc_start_fdiscs - send fdiscs for each vports on this port. * @phba: pointer to lpfc hba data structure. * @@ -1734,8 +1887,6 @@ lpfc_start_fdiscs(struct lpfc_hba *phba) { struct lpfc_vport **vports; int i; - LPFC_MBOXQ_t *mboxq; - int rc; vports = lpfc_create_vport_work_array(phba); if (vports != NULL) { @@ -1754,26 +1905,7 @@ lpfc_start_fdiscs(struct lpfc_hba *phba) continue; } if (vports[i]->fc_flag & FC_VPORT_NEEDS_INIT_VPI) { - mboxq = mempool_alloc(phba->mbox_mem_pool, - GFP_KERNEL); - if (!mboxq) { - lpfc_printf_vlog(vports[i], KERN_ERR, - LOG_MBOX, "2607 Failed to allocate " - "init_vpi mailbox\n"); - continue; - } - lpfc_init_vpi(phba, mboxq, vports[i]->vpi); - mboxq->vport = vports[i]; - mboxq->mbox_cmpl = lpfc_init_vpi_cmpl; - rc = lpfc_sli_issue_mbox(phba, mboxq, - MBX_NOWAIT); - if (rc == MBX_NOT_FINISHED) { - lpfc_printf_vlog(vports[i], KERN_ERR, - LOG_MBOX, "2608 Failed to issue " - "init_vpi mailbox\n"); - mempool_free(mboxq, - phba->mbox_mem_pool); - } + lpfc_issue_init_vpi(vports[i]); continue; } if (phba->link_flag & LS_NPIV_FAB_SUPPORTED) @@ -1796,6 +1928,7 @@ lpfc_mbx_cmpl_reg_vfi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) { struct lpfc_dmabuf *dmabuf = mboxq->context1; struct lpfc_vport *vport = mboxq->vport; + struct Scsi_Host *shost = lpfc_shost_from_vport(vport); if (mboxq->u.mb.mbxStatus) { lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX, @@ -1813,7 +1946,11 @@ lpfc_mbx_cmpl_reg_vfi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) goto fail_free_mem; } /* The VPI is implicitly registered when the VFI is registered */ + spin_lock_irq(shost->host_lock); vport->vpi_state |= LPFC_VPI_REGISTERED; + vport->fc_flag |= FC_VFI_REGISTERED; + vport->fc_flag &= ~FC_VPORT_NEEDS_REG_VPI; + spin_unlock_irq(shost->host_lock); if (vport->port_state == LPFC_FABRIC_CFG_LINK) { lpfc_start_fdiscs(phba); @@ -2050,8 +2187,7 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, READ_LA_VAR *la) return; } spin_unlock_irq(&phba->hbalock); - rc = lpfc_sli4_read_fcf_record(phba, - LPFC_FCOE_FCF_GET_FIRST); + rc = lpfc_sli4_read_fcf_record(phba, LPFC_FCOE_FCF_GET_FIRST); if (rc) goto out; } @@ -2139,10 +2275,12 @@ lpfc_mbx_cmpl_read_la(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) } phba->fc_eventTag = la->eventTag; + spin_lock_irq(&phba->hbalock); if (la->mm) phba->sli.sli_flag |= LPFC_MENLO_MAINT; else phba->sli.sli_flag &= ~LPFC_MENLO_MAINT; + spin_unlock_irq(&phba->hbalock); phba->link_events++; if (la->attType == AT_LINK_UP && (!la->mm)) { @@ -2271,10 +2409,10 @@ lpfc_mbx_cmpl_unreg_vpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) mb->mbxStatus); break; } - spin_lock_irq(&phba->hbalock); + spin_lock_irq(shost->host_lock); vport->vpi_state &= ~LPFC_VPI_REGISTERED; vport->fc_flag |= FC_VPORT_NEEDS_REG_VPI; - spin_unlock_irq(&phba->hbalock); + spin_unlock_irq(shost->host_lock); vport->unreg_vpi_cmpl = VPORT_OK; mempool_free(pmb, phba->mbox_mem_pool); /* @@ -2332,7 +2470,10 @@ lpfc_mbx_cmpl_reg_vpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) goto out; } + spin_lock_irq(shost->host_lock); vport->vpi_state |= LPFC_VPI_REGISTERED; + vport->fc_flag &= ~FC_VPORT_NEEDS_REG_VPI; + spin_unlock_irq(shost->host_lock); vport->num_disc_nodes = 0; /* go thru NPR list and issue ELS PLOGIs */ if (vport->fc_npr_cnt) @@ -3218,6 +3359,34 @@ lpfc_unreg_rpi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) return 0; } +/** + * lpfc_unreg_hba_rpis - Unregister rpis registered to the hba. + * @phba: pointer to lpfc hba data structure. + * + * This routine is invoked to unregister all the currently registered RPIs + * to the HBA. + **/ +void +lpfc_unreg_hba_rpis(struct lpfc_hba *phba) +{ + struct lpfc_vport **vports; + struct lpfc_nodelist *ndlp; + struct Scsi_Host *shost; + int i; + + vports = lpfc_create_vport_work_array(phba); + for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) { + shost = lpfc_shost_from_vport(vports[i]); + spin_lock_irq(shost->host_lock); + list_for_each_entry(ndlp, &vports[i]->fc_nodes, nlp_listp) { + if (ndlp->nlp_flag & NLP_RPI_VALID) + lpfc_unreg_rpi(vports[i], ndlp); + } + spin_unlock_irq(shost->host_lock); + } + lpfc_destroy_vport_work_array(phba, vports); +} + void lpfc_unreg_all_rpis(struct lpfc_vport *vport) { @@ -4448,63 +4617,56 @@ lpfc_unregister_fcfi_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) } /** - * lpfc_unregister_unused_fcf - Unregister FCF if all devices are disconnected. + * lpfc_unregister_fcf_prep - Unregister fcf record preparation * @phba: Pointer to hba context object. * - * This function check if there are any connected remote port for the FCF and - * if all the devices are disconnected, this function unregister FCFI. - * This function also tries to use another FCF for discovery. + * This function prepare the HBA for unregistering the currently registered + * FCF from the HBA. It performs unregistering, in order, RPIs, VPIs, and + * VFIs. */ -void -lpfc_unregister_unused_fcf(struct lpfc_hba *phba) +int +lpfc_unregister_fcf_prep(struct lpfc_hba *phba) { LPFC_MBOXQ_t *mbox; - int rc; struct lpfc_vport **vports; - int i; - - spin_lock_irq(&phba->hbalock); - /* - * If HBA is not running in FIP mode or - * If HBA does not support FCoE or - * If FCF is not registered. - * do nothing. - */ - if (!(phba->hba_flag & HBA_FCOE_SUPPORT) || - !(phba->fcf.fcf_flag & FCF_REGISTERED) || - (!(phba->hba_flag & HBA_FIP_SUPPORT))) { - spin_unlock_irq(&phba->hbalock); - return; - } - spin_unlock_irq(&phba->hbalock); + struct lpfc_nodelist *ndlp; + struct Scsi_Host *shost; + int i, rc; + /* Unregister RPIs */ if (lpfc_fcf_inuse(phba)) - return; + lpfc_unreg_hba_rpis(phba); /* At this point, all discovery is aborted */ phba->pport->port_state = LPFC_VPORT_UNKNOWN; /* Unregister VPIs */ vports = lpfc_create_vport_work_array(phba); - if (vports && - (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED)) + if (vports && (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED)) for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) { + /* Stop FLOGI/FDISC retries */ + ndlp = lpfc_findnode_did(vports[i], Fabric_DID); + if (ndlp) + lpfc_cancel_retry_delay_tmo(vports[i], ndlp); lpfc_mbx_unreg_vpi(vports[i]); - spin_lock_irq(&phba->hbalock); + shost = lpfc_shost_from_vport(vports[i]); + spin_lock_irq(shost->host_lock); vports[i]->fc_flag |= FC_VPORT_NEEDS_INIT_VPI; vports[i]->vpi_state &= ~LPFC_VPI_REGISTERED; - spin_unlock_irq(&phba->hbalock); + spin_unlock_irq(shost->host_lock); } lpfc_destroy_vport_work_array(phba, vports); + /* Cleanup any outstanding ELS commands */ + lpfc_els_flush_all_cmd(phba); + /* Unregister VFI */ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (!mbox) { lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY|LOG_MBOX, - "2556 UNREG_VFI mbox allocation failed" - "HBA state x%x\n", - phba->pport->port_state); - return; + "2556 UNREG_VFI mbox allocation failed" + "HBA state x%x\n", phba->pport->port_state); + return -ENOMEM; } lpfc_unreg_vfi(mbox, phba->pport); @@ -4514,58 +4676,163 @@ lpfc_unregister_unused_fcf(struct lpfc_hba *phba) rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT); if (rc == MBX_NOT_FINISHED) { lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY|LOG_MBOX, - "2557 UNREG_VFI issue mbox failed rc x%x " - "HBA state x%x\n", - rc, phba->pport->port_state); + "2557 UNREG_VFI issue mbox failed rc x%x " + "HBA state x%x\n", + rc, phba->pport->port_state); mempool_free(mbox, phba->mbox_mem_pool); - return; + return -EIO; } - /* Unregister FCF */ + shost = lpfc_shost_from_vport(phba->pport); + spin_lock_irq(shost->host_lock); + phba->pport->fc_flag &= ~FC_VFI_REGISTERED; + spin_unlock_irq(shost->host_lock); + + return 0; +} + +/** + * lpfc_sli4_unregister_fcf - Unregister currently registered FCF record + * @phba: Pointer to hba context object. + * + * This function issues synchronous unregister FCF mailbox command to HBA to + * unregister the currently registered FCF record. The driver does not reset + * the driver FCF usage state flags. + * + * Return 0 if successfully issued, none-zero otherwise. + */ +int +lpfc_sli4_unregister_fcf(struct lpfc_hba *phba) +{ + LPFC_MBOXQ_t *mbox; + int rc; + mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (!mbox) { lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY|LOG_MBOX, - "2551 UNREG_FCFI mbox allocation failed" - "HBA state x%x\n", - phba->pport->port_state); - return; + "2551 UNREG_FCFI mbox allocation failed" + "HBA state x%x\n", phba->pport->port_state); + return -ENOMEM; } - lpfc_unreg_fcfi(mbox, phba->fcf.fcfi); mbox->vport = phba->pport; mbox->mbox_cmpl = lpfc_unregister_fcfi_cmpl; rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT); if (rc == MBX_NOT_FINISHED) { - lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY|LOG_MBOX, - "2552 UNREG_FCFI issue mbox failed rc x%x " - "HBA state x%x\n", - rc, phba->pport->port_state); - mempool_free(mbox, phba->mbox_mem_pool); + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "2552 Unregister FCFI command failed rc x%x " + "HBA state x%x\n", + rc, phba->pport->port_state); + return -EINVAL; + } + return 0; +} + +/** + * lpfc_unregister_fcf_rescan - Unregister currently registered fcf and rescan + * @phba: Pointer to hba context object. + * + * This function unregisters the currently reigstered FCF. This function + * also tries to find another FCF for discovery by rescan the HBA FCF table. + */ +void +lpfc_unregister_fcf_rescan(struct lpfc_hba *phba) +{ + int rc; + + /* Preparation for unregistering fcf */ + rc = lpfc_unregister_fcf_prep(phba); + if (rc) { + lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY, + "2748 Failed to prepare for unregistering " + "HBA's FCF record: rc=%d\n", rc); return; } - spin_lock_irq(&phba->hbalock); - phba->fcf.fcf_flag &= ~(FCF_AVAILABLE | FCF_REGISTERED | - FCF_DISCOVERED | FCF_BOOT_ENABLE | FCF_IN_USE | - FCF_VALID_VLAN); - spin_unlock_irq(&phba->hbalock); + /* Now, unregister FCF record and reset HBA FCF state */ + rc = lpfc_sli4_unregister_fcf(phba); + if (rc) + return; + /* Reset HBA FCF states after successful unregister FCF */ + phba->fcf.fcf_flag = 0; /* * If driver is not unloading, check if there is any other * FCF record that can be used for discovery. */ if ((phba->pport->load_flag & FC_UNLOADING) || - (phba->link_state < LPFC_LINK_UP)) + (phba->link_state < LPFC_LINK_UP)) return; rc = lpfc_sli4_read_fcf_record(phba, LPFC_FCOE_FCF_GET_FIRST); if (rc) lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY|LOG_MBOX, - "2553 lpfc_unregister_unused_fcf failed to read FCF" - " record HBA state x%x\n", - phba->pport->port_state); + "2553 lpfc_unregister_unused_fcf failed " + "to read FCF record HBA state x%x\n", + phba->pport->port_state); +} + +/** + * lpfc_unregister_fcf - Unregister the currently registered fcf record + * @phba: Pointer to hba context object. + * + * This function just unregisters the currently reigstered FCF. It does not + * try to find another FCF for discovery. + */ +void +lpfc_unregister_fcf(struct lpfc_hba *phba) +{ + int rc; + + /* Preparation for unregistering fcf */ + rc = lpfc_unregister_fcf_prep(phba); + if (rc) { + lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY, + "2749 Failed to prepare for unregistering " + "HBA's FCF record: rc=%d\n", rc); + return; + } + + /* Now, unregister FCF record and reset HBA FCF state */ + rc = lpfc_sli4_unregister_fcf(phba); + if (rc) + return; + /* Set proper HBA FCF states after successful unregister FCF */ + spin_lock_irq(&phba->hbalock); + phba->fcf.fcf_flag &= ~FCF_REGISTERED; + spin_unlock_irq(&phba->hbalock); +} + +/** + * lpfc_unregister_unused_fcf - Unregister FCF if all devices are disconnected. + * @phba: Pointer to hba context object. + * + * This function check if there are any connected remote port for the FCF and + * if all the devices are disconnected, this function unregister FCFI. + * This function also tries to use another FCF for discovery. + */ +void +lpfc_unregister_unused_fcf(struct lpfc_hba *phba) +{ + /* + * If HBA is not running in FIP mode or if HBA does not support + * FCoE or if FCF is not registered, do nothing. + */ + spin_lock_irq(&phba->hbalock); + if (!(phba->hba_flag & HBA_FCOE_SUPPORT) || + !(phba->fcf.fcf_flag & FCF_REGISTERED) || + !(phba->hba_flag & HBA_FIP_SUPPORT)) { + spin_unlock_irq(&phba->hbalock); + return; + } + spin_unlock_irq(&phba->hbalock); + + if (lpfc_fcf_inuse(phba)) + return; + + lpfc_unregister_fcf_rescan(phba); } /** diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h index c9faa1d8c3c..89ff7c09e29 100644 --- a/drivers/scsi/lpfc/lpfc_hw.h +++ b/drivers/scsi/lpfc/lpfc_hw.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2009 Emulex. All rights reserved. * + * Copyright (C) 2004-2010 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * * @@ -1346,6 +1346,9 @@ typedef struct { /* FireFly BIU registers */ #define MBX_HEARTBEAT 0x31 #define MBX_WRITE_VPARMS 0x32 #define MBX_ASYNCEVT_ENABLE 0x33 +#define MBX_READ_EVENT_LOG_STATUS 0x37 +#define MBX_READ_EVENT_LOG 0x38 +#define MBX_WRITE_EVENT_LOG 0x39 #define MBX_PORT_CAPABILITIES 0x3B #define MBX_PORT_IOV_CONTROL 0x3C @@ -1465,17 +1468,13 @@ typedef struct { /* FireFly BIU registers */ #define CMD_IOCB_LOGENTRY_CN 0x94 #define CMD_IOCB_LOGENTRY_ASYNC_CN 0x96 -/* Unhandled Data Security SLI Commands */ -#define DSSCMD_IWRITE64_CR 0xD8 -#define DSSCMD_IWRITE64_CX 0xD9 -#define DSSCMD_IREAD64_CR 0xDA -#define DSSCMD_IREAD64_CX 0xDB -#define DSSCMD_INVALIDATE_DEK 0xDC -#define DSSCMD_SET_KEK 0xDD -#define DSSCMD_GET_KEK_ID 0xDE -#define DSSCMD_GEN_XFER 0xDF - -#define CMD_MAX_IOCB_CMD 0xE6 +/* Data Security SLI Commands */ +#define DSSCMD_IWRITE64_CR 0xF8 +#define DSSCMD_IWRITE64_CX 0xF9 +#define DSSCMD_IREAD64_CR 0xFA +#define DSSCMD_IREAD64_CX 0xFB + +#define CMD_MAX_IOCB_CMD 0xFB #define CMD_IOCB_MASK 0xff #define MAX_MSG_DATA 28 /* max msg data in CMD_ADAPTER_MSG diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h index 8a2a1c5935c..820015fbc4d 100644 --- a/drivers/scsi/lpfc/lpfc_hw4.h +++ b/drivers/scsi/lpfc/lpfc_hw4.h @@ -52,35 +52,37 @@ struct dma_address { uint32_t addr_hi; }; -#define LPFC_SLIREV_CONF_WORD 0x58 struct lpfc_sli_intf { uint32_t word0; -#define lpfc_sli_intf_iftype_MASK 0x00000007 -#define lpfc_sli_intf_iftype_SHIFT 0 -#define lpfc_sli_intf_iftype_WORD word0 -#define lpfc_sli_intf_rev_MASK 0x0000000f -#define lpfc_sli_intf_rev_SHIFT 4 -#define lpfc_sli_intf_rev_WORD word0 -#define LPFC_SLIREV_CONF_SLI4 4 -#define lpfc_sli_intf_family_MASK 0x000000ff -#define lpfc_sli_intf_family_SHIFT 8 -#define lpfc_sli_intf_family_WORD word0 -#define lpfc_sli_intf_feat1_MASK 0x000000ff -#define lpfc_sli_intf_feat1_SHIFT 16 -#define lpfc_sli_intf_feat1_WORD word0 -#define lpfc_sli_intf_feat2_MASK 0x0000001f -#define lpfc_sli_intf_feat2_SHIFT 24 -#define lpfc_sli_intf_feat2_WORD word0 -#define lpfc_sli_intf_valid_MASK 0x00000007 -#define lpfc_sli_intf_valid_SHIFT 29 -#define lpfc_sli_intf_valid_WORD word0 +#define lpfc_sli_intf_valid_SHIFT 29 +#define lpfc_sli_intf_valid_MASK 0x00000007 +#define lpfc_sli_intf_valid_WORD word0 #define LPFC_SLI_INTF_VALID 6 +#define lpfc_sli_intf_featurelevel2_SHIFT 24 +#define lpfc_sli_intf_featurelevel2_MASK 0x0000001F +#define lpfc_sli_intf_featurelevel2_WORD word0 +#define lpfc_sli_intf_featurelevel1_SHIFT 16 +#define lpfc_sli_intf_featurelevel1_MASK 0x000000FF +#define lpfc_sli_intf_featurelevel1_WORD word0 +#define LPFC_SLI_INTF_FEATURELEVEL1_1 1 +#define LPFC_SLI_INTF_FEATURELEVEL1_2 2 +#define lpfc_sli_intf_sli_family_SHIFT 8 +#define lpfc_sli_intf_sli_family_MASK 0x000000FF +#define lpfc_sli_intf_sli_family_WORD word0 +#define LPFC_SLI_INTF_FAMILY_BE2 0 +#define LPFC_SLI_INTF_FAMILY_BE3 1 +#define lpfc_sli_intf_slirev_SHIFT 4 +#define lpfc_sli_intf_slirev_MASK 0x0000000F +#define lpfc_sli_intf_slirev_WORD word0 +#define LPFC_SLI_INTF_REV_SLI3 3 +#define LPFC_SLI_INTF_REV_SLI4 4 +#define lpfc_sli_intf_if_type_SHIFT 0 +#define lpfc_sli_intf_if_type_MASK 0x00000007 +#define lpfc_sli_intf_if_type_WORD word0 +#define LPFC_SLI_INTF_IF_TYPE_0 0 +#define LPFC_SLI_INTF_IF_TYPE_1 1 }; -#define LPFC_SLI4_BAR0 1 -#define LPFC_SLI4_BAR1 2 -#define LPFC_SLI4_BAR2 4 - #define LPFC_SLI4_MBX_EMBED true #define LPFC_SLI4_MBX_NEMBED false @@ -161,6 +163,9 @@ struct lpfc_sli_intf { #define LPFC_FP_DEF_IMAX 10000 #define LPFC_SP_DEF_IMAX 10000 +/* PORT_CAPABILITIES constants. */ +#define LPFC_MAX_SUPPORTED_PAGES 8 + struct ulp_bde64 { union ULP_BDE_TUS { uint32_t w; @@ -516,7 +521,7 @@ struct lpfc_register { #define LPFC_UERR_STATUS_LO 0x00A0 #define LPFC_UE_MASK_HI 0x00AC #define LPFC_UE_MASK_LO 0x00A8 -#define LPFC_SCRATCHPAD 0x0058 +#define LPFC_SLI_INTF 0x0058 /* BAR0 Registers */ #define LPFC_HST_STATE 0x00AC @@ -576,19 +581,6 @@ struct lpfc_register { #define LPFC_POST_STAGE_ARMFW_READY 0xC000 #define LPFC_POST_STAGE_ARMFW_UE 0xF000 -#define lpfc_scratchpad_slirev_SHIFT 4 -#define lpfc_scratchpad_slirev_MASK 0xF -#define lpfc_scratchpad_slirev_WORD word0 -#define lpfc_scratchpad_chiptype_SHIFT 8 -#define lpfc_scratchpad_chiptype_MASK 0xFF -#define lpfc_scratchpad_chiptype_WORD word0 -#define lpfc_scratchpad_featurelevel1_SHIFT 16 -#define lpfc_scratchpad_featurelevel1_MASK 0xFF -#define lpfc_scratchpad_featurelevel1_WORD word0 -#define lpfc_scratchpad_featurelevel2_SHIFT 24 -#define lpfc_scratchpad_featurelevel2_MASK 0xFF -#define lpfc_scratchpad_featurelevel2_WORD word0 - /* BAR1 Registers */ #define LPFC_IMR_MASK_ALL 0xFFFFFFFF #define LPFC_ISCR_CLEAR_ALL 0xFFFFFFFF @@ -801,6 +793,7 @@ struct mbox_header { #define LPFC_MBOX_OPCODE_FCOE_ADD_FCF 0x09 #define LPFC_MBOX_OPCODE_FCOE_DELETE_FCF 0x0A #define LPFC_MBOX_OPCODE_FCOE_POST_HDR_TEMPLATE 0x0B +#define LPFC_MBOX_OPCODE_FCOE_REDISCOVER_FCF 0x10 /* Mailbox command structures */ struct eq_context { @@ -1149,10 +1142,7 @@ struct sli4_sge { /* SLI-4 */ this flag !! */ #define lpfc_sli4_sge_last_MASK 0x00000001 #define lpfc_sli4_sge_last_WORD word2 - uint32_t word3; -#define lpfc_sli4_sge_len_SHIFT 0 -#define lpfc_sli4_sge_len_MASK 0x0001FFFF -#define lpfc_sli4_sge_len_WORD word3 + uint32_t sge_len; }; struct fcf_record { @@ -1301,6 +1291,19 @@ struct lpfc_mbx_del_fcf_tbl_entry { #define lpfc_mbx_del_fcf_tbl_index_WORD word10 }; +struct lpfc_mbx_redisc_fcf_tbl { + struct mbox_header header; + uint32_t word10; +#define lpfc_mbx_redisc_fcf_count_SHIFT 0 +#define lpfc_mbx_redisc_fcf_count_MASK 0x0000FFFF +#define lpfc_mbx_redisc_fcf_count_WORD word10 + uint32_t resvd; + uint32_t word12; +#define lpfc_mbx_redisc_fcf_index_SHIFT 0 +#define lpfc_mbx_redisc_fcf_index_MASK 0x0000FFFF +#define lpfc_mbx_redisc_fcf_index_WORD word12 +}; + struct lpfc_mbx_query_fw_cfg { struct mbox_header header; uint32_t config_number; @@ -1834,6 +1837,177 @@ struct lpfc_mbx_request_features { #define lpfc_mbx_rq_ftr_rsp_ifip_WORD word3 }; +struct lpfc_mbx_supp_pages { + uint32_t word1; +#define qs_SHIFT 0 +#define qs_MASK 0x00000001 +#define qs_WORD word1 +#define wr_SHIFT 1 +#define wr_MASK 0x00000001 +#define wr_WORD word1 +#define pf_SHIFT 8 +#define pf_MASK 0x000000ff +#define pf_WORD word1 +#define cpn_SHIFT 16 +#define cpn_MASK 0x000000ff +#define cpn_WORD word1 + uint32_t word2; +#define list_offset_SHIFT 0 +#define list_offset_MASK 0x000000ff +#define list_offset_WORD word2 +#define next_offset_SHIFT 8 +#define next_offset_MASK 0x000000ff +#define next_offset_WORD word2 +#define elem_cnt_SHIFT 16 +#define elem_cnt_MASK 0x000000ff +#define elem_cnt_WORD word2 + uint32_t word3; +#define pn_0_SHIFT 24 +#define pn_0_MASK 0x000000ff +#define pn_0_WORD word3 +#define pn_1_SHIFT 16 +#define pn_1_MASK 0x000000ff +#define pn_1_WORD word3 +#define pn_2_SHIFT 8 +#define pn_2_MASK 0x000000ff +#define pn_2_WORD word3 +#define pn_3_SHIFT 0 +#define pn_3_MASK 0x000000ff +#define pn_3_WORD word3 + uint32_t word4; +#define pn_4_SHIFT 24 +#define pn_4_MASK 0x000000ff +#define pn_4_WORD word4 +#define pn_5_SHIFT 16 +#define pn_5_MASK 0x000000ff +#define pn_5_WORD word4 +#define pn_6_SHIFT 8 +#define pn_6_MASK 0x000000ff +#define pn_6_WORD word4 +#define pn_7_SHIFT 0 +#define pn_7_MASK 0x000000ff +#define pn_7_WORD word4 + uint32_t rsvd[27]; +#define LPFC_SUPP_PAGES 0 +#define LPFC_BLOCK_GUARD_PROFILES 1 +#define LPFC_SLI4_PARAMETERS 2 +}; + +struct lpfc_mbx_sli4_params { + uint32_t word1; +#define qs_SHIFT 0 +#define qs_MASK 0x00000001 +#define qs_WORD word1 +#define wr_SHIFT 1 +#define wr_MASK 0x00000001 +#define wr_WORD word1 +#define pf_SHIFT 8 +#define pf_MASK 0x000000ff +#define pf_WORD word1 +#define cpn_SHIFT 16 +#define cpn_MASK 0x000000ff +#define cpn_WORD word1 + uint32_t word2; +#define if_type_SHIFT 0 +#define if_type_MASK 0x00000007 +#define if_type_WORD word2 +#define sli_rev_SHIFT 4 +#define sli_rev_MASK 0x0000000f +#define sli_rev_WORD word2 +#define sli_family_SHIFT 8 +#define sli_family_MASK 0x000000ff +#define sli_family_WORD word2 +#define featurelevel_1_SHIFT 16 +#define featurelevel_1_MASK 0x000000ff +#define featurelevel_1_WORD word2 +#define featurelevel_2_SHIFT 24 +#define featurelevel_2_MASK 0x0000001f +#define featurelevel_2_WORD word2 + uint32_t word3; +#define fcoe_SHIFT 0 +#define fcoe_MASK 0x00000001 +#define fcoe_WORD word3 +#define fc_SHIFT 1 +#define fc_MASK 0x00000001 +#define fc_WORD word3 +#define nic_SHIFT 2 +#define nic_MASK 0x00000001 +#define nic_WORD word3 +#define iscsi_SHIFT 3 +#define iscsi_MASK 0x00000001 +#define iscsi_WORD word3 +#define rdma_SHIFT 4 +#define rdma_MASK 0x00000001 +#define rdma_WORD word3 + uint32_t sge_supp_len; + uint32_t word5; +#define if_page_sz_SHIFT 0 +#define if_page_sz_MASK 0x0000ffff +#define if_page_sz_WORD word5 +#define loopbk_scope_SHIFT 24 +#define loopbk_scope_MASK 0x0000000f +#define loopbk_scope_WORD word5 +#define rq_db_window_SHIFT 28 +#define rq_db_window_MASK 0x0000000f +#define rq_db_window_WORD word5 + uint32_t word6; +#define eq_pages_SHIFT 0 +#define eq_pages_MASK 0x0000000f +#define eq_pages_WORD word6 +#define eqe_size_SHIFT 8 +#define eqe_size_MASK 0x000000ff +#define eqe_size_WORD word6 + uint32_t word7; +#define cq_pages_SHIFT 0 +#define cq_pages_MASK 0x0000000f +#define cq_pages_WORD word7 +#define cqe_size_SHIFT 8 +#define cqe_size_MASK 0x000000ff +#define cqe_size_WORD word7 + uint32_t word8; +#define mq_pages_SHIFT 0 +#define mq_pages_MASK 0x0000000f +#define mq_pages_WORD word8 +#define mqe_size_SHIFT 8 +#define mqe_size_MASK 0x000000ff +#define mqe_size_WORD word8 +#define mq_elem_cnt_SHIFT 16 +#define mq_elem_cnt_MASK 0x000000ff +#define mq_elem_cnt_WORD word8 + uint32_t word9; +#define wq_pages_SHIFT 0 +#define wq_pages_MASK 0x0000ffff +#define wq_pages_WORD word9 +#define wqe_size_SHIFT 8 +#define wqe_size_MASK 0x000000ff +#define wqe_size_WORD word9 + uint32_t word10; +#define rq_pages_SHIFT 0 +#define rq_pages_MASK 0x0000ffff +#define rq_pages_WORD word10 +#define rqe_size_SHIFT 8 +#define rqe_size_MASK 0x000000ff +#define rqe_size_WORD word10 + uint32_t word11; +#define hdr_pages_SHIFT 0 +#define hdr_pages_MASK 0x0000000f +#define hdr_pages_WORD word11 +#define hdr_size_SHIFT 8 +#define hdr_size_MASK 0x0000000f +#define hdr_size_WORD word11 +#define hdr_pp_align_SHIFT 16 +#define hdr_pp_align_MASK 0x0000ffff +#define hdr_pp_align_WORD word11 + uint32_t word12; +#define sgl_pages_SHIFT 0 +#define sgl_pages_MASK 0x0000000f +#define sgl_pages_WORD word12 +#define sgl_pp_align_SHIFT 16 +#define sgl_pp_align_MASK 0x0000ffff +#define sgl_pp_align_WORD word12 + uint32_t rsvd_13_63[51]; +}; + /* Mailbox Completion Queue Error Messages */ #define MB_CQE_STATUS_SUCCESS 0x0 #define MB_CQE_STATUS_INSUFFICIENT_PRIVILEGES 0x1 @@ -1863,6 +2037,7 @@ struct lpfc_mqe { struct lpfc_mbx_read_fcf_tbl read_fcf_tbl; struct lpfc_mbx_add_fcf_tbl_entry add_fcf_entry; struct lpfc_mbx_del_fcf_tbl_entry del_fcf_entry; + struct lpfc_mbx_redisc_fcf_tbl redisc_fcf_tbl; struct lpfc_mbx_reg_fcfi reg_fcfi; struct lpfc_mbx_unreg_fcfi unreg_fcfi; struct lpfc_mbx_mq_create mq_create; @@ -1883,6 +2058,8 @@ struct lpfc_mqe { struct lpfc_mbx_request_features req_ftrs; struct lpfc_mbx_post_hdr_tmpl hdr_tmpl; struct lpfc_mbx_query_fw_cfg query_fw_cfg; + struct lpfc_mbx_supp_pages supp_pages; + struct lpfc_mbx_sli4_params sli4_params; struct lpfc_mbx_nop nop; } un; }; @@ -1959,6 +2136,9 @@ struct lpfc_acqe_link { #define LPFC_ASYNC_LINK_FAULT_NONE 0x0 #define LPFC_ASYNC_LINK_FAULT_LOCAL 0x1 #define LPFC_ASYNC_LINK_FAULT_REMOTE 0x2 +#define lpfc_acqe_qos_link_speed_SHIFT 16 +#define lpfc_acqe_qos_link_speed_MASK 0x0000FFFF +#define lpfc_acqe_qos_link_speed_WORD word1 uint32_t event_tag; uint32_t trailer; }; @@ -1976,6 +2156,7 @@ struct lpfc_acqe_fcoe { #define LPFC_FCOE_EVENT_TYPE_FCF_TABLE_FULL 0x2 #define LPFC_FCOE_EVENT_TYPE_FCF_DEAD 0x3 #define LPFC_FCOE_EVENT_TYPE_CVL 0x4 +#define LPFC_FCOE_EVENT_TYPE_FCF_PARAM_MOD 0x5 uint32_t event_tag; uint32_t trailer; }; diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index b8eb1b6e5e7..d29ac7c317d 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2009 Emulex. All rights reserved. * + * Copyright (C) 2004-2010 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * @@ -544,7 +544,7 @@ lpfc_config_port_post(struct lpfc_hba *phba) mempool_free(pmb, phba->mbox_mem_pool); return -EIO; } - } else { + } else if (phba->cfg_suppress_link_up == 0) { lpfc_init_link(phba, pmb, phba->cfg_topology, phba->cfg_link_speed); pmb->mbox_cmpl = lpfc_sli_def_mbox_cmpl; @@ -603,6 +603,102 @@ lpfc_config_port_post(struct lpfc_hba *phba) } /** + * lpfc_hba_init_link - Initialize the FC link + * @phba: pointer to lpfc hba data structure. + * + * This routine will issue the INIT_LINK mailbox command call. + * It is available to other drivers through the lpfc_hba data + * structure for use as a delayed link up mechanism with the + * module parameter lpfc_suppress_link_up. + * + * Return code + * 0 - success + * Any other value - error + **/ +int +lpfc_hba_init_link(struct lpfc_hba *phba) +{ + struct lpfc_vport *vport = phba->pport; + LPFC_MBOXQ_t *pmb; + MAILBOX_t *mb; + int rc; + + pmb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); + if (!pmb) { + phba->link_state = LPFC_HBA_ERROR; + return -ENOMEM; + } + mb = &pmb->u.mb; + pmb->vport = vport; + + lpfc_init_link(phba, pmb, phba->cfg_topology, + phba->cfg_link_speed); + pmb->mbox_cmpl = lpfc_sli_def_mbox_cmpl; + lpfc_set_loopback_flag(phba); + rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT); + if (rc != MBX_SUCCESS) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "0498 Adapter failed to init, mbxCmd x%x " + "INIT_LINK, mbxStatus x%x\n", + mb->mbxCommand, mb->mbxStatus); + /* Clear all interrupt enable conditions */ + writel(0, phba->HCregaddr); + readl(phba->HCregaddr); /* flush */ + /* Clear all pending interrupts */ + writel(0xffffffff, phba->HAregaddr); + readl(phba->HAregaddr); /* flush */ + phba->link_state = LPFC_HBA_ERROR; + if (rc != MBX_BUSY) + mempool_free(pmb, phba->mbox_mem_pool); + return -EIO; + } + phba->cfg_suppress_link_up = 0; + + return 0; +} + +/** + * lpfc_hba_down_link - this routine downs the FC link + * + * This routine will issue the DOWN_LINK mailbox command call. + * It is available to other drivers through the lpfc_hba data + * structure for use to stop the link. + * + * Return code + * 0 - success + * Any other value - error + **/ +int +lpfc_hba_down_link(struct lpfc_hba *phba) +{ + LPFC_MBOXQ_t *pmb; + int rc; + + pmb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); + if (!pmb) { + phba->link_state = LPFC_HBA_ERROR; + return -ENOMEM; + } + + lpfc_printf_log(phba, + KERN_ERR, LOG_INIT, + "0491 Adapter Link is disabled.\n"); + lpfc_down_link(phba, pmb); + pmb->mbox_cmpl = lpfc_sli_def_mbox_cmpl; + rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT); + if ((rc != MBX_SUCCESS) && (rc != MBX_BUSY)) { + lpfc_printf_log(phba, + KERN_ERR, LOG_INIT, + "2522 Adapter failed to issue DOWN_LINK" + " mbox command rc 0x%x\n", rc); + + mempool_free(pmb, phba->mbox_mem_pool); + return -EIO; + } + return 0; +} + +/** * lpfc_hba_down_prep - Perform lpfc uninitialization prior to HBA reset * @phba: pointer to lpfc HBA data structure. * @@ -2073,6 +2169,44 @@ lpfc_stop_vport_timers(struct lpfc_vport *vport) } /** + * __lpfc_sli4_stop_fcf_redisc_wait_timer - Stop FCF rediscovery wait timer + * @phba: pointer to lpfc hba data structure. + * + * This routine stops the SLI4 FCF rediscover wait timer if it's on. The + * caller of this routine should already hold the host lock. + **/ +void +__lpfc_sli4_stop_fcf_redisc_wait_timer(struct lpfc_hba *phba) +{ + /* Clear pending FCF rediscovery wait timer */ + phba->fcf.fcf_flag &= ~FCF_REDISC_PEND; + /* Now, try to stop the timer */ + del_timer(&phba->fcf.redisc_wait); +} + +/** + * lpfc_sli4_stop_fcf_redisc_wait_timer - Stop FCF rediscovery wait timer + * @phba: pointer to lpfc hba data structure. + * + * This routine stops the SLI4 FCF rediscover wait timer if it's on. It + * checks whether the FCF rediscovery wait timer is pending with the host + * lock held before proceeding with disabling the timer and clearing the + * wait timer pendig flag. + **/ +void +lpfc_sli4_stop_fcf_redisc_wait_timer(struct lpfc_hba *phba) +{ + spin_lock_irq(&phba->hbalock); + if (!(phba->fcf.fcf_flag & FCF_REDISC_PEND)) { + /* FCF rediscovery timer already fired or stopped */ + spin_unlock_irq(&phba->hbalock); + return; + } + __lpfc_sli4_stop_fcf_redisc_wait_timer(phba); + spin_unlock_irq(&phba->hbalock); +} + +/** * lpfc_stop_hba_timers - Stop all the timers associated with an HBA * @phba: pointer to lpfc hba data structure. * @@ -2096,6 +2230,7 @@ lpfc_stop_hba_timers(struct lpfc_hba *phba) break; case LPFC_PCI_DEV_OC: /* Stop any OneConnect device sepcific driver timers */ + lpfc_sli4_stop_fcf_redisc_wait_timer(phba); break; default: lpfc_printf_log(phba, KERN_ERR, LOG_INIT, @@ -2228,6 +2363,7 @@ lpfc_offline_prep(struct lpfc_hba * phba) struct lpfc_vport *vport = phba->pport; struct lpfc_nodelist *ndlp, *next_ndlp; struct lpfc_vport **vports; + struct Scsi_Host *shost; int i; if (vport->fc_flag & FC_OFFLINE_MODE) @@ -2241,11 +2377,15 @@ lpfc_offline_prep(struct lpfc_hba * phba) vports = lpfc_create_vport_work_array(phba); if (vports != NULL) { for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) { - struct Scsi_Host *shost; - if (vports[i]->load_flag & FC_UNLOADING) continue; + shost = lpfc_shost_from_vport(vports[i]); + spin_lock_irq(shost->host_lock); vports[i]->vpi_state &= ~LPFC_VPI_REGISTERED; + vports[i]->fc_flag |= FC_VPORT_NEEDS_REG_VPI; + vports[i]->fc_flag &= ~FC_VFI_REGISTERED; + spin_unlock_irq(shost->host_lock); + shost = lpfc_shost_from_vport(vports[i]); list_for_each_entry_safe(ndlp, next_ndlp, &vports[i]->fc_nodes, @@ -2401,7 +2541,8 @@ lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev) shost->this_id = -1; shost->max_cmd_len = 16; if (phba->sli_rev == LPFC_SLI_REV4) { - shost->dma_boundary = LPFC_SLI4_MAX_SEGMENT_SIZE; + shost->dma_boundary = + phba->sli4_hba.pc_sli4_params.sge_supp_len; shost->sg_tablesize = phba->cfg_sg_seg_cnt; } @@ -2650,8 +2791,6 @@ lpfc_stop_port_s4(struct lpfc_hba *phba) lpfc_stop_hba_timers(phba); phba->pport->work_port_events = 0; phba->sli4_hba.intr_enable = 0; - /* Hard clear it for now, shall have more graceful way to wait later */ - phba->sli.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE; } /** @@ -2703,7 +2842,7 @@ lpfc_sli_remove_dflt_fcf(struct lpfc_hba *phba) del_fcf_record = &mboxq->u.mqe.un.del_fcf_entry; bf_set(lpfc_mbx_del_fcf_tbl_count, del_fcf_record, 1); bf_set(lpfc_mbx_del_fcf_tbl_index, del_fcf_record, - phba->fcf.fcf_indx); + phba->fcf.current_rec.fcf_indx); if (!phba->sli4_hba.intr_enable) rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL); @@ -2727,6 +2866,57 @@ lpfc_sli_remove_dflt_fcf(struct lpfc_hba *phba) } /** + * lpfc_fcf_redisc_wait_start_timer - Start fcf rediscover wait timer + * @phba: Pointer to hba for which this call is being executed. + * + * This routine starts the timer waiting for the FCF rediscovery to complete. + **/ +void +lpfc_fcf_redisc_wait_start_timer(struct lpfc_hba *phba) +{ + unsigned long fcf_redisc_wait_tmo = + (jiffies + msecs_to_jiffies(LPFC_FCF_REDISCOVER_WAIT_TMO)); + /* Start fcf rediscovery wait period timer */ + mod_timer(&phba->fcf.redisc_wait, fcf_redisc_wait_tmo); + spin_lock_irq(&phba->hbalock); + /* Allow action to new fcf asynchronous event */ + phba->fcf.fcf_flag &= ~(FCF_AVAILABLE | FCF_SCAN_DONE); + /* Mark the FCF rediscovery pending state */ + phba->fcf.fcf_flag |= FCF_REDISC_PEND; + spin_unlock_irq(&phba->hbalock); +} + +/** + * lpfc_sli4_fcf_redisc_wait_tmo - FCF table rediscover wait timeout + * @ptr: Map to lpfc_hba data structure pointer. + * + * This routine is invoked when waiting for FCF table rediscover has been + * timed out. If new FCF record(s) has (have) been discovered during the + * wait period, a new FCF event shall be added to the FCOE async event + * list, and then worker thread shall be waked up for processing from the + * worker thread context. + **/ +void +lpfc_sli4_fcf_redisc_wait_tmo(unsigned long ptr) +{ + struct lpfc_hba *phba = (struct lpfc_hba *)ptr; + + /* Don't send FCF rediscovery event if timer cancelled */ + spin_lock_irq(&phba->hbalock); + if (!(phba->fcf.fcf_flag & FCF_REDISC_PEND)) { + spin_unlock_irq(&phba->hbalock); + return; + } + /* Clear FCF rediscovery timer pending flag */ + phba->fcf.fcf_flag &= ~FCF_REDISC_PEND; + /* FCF rediscovery event to worker thread */ + phba->fcf.fcf_flag |= FCF_REDISC_EVT; + spin_unlock_irq(&phba->hbalock); + /* wake up worker thread */ + lpfc_worker_wake_up(phba); +} + +/** * lpfc_sli4_fw_cfg_check - Read the firmware config and verify FCoE support * @phba: pointer to lpfc hba data structure. * @@ -2978,6 +3168,8 @@ lpfc_sli4_async_link_evt(struct lpfc_hba *phba, bf_get(lpfc_acqe_link_physical, acqe_link); phba->sli4_hba.link_state.fault = bf_get(lpfc_acqe_link_fault, acqe_link); + phba->sli4_hba.link_state.logical_speed = + bf_get(lpfc_acqe_qos_link_speed, acqe_link); /* Invoke the lpfc_handle_latt mailbox command callback function */ lpfc_mbx_cmpl_read_la(phba, pmb); @@ -3007,22 +3199,34 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp; struct Scsi_Host *shost; uint32_t link_state; + int active_vlink_present; + struct lpfc_vport **vports; + int i; phba->fc_eventTag = acqe_fcoe->event_tag; phba->fcoe_eventtag = acqe_fcoe->event_tag; switch (event_type) { case LPFC_FCOE_EVENT_TYPE_NEW_FCF: + case LPFC_FCOE_EVENT_TYPE_FCF_PARAM_MOD: lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY, "2546 New FCF found index 0x%x tag 0x%x\n", acqe_fcoe->index, acqe_fcoe->event_tag); - /* - * If the current FCF is in discovered state, or - * FCF discovery is in progress do nothing. - */ spin_lock_irq(&phba->hbalock); - if ((phba->fcf.fcf_flag & FCF_DISCOVERED) || - (phba->hba_flag & FCF_DISC_INPROGRESS)) { + if ((phba->fcf.fcf_flag & FCF_SCAN_DONE) || + (phba->hba_flag & FCF_DISC_INPROGRESS)) { + /* + * If the current FCF is in discovered state or + * FCF discovery is in progress, do nothing. + */ + spin_unlock_irq(&phba->hbalock); + break; + } + if (phba->fcf.fcf_flag & FCF_REDISC_EVT) { + /* + * If fast FCF failover rescan event is pending, + * do nothing. + */ spin_unlock_irq(&phba->hbalock); break; } @@ -3049,7 +3253,7 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba, " tag 0x%x\n", acqe_fcoe->index, acqe_fcoe->event_tag); /* If the event is not for currently used fcf do nothing */ - if (phba->fcf.fcf_indx != acqe_fcoe->index) + if (phba->fcf.current_rec.fcf_indx != acqe_fcoe->index) break; /* * Currently, driver support only one FCF - so treat this as @@ -3074,14 +3278,58 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba, if (!ndlp) break; shost = lpfc_shost_from_vport(vport); + if (phba->pport->port_state <= LPFC_FLOGI) + break; + /* If virtual link is not yet instantiated ignore CVL */ + if (vport->port_state <= LPFC_FDISC) + break; + lpfc_linkdown_port(vport); - if (vport->port_type != LPFC_NPIV_PORT) { + lpfc_cleanup_pending_mbox(vport); + spin_lock_irq(shost->host_lock); + vport->fc_flag |= FC_VPORT_CVL_RCVD; + spin_unlock_irq(shost->host_lock); + active_vlink_present = 0; + + vports = lpfc_create_vport_work_array(phba); + if (vports) { + for (i = 0; i <= phba->max_vports && vports[i] != NULL; + i++) { + if ((!(vports[i]->fc_flag & + FC_VPORT_CVL_RCVD)) && + (vports[i]->port_state > LPFC_FDISC)) { + active_vlink_present = 1; + break; + } + } + lpfc_destroy_vport_work_array(phba, vports); + } + + if (active_vlink_present) { + /* + * If there are other active VLinks present, + * re-instantiate the Vlink using FDISC. + */ mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ); spin_lock_irq(shost->host_lock); ndlp->nlp_flag |= NLP_DELAY_TMO; spin_unlock_irq(shost->host_lock); - ndlp->nlp_last_elscmd = ELS_CMD_FLOGI; - vport->port_state = LPFC_FLOGI; + ndlp->nlp_last_elscmd = ELS_CMD_FDISC; + vport->port_state = LPFC_FDISC; + } else { + /* + * Otherwise, we request port to rediscover + * the entire FCF table for a fast recovery + * from possible case that the current FCF + * is no longer valid. + */ + rc = lpfc_sli4_redisc_fcf_table(phba); + if (rc) + /* + * Last resort will be re-try on the + * the current registered FCF entry. + */ + lpfc_retry_pport_discovery(phba); } break; default: @@ -3158,6 +3406,34 @@ void lpfc_sli4_async_event_proc(struct lpfc_hba *phba) } /** + * lpfc_sli4_fcf_redisc_event_proc - Process fcf table rediscovery event + * @phba: pointer to lpfc hba data structure. + * + * This routine is invoked by the worker thread to process FCF table + * rediscovery pending completion event. + **/ +void lpfc_sli4_fcf_redisc_event_proc(struct lpfc_hba *phba) +{ + int rc; + + spin_lock_irq(&phba->hbalock); + /* Clear FCF rediscovery timeout event */ + phba->fcf.fcf_flag &= ~FCF_REDISC_EVT; + /* Clear driver fast failover FCF record flag */ + phba->fcf.failover_rec.flag = 0; + /* Set state for FCF fast failover */ + phba->fcf.fcf_flag |= FCF_REDISC_FOV; + spin_unlock_irq(&phba->hbalock); + + /* Scan FCF table from the first entry to re-discover SAN */ + rc = lpfc_sli4_read_fcf_record(phba, LPFC_FCOE_FCF_GET_FIRST); + if (rc) + lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY, + "2747 Post FCF rediscovery read FCF record " + "failed 0x%x\n", rc); +} + +/** * lpfc_api_table_setup - Set up per hba pci-device group func api jump table * @phba: pointer to lpfc hba data structure. * @dev_grp: The HBA PCI-Device group number. @@ -3442,8 +3718,10 @@ static int lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) { struct lpfc_sli *psli; - int rc; - int i, hbq_count; + LPFC_MBOXQ_t *mboxq; + int rc, i, hbq_count, buf_size, dma_buf_size, max_buf_size; + uint8_t pn_page[LPFC_MAX_SUPPORTED_PAGES] = {0}; + struct lpfc_mqe *mqe; /* Before proceed, wait for POST done and device ready */ rc = lpfc_sli4_post_status_check(phba); @@ -3472,6 +3750,11 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) init_timer(&phba->eratt_poll); phba->eratt_poll.function = lpfc_poll_eratt; phba->eratt_poll.data = (unsigned long) phba; + /* FCF rediscover timer */ + init_timer(&phba->fcf.redisc_wait); + phba->fcf.redisc_wait.function = lpfc_sli4_fcf_redisc_wait_tmo; + phba->fcf.redisc_wait.data = (unsigned long)phba; + /* * We need to do a READ_CONFIG mailbox command here before * calling lpfc_get_cfgparam. For VFs this will report the @@ -3496,31 +3779,26 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) * used to create the sg_dma_buf_pool must be dynamically calculated. * 2 segments are added since the IOCB needs a command and response bde. * To insure that the scsi sgl does not cross a 4k page boundary only - * sgl sizes of 1k, 2k, 4k, and 8k are supported. - * Table of sgl sizes and seg_cnt: - * sgl size, sg_seg_cnt total seg - * 1k 50 52 - * 2k 114 116 - * 4k 242 244 - * 8k 498 500 - * cmd(32) + rsp(160) + (52 * sizeof(sli4_sge)) = 1024 - * cmd(32) + rsp(160) + (116 * sizeof(sli4_sge)) = 2048 - * cmd(32) + rsp(160) + (244 * sizeof(sli4_sge)) = 4096 - * cmd(32) + rsp(160) + (500 * sizeof(sli4_sge)) = 8192 + * sgl sizes of must be a power of 2. */ - if (phba->cfg_sg_seg_cnt <= LPFC_DEFAULT_SG_SEG_CNT) - phba->cfg_sg_seg_cnt = 50; - else if (phba->cfg_sg_seg_cnt <= 114) - phba->cfg_sg_seg_cnt = 114; - else if (phba->cfg_sg_seg_cnt <= 242) - phba->cfg_sg_seg_cnt = 242; + buf_size = (sizeof(struct fcp_cmnd) + sizeof(struct fcp_rsp) + + ((phba->cfg_sg_seg_cnt + 2) * sizeof(struct sli4_sge))); + /* Feature Level 1 hardware is limited to 2 pages */ + if ((bf_get(lpfc_sli_intf_featurelevel1, &phba->sli4_hba.sli_intf) == + LPFC_SLI_INTF_FEATURELEVEL1_1)) + max_buf_size = LPFC_SLI4_FL1_MAX_BUF_SIZE; else - phba->cfg_sg_seg_cnt = 498; - - phba->cfg_sg_dma_buf_size = sizeof(struct fcp_cmnd) - + sizeof(struct fcp_rsp); - phba->cfg_sg_dma_buf_size += - ((phba->cfg_sg_seg_cnt + 2) * sizeof(struct sli4_sge)); + max_buf_size = LPFC_SLI4_MAX_BUF_SIZE; + for (dma_buf_size = LPFC_SLI4_MIN_BUF_SIZE; + dma_buf_size < max_buf_size && buf_size > dma_buf_size; + dma_buf_size = dma_buf_size << 1) + ; + if (dma_buf_size == max_buf_size) + phba->cfg_sg_seg_cnt = (dma_buf_size - + sizeof(struct fcp_cmnd) - sizeof(struct fcp_rsp) - + (2 * sizeof(struct sli4_sge))) / + sizeof(struct sli4_sge); + phba->cfg_sg_dma_buf_size = dma_buf_size; /* Initialize buffer queue management fields */ hbq_count = lpfc_sli_hbq_count(); @@ -3638,6 +3916,43 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) goto out_free_fcp_eq_hdl; } + mboxq = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, + GFP_KERNEL); + if (!mboxq) { + rc = -ENOMEM; + goto out_free_fcp_eq_hdl; + } + + /* Get the Supported Pages. It is always available. */ + lpfc_supported_pages(mboxq); + rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL); + if (unlikely(rc)) { + rc = -EIO; + mempool_free(mboxq, phba->mbox_mem_pool); + goto out_free_fcp_eq_hdl; + } + + mqe = &mboxq->u.mqe; + memcpy(&pn_page[0], ((uint8_t *)&mqe->un.supp_pages.word3), + LPFC_MAX_SUPPORTED_PAGES); + for (i = 0; i < LPFC_MAX_SUPPORTED_PAGES; i++) { + switch (pn_page[i]) { + case LPFC_SLI4_PARAMETERS: + phba->sli4_hba.pc_sli4_params.supported = 1; + break; + default: + break; + } + } + + /* Read the port's SLI4 Parameters capabilities if supported. */ + if (phba->sli4_hba.pc_sli4_params.supported) + rc = lpfc_pc_sli4_params_get(phba, mboxq); + mempool_free(mboxq, phba->mbox_mem_pool); + if (rc) { + rc = -EIO; + goto out_free_fcp_eq_hdl; + } return rc; out_free_fcp_eq_hdl: @@ -3733,6 +4048,8 @@ lpfc_sli4_driver_resource_unset(struct lpfc_hba *phba) int lpfc_init_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp) { + phba->lpfc_hba_init_link = lpfc_hba_init_link; + phba->lpfc_hba_down_link = lpfc_hba_down_link; switch (dev_grp) { case LPFC_PCI_DEV_LP: phba->lpfc_hba_down_post = lpfc_hba_down_post_s3; @@ -4291,7 +4608,7 @@ lpfc_hba_alloc(struct pci_dev *pdev) return NULL; } - mutex_init(&phba->ct_event_mutex); + spin_lock_init(&phba->ct_ev_lock); INIT_LIST_HEAD(&phba->ct_ev_waiters); return phba; @@ -4641,7 +4958,7 @@ lpfc_sli_pci_mem_unset(struct lpfc_hba *phba) int lpfc_sli4_post_status_check(struct lpfc_hba *phba) { - struct lpfc_register sta_reg, uerrlo_reg, uerrhi_reg, scratchpad; + struct lpfc_register sta_reg, uerrlo_reg, uerrhi_reg; int i, port_error = -ENODEV; if (!phba->sli4_hba.STAregaddr) @@ -4677,14 +4994,21 @@ lpfc_sli4_post_status_check(struct lpfc_hba *phba) bf_get(lpfc_hst_state_port_status, &sta_reg)); /* Log device information */ - scratchpad.word0 = readl(phba->sli4_hba.SCRATCHPADregaddr); - lpfc_printf_log(phba, KERN_INFO, LOG_INIT, - "2534 Device Info: ChipType=0x%x, SliRev=0x%x, " - "FeatureL1=0x%x, FeatureL2=0x%x\n", - bf_get(lpfc_scratchpad_chiptype, &scratchpad), - bf_get(lpfc_scratchpad_slirev, &scratchpad), - bf_get(lpfc_scratchpad_featurelevel1, &scratchpad), - bf_get(lpfc_scratchpad_featurelevel2, &scratchpad)); + phba->sli4_hba.sli_intf.word0 = readl(phba->sli4_hba.SLIINTFregaddr); + if (bf_get(lpfc_sli_intf_valid, + &phba->sli4_hba.sli_intf) == LPFC_SLI_INTF_VALID) { + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "2534 Device Info: ChipType=0x%x, SliRev=0x%x, " + "FeatureL1=0x%x, FeatureL2=0x%x\n", + bf_get(lpfc_sli_intf_sli_family, + &phba->sli4_hba.sli_intf), + bf_get(lpfc_sli_intf_slirev, + &phba->sli4_hba.sli_intf), + bf_get(lpfc_sli_intf_featurelevel1, + &phba->sli4_hba.sli_intf), + bf_get(lpfc_sli_intf_featurelevel2, + &phba->sli4_hba.sli_intf)); + } phba->sli4_hba.ue_mask_lo = readl(phba->sli4_hba.UEMASKLOregaddr); phba->sli4_hba.ue_mask_hi = readl(phba->sli4_hba.UEMASKHIregaddr); /* With uncoverable error, log the error message and return error */ @@ -4723,8 +5047,8 @@ lpfc_sli4_bar0_register_memmap(struct lpfc_hba *phba) LPFC_UE_MASK_LO; phba->sli4_hba.UEMASKHIregaddr = phba->sli4_hba.conf_regs_memmap_p + LPFC_UE_MASK_HI; - phba->sli4_hba.SCRATCHPADregaddr = phba->sli4_hba.conf_regs_memmap_p + - LPFC_SCRATCHPAD; + phba->sli4_hba.SLIINTFregaddr = phba->sli4_hba.conf_regs_memmap_p + + LPFC_SLI_INTF; } /** @@ -5999,7 +6323,7 @@ lpfc_sli4_fcfi_unreg(struct lpfc_hba *phba, uint16_t fcfi) spin_lock_irqsave(&phba->hbalock, flags); /* Mark the FCFI is no longer registered */ phba->fcf.fcf_flag &= - ~(FCF_AVAILABLE | FCF_REGISTERED | FCF_DISCOVERED); + ~(FCF_AVAILABLE | FCF_REGISTERED | FCF_SCAN_DONE); spin_unlock_irqrestore(&phba->hbalock, flags); } } @@ -6039,16 +6363,20 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba) /* Get the bus address of SLI4 device Bar0, Bar1, and Bar2 and the * number of bytes required by each mapping. They are actually - * mapping to the PCI BAR regions 1, 2, and 4 by the SLI4 device. + * mapping to the PCI BAR regions 0 or 1, 2, and 4 by the SLI4 device. */ - phba->pci_bar0_map = pci_resource_start(pdev, LPFC_SLI4_BAR0); - bar0map_len = pci_resource_len(pdev, LPFC_SLI4_BAR0); - - phba->pci_bar1_map = pci_resource_start(pdev, LPFC_SLI4_BAR1); - bar1map_len = pci_resource_len(pdev, LPFC_SLI4_BAR1); + if (pci_resource_start(pdev, 0)) { + phba->pci_bar0_map = pci_resource_start(pdev, 0); + bar0map_len = pci_resource_len(pdev, 0); + } else { + phba->pci_bar0_map = pci_resource_start(pdev, 1); + bar0map_len = pci_resource_len(pdev, 1); + } + phba->pci_bar1_map = pci_resource_start(pdev, 2); + bar1map_len = pci_resource_len(pdev, 2); - phba->pci_bar2_map = pci_resource_start(pdev, LPFC_SLI4_BAR2); - bar2map_len = pci_resource_len(pdev, LPFC_SLI4_BAR2); + phba->pci_bar2_map = pci_resource_start(pdev, 4); + bar2map_len = pci_resource_len(pdev, 4); /* Map SLI4 PCI Config Space Register base to a kernel virtual addr */ phba->sli4_hba.conf_regs_memmap_p = @@ -6793,6 +7121,73 @@ lpfc_sli4_hba_unset(struct lpfc_hba *phba) phba->pport->work_port_events = 0; } + /** + * lpfc_pc_sli4_params_get - Get the SLI4_PARAMS port capabilities. + * @phba: Pointer to HBA context object. + * @mboxq: Pointer to the mailboxq memory for the mailbox command response. + * + * This function is called in the SLI4 code path to read the port's + * sli4 capabilities. + * + * This function may be be called from any context that can block-wait + * for the completion. The expectation is that this routine is called + * typically from probe_one or from the online routine. + **/ +int +lpfc_pc_sli4_params_get(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) +{ + int rc; + struct lpfc_mqe *mqe; + struct lpfc_pc_sli4_params *sli4_params; + uint32_t mbox_tmo; + + rc = 0; + mqe = &mboxq->u.mqe; + + /* Read the port's SLI4 Parameters port capabilities */ + lpfc_sli4_params(mboxq); + if (!phba->sli4_hba.intr_enable) + rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL); + else { + mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_PORT_CAPABILITIES); + rc = lpfc_sli_issue_mbox_wait(phba, mboxq, mbox_tmo); + } + + if (unlikely(rc)) + return 1; + + sli4_params = &phba->sli4_hba.pc_sli4_params; + sli4_params->if_type = bf_get(if_type, &mqe->un.sli4_params); + sli4_params->sli_rev = bf_get(sli_rev, &mqe->un.sli4_params); + sli4_params->sli_family = bf_get(sli_family, &mqe->un.sli4_params); + sli4_params->featurelevel_1 = bf_get(featurelevel_1, + &mqe->un.sli4_params); + sli4_params->featurelevel_2 = bf_get(featurelevel_2, + &mqe->un.sli4_params); + sli4_params->proto_types = mqe->un.sli4_params.word3; + sli4_params->sge_supp_len = mqe->un.sli4_params.sge_supp_len; + sli4_params->if_page_sz = bf_get(if_page_sz, &mqe->un.sli4_params); + sli4_params->rq_db_window = bf_get(rq_db_window, &mqe->un.sli4_params); + sli4_params->loopbk_scope = bf_get(loopbk_scope, &mqe->un.sli4_params); + sli4_params->eq_pages_max = bf_get(eq_pages, &mqe->un.sli4_params); + sli4_params->eqe_size = bf_get(eqe_size, &mqe->un.sli4_params); + sli4_params->cq_pages_max = bf_get(cq_pages, &mqe->un.sli4_params); + sli4_params->cqe_size = bf_get(cqe_size, &mqe->un.sli4_params); + sli4_params->mq_pages_max = bf_get(mq_pages, &mqe->un.sli4_params); + sli4_params->mqe_size = bf_get(mqe_size, &mqe->un.sli4_params); + sli4_params->mq_elem_cnt = bf_get(mq_elem_cnt, &mqe->un.sli4_params); + sli4_params->wq_pages_max = bf_get(wq_pages, &mqe->un.sli4_params); + sli4_params->wqe_size = bf_get(wqe_size, &mqe->un.sli4_params); + sli4_params->rq_pages_max = bf_get(rq_pages, &mqe->un.sli4_params); + sli4_params->rqe_size = bf_get(rqe_size, &mqe->un.sli4_params); + sli4_params->hdr_pages_max = bf_get(hdr_pages, &mqe->un.sli4_params); + sli4_params->hdr_size = bf_get(hdr_size, &mqe->un.sli4_params); + sli4_params->hdr_pp_align = bf_get(hdr_pp_align, &mqe->un.sli4_params); + sli4_params->sgl_pages_max = bf_get(sgl_pages, &mqe->un.sli4_params); + sli4_params->sgl_pp_align = bf_get(sgl_pp_align, &mqe->un.sli4_params); + return rc; +} + /** * lpfc_pci_probe_one_s3 - PCI probe func to reg SLI-3 device to PCI subsystem. * @pdev: pointer to PCI device @@ -7134,6 +7529,12 @@ lpfc_pci_resume_one_s3(struct pci_dev *pdev) pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); + /* + * As the new kernel behavior of pci_restore_state() API call clears + * device saved_state flag, need to save the restored state again. + */ + pci_save_state(pdev); + if (pdev->is_busmaster) pci_set_master(pdev); @@ -7317,6 +7718,13 @@ lpfc_io_slot_reset_s3(struct pci_dev *pdev) } pci_restore_state(pdev); + + /* + * As the new kernel behavior of pci_restore_state() API call clears + * device saved_state flag, need to save the restored state again. + */ + pci_save_state(pdev); + if (pdev->is_busmaster) pci_set_master(pdev); @@ -7726,6 +8134,13 @@ lpfc_pci_resume_one_s4(struct pci_dev *pdev) /* Restore device state from PCI config space */ pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); + + /* + * As the new kernel behavior of pci_restore_state() API call clears + * device saved_state flag, need to save the restored state again. + */ + pci_save_state(pdev); + if (pdev->is_busmaster) pci_set_master(pdev); @@ -7845,11 +8260,11 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid) int rc; struct lpfc_sli_intf intf; - if (pci_read_config_dword(pdev, LPFC_SLIREV_CONF_WORD, &intf.word0)) + if (pci_read_config_dword(pdev, LPFC_SLI_INTF, &intf.word0)) return -ENODEV; if ((bf_get(lpfc_sli_intf_valid, &intf) == LPFC_SLI_INTF_VALID) && - (bf_get(lpfc_sli_intf_rev, &intf) == LPFC_SLIREV_CONF_SLI4)) + (bf_get(lpfc_sli_intf_slirev, &intf) == LPFC_SLI_INTF_REV_SLI4)) rc = lpfc_pci_probe_one_s4(pdev, pid); else rc = lpfc_pci_probe_one_s3(pdev, pid); diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c index a9afd8b94b6..6c4dce1a30c 100644 --- a/drivers/scsi/lpfc/lpfc_mbox.c +++ b/drivers/scsi/lpfc/lpfc_mbox.c @@ -1707,7 +1707,8 @@ lpfc_sli4_config(struct lpfc_hba *phba, struct lpfcMboxq *mbox, alloc_len - sizeof(union lpfc_sli4_cfg_shdr); } /* The sub-header is in DMA memory, which needs endian converstion */ - lpfc_sli_pcimem_bcopy(cfg_shdr, cfg_shdr, + if (cfg_shdr) + lpfc_sli_pcimem_bcopy(cfg_shdr, cfg_shdr, sizeof(union lpfc_sli4_cfg_shdr)); return alloc_len; @@ -1747,6 +1748,65 @@ lpfc_sli4_mbox_opcode_get(struct lpfc_hba *phba, struct lpfcMboxq *mbox) } /** + * lpfc_sli4_mbx_read_fcf_record - Allocate and construct read fcf mbox cmd + * @phba: pointer to lpfc hba data structure. + * @fcf_index: index to fcf table. + * + * This routine routine allocates and constructs non-embedded mailbox command + * for reading a FCF table entry refered by @fcf_index. + * + * Return: pointer to the mailbox command constructed if successful, otherwise + * NULL. + **/ +int +lpfc_sli4_mbx_read_fcf_record(struct lpfc_hba *phba, + struct lpfcMboxq *mboxq, + uint16_t fcf_index) +{ + void *virt_addr; + dma_addr_t phys_addr; + uint8_t *bytep; + struct lpfc_mbx_sge sge; + uint32_t alloc_len, req_len; + struct lpfc_mbx_read_fcf_tbl *read_fcf; + + if (!mboxq) + return -ENOMEM; + + req_len = sizeof(struct fcf_record) + + sizeof(union lpfc_sli4_cfg_shdr) + 2 * sizeof(uint32_t); + + /* Set up READ_FCF SLI4_CONFIG mailbox-ioctl command */ + alloc_len = lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_FCOE, + LPFC_MBOX_OPCODE_FCOE_READ_FCF_TABLE, req_len, + LPFC_SLI4_MBX_NEMBED); + + if (alloc_len < req_len) { + lpfc_printf_log(phba, KERN_ERR, LOG_MBOX, + "0291 Allocated DMA memory size (x%x) is " + "less than the requested DMA memory " + "size (x%x)\n", alloc_len, req_len); + return -ENOMEM; + } + + /* Get the first SGE entry from the non-embedded DMA memory. This + * routine only uses a single SGE. + */ + lpfc_sli4_mbx_sge_get(mboxq, 0, &sge); + phys_addr = getPaddr(sge.pa_hi, sge.pa_lo); + virt_addr = mboxq->sge_array->addr[0]; + read_fcf = (struct lpfc_mbx_read_fcf_tbl *)virt_addr; + + /* Set up command fields */ + bf_set(lpfc_mbx_read_fcf_tbl_indx, &read_fcf->u.request, fcf_index); + /* Perform necessary endian conversion */ + bytep = virt_addr + sizeof(union lpfc_sli4_cfg_shdr); + lpfc_sli_pcimem_bcopy(bytep, bytep, sizeof(uint32_t)); + + return 0; +} + +/** * lpfc_request_features: Configure SLI4 REQUEST_FEATURES mailbox * @mboxq: pointer to lpfc mbox command. * @@ -1946,13 +2006,14 @@ lpfc_reg_fcfi(struct lpfc_hba *phba, struct lpfcMboxq *mbox) bf_set(lpfc_reg_fcfi_rq_id1, reg_fcfi, REG_FCF_INVALID_QID); bf_set(lpfc_reg_fcfi_rq_id2, reg_fcfi, REG_FCF_INVALID_QID); bf_set(lpfc_reg_fcfi_rq_id3, reg_fcfi, REG_FCF_INVALID_QID); - bf_set(lpfc_reg_fcfi_info_index, reg_fcfi, phba->fcf.fcf_indx); + bf_set(lpfc_reg_fcfi_info_index, reg_fcfi, + phba->fcf.current_rec.fcf_indx); /* reg_fcf addr mode is bit wise inverted value of fcf addr_mode */ - bf_set(lpfc_reg_fcfi_mam, reg_fcfi, - (~phba->fcf.addr_mode) & 0x3); - if (phba->fcf.fcf_flag & FCF_VALID_VLAN) { + bf_set(lpfc_reg_fcfi_mam, reg_fcfi, (~phba->fcf.addr_mode) & 0x3); + if (phba->fcf.current_rec.vlan_id != 0xFFFF) { bf_set(lpfc_reg_fcfi_vv, reg_fcfi, 1); - bf_set(lpfc_reg_fcfi_vlan_tag, reg_fcfi, phba->fcf.vlan_id); + bf_set(lpfc_reg_fcfi_vlan_tag, reg_fcfi, + phba->fcf.current_rec.vlan_id); } } @@ -1992,3 +2053,41 @@ lpfc_resume_rpi(struct lpfcMboxq *mbox, struct lpfc_nodelist *ndlp) bf_set(lpfc_resume_rpi_ii, resume_rpi, RESUME_INDEX_RPI); resume_rpi->event_tag = ndlp->phba->fc_eventTag; } + +/** + * lpfc_supported_pages - Initialize the PORT_CAPABILITIES supported pages + * mailbox command. + * @mbox: pointer to lpfc mbox command to initialize. + * + * The PORT_CAPABILITIES supported pages mailbox command is issued to + * retrieve the particular feature pages supported by the port. + **/ +void +lpfc_supported_pages(struct lpfcMboxq *mbox) +{ + struct lpfc_mbx_supp_pages *supp_pages; + + memset(mbox, 0, sizeof(*mbox)); + supp_pages = &mbox->u.mqe.un.supp_pages; + bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_PORT_CAPABILITIES); + bf_set(cpn, supp_pages, LPFC_SUPP_PAGES); +} + +/** + * lpfc_sli4_params - Initialize the PORT_CAPABILITIES SLI4 Params + * mailbox command. + * @mbox: pointer to lpfc mbox command to initialize. + * + * The PORT_CAPABILITIES SLI4 parameters mailbox command is issued to + * retrieve the particular SLI4 features supported by the port. + **/ +void +lpfc_sli4_params(struct lpfcMboxq *mbox) +{ + struct lpfc_mbx_sli4_params *sli4_params; + + memset(mbox, 0, sizeof(*mbox)); + sli4_params = &mbox->u.mqe.un.sli4_params; + bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_PORT_CAPABILITIES); + bf_set(cpn, sli4_params, LPFC_SLI4_PARAMETERS); +} diff --git a/drivers/scsi/lpfc/lpfc_nl.h b/drivers/scsi/lpfc/lpfc_nl.h index d655ed3eebe..f3cfbe2ce98 100644 --- a/drivers/scsi/lpfc/lpfc_nl.h +++ b/drivers/scsi/lpfc/lpfc_nl.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2008 Emulex. All rights reserved. * + * Copyright (C) 2010 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * * @@ -177,23 +177,3 @@ struct temp_event { uint32_t data; }; -/* bsg definitions */ -#define LPFC_BSG_VENDOR_SET_CT_EVENT 1 -#define LPFC_BSG_VENDOR_GET_CT_EVENT 2 - -struct set_ct_event { - uint32_t command; - uint32_t ev_req_id; - uint32_t ev_reg_id; -}; - -struct get_ct_event { - uint32_t command; - uint32_t ev_reg_id; - uint32_t ev_req_id; -}; - -struct get_ct_event_reply { - uint32_t immed_data; - uint32_t type; -}; diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c index 2ed6af19493..d20ae6b3b3c 100644 --- a/drivers/scsi/lpfc/lpfc_nportdisc.c +++ b/drivers/scsi/lpfc/lpfc_nportdisc.c @@ -62,7 +62,7 @@ lpfc_check_adisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, int lpfc_check_sparm(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, - struct serv_parm * sp, uint32_t class) + struct serv_parm *sp, uint32_t class, int flogi) { volatile struct serv_parm *hsp = &vport->fc_sparam; uint16_t hsp_value, ssp_value = 0; @@ -75,49 +75,56 @@ lpfc_check_sparm(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, * correcting the byte values. */ if (sp->cls1.classValid) { - hsp_value = (hsp->cls1.rcvDataSizeMsb << 8) | - hsp->cls1.rcvDataSizeLsb; - ssp_value = (sp->cls1.rcvDataSizeMsb << 8) | - sp->cls1.rcvDataSizeLsb; - if (!ssp_value) - goto bad_service_param; - if (ssp_value > hsp_value) { - sp->cls1.rcvDataSizeLsb = hsp->cls1.rcvDataSizeLsb; - sp->cls1.rcvDataSizeMsb = hsp->cls1.rcvDataSizeMsb; + if (!flogi) { + hsp_value = ((hsp->cls1.rcvDataSizeMsb << 8) | + hsp->cls1.rcvDataSizeLsb); + ssp_value = ((sp->cls1.rcvDataSizeMsb << 8) | + sp->cls1.rcvDataSizeLsb); + if (!ssp_value) + goto bad_service_param; + if (ssp_value > hsp_value) { + sp->cls1.rcvDataSizeLsb = + hsp->cls1.rcvDataSizeLsb; + sp->cls1.rcvDataSizeMsb = + hsp->cls1.rcvDataSizeMsb; + } } - } else if (class == CLASS1) { + } else if (class == CLASS1) goto bad_service_param; - } - if (sp->cls2.classValid) { - hsp_value = (hsp->cls2.rcvDataSizeMsb << 8) | - hsp->cls2.rcvDataSizeLsb; - ssp_value = (sp->cls2.rcvDataSizeMsb << 8) | - sp->cls2.rcvDataSizeLsb; - if (!ssp_value) - goto bad_service_param; - if (ssp_value > hsp_value) { - sp->cls2.rcvDataSizeLsb = hsp->cls2.rcvDataSizeLsb; - sp->cls2.rcvDataSizeMsb = hsp->cls2.rcvDataSizeMsb; + if (!flogi) { + hsp_value = ((hsp->cls2.rcvDataSizeMsb << 8) | + hsp->cls2.rcvDataSizeLsb); + ssp_value = ((sp->cls2.rcvDataSizeMsb << 8) | + sp->cls2.rcvDataSizeLsb); + if (!ssp_value) + goto bad_service_param; + if (ssp_value > hsp_value) { + sp->cls2.rcvDataSizeLsb = + hsp->cls2.rcvDataSizeLsb; + sp->cls2.rcvDataSizeMsb = + hsp->cls2.rcvDataSizeMsb; + } } - } else if (class == CLASS2) { + } else if (class == CLASS2) goto bad_service_param; - } - if (sp->cls3.classValid) { - hsp_value = (hsp->cls3.rcvDataSizeMsb << 8) | - hsp->cls3.rcvDataSizeLsb; - ssp_value = (sp->cls3.rcvDataSizeMsb << 8) | - sp->cls3.rcvDataSizeLsb; - if (!ssp_value) - goto bad_service_param; - if (ssp_value > hsp_value) { - sp->cls3.rcvDataSizeLsb = hsp->cls3.rcvDataSizeLsb; - sp->cls3.rcvDataSizeMsb = hsp->cls3.rcvDataSizeMsb; + if (!flogi) { + hsp_value = ((hsp->cls3.rcvDataSizeMsb << 8) | + hsp->cls3.rcvDataSizeLsb); + ssp_value = ((sp->cls3.rcvDataSizeMsb << 8) | + sp->cls3.rcvDataSizeLsb); + if (!ssp_value) + goto bad_service_param; + if (ssp_value > hsp_value) { + sp->cls3.rcvDataSizeLsb = + hsp->cls3.rcvDataSizeLsb; + sp->cls3.rcvDataSizeMsb = + hsp->cls3.rcvDataSizeMsb; + } } - } else if (class == CLASS3) { + } else if (class == CLASS3) goto bad_service_param; - } /* * Preserve the upper four bits of the MSB from the PLOGI response. @@ -247,7 +254,7 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, int rc; memset(&stat, 0, sizeof (struct ls_rjt)); - if (vport->port_state <= LPFC_FLOGI) { + if (vport->port_state <= LPFC_FDISC) { /* Before responding to PLOGI, check for pt2pt mode. * If we are pt2pt, with an outstanding FLOGI, abort * the FLOGI and resend it first. @@ -295,7 +302,7 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, NULL); return 0; } - if ((lpfc_check_sparm(vport, ndlp, sp, CLASS3) == 0)) { + if ((lpfc_check_sparm(vport, ndlp, sp, CLASS3, 0) == 0)) { /* Reject this request because invalid parameters */ stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; stat.un.b.lsRjtRsnCodeExp = LSEXP_SPARM_OPTIONS; @@ -831,7 +838,7 @@ lpfc_cmpl_plogi_plogi_issue(struct lpfc_vport *vport, "0142 PLOGI RSP: Invalid WWN.\n"); goto out; } - if (!lpfc_check_sparm(vport, ndlp, sp, CLASS3)) + if (!lpfc_check_sparm(vport, ndlp, sp, CLASS3, 0)) goto out; /* PLOGI chkparm OK */ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index a246410ce9d..7f21b47db79 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -626,6 +626,7 @@ lpfc_sli4_fcp_xri_aborted(struct lpfc_hba *phba, &phba->sli4_hba.lpfc_abts_scsi_buf_list, list) { if (psb->cur_iocbq.sli4_xritag == xri) { list_del(&psb->list); + psb->exch_busy = 0; psb->status = IOSTAT_SUCCESS; spin_unlock_irqrestore( &phba->sli4_hba.abts_scsi_buf_list_lock, @@ -688,11 +689,12 @@ lpfc_sli4_repost_scsi_sgl_list(struct lpfc_hba *phba) list); if (status) { /* Put this back on the abort scsi list */ - psb->status = IOSTAT_LOCAL_REJECT; - psb->result = IOERR_ABORT_REQUESTED; + psb->exch_busy = 1; rc++; - } else + } else { + psb->exch_busy = 0; psb->status = IOSTAT_SUCCESS; + } /* Put it back into the SCSI buffer list */ lpfc_release_scsi_buf_s4(phba, psb); } @@ -796,19 +798,17 @@ lpfc_new_scsi_buf_s4(struct lpfc_vport *vport, int num_to_alloc) */ sgl->addr_hi = cpu_to_le32(putPaddrHigh(pdma_phys_fcp_cmd)); sgl->addr_lo = cpu_to_le32(putPaddrLow(pdma_phys_fcp_cmd)); - bf_set(lpfc_sli4_sge_len, sgl, sizeof(struct fcp_cmnd)); bf_set(lpfc_sli4_sge_last, sgl, 0); sgl->word2 = cpu_to_le32(sgl->word2); - sgl->word3 = cpu_to_le32(sgl->word3); + sgl->sge_len = cpu_to_le32(sizeof(struct fcp_cmnd)); sgl++; /* Setup the physical region for the FCP RSP */ sgl->addr_hi = cpu_to_le32(putPaddrHigh(pdma_phys_fcp_rsp)); sgl->addr_lo = cpu_to_le32(putPaddrLow(pdma_phys_fcp_rsp)); - bf_set(lpfc_sli4_sge_len, sgl, sizeof(struct fcp_rsp)); bf_set(lpfc_sli4_sge_last, sgl, 1); sgl->word2 = cpu_to_le32(sgl->word2); - sgl->word3 = cpu_to_le32(sgl->word3); + sgl->sge_len = cpu_to_le32(sizeof(struct fcp_rsp)); /* * Since the IOCB for the FCP I/O is built into this @@ -839,11 +839,12 @@ lpfc_new_scsi_buf_s4(struct lpfc_vport *vport, int num_to_alloc) psb->cur_iocbq.sli4_xritag); if (status) { /* Put this back on the abort scsi list */ - psb->status = IOSTAT_LOCAL_REJECT; - psb->result = IOERR_ABORT_REQUESTED; + psb->exch_busy = 1; rc++; - } else + } else { + psb->exch_busy = 0; psb->status = IOSTAT_SUCCESS; + } /* Put it back into the SCSI buffer list */ lpfc_release_scsi_buf_s4(phba, psb); break; @@ -857,11 +858,12 @@ lpfc_new_scsi_buf_s4(struct lpfc_vport *vport, int num_to_alloc) list); if (status) { /* Put this back on the abort scsi list */ - psb->status = IOSTAT_LOCAL_REJECT; - psb->result = IOERR_ABORT_REQUESTED; + psb->exch_busy = 1; rc++; - } else + } else { + psb->exch_busy = 0; psb->status = IOSTAT_SUCCESS; + } /* Put it back into the SCSI buffer list */ lpfc_release_scsi_buf_s4(phba, psb); } @@ -951,8 +953,7 @@ lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb) { unsigned long iflag = 0; - if (psb->status == IOSTAT_LOCAL_REJECT - && psb->result == IOERR_ABORT_REQUESTED) { + if (psb->exch_busy) { spin_lock_irqsave(&phba->sli4_hba.abts_scsi_buf_list_lock, iflag); psb->pCmd = NULL; @@ -1869,7 +1870,6 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) scsi_for_each_sg(scsi_cmnd, sgel, nseg, num_bde) { physaddr = sg_dma_address(sgel); dma_len = sg_dma_len(sgel); - bf_set(lpfc_sli4_sge_len, sgl, sg_dma_len(sgel)); sgl->addr_lo = cpu_to_le32(putPaddrLow(physaddr)); sgl->addr_hi = cpu_to_le32(putPaddrHigh(physaddr)); if ((num_bde + 1) == nseg) @@ -1878,7 +1878,7 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) bf_set(lpfc_sli4_sge_last, sgl, 0); bf_set(lpfc_sli4_sge_offset, sgl, dma_offset); sgl->word2 = cpu_to_le32(sgl->word2); - sgl->word3 = cpu_to_le32(sgl->word3); + sgl->sge_len = cpu_to_le32(dma_len); dma_offset += dma_len; sgl++; } @@ -2221,6 +2221,9 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, lpfc_cmd->result = pIocbOut->iocb.un.ulpWord[4]; lpfc_cmd->status = pIocbOut->iocb.ulpStatus; + /* pick up SLI4 exhange busy status from HBA */ + lpfc_cmd->exch_busy = pIocbOut->iocb_flag & LPFC_EXCHANGE_BUSY; + if (pnode && NLP_CHK_NODE_ACT(pnode)) atomic_dec(&pnode->cmd_pending); @@ -2637,6 +2640,7 @@ lpfc_scsi_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp) } phba->lpfc_get_scsi_buf = lpfc_get_scsi_buf; phba->lpfc_rampdown_queue_depth = lpfc_rampdown_queue_depth; + phba->lpfc_scsi_cmd_iocb_cmpl = lpfc_scsi_cmd_iocb_cmpl; return 0; } @@ -2695,6 +2699,13 @@ lpfc_info(struct Scsi_Host *host) " port %s", phba->Port); } + len = strlen(lpfcinfobuf); + if (phba->sli4_hba.link_state.logical_speed) { + snprintf(lpfcinfobuf + len, + 384-len, + " Logical Link Speed: %d Mbps", + phba->sli4_hba.link_state.logical_speed * 10); + } } return lpfcinfobuf; } @@ -2990,6 +3001,7 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) /* ABTS WQE must go to the same WQ as the WQE to be aborted */ abtsiocb->fcp_wqidx = iocb->fcp_wqidx; + abtsiocb->iocb_flag |= LPFC_USE_FCPWQIDX; if (lpfc_is_link_up(phba)) icmd->ulpCommand = CMD_ABORT_XRI_CN; diff --git a/drivers/scsi/lpfc/lpfc_scsi.h b/drivers/scsi/lpfc/lpfc_scsi.h index 65dfc8bd5b4..5932273870a 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.h +++ b/drivers/scsi/lpfc/lpfc_scsi.h @@ -118,6 +118,7 @@ struct lpfc_scsi_buf { uint32_t timeout; + uint16_t exch_busy; /* SLI4 hba reported XB on complete WCQE */ uint16_t status; /* From IOCB Word 7- ulpStatus */ uint32_t result; /* From IOCB Word 4. */ diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 589549b2bf0..35e3b96d4e0 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -580,10 +580,7 @@ __lpfc_sli_release_iocbq_s4(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq) else sglq = __lpfc_clear_active_sglq(phba, iocbq->sli4_xritag); if (sglq) { - if (iocbq->iocb_flag & LPFC_DRIVER_ABORTED - && ((iocbq->iocb.ulpStatus == IOSTAT_LOCAL_REJECT) - && (iocbq->iocb.un.ulpWord[4] - == IOERR_ABORT_REQUESTED))) { + if (iocbq->iocb_flag & LPFC_EXCHANGE_BUSY) { spin_lock_irqsave(&phba->sli4_hba.abts_sgl_list_lock, iflag); list_add(&sglq->list, @@ -764,10 +761,6 @@ lpfc_sli_iocb_cmd_type(uint8_t iocb_cmnd) case DSSCMD_IWRITE64_CX: case DSSCMD_IREAD64_CR: case DSSCMD_IREAD64_CX: - case DSSCMD_INVALIDATE_DEK: - case DSSCMD_SET_KEK: - case DSSCMD_GET_KEK_ID: - case DSSCMD_GEN_XFER: type = LPFC_SOL_IOCB; break; case CMD_ABORT_XRI_CN: @@ -1717,6 +1710,7 @@ lpfc_sli_def_mbox_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) struct lpfc_dmabuf *mp; uint16_t rpi, vpi; int rc; + struct lpfc_vport *vport = pmb->vport; mp = (struct lpfc_dmabuf *) (pmb->context1); @@ -1745,6 +1739,18 @@ lpfc_sli_def_mbox_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) return; } + /* Unreg VPI, if the REG_VPI succeed after VLink failure */ + if ((pmb->u.mb.mbxCommand == MBX_REG_VPI) && + !(phba->pport->load_flag & FC_UNLOADING) && + !pmb->u.mb.mbxStatus) { + lpfc_unreg_vpi(phba, pmb->u.mb.un.varRegVpi.vpi, pmb); + pmb->vport = vport; + pmb->mbox_cmpl = lpfc_sli_def_mbox_cmpl; + rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT); + if (rc != MBX_NOT_FINISHED) + return; + } + if (bf_get(lpfc_mqe_command, &pmb->u.mqe) == MBX_SLI4_CONFIG) lpfc_sli4_mbox_cmd_free(phba, pmb); else @@ -2228,9 +2234,15 @@ lpfc_sli_process_sol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, * All other are passed to the completion callback. */ if (pring->ringno == LPFC_ELS_RING) { - if (cmdiocbp->iocb_flag & LPFC_DRIVER_ABORTED) { + if ((phba->sli_rev < LPFC_SLI_REV4) && + (cmdiocbp->iocb_flag & + LPFC_DRIVER_ABORTED)) { + spin_lock_irqsave(&phba->hbalock, + iflag); cmdiocbp->iocb_flag &= ~LPFC_DRIVER_ABORTED; + spin_unlock_irqrestore(&phba->hbalock, + iflag); saveq->iocb.ulpStatus = IOSTAT_LOCAL_REJECT; saveq->iocb.un.ulpWord[4] = @@ -2240,7 +2252,47 @@ lpfc_sli_process_sol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, * of DMAing payload, so don't free data * buffer till after a hbeat. */ + spin_lock_irqsave(&phba->hbalock, + iflag); saveq->iocb_flag |= LPFC_DELAY_MEM_FREE; + spin_unlock_irqrestore(&phba->hbalock, + iflag); + } + if ((phba->sli_rev == LPFC_SLI_REV4) && + (saveq->iocb_flag & LPFC_EXCHANGE_BUSY)) { + /* Set cmdiocb flag for the exchange + * busy so sgl (xri) will not be + * released until the abort xri is + * received from hba, clear the + * LPFC_DRIVER_ABORTED bit in case + * it was driver initiated abort. + */ + spin_lock_irqsave(&phba->hbalock, + iflag); + cmdiocbp->iocb_flag &= + ~LPFC_DRIVER_ABORTED; + cmdiocbp->iocb_flag |= + LPFC_EXCHANGE_BUSY; + spin_unlock_irqrestore(&phba->hbalock, + iflag); + cmdiocbp->iocb.ulpStatus = + IOSTAT_LOCAL_REJECT; + cmdiocbp->iocb.un.ulpWord[4] = + IOERR_ABORT_REQUESTED; + /* + * For SLI4, irsiocb contains NO_XRI + * in sli_xritag, it shall not affect + * releasing sgl (xri) process. + */ + saveq->iocb.ulpStatus = + IOSTAT_LOCAL_REJECT; + saveq->iocb.un.ulpWord[4] = + IOERR_SLI_ABORTED; + spin_lock_irqsave(&phba->hbalock, + iflag); + saveq->iocb_flag |= LPFC_DELAY_MEM_FREE; + spin_unlock_irqrestore(&phba->hbalock, + iflag); } } (cmdiocbp->iocb_cmpl) (phba, cmdiocbp, saveq); @@ -5687,19 +5739,19 @@ lpfc_sli4_bpl2sgl(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq, for (i = 0; i < numBdes; i++) { /* Should already be byte swapped. */ - sgl->addr_hi = bpl->addrHigh; - sgl->addr_lo = bpl->addrLow; - /* swap the size field back to the cpu so we - * can assign it to the sgl. - */ - bde.tus.w = le32_to_cpu(bpl->tus.w); - bf_set(lpfc_sli4_sge_len, sgl, bde.tus.f.bdeSize); + sgl->addr_hi = bpl->addrHigh; + sgl->addr_lo = bpl->addrLow; + if ((i+1) == numBdes) bf_set(lpfc_sli4_sge_last, sgl, 1); else bf_set(lpfc_sli4_sge_last, sgl, 0); sgl->word2 = cpu_to_le32(sgl->word2); - sgl->word3 = cpu_to_le32(sgl->word3); + /* swap the size field back to the cpu so we + * can assign it to the sgl. + */ + bde.tus.w = le32_to_cpu(bpl->tus.w); + sgl->sge_len = cpu_to_le32(bde.tus.f.bdeSize); bpl++; sgl++; } @@ -5712,11 +5764,10 @@ lpfc_sli4_bpl2sgl(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq, cpu_to_le32(icmd->un.genreq64.bdl.addrHigh); sgl->addr_lo = cpu_to_le32(icmd->un.genreq64.bdl.addrLow); - bf_set(lpfc_sli4_sge_len, sgl, - icmd->un.genreq64.bdl.bdeSize); bf_set(lpfc_sli4_sge_last, sgl, 1); sgl->word2 = cpu_to_le32(sgl->word2); - sgl->word3 = cpu_to_le32(sgl->word3); + sgl->sge_len = + cpu_to_le32(icmd->un.genreq64.bdl.bdeSize); } return sglq->sli4_xritag; } @@ -5987,12 +6038,10 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, else bf_set(abort_cmd_ia, &wqe->abort_cmd, 0); bf_set(abort_cmd_criteria, &wqe->abort_cmd, T_XRI_TAG); - abort_tag = iocbq->iocb.un.acxri.abortIoTag; wqe->words[5] = 0; bf_set(lpfc_wqe_gen_ct, &wqe->generic, ((iocbq->iocb.ulpCt_h << 1) | iocbq->iocb.ulpCt_l)); abort_tag = iocbq->iocb.un.acxri.abortIoTag; - wqe->generic.abort_tag = abort_tag; /* * The abort handler will send us CMD_ABORT_XRI_CN or * CMD_CLOSE_XRI_CN and the fw only accepts CMD_ABORT_XRI_CX @@ -6121,15 +6170,15 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number, if (lpfc_sli4_iocb2wqe(phba, piocb, &wqe)) return IOCB_ERROR; - if (piocb->iocb_flag & LPFC_IO_FCP) { + if ((piocb->iocb_flag & LPFC_IO_FCP) || + (piocb->iocb_flag & LPFC_USE_FCPWQIDX)) { /* * For FCP command IOCB, get a new WQ index to distribute * WQE across the WQsr. On the other hand, for abort IOCB, * it carries the same WQ index to the original command * IOCB. */ - if ((piocb->iocb.ulpCommand != CMD_ABORT_XRI_CN) && - (piocb->iocb.ulpCommand != CMD_CLOSE_XRI_CN)) + if (piocb->iocb_flag & LPFC_IO_FCP) piocb->fcp_wqidx = lpfc_sli4_scmd_to_wqidx_distr(phba); if (lpfc_sli4_wq_put(phba->sli4_hba.fcp_wq[piocb->fcp_wqidx], &wqe)) @@ -7004,7 +7053,14 @@ lpfc_sli_abort_els_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, abort_iocb->iocb.ulpContext != abort_context || (abort_iocb->iocb_flag & LPFC_DRIVER_ABORTED) == 0) spin_unlock_irq(&phba->hbalock); - else { + else if (phba->sli_rev < LPFC_SLI_REV4) { + /* + * leave the SLI4 aborted command on the txcmplq + * list and the command complete WCQE's XB bit + * will tell whether the SGL (XRI) can be released + * immediately or to the aborted SGL list for the + * following abort XRI from the HBA. + */ list_del_init(&abort_iocb->list); pring->txcmplq_cnt--; spin_unlock_irq(&phba->hbalock); @@ -7013,11 +7069,13 @@ lpfc_sli_abort_els_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, * payload, so don't free data buffer till after * a hbeat. */ + spin_lock_irq(&phba->hbalock); abort_iocb->iocb_flag |= LPFC_DELAY_MEM_FREE; - abort_iocb->iocb_flag &= ~LPFC_DRIVER_ABORTED; + spin_unlock_irq(&phba->hbalock); + abort_iocb->iocb.ulpStatus = IOSTAT_LOCAL_REJECT; - abort_iocb->iocb.un.ulpWord[4] = IOERR_SLI_ABORTED; + abort_iocb->iocb.un.ulpWord[4] = IOERR_ABORT_REQUESTED; (abort_iocb->iocb_cmpl)(phba, abort_iocb, abort_iocb); } } @@ -7106,7 +7164,7 @@ lpfc_sli_issue_abort_iotag(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, return 0; /* This signals the response to set the correct status - * before calling the completion handler. + * before calling the completion handler */ cmdiocb->iocb_flag |= LPFC_DRIVER_ABORTED; @@ -7124,6 +7182,8 @@ lpfc_sli_issue_abort_iotag(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, /* ABTS WQE must go to the same WQ as the WQE to be aborted */ abtsiocbp->fcp_wqidx = cmdiocb->fcp_wqidx; + if (cmdiocb->iocb_flag & LPFC_IO_FCP) + abtsiocbp->iocb_flag |= LPFC_USE_FCPWQIDX; if (phba->link_state >= LPFC_LINK_UP) iabt->ulpCommand = CMD_ABORT_XRI_CN; @@ -7330,6 +7390,8 @@ lpfc_sli_abort_iocb(struct lpfc_vport *vport, struct lpfc_sli_ring *pring, /* ABTS WQE must go to the same WQ as the WQE to be aborted */ abtsiocb->fcp_wqidx = iocbq->fcp_wqidx; + if (iocbq->iocb_flag & LPFC_IO_FCP) + abtsiocb->iocb_flag |= LPFC_USE_FCPWQIDX; if (lpfc_is_link_up(phba)) abtsiocb->iocb.ulpCommand = CMD_ABORT_XRI_CN; @@ -8359,11 +8421,24 @@ void lpfc_sli4_els_xri_abort_event_proc(struct lpfc_hba *phba) } } +/** + * lpfc_sli4_iocb_param_transfer - Transfer pIocbOut and cmpl status to pIocbIn + * @phba: pointer to lpfc hba data structure + * @pIocbIn: pointer to the rspiocbq + * @pIocbOut: pointer to the cmdiocbq + * @wcqe: pointer to the complete wcqe + * + * This routine transfers the fields of a command iocbq to a response iocbq + * by copying all the IOCB fields from command iocbq and transferring the + * completion status information from the complete wcqe. + **/ static void -lpfc_sli4_iocb_param_transfer(struct lpfc_iocbq *pIocbIn, +lpfc_sli4_iocb_param_transfer(struct lpfc_hba *phba, + struct lpfc_iocbq *pIocbIn, struct lpfc_iocbq *pIocbOut, struct lpfc_wcqe_complete *wcqe) { + unsigned long iflags; size_t offset = offsetof(struct lpfc_iocbq, iocb); memcpy((char *)pIocbIn + offset, (char *)pIocbOut + offset, @@ -8377,8 +8452,17 @@ lpfc_sli4_iocb_param_transfer(struct lpfc_iocbq *pIocbIn, wcqe->total_data_placed; else pIocbIn->iocb.un.ulpWord[4] = wcqe->parameter; - else + else { pIocbIn->iocb.un.ulpWord[4] = wcqe->parameter; + pIocbIn->iocb.un.genreq64.bdl.bdeSize = wcqe->total_data_placed; + } + + /* Pick up HBA exchange busy condition */ + if (bf_get(lpfc_wcqe_c_xb, wcqe)) { + spin_lock_irqsave(&phba->hbalock, iflags); + pIocbIn->iocb_flag |= LPFC_EXCHANGE_BUSY; + spin_unlock_irqrestore(&phba->hbalock, iflags); + } } /** @@ -8419,7 +8503,7 @@ lpfc_sli4_els_wcqe_to_rspiocbq(struct lpfc_hba *phba, } /* Fake the irspiocbq and copy necessary response information */ - lpfc_sli4_iocb_param_transfer(irspiocbq, cmdiocbq, wcqe); + lpfc_sli4_iocb_param_transfer(phba, irspiocbq, cmdiocbq, wcqe); return irspiocbq; } @@ -8849,8 +8933,7 @@ lpfc_sli4_sp_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe) int ecount = 0; uint16_t cqid; - if (bf_get(lpfc_eqe_major_code, eqe) != 0 || - bf_get(lpfc_eqe_minor_code, eqe) != 0) { + if (bf_get(lpfc_eqe_major_code, eqe) != 0) { lpfc_printf_log(phba, KERN_ERR, LOG_SLI, "0359 Not a valid slow-path completion " "event: majorcode=x%x, minorcode=x%x\n", @@ -8976,7 +9059,7 @@ lpfc_sli4_fp_handle_fcp_wcqe(struct lpfc_hba *phba, } /* Fake the irspiocb and copy necessary response information */ - lpfc_sli4_iocb_param_transfer(&irspiocbq, cmdiocbq, wcqe); + lpfc_sli4_iocb_param_transfer(phba, &irspiocbq, cmdiocbq, wcqe); /* Pass the cmd_iocb and the rsp state to the upper layer */ (cmdiocbq->iocb_cmpl)(phba, cmdiocbq, &irspiocbq); @@ -9082,8 +9165,7 @@ lpfc_sli4_fp_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe, uint16_t cqid; int ecount = 0; - if (unlikely(bf_get(lpfc_eqe_major_code, eqe) != 0) || - unlikely(bf_get(lpfc_eqe_minor_code, eqe) != 0)) { + if (unlikely(bf_get(lpfc_eqe_major_code, eqe) != 0)) { lpfc_printf_log(phba, KERN_ERR, LOG_SLI, "0366 Not a valid fast-path completion " "event: majorcode=x%x, minorcode=x%x\n", @@ -11871,12 +11953,6 @@ lpfc_sli4_read_fcf_record(struct lpfc_hba *phba, uint16_t fcf_index) { int rc = 0, error; LPFC_MBOXQ_t *mboxq; - void *virt_addr; - dma_addr_t phys_addr; - uint8_t *bytep; - struct lpfc_mbx_sge sge; - uint32_t alloc_len, req_len; - struct lpfc_mbx_read_fcf_tbl *read_fcf; phba->fcoe_eventtag_at_fcf_scan = phba->fcoe_eventtag; mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); @@ -11887,43 +11963,19 @@ lpfc_sli4_read_fcf_record(struct lpfc_hba *phba, uint16_t fcf_index) error = -ENOMEM; goto fail_fcfscan; } - - req_len = sizeof(struct fcf_record) + - sizeof(union lpfc_sli4_cfg_shdr) + 2 * sizeof(uint32_t); - - /* Set up READ_FCF SLI4_CONFIG mailbox-ioctl command */ - alloc_len = lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_FCOE, - LPFC_MBOX_OPCODE_FCOE_READ_FCF_TABLE, req_len, - LPFC_SLI4_MBX_NEMBED); - - if (alloc_len < req_len) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "0291 Allocated DMA memory size (x%x) is " - "less than the requested DMA memory " - "size (x%x)\n", alloc_len, req_len); - error = -ENOMEM; + /* Construct the read FCF record mailbox command */ + rc = lpfc_sli4_mbx_read_fcf_record(phba, mboxq, fcf_index); + if (rc) { + error = -EINVAL; goto fail_fcfscan; } - - /* Get the first SGE entry from the non-embedded DMA memory. This - * routine only uses a single SGE. - */ - lpfc_sli4_mbx_sge_get(mboxq, 0, &sge); - phys_addr = getPaddr(sge.pa_hi, sge.pa_lo); - virt_addr = mboxq->sge_array->addr[0]; - read_fcf = (struct lpfc_mbx_read_fcf_tbl *)virt_addr; - - /* Set up command fields */ - bf_set(lpfc_mbx_read_fcf_tbl_indx, &read_fcf->u.request, fcf_index); - /* Perform necessary endian conversion */ - bytep = virt_addr + sizeof(union lpfc_sli4_cfg_shdr); - lpfc_sli_pcimem_bcopy(bytep, bytep, sizeof(uint32_t)); + /* Issue the mailbox command asynchronously */ mboxq->vport = phba->pport; mboxq->mbox_cmpl = lpfc_mbx_cmpl_read_fcf_record; rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_NOWAIT); - if (rc == MBX_NOT_FINISHED) { + if (rc == MBX_NOT_FINISHED) error = -EIO; - } else { + else { spin_lock_irq(&phba->hbalock); phba->hba_flag |= FCF_DISC_INPROGRESS; spin_unlock_irq(&phba->hbalock); @@ -11942,6 +11994,90 @@ fail_fcfscan: } /** + * lpfc_mbx_cmpl_redisc_fcf_table - completion routine for rediscover FCF table + * @phba: pointer to lpfc hba data structure. + * + * This routine is the completion routine for the rediscover FCF table mailbox + * command. If the mailbox command returned failure, it will try to stop the + * FCF rediscover wait timer. + **/ +void +lpfc_mbx_cmpl_redisc_fcf_table(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox) +{ + struct lpfc_mbx_redisc_fcf_tbl *redisc_fcf; + uint32_t shdr_status, shdr_add_status; + + redisc_fcf = &mbox->u.mqe.un.redisc_fcf_tbl; + + shdr_status = bf_get(lpfc_mbox_hdr_status, + &redisc_fcf->header.cfg_shdr.response); + shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, + &redisc_fcf->header.cfg_shdr.response); + if (shdr_status || shdr_add_status) { + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "2746 Requesting for FCF rediscovery failed " + "status x%x add_status x%x\n", + shdr_status, shdr_add_status); + /* + * Request failed, last resort to re-try current + * registered FCF entry + */ + lpfc_retry_pport_discovery(phba); + } else + /* + * Start FCF rediscovery wait timer for pending FCF + * before rescan FCF record table. + */ + lpfc_fcf_redisc_wait_start_timer(phba); + + mempool_free(mbox, phba->mbox_mem_pool); +} + +/** + * lpfc_sli4_redisc_all_fcf - Request to rediscover entire FCF table by port. + * @phba: pointer to lpfc hba data structure. + * + * This routine is invoked to request for rediscovery of the entire FCF table + * by the port. + **/ +int +lpfc_sli4_redisc_fcf_table(struct lpfc_hba *phba) +{ + LPFC_MBOXQ_t *mbox; + struct lpfc_mbx_redisc_fcf_tbl *redisc_fcf; + int rc, length; + + mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); + if (!mbox) { + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "2745 Failed to allocate mbox for " + "requesting FCF rediscover.\n"); + return -ENOMEM; + } + + length = (sizeof(struct lpfc_mbx_redisc_fcf_tbl) - + sizeof(struct lpfc_sli4_cfg_mhdr)); + lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE, + LPFC_MBOX_OPCODE_FCOE_REDISCOVER_FCF, + length, LPFC_SLI4_MBX_EMBED); + + redisc_fcf = &mbox->u.mqe.un.redisc_fcf_tbl; + /* Set count to 0 for invalidating the entire FCF database */ + bf_set(lpfc_mbx_redisc_fcf_count, redisc_fcf, 0); + + /* Issue the mailbox command asynchronously */ + mbox->vport = phba->pport; + mbox->mbox_cmpl = lpfc_mbx_cmpl_redisc_fcf_table; + rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT); + + if (rc == MBX_NOT_FINISHED) { + mempool_free(mbox, phba->mbox_mem_pool); + return -EIO; + } + return 0; +} + +/** * lpfc_sli_read_link_ste - Read region 23 to decide if link is disabled. * @phba: pointer to lpfc hba data structure. * @@ -12069,3 +12205,48 @@ out: kfree(rgn23_data); return; } + +/** + * lpfc_cleanup_pending_mbox - Free up vport discovery mailbox commands. + * @vport: pointer to vport data structure. + * + * This function iterate through the mailboxq and clean up all REG_LOGIN + * and REG_VPI mailbox commands associated with the vport. This function + * is called when driver want to restart discovery of the vport due to + * a Clear Virtual Link event. + **/ +void +lpfc_cleanup_pending_mbox(struct lpfc_vport *vport) +{ + struct lpfc_hba *phba = vport->phba; + LPFC_MBOXQ_t *mb, *nextmb; + struct lpfc_dmabuf *mp; + + spin_lock_irq(&phba->hbalock); + list_for_each_entry_safe(mb, nextmb, &phba->sli.mboxq, list) { + if (mb->vport != vport) + continue; + + if ((mb->u.mb.mbxCommand != MBX_REG_LOGIN64) && + (mb->u.mb.mbxCommand != MBX_REG_VPI)) + continue; + + if (mb->u.mb.mbxCommand == MBX_REG_LOGIN64) { + mp = (struct lpfc_dmabuf *) (mb->context1); + if (mp) { + __lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + } + } + list_del(&mb->list); + mempool_free(mb, phba->mbox_mem_pool); + } + mb = phba->sli.mbox_active; + if (mb && (mb->vport == vport)) { + if ((mb->u.mb.mbxCommand == MBX_REG_LOGIN64) || + (mb->u.mb.mbxCommand == MBX_REG_VPI)) + mb->mbox_cmpl = lpfc_sli_def_mbox_cmpl; + } + spin_unlock_irq(&phba->hbalock); +} + diff --git a/drivers/scsi/lpfc/lpfc_sli.h b/drivers/scsi/lpfc/lpfc_sli.h index ba38de3c28f..dfcf5437d1f 100644 --- a/drivers/scsi/lpfc/lpfc_sli.h +++ b/drivers/scsi/lpfc/lpfc_sli.h @@ -53,17 +53,19 @@ struct lpfc_iocbq { IOCB_t iocb; /* IOCB cmd */ uint8_t retry; /* retry counter for IOCB cmd - if needed */ - uint8_t iocb_flag; + uint16_t iocb_flag; #define LPFC_IO_LIBDFC 1 /* libdfc iocb */ #define LPFC_IO_WAKE 2 /* High Priority Queue signal flag */ #define LPFC_IO_FCP 4 /* FCP command -- iocbq in scsi_buf */ #define LPFC_DRIVER_ABORTED 8 /* driver aborted this request */ #define LPFC_IO_FABRIC 0x10 /* Iocb send using fabric scheduler */ #define LPFC_DELAY_MEM_FREE 0x20 /* Defer free'ing of FC data */ -#define LPFC_FIP_ELS_ID_MASK 0xc0 /* ELS_ID range 0-3 */ -#define LPFC_FIP_ELS_ID_SHIFT 6 +#define LPFC_EXCHANGE_BUSY 0x40 /* SLI4 hba reported XB in response */ +#define LPFC_USE_FCPWQIDX 0x80 /* Submit to specified FCPWQ index */ + +#define LPFC_FIP_ELS_ID_MASK 0xc000 /* ELS_ID range 0-3, non-shifted mask */ +#define LPFC_FIP_ELS_ID_SHIFT 14 - uint8_t abort_count; uint8_t rsvd2; uint32_t drvrTimeout; /* driver timeout in seconds */ uint32_t fcp_wqidx; /* index to FCP work queue */ diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h index 44e5f574236..86308836600 100644 --- a/drivers/scsi/lpfc/lpfc_sli4.h +++ b/drivers/scsi/lpfc/lpfc_sli4.h @@ -22,6 +22,10 @@ #define LPFC_RELEASE_NOTIFICATION_INTERVAL 32 #define LPFC_GET_QE_REL_INT 32 #define LPFC_RPI_LOW_WATER_MARK 10 + +/* Amount of time in seconds for waiting FCF rediscovery to complete */ +#define LPFC_FCF_REDISCOVER_WAIT_TMO 2000 /* msec */ + /* Number of SGL entries can be posted in a 4KB nonembedded mbox command */ #define LPFC_NEMBED_MBOX_SGL_CNT 254 @@ -126,24 +130,36 @@ struct lpfc_sli4_link { uint8_t status; uint8_t physical; uint8_t fault; + uint16_t logical_speed; }; -struct lpfc_fcf { - uint8_t fabric_name[8]; - uint8_t switch_name[8]; +struct lpfc_fcf_rec { + uint8_t fabric_name[8]; + uint8_t switch_name[8]; uint8_t mac_addr[6]; uint16_t fcf_indx; + uint32_t priority; + uint16_t vlan_id; + uint32_t addr_mode; + uint32_t flag; +#define BOOT_ENABLE 0x01 +#define RECORD_VALID 0x02 +}; + +struct lpfc_fcf { uint16_t fcfi; uint32_t fcf_flag; #define FCF_AVAILABLE 0x01 /* FCF available for discovery */ #define FCF_REGISTERED 0x02 /* FCF registered with FW */ -#define FCF_DISCOVERED 0x04 /* FCF discovery started */ -#define FCF_BOOT_ENABLE 0x08 /* Boot bios use this FCF */ -#define FCF_IN_USE 0x10 /* Atleast one discovery completed */ -#define FCF_VALID_VLAN 0x20 /* Use the vlan id specified */ - uint32_t priority; +#define FCF_SCAN_DONE 0x04 /* FCF table scan done */ +#define FCF_IN_USE 0x08 /* Atleast one discovery completed */ +#define FCF_REDISC_PEND 0x10 /* FCF rediscovery pending */ +#define FCF_REDISC_EVT 0x20 /* FCF rediscovery event to worker thread */ +#define FCF_REDISC_FOV 0x40 /* Post FCF rediscovery fast failover */ uint32_t addr_mode; - uint16_t vlan_id; + struct lpfc_fcf_rec current_rec; + struct lpfc_fcf_rec failover_rec; + struct timer_list redisc_wait; }; #define LPFC_REGION23_SIGNATURE "RG23" @@ -248,7 +264,10 @@ struct lpfc_bmbx { #define SLI4_CT_VFI 2 #define SLI4_CT_FCFI 3 -#define LPFC_SLI4_MAX_SEGMENT_SIZE 0x10000 +#define LPFC_SLI4_FL1_MAX_SEGMENT_SIZE 0x10000 +#define LPFC_SLI4_FL1_MAX_BUF_SIZE 0X2000 +#define LPFC_SLI4_MIN_BUF_SIZE 0x400 +#define LPFC_SLI4_MAX_BUF_SIZE 0x20000 /* * SLI4 specific data structures @@ -282,6 +301,42 @@ struct lpfc_fcp_eq_hdl { struct lpfc_hba *phba; }; +/* Port Capabilities for SLI4 Parameters */ +struct lpfc_pc_sli4_params { + uint32_t supported; + uint32_t if_type; + uint32_t sli_rev; + uint32_t sli_family; + uint32_t featurelevel_1; + uint32_t featurelevel_2; + uint32_t proto_types; +#define LPFC_SLI4_PROTO_FCOE 0x0000001 +#define LPFC_SLI4_PROTO_FC 0x0000002 +#define LPFC_SLI4_PROTO_NIC 0x0000004 +#define LPFC_SLI4_PROTO_ISCSI 0x0000008 +#define LPFC_SLI4_PROTO_RDMA 0x0000010 + uint32_t sge_supp_len; + uint32_t if_page_sz; + uint32_t rq_db_window; + uint32_t loopbk_scope; + uint32_t eq_pages_max; + uint32_t eqe_size; + uint32_t cq_pages_max; + uint32_t cqe_size; + uint32_t mq_pages_max; + uint32_t mqe_size; + uint32_t mq_elem_cnt; + uint32_t wq_pages_max; + uint32_t wqe_size; + uint32_t rq_pages_max; + uint32_t rqe_size; + uint32_t hdr_pages_max; + uint32_t hdr_size; + uint32_t hdr_pp_align; + uint32_t sgl_pages_max; + uint32_t sgl_pp_align; +}; + /* SLI4 HBA data structure entries */ struct lpfc_sli4_hba { void __iomem *conf_regs_memmap_p; /* Kernel memory mapped address for @@ -295,7 +350,7 @@ struct lpfc_sli4_hba { void __iomem *UERRHIregaddr; /* Address to UERR_STATUS_HI register */ void __iomem *UEMASKLOregaddr; /* Address to UE_MASK_LO register */ void __iomem *UEMASKHIregaddr; /* Address to UE_MASK_HI register */ - void __iomem *SCRATCHPADregaddr; /* Address to scratchpad register */ + void __iomem *SLIINTFregaddr; /* Address to SLI_INTF register */ /* BAR1 FCoE function CSR register memory map */ void __iomem *STAregaddr; /* Address to HST_STATE register */ void __iomem *ISRregaddr; /* Address to HST_ISR register */ @@ -310,6 +365,8 @@ struct lpfc_sli4_hba { uint32_t ue_mask_lo; uint32_t ue_mask_hi; + struct lpfc_register sli_intf; + struct lpfc_pc_sli4_params pc_sli4_params; struct msix_entry *msix_entries; uint32_t cfg_eqn; struct lpfc_fcp_eq_hdl *fcp_eq_hdl; /* FCP per-WQ handle */ @@ -406,6 +463,8 @@ void lpfc_sli4_mbox_cmd_free(struct lpfc_hba *, struct lpfcMboxq *); void lpfc_sli4_mbx_sge_set(struct lpfcMboxq *, uint32_t, dma_addr_t, uint32_t); void lpfc_sli4_mbx_sge_get(struct lpfcMboxq *, uint32_t, struct lpfc_mbx_sge *); +int lpfc_sli4_mbx_read_fcf_record(struct lpfc_hba *, struct lpfcMboxq *, + uint16_t); void lpfc_sli4_hba_reset(struct lpfc_hba *); struct lpfc_queue *lpfc_sli4_queue_alloc(struct lpfc_hba *, uint32_t, @@ -448,6 +507,7 @@ int lpfc_sli4_alloc_rpi(struct lpfc_hba *); void lpfc_sli4_free_rpi(struct lpfc_hba *, int); void lpfc_sli4_remove_rpis(struct lpfc_hba *); void lpfc_sli4_async_event_proc(struct lpfc_hba *); +void lpfc_sli4_fcf_redisc_event_proc(struct lpfc_hba *); int lpfc_sli4_resume_rpi(struct lpfc_nodelist *); void lpfc_sli4_fcp_xri_abort_event_proc(struct lpfc_hba *); void lpfc_sli4_els_xri_abort_event_proc(struct lpfc_hba *); diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h index 792f72263f1..ac276aa46fb 100644 --- a/drivers/scsi/lpfc/lpfc_version.h +++ b/drivers/scsi/lpfc/lpfc_version.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2009 Emulex. All rights reserved. * + * Copyright (C) 2004-2010 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * * @@ -18,7 +18,7 @@ * included with this package. * *******************************************************************/ -#define LPFC_DRIVER_VERSION "8.3.7" +#define LPFC_DRIVER_VERSION "8.3.9" #define LPFC_DRIVER_NAME "lpfc" #define LPFC_SP_DRIVER_HANDLER_NAME "lpfc:sp" #define LPFC_FP_DRIVER_HANDLER_NAME "lpfc:fp" diff --git a/drivers/scsi/lpfc/lpfc_vport.c b/drivers/scsi/lpfc/lpfc_vport.c index e3c7fa64230..dc86e873102 100644 --- a/drivers/scsi/lpfc/lpfc_vport.c +++ b/drivers/scsi/lpfc/lpfc_vport.c @@ -389,7 +389,7 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable) * by the port. */ if ((phba->sli_rev == LPFC_SLI_REV4) && - (pport->vpi_state & LPFC_VPI_REGISTERED)) { + (pport->fc_flag & FC_VFI_REGISTERED)) { rc = lpfc_sli4_init_vpi(phba, vpi); if (rc) { lpfc_printf_log(phba, KERN_ERR, LOG_VPORT, @@ -505,6 +505,7 @@ enable_vport(struct fc_vport *fc_vport) struct lpfc_vport *vport = *(struct lpfc_vport **)fc_vport->dd_data; struct lpfc_hba *phba = vport->phba; struct lpfc_nodelist *ndlp = NULL; + struct Scsi_Host *shost = lpfc_shost_from_vport(vport); if ((phba->link_state < LPFC_LINK_UP) || (phba->fc_topology == TOPOLOGY_LOOP)) { @@ -512,10 +513,10 @@ enable_vport(struct fc_vport *fc_vport) return VPORT_OK; } - spin_lock_irq(&phba->hbalock); + spin_lock_irq(shost->host_lock); vport->load_flag |= FC_LOADING; vport->fc_flag |= FC_VPORT_NEEDS_REG_VPI; - spin_unlock_irq(&phba->hbalock); + spin_unlock_irq(shost->host_lock); /* Use the Physical nodes Fabric NDLP to determine if the link is * up and ready to FDISC. |