2009-07-25 05:25:06

by Jing Huang

[permalink] [raw]
Subject: [PATCH 1/14] bfa: Brocade BFA FC SCSI driver (bfad)

From: Jing Huang <[email protected]>

This patch contains code that interfaces to upper layer linux kernel, such as
PCI, SCSI mid-layer and sysfs etc.

Signed-off-by: Jing Huang <[email protected]>
---
bfad.c | 1467 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
bfad_attr.c | 660 ++++++++++++++++++++++++
bfad_attr.h | 67 ++
bfad_drv.h | 388 ++++++++++++++
bfad_fwimg.c | 97 +++
bfad_im.c | 1349 ++++++++++++++++++++++++++++++++++++++++++++++++++
bfad_im.h | 205 +++++++
bfad_im_compat.h | 46 +
bfad_intr.c | 241 +++++++++
bfad_ipfc.h | 42 +
bfad_os.c | 56 ++
bfad_tm.h | 59 ++
bfad_trcmod.h | 52 +
13 files changed, 4729 insertions(+)

diff -urpN orig/drivers/scsi/bfa/bfad.c patch/drivers/scsi/bfa/bfad.c
--- orig/drivers/scsi/bfa/bfad.c 1969-12-31 16:00:00.000000000 -0800
+++ patch/drivers/scsi/bfa/bfad.c 2009-07-21 15:34:02.000000000 -0700
@@ -0,0 +1,1467 @@
+/*
+ * Copyright (c) 2005-2008 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * http://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 "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 bfa_status_t bfad_fc4_port_new(struct bfad_s *bfad,
+ struct bfad_port_s *port, int roles);
+static void bfad_fc4_port_delete(struct bfad_s *bfad,
+ struct bfad_port_s *port, int roles);
+
+struct semaphore bfad_sem;
+int bfad_scan_done;
+
+LIST_HEAD(bfad_list);
+
+static int bfad_inst;
+int supported_fc4s;
+
+char *host_name;
+char *os_name;
+char *os_patch;
+int num_rports;
+int num_ios;
+int num_tms;
+int num_fcxps;
+int num_ufbufs;
+int reqq_size;
+int rspq_size;
+int num_sgpgs;
+int rport_del_timeout = BFA_FCS_RPORT_DEF_DEL_TIMEOUT;
+int bfa_lun_queue_depth = BFAD_LUN_QUEUE_DEPTH;
+int bfa_io_max_sge = BFAD_IO_MAX_SGE;
+int log_level = BFA_LOG_WARNING;
+int ioc_auto_recover = BFA_TRUE;
+int ipfc_enable = BFA_FALSE;
+int ipfc_mtu = -1;
+int linkup_delay = -1;
+
+/*
+ * Stores the module parm num_sgpgs value;
+ * used to reset for bfad next instance.
+ */
+static int num_sgpgs_parm;
+
+bfa_boolean_t
+bfad_is_ready(void)
+{
+ struct bfad_s *bfad = NULL;
+
+ down(&bfad_sem);
+ if (!bfad_scan_done) {
+ up(&bfad_sem);
+ return BFA_FALSE;
+ }
+
+ list_for_each_entry(bfad, &bfad_list, list_entry) {
+ if (!(bfad->bfad_flags & BFAD_DRV_INIT_DONE)) {
+ up(&bfad_sem);
+ return BFA_FALSE;
+ }
+ }
+ up(&bfad_sem);
+
+ return BFA_TRUE;
+}
+
+void
+bfad_flags_set(struct bfad_s *bfad, u32 flags)
+{
+ if (bfad)
+ bfad->bfad_flags |= flags;
+}
+
+/**
+ * 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;
+
+ complete(&bfad->comp);
+}
+
+/**
+ * bfa_stop callback
+ */
+void
+bfa_cb_stop(void *drv, bfa_status_t stop_status)
+{
+ struct bfad_s *bfad = drv;
+
+ /*
+ * Signal the BFAD stop waiting thread
+ */
+ complete(&bfad->comp);
+}
+
+/**
+ * bfa_ioc_diable() callback.
+ */
+void
+bfa_cb_ioc_disable(void *drv)
+{
+ struct bfad_s *bfad = drv;
+
+ complete(&bfad->disable_comp);
+}
+
+void
+bfa_fcb_exit(struct bfad_s *drv)
+{
+ complete(&drv->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_flags_set(bfad, 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_vf_stop(struct bfad_vf_s *vf_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;
+}
+
+/**
+ * FCS RPORT free callback.
+ */
+void
+bfa_fcb_rport_free(struct bfad_s *bfad, struct bfad_rport_s **rport_drv)
+{
+ kfree(*rport_drv);
+}
+
+
+
+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:
+ bfad_os_dma_free(bfad, meminfo_elem);
+ 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)
+{
+ int i;
+ struct bfa_meminfo_s *hal_meminfo = &bfad->meminfo;
+ struct bfa_mem_elem_s *meminfo_elem;
+ dma_addr_t phys_addr;
+ void *kva;
+ bfa_status_t rc = BFA_STATUS_OK;
+ int retry_count = 0;
+ int reset_value = 1;
+ int min_num_sgpgs = 512;
+
+ 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 = bfad_os_dma_alloc(bfad, meminfo_elem, &phys_addr);
+ 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;
+}
+
+struct bfad_s *
+bfad_find_bfad_by_inst_no(int inst_no)
+{
+ struct bfad_s *bfad = NULL, *rc = NULL;
+
+ down(&bfad_sem);
+ list_for_each_entry(bfad, &bfad_list, list_entry) {
+ if (bfad->inst_no == inst_no) {
+ rc = bfad;
+ break;
+ }
+ }
+ up(&bfad_sem);
+ return rc;
+}
+
+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);
+ext:
+ return rc;
+}
+
+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);
+}
+
+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);
+}
+
+/**
+ * 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_64BIT_MASK) != 0)
+ if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) != 0) {
+ BFA_PRINTF(BFA_ERR, "pci_set_dma_mask fail %p\n", pdev);
+ goto out_release_region;
+ }
+#ifdef SUPPORT_PCI_AER
+ /*
+ * Enable PCIE Advanced Error Recovery (AER) if the kernel version
+ * supports.
+ */
+ BFAD_ENABLE_PCIE_AER(pdev);
+#endif
+
+ 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)
+{
+ 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);
+
+#ifdef CONFIG_PCI_MSI
+ /*
+ * 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",
+ __FUNCTION__, bfad->inst_no);
+ }
+#endif
+
+ 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_init(&bfad->bfa_fcs, &bfad->bfa, bfad, BFA_FALSE);
+ bfa_fcs_driver_info_init(&bfad->bfa_fcs, &driver_info);
+ 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)
+{
+ 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);
+}
+
+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 ((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 ((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);
+}
+
+ /*
+ * 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;
+ }
+
+ down(&bfad_sem);
+ bfad->inst_no = bfad_inst++;
+ list_add_tail(&bfad->list_entry, &bfad_list);
+ up(&bfad_sem);
+
+ spin_lock_init(&bfad->bfad_lock);
+ pci_set_drvdata(pdev, bfad);
+
+ bfad->ref_count = 0;
+ bfad->pport.bfad = bfad;
+
+ retval = bfad_drv_init(bfad);
+ if (retval != BFA_STATUS_OK)
+ goto out_drv_init_failure;
+ if (!(bfad->bfad_flags & BFAD_HAL_INIT_DONE)) {
+ printk(KERN_WARNING "bfad%d: hal init failed\n", bfad->inst_no);
+ goto ok;
+ }
+
+ /*
+ * 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 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 linkup_delay.
+ */
+ if (linkup_delay < 0) {
+#if defined(__ia64__)
+ linkup_delay = 10;
+#else
+ linkup_delay = bfad_os_get_linkup_delay(bfad);
+#endif
+ bfad_os_rport_online_wait(bfad);
+ linkup_delay = -1;
+ } else {
+ bfad_os_rport_online_wait(bfad);
+ }
+
+ bfa_log(bfad->logmod, BFA_LOG_LINUX_DEVICE_CLAIMED, bfad->pci_name);
+ok:
+ return 0;
+
+out_fc4_probe_failure:
+ bfad_fc4_probe_undo(bfad);
+ bfad_uncfg_pport(bfad);
+out_cfg_pport_failure:
+ bfad_drv_uninit(bfad);
+out_drv_init_failure:
+ down(&bfad_sem);
+ bfad_inst--;
+ list_del(&bfad->list_entry);
+ up(&bfad_sem);
+ 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);
+
+ 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);
+
+ bfad_remove_intr(bfad);
+
+ del_timer_sync(&bfad->hal_tmo);
+ 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:
+
+ down(&bfad_sem);
+ bfad_inst--;
+ list_del(&bfad->list_entry);
+ up(&bfad_sem);
+ bfad_pci_uninit(pdev, bfad);
+
+ kfree(bfad->trcmod);
+ kfree(bfad);
+}
+
+#define BFAD_MAX_PCIIDS 4
+struct pci_device_id bfad_id_table[BFAD_MAX_PCIIDS] = {
+ {
+ .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);
+
+/**
+ * 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",
+ bfa_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;
+ }
+
+ sema_init(&bfad_sem, 1);
+
+ if (!strcmp(FCPI_NAME, " fcpim"))
+ supported_fc4s |= BFA_PORT_ROLE_FCP_IM;
+ if (!strcmp(FCPT_NAME, " fcptm"))
+ supported_fc4s |= BFA_PORT_ROLE_FCP_TM;
+ if (!strcmp(IPFC_NAME, " ipfc"))
+ 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_p);
+
+ down(&bfad_sem);
+ bfad_scan_done = 1;
+ up(&bfad_sem);
+
+ if (error) {
+ printk(KERN_WARNING "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_p);
+ 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);
+
+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(linkup_delay, int, S_IRUGO | S_IWUSR);
+module_param(msix_disable, int, S_IRUGO | S_IWUSR);
+
+#ifdef CONFIG_PM
+int pci_save_state(struct pci_dev *pdev);
+int pci_restore_state(struct pci_dev *pdev);
+#endif
+
+void *
+bfad_os_dma_alloc(struct bfad_s *bfad, struct bfa_mem_elem_s *meminfo_elem,
+ dma_addr_t *phys_addr)
+{
+ return dma_alloc_coherent(&bfad->pcidev->dev,
+ meminfo_elem->mem_len,
+ phys_addr, GFP_KERNEL);
+}
+
+void
+bfad_os_dma_free(struct bfad_s *bfad, struct bfa_mem_elem_s *meminfo_elem)
+{
+ dma_free_coherent(&bfad->pcidev->dev,
+ meminfo_elem->mem_len,
+ meminfo_elem->kva,
+ (dma_addr_t) meminfo_elem->dma);
+}
+
+#ifdef CONFIG_PM
+/**
+ * PCI suspend entry.
+ */
+static int
+bfad_os_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct bfad_s *bfad = pci_get_drvdata(pdev);
+ pci_power_t device_state;
+
+ device_state = pci_choose_state(pdev, state);
+
+ pci_save_state(pdev);
+ init_completion(&bfad->suspend);
+ wait_for_completion(&bfad->suspend);
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, device_state);
+
+ return 0;
+}
+
+/**
+ * PCI resume entry.
+ */
+static int
+bfad_os_resume(struct pci_dev *pdev)
+{
+
+ /* Resume the device power state to D0 */
+ pci_set_power_state(pdev, 0);
+
+ /* Restore all device PCI configuation space to its original state. */
+ pci_restore_state(pdev);
+
+ if (pci_enable_device(pdev))
+ goto out;
+
+ return 0;
+
+out:
+ return 1;
+
+}
+#endif
+
+#ifdef SUPPORT_PCI_AER
+/*
+ * PCI error recovery support, this fearure is only availbe for kernel
+ * .6.16 or later.
+ */
+/**
+ * PCI Error Recovery entry, error detected.
+ */
+static pci_ers_result_t
+bfad_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
+{
+ struct bfad_s *bfad = pci_get_drvdata(pdev);
+
+ switch (state) {
+ case pci_channel_io_perm_failure:
+ /*
+ * PCI bus has permanently failed. This is
+ * unrecoveralbe, Do we need to do the cleanup like PCI
+ * remove? or kernel will automatically do it?
+ */
+ return PCI_ERS_RESULT_DISCONNECT;
+
+ case pci_channel_io_frozen:
+
+ default:
+
+ init_completion(&bfad->comp);
+ bfa_fcs_exit(&bfad->bfa_fcs);
+ bfa_stop(&bfad->bfa);
+ wait_for_completion_timeout(&bfad->comp, BFAD_STOP_TIMEOUT);
+
+ pci_disable_device(pdev);
+
+ break;
+ }
+
+ /*
+ * Request a slot reset
+ */
+ return PCI_ERS_RESULT_NEED_RESET;
+}
+
+/**
+ * PCI Error Recovery entry, re-initialize the chip.
+ */
+static pci_ers_result_t
+bfad_pci_slot_reset(struct pci_dev *pdev)
+{
+ struct bfad_s *bfad = pci_get_drvdata(pdev);
+ int retval;
+
+ if (pci_enable_device(pdev))
+ goto out;
+
+ pci_set_master(pdev);
+
+ retval = pci_set_mwi(pdev);
+
+ if (retval)
+ dev_printk(KERN_WARNING, &pdev->dev,
+ "Warning: pci_set_mwi returned %d\n", retval);
+
+ if (pci_set_dma_mask(bfad->pcidev, DMA_64BIT_MASK) != 0)
+ if (pci_set_dma_mask(bfad->pcidev, DMA_32BIT_MASK) != 0)
+ goto out_disable_device;
+
+ BFAD_ENABLE_PCIE_AER(pdev);
+
+ /* bfa should still keep all the BFAD and port config info */
+ bfa_start(&bfad->bfa);
+ bfa_fcs_start(&bfad->bfa_fcs);
+
+ return PCI_ERS_RESULT_RECOVERED;
+
+out_disable_device:
+ pci_disable_device(pdev);
+out:
+ return PCI_ERS_RESULT_DISCONNECT;
+
+}
+
+/*
+ * PCI error recovery support, this fearure is only available for kernel
+ * 2.6.16 or later. At this point, we only define error_detected and
+ * slot_reset.
+ */
+static struct pci_error_handlers bfad_err_handler = {
+ .error_detected = bfad_pci_error_detected,
+ .slot_reset = bfad_pci_slot_reset,
+};
+#endif
+
+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),
+#ifdef CONFIG_PM
+ .suspend = bfad_os_suspend,
+ .resume = bfad_os_resume,
+#endif
+
+#ifdef SUPPORT_PCI_AER
+ /*
+ * PCI error recovery support, this fearure is only available for
+ * kernel 2.6.16 or later.
+ */
+ .err_handler = &bfad_err_handler,
+#endif
+};
+
+struct pci_driver *bfad_pci_driver_p = &bfad_pci_driver;
+
+
diff -urpN orig/drivers/scsi/bfa/bfad_attr.c patch/drivers/scsi/bfa/bfad_attr.c
--- orig/drivers/scsi/bfa/bfad_attr.c 1969-12-31 16:00:00.000000000 -0800
+++ patch/drivers/scsi/bfa/bfad_attr.c 2009-07-21 15:34:02.000000000 -0700
@@ -0,0 +1,660 @@
+/*
+ * Copyright (c) 2005-2008 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * http://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.
+ */
+
+/**
+ * bfa_attr.c Linux driver configuration interface module.
+ */
+
+#include "bfad_drv.h"
+#include "bfad_im.h"
+#include "bfad_trcmod.h"
+#include "bfad_attr.h"
+
+/**
+ * FC_transport_template FC transport template
+ */
+
+/**
+ * FC transport template entry, get SCSI target port ID.
+ */
+void
+bfad_im_get_starget_port_id(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost;
+ struct bfad_im_port_s *im_port;
+ struct bfad_s *bfad;
+ struct bfad_itnim_s *itnim = NULL;
+ u32 fc_id = -1;
+ unsigned long flags;
+
+ shost = bfad_os_starget_to_shost(starget);
+ im_port = (struct bfad_im_port_s *) shost->hostdata[0];
+ bfad = im_port->bfad;
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+
+ itnim = bfad_os_get_itnim(im_port, starget->id);
+ if (itnim)
+ fc_id = bfa_fcs_itnim_get_fcid(&itnim->fcs_itnim);
+
+ fc_starget_port_id(starget) = fc_id;
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+}
+
+/**
+ * FC transport template entry, get SCSI target nwwn.
+ */
+void
+bfad_im_get_starget_node_name(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost;
+ struct bfad_im_port_s *im_port;
+ struct bfad_s *bfad;
+ struct bfad_itnim_s *itnim = NULL;
+ u64 node_name = 0;
+ unsigned long flags;
+
+ shost = bfad_os_starget_to_shost(starget);
+ im_port = (struct bfad_im_port_s *) shost->hostdata[0];
+ bfad = im_port->bfad;
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+
+ itnim = bfad_os_get_itnim(im_port, starget->id);
+ if (itnim)
+ node_name = bfa_fcs_itnim_get_nwwn(&itnim->fcs_itnim);
+
+ fc_starget_node_name(starget) = bfa_os_htonll(node_name);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+}
+
+/**
+ * FC transport template entry, get SCSI target pwwn.
+ */
+void
+bfad_im_get_starget_port_name(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost;
+ struct bfad_im_port_s *im_port;
+ struct bfad_s *bfad;
+ struct bfad_itnim_s *itnim = NULL;
+ u64 port_name = 0;
+ unsigned long flags;
+
+ shost = bfad_os_starget_to_shost(starget);
+ im_port = (struct bfad_im_port_s *) shost->hostdata[0];
+ bfad = im_port->bfad;
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+
+ itnim = bfad_os_get_itnim(im_port, starget->id);
+ if (itnim)
+ port_name = bfa_fcs_itnim_get_pwwn(&itnim->fcs_itnim);
+
+ fc_starget_port_name(starget) = bfa_os_htonll(port_name);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+}
+
+/**
+ * FC transport template entry, get SCSI host port ID.
+ */
+void
+bfad_im_get_host_port_id(struct Scsi_Host *shost)
+{
+ struct bfad_im_port_s *im_port =
+ (struct bfad_im_port_s *) shost->hostdata[0];
+ struct bfad_port_s *port = im_port->port;
+
+ fc_host_port_id(shost) =
+ bfa_os_hton3b(bfa_fcs_port_get_fcid(port->fcs_port));
+}
+
+/**
+ * FC transport template entry, issue a LIP.
+ */
+int
+bfad_im_issue_fc_host_lip(struct Scsi_Host *shost)
+{
+ return 0;
+}
+
+
+
+
+
+struct Scsi_Host *
+bfad_os_starget_to_shost(struct scsi_target *starget)
+{
+ return dev_to_shost(starget->dev.parent);
+}
+
+/**
+ * FC transport template entry, get SCSI host port type.
+ */
+static void
+bfad_im_get_host_port_type(struct Scsi_Host *shost)
+{
+ struct bfad_im_port_s *im_port =
+ (struct bfad_im_port_s *) shost->hostdata[0];
+ struct bfad_s *bfad = im_port->bfad;
+ struct bfa_pport_attr_s attr;
+
+ bfa_pport_get_attr(&bfad->bfa, &attr);
+
+ switch (attr.port_type) {
+ case BFA_PPORT_TYPE_NPORT:
+ fc_host_port_type(shost) = FC_PORTTYPE_NPORT;
+ break;
+ case BFA_PPORT_TYPE_NLPORT:
+ fc_host_port_type(shost) = FC_PORTTYPE_NLPORT;
+ break;
+ case BFA_PPORT_TYPE_P2P:
+ fc_host_port_type(shost) = FC_PORTTYPE_PTP;
+ break;
+ case BFA_PPORT_TYPE_LPORT:
+ fc_host_port_type(shost) = FC_PORTTYPE_LPORT;
+ break;
+ default:
+ fc_host_port_type(shost) = FC_PORTTYPE_UNKNOWN;
+ break;
+ }
+}
+
+/**
+ * FC transport template entry, get SCSI host port state.
+ */
+static void
+bfad_im_get_host_port_state(struct Scsi_Host *shost)
+{
+ struct bfad_im_port_s *im_port =
+ (struct bfad_im_port_s *) shost->hostdata[0];
+ struct bfad_s *bfad = im_port->bfad;
+ struct bfa_pport_attr_s attr;
+
+ bfa_pport_get_attr(&bfad->bfa, &attr);
+
+ switch (attr.port_state) {
+ case BFA_PPORT_ST_LINKDOWN:
+ fc_host_port_state(shost) = FC_PORTSTATE_LINKDOWN;
+ break;
+ case BFA_PPORT_ST_LINKUP:
+ fc_host_port_state(shost) = FC_PORTSTATE_ONLINE;
+ break;
+ case BFA_PPORT_ST_UNINIT:
+ case BFA_PPORT_ST_ENABLING_QWAIT:
+ case BFA_PPORT_ST_ENABLING:
+ case BFA_PPORT_ST_DISABLING_QWAIT:
+ case BFA_PPORT_ST_DISABLING:
+ case BFA_PPORT_ST_DISABLED:
+ case BFA_PPORT_ST_STOPPED:
+ case BFA_PPORT_ST_IOCDOWN:
+ default:
+ fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN;
+ break;
+ }
+}
+
+/**
+ * FC transport template entry, get SCSI host active fc4s.
+ */
+static void
+bfad_im_get_host_active_fc4s(struct Scsi_Host *shost)
+{
+ struct bfad_im_port_s *im_port =
+ (struct bfad_im_port_s *) shost->hostdata[0];
+ struct bfad_port_s *port = im_port->port;
+
+ memset(fc_host_active_fc4s(shost), 0,
+ sizeof(fc_host_active_fc4s(shost)));
+
+ if (port->
+ supported_fc4s & (BFA_PORT_ROLE_FCP_IM | BFA_PORT_ROLE_FCP_TM))
+ fc_host_active_fc4s(shost)[2] = 1;
+
+ if (port->supported_fc4s & BFA_PORT_ROLE_FCP_IPFC)
+ fc_host_active_fc4s(shost)[3] = 0x20;
+
+ fc_host_active_fc4s(shost)[7] = 1;
+}
+
+/**
+ * FC transport template entry, get SCSI host link speed.
+ */
+static void
+bfad_im_get_host_speed(struct Scsi_Host *shost)
+{
+ struct bfad_im_port_s *im_port =
+ (struct bfad_im_port_s *) shost->hostdata[0];
+ struct bfad_s *bfad = im_port->bfad;
+ struct bfa_pport_attr_s attr;
+
+ bfa_pport_get_attr(&bfad->bfa, &attr);
+ switch (attr.speed) {
+ case BFA_PPORT_SPEED_8GBPS:
+ fc_host_speed(shost) = FC_PORTSPEED_8GBIT;
+ break;
+ case BFA_PPORT_SPEED_4GBPS:
+ fc_host_speed(shost) = FC_PORTSPEED_4GBIT;
+ break;
+ case BFA_PPORT_SPEED_2GBPS:
+ fc_host_speed(shost) = FC_PORTSPEED_2GBIT;
+ break;
+ case BFA_PPORT_SPEED_1GBPS:
+ fc_host_speed(shost) = FC_PORTSPEED_1GBIT;
+ break;
+ default:
+ fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN;
+ break;
+ }
+}
+
+/**
+ * FC transport template entry, get SCSI host port type.
+ */
+static void
+bfad_im_get_host_fabric_name(struct Scsi_Host *shost)
+{
+ struct bfad_im_port_s *im_port =
+ (struct bfad_im_port_s *) shost->hostdata[0];
+ struct bfad_port_s *port = im_port->port;
+ wwn_t fabric_nwwn = 0;
+
+ fabric_nwwn = bfa_fcs_port_get_fabric_name(port->fcs_port);
+
+ fc_host_fabric_name(shost) = bfa_os_htonll(fabric_nwwn);
+
+}
+
+/**
+ * FC transport template entry, get BFAD statistics.
+ */
+static struct fc_host_statistics *
+bfad_im_get_stats(struct Scsi_Host *shost)
+{
+ struct bfad_im_port_s *im_port =
+ (struct bfad_im_port_s *) shost->hostdata[0];
+ struct bfad_s *bfad = im_port->bfad;
+ struct bfad_hal_comp fcomp;
+ struct fc_host_statistics *hstats;
+ bfa_status_t rc;
+ unsigned long flags;
+
+ hstats = &bfad->link_stats;
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ memset(hstats, 0, sizeof(struct fc_host_statistics));
+ rc = bfa_pport_get_stats(&bfad->bfa,
+ (union bfa_pport_stats_u *) hstats,
+ bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (rc != BFA_STATUS_OK)
+ return NULL;
+
+ wait_for_completion(&fcomp.comp);
+
+ return hstats;
+}
+
+/**
+ * FC transport template entry, reset BFAD statistics.
+ */
+static void
+bfad_im_reset_stats(struct Scsi_Host *shost)
+{
+ struct bfad_im_port_s *im_port =
+ (struct bfad_im_port_s *) shost->hostdata[0];
+ struct bfad_s *bfad = im_port->bfad;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags;
+ bfa_status_t rc;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ rc = bfa_pport_clear_stats(&bfad->bfa, bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ if (rc != BFA_STATUS_OK)
+ return;
+
+ wait_for_completion(&fcomp.comp);
+
+ return;
+}
+
+/**
+ * FC transport template entry, get rport loss timeout.
+ */
+static void
+bfad_im_get_rport_loss_tmo(struct fc_rport *rport)
+{
+ struct bfad_itnim_data_s *itnim_data = rport->dd_data;
+ struct bfad_itnim_s *itnim = itnim_data->itnim;
+ struct bfad_s *bfad = itnim->im->bfad;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ rport->dev_loss_tmo = bfa_fcpim_path_tov_get(&bfad->bfa);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+}
+
+/**
+ * FC transport template entry, set rport loss timeout.
+ */
+static void
+bfad_im_set_rport_loss_tmo(struct fc_rport *rport, u32 timeout)
+{
+ struct bfad_itnim_data_s *itnim_data = rport->dd_data;
+ struct bfad_itnim_s *itnim = itnim_data->itnim;
+ struct bfad_s *bfad = itnim->im->bfad;
+ unsigned long flags;
+
+ if (timeout > 0) {
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ bfa_fcpim_path_tov_set(&bfad->bfa, timeout);
+ rport->dev_loss_tmo = bfa_fcpim_path_tov_get(&bfad->bfa);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ }
+
+}
+
+struct fc_function_template bfad_im_fc_function_template = {
+
+ /* Target dynamic attributes */
+ .get_starget_port_id = bfad_im_get_starget_port_id,
+ .show_starget_port_id = 1,
+ .get_starget_node_name = bfad_im_get_starget_node_name,
+ .show_starget_node_name = 1,
+ .get_starget_port_name = bfad_im_get_starget_port_name,
+ .show_starget_port_name = 1,
+
+ /* Host dynamic attribute */
+ .get_host_port_id = bfad_im_get_host_port_id,
+ .show_host_port_id = 1,
+
+ .issue_fc_host_lip = bfad_im_issue_fc_host_lip,
+
+ /* Host fixed attributes */
+ .show_host_node_name = 1,
+ .show_host_port_name = 1,
+ .show_host_supported_classes = 1,
+ .show_host_supported_fc4s = 1,
+ .show_host_supported_speeds = 1,
+ .show_host_maxframe_size = 1,
+
+ /* More host dynamic attributes */
+ .show_host_port_type = 1,
+ .get_host_port_type = bfad_im_get_host_port_type,
+ .show_host_port_state = 1,
+ .get_host_port_state = bfad_im_get_host_port_state,
+ .show_host_active_fc4s = 1,
+ .get_host_active_fc4s = bfad_im_get_host_active_fc4s,
+ .show_host_speed = 1,
+ .get_host_speed = bfad_im_get_host_speed,
+ .show_host_fabric_name = 1,
+ .get_host_fabric_name = bfad_im_get_host_fabric_name,
+
+ .show_host_symbolic_name = 1,
+
+ /* Statistics */
+ .get_fc_host_stats = bfad_im_get_stats,
+ .reset_fc_host_stats = bfad_im_reset_stats,
+
+ /* Allocation length for host specific data */
+ .dd_fcrport_size = sizeof(struct bfad_itnim_data_s *),
+
+ /* Remote port fixed attributes */
+ .show_rport_maxframe_size = 1,
+ .show_rport_supported_classes = 1,
+ .show_rport_dev_loss_tmo = 1,
+ .get_rport_dev_loss_tmo = bfad_im_get_rport_loss_tmo,
+ .set_rport_dev_loss_tmo = bfad_im_set_rport_loss_tmo,
+};
+
+/**
+ * Scsi_Host_attrs SCSI host attributes
+ */
+static ssize_t
+bfad_im_serial_num_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct bfad_im_port_s *im_port =
+ (struct bfad_im_port_s *) shost->hostdata[0];
+ struct bfad_s *bfad = im_port->bfad;
+ struct bfa_ioc_attr_s ioc_attr;
+
+ memset(&ioc_attr, 0, sizeof(ioc_attr));
+ bfa_get_attr(&bfad->bfa, &ioc_attr);
+ return snprintf(buf, PAGE_SIZE, "%s\n",
+ ioc_attr.adapter_attr.serial_num);
+}
+
+static ssize_t
+bfad_im_model_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct bfad_im_port_s *im_port =
+ (struct bfad_im_port_s *) shost->hostdata[0];
+ struct bfad_s *bfad = im_port->bfad;
+ struct bfa_ioc_attr_s ioc_attr;
+
+ memset(&ioc_attr, 0, sizeof(ioc_attr));
+ bfa_get_attr(&bfad->bfa, &ioc_attr);
+ return snprintf(buf, PAGE_SIZE, "%s\n", ioc_attr.adapter_attr.model);
+}
+
+static ssize_t
+bfad_im_model_desc_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct bfad_im_port_s *im_port =
+ (struct bfad_im_port_s *) shost->hostdata[0];
+ struct bfad_s *bfad = im_port->bfad;
+ struct bfa_ioc_attr_s ioc_attr;
+
+ memset(&ioc_attr, 0, sizeof(ioc_attr));
+ bfa_get_attr(&bfad->bfa, &ioc_attr);
+ return snprintf(buf, PAGE_SIZE, "%s\n",
+ ioc_attr.adapter_attr.model_descr);
+}
+
+static ssize_t
+bfad_im_node_name_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct bfad_im_port_s *im_port =
+ (struct bfad_im_port_s *) shost->hostdata[0];
+ struct bfad_port_s *port = im_port->port;
+ u64 nwwn;
+
+ nwwn = bfa_fcs_port_get_nwwn(port->fcs_port);
+ return snprintf(buf, PAGE_SIZE, "0x%llx\n", bfa_os_htonll(nwwn));
+}
+
+static ssize_t
+bfad_im_symbolic_name_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct bfad_im_port_s *im_port =
+ (struct bfad_im_port_s *) shost->hostdata[0];
+ struct bfad_s *bfad = im_port->bfad;
+ struct bfa_ioc_attr_s ioc_attr;
+
+ memset(&ioc_attr, 0, sizeof(ioc_attr));
+ bfa_get_attr(&bfad->bfa, &ioc_attr);
+
+ return snprintf(buf, PAGE_SIZE, "Brocade %s FV%s DV%s\n",
+ ioc_attr.adapter_attr.model,
+ ioc_attr.adapter_attr.fw_ver, BFAD_DRIVER_VERSION);
+}
+
+static ssize_t
+bfad_im_hw_version_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct bfad_im_port_s *im_port =
+ (struct bfad_im_port_s *) shost->hostdata[0];
+ struct bfad_s *bfad = im_port->bfad;
+ struct bfa_ioc_attr_s ioc_attr;
+
+ memset(&ioc_attr, 0, sizeof(ioc_attr));
+ bfa_get_attr(&bfad->bfa, &ioc_attr);
+ return snprintf(buf, PAGE_SIZE, "%s\n", ioc_attr.adapter_attr.hw_ver);
+}
+
+static ssize_t
+bfad_im_drv_version_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", BFAD_DRIVER_VERSION);
+}
+
+static ssize_t
+bfad_im_optionrom_version_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct bfad_im_port_s *im_port =
+ (struct bfad_im_port_s *) shost->hostdata[0];
+ struct bfad_s *bfad = im_port->bfad;
+ struct bfa_ioc_attr_s ioc_attr;
+
+ memset(&ioc_attr, 0, sizeof(ioc_attr));
+ bfa_get_attr(&bfad->bfa, &ioc_attr);
+ return snprintf(buf, PAGE_SIZE, "%s\n",
+ ioc_attr.adapter_attr.optrom_ver);
+}
+
+static ssize_t
+bfad_im_fw_version_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct bfad_im_port_s *im_port =
+ (struct bfad_im_port_s *) shost->hostdata[0];
+ struct bfad_s *bfad = im_port->bfad;
+ struct bfa_ioc_attr_s ioc_attr;
+
+ memset(&ioc_attr, 0, sizeof(ioc_attr));
+ bfa_get_attr(&bfad->bfa, &ioc_attr);
+ return snprintf(buf, PAGE_SIZE, "%s\n", ioc_attr.adapter_attr.fw_ver);
+}
+
+static ssize_t
+bfad_im_num_of_ports_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct bfad_im_port_s *im_port =
+ (struct bfad_im_port_s *) shost->hostdata[0];
+ struct bfad_s *bfad = im_port->bfad;
+ struct bfa_ioc_attr_s ioc_attr;
+
+ memset(&ioc_attr, 0, sizeof(ioc_attr));
+ bfa_get_attr(&bfad->bfa, &ioc_attr);
+ return snprintf(buf, PAGE_SIZE, "%d\n", ioc_attr.adapter_attr.nports);
+}
+
+static ssize_t
+bfad_im_drv_name_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", BFAD_DRIVER_NAME);
+}
+
+static ssize_t
+bfad_im_num_of_discovered_ports_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct bfad_im_port_s *im_port =
+ (struct bfad_im_port_s *) shost->hostdata[0];
+ struct bfad_port_s *port = im_port->port;
+ struct bfad_s *bfad = im_port->bfad;
+ int nrports = 2048;
+ wwn_t *rports = NULL;
+ unsigned long flags;
+
+ rports = kzalloc(sizeof(wwn_t) * nrports , GFP_ATOMIC);
+ if (rports == NULL)
+ return snprintf(buf, PAGE_SIZE, "Failed\n");
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ bfa_fcs_port_get_rports(port->fcs_port, rports, &nrports);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ kfree(rports);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", nrports);
+}
+
+static DEVICE_ATTR(serial_number, S_IRUGO,
+ bfad_im_serial_num_show, NULL);
+static DEVICE_ATTR(model, S_IRUGO, bfad_im_model_show, NULL);
+static DEVICE_ATTR(model_description, S_IRUGO,
+ bfad_im_model_desc_show, NULL);
+static DEVICE_ATTR(node_name, S_IRUGO, bfad_im_node_name_show, NULL);
+static DEVICE_ATTR(symbolic_name, S_IRUGO,
+ bfad_im_symbolic_name_show, NULL);
+static DEVICE_ATTR(hardware_version, S_IRUGO,
+ bfad_im_hw_version_show, NULL);
+static DEVICE_ATTR(driver_version, S_IRUGO,
+ bfad_im_drv_version_show, NULL);
+static DEVICE_ATTR(option_rom_version, S_IRUGO,
+ bfad_im_optionrom_version_show, NULL);
+static DEVICE_ATTR(firmware_version, S_IRUGO,
+ bfad_im_fw_version_show, NULL);
+static DEVICE_ATTR(number_of_ports, S_IRUGO,
+ bfad_im_num_of_ports_show, NULL);
+static DEVICE_ATTR(driver_name, S_IRUGO, bfad_im_drv_name_show, NULL);
+static DEVICE_ATTR(number_of_discovered_ports, S_IRUGO,
+ bfad_im_num_of_discovered_ports_show, NULL);
+
+struct device_attribute *bfad_im_host_attrs[] = {
+ &dev_attr_serial_number,
+ &dev_attr_model,
+ &dev_attr_model_description,
+ &dev_attr_node_name,
+ &dev_attr_symbolic_name,
+ &dev_attr_hardware_version,
+ &dev_attr_driver_version,
+ &dev_attr_option_rom_version,
+ &dev_attr_firmware_version,
+ &dev_attr_number_of_ports,
+ &dev_attr_driver_name,
+ &dev_attr_number_of_discovered_ports,
+ NULL,
+};
+
+struct device_attribute *bfad_im_vport_attrs[] = {
+ &dev_attr_serial_number,
+ &dev_attr_model,
+ &dev_attr_model_description,
+ &dev_attr_node_name,
+ &dev_attr_symbolic_name,
+ &dev_attr_hardware_version,
+ &dev_attr_driver_version,
+ &dev_attr_option_rom_version,
+ &dev_attr_firmware_version,
+ &dev_attr_number_of_ports,
+ &dev_attr_driver_name,
+ &dev_attr_number_of_discovered_ports,
+ NULL,
+};
+
+
diff -urpN orig/drivers/scsi/bfa/bfad_attr.h patch/drivers/scsi/bfa/bfad_attr.h
--- orig/drivers/scsi/bfa/bfad_attr.h 1969-12-31 16:00:00.000000000 -0800
+++ patch/drivers/scsi/bfa/bfad_attr.h 2009-07-21 15:34:02.000000000 -0700
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2005-2008 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * http://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.
+ */
+
+#ifndef __BFAD_ATTR_H__
+#define __BFAD_ATTR_H__
+/**
+ * bfad_attr.h VMware driver configuration interface module.
+ */
+
+extern int supported_fc4s;
+
+/**
+ * FC_transport_template FC transport template
+ */
+
+struct Scsi_Host*
+bfad_os_dev_to_shost(struct scsi_target *starget);
+
+/**
+ * FC transport template entry, get SCSI target port ID.
+ */
+void
+bfad_im_get_starget_port_id(struct scsi_target *starget);
+
+/**
+ * FC transport template entry, get SCSI target nwwn.
+ */
+void
+bfad_im_get_starget_node_name(struct scsi_target *starget);
+
+/**
+ * FC transport template entry, get SCSI target pwwn.
+ */
+void
+bfad_im_get_starget_port_name(struct scsi_target *starget);
+
+/**
+ * FC transport template entry, get SCSI host port ID.
+ */
+void
+bfad_im_get_host_port_id(struct Scsi_Host *shost);
+
+/**
+ * FC transport template entry, issue a LIP.
+ */
+int
+bfad_im_issue_fc_host_lip(struct Scsi_Host *shost);
+
+struct Scsi_Host*
+bfad_os_starget_to_shost(struct scsi_target *starget);
+
+
+#endif /* __BFAD_ATTR_H__ */
diff -urpN orig/drivers/scsi/bfa/bfad_drv.h patch/drivers/scsi/bfa/bfad_drv.h
--- orig/drivers/scsi/bfa/bfad_drv.h 1969-12-31 16:00:00.000000000 -0800
+++ patch/drivers/scsi/bfa/bfad_drv.h 2009-07-21 15:34:02.000000000 -0700
@@ -0,0 +1,388 @@
+/*
+ * Copyright (c) 2005-2008 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * http://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.
+ */
+
+/**
+ * Contains base driver definitions.
+ */
+
+/**
+ * bfa_drv.h Linux driver data structures.
+ */
+
+#ifndef __BFAD_DRV_H__
+#define __BFAD_DRV_H__
+
+#include "bfa_os_inc.h"
+
+#include <bfa.h>
+#include <bfa_svc.h>
+#include <fcs/bfa_fcs.h>
+#include <defs/bfa_defs_pci.h>
+#include <defs/bfa_defs_port.h>
+#include <defs/bfa_defs_rport.h>
+#include <fcs/bfa_fcs_rport.h>
+#include <defs/bfa_defs_vport.h>
+#include <fcs/bfa_fcs_vport.h>
+
+#include <cs/bfa_plog.h>
+#include "aen/bfa_aen.h"
+#include <log/bfa_log_linux.h>
+
+#define BFAD_DRIVER_NAME "bfa"
+#ifdef BFA_DRIVER_VERSION
+#define BFAD_DRIVER_VERSION BFA_DRIVER_VERSION
+#else
+#define BFAD_DRIVER_VERSION "2.0.0.0"
+#endif
+
+#if defined(CONFIG_PCIEPORTBUS) && defined(CONFIG_PCIEAER)
+#define BFAD_ENABLE_PCIE_AER(x) pci_enable_pcie_error_reporting(x)
+#else
+#define BFAD_ENABLE_PCIE_AER(x) {}
+#endif
+
+
+#define BFAD_IRQ_FLAGS IRQF_SHARED
+
+#ifndef FC_PORTSPEED_8GBIT
+#define FC_PORTSPEED_8GBIT 0x10
+#endif
+
+/*
+ * BFAD flags
+ */
+#define BFAD_MSIX_ON 0x00000001
+#define BFAD_HAL_INIT_DONE 0x00000002
+#define BFAD_DRV_INIT_DONE 0x00000004
+#define BFAD_CFG_PPORT_DONE 0x00000008
+#define BFAD_HAL_START_DONE 0x00000010
+#define BFAD_PORT_ONLINE 0x00000020
+#define BFAD_RPORT_ONLINE 0x00000040
+
+#define BFAD_PORT_DELETE 0x00000001
+
+/*
+ * BFAD related definition
+ */
+#define SCSI_SCAN_DELAY HZ
+#define BFAD_STOP_TIMEOUT 30
+#define BFAD_SUSPEND_TIMEOUT BFAD_STOP_TIMEOUT
+
+/*
+ * BFAD configuration parameter default values
+ */
+#define BFAD_LUN_QUEUE_DEPTH 32
+#define BFAD_IO_MAX_SGE SG_ALL
+
+#define bfad_isr_t irq_handler_t
+
+#ifdef CONFIG_PCI_MSI
+#define MAX_MSIX_ENTRY 22
+
+struct bfad_msix_s {
+ struct bfad_s *bfad;
+ struct msix_entry msix;
+};
+#endif
+
+enum bfad_port_pvb_type {
+ BFAD_PORT_PHYS_BASE = 0,
+ BFAD_PORT_PHYS_VPORT = 1,
+ BFAD_PORT_VF_BASE = 2,
+ BFAD_PORT_VF_VPORT = 3,
+};
+
+/*
+ * PORT data structure
+ */
+struct bfad_port_s {
+ struct list_head list_entry;
+ struct bfad_s *bfad;
+ struct bfa_fcs_port_s *fcs_port;
+ u32 roles;
+ s32 flags;
+ u32 supported_fc4s;
+ u8 ipfc_flags;
+ enum bfad_port_pvb_type pvb_type;
+ struct bfad_im_port_s *im_port; /* IM specific data */
+ struct bfad_tm_port_s *tm_port; /* TM specific data */
+ struct bfad_ipfc_port_s *ipfc_port; /* IPFC specific data */
+};
+
+/*
+ * VPORT data structure
+ */
+struct bfad_vport_s {
+ struct bfad_port_s drv_port;
+ struct bfa_fcs_vport_s fcs_vport;
+ struct completion *comp_del;
+};
+
+/*
+ * VF data structure
+ */
+struct bfad_vf_s {
+ bfa_fcs_vf_t fcs_vf;
+ struct bfad_port_s base_port; /* base port for vf */
+ struct bfad_s *bfad;
+};
+
+struct bfad_cfg_param_s {
+ u32 rport_del_timeout;
+ u32 ioc_queue_depth;
+ u32 lun_queue_depth;
+ u32 io_max_sge;
+ u32 binding_method;
+};
+
+#define BFAD_AEN_MAX_APPS 8
+struct bfad_aen_file_s {
+ struct list_head qe;
+ struct bfad_s *bfad;
+ s32 ri;
+ s32 app_id;
+};
+
+/*
+ * BFAD (PCI function) data structure
+ */
+struct bfad_s {
+ struct list_head list_entry;
+ struct bfa_s bfa;
+ struct bfa_fcs_s bfa_fcs;
+ struct pci_dev *pcidev;
+ const char *pci_name;
+ struct bfa_pcidev_s hal_pcidev;
+ struct bfa_ioc_pci_attr_s pci_attr;
+ unsigned long pci_bar0_map;
+ void __iomem *pci_bar0_kva;
+ struct completion comp;
+ struct completion suspend;
+ struct completion disable_comp;
+ bfa_boolean_t disable_active;
+ struct bfad_port_s pport; /* physical port of the BFAD */
+ struct bfa_meminfo_s meminfo;
+ struct bfa_iocfc_cfg_s ioc_cfg;
+ u32 inst_no; /* BFAD instance number */
+ u32 bfad_flags;
+ spinlock_t bfad_lock;
+ struct bfad_cfg_param_s cfg_data;
+#ifdef CONFIG_PCI_MSI
+ struct bfad_msix_s msix_tab[MAX_MSIX_ENTRY];
+ int nvec;
+#endif
+
+ char adapter_name[BFA_ADAPTER_SYM_NAME_LEN];
+ char port_name[BFA_ADAPTER_SYM_NAME_LEN];
+ struct timer_list hal_tmo;
+ unsigned long hs_start;
+ struct bfad_im_s *im; /* IM specific data */
+ struct bfad_tm_s *tm; /* TM specific data */
+ struct bfad_ipfc_s *ipfc; /* IPFC specific data */
+ struct bfa_log_mod_s log_data;
+ struct bfa_trc_mod_s *trcmod;
+ struct bfa_log_mod_s *logmod;
+ struct bfa_aen_s *aen;
+ struct bfa_aen_s aen_buf;
+ struct bfad_aen_file_s file_buf[BFAD_AEN_MAX_APPS];
+ struct list_head file_q;
+ struct list_head file_free_q;
+ struct bfa_plog_s plog_buf;
+ int ref_count;
+ bfa_boolean_t ipfc_enabled;
+ struct fc_host_statistics link_stats;
+
+ struct kobject *bfa_kobj;
+ struct kobject *ioc_kobj;
+ struct kobject *pport_kobj;
+ struct kobject *lport_kobj;
+};
+
+/*
+ * RPORT data structure
+ */
+struct bfad_rport_s {
+ struct bfa_fcs_rport_s fcs_rport;
+};
+
+struct bfad_buf_info {
+ void *virt;
+ dma_addr_t phys;
+ u32 size;
+};
+
+struct bfad_fcxp {
+ struct bfad_port_s *port;
+ struct bfa_rport_s *bfa_rport;
+ bfa_status_t req_status;
+ u16 tag;
+ u16 rsp_len;
+ u16 rsp_maxlen;
+ u8 use_ireqbuf;
+ u8 use_irspbuf;
+ u32 num_req_sgles;
+ u32 num_rsp_sgles;
+ struct fchs_s fchs;
+ void *reqbuf_info;
+ void *rspbuf_info;
+ struct bfa_sge_s *req_sge;
+ struct bfa_sge_s *rsp_sge;
+ fcxp_send_cb_t send_cbfn;
+ void *send_cbarg;
+ void *bfa_fcxp;
+ struct completion comp;
+};
+
+struct bfad_hal_comp {
+ bfa_status_t status;
+ struct completion comp;
+};
+
+/*
+ * Macro to obtain the immediate lower power
+ * of two for the integer.
+ */
+#define nextLowerInt(x) \
+do { \
+ int j; \
+ (*x)--; \
+ for (j = 1; j < (sizeof(int) * 8); j <<= 1) \
+ (*x) = (*x) | (*x) >> j; \
+ (*x)++; \
+ (*x) = (*x) >> 1; \
+} while (0)
+
+
+#define BFAD_WORK_HANDLER(name) void name(struct work_struct *work)
+#define BFAD_INIT_WORK(x, work, func) INIT_WORK(&(x)->work, func)
+
+#define list_remove_head(list, entry, type, member) \
+do { \
+ entry = NULL; \
+ if (!list_empty(list)) { \
+ entry = list_entry((list)->next, type, member); \
+ list_del_init(&entry->member); \
+ } \
+} while (0)
+
+#define list_get_first(list, type, member) \
+((list_empty(list)) ? NULL : \
+ list_entry((list)->next, type, member))
+
+bfa_boolean_t bfad_is_ready(void);
+bfa_status_t bfad_vport_create(struct bfad_s *bfad, u16 vf_id,
+ struct bfa_port_cfg_s *port_cfg);
+bfa_status_t bfad_vf_create(struct bfad_s *bfad, u16 vf_id,
+ struct bfa_port_cfg_s *port_cfg);
+bfa_status_t bfad_cfg_pport(struct bfad_s *bfad, enum bfa_port_role role);
+bfa_status_t bfad_fc4_probe(struct bfad_s *bfad);
+bfa_status_t bfad_drv_init(struct bfad_s *bfad);
+struct bfad_s *bfad_find_bfad_by_inst_no(int inst_no);
+void bfad_drv_start(struct bfad_s *bfad);
+void bfad_uncfg_pport(struct bfad_s *bfad);
+void bfad_drv_stop(struct bfad_s *bfad);
+void bfad_remove_intr(struct bfad_s *bfad);
+void bfad_fc4_probe_undo(struct bfad_s *bfad);
+void bfad_hal_mem_release(struct bfad_s *bfad);
+void bfad_hcb_comp(void *arg, bfa_status_t status);
+
+int bfad_os_ioctl_init(void);
+int bfad_os_ioctl_exit(void);
+int bfad_setup_intr(struct bfad_s *bfad);
+void bfad_remove_intr(struct bfad_s *bfad);
+int bfad_os_pci_register_driver(struct pci_driver *drv);
+void bfad_os_pci_unregister_driver(struct pci_driver *drv);
+void bfad_os_device_sysfs_create(struct bfad_s *);
+void bfad_os_device_sysfs_remove(struct bfad_s *);
+void bfad_os_pci_set_mwi(struct pci_dev *pdev);
+void bfad_os_idr_init(struct idr *idr);
+void bfad_os_idr_destroy(struct idr *idr);
+void *bfad_os_dma_alloc(struct bfad_s *bfad, struct bfa_mem_elem_s
+ *meminfo_elem, dma_addr_t *phys_addr);
+void bfad_os_dma_free(struct bfad_s *bfad, struct bfa_mem_elem_s
+ *meminfo_elem);
+void bfad_os_idr_remove(struct idr *idp, int id);
+int bfad_os_idr_pre_get(struct idr *idp, gfp_t gfp_mask);
+void bfad_os_iounmap(struct pci_dev *pdev, struct bfad_s *bfad);
+
+void bfad_update_hal_cfg(struct bfa_iocfc_cfg_s *bfa_cfg);
+bfa_status_t bfad_hal_mem_alloc(struct bfad_s *bfad);
+void bfad_bfa_tmo(unsigned long data);
+void bfad_init_timer(struct bfad_s *bfad);
+int bfad_pci_init(struct pci_dev *pdev, struct bfad_s *bfad);
+void bfad_pci_uninit(struct pci_dev *pdev, struct bfad_s *bfad);
+void bfad_fcs_port_cfg(struct bfad_s *bfad);
+void bfad_drv_uninit(struct bfad_s *bfad);
+void bfad_drv_log_level_set(struct bfad_s *bfad);
+bfa_status_t bfad_fc4_module_init(void);
+void bfad_fc4_module_exit(void);
+int bfad_ipfc_probe(struct bfad_s *bfad);
+int bfad_ipfc_probe_undo(struct bfad_s *bfad);
+void bfad_ipfc_probe_post(struct bfad_s *bfad);
+int bfad_ipfc_module_init(void);
+void bfad_ipfc_module_exit(void);
+int bfad_ipfc_port_online(struct bfad_s *bfad,
+ struct bfad_port_s *port);
+int bfad_ipfc_port_offline(struct bfad_s *bfad,
+ struct bfad_port_s *port);
+int bfad_ipfc_port_new(struct bfad_s *bfad,
+ struct bfad_port_s *port, int port_type);
+int bfad_ipfc_port_delete(struct bfad_s *bfad,
+ struct bfad_port_s *port);
+
+
+
+
+void bfad_pci_remove(struct pci_dev *pdev);
+int bfad_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid);
+void bfad_os_rport_online_wait(struct bfad_s *bfad);
+int bfad_os_get_linkup_delay(struct bfad_s *bfad);
+u32 bfa_os_get_instance_id(struct bfad_s *bfad);
+int bfad_install_msix_handler(struct bfad_s *bfad);
+
+extern struct idr bfad_im_port_index;
+extern struct pci_driver *bfad_pci_driver_p;
+
+extern struct pci_device_id bfad_id_table[];
+extern struct semaphore bfad_sem;
+extern int bfad_scan_done;
+extern struct list_head bfad_list;
+extern char *os_name;
+extern char *os_patch;
+extern char *host_name;
+extern int num_rports;
+extern int num_ios;
+extern int num_tms;
+extern int num_fcxps;
+extern int num_ufbufs;
+extern int reqq_size;
+extern int rspq_size;
+extern int num_sgpgs;
+extern int rport_del_timeout;
+extern int bfa_lun_queue_depth;
+extern int bfa_io_max_sge;
+extern int log_level;
+extern int ioc_auto_recover;
+extern int ipfc_enable;
+extern int ipfc_mtu;
+extern int linkup_delay;
+extern int msix_disable;
+
+extern int supported_fc4s;
+
+
+#endif /* __BFAD_DRV_H__ */
diff -urpN orig/drivers/scsi/bfa/bfad_fwimg.c patch/drivers/scsi/bfa/bfad_fwimg.c
--- orig/drivers/scsi/bfa/bfad_fwimg.c 1969-12-31 16:00:00.000000000 -0800
+++ patch/drivers/scsi/bfa/bfad_fwimg.c 2009-07-21 15:34:02.000000000 -0700
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2005-2008 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * http://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_fwimg.c Linux driver PCI interface module.
+ */
+#include <bfa_os_inc.h>
+#include <bfad_drv.h>
+#include <bfad_im_compat.h>
+#include <defs/bfa_defs_version.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <asm/uaccess.h>
+#include <asm/fcntl.h>
+#include <linux/pci.h>
+#include <linux/firmware.h>
+#include <bfa_fwimg_priv.h>
+#include <bfa.h>
+
+u32 bfi_image_ct_size;
+u32 bfi_image_cb_size;
+u32 *bfi_image_ct;
+u32 *bfi_image_cb;
+
+
+#define BFAD_FW_FILE_CT "ctfw.bin"
+#define BFAD_FW_FILE_CB "cbfw.bin"
+
+u32 *
+bfad_read_firmware(struct pci_dev *pdev, u32 **bfi_image,
+ u32 *bfi_image_size, char *fw_name)
+{
+ const struct firmware *fw;
+
+ if (request_firmware(&fw, fw_name, &pdev->dev)) {
+ printk(KERN_ALERT "Can't locate firmware %s\n", fw_name);
+ goto error;
+ }
+
+ *bfi_image = vmalloc(fw->size);
+ if (NULL == *bfi_image) {
+ printk(KERN_ALERT "Fail to allocate buffer for fw image "
+ "size=%x!\n", (u32) fw->size);
+ goto error;
+ }
+
+ memcpy(*bfi_image, fw->data, fw->size);
+ *bfi_image_size = fw->size/sizeof(u32);
+
+ return(*bfi_image);
+
+error:
+ return(NULL);
+}
+
+u32 *
+bfad_get_firmware_buf(struct pci_dev *pdev)
+{
+ if (pdev->device == BFA_PCI_DEVICE_ID_CT) {
+ if (bfi_image_ct_size == 0)
+ bfad_read_firmware(pdev, &bfi_image_ct,
+ &bfi_image_ct_size, BFAD_FW_FILE_CT);
+ return(bfi_image_ct);
+ } else {
+ if (bfi_image_cb_size == 0)
+ bfad_read_firmware(pdev, &bfi_image_cb,
+ &bfi_image_cb_size, BFAD_FW_FILE_CB);
+ return(bfi_image_cb);
+ }
+}
+
+u32 *
+bfi_image_ct_get_chunk(u32 off)
+{ return (u32 *)(bfi_image_ct + off); }
+
+u32 *
+bfi_image_cb_get_chunk(u32 off)
+{ return (u32 *)(bfi_image_cb + off); }
+
+
+char bfa_version[BFA_VERSION_LEN] = "2.0.0.0 ";
diff -urpN orig/drivers/scsi/bfa/bfad_im.c patch/drivers/scsi/bfa/bfad_im.c
--- orig/drivers/scsi/bfa/bfad_im.c 1969-12-31 16:00:00.000000000 -0800
+++ patch/drivers/scsi/bfa/bfad_im.c 2009-07-21 15:34:02.000000000 -0700
@@ -0,0 +1,1349 @@
+/*
+ * Copyright (c) 2005-2008 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * http://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_im.c Linux driver IM module.
+ */
+
+#include "bfad_drv.h"
+#include "bfad_im.h"
+#include "bfad_trcmod.h"
+#include "bfa_cb_ioim_macros.h"
+#include <fcb/bfa_fcb_fcpim.h>
+
+BFA_TRC_FILE(LDRV, IM);
+
+DEFINE_IDR(bfad_im_port_index);
+struct scsi_transport_template *bfad_im_scsi_transport_template;
+BFAD_WORK_HANDLER(bfad_im_port_delete_handler);
+
+void
+bfa_cb_ioim_done(void *drv, struct bfad_ioim_s *dio,
+ enum bfi_ioim_status io_status, u8 scsi_status,
+ int sns_len, u8 *sns_info, s32 residue)
+{
+ struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio;
+ struct bfad_s *bfad = drv;
+ struct bfad_itnim_data_s *itnim_data;
+ struct bfad_itnim_s *itnim;
+ u8 host_status = DID_OK;
+
+ switch (io_status) {
+ case BFI_IOIM_STS_OK:
+ bfa_trc(bfad, scsi_status);
+ cmnd->result = ScsiResult(host_status, scsi_status);
+ scsi_set_resid(cmnd, 0);
+
+ if (sns_len > 0) {
+ bfa_trc(bfad, sns_len);
+ if (sns_len > SCSI_SENSE_BUFFERSIZE)
+ sns_len = SCSI_SENSE_BUFFERSIZE;
+ memcpy(cmnd->sense_buffer, sns_info, sns_len);
+ }
+ if (residue > 0)
+ scsi_set_resid(cmnd, residue);
+ break;
+
+ case BFI_IOIM_STS_ABORTED:
+ case BFI_IOIM_STS_TIMEDOUT:
+ case BFI_IOIM_STS_PATHTOV:
+ default:
+ host_status = DID_ERROR;
+ cmnd->result = ScsiResult(host_status, 0);
+ }
+
+ /* Unmap DMA, if host is NULL, it means a scsi passthru cmd */
+ if (cmnd->device->host != NULL)
+ bfad_os_im_dma_unmap(bfad, cmnd);
+
+ cmnd->host_scribble = NULL;
+ bfa_trc(bfad, cmnd->result);
+
+ itnim_data = cmnd->device->hostdata;
+ if (itnim_data) {
+ itnim = itnim_data->itnim;
+ if (!cmnd->result && itnim &&
+ (bfa_lun_queue_depth > cmnd->device->queue_depth)) {
+ /* Queue depth adjustment for good status completion */
+ bfad_os_ramp_up_qdepth(itnim, cmnd->device);
+ } else if (cmnd->result == SAM_STAT_TASK_SET_FULL && itnim) {
+ /* qfull handling */
+ bfad_os_handle_qfull(itnim, cmnd->device);
+ }
+ }
+
+ cmnd->scsi_done(cmnd);
+}
+
+void
+bfa_cb_ioim_good_comp(void *drv, struct bfad_ioim_s *dio)
+{
+ struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio;
+ struct bfad_itnim_data_s *itnim_data;
+ struct bfad_itnim_s *itnim;
+ struct bfad_s *bfad = drv;
+ u8 host_status = DID_OK;
+
+ cmnd->result = ScsiResult(host_status, SCSI_STATUS_GOOD);
+
+ /* Unmap DMA, if host is NULL, it means a scsi passthru cmd */
+ if (cmnd->device->host != NULL)
+ bfad_os_im_dma_unmap(bfad, cmnd);
+
+ cmnd->host_scribble = NULL;
+
+ bfa_trc_fp(bfad, cmnd->result);
+
+ /* Queue depth adjustment */
+ if (bfa_lun_queue_depth > cmnd->device->queue_depth) {
+ itnim_data = cmnd->device->hostdata;
+ if (itnim_data) {
+ itnim = itnim_data->itnim;
+ if (itnim)
+ bfad_os_ramp_up_qdepth(itnim, cmnd->device);
+ }
+ }
+
+ cmnd->scsi_done(cmnd);
+}
+
+void
+bfa_cb_ioim_abort(void *drv, struct bfad_ioim_s *dio)
+{
+ struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio;
+ struct bfad_s *bfad = drv;
+
+ cmnd->result = ScsiResult(DID_ERROR, 0);
+
+ /* Unmap DMA, if host is NULL, it means a scsi passthru cmd */
+ if (cmnd->device->host != NULL)
+ bfad_os_im_dma_unmap(bfad, cmnd);
+
+ bfa_trc(bfad, cmnd->result);
+ cmnd->host_scribble = NULL;
+}
+
+void
+bfa_cb_tskim_done(void *bfad, struct bfad_tskim_s *dtsk,
+ enum bfi_tskim_status tsk_status)
+{
+ struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dtsk;
+ wait_queue_head_t *wq;
+
+ cmnd->SCp.Status |= tsk_status << 1;
+ set_bit(IO_DONE_BIT, (unsigned long *)&cmnd->SCp.Status);
+ wq = (wait_queue_head_t *) cmnd->SCp.ptr;
+ cmnd->SCp.ptr = NULL;
+
+ if (wq)
+ wake_up(wq);
+}
+
+void
+bfa_cb_ioim_resfree(void *drv)
+{
+}
+
+/**
+ * Scsi_Host_template SCSI host template
+ */
+/**
+ * Scsi_Host template entry, returns BFAD PCI info.
+ */
+const char *
+bfad_im_info(struct Scsi_Host *shost)
+{
+ static char bfa_buf[256];
+ struct bfad_im_port_s *im_port =
+ (struct bfad_im_port_s *) shost->hostdata[0];
+ struct bfa_ioc_attr_s ioc_attr;
+ struct bfad_s *bfad = im_port->bfad;
+
+ memset(&ioc_attr, 0, sizeof(ioc_attr));
+ bfa_get_attr(&bfad->bfa, &ioc_attr);
+
+ memset(bfa_buf, 0, sizeof(bfa_buf));
+ snprintf(bfa_buf, sizeof(bfa_buf),
+ "Brocade FC/FCOE Adapter, " "model: %s hwpath: %s driver: %s",
+ ioc_attr.adapter_attr.model, bfad->pci_name,
+ BFAD_DRIVER_VERSION);
+ return bfa_buf;
+}
+
+/**
+ * Scsi_Host template entry, aborts the specified SCSI command.
+ *
+ * Returns: SUCCESS or FAILED.
+ */
+int
+bfad_im_abort_handler(struct scsi_cmnd *cmnd)
+{
+ struct Scsi_Host *shost = cmnd->device->host;
+ struct bfad_im_port_s *im_port =
+ (struct bfad_im_port_s *) shost->hostdata[0];
+ struct bfad_s *bfad = im_port->bfad;
+ struct bfa_ioim_s *hal_io;
+ unsigned long flags;
+ u32 timeout;
+ int rc = FAILED;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ hal_io = (struct bfa_ioim_s *) cmnd->host_scribble;
+ if (!hal_io) {
+ /* IO has been completed, retrun success */
+ rc = SUCCESS;
+ goto out;
+ }
+ if (hal_io->dio != (struct bfad_ioim_s *) cmnd) {
+ rc = FAILED;
+ goto out;
+ }
+
+ bfa_trc(bfad, hal_io->iotag);
+ bfa_log(bfad->logmod, BFA_LOG_LINUX_SCSI_ABORT,
+ im_port->shost->host_no, cmnd, hal_io->iotag);
+ bfa_ioim_abort(hal_io);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ /* Need to wait until the command get aborted */
+ timeout = 10;
+ while ((struct bfa_ioim_s *) cmnd->host_scribble == hal_io) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(timeout);
+ if (timeout < 4 * HZ)
+ timeout *= 2;
+ }
+
+ cmnd->scsi_done(cmnd);
+ bfa_trc(bfad, hal_io->iotag);
+ bfa_log(bfad->logmod, BFA_LOG_LINUX_SCSI_ABORT_COMP,
+ im_port->shost->host_no, cmnd, hal_io->iotag);
+ return SUCCESS;
+out:
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ return rc;
+}
+
+static bfa_status_t
+bfad_im_target_reset_send(struct bfad_s *bfad, struct scsi_cmnd *cmnd,
+ struct bfad_itnim_s *itnim)
+{
+ struct bfa_tskim_s *tskim;
+ struct bfa_itnim_s *bfa_itnim;
+ bfa_status_t rc = BFA_STATUS_OK;
+
+ bfa_itnim = bfa_fcs_itnim_get_halitn(&itnim->fcs_itnim);
+ tskim = bfa_tskim_alloc(&bfad->bfa, (struct bfad_tskim_s *) cmnd);
+ if (!tskim) {
+ BFA_DEV_PRINTF(bfad, BFA_ERR,
+ "target reset, fail to allocate tskim\n");
+ rc = BFA_STATUS_FAILED;
+ goto out;
+ }
+
+ /*
+ * Set host_scribble to NULL to avoid aborting a task command if
+ * happens.
+ */
+ cmnd->host_scribble = NULL;
+ cmnd->SCp.Status = 0;
+ bfa_itnim = bfa_fcs_itnim_get_halitn(&itnim->fcs_itnim);
+ bfa_tskim_start(tskim, bfa_itnim, (lun_t)0,
+ FCP_TM_TARGET_RESET, BFAD_TARGET_RESET_TMO);
+out:
+ return rc;
+}
+
+/**
+ * Scsi_Host template entry, resets a LUN and abort its all commands.
+ *
+ * Returns: SUCCESS or FAILED.
+ *
+ */
+int
+bfad_im_reset_lun_handler(struct scsi_cmnd *cmnd)
+{
+ struct Scsi_Host *shost = cmnd->device->host;
+ struct bfad_im_port_s *im_port =
+ (struct bfad_im_port_s *) shost->hostdata[0];
+ struct bfad_itnim_data_s *itnim_data = cmnd->device->hostdata;
+ struct bfad_s *bfad = im_port->bfad;
+ struct bfa_tskim_s *tskim;
+ struct bfad_itnim_s *itnim;
+ struct bfa_itnim_s *bfa_itnim;
+ DECLARE_WAIT_QUEUE_HEAD(wq);
+ int rc = SUCCESS;
+ unsigned long flags;
+ enum bfi_tskim_status task_status;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ itnim = itnim_data->itnim;
+ if (!itnim) {
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ rc = FAILED;
+ goto out;
+ }
+
+ tskim = bfa_tskim_alloc(&bfad->bfa, (struct bfad_tskim_s *) cmnd);
+ if (!tskim) {
+ BFA_DEV_PRINTF(bfad, BFA_ERR,
+ "LUN reset, fail to allocate tskim");
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ rc = FAILED;
+ goto out;
+ }
+
+ /**
+ * Set host_scribble to NULL to avoid aborting a task command
+ * if happens.
+ */
+ cmnd->host_scribble = NULL;
+ cmnd->SCp.ptr = (char *)&wq;
+ cmnd->SCp.Status = 0;
+ bfa_itnim = bfa_fcs_itnim_get_halitn(&itnim->fcs_itnim);
+ bfa_tskim_start(tskim, bfa_itnim,
+ bfad_int_to_lun(cmnd->device->lun),
+ FCP_TM_LUN_RESET, BFAD_LUN_RESET_TMO);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ wait_event(wq, test_bit(IO_DONE_BIT,
+ (unsigned long *)&cmnd->SCp.Status));
+
+ task_status = cmnd->SCp.Status >> 1;
+ if (task_status != BFI_TSKIM_STS_OK) {
+ BFA_DEV_PRINTF(bfad, BFA_ERR, "LUN reset failure, status: %d\n",
+ task_status);
+ rc = FAILED;
+ }
+
+out:
+ return rc;
+}
+
+/**
+ * Scsi_Host template entry, resets the bus and abort all commands.
+ */
+int
+bfad_im_reset_bus_handler(struct scsi_cmnd *cmnd)
+{
+ struct Scsi_Host *shost = cmnd->device->host;
+ struct bfad_im_port_s *im_port =
+ (struct bfad_im_port_s *) shost->hostdata[0];
+ struct bfad_s *bfad = im_port->bfad;
+ struct bfad_itnim_s *itnim;
+ unsigned long flags;
+ u32 i, rc, err_cnt = 0;
+ DECLARE_WAIT_QUEUE_HEAD(wq);
+ enum bfi_tskim_status task_status;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ for (i = 0; i < MAX_FCP_TARGET; i++) {
+ itnim = bfad_os_get_itnim(im_port, i);
+ if (itnim) {
+ cmnd->SCp.ptr = (char *)&wq;
+ rc = bfad_im_target_reset_send(bfad, cmnd, itnim);
+ if (rc != BFA_STATUS_OK) {
+ err_cnt++;
+ continue;
+ }
+
+ /* wait target reset to complete */
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ wait_event(wq, test_bit(IO_DONE_BIT,
+ (unsigned long *)&cmnd->SCp.Status));
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+
+ task_status = cmnd->SCp.Status >> 1;
+ if (task_status != BFI_TSKIM_STS_OK) {
+ BFA_DEV_PRINTF(bfad, BFA_ERR,
+ "target reset failure,"
+ " status: %d\n", task_status);
+ err_cnt++;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ if (err_cnt)
+ return FAILED;
+
+ return SUCCESS;
+}
+
+/**
+ * Scsi_Host template entry slave_destroy.
+ */
+void
+bfad_im_slave_destroy(struct scsi_device *sdev)
+{
+ sdev->hostdata = NULL;
+ return;
+}
+
+/**
+ * BFA FCS itnim callbacks
+ */
+
+/**
+ * BFA FCS itnim alloc callback, after successful PRLI
+ * Context: Interrupt
+ */
+void
+bfa_fcb_itnim_alloc(struct bfad_s *bfad, struct bfa_fcs_itnim_s **itnim,
+ struct bfad_itnim_s **itnim_drv)
+{
+ *itnim_drv = kzalloc(sizeof(struct bfad_itnim_s), GFP_ATOMIC);
+ if (*itnim_drv == NULL)
+ return;
+
+ (*itnim_drv)->im = bfad->im;
+ *itnim = &(*itnim_drv)->fcs_itnim;
+ (*itnim_drv)->state = ITNIM_STATE_NONE;
+
+ /*
+ * Initiaze the itnim_work
+ */
+ bfad_os_itnim_alloc(*itnim_drv);
+ bfad_flags_set(bfad, BFAD_RPORT_ONLINE);
+}
+
+void
+bfad_wwn_to_str(wwn_t wwn, char *str)
+{
+ sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+ *((u8 *)&wwn),
+ *(((u8 *)&wwn) + 1),
+ *(((u8 *)&wwn) + 2),
+ *(((u8 *)&wwn) + 3),
+ *(((u8 *)&wwn) + 4),
+ *(((u8 *)&wwn) + 5),
+ *(((u8 *)&wwn) + 6),
+ *(((u8 *)&wwn) + 7));
+}
+
+void
+bfad_fcid_to_str(u32 fcid, char *str)
+{
+ sprintf(str, "%02x%02x%02x",
+ *((u8 *)&fcid),
+ *(((u8 *)&fcid) + 1),
+ *(((u8 *)&fcid) + 2));
+}
+
+/**
+ * BFA FCS itnim free callback.
+ * Context: Interrupt. bfad_lock is held
+ */
+void
+bfa_fcb_itnim_free(struct bfad_s *bfad, struct bfad_itnim_s *itnim_drv)
+{
+ struct bfad_port_s *port;
+ wwn_t wwpn;
+ u32 fcid;
+ char wwpn_str[32], fcid_str[16];
+
+ /* online to free state transtion should not happen */
+ bfa_assert(itnim_drv->state != ITNIM_STATE_ONLINE);
+
+ itnim_drv->queue_work = 1;
+ /* offline request is not yet done, use the same request to free */
+ if (itnim_drv->state == ITNIM_STATE_OFFLINE_PENDING)
+ itnim_drv->queue_work = 0;
+
+ itnim_drv->state = ITNIM_STATE_FREE;
+ port = bfa_fcs_itnim_get_drvport(&itnim_drv->fcs_itnim);
+ itnim_drv->im_port = port->im_port;
+ wwpn = bfa_fcs_itnim_get_pwwn(&itnim_drv->fcs_itnim);
+ fcid = bfa_fcs_itnim_get_fcid(&itnim_drv->fcs_itnim);
+ bfad_wwn_to_str(wwpn, wwpn_str);
+ bfad_fcid_to_str(fcid, fcid_str);
+ bfa_log(bfad->logmod, BFA_LOG_LINUX_ITNIM_FREE,
+ port->im_port->shost->host_no,
+ fcid_str, wwpn_str);
+ bfad_os_itnim_process(itnim_drv);
+}
+
+/**
+ * BFA FCS itnim online callback.
+ * Context: Interrupt. bfad_lock is held
+ */
+void
+bfa_fcb_itnim_online(struct bfad_itnim_s *itnim_drv)
+{
+ struct bfad_port_s *port;
+
+ itnim_drv->bfa_itnim = bfa_fcs_itnim_get_halitn(&itnim_drv->fcs_itnim);
+ port = bfa_fcs_itnim_get_drvport(&itnim_drv->fcs_itnim);
+ itnim_drv->state = ITNIM_STATE_ONLINE;
+ itnim_drv->queue_work = 1;
+ itnim_drv->im_port = port->im_port;
+ bfad_os_itnim_process(itnim_drv);
+}
+
+/**
+ * BFA FCS itnim offline callback.
+ * Context: Interrupt. bfad_lock is held
+ */
+void
+bfa_fcb_itnim_offline(struct bfad_itnim_s *itnim_drv)
+{
+ struct bfad_port_s *port;
+ struct bfad_s *bfad;
+
+ port = bfa_fcs_itnim_get_drvport(&itnim_drv->fcs_itnim);
+ bfad = port->bfad;
+ if ((bfad->pport.flags & BFAD_PORT_DELETE) ||
+ (port->flags & BFAD_PORT_DELETE)) {
+ itnim_drv->state = ITNIM_STATE_OFFLINE;
+ return;
+ }
+ itnim_drv->im_port = port->im_port;
+ itnim_drv->state = ITNIM_STATE_OFFLINE_PENDING;
+ itnim_drv->queue_work = 1;
+ bfad_os_itnim_process(itnim_drv);
+}
+
+/**
+ * BFA FCS itnim timeout callback.
+ * Context: Interrupt. bfad_lock is held
+ */
+void bfa_fcb_itnim_tov(struct bfad_itnim_s *itnim)
+{
+ struct bfad_im_port_s *im_port = itnim->im_port;
+
+ itnim->state = ITNIM_STATE_TIMEOUT;
+ bfad_os_itnim_tov(itnim);
+ bfad_os_scsi_state_changed(im_port->shost, itnim);
+}
+
+/**
+ * Path TOV processing begin notification -- dummy for linux
+ */
+void
+bfa_fcb_itnim_tov_begin(struct bfad_itnim_s *itnim)
+{
+}
+
+
+
+/**
+ * Allocate a Scsi_Host for a port.
+ */
+int
+bfad_im_scsi_host_alloc(struct bfad_s *bfad, struct bfad_im_port_s *im_port)
+{
+ int error = 1;
+
+ if (!idr_pre_get(&bfad_im_port_index, GFP_KERNEL)) {
+ printk(KERN_WARNING "idr_pre_get failure\n");
+ goto out;
+ }
+
+ error = idr_get_new(&bfad_im_port_index, im_port,
+ &im_port->idr_id);
+ if (error) {
+ printk(KERN_WARNING "idr_get_new failure\n");
+ goto out;
+ }
+
+ im_port->shost = bfad_os_scsi_host_alloc(im_port, bfad);
+ if (!im_port->shost) {
+ error = 1;
+ goto out_free_idr;
+ }
+
+ im_port->shost->hostdata[0] = (unsigned long)im_port;
+ im_port->shost->unique_id = im_port->idr_id;
+ im_port->shost->this_id = -1;
+ im_port->shost->max_id = MAX_FCP_TARGET;
+ im_port->shost->max_lun = MAX_FCP_LUN;
+ im_port->shost->max_cmd_len = 16;
+ im_port->shost->can_queue = bfad->cfg_data.ioc_queue_depth;
+ im_port->shost->transportt = bfad_im_scsi_transport_template;
+
+ error = bfad_os_scsi_add_host(im_port->shost, im_port, bfad);
+ if (error) {
+ printk(KERN_WARNING "bfad_os_scsi_add_host failure %d\n",
+ error);
+ goto out_fc_rel;
+ }
+
+ /* setup host fixed attribute if the lk supports */
+ bfad_os_fc_host_init(im_port);
+
+ return 0;
+
+out_fc_rel:
+ scsi_host_put(im_port->shost);
+
+out_free_idr:
+ idr_remove(&bfad_im_port_index, im_port->idr_id);
+out:
+ return error;
+}
+
+void
+bfad_im_scsi_host_free(struct bfad_s *bfad, struct bfad_im_port_s *im_port)
+{
+ unsigned long flags;
+
+ bfa_trc(bfad, bfad->inst_no);
+ bfa_log(bfad->logmod, BFA_LOG_LINUX_SCSI_HOST_FREE,
+ im_port->shost->host_no);
+
+ fc_remove_host(im_port->shost);
+
+ scsi_remove_host(im_port->shost);
+ scsi_host_put(im_port->shost);
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ idr_remove(&bfad_im_port_index, im_port->idr_id);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+}
+
+bfa_status_t
+bfad_im_port_new(struct bfad_s *bfad, struct bfad_port_s *port)
+{
+ int rc = BFA_STATUS_OK;
+ struct bfad_im_port_s *im_port;
+
+ im_port = kzalloc(sizeof(struct bfad_im_port_s), GFP_ATOMIC);
+ if (im_port == NULL) {
+ rc = BFA_STATUS_ENOMEM;
+ goto ext;
+ }
+ port->im_port = im_port;
+ im_port->port = port;
+ im_port->bfad = bfad;
+
+ bfad_os_init_work(im_port);
+ INIT_LIST_HEAD(&im_port->itnim_mapped_list);
+ INIT_LIST_HEAD(&im_port->binding_list);
+
+ext:
+ return rc;
+}
+
+void
+bfad_im_port_delete(struct bfad_s *bfad, struct bfad_port_s *port)
+{
+ struct bfad_im_port_s *im_port = port->im_port;
+
+ queue_work(bfad->im->drv_workq,
+ &im_port->port_delete_work);
+}
+
+BFAD_WORK_HANDLER(bfad_im_port_delete_handler)
+{ /* work struct is the function arg */
+ struct bfad_im_port_s *im_port =
+ container_of(work, struct bfad_im_port_s, port_delete_work);
+
+ bfad_im_scsi_host_free(im_port->bfad, im_port);
+ bfad_im_port_clean(im_port);
+ kfree(im_port);
+}
+
+void
+bfad_im_port_clean(struct bfad_im_port_s *im_port)
+{
+ struct bfad_fcp_binding *bp, *bp_new;
+ unsigned long flags;
+ struct bfad_s *bfad = im_port->bfad;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ list_for_each_entry_safe(bp, bp_new, &im_port->binding_list,
+ list_entry) {
+ list_del(&bp->list_entry);
+ kfree(bp);
+ }
+
+ /* the itnim_mapped_list must be empty at this time */
+ bfa_assert(list_empty(&im_port->itnim_mapped_list));
+
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+}
+
+void
+bfad_im_port_online(struct bfad_s *bfad, struct bfad_port_s *port)
+{
+}
+
+void
+bfad_im_port_offline(struct bfad_s *bfad, struct bfad_port_s *port)
+{
+}
+
+bfa_status_t
+bfad_im_probe(struct bfad_s *bfad)
+{
+ struct bfad_im_s *im;
+ bfa_status_t rc = BFA_STATUS_OK;
+
+ im = kzalloc(sizeof(struct bfad_im_s), GFP_KERNEL);
+ if (im == NULL) {
+ rc = BFA_STATUS_ENOMEM;
+ goto ext;
+ }
+
+ bfad->im = im;
+ im->bfad = bfad;
+
+ if (bfad_os_thread_workq(bfad) != BFA_STATUS_OK) {
+ kfree(im);
+ rc = BFA_STATUS_FAILED;
+ }
+
+ext:
+ return rc;
+}
+
+void
+bfad_im_probe_undo(struct bfad_s *bfad)
+{
+ if (bfad->im) {
+ bfad_os_destroy_workq(bfad->im);
+ kfree(bfad->im);
+ bfad->im = NULL;
+ }
+}
+
+BFAD_WORK_HANDLER(bfad_im_itnim_work_handler);
+
+int
+bfad_os_scsi_add_host(struct Scsi_Host *shost, struct bfad_im_port_s *im_port,
+ struct bfad_s *bfad)
+{
+ struct device *dev;
+
+ if (im_port->port->pvb_type == BFAD_PORT_PHYS_BASE)
+ dev = &bfad->pcidev->dev;
+ else
+ dev = &bfad->pport.im_port->shost->shost_gendev;
+
+ return scsi_add_host(shost, dev);
+}
+
+struct Scsi_Host *
+bfad_os_scsi_host_alloc(struct bfad_im_port_s *im_port, struct bfad_s *bfad)
+{
+ struct scsi_host_template *sht;
+
+ if (im_port->port->pvb_type == BFAD_PORT_PHYS_BASE)
+ sht = &bfad_im_scsi_host_template;
+ else
+ sht = &bfad_im_vport_template;
+
+ sht->sg_tablesize = bfad->cfg_data.io_max_sge;
+
+ return scsi_host_alloc(sht, sizeof(unsigned long));
+}
+
+void
+bfad_os_scsi_host_free(struct bfad_s *bfad, struct bfad_im_port_s *im_port)
+{
+ flush_workqueue(bfad->im->drv_workq);
+ bfad_im_scsi_host_free(im_port->bfad, im_port);
+ bfad_im_port_clean(im_port);
+ kfree(im_port);
+}
+
+void
+bfad_os_destroy_workq(struct bfad_im_s *im)
+{
+ if (im && im->drv_workq) {
+ destroy_workqueue(im->drv_workq);
+ im->drv_workq = NULL;
+ }
+}
+
+bfa_status_t
+bfad_os_thread_workq(struct bfad_s *bfad)
+{
+ struct bfad_im_s *im = bfad->im;
+
+ bfa_trc(bfad, 0);
+ snprintf(im->drv_workq_name, KOBJ_NAME_LEN, "bfad_wq_%d",
+ bfad->inst_no);
+ im->drv_workq = create_singlethread_workqueue(im->drv_workq_name);
+ if (!im->drv_workq)
+ return BFA_STATUS_FAILED;
+
+ return BFA_STATUS_OK;
+}
+
+struct bfad_itnim_data_s *
+bfad_os_itnim_data(struct bfad_im_port_s *im_port, struct scsi_cmnd *cmnd)
+{
+ if (!cmnd || !cmnd->device)
+ return NULL;
+ return cmnd->device->hostdata;
+}
+
+/**
+ * Scsi_Host template entry.
+ *
+ * Description:
+ * OS entry point to adjust the queue_depths on a per-device basis.
+ * Called once per device during the bus scan.
+ * Return non-zero if fails.
+ */
+static int
+bfad_im_slave_configure(struct scsi_device *sdev)
+{
+ if (sdev->tagged_supported)
+ scsi_activate_tcq(sdev, bfa_lun_queue_depth);
+ else
+ scsi_deactivate_tcq(sdev, bfa_lun_queue_depth);
+
+ return 0;
+}
+
+struct scsi_host_template bfad_im_scsi_host_template = {
+ .module = THIS_MODULE,
+ .name = BFAD_DRIVER_NAME,
+ .info = bfad_im_info,
+ .queuecommand = bfad_im_queuecommand,
+ .eh_abort_handler = bfad_im_abort_handler,
+ .eh_device_reset_handler = bfad_im_reset_lun_handler,
+ .eh_bus_reset_handler = bfad_im_reset_bus_handler,
+
+ .slave_alloc = bfad_im_slave_alloc,
+ .slave_configure = bfad_im_slave_configure,
+ .slave_destroy = bfad_im_slave_destroy,
+
+ .this_id = -1,
+ .sg_tablesize = BFAD_IO_MAX_SGE,
+ .cmd_per_lun = 3,
+ .use_clustering = ENABLE_CLUSTERING,
+ .shost_attrs = bfad_im_host_attrs,
+ .max_sectors = 0xFFFF,
+};
+
+struct scsi_host_template bfad_im_vport_template = {
+ .module = THIS_MODULE,
+ .name = BFAD_DRIVER_NAME,
+ .info = bfad_im_info,
+ .queuecommand = bfad_im_queuecommand,
+ .eh_abort_handler = bfad_im_abort_handler,
+ .eh_device_reset_handler = bfad_im_reset_lun_handler,
+ .eh_bus_reset_handler = bfad_im_reset_bus_handler,
+
+ .slave_alloc = bfad_im_slave_alloc,
+ .slave_configure = bfad_im_slave_configure,
+ .slave_destroy = bfad_im_slave_destroy,
+
+ .this_id = -1,
+ .sg_tablesize = BFAD_IO_MAX_SGE,
+ .cmd_per_lun = 3,
+ .use_clustering = ENABLE_CLUSTERING,
+ .shost_attrs = bfad_im_vport_attrs,
+ .max_sectors = 0xFFFF,
+};
+
+void
+bfad_im_probe_post(struct bfad_im_s *im)
+{
+ flush_workqueue(im->drv_workq);
+}
+
+bfa_status_t
+bfad_im_module_init(void)
+{
+ bfad_im_scsi_transport_template =
+ fc_attach_transport(&bfad_im_fc_function_template);
+ if (!bfad_im_scsi_transport_template)
+ return BFA_STATUS_ENOMEM;
+
+ return BFA_STATUS_OK;
+}
+
+void
+bfad_im_module_exit(void)
+{
+ if (bfad_im_scsi_transport_template)
+ fc_release_transport(bfad_im_scsi_transport_template);
+}
+
+int
+bfad_os_im_dma_map(struct bfad_s *bfad, struct scsi_cmnd *cmnd)
+{
+ int sg_cnt, rc = 0;
+
+ if (scsi_sg_count(cmnd)) {
+ sg_cnt = dma_map_sg((struct device *)&bfad->pcidev->dev,
+ (struct scatterlist *)scsi_sglist(cmnd),
+ scsi_sg_count(cmnd),
+ cmnd->sc_data_direction);
+ if (sg_cnt == 0 || sg_cnt > bfad->cfg_data.io_max_sge) {
+ printk(KERN_WARNING
+ "bfad%d: dma_map_sg failure, sg_cnt=%d\n",
+ bfad->inst_no, sg_cnt);
+ rc = 1;
+ }
+
+ } else if (scsi_bufflen(cmnd)) {
+ cmnd->SCp.dma_handle =
+ dma_map_single((struct device *)&bfad->pcidev->
+ dev, scsi_sglist(cmnd),
+ scsi_bufflen(cmnd),
+ cmnd->sc_data_direction);
+ }
+
+ return rc;
+}
+
+void
+bfad_os_im_dma_unmap(struct bfad_s *bfad, struct scsi_cmnd *cmnd)
+{
+ if (scsi_sg_count(cmnd))
+ dma_unmap_sg((struct device *)&bfad->pcidev->dev,
+ scsi_sglist(cmnd), scsi_sg_count(cmnd),
+ cmnd->sc_data_direction);
+ else if (scsi_bufflen(cmnd))
+ dma_unmap_single((struct device *)&bfad->pcidev->dev,
+ cmnd->SCp.dma_handle,
+ scsi_bufflen(cmnd),
+ cmnd->sc_data_direction);
+}
+
+void
+bfad_os_itnim_alloc(struct bfad_itnim_s *itnim_drv)
+{
+ BFAD_INIT_WORK(itnim_drv, itnim_work, bfad_im_itnim_work_handler);
+}
+
+void
+bfad_os_itnim_process(struct bfad_itnim_s *itnim_drv)
+{
+ struct bfad_im_s *im = itnim_drv->im;
+
+ if (itnim_drv->queue_work)
+ queue_work(im->drv_workq, &itnim_drv->itnim_work);
+}
+
+void
+bfad_os_ramp_up_qdepth(struct bfad_itnim_s *itnim, struct scsi_device *sdev)
+{
+ struct scsi_device *tmp_sdev;
+
+ if (((jiffies - itnim->last_ramp_up_time) >
+ BFA_QUEUE_FULL_RAMP_UP_TIME * HZ) &&
+ ((jiffies - itnim->last_queue_full_time) >
+ BFA_QUEUE_FULL_RAMP_UP_TIME * HZ)) {
+ shost_for_each_device(tmp_sdev, sdev->host) {
+ if (bfa_lun_queue_depth > tmp_sdev->queue_depth) {
+ if (tmp_sdev->id != sdev->id)
+ continue;
+ if (tmp_sdev->ordered_tags)
+ scsi_adjust_queue_depth(tmp_sdev,
+ MSG_ORDERED_TAG,
+ tmp_sdev->queue_depth + 1);
+ else
+ scsi_adjust_queue_depth(tmp_sdev,
+ MSG_SIMPLE_TAG,
+ tmp_sdev->queue_depth + 1);
+
+ itnim->last_ramp_up_time = jiffies;
+ }
+ }
+ }
+}
+
+void
+bfad_os_handle_qfull(struct bfad_itnim_s *itnim, struct scsi_device *sdev)
+{
+ struct scsi_device *tmp_sdev;
+
+ itnim->last_queue_full_time = jiffies;
+
+ shost_for_each_device(tmp_sdev, sdev->host) {
+ if (tmp_sdev->id != sdev->id)
+ continue;
+ scsi_track_queue_full(tmp_sdev, tmp_sdev->queue_depth - 1);
+ }
+}
+
+void
+bfad_os_scsi_state_changed(struct Scsi_Host *shost, struct bfad_itnim_s *itnim)
+{
+ return;
+}
+
+
+
+
+BFAD_WORK_HANDLER(bfad_im_port_delete_handler);
+BFAD_WORK_HANDLER(bfad_im_itnim_work_handler);
+
+static void bfad_im_fc_rport_add(struct bfad_im_port_s *im_port,
+ struct bfad_itnim_s *itnim);
+
+struct bfad_itnim_s *
+bfad_os_get_itnim(struct bfad_im_port_s *im_port, int id)
+{
+ struct bfad_itnim_s *itnim = NULL;
+
+ /* Search the mapped list for this target ID */
+ list_for_each_entry(itnim, &im_port->itnim_mapped_list, list_entry) {
+ if (id == itnim->scsi_tgt_id)
+ return itnim;
+ }
+
+ return NULL;
+}
+
+void
+bfad_os_init_work(struct bfad_im_port_s *im_port)
+{
+ BFAD_INIT_WORK(im_port, port_delete_work, bfad_im_port_delete_handler);
+
+ return;
+}
+
+void
+bfad_os_itnim_tov(struct bfad_itnim_s *itnim)
+{
+}
+
+/**
+ * Scsi_Host template entry slave_alloc
+ */
+int
+bfad_im_slave_alloc(struct scsi_device *sdev)
+{
+ struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
+
+ if (!rport || fc_remote_port_chkready(rport))
+ return -ENXIO;
+
+ sdev->hostdata = rport->dd_data;
+
+ return 0;
+}
+
+void
+bfad_os_fc_host_init(struct bfad_im_port_s *im_port)
+{
+ struct Scsi_Host *host = im_port->shost;
+ struct bfad_s *bfad = im_port->bfad;
+ struct bfad_port_s *port = im_port->port;
+ union attr {
+ struct bfa_pport_attr_s pattr;
+ struct bfa_ioc_attr_s ioc_attr;
+ } attr;
+
+ fc_host_node_name(host) =
+ bfa_os_htonll((bfa_fcs_port_get_nwwn(port->fcs_port)));
+ fc_host_port_name(host) =
+ bfa_os_htonll((bfa_fcs_port_get_pwwn(port->fcs_port)));
+
+ fc_host_supported_classes(host) = FC_COS_CLASS3;
+
+ memset(fc_host_supported_fc4s(host), 0,
+ sizeof(fc_host_supported_fc4s(host)));
+ if (supported_fc4s & (BFA_PORT_ROLE_FCP_IM | BFA_PORT_ROLE_FCP_TM))
+ /* For FCP type 0x08 */
+ fc_host_supported_fc4s(host)[2] = 1;
+ if (supported_fc4s | BFA_PORT_ROLE_FCP_IPFC)
+ /* For LLC/SNAP type 0x05 */
+ fc_host_supported_fc4s(host)[3] = 0x20;
+ /* For fibre channel services type 0x20 */
+ fc_host_supported_fc4s(host)[7] = 1;
+
+ memset(&attr.ioc_attr, 0, sizeof(attr.ioc_attr));
+ bfa_get_attr(&bfad->bfa, &attr.ioc_attr);
+ sprintf(fc_host_symbolic_name(host), "Brocade %s FV%s DV%s",
+ attr.ioc_attr.adapter_attr.model,
+ attr.ioc_attr.adapter_attr.fw_ver, BFAD_DRIVER_VERSION);
+
+ fc_host_supported_speeds(host) = 0;
+ fc_host_supported_speeds(host) |=
+ FC_PORTSPEED_8GBIT | FC_PORTSPEED_4GBIT | FC_PORTSPEED_2GBIT |
+ FC_PORTSPEED_1GBIT;
+
+ memset(&attr.pattr, 0, sizeof(attr.pattr));
+ bfa_pport_get_attr(&bfad->bfa, &attr.pattr);
+ fc_host_maxframe_size(host) = attr.pattr.pport_cfg.maxfrsize;
+}
+
+static void
+bfad_im_fc_rport_add(struct bfad_im_port_s *im_port, struct bfad_itnim_s *itnim)
+{
+ struct fc_rport_identifiers rport_ids;
+ struct fc_rport *fc_rport;
+ struct bfad_itnim_data_s *itnim_data;
+
+ rport_ids.node_name =
+ bfa_os_htonll(bfa_fcs_itnim_get_nwwn(&itnim->fcs_itnim));
+ rport_ids.port_name =
+ bfa_os_htonll(bfa_fcs_itnim_get_pwwn(&itnim->fcs_itnim));
+ rport_ids.port_id =
+ bfa_os_hton3b(bfa_fcs_itnim_get_fcid(&itnim->fcs_itnim));
+ rport_ids.roles = FC_RPORT_ROLE_UNKNOWN;
+
+ itnim->fc_rport = fc_rport =
+ fc_remote_port_add(im_port->shost, 0, &rport_ids);
+
+ if (!fc_rport)
+ return;
+
+ fc_rport->maxframe_size =
+ bfa_fcs_itnim_get_maxfrsize(&itnim->fcs_itnim);
+ fc_rport->supported_classes = bfa_fcs_itnim_get_cos(&itnim->fcs_itnim);
+
+ itnim_data = fc_rport->dd_data;
+ itnim_data->itnim = itnim;
+
+ rport_ids.roles |= FC_RPORT_ROLE_FCP_TARGET;
+
+ if (rport_ids.roles != FC_RPORT_ROLE_UNKNOWN)
+ fc_remote_port_rolechg(fc_rport, rport_ids.roles);
+
+ if ((fc_rport->scsi_target_id != -1)
+ && (fc_rport->scsi_target_id < MAX_FCP_TARGET))
+ itnim->scsi_tgt_id = fc_rport->scsi_target_id;
+
+ return;
+}
+
+/**
+ * Work queue handler using FC transport service
+* Context: kernel
+ */
+BFAD_WORK_HANDLER(bfad_im_itnim_work_handler)
+{ /* work struct is the function arg */
+ struct bfad_itnim_s *itnim = container_of(work, struct bfad_itnim_s,
+ itnim_work);
+ struct bfad_im_s *im = itnim->im;
+ struct bfad_s *bfad = im->bfad;
+ struct bfad_im_port_s *im_port;
+ unsigned long flags;
+ struct fc_rport *fc_rport;
+ wwn_t wwpn;
+ u32 fcid;
+ char wwpn_str[32], fcid_str[16];
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ im_port = itnim->im_port;
+ bfa_trc(bfad, itnim->state);
+ switch (itnim->state) {
+ case ITNIM_STATE_ONLINE:
+ if (!itnim->fc_rport) {
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ bfad_im_fc_rport_add(im_port, itnim);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ wwpn = bfa_fcs_itnim_get_pwwn(&itnim->fcs_itnim);
+ fcid = bfa_fcs_itnim_get_fcid(&itnim->fcs_itnim);
+ bfad_wwn_to_str(wwpn, wwpn_str);
+ bfad_fcid_to_str(fcid, fcid_str);
+ list_add_tail(&itnim->list_entry,
+ &im_port->itnim_mapped_list);
+ bfa_log(bfad->logmod, BFA_LOG_LINUX_ITNIM_ONLINE,
+ im_port->shost->host_no,
+ itnim->scsi_tgt_id,
+ fcid_str, wwpn_str);
+ } else {
+ printk(KERN_WARNING
+ "%s: itnim %llx is already in online state\n",
+ __FUNCTION__,
+ bfa_fcs_itnim_get_pwwn(&itnim->fcs_itnim));
+ }
+
+ break;
+ case ITNIM_STATE_OFFLINE_PENDING:
+ itnim->state = ITNIM_STATE_OFFLINE;
+ if (itnim->fc_rport) {
+ fc_rport = itnim->fc_rport;
+ ((struct bfad_itnim_data_s *)
+ fc_rport->dd_data)->itnim = NULL;
+ itnim->fc_rport = NULL;
+ if (!(im_port->port->flags & BFAD_PORT_DELETE)) {
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ fc_rport->dev_loss_tmo =
+ bfa_fcpim_path_tov_get(&bfad->bfa) + 1;
+ fc_remote_port_delete(fc_rport);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ }
+ wwpn = bfa_fcs_itnim_get_pwwn(&itnim->fcs_itnim);
+ fcid = bfa_fcs_itnim_get_fcid(&itnim->fcs_itnim);
+ bfad_wwn_to_str(wwpn, wwpn_str);
+ bfad_fcid_to_str(fcid, fcid_str);
+ list_del(&itnim->list_entry);
+ bfa_log(bfad->logmod, BFA_LOG_LINUX_ITNIM_OFFLINE,
+ im_port->shost->host_no,
+ itnim->scsi_tgt_id,
+ fcid_str, wwpn_str);
+ }
+ break;
+ case ITNIM_STATE_FREE:
+ if (itnim->fc_rport) {
+ fc_rport = itnim->fc_rport;
+ ((struct bfad_itnim_data_s *)
+ fc_rport->dd_data)->itnim = NULL;
+ itnim->fc_rport = NULL;
+ if (!(im_port->port->flags & BFAD_PORT_DELETE)) {
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ fc_rport->dev_loss_tmo =
+ bfa_fcpim_path_tov_get(&bfad->bfa) + 1;
+ fc_remote_port_delete(fc_rport);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ }
+ list_del(&itnim->list_entry);
+ }
+
+ kfree(itnim);
+ break;
+ default:
+ bfa_assert(0);
+ break;
+ }
+
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+}
+
+/**
+ * Scsi_Host template entry, queue a SCSI command to the BFAD.
+ */
+int
+bfad_im_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
+{
+ struct bfad_im_port_s *im_port =
+ (struct bfad_im_port_s *) cmnd->device->host->hostdata[0];
+ struct bfad_s *bfad = im_port->bfad;
+ struct bfad_itnim_data_s *itnim_data = cmnd->device->hostdata;
+ struct bfad_itnim_s *itnim;
+ struct bfa_ioim_s *hal_io;
+ unsigned long flags;
+ int rc;
+ u16 sg_cnt = 0;
+ struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device));
+
+ rc = fc_remote_port_chkready(rport);
+ if (rc) {
+ cmnd->result = rc;
+ done(cmnd);
+ return 0;
+ }
+
+ rc = bfad_os_im_dma_map(bfad, cmnd);
+ if (rc)
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+ cmnd->scsi_done = done;
+ if (scsi_sg_count(cmnd))
+ sg_cnt = scsi_sg_count(cmnd);
+ else if (scsi_bufflen(cmnd))
+ sg_cnt = 1;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ if (!(bfad->bfad_flags & BFAD_HAL_START_DONE)) {
+ printk(KERN_WARNING
+ "bfad%d, queuecommand %p %x failed, BFA stopped\n",
+ bfad->inst_no, cmnd, cmnd->cmnd[0]);
+ cmnd->result = ScsiResult(DID_NO_CONNECT, 0);
+ goto out_fail_cmd;
+ }
+
+
+ itnim = itnim_data->itnim;
+ if (!itnim) {
+ cmnd->result = ScsiResult(DID_IMM_RETRY, 0);
+ goto out_fail_cmd;
+ }
+
+ hal_io = bfa_ioim_alloc(&bfad->bfa, (struct bfad_ioim_s *) cmnd,
+ itnim->bfa_itnim, sg_cnt);
+ if (!hal_io) {
+ printk(KERN_WARNING "hal_io failure\n");
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ bfad_os_im_dma_unmap(bfad, cmnd);
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+
+ cmnd->host_scribble = (char *)hal_io;
+ bfa_trc_fp(bfad, hal_io->iotag);
+ bfa_ioim_start(hal_io);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ return 0;
+
+out_fail_cmd:
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ bfad_os_im_dma_unmap(bfad, cmnd);
+ if (done)
+ done(cmnd);
+
+ return 0;
+}
+
+void
+bfad_os_rport_online_wait(struct bfad_s *bfad)
+{
+ int i;
+ int rport_delay = 10;
+
+ for (i = 0; !(bfad->bfad_flags & BFAD_PORT_ONLINE)
+ && i < linkup_delay; i++) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ);
+ }
+
+ if (bfad->bfad_flags & BFAD_PORT_ONLINE) {
+ rport_delay = rport_delay < linkup_delay ?
+ rport_delay : linkup_delay;
+ for (i = 0; !(bfad->bfad_flags & BFAD_RPORT_ONLINE)
+ && i < rport_delay; i++) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ);
+ }
+
+ if (rport_delay > 0 && (bfad->bfad_flags & BFAD_RPORT_ONLINE)) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(rport_delay * HZ);
+ }
+ }
+}
+
+int
+bfad_os_get_linkup_delay(struct bfad_s *bfad)
+{
+
+ u8 nwwns = 0;
+ wwn_t *wwns;
+ int ldelay;
+
+ /*
+ * Querying for the boot target port wwns
+ * -- read from boot information in flash.
+ * If nwwns > 0 => boot over SAN and set linkup_delay = 30
+ * else => local boot machine set linkup_delay = 10
+ */
+
+ bfa_iocfc_get_bootwwns(&bfad->bfa, &nwwns, &wwns);
+
+ if (nwwns > 0) {
+ /* If boot over SAN; linkup_delay = 30sec */
+ ldelay = 30;
+ } else {
+ /* If local boot; linkup_delay = 10sec */
+ ldelay = 0;
+ }
+
+ return ldelay;
+}
+
+
diff -urpN orig/drivers/scsi/bfa/bfad_im.h patch/drivers/scsi/bfa/bfad_im.h
--- orig/drivers/scsi/bfa/bfad_im.h 1969-12-31 16:00:00.000000000 -0800
+++ patch/drivers/scsi/bfa/bfad_im.h 2009-07-21 15:34:02.000000000 -0700
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2005-2008 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * http://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.
+ */
+
+#ifndef __BFAD_IM_H__
+#define __BFAD_IM_H__
+
+#include "fcs/bfa_fcs_fcpim.h"
+#include "bfad_im_compat.h"
+
+#define FCPI_NAME " fcpim"
+
+#ifndef KOBJ_NAME_LEN
+#define KOBJ_NAME_LEN 20
+#endif
+
+void bfad_flags_set(struct bfad_s *bfad, u32 flags);
+bfa_status_t bfad_im_module_init(void);
+void bfad_im_module_exit(void);
+bfa_status_t bfad_im_probe(struct bfad_s *bfad);
+void bfad_im_probe_undo(struct bfad_s *bfad);
+void bfad_im_probe_post(struct bfad_im_s *im);
+bfa_status_t bfad_im_port_new(struct bfad_s *bfad, struct bfad_port_s *port);
+void bfad_im_port_delete(struct bfad_s *bfad, struct bfad_port_s *port);
+void bfad_im_port_online(struct bfad_s *bfad, struct bfad_port_s *port);
+void bfad_im_port_offline(struct bfad_s *bfad, struct bfad_port_s *port);
+void bfad_im_port_clean(struct bfad_im_port_s *im_port);
+int bfad_im_scsi_host_alloc(struct bfad_s *bfad,
+ struct bfad_im_port_s *im_port);
+void bfad_im_scsi_host_free(struct bfad_s *bfad,
+ struct bfad_im_port_s *im_port);
+
+#define MAX_FCP_TARGET 1024
+#define MAX_FCP_LUN 16384
+#define BFAD_TARGET_RESET_TMO 60
+#define BFAD_LUN_RESET_TMO 60
+#define ScsiResult(host_code, scsi_code) (((host_code) << 16) | scsi_code)
+#define BFA_QUEUE_FULL_RAMP_UP_TIME 120
+
+/*
+ * itnim flags
+ */
+#define ITNIM_MAPPED 0x00000001
+
+#define SCSI_TASK_MGMT 0x00000001
+#define IO_DONE_BIT 0
+
+struct bfad_itnim_data_s {
+ struct bfad_itnim_s *itnim;
+};
+
+struct bfad_im_port_s {
+ struct bfad_s *bfad;
+ struct bfad_port_s *port;
+ struct work_struct port_delete_work;
+ int idr_id;
+ u16 cur_scsi_id;
+ struct list_head binding_list;
+ struct Scsi_Host *shost;
+ struct list_head itnim_mapped_list;
+};
+
+enum bfad_itnim_state {
+ ITNIM_STATE_NONE,
+ ITNIM_STATE_ONLINE,
+ ITNIM_STATE_OFFLINE_PENDING,
+ ITNIM_STATE_OFFLINE,
+ ITNIM_STATE_TIMEOUT,
+ ITNIM_STATE_FREE,
+};
+
+/*
+ * Per itnim data structure
+ */
+struct bfad_itnim_s {
+ struct list_head list_entry;
+ struct bfa_fcs_itnim_s fcs_itnim;
+ struct work_struct itnim_work;
+ u32 flags;
+ enum bfad_itnim_state state;
+ struct bfad_im_s *im;
+ struct bfad_im_port_s *im_port;
+ struct bfad_rport_s *drv_rport;
+ struct fc_rport *fc_rport;
+ struct bfa_itnim_s *bfa_itnim;
+ u16 scsi_tgt_id;
+ u16 queue_work;
+ unsigned long last_ramp_up_time;
+ unsigned long last_queue_full_time;
+};
+
+enum bfad_binding_type {
+ FCP_PWWN_BINDING = 0x1,
+ FCP_NWWN_BINDING = 0x2,
+ FCP_FCID_BINDING = 0x3,
+};
+
+struct bfad_fcp_binding {
+ struct list_head list_entry;
+ enum bfad_binding_type binding_type;
+ u16 scsi_target_id;
+ u32 fc_id;
+ wwn_t nwwn;
+ wwn_t pwwn;
+};
+
+struct bfad_im_s {
+ struct bfad_s *bfad;
+ struct workqueue_struct *drv_workq;
+ char drv_workq_name[KOBJ_NAME_LEN];
+};
+
+void bfad_os_spin_os_lock_irq(struct Scsi_Host *);
+void bfad_os_spin_os_unlock_irq(struct Scsi_Host *);
+void bfad_os_fc_release_transp(struct Scsi_Host *shost);
+struct Scsi_Host *bfad_os_scsi_host_alloc(struct bfad_im_port_s *im_port,
+ struct bfad_s *);
+int bfad_os_scsi_register(struct Scsi_Host *shost,
+ struct bfad_im_port_s *im_port, void *key);
+int bfad_os_fc_attach(struct Scsi_Host *shost,
+ struct fc_function_template *fct);
+int bfad_os_reset_tgt(struct scsi_cmnd *cmnd);
+void bfad_os_scsi_set_pci_device(struct Scsi_Host *shost,
+ struct pci_dev *pcidev);
+void bfad_os_select_queue_depths(struct bfad_im_port_s *im_port);
+bfa_status_t bfad_os_thread_workq(struct bfad_s *bfad);
+void bfad_os_destroy_workq(struct bfad_im_s *im);
+void bfad_os_queue_work(void *workq, void *work);
+void bfad_os_itnim_alloc(struct bfad_itnim_s *itnim_drv);
+void bfad_os_itnim_process(struct bfad_itnim_s *itnim_drv);
+void bfad_os_itnim_tov(struct bfad_itnim_s *itnim_drv);
+void bfad_os_scsi_unregister(struct Scsi_Host *shost);
+void bfad_os_scsi_remove_host(struct Scsi_Host *shost);
+void bfad_os_scsi_put_host(struct Scsi_Host *shost);
+int bfad_os_scsi_add_host(struct Scsi_Host *shost,
+ struct bfad_im_port_s *im_port, struct bfad_s *bfad);
+int bfad_os_fc_attach(struct Scsi_Host *shost,
+ struct fc_function_template *fct);
+
+struct bfad_itnim_data_s *bfad_os_itnim_data(struct bfad_im_port_s *im_port,
+ struct scsi_cmnd *cmnd);
+void bfad_os_fc_host_init(struct bfad_im_port_s *im_port);
+void bfad_os_fc_remove_host(struct Scsi_Host *shost);
+void bfad_os_init_work(struct bfad_im_port_s *im_port);
+int bfad_os_dev_init(struct Scsi_Host *shost);
+void bfad_os_transp_templ(struct Scsi_Host *sh,
+ struct scsi_transport_template *stt);
+dma_addr_t bfad_os_dma_map_single(struct device *dev, void *cpu_addr,
+ size_t size, enum dma_data_direction direction);
+void bfad_os_dma_unmap_single(struct device *dev, dma_addr_t dma_addr,
+ size_t size, enum dma_data_direction direction);
+int bfad_os_dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
+ enum dma_data_direction direction);
+void bfad_os_dma_unmap_sg(struct device *dev, struct scatterlist *sg,
+ int nhwentries, enum dma_data_direction direction);
+int bfad_os_im_dma_map(struct bfad_s *bfad, struct scsi_cmnd *cmnd);
+void bfad_os_im_dma_unmap(struct bfad_s *bfad, struct scsi_cmnd *cmnd);
+int bfad_os_idr_get_new(struct idr *idp, void *ptr, int *id);
+void bfad_os_scsi_state_changed(struct Scsi_Host *shost,
+ struct bfad_itnim_s *itnim);
+void bfad_os_scsi_scan(struct bfad_im_port_s *im_port);
+void bfad_os_scsi_host_free(struct bfad_s *bfad,
+ struct bfad_im_port_s *im_port);
+void bfad_wwn_to_str(wwn_t wwn, char *str);
+void bfad_fcid_to_str(u32 fcid, char *str);
+void bfad_os_ramp_up_qdepth(struct bfad_itnim_s *itnim,
+ struct scsi_device *sdev);
+void bfad_os_handle_qfull(struct bfad_itnim_s *itnim, struct scsi_device *sdev);
+void bfad_os_set_dev_loss_tmo(struct scsi_device *sdev);
+struct bfad_itnim_s *bfad_os_get_itnim(struct bfad_im_port_s *im_port, int id);
+int bfad_os_im_itnim_is_online(struct bfad_itnim_s *itnim);
+
+/*
+ * scsi_host_template entries
+ */
+int bfad_im_queuecommand(struct scsi_cmnd *cmnd,
+ void (*done)(struct scsi_cmnd *));
+const char *bfad_im_info(struct Scsi_Host *host);
+int bfad_im_slave_alloc(struct scsi_device *sdev);
+void bfad_im_slave_destroy(struct scsi_device *sdev);
+int bfad_im_abort_handler(struct scsi_cmnd *cmnd);
+int bfad_im_reset_lun_handler(struct scsi_cmnd *cmnd);
+int bfad_im_reset_bus_handler(struct scsi_cmnd *cmnd);
+void bfad_im_itnim_unmap(struct bfad_im_port_s *im_port,
+ struct bfad_itnim_s *itnim);
+
+extern struct scsi_host_template bfad_im_scsi_host_template;
+extern struct scsi_host_template bfad_im_vport_template;
+extern struct fc_function_template bfad_im_fc_function_template;
+extern struct scsi_transport_template *bfad_im_scsi_transport_template;
+
+#endif
diff -urpN orig/drivers/scsi/bfa/bfad_im_compat.h patch/drivers/scsi/bfa/bfad_im_compat.h
--- orig/drivers/scsi/bfa/bfad_im_compat.h 1969-12-31 16:00:00.000000000 -0800
+++ patch/drivers/scsi/bfa/bfad_im_compat.h 2009-07-21 15:34:02.000000000 -0700
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2005-2008 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * http://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.
+ */
+
+#ifndef __BFAD_IM_COMPAT_H__
+#define __BFAD_IM_COMPAT_H__
+
+extern u32 *bfi_image_buf;
+extern u32 bfi_image_size;
+
+extern struct device_attribute *bfad_im_host_attrs[];
+extern struct device_attribute *bfad_im_vport_attrs[];
+
+u32 *bfad_get_firmware_buf(struct pci_dev *pdev);
+u32 *bfad_read_firmware(struct pci_dev *pdev, u32 **bfi_image,
+ u32 *bfi_image_size, char *fw_name);
+
+static inline u32 *
+bfad_load_fwimg(struct pci_dev *pdev)
+{
+ return(bfad_get_firmware_buf(pdev));
+}
+
+static inline void
+bfad_free_fwimg(void)
+{
+ if (bfi_image_ct_size && bfi_image_ct)
+ vfree(bfi_image_ct);
+ if (bfi_image_cb_size && bfi_image_cb)
+ vfree(bfi_image_cb);
+}
+
+#endif
diff -urpN orig/drivers/scsi/bfa/bfad_intr.c patch/drivers/scsi/bfa/bfad_intr.c
--- orig/drivers/scsi/bfa/bfad_intr.c 1969-12-31 16:00:00.000000000 -0800
+++ patch/drivers/scsi/bfa/bfad_intr.c 2009-07-21 15:34:02.000000000 -0700
@@ -0,0 +1,241 @@
+/*
+ * Copyright (c) 2005-2008 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * http://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.
+ */
+
+#include "bfad_drv.h"
+#include "bfad_trcmod.h"
+
+BFA_TRC_FILE(LDRV, INTR);
+
+/**
+ * bfa_isr BFA driver interrupt functions
+ */
+irqreturn_t bfad_intx(int irq, void *dev_id);
+int msix_disable;
+/**
+ * Line based interrupt handler.
+ */
+irqreturn_t
+bfad_intx(int irq, void *dev_id)
+{
+ struct bfad_s *bfad = dev_id;
+ struct list_head doneq;
+ unsigned long flags;
+ bfa_boolean_t rc;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ rc = bfa_intx(&bfad->bfa);
+ if (!rc) {
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ return IRQ_NONE;
+ }
+
+ 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);
+ bfa_trc_fp(bfad, irq);
+ }
+
+ return IRQ_HANDLED;
+
+}
+
+#ifdef CONFIG_PCI_MSI
+
+static irqreturn_t
+bfad_msix(int irq, void *dev_id)
+{
+ struct bfad_msix_s *vec = dev_id;
+ struct bfad_s *bfad = vec->bfad;
+ struct list_head doneq;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+
+ bfa_msix(&bfad->bfa, vec->msix.entry);
+ 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);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * Initialize the MSIX entry table.
+ */
+static void
+bfad_init_msix_entry(struct bfad_s *bfad, struct msix_entry *msix_entries,
+ int mask, int max_bit)
+{
+ int i;
+ int match = 0x00000001;
+
+ for (i = 0, bfad->nvec = 0; i < MAX_MSIX_ENTRY; i++) {
+ if (mask & match) {
+ bfad->msix_tab[bfad->nvec].msix.entry = i;
+ bfad->msix_tab[bfad->nvec].bfad = bfad;
+ msix_entries[bfad->nvec].entry = i;
+ bfad->nvec++;
+ }
+
+ match <<= 1;
+ }
+
+}
+
+int
+bfad_install_msix_handler(struct bfad_s *bfad)
+{
+ int i, error = 0;
+
+ for (i = 0; i < bfad->nvec; i++) {
+ error = request_irq(bfad->msix_tab[i].msix.vector,
+ (irq_handler_t) bfad_msix, 0,
+ BFAD_DRIVER_NAME, &bfad->msix_tab[i]);
+ bfa_trc(bfad, i);
+ bfa_trc(bfad, bfad->msix_tab[i].msix.vector);
+ if (error) {
+ int j;
+
+ for (j = 0; j < i; j++)
+ free_irq(bfad->msix_tab[j].msix.vector,
+ &bfad->msix_tab[j]);
+
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Setup MSIX based interrupt.
+ */
+int
+bfad_setup_intr(struct bfad_s *bfad)
+{
+ int error = 0;
+ u32 mask = 0, i, num_bit = 0, max_bit = 0;
+ struct msix_entry msix_entries[MAX_MSIX_ENTRY];
+
+ /* Call BFA to get the msix map for this PCI function. */
+ bfa_msix_getvecs(&bfad->bfa, &mask, &num_bit, &max_bit);
+
+ /* Set up the msix entry table */
+ bfad_init_msix_entry(bfad, msix_entries, mask, max_bit);
+
+ if (!msix_disable) {
+ error = pci_enable_msix(bfad->pcidev, msix_entries, bfad->nvec);
+ if (error) {
+ /*
+ * Only error number of vector is available.
+ * We don't have a mechanism to map multiple
+ * interrupts into one vector, so even if we
+ * can try to request less vectors, we don't
+ * know how to associate interrupt events to
+ * vectors. Linux doesn't dupicate vectors
+ * in the MSIX table for this case.
+ */
+
+ printk(KERN_WARNING "bfad%d: "
+ "pci_enable_msix failed (%d),"
+ " use line based.\n", bfad->inst_no, error);
+
+ goto line_based;
+ }
+
+ /* Save the vectors */
+ for (i = 0; i < bfad->nvec; i++) {
+ bfa_trc(bfad, msix_entries[i].vector);
+ bfad->msix_tab[i].msix.vector = msix_entries[i].vector;
+ }
+
+ bfa_msix_init(&bfad->bfa, bfad->nvec);
+
+ bfad->bfad_flags |= BFAD_MSIX_ON;
+
+ return error;
+ }
+
+line_based:
+ error = 0;
+ if (request_irq
+ (bfad->pcidev->irq, (irq_handler_t) bfad_intx, BFAD_IRQ_FLAGS,
+ BFAD_DRIVER_NAME, bfad) != 0) {
+ /* Enable interrupt handler failed */
+ return 1;
+ }
+
+ return error;
+}
+
+void
+bfad_remove_intr(struct bfad_s *bfad)
+{
+ int i;
+
+ if (bfad->bfad_flags & BFAD_MSIX_ON) {
+ for (i = 0; i < bfad->nvec; i++)
+ free_irq(bfad->msix_tab[i].msix.vector,
+ &bfad->msix_tab[i]);
+
+ pci_disable_msix(bfad->pcidev);
+ bfad->bfad_flags &= ~BFAD_MSIX_ON;
+ } else {
+ free_irq(bfad->pcidev->irq, bfad);
+ }
+}
+
+#else /* CONFIG_PCI_MSI */
+/**
+ * Setup line-based interrupt.
+ */
+int
+bfad_setup_intr(struct bfad_s *bfad)
+{
+ if (request_irq
+ (bfad->pcidev->irq, (irq_handler_t) bfad_intx, SA_SHIRQ,
+ BFAD_DRIVER_NAME, bfad) != 0) {
+ /* Enable interrupt handler failed */
+ return 1;
+ }
+
+ return 0;
+}
+
+void
+bfad_remove_intr(struct bfad_s *bfad)
+{
+ bfa_trc(bfad, bfad->pcidev->irq);
+ free_irq(bfad->pcidev->irq, bfad);
+}
+
+#endif /* CONFIG_PCI_MSI */
+
+
diff -urpN orig/drivers/scsi/bfa/bfad_ipfc.h patch/drivers/scsi/bfa/bfad_ipfc.h
--- orig/drivers/scsi/bfa/bfad_ipfc.h 1969-12-31 16:00:00.000000000 -0800
+++ patch/drivers/scsi/bfa/bfad_ipfc.h 2009-07-21 15:34:02.000000000 -0700
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2005-2008 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * http://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.
+ */
+#ifndef __BFA_DRV_IPFC_H__
+#define __BFA_DRV_IPFC_H__
+
+
+#define IPFC_NAME ""
+
+#define bfad_ipfc_module_init(x) do {} while (0)
+#define bfad_ipfc_module_exit(x) do {} while (0)
+#define bfad_ipfc_probe(x) do {} while (0)
+#define bfad_ipfc_probe_undo(x) do {} while (0)
+#define bfad_ipfc_port_config(x, y) BFA_STATUS_OK
+#define bfad_ipfc_port_unconfig(x, y) do {} while (0)
+
+#define bfad_ipfc_probe_post(x) do {} while (0)
+#define bfad_ipfc_port_new(x, y, z) BFA_STATUS_OK
+#define bfad_ipfc_port_delete(x, y) do {} while (0)
+#define bfad_ipfc_port_online(x, y) do {} while (0)
+#define bfad_ipfc_port_offline(x, y) do {} while (0)
+
+#define bfad_ip_get_attr(x) BFA_STATUS_FAILED
+#define bfad_ip_reset_drv_stats(x) BFA_STATUS_FAILED
+#define bfad_ip_get_drv_stats(x, y) BFA_STATUS_FAILED
+#define bfad_ip_enable_ipfc(x, y, z) BFA_STATUS_FAILED
+
+
+#endif
diff -urpN orig/drivers/scsi/bfa/bfad_os.c patch/drivers/scsi/bfa/bfad_os.c
--- orig/drivers/scsi/bfa/bfad_os.c 1969-12-31 16:00:00.000000000 -0800
+++ patch/drivers/scsi/bfa/bfad_os.c 2009-07-21 15:34:02.000000000 -0700
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2005-2008 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * http://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_os.c Linux driver OS specific calls.
+ */
+
+#include "bfa_os_inc.h"
+#include "bfad_drv.h"
+
+void
+bfa_os_gettimeofday(struct bfa_timeval_s *tv)
+{
+ struct timeval tmp_tv;
+
+ do_gettimeofday(&tmp_tv);
+ tv->tv_sec = (u32) tmp_tv.tv_sec;
+ tv->tv_usec = (u32) tmp_tv.tv_usec;
+}
+
+void
+bfa_os_printf(struct bfa_log_mod_s *log_mod, u32 msg_id,
+ const char *fmt, ...)
+{
+ va_list ap;
+ #define BFA_STRING_256 256
+ char tmp[BFA_STRING_256];
+
+ va_start(ap, fmt);
+ vsprintf(tmp, fmt, ap);
+ va_end(ap);
+
+ printk(tmp);
+}
+
+u32
+bfa_os_get_instance_id(struct bfad_s *bfad)
+{
+ return (bfad->inst_no);
+}
+
+
diff -urpN orig/drivers/scsi/bfa/bfad_tm.h patch/drivers/scsi/bfa/bfad_tm.h
--- orig/drivers/scsi/bfa/bfad_tm.h 1969-12-31 16:00:00.000000000 -0800
+++ patch/drivers/scsi/bfa/bfad_tm.h 2009-07-21 15:34:02.000000000 -0700
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2005-2008 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * http://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.
+ */
+
+/*
+ * Brocade Fibre Channel HBA Linux Target Mode Driver
+ */
+
+/**
+ * tm/dummy/bfad_tm.h BFA callback dummy header file for BFA Linux target mode PCI interface module.
+ */
+
+#ifndef __BFAD_TM_H__
+#define __BFAD_TM_H__
+
+#include <defs/bfa_defs_status.h>
+
+#define FCPT_NAME ""
+
+/*
+ * Called from base Linux driver on (De)Init events
+ */
+
+/* attach tgt template with scst */
+#define bfad_tm_module_init() do {} while (0)
+
+/* detach/release tgt template */
+#define bfad_tm_module_exit() do {} while (0)
+
+#define bfad_tm_probe(x) do {} while (0)
+#define bfad_tm_probe_undo(x) do {} while (0)
+#define bfad_tm_probe_post(x) do {} while (0)
+
+/*
+ * Called by base Linux driver but triggered by BFA FCS on config events
+ */
+#define bfad_tm_port_new(x, y) BFA_STATUS_OK
+#define bfad_tm_port_delete(x, y) do {} while (0)
+
+/*
+ * Called by base Linux driver but triggered by BFA FCS on PLOGI/O events
+ */
+#define bfad_tm_port_online(x, y) do {} while (0)
+#define bfad_tm_port_offline(x, y) do {} while (0)
+
+#endif
diff -urpN orig/drivers/scsi/bfa/bfad_trcmod.h patch/drivers/scsi/bfa/bfad_trcmod.h
--- orig/drivers/scsi/bfa/bfad_trcmod.h 1969-12-31 16:00:00.000000000 -0800
+++ patch/drivers/scsi/bfa/bfad_trcmod.h 2009-07-21 15:34:02.000000000 -0700
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2005-2008 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * http://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_trcmod.h Linux driver trace modules
+ */
+
+
+#ifndef __BFAD_TRCMOD_H__
+#define __BFAD_TRCMOD_H__
+
+#include <cs/bfa_trc.h>
+
+/*
+ * !!! Only append to the enums defined here to avoid any versioning
+ * !!! needed between trace utility and driver version
+ */
+enum {
+ /* 2.6 Driver */
+ BFA_TRC_LDRV_BFAD = 1,
+ BFA_TRC_LDRV_BFAD_2_6 = 2,
+ BFA_TRC_LDRV_BFAD_2_6_9 = 3,
+ BFA_TRC_LDRV_BFAD_2_6_10 = 4,
+ BFA_TRC_LDRV_INTR = 5,
+ BFA_TRC_LDRV_IOCTL = 6,
+ BFA_TRC_LDRV_OS = 7,
+ BFA_TRC_LDRV_IM = 8,
+ BFA_TRC_LDRV_IM_2_6 = 9,
+ BFA_TRC_LDRV_IM_2_6_9 = 10,
+ BFA_TRC_LDRV_IM_2_6_10 = 11,
+ BFA_TRC_LDRV_TM = 12,
+ BFA_TRC_LDRV_IPFC = 13,
+ BFA_TRC_LDRV_IM_2_4 = 14,
+ BFA_TRC_LDRV_IM_VMW = 15,
+ BFA_TRC_LDRV_IM_LT_2_6_10 = 16,
+};
+
+#endif /* __BFAD_TRCMOD_H__ */


2009-09-02 19:42:05

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH 1/14] bfa: Brocade BFA FC SCSI driver (bfad)

On Fri, 28 Aug 2009 00:54:30 -0700
Jing Huang <[email protected]> wrote:

> From: Jing Huang <[email protected]>
>
> This patch contains code that interfaces to upper layer linux kernel, such as
> PCI, SCSI mid-layer and sysfs etc.
>

These patches seem to have been posted a lot of times. Is anyone
paying attention?

> +BFA_TRC_FILE(LDRV, BFAD);
> +
> +static DEFINE_MUTEX(bfad_mutex);
> +int bfad_scan_done;
> +
> +LIST_HEAD(bfad_list);
> +
> +static int bfad_inst;
> +int supported_fc4s;
> +
> +char *host_name;
> +char *os_name;
> +char *os_patch;
> +int num_rports;
> +int num_ios;
> +int num_tms;
> +int num_fcxps;
> +int num_ufbufs;
> +int reqq_size;
> +int rspq_size;
> +int num_sgpgs;
> +int rport_del_timeout = BFA_FCS_RPORT_DEF_DEL_TIMEOUT;
> +int bfa_lun_queue_depth = BFAD_LUN_QUEUE_DEPTH;
> +int bfa_io_max_sge = BFAD_IO_MAX_SGE;
> +int log_level = BFA_LOG_WARNING;
> +int ioc_auto_recover = BFA_TRUE;
> +int ipfc_enable = BFA_FALSE;
> +int ipfc_mtu = -1;
> +int linkup_delay = -1;

eek. These are all kernel-wide global symbols and many of them are
very inappropriately named. They should either be made static or given
a subsystem-specific prefix such as "bfad_".

2009-09-02 20:02:08

by Jing Huang

[permalink] [raw]
Subject: RE: [PATCH 1/14] bfa: Brocade BFA FC SCSI driver (bfad)

> These patches seem to have been posted a lot of times. Is anyone
> paying attention?
>
> > +BFA_TRC_FILE(LDRV, BFAD);
> > +
> > +static DEFINE_MUTEX(bfad_mutex);
> > +int bfad_scan_done;
> > +
> > +LIST_HEAD(bfad_list);
> > +
> > +static int bfad_inst;
> > +int supported_fc4s;
> > +
> > +char *host_name;
> > +char *os_name;
> > +char *os_patch;
> > +int num_rports;
> > +int num_ios;
> > +int num_tms;
> > +int num_fcxps;
> > +int num_ufbufs;
> > +int reqq_size;
> > +int rspq_size;
> > +int num_sgpgs;
> > +int rport_del_timeout = BFA_FCS_RPORT_DEF_DEL_TIMEOUT;
> > +int bfa_lun_queue_depth = BFAD_LUN_QUEUE_DEPTH;
> > +int bfa_io_max_sge = BFAD_IO_MAX_SGE;
> > +int log_level = BFA_LOG_WARNING;
> > +int ioc_auto_recover = BFA_TRUE;
> > +int ipfc_enable = BFA_FALSE;
> > +int ipfc_mtu = -1;
> > +int linkup_delay = -1;
>
> eek. These are all kernel-wide global symbols and many of them are
> very inappropriately named. They should either be made static or given
> a subsystem-specific prefix such as "bfad_".
>

Thanks Andrew. I will fix this and resubmit.

2009-09-03 14:44:43

by Brian King

[permalink] [raw]
Subject: Re: [PATCH 1/14] bfa: Brocade BFA FC SCSI driver (bfad)

Below are a few comments based on a quick review. Many of the comments
are general comments that will apply across all the patches.

-Brian

Jing Huang wrote:
> From: Jing Huang <[email protected]>
>
> This patch contains code that interfaces to upper layer linux kernel, such as
> PCI, SCSI mid-layer and sysfs etc.
>
> Signed-off-by: Jing Huang <[email protected]>
> ---
> bfad.c | 1407 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
> bfad_attr.c | 660 +++++++++++++++++++++++++
> bfad_attr.h | 67 ++
> bfad_drv.h | 385 +++++++++++++++
> bfad_fwimg.c | 97 +++
> bfad_im.c | 1349 ++++++++++++++++++++++++++++++++++++++++++++++++++++
> bfad_im.h | 205 ++++++++
> bfad_im_compat.h | 46 +
> bfad_intr.c | 241 +++++++++
> bfad_ipfc.h | 42 +
> bfad_os.c | 56 ++
> bfad_tm.h | 59 ++
> bfad_trcmod.h | 52 ++
> 13 files changed, 4666 insertions(+)
>
> diff -urpN orig/drivers/scsi/bfa/bfad_attr.c patch/drivers/scsi/bfa/bfad_attr.c
> --- orig/drivers/scsi/bfa/bfad_attr.c 1969-12-31 16:00:00.000000000 -0800
> +++ patch/drivers/scsi/bfa/bfad_attr.c 2009-08-19 17:47:54.000000000 -0700


> +
> +/**
> + * FC transport template entry, issue a LIP.
> + */
> +int
> +bfad_im_issue_fc_host_lip(struct Scsi_Host *shost)
> +{
> + return 0;
> +}

Is there code missing here, or can this be removed? If you don't support
issue_fc_host_lip, then don't setup the function pointer.


> +
> +
> +
> +
> +
> +struct Scsi_Host *
> +bfad_os_starget_to_shost(struct scsi_target *starget)
> +{
> + return dev_to_shost(starget->dev.parent);
> +}
> +

Just call dev_to_shost directly



> +
> +static ssize_t
> +bfad_im_num_of_discovered_ports_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct Scsi_Host *shost = class_to_shost(dev);
> + struct bfad_im_port_s *im_port =
> + (struct bfad_im_port_s *) shost->hostdata[0];
> + struct bfad_port_s *port = im_port->port;
> + struct bfad_s *bfad = im_port->bfad;
> + int nrports = 2048;
> + wwn_t *rports = NULL;
> + unsigned long flags;
> +
> + rports = kzalloc(sizeof(wwn_t) * nrports , GFP_ATOMIC);
> + if (rports == NULL)
> + return snprintf(buf, PAGE_SIZE, "Failed\n");

Just return -ENOMEM here instead of printing Failed.

> +
> + spin_lock_irqsave(&bfad->bfad_lock, flags);
> + bfa_fcs_port_get_rports(port->fcs_port, rports, &nrports);
> + spin_unlock_irqrestore(&bfad->bfad_lock, flags);
> + kfree(rports);
> +
> + return snprintf(buf, PAGE_SIZE, "%d\n", nrports);
> +}


> +#endif /* __BFAD_ATTR_H__ */
> diff -urpN orig/drivers/scsi/bfa/bfad.c patch/drivers/scsi/bfa/bfad.c
> --- orig/drivers/scsi/bfa/bfad.c 1969-12-31 16:00:00.000000000 -0800
> +++ patch/drivers/scsi/bfa/bfad.c 2009-08-19 17:47:54.000000000 -0700



> +
> +void
> +bfad_flags_set(struct bfad_s *bfad, u32 flags)
> +{
> + if (bfad)
> + bfad->bfad_flags |= flags;
> +}
> +
> +
> +/**
> + * bfa_ioc_diable() callback.
> + */
> +void
> +bfa_cb_ioc_disable(void *drv)
> +{
> + struct bfad_s *bfad = drv;
> +
> + complete(&bfad->disable_comp);
> +}
> +
> +void
> +bfa_fcb_exit(struct bfad_s *drv)
> +{
> + complete(&drv->comp);
> +}

I think a lot of these very small helper functions can just be removed as they
don't really make the driver any easier to read.


> +
> +void
> +bfa_fcb_vf_stop(struct bfad_vf_s *vf_drv)
> +{
> +}
> +

This can be removed.

> +
> +/**
> + * FCS RPORT free callback.
> + */
> +void
> +bfa_fcb_rport_free(struct bfad_s *bfad, struct bfad_rport_s **rport_drv)
> +{
> + kfree(*rport_drv);
> +}

Why not just call kfree directly?


> +
> +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;

> + 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);

