778 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2021 Broadcom. All Rights Reserved. The term4 * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.5 */6 7/*8 * NPORT9 *10 * Port object for physical port and NPIV ports.11 */12 13/*14 * NPORT REFERENCE COUNTING15 *16 * A nport reference should be taken when:17 * - an nport is allocated18 * - a vport populates associated nport19 * - a remote node is allocated20 * - a unsolicited frame is processed21 * The reference should be dropped when:22 * - the unsolicited frame processesing is done23 * - the remote node is removed24 * - the vport is removed25 * - the nport is removed26 */27 28#include "efc.h"29 30void31efc_nport_cb(void *arg, int event, void *data)32{33 struct efc *efc = arg;34 struct efc_nport *nport = data;35 unsigned long flags = 0;36 37 efc_log_debug(efc, "nport event: %s\n", efc_sm_event_name(event));38 39 spin_lock_irqsave(&efc->lock, flags);40 efc_sm_post_event(&nport->sm, event, NULL);41 spin_unlock_irqrestore(&efc->lock, flags);42}43 44static struct efc_nport *45efc_nport_find_wwn(struct efc_domain *domain, uint64_t wwnn, uint64_t wwpn)46{47 struct efc_nport *nport = NULL;48 49 /* Find a nport, given the WWNN and WWPN */50 list_for_each_entry(nport, &domain->nport_list, list_entry) {51 if (nport->wwnn == wwnn && nport->wwpn == wwpn)52 return nport;53 }54 return NULL;55}56 57static void58_efc_nport_free(struct kref *arg)59{60 struct efc_nport *nport = container_of(arg, struct efc_nport, ref);61 62 kfree(nport);63}64 65struct efc_nport *66efc_nport_alloc(struct efc_domain *domain, uint64_t wwpn, uint64_t wwnn,67 u32 fc_id, bool enable_ini, bool enable_tgt)68{69 struct efc_nport *nport;70 71 if (domain->efc->enable_ini)72 enable_ini = 0;73 74 /* Return a failure if this nport has already been allocated */75 if ((wwpn != 0) || (wwnn != 0)) {76 nport = efc_nport_find_wwn(domain, wwnn, wwpn);77 if (nport) {78 efc_log_err(domain->efc,79 "NPORT %016llX %016llX already allocated\n",80 wwnn, wwpn);81 return NULL;82 }83 }84 85 nport = kzalloc(sizeof(*nport), GFP_ATOMIC);86 if (!nport)87 return nport;88 89 /* initialize refcount */90 kref_init(&nport->ref);91 nport->release = _efc_nport_free;92 93 nport->efc = domain->efc;94 snprintf(nport->display_name, sizeof(nport->display_name), "------");95 nport->domain = domain;96 xa_init(&nport->lookup);97 nport->instance_index = domain->nport_count++;98 nport->sm.app = nport;99 nport->enable_ini = enable_ini;100 nport->enable_tgt = enable_tgt;101 nport->enable_rscn = (nport->enable_ini ||102 (nport->enable_tgt && enable_target_rscn(nport->efc)));103 104 /* Copy service parameters from domain */105 memcpy(nport->service_params, domain->service_params,106 sizeof(struct fc_els_flogi));107 108 /* Update requested fc_id */109 nport->fc_id = fc_id;110 111 /* Update the nport's service parameters for the new wwn's */112 nport->wwpn = wwpn;113 nport->wwnn = wwnn;114 snprintf(nport->wwnn_str, sizeof(nport->wwnn_str), "%016llX",115 (unsigned long long)wwnn);116 117 /*118 * if this is the "first" nport of the domain,119 * then make it the "phys" nport120 */121 if (list_empty(&domain->nport_list))122 domain->nport = nport;123 124 INIT_LIST_HEAD(&nport->list_entry);125 list_add_tail(&nport->list_entry, &domain->nport_list);126 127 kref_get(&domain->ref);128 129 efc_log_debug(domain->efc, "New Nport [%s]\n", nport->display_name);130 131 return nport;132}133 134void135efc_nport_free(struct efc_nport *nport)136{137 struct efc_domain *domain;138 139 if (!nport)140 return;141 142 domain = nport->domain;143 efc_log_debug(domain->efc, "[%s] free nport\n", nport->display_name);144 list_del(&nport->list_entry);145 /*146 * if this is the physical nport,147 * then clear it out of the domain148 */149 if (nport == domain->nport)150 domain->nport = NULL;151 152 xa_destroy(&nport->lookup);153 xa_erase(&domain->lookup, nport->fc_id);154 155 if (list_empty(&domain->nport_list))156 efc_domain_post_event(domain, EFC_EVT_ALL_CHILD_NODES_FREE,157 NULL);158 159 kref_put(&domain->ref, domain->release);160 kref_put(&nport->ref, nport->release);161}162 163struct efc_nport *164efc_nport_find(struct efc_domain *domain, u32 d_id)165{166 struct efc_nport *nport;167 168 /* Find a nport object, given an FC_ID */169 nport = xa_load(&domain->lookup, d_id);170 if (!nport || !kref_get_unless_zero(&nport->ref))171 return NULL;172 173 return nport;174}175 176int177efc_nport_attach(struct efc_nport *nport, u32 fc_id)178{179 int rc;180 struct efc_node *node;181 struct efc *efc = nport->efc;182 unsigned long index;183 184 /* Set our lookup */185 rc = xa_err(xa_store(&nport->domain->lookup, fc_id, nport, GFP_ATOMIC));186 if (rc) {187 efc_log_err(efc, "Sport lookup store failed: %d\n", rc);188 return rc;189 }190 191 /* Update our display_name */192 efc_node_fcid_display(fc_id, nport->display_name,193 sizeof(nport->display_name));194 195 xa_for_each(&nport->lookup, index, node) {196 efc_node_update_display_name(node);197 }198 199 efc_log_debug(nport->efc, "[%s] attach nport: fc_id x%06x\n",200 nport->display_name, fc_id);201 202 /* Register a nport, given an FC_ID */203 rc = efc_cmd_nport_attach(efc, nport, fc_id);204 if (rc < 0) {205 efc_log_err(nport->efc,206 "efc_hw_port_attach failed: %d\n", rc);207 return -EIO;208 }209 return 0;210}211 212static void213efc_nport_shutdown(struct efc_nport *nport)214{215 struct efc *efc = nport->efc;216 struct efc_node *node;217 unsigned long index;218 219 xa_for_each(&nport->lookup, index, node) {220 if (!(node->rnode.fc_id == FC_FID_FLOGI && nport->is_vport)) {221 efc_node_post_event(node, EFC_EVT_SHUTDOWN, NULL);222 continue;223 }224 225 /*226 * If this is a vport, logout of the fabric227 * controller so that it deletes the vport228 * on the switch.229 */230 /* if link is down, don't send logo */231 if (efc->link_status == EFC_LINK_STATUS_DOWN) {232 efc_node_post_event(node, EFC_EVT_SHUTDOWN, NULL);233 continue;234 }235 236 efc_log_debug(efc, "[%s] nport shutdown vport, send logo\n",237 node->display_name);238 239 if (!efc_send_logo(node)) {240 /* sent LOGO, wait for response */241 efc_node_transition(node, __efc_d_wait_logo_rsp, NULL);242 continue;243 }244 245 /*246 * failed to send LOGO,247 * go ahead and cleanup node anyways248 */249 node_printf(node, "Failed to send LOGO\n");250 efc_node_post_event(node, EFC_EVT_SHUTDOWN_EXPLICIT_LOGO, NULL);251 }252}253 254static void255efc_vport_link_down(struct efc_nport *nport)256{257 struct efc *efc = nport->efc;258 struct efc_vport *vport;259 260 /* Clear the nport reference in the vport specification */261 list_for_each_entry(vport, &efc->vport_list, list_entry) {262 if (vport->nport == nport) {263 kref_put(&nport->ref, nport->release);264 vport->nport = NULL;265 break;266 }267 }268}269 270static void271__efc_nport_common(const char *funcname, struct efc_sm_ctx *ctx,272 enum efc_sm_event evt, void *arg)273{274 struct efc_nport *nport = ctx->app;275 struct efc_domain *domain = nport->domain;276 struct efc *efc = nport->efc;277 278 switch (evt) {279 case EFC_EVT_ENTER:280 case EFC_EVT_REENTER:281 case EFC_EVT_EXIT:282 case EFC_EVT_ALL_CHILD_NODES_FREE:283 break;284 case EFC_EVT_NPORT_ATTACH_OK:285 efc_sm_transition(ctx, __efc_nport_attached, NULL);286 break;287 case EFC_EVT_SHUTDOWN:288 /* Flag this nport as shutting down */289 nport->shutting_down = true;290 291 if (nport->is_vport)292 efc_vport_link_down(nport);293 294 if (xa_empty(&nport->lookup)) {295 /* Remove the nport from the domain's lookup table */296 xa_erase(&domain->lookup, nport->fc_id);297 efc_sm_transition(ctx, __efc_nport_wait_port_free,298 NULL);299 if (efc_cmd_nport_free(efc, nport)) {300 efc_log_debug(nport->efc,301 "efc_hw_port_free failed\n");302 /* Not much we can do, free the nport anyways */303 efc_nport_free(nport);304 }305 } else {306 /* sm: node list is not empty / shutdown nodes */307 efc_sm_transition(ctx,308 __efc_nport_wait_shutdown, NULL);309 efc_nport_shutdown(nport);310 }311 break;312 default:313 efc_log_debug(nport->efc, "[%s] %-20s %-20s not handled\n",314 nport->display_name, funcname,315 efc_sm_event_name(evt));316 }317}318 319void320__efc_nport_allocated(struct efc_sm_ctx *ctx,321 enum efc_sm_event evt, void *arg)322{323 struct efc_nport *nport = ctx->app;324 struct efc_domain *domain = nport->domain;325 326 nport_sm_trace(nport);327 328 switch (evt) {329 /* the physical nport is attached */330 case EFC_EVT_NPORT_ATTACH_OK:331 WARN_ON(nport != domain->nport);332 efc_sm_transition(ctx, __efc_nport_attached, NULL);333 break;334 335 case EFC_EVT_NPORT_ALLOC_OK:336 /* ignore */337 break;338 default:339 __efc_nport_common(__func__, ctx, evt, arg);340 }341}342 343void344__efc_nport_vport_init(struct efc_sm_ctx *ctx,345 enum efc_sm_event evt, void *arg)346{347 struct efc_nport *nport = ctx->app;348 struct efc *efc = nport->efc;349 350 nport_sm_trace(nport);351 352 switch (evt) {353 case EFC_EVT_ENTER: {354 __be64 be_wwpn = cpu_to_be64(nport->wwpn);355 356 if (nport->wwpn == 0)357 efc_log_debug(efc, "vport: letting f/w select WWN\n");358 359 if (nport->fc_id != U32_MAX) {360 efc_log_debug(efc, "vport: hard coding port id: %x\n",361 nport->fc_id);362 }363 364 efc_sm_transition(ctx, __efc_nport_vport_wait_alloc, NULL);365 /* If wwpn is zero, then we'll let the f/w assign wwpn*/366 if (efc_cmd_nport_alloc(efc, nport, nport->domain,367 nport->wwpn == 0 ? NULL :368 (uint8_t *)&be_wwpn)) {369 efc_log_err(efc, "Can't allocate port\n");370 break;371 }372 373 break;374 }375 default:376 __efc_nport_common(__func__, ctx, evt, arg);377 }378}379 380void381__efc_nport_vport_wait_alloc(struct efc_sm_ctx *ctx,382 enum efc_sm_event evt, void *arg)383{384 struct efc_nport *nport = ctx->app;385 struct efc *efc = nport->efc;386 387 nport_sm_trace(nport);388 389 switch (evt) {390 case EFC_EVT_NPORT_ALLOC_OK: {391 struct fc_els_flogi *sp;392 393 sp = (struct fc_els_flogi *)nport->service_params;394 395 if (nport->wwnn == 0) {396 nport->wwnn = be64_to_cpu(nport->sli_wwnn);397 nport->wwpn = be64_to_cpu(nport->sli_wwpn);398 snprintf(nport->wwnn_str, sizeof(nport->wwnn_str),399 "%016llX", nport->wwpn);400 }401 402 /* Update the nport's service parameters */403 sp->fl_wwpn = cpu_to_be64(nport->wwpn);404 sp->fl_wwnn = cpu_to_be64(nport->wwnn);405 406 /*407 * if nport->fc_id is uninitialized,408 * then request that the fabric node use FDISC409 * to find an fc_id.410 * Otherwise we're restoring vports, or we're in411 * fabric emulation mode, so attach the fc_id412 */413 if (nport->fc_id == U32_MAX) {414 struct efc_node *fabric;415 416 fabric = efc_node_alloc(nport, FC_FID_FLOGI, false,417 false);418 if (!fabric) {419 efc_log_err(efc, "efc_node_alloc() failed\n");420 return;421 }422 efc_node_transition(fabric, __efc_vport_fabric_init,423 NULL);424 } else {425 snprintf(nport->wwnn_str, sizeof(nport->wwnn_str),426 "%016llX", nport->wwpn);427 efc_nport_attach(nport, nport->fc_id);428 }429 efc_sm_transition(ctx, __efc_nport_vport_allocated, NULL);430 break;431 }432 default:433 __efc_nport_common(__func__, ctx, evt, arg);434 }435}436 437void438__efc_nport_vport_allocated(struct efc_sm_ctx *ctx,439 enum efc_sm_event evt, void *arg)440{441 struct efc_nport *nport = ctx->app;442 struct efc *efc = nport->efc;443 444 nport_sm_trace(nport);445 446 /*447 * This state is entered after the nport is allocated;448 * it then waits for a fabric node449 * FDISC to complete, which requests a nport attach.450 * The nport attach complete is handled in this state.451 */452 switch (evt) {453 case EFC_EVT_NPORT_ATTACH_OK: {454 struct efc_node *node;455 456 /* Find our fabric node, and forward this event */457 node = efc_node_find(nport, FC_FID_FLOGI);458 if (!node) {459 efc_log_debug(efc, "can't find node %06x\n", FC_FID_FLOGI);460 break;461 }462 /* sm: / forward nport attach to fabric node */463 efc_node_post_event(node, evt, NULL);464 efc_sm_transition(ctx, __efc_nport_attached, NULL);465 break;466 }467 default:468 __efc_nport_common(__func__, ctx, evt, arg);469 }470}471 472static void473efc_vport_update_spec(struct efc_nport *nport)474{475 struct efc *efc = nport->efc;476 struct efc_vport *vport;477 unsigned long flags = 0;478 479 spin_lock_irqsave(&efc->vport_lock, flags);480 list_for_each_entry(vport, &efc->vport_list, list_entry) {481 if (vport->nport == nport) {482 vport->wwnn = nport->wwnn;483 vport->wwpn = nport->wwpn;484 vport->tgt_data = nport->tgt_data;485 vport->ini_data = nport->ini_data;486 break;487 }488 }489 spin_unlock_irqrestore(&efc->vport_lock, flags);490}491 492void493__efc_nport_attached(struct efc_sm_ctx *ctx,494 enum efc_sm_event evt, void *arg)495{496 struct efc_nport *nport = ctx->app;497 struct efc *efc = nport->efc;498 499 nport_sm_trace(nport);500 501 switch (evt) {502 case EFC_EVT_ENTER: {503 struct efc_node *node;504 unsigned long index;505 506 efc_log_debug(efc,507 "[%s] NPORT attached WWPN %016llX WWNN %016llX\n",508 nport->display_name,509 nport->wwpn, nport->wwnn);510 511 xa_for_each(&nport->lookup, index, node)512 efc_node_update_display_name(node);513 514 efc->tt.new_nport(efc, nport);515 516 /*517 * Update the vport (if its not the physical nport)518 * parameters519 */520 if (nport->is_vport)521 efc_vport_update_spec(nport);522 break;523 }524 525 case EFC_EVT_EXIT:526 efc_log_debug(efc,527 "[%s] NPORT deattached WWPN %016llX WWNN %016llX\n",528 nport->display_name,529 nport->wwpn, nport->wwnn);530 531 efc->tt.del_nport(efc, nport);532 break;533 default:534 __efc_nport_common(__func__, ctx, evt, arg);535 }536}537 538void539__efc_nport_wait_shutdown(struct efc_sm_ctx *ctx,540 enum efc_sm_event evt, void *arg)541{542 struct efc_nport *nport = ctx->app;543 struct efc_domain *domain = nport->domain;544 struct efc *efc = nport->efc;545 546 nport_sm_trace(nport);547 548 switch (evt) {549 case EFC_EVT_NPORT_ALLOC_OK:550 case EFC_EVT_NPORT_ALLOC_FAIL:551 case EFC_EVT_NPORT_ATTACH_OK:552 case EFC_EVT_NPORT_ATTACH_FAIL:553 /* ignore these events - just wait for the all free event */554 break;555 556 case EFC_EVT_ALL_CHILD_NODES_FREE: {557 /*558 * Remove the nport from the domain's559 * sparse vector lookup table560 */561 xa_erase(&domain->lookup, nport->fc_id);562 efc_sm_transition(ctx, __efc_nport_wait_port_free, NULL);563 if (efc_cmd_nport_free(efc, nport)) {564 efc_log_err(nport->efc, "efc_hw_port_free failed\n");565 /* Not much we can do, free the nport anyways */566 efc_nport_free(nport);567 }568 break;569 }570 default:571 __efc_nport_common(__func__, ctx, evt, arg);572 }573}574 575void576__efc_nport_wait_port_free(struct efc_sm_ctx *ctx,577 enum efc_sm_event evt, void *arg)578{579 struct efc_nport *nport = ctx->app;580 581 nport_sm_trace(nport);582 583 switch (evt) {584 case EFC_EVT_NPORT_ATTACH_OK:585 /* Ignore as we are waiting for the free CB */586 break;587 case EFC_EVT_NPORT_FREE_OK: {588 /* All done, free myself */589 efc_nport_free(nport);590 break;591 }592 default:593 __efc_nport_common(__func__, ctx, evt, arg);594 }595}596 597static int598efc_vport_nport_alloc(struct efc_domain *domain, struct efc_vport *vport)599{600 struct efc_nport *nport;601 602 lockdep_assert_held(&domain->efc->lock);603 604 nport = efc_nport_alloc(domain, vport->wwpn, vport->wwnn, vport->fc_id,605 vport->enable_ini, vport->enable_tgt);606 vport->nport = nport;607 if (!nport)608 return -EIO;609 610 kref_get(&nport->ref);611 nport->is_vport = true;612 nport->tgt_data = vport->tgt_data;613 nport->ini_data = vport->ini_data;614 615 efc_sm_transition(&nport->sm, __efc_nport_vport_init, NULL);616 617 return 0;618}619 620int621efc_vport_start(struct efc_domain *domain)622{623 struct efc *efc = domain->efc;624 struct efc_vport *vport;625 struct efc_vport *next;626 int rc = 0;627 unsigned long flags = 0;628 629 /* Use the vport spec to find the associated vports and start them */630 spin_lock_irqsave(&efc->vport_lock, flags);631 list_for_each_entry_safe(vport, next, &efc->vport_list, list_entry) {632 if (!vport->nport) {633 if (efc_vport_nport_alloc(domain, vport))634 rc = -EIO;635 }636 }637 spin_unlock_irqrestore(&efc->vport_lock, flags);638 639 return rc;640}641 642int643efc_nport_vport_new(struct efc_domain *domain, uint64_t wwpn, uint64_t wwnn,644 u32 fc_id, bool ini, bool tgt, void *tgt_data,645 void *ini_data)646{647 struct efc *efc = domain->efc;648 struct efc_vport *vport;649 int rc = 0;650 unsigned long flags = 0;651 652 if (ini && domain->efc->enable_ini == 0) {653 efc_log_debug(efc, "driver initiator mode not enabled\n");654 return -EIO;655 }656 657 if (tgt && domain->efc->enable_tgt == 0) {658 efc_log_debug(efc, "driver target mode not enabled\n");659 return -EIO;660 }661 662 /*663 * Create a vport spec if we need to recreate664 * this vport after a link up event665 */666 vport = efc_vport_create_spec(domain->efc, wwnn, wwpn, fc_id, ini, tgt,667 tgt_data, ini_data);668 if (!vport) {669 efc_log_err(efc, "failed to create vport object entry\n");670 return -EIO;671 }672 673 spin_lock_irqsave(&efc->lock, flags);674 rc = efc_vport_nport_alloc(domain, vport);675 spin_unlock_irqrestore(&efc->lock, flags);676 677 return rc;678}679 680int681efc_nport_vport_del(struct efc *efc, struct efc_domain *domain,682 u64 wwpn, uint64_t wwnn)683{684 struct efc_nport *nport;685 struct efc_vport *vport;686 struct efc_vport *next;687 unsigned long flags = 0;688 689 spin_lock_irqsave(&efc->vport_lock, flags);690 /* walk the efc_vport_list and remove from there */691 list_for_each_entry_safe(vport, next, &efc->vport_list, list_entry) {692 if (vport->wwpn == wwpn && vport->wwnn == wwnn) {693 list_del(&vport->list_entry);694 kfree(vport);695 break;696 }697 }698 spin_unlock_irqrestore(&efc->vport_lock, flags);699 700 if (!domain) {701 /* No domain means no nport to look for */702 return 0;703 }704 705 spin_lock_irqsave(&efc->lock, flags);706 list_for_each_entry(nport, &domain->nport_list, list_entry) {707 if (nport->wwpn == wwpn && nport->wwnn == wwnn) {708 /* Shutdown this NPORT */709 efc_sm_post_event(&nport->sm, EFC_EVT_SHUTDOWN, NULL);710 kref_put(&nport->ref, nport->release);711 break;712 }713 }714 715 spin_unlock_irqrestore(&efc->lock, flags);716 return 0;717}718 719void720efc_vport_del_all(struct efc *efc)721{722 struct efc_vport *vport;723 struct efc_vport *next;724 unsigned long flags = 0;725 726 spin_lock_irqsave(&efc->vport_lock, flags);727 list_for_each_entry_safe(vport, next, &efc->vport_list, list_entry) {728 list_del(&vport->list_entry);729 kfree(vport);730 }731 spin_unlock_irqrestore(&efc->vport_lock, flags);732}733 734struct efc_vport *735efc_vport_create_spec(struct efc *efc, uint64_t wwnn, uint64_t wwpn,736 u32 fc_id, bool enable_ini,737 bool enable_tgt, void *tgt_data, void *ini_data)738{739 struct efc_vport *vport;740 unsigned long flags = 0;741 742 /*743 * walk the efc_vport_list and return failure744 * if a valid(vport with non zero WWPN and WWNN) vport entry745 * is already created746 */747 spin_lock_irqsave(&efc->vport_lock, flags);748 list_for_each_entry(vport, &efc->vport_list, list_entry) {749 if ((wwpn && vport->wwpn == wwpn) &&750 (wwnn && vport->wwnn == wwnn)) {751 efc_log_err(efc,752 "VPORT %016llX %016llX already allocated\n",753 wwnn, wwpn);754 spin_unlock_irqrestore(&efc->vport_lock, flags);755 return NULL;756 }757 }758 759 vport = kzalloc(sizeof(*vport), GFP_ATOMIC);760 if (!vport) {761 spin_unlock_irqrestore(&efc->vport_lock, flags);762 return NULL;763 }764 765 vport->wwnn = wwnn;766 vport->wwpn = wwpn;767 vport->fc_id = fc_id;768 vport->enable_tgt = enable_tgt;769 vport->enable_ini = enable_ini;770 vport->tgt_data = tgt_data;771 vport->ini_data = ini_data;772 773 INIT_LIST_HEAD(&vport->list_entry);774 list_add_tail(&vport->list_entry, &efc->vport_list);775 spin_unlock_irqrestore(&efc->vport_lock, flags);776 return vport;777}778