// 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" struct unf_event_list fc_event_list; struct unf_global_event_queue global_event_queue; /* Max global event node */ #define UNF_MAX_GLOBAL_ENENT_NODE 24 unsigned int unf_init_event_msg(struct unf_lport_s *v_lport) { struct unf_event_mgr *event_mgr = NULL; struct unf_cm_event_report *event_node = NULL; unsigned int i; unsigned long flags = 0; UNF_CHECK_VALID(0x770, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); event_mgr = &v_lport->event_mgr; /* Get and Initial Event Node resource */ event_mgr->pmem_add = vmalloc((size_t)event_mgr->free_event_count * sizeof(struct unf_cm_event_report)); if (!event_mgr->pmem_add) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_REG_ATT, UNF_WARN, "[warn]Port(0x%x) allocate event manager failed", v_lport->port_id); return UNF_RETURN_ERROR; } memset(event_mgr->pmem_add, 0, ((size_t)event_mgr->free_event_count * sizeof(struct unf_cm_event_report))); event_node = (struct unf_cm_event_report *)(event_mgr->pmem_add); spin_lock_irqsave(&event_mgr->port_event_lock, flags); for (i = 0; i < event_mgr->free_event_count; i++) { INIT_LIST_HEAD(&event_node->list_entry); list_add_tail(&event_node->list_entry, &event_mgr->list_free_event); event_node++; } spin_unlock_irqrestore(&event_mgr->port_event_lock, flags); return RETURN_OK; } static void unf_del_eventcenter(struct unf_lport_s *v_lport) { struct unf_event_mgr *event_mgr = NULL; UNF_CHECK_VALID(0x771, UNF_TRUE, v_lport, return); event_mgr = &v_lport->event_mgr; event_mgr->pfn_unf_get_free_event = NULL; event_mgr->pfn_unf_release_event = NULL; event_mgr->pfn_unf_post_event = NULL; } void unf_init_event_node(struct unf_cm_event_report *v_event_node) { UNF_CHECK_VALID(0x776, UNF_TRUE, v_event_node, return); v_event_node->event = UNF_EVENT_TYPE_REQUIRE; v_event_node->event_asy_flag = UNF_EVENT_ASYN; v_event_node->delay_times = 0; v_event_node->para_in = NULL; v_event_node->para_out = NULL; v_event_node->result = 0; v_event_node->lport = NULL; v_event_node->pfn_unf_event_task = NULL; v_event_node->pfn_unf_event_recovery_strategy = NULL; v_event_node->pfn_unf_event_alarm_strategy = NULL; } struct unf_cm_event_report *unf_get_free_event_node(void *v_lport) { struct unf_event_mgr *event_mgr = NULL; struct unf_cm_event_report *event_node = NULL; struct list_head *list_node = NULL; struct unf_lport_s *root_lport = NULL; unsigned long flags = 0; UNF_CHECK_VALID(0x777, UNF_TRUE, v_lport, return NULL); root_lport = (struct unf_lport_s *)v_lport; root_lport = root_lport->root_lport; if (unlikely(atomic_read(&root_lport->port_no_operater_flag) == UNF_LPORT_NOP)) return NULL; /* Get EventMgr from Lport */ event_mgr = &root_lport->event_mgr; /* Get free node free pool */ spin_lock_irqsave(&event_mgr->port_event_lock, flags); if (list_empty(&event_mgr->list_free_event)) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_REG_ATT, UNF_WARN, "[warn]Port(0x%x) have no event node anymore", root_lport->port_id); spin_unlock_irqrestore(&event_mgr->port_event_lock, flags); return NULL; } list_node = (&event_mgr->list_free_event)->next; list_del(list_node); event_mgr->free_event_count--; event_node = list_entry(list_node, struct unf_cm_event_report, list_entry); /* Initial event node */ unf_init_event_node(event_node); spin_unlock_irqrestore(&event_mgr->port_event_lock, flags); return event_node; } void unf_check_event_mgr_status(struct unf_event_mgr *v_event_mgr) { unsigned long flag = 0; UNF_CHECK_VALID(0x773, UNF_TRUE, v_event_mgr, return); spin_lock_irqsave(&v_event_mgr->port_event_lock, flag); if ((v_event_mgr->emg_completion) && (v_event_mgr->free_event_count == UNF_MAX_EVENT_NODE)) { complete(v_event_mgr->emg_completion); } spin_unlock_irqrestore(&v_event_mgr->port_event_lock, flag); } void unf_release_event(void *v_lport, void *v_event_node) { struct unf_event_mgr *event_mgr = NULL; struct unf_lport_s *root_lport = NULL; struct unf_cm_event_report *event_node = NULL; unsigned long flags = 0; UNF_CHECK_VALID(0x778, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x779, UNF_TRUE, v_event_node, return); event_node = (struct unf_cm_event_report *)v_event_node; root_lport = (struct unf_lport_s *)v_lport; root_lport = root_lport->root_lport; event_mgr = &root_lport->event_mgr; spin_lock_irqsave(&event_mgr->port_event_lock, flags); event_mgr->free_event_count++; unf_init_event_node(event_node); list_add_tail(&event_node->list_entry, &event_mgr->list_free_event); spin_unlock_irqrestore(&event_mgr->port_event_lock, flags); unf_check_event_mgr_status(event_mgr); } void unf_post_event(void *v_lport, void *v_event_node) { struct unf_cm_event_report *event_node = NULL; struct unf_chip_manage_info_s *card_thread_info = NULL; struct unf_lport_s *root_lport = NULL; unsigned long flags = 0; UNF_CHECK_VALID(0x780, UNF_TRUE, v_event_node, return); event_node = (struct unf_cm_event_report *)v_event_node; UNF_REFERNCE_VAR(v_lport); /* If null, post to global event center */ if (!v_lport) { spin_lock_irqsave(&fc_event_list.fc_eventlist_lock, flags); fc_event_list.list_num++; list_add_tail(&event_node->list_entry, &fc_event_list.list_head); spin_unlock_irqrestore(&fc_event_list.fc_eventlist_lock, flags); wake_up_process(event_thread); } else { root_lport = (struct unf_lport_s *)v_lport; root_lport = root_lport->root_lport; card_thread_info = root_lport->chip_info; /* Post to global event center */ if (!card_thread_info) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_EVENT, UNF_WARN, "[warn]Port(0x%x) has strange event with type(0x%x)", root_lport->nport_id, event_node->event); spin_lock_irqsave(&fc_event_list.fc_eventlist_lock, flags); fc_event_list.list_num++; list_add_tail(&event_node->list_entry, &fc_event_list.list_head); spin_unlock_irqrestore( &fc_event_list.fc_eventlist_lock, flags); wake_up_process(event_thread); } else { spin_lock_irqsave( &card_thread_info->chip_event_list_lock, flags); card_thread_info->list_num++; list_add_tail(&event_node->list_entry, &card_thread_info->list_head); spin_unlock_irqrestore( &card_thread_info->chip_event_list_lock, flags); wake_up_process(card_thread_info->data_thread); } } } unsigned int unf_init_event_center(void *v_lport) { struct unf_event_mgr *event_mgr = NULL; unsigned int ret = RETURN_OK; struct unf_lport_s *lport = NULL; UNF_CHECK_VALID(0x772, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); lport = (struct unf_lport_s *)v_lport; /* Initial Disc manager */ event_mgr = &lport->event_mgr; event_mgr->free_event_count = UNF_MAX_EVENT_NODE; event_mgr->pfn_unf_get_free_event = unf_get_free_event_node; event_mgr->pfn_unf_release_event = unf_release_event; event_mgr->pfn_unf_post_event = unf_post_event; INIT_LIST_HEAD(&event_mgr->list_free_event); spin_lock_init(&event_mgr->port_event_lock); event_mgr->emg_completion = NULL; ret = unf_init_event_msg(lport); return ret; } void unf_wait_event_mgr_complete(struct unf_event_mgr *v_event_mgr) { struct unf_event_mgr *event_mgr = NULL; int wait = UNF_FALSE; unsigned long mg_flag = 0; struct completion fc_event_completion = COMPLETION_INITIALIZER(fc_event_completion); UNF_CHECK_VALID(0x774, UNF_TRUE, v_event_mgr, return); event_mgr = v_event_mgr; spin_lock_irqsave(&event_mgr->port_event_lock, mg_flag); if (event_mgr->free_event_count != UNF_MAX_EVENT_NODE) { event_mgr->emg_completion = &fc_event_completion; wait = UNF_TRUE; } spin_unlock_irqrestore(&event_mgr->port_event_lock, mg_flag); if (wait == UNF_TRUE) wait_for_completion(event_mgr->emg_completion); spin_lock_irqsave(&event_mgr->port_event_lock, mg_flag); event_mgr->emg_completion = NULL; spin_unlock_irqrestore(&event_mgr->port_event_lock, mg_flag); } unsigned int unf_event_center_destroy(void *v_lport) { struct unf_event_mgr *event_mgr = NULL; struct list_head *list = NULL; struct list_head *list_tmp = NULL; struct unf_cm_event_report *event_node = NULL; unsigned int ret = RETURN_OK; unsigned long flag = 0; unsigned long list_lock_flag = 0; struct unf_lport_s *lport = NULL; UNF_CHECK_VALID(0x775, UNF_TRUE, v_lport, return UNF_RETURN_ERROR); lport = (struct unf_lport_s *)v_lport; event_mgr = &lport->event_mgr; spin_lock_irqsave(&fc_event_list.fc_eventlist_lock, list_lock_flag); if (!list_empty(&fc_event_list.list_head)) { list_for_each_safe(list, list_tmp, &fc_event_list.list_head) { event_node = list_entry(list, struct unf_cm_event_report, list_entry); if (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, 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, flag); } } } spin_unlock_irqrestore(&fc_event_list.fc_eventlist_lock, list_lock_flag); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR, "[info]Port(0x%x) begin to wait event", lport->port_id); unf_wait_event_mgr_complete(event_mgr); UNF_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_REG_ATT, UNF_MAJOR, "[info]Port(0x%x) wait event process end", lport->port_id); unf_del_eventcenter(lport); vfree(event_mgr->pmem_add); event_mgr->pmem_add = NULL; lport->destroy_step = UNF_LPORT_DESTROY_STEP_3_DESTROY_EVENT_CENTER; return ret; } static void unf_procee_asyn_event(struct unf_cm_event_report *v_event_node) { unsigned int ret = UNF_RETURN_ERROR; struct unf_lport_s *lport = (struct unf_lport_s *)v_event_node->lport; UNF_CHECK_VALID(0x782, UNF_TRUE, lport, return); if (v_event_node->pfn_unf_event_task) ret = (unsigned int) v_event_node->pfn_unf_event_task(v_event_node->para_in, v_event_node->para_out); if (lport->event_mgr.pfn_unf_release_event) lport->event_mgr.pfn_unf_release_event(lport, v_event_node); if (ret != RETURN_OK) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_EVENT, UNF_WARN, "[warn]Port(0x%x) handle event(0x%x) failed", lport->port_id, v_event_node->event); } UNF_REFERNCE_VAR(ret); } void unf_release_global_event(void *v_event_node) { unsigned long flag = 0; struct unf_cm_event_report *event_node = NULL; UNF_CHECK_VALID(0x784, UNF_TRUE, v_event_node, return); event_node = (struct unf_cm_event_report *)v_event_node; unf_init_event_node(event_node); spin_lock_irqsave(&global_event_queue.global_eventlist_lock, flag); global_event_queue.list_number++; list_add_tail(&event_node->list_entry, &global_event_queue.global_eventlist); spin_unlock_irqrestore(&global_event_queue.global_eventlist_lock, flag); } void unf_handle_event(struct unf_cm_event_report *v_event_node) { unsigned int ret = UNF_RETURN_ERROR; unsigned int event = 0; unsigned int event_asy_flag = UNF_EVENT_ASYN; UNF_CHECK_VALID(0x781, UNF_TRUE, v_event_node, return); UNF_REFERNCE_VAR(ret); UNF_REFERNCE_VAR(event); event = v_event_node->event; event_asy_flag = v_event_node->event_asy_flag; switch (event_asy_flag) { case UNF_EVENT_SYN: /* synchronous event node */ case UNF_GLOBAL_EVENT_SYN: if (v_event_node->pfn_unf_event_task) { ret = (unsigned int)v_event_node->pfn_unf_event_task( v_event_node->para_in, v_event_node->para_out); } v_event_node->result = ret; complete(&v_event_node->event_comp); break; case UNF_EVENT_ASYN: /* asynchronous event node */ unf_procee_asyn_event(v_event_node); break; case UNF_GLOBAL_EVENT_ASYN: if (v_event_node->pfn_unf_event_task) { ret = (unsigned int)v_event_node->pfn_unf_event_task( v_event_node->para_in, v_event_node->para_out); } unf_release_global_event(v_event_node); if (ret != RETURN_OK) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_EVENT, UNF_WARN, "[warn]handle global event(0x%x) failed", event); } break; default: UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_EVENT, UNF_WARN, "[warn]Unknown event(0x%x)", event); break; } } unsigned int unf_init_global_event_msg(void) { struct unf_cm_event_report *event_node = NULL; unsigned int ret = RETURN_OK; unsigned int i = 0; unsigned long flag = 0; INIT_LIST_HEAD(&global_event_queue.global_eventlist); spin_lock_init(&global_event_queue.global_eventlist_lock); global_event_queue.list_number = 0; global_event_queue.global_event_add = vmalloc(UNF_MAX_GLOBAL_ENENT_NODE * sizeof(struct unf_cm_event_report)); if (!global_event_queue.global_event_add) { UNF_TRACE(UNF_EVTLOG_DRIVER_ERR, UNF_LOG_REG_ATT, UNF_ERR, "[err]Can't allocate global event queue"); return UNF_RETURN_ERROR; } memset(global_event_queue.global_event_add, 0, (sizeof(struct unf_cm_event_report) * UNF_MAX_GLOBAL_ENENT_NODE)); event_node = (struct unf_cm_event_report *) (global_event_queue.global_event_add); spin_lock_irqsave(&global_event_queue.global_eventlist_lock, flag); for (i = 0; i < UNF_MAX_GLOBAL_ENENT_NODE; i++) { INIT_LIST_HEAD(&event_node->list_entry); list_add_tail(&event_node->list_entry, &global_event_queue.global_eventlist); global_event_queue.list_number++; event_node++; } spin_unlock_irqrestore(&global_event_queue.global_eventlist_lock, flag); return ret; } void unf_destroy_global_event_msg(void) { if (global_event_queue.list_number != UNF_MAX_GLOBAL_ENENT_NODE) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_EVENT, UNF_CRITICAL, "[warn]Global event release not complete with remain nodes(0x%x)", global_event_queue.list_number); } vfree(global_event_queue.global_event_add); } unsigned int unf_schedule_global_event( void *v_para, unsigned int v_event_asy_flag, int (*pfn_unf_event_task)(void *v_argin, void *v_argout)) { struct list_head *list_node = NULL; struct unf_cm_event_report *event_node = NULL; unsigned long flag = 0; unsigned int ret = UNF_RETURN_ERROR; UNF_CHECK_VALID(0x783, UNF_TRUE, pfn_unf_event_task, return UNF_RETURN_ERROR); if ((v_event_asy_flag != UNF_GLOBAL_EVENT_ASYN) && (v_event_asy_flag != UNF_GLOBAL_EVENT_SYN)) { UNF_TRACE(UNF_EVTLOG_DRIVER_WARN, UNF_LOG_REG_ATT, UNF_WARN, "[warn]Event async flag(0x%x) abnormity", v_event_asy_flag); return UNF_RETURN_ERROR; } spin_lock_irqsave(&global_event_queue.global_eventlist_lock, flag); if (list_empty(&global_event_queue.global_eventlist)) { spin_unlock_irqrestore( &global_event_queue.global_eventlist_lock, flag); return UNF_RETURN_ERROR; } list_node = (&global_event_queue.global_eventlist)->next; list_del_init(list_node); global_event_queue.list_number--; event_node = list_entry(list_node, struct unf_cm_event_report, list_entry); spin_unlock_irqrestore(&global_event_queue.global_eventlist_lock, flag); /* Initial global event */ unf_init_event_node(event_node); init_completion(&event_node->event_comp); event_node->event_asy_flag = v_event_asy_flag; event_node->pfn_unf_event_task = pfn_unf_event_task; event_node->para_in = (void *)v_para; event_node->para_out = NULL; unf_post_event(NULL, event_node); if (v_event_asy_flag == UNF_GLOBAL_EVENT_SYN) { /* must wait for complete */ wait_for_completion(&event_node->event_comp); ret = event_node->result; unf_release_global_event(event_node); } else { ret = RETURN_OK; } return ret; } struct unf_cm_event_report *unf_get_one_event_node(void *v_lport) { struct unf_lport_s *lport = (struct unf_lport_s *)v_lport; UNF_CHECK_VALID(0x785, UNF_TRUE, v_lport, return NULL); UNF_CHECK_VALID(0x786, UNF_TRUE, lport->event_mgr.pfn_unf_get_free_event, return NULL); return lport->event_mgr.pfn_unf_get_free_event((void *)lport); } void unf_post_one_event_node(void *v_lport, struct unf_cm_event_report *v_event) { struct unf_lport_s *lport = (struct unf_lport_s *)v_lport; UNF_CHECK_VALID(0x787, UNF_TRUE, v_lport, return); UNF_CHECK_VALID(0x788, UNF_TRUE, v_event, return); UNF_CHECK_VALID(0x789, UNF_TRUE, lport->event_mgr.pfn_unf_post_event, return); UNF_CHECK_VALID(0x790, UNF_TRUE, v_event, return); lport->event_mgr.pfn_unf_post_event((void *)lport, v_event); }