mirror of
https://github.com/torvalds/linux.git
synced 2024-12-04 10:01:41 +00:00
e67143243a
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>
1296 lines
30 KiB
C
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);
|
|
|
|
|