2005-04-18 05:05:31 +08:00
|
|
|
/*******************************************************************
|
|
|
|
* This file is part of the Emulex Linux Device Driver for *
|
2005-06-25 22:34:39 +08:00
|
|
|
* Fibre Channel Host Bus Adapters. *
|
2008-02-09 07:49:26 +08:00
|
|
|
* Copyright (C) 2004-2008 Emulex. All rights reserved. *
|
2005-06-25 22:34:39 +08:00
|
|
|
* EMULEX and SLI are trademarks of Emulex. *
|
2005-04-18 05:05:31 +08:00
|
|
|
* www.emulex.com *
|
2005-06-25 22:34:39 +08:00
|
|
|
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
|
2005-04-18 05:05:31 +08:00
|
|
|
* *
|
|
|
|
* This program is free software; you can redistribute it and/or *
|
2005-06-25 22:34:39 +08:00
|
|
|
* 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. *
|
2005-04-18 05:05:31 +08:00
|
|
|
*******************************************************************/
|
|
|
|
|
|
|
|
#include <linux/blkdev.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/dma-mapping.h>
|
|
|
|
#include <linux/idr.h>
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/kthread.h>
|
|
|
|
#include <linux/pci.h>
|
|
|
|
#include <linux/spinlock.h>
|
2007-06-18 08:56:38 +08:00
|
|
|
#include <linux/ctype.h>
|
2005-04-18 05:05:31 +08:00
|
|
|
|
2005-08-11 03:03:09 +08:00
|
|
|
#include <scsi/scsi.h>
|
2005-04-18 05:05:31 +08:00
|
|
|
#include <scsi/scsi_device.h>
|
|
|
|
#include <scsi/scsi_host.h>
|
|
|
|
#include <scsi/scsi_transport_fc.h>
|
|
|
|
|
|
|
|
#include "lpfc_hw.h"
|
|
|
|
#include "lpfc_sli.h"
|
|
|
|
#include "lpfc_disc.h"
|
|
|
|
#include "lpfc_scsi.h"
|
|
|
|
#include "lpfc.h"
|
|
|
|
#include "lpfc_logmsg.h"
|
|
|
|
#include "lpfc_crtn.h"
|
2007-06-18 08:56:38 +08:00
|
|
|
#include "lpfc_vport.h"
|
2005-04-18 05:05:31 +08:00
|
|
|
#include "lpfc_version.h"
|
|
|
|
|
2006-03-01 11:33:04 +08:00
|
|
|
static int lpfc_parse_vpd(struct lpfc_hba *, uint8_t *, int);
|
2005-04-18 05:05:31 +08:00
|
|
|
static void lpfc_get_hba_model_desc(struct lpfc_hba *, uint8_t *, uint8_t *);
|
|
|
|
static int lpfc_post_rcv_buf(struct lpfc_hba *);
|
|
|
|
|
|
|
|
static struct scsi_transport_template *lpfc_transport_template = NULL;
|
2007-06-18 08:56:38 +08:00
|
|
|
static struct scsi_transport_template *lpfc_vport_transport_template = NULL;
|
2005-04-18 05:05:31 +08:00
|
|
|
static DEFINE_IDR(lpfc_hba_index);
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_config_port_prep: Perform lpfc initialization prior to config port.
|
|
|
|
* @phba: pointer to lpfc hba data structure.
|
|
|
|
*
|
|
|
|
* This routine will do LPFC initialization prior to issuing the CONFIG_PORT
|
|
|
|
* mailbox command. It retrieves the revision information from the HBA and
|
|
|
|
* collects the Vital Product Data (VPD) about the HBA for preparing the
|
|
|
|
* configuration of the HBA.
|
|
|
|
*
|
|
|
|
* Return codes:
|
|
|
|
* 0 - success.
|
|
|
|
* -ERESTART - requests the SLI layer to reset the HBA and try again.
|
|
|
|
* Any other value - indicates an error.
|
|
|
|
**/
|
2005-04-18 05:05:31 +08:00
|
|
|
int
|
2007-06-18 08:56:36 +08:00
|
|
|
lpfc_config_port_prep(struct lpfc_hba *phba)
|
2005-04-18 05:05:31 +08:00
|
|
|
{
|
|
|
|
lpfc_vpd_t *vp = &phba->vpd;
|
|
|
|
int i = 0, rc;
|
|
|
|
LPFC_MBOXQ_t *pmb;
|
|
|
|
MAILBOX_t *mb;
|
|
|
|
char *lpfc_vpd_data = NULL;
|
|
|
|
uint16_t offset = 0;
|
|
|
|
static char licensed[56] =
|
|
|
|
"key unlock for use with gnu public licensed code only\0";
|
2006-07-07 03:50:50 +08:00
|
|
|
static int init_key = 1;
|
2005-04-18 05:05:31 +08:00
|
|
|
|
|
|
|
pmb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
|
|
|
|
if (!pmb) {
|
2007-06-18 08:56:36 +08:00
|
|
|
phba->link_state = LPFC_HBA_ERROR;
|
2005-04-18 05:05:31 +08:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
mb = &pmb->mb;
|
2007-06-18 08:56:36 +08:00
|
|
|
phba->link_state = LPFC_INIT_MBX_CMDS;
|
2005-04-18 05:05:31 +08:00
|
|
|
|
|
|
|
if (lpfc_is_LC_HBA(phba->pcidev->device)) {
|
2006-07-07 03:50:50 +08:00
|
|
|
if (init_key) {
|
|
|
|
uint32_t *ptext = (uint32_t *) licensed;
|
2005-04-18 05:05:31 +08:00
|
|
|
|
2006-07-07 03:50:50 +08:00
|
|
|
for (i = 0; i < 56; i += sizeof (uint32_t), ptext++)
|
|
|
|
*ptext = cpu_to_be32(*ptext);
|
|
|
|
init_key = 0;
|
|
|
|
}
|
2005-04-18 05:05:31 +08:00
|
|
|
|
|
|
|
lpfc_read_nv(phba, pmb);
|
|
|
|
memset((char*)mb->un.varRDnvp.rsvd3, 0,
|
|
|
|
sizeof (mb->un.varRDnvp.rsvd3));
|
|
|
|
memcpy((char*)mb->un.varRDnvp.rsvd3, licensed,
|
|
|
|
sizeof (licensed));
|
|
|
|
|
|
|
|
rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);
|
|
|
|
|
|
|
|
if (rc != MBX_SUCCESS) {
|
2007-06-18 08:56:37 +08:00
|
|
|
lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
|
2007-08-02 23:10:09 +08:00
|
|
|
"0324 Config Port initialization "
|
2005-04-18 05:05:31 +08:00
|
|
|
"error, mbxCmd x%x READ_NVPARM, "
|
|
|
|
"mbxStatus x%x\n",
|
|
|
|
mb->mbxCommand, mb->mbxStatus);
|
|
|
|
mempool_free(pmb, phba->mbox_mem_pool);
|
|
|
|
return -ERESTART;
|
|
|
|
}
|
|
|
|
memcpy(phba->wwnn, (char *)mb->un.varRDnvp.nodename,
|
2007-06-18 08:56:36 +08:00
|
|
|
sizeof(phba->wwnn));
|
|
|
|
memcpy(phba->wwpn, (char *)mb->un.varRDnvp.portname,
|
|
|
|
sizeof(phba->wwpn));
|
2005-04-18 05:05:31 +08:00
|
|
|
}
|
|
|
|
|
2007-06-18 08:56:38 +08:00
|
|
|
phba->sli3_options = 0x0;
|
|
|
|
|
2005-04-18 05:05:31 +08:00
|
|
|
/* Setup and issue mailbox READ REV command */
|
|
|
|
lpfc_read_rev(phba, pmb);
|
|
|
|
rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);
|
|
|
|
if (rc != MBX_SUCCESS) {
|
2007-06-18 08:56:37 +08:00
|
|
|
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
|
2007-08-02 23:10:09 +08:00
|
|
|
"0439 Adapter failed to init, mbxCmd x%x "
|
2005-04-18 05:05:31 +08:00
|
|
|
"READ_REV, mbxStatus x%x\n",
|
|
|
|
mb->mbxCommand, mb->mbxStatus);
|
|
|
|
mempool_free( pmb, phba->mbox_mem_pool);
|
|
|
|
return -ERESTART;
|
|
|
|
}
|
|
|
|
|
2007-06-18 08:56:38 +08:00
|
|
|
|
2005-11-29 00:41:15 +08:00
|
|
|
/*
|
|
|
|
* The value of rr must be 1 since the driver set the cv field to 1.
|
|
|
|
* This setting requires the FW to set all revision fields.
|
2005-04-18 05:05:31 +08:00
|
|
|
*/
|
2005-11-29 00:41:15 +08:00
|
|
|
if (mb->un.varRdRev.rr == 0) {
|
2005-04-18 05:05:31 +08:00
|
|
|
vp->rev.rBit = 0;
|
2005-11-29 00:41:15 +08:00
|
|
|
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
|
2007-08-02 23:10:09 +08:00
|
|
|
"0440 Adapter failed to init, READ_REV has "
|
|
|
|
"missing revision information.\n");
|
2005-04-18 05:05:31 +08:00
|
|
|
mempool_free(pmb, phba->mbox_mem_pool);
|
|
|
|
return -ERESTART;
|
|
|
|
}
|
|
|
|
|
2008-06-15 10:52:59 +08:00
|
|
|
if (phba->sli_rev == 3 && !mb->un.varRdRev.v3rsp) {
|
|
|
|
mempool_free(pmb, phba->mbox_mem_pool);
|
2007-06-18 08:56:37 +08:00
|
|
|
return -EINVAL;
|
2008-06-15 10:52:59 +08:00
|
|
|
}
|
2007-06-18 08:56:37 +08:00
|
|
|
|
2005-04-18 05:05:31 +08:00
|
|
|
/* Save information as VPD data */
|
2005-11-29 00:41:15 +08:00
|
|
|
vp->rev.rBit = 1;
|
2007-06-18 08:56:38 +08:00
|
|
|
memcpy(&vp->sli3Feat, &mb->un.varRdRev.sli3Feat, sizeof(uint32_t));
|
2005-11-29 00:41:15 +08:00
|
|
|
vp->rev.sli1FwRev = mb->un.varRdRev.sli1FwRev;
|
|
|
|
memcpy(vp->rev.sli1FwName, (char*) mb->un.varRdRev.sli1FwName, 16);
|
|
|
|
vp->rev.sli2FwRev = mb->un.varRdRev.sli2FwRev;
|
|
|
|
memcpy(vp->rev.sli2FwName, (char *) mb->un.varRdRev.sli2FwName, 16);
|
2005-04-18 05:05:31 +08:00
|
|
|
vp->rev.biuRev = mb->un.varRdRev.biuRev;
|
|
|
|
vp->rev.smRev = mb->un.varRdRev.smRev;
|
|
|
|
vp->rev.smFwRev = mb->un.varRdRev.un.smFwRev;
|
|
|
|
vp->rev.endecRev = mb->un.varRdRev.endecRev;
|
|
|
|
vp->rev.fcphHigh = mb->un.varRdRev.fcphHigh;
|
|
|
|
vp->rev.fcphLow = mb->un.varRdRev.fcphLow;
|
|
|
|
vp->rev.feaLevelHigh = mb->un.varRdRev.feaLevelHigh;
|
|
|
|
vp->rev.feaLevelLow = mb->un.varRdRev.feaLevelLow;
|
|
|
|
vp->rev.postKernRev = mb->un.varRdRev.postKernRev;
|
|
|
|
vp->rev.opFwRev = mb->un.varRdRev.opFwRev;
|
|
|
|
|
2007-06-18 08:56:38 +08:00
|
|
|
/* If the sli feature level is less then 9, we must
|
|
|
|
* tear down all RPIs and VPIs on link down if NPIV
|
|
|
|
* is enabled.
|
|
|
|
*/
|
|
|
|
if (vp->rev.feaLevelHigh < 9)
|
|
|
|
phba->sli3_options |= LPFC_SLI3_VPORT_TEARDOWN;
|
|
|
|
|
2005-04-18 05:05:31 +08:00
|
|
|
if (lpfc_is_LC_HBA(phba->pcidev->device))
|
|
|
|
memcpy(phba->RandomData, (char *)&mb->un.varWords[24],
|
|
|
|
sizeof (phba->RandomData));
|
|
|
|
|
|
|
|
/* Get adapter VPD information */
|
|
|
|
pmb->context2 = kmalloc(DMP_RSP_SIZE, GFP_KERNEL);
|
|
|
|
if (!pmb->context2)
|
|
|
|
goto out_free_mbox;
|
|
|
|
lpfc_vpd_data = kmalloc(DMP_VPD_SIZE, GFP_KERNEL);
|
|
|
|
if (!lpfc_vpd_data)
|
|
|
|
goto out_free_context2;
|
|
|
|
|
|
|
|
do {
|
|
|
|
lpfc_dump_mem(phba, pmb, offset);
|
|
|
|
rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);
|
|
|
|
|
|
|
|
if (rc != MBX_SUCCESS) {
|
|
|
|
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
|
2007-08-02 23:10:09 +08:00
|
|
|
"0441 VPD not present on adapter, "
|
2005-04-18 05:05:31 +08:00
|
|
|
"mbxCmd x%x DUMP VPD, mbxStatus x%x\n",
|
|
|
|
mb->mbxCommand, mb->mbxStatus);
|
2006-03-01 11:33:04 +08:00
|
|
|
mb->un.varDmp.word_cnt = 0;
|
2005-04-18 05:05:31 +08:00
|
|
|
}
|
2006-03-01 11:33:04 +08:00
|
|
|
if (mb->un.varDmp.word_cnt > DMP_VPD_SIZE - offset)
|
|
|
|
mb->un.varDmp.word_cnt = DMP_VPD_SIZE - offset;
|
2005-04-18 05:05:31 +08:00
|
|
|
lpfc_sli_pcimem_bcopy(pmb->context2, lpfc_vpd_data + offset,
|
2007-06-18 08:56:38 +08:00
|
|
|
mb->un.varDmp.word_cnt);
|
2005-04-18 05:05:31 +08:00
|
|
|
offset += mb->un.varDmp.word_cnt;
|
2006-03-01 11:33:04 +08:00
|
|
|
} while (mb->un.varDmp.word_cnt && offset < DMP_VPD_SIZE);
|
|
|
|
lpfc_parse_vpd(phba, lpfc_vpd_data, offset);
|
2005-04-18 05:05:31 +08:00
|
|
|
|
|
|
|
kfree(lpfc_vpd_data);
|
|
|
|
out_free_context2:
|
|
|
|
kfree(pmb->context2);
|
|
|
|
out_free_mbox:
|
|
|
|
mempool_free(pmb, phba->mbox_mem_pool);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_config_async_cmpl: Completion handler for config async event mbox cmd.
|
|
|
|
* @phba: pointer to lpfc hba data structure.
|
|
|
|
* @pmboxq: pointer to the driver internal queue element for mailbox command.
|
|
|
|
*
|
|
|
|
* This is the completion handler for driver's configuring asynchronous event
|
|
|
|
* mailbox command to the device. If the mailbox command returns successfully,
|
|
|
|
* it will set internal async event support flag to 1; otherwise, it will
|
|
|
|
* set internal async event support flag to 0.
|
|
|
|
**/
|
2007-10-28 01:37:05 +08:00
|
|
|
static void
|
|
|
|
lpfc_config_async_cmpl(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq)
|
|
|
|
{
|
|
|
|
if (pmboxq->mb.mbxStatus == MBX_SUCCESS)
|
|
|
|
phba->temp_sensor_support = 1;
|
|
|
|
else
|
|
|
|
phba->temp_sensor_support = 0;
|
|
|
|
mempool_free(pmboxq, phba->mbox_mem_pool);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_config_port_post: Perform lpfc initialization after config port.
|
|
|
|
* @phba: pointer to lpfc hba data structure.
|
|
|
|
*
|
|
|
|
* This routine will do LPFC initialization after the CONFIG_PORT mailbox
|
|
|
|
* command call. It performs all internal resource and state setups on the
|
|
|
|
* port: post IOCB buffers, enable appropriate host interrupt attentions,
|
|
|
|
* ELS ring timers, etc.
|
|
|
|
*
|
|
|
|
* Return codes
|
|
|
|
* 0 - success.
|
|
|
|
* Any other value - error.
|
|
|
|
**/
|
2005-04-18 05:05:31 +08:00
|
|
|
int
|
2007-06-18 08:56:36 +08:00
|
|
|
lpfc_config_port_post(struct lpfc_hba *phba)
|
2005-04-18 05:05:31 +08:00
|
|
|
{
|
2007-06-18 08:56:36 +08:00
|
|
|
struct lpfc_vport *vport = phba->pport;
|
2005-04-18 05:05:31 +08:00
|
|
|
LPFC_MBOXQ_t *pmb;
|
|
|
|
MAILBOX_t *mb;
|
|
|
|
struct lpfc_dmabuf *mp;
|
|
|
|
struct lpfc_sli *psli = &phba->sli;
|
|
|
|
uint32_t status, timeout;
|
2007-06-18 08:56:36 +08:00
|
|
|
int i, j;
|
|
|
|
int rc;
|
2005-04-18 05:05:31 +08:00
|
|
|
|
2007-10-28 01:38:11 +08:00
|
|
|
spin_lock_irq(&phba->hbalock);
|
|
|
|
/*
|
|
|
|
* If the Config port completed correctly the HBA is not
|
|
|
|
* over heated any more.
|
|
|
|
*/
|
|
|
|
if (phba->over_temp_state == HBA_OVER_TEMP)
|
|
|
|
phba->over_temp_state = HBA_NORMAL_TEMP;
|
|
|
|
spin_unlock_irq(&phba->hbalock);
|
|
|
|
|
2005-04-18 05:05:31 +08:00
|
|
|
pmb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
|
|
|
|
if (!pmb) {
|
2007-06-18 08:56:36 +08:00
|
|
|
phba->link_state = LPFC_HBA_ERROR;
|
2005-04-18 05:05:31 +08:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
mb = &pmb->mb;
|
|
|
|
|
|
|
|
/* Get login parameters for NID. */
|
2007-06-18 08:56:38 +08:00
|
|
|
lpfc_read_sparam(phba, pmb, 0);
|
2007-06-18 08:56:37 +08:00
|
|
|
pmb->vport = vport;
|
2005-04-18 05:05:31 +08:00
|
|
|
if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) {
|
2007-06-18 08:56:37 +08:00
|
|
|
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
|
2007-08-02 23:10:09 +08:00
|
|
|
"0448 Adapter failed init, mbxCmd x%x "
|
2005-04-18 05:05:31 +08:00
|
|
|
"READ_SPARM mbxStatus x%x\n",
|
|
|
|
mb->mbxCommand, mb->mbxStatus);
|
2007-06-18 08:56:36 +08:00
|
|
|
phba->link_state = LPFC_HBA_ERROR;
|
2005-04-18 05:05:31 +08:00
|
|
|
mp = (struct lpfc_dmabuf *) pmb->context1;
|
|
|
|
mempool_free( pmb, phba->mbox_mem_pool);
|
|
|
|
lpfc_mbuf_free(phba, mp->virt, mp->phys);
|
|
|
|
kfree(mp);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
mp = (struct lpfc_dmabuf *) pmb->context1;
|
|
|
|
|
2007-06-18 08:56:36 +08:00
|
|
|
memcpy(&vport->fc_sparam, mp->virt, sizeof (struct serv_parm));
|
2005-04-18 05:05:31 +08:00
|
|
|
lpfc_mbuf_free(phba, mp->virt, mp->phys);
|
|
|
|
kfree(mp);
|
|
|
|
pmb->context1 = NULL;
|
|
|
|
|
2006-12-03 02:35:30 +08:00
|
|
|
if (phba->cfg_soft_wwnn)
|
2007-06-18 08:56:36 +08:00
|
|
|
u64_to_wwn(phba->cfg_soft_wwnn,
|
|
|
|
vport->fc_sparam.nodeName.u.wwn);
|
2006-08-19 05:47:18 +08:00
|
|
|
if (phba->cfg_soft_wwpn)
|
2007-06-18 08:56:36 +08:00
|
|
|
u64_to_wwn(phba->cfg_soft_wwpn,
|
|
|
|
vport->fc_sparam.portName.u.wwn);
|
|
|
|
memcpy(&vport->fc_nodename, &vport->fc_sparam.nodeName,
|
2005-04-18 05:05:31 +08:00
|
|
|
sizeof (struct lpfc_name));
|
2007-06-18 08:56:36 +08:00
|
|
|
memcpy(&vport->fc_portname, &vport->fc_sparam.portName,
|
2005-04-18 05:05:31 +08:00
|
|
|
sizeof (struct lpfc_name));
|
|
|
|
/* If no serial number in VPD data, use low 6 bytes of WWNN */
|
|
|
|
/* This should be consolidated into parse_vpd ? - mr */
|
|
|
|
if (phba->SerialNumber[0] == 0) {
|
|
|
|
uint8_t *outptr;
|
|
|
|
|
2007-06-18 08:56:36 +08:00
|
|
|
outptr = &vport->fc_nodename.u.s.IEEE[0];
|
2005-04-18 05:05:31 +08:00
|
|
|
for (i = 0; i < 12; i++) {
|
|
|
|
status = *outptr++;
|
|
|
|
j = ((status & 0xf0) >> 4);
|
|
|
|
if (j <= 9)
|
|
|
|
phba->SerialNumber[i] =
|
|
|
|
(char)((uint8_t) 0x30 + (uint8_t) j);
|
|
|
|
else
|
|
|
|
phba->SerialNumber[i] =
|
|
|
|
(char)((uint8_t) 0x61 + (uint8_t) (j - 10));
|
|
|
|
i++;
|
|
|
|
j = (status & 0xf);
|
|
|
|
if (j <= 9)
|
|
|
|
phba->SerialNumber[i] =
|
|
|
|
(char)((uint8_t) 0x30 + (uint8_t) j);
|
|
|
|
else
|
|
|
|
phba->SerialNumber[i] =
|
|
|
|
(char)((uint8_t) 0x61 + (uint8_t) (j - 10));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
lpfc_read_config(phba, pmb);
|
2007-06-18 08:56:37 +08:00
|
|
|
pmb->vport = vport;
|
2005-04-18 05:05:31 +08:00
|
|
|
if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) {
|
2007-06-18 08:56:37 +08:00
|
|
|
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
|
2007-08-02 23:10:09 +08:00
|
|
|
"0453 Adapter failed to init, mbxCmd x%x "
|
2005-04-18 05:05:31 +08:00
|
|
|
"READ_CONFIG, mbxStatus x%x\n",
|
|
|
|
mb->mbxCommand, mb->mbxStatus);
|
2007-06-18 08:56:36 +08:00
|
|
|
phba->link_state = LPFC_HBA_ERROR;
|
2005-04-18 05:05:31 +08:00
|
|
|
mempool_free( pmb, phba->mbox_mem_pool);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Reset the DFT_HBA_Q_DEPTH to the max xri */
|
|
|
|
if (phba->cfg_hba_queue_depth > (mb->un.varRdConfig.max_xri+1))
|
|
|
|
phba->cfg_hba_queue_depth =
|
|
|
|
mb->un.varRdConfig.max_xri + 1;
|
|
|
|
|
|
|
|
phba->lmt = mb->un.varRdConfig.lmt;
|
2006-03-01 11:33:04 +08:00
|
|
|
|
|
|
|
/* Get the default values for Model Name and Description */
|
|
|
|
lpfc_get_hba_model_desc(phba, phba->ModelName, phba->ModelDesc);
|
|
|
|
|
|
|
|
if ((phba->cfg_link_speed > LINK_SPEED_10G)
|
|
|
|
|| ((phba->cfg_link_speed == LINK_SPEED_1G)
|
|
|
|
&& !(phba->lmt & LMT_1Gb))
|
|
|
|
|| ((phba->cfg_link_speed == LINK_SPEED_2G)
|
|
|
|
&& !(phba->lmt & LMT_2Gb))
|
|
|
|
|| ((phba->cfg_link_speed == LINK_SPEED_4G)
|
|
|
|
&& !(phba->lmt & LMT_4Gb))
|
|
|
|
|| ((phba->cfg_link_speed == LINK_SPEED_8G)
|
|
|
|
&& !(phba->lmt & LMT_8Gb))
|
|
|
|
|| ((phba->cfg_link_speed == LINK_SPEED_10G)
|
|
|
|
&& !(phba->lmt & LMT_10Gb))) {
|
|
|
|
/* Reset link speed to auto */
|
2007-06-18 08:56:37 +08:00
|
|
|
lpfc_printf_log(phba, KERN_WARNING, LOG_LINK_EVENT,
|
2007-08-02 23:10:09 +08:00
|
|
|
"1302 Invalid speed for this board: "
|
2005-04-18 05:05:31 +08:00
|
|
|
"Reset link speed to auto: x%x\n",
|
|
|
|
phba->cfg_link_speed);
|
|
|
|
phba->cfg_link_speed = LINK_SPEED_AUTO;
|
|
|
|
}
|
|
|
|
|
2007-06-18 08:56:36 +08:00
|
|
|
phba->link_state = LPFC_LINK_DOWN;
|
2005-04-18 05:05:31 +08:00
|
|
|
|
2007-10-28 01:37:25 +08:00
|
|
|
/* Only process IOCBs on ELS ring till hba_state is READY */
|
2006-12-03 02:34:16 +08:00
|
|
|
if (psli->ring[psli->extra_ring].cmdringaddr)
|
|
|
|
psli->ring[psli->extra_ring].flag |= LPFC_STOP_IOCB_EVENT;
|
2005-04-18 05:05:31 +08:00
|
|
|
if (psli->ring[psli->fcp_ring].cmdringaddr)
|
|
|
|
psli->ring[psli->fcp_ring].flag |= LPFC_STOP_IOCB_EVENT;
|
|
|
|
if (psli->ring[psli->next_ring].cmdringaddr)
|
|
|
|
psli->ring[psli->next_ring].flag |= LPFC_STOP_IOCB_EVENT;
|
|
|
|
|
|
|
|
/* Post receive buffers for desired rings */
|
2007-06-18 08:56:37 +08:00
|
|
|
if (phba->sli_rev != 3)
|
|
|
|
lpfc_post_rcv_buf(phba);
|
2005-04-18 05:05:31 +08:00
|
|
|
|
|
|
|
/* Enable appropriate host interrupts */
|
2007-06-18 08:56:36 +08:00
|
|
|
spin_lock_irq(&phba->hbalock);
|
2005-04-18 05:05:31 +08:00
|
|
|
status = readl(phba->HCregaddr);
|
|
|
|
status |= HC_MBINT_ENA | HC_ERINT_ENA | HC_LAINT_ENA;
|
|
|
|
if (psli->num_rings > 0)
|
|
|
|
status |= HC_R0INT_ENA;
|
|
|
|
if (psli->num_rings > 1)
|
|
|
|
status |= HC_R1INT_ENA;
|
|
|
|
if (psli->num_rings > 2)
|
|
|
|
status |= HC_R2INT_ENA;
|
|
|
|
if (psli->num_rings > 3)
|
|
|
|
status |= HC_R3INT_ENA;
|
|
|
|
|
[SCSI] lpfc 8.1.1 : Add polled-mode support
- Add functionality to run in polled mode only. Includes run time
attribute to enable mode.
- Enable runtime writable hba settings for coallescing and delay parameters
Customers have requested a mode in the driver to run strictly polled.
This is generally to support an environment where the server is extremely
loaded and is looking to reclaim some cpu cycles from adapter interrupt
handling.
This patch adds a new "poll" attribute, and the following behavior:
if value is 0 (default):
The driver uses the normal method for i/o completion. It uses the
firmware feature of interrupt coalesing. The firmware allows a
minimum number of i/o completions before an interrupt, or a maximum
time delay between interrupts. By default, the driver sets these
to no delay (disabled) or 1 i/o - meaning coalescing is disabled.
Attributes were provided to change the coalescing values, but it was
a module-load time only and global across all adapters.
This patch allows them to be writable on a per-adapter basis.
if value is 1 :
Interrupts are left enabled, expecting that the user has tuned the
interrupt coalescing values. When this setting is enabled, the driver
will attempt to service completed i/o whenever new i/o is submitted
to the adapter. If the coalescing values are large, and the i/o
generation rate steady, an interrupt will be avoided by servicing
completed i/o prior to the coalescing thresholds kicking in. However,
if the i/o completion load is high enough or i/o generation slow, the
coalescion values will ensure that completed i/o is serviced in a timely
fashion.
if value is 3 :
Turns off FCP i/o interrupts altogether. The coalescing values now have
no effect. A new attribute "poll_tmo" (default 10ms) exists to set
the polling interval for i/o completion. When this setting is enabled,
the driver will attempt to service completed i/o and restart the
interval timer whenever new i/o is submitted. This behavior allows for
servicing of completed i/o sooner than the interval timer, but ensures
that if no i/o is being issued, then the interval timer will kick in
to service the outstanding i/o.
Signed-off-by: James Smart <James.Smart@emulex.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
2005-11-30 05:32:13 +08:00
|
|
|
if ((phba->cfg_poll & ENABLE_FCP_RING_POLLING) &&
|
|
|
|
(phba->cfg_poll & DISABLE_FCP_RING_INT))
|
|
|
|
status &= ~(HC_R0INT_ENA << LPFC_FCP_RING);
|
|
|
|
|
2005-04-18 05:05:31 +08:00
|
|
|
writel(status, phba->HCregaddr);
|
|
|
|
readl(phba->HCregaddr); /* flush */
|
2007-06-18 08:56:36 +08:00
|
|
|
spin_unlock_irq(&phba->hbalock);
|
2005-04-18 05:05:31 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Setup the ring 0 (els) timeout handler
|
|
|
|
*/
|
|
|
|
timeout = phba->fc_ratov << 1;
|
2007-06-18 08:56:36 +08:00
|
|
|
mod_timer(&vport->els_tmofunc, jiffies + HZ * timeout);
|
2007-06-18 08:56:39 +08:00
|
|
|
mod_timer(&phba->hb_tmofunc, jiffies + HZ * LPFC_HB_MBOX_INTERVAL);
|
|
|
|
phba->hb_outstanding = 0;
|
|
|
|
phba->last_completion_time = jiffies;
|
2005-04-18 05:05:31 +08:00
|
|
|
|
|
|
|
lpfc_init_link(phba, pmb, phba->cfg_topology, phba->cfg_link_speed);
|
|
|
|
pmb->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
|
2007-06-18 08:56:37 +08:00
|
|
|
pmb->vport = vport;
|
2006-09-01 00:27:57 +08:00
|
|
|
rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
|
2007-04-25 21:52:49 +08:00
|
|
|
lpfc_set_loopback_flag(phba);
|
2006-09-01 00:27:57 +08:00
|
|
|
if (rc != MBX_SUCCESS) {
|
2007-06-18 08:56:37 +08:00
|
|
|
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
|
2007-08-02 23:10:09 +08:00
|
|
|
"0454 Adapter failed to init, mbxCmd x%x "
|
2005-04-18 05:05:31 +08:00
|
|
|
"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 */
|
|
|
|
|
2007-06-18 08:56:36 +08:00
|
|
|
phba->link_state = LPFC_HBA_ERROR;
|
2006-09-01 00:27:57 +08:00
|
|
|
if (rc != MBX_BUSY)
|
|
|
|
mempool_free(pmb, phba->mbox_mem_pool);
|
2005-04-18 05:05:31 +08:00
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
/* MBOX buffer will be freed in mbox compl */
|
2007-10-28 01:37:05 +08:00
|
|
|
pmb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
|
|
|
|
lpfc_config_async(phba, pmb, LPFC_ELS_RING);
|
|
|
|
pmb->mbox_cmpl = lpfc_config_async_cmpl;
|
|
|
|
pmb->vport = phba->pport;
|
|
|
|
rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
|
2005-04-18 05:05:31 +08:00
|
|
|
|
2007-10-28 01:37:05 +08:00
|
|
|
if ((rc != MBX_BUSY) && (rc != MBX_SUCCESS)) {
|
|
|
|
lpfc_printf_log(phba,
|
|
|
|
KERN_ERR,
|
|
|
|
LOG_INIT,
|
|
|
|
"0456 Adapter failed to issue "
|
|
|
|
"ASYNCEVT_ENABLE mbox status x%x \n.",
|
|
|
|
rc);
|
|
|
|
mempool_free(pmb, phba->mbox_mem_pool);
|
|
|
|
}
|
2006-07-07 03:50:36 +08:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_hba_down_prep: Perform lpfc uninitialization prior to HBA reset.
|
|
|
|
* @phba: pointer to lpfc HBA data structure.
|
|
|
|
*
|
|
|
|
* This routine will do LPFC uninitialization before the HBA is reset when
|
|
|
|
* bringing down the SLI Layer.
|
|
|
|
*
|
|
|
|
* Return codes
|
|
|
|
* 0 - success.
|
|
|
|
* Any other value - error.
|
|
|
|
**/
|
2005-04-18 05:05:31 +08:00
|
|
|
int
|
2007-06-18 08:56:36 +08:00
|
|
|
lpfc_hba_down_prep(struct lpfc_hba *phba)
|
2005-04-18 05:05:31 +08:00
|
|
|
{
|
2008-02-09 07:49:39 +08:00
|
|
|
struct lpfc_vport **vports;
|
|
|
|
int i;
|
2005-04-18 05:05:31 +08:00
|
|
|
/* Disable interrupts */
|
|
|
|
writel(0, phba->HCregaddr);
|
|
|
|
readl(phba->HCregaddr); /* flush */
|
|
|
|
|
2008-02-09 07:49:39 +08:00
|
|
|
if (phba->pport->load_flag & FC_UNLOADING)
|
|
|
|
lpfc_cleanup_discovery_resources(phba->pport);
|
|
|
|
else {
|
|
|
|
vports = lpfc_create_vport_work_array(phba);
|
|
|
|
if (vports != NULL)
|
|
|
|
for(i = 0; i <= phba->max_vpi && vports[i] != NULL; i++)
|
|
|
|
lpfc_cleanup_discovery_resources(vports[i]);
|
|
|
|
lpfc_destroy_vport_work_array(phba, vports);
|
2008-02-09 07:50:14 +08:00
|
|
|
}
|
|
|
|
return 0;
|
2005-04-18 05:05:31 +08:00
|
|
|
}
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_hba_down_post: Perform lpfc uninitialization after HBA reset.
|
|
|
|
* @phba: pointer to lpfc HBA data structure.
|
|
|
|
*
|
|
|
|
* This routine will do uninitialization after the HBA is reset when bring
|
|
|
|
* down the SLI Layer.
|
|
|
|
*
|
|
|
|
* Return codes
|
|
|
|
* 0 - sucess.
|
|
|
|
* Any other value - error.
|
|
|
|
**/
|
2006-03-01 08:25:27 +08:00
|
|
|
int
|
2007-06-18 08:56:36 +08:00
|
|
|
lpfc_hba_down_post(struct lpfc_hba *phba)
|
2006-03-01 08:25:27 +08:00
|
|
|
{
|
|
|
|
struct lpfc_sli *psli = &phba->sli;
|
|
|
|
struct lpfc_sli_ring *pring;
|
|
|
|
struct lpfc_dmabuf *mp, *next_mp;
|
2008-01-11 14:52:54 +08:00
|
|
|
struct lpfc_iocbq *iocb;
|
|
|
|
IOCB_t *cmd = NULL;
|
|
|
|
LIST_HEAD(completions);
|
2006-03-01 08:25:27 +08:00
|
|
|
int i;
|
|
|
|
|
2007-06-18 08:56:38 +08:00
|
|
|
if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED)
|
|
|
|
lpfc_sli_hbqbuf_free_all(phba);
|
|
|
|
else {
|
|
|
|
/* Cleanup preposted buffers on the ELS ring */
|
|
|
|
pring = &psli->ring[LPFC_ELS_RING];
|
|
|
|
list_for_each_entry_safe(mp, next_mp, &pring->postbufq, list) {
|
|
|
|
list_del(&mp->list);
|
|
|
|
pring->postbufq_cnt--;
|
|
|
|
lpfc_mbuf_free(phba, mp->virt, mp->phys);
|
|
|
|
kfree(mp);
|
|
|
|
}
|
2006-03-01 08:25:27 +08:00
|
|
|
}
|
|
|
|
|
2008-01-11 14:52:54 +08:00
|
|
|
spin_lock_irq(&phba->hbalock);
|
2006-03-01 08:25:27 +08:00
|
|
|
for (i = 0; i < psli->num_rings; i++) {
|
|
|
|
pring = &psli->ring[i];
|
2008-01-11 14:52:54 +08:00
|
|
|
|
|
|
|
/* At this point in time the HBA is either reset or DOA. Either
|
|
|
|
* way, nothing should be on txcmplq as it will NEVER complete.
|
|
|
|
*/
|
|
|
|
list_splice_init(&pring->txcmplq, &completions);
|
|
|
|
pring->txcmplq_cnt = 0;
|
|
|
|
spin_unlock_irq(&phba->hbalock);
|
|
|
|
|
|
|
|
while (!list_empty(&completions)) {
|
|
|
|
iocb = list_get_first(&completions, struct lpfc_iocbq,
|
|
|
|
list);
|
|
|
|
cmd = &iocb->iocb;
|
|
|
|
list_del_init(&iocb->list);
|
|
|
|
|
|
|
|
if (!iocb->iocb_cmpl)
|
|
|
|
lpfc_sli_release_iocbq(phba, iocb);
|
|
|
|
else {
|
|
|
|
cmd->ulpStatus = IOSTAT_LOCAL_REJECT;
|
|
|
|
cmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
|
|
|
|
(iocb->iocb_cmpl) (phba, iocb, iocb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-03-01 08:25:27 +08:00
|
|
|
lpfc_sli_abort_iocb_ring(phba, pring);
|
2008-01-11 14:52:54 +08:00
|
|
|
spin_lock_irq(&phba->hbalock);
|
2006-03-01 08:25:27 +08:00
|
|
|
}
|
2008-01-11 14:52:54 +08:00
|
|
|
spin_unlock_irq(&phba->hbalock);
|
2006-03-01 08:25:27 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_hb_timeout: The HBA-timer timeout handler.
|
|
|
|
* @ptr: unsigned long holds the pointer to lpfc hba data structure.
|
|
|
|
*
|
|
|
|
* This is the HBA-timer timeout handler registered to the lpfc driver. When
|
|
|
|
* this timer fires, a HBA timeout event shall be posted to the lpfc driver
|
|
|
|
* work-port-events bitmap and the worker thread is notified. This timeout
|
|
|
|
* event will be used by the worker thread to invoke the actual timeout
|
|
|
|
* handler routine, lpfc_hb_timeout_handler. Any periodical operations will
|
|
|
|
* be performed in the timeout handler and the HBA timeout event bit shall
|
|
|
|
* be cleared by the worker thread after it has taken the event bitmap out.
|
|
|
|
**/
|
2007-11-06 01:07:33 +08:00
|
|
|
static void
|
2007-06-18 08:56:39 +08:00
|
|
|
lpfc_hb_timeout(unsigned long ptr)
|
|
|
|
{
|
|
|
|
struct lpfc_hba *phba;
|
2008-06-15 10:52:53 +08:00
|
|
|
uint32_t tmo_posted;
|
2007-06-18 08:56:39 +08:00
|
|
|
unsigned long iflag;
|
|
|
|
|
|
|
|
phba = (struct lpfc_hba *)ptr;
|
|
|
|
spin_lock_irqsave(&phba->pport->work_port_lock, iflag);
|
2008-06-15 10:52:53 +08:00
|
|
|
tmo_posted = phba->pport->work_port_events & WORKER_HB_TMO;
|
|
|
|
if (!tmo_posted)
|
2007-06-18 08:56:39 +08:00
|
|
|
phba->pport->work_port_events |= WORKER_HB_TMO;
|
|
|
|
spin_unlock_irqrestore(&phba->pport->work_port_lock, iflag);
|
|
|
|
|
2008-06-15 10:52:53 +08:00
|
|
|
if (!tmo_posted)
|
|
|
|
lpfc_worker_wake_up(phba);
|
2007-06-18 08:56:39 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_hb_mbox_cmpl: The lpfc heart-beat mailbox command callback function.
|
|
|
|
* @phba: pointer to lpfc hba data structure.
|
|
|
|
* @pmboxq: pointer to the driver internal queue element for mailbox command.
|
|
|
|
*
|
|
|
|
* This is the callback function to the lpfc heart-beat mailbox command.
|
|
|
|
* If configured, the lpfc driver issues the heart-beat mailbox command to
|
|
|
|
* the HBA every LPFC_HB_MBOX_INTERVAL (current 5) seconds. At the time the
|
|
|
|
* heart-beat mailbox command is issued, the driver shall set up heart-beat
|
|
|
|
* timeout timer to LPFC_HB_MBOX_TIMEOUT (current 30) seconds and marks
|
|
|
|
* heart-beat outstanding state. Once the mailbox command comes back and
|
|
|
|
* no error conditions detected, the heart-beat mailbox command timer is
|
|
|
|
* reset to LPFC_HB_MBOX_INTERVAL seconds and the heart-beat outstanding
|
|
|
|
* state is cleared for the next heart-beat. If the timer expired with the
|
|
|
|
* heart-beat outstanding state set, the driver will put the HBA offline.
|
|
|
|
**/
|
2007-06-18 08:56:39 +08:00
|
|
|
static void
|
|
|
|
lpfc_hb_mbox_cmpl(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq)
|
|
|
|
{
|
|
|
|
unsigned long drvr_flag;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&phba->hbalock, drvr_flag);
|
|
|
|
phba->hb_outstanding = 0;
|
|
|
|
spin_unlock_irqrestore(&phba->hbalock, drvr_flag);
|
|
|
|
|
|
|
|
mempool_free(pmboxq, phba->mbox_mem_pool);
|
|
|
|
if (!(phba->pport->fc_flag & FC_OFFLINE_MODE) &&
|
|
|
|
!(phba->link_state == LPFC_HBA_ERROR) &&
|
2007-08-02 23:10:31 +08:00
|
|
|
!(phba->pport->load_flag & FC_UNLOADING))
|
2007-06-18 08:56:39 +08:00
|
|
|
mod_timer(&phba->hb_tmofunc,
|
|
|
|
jiffies + HZ * LPFC_HB_MBOX_INTERVAL);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_hb_timeout_handler: The HBA-timer timeout handler.
|
|
|
|
* @phba: pointer to lpfc hba data structure.
|
|
|
|
*
|
|
|
|
* This is the actual HBA-timer timeout handler to be invoked by the worker
|
|
|
|
* thread whenever the HBA timer fired and HBA-timeout event posted. This
|
|
|
|
* handler performs any periodic operations needed for the device. If such
|
|
|
|
* periodic event has already been attended to either in the interrupt handler
|
|
|
|
* or by processing slow-ring or fast-ring events within the HBA-timer
|
|
|
|
* timeout window (LPFC_HB_MBOX_INTERVAL), this handler just simply resets
|
|
|
|
* the timer for the next timeout period. If lpfc heart-beat mailbox command
|
|
|
|
* is configured and there is no heart-beat mailbox command outstanding, a
|
|
|
|
* heart-beat mailbox is issued and timer set properly. Otherwise, if there
|
|
|
|
* has been a heart-beat mailbox command outstanding, the HBA shall be put
|
|
|
|
* to offline.
|
|
|
|
**/
|
2007-06-18 08:56:39 +08:00
|
|
|
void
|
|
|
|
lpfc_hb_timeout_handler(struct lpfc_hba *phba)
|
|
|
|
{
|
|
|
|
LPFC_MBOXQ_t *pmboxq;
|
2008-01-11 14:52:36 +08:00
|
|
|
struct lpfc_dmabuf *buf_ptr;
|
2007-06-18 08:56:39 +08:00
|
|
|
int retval;
|
|
|
|
struct lpfc_sli *psli = &phba->sli;
|
2008-01-11 14:52:36 +08:00
|
|
|
LIST_HEAD(completions);
|
2007-06-18 08:56:39 +08:00
|
|
|
|
|
|
|
if ((phba->link_state == LPFC_HBA_ERROR) ||
|
2007-08-02 23:10:31 +08:00
|
|
|
(phba->pport->load_flag & FC_UNLOADING) ||
|
2007-06-18 08:56:39 +08:00
|
|
|
(phba->pport->fc_flag & FC_OFFLINE_MODE))
|
|
|
|
return;
|
|
|
|
|
|
|
|
spin_lock_irq(&phba->pport->work_port_lock);
|
|
|
|
/* If the timer is already canceled do nothing */
|
|
|
|
if (!(phba->pport->work_port_events & WORKER_HB_TMO)) {
|
|
|
|
spin_unlock_irq(&phba->pport->work_port_lock);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (time_after(phba->last_completion_time + LPFC_HB_MBOX_INTERVAL * HZ,
|
|
|
|
jiffies)) {
|
|
|
|
spin_unlock_irq(&phba->pport->work_port_lock);
|
|
|
|
if (!phba->hb_outstanding)
|
|
|
|
mod_timer(&phba->hb_tmofunc,
|
|
|
|
jiffies + HZ * LPFC_HB_MBOX_INTERVAL);
|
|
|
|
else
|
|
|
|
mod_timer(&phba->hb_tmofunc,
|
|
|
|
jiffies + HZ * LPFC_HB_MBOX_TIMEOUT);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
spin_unlock_irq(&phba->pport->work_port_lock);
|
|
|
|
|
2008-01-11 14:52:36 +08:00
|
|
|
if (phba->elsbuf_cnt &&
|
|
|
|
(phba->elsbuf_cnt == phba->elsbuf_prev_cnt)) {
|
|
|
|
spin_lock_irq(&phba->hbalock);
|
|
|
|
list_splice_init(&phba->elsbuf, &completions);
|
|
|
|
phba->elsbuf_cnt = 0;
|
|
|
|
phba->elsbuf_prev_cnt = 0;
|
|
|
|
spin_unlock_irq(&phba->hbalock);
|
|
|
|
|
|
|
|
while (!list_empty(&completions)) {
|
|
|
|
list_remove_head(&completions, buf_ptr,
|
|
|
|
struct lpfc_dmabuf, list);
|
|
|
|
lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys);
|
|
|
|
kfree(buf_ptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
phba->elsbuf_prev_cnt = phba->elsbuf_cnt;
|
|
|
|
|
2007-06-18 08:56:39 +08:00
|
|
|
/* If there is no heart beat outstanding, issue a heartbeat command */
|
2008-01-11 14:52:48 +08:00
|
|
|
if (phba->cfg_enable_hba_heartbeat) {
|
|
|
|
if (!phba->hb_outstanding) {
|
|
|
|
pmboxq = mempool_alloc(phba->mbox_mem_pool,GFP_KERNEL);
|
|
|
|
if (!pmboxq) {
|
|
|
|
mod_timer(&phba->hb_tmofunc,
|
|
|
|
jiffies + HZ * LPFC_HB_MBOX_INTERVAL);
|
|
|
|
return;
|
|
|
|
}
|
2007-06-18 08:56:39 +08:00
|
|
|
|
2008-01-11 14:52:48 +08:00
|
|
|
lpfc_heart_beat(phba, pmboxq);
|
|
|
|
pmboxq->mbox_cmpl = lpfc_hb_mbox_cmpl;
|
|
|
|
pmboxq->vport = phba->pport;
|
|
|
|
retval = lpfc_sli_issue_mbox(phba, pmboxq, MBX_NOWAIT);
|
2007-06-18 08:56:39 +08:00
|
|
|
|
2008-01-11 14:52:48 +08:00
|
|
|
if (retval != MBX_BUSY && retval != MBX_SUCCESS) {
|
|
|
|
mempool_free(pmboxq, phba->mbox_mem_pool);
|
|
|
|
mod_timer(&phba->hb_tmofunc,
|
|
|
|
jiffies + HZ * LPFC_HB_MBOX_INTERVAL);
|
|
|
|
return;
|
|
|
|
}
|
2007-06-18 08:56:39 +08:00
|
|
|
mod_timer(&phba->hb_tmofunc,
|
2008-01-11 14:52:48 +08:00
|
|
|
jiffies + HZ * LPFC_HB_MBOX_TIMEOUT);
|
|
|
|
phba->hb_outstanding = 1;
|
2007-06-18 08:56:39 +08:00
|
|
|
return;
|
2008-01-11 14:52:48 +08:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* If heart beat timeout called with hb_outstanding set
|
|
|
|
* we need to take the HBA offline.
|
|
|
|
*/
|
|
|
|
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
|
|
|
|
"0459 Adapter heartbeat failure, "
|
|
|
|
"taking this port offline.\n");
|
|
|
|
|
|
|
|
spin_lock_irq(&phba->hbalock);
|
|
|
|
psli->sli_flag &= ~LPFC_SLI2_ACTIVE;
|
|
|
|
spin_unlock_irq(&phba->hbalock);
|
|
|
|
|
|
|
|
lpfc_offline_prep(phba);
|
|
|
|
lpfc_offline(phba);
|
|
|
|
lpfc_unblock_mgmt_io(phba);
|
|
|
|
phba->link_state = LPFC_HBA_ERROR;
|
|
|
|
lpfc_hba_down_post(phba);
|
2007-06-18 08:56:39 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_offline_eratt: Bring lpfc offline on hardware error attention.
|
|
|
|
* @phba: pointer to lpfc hba data structure.
|
|
|
|
*
|
|
|
|
* This routine is called to bring the HBA offline when HBA hardware error
|
|
|
|
* other than Port Error 6 has been detected.
|
|
|
|
**/
|
2008-01-11 14:52:54 +08:00
|
|
|
static void
|
|
|
|
lpfc_offline_eratt(struct lpfc_hba *phba)
|
|
|
|
{
|
|
|
|
struct lpfc_sli *psli = &phba->sli;
|
|
|
|
|
|
|
|
spin_lock_irq(&phba->hbalock);
|
|
|
|
psli->sli_flag &= ~LPFC_SLI2_ACTIVE;
|
|
|
|
spin_unlock_irq(&phba->hbalock);
|
|
|
|
lpfc_offline_prep(phba);
|
|
|
|
|
|
|
|
lpfc_offline(phba);
|
|
|
|
lpfc_reset_barrier(phba);
|
|
|
|
lpfc_sli_brdreset(phba);
|
|
|
|
lpfc_hba_down_post(phba);
|
|
|
|
lpfc_sli_brdready(phba, HS_MBRDY);
|
|
|
|
lpfc_unblock_mgmt_io(phba);
|
|
|
|
phba->link_state = LPFC_HBA_ERROR;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_handle_eratt: The HBA hardware error handler.
|
|
|
|
* @phba: pointer to lpfc hba data structure.
|
|
|
|
*
|
|
|
|
* This routine is invoked to handle the following HBA hardware error
|
|
|
|
* conditions:
|
|
|
|
* 1 - HBA error attention interrupt
|
|
|
|
* 2 - DMA ring index out of range
|
|
|
|
* 3 - Mailbox command came back as unknown
|
|
|
|
**/
|
2005-04-18 05:05:31 +08:00
|
|
|
void
|
2007-06-18 08:56:36 +08:00
|
|
|
lpfc_handle_eratt(struct lpfc_hba *phba)
|
2005-04-18 05:05:31 +08:00
|
|
|
{
|
2007-06-18 08:56:36 +08:00
|
|
|
struct lpfc_vport *vport = phba->pport;
|
|
|
|
struct lpfc_sli *psli = &phba->sli;
|
2005-04-18 05:05:31 +08:00
|
|
|
struct lpfc_sli_ring *pring;
|
2006-08-19 05:46:43 +08:00
|
|
|
uint32_t event_data;
|
2007-10-28 01:37:05 +08:00
|
|
|
unsigned long temperature;
|
|
|
|
struct temp_event temp_event_data;
|
2007-06-18 08:56:38 +08:00
|
|
|
struct Scsi_Host *shost;
|
2007-06-18 08:56:36 +08:00
|
|
|
|
2007-02-15 04:28:36 +08:00
|
|
|
/* If the pci channel is offline, ignore possible errors,
|
|
|
|
* since we cannot communicate with the pci card anyway. */
|
|
|
|
if (pci_channel_offline(phba->pcidev))
|
|
|
|
return;
|
2008-01-11 14:52:48 +08:00
|
|
|
/* If resets are disabled then leave the HBA alone and return */
|
|
|
|
if (!phba->cfg_enable_hba_reset)
|
|
|
|
return;
|
2005-04-18 05:05:31 +08:00
|
|
|
|
2008-04-07 22:16:05 +08:00
|
|
|
if (phba->work_hs & HS_FFER6) {
|
2005-04-18 05:05:31 +08:00
|
|
|
/* Re-establishing Link */
|
|
|
|
lpfc_printf_log(phba, KERN_INFO, LOG_LINK_EVENT,
|
2007-08-02 23:10:09 +08:00
|
|
|
"1301 Re-establishing Link "
|
2005-04-18 05:05:31 +08:00
|
|
|
"Data: x%x x%x x%x\n",
|
2007-08-02 23:10:09 +08:00
|
|
|
phba->work_hs,
|
2005-04-18 05:05:31 +08:00
|
|
|
phba->work_status[0], phba->work_status[1]);
|
2008-04-07 22:15:56 +08:00
|
|
|
|
2007-06-18 08:56:38 +08:00
|
|
|
spin_lock_irq(&phba->hbalock);
|
2006-03-08 04:04:13 +08:00
|
|
|
psli->sli_flag &= ~LPFC_SLI2_ACTIVE;
|
2007-06-18 08:56:38 +08:00
|
|
|
spin_unlock_irq(&phba->hbalock);
|
2005-04-18 05:05:31 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Firmware stops when it triggled erratt with HS_FFER6.
|
|
|
|
* That could cause the I/Os dropped by the firmware.
|
|
|
|
* Error iocb (I/O) on txcmplq and let the SCSI layer
|
|
|
|
* retry it after re-establishing link.
|
|
|
|
*/
|
|
|
|
pring = &psli->ring[psli->fcp_ring];
|
|
|
|
lpfc_sli_abort_iocb_ring(phba, pring);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* There was a firmware error. Take the hba offline and then
|
|
|
|
* attempt to restart it.
|
|
|
|
*/
|
2007-04-25 21:51:45 +08:00
|
|
|
lpfc_offline_prep(phba);
|
2005-04-18 05:05:31 +08:00
|
|
|
lpfc_offline(phba);
|
2006-03-01 08:25:27 +08:00
|
|
|
lpfc_sli_brdrestart(phba);
|
2005-04-18 05:05:31 +08:00
|
|
|
if (lpfc_online(phba) == 0) { /* Initialize the HBA */
|
2007-04-25 21:51:45 +08:00
|
|
|
lpfc_unblock_mgmt_io(phba);
|
2005-04-18 05:05:31 +08:00
|
|
|
return;
|
|
|
|
}
|
2007-04-25 21:51:45 +08:00
|
|
|
lpfc_unblock_mgmt_io(phba);
|
2007-10-28 01:37:05 +08:00
|
|
|
} else if (phba->work_hs & HS_CRIT_TEMP) {
|
|
|
|
temperature = readl(phba->MBslimaddr + TEMPERATURE_OFFSET);
|
|
|
|
temp_event_data.event_type = FC_REG_TEMPERATURE_EVENT;
|
|
|
|
temp_event_data.event_code = LPFC_CRIT_TEMP;
|
|
|
|
temp_event_data.data = (uint32_t)temperature;
|
|
|
|
|
|
|
|
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
|
|
|
|
"0459 Adapter maximum temperature exceeded "
|
|
|
|
"(%ld), taking this port offline "
|
|
|
|
"Data: x%x x%x x%x\n",
|
|
|
|
temperature, phba->work_hs,
|
|
|
|
phba->work_status[0], phba->work_status[1]);
|
|
|
|
|
|
|
|
shost = lpfc_shost_from_vport(phba->pport);
|
|
|
|
fc_host_post_vendor_event(shost, fc_get_event_number(),
|
|
|
|
sizeof(temp_event_data),
|
|
|
|
(char *) &temp_event_data,
|
|
|
|
SCSI_NL_VID_TYPE_PCI
|
|
|
|
| PCI_VENDOR_ID_EMULEX);
|
|
|
|
|
2007-10-28 01:38:11 +08:00
|
|
|
spin_lock_irq(&phba->hbalock);
|
|
|
|
phba->over_temp_state = HBA_OVER_TEMP;
|
|
|
|
spin_unlock_irq(&phba->hbalock);
|
2008-01-11 14:52:54 +08:00
|
|
|
lpfc_offline_eratt(phba);
|
2007-10-28 01:37:05 +08:00
|
|
|
|
2005-04-18 05:05:31 +08:00
|
|
|
} else {
|
|
|
|
/* The if clause above forces this code path when the status
|
|
|
|
* failure is a value other than FFER6. Do not call the offline
|
|
|
|
* twice. This is the adapter hardware error path.
|
|
|
|
*/
|
|
|
|
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
|
2007-08-02 23:10:09 +08:00
|
|
|
"0457 Adapter Hardware Error "
|
2005-04-18 05:05:31 +08:00
|
|
|
"Data: x%x x%x x%x\n",
|
2007-08-02 23:10:09 +08:00
|
|
|
phba->work_hs,
|
2005-04-18 05:05:31 +08:00
|
|
|
phba->work_status[0], phba->work_status[1]);
|
|
|
|
|
2006-08-19 05:46:43 +08:00
|
|
|
event_data = FC_REG_DUMP_EVENT;
|
2007-06-18 08:56:38 +08:00
|
|
|
shost = lpfc_shost_from_vport(vport);
|
2007-06-18 08:56:36 +08:00
|
|
|
fc_host_post_vendor_event(shost, fc_get_event_number(),
|
2006-08-19 05:46:43 +08:00
|
|
|
sizeof(event_data), (char *) &event_data,
|
|
|
|
SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
|
|
|
|
|
2008-01-11 14:52:54 +08:00
|
|
|
lpfc_offline_eratt(phba);
|
2005-04-18 05:05:31 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_handle_latt: The HBA link event handler.
|
|
|
|
* @phba: pointer to lpfc hba data structure.
|
|
|
|
*
|
|
|
|
* This routine is invoked from the worker thread to handle a HBA host
|
|
|
|
* attention link event.
|
|
|
|
**/
|
2005-04-18 05:05:31 +08:00
|
|
|
void
|
2007-06-18 08:56:36 +08:00
|
|
|
lpfc_handle_latt(struct lpfc_hba *phba)
|
2005-04-18 05:05:31 +08:00
|
|
|
{
|
2007-06-18 08:56:36 +08:00
|
|
|
struct lpfc_vport *vport = phba->pport;
|
|
|
|
struct lpfc_sli *psli = &phba->sli;
|
2005-04-18 05:05:31 +08:00
|
|
|
LPFC_MBOXQ_t *pmb;
|
|
|
|
volatile uint32_t control;
|
|
|
|
struct lpfc_dmabuf *mp;
|
2008-01-11 14:52:54 +08:00
|
|
|
int rc = 0;
|
2005-04-18 05:05:31 +08:00
|
|
|
|
|
|
|
pmb = (LPFC_MBOXQ_t *)mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
|
2008-01-11 14:52:54 +08:00
|
|
|
if (!pmb) {
|
|
|
|
rc = 1;
|
2005-04-18 05:05:31 +08:00
|
|
|
goto lpfc_handle_latt_err_exit;
|
2008-01-11 14:52:54 +08:00
|
|
|
}
|
2005-04-18 05:05:31 +08:00
|
|
|
|
|
|
|
mp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
|
2008-01-11 14:52:54 +08:00
|
|
|
if (!mp) {
|
|
|
|
rc = 2;
|
2005-04-18 05:05:31 +08:00
|
|
|
goto lpfc_handle_latt_free_pmb;
|
2008-01-11 14:52:54 +08:00
|
|
|
}
|
2005-04-18 05:05:31 +08:00
|
|
|
|
|
|
|
mp->virt = lpfc_mbuf_alloc(phba, 0, &mp->phys);
|
2008-01-11 14:52:54 +08:00
|
|
|
if (!mp->virt) {
|
|
|
|
rc = 3;
|
2005-04-18 05:05:31 +08:00
|
|
|
goto lpfc_handle_latt_free_mp;
|
2008-01-11 14:52:54 +08:00
|
|
|
}
|
2005-04-18 05:05:31 +08:00
|
|
|
|
2005-11-29 00:41:33 +08:00
|
|
|
/* Cleanup any outstanding ELS commands */
|
2007-08-02 23:09:51 +08:00
|
|
|
lpfc_els_flush_all_cmd(phba);
|
2005-04-18 05:05:31 +08:00
|
|
|
|
|
|
|
psli->slistat.link_event++;
|
|
|
|
lpfc_read_la(phba, pmb, mp);
|
|
|
|
pmb->mbox_cmpl = lpfc_mbx_cmpl_read_la;
|
2007-06-18 08:56:36 +08:00
|
|
|
pmb->vport = vport;
|
2008-06-15 10:52:47 +08:00
|
|
|
/* Block ELS IOCBs until we have processed this mbox command */
|
|
|
|
phba->sli.ring[LPFC_ELS_RING].flag |= LPFC_STOP_IOCB_EVENT;
|
2007-10-28 01:37:25 +08:00
|
|
|
rc = lpfc_sli_issue_mbox (phba, pmb, MBX_NOWAIT);
|
2008-01-11 14:52:54 +08:00
|
|
|
if (rc == MBX_NOT_FINISHED) {
|
|
|
|
rc = 4;
|
2006-12-03 02:34:28 +08:00
|
|
|
goto lpfc_handle_latt_free_mbuf;
|
2008-01-11 14:52:54 +08:00
|
|
|
}
|
2005-04-18 05:05:31 +08:00
|
|
|
|
|
|
|
/* Clear Link Attention in HA REG */
|
2007-06-18 08:56:36 +08:00
|
|
|
spin_lock_irq(&phba->hbalock);
|
2005-04-18 05:05:31 +08:00
|
|
|
writel(HA_LATT, phba->HAregaddr);
|
|
|
|
readl(phba->HAregaddr); /* flush */
|
2007-06-18 08:56:36 +08:00
|
|
|
spin_unlock_irq(&phba->hbalock);
|
2005-04-18 05:05:31 +08:00
|
|
|
|
|
|
|
return;
|
|
|
|
|
2006-12-03 02:34:28 +08:00
|
|
|
lpfc_handle_latt_free_mbuf:
|
2008-06-15 10:52:47 +08:00
|
|
|
phba->sli.ring[LPFC_ELS_RING].flag &= ~LPFC_STOP_IOCB_EVENT;
|
2006-12-03 02:34:28 +08:00
|
|
|
lpfc_mbuf_free(phba, mp->virt, mp->phys);
|
2005-04-18 05:05:31 +08:00
|
|
|
lpfc_handle_latt_free_mp:
|
|
|
|
kfree(mp);
|
|
|
|
lpfc_handle_latt_free_pmb:
|
2007-04-25 21:51:30 +08:00
|
|
|
mempool_free(pmb, phba->mbox_mem_pool);
|
2005-04-18 05:05:31 +08:00
|
|
|
lpfc_handle_latt_err_exit:
|
|
|
|
/* Enable Link attention interrupts */
|
2007-06-18 08:56:36 +08:00
|
|
|
spin_lock_irq(&phba->hbalock);
|
2005-04-18 05:05:31 +08:00
|
|
|
psli->sli_flag |= LPFC_PROCESS_LA;
|
|
|
|
control = readl(phba->HCregaddr);
|
|
|
|
control |= HC_LAINT_ENA;
|
|
|
|
writel(control, phba->HCregaddr);
|
|
|
|
readl(phba->HCregaddr); /* flush */
|
|
|
|
|
|
|
|
/* Clear Link Attention in HA REG */
|
|
|
|
writel(HA_LATT, phba->HAregaddr);
|
|
|
|
readl(phba->HAregaddr); /* flush */
|
2007-06-18 08:56:36 +08:00
|
|
|
spin_unlock_irq(&phba->hbalock);
|
2005-04-18 05:05:31 +08:00
|
|
|
lpfc_linkdown(phba);
|
2007-06-18 08:56:36 +08:00
|
|
|
phba->link_state = LPFC_HBA_ERROR;
|
2005-04-18 05:05:31 +08:00
|
|
|
|
2008-01-11 14:52:54 +08:00
|
|
|
lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
|
|
|
|
"0300 LATT: Cannot issue READ_LA: Data:%d\n", rc);
|
2005-04-18 05:05:31 +08:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_parse_vpd: Parse VPD (Vital Product Data).
|
|
|
|
* @phba: pointer to lpfc hba data structure.
|
|
|
|
* @vpd: pointer to the vital product data.
|
|
|
|
* @len: length of the vital product data in bytes.
|
|
|
|
*
|
|
|
|
* This routine parses the Vital Product Data (VPD). The VPD is treated as
|
|
|
|
* an array of characters. In this routine, the ModelName, ProgramType, and
|
|
|
|
* ModelDesc, etc. fields of the phba data structure will be populated.
|
|
|
|
*
|
|
|
|
* Return codes
|
|
|
|
* 0 - pointer to the VPD passed in is NULL
|
|
|
|
* 1 - success
|
|
|
|
**/
|
2005-04-18 05:05:31 +08:00
|
|
|
static int
|
2007-06-18 08:56:36 +08:00
|
|
|
lpfc_parse_vpd(struct lpfc_hba *phba, uint8_t *vpd, int len)
|
2005-04-18 05:05:31 +08:00
|
|
|
{
|
|
|
|
uint8_t lenlo, lenhi;
|
2007-03-21 21:41:47 +08:00
|
|
|
int Length;
|
2005-04-18 05:05:31 +08:00
|
|
|
int i, j;
|
|
|
|
int finished = 0;
|
|
|
|
int index = 0;
|
|
|
|
|
|
|
|
if (!vpd)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Vital Product */
|
2007-06-18 08:56:37 +08:00
|
|
|
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
|
2007-08-02 23:10:09 +08:00
|
|
|
"0455 Vital Product Data: x%x x%x x%x x%x\n",
|
2005-04-18 05:05:31 +08:00
|
|
|
(uint32_t) vpd[0], (uint32_t) vpd[1], (uint32_t) vpd[2],
|
|
|
|
(uint32_t) vpd[3]);
|
2006-03-01 11:33:04 +08:00
|
|
|
while (!finished && (index < (len - 4))) {
|
2005-04-18 05:05:31 +08:00
|
|
|
switch (vpd[index]) {
|
|
|
|
case 0x82:
|
2006-03-01 11:33:04 +08:00
|
|
|
case 0x91:
|
2005-04-18 05:05:31 +08:00
|
|
|
index += 1;
|
|
|
|
lenlo = vpd[index];
|
|
|
|
index += 1;
|
|
|
|
lenhi = vpd[index];
|
|
|
|
index += 1;
|
|
|
|
i = ((((unsigned short)lenhi) << 8) + lenlo);
|
|
|
|
index += i;
|
|
|
|
break;
|
|
|
|
case 0x90:
|
|
|
|
index += 1;
|
|
|
|
lenlo = vpd[index];
|
|
|
|
index += 1;
|
|
|
|
lenhi = vpd[index];
|
|
|
|
index += 1;
|
|
|
|
Length = ((((unsigned short)lenhi) << 8) + lenlo);
|
2006-03-01 11:33:04 +08:00
|
|
|
if (Length > len - index)
|
|
|
|
Length = len - index;
|
2005-04-18 05:05:31 +08:00
|
|
|
while (Length > 0) {
|
|
|
|
/* Look for Serial Number */
|
|
|
|
if ((vpd[index] == 'S') && (vpd[index+1] == 'N')) {
|
|
|
|
index += 2;
|
|
|
|
i = vpd[index];
|
|
|
|
index += 1;
|
|
|
|
j = 0;
|
|
|
|
Length -= (3+i);
|
|
|
|
while(i--) {
|
|
|
|
phba->SerialNumber[j++] = vpd[index++];
|
|
|
|
if (j == 31)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
phba->SerialNumber[j] = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if ((vpd[index] == 'V') && (vpd[index+1] == '1')) {
|
|
|
|
phba->vpd_flag |= VPD_MODEL_DESC;
|
|
|
|
index += 2;
|
|
|
|
i = vpd[index];
|
|
|
|
index += 1;
|
|
|
|
j = 0;
|
|
|
|
Length -= (3+i);
|
|
|
|
while(i--) {
|
|
|
|
phba->ModelDesc[j++] = vpd[index++];
|
|
|
|
if (j == 255)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
phba->ModelDesc[j] = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if ((vpd[index] == 'V') && (vpd[index+1] == '2')) {
|
|
|
|
phba->vpd_flag |= VPD_MODEL_NAME;
|
|
|
|
index += 2;
|
|
|
|
i = vpd[index];
|
|
|
|
index += 1;
|
|
|
|
j = 0;
|
|
|
|
Length -= (3+i);
|
|
|
|
while(i--) {
|
|
|
|
phba->ModelName[j++] = vpd[index++];
|
|
|
|
if (j == 79)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
phba->ModelName[j] = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if ((vpd[index] == 'V') && (vpd[index+1] == '3')) {
|
|
|
|
phba->vpd_flag |= VPD_PROGRAM_TYPE;
|
|
|
|
index += 2;
|
|
|
|
i = vpd[index];
|
|
|
|
index += 1;
|
|
|
|
j = 0;
|
|
|
|
Length -= (3+i);
|
|
|
|
while(i--) {
|
|
|
|
phba->ProgramType[j++] = vpd[index++];
|
|
|
|
if (j == 255)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
phba->ProgramType[j] = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if ((vpd[index] == 'V') && (vpd[index+1] == '4')) {
|
|
|
|
phba->vpd_flag |= VPD_PORT;
|
|
|
|
index += 2;
|
|
|
|
i = vpd[index];
|
|
|
|
index += 1;
|
|
|
|
j = 0;
|
|
|
|
Length -= (3+i);
|
|
|
|
while(i--) {
|
|
|
|
phba->Port[j++] = vpd[index++];
|
|
|
|
if (j == 19)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
phba->Port[j] = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
index += 2;
|
|
|
|
i = vpd[index];
|
|
|
|
index += 1;
|
|
|
|
index += i;
|
|
|
|
Length -= (3 + i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
finished = 0;
|
|
|
|
break;
|
|
|
|
case 0x78:
|
|
|
|
finished = 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
index ++;
|
|
|
|
break;
|
|
|
|
}
|
2006-03-01 11:33:04 +08:00
|
|
|
}
|
2005-04-18 05:05:31 +08:00
|
|
|
|
|
|
|
return(1);
|
|
|
|
}
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_get_hba_model_desc: Retrieve HBA device model name and description.
|
|
|
|
* @phba: pointer to lpfc hba data structure.
|
|
|
|
* @mdp: pointer to the data structure to hold the derived model name.
|
|
|
|
* @descp: pointer to the data structure to hold the derived description.
|
|
|
|
*
|
|
|
|
* This routine retrieves HBA's description based on its registered PCI device
|
|
|
|
* ID. The @descp passed into this function points to an array of 256 chars. It
|
|
|
|
* shall be returned with the model name, maximum speed, and the host bus type.
|
|
|
|
* The @mdp passed into this function points to an array of 80 chars. When the
|
|
|
|
* function returns, the @mdp will be filled with the model name.
|
|
|
|
**/
|
2005-04-18 05:05:31 +08:00
|
|
|
static void
|
2007-06-18 08:56:36 +08:00
|
|
|
lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp)
|
2005-04-18 05:05:31 +08:00
|
|
|
{
|
|
|
|
lpfc_vpd_t *vp;
|
2005-11-29 04:08:56 +08:00
|
|
|
uint16_t dev_id = phba->pcidev->device;
|
2006-03-01 11:33:04 +08:00
|
|
|
int max_speed;
|
|
|
|
struct {
|
|
|
|
char * name;
|
|
|
|
int max_speed;
|
|
|
|
char * bus;
|
2006-12-03 02:35:08 +08:00
|
|
|
} m = {"<Unknown>", 0, ""};
|
2006-03-01 11:33:04 +08:00
|
|
|
|
|
|
|
if (mdp && mdp[0] != '\0'
|
|
|
|
&& descp && descp[0] != '\0')
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (phba->lmt & LMT_10Gb)
|
|
|
|
max_speed = 10;
|
|
|
|
else if (phba->lmt & LMT_8Gb)
|
|
|
|
max_speed = 8;
|
|
|
|
else if (phba->lmt & LMT_4Gb)
|
|
|
|
max_speed = 4;
|
|
|
|
else if (phba->lmt & LMT_2Gb)
|
|
|
|
max_speed = 2;
|
|
|
|
else
|
|
|
|
max_speed = 1;
|
2005-04-18 05:05:31 +08:00
|
|
|
|
|
|
|
vp = &phba->vpd;
|
|
|
|
|
2005-11-29 00:42:12 +08:00
|
|
|
switch (dev_id) {
|
2005-06-25 22:34:22 +08:00
|
|
|
case PCI_DEVICE_ID_FIREFLY:
|
2006-12-03 02:35:08 +08:00
|
|
|
m = (typeof(m)){"LP6000", max_speed, "PCI"};
|
2005-06-25 22:34:22 +08:00
|
|
|
break;
|
2005-04-18 05:05:31 +08:00
|
|
|
case PCI_DEVICE_ID_SUPERFLY:
|
|
|
|
if (vp->rev.biuRev >= 1 && vp->rev.biuRev <= 3)
|
2006-12-03 02:35:08 +08:00
|
|
|
m = (typeof(m)){"LP7000", max_speed, "PCI"};
|
2005-04-18 05:05:31 +08:00
|
|
|
else
|
2006-12-03 02:35:08 +08:00
|
|
|
m = (typeof(m)){"LP7000E", max_speed, "PCI"};
|
2005-04-18 05:05:31 +08:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_DRAGONFLY:
|
2006-12-03 02:35:08 +08:00
|
|
|
m = (typeof(m)){"LP8000", max_speed, "PCI"};
|
2005-04-18 05:05:31 +08:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_CENTAUR:
|
|
|
|
if (FC_JEDEC_ID(vp->rev.biuRev) == CENTAUR_2G_JEDEC_ID)
|
2006-12-03 02:35:08 +08:00
|
|
|
m = (typeof(m)){"LP9002", max_speed, "PCI"};
|
2005-04-18 05:05:31 +08:00
|
|
|
else
|
2006-12-03 02:35:08 +08:00
|
|
|
m = (typeof(m)){"LP9000", max_speed, "PCI"};
|
2005-04-18 05:05:31 +08:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_RFLY:
|
2006-12-03 02:35:08 +08:00
|
|
|
m = (typeof(m)){"LP952", max_speed, "PCI"};
|
2005-04-18 05:05:31 +08:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_PEGASUS:
|
2006-12-03 02:35:08 +08:00
|
|
|
m = (typeof(m)){"LP9802", max_speed, "PCI-X"};
|
2005-04-18 05:05:31 +08:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_THOR:
|
2006-12-03 02:35:08 +08:00
|
|
|
m = (typeof(m)){"LP10000", max_speed, "PCI-X"};
|
2005-04-18 05:05:31 +08:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_VIPER:
|
2006-12-03 02:35:08 +08:00
|
|
|
m = (typeof(m)){"LPX1000", max_speed, "PCI-X"};
|
2005-04-18 05:05:31 +08:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_PFLY:
|
2006-12-03 02:35:08 +08:00
|
|
|
m = (typeof(m)){"LP982", max_speed, "PCI-X"};
|
2005-04-18 05:05:31 +08:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_TFLY:
|
2006-12-03 02:35:08 +08:00
|
|
|
m = (typeof(m)){"LP1050", max_speed, "PCI-X"};
|
2005-04-18 05:05:31 +08:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_HELIOS:
|
2006-12-03 02:35:08 +08:00
|
|
|
m = (typeof(m)){"LP11000", max_speed, "PCI-X2"};
|
2005-04-18 05:05:31 +08:00
|
|
|
break;
|
2005-11-29 00:42:12 +08:00
|
|
|
case PCI_DEVICE_ID_HELIOS_SCSP:
|
2006-12-03 02:35:08 +08:00
|
|
|
m = (typeof(m)){"LP11000-SP", max_speed, "PCI-X2"};
|
2005-11-29 00:42:12 +08:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_HELIOS_DCSP:
|
2006-12-03 02:35:08 +08:00
|
|
|
m = (typeof(m)){"LP11002-SP", max_speed, "PCI-X2"};
|
2005-11-29 00:42:12 +08:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_NEPTUNE:
|
2006-12-03 02:35:08 +08:00
|
|
|
m = (typeof(m)){"LPe1000", max_speed, "PCIe"};
|
2005-11-29 00:42:12 +08:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_NEPTUNE_SCSP:
|
2006-12-03 02:35:08 +08:00
|
|
|
m = (typeof(m)){"LPe1000-SP", max_speed, "PCIe"};
|
2005-11-29 00:42:12 +08:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_NEPTUNE_DCSP:
|
2006-12-03 02:35:08 +08:00
|
|
|
m = (typeof(m)){"LPe1002-SP", max_speed, "PCIe"};
|
2005-11-29 00:42:12 +08:00
|
|
|
break;
|
2005-04-18 05:05:31 +08:00
|
|
|
case PCI_DEVICE_ID_BMID:
|
2006-12-03 02:35:08 +08:00
|
|
|
m = (typeof(m)){"LP1150", max_speed, "PCI-X2"};
|
2005-04-18 05:05:31 +08:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_BSMB:
|
2006-12-03 02:35:08 +08:00
|
|
|
m = (typeof(m)){"LP111", max_speed, "PCI-X2"};
|
2005-04-18 05:05:31 +08:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_ZEPHYR:
|
2006-12-03 02:35:08 +08:00
|
|
|
m = (typeof(m)){"LPe11000", max_speed, "PCIe"};
|
2005-04-18 05:05:31 +08:00
|
|
|
break;
|
2005-11-29 00:42:12 +08:00
|
|
|
case PCI_DEVICE_ID_ZEPHYR_SCSP:
|
2006-12-03 02:35:08 +08:00
|
|
|
m = (typeof(m)){"LPe11000", max_speed, "PCIe"};
|
2005-11-29 00:42:12 +08:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_ZEPHYR_DCSP:
|
2006-12-03 02:35:08 +08:00
|
|
|
m = (typeof(m)){"LPe11002-SP", max_speed, "PCIe"};
|
2005-11-29 00:42:12 +08:00
|
|
|
break;
|
2005-04-18 05:05:31 +08:00
|
|
|
case PCI_DEVICE_ID_ZMID:
|
2006-12-03 02:35:08 +08:00
|
|
|
m = (typeof(m)){"LPe1150", max_speed, "PCIe"};
|
2005-04-18 05:05:31 +08:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_ZSMB:
|
2006-12-03 02:35:08 +08:00
|
|
|
m = (typeof(m)){"LPe111", max_speed, "PCIe"};
|
2005-04-18 05:05:31 +08:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_LP101:
|
2006-12-03 02:35:08 +08:00
|
|
|
m = (typeof(m)){"LP101", max_speed, "PCI-X"};
|
2005-04-18 05:05:31 +08:00
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_LP10000S:
|
2006-12-03 02:35:08 +08:00
|
|
|
m = (typeof(m)){"LP10000-S", max_speed, "PCI"};
|
2005-06-25 22:34:22 +08:00
|
|
|
break;
|
2005-11-29 00:42:12 +08:00
|
|
|
case PCI_DEVICE_ID_LP11000S:
|
2006-12-03 02:35:08 +08:00
|
|
|
m = (typeof(m)){"LP11000-S", max_speed,
|
|
|
|
"PCI-X2"};
|
|
|
|
break;
|
2005-11-29 00:42:12 +08:00
|
|
|
case PCI_DEVICE_ID_LPE11000S:
|
2006-12-03 02:35:08 +08:00
|
|
|
m = (typeof(m)){"LPe11000-S", max_speed,
|
|
|
|
"PCIe"};
|
2005-11-29 00:42:19 +08:00
|
|
|
break;
|
2007-04-25 21:53:28 +08:00
|
|
|
case PCI_DEVICE_ID_SAT:
|
|
|
|
m = (typeof(m)){"LPe12000", max_speed, "PCIe"};
|
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_SAT_MID:
|
|
|
|
m = (typeof(m)){"LPe1250", max_speed, "PCIe"};
|
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_SAT_SMB:
|
|
|
|
m = (typeof(m)){"LPe121", max_speed, "PCIe"};
|
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_SAT_DCSP:
|
|
|
|
m = (typeof(m)){"LPe12002-SP", max_speed, "PCIe"};
|
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_SAT_SCSP:
|
|
|
|
m = (typeof(m)){"LPe12000-SP", max_speed, "PCIe"};
|
|
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_SAT_S:
|
|
|
|
m = (typeof(m)){"LPe12000-S", max_speed, "PCIe"};
|
|
|
|
break;
|
2005-11-29 00:42:19 +08:00
|
|
|
default:
|
2006-06-25 16:58:51 +08:00
|
|
|
m = (typeof(m)){ NULL };
|
2005-11-29 00:42:12 +08:00
|
|
|
break;
|
2005-04-18 05:05:31 +08:00
|
|
|
}
|
2006-03-01 11:33:04 +08:00
|
|
|
|
|
|
|
if (mdp && mdp[0] == '\0')
|
|
|
|
snprintf(mdp, 79,"%s", m.name);
|
|
|
|
if (descp && descp[0] == '\0')
|
|
|
|
snprintf(descp, 255,
|
2006-12-03 02:35:08 +08:00
|
|
|
"Emulex %s %dGb %s Fibre Channel Adapter",
|
|
|
|
m.name, m.max_speed, m.bus);
|
2005-04-18 05:05:31 +08:00
|
|
|
}
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_post_buffer: Post IOCB(s) with DMA buffer descriptor(s) to a IOCB ring.
|
|
|
|
* @phba: pointer to lpfc hba data structure.
|
|
|
|
* @pring: pointer to a IOCB ring.
|
|
|
|
* @cnt: the number of IOCBs to be posted to the IOCB ring.
|
|
|
|
*
|
|
|
|
* This routine posts a given number of IOCBs with the associated DMA buffer
|
|
|
|
* descriptors specified by the cnt argument to the given IOCB ring.
|
|
|
|
*
|
|
|
|
* Return codes
|
|
|
|
* The number of IOCBs NOT able to be posted to the IOCB ring.
|
|
|
|
**/
|
2005-04-18 05:05:31 +08:00
|
|
|
int
|
2008-06-15 10:52:59 +08:00
|
|
|
lpfc_post_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, int cnt)
|
2005-04-18 05:05:31 +08:00
|
|
|
{
|
|
|
|
IOCB_t *icmd;
|
2005-10-29 08:30:02 +08:00
|
|
|
struct lpfc_iocbq *iocb;
|
2005-04-18 05:05:31 +08:00
|
|
|
struct lpfc_dmabuf *mp1, *mp2;
|
|
|
|
|
|
|
|
cnt += pring->missbufcnt;
|
|
|
|
|
|
|
|
/* While there are buffers to post */
|
|
|
|
while (cnt > 0) {
|
|
|
|
/* Allocate buffer for command iocb */
|
2005-10-29 08:30:02 +08:00
|
|
|
iocb = lpfc_sli_get_iocbq(phba);
|
2005-04-18 05:05:31 +08:00
|
|
|
if (iocb == NULL) {
|
|
|
|
pring->missbufcnt = cnt;
|
|
|
|
return cnt;
|
|
|
|
}
|
|
|
|
icmd = &iocb->iocb;
|
|
|
|
|
|
|
|
/* 2 buffers can be posted per command */
|
|
|
|
/* Allocate buffer to post */
|
|
|
|
mp1 = kmalloc(sizeof (struct lpfc_dmabuf), GFP_KERNEL);
|
|
|
|
if (mp1)
|
2007-10-28 01:37:33 +08:00
|
|
|
mp1->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &mp1->phys);
|
|
|
|
if (!mp1 || !mp1->virt) {
|
2005-11-07 17:01:26 +08:00
|
|
|
kfree(mp1);
|
2005-10-29 23:28:33 +08:00
|
|
|
lpfc_sli_release_iocbq(phba, iocb);
|
2005-04-18 05:05:31 +08:00
|
|
|
pring->missbufcnt = cnt;
|
|
|
|
return cnt;
|
|
|
|
}
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&mp1->list);
|
|
|
|
/* Allocate buffer to post */
|
|
|
|
if (cnt > 1) {
|
|
|
|
mp2 = kmalloc(sizeof (struct lpfc_dmabuf), GFP_KERNEL);
|
|
|
|
if (mp2)
|
|
|
|
mp2->virt = lpfc_mbuf_alloc(phba, MEM_PRI,
|
|
|
|
&mp2->phys);
|
2007-10-28 01:37:33 +08:00
|
|
|
if (!mp2 || !mp2->virt) {
|
2005-11-07 17:01:26 +08:00
|
|
|
kfree(mp2);
|
2005-04-18 05:05:31 +08:00
|
|
|
lpfc_mbuf_free(phba, mp1->virt, mp1->phys);
|
|
|
|
kfree(mp1);
|
2005-10-29 23:28:33 +08:00
|
|
|
lpfc_sli_release_iocbq(phba, iocb);
|
2005-04-18 05:05:31 +08:00
|
|
|
pring->missbufcnt = cnt;
|
|
|
|
return cnt;
|
|
|
|
}
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&mp2->list);
|
|
|
|
} else {
|
|
|
|
mp2 = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
icmd->un.cont64[0].addrHigh = putPaddrHigh(mp1->phys);
|
|
|
|
icmd->un.cont64[0].addrLow = putPaddrLow(mp1->phys);
|
|
|
|
icmd->un.cont64[0].tus.f.bdeSize = FCELSSIZE;
|
|
|
|
icmd->ulpBdeCount = 1;
|
|
|
|
cnt--;
|
|
|
|
if (mp2) {
|
|
|
|
icmd->un.cont64[1].addrHigh = putPaddrHigh(mp2->phys);
|
|
|
|
icmd->un.cont64[1].addrLow = putPaddrLow(mp2->phys);
|
|
|
|
icmd->un.cont64[1].tus.f.bdeSize = FCELSSIZE;
|
|
|
|
cnt--;
|
|
|
|
icmd->ulpBdeCount = 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
icmd->ulpCommand = CMD_QUE_RING_BUF64_CN;
|
|
|
|
icmd->ulpLe = 1;
|
|
|
|
|
|
|
|
if (lpfc_sli_issue_iocb(phba, pring, iocb, 0) == IOCB_ERROR) {
|
|
|
|
lpfc_mbuf_free(phba, mp1->virt, mp1->phys);
|
|
|
|
kfree(mp1);
|
|
|
|
cnt++;
|
|
|
|
if (mp2) {
|
|
|
|
lpfc_mbuf_free(phba, mp2->virt, mp2->phys);
|
|
|
|
kfree(mp2);
|
|
|
|
cnt++;
|
|
|
|
}
|
2005-10-29 23:28:33 +08:00
|
|
|
lpfc_sli_release_iocbq(phba, iocb);
|
2005-04-18 05:05:31 +08:00
|
|
|
pring->missbufcnt = cnt;
|
|
|
|
return cnt;
|
|
|
|
}
|
|
|
|
lpfc_sli_ringpostbuf_put(phba, pring, mp1);
|
2007-06-18 08:56:38 +08:00
|
|
|
if (mp2)
|
2005-04-18 05:05:31 +08:00
|
|
|
lpfc_sli_ringpostbuf_put(phba, pring, mp2);
|
|
|
|
}
|
|
|
|
pring->missbufcnt = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_post_rcv_buf: Post the initial receive IOCB buffers to ELS ring.
|
|
|
|
* @phba: pointer to lpfc hba data structure.
|
|
|
|
*
|
|
|
|
* This routine posts initial receive IOCB buffers to the ELS ring. The
|
|
|
|
* current number of initial IOCB buffers specified by LPFC_BUF_RING0 is
|
|
|
|
* set to 64 IOCBs.
|
|
|
|
*
|
|
|
|
* Return codes
|
|
|
|
* 0 - success (currently always success)
|
|
|
|
**/
|
2005-04-18 05:05:31 +08:00
|
|
|
static int
|
2007-06-18 08:56:36 +08:00
|
|
|
lpfc_post_rcv_buf(struct lpfc_hba *phba)
|
2005-04-18 05:05:31 +08:00
|
|
|
{
|
|
|
|
struct lpfc_sli *psli = &phba->sli;
|
|
|
|
|
|
|
|
/* Ring 0, ELS / CT buffers */
|
2008-06-15 10:52:59 +08:00
|
|
|
lpfc_post_buffer(phba, &psli->ring[LPFC_ELS_RING], LPFC_BUF_RING0);
|
2005-04-18 05:05:31 +08:00
|
|
|
/* Ring 2 - FCP no buffers needed */
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define S(N,V) (((V)<<(N))|((V)>>(32-(N))))
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_sha_init: Set up initial array of hash table entries.
|
|
|
|
* @HashResultPointer: pointer to an array as hash table.
|
|
|
|
*
|
|
|
|
* This routine sets up the initial values to the array of hash table entries
|
|
|
|
* for the LC HBAs.
|
|
|
|
**/
|
2005-04-18 05:05:31 +08:00
|
|
|
static void
|
|
|
|
lpfc_sha_init(uint32_t * HashResultPointer)
|
|
|
|
{
|
|
|
|
HashResultPointer[0] = 0x67452301;
|
|
|
|
HashResultPointer[1] = 0xEFCDAB89;
|
|
|
|
HashResultPointer[2] = 0x98BADCFE;
|
|
|
|
HashResultPointer[3] = 0x10325476;
|
|
|
|
HashResultPointer[4] = 0xC3D2E1F0;
|
|
|
|
}
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_sha_iterate: Iterate initial hash table with the working hash table.
|
|
|
|
* @HashResultPointer: pointer to an initial/result hash table.
|
|
|
|
* @HashWorkingPointer: pointer to an working hash table.
|
|
|
|
*
|
|
|
|
* This routine iterates an initial hash table pointed by @HashResultPointer
|
|
|
|
* with the values from the working hash table pointeed by @HashWorkingPointer.
|
|
|
|
* The results are putting back to the initial hash table, returned through
|
|
|
|
* the @HashResultPointer as the result hash table.
|
|
|
|
**/
|
2005-04-18 05:05:31 +08:00
|
|
|
static void
|
|
|
|
lpfc_sha_iterate(uint32_t * HashResultPointer, uint32_t * HashWorkingPointer)
|
|
|
|
{
|
|
|
|
int t;
|
|
|
|
uint32_t TEMP;
|
|
|
|
uint32_t A, B, C, D, E;
|
|
|
|
t = 16;
|
|
|
|
do {
|
|
|
|
HashWorkingPointer[t] =
|
|
|
|
S(1,
|
|
|
|
HashWorkingPointer[t - 3] ^ HashWorkingPointer[t -
|
|
|
|
8] ^
|
|
|
|
HashWorkingPointer[t - 14] ^ HashWorkingPointer[t - 16]);
|
|
|
|
} while (++t <= 79);
|
|
|
|
t = 0;
|
|
|
|
A = HashResultPointer[0];
|
|
|
|
B = HashResultPointer[1];
|
|
|
|
C = HashResultPointer[2];
|
|
|
|
D = HashResultPointer[3];
|
|
|
|
E = HashResultPointer[4];
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (t < 20) {
|
|
|
|
TEMP = ((B & C) | ((~B) & D)) + 0x5A827999;
|
|
|
|
} else if (t < 40) {
|
|
|
|
TEMP = (B ^ C ^ D) + 0x6ED9EBA1;
|
|
|
|
} else if (t < 60) {
|
|
|
|
TEMP = ((B & C) | (B & D) | (C & D)) + 0x8F1BBCDC;
|
|
|
|
} else {
|
|
|
|
TEMP = (B ^ C ^ D) + 0xCA62C1D6;
|
|
|
|
}
|
|
|
|
TEMP += S(5, A) + E + HashWorkingPointer[t];
|
|
|
|
E = D;
|
|
|
|
D = C;
|
|
|
|
C = S(30, B);
|
|
|
|
B = A;
|
|
|
|
A = TEMP;
|
|
|
|
} while (++t <= 79);
|
|
|
|
|
|
|
|
HashResultPointer[0] += A;
|
|
|
|
HashResultPointer[1] += B;
|
|
|
|
HashResultPointer[2] += C;
|
|
|
|
HashResultPointer[3] += D;
|
|
|
|
HashResultPointer[4] += E;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_challenge_key: Create challenge key based on WWPN of the HBA.
|
|
|
|
* @RandomChallenge: pointer to the entry of host challenge random number array.
|
|
|
|
* @HashWorking: pointer to the entry of the working hash array.
|
|
|
|
*
|
|
|
|
* This routine calculates the working hash array referred by @HashWorking
|
|
|
|
* from the challenge random numbers associated with the host, referred by
|
|
|
|
* @RandomChallenge. The result is put into the entry of the working hash
|
|
|
|
* array and returned by reference through @HashWorking.
|
|
|
|
**/
|
2005-04-18 05:05:31 +08:00
|
|
|
static void
|
|
|
|
lpfc_challenge_key(uint32_t * RandomChallenge, uint32_t * HashWorking)
|
|
|
|
{
|
|
|
|
*HashWorking = (*RandomChallenge ^ *HashWorking);
|
|
|
|
}
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_hba_init: Perform special handling for LC HBA initialization.
|
|
|
|
* @phba: pointer to lpfc hba data structure.
|
|
|
|
* @hbainit: pointer to an array of unsigned 32-bit integers.
|
|
|
|
*
|
|
|
|
* This routine performs the special handling for LC HBA initialization.
|
|
|
|
**/
|
2005-04-18 05:05:31 +08:00
|
|
|
void
|
|
|
|
lpfc_hba_init(struct lpfc_hba *phba, uint32_t *hbainit)
|
|
|
|
{
|
|
|
|
int t;
|
|
|
|
uint32_t *HashWorking;
|
2007-06-18 08:56:36 +08:00
|
|
|
uint32_t *pwwnn = (uint32_t *) phba->wwnn;
|
2005-04-18 05:05:31 +08:00
|
|
|
|
[SCSI] kmalloc + memset conversion to kzalloc
In NCR_D700, a4000t, aic7xxx_old, bvme6000, dpt_i2o, gdth, lpfc,
megaraid, mvme16x osst, pluto, qla2xxx, zorro7xx
Signed-off-by: Mariusz Kozlowski <m.kozlowski@tuxland.pl>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
2007-08-11 16:13:24 +08:00
|
|
|
HashWorking = kcalloc(80, sizeof(uint32_t), GFP_KERNEL);
|
2005-04-18 05:05:31 +08:00
|
|
|
if (!HashWorking)
|
|
|
|
return;
|
|
|
|
|
|
|
|
HashWorking[0] = HashWorking[78] = *pwwnn++;
|
|
|
|
HashWorking[1] = HashWorking[79] = *pwwnn;
|
|
|
|
|
|
|
|
for (t = 0; t < 7; t++)
|
|
|
|
lpfc_challenge_key(phba->RandomData + t, HashWorking + t);
|
|
|
|
|
|
|
|
lpfc_sha_init(hbainit);
|
|
|
|
lpfc_sha_iterate(hbainit, HashWorking);
|
|
|
|
kfree(HashWorking);
|
|
|
|
}
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_cleanup: Performs vport cleanups before deleting a vport.
|
|
|
|
* @vport: pointer to a virtual N_Port data structure.
|
|
|
|
*
|
|
|
|
* This routine performs the necessary cleanups before deleting the @vport.
|
|
|
|
* It invokes the discovery state machine to perform necessary state
|
|
|
|
* transitions and to release the ndlps associated with the @vport. Note,
|
|
|
|
* the physical port is treated as @vport 0.
|
|
|
|
**/
|
2007-10-28 01:37:43 +08:00
|
|
|
void
|
2007-06-18 08:56:36 +08:00
|
|
|
lpfc_cleanup(struct lpfc_vport *vport)
|
2005-04-18 05:05:31 +08:00
|
|
|
{
|
2007-10-28 01:37:43 +08:00
|
|
|
struct lpfc_hba *phba = vport->phba;
|
2005-04-18 05:05:31 +08:00
|
|
|
struct lpfc_nodelist *ndlp, *next_ndlp;
|
2007-10-28 01:37:53 +08:00
|
|
|
int i = 0;
|
2005-04-18 05:05:31 +08:00
|
|
|
|
2007-10-28 01:37:43 +08:00
|
|
|
if (phba->link_state > LPFC_LINK_DOWN)
|
|
|
|
lpfc_port_link_failure(vport);
|
|
|
|
|
|
|
|
list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) {
|
2008-02-09 07:49:26 +08:00
|
|
|
if (!NLP_CHK_NODE_ACT(ndlp)) {
|
|
|
|
ndlp = lpfc_enable_node(vport, ndlp,
|
|
|
|
NLP_STE_UNUSED_NODE);
|
|
|
|
if (!ndlp)
|
|
|
|
continue;
|
|
|
|
spin_lock_irq(&phba->ndlp_lock);
|
|
|
|
NLP_SET_FREE_REQ(ndlp);
|
|
|
|
spin_unlock_irq(&phba->ndlp_lock);
|
|
|
|
/* Trigger the release of the ndlp memory */
|
|
|
|
lpfc_nlp_put(ndlp);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
spin_lock_irq(&phba->ndlp_lock);
|
|
|
|
if (NLP_CHK_FREE_REQ(ndlp)) {
|
|
|
|
/* The ndlp should not be in memory free mode already */
|
|
|
|
spin_unlock_irq(&phba->ndlp_lock);
|
|
|
|
continue;
|
|
|
|
} else
|
|
|
|
/* Indicate request for freeing ndlp memory */
|
|
|
|
NLP_SET_FREE_REQ(ndlp);
|
|
|
|
spin_unlock_irq(&phba->ndlp_lock);
|
|
|
|
|
2008-04-07 22:15:56 +08:00
|
|
|
if (vport->port_type != LPFC_PHYSICAL_PORT &&
|
|
|
|
ndlp->nlp_DID == Fabric_DID) {
|
|
|
|
/* Just free up ndlp with Fabric_DID for vports */
|
|
|
|
lpfc_nlp_put(ndlp);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2007-10-28 01:37:43 +08:00
|
|
|
if (ndlp->nlp_type & NLP_FABRIC)
|
|
|
|
lpfc_disc_state_machine(vport, ndlp, NULL,
|
|
|
|
NLP_EVT_DEVICE_RECOVERY);
|
2008-02-09 07:49:26 +08:00
|
|
|
|
2007-10-28 01:37:43 +08:00
|
|
|
lpfc_disc_state_machine(vport, ndlp, NULL,
|
|
|
|
NLP_EVT_DEVICE_RM);
|
2008-06-15 10:52:59 +08:00
|
|
|
|
2007-10-28 01:37:43 +08:00
|
|
|
}
|
|
|
|
|
2007-10-28 01:37:53 +08:00
|
|
|
/* At this point, ALL ndlp's should be gone
|
|
|
|
* because of the previous NLP_EVT_DEVICE_RM.
|
|
|
|
* Lets wait for this to happen, if needed.
|
|
|
|
*/
|
2007-10-28 01:37:43 +08:00
|
|
|
while (!list_empty(&vport->fc_nodes)) {
|
|
|
|
|
2007-10-28 01:37:53 +08:00
|
|
|
if (i++ > 3000) {
|
2007-10-28 01:37:43 +08:00
|
|
|
lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,
|
2007-10-28 01:37:53 +08:00
|
|
|
"0233 Nodelist not empty\n");
|
2008-02-09 07:49:26 +08:00
|
|
|
list_for_each_entry_safe(ndlp, next_ndlp,
|
|
|
|
&vport->fc_nodes, nlp_listp) {
|
|
|
|
lpfc_printf_vlog(ndlp->vport, KERN_ERR,
|
|
|
|
LOG_NODE,
|
|
|
|
"0282: did:x%x ndlp:x%p "
|
|
|
|
"usgmap:x%x refcnt:%d\n",
|
|
|
|
ndlp->nlp_DID, (void *)ndlp,
|
|
|
|
ndlp->nlp_usg_map,
|
|
|
|
atomic_read(
|
|
|
|
&ndlp->kref.refcount));
|
|
|
|
}
|
2007-10-28 01:37:53 +08:00
|
|
|
break;
|
2007-10-28 01:37:43 +08:00
|
|
|
}
|
2007-10-28 01:37:53 +08:00
|
|
|
|
|
|
|
/* Wait for any activity on ndlps to settle */
|
|
|
|
msleep(10);
|
2007-10-28 01:37:43 +08:00
|
|
|
}
|
2005-04-18 05:05:31 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_stop_vport_timers: Stop all the timers associated with a vport.
|
|
|
|
* @vport: pointer to a virtual N_Port data structure.
|
|
|
|
*
|
|
|
|
* This routine stops all the timers associated with a @vport. This function
|
|
|
|
* is invoked before disabling or deleting a @vport. Note that the physical
|
|
|
|
* port is treated as @vport 0.
|
|
|
|
**/
|
2007-06-18 08:56:38 +08:00
|
|
|
void
|
|
|
|
lpfc_stop_vport_timers(struct lpfc_vport *vport)
|
2005-04-18 05:05:31 +08:00
|
|
|
{
|
2007-06-18 08:56:38 +08:00
|
|
|
del_timer_sync(&vport->els_tmofunc);
|
|
|
|
del_timer_sync(&vport->fc_fdmitmo);
|
|
|
|
lpfc_can_disctmo(vport);
|
|
|
|
return;
|
2005-04-18 05:05:31 +08:00
|
|
|
}
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_stop_phba_timers: Stop all the timers associated with an HBA.
|
|
|
|
* @phba: pointer to lpfc hba data structure.
|
|
|
|
*
|
|
|
|
* This routine stops all the timers associated with a HBA. This function is
|
|
|
|
* invoked before either putting a HBA offline or unloading the driver.
|
|
|
|
**/
|
2007-06-18 08:56:36 +08:00
|
|
|
static void
|
2007-06-18 08:56:38 +08:00
|
|
|
lpfc_stop_phba_timers(struct lpfc_hba *phba)
|
2005-04-18 05:05:31 +08:00
|
|
|
{
|
[SCSI] lpfc 8.1.1 : Add polled-mode support
- Add functionality to run in polled mode only. Includes run time
attribute to enable mode.
- Enable runtime writable hba settings for coallescing and delay parameters
Customers have requested a mode in the driver to run strictly polled.
This is generally to support an environment where the server is extremely
loaded and is looking to reclaim some cpu cycles from adapter interrupt
handling.
This patch adds a new "poll" attribute, and the following behavior:
if value is 0 (default):
The driver uses the normal method for i/o completion. It uses the
firmware feature of interrupt coalesing. The firmware allows a
minimum number of i/o completions before an interrupt, or a maximum
time delay between interrupts. By default, the driver sets these
to no delay (disabled) or 1 i/o - meaning coalescing is disabled.
Attributes were provided to change the coalescing values, but it was
a module-load time only and global across all adapters.
This patch allows them to be writable on a per-adapter basis.
if value is 1 :
Interrupts are left enabled, expecting that the user has tuned the
interrupt coalescing values. When this setting is enabled, the driver
will attempt to service completed i/o whenever new i/o is submitted
to the adapter. If the coalescing values are large, and the i/o
generation rate steady, an interrupt will be avoided by servicing
completed i/o prior to the coalescing thresholds kicking in. However,
if the i/o completion load is high enough or i/o generation slow, the
coalescion values will ensure that completed i/o is serviced in a timely
fashion.
if value is 3 :
Turns off FCP i/o interrupts altogether. The coalescing values now have
no effect. A new attribute "poll_tmo" (default 10ms) exists to set
the polling interval for i/o completion. When this setting is enabled,
the driver will attempt to service completed i/o and restart the
interval timer whenever new i/o is submitted. This behavior allows for
servicing of completed i/o sooner than the interval timer, but ensures
that if no i/o is being issued, then the interval timer will kick in
to service the outstanding i/o.
Signed-off-by: James Smart <James.Smart@emulex.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
2005-11-30 05:32:13 +08:00
|
|
|
del_timer_sync(&phba->fcp_poll_timer);
|
2007-08-02 23:10:31 +08:00
|
|
|
lpfc_stop_vport_timers(phba->pport);
|
2007-06-18 08:56:36 +08:00
|
|
|
del_timer_sync(&phba->sli.mbox_tmo);
|
2007-06-18 08:56:38 +08:00
|
|
|
del_timer_sync(&phba->fabric_block_timer);
|
2007-06-18 08:56:39 +08:00
|
|
|
phba->hb_outstanding = 0;
|
|
|
|
del_timer_sync(&phba->hb_tmofunc);
|
2007-06-18 08:56:36 +08:00
|
|
|
return;
|
2005-04-18 05:05:31 +08:00
|
|
|
}
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_block_mgmt_io: Mark a HBA's management interface as blocked.
|
|
|
|
* @phba: pointer to lpfc hba data structure.
|
|
|
|
*
|
|
|
|
* This routine marks a HBA's management interface as blocked. Once the HBA's
|
|
|
|
* management interface is marked as blocked, all the user space access to
|
|
|
|
* the HBA, whether they are from sysfs interface or libdfc interface will
|
|
|
|
* all be blocked. The HBA is set to block the management interface when the
|
|
|
|
* driver prepares the HBA interface for online or offline.
|
|
|
|
**/
|
2007-11-06 01:07:33 +08:00
|
|
|
static void
|
|
|
|
lpfc_block_mgmt_io(struct lpfc_hba * phba)
|
|
|
|
{
|
|
|
|
unsigned long iflag;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&phba->hbalock, iflag);
|
|
|
|
phba->sli.sli_flag |= LPFC_BLOCK_MGMT_IO;
|
|
|
|
spin_unlock_irqrestore(&phba->hbalock, iflag);
|
|
|
|
}
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_online: Initialize and bring a HBA online.
|
|
|
|
* @phba: pointer to lpfc hba data structure.
|
|
|
|
*
|
|
|
|
* This routine initializes the HBA and brings a HBA online. During this
|
|
|
|
* process, the management interface is blocked to prevent user space access
|
|
|
|
* to the HBA interfering with the driver initialization.
|
|
|
|
*
|
|
|
|
* Return codes
|
|
|
|
* 0 - successful
|
|
|
|
* 1 - failed
|
|
|
|
**/
|
2005-04-18 05:05:31 +08:00
|
|
|
int
|
2007-06-18 08:56:36 +08:00
|
|
|
lpfc_online(struct lpfc_hba *phba)
|
2005-04-18 05:05:31 +08:00
|
|
|
{
|
2007-06-18 08:56:36 +08:00
|
|
|
struct lpfc_vport *vport = phba->pport;
|
2007-08-02 23:09:51 +08:00
|
|
|
struct lpfc_vport **vports;
|
|
|
|
int i;
|
2007-06-18 08:56:36 +08:00
|
|
|
|
2005-04-18 05:05:31 +08:00
|
|
|
if (!phba)
|
|
|
|
return 0;
|
|
|
|
|
2007-06-18 08:56:36 +08:00
|
|
|
if (!(vport->fc_flag & FC_OFFLINE_MODE))
|
2005-04-18 05:05:31 +08:00
|
|
|
return 0;
|
|
|
|
|
2007-06-18 08:56:37 +08:00
|
|
|
lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
|
2007-08-02 23:10:09 +08:00
|
|
|
"0458 Bring Adapter online\n");
|
2005-04-18 05:05:31 +08:00
|
|
|
|
2007-04-25 21:51:45 +08:00
|
|
|
lpfc_block_mgmt_io(phba);
|
|
|
|
|
|
|
|
if (!lpfc_sli_queue_setup(phba)) {
|
|
|
|
lpfc_unblock_mgmt_io(phba);
|
2005-04-18 05:05:31 +08:00
|
|
|
return 1;
|
2007-04-25 21:51:45 +08:00
|
|
|
}
|
2005-04-18 05:05:31 +08:00
|
|
|
|
2007-04-25 21:51:45 +08:00
|
|
|
if (lpfc_sli_hba_setup(phba)) { /* Initialize the HBA */
|
|
|
|
lpfc_unblock_mgmt_io(phba);
|
2005-04-18 05:05:31 +08:00
|
|
|
return 1;
|
2007-04-25 21:51:45 +08:00
|
|
|
}
|
2005-04-18 05:05:31 +08:00
|
|
|
|
2007-08-02 23:09:51 +08:00
|
|
|
vports = lpfc_create_vport_work_array(phba);
|
|
|
|
if (vports != NULL)
|
2008-01-11 14:52:54 +08:00
|
|
|
for(i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
|
2007-08-02 23:09:51 +08:00
|
|
|
struct Scsi_Host *shost;
|
|
|
|
shost = lpfc_shost_from_vport(vports[i]);
|
|
|
|
spin_lock_irq(shost->host_lock);
|
|
|
|
vports[i]->fc_flag &= ~FC_OFFLINE_MODE;
|
|
|
|
if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED)
|
|
|
|
vports[i]->fc_flag |= FC_VPORT_NEEDS_REG_VPI;
|
|
|
|
spin_unlock_irq(shost->host_lock);
|
|
|
|
}
|
2008-01-11 14:52:54 +08:00
|
|
|
lpfc_destroy_vport_work_array(phba, vports);
|
2005-04-18 05:05:31 +08:00
|
|
|
|
2007-04-25 21:51:45 +08:00
|
|
|
lpfc_unblock_mgmt_io(phba);
|
2005-04-18 05:05:31 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_unblock_mgmt_io: Mark a HBA's management interface to be not blocked.
|
|
|
|
* @phba: pointer to lpfc hba data structure.
|
|
|
|
*
|
|
|
|
* This routine marks a HBA's management interface as not blocked. Once the
|
|
|
|
* HBA's management interface is marked as not blocked, all the user space
|
|
|
|
* access to the HBA, whether they are from sysfs interface or libdfc
|
|
|
|
* interface will be allowed. The HBA is set to block the management interface
|
|
|
|
* when the driver prepares the HBA interface for online or offline and then
|
|
|
|
* set to unblock the management interface afterwards.
|
|
|
|
**/
|
2007-04-25 21:51:45 +08:00
|
|
|
void
|
|
|
|
lpfc_unblock_mgmt_io(struct lpfc_hba * phba)
|
|
|
|
{
|
|
|
|
unsigned long iflag;
|
|
|
|
|
2007-06-18 08:56:36 +08:00
|
|
|
spin_lock_irqsave(&phba->hbalock, iflag);
|
|
|
|
phba->sli.sli_flag &= ~LPFC_BLOCK_MGMT_IO;
|
|
|
|
spin_unlock_irqrestore(&phba->hbalock, iflag);
|
2007-04-25 21:51:45 +08:00
|
|
|
}
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_offline_prep: Prepare a HBA to be brought offline.
|
|
|
|
* @phba: pointer to lpfc hba data structure.
|
|
|
|
*
|
|
|
|
* This routine is invoked to prepare a HBA to be brought offline. It performs
|
|
|
|
* unregistration login to all the nodes on all vports and flushes the mailbox
|
|
|
|
* queue to make it ready to be brought offline.
|
|
|
|
**/
|
2007-04-25 21:51:45 +08:00
|
|
|
void
|
|
|
|
lpfc_offline_prep(struct lpfc_hba * phba)
|
|
|
|
{
|
2007-06-18 08:56:36 +08:00
|
|
|
struct lpfc_vport *vport = phba->pport;
|
2007-04-25 21:51:45 +08:00
|
|
|
struct lpfc_nodelist *ndlp, *next_ndlp;
|
2007-10-28 01:37:43 +08:00
|
|
|
struct lpfc_vport **vports;
|
|
|
|
int i;
|
2005-04-18 05:05:31 +08:00
|
|
|
|
2007-06-18 08:56:36 +08:00
|
|
|
if (vport->fc_flag & FC_OFFLINE_MODE)
|
2007-04-25 21:51:45 +08:00
|
|
|
return;
|
2005-04-18 05:05:31 +08:00
|
|
|
|
2007-04-25 21:51:45 +08:00
|
|
|
lpfc_block_mgmt_io(phba);
|
2005-04-18 05:05:31 +08:00
|
|
|
|
|
|
|
lpfc_linkdown(phba);
|
|
|
|
|
2007-10-28 01:37:43 +08:00
|
|
|
/* Issue an unreg_login to all nodes on all vports */
|
|
|
|
vports = lpfc_create_vport_work_array(phba);
|
|
|
|
if (vports != NULL) {
|
2008-01-11 14:52:54 +08:00
|
|
|
for(i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
|
2007-10-28 01:37:43 +08:00
|
|
|
struct Scsi_Host *shost;
|
|
|
|
|
2007-10-28 01:37:53 +08:00
|
|
|
if (vports[i]->load_flag & FC_UNLOADING)
|
|
|
|
continue;
|
2007-10-28 01:37:43 +08:00
|
|
|
shost = lpfc_shost_from_vport(vports[i]);
|
|
|
|
list_for_each_entry_safe(ndlp, next_ndlp,
|
|
|
|
&vports[i]->fc_nodes,
|
|
|
|
nlp_listp) {
|
2008-02-09 07:49:26 +08:00
|
|
|
if (!NLP_CHK_NODE_ACT(ndlp))
|
|
|
|
continue;
|
2007-10-28 01:37:43 +08:00
|
|
|
if (ndlp->nlp_state == NLP_STE_UNUSED_NODE)
|
|
|
|
continue;
|
|
|
|
if (ndlp->nlp_type & NLP_FABRIC) {
|
|
|
|
lpfc_disc_state_machine(vports[i], ndlp,
|
|
|
|
NULL, NLP_EVT_DEVICE_RECOVERY);
|
|
|
|
lpfc_disc_state_machine(vports[i], ndlp,
|
|
|
|
NULL, NLP_EVT_DEVICE_RM);
|
|
|
|
}
|
|
|
|
spin_lock_irq(shost->host_lock);
|
|
|
|
ndlp->nlp_flag &= ~NLP_NPR_ADISC;
|
|
|
|
spin_unlock_irq(shost->host_lock);
|
|
|
|
lpfc_unreg_rpi(vports[i], ndlp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-01-11 14:52:54 +08:00
|
|
|
lpfc_destroy_vport_work_array(phba, vports);
|
2005-04-18 05:05:31 +08:00
|
|
|
|
2007-04-25 21:51:45 +08:00
|
|
|
lpfc_sli_flush_mbox_queue(phba);
|
|
|
|
}
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_offline: Bring a HBA offline.
|
|
|
|
* @phba: pointer to lpfc hba data structure.
|
|
|
|
*
|
|
|
|
* This routine actually brings a HBA offline. It stops all the timers
|
|
|
|
* associated with the HBA, brings down the SLI layer, and eventually
|
|
|
|
* marks the HBA as in offline state for the upper layer protocol.
|
|
|
|
**/
|
2007-04-25 21:51:45 +08:00
|
|
|
void
|
2007-06-18 08:56:36 +08:00
|
|
|
lpfc_offline(struct lpfc_hba *phba)
|
2007-04-25 21:51:45 +08:00
|
|
|
{
|
2007-08-02 23:09:51 +08:00
|
|
|
struct Scsi_Host *shost;
|
|
|
|
struct lpfc_vport **vports;
|
|
|
|
int i;
|
2007-04-25 21:51:45 +08:00
|
|
|
|
2007-08-02 23:09:51 +08:00
|
|
|
if (phba->pport->fc_flag & FC_OFFLINE_MODE)
|
2007-04-25 21:51:45 +08:00
|
|
|
return;
|
2006-07-07 03:49:56 +08:00
|
|
|
|
2005-04-18 05:05:31 +08:00
|
|
|
/* stop all timers associated with this hba */
|
2007-06-18 08:56:38 +08:00
|
|
|
lpfc_stop_phba_timers(phba);
|
2007-08-02 23:10:31 +08:00
|
|
|
vports = lpfc_create_vport_work_array(phba);
|
|
|
|
if (vports != NULL)
|
2008-01-11 14:52:54 +08:00
|
|
|
for(i = 0; i <= phba->max_vpi && vports[i] != NULL; i++)
|
2007-08-02 23:10:31 +08:00
|
|
|
lpfc_stop_vport_timers(vports[i]);
|
2008-01-11 14:52:54 +08:00
|
|
|
lpfc_destroy_vport_work_array(phba, vports);
|
2007-06-18 08:56:38 +08:00
|
|
|
lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
|
2007-08-02 23:10:09 +08:00
|
|
|
"0460 Bring Adapter offline\n");
|
2005-04-18 05:05:31 +08:00
|
|
|
/* Bring down the SLI Layer and cleanup. The HBA is offline
|
|
|
|
now. */
|
|
|
|
lpfc_sli_hba_down(phba);
|
2007-06-18 08:56:38 +08:00
|
|
|
spin_lock_irq(&phba->hbalock);
|
2007-04-25 21:52:34 +08:00
|
|
|
phba->work_ha = 0;
|
2007-06-18 08:56:38 +08:00
|
|
|
spin_unlock_irq(&phba->hbalock);
|
2007-08-02 23:09:51 +08:00
|
|
|
vports = lpfc_create_vport_work_array(phba);
|
|
|
|
if (vports != NULL)
|
2008-01-11 14:52:54 +08:00
|
|
|
for(i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
|
2007-08-02 23:09:51 +08:00
|
|
|
shost = lpfc_shost_from_vport(vports[i]);
|
|
|
|
spin_lock_irq(shost->host_lock);
|
|
|
|
vports[i]->work_port_events = 0;
|
|
|
|
vports[i]->fc_flag |= FC_OFFLINE_MODE;
|
|
|
|
spin_unlock_irq(shost->host_lock);
|
|
|
|
}
|
2008-01-11 14:52:54 +08:00
|
|
|
lpfc_destroy_vport_work_array(phba, vports);
|
2005-04-18 05:05:31 +08:00
|
|
|
}
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_scsi_free: Free all the SCSI buffers and IOCBs from driver lists.
|
|
|
|
* @phba: pointer to lpfc hba data structure.
|
|
|
|
*
|
|
|
|
* This routine is to free all the SCSI buffers and IOCBs from the driver
|
|
|
|
* list back to kernel. It is called from lpfc_pci_remove_one to free
|
|
|
|
* the internal resources before the device is removed from the system.
|
|
|
|
*
|
|
|
|
* Return codes
|
|
|
|
* 0 - successful (for now, it always returns 0)
|
|
|
|
**/
|
2005-04-18 05:05:31 +08:00
|
|
|
static int
|
2007-06-18 08:56:36 +08:00
|
|
|
lpfc_scsi_free(struct lpfc_hba *phba)
|
2005-04-18 05:05:31 +08:00
|
|
|
{
|
|
|
|
struct lpfc_scsi_buf *sb, *sb_next;
|
|
|
|
struct lpfc_iocbq *io, *io_next;
|
|
|
|
|
2007-06-18 08:56:36 +08:00
|
|
|
spin_lock_irq(&phba->hbalock);
|
2005-04-18 05:05:31 +08:00
|
|
|
/* Release all the lpfc_scsi_bufs maintained by this host. */
|
|
|
|
list_for_each_entry_safe(sb, sb_next, &phba->lpfc_scsi_buf_list, list) {
|
|
|
|
list_del(&sb->list);
|
|
|
|
pci_pool_free(phba->lpfc_scsi_dma_buf_pool, sb->data,
|
2007-06-18 08:56:38 +08:00
|
|
|
sb->dma_handle);
|
2005-04-18 05:05:31 +08:00
|
|
|
kfree(sb);
|
|
|
|
phba->total_scsi_bufs--;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Release all the lpfc_iocbq entries maintained by this host. */
|
|
|
|
list_for_each_entry_safe(io, io_next, &phba->lpfc_iocb_list, list) {
|
|
|
|
list_del(&io->list);
|
|
|
|
kfree(io);
|
|
|
|
phba->total_iocbq_bufs--;
|
|
|
|
}
|
|
|
|
|
2007-06-18 08:56:36 +08:00
|
|
|
spin_unlock_irq(&phba->hbalock);
|
2005-04-18 05:05:31 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_create_port: Create an FC port.
|
|
|
|
* @phba: pointer to lpfc hba data structure.
|
|
|
|
* @instance: a unique integer ID to this FC port.
|
|
|
|
* @dev: pointer to the device data structure.
|
|
|
|
*
|
|
|
|
* This routine creates a FC port for the upper layer protocol. The FC port
|
|
|
|
* can be created on top of either a physical port or a virtual port provided
|
|
|
|
* by the HBA. This routine also allocates a SCSI host data structure (shost)
|
|
|
|
* and associates the FC port created before adding the shost into the SCSI
|
|
|
|
* layer.
|
|
|
|
*
|
|
|
|
* Return codes
|
|
|
|
* @vport - pointer to the virtual N_Port data structure.
|
|
|
|
* NULL - port create failed.
|
|
|
|
**/
|
2007-06-18 08:56:36 +08:00
|
|
|
struct lpfc_vport *
|
2007-08-02 23:09:59 +08:00
|
|
|
lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev)
|
2007-04-25 21:53:22 +08:00
|
|
|
{
|
2007-06-18 08:56:36 +08:00
|
|
|
struct lpfc_vport *vport;
|
|
|
|
struct Scsi_Host *shost;
|
|
|
|
int error = 0;
|
2007-04-25 21:53:22 +08:00
|
|
|
|
2007-08-02 23:09:59 +08:00
|
|
|
if (dev != &phba->pcidev->dev)
|
|
|
|
shost = scsi_host_alloc(&lpfc_vport_template,
|
|
|
|
sizeof(struct lpfc_vport));
|
|
|
|
else
|
|
|
|
shost = scsi_host_alloc(&lpfc_template,
|
|
|
|
sizeof(struct lpfc_vport));
|
2007-06-18 08:56:36 +08:00
|
|
|
if (!shost)
|
|
|
|
goto out;
|
2007-04-25 21:53:22 +08:00
|
|
|
|
2007-06-18 08:56:36 +08:00
|
|
|
vport = (struct lpfc_vport *) shost->hostdata;
|
|
|
|
vport->phba = phba;
|
|
|
|
vport->load_flag |= FC_LOADING;
|
2007-06-18 08:56:38 +08:00
|
|
|
vport->fc_flag |= FC_VPORT_NEEDS_REG_VPI;
|
2008-02-09 07:50:14 +08:00
|
|
|
vport->fc_rscn_flush = 0;
|
2007-04-25 21:53:22 +08:00
|
|
|
|
2007-08-02 23:09:59 +08:00
|
|
|
lpfc_get_vport_cfgparam(vport);
|
2007-06-18 08:56:36 +08:00
|
|
|
shost->unique_id = instance;
|
|
|
|
shost->max_id = LPFC_MAX_TARGET;
|
2007-08-02 23:09:59 +08:00
|
|
|
shost->max_lun = vport->cfg_max_luns;
|
2007-06-18 08:56:36 +08:00
|
|
|
shost->this_id = -1;
|
|
|
|
shost->max_cmd_len = 16;
|
2007-04-25 21:53:22 +08:00
|
|
|
/*
|
2007-06-18 08:56:36 +08:00
|
|
|
* Set initial can_queue value since 0 is no longer supported and
|
|
|
|
* scsi_add_host will fail. This will be adjusted later based on the
|
|
|
|
* max xri value determined in hba setup.
|
2007-04-25 21:53:22 +08:00
|
|
|
*/
|
2007-06-18 08:56:36 +08:00
|
|
|
shost->can_queue = phba->cfg_hba_queue_depth - 10;
|
2007-08-02 23:09:59 +08:00
|
|
|
if (dev != &phba->pcidev->dev) {
|
2007-06-18 08:56:38 +08:00
|
|
|
shost->transportt = lpfc_vport_transport_template;
|
|
|
|
vport->port_type = LPFC_NPIV_PORT;
|
|
|
|
} else {
|
|
|
|
shost->transportt = lpfc_transport_template;
|
|
|
|
vport->port_type = LPFC_PHYSICAL_PORT;
|
|
|
|
}
|
2007-04-25 21:53:22 +08:00
|
|
|
|
2007-06-18 08:56:36 +08:00
|
|
|
/* Initialize all internally managed lists. */
|
|
|
|
INIT_LIST_HEAD(&vport->fc_nodes);
|
|
|
|
spin_lock_init(&vport->work_port_lock);
|
2007-04-25 21:53:22 +08:00
|
|
|
|
2007-06-18 08:56:36 +08:00
|
|
|
init_timer(&vport->fc_disctmo);
|
|
|
|
vport->fc_disctmo.function = lpfc_disc_timeout;
|
2007-06-18 08:56:38 +08:00
|
|
|
vport->fc_disctmo.data = (unsigned long)vport;
|
2007-04-25 21:53:22 +08:00
|
|
|
|
2007-06-18 08:56:36 +08:00
|
|
|
init_timer(&vport->fc_fdmitmo);
|
|
|
|
vport->fc_fdmitmo.function = lpfc_fdmi_tmo;
|
2007-06-18 08:56:38 +08:00
|
|
|
vport->fc_fdmitmo.data = (unsigned long)vport;
|
2007-04-25 21:53:22 +08:00
|
|
|
|
2007-06-18 08:56:36 +08:00
|
|
|
init_timer(&vport->els_tmofunc);
|
|
|
|
vport->els_tmofunc.function = lpfc_els_timeout;
|
2007-06-18 08:56:38 +08:00
|
|
|
vport->els_tmofunc.data = (unsigned long)vport;
|
2007-04-25 21:53:22 +08:00
|
|
|
|
2007-08-02 23:09:59 +08:00
|
|
|
error = scsi_add_host(shost, dev);
|
2007-06-18 08:56:36 +08:00
|
|
|
if (error)
|
|
|
|
goto out_put_shost;
|
2007-04-25 21:53:22 +08:00
|
|
|
|
2007-08-02 23:09:51 +08:00
|
|
|
spin_lock_irq(&phba->hbalock);
|
2007-06-18 08:56:36 +08:00
|
|
|
list_add_tail(&vport->listentry, &phba->port_list);
|
2007-08-02 23:09:51 +08:00
|
|
|
spin_unlock_irq(&phba->hbalock);
|
2007-06-18 08:56:36 +08:00
|
|
|
return vport;
|
2007-04-25 21:53:22 +08:00
|
|
|
|
2007-06-18 08:56:36 +08:00
|
|
|
out_put_shost:
|
|
|
|
scsi_host_put(shost);
|
|
|
|
out:
|
|
|
|
return NULL;
|
2007-04-25 21:53:22 +08:00
|
|
|
}
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* destroy_port: Destroy an FC port.
|
|
|
|
* @vport: pointer to an lpfc virtual N_Port data structure.
|
|
|
|
*
|
|
|
|
* This routine destroys a FC port from the upper layer protocol. All the
|
|
|
|
* resources associated with the port are released.
|
|
|
|
**/
|
2007-06-18 08:56:36 +08:00
|
|
|
void
|
|
|
|
destroy_port(struct lpfc_vport *vport)
|
2007-04-25 21:53:22 +08:00
|
|
|
{
|
2007-06-18 08:56:38 +08:00
|
|
|
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
|
|
|
|
struct lpfc_hba *phba = vport->phba;
|
2007-04-25 21:53:22 +08:00
|
|
|
|
2007-06-18 08:56:38 +08:00
|
|
|
kfree(vport->vname);
|
2007-04-25 21:53:22 +08:00
|
|
|
|
2007-06-18 08:56:39 +08:00
|
|
|
lpfc_debugfs_terminate(vport);
|
2007-06-18 08:56:38 +08:00
|
|
|
fc_remove_host(shost);
|
|
|
|
scsi_remove_host(shost);
|
2007-04-25 21:53:22 +08:00
|
|
|
|
2007-06-18 08:56:38 +08:00
|
|
|
spin_lock_irq(&phba->hbalock);
|
|
|
|
list_del_init(&vport->listentry);
|
|
|
|
spin_unlock_irq(&phba->hbalock);
|
2007-04-25 21:53:22 +08:00
|
|
|
|
2007-06-18 08:56:38 +08:00
|
|
|
lpfc_cleanup(vport);
|
2007-04-25 21:53:22 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_get_instance: Get a unique integer ID.
|
|
|
|
*
|
|
|
|
* This routine allocates a unique integer ID from lpfc_hba_index pool. It
|
|
|
|
* uses the kernel idr facility to perform the task.
|
|
|
|
*
|
|
|
|
* Return codes:
|
|
|
|
* instance - a unique integer ID allocated as the new instance.
|
|
|
|
* -1 - lpfc get instance failed.
|
|
|
|
**/
|
2007-06-18 08:56:38 +08:00
|
|
|
int
|
|
|
|
lpfc_get_instance(void)
|
|
|
|
{
|
|
|
|
int instance = 0;
|
2007-04-25 21:53:22 +08:00
|
|
|
|
2007-06-18 08:56:38 +08:00
|
|
|
/* Assign an unused number */
|
|
|
|
if (!idr_pre_get(&lpfc_hba_index, GFP_KERNEL))
|
|
|
|
return -1;
|
|
|
|
if (idr_get_new(&lpfc_hba_index, NULL, &instance))
|
|
|
|
return -1;
|
|
|
|
return instance;
|
2007-04-25 21:53:22 +08:00
|
|
|
}
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_scan_finished: method for SCSI layer to detect whether scan is done.
|
|
|
|
* @shost: pointer to SCSI host data structure.
|
|
|
|
* @time: elapsed time of the scan in jiffies.
|
|
|
|
*
|
|
|
|
* This routine is called by the SCSI layer with a SCSI host to determine
|
|
|
|
* whether the scan host is finished.
|
|
|
|
*
|
|
|
|
* Note: there is no scan_start function as adapter initialization will have
|
|
|
|
* asynchronously kicked off the link initialization.
|
|
|
|
*
|
|
|
|
* Return codes
|
|
|
|
* 0 - SCSI host scan is not over yet.
|
|
|
|
* 1 - SCSI host scan is over.
|
|
|
|
**/
|
2007-04-25 21:53:22 +08:00
|
|
|
int lpfc_scan_finished(struct Scsi_Host *shost, unsigned long time)
|
|
|
|
{
|
2007-06-18 08:56:36 +08:00
|
|
|
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
|
|
|
|
struct lpfc_hba *phba = vport->phba;
|
2007-06-18 08:56:39 +08:00
|
|
|
int stat = 0;
|
2007-04-25 21:53:22 +08:00
|
|
|
|
2007-06-18 08:56:39 +08:00
|
|
|
spin_lock_irq(shost->host_lock);
|
|
|
|
|
2007-08-02 23:10:31 +08:00
|
|
|
if (vport->load_flag & FC_UNLOADING) {
|
2007-06-18 08:56:39 +08:00
|
|
|
stat = 1;
|
|
|
|
goto finished;
|
|
|
|
}
|
2007-06-18 08:56:36 +08:00
|
|
|
if (time >= 30 * HZ) {
|
|
|
|
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
|
2007-08-02 23:10:09 +08:00
|
|
|
"0461 Scanning longer than 30 "
|
|
|
|
"seconds. Continuing initialization\n");
|
2007-06-18 08:56:39 +08:00
|
|
|
stat = 1;
|
2007-04-25 21:53:22 +08:00
|
|
|
goto finished;
|
2007-06-18 08:56:36 +08:00
|
|
|
}
|
|
|
|
if (time >= 15 * HZ && phba->link_state <= LPFC_LINK_DOWN) {
|
|
|
|
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
|
2007-08-02 23:10:09 +08:00
|
|
|
"0465 Link down longer than 15 "
|
|
|
|
"seconds. Continuing initialization\n");
|
2007-06-18 08:56:39 +08:00
|
|
|
stat = 1;
|
2007-04-25 21:53:22 +08:00
|
|
|
goto finished;
|
2007-06-18 08:56:36 +08:00
|
|
|
}
|
2007-04-25 21:53:22 +08:00
|
|
|
|
2007-06-18 08:56:36 +08:00
|
|
|
if (vport->port_state != LPFC_VPORT_READY)
|
2007-06-18 08:56:39 +08:00
|
|
|
goto finished;
|
2007-06-18 08:56:36 +08:00
|
|
|
if (vport->num_disc_nodes || vport->fc_prli_sent)
|
2007-06-18 08:56:39 +08:00
|
|
|
goto finished;
|
2007-06-18 08:56:36 +08:00
|
|
|
if (vport->fc_map_cnt == 0 && time < 2 * HZ)
|
2007-06-18 08:56:39 +08:00
|
|
|
goto finished;
|
2007-06-18 08:56:36 +08:00
|
|
|
if ((phba->sli.sli_flag & LPFC_SLI_MBOX_ACTIVE) != 0)
|
2007-06-18 08:56:39 +08:00
|
|
|
goto finished;
|
|
|
|
|
|
|
|
stat = 1;
|
2007-04-25 21:53:22 +08:00
|
|
|
|
|
|
|
finished:
|
2007-06-18 08:56:39 +08:00
|
|
|
spin_unlock_irq(shost->host_lock);
|
|
|
|
return stat;
|
2007-06-18 08:56:38 +08:00
|
|
|
}
|
2007-04-25 21:53:22 +08:00
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_host_attrib_init: Initialize SCSI host attributes on a FC port.
|
|
|
|
* @shost: pointer to SCSI host data structure.
|
|
|
|
*
|
|
|
|
* This routine initializes a given SCSI host attributes on a FC port. The
|
|
|
|
* SCSI host can be either on top of a physical port or a virtual port.
|
|
|
|
**/
|
2007-06-18 08:56:38 +08:00
|
|
|
void lpfc_host_attrib_init(struct Scsi_Host *shost)
|
|
|
|
{
|
|
|
|
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
|
|
|
|
struct lpfc_hba *phba = vport->phba;
|
2007-04-25 21:53:22 +08:00
|
|
|
/*
|
2007-06-18 08:56:36 +08:00
|
|
|
* Set fixed host attributes. Must done after lpfc_sli_hba_setup().
|
2007-04-25 21:53:22 +08:00
|
|
|
*/
|
|
|
|
|
2007-06-18 08:56:36 +08:00
|
|
|
fc_host_node_name(shost) = wwn_to_u64(vport->fc_nodename.u.wwn);
|
|
|
|
fc_host_port_name(shost) = wwn_to_u64(vport->fc_portname.u.wwn);
|
2007-04-25 21:53:22 +08:00
|
|
|
fc_host_supported_classes(shost) = FC_COS_CLASS3;
|
|
|
|
|
|
|
|
memset(fc_host_supported_fc4s(shost), 0,
|
2007-06-18 08:56:36 +08:00
|
|
|
sizeof(fc_host_supported_fc4s(shost)));
|
2007-04-25 21:53:22 +08:00
|
|
|
fc_host_supported_fc4s(shost)[2] = 1;
|
|
|
|
fc_host_supported_fc4s(shost)[7] = 1;
|
|
|
|
|
2007-06-18 08:56:38 +08:00
|
|
|
lpfc_vport_symbolic_node_name(vport, fc_host_symbolic_name(shost),
|
|
|
|
sizeof fc_host_symbolic_name(shost));
|
2007-04-25 21:53:22 +08:00
|
|
|
|
|
|
|
fc_host_supported_speeds(shost) = 0;
|
|
|
|
if (phba->lmt & LMT_10Gb)
|
|
|
|
fc_host_supported_speeds(shost) |= FC_PORTSPEED_10GBIT;
|
2007-10-28 01:37:53 +08:00
|
|
|
if (phba->lmt & LMT_8Gb)
|
|
|
|
fc_host_supported_speeds(shost) |= FC_PORTSPEED_8GBIT;
|
2007-04-25 21:53:22 +08:00
|
|
|
if (phba->lmt & LMT_4Gb)
|
|
|
|
fc_host_supported_speeds(shost) |= FC_PORTSPEED_4GBIT;
|
|
|
|
if (phba->lmt & LMT_2Gb)
|
|
|
|
fc_host_supported_speeds(shost) |= FC_PORTSPEED_2GBIT;
|
|
|
|
if (phba->lmt & LMT_1Gb)
|
|
|
|
fc_host_supported_speeds(shost) |= FC_PORTSPEED_1GBIT;
|
|
|
|
|
|
|
|
fc_host_maxframe_size(shost) =
|
2007-06-18 08:56:36 +08:00
|
|
|
(((uint32_t) vport->fc_sparam.cmn.bbRcvSizeMsb & 0x0F) << 8) |
|
|
|
|
(uint32_t) vport->fc_sparam.cmn.bbRcvSizeLsb;
|
2007-04-25 21:53:22 +08:00
|
|
|
|
|
|
|
/* This value is also unchanging */
|
|
|
|
memset(fc_host_active_fc4s(shost), 0,
|
2007-06-18 08:56:36 +08:00
|
|
|
sizeof(fc_host_active_fc4s(shost)));
|
2007-04-25 21:53:22 +08:00
|
|
|
fc_host_active_fc4s(shost)[2] = 1;
|
|
|
|
fc_host_active_fc4s(shost)[7] = 1;
|
|
|
|
|
2007-06-18 08:56:38 +08:00
|
|
|
fc_host_max_npiv_vports(shost) = phba->max_vpi;
|
2007-04-25 21:53:22 +08:00
|
|
|
spin_lock_irq(shost->host_lock);
|
2007-08-02 23:10:31 +08:00
|
|
|
vport->load_flag &= ~FC_LOADING;
|
2007-04-25 21:53:22 +08:00
|
|
|
spin_unlock_irq(shost->host_lock);
|
|
|
|
}
|
2005-04-18 05:05:31 +08:00
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_enable_msix: Enable MSI-X interrupt mode.
|
|
|
|
* @phba: pointer to lpfc hba data structure.
|
|
|
|
*
|
|
|
|
* This routine is invoked to enable the MSI-X interrupt vectors. The kernel
|
|
|
|
* function pci_enable_msix() is called to enable the MSI-X vectors. Note that
|
|
|
|
* pci_enable_msix(), once invoked, enables either all or nothing, depending
|
|
|
|
* on the current availability of PCI vector resources. The device driver is
|
|
|
|
* responsible for calling the individual request_irq() to register each MSI-X
|
|
|
|
* vector with a interrupt handler, which is done in this function. Note that
|
|
|
|
* later when device is unloading, the driver should always call free_irq()
|
|
|
|
* on all MSI-X vectors it has done request_irq() on before calling
|
|
|
|
* pci_disable_msix(). Failure to do so results in a BUG_ON() and a device
|
|
|
|
* will be left with MSI-X enabled and leaks its vectors.
|
|
|
|
*
|
|
|
|
* Return codes
|
|
|
|
* 0 - sucessful
|
|
|
|
* other values - error
|
|
|
|
**/
|
2008-02-09 07:49:51 +08:00
|
|
|
static int
|
|
|
|
lpfc_enable_msix(struct lpfc_hba *phba)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
|
|
|
|
phba->msix_entries[0].entry = 0;
|
|
|
|
phba->msix_entries[0].vector = 0;
|
|
|
|
|
|
|
|
error = pci_enable_msix(phba->pcidev, phba->msix_entries,
|
|
|
|
ARRAY_SIZE(phba->msix_entries));
|
|
|
|
if (error) {
|
|
|
|
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
|
|
|
|
"0420 Enable MSI-X failed (%d), continuing "
|
|
|
|
"with MSI\n", error);
|
|
|
|
pci_disable_msix(phba->pcidev);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = request_irq(phba->msix_entries[0].vector, lpfc_intr_handler, 0,
|
|
|
|
LPFC_DRIVER_NAME, phba);
|
|
|
|
if (error) {
|
|
|
|
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
|
|
|
|
"0421 MSI-X request_irq failed (%d), "
|
|
|
|
"continuing with MSI\n", error);
|
|
|
|
pci_disable_msix(phba->pcidev);
|
|
|
|
}
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_disable_msix: Disable MSI-X interrupt mode.
|
|
|
|
* @phba: pointer to lpfc hba data structure.
|
|
|
|
*
|
|
|
|
* This routine is invoked to release the MSI-X vectors and then disable the
|
|
|
|
* MSI-X interrupt mode.
|
|
|
|
**/
|
2008-02-09 07:49:51 +08:00
|
|
|
static void
|
|
|
|
lpfc_disable_msix(struct lpfc_hba *phba)
|
|
|
|
{
|
|
|
|
free_irq(phba->msix_entries[0].vector, phba);
|
|
|
|
pci_disable_msix(phba->pcidev);
|
|
|
|
}
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_pci_probe_one: lpfc PCI probe func to register device to PCI subsystem.
|
|
|
|
* @pdev: pointer to PCI device
|
|
|
|
* @pid: pointer to PCI device identifier
|
|
|
|
*
|
|
|
|
* This routine is to be registered to the kernel's PCI subsystem. When an
|
|
|
|
* Emulex HBA is presented in PCI bus, the kernel PCI subsystem looks at
|
|
|
|
* PCI device-specific information of the device and driver to see if the
|
|
|
|
* driver state that it can support this kind of device. If the match is
|
|
|
|
* successful, the driver core invokes this routine. If this routine
|
|
|
|
* determines it can claim the HBA, it does all the initialization that it
|
|
|
|
* needs to do to handle the HBA properly.
|
|
|
|
*
|
|
|
|
* Return code
|
|
|
|
* 0 - driver can claim the device
|
|
|
|
* negative value - driver can not claim the device
|
|
|
|
**/
|
2005-04-18 05:05:31 +08:00
|
|
|
static int __devinit
|
|
|
|
lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
|
|
|
|
{
|
2007-06-18 08:56:36 +08:00
|
|
|
struct lpfc_vport *vport = NULL;
|
|
|
|
struct lpfc_hba *phba;
|
|
|
|
struct lpfc_sli *psli;
|
2005-04-18 05:05:31 +08:00
|
|
|
struct lpfc_iocbq *iocbq_entry = NULL, *iocbq_next = NULL;
|
2007-06-18 08:56:36 +08:00
|
|
|
struct Scsi_Host *shost = NULL;
|
2007-08-02 23:10:31 +08:00
|
|
|
void *ptr;
|
2005-04-18 05:05:31 +08:00
|
|
|
unsigned long bar0map_len, bar2map_len;
|
2007-10-28 01:37:33 +08:00
|
|
|
int error = -ENODEV, retval;
|
2007-08-02 23:10:31 +08:00
|
|
|
int i, hbq_count;
|
2005-10-29 23:28:33 +08:00
|
|
|
uint16_t iotag;
|
2008-01-11 14:53:00 +08:00
|
|
|
int bars = pci_select_bars(pdev, IORESOURCE_MEM);
|
2005-04-18 05:05:31 +08:00
|
|
|
|
2008-02-02 19:13:22 +08:00
|
|
|
if (pci_enable_device_mem(pdev))
|
2005-04-18 05:05:31 +08:00
|
|
|
goto out;
|
2008-01-11 14:53:00 +08:00
|
|
|
if (pci_request_selected_regions(pdev, bars, LPFC_DRIVER_NAME))
|
2005-04-18 05:05:31 +08:00
|
|
|
goto out_disable_device;
|
|
|
|
|
2007-06-18 08:56:36 +08:00
|
|
|
phba = kzalloc(sizeof (struct lpfc_hba), GFP_KERNEL);
|
|
|
|
if (!phba)
|
2005-04-18 05:05:31 +08:00
|
|
|
goto out_release_regions;
|
|
|
|
|
2007-06-18 08:56:36 +08:00
|
|
|
spin_lock_init(&phba->hbalock);
|
2005-04-18 05:05:31 +08:00
|
|
|
|
2008-02-09 07:49:26 +08:00
|
|
|
/* Initialize ndlp management spinlock */
|
|
|
|
spin_lock_init(&phba->ndlp_lock);
|
|
|
|
|
2005-04-18 05:05:31 +08:00
|
|
|
phba->pcidev = pdev;
|
|
|
|
|
|
|
|
/* Assign an unused board number */
|
2007-06-18 08:56:38 +08:00
|
|
|
if ((phba->brd_no = lpfc_get_instance()) < 0)
|
2007-06-18 08:56:36 +08:00
|
|
|
goto out_free_phba;
|
2005-04-18 05:05:31 +08:00
|
|
|
|
2007-06-18 08:56:36 +08:00
|
|
|
INIT_LIST_HEAD(&phba->port_list);
|
|
|
|
/*
|
|
|
|
* Get all the module params for configuring this host and then
|
|
|
|
* establish the host.
|
|
|
|
*/
|
|
|
|
lpfc_get_cfgparam(phba);
|
2007-06-18 08:56:38 +08:00
|
|
|
phba->max_vpi = LPFC_MAX_VPI;
|
2005-04-18 05:05:31 +08:00
|
|
|
|
|
|
|
/* Initialize timers used by driver */
|
2007-06-18 08:56:39 +08:00
|
|
|
init_timer(&phba->hb_tmofunc);
|
|
|
|
phba->hb_tmofunc.function = lpfc_hb_timeout;
|
|
|
|
phba->hb_tmofunc.data = (unsigned long)phba;
|
|
|
|
|
2005-04-18 05:05:31 +08:00
|
|
|
psli = &phba->sli;
|
|
|
|
init_timer(&psli->mbox_tmo);
|
|
|
|
psli->mbox_tmo.function = lpfc_mbox_timeout;
|
2007-06-18 08:56:36 +08:00
|
|
|
psli->mbox_tmo.data = (unsigned long) phba;
|
[SCSI] lpfc 8.1.1 : Add polled-mode support
- Add functionality to run in polled mode only. Includes run time
attribute to enable mode.
- Enable runtime writable hba settings for coallescing and delay parameters
Customers have requested a mode in the driver to run strictly polled.
This is generally to support an environment where the server is extremely
loaded and is looking to reclaim some cpu cycles from adapter interrupt
handling.
This patch adds a new "poll" attribute, and the following behavior:
if value is 0 (default):
The driver uses the normal method for i/o completion. It uses the
firmware feature of interrupt coalesing. The firmware allows a
minimum number of i/o completions before an interrupt, or a maximum
time delay between interrupts. By default, the driver sets these
to no delay (disabled) or 1 i/o - meaning coalescing is disabled.
Attributes were provided to change the coalescing values, but it was
a module-load time only and global across all adapters.
This patch allows them to be writable on a per-adapter basis.
if value is 1 :
Interrupts are left enabled, expecting that the user has tuned the
interrupt coalescing values. When this setting is enabled, the driver
will attempt to service completed i/o whenever new i/o is submitted
to the adapter. If the coalescing values are large, and the i/o
generation rate steady, an interrupt will be avoided by servicing
completed i/o prior to the coalescing thresholds kicking in. However,
if the i/o completion load is high enough or i/o generation slow, the
coalescion values will ensure that completed i/o is serviced in a timely
fashion.
if value is 3 :
Turns off FCP i/o interrupts altogether. The coalescing values now have
no effect. A new attribute "poll_tmo" (default 10ms) exists to set
the polling interval for i/o completion. When this setting is enabled,
the driver will attempt to service completed i/o and restart the
interval timer whenever new i/o is submitted. This behavior allows for
servicing of completed i/o sooner than the interval timer, but ensures
that if no i/o is being issued, then the interval timer will kick in
to service the outstanding i/o.
Signed-off-by: James Smart <James.Smart@emulex.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
2005-11-30 05:32:13 +08:00
|
|
|
init_timer(&phba->fcp_poll_timer);
|
|
|
|
phba->fcp_poll_timer.function = lpfc_poll_timeout;
|
2007-06-18 08:56:36 +08:00
|
|
|
phba->fcp_poll_timer.data = (unsigned long) phba;
|
2007-06-18 08:56:38 +08:00
|
|
|
init_timer(&phba->fabric_block_timer);
|
|
|
|
phba->fabric_block_timer.function = lpfc_fabric_block_timeout;
|
|
|
|
phba->fabric_block_timer.data = (unsigned long) phba;
|
2005-04-18 05:05:31 +08:00
|
|
|
|
|
|
|
pci_set_master(pdev);
|
2007-07-10 02:55:54 +08:00
|
|
|
pci_try_set_mwi(pdev);
|
2005-04-18 05:05:31 +08:00
|
|
|
|
|
|
|
if (pci_set_dma_mask(phba->pcidev, DMA_64BIT_MASK) != 0)
|
|
|
|
if (pci_set_dma_mask(phba->pcidev, DMA_32BIT_MASK) != 0)
|
|
|
|
goto out_idr_remove;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the bus address of Bar0 and Bar2 and the number of bytes
|
|
|
|
* required by each mapping.
|
|
|
|
*/
|
|
|
|
phba->pci_bar0_map = pci_resource_start(phba->pcidev, 0);
|
|
|
|
bar0map_len = pci_resource_len(phba->pcidev, 0);
|
|
|
|
|
|
|
|
phba->pci_bar2_map = pci_resource_start(phba->pcidev, 2);
|
|
|
|
bar2map_len = pci_resource_len(phba->pcidev, 2);
|
|
|
|
|
2006-03-01 08:25:19 +08:00
|
|
|
/* Map HBA SLIM to a kernel virtual address. */
|
2005-04-18 05:05:31 +08:00
|
|
|
phba->slim_memmap_p = ioremap(phba->pci_bar0_map, bar0map_len);
|
2006-03-01 08:25:19 +08:00
|
|
|
if (!phba->slim_memmap_p) {
|
|
|
|
error = -ENODEV;
|
|
|
|
dev_printk(KERN_ERR, &pdev->dev,
|
|
|
|
"ioremap failed for SLIM memory.\n");
|
|
|
|
goto out_idr_remove;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Map HBA Control Registers to a kernel virtual address. */
|
2005-04-18 05:05:31 +08:00
|
|
|
phba->ctrl_regs_memmap_p = ioremap(phba->pci_bar2_map, bar2map_len);
|
2006-03-01 08:25:19 +08:00
|
|
|
if (!phba->ctrl_regs_memmap_p) {
|
|
|
|
error = -ENODEV;
|
|
|
|
dev_printk(KERN_ERR, &pdev->dev,
|
|
|
|
"ioremap failed for HBA control registers.\n");
|
|
|
|
goto out_iounmap_slim;
|
|
|
|
}
|
2005-04-18 05:05:31 +08:00
|
|
|
|
|
|
|
/* Allocate memory for SLI-2 structures */
|
2008-08-25 09:49:55 +08:00
|
|
|
phba->slim2p.virt = dma_alloc_coherent(&phba->pcidev->dev,
|
|
|
|
SLI2_SLIM_SIZE,
|
|
|
|
&phba->slim2p.phys,
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!phba->slim2p.virt)
|
2005-04-18 05:05:31 +08:00
|
|
|
goto out_iounmap;
|
|
|
|
|
2008-08-25 09:49:55 +08:00
|
|
|
memset(phba->slim2p.virt, 0, SLI2_SLIM_SIZE);
|
|
|
|
phba->mbox = phba->slim2p.virt;
|
|
|
|
phba->pcb = (phba->slim2p.virt + sizeof(MAILBOX_t));
|
|
|
|
phba->IOCBs = (phba->slim2p.virt + sizeof(MAILBOX_t) +
|
|
|
|
sizeof(struct _PCB));
|
2005-04-18 05:05:31 +08:00
|
|
|
|
2007-06-18 08:56:37 +08:00
|
|
|
phba->hbqslimp.virt = dma_alloc_coherent(&phba->pcidev->dev,
|
|
|
|
lpfc_sli_hbq_size(),
|
|
|
|
&phba->hbqslimp.phys,
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!phba->hbqslimp.virt)
|
|
|
|
goto out_free_slim;
|
|
|
|
|
2007-08-02 23:10:31 +08:00
|
|
|
hbq_count = lpfc_sli_hbq_count();
|
|
|
|
ptr = phba->hbqslimp.virt;
|
|
|
|
for (i = 0; i < hbq_count; ++i) {
|
|
|
|
phba->hbqs[i].hbq_virt = ptr;
|
|
|
|
INIT_LIST_HEAD(&phba->hbqs[i].hbq_buffer_list);
|
|
|
|
ptr += (lpfc_hbq_defs[i]->entry_count *
|
|
|
|
sizeof(struct lpfc_hbq_entry));
|
|
|
|
}
|
|
|
|
phba->hbqs[LPFC_ELS_HBQ].hbq_alloc_buffer = lpfc_els_hbq_alloc;
|
|
|
|
phba->hbqs[LPFC_ELS_HBQ].hbq_free_buffer = lpfc_els_hbq_free;
|
|
|
|
|
2007-06-18 08:56:37 +08:00
|
|
|
memset(phba->hbqslimp.virt, 0, lpfc_sli_hbq_size());
|
|
|
|
|
2008-02-09 07:50:25 +08:00
|
|
|
INIT_LIST_HEAD(&phba->hbqbuf_in_list);
|
|
|
|
|
2005-04-18 05:05:31 +08:00
|
|
|
/* Initialize the SLI Layer to run with lpfc HBAs. */
|
|
|
|
lpfc_sli_setup(phba);
|
|
|
|
lpfc_sli_queue_setup(phba);
|
|
|
|
|
2007-10-28 01:37:33 +08:00
|
|
|
retval = lpfc_mem_alloc(phba);
|
|
|
|
if (retval) {
|
|
|
|
error = retval;
|
2007-06-18 08:56:37 +08:00
|
|
|
goto out_free_hbqslimp;
|
2007-10-28 01:37:33 +08:00
|
|
|
}
|
2005-04-18 05:05:31 +08:00
|
|
|
|
|
|
|
/* Initialize and populate the iocb list per host. */
|
|
|
|
INIT_LIST_HEAD(&phba->lpfc_iocb_list);
|
|
|
|
for (i = 0; i < LPFC_IOCB_LIST_CNT; i++) {
|
some kmalloc/memset ->kzalloc (tree wide)
Transform some calls to kmalloc/memset to a single kzalloc (or kcalloc).
Here is a short excerpt of the semantic patch performing
this transformation:
@@
type T2;
expression x;
identifier f,fld;
expression E;
expression E1,E2;
expression e1,e2,e3,y;
statement S;
@@
x =
- kmalloc
+ kzalloc
(E1,E2)
... when != \(x->fld=E;\|y=f(...,x,...);\|f(...,x,...);\|x=E;\|while(...) S\|for(e1;e2;e3) S\)
- memset((T2)x,0,E1);
@@
expression E1,E2,E3;
@@
- kzalloc(E1 * E2,E3)
+ kcalloc(E1,E2,E3)
[akpm@linux-foundation.org: get kcalloc args the right way around]
Signed-off-by: Yoann Padioleau <padator@wanadoo.fr>
Cc: Richard Henderson <rth@twiddle.net>
Cc: Ivan Kokshaysky <ink@jurassic.park.msu.ru>
Acked-by: Russell King <rmk@arm.linux.org.uk>
Cc: Bryan Wu <bryan.wu@analog.com>
Acked-by: Jiri Slaby <jirislaby@gmail.com>
Cc: Dave Airlie <airlied@linux.ie>
Acked-by: Roland Dreier <rolandd@cisco.com>
Cc: Jiri Kosina <jkosina@suse.cz>
Acked-by: Dmitry Torokhov <dtor@mail.ru>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Acked-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Acked-by: Pierre Ossman <drzeus-list@drzeus.cx>
Cc: Jeff Garzik <jeff@garzik.org>
Cc: "David S. Miller" <davem@davemloft.net>
Acked-by: Greg KH <greg@kroah.com>
Cc: James Bottomley <James.Bottomley@steeleye.com>
Cc: "Antonino A. Daplas" <adaplas@pol.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-07-19 16:49:03 +08:00
|
|
|
iocbq_entry = kzalloc(sizeof(struct lpfc_iocbq), GFP_KERNEL);
|
2005-04-18 05:05:31 +08:00
|
|
|
if (iocbq_entry == NULL) {
|
|
|
|
printk(KERN_ERR "%s: only allocated %d iocbs of "
|
|
|
|
"expected %d count. Unloading driver.\n",
|
2008-07-04 14:47:27 +08:00
|
|
|
__func__, i, LPFC_IOCB_LIST_CNT);
|
2005-04-18 05:05:31 +08:00
|
|
|
error = -ENOMEM;
|
|
|
|
goto out_free_iocbq;
|
|
|
|
}
|
|
|
|
|
2005-10-29 23:28:33 +08:00
|
|
|
iotag = lpfc_sli_next_iotag(phba, iocbq_entry);
|
|
|
|
if (iotag == 0) {
|
|
|
|
kfree (iocbq_entry);
|
|
|
|
printk(KERN_ERR "%s: failed to allocate IOTAG. "
|
|
|
|
"Unloading driver.\n",
|
2008-07-04 14:47:27 +08:00
|
|
|
__func__);
|
2005-10-29 23:28:33 +08:00
|
|
|
error = -ENOMEM;
|
|
|
|
goto out_free_iocbq;
|
|
|
|
}
|
2007-06-18 08:56:36 +08:00
|
|
|
|
|
|
|
spin_lock_irq(&phba->hbalock);
|
2005-04-18 05:05:31 +08:00
|
|
|
list_add(&iocbq_entry->list, &phba->lpfc_iocb_list);
|
|
|
|
phba->total_iocbq_bufs++;
|
2007-06-18 08:56:36 +08:00
|
|
|
spin_unlock_irq(&phba->hbalock);
|
2005-04-18 05:05:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Initialize HBA structure */
|
|
|
|
phba->fc_edtov = FF_DEF_EDTOV;
|
|
|
|
phba->fc_ratov = FF_DEF_RATOV;
|
|
|
|
phba->fc_altov = FF_DEF_ALTOV;
|
|
|
|
phba->fc_arbtov = FF_DEF_ARBTOV;
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&phba->work_list);
|
|
|
|
phba->work_ha_mask = (HA_ERATT|HA_MBATT|HA_LATT);
|
|
|
|
phba->work_ha_mask |= (HA_RXMASK << (LPFC_ELS_RING * 4));
|
|
|
|
|
2008-06-15 10:52:53 +08:00
|
|
|
/* Initialize the wait queue head for the kernel thread */
|
|
|
|
init_waitqueue_head(&phba->work_waitq);
|
|
|
|
|
2005-04-18 05:05:31 +08:00
|
|
|
/* Startup the kernel thread for this host adapter. */
|
|
|
|
phba->worker_thread = kthread_run(lpfc_do_work, phba,
|
|
|
|
"lpfc_worker_%d", phba->brd_no);
|
|
|
|
if (IS_ERR(phba->worker_thread)) {
|
|
|
|
error = PTR_ERR(phba->worker_thread);
|
|
|
|
goto out_free_iocbq;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Initialize the list of scsi buffers used by driver for scsi IO. */
|
[SCSI] lpfc 8.1.1 : Add polled-mode support
- Add functionality to run in polled mode only. Includes run time
attribute to enable mode.
- Enable runtime writable hba settings for coallescing and delay parameters
Customers have requested a mode in the driver to run strictly polled.
This is generally to support an environment where the server is extremely
loaded and is looking to reclaim some cpu cycles from adapter interrupt
handling.
This patch adds a new "poll" attribute, and the following behavior:
if value is 0 (default):
The driver uses the normal method for i/o completion. It uses the
firmware feature of interrupt coalesing. The firmware allows a
minimum number of i/o completions before an interrupt, or a maximum
time delay between interrupts. By default, the driver sets these
to no delay (disabled) or 1 i/o - meaning coalescing is disabled.
Attributes were provided to change the coalescing values, but it was
a module-load time only and global across all adapters.
This patch allows them to be writable on a per-adapter basis.
if value is 1 :
Interrupts are left enabled, expecting that the user has tuned the
interrupt coalescing values. When this setting is enabled, the driver
will attempt to service completed i/o whenever new i/o is submitted
to the adapter. If the coalescing values are large, and the i/o
generation rate steady, an interrupt will be avoided by servicing
completed i/o prior to the coalescing thresholds kicking in. However,
if the i/o completion load is high enough or i/o generation slow, the
coalescion values will ensure that completed i/o is serviced in a timely
fashion.
if value is 3 :
Turns off FCP i/o interrupts altogether. The coalescing values now have
no effect. A new attribute "poll_tmo" (default 10ms) exists to set
the polling interval for i/o completion. When this setting is enabled,
the driver will attempt to service completed i/o and restart the
interval timer whenever new i/o is submitted. This behavior allows for
servicing of completed i/o sooner than the interval timer, but ensures
that if no i/o is being issued, then the interval timer will kick in
to service the outstanding i/o.
Signed-off-by: James Smart <James.Smart@emulex.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
2005-11-30 05:32:13 +08:00
|
|
|
spin_lock_init(&phba->scsi_buf_list_lock);
|
2005-04-18 05:05:31 +08:00
|
|
|
INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list);
|
|
|
|
|
2007-06-18 08:56:38 +08:00
|
|
|
/* Initialize list of fabric iocbs */
|
|
|
|
INIT_LIST_HEAD(&phba->fabric_iocb_list);
|
|
|
|
|
2008-01-11 14:52:36 +08:00
|
|
|
/* Initialize list to save ELS buffers */
|
|
|
|
INIT_LIST_HEAD(&phba->elsbuf);
|
|
|
|
|
2007-08-02 23:09:59 +08:00
|
|
|
vport = lpfc_create_port(phba, phba->brd_no, &phba->pcidev->dev);
|
2007-06-18 08:56:36 +08:00
|
|
|
if (!vport)
|
|
|
|
goto out_kthread_stop;
|
|
|
|
|
|
|
|
shost = lpfc_shost_from_vport(vport);
|
|
|
|
phba->pport = vport;
|
2007-06-18 08:56:39 +08:00
|
|
|
lpfc_debugfs_initialize(vport);
|
2007-06-18 08:56:36 +08:00
|
|
|
|
2007-06-18 08:56:38 +08:00
|
|
|
pci_set_drvdata(pdev, shost);
|
2008-02-09 07:49:51 +08:00
|
|
|
phba->intr_type = NONE;
|
|
|
|
|
|
|
|
if (phba->cfg_use_msi == 2) {
|
|
|
|
error = lpfc_enable_msix(phba);
|
|
|
|
if (!error)
|
|
|
|
phba->intr_type = MSIX;
|
|
|
|
}
|
2005-04-18 05:05:31 +08:00
|
|
|
|
2008-02-09 07:49:51 +08:00
|
|
|
/* Fallback to MSI if MSI-X initialization failed */
|
|
|
|
if (phba->cfg_use_msi >= 1 && phba->intr_type == NONE) {
|
2007-10-28 01:37:33 +08:00
|
|
|
retval = pci_enable_msi(phba->pcidev);
|
|
|
|
if (!retval)
|
2008-02-09 07:49:51 +08:00
|
|
|
phba->intr_type = MSI;
|
2007-08-02 23:10:31 +08:00
|
|
|
else
|
2007-08-02 23:10:09 +08:00
|
|
|
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
|
|
|
|
"0452 Enable MSI failed, continuing "
|
|
|
|
"with IRQ\n");
|
2006-12-03 02:34:56 +08:00
|
|
|
}
|
|
|
|
|
2008-02-09 07:49:51 +08:00
|
|
|
/* MSI-X is the only case the doesn't need to call request_irq */
|
|
|
|
if (phba->intr_type != MSIX) {
|
|
|
|
retval = request_irq(phba->pcidev->irq, lpfc_intr_handler,
|
|
|
|
IRQF_SHARED, LPFC_DRIVER_NAME, phba);
|
|
|
|
if (retval) {
|
|
|
|
lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0451 Enable "
|
|
|
|
"interrupt handler failed\n");
|
|
|
|
error = retval;
|
|
|
|
goto out_disable_msi;
|
|
|
|
} else if (phba->intr_type != MSI)
|
|
|
|
phba->intr_type = INTx;
|
2005-04-18 05:05:31 +08:00
|
|
|
}
|
|
|
|
|
2007-06-18 08:56:36 +08:00
|
|
|
phba->MBslimaddr = phba->slim_memmap_p;
|
|
|
|
phba->HAregaddr = phba->ctrl_regs_memmap_p + HA_REG_OFFSET;
|
|
|
|
phba->CAregaddr = phba->ctrl_regs_memmap_p + CA_REG_OFFSET;
|
|
|
|
phba->HSregaddr = phba->ctrl_regs_memmap_p + HS_REG_OFFSET;
|
|
|
|
phba->HCregaddr = phba->ctrl_regs_memmap_p + HC_REG_OFFSET;
|
|
|
|
|
2007-10-28 01:37:33 +08:00
|
|
|
if (lpfc_alloc_sysfs_attr(vport)) {
|
|
|
|
error = -ENOMEM;
|
2005-04-18 05:05:31 +08:00
|
|
|
goto out_free_irq;
|
2007-10-28 01:37:33 +08:00
|
|
|
}
|
[SCSI] lpfc 8.1.1 : Add polled-mode support
- Add functionality to run in polled mode only. Includes run time
attribute to enable mode.
- Enable runtime writable hba settings for coallescing and delay parameters
Customers have requested a mode in the driver to run strictly polled.
This is generally to support an environment where the server is extremely
loaded and is looking to reclaim some cpu cycles from adapter interrupt
handling.
This patch adds a new "poll" attribute, and the following behavior:
if value is 0 (default):
The driver uses the normal method for i/o completion. It uses the
firmware feature of interrupt coalesing. The firmware allows a
minimum number of i/o completions before an interrupt, or a maximum
time delay between interrupts. By default, the driver sets these
to no delay (disabled) or 1 i/o - meaning coalescing is disabled.
Attributes were provided to change the coalescing values, but it was
a module-load time only and global across all adapters.
This patch allows them to be writable on a per-adapter basis.
if value is 1 :
Interrupts are left enabled, expecting that the user has tuned the
interrupt coalescing values. When this setting is enabled, the driver
will attempt to service completed i/o whenever new i/o is submitted
to the adapter. If the coalescing values are large, and the i/o
generation rate steady, an interrupt will be avoided by servicing
completed i/o prior to the coalescing thresholds kicking in. However,
if the i/o completion load is high enough or i/o generation slow, the
coalescion values will ensure that completed i/o is serviced in a timely
fashion.
if value is 3 :
Turns off FCP i/o interrupts altogether. The coalescing values now have
no effect. A new attribute "poll_tmo" (default 10ms) exists to set
the polling interval for i/o completion. When this setting is enabled,
the driver will attempt to service completed i/o and restart the
interval timer whenever new i/o is submitted. This behavior allows for
servicing of completed i/o sooner than the interval timer, but ensures
that if no i/o is being issued, then the interval timer will kick in
to service the outstanding i/o.
Signed-off-by: James Smart <James.Smart@emulex.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
2005-11-30 05:32:13 +08:00
|
|
|
|
2007-10-28 01:37:33 +08:00
|
|
|
if (lpfc_sli_hba_setup(phba)) {
|
|
|
|
error = -ENODEV;
|
2007-06-18 08:56:39 +08:00
|
|
|
goto out_remove_device;
|
2007-10-28 01:37:33 +08:00
|
|
|
}
|
2007-06-18 08:56:39 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* hba setup may have changed the hba_queue_depth so we need to adjust
|
|
|
|
* the value of can_queue.
|
|
|
|
*/
|
|
|
|
shost->can_queue = phba->cfg_hba_queue_depth - 10;
|
|
|
|
|
|
|
|
lpfc_host_attrib_init(shost);
|
|
|
|
|
2007-06-18 08:56:36 +08:00
|
|
|
if (phba->cfg_poll & DISABLE_FCP_RING_INT) {
|
|
|
|
spin_lock_irq(shost->host_lock);
|
|
|
|
lpfc_poll_start_timer(phba);
|
|
|
|
spin_unlock_irq(shost->host_lock);
|
|
|
|
}
|
2005-04-18 05:05:31 +08:00
|
|
|
|
2007-06-18 08:56:39 +08:00
|
|
|
scsi_scan_host(shost);
|
2005-04-18 05:05:31 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
2007-06-18 08:56:39 +08:00
|
|
|
out_remove_device:
|
|
|
|
lpfc_free_sysfs_attr(vport);
|
|
|
|
spin_lock_irq(shost->host_lock);
|
2007-08-02 23:10:31 +08:00
|
|
|
vport->load_flag |= FC_UNLOADING;
|
2007-06-18 08:56:39 +08:00
|
|
|
spin_unlock_irq(shost->host_lock);
|
2005-04-18 05:05:31 +08:00
|
|
|
out_free_irq:
|
2007-06-18 08:56:38 +08:00
|
|
|
lpfc_stop_phba_timers(phba);
|
2007-06-18 08:56:36 +08:00
|
|
|
phba->pport->work_port_events = 0;
|
2008-02-09 07:49:51 +08:00
|
|
|
|
|
|
|
if (phba->intr_type == MSIX)
|
|
|
|
lpfc_disable_msix(phba);
|
|
|
|
else
|
|
|
|
free_irq(phba->pcidev->irq, phba);
|
|
|
|
|
2007-06-18 08:56:38 +08:00
|
|
|
out_disable_msi:
|
2008-02-09 07:49:51 +08:00
|
|
|
if (phba->intr_type == MSI)
|
2007-08-02 23:10:31 +08:00
|
|
|
pci_disable_msi(phba->pcidev);
|
2007-06-18 08:56:36 +08:00
|
|
|
destroy_port(vport);
|
2005-04-18 05:05:31 +08:00
|
|
|
out_kthread_stop:
|
|
|
|
kthread_stop(phba->worker_thread);
|
|
|
|
out_free_iocbq:
|
|
|
|
list_for_each_entry_safe(iocbq_entry, iocbq_next,
|
|
|
|
&phba->lpfc_iocb_list, list) {
|
|
|
|
kfree(iocbq_entry);
|
|
|
|
phba->total_iocbq_bufs--;
|
|
|
|
}
|
|
|
|
lpfc_mem_free(phba);
|
2007-06-18 08:56:37 +08:00
|
|
|
out_free_hbqslimp:
|
2008-08-25 09:49:55 +08:00
|
|
|
dma_free_coherent(&pdev->dev, lpfc_sli_hbq_size(),
|
|
|
|
phba->hbqslimp.virt, phba->hbqslimp.phys);
|
2005-04-18 05:05:31 +08:00
|
|
|
out_free_slim:
|
2008-08-25 09:49:55 +08:00
|
|
|
dma_free_coherent(&pdev->dev, SLI2_SLIM_SIZE,
|
|
|
|
phba->slim2p.virt, phba->slim2p.phys);
|
2005-04-18 05:05:31 +08:00
|
|
|
out_iounmap:
|
|
|
|
iounmap(phba->ctrl_regs_memmap_p);
|
2006-03-01 08:25:19 +08:00
|
|
|
out_iounmap_slim:
|
2005-04-18 05:05:31 +08:00
|
|
|
iounmap(phba->slim_memmap_p);
|
|
|
|
out_idr_remove:
|
|
|
|
idr_remove(&lpfc_hba_index, phba->brd_no);
|
2007-06-18 08:56:36 +08:00
|
|
|
out_free_phba:
|
|
|
|
kfree(phba);
|
2005-04-18 05:05:31 +08:00
|
|
|
out_release_regions:
|
2008-01-11 14:53:00 +08:00
|
|
|
pci_release_selected_regions(pdev, bars);
|
2005-04-18 05:05:31 +08:00
|
|
|
out_disable_device:
|
|
|
|
pci_disable_device(pdev);
|
|
|
|
out:
|
2006-04-17 10:26:50 +08:00
|
|
|
pci_set_drvdata(pdev, NULL);
|
2007-06-18 08:56:39 +08:00
|
|
|
if (shost)
|
|
|
|
scsi_host_put(shost);
|
2005-04-18 05:05:31 +08:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_pci_remove_one: lpfc PCI func to unregister device from PCI subsystem.
|
|
|
|
* @pdev: pointer to PCI device
|
|
|
|
*
|
|
|
|
* This routine is to be registered to the kernel's PCI subsystem. When an
|
|
|
|
* Emulex HBA is removed from PCI bus. It perform all the necessary cleanup
|
|
|
|
* for the HBA device to be removed from the PCI subsystem properly.
|
|
|
|
**/
|
2005-04-18 05:05:31 +08:00
|
|
|
static void __devexit
|
|
|
|
lpfc_pci_remove_one(struct pci_dev *pdev)
|
|
|
|
{
|
2007-06-18 08:56:36 +08:00
|
|
|
struct Scsi_Host *shost = pci_get_drvdata(pdev);
|
|
|
|
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
|
|
|
|
struct lpfc_hba *phba = vport->phba;
|
2008-01-11 14:53:00 +08:00
|
|
|
int bars = pci_select_bars(pdev, IORESOURCE_MEM);
|
|
|
|
|
2007-08-02 23:09:51 +08:00
|
|
|
spin_lock_irq(&phba->hbalock);
|
2007-08-02 23:10:31 +08:00
|
|
|
vport->load_flag |= FC_UNLOADING;
|
2007-08-02 23:09:51 +08:00
|
|
|
spin_unlock_irq(&phba->hbalock);
|
2007-06-18 08:56:36 +08:00
|
|
|
|
2007-06-18 08:56:39 +08:00
|
|
|
kfree(vport->vname);
|
|
|
|
lpfc_free_sysfs_attr(vport);
|
|
|
|
|
2008-01-11 14:52:54 +08:00
|
|
|
kthread_stop(phba->worker_thread);
|
|
|
|
|
2007-06-18 08:56:39 +08:00
|
|
|
fc_remove_host(shost);
|
|
|
|
scsi_remove_host(shost);
|
2007-10-28 01:37:43 +08:00
|
|
|
lpfc_cleanup(vport);
|
|
|
|
|
2007-06-18 08:56:36 +08:00
|
|
|
/*
|
|
|
|
* Bring down the SLI Layer. This step disable all interrupts,
|
|
|
|
* clears the rings, discards all mailbox commands, and resets
|
|
|
|
* the HBA.
|
|
|
|
*/
|
|
|
|
lpfc_sli_hba_down(phba);
|
|
|
|
lpfc_sli_brdrestart(phba);
|
|
|
|
|
2007-06-18 08:56:38 +08:00
|
|
|
lpfc_stop_phba_timers(phba);
|
2007-06-18 08:56:39 +08:00
|
|
|
spin_lock_irq(&phba->hbalock);
|
|
|
|
list_del_init(&vport->listentry);
|
|
|
|
spin_unlock_irq(&phba->hbalock);
|
|
|
|
|
|
|
|
lpfc_debugfs_terminate(vport);
|
2007-06-18 08:56:36 +08:00
|
|
|
|
2008-02-09 07:49:51 +08:00
|
|
|
if (phba->intr_type == MSIX)
|
|
|
|
lpfc_disable_msix(phba);
|
|
|
|
else {
|
|
|
|
free_irq(phba->pcidev->irq, phba);
|
|
|
|
if (phba->intr_type == MSI)
|
|
|
|
pci_disable_msi(phba->pcidev);
|
|
|
|
}
|
2005-04-18 05:05:31 +08:00
|
|
|
|
|
|
|
pci_set_drvdata(pdev, NULL);
|
2007-06-18 08:56:39 +08:00
|
|
|
scsi_host_put(shost);
|
2007-06-18 08:56:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Call scsi_free before mem_free since scsi bufs are released to their
|
|
|
|
* corresponding pools here.
|
|
|
|
*/
|
|
|
|
lpfc_scsi_free(phba);
|
|
|
|
lpfc_mem_free(phba);
|
|
|
|
|
2008-08-25 09:49:55 +08:00
|
|
|
dma_free_coherent(&pdev->dev, lpfc_sli_hbq_size(),
|
|
|
|
phba->hbqslimp.virt, phba->hbqslimp.phys);
|
2007-06-18 08:56:37 +08:00
|
|
|
|
2007-06-18 08:56:36 +08:00
|
|
|
/* Free resources associated with SLI2 interface */
|
|
|
|
dma_free_coherent(&pdev->dev, SLI2_SLIM_SIZE,
|
2008-08-25 09:49:55 +08:00
|
|
|
phba->slim2p.virt, phba->slim2p.phys);
|
2007-06-18 08:56:36 +08:00
|
|
|
|
|
|
|
/* unmap adapter SLIM and Control Registers */
|
|
|
|
iounmap(phba->ctrl_regs_memmap_p);
|
|
|
|
iounmap(phba->slim_memmap_p);
|
|
|
|
|
|
|
|
idr_remove(&lpfc_hba_index, phba->brd_no);
|
|
|
|
|
|
|
|
kfree(phba);
|
|
|
|
|
2008-01-11 14:53:00 +08:00
|
|
|
pci_release_selected_regions(pdev, bars);
|
2007-06-18 08:56:36 +08:00
|
|
|
pci_disable_device(pdev);
|
2005-04-18 05:05:31 +08:00
|
|
|
}
|
|
|
|
|
2007-02-15 04:28:36 +08:00
|
|
|
/**
|
2008-08-25 09:49:00 +08:00
|
|
|
* lpfc_io_error_detected: Driver method for handling PCI I/O error detected.
|
|
|
|
* @pdev: pointer to PCI device.
|
|
|
|
* @state: the current PCI connection state.
|
2007-02-15 04:28:36 +08:00
|
|
|
*
|
2008-08-25 09:49:00 +08:00
|
|
|
* This routine is registered to the PCI subsystem for error handling. This
|
|
|
|
* function is called by the PCI subsystem after a PCI bus error affecting
|
|
|
|
* this device has been detected. When this function is invoked, it will
|
|
|
|
* need to stop all the I/Os and interrupt(s) to the device. Once that is
|
|
|
|
* done, it will return PCI_ERS_RESULT_NEED_RESET for the PCI subsystem to
|
|
|
|
* perform proper recovery as desired.
|
|
|
|
*
|
|
|
|
* Return codes
|
|
|
|
* PCI_ERS_RESULT_NEED_RESET - need to reset before recovery
|
|
|
|
* PCI_ERS_RESULT_DISCONNECT - device could not be recovered
|
|
|
|
**/
|
2007-02-15 04:28:36 +08:00
|
|
|
static pci_ers_result_t lpfc_io_error_detected(struct pci_dev *pdev,
|
|
|
|
pci_channel_state_t state)
|
|
|
|
{
|
2007-08-02 23:10:31 +08:00
|
|
|
struct Scsi_Host *shost = pci_get_drvdata(pdev);
|
|
|
|
struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
|
2007-02-15 04:28:36 +08:00
|
|
|
struct lpfc_sli *psli = &phba->sli;
|
|
|
|
struct lpfc_sli_ring *pring;
|
|
|
|
|
2007-03-09 06:19:11 +08:00
|
|
|
if (state == pci_channel_io_perm_failure)
|
2007-02-15 04:28:36 +08:00
|
|
|
return PCI_ERS_RESULT_DISCONNECT;
|
2007-03-09 06:19:11 +08:00
|
|
|
|
2007-02-15 04:28:36 +08:00
|
|
|
pci_disable_device(pdev);
|
|
|
|
/*
|
|
|
|
* There may be I/Os dropped by the firmware.
|
|
|
|
* Error iocb (I/O) on txcmplq and let the SCSI layer
|
|
|
|
* retry it after re-establishing link.
|
|
|
|
*/
|
|
|
|
pring = &psli->ring[psli->fcp_ring];
|
|
|
|
lpfc_sli_abort_iocb_ring(phba, pring);
|
|
|
|
|
2008-02-09 07:49:51 +08:00
|
|
|
if (phba->intr_type == MSIX)
|
|
|
|
lpfc_disable_msix(phba);
|
|
|
|
else {
|
|
|
|
free_irq(phba->pcidev->irq, phba);
|
|
|
|
if (phba->intr_type == MSI)
|
|
|
|
pci_disable_msi(phba->pcidev);
|
|
|
|
}
|
2007-08-02 23:10:31 +08:00
|
|
|
|
2007-02-15 04:28:36 +08:00
|
|
|
/* Request a slot reset. */
|
|
|
|
return PCI_ERS_RESULT_NEED_RESET;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-08-25 09:49:00 +08:00
|
|
|
* lpfc_io_slot_reset: Restart a PCI device from scratch.
|
|
|
|
* @pdev: pointer to PCI device.
|
|
|
|
*
|
|
|
|
* This routine is registered to the PCI subsystem for error handling. This is
|
|
|
|
* called after PCI bus has been reset to restart the PCI card from scratch,
|
|
|
|
* as if from a cold-boot. During the PCI subsystem error recovery, after the
|
|
|
|
* driver returns PCI_ERS_RESULT_NEED_RESET, the PCI subsystem will perform
|
|
|
|
* proper error recovery and then call this routine before calling the .resume
|
|
|
|
* method to recover the device. This function will initialize the HBA device,
|
|
|
|
* enable the interrupt, but it will just put the HBA to offline state without
|
|
|
|
* passing any I/O traffic.
|
2007-02-15 04:28:36 +08:00
|
|
|
*
|
2008-08-25 09:49:00 +08:00
|
|
|
* Return codes
|
|
|
|
* PCI_ERS_RESULT_RECOVERED - the device has been recovered
|
|
|
|
* PCI_ERS_RESULT_DISCONNECT - device could not be recovered
|
2007-02-15 04:28:36 +08:00
|
|
|
*/
|
|
|
|
static pci_ers_result_t lpfc_io_slot_reset(struct pci_dev *pdev)
|
|
|
|
{
|
2007-08-02 23:10:31 +08:00
|
|
|
struct Scsi_Host *shost = pci_get_drvdata(pdev);
|
|
|
|
struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
|
2007-02-15 04:28:36 +08:00
|
|
|
struct lpfc_sli *psli = &phba->sli;
|
2008-04-07 22:16:00 +08:00
|
|
|
int error, retval;
|
2007-02-15 04:28:36 +08:00
|
|
|
|
|
|
|
dev_printk(KERN_INFO, &pdev->dev, "recovering from a slot reset.\n");
|
2007-12-20 12:28:09 +08:00
|
|
|
if (pci_enable_device_mem(pdev)) {
|
2007-02-15 04:28:36 +08:00
|
|
|
printk(KERN_ERR "lpfc: Cannot re-enable "
|
|
|
|
"PCI device after reset.\n");
|
|
|
|
return PCI_ERS_RESULT_DISCONNECT;
|
|
|
|
}
|
|
|
|
|
|
|
|
pci_set_master(pdev);
|
|
|
|
|
2007-06-18 08:56:38 +08:00
|
|
|
spin_lock_irq(&phba->hbalock);
|
2007-02-15 04:28:36 +08:00
|
|
|
psli->sli_flag &= ~LPFC_SLI2_ACTIVE;
|
2007-06-18 08:56:38 +08:00
|
|
|
spin_unlock_irq(&phba->hbalock);
|
2007-02-15 04:28:36 +08:00
|
|
|
|
2008-04-07 22:16:00 +08:00
|
|
|
/* Enable configured interrupt method */
|
|
|
|
phba->intr_type = NONE;
|
|
|
|
if (phba->cfg_use_msi == 2) {
|
|
|
|
error = lpfc_enable_msix(phba);
|
|
|
|
if (!error)
|
|
|
|
phba->intr_type = MSIX;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Fallback to MSI if MSI-X initialization failed */
|
|
|
|
if (phba->cfg_use_msi >= 1 && phba->intr_type == NONE) {
|
|
|
|
retval = pci_enable_msi(phba->pcidev);
|
|
|
|
if (!retval)
|
|
|
|
phba->intr_type = MSI;
|
|
|
|
else
|
|
|
|
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
|
|
|
|
"0470 Enable MSI failed, continuing "
|
|
|
|
"with IRQ\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* MSI-X is the only case the doesn't need to call request_irq */
|
|
|
|
if (phba->intr_type != MSIX) {
|
|
|
|
retval = request_irq(phba->pcidev->irq, lpfc_intr_handler,
|
|
|
|
IRQF_SHARED, LPFC_DRIVER_NAME, phba);
|
|
|
|
if (retval) {
|
|
|
|
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
|
|
|
|
"0471 Enable interrupt handler "
|
|
|
|
"failed\n");
|
|
|
|
} else if (phba->intr_type != MSI)
|
|
|
|
phba->intr_type = INTx;
|
|
|
|
}
|
2007-02-15 04:28:36 +08:00
|
|
|
|
|
|
|
/* Take device offline; this will perform cleanup */
|
|
|
|
lpfc_offline(phba);
|
|
|
|
lpfc_sli_brdrestart(phba);
|
|
|
|
|
|
|
|
return PCI_ERS_RESULT_RECOVERED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-08-25 09:49:00 +08:00
|
|
|
* lpfc_io_resume: Resume PCI I/O operation.
|
|
|
|
* @pdev: pointer to PCI device
|
2007-02-15 04:28:36 +08:00
|
|
|
*
|
2008-08-25 09:49:00 +08:00
|
|
|
* This routine is registered to the PCI subsystem for error handling. It is
|
|
|
|
* called when kernel error recovery tells the lpfc driver that it is ok to
|
|
|
|
* resume normal PCI operation after PCI bus error recovery. After this call,
|
|
|
|
* traffic can start to flow from this device again.
|
2007-02-15 04:28:36 +08:00
|
|
|
*/
|
|
|
|
static void lpfc_io_resume(struct pci_dev *pdev)
|
|
|
|
{
|
2007-08-02 23:10:31 +08:00
|
|
|
struct Scsi_Host *shost = pci_get_drvdata(pdev);
|
|
|
|
struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
|
2007-02-15 04:28:36 +08:00
|
|
|
|
2008-04-07 22:15:56 +08:00
|
|
|
lpfc_online(phba);
|
2007-02-15 04:28:36 +08:00
|
|
|
}
|
|
|
|
|
2005-04-18 05:05:31 +08:00
|
|
|
static struct pci_device_id lpfc_id_table[] = {
|
|
|
|
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_VIPER,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, },
|
2005-06-25 22:34:22 +08:00
|
|
|
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_FIREFLY,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, },
|
2005-04-18 05:05:31 +08:00
|
|
|
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_THOR,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, },
|
|
|
|
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_PEGASUS,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, },
|
|
|
|
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_CENTAUR,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, },
|
|
|
|
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_DRAGONFLY,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, },
|
|
|
|
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SUPERFLY,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, },
|
|
|
|
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_RFLY,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, },
|
|
|
|
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_PFLY,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, },
|
2005-11-29 00:42:12 +08:00
|
|
|
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_NEPTUNE,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, },
|
|
|
|
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_NEPTUNE_SCSP,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, },
|
|
|
|
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_NEPTUNE_DCSP,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, },
|
2005-04-18 05:05:31 +08:00
|
|
|
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_HELIOS,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, },
|
2005-11-29 00:42:12 +08:00
|
|
|
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_HELIOS_SCSP,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, },
|
|
|
|
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_HELIOS_DCSP,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, },
|
2005-04-18 05:05:31 +08:00
|
|
|
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_BMID,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, },
|
|
|
|
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_BSMB,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, },
|
|
|
|
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_ZEPHYR,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, },
|
2005-11-29 00:42:12 +08:00
|
|
|
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_ZEPHYR_SCSP,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, },
|
|
|
|
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_ZEPHYR_DCSP,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, },
|
2005-04-18 05:05:31 +08:00
|
|
|
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_ZMID,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, },
|
|
|
|
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_ZSMB,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, },
|
|
|
|
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_TFLY,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, },
|
|
|
|
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LP101,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, },
|
|
|
|
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LP10000S,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, },
|
2005-11-29 00:42:12 +08:00
|
|
|
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LP11000S,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, },
|
|
|
|
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LPE11000S,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, },
|
2007-04-25 21:53:28 +08:00
|
|
|
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SAT,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, },
|
|
|
|
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SAT_MID,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, },
|
|
|
|
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SAT_SMB,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, },
|
|
|
|
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SAT_DCSP,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, },
|
|
|
|
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SAT_SCSP,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, },
|
|
|
|
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SAT_S,
|
|
|
|
PCI_ANY_ID, PCI_ANY_ID, },
|
2005-04-18 05:05:31 +08:00
|
|
|
{ 0 }
|
|
|
|
};
|
|
|
|
|
|
|
|
MODULE_DEVICE_TABLE(pci, lpfc_id_table);
|
|
|
|
|
2007-02-15 04:28:36 +08:00
|
|
|
static struct pci_error_handlers lpfc_err_handler = {
|
|
|
|
.error_detected = lpfc_io_error_detected,
|
|
|
|
.slot_reset = lpfc_io_slot_reset,
|
|
|
|
.resume = lpfc_io_resume,
|
|
|
|
};
|
|
|
|
|
2005-04-18 05:05:31 +08:00
|
|
|
static struct pci_driver lpfc_driver = {
|
|
|
|
.name = LPFC_DRIVER_NAME,
|
|
|
|
.id_table = lpfc_id_table,
|
|
|
|
.probe = lpfc_pci_probe_one,
|
|
|
|
.remove = __devexit_p(lpfc_pci_remove_one),
|
2007-06-18 08:56:36 +08:00
|
|
|
.err_handler = &lpfc_err_handler,
|
2005-04-18 05:05:31 +08:00
|
|
|
};
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_init: lpfc module initialization routine.
|
|
|
|
*
|
|
|
|
* This routine is to be invoked when the lpfc module is loaded into the
|
|
|
|
* kernel. The special kernel macro module_init() is used to indicate the
|
|
|
|
* role of this routine to the kernel as lpfc module entry point.
|
|
|
|
*
|
|
|
|
* Return codes
|
|
|
|
* 0 - successful
|
|
|
|
* -ENOMEM - FC attach transport failed
|
|
|
|
* all others - failed
|
|
|
|
*/
|
2005-04-18 05:05:31 +08:00
|
|
|
static int __init
|
|
|
|
lpfc_init(void)
|
|
|
|
{
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
printk(LPFC_MODULE_DESC "\n");
|
2005-06-25 22:34:39 +08:00
|
|
|
printk(LPFC_COPYRIGHT "\n");
|
2005-04-18 05:05:31 +08:00
|
|
|
|
2007-10-28 01:37:17 +08:00
|
|
|
if (lpfc_enable_npiv) {
|
|
|
|
lpfc_transport_functions.vport_create = lpfc_vport_create;
|
|
|
|
lpfc_transport_functions.vport_delete = lpfc_vport_delete;
|
|
|
|
}
|
2005-04-18 05:05:31 +08:00
|
|
|
lpfc_transport_template =
|
|
|
|
fc_attach_transport(&lpfc_transport_functions);
|
2007-10-28 01:37:17 +08:00
|
|
|
if (lpfc_transport_template == NULL)
|
2005-04-18 05:05:31 +08:00
|
|
|
return -ENOMEM;
|
2007-10-28 01:37:17 +08:00
|
|
|
if (lpfc_enable_npiv) {
|
|
|
|
lpfc_vport_transport_template =
|
2007-10-28 01:37:33 +08:00
|
|
|
fc_attach_transport(&lpfc_vport_transport_functions);
|
|
|
|
if (lpfc_vport_transport_template == NULL) {
|
|
|
|
fc_release_transport(lpfc_transport_template);
|
2007-10-28 01:37:17 +08:00
|
|
|
return -ENOMEM;
|
2007-10-28 01:37:33 +08:00
|
|
|
}
|
2007-10-28 01:37:17 +08:00
|
|
|
}
|
2005-04-18 05:05:31 +08:00
|
|
|
error = pci_register_driver(&lpfc_driver);
|
2007-06-18 08:56:38 +08:00
|
|
|
if (error) {
|
2005-04-18 05:05:31 +08:00
|
|
|
fc_release_transport(lpfc_transport_template);
|
2007-06-18 08:56:38 +08:00
|
|
|
fc_release_transport(lpfc_vport_transport_template);
|
|
|
|
}
|
2005-04-18 05:05:31 +08:00
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2008-08-25 09:49:00 +08:00
|
|
|
/**
|
|
|
|
* lpfc_exit: lpfc module removal routine.
|
|
|
|
*
|
|
|
|
* This routine is invoked when the lpfc module is removed from the kernel.
|
|
|
|
* The special kernel macro module_exit() is used to indicate the role of
|
|
|
|
* this routine to the kernel as lpfc module exit point.
|
|
|
|
*/
|
2005-04-18 05:05:31 +08:00
|
|
|
static void __exit
|
|
|
|
lpfc_exit(void)
|
|
|
|
{
|
|
|
|
pci_unregister_driver(&lpfc_driver);
|
|
|
|
fc_release_transport(lpfc_transport_template);
|
2007-10-28 01:37:17 +08:00
|
|
|
if (lpfc_enable_npiv)
|
|
|
|
fc_release_transport(lpfc_vport_transport_template);
|
2005-04-18 05:05:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
module_init(lpfc_init);
|
|
|
|
module_exit(lpfc_exit);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_DESCRIPTION(LPFC_MODULE_DESC);
|
|
|
|
MODULE_AUTHOR("Emulex Corporation - tech.support@emulex.com");
|
|
|
|
MODULE_VERSION("0:" LPFC_DRIVER_VERSION);
|