2026-01-21 18:59:54 +08:00

565 lines
15 KiB
C

// SPDX-License-Identifier: GPL-2.0
/* Huawei Fabric Channel Linux driver
* Copyright(c) 2018 Huawei Technologies Co., Ltd
*
*/
#include "unf_log.h"
#include "unf_event.h"
#include "unf_exchg.h"
#include "unf_portman.h"
#include "unf_rport.h"
#include "unf_service.h"
#include "unf_io.h"
#define RPORT_FEATURE_POOL_SIZE 4096
static struct unf_esgl_page_s *unf_cm_get_one_free_esgl_page(
void *v_lport,
struct unf_frame_pkg_s *v_fra_pkg);
static unsigned int unf_recv_tmf_marker_status(
void *v_lport,
struct unf_frame_pkg_s *v_fra_pkg);
static unsigned int unf_recv_abts_mrker_status(
void *v_lport,
struct unf_frame_pkg_s *v_fra_pkg);
static int unf_get_cfg_parms(char *v_section_name,
struct unf_cfg_item_s *v_cfg_parm,
unsigned int *v_cfg_value,
unsigned int v_item_num);
/* global variables */
unsigned int event_thread_exit;
struct task_struct *event_thread;
struct completion *fc_event_handle_thd_comp;
struct workqueue_struct *unf_work_queue;
struct unf_global_card_thread_s card_thread_mgr;
unsigned int unf_dbg_level = UNF_MAJOR;
unsigned int log_print_level = UNF_INFO;
unsigned int log_limted_times = UNF_LOGIN_ATT_PRINT_TIMES;
struct unf_cm_handle_op_s cm_low_levle_handle = {
.pfn_unf_alloc_local_port = unf_lport_create_and_init,
.pfn_unf_release_local_port = unf_release_local_port,
.pfn_unf_receive_els_pkg = unf_receive_els_pkg,
.pfn_unf_receive_gs_pkg = unf_receive_gs_pkg,
.pfn_unf_receive_bls_pkg = unf_receive_bls_pkg,
.pfn_unf_send_els_done = unf_send_els_done,
.pfn_unf_receive_ini_rsponse = unf_ini_scsi_completed,
.pfn_unf_get_cfg_parms = unf_get_cfg_parms,
.pfn_unf_receive_marker_status = unf_recv_tmf_marker_status,
.pfn_unf_receive_abts_marker_status = unf_recv_abts_mrker_status,
.pfn_unf_cm_get_sgl_entry = unf_ini_get_sgl_entry,
.pfn_unf_cm_get_dif_sgl_entry = unf_ini_get_dif_sgl_entry,
.pfn_unf_get_one_free_esgl_page = unf_cm_get_one_free_esgl_page,
.pfn_unf_fc_port_link_event = unf_fc_port_link_event,
.pfn_unf_ioctl_to_com_handler = unf_cmd_adm_handler,
};
static struct unf_esgl_page_s *unf_cm_get_one_free_esgl_page(
void *v_lport,
struct unf_frame_pkg_s *v_fra_pkg)
{
struct unf_lport_s *lport = NULL;
struct unf_xchg_s *xchg = NULL;
UNF_CHECK_VALID(0x1700, 1, v_lport, return NULL);
UNF_CHECK_VALID(0x1701, 1, v_fra_pkg, return NULL);
lport = (struct unf_lport_s *)v_lport;
xchg = (struct unf_xchg_s *)v_fra_pkg->xchg_contex;
return unf_get_one_free_esgl_page(lport, xchg); /* from esgl pool */
}
static int unf_get_cfg_parms(char *v_section_name,
struct unf_cfg_item_s *v_cfg_parm,
unsigned int *v_cfg_value,
unsigned int v_item_num)
{
/* Maximum length of a configuration item value,
* including the end character
*/
#define UNF_MAX_ITEM_VALUE_LEN (256)
unsigned int *value = NULL;
struct unf_cfg_item_s *cfg_parm = NULL;
unsigned int i = 0;
cfg_parm = v_cfg_parm;
value = v_cfg_value;
for (i = 0; i < v_item_num; i++) {
if (!cfg_parm || !value) {
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR,
UNF_LOG_REG_ATT, UNF_ERR,
"[err]Config name or value is NULL");
return UNF_RETURN_ERROR;
}
if (strcmp("End", cfg_parm->name) == 0)
break;
if (strcmp("fw_path", cfg_parm->name) == 0) {
cfg_parm++;
value += UNF_MAX_ITEM_VALUE_LEN / sizeof(unsigned int);
continue;
}
*value = cfg_parm->default_value;
cfg_parm++;
value++;
}
return RETURN_OK;
}
static unsigned int unf_recv_tmf_marker_status(
void *v_lport,
struct unf_frame_pkg_s *v_fra_pkg)
{
struct unf_lport_s *lport = NULL;
struct unf_xchg_s *xchg = NULL;
unsigned short hot_pool_tag = 0;
UNF_CHECK_VALID(0x3543, UNF_TRUE, v_lport, return UNF_RETURN_ERROR);
UNF_CHECK_VALID(0x3544, UNF_TRUE, v_fra_pkg, return UNF_RETURN_ERROR);
lport = (struct unf_lport_s *)v_lport;
/* Find exchange which point to marker sts */
if (!lport->xchg_mgr_temp.pfn_unf_look_up_xchg_by_tag) {
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]Port(0x%x) tag function is NULL",
lport->port_id);
return UNF_RETURN_ERROR;
}
hot_pool_tag = (unsigned short)
(v_fra_pkg->private[PKG_PRIVATE_XCHG_HOT_POOL_INDEX]);
xchg = (struct unf_xchg_s *)
(lport->xchg_mgr_temp.pfn_unf_look_up_xchg_by_tag((void *)lport,
hot_pool_tag));
if (!xchg) {
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN,
"[warn]Port(0x%x_0x%x) find exchange by tag(0x%x) failed",
lport->port_id, lport->nport_id, hot_pool_tag);
return UNF_RETURN_ERROR;
}
/*
* NOTE: set exchange TMF state with MARKER_STS_RECEIVED
*
* About TMF state
* 1. STS received
* 2. Response received
* 3. Do check if necessary
*/
xchg->tmf_state |= MARKER_STS_RECEIVED;
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_IO_ATT, UNF_MAJOR,
"[info]Marker STS: D_ID(0x%x) S_ID(0x%x) OX_ID(0x%x) RX_ID(0x%x), EXCH: D_ID(0x%x) S_ID(0x%x) OX_ID(0x%x) RX_ID(0x%x)",
v_fra_pkg->frame_head.rctl_did & UNF_NPORTID_MASK,
v_fra_pkg->frame_head.csctl_sid & UNF_NPORTID_MASK,
(unsigned short)(v_fra_pkg->frame_head.oxid_rxid >> 16),
(unsigned short)(v_fra_pkg->frame_head.oxid_rxid),
xchg->did,
xchg->sid,
xchg->ox_id,
xchg->rx_id);
return RETURN_OK;
}
static unsigned int unf_recv_abts_mrker_status(
void *v_lport,
struct unf_frame_pkg_s *v_fra_pkg)
{
struct unf_lport_s *lport = NULL;
struct unf_xchg_s *xchg = NULL;
unsigned short hot_pool_tag = 0;
unsigned long flags = 0;
UNF_CHECK_VALID(0x3543, UNF_TRUE, v_lport, return UNF_RETURN_ERROR);
UNF_CHECK_VALID(0x3544, UNF_TRUE, v_fra_pkg, return UNF_RETURN_ERROR);
lport = (struct unf_lport_s *)v_lport;
/* Find exchange by tag */
if (!lport->xchg_mgr_temp.pfn_unf_look_up_xchg_by_tag) {
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_LOGIN_ATT, UNF_ERR,
"[err]Port(0x%x) tag function is NULL",
lport->port_id);
return UNF_RETURN_ERROR;
}
hot_pool_tag = (unsigned short)
(v_fra_pkg->private[PKG_PRIVATE_XCHG_HOT_POOL_INDEX]);
xchg = (struct unf_xchg_s *)
(lport->xchg_mgr_temp.pfn_unf_look_up_xchg_by_tag((void *)lport,
hot_pool_tag));
if (!xchg) {
UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_LOGIN_ATT, UNF_WARN,
"[warn]Port(0x%x_0x%x) find exchange by tag(0x%x) failed",
lport->port_id, lport->nport_id, hot_pool_tag);
return UNF_RETURN_ERROR;
}
/*
* NOTE: set exchange ABTS state with MARKER_STS_RECEIVED
*
* About exchange ABTS state
* 1. STS received
* 2. Response received
* 3. Do check if necessary
*
* About Exchange status get from low level
* 1. Set: when RCVD ABTS Marker
* 2. Set: when RCVD ABTS Req Done
* 3. value: set value with pkg->status
*/
spin_lock_irqsave(&xchg->xchg_state_lock, flags);
xchg->ucode_abts_state = v_fra_pkg->status;
xchg->abts_state |= MARKER_STS_RECEIVED;
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_KEVENT,
"[info]Port(0x%x) wake up SEMA for Abts marker exchange(0x%p) oxid(0x%x 0x%x) status(0x%x)",
lport->port_id, xchg, xchg->ox_id, xchg->hot_pool_tag,
v_fra_pkg->abts_maker_status);
/*
* NOTE: Second time for ABTS marker received, or
* ABTS response have been received, no need to wake up sema
*/
if ((xchg->io_state & INI_IO_STATE_ABORT_TIMEOUT) ||
(xchg->abts_state & ABTS_RESPONSE_RECEIVED)) {
spin_unlock_irqrestore(&xchg->xchg_state_lock, flags);
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT,
UNF_KEVENT,
"[info]Port(0x%x) no need to wake up SEMA for Abts marker ABTS_STATE(0x%x) IO_STATE(0x%x)",
lport->port_id, xchg->abts_state,
xchg->io_state);
return RETURN_OK;
}
if (xchg->io_state & INI_IO_STATE_TMF_ABORT) {
spin_unlock_irqrestore(&xchg->xchg_state_lock, flags);
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT,
UNF_KEVENT,
"[info]Port(0x%x) receive Abts marker, exchange(%p) state(0x%x) free it",
lport->port_id, xchg, xchg->io_state);
unf_cm_free_xchg(lport, xchg);
} else {
spin_unlock_irqrestore(&xchg->xchg_state_lock, flags);
up(&xchg->task_sema);
}
return RETURN_OK;
}
unsigned int unf_get_cm_handle_op(struct unf_cm_handle_op_s *v_cm_handle)
{
UNF_CHECK_VALID(0x1708, UNF_TRUE, v_cm_handle,
return UNF_RETURN_ERROR);
memcpy(v_cm_handle, &cm_low_levle_handle,
sizeof(struct unf_cm_handle_op_s));
return RETURN_OK;
}
static void unf_uninit_cm_low_level_handle(void)
{
memset(&cm_low_levle_handle, 0, sizeof(struct unf_cm_handle_op_s));
}
int unf_event_process(void *v_arg)
{
struct list_head *node = NULL;
struct unf_cm_event_report *event_node = NULL;
unsigned long flags = 0;
UNF_REFERNCE_VAR(v_arg);
set_user_nice(current, 4);
recalc_sigpending();
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_INFO,
"[event]Enter event thread");
complete(fc_event_handle_thd_comp);
do {
spin_lock_irqsave(&fc_event_list.fc_eventlist_lock, flags);
if (list_empty(&fc_event_list.list_head) == UNF_TRUE) {
spin_unlock_irqrestore(&fc_event_list.fc_eventlist_lock,
flags);
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout((long)msecs_to_jiffies(1000));
} else {
node = (&fc_event_list.list_head)->next;
list_del_init(node);
fc_event_list.list_num--;
event_node = list_entry(node,
struct unf_cm_event_report,
list_entry);
spin_unlock_irqrestore(&fc_event_list.fc_eventlist_lock,
flags);
/* Process event node */
unf_handle_event(event_node);
}
} while (!event_thread_exit);
complete_and_exit(fc_event_handle_thd_comp, 0);
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_EVENT, UNF_MAJOR,
"[event]Event thread exit");
return RETURN_OK;
}
static unsigned int unf_creat_event_center(void)
{
struct completion fc_event_completion =
COMPLETION_INITIALIZER(fc_event_completion);
struct completion *p_fc_event_completion = &fc_event_completion;
INIT_LIST_HEAD(&fc_event_list.list_head);
fc_event_list.list_num = 0;
spin_lock_init(&fc_event_list.fc_eventlist_lock);
fc_event_handle_thd_comp = p_fc_event_completion;
event_thread = kthread_run(unf_event_process, NULL, "hifc_event");
if (IS_ERR(event_thread)) {
complete_and_exit(fc_event_handle_thd_comp, 0);
fc_event_handle_thd_comp = NULL;
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
"[err]Create event thread failed(0x%p)",
event_thread);
return UNF_RETURN_ERROR;
}
wait_for_completion(fc_event_handle_thd_comp);
return RETURN_OK;
}
static void unf_cm_event_thread_exit(void)
{
struct completion fc_event_completion =
COMPLETION_INITIALIZER(fc_event_completion);
struct completion *p_fc_event_completion = &fc_event_completion;
fc_event_handle_thd_comp = p_fc_event_completion;
event_thread_exit = 1;
wake_up_process(event_thread);
wait_for_completion(fc_event_handle_thd_comp);
fc_event_handle_thd_comp = NULL;
}
static void unf_cm_cread_card_mgr_list(void)
{
/* So far, do not care */
INIT_LIST_HEAD(&card_thread_mgr.list_card_list_head);
spin_lock_init(&card_thread_mgr.global_card_list_lock);
card_thread_mgr.card_sum = 0;
}
static int unf_port_feature_pool_init(void)
{
unsigned int i = 0;
unsigned int rport_fea_pool_size = 0;
struct unf_rport_feature_recard_s *rport_fea_recard = NULL;
unsigned long flags = 0;
rport_fea_pool_size = sizeof(struct unf_rport_feature_pool_s);
port_fea_pool = vmalloc(rport_fea_pool_size);
if (!port_fea_pool) {
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
"[err]cannot allocate rport feature pool");
return UNF_RETURN_ERROR;
}
memset(port_fea_pool, 0, rport_fea_pool_size);
spin_lock_init(&port_fea_pool->port_fea_pool_lock);
INIT_LIST_HEAD(&port_fea_pool->list_busy_head);
INIT_LIST_HEAD(&port_fea_pool->list_free_head);
port_fea_pool->p_port_feature_pool_addr =
vmalloc((size_t)(RPORT_FEATURE_POOL_SIZE *
sizeof(struct unf_rport_feature_recard_s)));
if (!port_fea_pool->p_port_feature_pool_addr) {
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
"[err]cannot allocate rport feature pool address");
vfree(port_fea_pool);
port_fea_pool = NULL;
return UNF_RETURN_ERROR;
}
memset(port_fea_pool->p_port_feature_pool_addr, 0,
sizeof(struct unf_rport_feature_recard_s) *
RPORT_FEATURE_POOL_SIZE);
rport_fea_recard =
(struct unf_rport_feature_recard_s *)
port_fea_pool->p_port_feature_pool_addr;
spin_lock_irqsave(&port_fea_pool->port_fea_pool_lock, flags);
for (i = 0; i < RPORT_FEATURE_POOL_SIZE; i++) {
list_add_tail(&rport_fea_recard->entry_feature,
&port_fea_pool->list_free_head);
rport_fea_recard++;
}
spin_unlock_irqrestore(&port_fea_pool->port_fea_pool_lock, flags);
return RETURN_OK;
}
void unf_free_port_feature_pool(void)
{
if (port_fea_pool->p_port_feature_pool_addr) {
vfree(port_fea_pool->p_port_feature_pool_addr);
port_fea_pool->p_port_feature_pool_addr = NULL;
}
vfree(port_fea_pool);
port_fea_pool = NULL;
}
int unf_common_init(void)
{
int ret = RETURN_OK;
unf_dbg_level = UNF_MAJOR;
log_print_level = UNF_KEVENT;
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_KEVENT,
"UNF Driver Version:%s.", UNF_FC_VERSION);
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_KEVENT,
"UNF Compile Time: %s", __TIME_STR__);
ret = unf_port_feature_pool_init();
if (ret != RETURN_OK) {
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
"[err]Port Feature Pool init failed");
return ret;
}
/* 1. Init Transport */
ret = (int)unf_register_ini_transport();
if (ret != RETURN_OK) {
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
"[err]INI interface init failed");
unf_free_port_feature_pool();
return ret;
}
/* 2. Init L_Port MG: Y */
unf_port_mgmt_init();
/* 3. Init card MG list: N */
unf_cm_cread_card_mgr_list();
/* 4. Init global event resource: N */
ret = (int)unf_init_global_event_msg();
if (ret != RETURN_OK) {
unf_unregister_ini_transport();
unf_free_port_feature_pool();
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
"[err]Create global event center failed");
return ret;
}
/* 5. Create event center(one thread per pf): Y */
ret = (int)unf_creat_event_center();
if (ret != RETURN_OK) {
unf_destroy_global_event_msg();
unf_unregister_ini_transport();
unf_free_port_feature_pool();
fc_event_handle_thd_comp = NULL;
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
"[err]Create event center (thread) failed");
return ret;
}
/* 6. Create work queue: Y */
unf_work_queue = create_workqueue("unf_wq");
if (!unf_work_queue) {
/* event thread exist */
unf_cm_event_thread_exit();
unf_destroy_global_event_msg();
fc_event_handle_thd_comp = NULL;
unf_unregister_ini_transport();
unf_free_port_feature_pool();
UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR,
"[err]Create work queue failed");
return UNF_RETURN_ERROR;
}
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
"[info]Init common layer succeed");
return ret;
}
static void unf_destroy_dirty_port(void)
{
unsigned int v_ditry_port_num = 0;
unf_show_dirty_port(UNF_FALSE, &v_ditry_port_num);
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR,
"[info]Sys has %d dirty L_Port(s)", v_ditry_port_num);
}
void unf_common_exit(void)
{
unf_free_port_feature_pool();
unf_destroy_dirty_port();
flush_workqueue(unf_work_queue);
destroy_workqueue(unf_work_queue);
unf_work_queue = NULL;
unf_cm_event_thread_exit();
unf_destroy_global_event_msg();
unf_uninit_cm_low_level_handle();
unf_port_mgmt_deinit();
unf_unregister_ini_transport();
UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_KEVENT,
"[info]HIFC module remove succeed");
}