1089 lines
25 KiB
C
1089 lines
25 KiB
C
/*
|
|
* Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
|
|
* All rights reserved
|
|
* www.brocade.com
|
|
*
|
|
* Linux driver for Brocade Fibre Channel Host Bus Adapter.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License (GPL) Version 2 as
|
|
* published by the Free Software Foundation
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*/
|
|
|
|
#include <bfa.h>
|
|
#include <bfa_fcpim.h>
|
|
#include "bfa_fcpim_priv.h"
|
|
|
|
BFA_TRC_FILE(HAL, ITNIM);
|
|
|
|
#define BFA_ITNIM_FROM_TAG(_fcpim, _tag) \
|
|
((_fcpim)->itnim_arr + ((_tag) & ((_fcpim)->num_itnims - 1)))
|
|
|
|
#define bfa_fcpim_additn(__itnim) \
|
|
list_add_tail(&(__itnim)->qe, &(__itnim)->fcpim->itnim_q)
|
|
#define bfa_fcpim_delitn(__itnim) do { \
|
|
bfa_assert(bfa_q_is_on_q(&(__itnim)->fcpim->itnim_q, __itnim)); \
|
|
list_del(&(__itnim)->qe); \
|
|
bfa_assert(list_empty(&(__itnim)->io_q)); \
|
|
bfa_assert(list_empty(&(__itnim)->io_cleanup_q)); \
|
|
bfa_assert(list_empty(&(__itnim)->pending_q)); \
|
|
} while (0)
|
|
|
|
#define bfa_itnim_online_cb(__itnim) do { \
|
|
if ((__itnim)->bfa->fcs) \
|
|
bfa_cb_itnim_online((__itnim)->ditn); \
|
|
else { \
|
|
bfa_cb_queue((__itnim)->bfa, &(__itnim)->hcb_qe, \
|
|
__bfa_cb_itnim_online, (__itnim)); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define bfa_itnim_offline_cb(__itnim) do { \
|
|
if ((__itnim)->bfa->fcs) \
|
|
bfa_cb_itnim_offline((__itnim)->ditn); \
|
|
else { \
|
|
bfa_cb_queue((__itnim)->bfa, &(__itnim)->hcb_qe, \
|
|
__bfa_cb_itnim_offline, (__itnim)); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define bfa_itnim_sler_cb(__itnim) do { \
|
|
if ((__itnim)->bfa->fcs) \
|
|
bfa_cb_itnim_sler((__itnim)->ditn); \
|
|
else { \
|
|
bfa_cb_queue((__itnim)->bfa, &(__itnim)->hcb_qe, \
|
|
__bfa_cb_itnim_sler, (__itnim)); \
|
|
} \
|
|
} while (0)
|
|
|
|
/*
|
|
* forward declarations
|
|
*/
|
|
static void bfa_itnim_iocdisable_cleanup(struct bfa_itnim_s *itnim);
|
|
static bfa_boolean_t bfa_itnim_send_fwcreate(struct bfa_itnim_s *itnim);
|
|
static bfa_boolean_t bfa_itnim_send_fwdelete(struct bfa_itnim_s *itnim);
|
|
static void bfa_itnim_cleanp_comp(void *itnim_cbarg);
|
|
static void bfa_itnim_cleanup(struct bfa_itnim_s *itnim);
|
|
static void __bfa_cb_itnim_online(void *cbarg, bfa_boolean_t complete);
|
|
static void __bfa_cb_itnim_offline(void *cbarg, bfa_boolean_t complete);
|
|
static void __bfa_cb_itnim_sler(void *cbarg, bfa_boolean_t complete);
|
|
static void bfa_itnim_iotov_online(struct bfa_itnim_s *itnim);
|
|
static void bfa_itnim_iotov_cleanup(struct bfa_itnim_s *itnim);
|
|
static void bfa_itnim_iotov(void *itnim_arg);
|
|
static void bfa_itnim_iotov_start(struct bfa_itnim_s *itnim);
|
|
static void bfa_itnim_iotov_stop(struct bfa_itnim_s *itnim);
|
|
static void bfa_itnim_iotov_delete(struct bfa_itnim_s *itnim);
|
|
|
|
/**
|
|
* bfa_itnim_sm BFA itnim state machine
|
|
*/
|
|
|
|
|
|
enum bfa_itnim_event {
|
|
BFA_ITNIM_SM_CREATE = 1, /* itnim is created */
|
|
BFA_ITNIM_SM_ONLINE = 2, /* itnim is online */
|
|
BFA_ITNIM_SM_OFFLINE = 3, /* itnim is offline */
|
|
BFA_ITNIM_SM_FWRSP = 4, /* firmware response */
|
|
BFA_ITNIM_SM_DELETE = 5, /* deleting an existing itnim */
|
|
BFA_ITNIM_SM_CLEANUP = 6, /* IO cleanup completion */
|
|
BFA_ITNIM_SM_SLER = 7, /* second level error recovery */
|
|
BFA_ITNIM_SM_HWFAIL = 8, /* IOC h/w failure event */
|
|
BFA_ITNIM_SM_QRESUME = 9, /* queue space available */
|
|
};
|
|
|
|
static void bfa_itnim_sm_uninit(struct bfa_itnim_s *itnim,
|
|
enum bfa_itnim_event event);
|
|
static void bfa_itnim_sm_created(struct bfa_itnim_s *itnim,
|
|
enum bfa_itnim_event event);
|
|
static void bfa_itnim_sm_fwcreate(struct bfa_itnim_s *itnim,
|
|
enum bfa_itnim_event event);
|
|
static void bfa_itnim_sm_delete_pending(struct bfa_itnim_s *itnim,
|
|
enum bfa_itnim_event event);
|
|
static void bfa_itnim_sm_online(struct bfa_itnim_s *itnim,
|
|
enum bfa_itnim_event event);
|
|
static void bfa_itnim_sm_sler(struct bfa_itnim_s *itnim,
|
|
enum bfa_itnim_event event);
|
|
static void bfa_itnim_sm_cleanup_offline(struct bfa_itnim_s *itnim,
|
|
enum bfa_itnim_event event);
|
|
static void bfa_itnim_sm_cleanup_delete(struct bfa_itnim_s *itnim,
|
|
enum bfa_itnim_event event);
|
|
static void bfa_itnim_sm_fwdelete(struct bfa_itnim_s *itnim,
|
|
enum bfa_itnim_event event);
|
|
static void bfa_itnim_sm_offline(struct bfa_itnim_s *itnim,
|
|
enum bfa_itnim_event event);
|
|
static void bfa_itnim_sm_iocdisable(struct bfa_itnim_s *itnim,
|
|
enum bfa_itnim_event event);
|
|
static void bfa_itnim_sm_deleting(struct bfa_itnim_s *itnim,
|
|
enum bfa_itnim_event event);
|
|
static void bfa_itnim_sm_fwcreate_qfull(struct bfa_itnim_s *itnim,
|
|
enum bfa_itnim_event event);
|
|
static void bfa_itnim_sm_fwdelete_qfull(struct bfa_itnim_s *itnim,
|
|
enum bfa_itnim_event event);
|
|
static void bfa_itnim_sm_deleting_qfull(struct bfa_itnim_s *itnim,
|
|
enum bfa_itnim_event event);
|
|
|
|
/**
|
|
* Beginning/unallocated state - no events expected.
|
|
*/
|
|
static void
|
|
bfa_itnim_sm_uninit(struct bfa_itnim_s *itnim, enum bfa_itnim_event event)
|
|
{
|
|
bfa_trc(itnim->bfa, itnim->rport->rport_tag);
|
|
bfa_trc(itnim->bfa, event);
|
|
|
|
switch (event) {
|
|
case BFA_ITNIM_SM_CREATE:
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_created);
|
|
itnim->is_online = BFA_FALSE;
|
|
bfa_fcpim_additn(itnim);
|
|
break;
|
|
|
|
default:
|
|
bfa_assert(0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Beginning state, only online event expected.
|
|
*/
|
|
static void
|
|
bfa_itnim_sm_created(struct bfa_itnim_s *itnim, enum bfa_itnim_event event)
|
|
{
|
|
bfa_trc(itnim->bfa, itnim->rport->rport_tag);
|
|
bfa_trc(itnim->bfa, event);
|
|
|
|
switch (event) {
|
|
case BFA_ITNIM_SM_ONLINE:
|
|
if (bfa_itnim_send_fwcreate(itnim))
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_fwcreate);
|
|
else
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_fwcreate_qfull);
|
|
break;
|
|
|
|
case BFA_ITNIM_SM_DELETE:
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_uninit);
|
|
bfa_fcpim_delitn(itnim);
|
|
break;
|
|
|
|
case BFA_ITNIM_SM_HWFAIL:
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_iocdisable);
|
|
break;
|
|
|
|
default:
|
|
bfa_assert(0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Waiting for itnim create response from firmware.
|
|
*/
|
|
static void
|
|
bfa_itnim_sm_fwcreate(struct bfa_itnim_s *itnim, enum bfa_itnim_event event)
|
|
{
|
|
bfa_trc(itnim->bfa, itnim->rport->rport_tag);
|
|
bfa_trc(itnim->bfa, event);
|
|
|
|
switch (event) {
|
|
case BFA_ITNIM_SM_FWRSP:
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_online);
|
|
itnim->is_online = BFA_TRUE;
|
|
bfa_itnim_iotov_online(itnim);
|
|
bfa_itnim_online_cb(itnim);
|
|
break;
|
|
|
|
case BFA_ITNIM_SM_DELETE:
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_delete_pending);
|
|
break;
|
|
|
|
case BFA_ITNIM_SM_OFFLINE:
|
|
if (bfa_itnim_send_fwdelete(itnim))
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_fwdelete);
|
|
else
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_fwdelete_qfull);
|
|
break;
|
|
|
|
case BFA_ITNIM_SM_HWFAIL:
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_iocdisable);
|
|
break;
|
|
|
|
default:
|
|
bfa_assert(0);
|
|
}
|
|
}
|
|
|
|
static void
|
|
bfa_itnim_sm_fwcreate_qfull(struct bfa_itnim_s *itnim,
|
|
enum bfa_itnim_event event)
|
|
{
|
|
bfa_trc(itnim->bfa, itnim->rport->rport_tag);
|
|
bfa_trc(itnim->bfa, event);
|
|
|
|
switch (event) {
|
|
case BFA_ITNIM_SM_QRESUME:
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_fwcreate);
|
|
bfa_itnim_send_fwcreate(itnim);
|
|
break;
|
|
|
|
case BFA_ITNIM_SM_DELETE:
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_uninit);
|
|
bfa_reqq_wcancel(&itnim->reqq_wait);
|
|
bfa_fcpim_delitn(itnim);
|
|
break;
|
|
|
|
case BFA_ITNIM_SM_OFFLINE:
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_offline);
|
|
bfa_reqq_wcancel(&itnim->reqq_wait);
|
|
bfa_itnim_offline_cb(itnim);
|
|
break;
|
|
|
|
case BFA_ITNIM_SM_HWFAIL:
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_iocdisable);
|
|
bfa_reqq_wcancel(&itnim->reqq_wait);
|
|
break;
|
|
|
|
default:
|
|
bfa_assert(0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Waiting for itnim create response from firmware, a delete is pending.
|
|
*/
|
|
static void
|
|
bfa_itnim_sm_delete_pending(struct bfa_itnim_s *itnim,
|
|
enum bfa_itnim_event event)
|
|
{
|
|
bfa_trc(itnim->bfa, itnim->rport->rport_tag);
|
|
bfa_trc(itnim->bfa, event);
|
|
|
|
switch (event) {
|
|
case BFA_ITNIM_SM_FWRSP:
|
|
if (bfa_itnim_send_fwdelete(itnim))
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_deleting);
|
|
else
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_deleting_qfull);
|
|
break;
|
|
|
|
case BFA_ITNIM_SM_HWFAIL:
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_uninit);
|
|
bfa_fcpim_delitn(itnim);
|
|
break;
|
|
|
|
default:
|
|
bfa_assert(0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Online state - normal parking state.
|
|
*/
|
|
static void
|
|
bfa_itnim_sm_online(struct bfa_itnim_s *itnim, enum bfa_itnim_event event)
|
|
{
|
|
bfa_trc(itnim->bfa, itnim->rport->rport_tag);
|
|
bfa_trc(itnim->bfa, event);
|
|
|
|
switch (event) {
|
|
case BFA_ITNIM_SM_OFFLINE:
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_cleanup_offline);
|
|
itnim->is_online = BFA_FALSE;
|
|
bfa_itnim_iotov_start(itnim);
|
|
bfa_itnim_cleanup(itnim);
|
|
break;
|
|
|
|
case BFA_ITNIM_SM_DELETE:
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_cleanup_delete);
|
|
itnim->is_online = BFA_FALSE;
|
|
bfa_itnim_cleanup(itnim);
|
|
break;
|
|
|
|
case BFA_ITNIM_SM_SLER:
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_sler);
|
|
itnim->is_online = BFA_FALSE;
|
|
bfa_itnim_iotov_start(itnim);
|
|
bfa_itnim_sler_cb(itnim);
|
|
break;
|
|
|
|
case BFA_ITNIM_SM_HWFAIL:
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_iocdisable);
|
|
itnim->is_online = BFA_FALSE;
|
|
bfa_itnim_iotov_start(itnim);
|
|
bfa_itnim_iocdisable_cleanup(itnim);
|
|
break;
|
|
|
|
default:
|
|
bfa_assert(0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Second level error recovery need.
|
|
*/
|
|
static void
|
|
bfa_itnim_sm_sler(struct bfa_itnim_s *itnim, enum bfa_itnim_event event)
|
|
{
|
|
bfa_trc(itnim->bfa, itnim->rport->rport_tag);
|
|
bfa_trc(itnim->bfa, event);
|
|
|
|
switch (event) {
|
|
case BFA_ITNIM_SM_OFFLINE:
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_cleanup_offline);
|
|
bfa_itnim_cleanup(itnim);
|
|
break;
|
|
|
|
case BFA_ITNIM_SM_DELETE:
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_cleanup_delete);
|
|
bfa_itnim_cleanup(itnim);
|
|
bfa_itnim_iotov_delete(itnim);
|
|
break;
|
|
|
|
case BFA_ITNIM_SM_HWFAIL:
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_iocdisable);
|
|
bfa_itnim_iocdisable_cleanup(itnim);
|
|
break;
|
|
|
|
default:
|
|
bfa_assert(0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Going offline. Waiting for active IO cleanup.
|
|
*/
|
|
static void
|
|
bfa_itnim_sm_cleanup_offline(struct bfa_itnim_s *itnim,
|
|
enum bfa_itnim_event event)
|
|
{
|
|
bfa_trc(itnim->bfa, itnim->rport->rport_tag);
|
|
bfa_trc(itnim->bfa, event);
|
|
|
|
switch (event) {
|
|
case BFA_ITNIM_SM_CLEANUP:
|
|
if (bfa_itnim_send_fwdelete(itnim))
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_fwdelete);
|
|
else
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_fwdelete_qfull);
|
|
break;
|
|
|
|
case BFA_ITNIM_SM_DELETE:
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_cleanup_delete);
|
|
bfa_itnim_iotov_delete(itnim);
|
|
break;
|
|
|
|
case BFA_ITNIM_SM_HWFAIL:
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_iocdisable);
|
|
bfa_itnim_iocdisable_cleanup(itnim);
|
|
bfa_itnim_offline_cb(itnim);
|
|
break;
|
|
|
|
case BFA_ITNIM_SM_SLER:
|
|
break;
|
|
|
|
default:
|
|
bfa_assert(0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deleting itnim. Waiting for active IO cleanup.
|
|
*/
|
|
static void
|
|
bfa_itnim_sm_cleanup_delete(struct bfa_itnim_s *itnim,
|
|
enum bfa_itnim_event event)
|
|
{
|
|
bfa_trc(itnim->bfa, itnim->rport->rport_tag);
|
|
bfa_trc(itnim->bfa, event);
|
|
|
|
switch (event) {
|
|
case BFA_ITNIM_SM_CLEANUP:
|
|
if (bfa_itnim_send_fwdelete(itnim))
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_deleting);
|
|
else
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_deleting_qfull);
|
|
break;
|
|
|
|
case BFA_ITNIM_SM_HWFAIL:
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_iocdisable);
|
|
bfa_itnim_iocdisable_cleanup(itnim);
|
|
break;
|
|
|
|
default:
|
|
bfa_assert(0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Rport offline. Fimrware itnim is being deleted - awaiting f/w response.
|
|
*/
|
|
static void
|
|
bfa_itnim_sm_fwdelete(struct bfa_itnim_s *itnim, enum bfa_itnim_event event)
|
|
{
|
|
bfa_trc(itnim->bfa, itnim->rport->rport_tag);
|
|
bfa_trc(itnim->bfa, event);
|
|
|
|
switch (event) {
|
|
case BFA_ITNIM_SM_FWRSP:
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_offline);
|
|
bfa_itnim_offline_cb(itnim);
|
|
break;
|
|
|
|
case BFA_ITNIM_SM_DELETE:
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_deleting);
|
|
break;
|
|
|
|
case BFA_ITNIM_SM_HWFAIL:
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_iocdisable);
|
|
bfa_itnim_offline_cb(itnim);
|
|
break;
|
|
|
|
default:
|
|
bfa_assert(0);
|
|
}
|
|
}
|
|
|
|
static void
|
|
bfa_itnim_sm_fwdelete_qfull(struct bfa_itnim_s *itnim,
|
|
enum bfa_itnim_event event)
|
|
{
|
|
bfa_trc(itnim->bfa, itnim->rport->rport_tag);
|
|
bfa_trc(itnim->bfa, event);
|
|
|
|
switch (event) {
|
|
case BFA_ITNIM_SM_QRESUME:
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_fwdelete);
|
|
bfa_itnim_send_fwdelete(itnim);
|
|
break;
|
|
|
|
case BFA_ITNIM_SM_DELETE:
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_deleting_qfull);
|
|
break;
|
|
|
|
case BFA_ITNIM_SM_HWFAIL:
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_iocdisable);
|
|
bfa_reqq_wcancel(&itnim->reqq_wait);
|
|
bfa_itnim_offline_cb(itnim);
|
|
break;
|
|
|
|
default:
|
|
bfa_assert(0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Offline state.
|
|
*/
|
|
static void
|
|
bfa_itnim_sm_offline(struct bfa_itnim_s *itnim, enum bfa_itnim_event event)
|
|
{
|
|
bfa_trc(itnim->bfa, itnim->rport->rport_tag);
|
|
bfa_trc(itnim->bfa, event);
|
|
|
|
switch (event) {
|
|
case BFA_ITNIM_SM_DELETE:
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_uninit);
|
|
bfa_itnim_iotov_delete(itnim);
|
|
bfa_fcpim_delitn(itnim);
|
|
break;
|
|
|
|
case BFA_ITNIM_SM_ONLINE:
|
|
if (bfa_itnim_send_fwcreate(itnim))
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_fwcreate);
|
|
else
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_fwcreate_qfull);
|
|
break;
|
|
|
|
case BFA_ITNIM_SM_HWFAIL:
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_iocdisable);
|
|
break;
|
|
|
|
default:
|
|
bfa_assert(0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* IOC h/w failed state.
|
|
*/
|
|
static void
|
|
bfa_itnim_sm_iocdisable(struct bfa_itnim_s *itnim,
|
|
enum bfa_itnim_event event)
|
|
{
|
|
bfa_trc(itnim->bfa, itnim->rport->rport_tag);
|
|
bfa_trc(itnim->bfa, event);
|
|
|
|
switch (event) {
|
|
case BFA_ITNIM_SM_DELETE:
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_uninit);
|
|
bfa_itnim_iotov_delete(itnim);
|
|
bfa_fcpim_delitn(itnim);
|
|
break;
|
|
|
|
case BFA_ITNIM_SM_OFFLINE:
|
|
bfa_itnim_offline_cb(itnim);
|
|
break;
|
|
|
|
case BFA_ITNIM_SM_ONLINE:
|
|
if (bfa_itnim_send_fwcreate(itnim))
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_fwcreate);
|
|
else
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_fwcreate_qfull);
|
|
break;
|
|
|
|
case BFA_ITNIM_SM_HWFAIL:
|
|
break;
|
|
|
|
default:
|
|
bfa_assert(0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Itnim is deleted, waiting for firmware response to delete.
|
|
*/
|
|
static void
|
|
bfa_itnim_sm_deleting(struct bfa_itnim_s *itnim, enum bfa_itnim_event event)
|
|
{
|
|
bfa_trc(itnim->bfa, itnim->rport->rport_tag);
|
|
bfa_trc(itnim->bfa, event);
|
|
|
|
switch (event) {
|
|
case BFA_ITNIM_SM_FWRSP:
|
|
case BFA_ITNIM_SM_HWFAIL:
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_uninit);
|
|
bfa_fcpim_delitn(itnim);
|
|
break;
|
|
|
|
default:
|
|
bfa_assert(0);
|
|
}
|
|
}
|
|
|
|
static void
|
|
bfa_itnim_sm_deleting_qfull(struct bfa_itnim_s *itnim,
|
|
enum bfa_itnim_event event)
|
|
{
|
|
bfa_trc(itnim->bfa, itnim->rport->rport_tag);
|
|
bfa_trc(itnim->bfa, event);
|
|
|
|
switch (event) {
|
|
case BFA_ITNIM_SM_QRESUME:
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_deleting);
|
|
bfa_itnim_send_fwdelete(itnim);
|
|
break;
|
|
|
|
case BFA_ITNIM_SM_HWFAIL:
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_uninit);
|
|
bfa_reqq_wcancel(&itnim->reqq_wait);
|
|
bfa_fcpim_delitn(itnim);
|
|
break;
|
|
|
|
default:
|
|
bfa_assert(0);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* bfa_itnim_private
|
|
*/
|
|
|
|
/**
|
|
* Initiate cleanup of all IOs on an IOC failure.
|
|
*/
|
|
static void
|
|
bfa_itnim_iocdisable_cleanup(struct bfa_itnim_s *itnim)
|
|
{
|
|
struct bfa_tskim_s *tskim;
|
|
struct bfa_ioim_s *ioim;
|
|
struct list_head *qe, *qen;
|
|
|
|
list_for_each_safe(qe, qen, &itnim->tsk_q) {
|
|
tskim = (struct bfa_tskim_s *) qe;
|
|
bfa_tskim_iocdisable(tskim);
|
|
}
|
|
|
|
list_for_each_safe(qe, qen, &itnim->io_q) {
|
|
ioim = (struct bfa_ioim_s *) qe;
|
|
bfa_ioim_iocdisable(ioim);
|
|
}
|
|
|
|
/**
|
|
* For IO request in pending queue, we pretend an early timeout.
|
|
*/
|
|
list_for_each_safe(qe, qen, &itnim->pending_q) {
|
|
ioim = (struct bfa_ioim_s *) qe;
|
|
bfa_ioim_tov(ioim);
|
|
}
|
|
|
|
list_for_each_safe(qe, qen, &itnim->io_cleanup_q) {
|
|
ioim = (struct bfa_ioim_s *) qe;
|
|
bfa_ioim_iocdisable(ioim);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* IO cleanup completion
|
|
*/
|
|
static void
|
|
bfa_itnim_cleanp_comp(void *itnim_cbarg)
|
|
{
|
|
struct bfa_itnim_s *itnim = itnim_cbarg;
|
|
|
|
bfa_stats(itnim, cleanup_comps);
|
|
bfa_sm_send_event(itnim, BFA_ITNIM_SM_CLEANUP);
|
|
}
|
|
|
|
/**
|
|
* Initiate cleanup of all IOs.
|
|
*/
|
|
static void
|
|
bfa_itnim_cleanup(struct bfa_itnim_s *itnim)
|
|
{
|
|
struct bfa_ioim_s *ioim;
|
|
struct bfa_tskim_s *tskim;
|
|
struct list_head *qe, *qen;
|
|
|
|
bfa_wc_init(&itnim->wc, bfa_itnim_cleanp_comp, itnim);
|
|
|
|
list_for_each_safe(qe, qen, &itnim->io_q) {
|
|
ioim = (struct bfa_ioim_s *) qe;
|
|
|
|
/**
|
|
* Move IO to a cleanup queue from active queue so that a later
|
|
* TM will not pickup this IO.
|
|
*/
|
|
list_del(&ioim->qe);
|
|
list_add_tail(&ioim->qe, &itnim->io_cleanup_q);
|
|
|
|
bfa_wc_up(&itnim->wc);
|
|
bfa_ioim_cleanup(ioim);
|
|
}
|
|
|
|
list_for_each_safe(qe, qen, &itnim->tsk_q) {
|
|
tskim = (struct bfa_tskim_s *) qe;
|
|
bfa_wc_up(&itnim->wc);
|
|
bfa_tskim_cleanup(tskim);
|
|
}
|
|
|
|
bfa_wc_wait(&itnim->wc);
|
|
}
|
|
|
|
static void
|
|
__bfa_cb_itnim_online(void *cbarg, bfa_boolean_t complete)
|
|
{
|
|
struct bfa_itnim_s *itnim = cbarg;
|
|
|
|
if (complete)
|
|
bfa_cb_itnim_online(itnim->ditn);
|
|
}
|
|
|
|
static void
|
|
__bfa_cb_itnim_offline(void *cbarg, bfa_boolean_t complete)
|
|
{
|
|
struct bfa_itnim_s *itnim = cbarg;
|
|
|
|
if (complete)
|
|
bfa_cb_itnim_offline(itnim->ditn);
|
|
}
|
|
|
|
static void
|
|
__bfa_cb_itnim_sler(void *cbarg, bfa_boolean_t complete)
|
|
{
|
|
struct bfa_itnim_s *itnim = cbarg;
|
|
|
|
if (complete)
|
|
bfa_cb_itnim_sler(itnim->ditn);
|
|
}
|
|
|
|
/**
|
|
* Call to resume any I/O requests waiting for room in request queue.
|
|
*/
|
|
static void
|
|
bfa_itnim_qresume(void *cbarg)
|
|
{
|
|
struct bfa_itnim_s *itnim = cbarg;
|
|
|
|
bfa_sm_send_event(itnim, BFA_ITNIM_SM_QRESUME);
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
* bfa_itnim_public
|
|
*/
|
|
|
|
void
|
|
bfa_itnim_iodone(struct bfa_itnim_s *itnim)
|
|
{
|
|
bfa_wc_down(&itnim->wc);
|
|
}
|
|
|
|
void
|
|
bfa_itnim_tskdone(struct bfa_itnim_s *itnim)
|
|
{
|
|
bfa_wc_down(&itnim->wc);
|
|
}
|
|
|
|
void
|
|
bfa_itnim_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *km_len,
|
|
u32 *dm_len)
|
|
{
|
|
/**
|
|
* ITN memory
|
|
*/
|
|
*km_len += cfg->fwcfg.num_rports * sizeof(struct bfa_itnim_s);
|
|
}
|
|
|
|
void
|
|
bfa_itnim_attach(struct bfa_fcpim_mod_s *fcpim, struct bfa_meminfo_s *minfo)
|
|
{
|
|
struct bfa_s *bfa = fcpim->bfa;
|
|
struct bfa_itnim_s *itnim;
|
|
int i;
|
|
|
|
INIT_LIST_HEAD(&fcpim->itnim_q);
|
|
|
|
itnim = (struct bfa_itnim_s *) bfa_meminfo_kva(minfo);
|
|
fcpim->itnim_arr = itnim;
|
|
|
|
for (i = 0; i < fcpim->num_itnims; i++, itnim++) {
|
|
bfa_os_memset(itnim, 0, sizeof(struct bfa_itnim_s));
|
|
itnim->bfa = bfa;
|
|
itnim->fcpim = fcpim;
|
|
itnim->reqq = BFA_REQQ_QOS_LO;
|
|
itnim->rport = BFA_RPORT_FROM_TAG(bfa, i);
|
|
itnim->iotov_active = BFA_FALSE;
|
|
bfa_reqq_winit(&itnim->reqq_wait, bfa_itnim_qresume, itnim);
|
|
|
|
INIT_LIST_HEAD(&itnim->io_q);
|
|
INIT_LIST_HEAD(&itnim->io_cleanup_q);
|
|
INIT_LIST_HEAD(&itnim->pending_q);
|
|
INIT_LIST_HEAD(&itnim->tsk_q);
|
|
INIT_LIST_HEAD(&itnim->delay_comp_q);
|
|
bfa_sm_set_state(itnim, bfa_itnim_sm_uninit);
|
|
}
|
|
|
|
bfa_meminfo_kva(minfo) = (u8 *) itnim;
|
|
}
|
|
|
|
void
|
|
bfa_itnim_iocdisable(struct bfa_itnim_s *itnim)
|
|
{
|
|
bfa_stats(itnim, ioc_disabled);
|
|
bfa_sm_send_event(itnim, BFA_ITNIM_SM_HWFAIL);
|
|
}
|
|
|
|
static bfa_boolean_t
|
|
bfa_itnim_send_fwcreate(struct bfa_itnim_s *itnim)
|
|
{
|
|
struct bfi_itnim_create_req_s *m;
|
|
|
|
itnim->msg_no++;
|
|
|
|
/**
|
|
* check for room in queue to send request now
|
|
*/
|
|
m = bfa_reqq_next(itnim->bfa, itnim->reqq);
|
|
if (!m) {
|
|
bfa_reqq_wait(itnim->bfa, itnim->reqq, &itnim->reqq_wait);
|
|
return BFA_FALSE;
|
|
}
|
|
|
|
bfi_h2i_set(m->mh, BFI_MC_ITNIM, BFI_ITNIM_H2I_CREATE_REQ,
|
|
bfa_lpuid(itnim->bfa));
|
|
m->fw_handle = itnim->rport->fw_handle;
|
|
m->class = FC_CLASS_3;
|
|
m->seq_rec = itnim->seq_rec;
|
|
m->msg_no = itnim->msg_no;
|
|
|
|
/**
|
|
* queue I/O message to firmware
|
|
*/
|
|
bfa_reqq_produce(itnim->bfa, itnim->reqq);
|
|
return BFA_TRUE;
|
|
}
|
|
|
|
static bfa_boolean_t
|
|
bfa_itnim_send_fwdelete(struct bfa_itnim_s *itnim)
|
|
{
|
|
struct bfi_itnim_delete_req_s *m;
|
|
|
|
/**
|
|
* check for room in queue to send request now
|
|
*/
|
|
m = bfa_reqq_next(itnim->bfa, itnim->reqq);
|
|
if (!m) {
|
|
bfa_reqq_wait(itnim->bfa, itnim->reqq, &itnim->reqq_wait);
|
|
return BFA_FALSE;
|
|
}
|
|
|
|
bfi_h2i_set(m->mh, BFI_MC_ITNIM, BFI_ITNIM_H2I_DELETE_REQ,
|
|
bfa_lpuid(itnim->bfa));
|
|
m->fw_handle = itnim->rport->fw_handle;
|
|
|
|
/**
|
|
* queue I/O message to firmware
|
|
*/
|
|
bfa_reqq_produce(itnim->bfa, itnim->reqq);
|
|
return BFA_TRUE;
|
|
}
|
|
|
|
/**
|
|
* Cleanup all pending failed inflight requests.
|
|
*/
|
|
static void
|
|
bfa_itnim_delayed_comp(struct bfa_itnim_s *itnim, bfa_boolean_t iotov)
|
|
{
|
|
struct bfa_ioim_s *ioim;
|
|
struct list_head *qe, *qen;
|
|
|
|
list_for_each_safe(qe, qen, &itnim->delay_comp_q) {
|
|
ioim = (struct bfa_ioim_s *)qe;
|
|
bfa_ioim_delayed_comp(ioim, iotov);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Start all pending IO requests.
|
|
*/
|
|
static void
|
|
bfa_itnim_iotov_online(struct bfa_itnim_s *itnim)
|
|
{
|
|
struct bfa_ioim_s *ioim;
|
|
|
|
bfa_itnim_iotov_stop(itnim);
|
|
|
|
/**
|
|
* Abort all inflight IO requests in the queue
|
|
*/
|
|
bfa_itnim_delayed_comp(itnim, BFA_FALSE);
|
|
|
|
/**
|
|
* Start all pending IO requests.
|
|
*/
|
|
while (!list_empty(&itnim->pending_q)) {
|
|
bfa_q_deq(&itnim->pending_q, &ioim);
|
|
list_add_tail(&ioim->qe, &itnim->io_q);
|
|
bfa_ioim_start(ioim);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fail all pending IO requests
|
|
*/
|
|
static void
|
|
bfa_itnim_iotov_cleanup(struct bfa_itnim_s *itnim)
|
|
{
|
|
struct bfa_ioim_s *ioim;
|
|
|
|
/**
|
|
* Fail all inflight IO requests in the queue
|
|
*/
|
|
bfa_itnim_delayed_comp(itnim, BFA_TRUE);
|
|
|
|
/**
|
|
* Fail any pending IO requests.
|
|
*/
|
|
while (!list_empty(&itnim->pending_q)) {
|
|
bfa_q_deq(&itnim->pending_q, &ioim);
|
|
list_add_tail(&ioim->qe, &ioim->fcpim->ioim_comp_q);
|
|
bfa_ioim_tov(ioim);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* IO TOV timer callback. Fail any pending IO requests.
|
|
*/
|
|
static void
|
|
bfa_itnim_iotov(void *itnim_arg)
|
|
{
|
|
struct bfa_itnim_s *itnim = itnim_arg;
|
|
|
|
itnim->iotov_active = BFA_FALSE;
|
|
|
|
bfa_cb_itnim_tov_begin(itnim->ditn);
|
|
bfa_itnim_iotov_cleanup(itnim);
|
|
bfa_cb_itnim_tov(itnim->ditn);
|
|
}
|
|
|
|
/**
|
|
* Start IO TOV timer for failing back pending IO requests in offline state.
|
|
*/
|
|
static void
|
|
bfa_itnim_iotov_start(struct bfa_itnim_s *itnim)
|
|
{
|
|
if (itnim->fcpim->path_tov > 0) {
|
|
|
|
itnim->iotov_active = BFA_TRUE;
|
|
bfa_assert(bfa_itnim_hold_io(itnim));
|
|
bfa_timer_start(itnim->bfa, &itnim->timer,
|
|
bfa_itnim_iotov, itnim, itnim->fcpim->path_tov);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stop IO TOV timer.
|
|
*/
|
|
static void
|
|
bfa_itnim_iotov_stop(struct bfa_itnim_s *itnim)
|
|
{
|
|
if (itnim->iotov_active) {
|
|
itnim->iotov_active = BFA_FALSE;
|
|
bfa_timer_stop(&itnim->timer);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stop IO TOV timer.
|
|
*/
|
|
static void
|
|
bfa_itnim_iotov_delete(struct bfa_itnim_s *itnim)
|
|
{
|
|
bfa_boolean_t pathtov_active = BFA_FALSE;
|
|
|
|
if (itnim->iotov_active)
|
|
pathtov_active = BFA_TRUE;
|
|
|
|
bfa_itnim_iotov_stop(itnim);
|
|
if (pathtov_active)
|
|
bfa_cb_itnim_tov_begin(itnim->ditn);
|
|
bfa_itnim_iotov_cleanup(itnim);
|
|
if (pathtov_active)
|
|
bfa_cb_itnim_tov(itnim->ditn);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* bfa_itnim_public
|
|
*/
|
|
|
|
/**
|
|
* Itnim interrupt processing.
|
|
*/
|
|
void
|
|
bfa_itnim_isr(struct bfa_s *bfa, struct bfi_msg_s *m)
|
|
{
|
|
struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa);
|
|
union bfi_itnim_i2h_msg_u msg;
|
|
struct bfa_itnim_s *itnim;
|
|
|
|
bfa_trc(bfa, m->mhdr.msg_id);
|
|
|
|
msg.msg = m;
|
|
|
|
switch (m->mhdr.msg_id) {
|
|
case BFI_ITNIM_I2H_CREATE_RSP:
|
|
itnim = BFA_ITNIM_FROM_TAG(fcpim,
|
|
msg.create_rsp->bfa_handle);
|
|
bfa_assert(msg.create_rsp->status == BFA_STATUS_OK);
|
|
bfa_stats(itnim, create_comps);
|
|
bfa_sm_send_event(itnim, BFA_ITNIM_SM_FWRSP);
|
|
break;
|
|
|
|
case BFI_ITNIM_I2H_DELETE_RSP:
|
|
itnim = BFA_ITNIM_FROM_TAG(fcpim,
|
|
msg.delete_rsp->bfa_handle);
|
|
bfa_assert(msg.delete_rsp->status == BFA_STATUS_OK);
|
|
bfa_stats(itnim, delete_comps);
|
|
bfa_sm_send_event(itnim, BFA_ITNIM_SM_FWRSP);
|
|
break;
|
|
|
|
case BFI_ITNIM_I2H_SLER_EVENT:
|
|
itnim = BFA_ITNIM_FROM_TAG(fcpim,
|
|
msg.sler_event->bfa_handle);
|
|
bfa_stats(itnim, sler_events);
|
|
bfa_sm_send_event(itnim, BFA_ITNIM_SM_SLER);
|
|
break;
|
|
|
|
default:
|
|
bfa_trc(bfa, m->mhdr.msg_id);
|
|
bfa_assert(0);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* bfa_itnim_api
|
|
*/
|
|
|
|
struct bfa_itnim_s *
|
|
bfa_itnim_create(struct bfa_s *bfa, struct bfa_rport_s *rport, void *ditn)
|
|
{
|
|
struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa);
|
|
struct bfa_itnim_s *itnim;
|
|
|
|
itnim = BFA_ITNIM_FROM_TAG(fcpim, rport->rport_tag);
|
|
bfa_assert(itnim->rport == rport);
|
|
|
|
itnim->ditn = ditn;
|
|
|
|
bfa_stats(itnim, creates);
|
|
bfa_sm_send_event(itnim, BFA_ITNIM_SM_CREATE);
|
|
|
|
return (itnim);
|
|
}
|
|
|
|
void
|
|
bfa_itnim_delete(struct bfa_itnim_s *itnim)
|
|
{
|
|
bfa_stats(itnim, deletes);
|
|
bfa_sm_send_event(itnim, BFA_ITNIM_SM_DELETE);
|
|
}
|
|
|
|
void
|
|
bfa_itnim_online(struct bfa_itnim_s *itnim, bfa_boolean_t seq_rec)
|
|
{
|
|
itnim->seq_rec = seq_rec;
|
|
bfa_stats(itnim, onlines);
|
|
bfa_sm_send_event(itnim, BFA_ITNIM_SM_ONLINE);
|
|
}
|
|
|
|
void
|
|
bfa_itnim_offline(struct bfa_itnim_s *itnim)
|
|
{
|
|
bfa_stats(itnim, offlines);
|
|
bfa_sm_send_event(itnim, BFA_ITNIM_SM_OFFLINE);
|
|
}
|
|
|
|
/**
|
|
* Return true if itnim is considered offline for holding off IO request.
|
|
* IO is not held if itnim is being deleted.
|
|
*/
|
|
bfa_boolean_t
|
|
bfa_itnim_hold_io(struct bfa_itnim_s *itnim)
|
|
{
|
|
return (
|
|
itnim->fcpim->path_tov && itnim->iotov_active &&
|
|
(bfa_sm_cmp_state(itnim, bfa_itnim_sm_fwcreate) ||
|
|
bfa_sm_cmp_state(itnim, bfa_itnim_sm_sler) ||
|
|
bfa_sm_cmp_state(itnim, bfa_itnim_sm_cleanup_offline) ||
|
|
bfa_sm_cmp_state(itnim, bfa_itnim_sm_fwdelete) ||
|
|
bfa_sm_cmp_state(itnim, bfa_itnim_sm_offline) ||
|
|
bfa_sm_cmp_state(itnim, bfa_itnim_sm_iocdisable))
|
|
);
|
|
}
|
|
|
|
void
|
|
bfa_itnim_get_stats(struct bfa_itnim_s *itnim,
|
|
struct bfa_itnim_hal_stats_s *stats)
|
|
{
|
|
*stats = itnim->stats;
|
|
}
|
|
|
|
void
|
|
bfa_itnim_clear_stats(struct bfa_itnim_s *itnim)
|
|
{
|
|
bfa_os_memset(&itnim->stats, 0, sizeof(itnim->stats));
|
|
}
|
|
|
|
|