| /* |
| * 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_sm_fault(itnim->bfa, event); |
| } |
| } |
| |
| /** |
| * 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_sm_fault(itnim->bfa, event); |
| } |
| } |
| |
| /** |
| * 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_sm_fault(itnim->bfa, event); |
| } |
| } |
| |
| 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_sm_fault(itnim->bfa, event); |
| } |
| } |
| |
| /** |
| * 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_sm_fault(itnim->bfa, event); |
| } |
| } |
| |
| /** |
| * 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_sm_fault(itnim->bfa, event); |
| } |
| } |
| |
| /** |
| * 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_sm_fault(itnim->bfa, event); |
| } |
| } |
| |
| /** |
| * 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_sm_fault(itnim->bfa, event); |
| } |
| } |
| |
| /** |
| * 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_sm_fault(itnim->bfa, event); |
| } |
| } |
| |
| /** |
| * 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_sm_fault(itnim->bfa, event); |
| } |
| } |
| |
| 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_sm_fault(itnim->bfa, event); |
| } |
| } |
| |
| /** |
| * 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_sm_fault(itnim->bfa, event); |
| } |
| } |
| |
| /** |
| * 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_sm_fault(itnim->bfa, event); |
| } |
| } |
| |
| /** |
| * 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_sm_fault(itnim->bfa, event); |
| } |
| } |
| |
| 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_sm_fault(itnim->bfa, event); |
| } |
| } |
| |
| |
| |
| /** |
| * 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)); |
| } |
| |
| |