linux/drivers/scsi/bfa/bfad.c
Krishna Gudipati e67143243a [SCSI] bfa: Resume BFA operations after firmware mismatch is resolved.
bfad.c & bfad_drv.h:
  * Created a kernel thread from pci_probe that does the bfad start
    operations after BFA init done on a firmware mismatch.
  * The kernel thread on a fw mismatch waits for an event from IOC
    call back and is woken up from bfa_cb_init() on BFA init success.
  * In normal cases of no firmware mismatch this thread is terminated
    in pci_probe.

bfa_fcs_lport.c, fabric.c, fcs_lport.h & vport.c:
  * Split the lport init to attach time and init time code, so that
    proper config attributes are set after firmware mismatch.

bfa_iocfc.c:
  * Handle an IOC timer issue, where the IOC timer would expire before
    the init completion and send Init fail event to the driver,
    however IOC init continues and completes successfully at the later
    stage. The bfa and driver were not handling this kind of deferred
    init completion.

Signed-off-by: Krishna Gudipati <kgudipat@brocade.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
2010-03-04 16:17:02 +05:30

1296 lines
30 KiB
C

/*
* Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
* All rights reserved
* www.brocade.com
*
* Linux driver for Brocade Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
* published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
/**
* bfad.c Linux driver PCI interface module.
*/
#include <linux/module.h>
#include <linux/kthread.h>
#include "bfad_drv.h"
#include "bfad_im.h"
#include "bfad_tm.h"
#include "bfad_ipfc.h"
#include "bfad_trcmod.h"
#include <fcb/bfa_fcb_vf.h>
#include <fcb/bfa_fcb_rport.h>
#include <fcb/bfa_fcb_port.h>
#include <fcb/bfa_fcb.h>
BFA_TRC_FILE(LDRV, BFAD);
static DEFINE_MUTEX(bfad_mutex);
LIST_HEAD(bfad_list);
static int bfad_inst;
int bfad_supported_fc4s;
static char *host_name;
static char *os_name;
static char *os_patch;
static int num_rports;
static int num_ios;
static int num_tms;
static int num_fcxps;
static int num_ufbufs;
static int reqq_size;
static int rspq_size;
static int num_sgpgs;
static int rport_del_timeout = BFA_FCS_RPORT_DEF_DEL_TIMEOUT;
static int bfa_io_max_sge = BFAD_IO_MAX_SGE;
static int log_level = BFA_LOG_WARNING;
static int ioc_auto_recover = BFA_TRUE;
static int ipfc_enable = BFA_FALSE;
static int ipfc_mtu = -1;
static int fdmi_enable = BFA_TRUE;
int bfa_lun_queue_depth = BFAD_LUN_QUEUE_DEPTH;
int bfa_linkup_delay = -1;
module_param(os_name, charp, S_IRUGO | S_IWUSR);
module_param(os_patch, charp, S_IRUGO | S_IWUSR);
module_param(host_name, charp, S_IRUGO | S_IWUSR);
module_param(num_rports, int, S_IRUGO | S_IWUSR);
module_param(num_ios, int, S_IRUGO | S_IWUSR);
module_param(num_tms, int, S_IRUGO | S_IWUSR);
module_param(num_fcxps, int, S_IRUGO | S_IWUSR);
module_param(num_ufbufs, int, S_IRUGO | S_IWUSR);
module_param(reqq_size, int, S_IRUGO | S_IWUSR);
module_param(rspq_size, int, S_IRUGO | S_IWUSR);
module_param(num_sgpgs, int, S_IRUGO | S_IWUSR);
module_param(rport_del_timeout, int, S_IRUGO | S_IWUSR);
module_param(bfa_lun_queue_depth, int, S_IRUGO | S_IWUSR);
module_param(bfa_io_max_sge, int, S_IRUGO | S_IWUSR);
module_param(log_level, int, S_IRUGO | S_IWUSR);
module_param(ioc_auto_recover, int, S_IRUGO | S_IWUSR);
module_param(ipfc_enable, int, S_IRUGO | S_IWUSR);
module_param(ipfc_mtu, int, S_IRUGO | S_IWUSR);
module_param(fdmi_enable, int, S_IRUGO | S_IWUSR);
module_param(bfa_linkup_delay, int, S_IRUGO | S_IWUSR);
/*
* Stores the module parm num_sgpgs value;
* used to reset for bfad next instance.
*/
static int num_sgpgs_parm;
static bfa_status_t
bfad_fc4_probe(struct bfad_s *bfad)
{
int rc;
rc = bfad_im_probe(bfad);
if (rc != BFA_STATUS_OK)
goto ext;
bfad_tm_probe(bfad);
if (ipfc_enable)
bfad_ipfc_probe(bfad);
bfad->bfad_flags |= BFAD_FC4_PROBE_DONE;
ext:
return rc;
}
static void
bfad_fc4_probe_undo(struct bfad_s *bfad)
{
bfad_im_probe_undo(bfad);
bfad_tm_probe_undo(bfad);
if (ipfc_enable)
bfad_ipfc_probe_undo(bfad);
bfad->bfad_flags &= ~BFAD_FC4_PROBE_DONE;
}
static void
bfad_fc4_probe_post(struct bfad_s *bfad)
{
if (bfad->im)
bfad_im_probe_post(bfad->im);
bfad_tm_probe_post(bfad);
if (ipfc_enable)
bfad_ipfc_probe_post(bfad);
}
static bfa_status_t
bfad_fc4_port_new(struct bfad_s *bfad, struct bfad_port_s *port, int roles)
{
int rc = BFA_STATUS_FAILED;
if (roles & BFA_PORT_ROLE_FCP_IM)
rc = bfad_im_port_new(bfad, port);
if (rc != BFA_STATUS_OK)
goto ext;
if (roles & BFA_PORT_ROLE_FCP_TM)
rc = bfad_tm_port_new(bfad, port);
if (rc != BFA_STATUS_OK)
goto ext;
if ((roles & BFA_PORT_ROLE_FCP_IPFC) && ipfc_enable)
rc = bfad_ipfc_port_new(bfad, port, port->pvb_type);
ext:
return rc;
}
static void
bfad_fc4_port_delete(struct bfad_s *bfad, struct bfad_port_s *port, int roles)
{
if (roles & BFA_PORT_ROLE_FCP_IM)
bfad_im_port_delete(bfad, port);
if (roles & BFA_PORT_ROLE_FCP_TM)
bfad_tm_port_delete(bfad, port);
if ((roles & BFA_PORT_ROLE_FCP_IPFC) && ipfc_enable)
bfad_ipfc_port_delete(bfad, port);
}
/**
* BFA callbacks
*/
void
bfad_hcb_comp(void *arg, bfa_status_t status)
{
struct bfad_hal_comp *fcomp = (struct bfad_hal_comp *)arg;
fcomp->status = status;
complete(&fcomp->comp);
}
/**
* bfa_init callback
*/
void
bfa_cb_init(void *drv, bfa_status_t init_status)
{
struct bfad_s *bfad = drv;
if (init_status == BFA_STATUS_OK) {
bfad->bfad_flags |= BFAD_HAL_INIT_DONE;
/* If BFAD_HAL_INIT_FAIL flag is set:
* Wake up the kernel thread to start
* the bfad operations after HAL init done
*/
if ((bfad->bfad_flags & BFAD_HAL_INIT_FAIL)) {
bfad->bfad_flags &= ~BFAD_HAL_INIT_FAIL;
wake_up_process(bfad->bfad_tsk);
}
}
complete(&bfad->comp);
}
/**
* BFA_FCS callbacks
*/
static struct bfad_port_s *
bfad_get_drv_port(struct bfad_s *bfad, struct bfad_vf_s *vf_drv,
struct bfad_vport_s *vp_drv)
{
return (vp_drv) ? (&(vp_drv)->drv_port)
: ((vf_drv) ? (&(vf_drv)->base_port) : (&(bfad)->pport));
}
struct bfad_port_s *
bfa_fcb_port_new(struct bfad_s *bfad, struct bfa_fcs_port_s *port,
enum bfa_port_role roles, struct bfad_vf_s *vf_drv,
struct bfad_vport_s *vp_drv)
{
bfa_status_t rc;
struct bfad_port_s *port_drv;
if (!vp_drv && !vf_drv) {
port_drv = &bfad->pport;
port_drv->pvb_type = BFAD_PORT_PHYS_BASE;
} else if (!vp_drv && vf_drv) {
port_drv = &vf_drv->base_port;
port_drv->pvb_type = BFAD_PORT_VF_BASE;
} else if (vp_drv && !vf_drv) {
port_drv = &vp_drv->drv_port;
port_drv->pvb_type = BFAD_PORT_PHYS_VPORT;
} else {
port_drv = &vp_drv->drv_port;
port_drv->pvb_type = BFAD_PORT_VF_VPORT;
}
port_drv->fcs_port = port;
port_drv->roles = roles;
rc = bfad_fc4_port_new(bfad, port_drv, roles);
if (rc != BFA_STATUS_OK) {
bfad_fc4_port_delete(bfad, port_drv, roles);
port_drv = NULL;
}
return port_drv;
}
void
bfa_fcb_port_delete(struct bfad_s *bfad, enum bfa_port_role roles,
struct bfad_vf_s *vf_drv, struct bfad_vport_s *vp_drv)
{
struct bfad_port_s *port_drv;
/*
* this will be only called from rmmod context
*/
if (vp_drv && !vp_drv->comp_del) {
port_drv = bfad_get_drv_port(bfad, vf_drv, vp_drv);
bfa_trc(bfad, roles);
bfad_fc4_port_delete(bfad, port_drv, roles);
}
}
void
bfa_fcb_port_online(struct bfad_s *bfad, enum bfa_port_role roles,
struct bfad_vf_s *vf_drv, struct bfad_vport_s *vp_drv)
{
struct bfad_port_s *port_drv = bfad_get_drv_port(bfad, vf_drv, vp_drv);
if (roles & BFA_PORT_ROLE_FCP_IM)
bfad_im_port_online(bfad, port_drv);
if (roles & BFA_PORT_ROLE_FCP_TM)
bfad_tm_port_online(bfad, port_drv);
if ((roles & BFA_PORT_ROLE_FCP_IPFC) && ipfc_enable)
bfad_ipfc_port_online(bfad, port_drv);
bfad->bfad_flags |= BFAD_PORT_ONLINE;
}
void
bfa_fcb_port_offline(struct bfad_s *bfad, enum bfa_port_role roles,
struct bfad_vf_s *vf_drv, struct bfad_vport_s *vp_drv)
{
struct bfad_port_s *port_drv = bfad_get_drv_port(bfad, vf_drv, vp_drv);
if (roles & BFA_PORT_ROLE_FCP_IM)
bfad_im_port_offline(bfad, port_drv);
if (roles & BFA_PORT_ROLE_FCP_TM)
bfad_tm_port_offline(bfad, port_drv);
if ((roles & BFA_PORT_ROLE_FCP_IPFC) && ipfc_enable)
bfad_ipfc_port_offline(bfad, port_drv);
}
void
bfa_fcb_vport_delete(struct bfad_vport_s *vport_drv)
{
if (vport_drv->comp_del) {
complete(vport_drv->comp_del);
return;
}
kfree(vport_drv);
}
/**
* FCS RPORT alloc callback, after successful PLOGI by FCS
*/
bfa_status_t
bfa_fcb_rport_alloc(struct bfad_s *bfad, struct bfa_fcs_rport_s **rport,
struct bfad_rport_s **rport_drv)
{
bfa_status_t rc = BFA_STATUS_OK;
*rport_drv = kzalloc(sizeof(struct bfad_rport_s), GFP_ATOMIC);
if (*rport_drv == NULL) {
rc = BFA_STATUS_ENOMEM;
goto ext;
}
*rport = &(*rport_drv)->fcs_rport;
ext:
return rc;
}
void
bfad_hal_mem_release(struct bfad_s *bfad)
{
int i;
struct bfa_meminfo_s *hal_meminfo = &bfad->meminfo;
struct bfa_mem_elem_s *meminfo_elem;
for (i = 0; i < BFA_MEM_TYPE_MAX; i++) {
meminfo_elem = &hal_meminfo->meminfo[i];
if (meminfo_elem->kva != NULL) {
switch (meminfo_elem->mem_type) {
case BFA_MEM_TYPE_KVA:
vfree(meminfo_elem->kva);
break;
case BFA_MEM_TYPE_DMA:
dma_free_coherent(&bfad->pcidev->dev,
meminfo_elem->mem_len,
meminfo_elem->kva,
(dma_addr_t) meminfo_elem->dma);
break;
default:
bfa_assert(0);
break;
}
}
}
memset(hal_meminfo, 0, sizeof(struct bfa_meminfo_s));
}
void
bfad_update_hal_cfg(struct bfa_iocfc_cfg_s *bfa_cfg)
{
if (num_rports > 0)
bfa_cfg->fwcfg.num_rports = num_rports;
if (num_ios > 0)
bfa_cfg->fwcfg.num_ioim_reqs = num_ios;
if (num_tms > 0)
bfa_cfg->fwcfg.num_tskim_reqs = num_tms;
if (num_fcxps > 0)
bfa_cfg->fwcfg.num_fcxp_reqs = num_fcxps;
if (num_ufbufs > 0)
bfa_cfg->fwcfg.num_uf_bufs = num_ufbufs;
if (reqq_size > 0)
bfa_cfg->drvcfg.num_reqq_elems = reqq_size;
if (rspq_size > 0)
bfa_cfg->drvcfg.num_rspq_elems = rspq_size;
if (num_sgpgs > 0)
bfa_cfg->drvcfg.num_sgpgs = num_sgpgs;
/*
* populate the hal values back to the driver for sysfs use.
* otherwise, the default values will be shown as 0 in sysfs
*/
num_rports = bfa_cfg->fwcfg.num_rports;
num_ios = bfa_cfg->fwcfg.num_ioim_reqs;
num_tms = bfa_cfg->fwcfg.num_tskim_reqs;
num_fcxps = bfa_cfg->fwcfg.num_fcxp_reqs;
num_ufbufs = bfa_cfg->fwcfg.num_uf_bufs;
reqq_size = bfa_cfg->drvcfg.num_reqq_elems;
rspq_size = bfa_cfg->drvcfg.num_rspq_elems;
num_sgpgs = bfa_cfg->drvcfg.num_sgpgs;
}
bfa_status_t
bfad_hal_mem_alloc(struct bfad_s *bfad)
{
struct bfa_meminfo_s *hal_meminfo = &bfad->meminfo;
struct bfa_mem_elem_s *meminfo_elem;
bfa_status_t rc = BFA_STATUS_OK;
dma_addr_t phys_addr;
int retry_count = 0;
int reset_value = 1;
int min_num_sgpgs = 512;
void *kva;
int i;
bfa_cfg_get_default(&bfad->ioc_cfg);
retry:
bfad_update_hal_cfg(&bfad->ioc_cfg);
bfad->cfg_data.ioc_queue_depth = bfad->ioc_cfg.fwcfg.num_ioim_reqs;
bfa_cfg_get_meminfo(&bfad->ioc_cfg, hal_meminfo);
for (i = 0; i < BFA_MEM_TYPE_MAX; i++) {
meminfo_elem = &hal_meminfo->meminfo[i];
switch (meminfo_elem->mem_type) {
case BFA_MEM_TYPE_KVA:
kva = vmalloc(meminfo_elem->mem_len);
if (kva == NULL) {
bfad_hal_mem_release(bfad);
rc = BFA_STATUS_ENOMEM;
goto ext;
}
memset(kva, 0, meminfo_elem->mem_len);
meminfo_elem->kva = kva;
break;
case BFA_MEM_TYPE_DMA:
kva = dma_alloc_coherent(&bfad->pcidev->dev,
meminfo_elem->mem_len,
&phys_addr, GFP_KERNEL);
if (kva == NULL) {
bfad_hal_mem_release(bfad);
/*
* If we cannot allocate with default
* num_sgpages try with half the value.
*/
if (num_sgpgs > min_num_sgpgs) {
printk(KERN_INFO "bfad[%d]: memory"
" allocation failed with"
" num_sgpgs: %d\n",
bfad->inst_no, num_sgpgs);
nextLowerInt(&num_sgpgs);
printk(KERN_INFO "bfad[%d]: trying to"
" allocate memory with"
" num_sgpgs: %d\n",
bfad->inst_no, num_sgpgs);
retry_count++;
goto retry;
} else {
if (num_sgpgs_parm > 0)
num_sgpgs = num_sgpgs_parm;
else {
reset_value =
(1 << retry_count);
num_sgpgs *= reset_value;
}
rc = BFA_STATUS_ENOMEM;
goto ext;
}
}
if (num_sgpgs_parm > 0)
num_sgpgs = num_sgpgs_parm;
else {
reset_value = (1 << retry_count);
num_sgpgs *= reset_value;
}
memset(kva, 0, meminfo_elem->mem_len);
meminfo_elem->kva = kva;
meminfo_elem->dma = phys_addr;
break;
default:
break;
}
}
ext:
return rc;
}
/**
* Create a vport under a vf.
*/
bfa_status_t
bfad_vport_create(struct bfad_s *bfad, u16 vf_id,
struct bfa_port_cfg_s *port_cfg)
{
struct bfad_vport_s *vport;
int rc = BFA_STATUS_OK;
unsigned long flags;
struct completion fcomp;
vport = kzalloc(sizeof(struct bfad_vport_s), GFP_KERNEL);
if (!vport) {
rc = BFA_STATUS_ENOMEM;
goto ext;
}
vport->drv_port.bfad = bfad;
spin_lock_irqsave(&bfad->bfad_lock, flags);
rc = bfa_fcs_vport_create(&vport->fcs_vport, &bfad->bfa_fcs, vf_id,
port_cfg, vport);
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
if (rc != BFA_STATUS_OK)
goto ext_free_vport;
if (port_cfg->roles & BFA_PORT_ROLE_FCP_IM) {
rc = bfad_im_scsi_host_alloc(bfad, vport->drv_port.im_port);
if (rc != BFA_STATUS_OK)
goto ext_free_fcs_vport;
}
spin_lock_irqsave(&bfad->bfad_lock, flags);
bfa_fcs_vport_start(&vport->fcs_vport);
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
return BFA_STATUS_OK;
ext_free_fcs_vport:
spin_lock_irqsave(&bfad->bfad_lock, flags);
vport->comp_del = &fcomp;
init_completion(vport->comp_del);
bfa_fcs_vport_delete(&vport->fcs_vport);
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
wait_for_completion(vport->comp_del);
ext_free_vport:
kfree(vport);
ext:
return rc;
}
/**
* Create a vf and its base vport implicitely.
*/
bfa_status_t
bfad_vf_create(struct bfad_s *bfad, u16 vf_id,
struct bfa_port_cfg_s *port_cfg)
{
struct bfad_vf_s *vf;
int rc = BFA_STATUS_OK;
vf = kzalloc(sizeof(struct bfad_vf_s), GFP_KERNEL);
if (!vf) {
rc = BFA_STATUS_FAILED;
goto ext;
}
rc = bfa_fcs_vf_create(&vf->fcs_vf, &bfad->bfa_fcs, vf_id, port_cfg,
vf);
if (rc != BFA_STATUS_OK)
kfree(vf);
ext:
return rc;
}
void
bfad_bfa_tmo(unsigned long data)
{
struct bfad_s *bfad = (struct bfad_s *)data;
unsigned long flags;
struct list_head doneq;
spin_lock_irqsave(&bfad->bfad_lock, flags);
bfa_timer_tick(&bfad->bfa);
bfa_comp_deq(&bfad->bfa, &doneq);
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
if (!list_empty(&doneq)) {
bfa_comp_process(&bfad->bfa, &doneq);
spin_lock_irqsave(&bfad->bfad_lock, flags);
bfa_comp_free(&bfad->bfa, &doneq);
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
}
mod_timer(&bfad->hal_tmo, jiffies + msecs_to_jiffies(BFA_TIMER_FREQ));
}
void
bfad_init_timer(struct bfad_s *bfad)
{
init_timer(&bfad->hal_tmo);
bfad->hal_tmo.function = bfad_bfa_tmo;
bfad->hal_tmo.data = (unsigned long)bfad;
mod_timer(&bfad->hal_tmo, jiffies + msecs_to_jiffies(BFA_TIMER_FREQ));
}
int
bfad_pci_init(struct pci_dev *pdev, struct bfad_s *bfad)
{
unsigned long bar0_len;
int rc = -ENODEV;
if (pci_enable_device(pdev)) {
BFA_PRINTF(BFA_ERR, "pci_enable_device fail %p\n", pdev);
goto out;
}
if (pci_request_regions(pdev, BFAD_DRIVER_NAME))
goto out_disable_device;
pci_set_master(pdev);
if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) != 0)
if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0) {
BFA_PRINTF(BFA_ERR, "pci_set_dma_mask fail %p\n", pdev);
goto out_release_region;
}
bfad->pci_bar0_map = pci_resource_start(pdev, 0);
bar0_len = pci_resource_len(pdev, 0);
bfad->pci_bar0_kva = ioremap(bfad->pci_bar0_map, bar0_len);
if (bfad->pci_bar0_kva == NULL) {
BFA_PRINTF(BFA_ERR, "Fail to map bar0\n");
goto out_release_region;
}
bfad->hal_pcidev.pci_slot = PCI_SLOT(pdev->devfn);
bfad->hal_pcidev.pci_func = PCI_FUNC(pdev->devfn);
bfad->hal_pcidev.pci_bar_kva = bfad->pci_bar0_kva;
bfad->hal_pcidev.device_id = pdev->device;
bfad->pci_name = pci_name(pdev);
bfad->pci_attr.vendor_id = pdev->vendor;
bfad->pci_attr.device_id = pdev->device;
bfad->pci_attr.ssid = pdev->subsystem_device;
bfad->pci_attr.ssvid = pdev->subsystem_vendor;
bfad->pci_attr.pcifn = PCI_FUNC(pdev->devfn);
bfad->pcidev = pdev;
return 0;
out_release_region:
pci_release_regions(pdev);
out_disable_device:
pci_disable_device(pdev);
out:
return rc;
}
void
bfad_pci_uninit(struct pci_dev *pdev, struct bfad_s *bfad)
{
#if defined(__ia64__)
pci_iounmap(pdev, bfad->pci_bar0_kva);
#else
iounmap(bfad->pci_bar0_kva);
#endif
pci_release_regions(pdev);
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
}
void
bfad_fcs_port_cfg(struct bfad_s *bfad)
{
struct bfa_port_cfg_s port_cfg;
struct bfa_pport_attr_s attr;
char symname[BFA_SYMNAME_MAXLEN];
sprintf(symname, "%s-%d", BFAD_DRIVER_NAME, bfad->inst_no);
memcpy(port_cfg.sym_name.symname, symname, strlen(symname));
bfa_pport_get_attr(&bfad->bfa, &attr);
port_cfg.nwwn = attr.nwwn;
port_cfg.pwwn = attr.pwwn;
bfa_fcs_cfg_base_port(&bfad->bfa_fcs, &port_cfg);
}
bfa_status_t
bfad_drv_init(struct bfad_s *bfad)
{
bfa_status_t rc;
unsigned long flags;
struct bfa_fcs_driver_info_s driver_info;
int i;
bfad->cfg_data.rport_del_timeout = rport_del_timeout;
bfad->cfg_data.lun_queue_depth = bfa_lun_queue_depth;
bfad->cfg_data.io_max_sge = bfa_io_max_sge;
bfad->cfg_data.binding_method = FCP_PWWN_BINDING;
rc = bfad_hal_mem_alloc(bfad);
if (rc != BFA_STATUS_OK) {
printk(KERN_WARNING "bfad%d bfad_hal_mem_alloc failure\n",
bfad->inst_no);
printk(KERN_WARNING
"Not enough memory to attach all Brocade HBA ports,"
" System may need more memory.\n");
goto out_hal_mem_alloc_failure;
}
bfa_init_log(&bfad->bfa, bfad->logmod);
bfa_init_trc(&bfad->bfa, bfad->trcmod);
bfa_init_aen(&bfad->bfa, bfad->aen);
INIT_LIST_HEAD(&bfad->file_q);
INIT_LIST_HEAD(&bfad->file_free_q);
for (i = 0; i < BFAD_AEN_MAX_APPS; i++) {
bfa_q_qe_init(&bfad->file_buf[i].qe);
list_add_tail(&bfad->file_buf[i].qe, &bfad->file_free_q);
}
bfa_init_plog(&bfad->bfa, &bfad->plog_buf);
bfa_plog_init(&bfad->plog_buf);
bfa_plog_str(&bfad->plog_buf, BFA_PL_MID_DRVR, BFA_PL_EID_DRIVER_START,
0, "Driver Attach");
bfa_attach(&bfad->bfa, bfad, &bfad->ioc_cfg, &bfad->meminfo,
&bfad->hal_pcidev);
init_completion(&bfad->comp);
/*
* Enable Interrupt and wait bfa_init completion
*/
if (bfad_setup_intr(bfad)) {
printk(KERN_WARNING "bfad%d: bfad_setup_intr failed\n",
bfad->inst_no);
goto out_setup_intr_failure;
}
spin_lock_irqsave(&bfad->bfad_lock, flags);
bfa_init(&bfad->bfa);
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
/*
* Set up interrupt handler for each vectors
*/
if ((bfad->bfad_flags & BFAD_MSIX_ON)
&& bfad_install_msix_handler(bfad)) {
printk(KERN_WARNING "%s: install_msix failed, bfad%d\n",
__func__, bfad->inst_no);
}
bfad_init_timer(bfad);
wait_for_completion(&bfad->comp);
memset(&driver_info, 0, sizeof(driver_info));
strncpy(driver_info.version, BFAD_DRIVER_VERSION,
sizeof(driver_info.version) - 1);
if (host_name)
strncpy(driver_info.host_machine_name, host_name,
sizeof(driver_info.host_machine_name) - 1);
if (os_name)
strncpy(driver_info.host_os_name, os_name,
sizeof(driver_info.host_os_name) - 1);
if (os_patch)
strncpy(driver_info.host_os_patch, os_patch,
sizeof(driver_info.host_os_patch) - 1);
strncpy(driver_info.os_device_name, bfad->pci_name,
sizeof(driver_info.os_device_name - 1));
/*
* FCS INIT
*/
spin_lock_irqsave(&bfad->bfad_lock, flags);
bfa_fcs_log_init(&bfad->bfa_fcs, bfad->logmod);
bfa_fcs_trc_init(&bfad->bfa_fcs, bfad->trcmod);
bfa_fcs_aen_init(&bfad->bfa_fcs, bfad->aen);
bfa_fcs_attach(&bfad->bfa_fcs, &bfad->bfa, bfad, BFA_FALSE);
/* Do FCS init only when HAL init is done */
if ((bfad->bfad_flags & BFAD_HAL_INIT_DONE)) {
bfa_fcs_init(&bfad->bfa_fcs);
bfad->bfad_flags |= BFAD_FCS_INIT_DONE;
}
bfa_fcs_driver_info_init(&bfad->bfa_fcs, &driver_info);
bfa_fcs_set_fdmi_param(&bfad->bfa_fcs, fdmi_enable);
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
bfad->bfad_flags |= BFAD_DRV_INIT_DONE;
return BFA_STATUS_OK;
out_setup_intr_failure:
bfa_detach(&bfad->bfa);
bfad_hal_mem_release(bfad);
out_hal_mem_alloc_failure:
return BFA_STATUS_FAILED;
}
void
bfad_drv_uninit(struct bfad_s *bfad)
{
unsigned long flags;
spin_lock_irqsave(&bfad->bfad_lock, flags);
init_completion(&bfad->comp);
bfa_stop(&bfad->bfa);
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
wait_for_completion(&bfad->comp);
del_timer_sync(&bfad->hal_tmo);
bfa_isr_disable(&bfad->bfa);
bfa_detach(&bfad->bfa);
bfad_remove_intr(bfad);
bfa_assert(list_empty(&bfad->file_q));
bfad_hal_mem_release(bfad);
bfad->bfad_flags &= ~BFAD_DRV_INIT_DONE;
}
void
bfad_drv_start(struct bfad_s *bfad)
{
unsigned long flags;
spin_lock_irqsave(&bfad->bfad_lock, flags);
bfa_start(&bfad->bfa);
bfa_fcs_start(&bfad->bfa_fcs);
bfad->bfad_flags |= BFAD_HAL_START_DONE;
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
bfad_fc4_probe_post(bfad);
}
void
bfad_drv_stop(struct bfad_s *bfad)
{
unsigned long flags;
spin_lock_irqsave(&bfad->bfad_lock, flags);
init_completion(&bfad->comp);
bfad->pport.flags |= BFAD_PORT_DELETE;
bfa_fcs_exit(&bfad->bfa_fcs);
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
wait_for_completion(&bfad->comp);
spin_lock_irqsave(&bfad->bfad_lock, flags);
init_completion(&bfad->comp);
bfa_stop(&bfad->bfa);
bfad->bfad_flags &= ~BFAD_HAL_START_DONE;
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
wait_for_completion(&bfad->comp);
}
bfa_status_t
bfad_cfg_pport(struct bfad_s *bfad, enum bfa_port_role role)
{
int rc = BFA_STATUS_OK;
/*
* Allocate scsi_host for the physical port
*/
if ((bfad_supported_fc4s & BFA_PORT_ROLE_FCP_IM)
&& (role & BFA_PORT_ROLE_FCP_IM)) {
if (bfad->pport.im_port == NULL) {
rc = BFA_STATUS_FAILED;
goto out;
}
rc = bfad_im_scsi_host_alloc(bfad, bfad->pport.im_port);
if (rc != BFA_STATUS_OK)
goto out;
bfad->pport.roles |= BFA_PORT_ROLE_FCP_IM;
}
bfad->bfad_flags |= BFAD_CFG_PPORT_DONE;
out:
return rc;
}
void
bfad_uncfg_pport(struct bfad_s *bfad)
{
if ((bfad->pport.roles & BFA_PORT_ROLE_FCP_IPFC) && ipfc_enable) {
bfad_ipfc_port_delete(bfad, &bfad->pport);
bfad->pport.roles &= ~BFA_PORT_ROLE_FCP_IPFC;
}
if ((bfad_supported_fc4s & BFA_PORT_ROLE_FCP_IM)
&& (bfad->pport.roles & BFA_PORT_ROLE_FCP_IM)) {
bfad_im_scsi_host_free(bfad, bfad->pport.im_port);
bfad_im_port_clean(bfad->pport.im_port);
kfree(bfad->pport.im_port);
bfad->pport.roles &= ~BFA_PORT_ROLE_FCP_IM;
}
bfad->bfad_flags &= ~BFAD_CFG_PPORT_DONE;
}
void
bfad_drv_log_level_set(struct bfad_s *bfad)
{
if (log_level > BFA_LOG_INVALID && log_level <= BFA_LOG_LEVEL_MAX)
bfa_log_set_level_all(&bfad->log_data, log_level);
}
bfa_status_t
bfad_start_ops(struct bfad_s *bfad)
{
int retval;
/* PPORT FCS config */
bfad_fcs_port_cfg(bfad);
retval = bfad_cfg_pport(bfad, BFA_PORT_ROLE_FCP_IM);
if (retval != BFA_STATUS_OK)
goto out_cfg_pport_failure;
/* BFAD level FC4 (IM/TM/IPFC) specific resource allocation */
retval = bfad_fc4_probe(bfad);
if (retval != BFA_STATUS_OK) {
printk(KERN_WARNING "bfad_fc4_probe failed\n");
goto out_fc4_probe_failure;
}
bfad_drv_start(bfad);
/*
* If bfa_linkup_delay is set to -1 default; try to retrive the
* value using the bfad_os_get_linkup_delay(); else use the
* passed in module param value as the bfa_linkup_delay.
*/
if (bfa_linkup_delay < 0) {
bfa_linkup_delay = bfad_os_get_linkup_delay(bfad);
bfad_os_rport_online_wait(bfad);
bfa_linkup_delay = -1;
} else {
bfad_os_rport_online_wait(bfad);
}
bfa_log(bfad->logmod, BFA_LOG_LINUX_DEVICE_CLAIMED, bfad->pci_name);
return BFA_STATUS_OK;
out_fc4_probe_failure:
bfad_fc4_probe_undo(bfad);
bfad_uncfg_pport(bfad);
out_cfg_pport_failure:
return BFA_STATUS_FAILED;
}
int
bfad_worker (void *ptr)
{
struct bfad_s *bfad;
unsigned long flags;
bfad = (struct bfad_s *)ptr;
while (!kthread_should_stop()) {
/* Check if the FCS init is done from bfad_drv_init;
* if not done do FCS init and set the flag.
*/
if (!(bfad->bfad_flags & BFAD_FCS_INIT_DONE)) {
spin_lock_irqsave(&bfad->bfad_lock, flags);
bfa_fcs_init(&bfad->bfa_fcs);
bfad->bfad_flags |= BFAD_FCS_INIT_DONE;
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
}
/* Start the bfad operations after HAL init done */
bfad_start_ops(bfad);
spin_lock_irqsave(&bfad->bfad_lock, flags);
bfad->bfad_tsk = NULL;
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
break;
}
return 0;
}
/*
* PCI_entry PCI driver entries * {
*/
/**
* PCI probe entry.
*/
int
bfad_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid)
{
struct bfad_s *bfad;
int error = -ENODEV, retval;
char buf[16];
/*
* For single port cards - only claim function 0
*/
if ((pdev->device == BFA_PCI_DEVICE_ID_FC_8G1P)
&& (PCI_FUNC(pdev->devfn) != 0))
return -ENODEV;
BFA_TRACE(BFA_INFO, "bfad_pci_probe entry");
bfad = kzalloc(sizeof(struct bfad_s), GFP_KERNEL);
if (!bfad) {
error = -ENOMEM;
goto out;
}
bfad->trcmod = kzalloc(sizeof(struct bfa_trc_mod_s), GFP_KERNEL);
if (!bfad->trcmod) {
printk(KERN_WARNING "Error alloc trace buffer!\n");
error = -ENOMEM;
goto out_alloc_trace_failure;
}
/*
* LOG/TRACE INIT
*/
bfa_trc_init(bfad->trcmod);
bfa_trc(bfad, bfad_inst);
bfad->logmod = &bfad->log_data;
sprintf(buf, "%d", bfad_inst);
bfa_log_init(bfad->logmod, buf, bfa_os_printf);
bfad_drv_log_level_set(bfad);
bfad->aen = &bfad->aen_buf;
if (!(bfad_load_fwimg(pdev))) {
printk(KERN_WARNING "bfad_load_fwimg failure!\n");
kfree(bfad->trcmod);
goto out_alloc_trace_failure;
}
retval = bfad_pci_init(pdev, bfad);
if (retval) {
printk(KERN_WARNING "bfad_pci_init failure!\n");
error = retval;
goto out_pci_init_failure;
}
mutex_lock(&bfad_mutex);
bfad->inst_no = bfad_inst++;
list_add_tail(&bfad->list_entry, &bfad_list);
mutex_unlock(&bfad_mutex);
spin_lock_init(&bfad->bfad_lock);
pci_set_drvdata(pdev, bfad);
bfad->ref_count = 0;
bfad->pport.bfad = bfad;
bfad->bfad_tsk = kthread_create(bfad_worker, (void *) bfad, "%s",
"bfad_worker");
if (IS_ERR(bfad->bfad_tsk)) {
printk(KERN_INFO "bfad[%d]: Kernel thread"
" creation failed!\n",
bfad->inst_no);
goto out_kthread_create_failure;
}
retval = bfad_drv_init(bfad);
if (retval != BFA_STATUS_OK)
goto out_drv_init_failure;
if (!(bfad->bfad_flags & BFAD_HAL_INIT_DONE)) {
bfad->bfad_flags |= BFAD_HAL_INIT_FAIL;
printk(KERN_WARNING "bfad%d: hal init failed\n", bfad->inst_no);
goto ok;
}
retval = bfad_start_ops(bfad);
if (retval != BFA_STATUS_OK)
goto out_start_ops_failure;
kthread_stop(bfad->bfad_tsk);
bfad->bfad_tsk = NULL;
ok:
return 0;
out_start_ops_failure:
bfad_drv_uninit(bfad);
out_drv_init_failure:
kthread_stop(bfad->bfad_tsk);
out_kthread_create_failure:
mutex_lock(&bfad_mutex);
bfad_inst--;
list_del(&bfad->list_entry);
mutex_unlock(&bfad_mutex);
bfad_pci_uninit(pdev, bfad);
out_pci_init_failure:
kfree(bfad->trcmod);
out_alloc_trace_failure:
kfree(bfad);
out:
return error;
}
/**
* PCI remove entry.
*/
void
bfad_pci_remove(struct pci_dev *pdev)
{
struct bfad_s *bfad = pci_get_drvdata(pdev);
unsigned long flags;
bfa_trc(bfad, bfad->inst_no);
spin_lock_irqsave(&bfad->bfad_lock, flags);
if (bfad->bfad_tsk != NULL)
kthread_stop(bfad->bfad_tsk);
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
if ((bfad->bfad_flags & BFAD_DRV_INIT_DONE)
&& !(bfad->bfad_flags & BFAD_HAL_INIT_DONE)) {
spin_lock_irqsave(&bfad->bfad_lock, flags);
init_completion(&bfad->comp);
bfa_stop(&bfad->bfa);
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
wait_for_completion(&bfad->comp);
bfad_remove_intr(bfad);
del_timer_sync(&bfad->hal_tmo);
goto hal_detach;
} else if (!(bfad->bfad_flags & BFAD_DRV_INIT_DONE)) {
goto remove_sysfs;
}
if (bfad->bfad_flags & BFAD_HAL_START_DONE) {
bfad_drv_stop(bfad);
} else if (bfad->bfad_flags & BFAD_DRV_INIT_DONE) {
/* Invoking bfa_stop() before bfa_detach
* when HAL and DRV init are success
* but HAL start did not occur.
*/
spin_lock_irqsave(&bfad->bfad_lock, flags);
init_completion(&bfad->comp);
bfa_stop(&bfad->bfa);
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
wait_for_completion(&bfad->comp);
}
bfad_remove_intr(bfad);
del_timer_sync(&bfad->hal_tmo);
if (bfad->bfad_flags & BFAD_FC4_PROBE_DONE)
bfad_fc4_probe_undo(bfad);
if (bfad->bfad_flags & BFAD_CFG_PPORT_DONE)
bfad_uncfg_pport(bfad);
hal_detach:
spin_lock_irqsave(&bfad->bfad_lock, flags);
bfa_detach(&bfad->bfa);
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
bfad_hal_mem_release(bfad);
remove_sysfs:
mutex_lock(&bfad_mutex);
bfad_inst--;
list_del(&bfad->list_entry);
mutex_unlock(&bfad_mutex);
bfad_pci_uninit(pdev, bfad);
kfree(bfad->trcmod);
kfree(bfad);
}
static struct pci_device_id bfad_id_table[] = {
{
.vendor = BFA_PCI_VENDOR_ID_BROCADE,
.device = BFA_PCI_DEVICE_ID_FC_8G2P,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{
.vendor = BFA_PCI_VENDOR_ID_BROCADE,
.device = BFA_PCI_DEVICE_ID_FC_8G1P,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{
.vendor = BFA_PCI_VENDOR_ID_BROCADE,
.device = BFA_PCI_DEVICE_ID_CT,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.class = (PCI_CLASS_SERIAL_FIBER << 8),
.class_mask = ~0,
},
{0, 0},
};
MODULE_DEVICE_TABLE(pci, bfad_id_table);
static struct pci_driver bfad_pci_driver = {
.name = BFAD_DRIVER_NAME,
.id_table = bfad_id_table,
.probe = bfad_pci_probe,
.remove = __devexit_p(bfad_pci_remove),
};
/**
* Linux driver module functions
*/
bfa_status_t
bfad_fc4_module_init(void)
{
int rc;
rc = bfad_im_module_init();
if (rc != BFA_STATUS_OK)
goto ext;
bfad_tm_module_init();
if (ipfc_enable)
bfad_ipfc_module_init();
ext:
return rc;
}
void
bfad_fc4_module_exit(void)
{
if (ipfc_enable)
bfad_ipfc_module_exit();
bfad_tm_module_exit();
bfad_im_module_exit();
}
/**
* Driver module init.
*/
static int __init
bfad_init(void)
{
int error = 0;
printk(KERN_INFO "Brocade BFA FC/FCOE SCSI driver - version: %s\n",
BFAD_DRIVER_VERSION);
if (num_sgpgs > 0)
num_sgpgs_parm = num_sgpgs;
error = bfad_fc4_module_init();
if (error) {
error = -ENOMEM;
printk(KERN_WARNING "bfad_fc4_module_init failure\n");
goto ext;
}
if (!strcmp(FCPI_NAME, " fcpim"))
bfad_supported_fc4s |= BFA_PORT_ROLE_FCP_IM;
if (!strcmp(FCPT_NAME, " fcptm"))
bfad_supported_fc4s |= BFA_PORT_ROLE_FCP_TM;
if (!strcmp(IPFC_NAME, " ipfc"))
bfad_supported_fc4s |= BFA_PORT_ROLE_FCP_IPFC;
bfa_ioc_auto_recover(ioc_auto_recover);
bfa_fcs_rport_set_del_timeout(rport_del_timeout);
error = pci_register_driver(&bfad_pci_driver);
if (error) {
printk(KERN_WARNING "bfad pci_register_driver failure\n");
goto ext;
}
return 0;
ext:
bfad_fc4_module_exit();
return error;
}
/**
* Driver module exit.
*/
static void __exit
bfad_exit(void)
{
pci_unregister_driver(&bfad_pci_driver);
bfad_fc4_module_exit();
bfad_free_fwimg();
}
#define BFAD_PROTO_NAME FCPI_NAME FCPT_NAME IPFC_NAME
module_init(bfad_init);
module_exit(bfad_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Brocade Fibre Channel HBA Driver" BFAD_PROTO_NAME);
MODULE_AUTHOR("Brocade Communications Systems, Inc.");
MODULE_VERSION(BFAD_DRIVER_VERSION);