Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
0004  * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
0005  */
0006 
0007 /*
0008  * domain_sm Domain State Machine: States
0009  */
0010 
0011 #include "efc.h"
0012 
0013 int
0014 efc_domain_cb(void *arg, int event, void *data)
0015 {
0016     struct efc *efc = arg;
0017     struct efc_domain *domain = NULL;
0018     int rc = 0;
0019     unsigned long flags = 0;
0020 
0021     if (event != EFC_HW_DOMAIN_FOUND)
0022         domain = data;
0023 
0024     /* Accept domain callback events from the user driver */
0025     spin_lock_irqsave(&efc->lock, flags);
0026     switch (event) {
0027     case EFC_HW_DOMAIN_FOUND: {
0028         u64 fcf_wwn = 0;
0029         struct efc_domain_record *drec = data;
0030 
0031         /* extract the fcf_wwn */
0032         fcf_wwn = be64_to_cpu(*((__be64 *)drec->wwn));
0033 
0034         efc_log_debug(efc, "Domain found: wwn %016llX\n", fcf_wwn);
0035 
0036         /* lookup domain, or allocate a new one */
0037         domain = efc->domain;
0038         if (!domain) {
0039             domain = efc_domain_alloc(efc, fcf_wwn);
0040             if (!domain) {
0041                 efc_log_err(efc, "efc_domain_alloc() failed\n");
0042                 rc = -1;
0043                 break;
0044             }
0045             efc_sm_transition(&domain->drvsm, __efc_domain_init,
0046                       NULL);
0047         }
0048         efc_domain_post_event(domain, EFC_EVT_DOMAIN_FOUND, drec);
0049         break;
0050     }
0051 
0052     case EFC_HW_DOMAIN_LOST:
0053         domain_trace(domain, "EFC_HW_DOMAIN_LOST:\n");
0054         efc->hold_frames = true;
0055         efc_domain_post_event(domain, EFC_EVT_DOMAIN_LOST, NULL);
0056         break;
0057 
0058     case EFC_HW_DOMAIN_ALLOC_OK:
0059         domain_trace(domain, "EFC_HW_DOMAIN_ALLOC_OK:\n");
0060         efc_domain_post_event(domain, EFC_EVT_DOMAIN_ALLOC_OK, NULL);
0061         break;
0062 
0063     case EFC_HW_DOMAIN_ALLOC_FAIL:
0064         domain_trace(domain, "EFC_HW_DOMAIN_ALLOC_FAIL:\n");
0065         efc_domain_post_event(domain, EFC_EVT_DOMAIN_ALLOC_FAIL,
0066                       NULL);
0067         break;
0068 
0069     case EFC_HW_DOMAIN_ATTACH_OK:
0070         domain_trace(domain, "EFC_HW_DOMAIN_ATTACH_OK:\n");
0071         efc_domain_post_event(domain, EFC_EVT_DOMAIN_ATTACH_OK, NULL);
0072         break;
0073 
0074     case EFC_HW_DOMAIN_ATTACH_FAIL:
0075         domain_trace(domain, "EFC_HW_DOMAIN_ATTACH_FAIL:\n");
0076         efc_domain_post_event(domain,
0077                       EFC_EVT_DOMAIN_ATTACH_FAIL, NULL);
0078         break;
0079 
0080     case EFC_HW_DOMAIN_FREE_OK:
0081         domain_trace(domain, "EFC_HW_DOMAIN_FREE_OK:\n");
0082         efc_domain_post_event(domain, EFC_EVT_DOMAIN_FREE_OK, NULL);
0083         break;
0084 
0085     case EFC_HW_DOMAIN_FREE_FAIL:
0086         domain_trace(domain, "EFC_HW_DOMAIN_FREE_FAIL:\n");
0087         efc_domain_post_event(domain, EFC_EVT_DOMAIN_FREE_FAIL, NULL);
0088         break;
0089 
0090     default:
0091         efc_log_warn(efc, "unsupported event %#x\n", event);
0092     }
0093     spin_unlock_irqrestore(&efc->lock, flags);
0094 
0095     if (efc->domain && domain->req_accept_frames) {
0096         domain->req_accept_frames = false;
0097         efc->hold_frames = false;
0098     }
0099 
0100     return rc;
0101 }
0102 
0103 static void
0104 _efc_domain_free(struct kref *arg)
0105 {
0106     struct efc_domain *domain = container_of(arg, struct efc_domain, ref);
0107     struct efc *efc = domain->efc;
0108 
0109     if (efc->domain_free_cb)
0110         (*efc->domain_free_cb)(efc, efc->domain_free_cb_arg);
0111 
0112     kfree(domain);
0113 }
0114 
0115 void
0116 efc_domain_free(struct efc_domain *domain)
0117 {
0118     struct efc *efc;
0119 
0120     efc = domain->efc;
0121 
0122     /* Hold frames to clear the domain pointer from the xport lookup */
0123     efc->hold_frames = false;
0124 
0125     efc_log_debug(efc, "Domain free: wwn %016llX\n", domain->fcf_wwn);
0126 
0127     xa_destroy(&domain->lookup);
0128     efc->domain = NULL;
0129     kref_put(&domain->ref, domain->release);
0130 }
0131 
0132 struct efc_domain *
0133 efc_domain_alloc(struct efc *efc, uint64_t fcf_wwn)
0134 {
0135     struct efc_domain *domain;
0136 
0137     domain = kzalloc(sizeof(*domain), GFP_ATOMIC);
0138     if (!domain)
0139         return NULL;
0140 
0141     domain->efc = efc;
0142     domain->drvsm.app = domain;
0143 
0144     /* initialize refcount */
0145     kref_init(&domain->ref);
0146     domain->release = _efc_domain_free;
0147 
0148     xa_init(&domain->lookup);
0149 
0150     INIT_LIST_HEAD(&domain->nport_list);
0151     efc->domain = domain;
0152     domain->fcf_wwn = fcf_wwn;
0153     efc_log_debug(efc, "Domain allocated: wwn %016llX\n", domain->fcf_wwn);
0154 
0155     return domain;
0156 }
0157 
0158 void
0159 efc_register_domain_free_cb(struct efc *efc,
0160                 void (*callback)(struct efc *efc, void *arg),
0161                 void *arg)
0162 {
0163     /* Register a callback to be called when the domain is freed */
0164     efc->domain_free_cb = callback;
0165     efc->domain_free_cb_arg = arg;
0166     if (!efc->domain && callback)
0167         (*callback)(efc, arg);
0168 }
0169 
0170 static void
0171 __efc_domain_common(const char *funcname, struct efc_sm_ctx *ctx,
0172             enum efc_sm_event evt, void *arg)
0173 {
0174     struct efc_domain *domain = ctx->app;
0175 
0176     switch (evt) {
0177     case EFC_EVT_ENTER:
0178     case EFC_EVT_REENTER:
0179     case EFC_EVT_EXIT:
0180     case EFC_EVT_ALL_CHILD_NODES_FREE:
0181         /*
0182          * this can arise if an FLOGI fails on the NPORT,
0183          * and the NPORT is shutdown
0184          */
0185         break;
0186     default:
0187         efc_log_warn(domain->efc, "%-20s %-20s not handled\n",
0188                  funcname, efc_sm_event_name(evt));
0189     }
0190 }
0191 
0192 static void
0193 __efc_domain_common_shutdown(const char *funcname, struct efc_sm_ctx *ctx,
0194                  enum efc_sm_event evt, void *arg)
0195 {
0196     struct efc_domain *domain = ctx->app;
0197 
0198     switch (evt) {
0199     case EFC_EVT_ENTER:
0200     case EFC_EVT_REENTER:
0201     case EFC_EVT_EXIT:
0202         break;
0203     case EFC_EVT_DOMAIN_FOUND:
0204         /* save drec, mark domain_found_pending */
0205         memcpy(&domain->pending_drec, arg,
0206                sizeof(domain->pending_drec));
0207         domain->domain_found_pending = true;
0208         break;
0209     case EFC_EVT_DOMAIN_LOST:
0210         /* unmark domain_found_pending */
0211         domain->domain_found_pending = false;
0212         break;
0213 
0214     default:
0215         efc_log_warn(domain->efc, "%-20s %-20s not handled\n",
0216                  funcname, efc_sm_event_name(evt));
0217     }
0218 }
0219 
0220 #define std_domain_state_decl(...)\
0221     struct efc_domain *domain = NULL;\
0222     struct efc *efc = NULL;\
0223     \
0224     WARN_ON(!ctx || !ctx->app);\
0225     domain = ctx->app;\
0226     WARN_ON(!domain->efc);\
0227     efc = domain->efc
0228 
0229 void
0230 __efc_domain_init(struct efc_sm_ctx *ctx, enum efc_sm_event evt,
0231           void *arg)
0232 {
0233     std_domain_state_decl();
0234 
0235     domain_sm_trace(domain);
0236 
0237     switch (evt) {
0238     case EFC_EVT_ENTER:
0239         domain->attached = false;
0240         break;
0241 
0242     case EFC_EVT_DOMAIN_FOUND: {
0243         u32 i;
0244         struct efc_domain_record *drec = arg;
0245         struct efc_nport *nport;
0246 
0247         u64 my_wwnn = efc->req_wwnn;
0248         u64 my_wwpn = efc->req_wwpn;
0249         __be64 bewwpn;
0250 
0251         if (my_wwpn == 0 || my_wwnn == 0) {
0252             efc_log_debug(efc, "using default hardware WWN config\n");
0253             my_wwpn = efc->def_wwpn;
0254             my_wwnn = efc->def_wwnn;
0255         }
0256 
0257         efc_log_debug(efc, "Create nport WWPN %016llX WWNN %016llX\n",
0258                   my_wwpn, my_wwnn);
0259 
0260         /* Allocate a nport and transition to __efc_nport_allocated */
0261         nport = efc_nport_alloc(domain, my_wwpn, my_wwnn, U32_MAX,
0262                     efc->enable_ini, efc->enable_tgt);
0263 
0264         if (!nport) {
0265             efc_log_err(efc, "efc_nport_alloc() failed\n");
0266             break;
0267         }
0268         efc_sm_transition(&nport->sm, __efc_nport_allocated, NULL);
0269 
0270         bewwpn = cpu_to_be64(nport->wwpn);
0271 
0272         /* allocate struct efc_nport object for local port
0273          * Note: drec->fc_id is ALPA from read_topology only if loop
0274          */
0275         if (efc_cmd_nport_alloc(efc, nport, NULL, (uint8_t *)&bewwpn)) {
0276             efc_log_err(efc, "Can't allocate port\n");
0277             efc_nport_free(nport);
0278             break;
0279         }
0280 
0281         domain->is_loop = drec->is_loop;
0282 
0283         /*
0284          * If the loop position map includes ALPA == 0,
0285          * then we are in a public loop (NL_PORT)
0286          * Note that the first element of the loopmap[]
0287          * contains the count of elements, and if
0288          * ALPA == 0 is present, it will occupy the first
0289          * location after the count.
0290          */
0291         domain->is_nlport = drec->map.loop[1] == 0x00;
0292 
0293         if (!domain->is_loop) {
0294             /* Initiate HW domain alloc */
0295             if (efc_cmd_domain_alloc(efc, domain, drec->index)) {
0296                 efc_log_err(efc,
0297                         "Failed to initiate HW domain allocation\n");
0298                 break;
0299             }
0300             efc_sm_transition(ctx, __efc_domain_wait_alloc, arg);
0301             break;
0302         }
0303 
0304         efc_log_debug(efc, "%s fc_id=%#x speed=%d\n",
0305                   drec->is_loop ?
0306                   (domain->is_nlport ?
0307                   "public-loop" : "loop") : "other",
0308                   drec->fc_id, drec->speed);
0309 
0310         nport->fc_id = drec->fc_id;
0311         nport->topology = EFC_NPORT_TOPO_FC_AL;
0312         snprintf(nport->display_name, sizeof(nport->display_name),
0313              "s%06x", drec->fc_id);
0314 
0315         if (efc->enable_ini) {
0316             u32 count = drec->map.loop[0];
0317 
0318             efc_log_debug(efc, "%d position map entries\n",
0319                       count);
0320             for (i = 1; i <= count; i++) {
0321                 if (drec->map.loop[i] != drec->fc_id) {
0322                     struct efc_node *node;
0323 
0324                     efc_log_debug(efc, "%#x -> %#x\n",
0325                               drec->fc_id,
0326                               drec->map.loop[i]);
0327                     node = efc_node_alloc(nport,
0328                                   drec->map.loop[i],
0329                                   false, true);
0330                     if (!node) {
0331                         efc_log_err(efc,
0332                                 "efc_node_alloc() failed\n");
0333                         break;
0334                     }
0335                     efc_node_transition(node,
0336                                 __efc_d_wait_loop,
0337                                 NULL);
0338                 }
0339             }
0340         }
0341 
0342         /* Initiate HW domain alloc */
0343         if (efc_cmd_domain_alloc(efc, domain, drec->index)) {
0344             efc_log_err(efc,
0345                     "Failed to initiate HW domain allocation\n");
0346             break;
0347         }
0348         efc_sm_transition(ctx, __efc_domain_wait_alloc, arg);
0349         break;
0350     }
0351     default:
0352         __efc_domain_common(__func__, ctx, evt, arg);
0353     }
0354 }
0355 
0356 void
0357 __efc_domain_wait_alloc(struct efc_sm_ctx *ctx,
0358             enum efc_sm_event evt, void *arg)
0359 {
0360     std_domain_state_decl();
0361 
0362     domain_sm_trace(domain);
0363 
0364     switch (evt) {
0365     case EFC_EVT_DOMAIN_ALLOC_OK: {
0366         struct fc_els_flogi  *sp;
0367         struct efc_nport *nport;
0368 
0369         nport = domain->nport;
0370         if (WARN_ON(!nport))
0371             return;
0372 
0373         sp = (struct fc_els_flogi  *)nport->service_params;
0374 
0375         /* Save the domain service parameters */
0376         memcpy(domain->service_params + 4, domain->dma.virt,
0377                sizeof(struct fc_els_flogi) - 4);
0378         memcpy(nport->service_params + 4, domain->dma.virt,
0379                sizeof(struct fc_els_flogi) - 4);
0380 
0381         /*
0382          * Update the nport's service parameters,
0383          * user might have specified non-default names
0384          */
0385         sp->fl_wwpn = cpu_to_be64(nport->wwpn);
0386         sp->fl_wwnn = cpu_to_be64(nport->wwnn);
0387 
0388         /*
0389          * Take the loop topology path,
0390          * unless we are an NL_PORT (public loop)
0391          */
0392         if (domain->is_loop && !domain->is_nlport) {
0393             /*
0394              * For loop, we already have our FC ID
0395              * and don't need fabric login.
0396              * Transition to the allocated state and
0397              * post an event to attach to
0398              * the domain. Note that this breaks the
0399              * normal action/transition
0400              * pattern here to avoid a race with the
0401              * domain attach callback.
0402              */
0403             /* sm: is_loop / domain_attach */
0404             efc_sm_transition(ctx, __efc_domain_allocated, NULL);
0405             __efc_domain_attach_internal(domain, nport->fc_id);
0406             break;
0407         }
0408         {
0409             struct efc_node *node;
0410 
0411             /* alloc fabric node, send FLOGI */
0412             node = efc_node_find(nport, FC_FID_FLOGI);
0413             if (node) {
0414                 efc_log_err(efc,
0415                         "Fabric Controller node already exists\n");
0416                 break;
0417             }
0418             node = efc_node_alloc(nport, FC_FID_FLOGI,
0419                           false, false);
0420             if (!node) {
0421                 efc_log_err(efc,
0422                         "Error: efc_node_alloc() failed\n");
0423             } else {
0424                 efc_node_transition(node,
0425                             __efc_fabric_init, NULL);
0426             }
0427             /* Accept frames */
0428             domain->req_accept_frames = true;
0429         }
0430         /* sm: / start fabric logins */
0431         efc_sm_transition(ctx, __efc_domain_allocated, NULL);
0432         break;
0433     }
0434 
0435     case EFC_EVT_DOMAIN_ALLOC_FAIL:
0436         efc_log_err(efc, "%s recv'd waiting for DOMAIN_ALLOC_OK;",
0437                 efc_sm_event_name(evt));
0438         efc_log_err(efc, "shutting down domain\n");
0439         domain->req_domain_free = true;
0440         break;
0441 
0442     case EFC_EVT_DOMAIN_FOUND:
0443         /* Should not happen */
0444         break;
0445 
0446     case EFC_EVT_DOMAIN_LOST:
0447         efc_log_debug(efc,
0448                   "%s received while waiting for hw_domain_alloc()\n",
0449             efc_sm_event_name(evt));
0450         efc_sm_transition(ctx, __efc_domain_wait_domain_lost, NULL);
0451         break;
0452 
0453     default:
0454         __efc_domain_common(__func__, ctx, evt, arg);
0455     }
0456 }
0457 
0458 void
0459 __efc_domain_allocated(struct efc_sm_ctx *ctx,
0460                enum efc_sm_event evt, void *arg)
0461 {
0462     std_domain_state_decl();
0463 
0464     domain_sm_trace(domain);
0465 
0466     switch (evt) {
0467     case EFC_EVT_DOMAIN_REQ_ATTACH: {
0468         int rc = 0;
0469         u32 fc_id;
0470 
0471         if (WARN_ON(!arg))
0472             return;
0473 
0474         fc_id = *((u32 *)arg);
0475         efc_log_debug(efc, "Requesting hw domain attach fc_id x%x\n",
0476                   fc_id);
0477         /* Update nport lookup */
0478         rc = xa_err(xa_store(&domain->lookup, fc_id, domain->nport,
0479                      GFP_ATOMIC));
0480         if (rc) {
0481             efc_log_err(efc, "Sport lookup store failed: %d\n", rc);
0482             return;
0483         }
0484 
0485         /* Update display name for the nport */
0486         efc_node_fcid_display(fc_id, domain->nport->display_name,
0487                       sizeof(domain->nport->display_name));
0488 
0489         /* Issue domain attach call */
0490         rc = efc_cmd_domain_attach(efc, domain, fc_id);
0491         if (rc) {
0492             efc_log_err(efc, "efc_hw_domain_attach failed: %d\n",
0493                     rc);
0494             return;
0495         }
0496         /* sm: / domain_attach */
0497         efc_sm_transition(ctx, __efc_domain_wait_attach, NULL);
0498         break;
0499     }
0500 
0501     case EFC_EVT_DOMAIN_FOUND:
0502         /* Should not happen */
0503         efc_log_err(efc, "%s: evt: %d should not happen\n",
0504                 __func__, evt);
0505         break;
0506 
0507     case EFC_EVT_DOMAIN_LOST: {
0508         efc_log_debug(efc,
0509                   "%s received while in EFC_EVT_DOMAIN_REQ_ATTACH\n",
0510             efc_sm_event_name(evt));
0511         if (!list_empty(&domain->nport_list)) {
0512             /*
0513              * if there are nports, transition to
0514              * wait state and send shutdown to each
0515              * nport
0516              */
0517             struct efc_nport *nport = NULL, *nport_next = NULL;
0518 
0519             efc_sm_transition(ctx, __efc_domain_wait_nports_free,
0520                       NULL);
0521             list_for_each_entry_safe(nport, nport_next,
0522                          &domain->nport_list,
0523                          list_entry) {
0524                 efc_sm_post_event(&nport->sm,
0525                           EFC_EVT_SHUTDOWN, NULL);
0526             }
0527         } else {
0528             /* no nports exist, free domain */
0529             efc_sm_transition(ctx, __efc_domain_wait_shutdown,
0530                       NULL);
0531             if (efc_cmd_domain_free(efc, domain))
0532                 efc_log_err(efc, "hw_domain_free failed\n");
0533         }
0534 
0535         break;
0536     }
0537 
0538     default:
0539         __efc_domain_common(__func__, ctx, evt, arg);
0540     }
0541 }
0542 
0543 void
0544 __efc_domain_wait_attach(struct efc_sm_ctx *ctx,
0545              enum efc_sm_event evt, void *arg)
0546 {
0547     std_domain_state_decl();
0548 
0549     domain_sm_trace(domain);
0550 
0551     switch (evt) {
0552     case EFC_EVT_DOMAIN_ATTACH_OK: {
0553         struct efc_node *node = NULL;
0554         struct efc_nport *nport, *next_nport;
0555         unsigned long index;
0556 
0557         /*
0558          * Set domain notify pending state to avoid
0559          * duplicate domain event post
0560          */
0561         domain->domain_notify_pend = true;
0562 
0563         /* Mark as attached */
0564         domain->attached = true;
0565 
0566         /* Transition to ready */
0567         /* sm: / forward event to all nports and nodes */
0568         efc_sm_transition(ctx, __efc_domain_ready, NULL);
0569 
0570         /* We have an FCFI, so we can accept frames */
0571         domain->req_accept_frames = true;
0572 
0573         /*
0574          * Notify all nodes that the domain attach request
0575          * has completed
0576          * Note: nport will have already received notification
0577          * of nport attached as a result of the HW's port attach.
0578          */
0579         list_for_each_entry_safe(nport, next_nport,
0580                      &domain->nport_list, list_entry) {
0581             xa_for_each(&nport->lookup, index, node) {
0582                 efc_node_post_event(node,
0583                             EFC_EVT_DOMAIN_ATTACH_OK,
0584                             NULL);
0585             }
0586         }
0587         domain->domain_notify_pend = false;
0588         break;
0589     }
0590 
0591     case EFC_EVT_DOMAIN_ATTACH_FAIL:
0592         efc_log_debug(efc,
0593                   "%s received while waiting for hw attach\n",
0594                   efc_sm_event_name(evt));
0595         break;
0596 
0597     case EFC_EVT_DOMAIN_FOUND:
0598         /* Should not happen */
0599         efc_log_err(efc, "%s: evt: %d should not happen\n",
0600                 __func__, evt);
0601         break;
0602 
0603     case EFC_EVT_DOMAIN_LOST:
0604         /*
0605          * Domain lost while waiting for an attach to complete,
0606          * go to a state that waits for  the domain attach to
0607          * complete, then handle domain lost
0608          */
0609         efc_sm_transition(ctx, __efc_domain_wait_domain_lost, NULL);
0610         break;
0611 
0612     case EFC_EVT_DOMAIN_REQ_ATTACH:
0613         /*
0614          * In P2P we can get an attach request from
0615          * the other FLOGI path, so drop this one
0616          */
0617         break;
0618 
0619     default:
0620         __efc_domain_common(__func__, ctx, evt, arg);
0621     }
0622 }
0623 
0624 void
0625 __efc_domain_ready(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg)
0626 {
0627     std_domain_state_decl();
0628 
0629     domain_sm_trace(domain);
0630 
0631     switch (evt) {
0632     case EFC_EVT_ENTER: {
0633         /* start any pending vports */
0634         if (efc_vport_start(domain)) {
0635             efc_log_debug(domain->efc,
0636                       "efc_vport_start didn't start vports\n");
0637         }
0638         break;
0639     }
0640     case EFC_EVT_DOMAIN_LOST: {
0641         if (!list_empty(&domain->nport_list)) {
0642             /*
0643              * if there are nports, transition to wait state
0644              * and send shutdown to each nport
0645              */
0646             struct efc_nport *nport = NULL, *nport_next = NULL;
0647 
0648             efc_sm_transition(ctx, __efc_domain_wait_nports_free,
0649                       NULL);
0650             list_for_each_entry_safe(nport, nport_next,
0651                          &domain->nport_list,
0652                          list_entry) {
0653                 efc_sm_post_event(&nport->sm,
0654                           EFC_EVT_SHUTDOWN, NULL);
0655             }
0656         } else {
0657             /* no nports exist, free domain */
0658             efc_sm_transition(ctx, __efc_domain_wait_shutdown,
0659                       NULL);
0660             if (efc_cmd_domain_free(efc, domain))
0661                 efc_log_err(efc, "hw_domain_free failed\n");
0662         }
0663         break;
0664     }
0665 
0666     case EFC_EVT_DOMAIN_FOUND:
0667         /* Should not happen */
0668         efc_log_err(efc, "%s: evt: %d should not happen\n",
0669                 __func__, evt);
0670         break;
0671 
0672     case EFC_EVT_DOMAIN_REQ_ATTACH: {
0673         /* can happen during p2p */
0674         u32 fc_id;
0675 
0676         fc_id = *((u32 *)arg);
0677 
0678         /* Assume that the domain is attached */
0679         WARN_ON(!domain->attached);
0680 
0681         /*
0682          * Verify that the requested FC_ID
0683          * is the same as the one we're working with
0684          */
0685         WARN_ON(domain->nport->fc_id != fc_id);
0686         break;
0687     }
0688 
0689     default:
0690         __efc_domain_common(__func__, ctx, evt, arg);
0691     }
0692 }
0693 
0694 void
0695 __efc_domain_wait_nports_free(struct efc_sm_ctx *ctx, enum efc_sm_event evt,
0696                   void *arg)
0697 {
0698     std_domain_state_decl();
0699 
0700     domain_sm_trace(domain);
0701 
0702     /* Wait for nodes to free prior to the domain shutdown */
0703     switch (evt) {
0704     case EFC_EVT_ALL_CHILD_NODES_FREE: {
0705         int rc;
0706 
0707         /* sm: / efc_hw_domain_free */
0708         efc_sm_transition(ctx, __efc_domain_wait_shutdown, NULL);
0709 
0710         /* Request efc_hw_domain_free and wait for completion */
0711         rc = efc_cmd_domain_free(efc, domain);
0712         if (rc) {
0713             efc_log_err(efc, "efc_hw_domain_free() failed: %d\n",
0714                     rc);
0715         }
0716         break;
0717     }
0718     default:
0719         __efc_domain_common_shutdown(__func__, ctx, evt, arg);
0720     }
0721 }
0722 
0723 void
0724 __efc_domain_wait_shutdown(struct efc_sm_ctx *ctx,
0725                enum efc_sm_event evt, void *arg)
0726 {
0727     std_domain_state_decl();
0728 
0729     domain_sm_trace(domain);
0730 
0731     switch (evt) {
0732     case EFC_EVT_DOMAIN_FREE_OK:
0733         /* sm: / domain_free */
0734         if (domain->domain_found_pending) {
0735             /*
0736              * save fcf_wwn and drec from this domain,
0737              * free current domain and allocate
0738              * a new one with the same fcf_wwn
0739              * could use a SLI-4 "re-register VPI"
0740              * operation here?
0741              */
0742             u64 fcf_wwn = domain->fcf_wwn;
0743             struct efc_domain_record drec = domain->pending_drec;
0744 
0745             efc_log_debug(efc, "Reallocating domain\n");
0746             domain->req_domain_free = true;
0747             domain = efc_domain_alloc(efc, fcf_wwn);
0748 
0749             if (!domain) {
0750                 efc_log_err(efc,
0751                         "efc_domain_alloc() failed\n");
0752                 return;
0753             }
0754             /*
0755              * got a new domain; at this point,
0756              * there are at least two domains
0757              * once the req_domain_free flag is processed,
0758              * the associated domain will be removed.
0759              */
0760             efc_sm_transition(&domain->drvsm, __efc_domain_init,
0761                       NULL);
0762             efc_sm_post_event(&domain->drvsm,
0763                       EFC_EVT_DOMAIN_FOUND, &drec);
0764         } else {
0765             domain->req_domain_free = true;
0766         }
0767         break;
0768     default:
0769         __efc_domain_common_shutdown(__func__, ctx, evt, arg);
0770     }
0771 }
0772 
0773 void
0774 __efc_domain_wait_domain_lost(struct efc_sm_ctx *ctx,
0775                   enum efc_sm_event evt, void *arg)
0776 {
0777     std_domain_state_decl();
0778 
0779     domain_sm_trace(domain);
0780 
0781     /*
0782      * Wait for the domain alloc/attach completion
0783      * after receiving a domain lost.
0784      */
0785     switch (evt) {
0786     case EFC_EVT_DOMAIN_ALLOC_OK:
0787     case EFC_EVT_DOMAIN_ATTACH_OK: {
0788         if (!list_empty(&domain->nport_list)) {
0789             /*
0790              * if there are nports, transition to
0791              * wait state and send shutdown to each nport
0792              */
0793             struct efc_nport *nport = NULL, *nport_next = NULL;
0794 
0795             efc_sm_transition(ctx, __efc_domain_wait_nports_free,
0796                       NULL);
0797             list_for_each_entry_safe(nport, nport_next,
0798                          &domain->nport_list,
0799                          list_entry) {
0800                 efc_sm_post_event(&nport->sm,
0801                           EFC_EVT_SHUTDOWN, NULL);
0802             }
0803         } else {
0804             /* no nports exist, free domain */
0805             efc_sm_transition(ctx, __efc_domain_wait_shutdown,
0806                       NULL);
0807             if (efc_cmd_domain_free(efc, domain))
0808                 efc_log_err(efc, "hw_domain_free() failed\n");
0809         }
0810         break;
0811     }
0812     case EFC_EVT_DOMAIN_ALLOC_FAIL:
0813     case EFC_EVT_DOMAIN_ATTACH_FAIL:
0814         efc_log_err(efc, "[domain] %-20s: failed\n",
0815                 efc_sm_event_name(evt));
0816         break;
0817 
0818     default:
0819         __efc_domain_common_shutdown(__func__, ctx, evt, arg);
0820     }
0821 }
0822 
0823 void
0824 __efc_domain_attach_internal(struct efc_domain *domain, u32 s_id)
0825 {
0826     memcpy(domain->dma.virt,
0827            ((uint8_t *)domain->flogi_service_params) + 4,
0828            sizeof(struct fc_els_flogi) - 4);
0829     (void)efc_sm_post_event(&domain->drvsm, EFC_EVT_DOMAIN_REQ_ATTACH,
0830                  &s_id);
0831 }
0832 
0833 void
0834 efc_domain_attach(struct efc_domain *domain, u32 s_id)
0835 {
0836     __efc_domain_attach_internal(domain, s_id);
0837 }
0838 
0839 int
0840 efc_domain_post_event(struct efc_domain *domain,
0841               enum efc_sm_event event, void *arg)
0842 {
0843     int rc;
0844     bool req_domain_free;
0845 
0846     rc = efc_sm_post_event(&domain->drvsm, event, arg);
0847 
0848     req_domain_free = domain->req_domain_free;
0849     domain->req_domain_free = false;
0850 
0851     if (req_domain_free)
0852         efc_domain_free(domain);
0853 
0854     return rc;
0855 }
0856 
0857 static void
0858 efct_domain_process_pending(struct efc_domain *domain)
0859 {
0860     struct efc *efc = domain->efc;
0861     struct efc_hw_sequence *seq = NULL;
0862     u32 processed = 0;
0863     unsigned long flags = 0;
0864 
0865     for (;;) {
0866         /* need to check for hold frames condition after each frame
0867          * processed because any given frame could cause a transition
0868          * to a state that holds frames
0869          */
0870         if (efc->hold_frames)
0871             break;
0872 
0873         /* Get next frame/sequence */
0874         spin_lock_irqsave(&efc->pend_frames_lock, flags);
0875 
0876         if (!list_empty(&efc->pend_frames)) {
0877             seq = list_first_entry(&efc->pend_frames,
0878                     struct efc_hw_sequence, list_entry);
0879             list_del(&seq->list_entry);
0880         }
0881 
0882         if (!seq) {
0883             processed = efc->pend_frames_processed;
0884             efc->pend_frames_processed = 0;
0885             spin_unlock_irqrestore(&efc->pend_frames_lock, flags);
0886             break;
0887         }
0888         efc->pend_frames_processed++;
0889 
0890         spin_unlock_irqrestore(&efc->pend_frames_lock, flags);
0891 
0892         /* now dispatch frame(s) to dispatch function */
0893         if (efc_domain_dispatch_frame(domain, seq))
0894             efc->tt.hw_seq_free(efc, seq);
0895 
0896         seq = NULL;
0897     }
0898 
0899     if (processed != 0)
0900         efc_log_debug(efc, "%u domain frames held and processed\n",
0901                   processed);
0902 }
0903 
0904 void
0905 efc_dispatch_frame(struct efc *efc, struct efc_hw_sequence *seq)
0906 {
0907     struct efc_domain *domain = efc->domain;
0908 
0909     /*
0910      * If we are holding frames or the domain is not yet registered or
0911      * there's already frames on the pending list,
0912      * then add the new frame to pending list
0913      */
0914     if (!domain || efc->hold_frames || !list_empty(&efc->pend_frames)) {
0915         unsigned long flags = 0;
0916 
0917         spin_lock_irqsave(&efc->pend_frames_lock, flags);
0918         INIT_LIST_HEAD(&seq->list_entry);
0919         list_add_tail(&seq->list_entry, &efc->pend_frames);
0920         spin_unlock_irqrestore(&efc->pend_frames_lock, flags);
0921 
0922         if (domain) {
0923             /* immediately process pending frames */
0924             efct_domain_process_pending(domain);
0925         }
0926     } else {
0927         /*
0928          * We are not holding frames and pending list is empty,
0929          * just process frame. A non-zero return means the frame
0930          * was not handled - so cleanup
0931          */
0932         if (efc_domain_dispatch_frame(domain, seq))
0933             efc->tt.hw_seq_free(efc, seq);
0934     }
0935 }
0936 
0937 int
0938 efc_domain_dispatch_frame(void *arg, struct efc_hw_sequence *seq)
0939 {
0940     struct efc_domain *domain = (struct efc_domain *)arg;
0941     struct efc *efc = domain->efc;
0942     struct fc_frame_header *hdr;
0943     struct efc_node *node = NULL;
0944     struct efc_nport *nport = NULL;
0945     unsigned long flags = 0;
0946     u32 s_id, d_id, rc = EFC_HW_SEQ_FREE;
0947 
0948     if (!seq->header || !seq->header->dma.virt || !seq->payload->dma.virt) {
0949         efc_log_err(efc, "Sequence header or payload is null\n");
0950         return rc;
0951     }
0952 
0953     hdr = seq->header->dma.virt;
0954 
0955     /* extract the s_id and d_id */
0956     s_id = ntoh24(hdr->fh_s_id);
0957     d_id = ntoh24(hdr->fh_d_id);
0958 
0959     spin_lock_irqsave(&efc->lock, flags);
0960 
0961     nport = efc_nport_find(domain, d_id);
0962     if (!nport) {
0963         if (hdr->fh_type == FC_TYPE_FCP) {
0964             /* Drop frame */
0965             efc_log_warn(efc, "FCP frame with invalid d_id x%x\n",
0966                      d_id);
0967             goto out;
0968         }
0969 
0970         /* p2p will use this case */
0971         nport = domain->nport;
0972         if (!nport || !kref_get_unless_zero(&nport->ref)) {
0973             efc_log_err(efc, "Physical nport is NULL\n");
0974             goto out;
0975         }
0976     }
0977 
0978     /* Lookup the node given the remote s_id */
0979     node = efc_node_find(nport, s_id);
0980 
0981     /* If not found, then create a new node */
0982     if (!node) {
0983         /*
0984          * If this is solicited data or control based on R_CTL and
0985          * there is no node context, then we can drop the frame
0986          */
0987         if ((hdr->fh_r_ctl == FC_RCTL_DD_SOL_DATA) ||
0988             (hdr->fh_r_ctl == FC_RCTL_DD_SOL_CTL)) {
0989             efc_log_debug(efc, "sol data/ctrl frame without node\n");
0990             goto out_release;
0991         }
0992 
0993         node = efc_node_alloc(nport, s_id, false, false);
0994         if (!node) {
0995             efc_log_err(efc, "efc_node_alloc() failed\n");
0996             goto out_release;
0997         }
0998         /* don't send PLOGI on efc_d_init entry */
0999         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 
1015 out_release:
1016     kref_put(&nport->ref, nport->release);
1017 out:
1018     spin_unlock_irqrestore(&efc->lock, flags);
1019     return rc;
1020 }
1021 
1022 void
1023 efc_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 }