1102 lines · 26.1 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * Copyright (C) 2021 Broadcom. All Rights Reserved. The term * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. */ #include "efc.h" intefc_remote_node_cb(void *arg, int event, void *data){	struct efc *efc = arg;	struct efc_remote_node *rnode = data;	struct efc_node *node = rnode->node;	unsigned long flags = 0; 	spin_lock_irqsave(&efc->lock, flags);	efc_node_post_event(node, event, NULL);	spin_unlock_irqrestore(&efc->lock, flags); 	return 0;} struct efc_node *efc_node_find(struct efc_nport *nport, u32 port_id){	/* Find an FC node structure given the FC port ID */	return xa_load(&nport->lookup, port_id);} static void_efc_node_free(struct kref *arg){	struct efc_node *node = container_of(arg, struct efc_node, ref);	struct efc *efc = node->efc;	struct efc_dma *dma; 	dma = &node->sparm_dma_buf;	dma_pool_free(efc->node_dma_pool, dma->virt, dma->phys);	memset(dma, 0, sizeof(struct efc_dma));	mempool_free(node, efc->node_pool);} struct efc_node *efc_node_alloc(struct efc_nport *nport,				u32 port_id, bool init, bool targ){	int rc;	struct efc_node *node = NULL;	struct efc *efc = nport->efc;	struct efc_dma *dma; 	if (nport->shutting_down) {		efc_log_debug(efc, "node allocation when shutting down %06x",			      port_id);		return NULL;	} 	node = mempool_alloc(efc->node_pool, GFP_ATOMIC);	if (!node) {		efc_log_err(efc, "node allocation failed %06x", port_id);		return NULL;	}	memset(node, 0, sizeof(*node)); 	dma = &node->sparm_dma_buf;	dma->size = NODE_SPARAMS_SIZE;	dma->virt = dma_pool_zalloc(efc->node_dma_pool, GFP_ATOMIC, &dma->phys);	if (!dma->virt) {		efc_log_err(efc, "node dma alloc failed\n");		goto dma_fail;	}	node->rnode.indicator = U32_MAX;	node->nport = nport; 	node->efc = efc;	node->init = init;	node->targ = targ; 	spin_lock_init(&node->pend_frames_lock);	INIT_LIST_HEAD(&node->pend_frames);	spin_lock_init(&node->els_ios_lock);	INIT_LIST_HEAD(&node->els_ios_list);	node->els_io_enabled = true; 	rc = efc_cmd_node_alloc(efc, &node->rnode, port_id, nport);	if (rc) {		efc_log_err(efc, "efc_hw_node_alloc failed: %d\n", rc);		goto hw_alloc_fail;	} 	node->rnode.node = node;	node->sm.app = node;	node->evtdepth = 0; 	efc_node_update_display_name(node); 	rc = xa_err(xa_store(&nport->lookup, port_id, node, GFP_ATOMIC));	if (rc) {		efc_log_err(efc, "Node lookup store failed: %d\n", rc);		goto xa_fail;	} 	/* initialize refcount */	kref_init(&node->ref);	node->release = _efc_node_free;	kref_get(&nport->ref); 	return node; xa_fail:	efc_node_free_resources(efc, &node->rnode);hw_alloc_fail:	dma_pool_free(efc->node_dma_pool, dma->virt, dma->phys);dma_fail:	mempool_free(node, efc->node_pool);	return NULL;} voidefc_node_free(struct efc_node *node){	struct efc_nport *nport;	struct efc *efc;	int rc = 0;	struct efc_node *ns = NULL; 	nport = node->nport;	efc = node->efc; 	node_printf(node, "Free'd\n"); 	if (node->refound) {		/*		 * Save the name server node. We will send fake RSCN event at		 * the end to handle ignored RSCN event during node deletion		 */		ns = efc_node_find(node->nport, FC_FID_DIR_SERV);	} 	if (!node->nport) {		efc_log_err(efc, "Node already Freed\n");		return;	} 	/* Free HW resources */	rc = efc_node_free_resources(efc, &node->rnode);	if (rc < 0)		efc_log_err(efc, "efc_hw_node_free failed: %d\n", rc); 	/* if the gidpt_delay_timer is still running, then delete it */	if (timer_pending(&node->gidpt_delay_timer))		timer_delete(&node->gidpt_delay_timer); 	xa_erase(&nport->lookup, node->rnode.fc_id); 	/*	 * If the node_list is empty,	 * then post a ALL_CHILD_NODES_FREE event to the nport,	 * after the lock is released.	 * The nport may be free'd as a result of the event.	 */	if (xa_empty(&nport->lookup))		efc_sm_post_event(&nport->sm, EFC_EVT_ALL_CHILD_NODES_FREE,				  NULL); 	node->nport = NULL;	node->sm.current_state = NULL; 	kref_put(&nport->ref, nport->release);	kref_put(&node->ref, node->release); 	if (ns) {		/* sending fake RSCN event to name server node */		efc_node_post_event(ns, EFC_EVT_RSCN_RCVD, NULL);	}} static voidefc_dma_copy_in(struct efc_dma *dma, void *buffer, u32 buffer_length){	if (!dma || !buffer || !buffer_length)		return; 	if (buffer_length > dma->size)		buffer_length = dma->size; 	memcpy(dma->virt, buffer, buffer_length);	dma->len = buffer_length;} intefc_node_attach(struct efc_node *node){	int rc = 0;	struct efc_nport *nport = node->nport;	struct efc_domain *domain = nport->domain;	struct efc *efc = node->efc; 	if (!domain->attached) {		efc_log_err(efc, "Warning: unattached domain\n");		return -EIO;	}	/* Update node->wwpn/wwnn */ 	efc_node_build_eui_name(node->wwpn, sizeof(node->wwpn),				efc_node_get_wwpn(node));	efc_node_build_eui_name(node->wwnn, sizeof(node->wwnn),				efc_node_get_wwnn(node)); 	efc_dma_copy_in(&node->sparm_dma_buf, node->service_params + 4,			sizeof(node->service_params) - 4); 	/* take lock to protect node->rnode.attached */	rc = efc_cmd_node_attach(efc, &node->rnode, &node->sparm_dma_buf);	if (rc < 0)		efc_log_debug(efc, "efc_hw_node_attach failed: %d\n", rc); 	return rc;} voidefc_node_fcid_display(u32 fc_id, char *buffer, u32 buffer_length){	switch (fc_id) {	case FC_FID_FLOGI:		snprintf(buffer, buffer_length, "fabric");		break;	case FC_FID_FCTRL:		snprintf(buffer, buffer_length, "fabctl");		break;	case FC_FID_DIR_SERV:		snprintf(buffer, buffer_length, "nserve");		break;	default:		if (fc_id == FC_FID_DOM_MGR) {			snprintf(buffer, buffer_length, "dctl%02x",				 (fc_id & 0x0000ff));		} else {			snprintf(buffer, buffer_length, "%06x", fc_id);		}		break;	}} voidefc_node_update_display_name(struct efc_node *node){	u32 port_id = node->rnode.fc_id;	struct efc_nport *nport = node->nport;	char portid_display[16]; 	efc_node_fcid_display(port_id, portid_display, sizeof(portid_display)); 	snprintf(node->display_name, sizeof(node->display_name), "%s.%s",		 nport->display_name, portid_display);} voidefc_node_send_ls_io_cleanup(struct efc_node *node){	if (node->send_ls_acc != EFC_NODE_SEND_LS_ACC_NONE) {		efc_log_debug(node->efc, "[%s] cleaning up LS_ACC oxid=0x%x\n",			      node->display_name, node->ls_acc_oxid); 		node->send_ls_acc = EFC_NODE_SEND_LS_ACC_NONE;		node->ls_acc_io = NULL;	}} static void efc_node_handle_implicit_logo(struct efc_node *node){	int rc; 	/*	 * currently, only case for implicit logo is PLOGI	 * recvd. Thus, node's ELS IO pending list won't be	 * empty (PLOGI will be on it)	 */	WARN_ON(node->send_ls_acc != EFC_NODE_SEND_LS_ACC_PLOGI);	node_printf(node, "Reason: implicit logout, re-authenticate\n"); 	/* Re-attach node with the same HW node resources */	node->req_free = false;	rc = efc_node_attach(node);	efc_node_transition(node, __efc_d_wait_node_attach, NULL);	node->els_io_enabled = true; 	if (rc < 0)		efc_node_post_event(node, EFC_EVT_NODE_ATTACH_FAIL, NULL);} static void efc_node_handle_explicit_logo(struct efc_node *node){	s8 pend_frames_empty;	unsigned long flags = 0; 	/* cleanup any pending LS_ACC ELSs */	efc_node_send_ls_io_cleanup(node); 	spin_lock_irqsave(&node->pend_frames_lock, flags);	pend_frames_empty = list_empty(&node->pend_frames);	spin_unlock_irqrestore(&node->pend_frames_lock, flags); 	/*	 * there are two scenarios where we want to keep	 * this node alive:	 * 1. there are pending frames that need to be	 *    processed or	 * 2. we're an initiator and the remote node is	 *    a target and we need to re-authenticate	 */	node_printf(node, "Shutdown: explicit logo pend=%d ", !pend_frames_empty);	node_printf(node, "nport.ini=%d node.tgt=%d\n",		    node->nport->enable_ini, node->targ);	if (!pend_frames_empty || (node->nport->enable_ini && node->targ)) {		u8 send_plogi = false; 		if (node->nport->enable_ini && node->targ) {			/*			 * we're an initiator and			 * node shutting down is a target;			 * we'll need to re-authenticate in			 * initial state			 */			send_plogi = true;		} 		/*		 * transition to __efc_d_init		 * (will retain HW node resources)		 */		node->els_io_enabled = true;		node->req_free = false; 		/*		 * either pending frames exist or we are re-authenticating		 * with PLOGI (or both); in either case, return to initial		 * state		 */		efc_node_init_device(node, send_plogi);	}	/* else: let node shutdown occur */} static voidefc_node_purge_pending(struct efc_node *node){	struct efc *efc = node->efc;	struct efc_hw_sequence *frame, *next;	unsigned long flags = 0; 	spin_lock_irqsave(&node->pend_frames_lock, flags); 	list_for_each_entry_safe(frame, next, &node->pend_frames, list_entry) {		list_del(&frame->list_entry);		efc->tt.hw_seq_free(efc, frame);	} 	spin_unlock_irqrestore(&node->pend_frames_lock, flags);} void__efc_node_shutdown(struct efc_sm_ctx *ctx,		    enum efc_sm_event evt, void *arg){	struct efc_node *node = ctx->app; 	efc_node_evt_set(ctx, evt, __func__); 	node_sm_trace(); 	switch (evt) {	case EFC_EVT_ENTER: {		efc_node_hold_frames(node);		WARN_ON(!efc_els_io_list_empty(node, &node->els_ios_list));		/* by default, we will be freeing node after we unwind */		node->req_free = true; 		switch (node->shutdown_reason) {		case EFC_NODE_SHUTDOWN_IMPLICIT_LOGO:			/* Node shutdown b/c of PLOGI received when node			 * already logged in. We have PLOGI service			 * parameters, so submit node attach; we won't be			 * freeing this node			 */ 			efc_node_handle_implicit_logo(node);			break; 		case EFC_NODE_SHUTDOWN_EXPLICIT_LOGO:			efc_node_handle_explicit_logo(node);			break; 		case EFC_NODE_SHUTDOWN_DEFAULT:		default: {			/*			 * shutdown due to link down,			 * node going away (xport event) or			 * nport shutdown, purge pending and			 * proceed to cleanup node			 */ 			/* cleanup any pending LS_ACC ELSs */			efc_node_send_ls_io_cleanup(node); 			node_printf(node,				    "Shutdown reason: default, purge pending\n");			efc_node_purge_pending(node);			break;		}		} 		break;	}	case EFC_EVT_EXIT:		efc_node_accept_frames(node);		break; 	default:		__efc_node_common(__func__, ctx, evt, arg);	}} static boolefc_node_check_els_quiesced(struct efc_node *node){	/* check to see if ELS requests, completions are quiesced */	if (node->els_req_cnt == 0 && node->els_cmpl_cnt == 0 &&	    efc_els_io_list_empty(node, &node->els_ios_list)) {		if (!node->attached) {			/* hw node detach already completed, proceed */			node_printf(node, "HW node not attached\n");			efc_node_transition(node,					    __efc_node_wait_ios_shutdown,					     NULL);		} else {			/*			 * hw node detach hasn't completed,			 * transition and wait			 */			node_printf(node, "HW node still attached\n");			efc_node_transition(node, __efc_node_wait_node_free,					    NULL);		}		return true;	}	return false;} voidefc_node_initiate_cleanup(struct efc_node *node){	/*	 * if ELS's have already been quiesced, will move to next state	 * if ELS's have not been quiesced, abort them	 */	if (!efc_node_check_els_quiesced(node)) {		efc_node_hold_frames(node);		efc_node_transition(node, __efc_node_wait_els_shutdown, NULL);	}} void__efc_node_wait_els_shutdown(struct efc_sm_ctx *ctx,			     enum efc_sm_event evt, void *arg){	bool check_quiesce = false;	struct efc_node *node = ctx->app; 	efc_node_evt_set(ctx, evt, __func__); 	node_sm_trace();	/* Node state machine: Wait for all ELSs to complete */	switch (evt) {	case EFC_EVT_ENTER:		efc_node_hold_frames(node);		if (efc_els_io_list_empty(node, &node->els_ios_list)) {			node_printf(node, "All ELS IOs complete\n");			check_quiesce = true;		}		break;	case EFC_EVT_EXIT:		efc_node_accept_frames(node);		break; 	case EFC_EVT_SRRS_ELS_REQ_OK:	case EFC_EVT_SRRS_ELS_REQ_FAIL:	case EFC_EVT_SRRS_ELS_REQ_RJT:	case EFC_EVT_ELS_REQ_ABORTED:		if (WARN_ON(!node->els_req_cnt))			break;		node->els_req_cnt--;		check_quiesce = true;		break; 	case EFC_EVT_SRRS_ELS_CMPL_OK:	case EFC_EVT_SRRS_ELS_CMPL_FAIL:		if (WARN_ON(!node->els_cmpl_cnt))			break;		node->els_cmpl_cnt--;

Lines 1–500 of 1102.