5566 lines
165 KiB
C
5566 lines
165 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/* Huawei Hifc PCI Express Linux driver
|
|
* Copyright(c) 2017 Huawei Technologies Co., Ltd
|
|
*
|
|
*/
|
|
|
|
#include "unf_log.h"
|
|
#include "unf_common.h"
|
|
#include "unf_event.h"
|
|
#include "unf_lport.h"
|
|
#include "unf_exchg.h"
|
|
#include "unf_portman.h"
|
|
#include "unf_rport.h"
|
|
#include "unf_io.h"
|
|
#include "unf_service.h"
|
|
#include "unf_rport.h"
|
|
#include "unf_npiv.h"
|
|
#include "hifc_portmng.h"
|
|
|
|
#define UNF_LOOP_STOP_NEED_WAIT 0
|
|
#define UNF_LOOP_STOP_NO_NEED_WAIT 1
|
|
|
|
#define UNF_MAX_SAVE_ENTRY_NUM 60
|
|
#define UNF_CHECK_CONFIG_SPEED_BY_SFSSPEED(sfs_speed, cfg_speed) \
|
|
((sfs_speed) < (cfg_speed) || (sfs_speed) == UNF_PORT_SFP_SPEED_ERR)
|
|
#define UNF_LPORT_CHIP_ERROR(lport) \
|
|
((lport)->pcie_error_cnt.pcie_error_count[UNF_PCIE_FATALERRORDETECTED])
|
|
|
|
struct unf_global_lport_s global_lport_mgr;
|
|
|
|
static unsigned int unf_port_link_up(struct unf_lport_s *v_lport,
|
|
void *v_in_put);
|
|
static unsigned int unf_port_link_down(struct unf_lport_s *v_lport,
|
|
void *v_in_put);
|
|
static unsigned int unf_port_abnormal_reset(struct unf_lport_s *v_lport,
|
|
void *v_in_put);
|
|
static unsigned int unf_port_reset_start(struct unf_lport_s *v_lport,
|
|
void *v_in_put);
|
|
static unsigned int unf_port_reset_end(struct unf_lport_s *v_lport,
|
|
void *v_in_put);
|
|
static unsigned int unf_port_nop(struct unf_lport_s *v_lport, void *v_in_put);
|
|
static unsigned int unf_port_clean_done(struct unf_lport_s *v_lport,
|
|
void *v_in_put);
|
|
static unsigned int unf_port_begin_remove(struct unf_lport_s *v_lport,
|
|
void *v_in_put);
|
|
static unsigned int unf_port_release_rport_index(struct unf_lport_s *v_lport,
|
|
void *v_in_put);
|
|
static int unf_cm_port_info_get(struct unf_lport_s *v_lport,
|
|
struct unf_hinicam_pkg *v_in_put);
|
|
static int unf_cm_port_speed_set(struct unf_lport_s *v_lport,
|
|
struct unf_hinicam_pkg *v_in_put);
|
|
static int unf_cm_topo_set(struct unf_lport_s *v_lport,
|
|
struct unf_hinicam_pkg *v_in_put);
|
|
static int unf_cm_port_set(struct unf_lport_s *v_lport,
|
|
struct unf_hinicam_pkg *v_in_put);
|
|
static int unf_get_port_sfp_info(struct unf_lport_s *v_lport,
|
|
struct unf_hinicam_pkg *v_in_put);
|
|
static int unf_cm_get_all_port_info(struct unf_lport_s *v_lport,
|
|
struct unf_hinicam_pkg *v_in_put);
|
|
static int unf_cm_clear_error_code_sum(struct unf_lport_s *v_lport,
|
|
struct unf_hinicam_pkg *v_in_put);
|
|
static int unf_cm_bbscn_set(struct unf_lport_s *v_lport,
|
|
struct unf_hinicam_pkg *v_in_put);
|
|
static int unf_get_io_dfx_statistics(struct unf_lport_s *v_pstLPort,
|
|
struct unf_hinicam_pkg *v_input);
|
|
static int unf_cm_set_vport(struct unf_lport_s *v_lport,
|
|
struct unf_hinicam_pkg *v_input);
|
|
static int unf_cm_link_delay_get(struct unf_lport_s *v_lport,
|
|
struct unf_hinicam_pkg *v_in_put);
|
|
static int unf_cm_save_data_mode(struct unf_lport_s *v_lport,
|
|
struct unf_hinicam_pkg *v_in_put);
|
|
static int unf_cm_set_dif(struct unf_lport_s *v_lport,
|
|
struct unf_hinicam_pkg *v_in_put);
|
|
static int unf_cm_select_dif_mode(struct unf_lport_s *v_lport,
|
|
struct unf_hinicam_pkg *v_in_put);
|
|
static int unf_cm_adm_show_xchg(struct unf_lport_s *v_lport,
|
|
struct unf_hinicam_pkg *v_in_put);
|
|
static int unf_cm_adm_link_time_out_opt(struct unf_lport_s *v_lport,
|
|
struct unf_hinicam_pkg *v_in_put);
|
|
static int unf_cm_adm_log_level_opt(struct unf_lport_s *v_lport,
|
|
struct unf_hinicam_pkg *v_in_put);
|
|
|
|
static struct unf_port_action_s lport_action[] = {
|
|
{ UNF_PORT_LINK_UP, unf_port_link_up },
|
|
{ UNF_PORT_LINK_DOWN, unf_port_link_down },
|
|
{ UNF_PORT_RESET_START, unf_port_reset_start },
|
|
{ UNF_PORT_RESET_END, unf_port_reset_end },
|
|
{ UNF_PORT_NOP, unf_port_nop },
|
|
{ UNF_PORT_CLEAN_DONE, unf_port_clean_done },
|
|
{ UNF_PORT_BEGIN_REMOVE, unf_port_begin_remove },
|
|
{ UNF_PORT_RELEASE_RPORT_INDEX, unf_port_release_rport_index },
|
|
{ UNF_PORT_ABNORMAL_RESET, unf_port_abnormal_reset },
|
|
};
|
|
|
|
static struct unf_hifcadm_action_s unf_hifcadm_action[] = {
|
|
{ UNF_PORT_SET_OP, unf_cm_port_set },
|
|
{ UNF_TOPO_SET_OP, unf_cm_topo_set },
|
|
{ UNF_SPEED_SET_OP, unf_cm_port_speed_set },
|
|
{ UNF_INFO_GET_OP, unf_cm_port_info_get },
|
|
{ UNF_INFO_CLEAR_OP, unf_cm_clear_error_code_sum },
|
|
{ UNF_SFP_INFO_OP, unf_get_port_sfp_info },
|
|
{ UNF_ALL_INFO_OP, unf_cm_get_all_port_info },
|
|
{ UNF_BBSCN, unf_cm_bbscn_set },
|
|
{ UNF_DFX, unf_get_io_dfx_statistics },
|
|
{ UNF_VPORT, unf_cm_set_vport },
|
|
{ UNF_LINK_DELAY, unf_cm_link_delay_get },
|
|
{ UNF_SAVA_DATA, unf_cm_save_data_mode },
|
|
{ UNF_DIF, unf_cm_set_dif },
|
|
{ UNF_DIF_CONFIG, unf_cm_select_dif_mode },
|
|
{ UNF_SHOW_XCHG, unf_cm_adm_show_xchg },
|
|
{ FC_LINK_TMO_OPT, unf_cm_adm_link_time_out_opt },
|
|
{ FC_DRV_LOG_OPT, unf_cm_adm_log_level_opt },
|
|
};
|
|
|
|
static void unf_destroy_dirty_rport(struct unf_lport_s *v_lport,
|
|
int v_show_only)
|
|
{
|
|
unsigned int dirty_rport = 0;
|
|
|
|
UNF_REFERNCE_VAR(dirty_rport);
|
|
|
|
/* for whole L_Port */
|
|
if (v_lport->dirty_flag & UNF_LPORT_DIRTY_FLAG_RPORT_POOL_DIRTY) {
|
|
dirty_rport = v_lport->rport_pool.rport_pool_count;
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"[info]Port(0x%x) has %u dirty RPort(s)",
|
|
v_lport->port_id, dirty_rport);
|
|
|
|
/* free R_Port pool memory & bitmap */
|
|
if (v_show_only == UNF_FALSE) {
|
|
vfree(v_lport->rport_pool.rport_pool_add);
|
|
v_lport->rport_pool.rport_pool_add = NULL;
|
|
vfree(v_lport->rport_pool.pul_rpi_bitmap);
|
|
v_lport->rport_pool.pul_rpi_bitmap = NULL;
|
|
}
|
|
}
|
|
|
|
UNF_REFERNCE_VAR(dirty_rport);
|
|
}
|
|
|
|
void unf_show_dirty_port(int v_show_only, unsigned int *v_ditry_port_num)
|
|
{
|
|
struct list_head *node = NULL;
|
|
struct list_head *node_next = NULL;
|
|
struct unf_lport_s *lport = NULL;
|
|
unsigned long flags = 0;
|
|
unsigned int port_num = 0;
|
|
|
|
UNF_CHECK_VALID(0x2200, UNF_TRUE, NULL != v_ditry_port_num, return);
|
|
|
|
/* for each dirty L_Port from global L_Port list */
|
|
spin_lock_irqsave(&global_lport_mgr.global_lport_list_lock, flags);
|
|
list_for_each_safe(node, node_next, &global_lport_mgr.list_dirty_head) {
|
|
lport = list_entry(node, struct unf_lport_s, entry_lport);
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"[info]Port(0x%x) has dirty data(0x%x)",
|
|
lport->port_id, lport->dirty_flag);
|
|
|
|
/* Destroy dirty L_Port's exchange(s) & R_Port(s) */
|
|
unf_destroy_dirty_xchg(lport, v_show_only);
|
|
unf_destroy_dirty_rport(lport, v_show_only);
|
|
|
|
/* Delete (dirty L_Port) list entry if necessary */
|
|
if (v_show_only == UNF_FALSE) {
|
|
list_del_init(node);
|
|
vfree(lport);
|
|
}
|
|
|
|
port_num++;
|
|
}
|
|
spin_unlock_irqrestore(&global_lport_mgr.global_lport_list_lock,
|
|
flags);
|
|
|
|
*v_ditry_port_num = port_num;
|
|
}
|
|
|
|
int unf_send_event(unsigned int port_id,
|
|
unsigned int syn_flag,
|
|
void *argc_in,
|
|
void *argc_out,
|
|
int (*p_func)(void *argc_in, void *argc_out))
|
|
{
|
|
struct unf_lport_s *lport = NULL;
|
|
struct unf_cm_event_report *event = NULL;
|
|
int ret = 0;
|
|
|
|
lport = unf_find_lport_by_port_id(port_id);
|
|
if (!lport) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT, UNF_INFO,
|
|
"Cannot find LPort(0x%x).", port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
if (unf_lport_refinc(lport) != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR,
|
|
"LPort(0x%x) is removing, no need process.",
|
|
lport->port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
if (unlikely((!lport->event_mgr.pfn_unf_get_free_event) ||
|
|
(!lport->event_mgr.pfn_unf_post_event) ||
|
|
(!lport->event_mgr.pfn_unf_release_event))) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT, UNF_MAJOR,
|
|
"Event function is NULL.");
|
|
|
|
unf_lport_ref_dec_to_destroy(lport);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
if (lport->b_port_removing == UNF_TRUE) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR,
|
|
"LPort(0x%x) is removing, no need process.",
|
|
lport->port_id);
|
|
|
|
unf_lport_ref_dec_to_destroy(lport);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
event = lport->event_mgr.pfn_unf_get_free_event((void *)lport);
|
|
if (!event) {
|
|
unf_lport_ref_dec_to_destroy(lport);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
init_completion(&event->event_comp);
|
|
event->lport = lport;
|
|
event->event_asy_flag = syn_flag;
|
|
event->pfn_unf_event_task = p_func;
|
|
event->para_in = argc_in;
|
|
event->para_out = argc_out;
|
|
lport->event_mgr.pfn_unf_post_event(lport, event);
|
|
|
|
if (event->event_asy_flag) {
|
|
/* You must wait for the other party to return. Otherwise,
|
|
*the linked list may be in disorder.
|
|
*/
|
|
wait_for_completion(&event->event_comp);
|
|
ret = (int)event->result;
|
|
lport->event_mgr.pfn_unf_release_event(lport, event);
|
|
} else {
|
|
ret = RETURN_OK;
|
|
}
|
|
|
|
unf_lport_ref_dec_to_destroy(lport);
|
|
return ret;
|
|
}
|
|
|
|
void unf_lport_update_topo(struct unf_lport_s *v_lport,
|
|
enum unf_act_topo_e v_enactive_topo)
|
|
{
|
|
unsigned long flag = 0;
|
|
|
|
UNF_CHECK_VALID(0x2210, UNF_TRUE, NULL != v_lport, return);
|
|
|
|
if ((v_enactive_topo > UNF_ACT_TOP_UNKNOWN) ||
|
|
(v_enactive_topo < UNF_ACT_TOP_PUBLIC_LOOP)) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) set invalid topology(0x%x) with current value(0x%x)",
|
|
v_lport->nport_id, v_enactive_topo,
|
|
v_lport->en_act_topo);
|
|
|
|
return;
|
|
}
|
|
|
|
spin_lock_irqsave(&v_lport->lport_state_lock, flag);
|
|
v_lport->en_act_topo = v_enactive_topo;
|
|
spin_unlock_irqrestore(&v_lport->lport_state_lock, flag);
|
|
}
|
|
|
|
void unf_set_lport_removing(struct unf_lport_s *v_lport)
|
|
{
|
|
UNF_CHECK_VALID(0x2216, UNF_TRUE, (v_lport), return);
|
|
|
|
v_lport->fc_port = NULL;
|
|
v_lport->b_port_removing = UNF_TRUE;
|
|
v_lport->destroy_step = UNF_LPORT_DESTROY_STEP_0_SET_REMOVING;
|
|
}
|
|
|
|
unsigned int unf_release_local_port(void *v_lport)
|
|
{
|
|
struct unf_lport_s *lport = v_lport;
|
|
struct completion local_port_free_completion =
|
|
COMPLETION_INITIALIZER(local_port_free_completion);
|
|
|
|
UNF_CHECK_VALID(0x2217, UNF_TRUE, (lport),
|
|
return UNF_RETURN_ERROR);
|
|
|
|
lport->lport_free_completion = &local_port_free_completion;
|
|
unf_set_lport_removing(lport);
|
|
unf_lport_ref_dec(lport);
|
|
wait_for_completion(lport->lport_free_completion);
|
|
/* for dirty case */
|
|
if (lport->dirty_flag == 0)
|
|
vfree(lport);
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
static void unf_free_all_esgl_pages(struct unf_lport_s *v_lport)
|
|
{
|
|
struct list_head *node = NULL;
|
|
struct list_head *next_node = NULL;
|
|
unsigned long flag = 0;
|
|
unsigned int alloc_idx;
|
|
|
|
UNF_CHECK_VALID(0x2218, UNF_TRUE, (v_lport), return);
|
|
spin_lock_irqsave(&v_lport->esgl_pool.esgl_pool_lock, flag);
|
|
list_for_each_safe(node, next_node,
|
|
&v_lport->esgl_pool.list_esgl_pool) {
|
|
list_del(node);
|
|
}
|
|
|
|
spin_unlock_irqrestore(&v_lport->esgl_pool.esgl_pool_lock, flag);
|
|
|
|
if (v_lport->esgl_pool.esgl_buf_list.buflist) {
|
|
for (alloc_idx = 0;
|
|
alloc_idx < v_lport->esgl_pool.esgl_buf_list.buf_num;
|
|
alloc_idx++) {
|
|
if (v_lport->esgl_pool.esgl_buf_list.buflist[alloc_idx].vaddr) {
|
|
dma_free_coherent(&v_lport->low_level_func.dev->dev,
|
|
v_lport->esgl_pool.esgl_buf_list.buf_size,
|
|
v_lport->esgl_pool.esgl_buf_list.buflist[alloc_idx].vaddr,
|
|
v_lport->esgl_pool.esgl_buf_list.buflist[alloc_idx].paddr);
|
|
v_lport->esgl_pool.esgl_buf_list.buflist[alloc_idx].vaddr = NULL;
|
|
}
|
|
}
|
|
kfree(v_lport->esgl_pool.esgl_buf_list.buflist);
|
|
v_lport->esgl_pool.esgl_buf_list.buflist = NULL;
|
|
}
|
|
}
|
|
|
|
static unsigned int unf_init_esgl_pool(struct unf_lport_s *v_lport)
|
|
{
|
|
struct unf_esgl_s *esgl = NULL;
|
|
unsigned int ret = RETURN_OK;
|
|
unsigned int index = 0;
|
|
unsigned int buf_total_size;
|
|
unsigned int buf_num;
|
|
unsigned int alloc_idx;
|
|
unsigned int cur_buf_idx = 0;
|
|
unsigned int cur_buf_offset = 0;
|
|
unsigned int buf_cnt_perhugebuf;
|
|
|
|
UNF_CHECK_VALID(0x2219, UNF_TRUE, NULL != v_lport,
|
|
return UNF_RETURN_ERROR);
|
|
|
|
v_lport->esgl_pool.esgl_pool_count =
|
|
v_lport->low_level_func.lport_cfg_items.max_io;
|
|
spin_lock_init(&v_lport->esgl_pool.esgl_pool_lock);
|
|
INIT_LIST_HEAD(&v_lport->esgl_pool.list_esgl_pool);
|
|
|
|
v_lport->esgl_pool.esgl_pool_addr =
|
|
vmalloc((size_t)((v_lport->esgl_pool.esgl_pool_count) *
|
|
sizeof(struct unf_esgl_s)));
|
|
if (!v_lport->esgl_pool.esgl_pool_addr) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"LPort(0x%x) cannot allocate ESGL Pool.",
|
|
v_lport->port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
esgl = (struct unf_esgl_s *)v_lport->esgl_pool.esgl_pool_addr;
|
|
memset(esgl, 0, ((v_lport->esgl_pool.esgl_pool_count) *
|
|
sizeof(struct unf_esgl_s)));
|
|
|
|
buf_total_size =
|
|
(unsigned int)(PAGE_SIZE * v_lport->esgl_pool.esgl_pool_count);
|
|
|
|
v_lport->esgl_pool.esgl_buf_list.buf_size =
|
|
buf_total_size > BUF_LIST_PAGE_SIZE ? BUF_LIST_PAGE_SIZE :
|
|
buf_total_size;
|
|
buf_cnt_perhugebuf =
|
|
v_lport->esgl_pool.esgl_buf_list.buf_size / PAGE_SIZE;
|
|
buf_num = v_lport->esgl_pool.esgl_pool_count %
|
|
buf_cnt_perhugebuf ? v_lport->esgl_pool.esgl_pool_count /
|
|
buf_cnt_perhugebuf + 1 : v_lport->esgl_pool.esgl_pool_count /
|
|
buf_cnt_perhugebuf;
|
|
v_lport->esgl_pool.esgl_buf_list.buflist =
|
|
(struct buff_list_s *)
|
|
kmalloc(buf_num * sizeof(struct buff_list_s), GFP_KERNEL);
|
|
v_lport->esgl_pool.esgl_buf_list.buf_num = buf_num;
|
|
|
|
if (!v_lport->esgl_pool.esgl_buf_list.buflist) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_REG_ATT, UNF_WARN,
|
|
"[err]Allocate Esgl pool buf list failed out of memory");
|
|
goto free_buff;
|
|
}
|
|
memset(v_lport->esgl_pool.esgl_buf_list.buflist, 0,
|
|
buf_num * sizeof(struct buff_list_s));
|
|
|
|
for (alloc_idx = 0; alloc_idx < buf_num; alloc_idx++) {
|
|
v_lport->esgl_pool.esgl_buf_list.buflist[alloc_idx].vaddr =
|
|
dma_alloc_coherent(
|
|
&v_lport->low_level_func.dev->dev,
|
|
v_lport->esgl_pool.esgl_buf_list.buf_size,
|
|
&v_lport->esgl_pool.esgl_buf_list.buflist[alloc_idx].paddr,
|
|
GFP_KERNEL);
|
|
if (!v_lport->esgl_pool.esgl_buf_list.buflist[alloc_idx].vaddr)
|
|
goto free_buff;
|
|
|
|
memset(v_lport->esgl_pool.esgl_buf_list.buflist[alloc_idx].vaddr,
|
|
0, v_lport->esgl_pool.esgl_buf_list.buf_size);
|
|
}
|
|
|
|
/* allocates the Esgl page, and the DMA uses the */
|
|
for (index = 0; index < v_lport->esgl_pool.esgl_pool_count; index++) {
|
|
if ((index != 0) && !(index % buf_cnt_perhugebuf))
|
|
cur_buf_idx++;
|
|
|
|
cur_buf_offset =
|
|
(unsigned int)
|
|
(PAGE_SIZE * (index % buf_cnt_perhugebuf));
|
|
esgl->page.page_address =
|
|
(unsigned long long)v_lport->esgl_pool.esgl_buf_list.buflist[cur_buf_idx].vaddr +
|
|
cur_buf_offset;
|
|
esgl->page.page_size = PAGE_SIZE;
|
|
esgl->page.esgl_phyaddr =
|
|
v_lport->esgl_pool.esgl_buf_list.buflist[cur_buf_idx].paddr +
|
|
cur_buf_offset;
|
|
list_add_tail(&esgl->entry_esgl,
|
|
&v_lport->esgl_pool.list_esgl_pool);
|
|
esgl++;
|
|
}
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_INFO,
|
|
"[EVENT]Allocate bufnum:%u, buf_total_size:%u", buf_num,
|
|
buf_total_size);
|
|
return ret;
|
|
free_buff:
|
|
unf_free_all_esgl_pages(v_lport);
|
|
vfree(v_lport->esgl_pool.esgl_pool_addr);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
static void unf_free_esgl_pool(struct unf_lport_s *v_lport)
|
|
{
|
|
UNF_CHECK_VALID(0x2220, UNF_TRUE, (v_lport), return);
|
|
|
|
unf_free_all_esgl_pages(v_lport);
|
|
v_lport->esgl_pool.esgl_pool_count = 0;
|
|
|
|
if (v_lport->esgl_pool.esgl_pool_addr) {
|
|
vfree(v_lport->esgl_pool.esgl_pool_addr);
|
|
v_lport->esgl_pool.esgl_pool_addr = NULL;
|
|
}
|
|
|
|
v_lport->destroy_step = UNF_LPORT_DESTROY_STEP_5_DESTROY_ESGL_POOL;
|
|
}
|
|
|
|
struct unf_lport_s *unf_find_lport_by_port_id(unsigned int v_port_id)
|
|
{
|
|
struct unf_lport_s *lport = NULL;
|
|
struct list_head *node = NULL;
|
|
struct list_head *next_node = NULL;
|
|
unsigned long flags = 0;
|
|
unsigned int port_id = v_port_id & (~PORTID_VPINDEX_MASK);
|
|
unsigned short vport_index = (v_port_id & PORTID_VPINDEX_MASK) >>
|
|
PORTID_VPINDEX_SHIT;
|
|
|
|
spin_lock_irqsave(&global_lport_mgr.global_lport_list_lock, flags);
|
|
|
|
list_for_each_safe(node, next_node,
|
|
&global_lport_mgr.list_lport_list_head) {
|
|
lport = list_entry(node, struct unf_lport_s, entry_lport);
|
|
if ((port_id == lport->port_id) &&
|
|
(lport->b_port_removing != UNF_TRUE)) {
|
|
spin_unlock_irqrestore(
|
|
&global_lport_mgr.global_lport_list_lock,
|
|
flags);
|
|
return unf_cm_lookup_vport_by_vp_index(lport,
|
|
vport_index);
|
|
}
|
|
}
|
|
|
|
list_for_each_safe(node, next_node,
|
|
&global_lport_mgr.list_intergrad_head) {
|
|
lport = list_entry(node, struct unf_lport_s, entry_lport);
|
|
if ((port_id == lport->port_id) &&
|
|
(lport->b_port_removing != UNF_TRUE)) {
|
|
spin_unlock_irqrestore(
|
|
&global_lport_mgr.global_lport_list_lock,
|
|
flags);
|
|
return unf_cm_lookup_vport_by_vp_index(lport,
|
|
vport_index);
|
|
}
|
|
}
|
|
|
|
spin_unlock_irqrestore(&global_lport_mgr.global_lport_list_lock, flags);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
unsigned int unf_is_vport_valid(struct unf_lport_s *v_lport,
|
|
struct unf_lport_s *v_vport)
|
|
{
|
|
struct unf_lport_s *lport = NULL;
|
|
struct unf_vport_pool_s *vport_pool = NULL;
|
|
struct unf_lport_s *vport = NULL;
|
|
struct list_head *node = NULL;
|
|
struct list_head *next_node = NULL;
|
|
unsigned long flag = 0;
|
|
|
|
UNF_CHECK_VALID(0x1977, UNF_TRUE, v_lport, return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(0x1977, UNF_TRUE, v_vport, return UNF_RETURN_ERROR);
|
|
|
|
lport = v_lport;
|
|
vport_pool = lport->vport_pool;
|
|
if (unlikely(!vport_pool)) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_IO_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) vport pool is NULL",
|
|
lport->port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
spin_lock_irqsave(&vport_pool->vport_pool_lock, flag);
|
|
list_for_each_safe(node, next_node, &lport->list_vports_head) {
|
|
vport = list_entry(node, struct unf_lport_s, entry_vport);
|
|
|
|
if (vport == v_vport && vport->b_port_removing != UNF_TRUE) {
|
|
spin_unlock_irqrestore(&vport_pool->vport_pool_lock,
|
|
flag);
|
|
|
|
return RETURN_OK;
|
|
}
|
|
}
|
|
|
|
list_for_each_safe(node, next_node, &lport->list_intergrad_vports) {
|
|
vport = list_entry(node, struct unf_lport_s, entry_vport);
|
|
|
|
if (vport == v_vport && vport->b_port_removing != UNF_TRUE) {
|
|
spin_unlock_irqrestore(&vport_pool->vport_pool_lock,
|
|
flag);
|
|
|
|
return RETURN_OK;
|
|
}
|
|
}
|
|
spin_unlock_irqrestore(&vport_pool->vport_pool_lock, flag);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
unsigned int unf_is_lport_valid(struct unf_lport_s *v_lport)
|
|
{
|
|
struct unf_lport_s *lport = NULL;
|
|
struct list_head *node = NULL;
|
|
struct list_head *next_node = NULL;
|
|
unsigned long flags = 0;
|
|
|
|
spin_lock_irqsave(&global_lport_mgr.global_lport_list_lock, flags);
|
|
|
|
list_for_each_safe(node, next_node,
|
|
&global_lport_mgr.list_lport_list_head) {
|
|
lport = list_entry(node, struct unf_lport_s, entry_lport);
|
|
|
|
if ((v_lport == lport) &&
|
|
(lport->b_port_removing != UNF_TRUE)) {
|
|
spin_unlock_irqrestore(
|
|
&global_lport_mgr.global_lport_list_lock,
|
|
flags);
|
|
return RETURN_OK;
|
|
}
|
|
|
|
if (unf_is_vport_valid(lport, v_lport) == RETURN_OK) {
|
|
spin_unlock_irqrestore(
|
|
&global_lport_mgr.global_lport_list_lock,
|
|
flags);
|
|
|
|
return RETURN_OK;
|
|
}
|
|
}
|
|
|
|
list_for_each_safe(node, next_node,
|
|
&global_lport_mgr.list_intergrad_head) {
|
|
lport = list_entry(node, struct unf_lport_s, entry_lport);
|
|
|
|
if ((v_lport == lport) &&
|
|
(lport->b_port_removing != UNF_TRUE)) {
|
|
spin_unlock_irqrestore(
|
|
&global_lport_mgr.global_lport_list_lock,
|
|
flags);
|
|
return RETURN_OK;
|
|
}
|
|
|
|
if (unf_is_vport_valid(lport, v_lport) == RETURN_OK) {
|
|
spin_unlock_irqrestore(
|
|
&global_lport_mgr.global_lport_list_lock,
|
|
flags);
|
|
|
|
return RETURN_OK;
|
|
}
|
|
}
|
|
|
|
list_for_each_safe(node, next_node,
|
|
&global_lport_mgr.list_destroy_head) {
|
|
lport = list_entry(node, struct unf_lport_s, entry_lport);
|
|
|
|
if ((v_lport == lport) &&
|
|
(lport->b_port_removing != UNF_TRUE)) {
|
|
spin_unlock_irqrestore(
|
|
&global_lport_mgr.global_lport_list_lock,
|
|
flags);
|
|
return RETURN_OK;
|
|
}
|
|
|
|
if (unf_is_vport_valid(lport, v_lport) == RETURN_OK) {
|
|
spin_unlock_irqrestore(
|
|
&global_lport_mgr.global_lport_list_lock,
|
|
flags);
|
|
|
|
return RETURN_OK;
|
|
}
|
|
}
|
|
|
|
spin_unlock_irqrestore(&global_lport_mgr.global_lport_list_lock,
|
|
flags);
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
static void unf_clean_link_down_io(struct unf_lport_s *v_lport,
|
|
int v_clean_flag)
|
|
{
|
|
/* Clean L_Port/V_Port Link Down I/O: Set Abort Tag */
|
|
UNF_CHECK_VALID(0x2225, UNF_TRUE, v_lport, return);
|
|
UNF_CHECK_VALID(0x2685, UNF_TRUE,
|
|
v_lport->xchg_mgr_temp.pfn_unf_xchg_abort_all_io,
|
|
return);
|
|
|
|
v_lport->xchg_mgr_temp.pfn_unf_xchg_abort_all_io(v_lport,
|
|
UNF_XCHG_TYPE_INI, v_clean_flag);
|
|
v_lport->xchg_mgr_temp.pfn_unf_xchg_abort_all_io(v_lport,
|
|
UNF_XCHG_TYPE_SFS, v_clean_flag);
|
|
}
|
|
|
|
unsigned int unf_fc_port_link_event(void *v_lport, unsigned int v_events,
|
|
void *v_input)
|
|
{
|
|
struct unf_lport_s *lport = NULL;
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
unsigned int index = 0;
|
|
|
|
if (unlikely(!v_lport))
|
|
return UNF_RETURN_ERROR;
|
|
|
|
lport = (struct unf_lport_s *)v_lport;
|
|
|
|
ret = unf_lport_refinc(lport);
|
|
if (ret != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR,
|
|
"[info]Port(0x%x) is removing and do nothing",
|
|
lport->port_id);
|
|
return RETURN_OK;
|
|
}
|
|
|
|
/* process port event */
|
|
while (index < (sizeof(lport_action) /
|
|
sizeof(struct unf_port_action_s))) {
|
|
if (v_events == lport_action[index].action) {
|
|
ret = lport_action[index].fn_unf_action(lport, v_input);
|
|
|
|
unf_lport_ref_dec_to_destroy(lport);
|
|
|
|
return ret;
|
|
}
|
|
index++;
|
|
}
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN,
|
|
"[warn]Port(0x%x) receive unknown event(0x%x)",
|
|
lport->port_id, v_events);
|
|
|
|
unf_lport_ref_dec_to_destroy(lport);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void unf_port_mgmt_init(void)
|
|
{
|
|
memset(&global_lport_mgr, 0, sizeof(struct unf_global_lport_s));
|
|
|
|
INIT_LIST_HEAD(&global_lport_mgr.list_lport_list_head);
|
|
|
|
INIT_LIST_HEAD(&global_lport_mgr.list_intergrad_head);
|
|
|
|
INIT_LIST_HEAD(&global_lport_mgr.list_destroy_head);
|
|
|
|
INIT_LIST_HEAD(&global_lport_mgr.list_dirty_head);
|
|
|
|
spin_lock_init(&global_lport_mgr.global_lport_list_lock);
|
|
|
|
UNF_SET_NOMAL_MODE(global_lport_mgr.dft_mode);
|
|
|
|
global_lport_mgr.b_start_work = UNF_TRUE;
|
|
}
|
|
|
|
void unf_port_mgmt_deinit(void)
|
|
{
|
|
if (global_lport_mgr.lport_sum != 0)
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_REG_ATT, UNF_WARN,
|
|
"[warn]There are %u port pool memory giveaway",
|
|
global_lport_mgr.lport_sum);
|
|
|
|
memset(&global_lport_mgr, 0, sizeof(struct unf_global_lport_s));
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"[info]Common port manager exit succeed");
|
|
}
|
|
|
|
static void unf_port_register(struct unf_lport_s *v_lport)
|
|
{
|
|
unsigned long flags = 0;
|
|
|
|
UNF_CHECK_VALID(0x2230, UNF_TRUE, (v_lport), return);
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_INFO,
|
|
"Register LPort(0x%p), port ID(0x%x).",
|
|
v_lport, v_lport->port_id);
|
|
|
|
/* Add to the global management linked list header */
|
|
spin_lock_irqsave(&global_lport_mgr.global_lport_list_lock, flags);
|
|
list_add_tail(&v_lport->entry_lport,
|
|
&global_lport_mgr.list_lport_list_head);
|
|
global_lport_mgr.lport_sum++;
|
|
spin_unlock_irqrestore(&global_lport_mgr.global_lport_list_lock, flags);
|
|
}
|
|
|
|
static void unf_port_unregister(struct unf_lport_s *v_lport)
|
|
{
|
|
unsigned long flags = 0;
|
|
|
|
UNF_CHECK_VALID(0x2703, UNF_TRUE, (v_lport), return);
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_INFO,
|
|
"Unregister LPort(0x%p), port ID(0x%x).",
|
|
v_lport, v_lport->port_id);
|
|
|
|
/* Remove from the global management linked list header */
|
|
spin_lock_irqsave(&global_lport_mgr.global_lport_list_lock, flags);
|
|
list_del(&v_lport->entry_lport);
|
|
global_lport_mgr.lport_sum--;
|
|
spin_unlock_irqrestore(&global_lport_mgr.global_lport_list_lock, flags);
|
|
}
|
|
|
|
static int unf_port_switch(struct unf_lport_s *v_lport,
|
|
unsigned int v_switch_flag)
|
|
{
|
|
struct unf_lport_s *lport = v_lport;
|
|
int ret = UNF_RETURN_ERROR;
|
|
int switch_flag = UNF_FALSE;
|
|
|
|
UNF_CHECK_VALID(0x2261, UNF_TRUE, lport, return UNF_RETURN_ERROR);
|
|
|
|
if (!lport->low_level_func.port_mgr_op.pfn_ll_port_config_set) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_EQUIP_ATT, UNF_WARN,
|
|
"[warn]Port(0x%x)'s config(switch) function is NULL",
|
|
lport->port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
switch_flag = v_switch_flag ? UNF_TRUE : UNF_FALSE;
|
|
ret = (int)lport->low_level_func.port_mgr_op.pfn_ll_port_config_set(
|
|
lport->fc_port,
|
|
UNF_PORT_CFG_SET_PORT_SWITCH, (void *)&switch_flag);
|
|
if (ret != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT, UNF_WARN,
|
|
"[warn]Port(0x%x) switch %s failed",
|
|
lport->port_id,
|
|
v_switch_flag ? "On" : "Off");
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
lport->b_switch_state = (enum int_e)switch_flag;
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
int unf_port_start_work(struct unf_lport_s *v_lport)
|
|
{
|
|
unsigned long flag = 0;
|
|
struct unf_fw_version_s fw_version = { 0 };
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
|
|
UNF_CHECK_VALID(0x2231, UNF_TRUE, v_lport, return UNF_RETURN_ERROR);
|
|
|
|
spin_lock_irqsave(&v_lport->lport_state_lock, flag);
|
|
if (v_lport->en_start_work_state != UNF_START_WORK_STOP) {
|
|
spin_unlock_irqrestore(&v_lport->lport_state_lock, flag);
|
|
|
|
return RETURN_OK;
|
|
}
|
|
v_lport->en_start_work_state = UNF_START_WORK_COMPLETE;
|
|
spin_unlock_irqrestore(&v_lport->lport_state_lock, flag);
|
|
|
|
if (!v_lport->low_level_func.port_mgr_op.pfn_ll_port_diagnose) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT, UNF_MAJOR,
|
|
"Port(0x%x)'s corresponding function is NULL.",
|
|
v_lport->port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
fw_version.message_type = UNF_DEBUG_TYPE_MESSAGE;
|
|
|
|
ret = v_lport->low_level_func.port_mgr_op.pfn_ll_port_diagnose(
|
|
(void *)v_lport->fc_port,
|
|
UNF_PORT_DIAG_PORT_DETAIL, &fw_version);
|
|
if (ret != RETURN_OK)
|
|
v_lport->fw_version[0] = '\0';
|
|
else
|
|
memcpy(v_lport->fw_version, fw_version.fw_version,
|
|
HIFC_VER_LEN);
|
|
|
|
unf_cm_get_save_info(v_lport);
|
|
/* switch sfp to start work */
|
|
(void)unf_port_switch(v_lport, UNF_TRUE);
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
static unsigned int unf_lport_init_lw_fun_op(
|
|
struct unf_lport_s *v_lport,
|
|
struct unf_low_level_function_op_s *low_level_op)
|
|
{
|
|
UNF_CHECK_VALID(0x2235, UNF_TRUE, (v_lport), return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(0x2236, UNF_TRUE, (low_level_op),
|
|
return UNF_RETURN_ERROR);
|
|
|
|
v_lport->port_id = low_level_op->lport_cfg_items.port_id;
|
|
v_lport->port_name = low_level_op->sys_port_name;
|
|
v_lport->node_name = low_level_op->sys_node_name;
|
|
v_lport->options = low_level_op->lport_cfg_items.port_mode;
|
|
v_lport->en_act_topo = UNF_ACT_TOP_UNKNOWN;
|
|
|
|
memcpy(&v_lport->low_level_func, low_level_op,
|
|
sizeof(struct unf_low_level_function_op_s));
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
void unf_lport_release_lw_fun_op(struct unf_lport_s *v_lport)
|
|
{
|
|
UNF_CHECK_VALID(0x2237, UNF_TRUE, v_lport, return);
|
|
|
|
memset(&v_lport->low_level_func, 0,
|
|
sizeof(struct unf_low_level_function_op_s));
|
|
|
|
v_lport->destroy_step = UNF_LPORT_DESTROY_STEP_13_DESTROY_LW_INTERFACE;
|
|
}
|
|
|
|
struct unf_lport_s *unf_find_lport_by_scsi_host_id(unsigned int scsi_host_id)
|
|
{
|
|
struct list_head *node = NULL, *next_node = NULL;
|
|
struct list_head *vp_node = NULL, *next_vp_node = NULL;
|
|
struct unf_lport_s *lport = NULL;
|
|
struct unf_lport_s *vport = NULL;
|
|
unsigned long flags = 0;
|
|
unsigned long vpool_flags = 0;
|
|
|
|
spin_lock_irqsave(&global_lport_mgr.global_lport_list_lock, flags);
|
|
list_for_each_safe(node, next_node,
|
|
&global_lport_mgr.list_lport_list_head) {
|
|
lport = list_entry(node, struct unf_lport_s, entry_lport);
|
|
|
|
if (scsi_host_id ==
|
|
UNF_GET_SCSI_HOST_ID((lport->host_info.p_scsi_host))) {
|
|
spin_unlock_irqrestore(
|
|
&global_lport_mgr.global_lport_list_lock,
|
|
flags);
|
|
|
|
return lport;
|
|
}
|
|
|
|
/* support NPIV */
|
|
if (lport->vport_pool) {
|
|
spin_lock_irqsave(&lport->vport_pool->vport_pool_lock,
|
|
vpool_flags);
|
|
list_for_each_safe(vp_node, next_vp_node,
|
|
&lport->list_vports_head) {
|
|
vport = list_entry(vp_node, struct unf_lport_s,
|
|
entry_vport);
|
|
|
|
if (scsi_host_id ==
|
|
UNF_GET_SCSI_HOST_ID(vport->host_info.p_scsi_host)) {
|
|
spin_unlock_irqrestore(
|
|
&lport->vport_pool->vport_pool_lock,
|
|
vpool_flags);
|
|
spin_unlock_irqrestore(
|
|
&global_lport_mgr.global_lport_list_lock,
|
|
flags);
|
|
|
|
return vport;
|
|
}
|
|
}
|
|
spin_unlock_irqrestore(
|
|
&lport->vport_pool->vport_pool_lock, vpool_flags);
|
|
}
|
|
}
|
|
|
|
list_for_each_safe(node, next_node,
|
|
&global_lport_mgr.list_intergrad_head) {
|
|
lport = list_entry(node, struct unf_lport_s, entry_lport);
|
|
|
|
if (scsi_host_id ==
|
|
UNF_GET_SCSI_HOST_ID(lport->host_info.p_scsi_host)) {
|
|
spin_unlock_irqrestore(
|
|
&global_lport_mgr.global_lport_list_lock,
|
|
flags);
|
|
|
|
return lport;
|
|
}
|
|
|
|
/* support NPIV */
|
|
if (lport->vport_pool) {
|
|
spin_lock_irqsave(&lport->vport_pool->vport_pool_lock,
|
|
vpool_flags);
|
|
list_for_each_safe(vp_node, next_vp_node,
|
|
&lport->list_vports_head) {
|
|
vport = list_entry(vp_node, struct unf_lport_s,
|
|
entry_vport);
|
|
|
|
if (scsi_host_id ==
|
|
UNF_GET_SCSI_HOST_ID(vport->host_info.p_scsi_host)) {
|
|
spin_unlock_irqrestore(
|
|
&lport->vport_pool->vport_pool_lock,
|
|
vpool_flags);
|
|
spin_unlock_irqrestore(
|
|
&global_lport_mgr.global_lport_list_lock,
|
|
flags);
|
|
|
|
return vport;
|
|
}
|
|
}
|
|
spin_unlock_irqrestore(
|
|
&lport->vport_pool->vport_pool_lock, vpool_flags);
|
|
}
|
|
}
|
|
spin_unlock_irqrestore(&global_lport_mgr.global_lport_list_lock, flags);
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_IO_ATT, UNF_WARN,
|
|
"[warn]Can not find port by scsi_host_id(0x%x), may be removing",
|
|
scsi_host_id);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
unsigned int unf_init_scsi_id_table(struct unf_lport_s *v_lport)
|
|
{
|
|
struct unf_rport_scsi_id_image_s *rport_scsi_id_image = NULL;
|
|
struct unf_wwpn_rport_info_s *wwpn_port_info = NULL;
|
|
unsigned int idx;
|
|
|
|
UNF_CHECK_VALID(0x2238, UNF_TRUE, (v_lport),
|
|
return UNF_RETURN_ERROR);
|
|
|
|
rport_scsi_id_image = &v_lport->rport_scsi_table;
|
|
rport_scsi_id_image->max_scsi_id = UNF_MAX_SCSI_ID;
|
|
|
|
/* If the number of remote connections supported by the L_Port is 0,
|
|
* an exception occurs
|
|
*/
|
|
if (rport_scsi_id_image->max_scsi_id == 0) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]Port(0x%x), supported maximum login is zero.",
|
|
v_lport->port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
rport_scsi_id_image->wwn_rport_info_table =
|
|
vmalloc(rport_scsi_id_image->max_scsi_id *
|
|
sizeof(struct unf_wwpn_rport_info_s));
|
|
if (!rport_scsi_id_image->wwn_rport_info_table) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) can't allocate SCSI ID Table(0x%x).",
|
|
v_lport->port_id, rport_scsi_id_image->max_scsi_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
memset(rport_scsi_id_image->wwn_rport_info_table, 0,
|
|
rport_scsi_id_image->max_scsi_id *
|
|
sizeof(struct unf_wwpn_rport_info_s));
|
|
|
|
wwpn_port_info = rport_scsi_id_image->wwn_rport_info_table;
|
|
|
|
for (idx = 0; idx < rport_scsi_id_image->max_scsi_id; idx++) {
|
|
INIT_DELAYED_WORK(&wwpn_port_info->loss_tmo_work,
|
|
unf_sesion_loss_timeout);
|
|
INIT_LIST_HEAD(&wwpn_port_info->fc_lun_list);
|
|
wwpn_port_info->lport = v_lport;
|
|
wwpn_port_info->target_id = INVALID_VALUE32;
|
|
wwpn_port_info++;
|
|
}
|
|
|
|
spin_lock_init(&rport_scsi_id_image->scsi_image_table_lock);
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_INFO,
|
|
"[info]Port(0x%x) supported maximum login is %d.",
|
|
v_lport->port_id, rport_scsi_id_image->max_scsi_id);
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
void unf_destroy_scsi_id_table(struct unf_lport_s *v_lport)
|
|
{
|
|
struct unf_rport_scsi_id_image_s *rport_scsi_id_image = NULL;
|
|
struct unf_wwpn_rport_info_s *wwpn_rport_info = NULL;
|
|
unsigned int i = 0;
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
|
|
UNF_CHECK_VALID(0x2239, UNF_TRUE, (v_lport), return);
|
|
|
|
rport_scsi_id_image = &v_lport->rport_scsi_table;
|
|
if (rport_scsi_id_image->wwn_rport_info_table) {
|
|
for (i = 0; i < UNF_MAX_SCSI_ID; i++) {
|
|
wwpn_rport_info =
|
|
&rport_scsi_id_image->wwn_rport_info_table[i];
|
|
UNF_DELAYED_WORK_SYNC(ret, v_lport->port_id,
|
|
&wwpn_rport_info->loss_tmo_work,
|
|
"loss tmo Timer work");
|
|
if (wwpn_rport_info->dfx_counter)
|
|
vfree(wwpn_rport_info->dfx_counter);
|
|
}
|
|
|
|
/* just for pc_lint */
|
|
if (ret)
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT,
|
|
UNF_INFO,
|
|
"Port(0x%x) cancel loss tmo work success",
|
|
v_lport->port_id);
|
|
|
|
vfree(rport_scsi_id_image->wwn_rport_info_table);
|
|
rport_scsi_id_image->wwn_rport_info_table = NULL;
|
|
}
|
|
|
|
rport_scsi_id_image->max_scsi_id = 0;
|
|
v_lport->destroy_step = UNF_LPORT_DESTROY_STEP_10_DESTROY_SCSI_TABLE;
|
|
}
|
|
|
|
static unsigned int unf_lport_init(
|
|
struct unf_lport_s *v_lport,
|
|
void *private_data,
|
|
struct unf_low_level_function_op_s *low_level_op)
|
|
{
|
|
unsigned int ret = RETURN_OK;
|
|
int ret_value = RETURN_ERROR_S32;
|
|
char work_queue_name[16];
|
|
|
|
unf_init_portparms(v_lport);
|
|
|
|
/* Associating LPort with FCPort */
|
|
v_lport->fc_port = private_data;
|
|
|
|
/* VpIndx=0 is reserved for Lport, and rootLport points to its own */
|
|
v_lport->vp_index = 0;
|
|
v_lport->root_lport = v_lport;
|
|
v_lport->chip_info = NULL;
|
|
|
|
/* Initialize the units related to L_Port and lw func */
|
|
ret = unf_lport_init_lw_fun_op(v_lport, low_level_op);
|
|
if (ret != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"LPort(0x%x) initialize lowlevel function unsuccessful.",
|
|
v_lport->port_id);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Init Linkevent workqueue */
|
|
ret_value = snprintf(work_queue_name, sizeof(work_queue_name),
|
|
"%x_lkq", (unsigned int)v_lport->port_id);
|
|
UNF_FUNCTION_RETURN_CHECK(ret_value, (int)sizeof(work_queue_name));
|
|
|
|
v_lport->link_event_wq = create_singlethread_workqueue(work_queue_name);
|
|
if (!v_lport->link_event_wq) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_NORMAL, UNF_ERR,
|
|
"[err]Port(0x%x) creat link event work queue failed",
|
|
v_lport->port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
ret_value = snprintf(work_queue_name, sizeof(work_queue_name),
|
|
"%x_xchgwq", (unsigned int)v_lport->port_id);
|
|
UNF_FUNCTION_RETURN_CHECK(ret_value, (int)sizeof(work_queue_name));
|
|
|
|
v_lport->xchg_wq = create_workqueue(work_queue_name);
|
|
if (!v_lport->xchg_wq) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_NORMAL, UNF_ERR,
|
|
"[err]Port(0x%x) creat Exchg work queue failed",
|
|
v_lport->port_id);
|
|
flush_workqueue(v_lport->link_event_wq);
|
|
destroy_workqueue(v_lport->link_event_wq);
|
|
v_lport->link_event_wq = NULL;
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
/* scsi table (R_Port) required for initializing INI
|
|
* Initialize the scsi id Table table to manage the
|
|
* mapping between SCSI ID, WWN, and Rport.
|
|
*/
|
|
ret = unf_init_scsi_id_table(v_lport);
|
|
if (ret != RETURN_OK) {
|
|
flush_workqueue(v_lport->link_event_wq);
|
|
destroy_workqueue(v_lport->link_event_wq);
|
|
v_lport->link_event_wq = NULL;
|
|
|
|
flush_workqueue(v_lport->xchg_wq);
|
|
destroy_workqueue(v_lport->xchg_wq);
|
|
v_lport->xchg_wq = NULL;
|
|
return ret;
|
|
}
|
|
|
|
/* Initialize the EXCH resource */
|
|
ret = unf_alloc_xchg_resource(v_lport);
|
|
if (ret != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"LPort(0x%x) can't allocate exchange resource.",
|
|
v_lport->port_id);
|
|
|
|
flush_workqueue(v_lport->link_event_wq);
|
|
destroy_workqueue(v_lport->link_event_wq);
|
|
v_lport->link_event_wq = NULL;
|
|
|
|
flush_workqueue(v_lport->xchg_wq);
|
|
destroy_workqueue(v_lport->xchg_wq);
|
|
v_lport->xchg_wq = NULL;
|
|
unf_destroy_scsi_id_table(v_lport);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Initialize the ESGL resource pool used by Lport */
|
|
ret = unf_init_esgl_pool(v_lport);
|
|
if (ret != RETURN_OK) {
|
|
flush_workqueue(v_lport->link_event_wq);
|
|
destroy_workqueue(v_lport->link_event_wq);
|
|
v_lport->link_event_wq = NULL;
|
|
|
|
flush_workqueue(v_lport->xchg_wq);
|
|
destroy_workqueue(v_lport->xchg_wq);
|
|
v_lport->xchg_wq = NULL;
|
|
unf_free_all_xchg_mgr(v_lport);
|
|
unf_destroy_scsi_id_table(v_lport);
|
|
|
|
return ret;
|
|
}
|
|
/* Initialize the disc manager under Lport */
|
|
ret = unf_init_disc_mgr(v_lport);
|
|
if (ret != RETURN_OK) {
|
|
flush_workqueue(v_lport->link_event_wq);
|
|
destroy_workqueue(v_lport->link_event_wq);
|
|
v_lport->link_event_wq = NULL;
|
|
|
|
flush_workqueue(v_lport->xchg_wq);
|
|
destroy_workqueue(v_lport->xchg_wq);
|
|
v_lport->xchg_wq = NULL;
|
|
unf_free_esgl_pool(v_lport);
|
|
unf_free_all_xchg_mgr(v_lport);
|
|
unf_destroy_scsi_id_table(v_lport);
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"LPort(0x%x) initialize discover manager unsuccessful.",
|
|
v_lport->port_id);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Initialize the LPort manager */
|
|
ret = unf_init_lport_mgr_temp(v_lport);
|
|
if (ret != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"LPort(0x%x) initialize RPort manager unsuccessful.",
|
|
v_lport->port_id);
|
|
|
|
goto RELEASE_LPORT;
|
|
}
|
|
|
|
/* Initialize the EXCH manager */
|
|
ret = unf_init_xchg_mgr_temp(v_lport);
|
|
if (ret != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"LPort(0x%x) initialize exchange manager unsuccessful.",
|
|
v_lport->port_id);
|
|
|
|
goto RELEASE_LPORT;
|
|
}
|
|
/* Initialize the resources required by the event processing center */
|
|
ret = unf_init_event_center(v_lport);
|
|
if (ret != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"LPort(0x%x) initialize event center unsuccessful.",
|
|
v_lport->port_id);
|
|
|
|
goto RELEASE_LPORT;
|
|
}
|
|
/* Initialize the initialization status of Lport */
|
|
unf_set_lport_state(v_lport, UNF_LPORT_ST_INITIAL);
|
|
|
|
/* Initialize the Lport route test case */
|
|
ret = unf_init_lport_route(v_lport);
|
|
if (ret != RETURN_OK) {
|
|
flush_workqueue(v_lport->link_event_wq);
|
|
destroy_workqueue(v_lport->link_event_wq);
|
|
v_lport->link_event_wq = NULL;
|
|
|
|
flush_workqueue(v_lport->xchg_wq);
|
|
destroy_workqueue(v_lport->xchg_wq);
|
|
v_lport->xchg_wq = NULL;
|
|
(void)unf_event_center_destroy(v_lport);
|
|
unf_disc_mgr_destroy(v_lport);
|
|
unf_free_esgl_pool(v_lport);
|
|
unf_free_all_xchg_mgr(v_lport);
|
|
unf_destroy_scsi_id_table(v_lport);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Thesupports the initialization stepof the NPIV */
|
|
ret = unf_init_vport_pool(v_lport);
|
|
if (ret != RETURN_OK) {
|
|
flush_workqueue(v_lport->link_event_wq);
|
|
destroy_workqueue(v_lport->link_event_wq);
|
|
v_lport->link_event_wq = NULL;
|
|
|
|
flush_workqueue(v_lport->xchg_wq);
|
|
destroy_workqueue(v_lport->xchg_wq);
|
|
v_lport->xchg_wq = NULL;
|
|
|
|
unf_destroy_lport_route(v_lport);
|
|
(void)unf_event_center_destroy(v_lport);
|
|
unf_disc_mgr_destroy(v_lport);
|
|
unf_free_esgl_pool(v_lport);
|
|
unf_free_all_xchg_mgr(v_lport);
|
|
unf_destroy_scsi_id_table(v_lport);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* qualifier rport callback */
|
|
v_lport->pfn_unf_qualify_rport = unf_rport_set_qualifier_key_reuse;
|
|
v_lport->pfn_unf_tmf_abnormal_recovery =
|
|
unf_tmf_timeout_recovery_special;
|
|
return RETURN_OK;
|
|
RELEASE_LPORT:
|
|
flush_workqueue(v_lport->link_event_wq);
|
|
destroy_workqueue(v_lport->link_event_wq);
|
|
v_lport->link_event_wq = NULL;
|
|
|
|
flush_workqueue(v_lport->xchg_wq);
|
|
destroy_workqueue(v_lport->xchg_wq);
|
|
v_lport->xchg_wq = NULL;
|
|
|
|
unf_disc_mgr_destroy(v_lport);
|
|
unf_free_esgl_pool(v_lport);
|
|
unf_free_all_xchg_mgr(v_lport);
|
|
unf_destroy_scsi_id_table(v_lport);
|
|
return ret;
|
|
}
|
|
|
|
static void unf_destroy_card_thread(struct unf_lport_s *v_lport)
|
|
{
|
|
struct unf_event_mgr *event_mgr = NULL;
|
|
struct unf_chip_manage_info_s *chip_info = NULL;
|
|
struct list_head *list = NULL;
|
|
struct list_head *list_tmp = NULL;
|
|
struct unf_cm_event_report *event_node = NULL;
|
|
unsigned long event_lock_flag = 0;
|
|
unsigned long flag = 0;
|
|
|
|
UNF_CHECK_VALID(0x2249, UNF_TRUE, (v_lport), return);
|
|
|
|
/* If the thread cannot be found, apply for a new thread. */
|
|
chip_info = v_lport->chip_info;
|
|
if (!chip_info) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"Port(0x%x) has no event thread.", v_lport->port_id);
|
|
return;
|
|
}
|
|
event_mgr = &v_lport->event_mgr;
|
|
|
|
spin_lock_irqsave(&chip_info->chip_event_list_lock, flag);
|
|
if (!list_empty(&chip_info->list_head)) {
|
|
list_for_each_safe(list, list_tmp, &chip_info->list_head) {
|
|
event_node = list_entry(list,
|
|
struct unf_cm_event_report,
|
|
list_entry);
|
|
|
|
/* The LPort under the global event node is null. */
|
|
if (v_lport == event_node->lport) {
|
|
list_del_init(&event_node->list_entry);
|
|
if (event_node->event_asy_flag ==
|
|
UNF_EVENT_SYN) {
|
|
event_node->result = UNF_RETURN_ERROR;
|
|
complete(&event_node->event_comp);
|
|
}
|
|
|
|
spin_lock_irqsave(&event_mgr->port_event_lock,
|
|
event_lock_flag);
|
|
event_mgr->free_event_count++;
|
|
list_add_tail(&event_node->list_entry,
|
|
&event_mgr->list_free_event);
|
|
spin_unlock_irqrestore(
|
|
&event_mgr->port_event_lock,
|
|
event_lock_flag);
|
|
}
|
|
}
|
|
}
|
|
spin_unlock_irqrestore(&chip_info->chip_event_list_lock, flag);
|
|
|
|
/* If the number of events introduced by the event thread is 0,
|
|
* it indicates that no interface is used. In this case, thread
|
|
* resources need to be consumed
|
|
*/
|
|
if (atomic_dec_and_test(&chip_info->ref_cnt)) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"Port(0x%x) destroy slot(%u) chip(0x%x) event thread succeed.",
|
|
v_lport->port_id, chip_info->slot_id,
|
|
chip_info->chip_id);
|
|
chip_info->b_thread_exit = UNF_TRUE;
|
|
wake_up_process(chip_info->data_thread);
|
|
kthread_stop(chip_info->data_thread);
|
|
chip_info->data_thread = NULL;
|
|
|
|
spin_lock_irqsave(&card_thread_mgr.global_card_list_lock, flag);
|
|
list_del_init(&chip_info->list_chip_thread_entry);
|
|
card_thread_mgr.card_sum--;
|
|
spin_unlock_irqrestore(&card_thread_mgr.global_card_list_lock,
|
|
flag);
|
|
|
|
vfree(chip_info);
|
|
}
|
|
|
|
v_lport->chip_info = NULL;
|
|
}
|
|
|
|
unsigned int unf_lport_deinit(struct unf_lport_s *v_lport)
|
|
{
|
|
UNF_CHECK_VALID(0x2246, UNF_TRUE, (v_lport), return UNF_RETURN_ERROR);
|
|
|
|
/* If the card is unloaded normally, the thread is stopped once.
|
|
* The problem does not occur if you stop the thread again.
|
|
*/
|
|
unf_destroy_lport_route(v_lport);
|
|
|
|
/* minus the reference count of the card event;
|
|
* the last port deletes the card thread
|
|
*/
|
|
unf_destroy_card_thread(v_lport);
|
|
flush_workqueue(v_lport->link_event_wq);
|
|
destroy_workqueue(v_lport->link_event_wq);
|
|
v_lport->link_event_wq = NULL;
|
|
|
|
/* Release Event Processing Center */
|
|
(void)unf_event_center_destroy(v_lport);
|
|
|
|
/* Release the Vport resource pool */
|
|
unf_free_vport_pool(v_lport);
|
|
|
|
/* Destroying the Xchg Manager */
|
|
unf_xchg_mgr_destroy(v_lport);
|
|
|
|
/* Release Esgl pool */
|
|
unf_free_esgl_pool(v_lport);
|
|
|
|
/* reliability review :Disc should release after Xchg.
|
|
* Destroy the disc manager
|
|
*/
|
|
unf_disc_mgr_destroy(v_lport);
|
|
|
|
/* Release Xchg Mg template */
|
|
unf_release_xchg_mgr_temp(v_lport);
|
|
|
|
/* Release the Lport Mg template */
|
|
unf_release_lport_mgr_temp(v_lport);
|
|
|
|
/* Destroy the ScsiId Table */
|
|
unf_destroy_scsi_id_table(v_lport);
|
|
|
|
flush_workqueue(v_lport->xchg_wq);
|
|
destroy_workqueue(v_lport->xchg_wq);
|
|
v_lport->xchg_wq = NULL;
|
|
|
|
/* Deregister SCSI Host */
|
|
unf_unregister_scsi_host(v_lport);
|
|
|
|
/* Releasing the lw Interface Template */
|
|
unf_lport_release_lw_fun_op(v_lport);
|
|
v_lport->fc_port = NULL;
|
|
return RETURN_OK;
|
|
}
|
|
|
|
static int unf_card_event_process(void *v_arg)
|
|
{
|
|
struct list_head *node = NULL;
|
|
struct unf_cm_event_report *event_node = NULL;
|
|
unsigned long flags = 0;
|
|
struct unf_chip_manage_info_s *chip_info =
|
|
(struct unf_chip_manage_info_s *)v_arg;
|
|
|
|
UNF_REFERNCE_VAR(v_arg);
|
|
|
|
set_user_nice(current, 4);
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_INFO,
|
|
"Slot(%u) chip(0x%x) enter event thread.",
|
|
chip_info->slot_id, chip_info->chip_id);
|
|
|
|
while (!kthread_should_stop()) {
|
|
if (chip_info->b_thread_exit == UNF_TRUE)
|
|
break;
|
|
|
|
spin_lock_irqsave(&chip_info->chip_event_list_lock, flags);
|
|
if (list_empty(&chip_info->list_head) == UNF_TRUE) {
|
|
spin_unlock_irqrestore(&chip_info->chip_event_list_lock,
|
|
flags);
|
|
|
|
set_current_state(TASK_INTERRUPTIBLE);
|
|
schedule_timeout((long)msecs_to_jiffies(1000));
|
|
} else {
|
|
node = (&chip_info->list_head)->next;
|
|
list_del_init(node);
|
|
chip_info->list_num--;
|
|
event_node = list_entry(node,
|
|
struct unf_cm_event_report,
|
|
list_entry);
|
|
spin_unlock_irqrestore(&chip_info->chip_event_list_lock,
|
|
flags);
|
|
unf_handle_event(event_node);
|
|
}
|
|
}
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EVENT, UNF_MAJOR,
|
|
"Slot(%u) chip(0x%x) exit event thread.",
|
|
chip_info->slot_id, chip_info->chip_id);
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
static unsigned int unf_creat_chip_thread(struct unf_lport_s *v_lport)
|
|
{
|
|
unsigned long flag = 0;
|
|
struct unf_chip_manage_info_s *chip_info = NULL;
|
|
|
|
UNF_CHECK_VALID(0x2250, UNF_TRUE, (v_lport), return UNF_RETURN_ERROR);
|
|
|
|
/* If the thread cannot be found, apply for a new thread. */
|
|
chip_info = (struct unf_chip_manage_info_s *)vmalloc(
|
|
sizeof(struct unf_chip_manage_info_s));
|
|
if (!chip_info) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"Port(0x%x) cannot allocate thread memory.",
|
|
v_lport->port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
memset(chip_info, 0, sizeof(struct unf_chip_manage_info_s));
|
|
|
|
memcpy(&chip_info->chip_info, &v_lport->low_level_func.chip_info,
|
|
sizeof(struct unf_chip_info_s));
|
|
chip_info->slot_id =
|
|
UNF_GET_BOARD_TYPE_AND_SLOT_ID_BY_PORTID(v_lport->port_id);
|
|
chip_info->chip_id = v_lport->low_level_func.chip_id;
|
|
chip_info->list_num = 0;
|
|
chip_info->sfp_9545_fault = UNF_FALSE;
|
|
chip_info->sfp_power_fault = UNF_FALSE;
|
|
atomic_set(&chip_info->ref_cnt, 1);
|
|
atomic_set(&chip_info->card_loop_test_flag, UNF_FALSE);
|
|
spin_lock_init(&chip_info->card_loop_back_state_lock);
|
|
INIT_LIST_HEAD(&chip_info->list_head);
|
|
spin_lock_init(&chip_info->chip_event_list_lock);
|
|
|
|
chip_info->b_thread_exit = UNF_FALSE;
|
|
chip_info->data_thread =
|
|
kthread_create(unf_card_event_process, chip_info,
|
|
"%x_et", v_lport->port_id);
|
|
|
|
if (IS_ERR(chip_info->data_thread) ||
|
|
(!chip_info->data_thread)) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"Port(0x%x) creat event thread(0x%p) unsuccessful.",
|
|
v_lport->port_id, chip_info->data_thread);
|
|
|
|
vfree(chip_info);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
v_lport->chip_info = chip_info;
|
|
wake_up_process(chip_info->data_thread);
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_INFO,
|
|
"Port(0x%x) creat slot(%u) chip(0x%x) event thread succeed.",
|
|
v_lport->port_id, chip_info->slot_id, chip_info->chip_id);
|
|
|
|
spin_lock_irqsave(&card_thread_mgr.global_card_list_lock, flag);
|
|
list_add_tail(&chip_info->list_chip_thread_entry,
|
|
&card_thread_mgr.list_card_list_head);
|
|
card_thread_mgr.card_sum++;
|
|
spin_unlock_irqrestore(&card_thread_mgr.global_card_list_lock, flag);
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
static unsigned int unf_find_chip_thread(struct unf_lport_s *v_lport)
|
|
{
|
|
unsigned long flag = 0;
|
|
struct list_head *node = NULL;
|
|
struct list_head *next_node = NULL;
|
|
struct unf_chip_manage_info_s *chip_info = NULL;
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
|
|
spin_lock_irqsave(&card_thread_mgr.global_card_list_lock, flag);
|
|
list_for_each_safe(node, next_node,
|
|
&card_thread_mgr.list_card_list_head) {
|
|
chip_info = list_entry(node, struct unf_chip_manage_info_s,
|
|
list_chip_thread_entry);
|
|
|
|
if ((chip_info->chip_id == v_lport->low_level_func.chip_id) &&
|
|
(chip_info->slot_id == UNF_GET_BOARD_TYPE_AND_SLOT_ID_BY_PORTID(v_lport->port_id))) {
|
|
atomic_inc(&chip_info->ref_cnt);
|
|
v_lport->chip_info = chip_info;
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT,
|
|
UNF_MAJOR,
|
|
"Port(0x%x) find card(%u) chip(0x%x) event thread succeed.",
|
|
v_lport->port_id, chip_info->slot_id,
|
|
chip_info->chip_id);
|
|
|
|
spin_unlock_irqrestore(
|
|
&card_thread_mgr.global_card_list_lock, flag);
|
|
|
|
return RETURN_OK;
|
|
}
|
|
}
|
|
spin_unlock_irqrestore(&card_thread_mgr.global_card_list_lock, flag);
|
|
|
|
ret = unf_creat_chip_thread(v_lport);
|
|
if (ret != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"LPort(0x%x) creat event thread unsuccessful. Destroy LPort.",
|
|
v_lport->port_id);
|
|
return UNF_RETURN_ERROR;
|
|
} else {
|
|
return RETURN_OK;
|
|
}
|
|
}
|
|
|
|
static int unf_cm_get_mac_adr(void *argc_in, void *argc_out)
|
|
{
|
|
struct unf_lport_s *lport = NULL;
|
|
struct unf_get_chip_info_argout *chp_info = NULL;
|
|
|
|
UNF_CHECK_VALID(0x2398, UNF_TRUE, argc_in, return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(0x2398, UNF_TRUE, argc_out, return UNF_RETURN_ERROR);
|
|
|
|
lport = (struct unf_lport_s *)argc_in;
|
|
chp_info = (struct unf_get_chip_info_argout *)argc_out;
|
|
|
|
if (!lport) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT, UNF_MAJOR,
|
|
" LPort is null.");
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
if (!lport->low_level_func.port_mgr_op.pfn_ll_port_config_get) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"Port(0x%x)'s corresponding function is NULL.",
|
|
lport->port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
if (lport->low_level_func.port_mgr_op.pfn_ll_port_config_get(
|
|
lport->fc_port,
|
|
UNF_PORT_CFG_GET_MAC_ADDR, chp_info) != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"Port(0x%x) get .", lport->port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
return RETURN_OK;
|
|
}
|
|
|
|
static unsigned int unf_build_lport_wwn(struct unf_lport_s *v_lport)
|
|
{
|
|
struct unf_get_chip_info_argout v_wwn = { 0 };
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
|
|
UNF_CHECK_VALID(0x2403, UNF_TRUE, (v_lport), return UNF_RETURN_ERROR);
|
|
|
|
ret = (unsigned int)unf_send_event(v_lport->port_id,
|
|
UNF_EVENT_SYN,
|
|
(void *)v_lport,
|
|
(void *)&v_wwn,
|
|
unf_cm_get_mac_adr);
|
|
if (ret != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"UNF_BuildSysWwn SendEvent(UNF_PortGetMacAdr) fail.");
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
/* save card mode: UNF_FC_SERVER_BOARD_32_G(6):32G;
|
|
* UNF_FC_SERVER_BOARD_16_G(7):16G MODE
|
|
*/
|
|
v_lport->card_type = v_wwn.board_type;
|
|
|
|
/* update port max speed */
|
|
if (v_wwn.board_type == UNF_FC_SERVER_BOARD_32_G)
|
|
v_lport->low_level_func.fc_ser_max_speed = UNF_PORT_SPEED_32_G;
|
|
else if (v_wwn.board_type == UNF_FC_SERVER_BOARD_16_G)
|
|
v_lport->low_level_func.fc_ser_max_speed = UNF_PORT_SPEED_16_G;
|
|
else if (v_wwn.board_type == UNF_FC_SERVER_BOARD_8_G)
|
|
v_lport->low_level_func.fc_ser_max_speed = UNF_PORT_SPEED_8_G;
|
|
else
|
|
v_lport->low_level_func.fc_ser_max_speed = UNF_PORT_SPEED_32_G;
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
void *unf_lport_create_and_init(
|
|
void *private_data,
|
|
struct unf_low_level_function_op_s *low_level_op)
|
|
{
|
|
struct unf_lport_s *lport = NULL;
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
|
|
if (!private_data) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"Private Data is NULL");
|
|
|
|
return NULL;
|
|
}
|
|
if (!low_level_op) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"LowLevel port(0x%p) function is NULL", private_data);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* 1. vmalloc & Memset L_Port */
|
|
lport = vmalloc(sizeof(struct unf_lport_s));
|
|
if (!lport) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"Alloc LPort memory failed.");
|
|
|
|
return NULL;
|
|
}
|
|
memset(lport, 0, sizeof(struct unf_lport_s));
|
|
|
|
/* 2. L_Port Init */
|
|
if (unf_lport_init(lport, private_data, low_level_op) != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"LPort initialize unsuccessful.");
|
|
|
|
vfree(lport);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* 4. Get or Create Chip Thread Chip_ID & Slot_ID */
|
|
ret = unf_find_chip_thread(lport);
|
|
if (ret != RETURN_OK) {
|
|
(void)unf_lport_deinit(lport);
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"LPort(0x%x) Find Chip thread unsuccessful. Destroy LPort.",
|
|
lport->port_id);
|
|
|
|
vfree(lport);
|
|
return NULL;
|
|
}
|
|
|
|
/* 5. Registers with in the port management global linked list */
|
|
unf_port_register(lport);
|
|
/* update WWN */
|
|
if (unf_build_lport_wwn(lport) != RETURN_OK) {
|
|
unf_port_unregister(lport);
|
|
(void)unf_lport_deinit(lport);
|
|
vfree(lport);
|
|
return NULL;
|
|
}
|
|
|
|
unf_init_link_lose_tmo(lport);
|
|
|
|
/* initialize Scsi Host */
|
|
if (unf_register_scsi_host(lport) != RETURN_OK) {
|
|
unf_port_unregister(lport);
|
|
(void)unf_lport_deinit(lport);
|
|
vfree(lport);
|
|
return NULL;
|
|
}
|
|
|
|
/* 7. Here, start work now */
|
|
if (global_lport_mgr.b_start_work == UNF_TRUE) {
|
|
if (unf_port_start_work(lport) != RETURN_OK) {
|
|
unf_port_unregister(lport);
|
|
|
|
(void)unf_lport_deinit(lport);
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_REG_ATT,
|
|
UNF_WARN,
|
|
"[warn]Port(0x%x) start work failed",
|
|
lport->port_id);
|
|
vfree(lport);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
UNF_REFERNCE_VAR(lport);
|
|
return lport;
|
|
}
|
|
|
|
static int unf_lport_destroy(void *v_lport, void *v_arg_out)
|
|
{
|
|
struct unf_lport_s *lport = NULL;
|
|
unsigned long flags = 0;
|
|
|
|
if (!v_lport) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"LPort is NULL.");
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
UNF_REFERNCE_VAR(v_arg_out);
|
|
|
|
lport = (struct unf_lport_s *)v_lport;
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_NORMAL, UNF_MAJOR,
|
|
"Destroy LPort(0x%p), ID(0x%x).",
|
|
lport, lport->port_id);
|
|
|
|
/* NPIV Ensure that all Vport are deleted */
|
|
unf_destroy_all_vports(lport);
|
|
|
|
lport->destroy_step = UNF_LPORT_DESTROY_STEP_1_REPORT_PORT_OUT;
|
|
|
|
(void)unf_lport_deinit(v_lport);
|
|
|
|
/* The port is removed from the destroy linked list.
|
|
* The next step is to release the memory
|
|
*/
|
|
spin_lock_irqsave(&global_lport_mgr.global_lport_list_lock, flags);
|
|
list_del(&lport->entry_lport);
|
|
|
|
/* If the port has dirty memory, the port is mounted to the
|
|
* linked list of dirty ports
|
|
*/
|
|
if (lport->dirty_flag)
|
|
list_add_tail(&lport->entry_lport,
|
|
&global_lport_mgr.list_dirty_head);
|
|
spin_unlock_irqrestore(&global_lport_mgr.global_lport_list_lock,
|
|
flags);
|
|
|
|
if (lport->lport_free_completion) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"Complete LPort(0x%p), port ID(0x%x)'s Free Completion.",
|
|
lport, lport->port_id);
|
|
complete(lport->lport_free_completion);
|
|
} else {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"LPort(0x%p), port ID(0x%x)'s Free Completion is NULL.",
|
|
lport, lport->port_id);
|
|
dump_stack();
|
|
}
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
unsigned int unf_lport_refinc(struct unf_lport_s *v_lport)
|
|
{
|
|
unsigned long lport_flags = 0;
|
|
|
|
UNF_CHECK_VALID(0x2208, UNF_TRUE, v_lport, return UNF_RETURN_ERROR);
|
|
|
|
spin_lock_irqsave(&v_lport->lport_state_lock, lport_flags);
|
|
if (atomic_read(&v_lport->lport_ref_cnt) <= 0) {
|
|
spin_unlock_irqrestore(&v_lport->lport_state_lock,
|
|
lport_flags);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
atomic_inc(&v_lport->lport_ref_cnt);
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO,
|
|
"[info]Port(0x%p) port_id(0x%x) reference count is %d",
|
|
v_lport, v_lport->port_id,
|
|
atomic_read(&v_lport->lport_ref_cnt));
|
|
|
|
spin_unlock_irqrestore(&v_lport->lport_state_lock, lport_flags);
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
void unf_lport_ref_dec(struct unf_lport_s *v_lport)
|
|
{
|
|
unsigned long flags = 0;
|
|
unsigned long lport_flags = 0;
|
|
|
|
UNF_CHECK_VALID(0x2209, UNF_TRUE, v_lport, return);
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO,
|
|
"LPort(0x%p), port ID(0x%x), reference count is %d.",
|
|
v_lport, v_lport->port_id,
|
|
atomic_read(&v_lport->lport_ref_cnt));
|
|
|
|
spin_lock_irqsave(&global_lport_mgr.global_lport_list_lock, flags);
|
|
spin_lock_irqsave(&v_lport->lport_state_lock, lport_flags);
|
|
if (atomic_dec_and_test(&v_lport->lport_ref_cnt)) {
|
|
spin_unlock_irqrestore(&v_lport->lport_state_lock, lport_flags);
|
|
list_del(&v_lport->entry_lport);
|
|
global_lport_mgr.lport_sum--;
|
|
|
|
/* attaches the lport to the destroy linked list for dfx */
|
|
list_add_tail(&v_lport->entry_lport,
|
|
&global_lport_mgr.list_destroy_head);
|
|
spin_unlock_irqrestore(&global_lport_mgr.global_lport_list_lock,
|
|
flags);
|
|
|
|
(void)unf_lport_destroy(v_lport, NULL);
|
|
} else {
|
|
spin_unlock_irqrestore(&v_lport->lport_state_lock, lport_flags);
|
|
spin_unlock_irqrestore(&global_lport_mgr.global_lport_list_lock,
|
|
flags);
|
|
}
|
|
}
|
|
|
|
static int unf_reset_port(void *v_arg_in, void *v_arg_out)
|
|
{
|
|
struct unf_reset_port_argin *arg_in =
|
|
(struct unf_reset_port_argin *)v_arg_in;
|
|
struct unf_lport_s *lport = NULL;
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
enum unf_port_config_state_e port_state = UNF_PORT_CONFIG_STATE_RESET;
|
|
|
|
UNF_REFERNCE_VAR(v_arg_out);
|
|
UNF_CHECK_VALID(0x2262, UNF_TRUE, arg_in, return UNF_RETURN_ERROR);
|
|
|
|
lport = unf_find_lport_by_port_id(arg_in->port_id);
|
|
if (!lport) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT, UNF_MAJOR,
|
|
"Not find LPort(0x%x).", arg_in->port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
/* reset port */
|
|
if (!lport->low_level_func.port_mgr_op.pfn_ll_port_config_set) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT, UNF_MAJOR,
|
|
"Port(0x%x)'s corresponding function is NULL.",
|
|
lport->port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
lport->en_act_topo = UNF_ACT_TOP_UNKNOWN;
|
|
lport->speed = UNF_PORT_SPEED_UNKNOWN;
|
|
lport->fabric_node_name = 0;
|
|
|
|
ret = lport->low_level_func.port_mgr_op.pfn_ll_port_config_set(
|
|
lport->fc_port,
|
|
UNF_PORT_CFG_SET_PORT_STATE, (void *)&port_state);
|
|
|
|
if (ret != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT, UNF_MAJOR,
|
|
"Reset port(0x%x) unsuccessful.", lport->port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
static int unf_sfp_switch(unsigned int v_port_id, int v_turn_on)
|
|
{
|
|
struct unf_lport_s *lport = NULL;
|
|
int turn_on = v_turn_on;
|
|
int ret = UNF_RETURN_ERROR;
|
|
unsigned long flag = 0;
|
|
|
|
if (global_lport_mgr.b_start_work == UNF_FALSE) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_EQUIP_ATT, UNF_WARN,
|
|
"[warn]Port(0x%x) not start work, ignored command:turn %s.",
|
|
v_port_id, (v_turn_on == UNF_TRUE) ? "ON" : "OFF");
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
lport = unf_find_lport_by_port_id(v_port_id);
|
|
if (!lport) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_EQUIP_ATT, UNF_WARN,
|
|
"[warn]Not find LPort(0x%x).", v_port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
spin_lock_irqsave(&lport->lport_state_lock, flag);
|
|
if (lport->en_start_work_state != UNF_START_WORK_COMPLETE) {
|
|
spin_unlock_irqrestore(&lport->lport_state_lock, flag);
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_EQUIP_ATT, UNF_WARN,
|
|
"[warn]LPort(0x%x) not start work, ignored command:turn %s.",
|
|
v_port_id, (v_turn_on == UNF_TRUE) ? "ON" : "OFF");
|
|
|
|
return RETURN_OK;
|
|
}
|
|
spin_unlock_irqrestore(&lport->lport_state_lock, flag);
|
|
|
|
if (!lport->low_level_func.port_mgr_op.pfn_ll_port_config_set) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_EQUIP_ATT, UNF_WARN,
|
|
"[warn]Port(0x%x)'s corresponding function is NULL.",
|
|
v_port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
ret = (int)lport->low_level_func.port_mgr_op.pfn_ll_port_config_set(
|
|
lport->fc_port,
|
|
UNF_PORT_CFG_SET_SFP_SWITCH,
|
|
(void *)&turn_on);
|
|
if (ret != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_EQUIP_ATT, UNF_WARN,
|
|
"[warn]Port(0x%x) switch SFP+ %s unsuccessful.",
|
|
v_port_id, v_turn_on ? "On" : "Off");
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
lport->b_switch_state = (enum int_e)turn_on;
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
static int unf_sfp_switch_event(void *v_argc_in, void *v_argc_out)
|
|
{
|
|
struct unf_set_sfp_argin *in = (struct unf_set_sfp_argin *)v_argc_in;
|
|
|
|
UNF_REFERNCE_VAR(v_argc_out);
|
|
UNF_CHECK_VALID(0x2267, UNF_TRUE, v_argc_in, return UNF_RETURN_ERROR);
|
|
|
|
return unf_sfp_switch(in->port_id, in->turn_on);
|
|
}
|
|
|
|
int unf_cm_sfp_switch(unsigned int v_port_id, int v_bturn_on)
|
|
{
|
|
struct unf_set_sfp_argin in = { 0 };
|
|
|
|
in.port_id = v_port_id;
|
|
in.turn_on = v_bturn_on;
|
|
return unf_send_event(v_port_id, UNF_EVENT_SYN, (void *)&in,
|
|
(void *)NULL, unf_sfp_switch_event);
|
|
}
|
|
|
|
static int unf_get_port_speed(void *v_argc_in, void *v_argc_out)
|
|
{
|
|
unsigned int *speed = (unsigned int *)v_argc_out;
|
|
struct unf_low_level_port_mgr_op_s *port_mgr = NULL;
|
|
struct unf_lport_s *lport = NULL;
|
|
int ret = 0;
|
|
unsigned int port_id = *(unsigned int *)v_argc_in;
|
|
|
|
UNF_CHECK_VALID(0x2268, UNF_TRUE, v_argc_in, return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(0x2269, UNF_TRUE, v_argc_out, return UNF_RETURN_ERROR);
|
|
lport = unf_find_lport_by_port_id(port_id);
|
|
if (!lport) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT, UNF_MAJOR,
|
|
"Cannot Find LPort by (0x%x).", port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
port_mgr = &lport->low_level_func.port_mgr_op;
|
|
|
|
if (!port_mgr->pfn_ll_port_config_get) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT, UNF_MAJOR,
|
|
"Port(0x%x)'s corresponding function is NULL.",
|
|
port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
if (lport->link_up == UNF_PORT_LINK_UP)
|
|
ret = (int)port_mgr->pfn_ll_port_config_get(lport->fc_port,
|
|
UNF_PORT_CFG_GET_SPEED_ACT, (void *)speed);
|
|
else
|
|
*speed = UNF_PORT_SPEED_UNKNOWN;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int unf_cm_get_port_speed(unsigned int v_port_id, unsigned int *v_speed)
|
|
{
|
|
UNF_CHECK_VALID(0x2270, UNF_TRUE, v_speed, return UNF_RETURN_ERROR);
|
|
|
|
return unf_send_event(v_port_id, UNF_EVENT_SYN, (void *)&v_port_id,
|
|
(void *)v_speed, unf_get_port_speed);
|
|
}
|
|
|
|
static int unf_set_port_speed(void *v_argc_in, void *v_argc_out)
|
|
{
|
|
unsigned int ret = RETURN_OK;
|
|
struct unf_set_speed_argin *in =
|
|
(struct unf_set_speed_argin *)v_argc_in;
|
|
struct unf_lport_s *lport = NULL;
|
|
|
|
UNF_REFERNCE_VAR(v_argc_out);
|
|
UNF_CHECK_VALID(0x2271, UNF_TRUE, v_argc_in, return UNF_RETURN_ERROR);
|
|
lport = unf_find_lport_by_port_id(in->port_id);
|
|
if (!lport) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT, UNF_MAJOR,
|
|
"Cannot Find LPort by (0x%x).", in->port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
if (!lport->low_level_func.port_mgr_op.pfn_ll_port_config_set) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT, UNF_MAJOR,
|
|
"Port(0x%x)'s corresponding function is NULL.",
|
|
in->port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
ret = lport->low_level_func.port_mgr_op.pfn_ll_port_config_set(
|
|
lport->fc_port,
|
|
UNF_PORT_CFG_SET_SPEED, (void *)in->speed);
|
|
|
|
return (int)ret;
|
|
}
|
|
|
|
int unf_cm_set_port_speed(unsigned int v_port_id, unsigned int *v_speed)
|
|
{
|
|
struct unf_set_speed_argin in = { 0 };
|
|
|
|
in.port_id = v_port_id;
|
|
in.speed = v_speed;
|
|
return unf_send_event(v_port_id, UNF_EVENT_SYN, (void *)&in,
|
|
(void *)NULL, unf_set_port_speed);
|
|
}
|
|
|
|
static int unf_get_port_topo(void *argc_in, void *argc_out)
|
|
{
|
|
struct unf_lport_s *lport = NULL;
|
|
struct unf_get_topo_argout *out = NULL;
|
|
struct unf_low_level_port_mgr_op_s *port_mgr = NULL;
|
|
int ret = UNF_TRUE;
|
|
unsigned int port_id = 0;
|
|
|
|
UNF_CHECK_VALID(0x2283, UNF_TRUE, argc_in, return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(0x2284, UNF_TRUE, argc_out, return UNF_RETURN_ERROR);
|
|
port_id = *(unsigned int *)argc_in;
|
|
out = (struct unf_get_topo_argout *)argc_out;
|
|
|
|
lport = unf_find_lport_by_port_id(port_id);
|
|
if (!lport) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT, UNF_MAJOR,
|
|
"Not find LPort(0x%x).", port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
port_mgr = &lport->low_level_func.port_mgr_op;
|
|
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE,
|
|
port_mgr->pfn_ll_port_config_get,
|
|
return UNF_RETURN_ERROR);
|
|
|
|
if (lport->link_up == UNF_PORT_LINK_UP) {
|
|
ret = (int)port_mgr->pfn_ll_port_config_get(lport->fc_port,
|
|
UNF_PORT_CFG_GET_TOPO_ACT, (void *)out->en_act_topo);
|
|
if (ret != RETURN_OK)
|
|
return ret;
|
|
|
|
} else {
|
|
*out->en_act_topo = UNF_ACT_TOP_UNKNOWN;
|
|
}
|
|
|
|
ret = (int)port_mgr->pfn_ll_port_config_get(lport->fc_port,
|
|
UNF_PORT_CFG_GET_TOPO_CFG, (void *)out->topo_cfg);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int unf_cm_get_port_topo(unsigned int v_port_id, unsigned int *v_topo_cfg,
|
|
enum unf_act_topo_e *v_en_act_topo)
|
|
{
|
|
struct unf_get_topo_argout out = { 0 };
|
|
|
|
UNF_CHECK_VALID(0x2286, UNF_TRUE, v_topo_cfg, return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(0x2287, UNF_TRUE, v_en_act_topo,
|
|
return UNF_RETURN_ERROR);
|
|
|
|
out.en_act_topo = v_en_act_topo;
|
|
out.topo_cfg = v_topo_cfg;
|
|
|
|
return unf_send_event(v_port_id, UNF_EVENT_SYN, (void *)&v_port_id,
|
|
(void *)&out, unf_get_port_topo);
|
|
}
|
|
|
|
static int unf_set_port_topo(void *argc_in, void *argc_out)
|
|
{
|
|
struct unf_lport_s *lport = NULL;
|
|
struct unf_set_topo_argin *in = NULL;
|
|
enum int_e *b_arg_out = (enum int_e *)argc_out;
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
|
|
UNF_CHECK_VALID(0x2257, UNF_TRUE, argc_out, return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(0x2288, UNF_TRUE, argc_in, return UNF_RETURN_ERROR);
|
|
in = (struct unf_set_topo_argin *)argc_in;
|
|
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE,
|
|
(in->topo == UNF_TOP_LOOP_MASK) ||
|
|
(in->topo == UNF_TOP_P2P_MASK) ||
|
|
(in->topo == UNF_TOP_AUTO_MASK),
|
|
return UNF_RETURN_ERROR);
|
|
|
|
lport = unf_find_lport_by_port_id(in->port_id);
|
|
if (!lport) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT, UNF_MAJOR,
|
|
"Not find LPort(0x%x).", in->port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
UNF_CHECK_VALID(
|
|
INVALID_VALUE32, UNF_TRUE,
|
|
lport->low_level_func.port_mgr_op.pfn_ll_port_config_set,
|
|
return UNF_RETURN_ERROR);
|
|
|
|
ret = lport->low_level_func.port_mgr_op.pfn_ll_port_config_set(
|
|
lport->fc_port,
|
|
UNF_PORT_CFG_SET_TOPO, (void *)&in->topo);
|
|
if (ret != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT, UNF_MAJOR,
|
|
"Can't set port topology.");
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
lport->low_level_func.lport_cfg_items.port_topology = in->topo;
|
|
*b_arg_out = lport->b_switch_state;
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
int unf_cm_set_port_topo(unsigned int v_port_id, unsigned int v_topo)
|
|
{
|
|
struct unf_set_topo_argin in = { 0 };
|
|
int ret = UNF_RETURN_ERROR;
|
|
enum int_e b_switch_state = UNF_FALSE;
|
|
|
|
in.port_id = v_port_id;
|
|
in.topo = v_topo;
|
|
|
|
ret = unf_send_event(v_port_id, UNF_EVENT_SYN, (void *)&in,
|
|
(void *)&b_switch_state, unf_set_port_topo);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int unf_set_port_bbscn(void *argc_in, void *argc_out)
|
|
{
|
|
struct unf_lport_s *lport = NULL;
|
|
struct unf_set_bbscn_argin *in = NULL;
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
|
|
UNF_REFERNCE_VAR(argc_out);
|
|
UNF_CHECK_VALID(0x2300, UNF_TRUE, argc_in, return UNF_RETURN_ERROR);
|
|
in = (struct unf_set_bbscn_argin *)argc_in;
|
|
|
|
lport = unf_find_lport_by_port_id(in->port_id);
|
|
if (!lport) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT, UNF_MAJOR,
|
|
"Not find LPort(0x%x).", in->port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
UNF_CHECK_VALID(
|
|
INVALID_VALUE32, UNF_TRUE,
|
|
lport->low_level_func.port_mgr_op.pfn_ll_port_config_set,
|
|
return UNF_RETURN_ERROR);
|
|
|
|
ret = lport->low_level_func.port_mgr_op.pfn_ll_port_config_set(
|
|
lport->fc_port,
|
|
UNF_PORT_CFG_SET_BBSCN, (void *)&in->bb_scn);
|
|
if (ret != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT, UNF_MAJOR,
|
|
"Cannot set port BB_SC_N.");
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
/* update bbsn cfg to Lport */
|
|
lport->low_level_func.lport_cfg_items.bb_scn = in->bb_scn;
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
int unf_cm_set_port_bbscn(unsigned int v_port_id, unsigned int v_bbscn)
|
|
{
|
|
struct unf_set_bbscn_argin in = { 0 };
|
|
|
|
in.port_id = v_port_id;
|
|
in.bb_scn = v_bbscn;
|
|
|
|
return unf_send_event(v_port_id, UNF_EVENT_SYN, (void *)&in,
|
|
(void *)NULL, unf_set_port_bbscn);
|
|
}
|
|
|
|
unsigned int unf_get_error_code_sum(struct unf_lport_s *v_lport,
|
|
struct unf_err_code_s *v_fc_err_code)
|
|
{
|
|
struct unf_low_level_port_mgr_op_s *port_mgr = NULL;
|
|
struct unf_lport_s *lport = v_lport;
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
struct unf_err_code_s fc_err_code;
|
|
|
|
UNF_CHECK_VALID(0x2328, UNF_TRUE, v_lport, return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(0x2329, UNF_TRUE, v_fc_err_code,
|
|
return UNF_RETURN_ERROR);
|
|
|
|
memset(&fc_err_code, 0, sizeof(struct unf_err_code_s));
|
|
|
|
port_mgr = &lport->low_level_func.port_mgr_op;
|
|
if (!port_mgr->pfn_ll_port_config_get) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT, UNF_MAJOR,
|
|
"Port(0x%x)'s corresponding function is NULL.",
|
|
lport->port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
ret = port_mgr->pfn_ll_port_config_get((void *)lport->fc_port,
|
|
UNF_PORT_CFG_GET_LESB_THEN_CLR, (void *)&fc_err_code);
|
|
if (ret != RETURN_OK)
|
|
return UNF_RETURN_ERROR;
|
|
|
|
if (lport->link_up != UNF_PORT_LINK_UP) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT, UNF_INFO,
|
|
"LPort(0x%x) is not link up.", lport->port_id);
|
|
memcpy(v_fc_err_code, &lport->err_code_sum,
|
|
sizeof(struct unf_err_code_s));
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
lport->err_code_sum.bad_rx_char_count += fc_err_code.bad_rx_char_count;
|
|
lport->err_code_sum.link_fail_count += fc_err_code.link_fail_count;
|
|
lport->err_code_sum.loss_of_signal_count +=
|
|
fc_err_code.loss_of_signal_count;
|
|
lport->err_code_sum.loss_of_sync_count +=
|
|
fc_err_code.loss_of_sync_count;
|
|
lport->err_code_sum.proto_error_count += fc_err_code.proto_error_count;
|
|
|
|
lport->err_code_sum.rx_eo_fa_count = fc_err_code.rx_eo_fa_count;
|
|
lport->err_code_sum.dis_frame_count = fc_err_code.dis_frame_count;
|
|
lport->err_code_sum.bad_crc_count = fc_err_code.bad_crc_count;
|
|
|
|
memcpy(v_fc_err_code, &lport->err_code_sum,
|
|
sizeof(struct unf_err_code_s));
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
static int unf_clear_port_error_code_sum(void *argc_in, void *argc_out)
|
|
{
|
|
struct unf_lport_s *lport = NULL;
|
|
unsigned int port_id = 0;
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
|
|
UNF_CHECK_VALID(0x2331, UNF_TRUE, argc_in, return UNF_RETURN_ERROR);
|
|
UNF_REFERNCE_VAR(argc_out);
|
|
|
|
port_id = *(unsigned int *)argc_in;
|
|
lport = unf_find_lport_by_port_id(port_id);
|
|
if (!lport) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT, UNF_MAJOR,
|
|
"Cannot find LPort(0x%x).", port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
if (!lport->low_level_func.port_mgr_op.pfn_ll_port_config_get) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT, UNF_MAJOR,
|
|
"Port(0x%x)'s corresponding function is NULL.",
|
|
port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
ret = lport->low_level_func.port_mgr_op.pfn_ll_port_config_get(
|
|
(void *)lport->fc_port,
|
|
UNF_PORT_CFG_CLR_LESB, NULL);
|
|
if (ret != RETURN_OK)
|
|
return UNF_RETURN_ERROR;
|
|
|
|
memset(&lport->err_code_sum, 0, sizeof(struct unf_err_code_s));
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
int unf_cm_clear_port_error_code_sum(unsigned int v_port_id)
|
|
{
|
|
return unf_send_event(v_port_id, UNF_EVENT_SYN, (void *)&v_port_id,
|
|
(void *)NULL, unf_clear_port_error_code_sum);
|
|
}
|
|
|
|
static int unf_update_lport_sfp_info(struct unf_lport_s *v_lport,
|
|
enum unf_port_config_get_op_e v_type)
|
|
{
|
|
struct unf_lport_s *lport = NULL;
|
|
int ret = UNF_RETURN_ERROR;
|
|
|
|
UNF_CHECK_VALID(0x2332, UNF_TRUE, v_lport, return UNF_RETURN_ERROR);
|
|
lport = v_lport;
|
|
|
|
if (!lport->low_level_func.port_mgr_op.pfn_ll_port_config_get) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT, UNF_MAJOR,
|
|
"Port(0x%x)'s corresponding function is NULL.",
|
|
lport->port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
ret = (int)(lport->low_level_func.port_mgr_op.pfn_ll_port_config_get(
|
|
(void *)lport->fc_port,
|
|
v_type, (void *)&lport->sfp_info));
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int unf_translate_sfp_status(struct unf_lport_s *v_lport,
|
|
struct unf_get_sfp_argout *v_out)
|
|
{
|
|
struct unf_lport_s *lport = v_lport;
|
|
int ret = UNF_RETURN_ERROR;
|
|
|
|
switch (lport->sfp_info.status) {
|
|
case UNF_SFP_PRESENT_FAIL:
|
|
*v_out->status = DRV_CABLE_CONNECTOR_NONE;
|
|
ret = RETURN_OK;
|
|
break;
|
|
case UNF_SFP_POWER_FAIL:
|
|
*v_out->status = DRV_CABLE_CONNECTOR_INVALID;
|
|
ret = RETURN_OK;
|
|
break;
|
|
case UNF_9545_FAIL:
|
|
*v_out->status = DRV_CABLE_CONNECTOR_INVALID;
|
|
ret = RETURN_OK;
|
|
break;
|
|
default:
|
|
*v_out->status = DRV_CABLE_CONNECTOR_BUTT;
|
|
ret = UNF_RETURN_ERROR;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void unf_record_chip_fault(struct unf_lport_s *v_lport)
|
|
{
|
|
#define UNF_CHIP_FAULT_MAX_CHECK_TIME 3
|
|
|
|
if (v_lport->sfp_info.status == UNF_9545_FAIL) {
|
|
/* If there are 9545 fault,explain that the sfp is power on,
|
|
* and reset sfp_power_fault_count
|
|
*/
|
|
v_lport->sfp_power_fault_count = 0;
|
|
|
|
if (v_lport->sfp_9545_fault_count <
|
|
UNF_CHIP_FAULT_MAX_CHECK_TIME) {
|
|
v_lport->sfp_9545_fault_count++;
|
|
} else {
|
|
v_lport->chip_info->sfp_9545_fault = UNF_TRUE;
|
|
v_lport->sfp_9545_fault_count = 0;
|
|
}
|
|
} else if (v_lport->sfp_info.status == UNF_SFP_POWER_FAIL) {
|
|
if (v_lport->sfp_power_fault_count <
|
|
UNF_CHIP_FAULT_MAX_CHECK_TIME) {
|
|
v_lport->sfp_power_fault_count++;
|
|
} else {
|
|
v_lport->chip_info->sfp_power_fault = UNF_TRUE;
|
|
v_lport->sfp_power_fault_count = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
int unf_check_sfp_tx_fault(struct unf_lport_s *v_lport,
|
|
struct unf_sfp_info_s *v_sfp_info)
|
|
{
|
|
/* 24 hours ms(24*60*60*1000) */
|
|
#define UNF_SFP_TXFALT_RECOVER_INTERVEL 86400000
|
|
|
|
struct unf_sfp_info_s *sfp_info = NULL;
|
|
struct unf_lport_s *lport = NULL;
|
|
|
|
sfp_info = v_sfp_info;
|
|
lport = v_lport;
|
|
|
|
if (sfp_info->sfp_info_a2.diag.status_ctrl.tx_fault_state == 0)
|
|
return RETURN_OK;
|
|
|
|
/* Repair conditions:
|
|
* 1 port linkdown;
|
|
* 2 from the last repair more than 24 hours;
|
|
* 3 sfp is on
|
|
*/
|
|
if ((lport->link_up == UNF_PORT_LINK_DOWN) &&
|
|
(lport->b_switch_state) &&
|
|
((lport->last_tx_fault_jif == 0) ||
|
|
(jiffies_to_msecs(jiffies - lport->last_tx_fault_jif) >
|
|
UNF_SFP_TXFALT_RECOVER_INTERVEL))) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT, UNF_MAJOR,
|
|
"LPort(0x%x) stat(0x%x) jiff(%ld) lastjiff(%llu) Ctrl(0x%x) TxFault set 1.",
|
|
lport->port_id, lport->link_up, jiffies,
|
|
lport->last_tx_fault_jif,
|
|
*((unsigned char *)
|
|
&sfp_info->sfp_info_a2.diag.status_ctrl));
|
|
|
|
lport->last_tx_fault_jif = jiffies;
|
|
(void)unf_sfp_switch(lport->port_id, UNF_FALSE);
|
|
msleep(100);
|
|
|
|
/* Around quickly switch port FW state error problem */
|
|
(void)unf_sfp_switch(lport->port_id, UNF_TRUE);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
static int unf_get_sfp_info(void *argc_in, void *argc_out)
|
|
{
|
|
struct unf_lport_s *lport = NULL;
|
|
struct unf_get_sfp_argout *out = NULL;
|
|
unsigned int port_id = 0;
|
|
int ret = RETURN_OK;
|
|
|
|
UNF_CHECK_VALID(0x2333, UNF_TRUE, argc_in, return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(0x2334, UNF_TRUE, argc_out, return UNF_RETURN_ERROR);
|
|
|
|
port_id = *(unsigned int *)argc_in;
|
|
out = (struct unf_get_sfp_argout *)argc_out;
|
|
lport = unf_find_lport_by_port_id(port_id);
|
|
if (!lport) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT, UNF_MAJOR,
|
|
"Cannot find LPort(0x%x).", port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
lport->sfp_info.status = 0;
|
|
|
|
ret = unf_update_lport_sfp_info(lport, UNF_PORT_CFG_GET_SFP_INFO);
|
|
|
|
if (ret == RETURN_OK) {
|
|
lport->sfp_power_fault_count = 0;
|
|
lport->sfp_9545_fault_count = 0;
|
|
*out->status = DRV_CABLE_CONNECTOR_OPTICAL;
|
|
if (unf_check_sfp_tx_fault(
|
|
lport,
|
|
&lport->sfp_info.sfp_eeprom_info.sfp_info) ==
|
|
UNF_RETURN_ERROR) {
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
memcpy(out->sfp_info, &lport->sfp_info.sfp_eeprom_info,
|
|
sizeof(union unf_sfp_eeprome_info));
|
|
ret = RETURN_OK;
|
|
} else {
|
|
ret = unf_translate_sfp_status(lport, out);
|
|
|
|
unf_record_chip_fault(lport);
|
|
|
|
UNF_TRACE(UNF_EVTLOG_IO_INFO, UNF_LOG_IO_ATT, UNF_MAJOR,
|
|
"Port(0x%x)'s getsfpinfo fail, sfp status(0x%x).",
|
|
lport->port_id, lport->sfp_info.status);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int unf_cm_get_sfp_info(unsigned int v_port_id, unsigned int *v_status,
|
|
union unf_sfp_eeprome_info *v_sfp_info,
|
|
unsigned int *sfp_type)
|
|
{
|
|
struct unf_lport_s *lport = NULL;
|
|
struct unf_get_sfp_argout out = { 0 };
|
|
|
|
lport = unf_find_lport_by_port_id(v_port_id);
|
|
if (!lport)
|
|
return UNF_RETURN_ERROR;
|
|
|
|
UNF_CHECK_VALID(0x2335, UNF_TRUE, v_status, return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(0x2336, UNF_TRUE, v_sfp_info, return UNF_RETURN_ERROR);
|
|
|
|
out.status = v_status;
|
|
out.sfp_info = v_sfp_info;
|
|
|
|
if (global_lport_mgr.b_start_work == UNF_FALSE) {
|
|
UNF_TRACE(UNF_EVTLOG_IO_INFO, UNF_LOG_IO_ATT, UNF_MAJOR,
|
|
"Port(0x%x) have not start work, return.", v_port_id);
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
*sfp_type = lport->low_level_func.sfp_type;
|
|
return unf_send_event(v_port_id, UNF_EVENT_SYN, (void *)&v_port_id,
|
|
(void *)&out, unf_get_sfp_info);
|
|
}
|
|
|
|
int unf_cm_reset_port(unsigned int v_port_id)
|
|
{
|
|
int ret = UNF_RETURN_ERROR;
|
|
|
|
ret = unf_send_event(v_port_id, UNF_EVENT_SYN, (void *)&v_port_id,
|
|
(void *)NULL, unf_reset_port);
|
|
return ret;
|
|
}
|
|
|
|
int unf_lport_reset_port(struct unf_lport_s *v_lport, unsigned int v_flag)
|
|
{
|
|
UNF_CHECK_VALID(0x2352, UNF_TRUE, v_lport, return UNF_RETURN_ERROR);
|
|
|
|
return unf_send_event(v_lport->port_id, v_flag,
|
|
(void *)&v_lport->port_id,
|
|
(void *)NULL,
|
|
unf_reset_port);
|
|
}
|
|
|
|
static inline unsigned int unf_get_loop_alpa(struct unf_lport_s *v_lport,
|
|
void *v_loop_alpa)
|
|
{
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
|
|
UNF_CHECK_VALID(0x2357, UNF_TRUE,
|
|
v_lport->low_level_func.port_mgr_op.pfn_ll_port_config_get,
|
|
return UNF_RETURN_ERROR);
|
|
|
|
ret = v_lport->low_level_func.port_mgr_op.pfn_ll_port_config_get(
|
|
v_lport->fc_port,
|
|
UNF_PORT_CFG_GET_LOOP_ALPA, v_loop_alpa);
|
|
return ret;
|
|
}
|
|
|
|
static unsigned int unf_lport_enter_private_loop_login(
|
|
struct unf_lport_s *v_lport)
|
|
{
|
|
struct unf_lport_s *lport = v_lport;
|
|
unsigned long flag = 0;
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
|
|
UNF_CHECK_VALID(0x2358, UNF_TRUE, v_lport, return UNF_RETURN_ERROR);
|
|
|
|
spin_lock_irqsave(&lport->lport_state_lock, flag);
|
|
unf_lport_stat_ma(lport, UNF_EVENT_LPORT_READY);
|
|
/* LPort: LINK_UP --> READY */
|
|
spin_unlock_irqrestore(&lport->lport_state_lock, flag);
|
|
|
|
unf_lport_update_topo(lport, UNF_ACT_TOP_PRIVATE_LOOP);
|
|
|
|
/* NOP: check L_Port state */
|
|
if (atomic_read(&lport->port_no_operater_flag) == UNF_LPORT_NOP) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR,
|
|
"[info]Port(0x%x) is NOP, do nothing",
|
|
lport->port_id);
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
/* INI: check L_Port mode */
|
|
if ((lport->options & UNF_PORT_MODE_INI) != UNF_PORT_MODE_INI) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR,
|
|
"[info]Port(0x%x) has no INI feature(0x%x), do nothing",
|
|
lport->port_id, lport->options);
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
if (lport->disc.unf_disc_temp.pfn_unf_disc_start) {
|
|
ret = lport->disc.unf_disc_temp.pfn_unf_disc_start(lport);
|
|
if (ret != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT,
|
|
UNF_WARN,
|
|
"[warn]Port(0x%x) with nportid(0x%x) start discovery failed",
|
|
lport->port_id, lport->nport_id);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
unsigned int unf_lport_login(struct unf_lport_s *v_lport,
|
|
enum unf_act_topo_e v_en_act_topo)
|
|
{
|
|
unsigned int loop_alpa = 0;
|
|
unsigned int ret = RETURN_OK;
|
|
unsigned long flag = 0;
|
|
|
|
UNF_CHECK_VALID(0x2359, UNF_TRUE, v_lport, return UNF_RETURN_ERROR);
|
|
|
|
/* 1. Update (set) L_Port topo which get from low level */
|
|
unf_lport_update_topo(v_lport, v_en_act_topo);
|
|
|
|
spin_lock_irqsave(&v_lport->lport_state_lock, flag);
|
|
|
|
/* 2. Link state check */
|
|
if (v_lport->link_up != UNF_PORT_LINK_UP) {
|
|
spin_unlock_irqrestore(&v_lport->lport_state_lock, flag);
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN,
|
|
"[warn]Port(0x%x) with link_state(0x%x) port_state(0x%x) when login",
|
|
v_lport->port_id, v_lport->link_up,
|
|
v_lport->en_states);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
/* 3. Update L_Port state */
|
|
unf_lport_stat_ma(v_lport, UNF_EVENT_LPORT_LINK_UP);
|
|
/* LPort: INITIAL --> LINK UP */
|
|
spin_unlock_irqrestore(&v_lport->lport_state_lock, flag);
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO,
|
|
"[info]LOGIN: Port(0x%x) start to login with topology(0x%x)",
|
|
v_lport->port_id, v_lport->en_act_topo);
|
|
|
|
/* 4. Start logoin */
|
|
if ((v_en_act_topo == UNF_TOP_P2P_MASK) ||
|
|
(v_en_act_topo == UNF_ACT_TOP_P2P_FABRIC) ||
|
|
(v_en_act_topo == UNF_ACT_TOP_P2P_DIRECT)) {
|
|
/* P2P or Fabric mode */
|
|
ret = unf_lport_enter_flogi(v_lport);
|
|
} else if (v_en_act_topo == UNF_ACT_TOP_PUBLIC_LOOP) {
|
|
/* Public loop */
|
|
(void)unf_get_loop_alpa(v_lport, &loop_alpa);
|
|
|
|
/* Before FLOGI ALPA just low 8 bit after FLOGI ACC switch
|
|
* will assign complete addresses
|
|
*/
|
|
spin_lock_irqsave(&v_lport->lport_state_lock, flag);
|
|
v_lport->nport_id = loop_alpa;
|
|
spin_unlock_irqrestore(&v_lport->lport_state_lock, flag);
|
|
|
|
ret = unf_lport_enter_flogi(v_lport);
|
|
} else if (v_en_act_topo == UNF_ACT_TOP_PRIVATE_LOOP) {
|
|
/* Private loop */
|
|
(void)unf_get_loop_alpa(v_lport, &loop_alpa);
|
|
|
|
spin_lock_irqsave(&v_lport->lport_state_lock, flag);
|
|
v_lport->nport_id = loop_alpa;
|
|
spin_unlock_irqrestore(&v_lport->lport_state_lock, flag);
|
|
|
|
ret = unf_lport_enter_private_loop_login(v_lport);
|
|
} else {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR,
|
|
"[err]LOGIN: Port(0x%x) login with unknown topology(0x%x)",
|
|
v_lport->port_id, v_lport->en_act_topo);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static unsigned int unf_port_link_up(struct unf_lport_s *v_lport,
|
|
void *v_in_put)
|
|
{
|
|
struct unf_lport_s *lport = v_lport;
|
|
unsigned int ret = RETURN_OK;
|
|
enum unf_act_topo_e en_act_topo = UNF_ACT_TOP_UNKNOWN;
|
|
unsigned long flag = 0;
|
|
|
|
UNF_CHECK_VALID(0x2361, UNF_TRUE, v_lport, return UNF_RETURN_ERROR);
|
|
UNF_REFERNCE_VAR(v_in_put);
|
|
|
|
/* If NOP state, stop */
|
|
if (atomic_read(&lport->port_no_operater_flag) == UNF_LPORT_NOP) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR,
|
|
"[warn]Port(0x%x) is NOP and do nothing",
|
|
lport->port_id);
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
/* Update port state */
|
|
spin_lock_irqsave(&lport->lport_state_lock, flag);
|
|
lport->link_up = UNF_PORT_LINK_UP;
|
|
lport->speed = *((unsigned int *)v_in_put);
|
|
unf_set_lport_state(v_lport, UNF_LPORT_ST_INITIAL);
|
|
/* INITIAL state */
|
|
spin_unlock_irqrestore(&lport->lport_state_lock, flag);
|
|
|
|
/* set hot pool wait state: so far, do not care */
|
|
unf_set_hot_pool_wait_state(lport, UNF_TRUE);
|
|
|
|
lport->enhanced_features |= UNF_LPORT_ENHANCED_FEATURE_READ_SFP_ONCE;
|
|
|
|
/* Get port active topopolgy (from low level) */
|
|
if (!lport->low_level_func.port_mgr_op.pfn_ll_port_config_get) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR,
|
|
"[warn]Port(0x%x) get topo function is NULL",
|
|
lport->port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
ret = lport->low_level_func.port_mgr_op.pfn_ll_port_config_get(
|
|
lport->fc_port,
|
|
UNF_PORT_CFG_GET_TOPO_ACT, (void *)&en_act_topo);
|
|
|
|
if (ret != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_ERR,
|
|
"[warn]Port(0x%x) get topo from low level failed",
|
|
lport->port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
/* Start Login process */
|
|
ret = unf_lport_login(lport, en_act_topo);
|
|
|
|
unf_report_io_dm_event(lport, UNF_PORT_LINK_UP, 0);
|
|
return ret;
|
|
}
|
|
|
|
static unsigned int unf_port_link_down(struct unf_lport_s *v_lport,
|
|
void *v_in_put)
|
|
{
|
|
unsigned long flag = 0;
|
|
struct unf_lport_s *lport = NULL;
|
|
|
|
UNF_CHECK_VALID(0x2363, UNF_FALSE, v_lport, return UNF_RETURN_ERROR);
|
|
UNF_REFERNCE_VAR(v_in_put);
|
|
lport = v_lport;
|
|
unf_report_io_dm_event(lport, UNF_PORT_LINK_DOWN, 0);
|
|
|
|
/* To prevent repeated reporting linkdown */
|
|
spin_lock_irqsave(&lport->lport_state_lock, flag);
|
|
lport->speed = UNF_PORT_SPEED_UNKNOWN;
|
|
lport->en_act_topo = UNF_ACT_TOP_UNKNOWN;
|
|
if (lport->link_up == UNF_PORT_LINK_DOWN) {
|
|
spin_unlock_irqrestore(&lport->lport_state_lock, flag);
|
|
|
|
return RETURN_OK;
|
|
}
|
|
unf_lport_stat_ma(lport, UNF_EVENT_LPORT_LINK_DOWN);
|
|
unf_reset_lport_params(lport);
|
|
spin_unlock_irqrestore(&lport->lport_state_lock, flag);
|
|
|
|
unf_set_hot_pool_wait_state(lport, UNF_FALSE);
|
|
|
|
/*
|
|
* clear I/O:
|
|
* 1. INI do ABORT only,
|
|
* for INI: busy/delay/delay_transfer/wait
|
|
* Clean L_Port/V_Port Link Down I/O: only set ABORT tag
|
|
*/
|
|
unf_flush_disc_event(&lport->disc, NULL);
|
|
|
|
unf_clean_link_down_io(lport, UNF_FALSE);
|
|
|
|
/* for L_Port's R_Ports */
|
|
unf_clean_linkdown_rport(lport);
|
|
/* for L_Port's all Vports */
|
|
unf_linkdown_all_vports(v_lport);
|
|
return RETURN_OK;
|
|
}
|
|
|
|
static unsigned int unf_port_abnormal_reset(struct unf_lport_s *v_lport,
|
|
void *v_in_put)
|
|
{
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
struct unf_lport_s *lport = NULL;
|
|
|
|
UNF_CHECK_VALID(0x2363, UNF_FALSE, v_lport, return UNF_RETURN_ERROR);
|
|
|
|
UNF_REFERNCE_VAR(v_in_put);
|
|
|
|
lport = v_lport;
|
|
|
|
ret = (unsigned int)unf_lport_reset_port(lport, UNF_EVENT_ASYN);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static unsigned int unf_port_reset_start(struct unf_lport_s *v_lport,
|
|
void *v_in_put)
|
|
{
|
|
unsigned int ret = RETURN_OK;
|
|
unsigned long flag = 0;
|
|
|
|
UNF_CHECK_VALID(0x2364, UNF_FALSE, v_lport, return UNF_RETURN_ERROR);
|
|
UNF_REFERNCE_VAR(v_in_put);
|
|
|
|
spin_lock_irqsave(&v_lport->lport_state_lock, flag);
|
|
unf_set_lport_state(v_lport, UNF_LPORT_ST_RESET);
|
|
spin_unlock_irqrestore(&v_lport->lport_state_lock, flag);
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO,
|
|
"Port(0x%x) begin to reset.", v_lport->port_id);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static unsigned int unf_port_reset_end(struct unf_lport_s *v_lport,
|
|
void *v_in_put)
|
|
{
|
|
unsigned long flag = 0;
|
|
|
|
UNF_CHECK_VALID(0x2365, UNF_FALSE, v_lport, return UNF_RETURN_ERROR);
|
|
|
|
UNF_REFERNCE_VAR(v_in_put);
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO,
|
|
"Port(0x%x) reset end.", v_lport->port_id);
|
|
|
|
/* Task management command returns success and avoid
|
|
* repair measures case offline device
|
|
*/
|
|
unf_wakeup_scsi_task_cmnd(v_lport);
|
|
|
|
spin_lock_irqsave(&v_lport->lport_state_lock, flag);
|
|
unf_set_lport_state(v_lport, UNF_LPORT_ST_INITIAL);
|
|
spin_unlock_irqrestore(&v_lport->lport_state_lock, flag);
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
static unsigned int unf_port_nop(struct unf_lport_s *v_lport, void *v_in_put)
|
|
{
|
|
struct unf_lport_s *lport = NULL;
|
|
unsigned long flag = 0;
|
|
|
|
UNF_CHECK_VALID(0x2366, UNF_FALSE, v_lport, return UNF_RETURN_ERROR);
|
|
UNF_REFERNCE_VAR(v_in_put);
|
|
lport = v_lport;
|
|
|
|
atomic_set(&lport->port_no_operater_flag, UNF_LPORT_NOP);
|
|
|
|
spin_lock_irqsave(&lport->lport_state_lock, flag);
|
|
unf_lport_stat_ma(lport, UNF_EVENT_LPORT_LINK_DOWN);
|
|
unf_reset_lport_params(lport);
|
|
spin_unlock_irqrestore(&lport->lport_state_lock, flag);
|
|
|
|
/* Set Tag prevent pending I/O to wait_list when close sfp failed */
|
|
unf_set_hot_pool_wait_state(lport, UNF_FALSE);
|
|
|
|
unf_flush_disc_event(&lport->disc, NULL);
|
|
|
|
/* L_Port/V_Port's I/O(s): Clean Link Down I/O: Set Abort Tag */
|
|
unf_clean_link_down_io(lport, UNF_FALSE);
|
|
|
|
/* L_Port/V_Port's R_Port(s): report link down event to
|
|
* scsi & clear resource
|
|
*/
|
|
unf_clean_linkdown_rport(lport);
|
|
unf_linkdown_all_vports(lport);
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR,
|
|
"[info]Port(0x%x) report NOP event done",
|
|
lport->nport_id);
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
static unsigned int unf_port_clean_done(struct unf_lport_s *v_lport,
|
|
void *v_in_put)
|
|
{
|
|
UNF_CHECK_VALID(0x2691, UNF_FALSE, v_lport, return UNF_RETURN_ERROR);
|
|
|
|
UNF_REFERNCE_VAR(v_in_put);
|
|
|
|
/* when port reset,delte delete all Rport immediately,
|
|
* in order to remove immediately for resources
|
|
*/
|
|
unf_clean_linkdown_rport(v_lport);
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
static unsigned int unf_port_begin_remove(struct unf_lport_s *v_lport,
|
|
void *v_in_put)
|
|
{
|
|
UNF_CHECK_VALID(0x2691, UNF_FALSE, v_lport, return UNF_RETURN_ERROR);
|
|
|
|
UNF_REFERNCE_VAR(v_in_put);
|
|
|
|
/* Cancel route timer delay work */
|
|
unf_destroy_lport_route(v_lport);
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
static unsigned int unf_get_pcie_link_state(struct unf_lport_s *v_lport)
|
|
{
|
|
struct unf_lport_s *lport = v_lport;
|
|
int link_state = UNF_TRUE;
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
|
|
UNF_CHECK_VALID(0x2257, UNF_TRUE, v_lport, return UNF_RETURN_ERROR);
|
|
|
|
UNF_CHECK_VALID(
|
|
INVALID_VALUE32, UNF_TRUE,
|
|
lport->low_level_func.port_mgr_op.pfn_ll_port_config_get,
|
|
return UNF_RETURN_ERROR);
|
|
|
|
ret = lport->low_level_func.port_mgr_op.pfn_ll_port_config_get(
|
|
lport->fc_port,
|
|
UNF_PORT_CFG_GET_PCIE_LINK_STATE, (void *)&link_state);
|
|
if (ret != RETURN_OK || link_state != UNF_TRUE) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT, UNF_KEVENT,
|
|
"[err]Can't Get Pcie Link State");
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
void unf_root_lport_ref_dec(struct unf_lport_s *v_lport)
|
|
{
|
|
unsigned long flags = 0;
|
|
unsigned long lport_flags = 0;
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
|
|
UNF_CHECK_VALID(0x2385, UNF_TRUE, v_lport, return);
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO,
|
|
"[info]Port(0x%p) port_id(0x%x) reference count is %d",
|
|
v_lport, v_lport->port_id,
|
|
atomic_read(&v_lport->lport_ref_cnt));
|
|
|
|
spin_lock_irqsave(&global_lport_mgr.global_lport_list_lock, flags);
|
|
spin_lock_irqsave(&v_lport->lport_state_lock, lport_flags);
|
|
if (atomic_dec_and_test(&v_lport->lport_ref_cnt)) {
|
|
spin_unlock_irqrestore(&v_lport->lport_state_lock, lport_flags);
|
|
|
|
list_del(&v_lport->entry_lport);
|
|
global_lport_mgr.lport_sum--;
|
|
|
|
/* Put L_Port to destroy list for debuging */
|
|
list_add_tail(&v_lport->entry_lport,
|
|
&global_lport_mgr.list_destroy_head);
|
|
spin_unlock_irqrestore(&global_lport_mgr.global_lport_list_lock,
|
|
flags);
|
|
|
|
ret = unf_schedule_global_event((void *)v_lport,
|
|
UNF_GLOBAL_EVENT_ASYN,
|
|
unf_lport_destroy);
|
|
if (ret != RETURN_OK)
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_EVENT,
|
|
UNF_CRITICAL,
|
|
"[warn]Schedule global event faile. remain nodes(0x%x)",
|
|
global_event_queue.list_number);
|
|
} else {
|
|
spin_unlock_irqrestore(&v_lport->lport_state_lock, lport_flags);
|
|
spin_unlock_irqrestore(&global_lport_mgr.global_lport_list_lock,
|
|
flags);
|
|
}
|
|
}
|
|
|
|
void unf_lport_ref_dec_to_destroy(struct unf_lport_s *v_lport)
|
|
{
|
|
if (v_lport->root_lport != v_lport)
|
|
unf_vport_ref_dec(v_lport);
|
|
else
|
|
unf_root_lport_ref_dec(v_lport);
|
|
}
|
|
|
|
void unf_lport_route_work(struct work_struct *v_work)
|
|
{
|
|
#define MAX_INTERVAL_TIMES 60
|
|
|
|
struct unf_lport_s *lport = NULL;
|
|
int ret = 0;
|
|
struct unf_err_code_s fc_err_code;
|
|
|
|
UNF_CHECK_VALID(0x2388, UNF_TRUE, v_work, return);
|
|
|
|
lport = container_of(v_work, struct unf_lport_s, route_timer_work.work);
|
|
if (unlikely(!lport)) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT,
|
|
UNF_KEVENT, "[err]LPort is NULL");
|
|
|
|
return;
|
|
}
|
|
|
|
if (unlikely(lport->b_port_removing == UNF_TRUE)) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT, UNF_KEVENT,
|
|
"[warn]LPort(0x%x) route work is closing.",
|
|
lport->port_id);
|
|
|
|
unf_lport_ref_dec_to_destroy(lport);
|
|
|
|
return;
|
|
}
|
|
|
|
if (unlikely(unf_get_pcie_link_state(lport)))
|
|
lport->pcie_link_down_cnt++;
|
|
else
|
|
lport->pcie_link_down_cnt = 0;
|
|
|
|
if (lport->pcie_link_down_cnt >= 3) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT, UNF_KEVENT,
|
|
"[warn]LPort(0x%x) detected pcie linkdown, closing route work",
|
|
lport->port_id);
|
|
lport->b_pcie_linkdown = UNF_TRUE;
|
|
unf_free_lport_all_xchg(lport);
|
|
unf_lport_ref_dec_to_destroy(lport);
|
|
return;
|
|
}
|
|
|
|
if (unlikely(UNF_LPORT_CHIP_ERROR(lport))) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT, UNF_KEVENT,
|
|
"[warn]LPort(0x%x) reported chip error, closing route work. ",
|
|
lport->port_id);
|
|
|
|
unf_lport_ref_dec_to_destroy(lport);
|
|
|
|
return;
|
|
}
|
|
|
|
if (lport->enhanced_features &
|
|
UNF_LPORT_ENHANCED_FEATURE_CLOSE_FW_ROUTE) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT, UNF_KEVENT,
|
|
"[warn]User close LPort(0x%x) route work. ",
|
|
lport->port_id);
|
|
|
|
unf_lport_ref_dec_to_destroy(lport);
|
|
|
|
return;
|
|
}
|
|
if (atomic_read(&lport->err_code_obtain_freq) == 0) {
|
|
memset(&fc_err_code, 0, sizeof(struct unf_err_code_s));
|
|
unf_get_error_code_sum(lport, &fc_err_code);
|
|
atomic_inc(&lport->err_code_obtain_freq);
|
|
} else if (atomic_read(&lport->err_code_obtain_freq) ==
|
|
MAX_INTERVAL_TIMES) {
|
|
atomic_set(&lport->err_code_obtain_freq, 0);
|
|
} else {
|
|
atomic_inc(&lport->err_code_obtain_freq);
|
|
}
|
|
/* Scheduling 1 second */
|
|
ret = queue_delayed_work(
|
|
unf_work_queue, &lport->route_timer_work,
|
|
(unsigned long)msecs_to_jiffies(UNF_LPORT_POLL_TIMER));
|
|
if (ret == 0) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT, UNF_KEVENT,
|
|
"[warn]LPort(0x%x) schedule work unsuccessful.",
|
|
lport->port_id);
|
|
|
|
unf_lport_ref_dec_to_destroy(lport);
|
|
}
|
|
}
|
|
|
|
int unf_cm_get_port_info(void *argc_in, void *argc_out)
|
|
{
|
|
struct unf_lport_s *lport = NULL;
|
|
struct unf_get_port_info_argout *port_info = NULL;
|
|
|
|
UNF_CHECK_VALID(0x2398, UNF_TRUE, argc_in, return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(0x2398, UNF_TRUE, argc_out, return UNF_RETURN_ERROR);
|
|
|
|
lport = (struct unf_lport_s *)argc_in;
|
|
port_info = (struct unf_get_port_info_argout *)argc_out;
|
|
|
|
if (!lport->low_level_func.port_mgr_op.pfn_ll_port_config_get) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"Port(0x%x)'s corresponding function is NULL.",
|
|
lport->port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
if (lport->low_level_func.port_mgr_op.pfn_ll_port_config_get(
|
|
lport->fc_port,
|
|
UNF_PORT_CFG_GET_PORT_INFO, port_info) !=
|
|
RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"Port(0x%x) get current info failed.",
|
|
lport->port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
static unsigned int unf_get_lport_current_info(struct unf_lport_s *v_lport)
|
|
{
|
|
struct unf_get_port_info_argout port_info = { 0 };
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
struct unf_lport_s *lport = NULL;
|
|
|
|
UNF_CHECK_VALID(0x2403, UNF_TRUE, v_lport, return UNF_RETURN_ERROR);
|
|
|
|
lport = unf_find_lport_by_port_id(v_lport->port_id);
|
|
|
|
if (!lport)
|
|
return UNF_RETURN_ERROR;
|
|
|
|
ret = (unsigned int)unf_send_event(lport->port_id, UNF_EVENT_SYN,
|
|
(void *)lport,
|
|
(void *)&port_info,
|
|
unf_cm_get_port_info);
|
|
if (ret != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"UNF_GetPortCurrentInfo SendEvent(unf_cm_get_port_info) fail.");
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
lport->low_level_func.sfp_speed = port_info.sfp_speed;
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
int unf_set_link_lose_tmo_to_up(struct unf_lport_s *v_lport,
|
|
struct unf_flash_link_tmo_s *v_link_tmo)
|
|
{
|
|
int ret = UNF_RETURN_ERROR;
|
|
struct unf_flash_data_s flash_data;
|
|
|
|
if ((!v_lport) || (!v_link_tmo) ||
|
|
(sizeof(struct unf_flash_data_s) > HIFC_FLASH_DATA_MAX_LEN)) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_NORMAL, UNF_KEVENT,
|
|
"[warn]set link tmo param check fail");
|
|
return ret;
|
|
}
|
|
memset(&flash_data, 0, sizeof(struct unf_flash_data_s));
|
|
|
|
if (!v_lport->low_level_func.port_mgr_op.pfn_ll_port_config_get) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_NORMAL, UNF_KEVENT,
|
|
"[warn]link tmo fun null");
|
|
return ret;
|
|
}
|
|
if (v_lport->low_level_func.port_mgr_op.pfn_ll_port_config_get(
|
|
v_lport->fc_port,
|
|
UNF_PORT_CFG_GET_FLASH_DATA_INFO, &flash_data) !=
|
|
RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_NORMAL, UNF_KEVENT,
|
|
"[warn]get link tmo to up fail");
|
|
return ret;
|
|
}
|
|
|
|
memcpy(&flash_data.link_tmo, v_link_tmo, HIFC_FLASH_LINK_TMO_MAX_LEN);
|
|
|
|
if (!v_lport->low_level_func.port_mgr_op.pfn_ll_port_config_set) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_NORMAL, UNF_KEVENT,
|
|
"[warn]set link tmo fun null");
|
|
return ret;
|
|
}
|
|
|
|
if (v_lport->low_level_func.port_mgr_op.pfn_ll_port_config_set(
|
|
v_lport->fc_port, UNF_PORT_CFG_SET_FLASH_DATA_INFO,
|
|
&flash_data) !=
|
|
RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_NORMAL, UNF_KEVENT,
|
|
"[warn]set link tmo to up fail");
|
|
return ret;
|
|
}
|
|
ret = RETURN_OK;
|
|
|
|
return ret;
|
|
}
|
|
|
|
int unf_set_link_lose_tmo(struct unf_lport_s *v_lport, int time_out)
|
|
{
|
|
struct unf_flash_link_tmo_s flash_link_tmo;
|
|
int ret = UNF_RETURN_ERROR;
|
|
unsigned int link_tmo = (unsigned int)time_out;
|
|
|
|
memset(&flash_link_tmo, 0, sizeof(struct unf_flash_link_tmo_s));
|
|
|
|
if (!v_lport) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_NORMAL,
|
|
UNF_KEVENT, "[warn]set link tmo lport null");
|
|
return ret;
|
|
}
|
|
|
|
/* 1. update gloabl var */
|
|
if ((int)atomic_read(&v_lport->link_lose_tmo) == time_out)
|
|
return RETURN_OK;
|
|
|
|
atomic_set(&v_lport->link_lose_tmo, time_out);
|
|
|
|
flash_link_tmo.writeflag = HIFC_MGMT_TMO_MAGIC_NUM;
|
|
flash_link_tmo.link_tmo0 = (unsigned char)link_tmo;
|
|
flash_link_tmo.link_tmo1 = (unsigned char)(link_tmo >> 8);
|
|
flash_link_tmo.link_tmo2 = (unsigned char)(link_tmo >> 16);
|
|
flash_link_tmo.link_tmo3 = (unsigned char)(link_tmo >> 24);
|
|
|
|
/* 2. write to up */
|
|
ret = unf_set_link_lose_tmo_to_up(v_lport, &flash_link_tmo);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int unf_set_link_lose_tmo_to_all(int time_out)
|
|
{
|
|
int ret = RETURN_OK;
|
|
struct list_head list_lport_tmp_head;
|
|
struct list_head *node = NULL;
|
|
struct list_head *next_node = NULL;
|
|
struct unf_lport_s *lport = NULL;
|
|
unsigned long flags = 0;
|
|
|
|
INIT_LIST_HEAD(&list_lport_tmp_head);
|
|
spin_lock_irqsave(&global_lport_mgr.global_lport_list_lock, flags);
|
|
list_for_each_safe(node, next_node,
|
|
&global_lport_mgr.list_lport_list_head) {
|
|
lport = list_entry(node, struct unf_lport_s, entry_lport);
|
|
list_del_init(&lport->entry_lport);
|
|
list_add_tail(&lport->entry_lport, &list_lport_tmp_head);
|
|
(void)unf_lport_refinc(lport);
|
|
}
|
|
spin_unlock_irqrestore(&global_lport_mgr.global_lport_list_lock, flags);
|
|
|
|
while (!list_empty(&list_lport_tmp_head)) {
|
|
node = (&list_lport_tmp_head)->next;
|
|
lport = list_entry(node, struct unf_lport_s, entry_lport);
|
|
if (lport)
|
|
unf_set_link_lose_tmo(lport, time_out);
|
|
|
|
spin_lock_irqsave(&global_lport_mgr.global_lport_list_lock,
|
|
flags);
|
|
list_del_init(&lport->entry_lport);
|
|
list_add_tail(&lport->entry_lport,
|
|
&global_lport_mgr.list_lport_list_head);
|
|
spin_unlock_irqrestore(&global_lport_mgr.global_lport_list_lock,
|
|
flags);
|
|
|
|
unf_lport_ref_dec_to_destroy(lport);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int unf_cm_adm_show_xchg(struct unf_lport_s *v_lport,
|
|
struct unf_hinicam_pkg *v_input)
|
|
{
|
|
int ret = UNF_RETURN_ERROR;
|
|
struct unf_xchg_mgr_s *xchg_mgr = NULL;
|
|
struct unf_xchg_s *xchg = NULL;
|
|
struct list_head *xchg_node = NULL;
|
|
struct list_head *next_xchg_node = NULL;
|
|
unsigned long flags = 0;
|
|
unsigned int aborted = 0;
|
|
unsigned int ini_busy = 0;
|
|
unsigned int tgt_busy = 0;
|
|
unsigned int delay = 0;
|
|
unsigned int free = 0;
|
|
unsigned int wait = 0;
|
|
unsigned int sfs_free = 0;
|
|
unsigned int sfs_busy = 0;
|
|
unsigned int i;
|
|
struct unf_adm_xchg *buff_out = NULL;
|
|
|
|
buff_out = (struct unf_adm_xchg *)v_input->buff_out;
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, *v_input->out_size >=
|
|
sizeof(struct unf_adm_xchg), return UNF_RETURN_ERROR);
|
|
|
|
for (i = 0; i < UNF_EXCHG_MGR_NUM; i++) {
|
|
xchg_mgr = unf_get_xchg_mgr_by_lport(v_lport, i);
|
|
if (!xchg_mgr)
|
|
continue;
|
|
|
|
if (!xchg_mgr->hot_pool)
|
|
continue;
|
|
|
|
/* hot Xchg */
|
|
spin_lock_irqsave(&xchg_mgr->hot_pool->xchg_hot_pool_lock,
|
|
flags);
|
|
|
|
UNF_TRACE(0x2659, UNF_LOG_NORMAL, UNF_INFO, "ini busy :");
|
|
list_for_each_safe(xchg_node, next_xchg_node,
|
|
&xchg_mgr->hot_pool->ini_busylist) {
|
|
ini_busy++;
|
|
|
|
xchg = list_entry(xchg_node, struct unf_xchg_s,
|
|
list_xchg_entry);
|
|
UNF_TRACE(0x2660, UNF_LOG_NORMAL, UNF_INFO,
|
|
"0x%p--0x%x--0x%x--0x%x--0x%x--0x%x--0x%x--0x%x--0x%x--0x%x--(%llu)",
|
|
xchg,
|
|
(unsigned int)xchg->hot_pool_tag,
|
|
(unsigned int)xchg->xchg_type,
|
|
(unsigned int)xchg->ox_id,
|
|
(unsigned int)xchg->rx_id,
|
|
(unsigned int)xchg->sid,
|
|
(unsigned int)xchg->did,
|
|
(unsigned int)xchg->seq_id,
|
|
(unsigned int)xchg->io_state,
|
|
atomic_read(&xchg->ref_cnt),
|
|
xchg->alloc_jif);
|
|
}
|
|
|
|
UNF_TRACE(0x2665, UNF_LOG_NORMAL, UNF_INFO, "SFS Busy:");
|
|
list_for_each_safe(xchg_node, next_xchg_node,
|
|
&xchg_mgr->hot_pool->sfs_busylist) {
|
|
sfs_busy++;
|
|
|
|
xchg = list_entry(xchg_node, struct unf_xchg_s,
|
|
list_xchg_entry);
|
|
UNF_TRACE(0x2666, UNF_LOG_NORMAL, UNF_INFO,
|
|
"0x%p--0x%x--0x%x--0x%x--0x%x--0x%x--0x%x--0x%x--0x%x--0x%x--(%llu)",
|
|
xchg,
|
|
(unsigned int)xchg->hot_pool_tag,
|
|
(unsigned int)xchg->xchg_type,
|
|
(unsigned int)xchg->ox_id,
|
|
(unsigned int)xchg->rx_id,
|
|
(unsigned int)xchg->sid,
|
|
(unsigned int)xchg->did,
|
|
(unsigned int)xchg->seq_id,
|
|
(unsigned int)xchg->io_state,
|
|
atomic_read(&xchg->ref_cnt),
|
|
xchg->alloc_jif);
|
|
}
|
|
|
|
spin_unlock_irqrestore(&xchg_mgr->hot_pool->xchg_hot_pool_lock,
|
|
flags);
|
|
|
|
/* free Xchg */
|
|
spin_lock_irqsave(&xchg_mgr->free_pool.xchg_free_pool_lock,
|
|
flags);
|
|
|
|
list_for_each_safe(xchg_node, next_xchg_node,
|
|
&xchg_mgr->free_pool.list_free_xchg_list) {
|
|
free++;
|
|
}
|
|
|
|
list_for_each_safe(xchg_node, next_xchg_node,
|
|
&xchg_mgr->free_pool.list_sfs_xchg_list) {
|
|
sfs_free++;
|
|
}
|
|
spin_unlock_irqrestore(&xchg_mgr->free_pool.xchg_free_pool_lock,
|
|
flags);
|
|
|
|
ret = RETURN_OK;
|
|
}
|
|
|
|
buff_out->aborted = aborted;
|
|
buff_out->ini_busy = ini_busy;
|
|
buff_out->tgt_busy = tgt_busy;
|
|
buff_out->delay = delay;
|
|
buff_out->free = free;
|
|
buff_out->wait = wait;
|
|
buff_out->sfs_free = sfs_free;
|
|
buff_out->sfs_busy = sfs_busy;
|
|
UNF_REFERNCE_VAR(xchg);
|
|
return ret;
|
|
}
|
|
|
|
static int unf_cm_adm_link_time_out_opt(struct unf_lport_s *v_lport,
|
|
struct unf_hinicam_pkg *v_input)
|
|
{
|
|
int ret = RETURN_OK;
|
|
int time_out = 0;
|
|
struct unf_link_tmo_opt_s *buff_in = NULL;
|
|
struct unf_link_tmo_opt_s *buff_out = NULL;
|
|
struct unf_admin_msg_head msg_head = { 0 };
|
|
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_input,
|
|
return RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_lport,
|
|
return RETURN_ERROR);
|
|
|
|
buff_in = (struct unf_link_tmo_opt_s *)(v_input->buff_in);
|
|
buff_out = (struct unf_link_tmo_opt_s *)(v_input->buff_out);
|
|
|
|
msg_head.status = UNF_ADMIN_MSG_DONE;
|
|
msg_head.size = sizeof(struct unf_admin_msg_head);
|
|
if (buff_in->link_opt) {
|
|
/* set link tmo value */
|
|
time_out = unf_get_link_lose_tmo(v_lport);
|
|
/* compatible for PI2 branch tool (not release)not
|
|
* include syncAllPort section
|
|
*/
|
|
if (v_input->in_size > 16) {
|
|
if (buff_in->sync_all_port)
|
|
/* sync to all other lport */
|
|
unf_set_link_lose_tmo_to_all(buff_in->tmo_value);
|
|
else
|
|
unf_set_link_lose_tmo(v_lport,
|
|
buff_in->tmo_value);
|
|
|
|
buff_out->sync_all_port = 1;
|
|
} else {
|
|
unf_set_link_lose_tmo_to_all(buff_in->tmo_value);
|
|
}
|
|
|
|
buff_out->link_opt = 1;
|
|
|
|
/* return orige value */
|
|
buff_out->tmo_value = time_out;
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_NORMAL, UNF_KEVENT,
|
|
"[info]set fc port(0x%0x)link tmo value(%d -> %d) success .",
|
|
v_lport->nport_id, time_out, buff_out->tmo_value);
|
|
} else {
|
|
/* get link tmo value */
|
|
buff_out->tmo_value = unf_get_link_lose_tmo(v_lport);
|
|
buff_out->link_opt = 0;
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_NORMAL, UNF_MAJOR,
|
|
"get fc port(0x%0x) link tmo value(%d) success .",
|
|
v_lport->nport_id, buff_out->tmo_value);
|
|
}
|
|
*v_input->out_size = v_input->in_size;
|
|
memcpy((void *)buff_out, &msg_head, sizeof(struct unf_admin_msg_head));
|
|
return ret;
|
|
}
|
|
|
|
static int unf_cm_adm_log_level_opt(struct unf_lport_s *v_lport,
|
|
struct unf_hinicam_pkg *v_input)
|
|
{
|
|
int ret = RETURN_OK;
|
|
unsigned int log_level = 0;
|
|
unsigned int log_count = 0;
|
|
struct unf_log_level_opt_s *buff_in = NULL;
|
|
struct unf_log_level_opt_s *buff_out = NULL;
|
|
struct unf_admin_msg_head msg_head = { 0 };
|
|
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_input,
|
|
return RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE,
|
|
v_input->in_size >= sizeof(struct unf_log_level_opt_s),
|
|
return RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE,
|
|
*v_input->out_size >=
|
|
sizeof(struct unf_log_level_opt_s),
|
|
return RETURN_ERROR);
|
|
|
|
buff_in = (struct unf_log_level_opt_s *)(v_input->buff_in);
|
|
buff_out = (struct unf_log_level_opt_s *)(v_input->buff_out);
|
|
|
|
msg_head.status = UNF_ADMIN_MSG_DONE;
|
|
msg_head.size = sizeof(struct unf_admin_msg_head);
|
|
if (buff_in->log_opt) {
|
|
/* set log level value */
|
|
log_level = log_print_level;
|
|
log_count = log_limted_times;
|
|
log_print_level = buff_in->log_level;
|
|
log_limted_times = buff_in->log_fre_qunce;
|
|
buff_out->log_opt = 1;
|
|
/* return orige value */
|
|
|
|
buff_out->log_level = log_level;
|
|
buff_out->log_fre_qunce = log_count;
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_NORMAL, UNF_MAJOR,
|
|
"set fc log level(%u -> %u), frenqunce(%u -> %u)in 2s success .",
|
|
log_level, log_print_level, log_count,
|
|
log_limted_times);
|
|
} else {
|
|
/* get link tmo value */
|
|
buff_out->log_level = log_print_level;
|
|
buff_out->log_fre_qunce = log_limted_times;
|
|
buff_out->log_opt = 0;
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_NORMAL, UNF_MAJOR,
|
|
"get fc log level(%u),frenqunce(%u) in 2s success .",
|
|
buff_out->log_level, buff_out->log_fre_qunce);
|
|
}
|
|
*v_input->out_size = sizeof(struct unf_log_level_opt_s);
|
|
memcpy((void *)buff_out, &msg_head, sizeof(struct unf_admin_msg_head));
|
|
return ret;
|
|
}
|
|
|
|
int unf_cm_echo_test(unsigned int v_port_id, unsigned int v_nport_id,
|
|
unsigned int *v_link_delay)
|
|
{
|
|
struct unf_lport_s *lport = NULL;
|
|
struct unf_rport_s *rport = NULL;
|
|
int ret = RETURN_OK;
|
|
unsigned int index = 0;
|
|
|
|
lport = unf_find_lport_by_port_id(v_port_id);
|
|
if (!lport) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_NORMAL, UNF_MAJOR,
|
|
"fcping request failed [invalid source lport (0x%x)].\n",
|
|
v_port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
rport = unf_get_rport_by_nport_id(lport, v_nport_id);
|
|
if ((!rport) || (v_nport_id == UNF_FC_FID_FLOGI)) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_NORMAL, UNF_MAJOR,
|
|
"fcping request failed [invalid destination rport(0x%x)].\n",
|
|
v_nport_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
for (index = 0; index < UNF_ECHO_SEND_MAX_TIMES; index++) {
|
|
ret = (int)unf_send_echo(lport, rport, v_link_delay);
|
|
if (ret != RETURN_OK) {
|
|
*v_link_delay = 0xffffffff;
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_NORMAL,
|
|
UNF_MAJOR,
|
|
"fcping request failed [lport(0x%x)-> rport(0x%x)].\n",
|
|
v_port_id, v_nport_id);
|
|
|
|
} else {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_NORMAL,
|
|
UNF_MAJOR,
|
|
"fcping request succeed within %u us [lport(0x%x)->rport(0x%x)].\n",
|
|
*(unsigned int *)v_link_delay, v_port_id,
|
|
v_nport_id);
|
|
}
|
|
|
|
msleep(1000);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int unf_cm_link_delay_get(struct unf_lport_s *v_lport,
|
|
struct unf_hinicam_pkg *v_input)
|
|
{
|
|
int ret = UNF_RETURN_ERROR;
|
|
unsigned int link_delay = 0xffffffff;
|
|
unsigned int nport_id = 0xffffff;
|
|
unsigned int port_id = 0;
|
|
struct unf_adm_cmd *buff_in = NULL;
|
|
struct unf_adm_cmd *buff_out = NULL;
|
|
struct unf_admin_msg_head msg_head = { 0 };
|
|
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_input,
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, *v_input->out_size >=
|
|
sizeof(struct unf_adm_cmd), return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_input->in_size >=
|
|
sizeof(struct unf_adm_cmd), return UNF_RETURN_ERROR);
|
|
|
|
buff_in = (struct unf_adm_cmd *)(v_input->buff_in);
|
|
buff_out = (struct unf_adm_cmd *)(v_input->buff_out);
|
|
port_id = v_lport->port_id;
|
|
nport_id = buff_in->arg[0];
|
|
|
|
msg_head.status = UNF_ADMIN_MSG_DONE;
|
|
|
|
ret = unf_cm_echo_test(port_id, nport_id, &link_delay);
|
|
if ((ret == RETURN_OK) && (link_delay != 0xffffffff)) {
|
|
buff_out->arg[0] = link_delay;
|
|
msg_head.size = sizeof(struct unf_admin_msg_head) +
|
|
sizeof(unsigned int) * 1;
|
|
} else {
|
|
msg_head.status = UNF_ADMIN_MSG_FAILED;
|
|
msg_head.size = sizeof(struct unf_admin_msg_head);
|
|
}
|
|
|
|
*v_input->out_size = sizeof(struct unf_adm_cmd);
|
|
memcpy((void *)buff_out, &msg_head, sizeof(struct unf_admin_msg_head));
|
|
|
|
return ret;
|
|
}
|
|
|
|
static unsigned int unf_port_release_rport_index(struct unf_lport_s *v_lport,
|
|
void *v_input)
|
|
{
|
|
unsigned int index = INVALID_VALUE32;
|
|
unsigned int *rport_index = NULL;
|
|
unsigned long flag = 0;
|
|
struct unf_rport_pool_s *rport_pool = NULL;
|
|
|
|
UNF_CHECK_VALID(0x2370, UNF_FALSE, v_lport, return UNF_RETURN_ERROR);
|
|
|
|
if (v_input) {
|
|
rport_index = (unsigned int *)v_input;
|
|
index = *rport_index;
|
|
if (index < v_lport->low_level_func.support_max_rport) {
|
|
rport_pool = &((struct unf_lport_s *)v_lport->root_lport)->rport_pool;
|
|
spin_lock_irqsave(&rport_pool->rport_free_pool_lock,
|
|
flag);
|
|
if (test_bit((int)index, rport_pool->pul_rpi_bitmap))
|
|
clear_bit((int)index,
|
|
rport_pool->pul_rpi_bitmap);
|
|
else
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN,
|
|
UNF_LOG_LOGIN_ATT, UNF_ERR,
|
|
"[warn]Port(0x%x) try to release a free rport index(0x%x)",
|
|
v_lport->port_id, index);
|
|
|
|
spin_unlock_irqrestore(
|
|
&rport_pool->rport_free_pool_lock,
|
|
flag);
|
|
} else {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT,
|
|
UNF_ERR,
|
|
"[warn]Port(0x%x) try to release a not exist rport index(0x%x)",
|
|
v_lport->port_id, index);
|
|
}
|
|
}
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
void *unf_lookup_lport_by_nport_id(void *v_lport, unsigned int v_nport_id)
|
|
{
|
|
struct unf_lport_s *lport = NULL;
|
|
struct unf_vport_pool_s *vport_pool = NULL;
|
|
struct unf_lport_s *vport = NULL;
|
|
struct list_head *node = NULL;
|
|
struct list_head *next_node = NULL;
|
|
unsigned long flag = 0;
|
|
|
|
UNF_CHECK_VALID(0x1978, UNF_TRUE, v_lport, return NULL);
|
|
|
|
lport = (struct unf_lport_s *)v_lport;
|
|
lport = lport->root_lport;
|
|
vport_pool = lport->vport_pool;
|
|
|
|
if (v_nport_id == lport->nport_id)
|
|
return lport;
|
|
|
|
if (unlikely(!vport_pool)) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_IO_ATT, UNF_WARN,
|
|
"[warn]Port(0x%x) vport pool is NULL",
|
|
lport->port_id);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
spin_lock_irqsave(&vport_pool->vport_pool_lock, flag);
|
|
list_for_each_safe(node, next_node, &lport->list_vports_head) {
|
|
vport = list_entry(node, struct unf_lport_s, entry_vport);
|
|
if (vport->nport_id == v_nport_id) {
|
|
spin_unlock_irqrestore(&vport_pool->vport_pool_lock,
|
|
flag);
|
|
return vport;
|
|
}
|
|
}
|
|
|
|
list_for_each_safe(node, next_node, &lport->list_intergrad_vports) {
|
|
vport = list_entry(node, struct unf_lport_s, entry_vport);
|
|
if (vport->nport_id == v_nport_id) {
|
|
spin_unlock_irqrestore(&vport_pool->vport_pool_lock,
|
|
flag);
|
|
return vport;
|
|
}
|
|
}
|
|
spin_unlock_irqrestore(&vport_pool->vport_pool_lock, flag);
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO,
|
|
"Port(0x%x) has no vport Nport ID(0x%x)",
|
|
lport->port_id, v_nport_id);
|
|
return NULL;
|
|
}
|
|
|
|
static int unf_get_port_info(struct unf_lport_s *v_lport,
|
|
struct unf_lport_info *v_port_info)
|
|
{
|
|
unsigned int act_speed = INVALID_VALUE32;
|
|
unsigned int cfg_speed = INVALID_VALUE32;
|
|
unsigned int cfg_topo = INVALID_VALUE32;
|
|
enum unf_act_topo_e act_topo = UNF_ACT_TOP_UNKNOWN;
|
|
struct unf_err_code_s fc_err_code;
|
|
unsigned int cfg_led_mode = INVALID_VALUE32;
|
|
struct unf_vport_pool_s *vport_pool = NULL;
|
|
struct unf_lport_s *vport = NULL;
|
|
struct list_head *node = NULL;
|
|
struct list_head *next_node = NULL;
|
|
unsigned long flag = 0;
|
|
|
|
UNF_CHECK_VALID(0x2205, UNF_TRUE, v_lport, return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(0x2206, UNF_TRUE, v_port_info, return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(0x2207, UNF_TRUE, v_lport->fc_port,
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(
|
|
0x2208, UNF_TRUE,
|
|
v_lport->low_level_func.port_mgr_op.pfn_ll_port_config_get,
|
|
return UNF_RETURN_ERROR);
|
|
UNF_REFERNCE_VAR(cfg_speed);
|
|
UNF_REFERNCE_VAR(act_topo);
|
|
|
|
memset(&fc_err_code, 0, sizeof(fc_err_code));
|
|
|
|
/* get port speed */
|
|
cfg_speed = v_lport->low_level_func.lport_cfg_items.port_speed;
|
|
|
|
if (v_lport->link_up == UNF_PORT_LINK_UP)
|
|
(void)v_lport->low_level_func.port_mgr_op.pfn_ll_port_config_get(
|
|
v_lport->fc_port,
|
|
UNF_PORT_CFG_GET_SPEED_ACT, (void *)&act_speed);
|
|
else
|
|
act_speed = UNF_PORT_SPEED_UNKNOWN;
|
|
|
|
(void)v_lport->low_level_func.port_mgr_op.pfn_ll_port_config_get(
|
|
v_lport->fc_port,
|
|
UNF_PORT_CFG_GET_SPEED_CFG, (void *)&cfg_speed);
|
|
|
|
if (v_lport->link_up == UNF_PORT_LINK_UP)
|
|
(void)v_lport->low_level_func.port_mgr_op.pfn_ll_port_config_get(
|
|
v_lport->fc_port,
|
|
UNF_PORT_CFG_GET_TOPO_ACT, (void *)&act_topo);
|
|
else
|
|
act_topo = UNF_ACT_TOP_UNKNOWN;
|
|
|
|
(void)v_lport->low_level_func.port_mgr_op.pfn_ll_port_config_get(
|
|
v_lport->fc_port,
|
|
UNF_PORT_CFG_GET_TOPO_CFG, (void *)&cfg_topo);
|
|
|
|
(void)v_lport->low_level_func.port_mgr_op.pfn_ll_port_config_get(
|
|
v_lport->fc_port,
|
|
UNF_PORT_CFG_GET_LED_STATE, (void *)&cfg_led_mode);
|
|
|
|
v_port_info->port_id = v_lport->port_id;
|
|
v_port_info->options = v_lport->options;
|
|
v_port_info->b_start_work = global_lport_mgr.b_start_work;
|
|
v_port_info->phy_link = UNF_PORT_LINK_UP;
|
|
v_port_info->link_up = v_lport->link_up;
|
|
v_port_info->act_speed = act_speed;
|
|
v_port_info->cfg_speed = cfg_speed;
|
|
v_port_info->port_name = v_lport->port_name;
|
|
v_port_info->tape_support =
|
|
v_lport->low_level_func.lport_cfg_items.tape_support;
|
|
v_port_info->msi = 0;
|
|
v_port_info->ini_io_retry_timeout = 0;
|
|
v_port_info->support_max_npiv_num =
|
|
v_lport->low_level_func.support_max_npiv_num;
|
|
v_port_info->act_topo = act_topo;
|
|
v_port_info->port_topology =
|
|
v_lport->low_level_func.lport_cfg_items.port_topology;
|
|
v_port_info->fc_ser_max_speed =
|
|
v_lport->low_level_func.fc_ser_max_speed;
|
|
|
|
if (unf_get_error_code_sum(v_lport, &fc_err_code) != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) get error code failed",
|
|
v_lport->port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
v_port_info->loss_of_signal_count = fc_err_code.loss_of_signal_count;
|
|
v_port_info->bad_rx_char_count = fc_err_code.bad_rx_char_count;
|
|
v_port_info->loss_of_sync_count = fc_err_code.loss_of_sync_count;
|
|
v_port_info->link_fail_count = fc_err_code.link_fail_count;
|
|
v_port_info->rx_eo_fa_count = fc_err_code.rx_eo_fa_count;
|
|
v_port_info->dis_frame_count = fc_err_code.dis_frame_count;
|
|
v_port_info->bad_crc_count = fc_err_code.bad_crc_count;
|
|
v_port_info->proto_error_count = fc_err_code.proto_error_count;
|
|
v_port_info->chip_type = v_lport->low_level_func.chip_info.chip_type;
|
|
v_port_info->cfg_led_mode = cfg_led_mode;
|
|
|
|
v_port_info->vport_num = 0;
|
|
|
|
vport_pool = v_lport->vport_pool;
|
|
if (unlikely(!vport_pool))
|
|
return RETURN_OK;
|
|
|
|
spin_lock_irqsave(&vport_pool->vport_pool_lock, flag);
|
|
list_for_each_safe(node, next_node, &v_lport->list_vports_head) {
|
|
vport = list_entry(node, struct unf_lport_s, entry_vport);
|
|
|
|
v_port_info->vport_id[v_port_info->vport_num] = vport->port_id;
|
|
|
|
v_port_info->vport_num = v_port_info->vport_num + 1;
|
|
}
|
|
spin_unlock_irqrestore(&vport_pool->vport_pool_lock, flag);
|
|
return RETURN_OK;
|
|
}
|
|
|
|
static int unf_get_vport_info(struct unf_lport_s *v_lport,
|
|
unsigned int v_vport_id,
|
|
struct unf_lport_info *v_port_info)
|
|
{
|
|
unsigned char vport_index = INVALID_VALUE8;
|
|
struct unf_lport_s *vport = NULL;
|
|
|
|
UNF_CHECK_VALID(0x2203, UNF_TRUE, v_lport, return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(0x2203, UNF_TRUE, v_port_info, return UNF_RETURN_ERROR);
|
|
|
|
vport_index = (v_vport_id & PORTID_VPINDEX_MASK) >> PORTID_VPINDEX_SHIT;
|
|
if (unlikely(vport_index == 0)) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_NORMAL, UNF_ERR,
|
|
"[err]VPortId(0x%x) is not vport", v_vport_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
vport = unf_cm_lookup_vport_by_vp_index(v_lport, vport_index);
|
|
if (unlikely(!vport)) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_NORMAL, UNF_ERR,
|
|
"[err]VPortId(0x%x) can not be found",
|
|
v_vport_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
v_port_info->port_id = vport->port_id;
|
|
v_port_info->port_name = vport->port_name;
|
|
v_port_info->nport_id = vport->nport_id;
|
|
v_port_info->options = 0;
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
static int unf_get_all_port_info(void *v_arg_in, void *v_arg_out)
|
|
{
|
|
struct unf_lport_s *lport = NULL;
|
|
struct unf_get_allinfo_argout *arg_in = NULL;
|
|
unsigned int current_len = 0;
|
|
struct unf_lport_info *cur_lport_info = NULL;
|
|
struct unf_admin_msg_head msg_head = { 0 };
|
|
int ret = UNF_RETURN_ERROR;
|
|
unsigned int out_buf_len = 0;
|
|
char *out_buf = NULL;
|
|
struct hifc_adm_cmd_s *buff_in = NULL;
|
|
|
|
UNF_CHECK_VALID(0x2203, UNF_TRUE, v_arg_in, return UNF_RETURN_ERROR);
|
|
UNF_REFERNCE_VAR(v_arg_out);
|
|
|
|
arg_in = (struct unf_get_allinfo_argout *)v_arg_in;
|
|
out_buf = (char *)arg_in->out_buf;
|
|
buff_in = (struct hifc_adm_cmd_s *)arg_in->in_buf;
|
|
lport = (struct unf_lport_s *)arg_in->lport;
|
|
|
|
UNF_CHECK_VALID(0x2203, UNF_TRUE, out_buf, return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(0x2203, UNF_TRUE, buff_in, return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(0x2203, UNF_TRUE, lport, return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, arg_in->in_size >=
|
|
sizeof(struct hifc_adm_cmd_s), return UNF_RETURN_ERROR);
|
|
|
|
cur_lport_info = vmalloc(sizeof(struct unf_lport_info));
|
|
if (!cur_lport_info) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) malloc memory fail", lport->port_id);
|
|
((struct unf_admin_msg_head *)out_buf)->status =
|
|
UNF_ADMIN_MSG_FAILED;
|
|
return ret;
|
|
}
|
|
|
|
memset(cur_lport_info, 0, sizeof(struct unf_lport_info));
|
|
out_buf_len = arg_in->in_size;
|
|
msg_head.status = UNF_ADMIN_MSG_DONE;
|
|
*arg_in->out_size = out_buf_len;
|
|
|
|
/* Storage info */
|
|
current_len += sizeof(struct unf_admin_msg_head);
|
|
|
|
if (lport->b_port_removing != UNF_TRUE) {
|
|
/* Cmd[3] is Vportid */
|
|
if (buff_in->cmd[3] != 0) {
|
|
ret = unf_get_vport_info(lport, buff_in->cmd[3],
|
|
cur_lport_info);
|
|
} else {
|
|
ret = unf_get_port_info(lport, cur_lport_info);
|
|
}
|
|
if (ret != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT,
|
|
UNF_INFO,
|
|
"[err]Port(0x%x) get port information error",
|
|
lport->port_id);
|
|
|
|
msg_head.status = UNF_ADMIN_MSG_FAILED;
|
|
msg_head.size = current_len;
|
|
memcpy(out_buf, &msg_head,
|
|
sizeof(struct unf_admin_msg_head));
|
|
vfree(cur_lport_info);
|
|
return ret;
|
|
}
|
|
|
|
if (out_buf_len < current_len + sizeof(struct unf_lport_info)) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_NORMAL,
|
|
UNF_ERR,
|
|
"[warn]Allocated buff size (%u < %lu) is not enough",
|
|
out_buf_len,
|
|
current_len + sizeof(struct unf_lport_info));
|
|
|
|
/* Compatible for vport: return Lport info
|
|
* if tools version is not support npiv
|
|
*/
|
|
memcpy(out_buf + current_len, cur_lport_info,
|
|
out_buf_len - current_len);
|
|
|
|
current_len = out_buf_len;
|
|
|
|
} else {
|
|
memcpy(out_buf + current_len, cur_lport_info,
|
|
sizeof(struct unf_lport_info));
|
|
current_len += sizeof(struct unf_lport_info);
|
|
}
|
|
} else {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_INFO,
|
|
"[warn]Port(0x%x) is removing. Ref count 0x%x",
|
|
lport->port_id, atomic_read(&lport->lport_ref_cnt));
|
|
|
|
msg_head.status = UNF_ADMIN_MSG_FAILED;
|
|
}
|
|
|
|
msg_head.size = current_len;
|
|
memcpy(out_buf, &msg_head, sizeof(struct unf_admin_msg_head));
|
|
vfree(cur_lport_info);
|
|
return ret;
|
|
}
|
|
|
|
static int unf_cm_get_all_port_info(struct unf_lport_s *v_lport,
|
|
struct unf_hinicam_pkg *v_input)
|
|
{
|
|
struct unf_get_allinfo_argout out = { 0 };
|
|
int ret = UNF_RETURN_ERROR;
|
|
|
|
out.out_buf = v_input->buff_out;
|
|
out.in_buf = v_input->buff_in;
|
|
out.out_size = v_input->out_size;
|
|
out.in_size = v_input->in_size;
|
|
out.lport = v_lport;
|
|
|
|
ret = (int)unf_schedule_global_event((void *)&out,
|
|
UNF_GLOBAL_EVENT_SYN,
|
|
unf_get_all_port_info);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int unf_cm_port_set(struct unf_lport_s *v_lport,
|
|
struct unf_hinicam_pkg *v_input)
|
|
{
|
|
int ret = UNF_RETURN_ERROR;
|
|
unsigned int mode = 0; /* 1:portreset 2:sfp on/off */
|
|
int turn_on = 0; /* 0:sfp off 1:sfp on */
|
|
unsigned int port_id = 0;
|
|
void *out_buf = NULL;
|
|
struct unf_adm_cmd *buff_in = NULL;
|
|
struct unf_admin_msg_head msg_head = { 0 };
|
|
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_input,
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_input->in_size >=
|
|
sizeof(struct unf_adm_cmd), return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, *v_input->out_size >=
|
|
sizeof(struct unf_adm_cmd), return UNF_RETURN_ERROR);
|
|
out_buf = v_input->buff_out;
|
|
buff_in = v_input->buff_in;
|
|
mode = buff_in->arg[0];
|
|
port_id = v_lport->port_id;
|
|
|
|
msg_head.status = UNF_ADMIN_MSG_DONE;
|
|
|
|
if (mode == 1) {
|
|
ret = unf_cm_reset_port(port_id);
|
|
|
|
if (ret != RETURN_OK)
|
|
msg_head.status = UNF_ADMIN_MSG_FAILED;
|
|
|
|
} else if (mode == 2) {
|
|
turn_on = (int)buff_in->arg[1];
|
|
|
|
if ((turn_on == 0) || (turn_on == 1)) {
|
|
ret = unf_cm_sfp_switch(port_id, turn_on);
|
|
if (ret != RETURN_OK)
|
|
msg_head.status = UNF_ADMIN_MSG_FAILED;
|
|
} else {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT,
|
|
UNF_ERR,
|
|
"[err]Switch sfp failed. Parameter(0x%x) error",
|
|
turn_on);
|
|
msg_head.status = UNF_ADMIN_MSG_FAILED;
|
|
}
|
|
}
|
|
|
|
msg_head.size = sizeof(struct unf_admin_msg_head);
|
|
*v_input->out_size = sizeof(struct unf_adm_cmd);
|
|
memcpy(out_buf, &msg_head, sizeof(struct unf_admin_msg_head));
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int unf_cm_topo_set(struct unf_lport_s *v_lport,
|
|
struct unf_hinicam_pkg *v_input)
|
|
{
|
|
int ret = UNF_RETURN_ERROR;
|
|
unsigned int topo = 0; /* topology set */
|
|
unsigned int port_id = 0;
|
|
void *out_buf = NULL;
|
|
struct unf_adm_cmd *buff_in = NULL;
|
|
struct unf_admin_msg_head msg_head = { 0 };
|
|
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_input,
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_input->in_size >=
|
|
sizeof(struct unf_adm_cmd), return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, *v_input->out_size >=
|
|
sizeof(struct unf_adm_cmd), return UNF_RETURN_ERROR);
|
|
out_buf = v_input->buff_out;
|
|
buff_in = v_input->buff_in;
|
|
topo = buff_in->arg[0];
|
|
port_id = v_lport->port_id;
|
|
|
|
msg_head.status = UNF_ADMIN_MSG_DONE;
|
|
|
|
if ((topo == UNF_TOP_AUTO_MASK) || (topo == UNF_TOP_LOOP_MASK) ||
|
|
(topo == UNF_TOP_P2P_MASK)) {
|
|
ret = unf_cm_set_port_topo(port_id, topo);
|
|
if (ret != RETURN_OK)
|
|
msg_head.status = UNF_ADMIN_MSG_FAILED;
|
|
} else {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]Set topo failed. Parameter(0x%x) error", topo);
|
|
msg_head.status = UNF_ADMIN_MSG_FAILED;
|
|
}
|
|
|
|
msg_head.size = sizeof(struct unf_admin_msg_head);
|
|
*v_input->out_size = sizeof(struct unf_adm_cmd);
|
|
memcpy(out_buf, &msg_head, sizeof(struct unf_admin_msg_head));
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int unf_cm_port_speed_set(struct unf_lport_s *v_lport,
|
|
struct unf_hinicam_pkg *v_input)
|
|
{
|
|
int ret = UNF_RETURN_ERROR;
|
|
unsigned int port_speed = 0;
|
|
unsigned int port_id = 0;
|
|
void *out_buf = NULL;
|
|
struct unf_adm_cmd *buff_in = NULL;
|
|
struct unf_admin_msg_head msg_head = { 0 };
|
|
struct unf_lport_s *lport = NULL;
|
|
int check_speed_flag = UNF_TRUE;
|
|
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_input,
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_lport,
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE,
|
|
v_input->in_size >= sizeof(struct unf_adm_cmd),
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE,
|
|
*v_input->out_size >= sizeof(struct unf_adm_cmd),
|
|
return UNF_RETURN_ERROR);
|
|
lport = v_lport;
|
|
out_buf = v_input->buff_out;
|
|
buff_in = v_input->buff_in;
|
|
port_speed = buff_in->arg[0];
|
|
port_id = v_lport->port_id;
|
|
|
|
msg_head.status = UNF_ADMIN_MSG_DONE;
|
|
|
|
/* get and check sfp speed */
|
|
if (unf_get_lport_current_info(lport) != RETURN_OK) {
|
|
msg_head.status = UNF_ADMIN_MSG_FAILED;
|
|
lport->low_level_func.sfp_speed = UNF_PORT_SFP_SPEED_ERR;
|
|
}
|
|
if (UNF_CHECK_CONFIG_SPEED_BY_SFSSPEED(lport->low_level_func.sfp_speed,
|
|
port_speed)) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]Set port speed failed. Speed (0x%x) is greater than SfpSpeed (0x%x)",
|
|
port_speed, lport->low_level_func.sfp_speed);
|
|
msg_head.status = UNF_ADMIN_MSG_FAILED;
|
|
check_speed_flag = UNF_FALSE;
|
|
} else {
|
|
if (lport->low_level_func.fc_ser_max_speed ==
|
|
UNF_PORT_SPEED_32_G) {
|
|
check_speed_flag =
|
|
(port_speed == UNF_PORT_SPEED_AUTO) ||
|
|
(port_speed == UNF_PORT_SPEED_8_G) ||
|
|
(port_speed == UNF_PORT_SPEED_16_G) ||
|
|
(port_speed == UNF_PORT_SPEED_32_G);
|
|
} else if (lport->low_level_func.fc_ser_max_speed ==
|
|
UNF_PORT_SPEED_16_G) {
|
|
check_speed_flag =
|
|
(port_speed == UNF_PORT_SPEED_AUTO) ||
|
|
(port_speed == UNF_PORT_SPEED_4_G) ||
|
|
(port_speed == UNF_PORT_SPEED_8_G) ||
|
|
(port_speed == UNF_PORT_SPEED_16_G);
|
|
} else if (lport->low_level_func.fc_ser_max_speed ==
|
|
UNF_PORT_SPEED_8_G) {
|
|
check_speed_flag =
|
|
(port_speed == UNF_PORT_SPEED_AUTO) ||
|
|
(port_speed == UNF_PORT_SPEED_2_G) ||
|
|
(port_speed == UNF_PORT_SPEED_4_G) ||
|
|
(port_speed == UNF_PORT_SPEED_8_G);
|
|
} else {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT,
|
|
UNF_ERR,
|
|
"[err]Board maxspeed is unknown");
|
|
msg_head.status = UNF_ADMIN_MSG_FAILED;
|
|
check_speed_flag = UNF_FALSE;
|
|
}
|
|
}
|
|
|
|
if (check_speed_flag) {
|
|
ret = unf_cm_set_port_speed(port_id, &port_speed);
|
|
if (ret != RETURN_OK)
|
|
msg_head.status = UNF_ADMIN_MSG_FAILED;
|
|
}
|
|
|
|
msg_head.size = sizeof(struct unf_admin_msg_head);
|
|
*v_input->out_size = sizeof(struct unf_adm_cmd);
|
|
memcpy(out_buf, &msg_head, sizeof(struct unf_admin_msg_head));
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int unf_cm_set_vport(struct unf_lport_s *v_lport,
|
|
struct unf_hinicam_pkg *v_input)
|
|
{
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
struct unf_lport_s *lport = NULL;
|
|
unsigned int mode = 0;
|
|
unsigned int index = 0;
|
|
unsigned int high32 = 0x2000286e;
|
|
unsigned int low32 = 0;
|
|
unsigned long long port_name = 0;
|
|
unsigned int port_id = 0;
|
|
|
|
void *out_buf = NULL;
|
|
struct unf_adm_cmd *buff_in = NULL;
|
|
struct unf_admin_msg_head msg_head = { 0 };
|
|
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_input,
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE,
|
|
v_input->in_size >= sizeof(struct unf_adm_cmd),
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE,
|
|
*v_input->out_size >= sizeof(struct unf_adm_cmd),
|
|
return UNF_RETURN_ERROR);
|
|
out_buf = v_input->buff_out;
|
|
buff_in = v_input->buff_in;
|
|
port_id = v_lport->port_id;
|
|
|
|
msg_head.status = UNF_ADMIN_MSG_DONE;
|
|
|
|
mode = buff_in->arg[0];
|
|
|
|
switch (mode) {
|
|
case 1:
|
|
/* create vport with wwpn */
|
|
low32 = buff_in->arg[1];
|
|
port_name = ((unsigned long)high32 << 32) | low32;
|
|
|
|
//lint -fallthrough
|
|
case 3:
|
|
/* create vport and autogeneration wwpn */
|
|
ret = unf_npiv_conf(port_id, port_name);
|
|
if (ret != RETURN_OK)
|
|
msg_head.status = UNF_ADMIN_MSG_FAILED;
|
|
msleep(2000);
|
|
break;
|
|
|
|
case 2:
|
|
/* delete vport by vport index */
|
|
index = buff_in->arg[2];
|
|
ret = unf_delete_vport(port_id, index);
|
|
if (ret != RETURN_OK)
|
|
msg_head.status = UNF_ADMIN_MSG_FAILED;
|
|
break;
|
|
|
|
case 4:
|
|
/* delete all vport on Lport */
|
|
lport = unf_find_lport_by_port_id(port_id);
|
|
if (!lport) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_NORMAL,
|
|
UNF_ERR,
|
|
"[err]Port(0x%x) can't find", port_id);
|
|
msg_head.status = UNF_ADMIN_MSG_FAILED;
|
|
} else {
|
|
unf_destroy_all_vports(lport);
|
|
ret = RETURN_OK;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_NORMAL, UNF_ERR,
|
|
"[err]Mode is unknown");
|
|
msg_head.status = UNF_ADMIN_MSG_FAILED;
|
|
break;
|
|
}
|
|
|
|
msg_head.size = sizeof(struct unf_admin_msg_head);
|
|
*v_input->out_size = sizeof(struct unf_adm_cmd);
|
|
memcpy(out_buf, &msg_head, sizeof(struct unf_admin_msg_head));
|
|
|
|
return (int)ret;
|
|
}
|
|
|
|
static int unf_cm_port_info_get(struct unf_lport_s *v_lport,
|
|
struct unf_hinicam_pkg *v_input)
|
|
{
|
|
int ret = UNF_RETURN_ERROR;
|
|
unsigned int topo_cfg = 0;
|
|
enum unf_act_topo_e topo = UNF_ACT_TOP_UNKNOWN;
|
|
unsigned int port_speed = 0;
|
|
unsigned int port_id = 0;
|
|
struct unf_adm_cmd *buff_out = NULL;
|
|
struct unf_admin_msg_head msg_head = { 0 };
|
|
struct unf_lport_s *lport = NULL;
|
|
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_input,
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE,
|
|
*v_input->out_size >= sizeof(struct unf_adm_cmd),
|
|
return UNF_RETURN_ERROR);
|
|
lport = v_lport;
|
|
port_id = v_lport->port_id;
|
|
buff_out = (struct unf_adm_cmd *)v_input->buff_out;
|
|
msg_head.status = UNF_ADMIN_MSG_DONE;
|
|
|
|
ret = unf_cm_get_port_topo(port_id, &topo_cfg, &topo);
|
|
if (ret == RETURN_OK) {
|
|
ret = unf_cm_get_port_speed(port_id, &port_speed);
|
|
if (ret == RETURN_OK) {
|
|
buff_out->arg[0] = lport->port_id;
|
|
buff_out->arg[1] = topo_cfg;
|
|
buff_out->arg[2] = topo;
|
|
buff_out->arg[3] = port_speed;
|
|
buff_out->arg[4] = lport->link_up;
|
|
|
|
msg_head.size = sizeof(struct unf_admin_msg_head) +
|
|
sizeof(unsigned int) * 5;
|
|
} else {
|
|
msg_head.status = UNF_ADMIN_MSG_FAILED;
|
|
msg_head.size = sizeof(struct unf_admin_msg_head);
|
|
}
|
|
} else {
|
|
msg_head.status = UNF_ADMIN_MSG_FAILED;
|
|
msg_head.size = sizeof(struct unf_admin_msg_head);
|
|
}
|
|
|
|
*v_input->out_size = sizeof(struct unf_adm_cmd);
|
|
memcpy(buff_out, &msg_head, sizeof(struct unf_admin_msg_head));
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int unf_get_port_sfp_info(struct unf_lport_s *v_lport,
|
|
struct unf_hinicam_pkg *v_input)
|
|
{
|
|
#define MIN_SFPINFO_LEN 512
|
|
union unf_sfp_eeprome_info *sfp_info = NULL;
|
|
int ret = UNF_RETURN_ERROR;
|
|
unsigned int status = 0;
|
|
unsigned int sfp_type = 0;
|
|
unsigned int port_id = 0;
|
|
char *buff_out = NULL;
|
|
struct unf_admin_msg_head msg_head = { 0 };
|
|
|
|
UNF_CHECK_VALID(0x2203, UNF_TRUE, v_input, return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE,
|
|
*v_input->out_size >= MIN_SFPINFO_LEN,
|
|
return UNF_RETURN_ERROR);
|
|
buff_out = v_input->buff_out;
|
|
port_id = v_lport->port_id;
|
|
|
|
*v_input->out_size = MIN_SFPINFO_LEN;
|
|
msg_head.status = UNF_ADMIN_MSG_DONE;
|
|
|
|
sfp_info = vmalloc(sizeof(union unf_sfp_eeprome_info));
|
|
if (!sfp_info)
|
|
return UNF_RETURN_ERROR;
|
|
|
|
memset(sfp_info, 0, sizeof(union unf_sfp_eeprome_info));
|
|
|
|
ret = unf_cm_get_sfp_info(port_id, &status, sfp_info, &sfp_type);
|
|
if (ret == UNF_RETURN_ERROR || (status != DRV_CABLE_CONNECTOR_OPTICAL))
|
|
msg_head.status = UNF_ADMIN_MSG_FAILED;
|
|
|
|
msg_head.size = sizeof(struct unf_admin_msg_head);
|
|
memcpy(buff_out, &msg_head, sizeof(struct unf_admin_msg_head));
|
|
memcpy((buff_out + msg_head.size),
|
|
&sfp_info->sfp_info, sizeof(struct unf_sfp_info_s));
|
|
|
|
vfree(sfp_info);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int unf_cm_clear_error_code_sum(struct unf_lport_s *v_lport,
|
|
struct unf_hinicam_pkg *v_input)
|
|
{
|
|
int ret = RETURN_OK;
|
|
void *out_buf = NULL;
|
|
unsigned int port_id = 0;
|
|
struct unf_admin_msg_head msg_head = { 0 };
|
|
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_input,
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE,
|
|
*v_input->out_size >= sizeof(struct unf_adm_cmd),
|
|
return UNF_RETURN_ERROR);
|
|
out_buf = v_input->buff_out;
|
|
port_id = v_lport->port_id;
|
|
msg_head.status = UNF_ADMIN_MSG_DONE;
|
|
|
|
ret = unf_cm_clear_port_error_code_sum(port_id);
|
|
if (ret != RETURN_OK)
|
|
msg_head.status = UNF_ADMIN_MSG_FAILED;
|
|
|
|
msg_head.size = sizeof(struct unf_admin_msg_head);
|
|
*v_input->out_size = sizeof(struct unf_adm_cmd);
|
|
memcpy(out_buf, &msg_head, sizeof(struct unf_admin_msg_head));
|
|
return ret;
|
|
}
|
|
|
|
static int unf_cm_bbscn_set(struct unf_lport_s *v_lport,
|
|
struct unf_hinicam_pkg *v_input)
|
|
{
|
|
int ret = UNF_RETURN_ERROR;
|
|
unsigned int bbscn_val = 0;
|
|
unsigned int port_id = 0;
|
|
void *out_buf = NULL;
|
|
struct unf_adm_cmd *buff_in = NULL;
|
|
struct unf_admin_msg_head msg_head = { 0 };
|
|
|
|
UNF_CHECK_VALID(INVALID_VALUE32,
|
|
UNF_TRUE, v_input, return UNF_RETURN_ERROR);
|
|
out_buf = v_input->buff_out;
|
|
buff_in = v_input->buff_in;
|
|
port_id = v_lport->port_id;
|
|
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE,
|
|
out_buf, return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE,
|
|
buff_in, return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE,
|
|
v_input->in_size >= sizeof(struct unf_adm_cmd),
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE,
|
|
*v_input->out_size >= sizeof(struct unf_adm_cmd),
|
|
return UNF_RETURN_ERROR);
|
|
bbscn_val = buff_in->arg[1];
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"[info]BBSCN value (0x%x)", bbscn_val);
|
|
msg_head.status = UNF_ADMIN_MSG_DONE;
|
|
if (bbscn_val <= UNF_MAX_BBSCN_VALUE) {
|
|
ret = unf_cm_set_port_bbscn(port_id, bbscn_val);
|
|
if (ret != RETURN_OK)
|
|
msg_head.status = UNF_ADMIN_MSG_FAILED;
|
|
} else {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]BBSCN value is invalid(0x%x)", bbscn_val);
|
|
msg_head.status = UNF_ADMIN_MSG_FAILED;
|
|
}
|
|
|
|
msg_head.size = sizeof(struct unf_admin_msg_head);
|
|
*v_input->out_size = sizeof(struct unf_adm_cmd);
|
|
memcpy(out_buf, &msg_head, sizeof(struct unf_admin_msg_head));
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void unf_fc_host_counter(struct unf_lport_s *v_lport,
|
|
struct hifc_adm_dfx_cmd_s *v_buff_out)
|
|
{
|
|
unsigned int scsi_id = 0;
|
|
unsigned int index = 0;
|
|
struct unf_rport_scsi_id_image_s *scsi_image_table = NULL;
|
|
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_lport, return);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_buff_out, return);
|
|
|
|
scsi_image_table = &v_lport->rport_scsi_table;
|
|
v_buff_out->unresult.host_cnt.host_num =
|
|
v_lport->host_info.p_scsi_host->host_no;
|
|
v_buff_out->unresult.host_cnt.port_id = v_lport->port_id;
|
|
v_buff_out->unresult.host_cnt.scsi_session_add_success =
|
|
atomic_read(&v_lport->scsi_session_add_success);
|
|
v_buff_out->unresult.host_cnt.scsi_session_add_failed =
|
|
atomic_read(&v_lport->scsi_session_add_failed);
|
|
v_buff_out->unresult.host_cnt.scsi_session_del_success =
|
|
atomic_read(&v_lport->scsi_session_del_success);
|
|
v_buff_out->unresult.host_cnt.scsi_session_del_failed =
|
|
atomic_read(&v_lport->scsi_session_del_failed);
|
|
v_buff_out->unresult.host_cnt.device_alloc =
|
|
atomic_read(&v_lport->device_alloc);
|
|
v_buff_out->unresult.host_cnt.device_destroy =
|
|
atomic_read(&v_lport->device_destroy);
|
|
v_buff_out->unresult.host_cnt.session_loss_tmo =
|
|
atomic_read(&v_lport->session_loss_tmo);
|
|
v_buff_out->unresult.host_cnt.alloc_scsi_id =
|
|
atomic_read(&v_lport->alloc_scsi_id);
|
|
v_buff_out->unresult.host_cnt.reuse_scsi_id =
|
|
atomic_read(&v_lport->reuse_scsi_id);
|
|
v_buff_out->unresult.host_cnt.resume_scsi_id =
|
|
atomic_read(&v_lport->resume_scsi_id);
|
|
v_buff_out->unresult.host_cnt.add_start_work_failed =
|
|
atomic_read(&v_lport->add_start_work_failed);
|
|
v_buff_out->unresult.host_cnt.add_closing_work_failed =
|
|
atomic_read(&v_lport->add_closing_work_failed);
|
|
|
|
for (scsi_id = 0; scsi_id < UNF_MAX_SCSI_ID / 2; scsi_id++) {
|
|
index = scsi_id * 2;
|
|
v_buff_out->unresult.host_cnt.session_state[scsi_id].session1 =
|
|
(unsigned char)atomic_read(&scsi_image_table->wwn_rport_info_table[index].en_scsi_state);
|
|
|
|
index = scsi_id * 2 + 1;
|
|
v_buff_out->unresult.host_cnt.session_state[scsi_id].session2 =
|
|
(unsigned char)atomic_read(&scsi_image_table->wwn_rport_info_table[index].en_scsi_state);
|
|
}
|
|
|
|
for (scsi_id = 0; scsi_id < UNF_MAX_SCSI_ID; scsi_id++) {
|
|
if (!scsi_image_table->wwn_rport_info_table[scsi_id].dfx_counter)
|
|
continue;
|
|
v_buff_out->unresult.host_cnt.abort_io +=
|
|
atomic_read(&scsi_image_table->wwn_rport_info_table[scsi_id].dfx_counter->error_handle[UNF_SCSI_ABORT_IO_TYPE]);
|
|
v_buff_out->unresult.host_cnt.device_reset +=
|
|
atomic_read(&scsi_image_table->wwn_rport_info_table[scsi_id].dfx_counter->error_handle[UNF_SCSI_DEVICE_RESET_TYPE]);
|
|
v_buff_out->unresult.host_cnt.target_reset +=
|
|
atomic_read(&scsi_image_table->wwn_rport_info_table[scsi_id].dfx_counter->error_handle[UNF_SCSI_TARGET_RESET_TYPE]);
|
|
v_buff_out->unresult.host_cnt.bus_reset +=
|
|
atomic_read(&scsi_image_table->wwn_rport_info_table[scsi_id].dfx_counter->error_handle[UNF_SCSI_BUS_RESET_TYPE]);
|
|
v_buff_out->unresult.host_cnt.virtual_reset +=
|
|
atomic_read(&scsi_image_table->wwn_rport_info_table[scsi_id].dfx_counter->error_handle[UNF_SCSI_VIRTUAL_RESET_TYPE]);
|
|
v_buff_out->unresult.host_cnt.abort_io_result +=
|
|
atomic_read(&scsi_image_table->wwn_rport_info_table[scsi_id].dfx_counter->error_handle_result[UNF_SCSI_ABORT_IO_TYPE]);
|
|
v_buff_out->unresult.host_cnt.device_reset_result +=
|
|
atomic_read(&scsi_image_table->wwn_rport_info_table[scsi_id].dfx_counter->error_handle_result[UNF_SCSI_DEVICE_RESET_TYPE]);
|
|
v_buff_out->unresult.host_cnt.target_reset_result +=
|
|
atomic_read(&scsi_image_table->wwn_rport_info_table[scsi_id].dfx_counter->error_handle_result[UNF_SCSI_TARGET_RESET_TYPE]);
|
|
v_buff_out->unresult.host_cnt.bus_reset_result +=
|
|
atomic_read(&scsi_image_table->wwn_rport_info_table[scsi_id].dfx_counter->error_handle_result[UNF_SCSI_BUS_RESET_TYPE]);
|
|
v_buff_out->unresult.host_cnt.virtual_reset_result +=
|
|
atomic_read(&scsi_image_table->wwn_rport_info_table[scsi_id].dfx_counter->error_handle_result[UNF_SCSI_VIRTUAL_RESET_TYPE]);
|
|
}
|
|
}
|
|
|
|
static void unf_fc_session_counter(struct unf_lport_s *v_lport,
|
|
unsigned int scsi_id,
|
|
struct hifc_adm_dfx_cmd_s *v_buff_out)
|
|
{
|
|
struct unf_wwpn_rport_info_s *rport_info = NULL;
|
|
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_lport, return);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_buff_out, return);
|
|
|
|
rport_info = &v_lport->rport_scsi_table.wwn_rport_info_table[scsi_id];
|
|
v_buff_out->unresult.session_cnt.port_id = v_lport->port_id;
|
|
v_buff_out->unresult.session_cnt.host_id =
|
|
v_lport->host_info.p_scsi_host->host_no;
|
|
|
|
if (rport_info->dfx_counter) {
|
|
v_buff_out->unresult.session_cnt.target_busy =
|
|
atomic64_read(&rport_info->dfx_counter->target_busy);
|
|
v_buff_out->unresult.session_cnt.host_busy =
|
|
atomic64_read(&rport_info->dfx_counter->host_busy);
|
|
v_buff_out->unresult.session_cnt.abort_io =
|
|
atomic_read(&rport_info->dfx_counter->error_handle[UNF_SCSI_ABORT_IO_TYPE]);
|
|
v_buff_out->unresult.session_cnt.device_reset =
|
|
atomic_read(&rport_info->dfx_counter->error_handle[UNF_SCSI_DEVICE_RESET_TYPE]);
|
|
v_buff_out->unresult.session_cnt.target_reset =
|
|
atomic_read(&rport_info->dfx_counter->error_handle[UNF_SCSI_TARGET_RESET_TYPE]);
|
|
v_buff_out->unresult.session_cnt.bus_reset =
|
|
atomic_read(&rport_info->dfx_counter->error_handle[UNF_SCSI_BUS_RESET_TYPE]);
|
|
v_buff_out->unresult.session_cnt.virtual_reset =
|
|
atomic_read(&rport_info->dfx_counter->error_handle[UNF_SCSI_VIRTUAL_RESET_TYPE]);
|
|
|
|
v_buff_out->unresult.session_cnt.abort_io_result =
|
|
atomic_read(&rport_info->dfx_counter->error_handle_result[UNF_SCSI_ABORT_IO_TYPE]);
|
|
v_buff_out->unresult.session_cnt.device_reset_result =
|
|
atomic_read(&rport_info->dfx_counter->error_handle_result[UNF_SCSI_DEVICE_RESET_TYPE]);
|
|
v_buff_out->unresult.session_cnt.target_reset_result =
|
|
atomic_read(&rport_info->dfx_counter->error_handle_result[UNF_SCSI_TARGET_RESET_TYPE]);
|
|
v_buff_out->unresult.session_cnt.bus_reset_result =
|
|
atomic_read(&rport_info->dfx_counter->error_handle_result[UNF_SCSI_BUS_RESET_TYPE]);
|
|
v_buff_out->unresult.session_cnt.virtual_reset_result =
|
|
atomic_read(&rport_info->dfx_counter->error_handle_result[UNF_SCSI_VIRTUAL_RESET_TYPE]);
|
|
|
|
v_buff_out->unresult.session_cnt.device_alloc =
|
|
atomic_read(&rport_info->dfx_counter->device_alloc);
|
|
v_buff_out->unresult.session_cnt.device_destroy =
|
|
atomic_read(&rport_info->dfx_counter->device_destroy);
|
|
}
|
|
|
|
v_buff_out->unresult.session_cnt.target_id = rport_info->target_id;
|
|
|
|
if ((rport_info->wwpn != INVALID_WWPN) && (rport_info->rport)) {
|
|
v_buff_out->unresult.session_cnt.remote_port_wwpn =
|
|
rport_info->wwpn;
|
|
v_buff_out->unresult.session_cnt.remote_port_nportid =
|
|
rport_info->rport->nport_id;
|
|
v_buff_out->unresult.session_cnt.scsi_state =
|
|
atomic_read(&rport_info->en_scsi_state);
|
|
v_buff_out->unresult.session_cnt.remote_port_state =
|
|
rport_info->rport->rp_state;
|
|
v_buff_out->unresult.session_cnt.remote_port_scsiid =
|
|
rport_info->rport->scsi_id;
|
|
v_buff_out->unresult.session_cnt.remote_port_index =
|
|
rport_info->rport->rport_index;
|
|
|
|
if (rport_info->rport->lport) {
|
|
v_buff_out->unresult.session_cnt.local_port_wwpn =
|
|
rport_info->rport->lport->port_name;
|
|
v_buff_out->unresult.session_cnt.local_port_nportid =
|
|
rport_info->rport->local_nport_id;
|
|
v_buff_out->unresult.session_cnt.local_port_ini_state =
|
|
rport_info->rport->lport_ini_state;
|
|
v_buff_out->unresult.session_cnt.local_port_state =
|
|
rport_info->rport->lport->en_states;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int unf_fc_session_scsi_cmd_in(
|
|
struct unf_hinicam_pkg *v_input,
|
|
struct unf_rport_scsi_id_image_s *scsi_image_table)
|
|
{
|
|
unsigned int scsi_id = 0;
|
|
unsigned int scsi_cmd_type = 0;
|
|
int ret = RETURN_OK;
|
|
|
|
struct hifc_adm_dfx_cmd_s *buff_out = NULL;
|
|
struct unf_adm_cmd *buff_in = NULL;
|
|
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_input,
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, scsi_image_table,
|
|
return UNF_RETURN_ERROR);
|
|
|
|
buff_in = (struct unf_adm_cmd *)v_input->buff_in;
|
|
buff_out = (struct hifc_adm_dfx_cmd_s *)v_input->buff_out;
|
|
|
|
scsi_id = buff_in->arg[2];
|
|
scsi_cmd_type = buff_in->arg[3];
|
|
|
|
if (scsi_id >= UNF_MAX_SCSI_ID || scsi_cmd_type >= UNF_MAX_SCSI_CMD)
|
|
return UNF_RETURN_ERROR;
|
|
|
|
if (scsi_image_table->wwn_rport_info_table[scsi_id].dfx_counter)
|
|
buff_out->unresult.scsi_cmd_in =
|
|
atomic64_read(&scsi_image_table->wwn_rport_info_table[scsi_id].dfx_counter->scsi_cmd_cnt[scsi_cmd_type]);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int unf_fc_host_scsi_cmd_in_total(
|
|
struct unf_hinicam_pkg *v_input,
|
|
struct unf_rport_scsi_id_image_s *scsi_image_table)
|
|
{
|
|
unsigned int scsi_id = 0;
|
|
unsigned int scsi_cmd_type = 0;
|
|
|
|
struct hifc_adm_dfx_cmd_s *buff_out = NULL;
|
|
struct unf_adm_cmd *buff_in = NULL;
|
|
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_input,
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, scsi_image_table,
|
|
return UNF_RETURN_ERROR);
|
|
|
|
buff_in = (struct unf_adm_cmd *)v_input->buff_in;
|
|
buff_out = (struct hifc_adm_dfx_cmd_s *)v_input->buff_out;
|
|
|
|
scsi_cmd_type = buff_in->arg[3];
|
|
|
|
if (scsi_cmd_type >= UNF_MAX_SCSI_CMD)
|
|
return UNF_RETURN_ERROR;
|
|
|
|
for (scsi_id = 0; scsi_id < UNF_MAX_SCSI_ID; scsi_id++) {
|
|
if (!scsi_image_table->wwn_rport_info_table[scsi_id].dfx_counter)
|
|
continue;
|
|
buff_out->unresult.scsi_cmd_in +=
|
|
atomic64_read(&scsi_image_table->wwn_rport_info_table[scsi_id].dfx_counter->scsi_cmd_cnt[scsi_cmd_type]);
|
|
}
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
static int unf_fc_host_scsi_cmd_done_total(
|
|
struct unf_hinicam_pkg *v_input,
|
|
struct unf_rport_scsi_id_image_s *scsi_image_table)
|
|
{
|
|
unsigned int scsi_id = 0;
|
|
unsigned int io_return_value = 0;
|
|
int ret = RETURN_OK;
|
|
|
|
struct hifc_adm_dfx_cmd_s *buff_out = NULL;
|
|
struct unf_adm_cmd *buff_in = NULL;
|
|
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_input,
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, scsi_image_table,
|
|
return UNF_RETURN_ERROR);
|
|
|
|
buff_in = (struct unf_adm_cmd *)v_input->buff_in;
|
|
buff_out = (struct hifc_adm_dfx_cmd_s *)v_input->buff_out;
|
|
|
|
io_return_value = buff_in->arg[3];
|
|
|
|
if (io_return_value >= UNF_MAX_IO_RETURN_VALUE)
|
|
return UNF_RETURN_ERROR;
|
|
|
|
for (scsi_id = 0; scsi_id < UNF_MAX_SCSI_ID; scsi_id++) {
|
|
if (!scsi_image_table->wwn_rport_info_table[scsi_id].dfx_counter)
|
|
continue;
|
|
buff_out->unresult.scsi_cmd_done +=
|
|
atomic64_read(&scsi_image_table->wwn_rport_info_table[scsi_id].dfx_counter->io_done_cnt[io_return_value]);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int unf_fc_session_scsi_cmd_done(
|
|
struct unf_hinicam_pkg *v_input,
|
|
struct unf_rport_scsi_id_image_s *scsi_image_table)
|
|
{
|
|
unsigned int scsi_id = 0;
|
|
unsigned int io_return_value = 0;
|
|
int ret = RETURN_OK;
|
|
|
|
struct hifc_adm_dfx_cmd_s *buff_out = NULL;
|
|
struct unf_adm_cmd *buff_in = NULL;
|
|
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_input,
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, scsi_image_table,
|
|
return UNF_RETURN_ERROR);
|
|
|
|
buff_in = (struct unf_adm_cmd *)v_input->buff_in;
|
|
buff_out = (struct hifc_adm_dfx_cmd_s *)v_input->buff_out;
|
|
|
|
scsi_id = buff_in->arg[2];
|
|
io_return_value = buff_in->arg[3];
|
|
|
|
if (scsi_id >= UNF_MAX_SCSI_ID ||
|
|
io_return_value >= UNF_MAX_IO_RETURN_VALUE)
|
|
return UNF_RETURN_ERROR;
|
|
|
|
if (scsi_image_table->wwn_rport_info_table[scsi_id].dfx_counter)
|
|
buff_out->unresult.scsi_cmd_done =
|
|
atomic64_read(&scsi_image_table->wwn_rport_info_table[scsi_id].dfx_counter->io_done_cnt[io_return_value]);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int unf_get_io_dfx_statistics(struct unf_lport_s *v_lport,
|
|
struct unf_hinicam_pkg *v_input)
|
|
{
|
|
int ret = RETURN_OK;
|
|
unsigned int counter_mode = 0;
|
|
struct hifc_adm_dfx_cmd_s *buff_out = NULL;
|
|
struct unf_adm_cmd *buff_in = NULL;
|
|
struct unf_admin_msg_head msg_head = { 0 };
|
|
struct unf_rport_scsi_id_image_s *scsi_image_table = NULL;
|
|
unsigned int scsi_id = 0;
|
|
struct unf_lport_s *vport = NULL;
|
|
unsigned int buff_flag = 0;
|
|
|
|
buff_flag = (!v_input) || (!v_input->buff_out) ||
|
|
(!v_input->buff_in) || (!v_lport);
|
|
if (buff_flag)
|
|
return UNF_RETURN_ERROR;
|
|
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE,
|
|
v_input->in_size >= sizeof(struct unf_adm_cmd),
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE,
|
|
*v_input->out_size >= sizeof(struct hifc_adm_dfx_cmd_s),
|
|
return UNF_RETURN_ERROR);
|
|
buff_in = (struct unf_adm_cmd *)v_input->buff_in;
|
|
buff_out = (struct hifc_adm_dfx_cmd_s *)v_input->buff_out;
|
|
msg_head.status = UNF_ADMIN_MSG_DONE;
|
|
|
|
vport = unf_cm_lookup_vport_by_vp_index(
|
|
v_lport, (unsigned short)(buff_in->arg[4]));
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, vport,
|
|
return UNF_RETURN_ERROR);
|
|
|
|
scsi_image_table = &vport->rport_scsi_table;
|
|
FC_DRIVE_ACTION_CHECK((!scsi_image_table->wwn_rport_info_table),
|
|
(msg_head.status = UNF_ADMIN_MSG_FAILED),
|
|
(ret = UNF_RETURN_ERROR),
|
|
goto err);
|
|
|
|
counter_mode = buff_in->arg[1];
|
|
switch (counter_mode) {
|
|
case FC_HOST_COUNTER:
|
|
unf_fc_host_counter(vport, buff_out);
|
|
break;
|
|
case FC_SESSION_SCSI_CMD_IN:
|
|
ret = unf_fc_session_scsi_cmd_in(v_input, scsi_image_table);
|
|
break;
|
|
case FC_HOST_SCSI_CMD_IN_TOTAL:
|
|
ret = unf_fc_host_scsi_cmd_in_total(v_input, scsi_image_table);
|
|
break;
|
|
case FC_HOST_SCSI_CMD_DONE_TOTAL:
|
|
ret = unf_fc_host_scsi_cmd_done_total(v_input,
|
|
scsi_image_table);
|
|
break;
|
|
case FC_SESSION_SCSI_CMD_DONE:
|
|
ret = unf_fc_session_scsi_cmd_done(v_input, scsi_image_table);
|
|
break;
|
|
case FC_SESSION_COUNTER:
|
|
scsi_id = buff_in->arg[2];
|
|
FC_DRIVE_ACTION_CHECK((scsi_id >= UNF_MAX_SCSI_ID),
|
|
(msg_head.status = UNF_ADMIN_MSG_FAILED),
|
|
(ret = UNF_RETURN_ERROR),
|
|
goto err);
|
|
unf_fc_session_counter(vport, scsi_id, buff_out);
|
|
break;
|
|
default:
|
|
msg_head.status = UNF_ADMIN_MSG_FAILED;
|
|
ret = UNF_RETURN_ERROR;
|
|
break;
|
|
}
|
|
|
|
if (ret != RETURN_OK)
|
|
return ret;
|
|
|
|
err:
|
|
msg_head.size = sizeof(struct unf_admin_msg_head);
|
|
*v_input->out_size = sizeof(struct hifc_adm_dfx_cmd_s);
|
|
memcpy(buff_out, &msg_head, sizeof(struct unf_admin_msg_head));
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int unf_cm_switch_dif(unsigned int v_option,
|
|
unsigned int v_dix_ip_checksum)
|
|
{
|
|
#define UNF_WAIT_IO_COMPLETE_TIME_MS 5000
|
|
#define UNF_WAIT_ONE_TIME_MS 100
|
|
#define UNF_LOOP_TIMES (UNF_WAIT_IO_COMPLETE_TIME_MS / UNF_WAIT_ONE_TIME_MS)
|
|
|
|
int ret = UNF_RETURN_ERROR;
|
|
struct unf_lport_s *lport = NULL;
|
|
unsigned long flags = 0;
|
|
int enable_dif;
|
|
unsigned int index;
|
|
|
|
dix_flag = v_dix_ip_checksum ? UNF_TRUE : UNF_FALSE;
|
|
|
|
enable_dif = (v_option >= UNF_ENABLE_DIF_DIX_PROT &&
|
|
v_option <= UNF_ENABLE_DIX_PROT);
|
|
if (enable_dif) {
|
|
dif_sgl_mode = UNF_TRUE;
|
|
hifc_dif_enable = UNF_TRUE;
|
|
}
|
|
|
|
switch (v_option) {
|
|
case UNF_DIF_ACTION_NONE:
|
|
dif_sgl_mode = UNF_FALSE;
|
|
hifc_dif_enable = UNF_FALSE;
|
|
hifc_dif_type = 0;
|
|
hifc_guard = 0;
|
|
break;
|
|
|
|
case UNF_ENABLE_DIF_DIX_PROT:
|
|
hifc_dif_type = SHOST_DIF_TYPE1_PROTECTION |
|
|
SHOST_DIX_TYPE1_PROTECTION;
|
|
break;
|
|
|
|
case UNF_ENABLE_DIF_PROT:
|
|
hifc_dif_type = SHOST_DIF_TYPE1_PROTECTION;
|
|
dif_sgl_mode = UNF_FALSE;
|
|
break;
|
|
|
|
case UNF_ENABLE_DIX_PROT:
|
|
hifc_dif_type = SHOST_DIX_TYPE0_PROTECTION;
|
|
break;
|
|
|
|
default:
|
|
return UNF_ADMIN_MSG_FAILED;
|
|
}
|
|
|
|
/* 1. Close Lport's SFP */
|
|
spin_lock_irqsave(&global_lport_mgr.global_lport_list_lock, flags);
|
|
list_for_each_entry(lport, &global_lport_mgr.list_lport_list_head,
|
|
entry_lport) {
|
|
spin_unlock_irqrestore(&global_lport_mgr.global_lport_list_lock,
|
|
flags);
|
|
|
|
ret = unf_cm_sfp_switch(lport->port_id, UNF_FALSE);
|
|
if (ret != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT,
|
|
UNF_ERR,
|
|
"[err]Port(0x%x) close SFP failed in DIF switch",
|
|
lport->port_id);
|
|
|
|
return UNF_ADMIN_MSG_FAILED;
|
|
}
|
|
for (index = 0; index < UNF_LOOP_TIMES; index++) {
|
|
if (unf_busy_io_completed(lport) == UNF_TRUE)
|
|
break;
|
|
msleep(UNF_WAIT_ONE_TIME_MS);
|
|
}
|
|
|
|
spin_lock_irqsave(&global_lport_mgr.global_lport_list_lock,
|
|
flags);
|
|
}
|
|
spin_unlock_irqrestore(&global_lport_mgr.global_lport_list_lock,
|
|
flags);
|
|
|
|
/* 2. UnRegister the SCSI host of LPort, including its Vports */
|
|
spin_lock_irqsave(&global_lport_mgr.global_lport_list_lock, flags);
|
|
list_for_each_entry(lport, &global_lport_mgr.list_lport_list_head,
|
|
entry_lport) {
|
|
spin_unlock_irqrestore(&global_lport_mgr.global_lport_list_lock,
|
|
flags);
|
|
unf_unregister_scsi_host(lport);
|
|
spin_lock_irqsave(&global_lport_mgr.global_lport_list_lock,
|
|
flags);
|
|
}
|
|
spin_unlock_irqrestore(&global_lport_mgr.global_lport_list_lock, flags);
|
|
|
|
/* 3. Register the SCSI host of LPort, including its Vports */
|
|
spin_lock_irqsave(&global_lport_mgr.global_lport_list_lock, flags);
|
|
list_for_each_entry(lport, &global_lport_mgr.list_lport_list_head,
|
|
entry_lport) {
|
|
spin_unlock_irqrestore(&global_lport_mgr.global_lport_list_lock,
|
|
flags);
|
|
if (unf_register_scsi_host(lport) != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_REG_ATT,
|
|
UNF_WARN, "[warn]Port(0x%x) register scsi host failed in DIF switch",
|
|
lport->port_id);
|
|
return UNF_ADMIN_MSG_FAILED;
|
|
}
|
|
spin_lock_irqsave(&global_lport_mgr.global_lport_list_lock,
|
|
flags);
|
|
}
|
|
spin_unlock_irqrestore(&global_lport_mgr.global_lport_list_lock, flags);
|
|
|
|
/* 4. Open Lport's SFP */
|
|
spin_lock_irqsave(&global_lport_mgr.global_lport_list_lock, flags);
|
|
list_for_each_entry(lport, &global_lport_mgr.list_lport_list_head,
|
|
entry_lport) {
|
|
spin_unlock_irqrestore(&global_lport_mgr.global_lport_list_lock,
|
|
flags);
|
|
|
|
ret = unf_cm_sfp_switch(lport->port_id, UNF_TRUE);
|
|
if (ret != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT,
|
|
UNF_ERR,
|
|
"[err]Port(0x%x) reopen SFP failed in DIF switch",
|
|
lport->port_id);
|
|
|
|
return UNF_ADMIN_MSG_FAILED;
|
|
}
|
|
|
|
spin_lock_irqsave(&global_lport_mgr.global_lport_list_lock,
|
|
flags);
|
|
}
|
|
spin_unlock_irqrestore(&global_lport_mgr.global_lport_list_lock, flags);
|
|
|
|
return UNF_ADMIN_MSG_DONE;
|
|
}
|
|
|
|
static int unf_cm_switch_app_ref_escape(unsigned int v_option)
|
|
{
|
|
switch (v_option) {
|
|
case UNF_APP_REF_ESC_BOTH_NOT_CHECK:
|
|
dif_app_esc_check = HIFC_DIF_APP_REF_ESC_NOT_CHECK;
|
|
dif_ref_esc_check = HIFC_DIF_APP_REF_ESC_NOT_CHECK;
|
|
break;
|
|
|
|
case UNF_APP_ESC_CHECK:
|
|
dif_app_esc_check = HIFC_DIF_APP_REF_ESC_CHECK;
|
|
dif_ref_esc_check = HIFC_DIF_APP_REF_ESC_NOT_CHECK;
|
|
break;
|
|
|
|
case UNF_REF_ESC_CHECK:
|
|
dif_app_esc_check = HIFC_DIF_APP_REF_ESC_NOT_CHECK;
|
|
dif_ref_esc_check = HIFC_DIF_APP_REF_ESC_CHECK;
|
|
break;
|
|
|
|
case UNF_APP_REF_ESC_BOTH_CHECK:
|
|
dif_app_esc_check = HIFC_DIF_APP_REF_ESC_CHECK;
|
|
dif_ref_esc_check = HIFC_DIF_APP_REF_ESC_CHECK;
|
|
break;
|
|
|
|
default:
|
|
dif_app_esc_check = HIFC_DIF_APP_REF_ESC_CHECK;
|
|
dif_ref_esc_check = HIFC_DIF_APP_REF_ESC_CHECK;
|
|
break;
|
|
}
|
|
|
|
return UNF_ADMIN_MSG_DONE;
|
|
}
|
|
|
|
static int unf_cm_select_dif_mode(struct unf_lport_s *v_lport,
|
|
struct unf_hinicam_pkg *v_input)
|
|
{
|
|
unsigned int dif_mode = 0;
|
|
unsigned int option = 0;
|
|
unsigned int dix_ip_checksum = 0;
|
|
struct unf_adm_cmd *buff_in = NULL;
|
|
struct unf_adm_cmd *buff_out = NULL;
|
|
struct unf_admin_msg_head msg_head = { 0 };
|
|
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_input,
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_input->buff_out,
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_input->buff_in,
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE,
|
|
v_input->in_size >= sizeof(struct unf_adm_cmd),
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE,
|
|
*v_input->out_size >= sizeof(struct unf_adm_cmd),
|
|
return UNF_RETURN_ERROR);
|
|
|
|
buff_in = (struct unf_adm_cmd *)v_input->buff_in;
|
|
buff_out = (struct unf_adm_cmd *)v_input->buff_out;
|
|
msg_head.status = UNF_ADMIN_MSG_DONE;
|
|
dif_mode = buff_in->arg[0];
|
|
option = buff_in->arg[1];
|
|
dix_ip_checksum = buff_in->arg[2];
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"[info]DIF mode(0x%x) sub option(0x%x 0x%x)",
|
|
dif_mode, option, dix_ip_checksum);
|
|
|
|
switch (dif_mode) {
|
|
case UNF_SWITCH_DIF_DIX:
|
|
msg_head.status =
|
|
(unsigned short)unf_cm_switch_dif(option,
|
|
dix_ip_checksum);
|
|
break;
|
|
|
|
case UNF_APP_REF_ESCAPE:
|
|
msg_head.status =
|
|
(unsigned short)unf_cm_switch_app_ref_escape(option);
|
|
break;
|
|
|
|
default:
|
|
msg_head.status = UNF_ADMIN_MSG_FAILED;
|
|
goto end;
|
|
}
|
|
|
|
end:
|
|
msg_head.size = sizeof(struct unf_admin_msg_head);
|
|
*v_input->out_size = sizeof(struct unf_adm_cmd);
|
|
memcpy(buff_out, &msg_head, sizeof(struct unf_admin_msg_head));
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
static int unf_cm_set_dif(struct unf_lport_s *v_lport,
|
|
struct unf_hinicam_pkg *v_input)
|
|
{
|
|
unsigned int dif_switch = 0;
|
|
struct unf_adm_cmd *buff_in = NULL;
|
|
struct unf_adm_cmd *buff_out = NULL;
|
|
struct unf_admin_msg_head msg_head = { 0 };
|
|
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_input,
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_input->buff_out,
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_input->buff_in,
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE,
|
|
v_input->in_size >= sizeof(struct unf_adm_cmd),
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE,
|
|
*v_input->out_size >= sizeof(struct unf_adm_cmd),
|
|
return UNF_RETURN_ERROR);
|
|
buff_in = (struct unf_adm_cmd *)v_input->buff_in;
|
|
buff_out = (struct unf_adm_cmd *)v_input->buff_out;
|
|
msg_head.status = UNF_ADMIN_MSG_DONE;
|
|
dif_switch = (buff_in->arg[0]) ?
|
|
UNF_ENABLE_DIF_DIX_PROT : UNF_DIF_ACTION_NONE;
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"[info]DIF switch is 0x%x", dif_switch);
|
|
|
|
if (dif_switch == UNF_ENABLE_DIF_DIX_PROT)
|
|
msg_head.status = (unsigned short)unf_cm_switch_dif(dif_switch,
|
|
UNF_ENABLE_IP_CHECKSUM);
|
|
else
|
|
msg_head.status = (unsigned short)unf_cm_switch_dif(dif_switch,
|
|
UNF_DISABLE_IP_CHECKSUM);
|
|
|
|
msg_head.size = sizeof(struct unf_admin_msg_head);
|
|
*v_input->out_size = sizeof(struct unf_adm_cmd);
|
|
memcpy(buff_out, &msg_head, sizeof(struct unf_admin_msg_head));
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
static unsigned int unf_save_port_info(struct unf_lport_s *lport,
|
|
void *save_info_addr)
|
|
{
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
|
|
UNF_CHECK_VALID(0x2271, UNF_TRUE, save_info_addr,
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(0x2271, UNF_TRUE, lport, return UNF_RETURN_ERROR);
|
|
|
|
if (!lport->low_level_func.port_mgr_op.pfn_ll_port_config_set) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT, UNF_MAJOR,
|
|
"Port(0x%x)'s corresponding function is NULL.",
|
|
lport->port_id);
|
|
|
|
return ret;
|
|
}
|
|
|
|
ret = lport->low_level_func.port_mgr_op.pfn_ll_port_config_set(
|
|
lport->fc_port,
|
|
UNF_PORT_CFG_SAVE_HBA_INFO, (void *)save_info_addr);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static unsigned int unf_save_port_base_info(struct unf_lport_s *lport,
|
|
void *v_save_info)
|
|
{
|
|
struct unf_save_info_head_s *save_info_head = v_save_info;
|
|
struct unf_port_info_entry_s *sava_port_entry = NULL;
|
|
struct unf_low_level_port_mgr_op_s *port_mgr = NULL;
|
|
unsigned int cfg_speed = 0;
|
|
unsigned int topo_cfg = 0;
|
|
int fec = UNF_FALSE;
|
|
int ret = UNF_RETURN_ERROR;
|
|
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, lport,
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, save_info_head,
|
|
return UNF_RETURN_ERROR);
|
|
|
|
save_info_head->opcode = 0;
|
|
/* write information to up */
|
|
save_info_head->type = UNF_PORT_BASE_INFO; /* port base info */
|
|
save_info_head->entry_num = 1;
|
|
save_info_head->next = 0xffff;
|
|
|
|
sava_port_entry = (struct unf_port_info_entry_s *)
|
|
((void *)(save_info_head + 1));
|
|
|
|
port_mgr = &lport->low_level_func.port_mgr_op;
|
|
if (!port_mgr->pfn_ll_port_config_get) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EQUIP_ATT, UNF_MAJOR,
|
|
"Port(0x%x)'s corresponding function is NULL.",
|
|
lport->nport_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
/* get Bbscn */
|
|
sava_port_entry->bb_scn = unf_low_level_bbscn(lport);
|
|
|
|
/* get speed */
|
|
port_mgr->pfn_ll_port_config_get(lport->fc_port,
|
|
UNF_PORT_CFG_GET_SPEED_CFG,
|
|
(void *)&cfg_speed);
|
|
sava_port_entry->speed = cfg_speed;
|
|
|
|
/* get topo */
|
|
port_mgr->pfn_ll_port_config_get(lport->fc_port,
|
|
UNF_PORT_CFG_GET_TOPO_CFG,
|
|
(void *)&topo_cfg);
|
|
sava_port_entry->topo = topo_cfg;
|
|
|
|
/* get fec */
|
|
port_mgr->pfn_ll_port_config_get(lport->fc_port,
|
|
UNF_PORT_CFG_GET_FEC,
|
|
(void *)&fec);
|
|
sava_port_entry->fec = fec;
|
|
|
|
ret = (int)unf_save_port_info(lport, v_save_info);
|
|
if (ret != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_EQUIP_ATT, UNF_WARN,
|
|
"[warn]Port(0x%x) send mailbox fail",
|
|
lport->port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
unsigned int unf_cm_save_port_info(unsigned int v_port_id)
|
|
{
|
|
unsigned int port_id = v_port_id;
|
|
struct unf_lport_s *lport = NULL;
|
|
struct unf_save_info_head_s *save_info = NULL;
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
|
|
lport = unf_find_lport_by_port_id(port_id);
|
|
if (!lport) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_EQUIP_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) can not be found", port_id);
|
|
|
|
return ret;
|
|
}
|
|
|
|
save_info = vmalloc(SAVE_PORT_INFO_LEN);
|
|
if (!save_info) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_NORMAL, UNF_ERR,
|
|
"[err]Can't alloc buffer for saving port info");
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* 1 clean flush */
|
|
memset(save_info, 0, SAVE_PORT_INFO_LEN);
|
|
|
|
save_info->opcode = 2; /* notify up to clean flush */
|
|
save_info->type = 0xf;
|
|
save_info->entry_num = 0;
|
|
save_info->next = 0xffff;
|
|
|
|
ret = unf_save_port_info(lport, save_info);
|
|
if (ret != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_EQUIP_ATT, UNF_MAJOR,
|
|
"[warn]Port(0x%x) send mailbox fail", lport->port_id);
|
|
|
|
vfree(save_info);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* 2 save port base information */
|
|
memset(save_info, 0, SAVE_PORT_INFO_LEN);
|
|
|
|
ret = unf_save_port_base_info(lport, save_info);
|
|
if (ret != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_EQUIP_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) save port base information failed",
|
|
lport->port_id);
|
|
|
|
vfree(save_info);
|
|
|
|
return ret;
|
|
}
|
|
|
|
vfree(save_info);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void unf_handle_port_base_info(struct unf_lport_s *lport,
|
|
struct unf_port_info_entry_s *v_save_info)
|
|
{
|
|
struct unf_port_info_entry_s *sava_port_entry = NULL;
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, lport, return);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_save_info, return);
|
|
|
|
sava_port_entry = v_save_info;
|
|
|
|
UNF_CHECK_VALID(INVALID_VALUE32,
|
|
UNF_TRUE,
|
|
(sava_port_entry->topo == UNF_TOP_LOOP_MASK) ||
|
|
(sava_port_entry->topo == UNF_TOP_P2P_MASK) ||
|
|
(sava_port_entry->topo == UNF_TOP_AUTO_MASK),
|
|
return);
|
|
|
|
if (!lport->low_level_func.port_mgr_op.pfn_ll_port_config_set) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_EQUIP_ATT, UNF_ERR,
|
|
"Port(0x%x)'s corresponding function is NULL.",
|
|
lport->port_id);
|
|
return;
|
|
}
|
|
|
|
ret = lport->low_level_func.port_mgr_op.pfn_ll_port_config_set(
|
|
lport->fc_port,
|
|
UNF_PORT_CFG_SET_HBA_BASE_INFO, (void *)sava_port_entry);
|
|
|
|
if (ret != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_EQUIP_ATT, UNF_ERR,
|
|
"Cannot set port base info");
|
|
return;
|
|
}
|
|
|
|
/* update bbsn cfg to Lport */
|
|
lport->low_level_func.lport_cfg_items.bb_scn = sava_port_entry->bb_scn;
|
|
|
|
lport->low_level_func.lport_cfg_items.port_topology =
|
|
sava_port_entry->topo;
|
|
}
|
|
|
|
static unsigned int unf_recovery_save_info(struct unf_lport_s *lport,
|
|
void *v_save_info,
|
|
unsigned char v_type)
|
|
{
|
|
struct unf_save_info_head_s *save_info_head = v_save_info;
|
|
void *info_entry = NULL;
|
|
int ret = 0;
|
|
unsigned short next_flag = 0;
|
|
unsigned char entry_num = 0;
|
|
unsigned char index = 0;
|
|
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, lport,
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, save_info_head,
|
|
return UNF_RETURN_ERROR);
|
|
|
|
do {
|
|
memset(save_info_head, 0, SAVE_PORT_INFO_LEN);
|
|
save_info_head->opcode = 1;
|
|
/* read information from up */
|
|
save_info_head->type = v_type;
|
|
/* vport[qos] info */
|
|
save_info_head->entry_num = 0xff;
|
|
save_info_head->next = next_flag;
|
|
|
|
ret = (int)unf_save_port_info(lport, save_info_head);
|
|
if (ret != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_EQUIP_ATT,
|
|
UNF_WARN,
|
|
"[warn]Port(0x%x) send mailbox fail",
|
|
lport->port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
next_flag = (unsigned short)save_info_head->next;
|
|
entry_num = (unsigned char)save_info_head->entry_num;
|
|
info_entry = save_info_head + 1;
|
|
|
|
for (index = 0; index < entry_num; index++) {
|
|
switch (v_type) {
|
|
case UNF_PORT_BASE_INFO:
|
|
unf_handle_port_base_info(lport, info_entry);
|
|
info_entry = ((struct unf_port_info_entry_s *)
|
|
info_entry) + 1;
|
|
break;
|
|
|
|
default:
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR,
|
|
UNF_LOG_EQUIP_ATT,
|
|
UNF_ERR,
|
|
"[err]Port(0x%x) handle message failed",
|
|
lport->port_id);
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
}
|
|
|
|
} while (next_flag != 0xffff);
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
unsigned int unf_cm_get_save_info(struct unf_lport_s *v_lport)
|
|
{
|
|
struct unf_lport_s *lport = NULL;
|
|
void *save_info = NULL;
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_lport,
|
|
return UNF_RETURN_ERROR);
|
|
|
|
lport = v_lport;
|
|
save_info = vmalloc(SAVE_PORT_INFO_LEN);
|
|
if (!save_info) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_NORMAL, UNF_ERR,
|
|
"[err]Can't alloc buffer for saving port info");
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* 1 get port base information */
|
|
ret = unf_recovery_save_info(lport, save_info, UNF_PORT_BASE_INFO);
|
|
if (ret != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_EQUIP_ATT, UNF_MAJOR,
|
|
"[warn]Port(0x%x) send mailbox fail", lport->port_id);
|
|
|
|
vfree(save_info);
|
|
|
|
return ret;
|
|
}
|
|
|
|
vfree(save_info);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int unf_get_link_lose_tmo(struct unf_lport_s *v_lport)
|
|
{
|
|
unsigned int tmo_value = 0;
|
|
|
|
if (!v_lport)
|
|
return UNF_LOSE_TMO;
|
|
|
|
tmo_value = atomic_read(&v_lport->link_lose_tmo);
|
|
|
|
if (!tmo_value)
|
|
tmo_value = UNF_LOSE_TMO;
|
|
|
|
return (int)tmo_value;
|
|
}
|
|
|
|
int unf_get_link_lose_tmo_from_up(struct unf_lport_s *v_lport,
|
|
struct unf_flash_link_tmo_s *v_link_tmo)
|
|
{
|
|
int ret = UNF_RETURN_ERROR;
|
|
struct unf_flash_data_s flash_data;
|
|
|
|
if (!v_lport || !v_link_tmo || (sizeof(struct unf_flash_data_s)
|
|
> HIFC_FLASH_DATA_MAX_LEN)) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_NORMAL, UNF_KEVENT,
|
|
"[warn]get flas link tmo param check fail");
|
|
return ret;
|
|
}
|
|
memset(&flash_data, 0, sizeof(struct unf_flash_data_s));
|
|
|
|
if (!v_lport->low_level_func.port_mgr_op.pfn_ll_port_config_get) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_NORMAL, UNF_KEVENT,
|
|
"[warn]link tmo fun null");
|
|
return ret;
|
|
}
|
|
if (v_lport->low_level_func.port_mgr_op.pfn_ll_port_config_get(
|
|
v_lport->fc_port,
|
|
UNF_PORT_CFG_GET_FLASH_DATA_INFO, &flash_data) !=
|
|
RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_NORMAL, UNF_KEVENT,
|
|
"[warn]get link tmo from up fail");
|
|
return ret;
|
|
}
|
|
ret = RETURN_OK;
|
|
memcpy(v_link_tmo, &flash_data.link_tmo, HIFC_FLASH_LINK_TMO_MAX_LEN);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void unf_init_link_lose_tmo(struct unf_lport_s *v_lport)
|
|
{
|
|
struct unf_flash_link_tmo_s flash_link_tmo;
|
|
unsigned int tmo;
|
|
|
|
memset(&flash_link_tmo, 0, sizeof(struct unf_flash_link_tmo_s));
|
|
|
|
if (!v_lport) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_EQUIP_ATT, UNF_MAJOR,
|
|
"[warn]int link tmo param check fail");
|
|
return;
|
|
}
|
|
if ((unf_get_link_lose_tmo_from_up(v_lport, &flash_link_tmo) ==
|
|
RETURN_OK) &&
|
|
(flash_link_tmo.writeflag == HIFC_MGMT_TMO_MAGIC_NUM)) {
|
|
tmo = (((unsigned int)flash_link_tmo.link_tmo3 << 24) |
|
|
((unsigned int)flash_link_tmo.link_tmo2 << 16) |
|
|
((unsigned int)flash_link_tmo.link_tmo1 << 8) |
|
|
flash_link_tmo.link_tmo0);
|
|
if (tmo > 600)
|
|
unf_set_link_lose_tmo(v_lport, UNF_LOSE_TMO);
|
|
else
|
|
atomic_set(&v_lport->link_lose_tmo, (int)tmo);
|
|
} else {
|
|
unf_set_link_lose_tmo(v_lport, UNF_LOSE_TMO);
|
|
}
|
|
}
|
|
|
|
unsigned int unf_register_scsi_host(struct unf_lport_s *v_lport)
|
|
{
|
|
struct unf_host_param_s host_param = { 0 };
|
|
unf_scsi_host_s **p_scsi_host = NULL;
|
|
struct unf_lport_cfg_item_s *lport_cfg_items = NULL;
|
|
|
|
UNF_CHECK_VALID(0x1359, TRUE, v_lport, return UNF_RETURN_ERROR);
|
|
|
|
/* Point to -->> L_port->Scsi_host */
|
|
p_scsi_host = &v_lport->host_info.p_scsi_host;
|
|
|
|
lport_cfg_items = &v_lport->low_level_func.lport_cfg_items;
|
|
host_param.can_queue = (int)lport_cfg_items->max_queue_depth;
|
|
|
|
/* Performance optimization */
|
|
host_param.cmnd_per_lun = UNF_MAX_CMND_PER_LUN;
|
|
|
|
host_param.sg_table_size = UNF_MAX_DMA_SEGS;
|
|
host_param.max_id = UNF_MAX_TARGET_NUMBER;
|
|
host_param.max_lun = UNF_DEFAULT_MAX_LUN;
|
|
host_param.max_channel = UNF_MAX_BUS_CHANNEL;
|
|
host_param.max_cmnd_len = UNF_MAX_SCSI_CMND_LEN; /* CDB-16 */
|
|
host_param.dma_boundary = UNF_DMA_BOUNDARY;
|
|
host_param.max_sectors = UNF_MAX_SECTORS;
|
|
host_param.port_id = v_lport->port_id;
|
|
host_param.lport = v_lport;
|
|
host_param.pdev = &v_lport->low_level_func.dev->dev;
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_INFO,
|
|
"[info]Port(0x%x) allocate scsi host: can queue(%u), command performance LUN(%u), max lun(%u)",
|
|
v_lport->port_id, host_param.can_queue,
|
|
host_param.cmnd_per_lun, host_param.max_lun);
|
|
|
|
if (unf_alloc_scsi_host(p_scsi_host, &host_param) != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) allocate scsi host failed",
|
|
v_lport->port_id);
|
|
|
|
return UNF_RETURN_ERROR;
|
|
}
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_KEVENT,
|
|
"[event]Port(0x%x) allocate scsi host(0x%x) succeed",
|
|
v_lport->port_id, UNF_GET_SCSI_HOST_ID(*p_scsi_host));
|
|
|
|
return RETURN_OK;
|
|
}
|
|
|
|
void unf_unregister_scsi_host(struct unf_lport_s *v_lport)
|
|
{
|
|
unf_scsi_host_s *p_scsi_host = NULL;
|
|
unsigned int host_no = 0;
|
|
|
|
UNF_REFERNCE_VAR(p_scsi_host);
|
|
UNF_CHECK_VALID(0x1360, TRUE, v_lport, return);
|
|
|
|
p_scsi_host = v_lport->host_info.p_scsi_host;
|
|
|
|
if (p_scsi_host) {
|
|
host_no = UNF_GET_SCSI_HOST_ID(p_scsi_host);
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"[event]Port(0x%x) starting unregister scsi host(0x%x)",
|
|
v_lport->port_id, host_no);
|
|
|
|
unf_free_scsi_host(p_scsi_host);
|
|
/* can`t set p_scsi_host for NULL,
|
|
* since it does`t alloc by itself
|
|
*/
|
|
} else {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_KEVENT,
|
|
"[warn]Port(0x%x) unregister scsi host, invalid ScsiHost ",
|
|
v_lport->port_id);
|
|
}
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"[event]Port(0x%x) unregister scsi host(0x%x) succeed",
|
|
v_lport->port_id, host_no);
|
|
|
|
v_lport->destroy_step = UNF_LPORT_DESTROY_STEP_12_UNREG_SCSI_HOST;
|
|
|
|
UNF_REFERNCE_VAR(p_scsi_host);
|
|
UNF_REFERNCE_VAR(host_no);
|
|
}
|
|
|
|
unsigned int unf_cm_clear_flush(unsigned int v_port_id)
|
|
{
|
|
unsigned int port_id = v_port_id;
|
|
struct unf_lport_s *lport = NULL;
|
|
struct unf_save_info_head_s *save_info = NULL;
|
|
unsigned int ret = UNF_RETURN_ERROR;
|
|
|
|
lport = unf_find_lport_by_port_id(port_id);
|
|
if (!lport) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_EQUIP_ATT, UNF_ERR,
|
|
"[err]Port(0x%x) can not be found", port_id);
|
|
|
|
return ret;
|
|
}
|
|
|
|
save_info = vmalloc(SAVE_PORT_INFO_LEN);
|
|
if (!save_info) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_NORMAL, UNF_ERR,
|
|
"[err]Can't alloc buffer for saving port info");
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* 1 clean flush */
|
|
memset(save_info, 0, SAVE_PORT_INFO_LEN);
|
|
|
|
save_info->opcode = 2; /* notify up to clean flush */
|
|
save_info->type = 0xf;
|
|
save_info->entry_num = 0;
|
|
save_info->next = 0xffff;
|
|
|
|
ret = unf_save_port_info(lport, save_info);
|
|
if (ret != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_EQUIP_ATT, UNF_MAJOR,
|
|
"[warn]Port(0x%x) send mailbox fail", lport->port_id);
|
|
|
|
vfree(save_info);
|
|
|
|
return ret;
|
|
}
|
|
|
|
vfree(save_info);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int unf_cm_save_data_mode(struct unf_lport_s *v_lport,
|
|
struct unf_hinicam_pkg *v_input)
|
|
{
|
|
int ret = UNF_RETURN_ERROR;
|
|
unsigned int save_data_mode = 0;
|
|
unsigned int port_id = 0;
|
|
void *out_buf = NULL;
|
|
struct unf_adm_cmd *buff_in = NULL;
|
|
struct unf_admin_msg_head msg_head = { 0 };
|
|
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_input,
|
|
return UNF_RETURN_ERROR);
|
|
out_buf = v_input->buff_out;
|
|
buff_in = v_input->buff_in;
|
|
port_id = v_lport->port_id;
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, out_buf,
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, buff_in,
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE,
|
|
v_input->in_size >= sizeof(struct unf_adm_cmd),
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE,
|
|
*v_input->out_size >= sizeof(struct unf_adm_cmd),
|
|
return UNF_RETURN_ERROR);
|
|
|
|
save_data_mode = buff_in->arg[0];
|
|
|
|
msg_head.status = UNF_ADMIN_MSG_DONE;
|
|
|
|
if (save_data_mode == UNF_SAVA_INFO_MODE) {
|
|
ret = (int)unf_cm_save_port_info(port_id);
|
|
if (ret != RETURN_OK)
|
|
msg_head.status = UNF_ADMIN_MSG_FAILED;
|
|
} else if (save_data_mode == UNF_CLEAN_INFO_MODE) {
|
|
ret = (int)unf_cm_clear_flush(port_id);
|
|
if (ret != RETURN_OK)
|
|
msg_head.status = UNF_ADMIN_MSG_FAILED;
|
|
} else {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_MAJOR,
|
|
"[err]This mode(0x%x) is unknown", save_data_mode);
|
|
msg_head.status = UNF_ADMIN_MSG_FAILED;
|
|
}
|
|
|
|
msg_head.size = sizeof(struct unf_admin_msg_head);
|
|
*v_input->out_size = sizeof(struct unf_adm_cmd);
|
|
memcpy(out_buf, &msg_head, sizeof(struct unf_admin_msg_head));
|
|
|
|
return ret;
|
|
}
|
|
|
|
int unf_cmd_adm_handler(void *v_lport, struct unf_hinicam_pkg *v_input)
|
|
{
|
|
struct unf_lport_s *lport = NULL;
|
|
int ret = UNF_RETURN_ERROR;
|
|
enum unf_msg_format_e msg_formate;
|
|
unsigned int index = 0;
|
|
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_lport,
|
|
return UNF_RETURN_ERROR);
|
|
UNF_CHECK_VALID(INVALID_VALUE32, UNF_TRUE, v_input,
|
|
return UNF_RETURN_ERROR);
|
|
lport = (struct unf_lport_s *)v_lport;
|
|
msg_formate = v_input->msg_format;
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_INFO,
|
|
"[info]Enter HIFC_Adm, msg_formate=0x%x, 0x%x",
|
|
msg_formate, *v_input->out_size);
|
|
|
|
/* hifcadm event */
|
|
while (index < (sizeof(unf_hifcadm_action) /
|
|
sizeof(struct unf_hifcadm_action_s))) {
|
|
if ((msg_formate == unf_hifcadm_action[index].hifc_action) &&
|
|
unf_hifcadm_action[index].fn_unf_hifc_action) {
|
|
ret = unf_hifcadm_action[index].fn_unf_hifc_action(lport, v_input);
|
|
if (ret != RETURN_OK) {
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_EVENT,
|
|
UNF_WARN,
|
|
"[warn]Port(0x%x) process up msg(0x%x) failed",
|
|
lport->port_id, msg_formate);
|
|
}
|
|
return ret;
|
|
}
|
|
index++;
|
|
}
|
|
|
|
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_EVENT, UNF_KEVENT,
|
|
"[event]Port(0x%x) not support adm cmd, msg type(0x%x) ",
|
|
lport->port_id, msg_formate);
|
|
|
|
return ret;
|
|
}
|