Can you use dev_info here instead?

> + retry_count++;
> + goto retry;
> + } else {
> + if (num_sgpgs_parm > 0)




> +
> +int
> +bfad_pci_init(struct pci_dev *pdev, struct bfad_s *bfad)
> +{

> +
> + 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);

Why do you need to copy all this data to your own structure? Can't you
just access it from the pdev when you need it?

> +
> + bfad->pcidev = pdev;
> + return 0;
> +
> +out_release_region:
> + pci_release_regions(pdev);
> +out_disable_device:
> + pci_disable_device(pdev);
> +out:
> + return rc;
> +}
> +



> +/**
> + * 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];
> +

> + /*
> + * If 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 linkup_delay.
> + */
> + if (linkup_delay < 0) {
> +#if defined(__ia64__)
> + linkup_delay = 10;
> +#else
> + linkup_delay = bfad_os_get_linkup_delay(bfad);
> +#endif

This looks rather strange. What does ia64 need a different delay?

> + bfad_os_rport_online_wait(bfad);
> + linkup_delay = -1;
> + } else {
> + bfad_os_rport_online_wait(bfad);
> + }


> +
> +#define BFAD_MAX_PCIIDS 4
> +struct pci_device_id bfad_id_table[BFAD_MAX_PCIIDS] = {

Don't need BFAD_MAX_PCIIDS here. Just declare this [] and have
the compiler figure it out. This could also be flagged __devinitdata.

> + {
> + .vendor = BFA_PCI_VENDOR_ID_BROCADE,
> + .device = BFA_PCI_DEVICE_ID_FC_8G2P,
> + .subvendor = PCI_ANY_ID,
> + .subdevice = PCI_ANY_ID,
> + },



> +
> +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(linkup_delay, int, S_IRUGO | S_IWUSR);
> +module_param(msix_disable, int, S_IRUGO | S_IWUSR);

This seems like a lot of module parameters. Are they all needed?
Would some of them work better as scsi_host sysfs parameters?

> +
> +#ifdef CONFIG_PM
> +int pci_save_state(struct pci_dev *pdev);
> +int pci_restore_state(struct pci_dev *pdev);

Don't need these..

> +
> +/**
> + * PCI suspend entry.
> + */
> +static int
> +bfad_os_suspend(struct pci_dev *pdev, pm_message_t state)
> +{
> + struct bfad_s *bfad = pci_get_drvdata(pdev);
> + pci_power_t device_state;
> +
> + device_state = pci_choose_state(pdev, state);
> +
> + pci_save_state(pdev);
> + init_completion(&bfad->suspend);
> + wait_for_completion(&bfad->suspend);

What triggers you to wake up here? Won't you lose initiative here
and never wake up?

> + pci_disable_device(pdev);
> + pci_set_power_state(pdev, device_state);
> +
> + return 0;
> +}
> +
> +/**
> + * PCI resume entry.
> + */
> +static int
> +bfad_os_resume(struct pci_dev *pdev)
> +{
> +
> + /* Resume the device power state to D0 */
> + pci_set_power_state(pdev, 0);

There is a define for PCI_D0 you could use here..

> +
> + /* Restore all device PCI configuation space to its original state. */
> + pci_restore_state(pdev);
> +
> + if (pci_enable_device(pdev))
> + goto out;
> +
> + return 0;
> +
> +out:
> + return 1;
> +
> +}
> +#endif
> +
> +#ifdef SUPPORT_PCI_AER
> +/*
> + * PCI error recovery support, this fearure is only availbe for kernel
> + * .6.16 or later.
> + */
> +/**
> + * PCI Error Recovery entry, error detected.
> + */
> +static pci_ers_result_t
> +bfad_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
> +{
> + struct bfad_s *bfad = pci_get_drvdata(pdev);
> +
> + switch (state) {
> + case pci_channel_io_perm_failure:
> + /*
> + * PCI bus has permanently failed. This is
> + * unrecoveralbe, Do we need to do the cleanup like PCI
> + * remove? or kernel will automatically do it?
> + */

You need to clean things up yourself. Just be careful you don't go off and
try to talk to the adapter, like you might do in the PCI remove case.


> +
> +/**
> + * PCI Error Recovery entry, re-initialize the chip.
> + */
> +static pci_ers_result_t
> +bfad_pci_slot_reset(struct pci_dev *pdev)
> +{
> + struct bfad_s *bfad = pci_get_drvdata(pdev);
> + int retval;
> +
> + if (pci_enable_device(pdev))
> + goto out;
> +
> + pci_set_master(pdev);
> +
> + retval = pci_set_mwi(pdev);
> +
> + if (retval)
> + dev_printk(KERN_WARNING, &pdev->dev,
> + "Warning: pci_set_mwi returned %d\n", retval);

Could use dev_warn here instead.


> +
> diff -urpN orig/drivers/scsi/bfa/bfad_drv.h patch/drivers/scsi/bfa/bfad_drv.h
> --- orig/drivers/scsi/bfa/bfad_drv.h 1969-12-31 16:00:00.000000000 -0800
> +++ patch/drivers/scsi/bfa/bfad_drv.h 2009-08-19 17:47:54.000000000 -0700
> @@ -0,0 +1,385 @@

> +
> +#if defined(CONFIG_PCIEPORTBUS) && defined(CONFIG_PCIEAER)
> +#define BFAD_ENABLE_PCIE_AER(x) pci_enable_pcie_error_reporting(x)
> +#else
> +#define BFAD_ENABLE_PCIE_AER(x) {}
> +#endif

Shouldn't need to ifdef this sort of stuff. If CONFIG_PCIAER=n,
pci_enable_pcie_error_reporting will return -EINVAL.

> +#define BFAD_IRQ_FLAGS IRQF_SHARED
> +
> +#ifndef FC_PORTSPEED_8GBIT
> +#define FC_PORTSPEED_8GBIT 0x10
> +#endif

Not needed in an upstream Linux driver.


> +
> +#define BFAD_WORK_HANDLER(name) void name(struct work_struct *work)
> +#define BFAD_INIT_WORK(x, work, func) INIT_WORK(&(x)->work, func)

Why not just call INIT_WORK directly?

> +
> +#define list_remove_head(list, entry, type, member) \
> +do { \
> + entry = NULL; \
> + if (!list_empty(list)) { \
> + entry = list_entry((list)->next, type, member); \
> + list_del_init(&entry->member); \
> + } \
> +} while (0)
> +
> +#define list_get_first(list, type, member) \
> +((list_empty(list)) ? NULL : \
> + list_entry((list)->next, type, member))

Why not add these to list.h so others can use them as well?

> +
> +bfa_boolean_t bfad_is_ready(void);
> +bfa_status_t bfad_vport_create(struct bfad_s *bfad, u16 vf_id,
> + struct bfa_port_cfg_s *port_cfg);
> +bfa_status_t bfad_vf_create(struct bfad_s *bfad, u16 vf_id,
> + struct bfa_port_cfg_s *port_cfg);
> +bfa_status_t bfad_cfg_pport(struct bfad_s *bfad, enum bfa_port_role role);
> +bfa_status_t bfad_drv_init(struct bfad_s *bfad);
> +struct bfad_s *bfad_find_bfad_by_inst_no(int inst_no);
> +void bfad_drv_start(struct bfad_s *bfad);
> +void bfad_uncfg_pport(struct bfad_s *bfad);
> +void bfad_drv_stop(struct bfad_s *bfad);
> +void bfad_remove_intr(struct bfad_s *bfad);
> +void bfad_hal_mem_release(struct bfad_s *bfad);
> +void bfad_hcb_comp(void *arg, bfa_status_t status);
> +
> +int bfad_os_ioctl_init(void);
> +int bfad_os_ioctl_exit(void);
> +int bfad_setup_intr(struct bfad_s *bfad);
> +void bfad_remove_intr(struct bfad_s *bfad);
> +int bfad_os_pci_register_driver(struct pci_driver *drv);
> +void bfad_os_pci_unregister_driver(struct pci_driver *drv);
> +void bfad_os_device_sysfs_create(struct bfad_s *);
> +void bfad_os_device_sysfs_remove(struct bfad_s *);
> +void bfad_os_pci_set_mwi(struct pci_dev *pdev);
> +void bfad_os_idr_init(struct idr *idr);
> +void bfad_os_idr_destroy(struct idr *idr);
> +void *bfad_os_dma_alloc(struct bfad_s *bfad, struct bfa_mem_elem_s
> + *meminfo_elem, dma_addr_t *phys_addr);
> +void bfad_os_dma_free(struct bfad_s *bfad, struct bfa_mem_elem_s
> + *meminfo_elem);
> +void bfad_os_idr_remove(struct idr *idp, int id);
> +int bfad_os_idr_pre_get(struct idr *idp, gfp_t gfp_mask);
> +void bfad_os_iounmap(struct pci_dev *pdev, struct bfad_s *bfad);
> +
> +void bfad_update_hal_cfg(struct bfa_iocfc_cfg_s *bfa_cfg);
> +bfa_status_t bfad_hal_mem_alloc(struct bfad_s *bfad);
> +void bfad_bfa_tmo(unsigned long data);
> +void bfad_init_timer(struct bfad_s *bfad);
> +int bfad_pci_init(struct pci_dev *pdev, struct bfad_s *bfad);
> +void bfad_pci_uninit(struct pci_dev *pdev, struct bfad_s *bfad);
> +void bfad_fcs_port_cfg(struct bfad_s *bfad);
> +void bfad_drv_uninit(struct bfad_s *bfad);
> +void bfad_drv_log_level_set(struct bfad_s *bfad);
> +bfa_status_t bfad_fc4_module_init(void);
> +void bfad_fc4_module_exit(void);
> +int bfad_ipfc_probe(struct bfad_s *bfad);
> +int bfad_ipfc_probe_undo(struct bfad_s *bfad);
> +void bfad_ipfc_probe_post(struct bfad_s *bfad);
> +int bfad_ipfc_module_init(void);
> +void bfad_ipfc_module_exit(void);
> +int bfad_ipfc_port_online(struct bfad_s *bfad,
> + struct bfad_port_s *port);
> +int bfad_ipfc_port_offline(struct bfad_s *bfad,
> + struct bfad_port_s *port);
> +int bfad_ipfc_port_new(struct bfad_s *bfad,
> + struct bfad_port_s *port, int port_type);
> +int bfad_ipfc_port_delete(struct bfad_s *bfad,
> + struct bfad_port_s *port);

Are all these function prototypes necessary?

> +#endif /* __BFAD_DRV_H__ */
> diff -urpN orig/drivers/scsi/bfa/bfad_fwimg.c patch/drivers/scsi/bfa/bfad_fwimg.c
> --- orig/drivers/scsi/bfa/bfad_fwimg.c 1969-12-31 16:00:00.000000000 -0800
> +++ patch/drivers/scsi/bfa/bfad_fwimg.c 2009-08-19 17:47:54.000000000 -0700


> +
> +char bfa_version[BFA_VERSION_LEN] = "2.0.0.0 ";

Shouldn't this use BFA_DRIVER_VERSION?

> diff -urpN orig/drivers/scsi/bfa/bfad_im.c patch/drivers/scsi/bfa/bfad_im.c
> --- orig/drivers/scsi/bfa/bfad_im.c 1969-12-31 16:00:00.000000000 -0800
> +++ patch/drivers/scsi/bfa/bfad_im.c 2009-08-19 17:47:54.000000000 -0700

> +
> +void
> +bfa_cb_ioim_done(void *drv, struct bfad_ioim_s *dio,
> + enum bfi_ioim_status io_status, u8 scsi_status,
> + int sns_len, u8 *sns_info, s32 residue)
> +{
> + struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio;
> + struct bfad_s *bfad = drv;
> + struct bfad_itnim_data_s *itnim_data;
> + struct bfad_itnim_s *itnim;
> + u8 host_status = DID_OK;

Why bother with this local at all? Don't think it simplifies anything...

> +
> + switch (io_status) {
> + case BFI_IOIM_STS_OK:
> + bfa_trc(bfad, scsi_status);
> + cmnd->result = ScsiResult(host_status, scsi_status);
> + scsi_set_resid(cmnd, 0);
> +
> + if (sns_len > 0) {
> + bfa_trc(bfad, sns_len);
> + if (sns_len > SCSI_SENSE_BUFFERSIZE)
> + sns_len = SCSI_SENSE_BUFFERSIZE;
> + memcpy(cmnd->sense_buffer, sns_info, sns_len);
> + }
> + if (residue > 0)
> + scsi_set_resid(cmnd, residue);
> + break;
> +
> + case BFI_IOIM_STS_ABORTED:
> + case BFI_IOIM_STS_TIMEDOUT:
> + case BFI_IOIM_STS_PATHTOV:
> + default:
> + host_status = DID_ERROR;
> + cmnd->result = ScsiResult(host_status, 0);
> + }
> +
> + /* Unmap DMA, if host is NULL, it means a scsi passthru cmd */
> + if (cmnd->device->host != NULL)
> + bfad_os_im_dma_unmap(bfad, cmnd);
> +
> + cmnd->host_scribble = NULL;
> + bfa_trc(bfad, cmnd->result);
> +
> + itnim_data = cmnd->device->hostdata;
> + if (itnim_data) {
> + itnim = itnim_data->itnim;
> + if (!cmnd->result && itnim &&
> + (bfa_lun_queue_depth > cmnd->device->queue_depth)) {
> + /* Queue depth adjustment for good status completion */
> + bfad_os_ramp_up_qdepth(itnim, cmnd->device);
> + } else if (cmnd->result == SAM_STAT_TASK_SET_FULL && itnim) {
> + /* qfull handling */
> + bfad_os_handle_qfull(itnim, cmnd->device);
> + }
> + }
> +
> + cmnd->scsi_done(cmnd);
> +}
> +


> +
> +/**
> + * Scsi_Host_template SCSI host template
> + */
> +/**
> + * Scsi_Host template entry, returns BFAD PCI info.
> + */
> +const char *
> +bfad_im_info(struct Scsi_Host *shost)
> +{
> + static char bfa_buf[256];
> + struct bfad_im_port_s *im_port =
> + (struct bfad_im_port_s *) shost->hostdata[0];
> + struct bfa_ioc_attr_s ioc_attr;
> + struct bfad_s *bfad = im_port->bfad;
> +
> + memset(&ioc_attr, 0, sizeof(ioc_attr));
> + bfa_get_attr(&bfad->bfa, &ioc_attr);
> +
> + memset(bfa_buf, 0, sizeof(bfa_buf));
> + snprintf(bfa_buf, sizeof(bfa_buf),
> + "Brocade FC/FCOE Adapter, " "model: %s hwpath: %s driver: %s",
> + ioc_attr.adapter_attr.model, bfad->pci_name,
> + BFAD_DRIVER_VERSION);
> + return bfa_buf;
> +}
> +

Can any of these functions be made static?


> +
> +/**
> + * Scsi_Host template entry, resets the bus and abort all commands.
> + */
> +int
> +bfad_im_reset_bus_handler(struct scsi_cmnd *cmnd)
> +{
> + struct Scsi_Host *shost = cmnd->device->host;
> + struct bfad_im_port_s *im_port =
> + (struct bfad_im_port_s *) shost->hostdata[0];
> + struct bfad_s *bfad = im_port->bfad;
> + struct bfad_itnim_s *itnim;
> + unsigned long flags;
> + u32 i, rc, err_cnt = 0;
> + DECLARE_WAIT_QUEUE_HEAD(wq);
> + enum bfi_tskim_status task_status;
> +
> + spin_lock_irqsave(&bfad->bfad_lock, flags);
> + for (i = 0; i < MAX_FCP_TARGET; i++) {
> + itnim = bfad_os_get_itnim(im_port, i);
> + if (itnim) {
> + cmnd->SCp.ptr = (char *)&wq;
> + rc = bfad_im_target_reset_send(bfad, cmnd, itnim);

Since you are just sending target resets anyway, why not just support
eh_target_reset_handler and not support eh_bus_reset_handler?


> +
> +void
> +bfad_wwn_to_str(wwn_t wwn, char *str)
> +{
> + sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
> + *((u8 *)&wwn),
> + *(((u8 *)&wwn) + 1),
> + *(((u8 *)&wwn) + 2),
> + *(((u8 *)&wwn) + 3),
> + *(((u8 *)&wwn) + 4),
> + *(((u8 *)&wwn) + 5),
> + *(((u8 *)&wwn) + 6),
> + *(((u8 *)&wwn) + 7));
> +}
> +

Seems like a candidate to go in include/scsi/fc?



> +
> +/**
> + * Path TOV processing begin notification -- dummy for linux
> + */
> +void
> +bfa_fcb_itnim_tov_begin(struct bfad_itnim_s *itnim)
> +{
> +}
> +
> +

I'm seeing a lot of dummy functions... No need for those in an upstream
Linux driver. It just makes the driver more difficult to understand.


> +
> +int
> +bfad_os_im_dma_map(struct bfad_s *bfad, struct scsi_cmnd *cmnd)
> +{
> + int sg_cnt, rc = 0;
> +
> + if (scsi_sg_count(cmnd)) {
> + sg_cnt = dma_map_sg((struct device *)&bfad->pcidev->dev,
> + (struct scatterlist *)scsi_sglist(cmnd),
> + scsi_sg_count(cmnd),
> + cmnd->sc_data_direction);
> + if (sg_cnt == 0 || sg_cnt > bfad->cfg_data.io_max_sge) {
> + printk(KERN_WARNING
> + "bfad%d: dma_map_sg failure, sg_cnt=%d\n",
> + bfad->inst_no, sg_cnt);
> + rc = 1;
> + }
> +
> + } else if (scsi_bufflen(cmnd)) {
> + cmnd->SCp.dma_handle =
> + dma_map_single((struct device *)&bfad->pcidev->
> + dev, scsi_sglist(cmnd),
> + scsi_bufflen(cmnd),
> + cmnd->sc_data_direction);
> + }

This should be simplified to use scsi_dma_map, which would eliminate the need
for this helper function altogether.

> +
> +static void
> +bfad_im_fc_rport_add(struct bfad_im_port_s *im_port, struct bfad_itnim_s *itnim)
> +{
> + struct fc_rport_identifiers rport_ids;
> + struct fc_rport *fc_rport;
> + struct bfad_itnim_data_s *itnim_data;
> +
> + rport_ids.node_name =
> + bfa_os_htonll(bfa_fcs_itnim_get_nwwn(&itnim->fcs_itnim));

As mentioned below, you should be able to do away with the bfa_os_htonll wrapper.



> +
> +/**
> + * Scsi_Host template entry, queue a SCSI command to the BFAD.
> + */
> +int
> +bfad_im_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
> +{
> + struct bfad_im_port_s *im_port =
> + (struct bfad_im_port_s *) cmnd->device->host->hostdata[0];

You can use shost_priv here instead.

> + struct bfad_s *bfad = im_port->bfad;
> + struct bfad_itnim_data_s *itnim_data = cmnd->device->hostdata;

> +
> +void
> +bfad_os_rport_online_wait(struct bfad_s *bfad)
> +{
> + int i;
> + int rport_delay = 10;
> +
> + for (i = 0; !(bfad->bfad_flags & BFAD_PORT_ONLINE)
> + && i < linkup_delay; i++) {
> + set_current_state(TASK_UNINTERRUPTIBLE);
> + schedule_timeout(HZ);

This can be simplified to use schedule_timeout_uninterruptible.
You might also look into using wait_event_timeout as an alternative.

> +
> diff -urpN orig/drivers/scsi/bfa/bfad_im_compat.h patch/drivers/scsi/bfa/bfad_im_compat.h
> --- orig/drivers/scsi/bfa/bfad_im_compat.h 1969-12-31 16:00:00.000000000 -0800
> +++ patch/drivers/scsi/bfa/bfad_im_compat.h 2009-08-19 17:47:54.000000000 -0700

> +
> +extern u32 *bfi_image_buf;
> +extern u32 bfi_image_size;
> +
> +extern struct device_attribute *bfad_im_host_attrs[];
> +extern struct device_attribute *bfad_im_vport_attrs[];

I will echo Andrew's comment regarding globals... Can't some of these
be scoped to a single instance of an adapter?

> diff -urpN orig/drivers/scsi/bfa/bfad_im.h patch/drivers/scsi/bfa/bfad_im.h
> --- orig/drivers/scsi/bfa/bfad_im.h 1969-12-31 16:00:00.000000000 -0800
> +++ patch/drivers/scsi/bfa/bfad_im.h 2009-08-19 17:47:54.000000000 -0700

> +#define FCPI_NAME " fcpim"
> +
> +#ifndef KOBJ_NAME_LEN
> +#define KOBJ_NAME_LEN 20
> +#endif

This can be removed as it is not needed in an upstream driver.



> +
> +void bfad_os_spin_os_lock_irq(struct Scsi_Host *);
> +void bfad_os_spin_os_unlock_irq(struct Scsi_Host *);

These sort of wrappers are generally frowned upon in Linux as they
obfuscate the code and make it more difficult to read. Recommend
you call the kernel interfaces directly. The same comment follows for
a lot, if not all, of the function prototypes below.

> +void bfad_os_fc_release_transp(struct Scsi_Host *shost);
> +struct Scsi_Host *bfad_os_scsi_host_alloc(struct bfad_im_port_s *im_port,
> + struct bfad_s *);
> +int bfad_os_scsi_register(struct Scsi_Host *shost,
> + struct bfad_im_port_s *im_port, void *key);
> +int bfad_os_fc_attach(struct Scsi_Host *shost,
> + struct fc_function_template *fct);
> +int bfad_os_reset_tgt(struct scsi_cmnd *cmnd);
> +void bfad_os_scsi_set_pci_device(struct Scsi_Host *shost,
> + struct pci_dev *pcidev);
> +void bfad_os_select_queue_depths(struct bfad_im_port_s *im_port);
> +bfa_status_t bfad_os_thread_workq(struct bfad_s *bfad);
> +void bfad_os_destroy_workq(struct bfad_im_s *im);
> +void bfad_os_queue_work(void *workq, void *work);
> +void bfad_os_itnim_alloc(struct bfad_itnim_s *itnim_drv);
> +void bfad_os_itnim_process(struct bfad_itnim_s *itnim_drv);
> +void bfad_os_itnim_tov(struct bfad_itnim_s *itnim_drv);
> +void bfad_os_scsi_unregister(struct Scsi_Host *shost);
> +void bfad_os_scsi_remove_host(struct Scsi_Host *shost);
> +void bfad_os_scsi_put_host(struct Scsi_Host *shost);
> +int bfad_os_scsi_add_host(struct Scsi_Host *shost,
> + struct bfad_im_port_s *im_port, struct bfad_s *bfad);
> +int bfad_os_fc_attach(struct Scsi_Host *shost,
> + struct fc_function_template *fct);
> +
> +struct bfad_itnim_data_s *bfad_os_itnim_data(struct bfad_im_port_s *im_port,
> + struct scsi_cmnd *cmnd);
> +void bfad_os_fc_host_init(struct bfad_im_port_s *im_port);
> +void bfad_os_fc_remove_host(struct Scsi_Host *shost);
> +void bfad_os_init_work(struct bfad_im_port_s *im_port);
> +int bfad_os_dev_init(struct Scsi_Host *shost);
> +void bfad_os_transp_templ(struct Scsi_Host *sh,
> + struct scsi_transport_template *stt);
> +dma_addr_t bfad_os_dma_map_single(struct device *dev, void *cpu_addr,
> + size_t size, enum dma_data_direction direction);
> +void bfad_os_dma_unmap_single(struct device *dev, dma_addr_t dma_addr,
> + size_t size, enum dma_data_direction direction);
> +int bfad_os_dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
> + enum dma_data_direction direction);
> +void bfad_os_dma_unmap_sg(struct device *dev, struct scatterlist *sg,
> + int nhwentries, enum dma_data_direction direction);
> +int bfad_os_im_dma_map(struct bfad_s *bfad, struct scsi_cmnd *cmnd);
> +void bfad_os_im_dma_unmap(struct bfad_s *bfad, struct scsi_cmnd *cmnd);
> +int bfad_os_idr_get_new(struct idr *idp, void *ptr, int *id);
> +void bfad_os_scsi_state_changed(struct Scsi_Host *shost,
> + struct bfad_itnim_s *itnim);
> +void bfad_os_scsi_scan(struct bfad_im_port_s *im_port);
> +void bfad_os_scsi_host_free(struct bfad_s *bfad,
> + struct bfad_im_port_s *im_port);
> +void bfad_wwn_to_str(wwn_t wwn, char *str);
> +void bfad_fcid_to_str(u32 fcid, char *str);
> +void bfad_os_ramp_up_qdepth(struct bfad_itnim_s *itnim,
> + struct scsi_device *sdev);
> +void bfad_os_handle_qfull(struct bfad_itnim_s *itnim, struct scsi_device *sdev);
> +void bfad_os_set_dev_loss_tmo(struct scsi_device *sdev);
> +struct bfad_itnim_s *bfad_os_get_itnim(struct bfad_im_port_s *im_port, int id);
> +int bfad_os_im_itnim_is_online(struct bfad_itnim_s *itnim);
> +
> +/*
> + * scsi_host_template entries
> + */
> +int bfad_im_queuecommand(struct scsi_cmnd *cmnd,
> + void (*done)(struct scsi_cmnd *));
> +const char *bfad_im_info(struct Scsi_Host *host);
> +int bfad_im_slave_alloc(struct scsi_device *sdev);
> +void bfad_im_slave_destroy(struct scsi_device *sdev);
> +int bfad_im_abort_handler(struct scsi_cmnd *cmnd);
> +int bfad_im_reset_lun_handler(struct scsi_cmnd *cmnd);
> +int bfad_im_reset_bus_handler(struct scsi_cmnd *cmnd);
> +void bfad_im_itnim_unmap(struct bfad_im_port_s *im_port,
> + struct bfad_itnim_s *itnim);
> +
> +extern struct scsi_host_template bfad_im_scsi_host_template;
> +extern struct scsi_host_template bfad_im_vport_template;
> +extern struct fc_function_template bfad_im_fc_function_template;
> +extern struct scsi_transport_template *bfad_im_scsi_transport_template;
> +
> +#endif
> diff -urpN orig/drivers/scsi/bfa/bfad_intr.c patch/drivers/scsi/bfa/bfad_intr.c
> --- orig/drivers/scsi/bfa/bfad_intr.c 1969-12-31 16:00:00.000000000 -0800
> +++ patch/drivers/scsi/bfa/bfad_intr.c 2009-08-19 17:47:54.000000000 -0700

> +
> +#ifdef CONFIG_PCI_MSI

If CONFIG_PCI_MSI=n, pci_enable_msix will fail with a -1 rc, so there
is no need to ifdef this code. Just handle pci_enable_msix failing, which
it looks like you already do.

> +
> +static irqreturn_t
> +bfad_msix(int irq, void *dev_id)
> +{


> diff -urpN orig/drivers/scsi/bfa/bfad_os.c patch/drivers/scsi/bfa/bfad_os.c
> --- orig/drivers/scsi/bfa/bfad_os.c 1969-12-31 16:00:00.000000000 -0800
> +++ patch/drivers/scsi/bfa/bfad_os.c 2009-08-19 17:47:54.000000000 -0700
> +
> +void
> +bfa_os_printf(struct bfa_log_mod_s *log_mod, u32 msg_id,
> + const char *fmt, ...)
> +{
> + va_list ap;
> + #define BFA_STRING_256 256
> + char tmp[BFA_STRING_256];
> +
> + va_start(ap, fmt);
> + vsprintf(tmp, fmt, ap);
> + va_end(ap);
> +
> + printk(tmp);
> +}

Don't see any value in re-inventing printk and using a printk buffer on the
stack, which seems a little dangerous...

> +
> +u32
> +bfa_os_get_instance_id(struct bfad_s *bfad)
> +{
> + return (bfad->inst_no);
> +}

Why bother wrappering this in a function?

Thanks,

Brian

--
Brian King
Linux on Power Virtualization
IBM Linux Technology Center

2009-09-24 02:41:15

by Jing Huang

[permalink] [raw]
Subject: RE: [PATCH 1/14] bfa: Brocade BFA FC SCSI driver (bfad)

Hi Brian,

Thanks you so much for the detailed code review. I just resubmitted the patch set with changes to address most of your findings. Please find inline reply for each of your code review comment.

Jing

> > +/**
> > + * FC transport template entry, issue a LIP.
> > + */
> > +int
> > +bfad_im_issue_fc_host_lip(struct Scsi_Host *shost)
> > +{
> > + return 0;
> > +}
>
> Is there code missing here, or can this be removed? If you don't support
> issue_fc_host_lip, then don't setup the function pointer.
>

Removed the function.

> > +
> > +
> > +
> > +
> > +
> > +struct Scsi_Host *
> > +bfad_os_starget_to_shost(struct scsi_target *starget)
> > +{
> > + return dev_to_shost(starget->dev.parent);
> > +}
> > +
>
> Just call dev_to_shost directly
>

Fixed.

>
> > +
> > +static ssize_t
> > +bfad_im_num_of_discovered_ports_show(struct device *dev,
> > + struct device_attribute *attr, char *buf)
> > +{
> > + struct Scsi_Host *shost = class_to_shost(dev);
> > + struct bfad_im_port_s *im_port =
> > + (struct bfad_im_port_s *) shost->hostdata[0];
> > + struct bfad_port_s *port = im_port->port;
> > + struct bfad_s *bfad = im_port->bfad;
> > + int nrports = 2048;
> > + wwn_t *rports = NULL;
> > + unsigned long flags;
> > +
> > + rports = kzalloc(sizeof(wwn_t) * nrports , GFP_ATOMIC);
> > + if (rports == NULL)
> > + return snprintf(buf, PAGE_SIZE, "Failed\n");
>
> Just return -ENOMEM here instead of printing Failed.
>

Fixed.

> > +
> > +void
> > +bfad_flags_set(struct bfad_s *bfad, u32 flags)
> > +{
> > + if (bfad)
> > + bfad->bfad_flags |= flags;
> > +}
> > +
> > +
> > +/**
> > + * bfa_ioc_diable() callback.
> > + */
> > +void
> > +bfa_cb_ioc_disable(void *drv)
> > +{
> > + struct bfad_s *bfad = drv;
> > +
> > + complete(&bfad->disable_comp);
> > +}
> > +
> > +void
> > +bfa_fcb_exit(struct bfad_s *drv)
> > +{
> > + complete(&drv->comp);
> > +}
>
> I think a lot of these very small helper functions can just be removed as
> they
> don't really make the driver any easier to read.
>

Removed these small helper functions.

>
> > +
> > +void
> > +bfa_fcb_vf_stop(struct bfad_vf_s *vf_drv)
> > +{
> > +}
> > +
>
> This can be removed.
>

Removed.

> > +
> > +/**
> > + * FCS RPORT free callback.
> > + */
> > +void
> > +bfa_fcb_rport_free(struct bfad_s *bfad, struct bfad_rport_s
> **rport_drv)
> > +{
> > + kfree(*rport_drv);
> > +}
>
> Why not just call kfree directly?
>

Fixed. We have some code that can be used by multiple OS's, that is the reason for those small helper functions etc. I fixed them anyway.

>
> > +
> > +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;
>
> > + 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);
>
> Can you use dev_info here instead?

Forgot to change this in last submission. Will fix it in subsequent patch.

>
> > + retry_count++;
> > + goto retry;
> > + } else {
> > + if (num_sgpgs_parm > 0)
>
>
>
>
> > +
> > +int
> > +bfad_pci_init(struct pci_dev *pdev, struct bfad_s *bfad)
> > +{
>
> > +
> > + 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);
>
> Why do you need to copy all this data to your own structure? Can't you
> just access it from the pdev when you need it?
>

This is also due to some common code consideration. I didn't fix it this time due to the amount of code changes, but I will keep this mind and try to fix it in subsequent patches.

> > + /*
> > + * If 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 linkup_delay.
> > + */
> > + if (linkup_delay < 0) {
> > +#if defined(__ia64__)
> > + linkup_delay = 10;
> > +#else
> > + linkup_delay = bfad_os_get_linkup_delay(bfad);
> > +#endif
>
> This looks rather strange. What does ia64 need a different delay?
>

I agree. This linkup delay is introduced for boot-over-san case. We implemented some mechanism in BIOS so that this linkup delay is only introduced to the port that is connected to the boot LUN. This feature was not available for EFI boot at the time when the patch was submitted. We recently implemented similar mechanism for EFI boot, so I can remove it now.

> > +
> > +#define BFAD_MAX_PCIIDS 4
> > +struct pci_device_id bfad_id_table[BFAD_MAX_PCIIDS] = {
>
> Don't need BFAD_MAX_PCIIDS here. Just declare this [] and have
> the compiler figure it out. This could also be flagged __devinitdata.
>

Fixed.

> > +
> > +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(linkup_delay, int, S_IRUGO | S_IWUSR);
> > +module_param(msix_disable, int, S_IRUGO | S_IWUSR);
>
> This seems like a lot of module parameters. Are they all needed?
> Would some of them work better as scsi_host sysfs parameters?
>

Yes. They are all module parameters therefore I chose to not populate them for each scsi_host.

> > +
> > +#ifdef CONFIG_PM
> > +int pci_save_state(struct pci_dev *pdev);
> > +int pci_restore_state(struct pci_dev *pdev);
>
> Don't need these..
>

Removed them.

> > + */
> > +static int
> > +bfad_os_suspend(struct pci_dev *pdev, pm_message_t state)
> > +{
> > + struct bfad_s *bfad = pci_get_drvdata(pdev);
> > + pci_power_t device_state;
> > +
> > + device_state = pci_choose_state(pdev, state);
> > +
> > + pci_save_state(pdev);
> > + init_completion(&bfad->suspend);
> > + wait_for_completion(&bfad->suspend);
>
> What triggers you to wake up here? Won't you lose initiative here
> and never wake up?
>

Thanks for finding the bug. The suspend/resume and the PCI error recovery are not tested. I removed for now. I will add them back when they are ready.

> > +#define BFAD_IRQ_FLAGS IRQF_SHARED
> > +
> > +#ifndef FC_PORTSPEED_8GBIT
> > +#define FC_PORTSPEED_8GBIT 0x10
> > +#endif
>
> Not needed in an upstream Linux driver.
>

Removed.

> > +
> > +#define BFAD_WORK_HANDLER(name) void name(struct work_struct *work)
> > +#define BFAD_INIT_WORK(x, work, func) INIT_WORK(&(x)->work, func)
>
> Why not just call INIT_WORK directly?
>

Fixed.

> > +
> > +#define list_remove_head(list, entry, type, member) \
> > +do { \
> > + entry = NULL; \
> > + if (!list_empty(list)) { \
> > + entry = list_entry((list)->next, type, member); \
> > + list_del_init(&entry->member); \
> > + } \
> > +} while (0)
> > +
> > +#define list_get_first(list, type, member) \
> > +((list_empty(list)) ? NULL : \
> > + list_entry((list)->next, type, member))
>
> Why not add these to list.h so others can use them as well?
>

Removed.

> > +int bfad_ipfc_port_offline(struct bfad_s *bfad,
> > + struct bfad_port_s *port);
> > +int bfad_ipfc_port_new(struct bfad_s *bfad,
> > + struct bfad_port_s *port, int port_type);
> > +int bfad_ipfc_port_delete(struct bfad_s *bfad,
> > + struct bfad_port_s *port);
>
> Are all these function prototypes necessary?
>

Removed all unnecessary function prototypes.


> > +
> > +char bfa_version[BFA_VERSION_LEN] = "2.0.0.0 ";
>
> Shouldn't this use BFA_DRIVER_VERSION?
>

Fixed.

> > +void
> > +bfa_cb_ioim_done(void *drv, struct bfad_ioim_s *dio,
> > + enum bfi_ioim_status io_status, u8 scsi_status,
> > + int sns_len, u8 *sns_info, s32 residue)
> > +{
> > + struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio;
> > + struct bfad_s *bfad = drv;
> > + struct bfad_itnim_data_s *itnim_data;
> > + struct bfad_itnim_s *itnim;
> > + u8 host_status = DID_OK;
>
> Why bother with this local at all? Don't think it simplifies anything...

Removed the local.

> > +
> > +/**
> > + * Scsi_Host_template SCSI host template
> > + */
> > +/**
> > + * Scsi_Host template entry, returns BFAD PCI info.
> > + */
> > +const char *
> > +bfad_im_info(struct Scsi_Host *shost)
> > +{
> > + static char bfa_buf[256];
> > + struct bfad_im_port_s *im_port =
> > + (struct bfad_im_port_s *) shost->hostdata[0];
> > + struct bfa_ioc_attr_s ioc_attr;
> > + struct bfad_s *bfad = im_port->bfad;
> > +
> > + memset(&ioc_attr, 0, sizeof(ioc_attr));
> > + bfa_get_attr(&bfad->bfa, &ioc_attr);
> > +
> > + memset(bfa_buf, 0, sizeof(bfa_buf));
> > + snprintf(bfa_buf, sizeof(bfa_buf),
> > + "Brocade FC/FCOE Adapter, " "model: %s hwpath: %s driver:
> %s",
> > + ioc_attr.adapter_attr.model, bfad->pci_name,
> > + BFAD_DRIVER_VERSION);
> > + return bfa_buf;
> > +}
> > +
>
> Can any of these functions be made static?

Changed them to static.

> > +/**
> > + * Scsi_Host template entry, resets the bus and abort all commands.
> > + */
> > +int
> > +bfad_im_reset_bus_handler(struct scsi_cmnd *cmnd)
> > +{
> > + struct Scsi_Host *shost = cmnd->device->host;
> > + struct bfad_im_port_s *im_port =
> > + (struct bfad_im_port_s *) shost->hostdata[0];
> > + struct bfad_s *bfad = im_port->bfad;
> > + struct bfad_itnim_s *itnim;
> > + unsigned long flags;
> > + u32 i, rc, err_cnt = 0;
> > + DECLARE_WAIT_QUEUE_HEAD(wq);
> > + enum bfi_tskim_status task_status;
> > +
> > + spin_lock_irqsave(&bfad->bfad_lock, flags);
> > + for (i = 0; i < MAX_FCP_TARGET; i++) {
> > + itnim = bfad_os_get_itnim(im_port, i);
> > + if (itnim) {
> > + cmnd->SCp.ptr = (char *)&wq;
> > + rc = bfad_im_target_reset_send(bfad, cmnd, itnim);
>
> Since you are just sending target resets anyway, why not just support
> eh_target_reset_handler and not support eh_bus_reset_handler?
>

You are right. But the change was not made this time. I will look into it.

> > +void
> > +bfad_wwn_to_str(wwn_t wwn, char *str)
> > +{
> > + sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
> > + *((u8 *)&wwn),
> > + *(((u8 *)&wwn) + 1),
> > + *(((u8 *)&wwn) + 2),
> > + *(((u8 *)&wwn) + 3),
> > + *(((u8 *)&wwn) + 4),
> > + *(((u8 *)&wwn) + 5),
> > + *(((u8 *)&wwn) + 6),
> > + *(((u8 *)&wwn) + 7));
> > +}
> > +
>
> Seems like a candidate to go in include/scsi/fc?
>

I agree, removed this function.

>
> I'm seeing a lot of dummy functions... No need for those in an upstream
> Linux driver. It just makes the driver more difficult to understand.
>

Those dummy functions are also introduced for the common code. I fixed most of them. And I will keep working on this.

> > +int
> > +bfad_os_im_dma_map(struct bfad_s *bfad, struct scsi_cmnd *cmnd)
> > +{
> > + int sg_cnt, rc = 0;
> > +
> > + if (scsi_sg_count(cmnd)) {
> > + sg_cnt = dma_map_sg((struct device *)&bfad->pcidev->dev,
> > + (struct scatterlist *)scsi_sglist(cmnd),
> > + scsi_sg_count(cmnd),
> > + cmnd->sc_data_direction);
> > + if (sg_cnt == 0 || sg_cnt > bfad->cfg_data.io_max_sge) {
> > + printk(KERN_WARNING
> > + "bfad%d: dma_map_sg failure, sg_cnt=%d\n",
> > + bfad->inst_no, sg_cnt);
> > + rc = 1;
> > + }
> > +
> > + } else if (scsi_bufflen(cmnd)) {
> > + cmnd->SCp.dma_handle =
> > + dma_map_single((struct device *)&bfad->pcidev->
> > + dev, scsi_sglist(cmnd),
> > + scsi_bufflen(cmnd),
> > + cmnd->sc_data_direction);
> > + }
>
> This should be simplified to use scsi_dma_map, which would eliminate the
> need
> for this helper function altogether.
>

Fixed.

> > +
> > +static void
> > +bfad_im_fc_rport_add(struct bfad_im_port_s *im_port, struct
> bfad_itnim_s *itnim)
> > +{
> > + struct fc_rport_identifiers rport_ids;
> > + struct fc_rport *fc_rport;
> > + struct bfad_itnim_data_s *itnim_data;
> > +
> > + rport_ids.node_name =
> > + bfa_os_htonll(bfa_fcs_itnim_get_nwwn(&itnim->fcs_itnim));
>
> As mentioned below, you should be able to do away with the bfa_os_htonll
> wrapper.
>

The swap is necessary here since we save wwnn in native FC format.

> > + for (i = 0; !(bfad->bfad_flags & BFAD_PORT_ONLINE)
> > + && i < linkup_delay; i++) {
> > + set_current_state(TASK_UNINTERRUPTIBLE);
> > + schedule_timeout(HZ);
>
> This can be simplified to use schedule_timeout_uninterruptible.
> You might also look into using wait_event_timeout as an alternative.
>

Fixed.

> > +
> > diff -urpN orig/drivers/scsi/bfa/bfad_im_compat.h
> patch/drivers/scsi/bfa/bfad_im_compat.h
> > --- orig/drivers/scsi/bfa/bfad_im_compat.h 1969-12-31
> 16:00:00.000000000 -0800
> > +++ patch/drivers/scsi/bfa/bfad_im_compat.h 2009-08-19
> 17:47:54.000000000 -0700
>
> > +
> > +extern u32 *bfi_image_buf;
> > +extern u32 bfi_image_size;
> > +
> > +extern struct device_attribute *bfad_im_host_attrs[];
> > +extern struct device_attribute *bfad_im_vport_attrs[];
>
> I will echo Andrew's comment regarding globals... Can't some of these
> be scoped to a single instance of an adapter?
>

Fixed Andrew's code review comments. I have to keep some globals since they are intended for the module instead of a single instance.

> > +#define FCPI_NAME " fcpim"
> > +
> > +#ifndef KOBJ_NAME_LEN
> > +#define KOBJ_NAME_LEN 20
> > +#endif
>
> This can be removed as it is not needed in an upstream driver.
>

Removed.

> > +
> > +void bfad_os_spin_os_lock_irq(struct Scsi_Host *);
> > +void bfad_os_spin_os_unlock_irq(struct Scsi_Host *);
>
> These sort of wrappers are generally frowned upon in Linux as they
> obfuscate the code and make it more difficult to read. Recommend
> you call the kernel interfaces directly. The same comment follows for
> a lot, if not all, of the function prototypes below.
>

Removed.

> > +
> > +#ifdef CONFIG_PCI_MSI
>
> If CONFIG_PCI_MSI=n, pci_enable_msix will fail with a -1 rc, so there
> is no need to ifdef this code. Just handle pci_enable_msix failing, which
> it looks like you already do.
>

Fixed.

> > +void
> > +bfa_os_printf(struct bfa_log_mod_s *log_mod, u32 msg_id,
> > + const char *fmt, ...)
> > +{
> > + va_list ap;
> > + #define BFA_STRING_256 256
> > + char tmp[BFA_STRING_256];
> > +
> > + va_start(ap, fmt);
> > + vsprintf(tmp, fmt, ap);
> > + va_end(ap);
> > +
> > + printk(tmp);
> > +}
>
> Don't see any value in re-inventing printk and using a printk buffer on
> the
> stack, which seems a little dangerous...
>

This is also due to some common code and log message format consideration. I didn't fix it in last submission due to the amount of code changes. But I will keep this in mind and try to fix them in subsequent patches.

> > +
> > +u32
> > +bfa_os_get_instance_id(struct bfad_s *bfad)
> > +{
> > + return (bfad->inst_no);
> > +}
>
> Why bother wrappering this in a function?
>

Fixed.