mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-15 07:56:41 +07:00
e99e88a9d2
This converts all remaining cases of the old setup_timer() API into using timer_setup(), where the callback argument is the structure already holding the struct timer_list. These should have no behavioral changes, since they just change which pointer is passed into the callback with the same available pointers after conversion. It handles the following examples, in addition to some other variations. Casting from unsigned long: void my_callback(unsigned long data) { struct something *ptr = (struct something *)data; ... } ... setup_timer(&ptr->my_timer, my_callback, ptr); and forced object casts: void my_callback(struct something *ptr) { ... } ... setup_timer(&ptr->my_timer, my_callback, (unsigned long)ptr); become: void my_callback(struct timer_list *t) { struct something *ptr = from_timer(ptr, t, my_timer); ... } ... timer_setup(&ptr->my_timer, my_callback, 0); Direct function assignments: void my_callback(unsigned long data) { struct something *ptr = (struct something *)data; ... } ... ptr->my_timer.function = my_callback; have a temporary cast added, along with converting the args: void my_callback(struct timer_list *t) { struct something *ptr = from_timer(ptr, t, my_timer); ... } ... ptr->my_timer.function = (TIMER_FUNC_TYPE)my_callback; And finally, callbacks without a data assignment: void my_callback(unsigned long data) { ... } ... setup_timer(&ptr->my_timer, my_callback, 0); have their argument renamed to verify they're unused during conversion: void my_callback(struct timer_list *unused) { ... } ... timer_setup(&ptr->my_timer, my_callback, 0); The conversion is done with the following Coccinelle script: spatch --very-quiet --all-includes --include-headers \ -I ./arch/x86/include -I ./arch/x86/include/generated \ -I ./include -I ./arch/x86/include/uapi \ -I ./arch/x86/include/generated/uapi -I ./include/uapi \ -I ./include/generated/uapi --include ./include/linux/kconfig.h \ --dir . \ --cocci-file ~/src/data/timer_setup.cocci @fix_address_of@ expression e; @@ setup_timer( -&(e) +&e , ...) // Update any raw setup_timer() usages that have a NULL callback, but // would otherwise match change_timer_function_usage, since the latter // will update all function assignments done in the face of a NULL // function initialization in setup_timer(). @change_timer_function_usage_NULL@ expression _E; identifier _timer; type _cast_data; @@ ( -setup_timer(&_E->_timer, NULL, _E); +timer_setup(&_E->_timer, NULL, 0); | -setup_timer(&_E->_timer, NULL, (_cast_data)_E); +timer_setup(&_E->_timer, NULL, 0); | -setup_timer(&_E._timer, NULL, &_E); +timer_setup(&_E._timer, NULL, 0); | -setup_timer(&_E._timer, NULL, (_cast_data)&_E); +timer_setup(&_E._timer, NULL, 0); ) @change_timer_function_usage@ expression _E; identifier _timer; struct timer_list _stl; identifier _callback; type _cast_func, _cast_data; @@ ( -setup_timer(&_E->_timer, _callback, _E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, &_callback, _E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, _callback, (_cast_data)_E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, &_callback, (_cast_data)_E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, (_cast_func)_callback, _E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, (_cast_func)&_callback, _E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, (_cast_func)_callback, (_cast_data)_E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, (_cast_func)&_callback, (_cast_data)_E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E._timer, _callback, (_cast_data)_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, _callback, (_cast_data)&_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, &_callback, (_cast_data)_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, &_callback, (_cast_data)&_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)&_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)&_E); +timer_setup(&_E._timer, _callback, 0); | _E->_timer@_stl.function = _callback; | _E->_timer@_stl.function = &_callback; | _E->_timer@_stl.function = (_cast_func)_callback; | _E->_timer@_stl.function = (_cast_func)&_callback; | _E._timer@_stl.function = _callback; | _E._timer@_stl.function = &_callback; | _E._timer@_stl.function = (_cast_func)_callback; | _E._timer@_stl.function = (_cast_func)&_callback; ) // callback(unsigned long arg) @change_callback_handle_cast depends on change_timer_function_usage@ identifier change_timer_function_usage._callback; identifier change_timer_function_usage._timer; type _origtype; identifier _origarg; type _handletype; identifier _handle; @@ void _callback( -_origtype _origarg +struct timer_list *t ) { ( ... when != _origarg _handletype *_handle = -(_handletype *)_origarg; +from_timer(_handle, t, _timer); ... when != _origarg | ... when != _origarg _handletype *_handle = -(void *)_origarg; +from_timer(_handle, t, _timer); ... when != _origarg | ... when != _origarg _handletype *_handle; ... when != _handle _handle = -(_handletype *)_origarg; +from_timer(_handle, t, _timer); ... when != _origarg | ... when != _origarg _handletype *_handle; ... when != _handle _handle = -(void *)_origarg; +from_timer(_handle, t, _timer); ... when != _origarg ) } // callback(unsigned long arg) without existing variable @change_callback_handle_cast_no_arg depends on change_timer_function_usage && !change_callback_handle_cast@ identifier change_timer_function_usage._callback; identifier change_timer_function_usage._timer; type _origtype; identifier _origarg; type _handletype; @@ void _callback( -_origtype _origarg +struct timer_list *t ) { + _handletype *_origarg = from_timer(_origarg, t, _timer); + ... when != _origarg - (_handletype *)_origarg + _origarg ... when != _origarg } // Avoid already converted callbacks. @match_callback_converted depends on change_timer_function_usage && !change_callback_handle_cast && !change_callback_handle_cast_no_arg@ identifier change_timer_function_usage._callback; identifier t; @@ void _callback(struct timer_list *t) { ... } // callback(struct something *handle) @change_callback_handle_arg depends on change_timer_function_usage && !match_callback_converted && !change_callback_handle_cast && !change_callback_handle_cast_no_arg@ identifier change_timer_function_usage._callback; identifier change_timer_function_usage._timer; type _handletype; identifier _handle; @@ void _callback( -_handletype *_handle +struct timer_list *t ) { + _handletype *_handle = from_timer(_handle, t, _timer); ... } // If change_callback_handle_arg ran on an empty function, remove // the added handler. @unchange_callback_handle_arg depends on change_timer_function_usage && change_callback_handle_arg@ identifier change_timer_function_usage._callback; identifier change_timer_function_usage._timer; type _handletype; identifier _handle; identifier t; @@ void _callback(struct timer_list *t) { - _handletype *_handle = from_timer(_handle, t, _timer); } // We only want to refactor the setup_timer() data argument if we've found // the matching callback. This undoes changes in change_timer_function_usage. @unchange_timer_function_usage depends on change_timer_function_usage && !change_callback_handle_cast && !change_callback_handle_cast_no_arg && !change_callback_handle_arg@ expression change_timer_function_usage._E; identifier change_timer_function_usage._timer; identifier change_timer_function_usage._callback; type change_timer_function_usage._cast_data; @@ ( -timer_setup(&_E->_timer, _callback, 0); +setup_timer(&_E->_timer, _callback, (_cast_data)_E); | -timer_setup(&_E._timer, _callback, 0); +setup_timer(&_E._timer, _callback, (_cast_data)&_E); ) // If we fixed a callback from a .function assignment, fix the // assignment cast now. @change_timer_function_assignment depends on change_timer_function_usage && (change_callback_handle_cast || change_callback_handle_cast_no_arg || change_callback_handle_arg)@ expression change_timer_function_usage._E; identifier change_timer_function_usage._timer; identifier change_timer_function_usage._callback; type _cast_func; typedef TIMER_FUNC_TYPE; @@ ( _E->_timer.function = -_callback +(TIMER_FUNC_TYPE)_callback ; | _E->_timer.function = -&_callback +(TIMER_FUNC_TYPE)_callback ; | _E->_timer.function = -(_cast_func)_callback; +(TIMER_FUNC_TYPE)_callback ; | _E->_timer.function = -(_cast_func)&_callback +(TIMER_FUNC_TYPE)_callback ; | _E._timer.function = -_callback +(TIMER_FUNC_TYPE)_callback ; | _E._timer.function = -&_callback; +(TIMER_FUNC_TYPE)_callback ; | _E._timer.function = -(_cast_func)_callback +(TIMER_FUNC_TYPE)_callback ; | _E._timer.function = -(_cast_func)&_callback +(TIMER_FUNC_TYPE)_callback ; ) // Sometimes timer functions are called directly. Replace matched args. @change_timer_function_calls depends on change_timer_function_usage && (change_callback_handle_cast || change_callback_handle_cast_no_arg || change_callback_handle_arg)@ expression _E; identifier change_timer_function_usage._timer; identifier change_timer_function_usage._callback; type _cast_data; @@ _callback( ( -(_cast_data)_E +&_E->_timer | -(_cast_data)&_E +&_E._timer | -_E +&_E->_timer ) ) // If a timer has been configured without a data argument, it can be // converted without regard to the callback argument, since it is unused. @match_timer_function_unused_data@ expression _E; identifier _timer; identifier _callback; @@ ( -setup_timer(&_E->_timer, _callback, 0); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, _callback, 0L); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, _callback, 0UL); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E._timer, _callback, 0); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, _callback, 0L); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, _callback, 0UL); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_timer, _callback, 0); +timer_setup(&_timer, _callback, 0); | -setup_timer(&_timer, _callback, 0L); +timer_setup(&_timer, _callback, 0); | -setup_timer(&_timer, _callback, 0UL); +timer_setup(&_timer, _callback, 0); | -setup_timer(_timer, _callback, 0); +timer_setup(_timer, _callback, 0); | -setup_timer(_timer, _callback, 0L); +timer_setup(_timer, _callback, 0); | -setup_timer(_timer, _callback, 0UL); +timer_setup(_timer, _callback, 0); ) @change_callback_unused_data depends on match_timer_function_unused_data@ identifier match_timer_function_unused_data._callback; type _origtype; identifier _origarg; @@ void _callback( -_origtype _origarg +struct timer_list *unused ) { ... when != _origarg } Signed-off-by: Kees Cook <keescook@chromium.org>
1817 lines
46 KiB
C
1817 lines
46 KiB
C
/*
|
|
* Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
|
|
* Copyright (c) 2014- QLogic Corporation.
|
|
* All rights reserved
|
|
* www.qlogic.com
|
|
*
|
|
* Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License (GPL) Version 2 as
|
|
* published by the Free Software Foundation
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*/
|
|
|
|
/*
|
|
* bfad.c Linux driver PCI interface module.
|
|
*/
|
|
#include <linux/module.h>
|
|
#include <linux/kthread.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/init.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/firmware.h>
|
|
#include <linux/uaccess.h>
|
|
#include <asm/fcntl.h>
|
|
|
|
#include "bfad_drv.h"
|
|
#include "bfad_im.h"
|
|
#include "bfa_fcs.h"
|
|
#include "bfa_defs.h"
|
|
#include "bfa.h"
|
|
|
|
BFA_TRC_FILE(LDRV, BFAD);
|
|
DEFINE_MUTEX(bfad_mutex);
|
|
LIST_HEAD(bfad_list);
|
|
|
|
static int bfad_inst;
|
|
static int num_sgpgs_parm;
|
|
int supported_fc4s;
|
|
char *host_name, *os_name, *os_patch;
|
|
int num_rports, num_ios, num_tms;
|
|
int num_fcxps, num_ufbufs;
|
|
int reqq_size, rspq_size, 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 bfa_log_level = 3; /* WARNING log level */
|
|
int ioc_auto_recover = BFA_TRUE;
|
|
int bfa_linkup_delay = -1;
|
|
int fdmi_enable = BFA_TRUE;
|
|
int pcie_max_read_reqsz;
|
|
int bfa_debugfs_enable = 1;
|
|
int msix_disable_cb = 0, msix_disable_ct = 0;
|
|
int max_xfer_size = BFAD_MAX_SECTORS >> 1;
|
|
int max_rport_logins = BFA_FCS_MAX_RPORT_LOGINS;
|
|
|
|
/* Firmware releated */
|
|
u32 bfi_image_cb_size, bfi_image_ct_size, bfi_image_ct2_size;
|
|
u32 *bfi_image_cb, *bfi_image_ct, *bfi_image_ct2;
|
|
|
|
#define BFAD_FW_FILE_CB "cbfw-3.2.5.1.bin"
|
|
#define BFAD_FW_FILE_CT "ctfw-3.2.5.1.bin"
|
|
#define BFAD_FW_FILE_CT2 "ct2fw-3.2.5.1.bin"
|
|
|
|
static u32 *bfad_load_fwimg(struct pci_dev *pdev);
|
|
static void bfad_free_fwimg(void);
|
|
static void bfad_read_firmware(struct pci_dev *pdev, u32 **bfi_image,
|
|
u32 *bfi_image_size, char *fw_name);
|
|
|
|
static const char *msix_name_ct[] = {
|
|
"ctrl",
|
|
"cpe0", "cpe1", "cpe2", "cpe3",
|
|
"rme0", "rme1", "rme2", "rme3" };
|
|
|
|
static const char *msix_name_cb[] = {
|
|
"cpe0", "cpe1", "cpe2", "cpe3",
|
|
"rme0", "rme1", "rme2", "rme3",
|
|
"eemc", "elpu0", "elpu1", "epss", "mlpu" };
|
|
|
|
MODULE_FIRMWARE(BFAD_FW_FILE_CB);
|
|
MODULE_FIRMWARE(BFAD_FW_FILE_CT);
|
|
MODULE_FIRMWARE(BFAD_FW_FILE_CT2);
|
|
|
|
module_param(os_name, charp, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(os_name, "OS name of the hba host machine");
|
|
module_param(os_patch, charp, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(os_patch, "OS patch level of the hba host machine");
|
|
module_param(host_name, charp, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(host_name, "Hostname of the hba host machine");
|
|
module_param(num_rports, int, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(num_rports, "Max number of rports supported per port "
|
|
"(physical/logical), default=1024");
|
|
module_param(num_ios, int, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(num_ios, "Max number of ioim requests, default=2000");
|
|
module_param(num_tms, int, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(num_tms, "Max number of task im requests, default=128");
|
|
module_param(num_fcxps, int, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(num_fcxps, "Max number of fcxp requests, default=64");
|
|
module_param(num_ufbufs, int, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(num_ufbufs, "Max number of unsolicited frame "
|
|
"buffers, default=64");
|
|
module_param(reqq_size, int, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(reqq_size, "Max number of request queue elements, "
|
|
"default=256");
|
|
module_param(rspq_size, int, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(rspq_size, "Max number of response queue elements, "
|
|
"default=64");
|
|
module_param(num_sgpgs, int, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(num_sgpgs, "Number of scatter/gather pages, default=2048");
|
|
module_param(rport_del_timeout, int, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(rport_del_timeout, "Rport delete timeout, default=90 secs, "
|
|
"Range[>0]");
|
|
module_param(bfa_lun_queue_depth, int, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(bfa_lun_queue_depth, "Lun queue depth, default=32, Range[>0]");
|
|
module_param(bfa_io_max_sge, int, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(bfa_io_max_sge, "Max io scatter/gather elements, default=255");
|
|
module_param(bfa_log_level, int, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(bfa_log_level, "Driver log level, default=3, "
|
|
"Range[Critical:1|Error:2|Warning:3|Info:4]");
|
|
module_param(ioc_auto_recover, int, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(ioc_auto_recover, "IOC auto recovery, default=1, "
|
|
"Range[off:0|on:1]");
|
|
module_param(bfa_linkup_delay, int, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(bfa_linkup_delay, "Link up delay, default=30 secs for "
|
|
"boot port. Otherwise 10 secs in RHEL4 & 0 for "
|
|
"[RHEL5, SLES10, ESX40] Range[>0]");
|
|
module_param(msix_disable_cb, int, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(msix_disable_cb, "Disable Message Signaled Interrupts for QLogic-415/425/815/825 cards, default=0 Range[false:0|true:1]");
|
|
module_param(msix_disable_ct, int, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(msix_disable_ct, "Disable Message Signaled Interrupts if possible for QLogic-1010/1020/804/1007/902/1741 cards, default=0, Range[false:0|true:1]");
|
|
module_param(fdmi_enable, int, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(fdmi_enable, "Enables fdmi registration, default=1, "
|
|
"Range[false:0|true:1]");
|
|
module_param(pcie_max_read_reqsz, int, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(pcie_max_read_reqsz, "PCIe max read request size, default=0 "
|
|
"(use system setting), Range[128|256|512|1024|2048|4096]");
|
|
module_param(bfa_debugfs_enable, int, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(bfa_debugfs_enable, "Enables debugfs feature, default=1,"
|
|
" Range[false:0|true:1]");
|
|
module_param(max_xfer_size, int, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(max_xfer_size, "default=32MB,"
|
|
" Range[64k|128k|256k|512k|1024k|2048k]");
|
|
module_param(max_rport_logins, int, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(max_rport_logins, "Max number of logins to initiator and target rports on a port (physical/logical), default=1024");
|
|
|
|
static void
|
|
bfad_sm_uninit(struct bfad_s *bfad, enum bfad_sm_event event);
|
|
static void
|
|
bfad_sm_created(struct bfad_s *bfad, enum bfad_sm_event event);
|
|
static void
|
|
bfad_sm_initializing(struct bfad_s *bfad, enum bfad_sm_event event);
|
|
static void
|
|
bfad_sm_operational(struct bfad_s *bfad, enum bfad_sm_event event);
|
|
static void
|
|
bfad_sm_stopping(struct bfad_s *bfad, enum bfad_sm_event event);
|
|
static void
|
|
bfad_sm_failed(struct bfad_s *bfad, enum bfad_sm_event event);
|
|
static void
|
|
bfad_sm_fcs_exit(struct bfad_s *bfad, enum bfad_sm_event event);
|
|
|
|
/*
|
|
* Beginning state for the driver instance, awaiting the pci_probe event
|
|
*/
|
|
static void
|
|
bfad_sm_uninit(struct bfad_s *bfad, enum bfad_sm_event event)
|
|
{
|
|
bfa_trc(bfad, event);
|
|
|
|
switch (event) {
|
|
case BFAD_E_CREATE:
|
|
bfa_sm_set_state(bfad, bfad_sm_created);
|
|
bfad->bfad_tsk = kthread_create(bfad_worker, (void *) bfad,
|
|
"%s", "bfad_worker");
|
|
if (IS_ERR(bfad->bfad_tsk)) {
|
|
printk(KERN_INFO "bfad[%d]: Kernel thread "
|
|
"creation failed!\n", bfad->inst_no);
|
|
bfa_sm_send_event(bfad, BFAD_E_KTHREAD_CREATE_FAILED);
|
|
}
|
|
bfa_sm_send_event(bfad, BFAD_E_INIT);
|
|
break;
|
|
|
|
case BFAD_E_STOP:
|
|
/* Ignore stop; already in uninit */
|
|
break;
|
|
|
|
default:
|
|
bfa_sm_fault(bfad, event);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Driver Instance is created, awaiting event INIT to initialize the bfad
|
|
*/
|
|
static void
|
|
bfad_sm_created(struct bfad_s *bfad, enum bfad_sm_event event)
|
|
{
|
|
unsigned long flags;
|
|
bfa_status_t ret;
|
|
|
|
bfa_trc(bfad, event);
|
|
|
|
switch (event) {
|
|
case BFAD_E_INIT:
|
|
bfa_sm_set_state(bfad, bfad_sm_initializing);
|
|
|
|
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);
|
|
bfa_sm_send_event(bfad, BFAD_E_INIT_FAILED);
|
|
break;
|
|
}
|
|
|
|
spin_lock_irqsave(&bfad->bfad_lock, flags);
|
|
bfa_iocfc_init(&bfad->bfa);
|
|
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
|
|
|
|
/* Set up interrupt handler for each vectors */
|
|
if ((bfad->bfad_flags & BFAD_MSIX_ON) &&
|
|
bfad_install_msix_handler(bfad)) {
|
|
printk(KERN_WARNING "%s: install_msix failed, bfad%d\n",
|
|
__func__, bfad->inst_no);
|
|
}
|
|
|
|
bfad_init_timer(bfad);
|
|
|
|
wait_for_completion(&bfad->comp);
|
|
|
|
if ((bfad->bfad_flags & BFAD_HAL_INIT_DONE)) {
|
|
bfa_sm_send_event(bfad, BFAD_E_INIT_SUCCESS);
|
|
} else {
|
|
printk(KERN_WARNING
|
|
"bfa %s: bfa init failed\n",
|
|
bfad->pci_name);
|
|
spin_lock_irqsave(&bfad->bfad_lock, flags);
|
|
bfa_fcs_init(&bfad->bfa_fcs);
|
|
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
|
|
|
|
ret = bfad_cfg_pport(bfad, BFA_LPORT_ROLE_FCP_IM);
|
|
if (ret != BFA_STATUS_OK) {
|
|
init_completion(&bfad->comp);
|
|
|
|
spin_lock_irqsave(&bfad->bfad_lock, flags);
|
|
bfad->pport.flags |= BFAD_PORT_DELETE;
|
|
bfa_fcs_exit(&bfad->bfa_fcs);
|
|
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
|
|
|
|
wait_for_completion(&bfad->comp);
|
|
|
|
bfa_sm_send_event(bfad, BFAD_E_INIT_FAILED);
|
|
break;
|
|
}
|
|
bfad->bfad_flags |= BFAD_HAL_INIT_FAIL;
|
|
bfa_sm_send_event(bfad, BFAD_E_HAL_INIT_FAILED);
|
|
}
|
|
|
|
break;
|
|
|
|
case BFAD_E_KTHREAD_CREATE_FAILED:
|
|
bfa_sm_set_state(bfad, bfad_sm_uninit);
|
|
break;
|
|
|
|
default:
|
|
bfa_sm_fault(bfad, event);
|
|
}
|
|
}
|
|
|
|
static void
|
|
bfad_sm_initializing(struct bfad_s *bfad, enum bfad_sm_event event)
|
|
{
|
|
int retval;
|
|
unsigned long flags;
|
|
|
|
bfa_trc(bfad, event);
|
|
|
|
switch (event) {
|
|
case BFAD_E_INIT_SUCCESS:
|
|
kthread_stop(bfad->bfad_tsk);
|
|
spin_lock_irqsave(&bfad->bfad_lock, flags);
|
|
bfad->bfad_tsk = NULL;
|
|
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
|
|
|
|
retval = bfad_start_ops(bfad);
|
|
if (retval != BFA_STATUS_OK) {
|
|
bfa_sm_set_state(bfad, bfad_sm_failed);
|
|
break;
|
|
}
|
|
bfa_sm_set_state(bfad, bfad_sm_operational);
|
|
break;
|
|
|
|
case BFAD_E_INIT_FAILED:
|
|
bfa_sm_set_state(bfad, bfad_sm_uninit);
|
|
kthread_stop(bfad->bfad_tsk);
|
|
spin_lock_irqsave(&bfad->bfad_lock, flags);
|
|
bfad->bfad_tsk = NULL;
|
|
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
|
|
break;
|
|
|
|
case BFAD_E_HAL_INIT_FAILED:
|
|
bfa_sm_set_state(bfad, bfad_sm_failed);
|
|
break;
|
|
default:
|
|
bfa_sm_fault(bfad, event);
|
|
}
|
|
}
|
|
|
|
static void
|
|
bfad_sm_failed(struct bfad_s *bfad, enum bfad_sm_event event)
|
|
{
|
|
int retval;
|
|
|
|
bfa_trc(bfad, event);
|
|
|
|
switch (event) {
|
|
case BFAD_E_INIT_SUCCESS:
|
|
retval = bfad_start_ops(bfad);
|
|
if (retval != BFA_STATUS_OK)
|
|
break;
|
|
bfa_sm_set_state(bfad, bfad_sm_operational);
|
|
break;
|
|
|
|
case BFAD_E_STOP:
|
|
bfa_sm_set_state(bfad, bfad_sm_fcs_exit);
|
|
bfa_sm_send_event(bfad, BFAD_E_FCS_EXIT_COMP);
|
|
break;
|
|
|
|
case BFAD_E_EXIT_COMP:
|
|
bfa_sm_set_state(bfad, bfad_sm_uninit);
|
|
bfad_remove_intr(bfad);
|
|
del_timer_sync(&bfad->hal_tmo);
|
|
break;
|
|
|
|
default:
|
|
bfa_sm_fault(bfad, event);
|
|
}
|
|
}
|
|
|
|
static void
|
|
bfad_sm_operational(struct bfad_s *bfad, enum bfad_sm_event event)
|
|
{
|
|
bfa_trc(bfad, event);
|
|
|
|
switch (event) {
|
|
case BFAD_E_STOP:
|
|
bfa_sm_set_state(bfad, bfad_sm_fcs_exit);
|
|
bfad_fcs_stop(bfad);
|
|
break;
|
|
|
|
default:
|
|
bfa_sm_fault(bfad, event);
|
|
}
|
|
}
|
|
|
|
static void
|
|
bfad_sm_fcs_exit(struct bfad_s *bfad, enum bfad_sm_event event)
|
|
{
|
|
bfa_trc(bfad, event);
|
|
|
|
switch (event) {
|
|
case BFAD_E_FCS_EXIT_COMP:
|
|
bfa_sm_set_state(bfad, bfad_sm_stopping);
|
|
bfad_stop(bfad);
|
|
break;
|
|
|
|
default:
|
|
bfa_sm_fault(bfad, event);
|
|
}
|
|
}
|
|
|
|
static void
|
|
bfad_sm_stopping(struct bfad_s *bfad, enum bfad_sm_event event)
|
|
{
|
|
bfa_trc(bfad, event);
|
|
|
|
switch (event) {
|
|
case BFAD_E_EXIT_COMP:
|
|
bfa_sm_set_state(bfad, bfad_sm_uninit);
|
|
bfad_remove_intr(bfad);
|
|
del_timer_sync(&bfad->hal_tmo);
|
|
bfad_im_probe_undo(bfad);
|
|
bfad->bfad_flags &= ~BFAD_FC4_PROBE_DONE;
|
|
bfad_uncfg_pport(bfad);
|
|
break;
|
|
|
|
default:
|
|
bfa_sm_fault(bfad, event);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* BFA callbacks
|
|
*/
|
|
void
|
|
bfad_hcb_comp(void *arg, bfa_status_t status)
|
|
{
|
|
struct bfad_hal_comp *fcomp = (struct bfad_hal_comp *)arg;
|
|
|
|
fcomp->status = status;
|
|
complete(&fcomp->comp);
|
|
}
|
|
|
|
/*
|
|
* bfa_init callback
|
|
*/
|
|
void
|
|
bfa_cb_init(void *drv, bfa_status_t init_status)
|
|
{
|
|
struct bfad_s *bfad = drv;
|
|
|
|
if (init_status == BFA_STATUS_OK) {
|
|
bfad->bfad_flags |= BFAD_HAL_INIT_DONE;
|
|
|
|
/*
|
|
* If BFAD_HAL_INIT_FAIL flag is set:
|
|
* Wake up the kernel thread to start
|
|
* the bfad operations after HAL init done
|
|
*/
|
|
if ((bfad->bfad_flags & BFAD_HAL_INIT_FAIL)) {
|
|
bfad->bfad_flags &= ~BFAD_HAL_INIT_FAIL;
|
|
wake_up_process(bfad->bfad_tsk);
|
|
}
|
|
}
|
|
|
|
complete(&bfad->comp);
|
|
}
|
|
|
|
/*
|
|
* BFA_FCS callbacks
|
|
*/
|
|
struct bfad_port_s *
|
|
bfa_fcb_lport_new(struct bfad_s *bfad, struct bfa_fcs_lport_s *port,
|
|
enum bfa_lport_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;
|
|
|
|
if (roles & BFA_LPORT_ROLE_FCP_IM) {
|
|
rc = bfad_im_port_new(bfad, port_drv);
|
|
if (rc != BFA_STATUS_OK) {
|
|
bfad_im_port_delete(bfad, port_drv);
|
|
port_drv = NULL;
|
|
}
|
|
}
|
|
|
|
return port_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 PBC VPORT Create
|
|
*/
|
|
void
|
|
bfa_fcb_pbc_vport_create(struct bfad_s *bfad, struct bfi_pbc_vport_s pbc_vport)
|
|
{
|
|
|
|
struct bfa_lport_cfg_s port_cfg = {0};
|
|
struct bfad_vport_s *vport;
|
|
int rc;
|
|
|
|
vport = kzalloc(sizeof(struct bfad_vport_s), GFP_ATOMIC);
|
|
if (!vport) {
|
|
bfa_trc(bfad, 0);
|
|
return;
|
|
}
|
|
|
|
vport->drv_port.bfad = bfad;
|
|
port_cfg.roles = BFA_LPORT_ROLE_FCP_IM;
|
|
port_cfg.pwwn = pbc_vport.vp_pwwn;
|
|
port_cfg.nwwn = pbc_vport.vp_nwwn;
|
|
port_cfg.preboot_vp = BFA_TRUE;
|
|
|
|
rc = bfa_fcs_pbc_vport_create(&vport->fcs_vport, &bfad->bfa_fcs, 0,
|
|
&port_cfg, vport);
|
|
|
|
if (rc != BFA_STATUS_OK) {
|
|
bfa_trc(bfad, 0);
|
|
return;
|
|
}
|
|
|
|
list_add_tail(&vport->list_entry, &bfad->pbc_vport_list);
|
|
}
|
|
|
|
void
|
|
bfad_hal_mem_release(struct bfad_s *bfad)
|
|
{
|
|
struct bfa_meminfo_s *hal_meminfo = &bfad->meminfo;
|
|
struct bfa_mem_dma_s *dma_info, *dma_elem;
|
|
struct bfa_mem_kva_s *kva_info, *kva_elem;
|
|
struct list_head *dm_qe, *km_qe;
|
|
|
|
dma_info = &hal_meminfo->dma_info;
|
|
kva_info = &hal_meminfo->kva_info;
|
|
|
|
/* Iterate through the KVA meminfo queue */
|
|
list_for_each(km_qe, &kva_info->qe) {
|
|
kva_elem = (struct bfa_mem_kva_s *) km_qe;
|
|
vfree(kva_elem->kva);
|
|
}
|
|
|
|
/* Iterate through the DMA meminfo queue */
|
|
list_for_each(dm_qe, &dma_info->qe) {
|
|
dma_elem = (struct bfa_mem_dma_s *) dm_qe;
|
|
dma_free_coherent(&bfad->pcidev->dev,
|
|
dma_elem->mem_len, dma_elem->kva,
|
|
(dma_addr_t) dma_elem->dma);
|
|
}
|
|
|
|
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 && num_fcxps <= BFA_FCXP_MAX)
|
|
bfa_cfg->fwcfg.num_fcxp_reqs = num_fcxps;
|
|
if (num_ufbufs > 0 && num_ufbufs <= BFA_UF_MAX)
|
|
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 && num_sgpgs <= BFA_SGPG_MAX)
|
|
bfa_cfg->drvcfg.num_sgpgs = num_sgpgs;
|
|
|
|
/*
|
|
* populate the hal values back to the driver for sysfs use.
|
|
* otherwise, the default values will be shown as 0 in sysfs
|
|
*/
|
|
num_rports = bfa_cfg->fwcfg.num_rports;
|
|
num_ios = bfa_cfg->fwcfg.num_ioim_reqs;
|
|
num_tms = bfa_cfg->fwcfg.num_tskim_reqs;
|
|
num_fcxps = bfa_cfg->fwcfg.num_fcxp_reqs;
|
|
num_ufbufs = bfa_cfg->fwcfg.num_uf_bufs;
|
|
reqq_size = bfa_cfg->drvcfg.num_reqq_elems;
|
|
rspq_size = bfa_cfg->drvcfg.num_rspq_elems;
|
|
num_sgpgs = bfa_cfg->drvcfg.num_sgpgs;
|
|
}
|
|
|
|
bfa_status_t
|
|
bfad_hal_mem_alloc(struct bfad_s *bfad)
|
|
{
|
|
struct bfa_meminfo_s *hal_meminfo = &bfad->meminfo;
|
|
struct bfa_mem_dma_s *dma_info, *dma_elem;
|
|
struct bfa_mem_kva_s *kva_info, *kva_elem;
|
|
struct list_head *dm_qe, *km_qe;
|
|
bfa_status_t rc = BFA_STATUS_OK;
|
|
dma_addr_t phys_addr;
|
|
|
|
bfa_cfg_get_default(&bfad->ioc_cfg);
|
|
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, &bfad->bfa);
|
|
|
|
dma_info = &hal_meminfo->dma_info;
|
|
kva_info = &hal_meminfo->kva_info;
|
|
|
|
/* Iterate through the KVA meminfo queue */
|
|
list_for_each(km_qe, &kva_info->qe) {
|
|
kva_elem = (struct bfa_mem_kva_s *) km_qe;
|
|
kva_elem->kva = vmalloc(kva_elem->mem_len);
|
|
if (kva_elem->kva == NULL) {
|
|
bfad_hal_mem_release(bfad);
|
|
rc = BFA_STATUS_ENOMEM;
|
|
goto ext;
|
|
}
|
|
memset(kva_elem->kva, 0, kva_elem->mem_len);
|
|
}
|
|
|
|
/* Iterate through the DMA meminfo queue */
|
|
list_for_each(dm_qe, &dma_info->qe) {
|
|
dma_elem = (struct bfa_mem_dma_s *) dm_qe;
|
|
dma_elem->kva = dma_alloc_coherent(&bfad->pcidev->dev,
|
|
dma_elem->mem_len,
|
|
&phys_addr, GFP_KERNEL);
|
|
if (dma_elem->kva == NULL) {
|
|
bfad_hal_mem_release(bfad);
|
|
rc = BFA_STATUS_ENOMEM;
|
|
goto ext;
|
|
}
|
|
dma_elem->dma = phys_addr;
|
|
memset(dma_elem->kva, 0, dma_elem->mem_len);
|
|
}
|
|
ext:
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* Create a vport under a vf.
|
|
*/
|
|
bfa_status_t
|
|
bfad_vport_create(struct bfad_s *bfad, u16 vf_id,
|
|
struct bfa_lport_cfg_s *port_cfg, struct device *dev)
|
|
{
|
|
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_LPORT_ROLE_FCP_IM) {
|
|
rc = bfad_im_scsi_host_alloc(bfad, vport->drv_port.im_port,
|
|
dev);
|
|
if (rc != BFA_STATUS_OK)
|
|
goto ext_free_fcs_vport;
|
|
}
|
|
|
|
spin_lock_irqsave(&bfad->bfad_lock, flags);
|
|
bfa_fcs_vport_start(&vport->fcs_vport);
|
|
list_add_tail(&vport->list_entry, &bfad->vport_list);
|
|
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;
|
|
}
|
|
|
|
void
|
|
bfad_bfa_tmo(struct timer_list *t)
|
|
{
|
|
struct bfad_s *bfad = from_timer(bfad, t, hal_tmo);
|
|
unsigned long flags;
|
|
struct list_head doneq;
|
|
|
|
spin_lock_irqsave(&bfad->bfad_lock, flags);
|
|
|
|
bfa_timer_beat(&bfad->bfa.timer_mod);
|
|
|
|
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)
|
|
{
|
|
timer_setup(&bfad->hal_tmo, bfad_bfa_tmo, 0);
|
|
|
|
mod_timer(&bfad->hal_tmo,
|
|
jiffies + msecs_to_jiffies(BFA_TIMER_FREQ));
|
|
}
|
|
|
|
int
|
|
bfad_pci_init(struct pci_dev *pdev, struct bfad_s *bfad)
|
|
{
|
|
int rc = -ENODEV;
|
|
|
|
if (pci_enable_device(pdev)) {
|
|
printk(KERN_ERR "pci_enable_device fail %p\n", pdev);
|
|
goto out;
|
|
}
|
|
|
|
if (pci_request_regions(pdev, BFAD_DRIVER_NAME))
|
|
goto out_disable_device;
|
|
|
|
pci_set_master(pdev);
|
|
|
|
|
|
if ((pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) != 0) ||
|
|
(pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)) != 0)) {
|
|
if ((pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0) ||
|
|
(pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)) != 0)) {
|
|
printk(KERN_ERR "pci_set_dma_mask fail %p\n", pdev);
|
|
goto out_release_region;
|
|
}
|
|
}
|
|
|
|
/* Enable PCIE Advanced Error Recovery (AER) if kernel supports */
|
|
pci_enable_pcie_error_reporting(pdev);
|
|
|
|
bfad->pci_bar0_kva = pci_iomap(pdev, 0, pci_resource_len(pdev, 0));
|
|
bfad->pci_bar2_kva = pci_iomap(pdev, 2, pci_resource_len(pdev, 2));
|
|
|
|
if (bfad->pci_bar0_kva == NULL) {
|
|
printk(KERN_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->hal_pcidev.ssid = pdev->subsystem_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;
|
|
|
|
/* Adjust PCIe Maximum Read Request Size */
|
|
if (pci_is_pcie(pdev) && pcie_max_read_reqsz) {
|
|
if (pcie_max_read_reqsz >= 128 &&
|
|
pcie_max_read_reqsz <= 4096 &&
|
|
is_power_of_2(pcie_max_read_reqsz)) {
|
|
int max_rq = pcie_get_readrq(pdev);
|
|
printk(KERN_WARNING "BFA[%s]: "
|
|
"pcie_max_read_request_size is %d, "
|
|
"reset to %d\n", bfad->pci_name, max_rq,
|
|
pcie_max_read_reqsz);
|
|
pcie_set_readrq(pdev, pcie_max_read_reqsz);
|
|
} else {
|
|
printk(KERN_WARNING "BFA[%s]: invalid "
|
|
"pcie_max_read_request_size %d ignored\n",
|
|
bfad->pci_name, pcie_max_read_reqsz);
|
|
}
|
|
}
|
|
|
|
pci_save_state(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_iounmap(pdev, bfad->pci_bar0_kva);
|
|
pci_iounmap(pdev, bfad->pci_bar2_kva);
|
|
pci_release_regions(pdev);
|
|
/* Disable PCIE Advanced Error Recovery (AER) */
|
|
pci_disable_pcie_error_reporting(pdev);
|
|
pci_disable_device(pdev);
|
|
}
|
|
|
|
bfa_status_t
|
|
bfad_drv_init(struct bfad_s *bfad)
|
|
{
|
|
bfa_status_t rc;
|
|
unsigned long flags;
|
|
|
|
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 QLogic BR-series HBA ports. System may need more memory.\n");
|
|
return BFA_STATUS_FAILED;
|
|
}
|
|
|
|
bfad->bfa.trcmod = bfad->trcmod;
|
|
bfad->bfa.plog = &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);
|
|
|
|
/* FCS INIT */
|
|
spin_lock_irqsave(&bfad->bfad_lock, flags);
|
|
bfad->bfa_fcs.trcmod = bfad->trcmod;
|
|
bfa_fcs_attach(&bfad->bfa_fcs, &bfad->bfa, bfad, BFA_FALSE);
|
|
bfad->bfa_fcs.fdmi_enabled = fdmi_enable;
|
|
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
|
|
|
|
bfad->bfad_flags |= BFAD_DRV_INIT_DONE;
|
|
|
|
return BFA_STATUS_OK;
|
|
}
|
|
|
|
void
|
|
bfad_drv_uninit(struct bfad_s *bfad)
|
|
{
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&bfad->bfad_lock, flags);
|
|
init_completion(&bfad->comp);
|
|
bfa_iocfc_stop(&bfad->bfa);
|
|
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
|
|
wait_for_completion(&bfad->comp);
|
|
|
|
del_timer_sync(&bfad->hal_tmo);
|
|
bfa_isr_disable(&bfad->bfa);
|
|
bfa_detach(&bfad->bfa);
|
|
bfad_remove_intr(bfad);
|
|
bfad_hal_mem_release(bfad);
|
|
|
|
bfad->bfad_flags &= ~BFAD_DRV_INIT_DONE;
|
|
}
|
|
|
|
void
|
|
bfad_drv_start(struct bfad_s *bfad)
|
|
{
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&bfad->bfad_lock, flags);
|
|
bfa_iocfc_start(&bfad->bfa);
|
|
bfa_fcs_pbc_vport_init(&bfad->bfa_fcs);
|
|
bfa_fcs_fabric_modstart(&bfad->bfa_fcs);
|
|
bfad->bfad_flags |= BFAD_HAL_START_DONE;
|
|
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
|
|
|
|
if (bfad->im)
|
|
flush_workqueue(bfad->im->drv_workq);
|
|
}
|
|
|
|
void
|
|
bfad_fcs_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);
|
|
|
|
bfa_sm_send_event(bfad, BFAD_E_FCS_EXIT_COMP);
|
|
}
|
|
|
|
void
|
|
bfad_stop(struct bfad_s *bfad)
|
|
{
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&bfad->bfad_lock, flags);
|
|
init_completion(&bfad->comp);
|
|
bfa_iocfc_stop(&bfad->bfa);
|
|
bfad->bfad_flags &= ~BFAD_HAL_START_DONE;
|
|
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
|
|
wait_for_completion(&bfad->comp);
|
|
|
|
bfa_sm_send_event(bfad, BFAD_E_EXIT_COMP);
|
|
}
|
|
|
|
bfa_status_t
|
|
bfad_cfg_pport(struct bfad_s *bfad, enum bfa_lport_role role)
|
|
{
|
|
int rc = BFA_STATUS_OK;
|
|
|
|
/* Allocate scsi_host for the physical port */
|
|
if ((supported_fc4s & BFA_LPORT_ROLE_FCP_IM) &&
|
|
(role & BFA_LPORT_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,
|
|
&bfad->pcidev->dev);
|
|
if (rc != BFA_STATUS_OK)
|
|
goto out;
|
|
|
|
bfad->pport.roles |= BFA_LPORT_ROLE_FCP_IM;
|
|
}
|
|
|
|
bfad->bfad_flags |= BFAD_CFG_PPORT_DONE;
|
|
|
|
out:
|
|
return rc;
|
|
}
|
|
|
|
void
|
|
bfad_uncfg_pport(struct bfad_s *bfad)
|
|
{
|
|
if ((supported_fc4s & BFA_LPORT_ROLE_FCP_IM) &&
|
|
(bfad->pport.roles & BFA_LPORT_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_LPORT_ROLE_FCP_IM;
|
|
}
|
|
|
|
bfad->bfad_flags &= ~BFAD_CFG_PPORT_DONE;
|
|
}
|
|
|
|
bfa_status_t
|
|
bfad_start_ops(struct bfad_s *bfad) {
|
|
|
|
int retval;
|
|
unsigned long flags;
|
|
struct bfad_vport_s *vport, *vport_new;
|
|
struct bfa_fcs_driver_info_s driver_info;
|
|
|
|
/* Limit min/max. xfer size to [64k-32MB] */
|
|
if (max_xfer_size < BFAD_MIN_SECTORS >> 1)
|
|
max_xfer_size = BFAD_MIN_SECTORS >> 1;
|
|
if (max_xfer_size > BFAD_MAX_SECTORS >> 1)
|
|
max_xfer_size = BFAD_MAX_SECTORS >> 1;
|
|
|
|
/* Fill the driver_info info to fcs*/
|
|
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 driver info init */
|
|
spin_lock_irqsave(&bfad->bfad_lock, flags);
|
|
bfa_fcs_driver_info_init(&bfad->bfa_fcs, &driver_info);
|
|
|
|
if (bfad->bfad_flags & BFAD_CFG_PPORT_DONE)
|
|
bfa_fcs_update_cfg(&bfad->bfa_fcs);
|
|
else
|
|
bfa_fcs_init(&bfad->bfa_fcs);
|
|
|
|
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
|
|
|
|
if (!(bfad->bfad_flags & BFAD_CFG_PPORT_DONE)) {
|
|
retval = bfad_cfg_pport(bfad, BFA_LPORT_ROLE_FCP_IM);
|
|
if (retval != BFA_STATUS_OK)
|
|
return BFA_STATUS_FAILED;
|
|
}
|
|
|
|
/* Setup fc host fixed attribute if the lk supports */
|
|
bfad_fc_host_init(bfad->pport.im_port);
|
|
|
|
/* BFAD level FC4 IM specific resource allocation */
|
|
retval = bfad_im_probe(bfad);
|
|
if (retval != BFA_STATUS_OK) {
|
|
printk(KERN_WARNING "bfad_im_probe failed\n");
|
|
if (bfa_sm_cmp_state(bfad, bfad_sm_initializing))
|
|
bfa_sm_set_state(bfad, bfad_sm_failed);
|
|
return BFA_STATUS_FAILED;
|
|
} else
|
|
bfad->bfad_flags |= BFAD_FC4_PROBE_DONE;
|
|
|
|
bfad_drv_start(bfad);
|
|
|
|
/* Complete pbc vport create */
|
|
list_for_each_entry_safe(vport, vport_new, &bfad->pbc_vport_list,
|
|
list_entry) {
|
|
struct fc_vport_identifiers vid;
|
|
struct fc_vport *fc_vport;
|
|
char pwwn_buf[BFA_STRING_32];
|
|
|
|
memset(&vid, 0, sizeof(vid));
|
|
vid.roles = FC_PORT_ROLE_FCP_INITIATOR;
|
|
vid.vport_type = FC_PORTTYPE_NPIV;
|
|
vid.disable = false;
|
|
vid.node_name = wwn_to_u64((u8 *)
|
|
(&((vport->fcs_vport).lport.port_cfg.nwwn)));
|
|
vid.port_name = wwn_to_u64((u8 *)
|
|
(&((vport->fcs_vport).lport.port_cfg.pwwn)));
|
|
fc_vport = fc_vport_create(bfad->pport.im_port->shost, 0, &vid);
|
|
if (!fc_vport) {
|
|
wwn2str(pwwn_buf, vid.port_name);
|
|
printk(KERN_WARNING "bfad%d: failed to create pbc vport"
|
|
" %s\n", bfad->inst_no, pwwn_buf);
|
|
}
|
|
list_del(&vport->list_entry);
|
|
kfree(vport);
|
|
}
|
|
|
|
/*
|
|
* If bfa_linkup_delay is set to -1 default; try to retrive the
|
|
* value using the bfad_get_linkup_delay(); else use the
|
|
* passed in module param value as the bfa_linkup_delay.
|
|
*/
|
|
if (bfa_linkup_delay < 0) {
|
|
bfa_linkup_delay = bfad_get_linkup_delay(bfad);
|
|
bfad_rport_online_wait(bfad);
|
|
bfa_linkup_delay = -1;
|
|
} else
|
|
bfad_rport_online_wait(bfad);
|
|
|
|
BFA_LOG(KERN_INFO, bfad, bfa_log_level, "bfa device claimed\n");
|
|
|
|
return BFA_STATUS_OK;
|
|
}
|
|
|
|
int
|
|
bfad_worker(void *ptr)
|
|
{
|
|
struct bfad_s *bfad = ptr;
|
|
unsigned long flags;
|
|
|
|
if (kthread_should_stop())
|
|
return 0;
|
|
|
|
/* Send event BFAD_E_INIT_SUCCESS */
|
|
bfa_sm_send_event(bfad, BFAD_E_INIT_SUCCESS);
|
|
|
|
spin_lock_irqsave(&bfad->bfad_lock, flags);
|
|
bfad->bfad_tsk = NULL;
|
|
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* BFA driver interrupt functions
|
|
*/
|
|
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);
|
|
}
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
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++) {
|
|
sprintf(bfad->msix_tab[i].name, "bfa-%s-%s",
|
|
bfad->pci_name,
|
|
((bfa_asic_id_cb(bfad->hal_pcidev.device_id)) ?
|
|
msix_name_cb[i] : msix_name_ct[i]));
|
|
|
|
error = request_irq(bfad->msix_tab[i].msix.vector,
|
|
(irq_handler_t) bfad_msix, 0,
|
|
bfad->msix_tab[i].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]);
|
|
|
|
bfad->bfad_flags &= ~BFAD_MSIX_ON;
|
|
pci_disable_msix(bfad->pcidev);
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Setup MSIX based interrupt.
|
|
*/
|
|
int
|
|
bfad_setup_intr(struct bfad_s *bfad)
|
|
{
|
|
int error;
|
|
u32 mask = 0, i, num_bit = 0, max_bit = 0;
|
|
struct msix_entry msix_entries[MAX_MSIX_ENTRY];
|
|
struct pci_dev *pdev = bfad->pcidev;
|
|
u16 reg;
|
|
|
|
/* 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 ((bfa_asic_id_ctc(pdev->device) && !msix_disable_ct) ||
|
|
(bfa_asic_id_cb(pdev->device) && !msix_disable_cb)) {
|
|
|
|
error = pci_enable_msix_exact(bfad->pcidev,
|
|
msix_entries, bfad->nvec);
|
|
/* In CT1 & CT2, try to allocate just one vector */
|
|
if (error == -ENOSPC && bfa_asic_id_ctc(pdev->device)) {
|
|
printk(KERN_WARNING "bfa %s: trying one msix "
|
|
"vector failed to allocate %d[%d]\n",
|
|
bfad->pci_name, bfad->nvec, error);
|
|
bfad->nvec = 1;
|
|
error = pci_enable_msix_exact(bfad->pcidev,
|
|
msix_entries, 1);
|
|
}
|
|
|
|
if (error) {
|
|
printk(KERN_WARNING "bfad%d: "
|
|
"pci_enable_msix_exact failed (%d), "
|
|
"use line based.\n",
|
|
bfad->inst_no, error);
|
|
goto line_based;
|
|
}
|
|
|
|
/* Disable INTX in MSI-X mode */
|
|
pci_read_config_word(pdev, PCI_COMMAND, ®);
|
|
|
|
if (!(reg & PCI_COMMAND_INTX_DISABLE))
|
|
pci_write_config_word(pdev, PCI_COMMAND,
|
|
reg | PCI_COMMAND_INTX_DISABLE);
|
|
|
|
/* 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 0;
|
|
}
|
|
|
|
line_based:
|
|
error = request_irq(bfad->pcidev->irq, (irq_handler_t)bfad_intx,
|
|
BFAD_IRQ_FLAGS, BFAD_DRIVER_NAME, bfad);
|
|
if (error)
|
|
return error;
|
|
|
|
bfad->bfad_flags |= BFAD_INTX_ON;
|
|
|
|
return 0;
|
|
}
|
|
|
|
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 if (bfad->bfad_flags & BFAD_INTX_ON) {
|
|
free_irq(bfad->pcidev->irq, bfad);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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, i;
|
|
|
|
/* For single port cards - only claim function 0 */
|
|
if ((pdev->device == BFA_PCI_DEVICE_ID_FC_8G1P) &&
|
|
(PCI_FUNC(pdev->devfn) != 0))
|
|
return -ENODEV;
|
|
|
|
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;
|
|
}
|
|
|
|
/* TRACE INIT */
|
|
bfa_trc_init(bfad->trcmod);
|
|
bfa_trc(bfad, bfad_inst);
|
|
|
|
/* AEN INIT */
|
|
INIT_LIST_HEAD(&bfad->free_aen_q);
|
|
INIT_LIST_HEAD(&bfad->active_aen_q);
|
|
for (i = 0; i < BFA_AEN_MAX_ENTRY; i++)
|
|
list_add_tail(&bfad->aen_list[i].qe, &bfad->free_aen_q);
|
|
|
|
if (!(bfad_load_fwimg(pdev))) {
|
|
kfree(bfad->trcmod);
|
|
goto out_alloc_trace_failure;
|
|
}
|
|
|
|
retval = bfad_pci_init(pdev, bfad);
|
|
if (retval) {
|
|
printk(KERN_WARNING "bfad_pci_init failure!\n");
|
|
error = retval;
|
|
goto out_pci_init_failure;
|
|
}
|
|
|
|
mutex_lock(&bfad_mutex);
|
|
bfad->inst_no = bfad_inst++;
|
|
list_add_tail(&bfad->list_entry, &bfad_list);
|
|
mutex_unlock(&bfad_mutex);
|
|
|
|
/* Initializing the state machine: State set to uninit */
|
|
bfa_sm_set_state(bfad, bfad_sm_uninit);
|
|
|
|
spin_lock_init(&bfad->bfad_lock);
|
|
spin_lock_init(&bfad->bfad_aen_spinlock);
|
|
|
|
pci_set_drvdata(pdev, bfad);
|
|
|
|
bfad->ref_count = 0;
|
|
bfad->pport.bfad = bfad;
|
|
INIT_LIST_HEAD(&bfad->pbc_vport_list);
|
|
INIT_LIST_HEAD(&bfad->vport_list);
|
|
|
|
/* Setup the debugfs node for this bfad */
|
|
if (bfa_debugfs_enable)
|
|
bfad_debugfs_init(&bfad->pport);
|
|
|
|
retval = bfad_drv_init(bfad);
|
|
if (retval != BFA_STATUS_OK)
|
|
goto out_drv_init_failure;
|
|
|
|
bfa_sm_send_event(bfad, BFAD_E_CREATE);
|
|
|
|
if (bfa_sm_cmp_state(bfad, bfad_sm_uninit))
|
|
goto out_bfad_sm_failure;
|
|
|
|
return 0;
|
|
|
|
out_bfad_sm_failure:
|
|
bfad_hal_mem_release(bfad);
|
|
out_drv_init_failure:
|
|
/* Remove the debugfs node for this bfad */
|
|
kfree(bfad->regdata);
|
|
bfad_debugfs_exit(&bfad->pport);
|
|
mutex_lock(&bfad_mutex);
|
|
bfad_inst--;
|
|
list_del(&bfad->list_entry);
|
|
mutex_unlock(&bfad_mutex);
|
|
bfad_pci_uninit(pdev, bfad);
|
|
out_pci_init_failure:
|
|
kfree(bfad->trcmod);
|
|
out_alloc_trace_failure:
|
|
kfree(bfad);
|
|
out:
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* PCI remove entry.
|
|
*/
|
|
void
|
|
bfad_pci_remove(struct pci_dev *pdev)
|
|
{
|
|
struct bfad_s *bfad = pci_get_drvdata(pdev);
|
|
unsigned long flags;
|
|
|
|
bfa_trc(bfad, bfad->inst_no);
|
|
|
|
spin_lock_irqsave(&bfad->bfad_lock, flags);
|
|
if (bfad->bfad_tsk != NULL) {
|
|
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
|
|
kthread_stop(bfad->bfad_tsk);
|
|
} else {
|
|
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
|
|
}
|
|
|
|
/* Send Event BFAD_E_STOP */
|
|
bfa_sm_send_event(bfad, BFAD_E_STOP);
|
|
|
|
/* Driver detach and dealloc mem */
|
|
spin_lock_irqsave(&bfad->bfad_lock, flags);
|
|
bfa_detach(&bfad->bfa);
|
|
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
|
|
bfad_hal_mem_release(bfad);
|
|
|
|
/* Remove the debugfs node for this bfad */
|
|
kfree(bfad->regdata);
|
|
bfad_debugfs_exit(&bfad->pport);
|
|
|
|
/* Cleaning the BFAD instance */
|
|
mutex_lock(&bfad_mutex);
|
|
bfad_inst--;
|
|
list_del(&bfad->list_entry);
|
|
mutex_unlock(&bfad_mutex);
|
|
bfad_pci_uninit(pdev, bfad);
|
|
|
|
kfree(bfad->trcmod);
|
|
kfree(bfad);
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
unsigned long flags;
|
|
pci_ers_result_t ret = PCI_ERS_RESULT_NONE;
|
|
|
|
dev_printk(KERN_ERR, &pdev->dev,
|
|
"error detected state: %d - flags: 0x%x\n",
|
|
state, bfad->bfad_flags);
|
|
|
|
switch (state) {
|
|
case pci_channel_io_normal: /* non-fatal error */
|
|
spin_lock_irqsave(&bfad->bfad_lock, flags);
|
|
bfad->bfad_flags &= ~BFAD_EEH_BUSY;
|
|
/* Suspend/fail all bfa operations */
|
|
bfa_ioc_suspend(&bfad->bfa.ioc);
|
|
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
|
|
del_timer_sync(&bfad->hal_tmo);
|
|
ret = PCI_ERS_RESULT_CAN_RECOVER;
|
|
break;
|
|
case pci_channel_io_frozen: /* fatal error */
|
|
init_completion(&bfad->comp);
|
|
spin_lock_irqsave(&bfad->bfad_lock, flags);
|
|
bfad->bfad_flags |= BFAD_EEH_BUSY;
|
|
/* Suspend/fail all bfa operations */
|
|
bfa_ioc_suspend(&bfad->bfa.ioc);
|
|
bfa_fcs_stop(&bfad->bfa_fcs);
|
|
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
|
|
wait_for_completion(&bfad->comp);
|
|
|
|
bfad_remove_intr(bfad);
|
|
del_timer_sync(&bfad->hal_tmo);
|
|
pci_disable_device(pdev);
|
|
ret = PCI_ERS_RESULT_NEED_RESET;
|
|
break;
|
|
case pci_channel_io_perm_failure: /* PCI Card is DEAD */
|
|
spin_lock_irqsave(&bfad->bfad_lock, flags);
|
|
bfad->bfad_flags |= BFAD_EEH_BUSY |
|
|
BFAD_EEH_PCI_CHANNEL_IO_PERM_FAILURE;
|
|
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
|
|
|
|
/* If the error_detected handler is called with the reason
|
|
* pci_channel_io_perm_failure - it will subsequently call
|
|
* pci_remove() entry point to remove the pci device from the
|
|
* system - So defer the cleanup to pci_remove(); cleaning up
|
|
* here causes inconsistent state during pci_remove().
|
|
*/
|
|
ret = PCI_ERS_RESULT_DISCONNECT;
|
|
break;
|
|
default:
|
|
WARN_ON(1);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
restart_bfa(struct bfad_s *bfad)
|
|
{
|
|
unsigned long flags;
|
|
struct pci_dev *pdev = bfad->pcidev;
|
|
|
|
bfa_attach(&bfad->bfa, bfad, &bfad->ioc_cfg,
|
|
&bfad->meminfo, &bfad->hal_pcidev);
|
|
|
|
/* Enable Interrupt and wait bfa_init completion */
|
|
if (bfad_setup_intr(bfad)) {
|
|
dev_printk(KERN_WARNING, &pdev->dev,
|
|
"%s: bfad_setup_intr failed\n", bfad->pci_name);
|
|
bfa_sm_send_event(bfad, BFAD_E_INIT_FAILED);
|
|
return -1;
|
|
}
|
|
|
|
init_completion(&bfad->comp);
|
|
spin_lock_irqsave(&bfad->bfad_lock, flags);
|
|
bfa_iocfc_init(&bfad->bfa);
|
|
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
|
|
|
|
/* Set up interrupt handler for each vectors */
|
|
if ((bfad->bfad_flags & BFAD_MSIX_ON) &&
|
|
bfad_install_msix_handler(bfad))
|
|
dev_printk(KERN_WARNING, &pdev->dev,
|
|
"%s: install_msix failed.\n", bfad->pci_name);
|
|
|
|
bfad_init_timer(bfad);
|
|
wait_for_completion(&bfad->comp);
|
|
bfad_drv_start(bfad);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
u8 byte;
|
|
|
|
dev_printk(KERN_ERR, &pdev->dev,
|
|
"bfad_pci_slot_reset flags: 0x%x\n", bfad->bfad_flags);
|
|
|
|
if (pci_enable_device(pdev)) {
|
|
dev_printk(KERN_ERR, &pdev->dev, "Cannot re-enable "
|
|
"PCI device after reset.\n");
|
|
return PCI_ERS_RESULT_DISCONNECT;
|
|
}
|
|
|
|
pci_restore_state(pdev);
|
|
|
|
/*
|
|
* Read some byte (e.g. DMA max. payload size which can't
|
|
* be 0xff any time) to make sure - we did not hit another PCI error
|
|
* in the middle of recovery. If we did, then declare permanent failure.
|
|
*/
|
|
pci_read_config_byte(pdev, 0x68, &byte);
|
|
if (byte == 0xff) {
|
|
dev_printk(KERN_ERR, &pdev->dev,
|
|
"slot_reset failed ... got another PCI error !\n");
|
|
goto out_disable_device;
|
|
}
|
|
|
|
pci_save_state(pdev);
|
|
pci_set_master(pdev);
|
|
|
|
if (pci_set_dma_mask(bfad->pcidev, DMA_BIT_MASK(64)) != 0)
|
|
if (pci_set_dma_mask(bfad->pcidev, DMA_BIT_MASK(32)) != 0)
|
|
goto out_disable_device;
|
|
|
|
pci_cleanup_aer_uncorrect_error_status(pdev);
|
|
|
|
if (restart_bfa(bfad) == -1)
|
|
goto out_disable_device;
|
|
|
|
pci_enable_pcie_error_reporting(pdev);
|
|
dev_printk(KERN_WARNING, &pdev->dev,
|
|
"slot_reset completed flags: 0x%x!\n", bfad->bfad_flags);
|
|
|
|
return PCI_ERS_RESULT_RECOVERED;
|
|
|
|
out_disable_device:
|
|
pci_disable_device(pdev);
|
|
return PCI_ERS_RESULT_DISCONNECT;
|
|
}
|
|
|
|
static pci_ers_result_t
|
|
bfad_pci_mmio_enabled(struct pci_dev *pdev)
|
|
{
|
|
unsigned long flags;
|
|
struct bfad_s *bfad = pci_get_drvdata(pdev);
|
|
|
|
dev_printk(KERN_INFO, &pdev->dev, "mmio_enabled\n");
|
|
|
|
/* Fetch FW diagnostic information */
|
|
bfa_ioc_debug_save_ftrc(&bfad->bfa.ioc);
|
|
|
|
/* Cancel all pending IOs */
|
|
spin_lock_irqsave(&bfad->bfad_lock, flags);
|
|
init_completion(&bfad->comp);
|
|
bfa_fcs_stop(&bfad->bfa_fcs);
|
|
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
|
|
wait_for_completion(&bfad->comp);
|
|
|
|
bfad_remove_intr(bfad);
|
|
del_timer_sync(&bfad->hal_tmo);
|
|
pci_disable_device(pdev);
|
|
|
|
return PCI_ERS_RESULT_NEED_RESET;
|
|
}
|
|
|
|
static void
|
|
bfad_pci_resume(struct pci_dev *pdev)
|
|
{
|
|
unsigned long flags;
|
|
struct bfad_s *bfad = pci_get_drvdata(pdev);
|
|
|
|
dev_printk(KERN_WARNING, &pdev->dev, "resume\n");
|
|
|
|
/* wait until the link is online */
|
|
bfad_rport_online_wait(bfad);
|
|
|
|
spin_lock_irqsave(&bfad->bfad_lock, flags);
|
|
bfad->bfad_flags &= ~BFAD_EEH_BUSY;
|
|
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
|
|
}
|
|
|
|
struct pci_device_id bfad_id_table[] = {
|
|
{
|
|
.vendor = BFA_PCI_VENDOR_ID_BROCADE,
|
|
.device = BFA_PCI_DEVICE_ID_FC_8G2P,
|
|
.subvendor = PCI_ANY_ID,
|
|
.subdevice = PCI_ANY_ID,
|
|
},
|
|
{
|
|
.vendor = BFA_PCI_VENDOR_ID_BROCADE,
|
|
.device = BFA_PCI_DEVICE_ID_FC_8G1P,
|
|
.subvendor = PCI_ANY_ID,
|
|
.subdevice = PCI_ANY_ID,
|
|
},
|
|
{
|
|
.vendor = BFA_PCI_VENDOR_ID_BROCADE,
|
|
.device = BFA_PCI_DEVICE_ID_CT,
|
|
.subvendor = PCI_ANY_ID,
|
|
.subdevice = PCI_ANY_ID,
|
|
.class = (PCI_CLASS_SERIAL_FIBER << 8),
|
|
.class_mask = ~0,
|
|
},
|
|
{
|
|
.vendor = BFA_PCI_VENDOR_ID_BROCADE,
|
|
.device = BFA_PCI_DEVICE_ID_CT_FC,
|
|
.subvendor = PCI_ANY_ID,
|
|
.subdevice = PCI_ANY_ID,
|
|
.class = (PCI_CLASS_SERIAL_FIBER << 8),
|
|
.class_mask = ~0,
|
|
},
|
|
{
|
|
.vendor = BFA_PCI_VENDOR_ID_BROCADE,
|
|
.device = BFA_PCI_DEVICE_ID_CT2,
|
|
.subvendor = PCI_ANY_ID,
|
|
.subdevice = PCI_ANY_ID,
|
|
.class = (PCI_CLASS_SERIAL_FIBER << 8),
|
|
.class_mask = ~0,
|
|
},
|
|
|
|
{
|
|
.vendor = BFA_PCI_VENDOR_ID_BROCADE,
|
|
.device = BFA_PCI_DEVICE_ID_CT2_QUAD,
|
|
.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);
|
|
|
|
/*
|
|
* PCI error recovery handlers.
|
|
*/
|
|
static struct pci_error_handlers bfad_err_handler = {
|
|
.error_detected = bfad_pci_error_detected,
|
|
.slot_reset = bfad_pci_slot_reset,
|
|
.mmio_enabled = bfad_pci_mmio_enabled,
|
|
.resume = bfad_pci_resume,
|
|
};
|
|
|
|
static struct pci_driver bfad_pci_driver = {
|
|
.name = BFAD_DRIVER_NAME,
|
|
.id_table = bfad_id_table,
|
|
.probe = bfad_pci_probe,
|
|
.remove = bfad_pci_remove,
|
|
.err_handler = &bfad_err_handler,
|
|
};
|
|
|
|
/*
|
|
* Driver module init.
|
|
*/
|
|
static int __init
|
|
bfad_init(void)
|
|
{
|
|
int error = 0;
|
|
|
|
pr_info("QLogic BR-series BFA FC/FCOE SCSI driver - version: %s\n",
|
|
BFAD_DRIVER_VERSION);
|
|
|
|
if (num_sgpgs > 0)
|
|
num_sgpgs_parm = num_sgpgs;
|
|
|
|
error = bfad_im_module_init();
|
|
if (error) {
|
|
error = -ENOMEM;
|
|
printk(KERN_WARNING "bfad_im_module_init failure\n");
|
|
goto ext;
|
|
}
|
|
|
|
if (strcmp(FCPI_NAME, " fcpim") == 0)
|
|
supported_fc4s |= BFA_LPORT_ROLE_FCP_IM;
|
|
|
|
bfa_auto_recover = ioc_auto_recover;
|
|
bfa_fcs_rport_set_del_timeout(rport_del_timeout);
|
|
bfa_fcs_rport_set_max_logins(max_rport_logins);
|
|
|
|
error = pci_register_driver(&bfad_pci_driver);
|
|
if (error) {
|
|
printk(KERN_WARNING "pci_register_driver failure\n");
|
|
goto ext;
|
|
}
|
|
|
|
return 0;
|
|
|
|
ext:
|
|
bfad_im_module_exit();
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* Driver module exit.
|
|
*/
|
|
static void __exit
|
|
bfad_exit(void)
|
|
{
|
|
pci_unregister_driver(&bfad_pci_driver);
|
|
bfad_im_module_exit();
|
|
bfad_free_fwimg();
|
|
}
|
|
|
|
/* Firmware handling */
|
|
static void
|
|
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);
|
|
*bfi_image = NULL;
|
|
goto out;
|
|
}
|
|
|
|
*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 out;
|
|
}
|
|
|
|
memcpy(*bfi_image, fw->data, fw->size);
|
|
*bfi_image_size = fw->size/sizeof(u32);
|
|
out:
|
|
release_firmware(fw);
|
|
}
|
|
|
|
static u32 *
|
|
bfad_load_fwimg(struct pci_dev *pdev)
|
|
{
|
|
if (bfa_asic_id_ct2(pdev->device)) {
|
|
if (bfi_image_ct2_size == 0)
|
|
bfad_read_firmware(pdev, &bfi_image_ct2,
|
|
&bfi_image_ct2_size, BFAD_FW_FILE_CT2);
|
|
return bfi_image_ct2;
|
|
} else if (bfa_asic_id_ct(pdev->device)) {
|
|
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 (bfa_asic_id_cb(pdev->device)) {
|
|
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;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
bfad_free_fwimg(void)
|
|
{
|
|
if (bfi_image_ct2_size && bfi_image_ct2)
|
|
vfree(bfi_image_ct2);
|
|
if (bfi_image_ct_size && bfi_image_ct)
|
|
vfree(bfi_image_ct);
|
|
if (bfi_image_cb_size && bfi_image_cb)
|
|
vfree(bfi_image_cb);
|
|
}
|
|
|
|
module_init(bfad_init);
|
|
module_exit(bfad_exit);
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_DESCRIPTION("QLogic BR-series Fibre Channel HBA Driver" BFAD_PROTO_NAME);
|
|
MODULE_AUTHOR("QLogic Corporation");
|
|
MODULE_VERSION(BFAD_DRIVER_VERSION);
|