1089 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 * domain_sm Domain State Machine: States9 */10 11#include "efc.h"12 13int14efc_domain_cb(void *arg, int event, void *data)15{16 struct efc *efc = arg;17 struct efc_domain *domain = NULL;18 int rc = 0;19 unsigned long flags = 0;20 21 if (event != EFC_HW_DOMAIN_FOUND)22 domain = data;23 24 /* Accept domain callback events from the user driver */25 spin_lock_irqsave(&efc->lock, flags);26 switch (event) {27 case EFC_HW_DOMAIN_FOUND: {28 u64 fcf_wwn = 0;29 struct efc_domain_record *drec = data;30 31 /* extract the fcf_wwn */32 fcf_wwn = be64_to_cpu(*((__be64 *)drec->wwn));33 34 efc_log_debug(efc, "Domain found: wwn %016llX\n", fcf_wwn);35 36 /* lookup domain, or allocate a new one */37 domain = efc->domain;38 if (!domain) {39 domain = efc_domain_alloc(efc, fcf_wwn);40 if (!domain) {41 efc_log_err(efc, "efc_domain_alloc() failed\n");42 rc = -1;43 break;44 }45 efc_sm_transition(&domain->drvsm, __efc_domain_init,46 NULL);47 }48 efc_domain_post_event(domain, EFC_EVT_DOMAIN_FOUND, drec);49 break;50 }51 52 case EFC_HW_DOMAIN_LOST:53 domain_trace(domain, "EFC_HW_DOMAIN_LOST:\n");54 efc->hold_frames = true;55 efc_domain_post_event(domain, EFC_EVT_DOMAIN_LOST, NULL);56 break;57 58 case EFC_HW_DOMAIN_ALLOC_OK:59 domain_trace(domain, "EFC_HW_DOMAIN_ALLOC_OK:\n");60 efc_domain_post_event(domain, EFC_EVT_DOMAIN_ALLOC_OK, NULL);61 break;62 63 case EFC_HW_DOMAIN_ALLOC_FAIL:64 domain_trace(domain, "EFC_HW_DOMAIN_ALLOC_FAIL:\n");65 efc_domain_post_event(domain, EFC_EVT_DOMAIN_ALLOC_FAIL,66 NULL);67 break;68 69 case EFC_HW_DOMAIN_ATTACH_OK:70 domain_trace(domain, "EFC_HW_DOMAIN_ATTACH_OK:\n");71 efc_domain_post_event(domain, EFC_EVT_DOMAIN_ATTACH_OK, NULL);72 break;73 74 case EFC_HW_DOMAIN_ATTACH_FAIL:75 domain_trace(domain, "EFC_HW_DOMAIN_ATTACH_FAIL:\n");76 efc_domain_post_event(domain,77 EFC_EVT_DOMAIN_ATTACH_FAIL, NULL);78 break;79 80 case EFC_HW_DOMAIN_FREE_OK:81 domain_trace(domain, "EFC_HW_DOMAIN_FREE_OK:\n");82 efc_domain_post_event(domain, EFC_EVT_DOMAIN_FREE_OK, NULL);83 break;84 85 case EFC_HW_DOMAIN_FREE_FAIL:86 domain_trace(domain, "EFC_HW_DOMAIN_FREE_FAIL:\n");87 efc_domain_post_event(domain, EFC_EVT_DOMAIN_FREE_FAIL, NULL);88 break;89 90 default:91 efc_log_warn(efc, "unsupported event %#x\n", event);92 }93 spin_unlock_irqrestore(&efc->lock, flags);94 95 if (efc->domain && domain->req_accept_frames) {96 domain->req_accept_frames = false;97 efc->hold_frames = false;98 }99 100 return rc;101}102 103static void104_efc_domain_free(struct kref *arg)105{106 struct efc_domain *domain = container_of(arg, struct efc_domain, ref);107 struct efc *efc = domain->efc;108 109 if (efc->domain_free_cb)110 (*efc->domain_free_cb)(efc, efc->domain_free_cb_arg);111 112 kfree(domain);113}114 115void116efc_domain_free(struct efc_domain *domain)117{118 struct efc *efc;119 120 efc = domain->efc;121 122 /* Hold frames to clear the domain pointer from the xport lookup */123 efc->hold_frames = false;124 125 efc_log_debug(efc, "Domain free: wwn %016llX\n", domain->fcf_wwn);126 127 xa_destroy(&domain->lookup);128 efc->domain = NULL;129 kref_put(&domain->ref, domain->release);130}131 132struct efc_domain *133efc_domain_alloc(struct efc *efc, uint64_t fcf_wwn)134{135 struct efc_domain *domain;136 137 domain = kzalloc(sizeof(*domain), GFP_ATOMIC);138 if (!domain)139 return NULL;140 141 domain->efc = efc;142 domain->drvsm.app = domain;143 144 /* initialize refcount */145 kref_init(&domain->ref);146 domain->release = _efc_domain_free;147 148 xa_init(&domain->lookup);149 150 INIT_LIST_HEAD(&domain->nport_list);151 efc->domain = domain;152 domain->fcf_wwn = fcf_wwn;153 efc_log_debug(efc, "Domain allocated: wwn %016llX\n", domain->fcf_wwn);154 155 return domain;156}157 158void159efc_register_domain_free_cb(struct efc *efc,160 void (*callback)(struct efc *efc, void *arg),161 void *arg)162{163 /* Register a callback to be called when the domain is freed */164 efc->domain_free_cb = callback;165 efc->domain_free_cb_arg = arg;166 if (!efc->domain && callback)167 (*callback)(efc, arg);168}169 170static void171__efc_domain_common(const char *funcname, struct efc_sm_ctx *ctx,172 enum efc_sm_event evt, void *arg)173{174 struct efc_domain *domain = ctx->app;175 176 switch (evt) {177 case EFC_EVT_ENTER:178 case EFC_EVT_REENTER:179 case EFC_EVT_EXIT:180 case EFC_EVT_ALL_CHILD_NODES_FREE:181 /*182 * this can arise if an FLOGI fails on the NPORT,183 * and the NPORT is shutdown184 */185 break;186 default:187 efc_log_warn(domain->efc, "%-20s %-20s not handled\n",188 funcname, efc_sm_event_name(evt));189 }190}191 192static void193__efc_domain_common_shutdown(const char *funcname, struct efc_sm_ctx *ctx,194 enum efc_sm_event evt, void *arg)195{196 struct efc_domain *domain = ctx->app;197 198 switch (evt) {199 case EFC_EVT_ENTER:200 case EFC_EVT_REENTER:201 case EFC_EVT_EXIT:202 break;203 case EFC_EVT_DOMAIN_FOUND:204 /* save drec, mark domain_found_pending */205 memcpy(&domain->pending_drec, arg,206 sizeof(domain->pending_drec));207 domain->domain_found_pending = true;208 break;209 case EFC_EVT_DOMAIN_LOST:210 /* unmark domain_found_pending */211 domain->domain_found_pending = false;212 break;213 214 default:215 efc_log_warn(domain->efc, "%-20s %-20s not handled\n",216 funcname, efc_sm_event_name(evt));217 }218}219 220#define std_domain_state_decl(...)\221 struct efc_domain *domain = NULL;\222 struct efc *efc = NULL;\223 \224 WARN_ON(!ctx || !ctx->app);\225 domain = ctx->app;\226 WARN_ON(!domain->efc);\227 efc = domain->efc228 229void230__efc_domain_init(struct efc_sm_ctx *ctx, enum efc_sm_event evt,231 void *arg)232{233 std_domain_state_decl();234 235 domain_sm_trace(domain);236 237 switch (evt) {238 case EFC_EVT_ENTER:239 domain->attached = false;240 break;241 242 case EFC_EVT_DOMAIN_FOUND: {243 u32 i;244 struct efc_domain_record *drec = arg;245 struct efc_nport *nport;246 247 u64 my_wwnn = efc->req_wwnn;248 u64 my_wwpn = efc->req_wwpn;249 __be64 bewwpn;250 251 if (my_wwpn == 0 || my_wwnn == 0) {252 efc_log_debug(efc, "using default hardware WWN config\n");253 my_wwpn = efc->def_wwpn;254 my_wwnn = efc->def_wwnn;255 }256 257 efc_log_debug(efc, "Create nport WWPN %016llX WWNN %016llX\n",258 my_wwpn, my_wwnn);259 260 /* Allocate a nport and transition to __efc_nport_allocated */261 nport = efc_nport_alloc(domain, my_wwpn, my_wwnn, U32_MAX,262 efc->enable_ini, efc->enable_tgt);263 264 if (!nport) {265 efc_log_err(efc, "efc_nport_alloc() failed\n");266 break;267 }268 efc_sm_transition(&nport->sm, __efc_nport_allocated, NULL);269 270 bewwpn = cpu_to_be64(nport->wwpn);271 272 /* allocate struct efc_nport object for local port273 * Note: drec->fc_id is ALPA from read_topology only if loop274 */275 if (efc_cmd_nport_alloc(efc, nport, NULL, (uint8_t *)&bewwpn)) {276 efc_log_err(efc, "Can't allocate port\n");277 efc_nport_free(nport);278 break;279 }280 281 domain->is_loop = drec->is_loop;282 283 /*284 * If the loop position map includes ALPA == 0,285 * then we are in a public loop (NL_PORT)286 * Note that the first element of the loopmap[]287 * contains the count of elements, and if288 * ALPA == 0 is present, it will occupy the first289 * location after the count.290 */291 domain->is_nlport = drec->map.loop[1] == 0x00;292 293 if (!domain->is_loop) {294 /* Initiate HW domain alloc */295 if (efc_cmd_domain_alloc(efc, domain, drec->index)) {296 efc_log_err(efc,297 "Failed to initiate HW domain allocation\n");298 break;299 }300 efc_sm_transition(ctx, __efc_domain_wait_alloc, arg);301 break;302 }303 304 efc_log_debug(efc, "%s fc_id=%#x speed=%d\n",305 drec->is_loop ?306 (domain->is_nlport ?307 "public-loop" : "loop") : "other",308 drec->fc_id, drec->speed);309 310 nport->fc_id = drec->fc_id;311 nport->topology = EFC_NPORT_TOPO_FC_AL;312 snprintf(nport->display_name, sizeof(nport->display_name),313 "s%06x", drec->fc_id);314 315 if (efc->enable_ini) {316 u32 count = drec->map.loop[0];317 318 efc_log_debug(efc, "%d position map entries\n",319 count);320 for (i = 1; i <= count; i++) {321 if (drec->map.loop[i] != drec->fc_id) {322 struct efc_node *node;323 324 efc_log_debug(efc, "%#x -> %#x\n",325 drec->fc_id,326 drec->map.loop[i]);327 node = efc_node_alloc(nport,328 drec->map.loop[i],329 false, true);330 if (!node) {331 efc_log_err(efc,332 "efc_node_alloc() failed\n");333 break;334 }335 efc_node_transition(node,336 __efc_d_wait_loop,337 NULL);338 }339 }340 }341 342 /* Initiate HW domain alloc */343 if (efc_cmd_domain_alloc(efc, domain, drec->index)) {344 efc_log_err(efc,345 "Failed to initiate HW domain allocation\n");346 break;347 }348 efc_sm_transition(ctx, __efc_domain_wait_alloc, arg);349 break;350 }351 default:352 __efc_domain_common(__func__, ctx, evt, arg);353 }354}355 356void357__efc_domain_wait_alloc(struct efc_sm_ctx *ctx,358 enum efc_sm_event evt, void *arg)359{360 std_domain_state_decl();361 362 domain_sm_trace(domain);363 364 switch (evt) {365 case EFC_EVT_DOMAIN_ALLOC_OK: {366 struct fc_els_flogi *sp;367 struct efc_nport *nport;368 369 nport = domain->nport;370 if (WARN_ON(!nport))371 return;372 373 sp = (struct fc_els_flogi *)nport->service_params;374 375 /* Save the domain service parameters */376 memcpy(domain->service_params + 4, domain->dma.virt,377 sizeof(struct fc_els_flogi) - 4);378 memcpy(nport->service_params + 4, domain->dma.virt,379 sizeof(struct fc_els_flogi) - 4);380 381 /*382 * Update the nport's service parameters,383 * user might have specified non-default names384 */385 sp->fl_wwpn = cpu_to_be64(nport->wwpn);386 sp->fl_wwnn = cpu_to_be64(nport->wwnn);387 388 /*389 * Take the loop topology path,390 * unless we are an NL_PORT (public loop)391 */392 if (domain->is_loop && !domain->is_nlport) {393 /*394 * For loop, we already have our FC ID395 * and don't need fabric login.396 * Transition to the allocated state and397 * post an event to attach to398 * the domain. Note that this breaks the399 * normal action/transition400 * pattern here to avoid a race with the401 * domain attach callback.402 */403 /* sm: is_loop / domain_attach */404 efc_sm_transition(ctx, __efc_domain_allocated, NULL);405 __efc_domain_attach_internal(domain, nport->fc_id);406 break;407 }408 {409 struct efc_node *node;410 411 /* alloc fabric node, send FLOGI */412 node = efc_node_find(nport, FC_FID_FLOGI);413 if (node) {414 efc_log_err(efc,415 "Fabric Controller node already exists\n");416 break;417 }418 node = efc_node_alloc(nport, FC_FID_FLOGI,419 false, false);420 if (!node) {421 efc_log_err(efc,422 "Error: efc_node_alloc() failed\n");423 } else {424 efc_node_transition(node,425 __efc_fabric_init, NULL);426 }427 /* Accept frames */428 domain->req_accept_frames = true;429 }430 /* sm: / start fabric logins */431 efc_sm_transition(ctx, __efc_domain_allocated, NULL);432 break;433 }434 435 case EFC_EVT_DOMAIN_ALLOC_FAIL:436 efc_log_err(efc, "%s recv'd waiting for DOMAIN_ALLOC_OK;",437 efc_sm_event_name(evt));438 efc_log_err(efc, "shutting down domain\n");439 domain->req_domain_free = true;440 break;441 442 case EFC_EVT_DOMAIN_FOUND:443 /* Should not happen */444 break;445 446 case EFC_EVT_DOMAIN_LOST:447 efc_log_debug(efc,448 "%s received while waiting for hw_domain_alloc()\n",449 efc_sm_event_name(evt));450 efc_sm_transition(ctx, __efc_domain_wait_domain_lost, NULL);451 break;452 453 default:454 __efc_domain_common(__func__, ctx, evt, arg);455 }456}457 458void459__efc_domain_allocated(struct efc_sm_ctx *ctx,460 enum efc_sm_event evt, void *arg)461{462 std_domain_state_decl();463 464 domain_sm_trace(domain);465 466 switch (evt) {467 case EFC_EVT_DOMAIN_REQ_ATTACH: {468 int rc = 0;469 u32 fc_id;470 471 if (WARN_ON(!arg))472 return;473 474 fc_id = *((u32 *)arg);475 efc_log_debug(efc, "Requesting hw domain attach fc_id x%x\n",476 fc_id);477 /* Update nport lookup */478 rc = xa_err(xa_store(&domain->lookup, fc_id, domain->nport,479 GFP_ATOMIC));480 if (rc) {481 efc_log_err(efc, "Sport lookup store failed: %d\n", rc);482 return;483 }484 485 /* Update display name for the nport */486 efc_node_fcid_display(fc_id, domain->nport->display_name,487 sizeof(domain->nport->display_name));488 489 /* Issue domain attach call */490 rc = efc_cmd_domain_attach(efc, domain, fc_id);491 if (rc) {492 efc_log_err(efc, "efc_hw_domain_attach failed: %d\n",493 rc);494 return;495 }496 /* sm: / domain_attach */497 efc_sm_transition(ctx, __efc_domain_wait_attach, NULL);498 break;499 }500 501 case EFC_EVT_DOMAIN_FOUND:502 /* Should not happen */503 efc_log_err(efc, "%s: evt: %d should not happen\n",504 __func__, evt);505 break;506 507 case EFC_EVT_DOMAIN_LOST: {508 efc_log_debug(efc,509 "%s received while in EFC_EVT_DOMAIN_REQ_ATTACH\n",510 efc_sm_event_name(evt));511 if (!list_empty(&domain->nport_list)) {512 /*513 * if there are nports, transition to514 * wait state and send shutdown to each515 * nport516 */517 struct efc_nport *nport = NULL, *nport_next = NULL;518 519 efc_sm_transition(ctx, __efc_domain_wait_nports_free,520 NULL);521 list_for_each_entry_safe(nport, nport_next,522 &domain->nport_list,523 list_entry) {524 efc_sm_post_event(&nport->sm,525 EFC_EVT_SHUTDOWN, NULL);526 }527 } else {528 /* no nports exist, free domain */529 efc_sm_transition(ctx, __efc_domain_wait_shutdown,530 NULL);531 if (efc_cmd_domain_free(efc, domain))532 efc_log_err(efc, "hw_domain_free failed\n");533 }534 535 break;536 }537 538 default:539 __efc_domain_common(__func__, ctx, evt, arg);540 }541}542 543void544__efc_domain_wait_attach(struct efc_sm_ctx *ctx,545 enum efc_sm_event evt, void *arg)546{547 std_domain_state_decl();548 549 domain_sm_trace(domain);550 551 switch (evt) {552 case EFC_EVT_DOMAIN_ATTACH_OK: {553 struct efc_node *node = NULL;554 struct efc_nport *nport, *next_nport;555 unsigned long index;556 557 /*558 * Set domain notify pending state to avoid559 * duplicate domain event post560 */561 domain->domain_notify_pend = true;562 563 /* Mark as attached */564 domain->attached = true;565 566 /* Transition to ready */567 /* sm: / forward event to all nports and nodes */568 efc_sm_transition(ctx, __efc_domain_ready, NULL);569 570 /* We have an FCFI, so we can accept frames */571 domain->req_accept_frames = true;572 573 /*574 * Notify all nodes that the domain attach request575 * has completed576 * Note: nport will have already received notification577 * of nport attached as a result of the HW's port attach.578 */579 list_for_each_entry_safe(nport, next_nport,580 &domain->nport_list, list_entry) {581 xa_for_each(&nport->lookup, index, node) {582 efc_node_post_event(node,583 EFC_EVT_DOMAIN_ATTACH_OK,584 NULL);585 }586 }587 domain->domain_notify_pend = false;588 break;589 }590 591 case EFC_EVT_DOMAIN_ATTACH_FAIL:592 efc_log_debug(efc,593 "%s received while waiting for hw attach\n",594 efc_sm_event_name(evt));595 break;596 597 case EFC_EVT_DOMAIN_FOUND:598 /* Should not happen */599 efc_log_err(efc, "%s: evt: %d should not happen\n",600 __func__, evt);601 break;602 603 case EFC_EVT_DOMAIN_LOST:604 /*605 * Domain lost while waiting for an attach to complete,606 * go to a state that waits for the domain attach to607 * complete, then handle domain lost608 */609 efc_sm_transition(ctx, __efc_domain_wait_domain_lost, NULL);610 break;611 612 case EFC_EVT_DOMAIN_REQ_ATTACH:613 /*614 * In P2P we can get an attach request from615 * the other FLOGI path, so drop this one616 */617 break;618 619 default:620 __efc_domain_common(__func__, ctx, evt, arg);621 }622}623 624void625__efc_domain_ready(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg)626{627 std_domain_state_decl();628 629 domain_sm_trace(domain);630 631 switch (evt) {632 case EFC_EVT_ENTER: {633 /* start any pending vports */634 if (efc_vport_start(domain)) {635 efc_log_debug(domain->efc,636 "efc_vport_start didn't start vports\n");637 }638 break;639 }640 case EFC_EVT_DOMAIN_LOST: {641 if (!list_empty(&domain->nport_list)) {642 /*643 * if there are nports, transition to wait state644 * and send shutdown to each nport645 */646 struct efc_nport *nport = NULL, *nport_next = NULL;647 648 efc_sm_transition(ctx, __efc_domain_wait_nports_free,649 NULL);650 list_for_each_entry_safe(nport, nport_next,651 &domain->nport_list,652 list_entry) {653 efc_sm_post_event(&nport->sm,654 EFC_EVT_SHUTDOWN, NULL);655 }656 } else {657 /* no nports exist, free domain */658 efc_sm_transition(ctx, __efc_domain_wait_shutdown,659 NULL);660 if (efc_cmd_domain_free(efc, domain))661 efc_log_err(efc, "hw_domain_free failed\n");662 }663 break;664 }665 666 case EFC_EVT_DOMAIN_FOUND:667 /* Should not happen */668 efc_log_err(efc, "%s: evt: %d should not happen\n",669 __func__, evt);670 break;671 672 case EFC_EVT_DOMAIN_REQ_ATTACH: {673 /* can happen during p2p */674 u32 fc_id;675 676 fc_id = *((u32 *)arg);677 678 /* Assume that the domain is attached */679 WARN_ON(!domain->attached);680 681 /*682 * Verify that the requested FC_ID683 * is the same as the one we're working with684 */685 WARN_ON(domain->nport->fc_id != fc_id);686 break;687 }688 689 default:690 __efc_domain_common(__func__, ctx, evt, arg);691 }692}693 694void695__efc_domain_wait_nports_free(struct efc_sm_ctx *ctx, enum efc_sm_event evt,696 void *arg)697{698 std_domain_state_decl();699 700 domain_sm_trace(domain);701 702 /* Wait for nodes to free prior to the domain shutdown */703 switch (evt) {704 case EFC_EVT_ALL_CHILD_NODES_FREE: {705 int rc;706 707 /* sm: / efc_hw_domain_free */708 efc_sm_transition(ctx, __efc_domain_wait_shutdown, NULL);709 710 /* Request efc_hw_domain_free and wait for completion */711 rc = efc_cmd_domain_free(efc, domain);712 if (rc) {713 efc_log_err(efc, "efc_hw_domain_free() failed: %d\n",714 rc);715 }716 break;717 }718 default:719 __efc_domain_common_shutdown(__func__, ctx, evt, arg);720 }721}722 723void724__efc_domain_wait_shutdown(struct efc_sm_ctx *ctx,725 enum efc_sm_event evt, void *arg)726{727 std_domain_state_decl();728 729 domain_sm_trace(domain);730 731 switch (evt) {732 case EFC_EVT_DOMAIN_FREE_OK:733 /* sm: / domain_free */734 if (domain->domain_found_pending) {735 /*736 * save fcf_wwn and drec from this domain,737 * free current domain and allocate738 * a new one with the same fcf_wwn739 * could use a SLI-4 "re-register VPI"740 * operation here?741 */742 u64 fcf_wwn = domain->fcf_wwn;743 struct efc_domain_record drec = domain->pending_drec;744 745 efc_log_debug(efc, "Reallocating domain\n");746 domain->req_domain_free = true;747 domain = efc_domain_alloc(efc, fcf_wwn);748 749 if (!domain) {750 efc_log_err(efc,751 "efc_domain_alloc() failed\n");752 return;753 }754 /*755 * got a new domain; at this point,756 * there are at least two domains757 * once the req_domain_free flag is processed,758 * the associated domain will be removed.759 */760 efc_sm_transition(&domain->drvsm, __efc_domain_init,761 NULL);762 efc_sm_post_event(&domain->drvsm,763 EFC_EVT_DOMAIN_FOUND, &drec);764 } else {765 domain->req_domain_free = true;766 }767 break;768 default:769 __efc_domain_common_shutdown(__func__, ctx, evt, arg);770 }771}772 773void774__efc_domain_wait_domain_lost(struct efc_sm_ctx *ctx,775 enum efc_sm_event evt, void *arg)776{777 std_domain_state_decl();778 779 domain_sm_trace(domain);780 781 /*782 * Wait for the domain alloc/attach completion783 * after receiving a domain lost.784 */785 switch (evt) {786 case EFC_EVT_DOMAIN_ALLOC_OK:787 case EFC_EVT_DOMAIN_ATTACH_OK: {788 if (!list_empty(&domain->nport_list)) {789 /*790 * if there are nports, transition to791 * wait state and send shutdown to each nport792 */793 struct efc_nport *nport = NULL, *nport_next = NULL;794 795 efc_sm_transition(ctx, __efc_domain_wait_nports_free,796 NULL);797 list_for_each_entry_safe(nport, nport_next,798 &domain->nport_list,799 list_entry) {800 efc_sm_post_event(&nport->sm,801 EFC_EVT_SHUTDOWN, NULL);802 }803 } else {804 /* no nports exist, free domain */805 efc_sm_transition(ctx, __efc_domain_wait_shutdown,806 NULL);807 if (efc_cmd_domain_free(efc, domain))808 efc_log_err(efc, "hw_domain_free() failed\n");809 }810 break;811 }812 case EFC_EVT_DOMAIN_ALLOC_FAIL:813 case EFC_EVT_DOMAIN_ATTACH_FAIL:814 efc_log_err(efc, "[domain] %-20s: failed\n",815 efc_sm_event_name(evt));816 break;817 818 default:819 __efc_domain_common_shutdown(__func__, ctx, evt, arg);820 }821}822 823void824__efc_domain_attach_internal(struct efc_domain *domain, u32 s_id)825{826 memcpy(domain->dma.virt,827 ((uint8_t *)domain->flogi_service_params) + 4,828 sizeof(struct fc_els_flogi) - 4);829 (void)efc_sm_post_event(&domain->drvsm, EFC_EVT_DOMAIN_REQ_ATTACH,830 &s_id);831}832 833void834efc_domain_attach(struct efc_domain *domain, u32 s_id)835{836 __efc_domain_attach_internal(domain, s_id);837}838 839int840efc_domain_post_event(struct efc_domain *domain,841 enum efc_sm_event event, void *arg)842{843 int rc;844 bool req_domain_free;845 846 rc = efc_sm_post_event(&domain->drvsm, event, arg);847 848 req_domain_free = domain->req_domain_free;849 domain->req_domain_free = false;850 851 if (req_domain_free)852 efc_domain_free(domain);853 854 return rc;855}856 857static void858efct_domain_process_pending(struct efc_domain *domain)859{860 struct efc *efc = domain->efc;861 struct efc_hw_sequence *seq = NULL;862 u32 processed = 0;863 unsigned long flags = 0;864 865 for (;;) {866 /* need to check for hold frames condition after each frame867 * processed because any given frame could cause a transition868 * to a state that holds frames869 */870 if (efc->hold_frames)871 break;872 873 /* Get next frame/sequence */874 spin_lock_irqsave(&efc->pend_frames_lock, flags);875 876 if (!list_empty(&efc->pend_frames)) {877 seq = list_first_entry(&efc->pend_frames,878 struct efc_hw_sequence, list_entry);879 list_del(&seq->list_entry);880 }881 882 if (!seq) {883 processed = efc->pend_frames_processed;884 efc->pend_frames_processed = 0;885 spin_unlock_irqrestore(&efc->pend_frames_lock, flags);886 break;887 }888 efc->pend_frames_processed++;889 890 spin_unlock_irqrestore(&efc->pend_frames_lock, flags);891 892 /* now dispatch frame(s) to dispatch function */893 if (efc_domain_dispatch_frame(domain, seq))894 efc->tt.hw_seq_free(efc, seq);895 896 seq = NULL;897 }898 899 if (processed != 0)900 efc_log_debug(efc, "%u domain frames held and processed\n",901 processed);902}903 904void905efc_dispatch_frame(struct efc *efc, struct efc_hw_sequence *seq)906{907 struct efc_domain *domain = efc->domain;908 909 /*910 * If we are holding frames or the domain is not yet registered or911 * there's already frames on the pending list,912 * then add the new frame to pending list913 */914 if (!domain || efc->hold_frames || !list_empty(&efc->pend_frames)) {915 unsigned long flags = 0;916 917 spin_lock_irqsave(&efc->pend_frames_lock, flags);918 INIT_LIST_HEAD(&seq->list_entry);919 list_add_tail(&seq->list_entry, &efc->pend_frames);920 spin_unlock_irqrestore(&efc->pend_frames_lock, flags);921 922 if (domain) {923 /* immediately process pending frames */924 efct_domain_process_pending(domain);925 }926 } else {927 /*928 * We are not holding frames and pending list is empty,929 * just process frame. A non-zero return means the frame930 * was not handled - so cleanup931 */932 if (efc_domain_dispatch_frame(domain, seq))933 efc->tt.hw_seq_free(efc, seq);934 }935}936 937int938efc_domain_dispatch_frame(void *arg, struct efc_hw_sequence *seq)939{940 struct efc_domain *domain = (struct efc_domain *)arg;941 struct efc *efc = domain->efc;942 struct fc_frame_header *hdr;943 struct efc_node *node = NULL;944 struct efc_nport *nport = NULL;945 unsigned long flags = 0;946 u32 s_id, d_id, rc = EFC_HW_SEQ_FREE;947 948 if (!seq->header || !seq->header->dma.virt || !seq->payload->dma.virt) {949 efc_log_err(efc, "Sequence header or payload is null\n");950 return rc;951 }952 953 hdr = seq->header->dma.virt;954 955 /* extract the s_id and d_id */956 s_id = ntoh24(hdr->fh_s_id);957 d_id = ntoh24(hdr->fh_d_id);958 959 spin_lock_irqsave(&efc->lock, flags);960 961 nport = efc_nport_find(domain, d_id);962 if (!nport) {963 if (hdr->fh_type == FC_TYPE_FCP) {964 /* Drop frame */965 efc_log_warn(efc, "FCP frame with invalid d_id x%x\n",966 d_id);967 goto out;968 }969 970 /* p2p will use this case */971 nport = domain->nport;972 if (!nport || !kref_get_unless_zero(&nport->ref)) {973 efc_log_err(efc, "Physical nport is NULL\n");974 goto out;975 }976 }977 978 /* Lookup the node given the remote s_id */979 node = efc_node_find(nport, s_id);980 981 /* If not found, then create a new node */982 if (!node) {983 /*984 * If this is solicited data or control based on R_CTL and985 * there is no node context, then we can drop the frame986 */987 if ((hdr->fh_r_ctl == FC_RCTL_DD_SOL_DATA) ||988 (hdr->fh_r_ctl == FC_RCTL_DD_SOL_CTL)) {989 efc_log_debug(efc, "sol data/ctrl frame without node\n");990 goto out_release;991 }992 993 node = efc_node_alloc(nport, s_id, false, false);994 if (!node) {995 efc_log_err(efc, "efc_node_alloc() failed\n");996 goto out_release;997 }998 /* don't send PLOGI on efc_d_init entry */999 efc_node_init_device(node, false);1000 }1001 1002 if (node->hold_frames || !list_empty(&node->pend_frames)) {1003 /* add frame to node's pending list */1004 spin_lock(&node->pend_frames_lock);1005 INIT_LIST_HEAD(&seq->list_entry);1006 list_add_tail(&seq->list_entry, &node->pend_frames);1007 spin_unlock(&node->pend_frames_lock);1008 rc = EFC_HW_SEQ_HOLD;1009 goto out_release;1010 }1011 1012 /* now dispatch frame to the node frame handler */1013 efc_node_dispatch_frame(node, seq);1014 1015out_release:1016 kref_put(&nport->ref, nport->release);1017out:1018 spin_unlock_irqrestore(&efc->lock, flags);1019 return rc;1020}1021 1022void1023efc_node_dispatch_frame(void *arg, struct efc_hw_sequence *seq)1024{1025 struct fc_frame_header *hdr = seq->header->dma.virt;1026 u32 port_id;1027 struct efc_node *node = (struct efc_node *)arg;1028 struct efc *efc = node->efc;1029 1030 port_id = ntoh24(hdr->fh_s_id);1031 1032 if (WARN_ON(port_id != node->rnode.fc_id))1033 return;1034 1035 if ((!(ntoh24(hdr->fh_f_ctl) & FC_FC_END_SEQ)) ||1036 !(ntoh24(hdr->fh_f_ctl) & FC_FC_SEQ_INIT)) {1037 node_printf(node,1038 "Drop frame hdr = %08x %08x %08x %08x %08x %08x\n",1039 cpu_to_be32(((u32 *)hdr)[0]),1040 cpu_to_be32(((u32 *)hdr)[1]),1041 cpu_to_be32(((u32 *)hdr)[2]),1042 cpu_to_be32(((u32 *)hdr)[3]),1043 cpu_to_be32(((u32 *)hdr)[4]),1044 cpu_to_be32(((u32 *)hdr)[5]));1045 return;1046 }1047 1048 switch (hdr->fh_r_ctl) {1049 case FC_RCTL_ELS_REQ:1050 case FC_RCTL_ELS_REP:1051 efc_node_recv_els_frame(node, seq);1052 break;1053 1054 case FC_RCTL_BA_ABTS:1055 case FC_RCTL_BA_ACC:1056 case FC_RCTL_BA_RJT:1057 case FC_RCTL_BA_NOP:1058 efc_log_err(efc, "Received ABTS:\n");1059 break;1060 1061 case FC_RCTL_DD_UNSOL_CMD:1062 case FC_RCTL_DD_UNSOL_CTL:1063 switch (hdr->fh_type) {1064 case FC_TYPE_FCP:1065 if ((hdr->fh_r_ctl & 0xf) == FC_RCTL_DD_UNSOL_CMD) {1066 if (!node->fcp_enabled) {1067 efc_node_recv_fcp_cmd(node, seq);1068 break;1069 }1070 efc_log_err(efc, "Recvd FCP CMD. Drop IO\n");1071 } else if ((hdr->fh_r_ctl & 0xf) ==1072 FC_RCTL_DD_SOL_DATA) {1073 node_printf(node,1074 "solicited data recvd. Drop IO\n");1075 }1076 break;1077 1078 case FC_TYPE_CT:1079 efc_node_recv_ct_frame(node, seq);1080 break;1081 default:1082 break;1083 }1084 break;1085 default:1086 efc_log_err(efc, "Unhandled frame rctl: %02x\n", hdr->fh_r_ctl);1087 }1088}1089