linux/drivers/scsi/bfa/bfad.c

1288 lines
30 KiB
C
Raw Normal View History

/*
* 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 cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h percpu.h is included by sched.h and module.h and thus ends up being included when building most .c files. percpu.h includes slab.h which in turn includes gfp.h making everything defined by the two files universally available and complicating inclusion dependencies. percpu.h -> slab.h dependency is about to be removed. Prepare for this change by updating users of gfp and slab facilities include those headers directly instead of assuming availability. As this conversion needs to touch large number of source files, the following script is used as the basis of conversion. http://userweb.kernel.org/~tj/misc/slabh-sweep.py The script does the followings. * Scan files for gfp and slab usages and update includes such that only the necessary includes are there. ie. if only gfp is used, gfp.h, if slab is used, slab.h. * When the script inserts a new include, it looks at the include blocks and try to put the new include such that its order conforms to its surrounding. It's put in the include block which contains core kernel includes, in the same order that the rest are ordered - alphabetical, Christmas tree, rev-Xmas-tree or at the end if there doesn't seem to be any matching order. * If the script can't find a place to put a new include (mostly because the file doesn't have fitting include block), it prints out an error message indicating which .h file needs to be added to the file. The conversion was done in the following steps. 1. The initial automatic conversion of all .c files updated slightly over 4000 files, deleting around 700 includes and adding ~480 gfp.h and ~3000 slab.h inclusions. The script emitted errors for ~400 files. 2. Each error was manually checked. Some didn't need the inclusion, some needed manual addition while adding it to implementation .h or embedding .c file was more appropriate for others. This step added inclusions to around 150 files. 3. The script was run again and the output was compared to the edits from #2 to make sure no file was left behind. 4. Several build tests were done and a couple of problems were fixed. e.g. lib/decompress_*.c used malloc/free() wrappers around slab APIs requiring slab.h to be added manually. 5. The script was run on all .h files but without automatically editing them as sprinkling gfp.h and slab.h inclusions around .h files could easily lead to inclusion dependency hell. Most gfp.h inclusion directives were ignored as stuff from gfp.h was usually wildly available and often used in preprocessor macros. Each slab.h inclusion directive was examined and added manually as necessary. 6. percpu.h was updated not to include slab.h. 7. Build test were done on the following configurations and failures were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my distributed build env didn't work with gcov compiles) and a few more options had to be turned off depending on archs to make things build (like ipr on powerpc/64 which failed due to missing writeq). * x86 and x86_64 UP and SMP allmodconfig and a custom test config. * powerpc and powerpc64 SMP allmodconfig * sparc and sparc64 SMP allmodconfig * ia64 SMP allmodconfig * s390 SMP allmodconfig * alpha SMP allmodconfig * um on x86_64 SMP allmodconfig 8. percpu.h modifications were reverted so that it could be applied as a separate patch and serve as bisection point. Given the fact that I had only a couple of failures from tests on step 6, I'm fairly confident about the coverage of this conversion patch. If there is a breakage, it's likely to be something in one of the arch headers which should be easily discoverable easily on most builds of the specific arch. Signed-off-by: Tejun Heo <tj@kernel.org> Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 16:04:11 +08:00
#include <linux/slab.h>
#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_fcport_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;
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);
memset(bfad->file_map, 0, sizeof(bfad->file_map));
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);
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;
/*
* 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;
bfa_log_init(bfad->logmod, (char *)pci_name(pdev), 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);