Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/scsi/elx/libefc/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 18 kB image not shown  

Quelle  efc_nport.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
 * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
 */


/*
 * NPORT
 *
 * Port object for physical port and NPIV ports.
 */


/*
 * NPORT REFERENCE COUNTING
 *
 * A nport reference should be taken when:
 * - an nport is allocated
 * - a vport populates associated nport
 * - a remote node is allocated
 * - a unsolicited frame is processed
 * The reference should be dropped when:
 * - the unsolicited frame processesing is done
 * - the remote node is removed
 * - the vport is removed
 * - the nport is removed
 */


#include "efc.h"

void
efc_nport_cb(void *arg, int event, void *data)
{
 struct efc *efc = arg;
 struct efc_nport *nport = data;
 unsigned long flags = 0;

 efc_log_debug(efc, "nport event: %s\n", efc_sm_event_name(event));

 spin_lock_irqsave(&efc->lock, flags);
 efc_sm_post_event(&nport->sm, event, NULL);
 spin_unlock_irqrestore(&efc->lock, flags);
}

static struct efc_nport *
efc_nport_find_wwn(struct efc_domain *domain, uint64_t wwnn, uint64_t wwpn)
{
 struct efc_nport *nport = NULL;

 /* Find a nport, given the WWNN and WWPN */
 list_for_each_entry(nport, &domain->nport_list, list_entry) {
  if (nport->wwnn == wwnn && nport->wwpn == wwpn)
   return nport;
 }
 return NULL;
}

static void
_efc_nport_free(struct kref *arg)
{
 struct efc_nport *nport = container_of(arg, struct efc_nport, ref);

 kfree(nport);
}

struct efc_nport *
efc_nport_alloc(struct efc_domain *domain, uint64_t wwpn, uint64_t wwnn,
  u32 fc_id, bool enable_ini, bool enable_tgt)
{
 struct efc_nport *nport;

 if (domain->efc->enable_ini)
  enable_ini = 0;

 /* Return a failure if this nport has already been allocated */
 if ((wwpn != 0) || (wwnn != 0)) {
  nport = efc_nport_find_wwn(domain, wwnn, wwpn);
  if (nport) {
   efc_log_err(domain->efc,
        "NPORT %016llX %016llX already allocated\n",
        wwnn, wwpn);
   return NULL;
  }
 }

 nport = kzalloc(sizeof(*nport), GFP_ATOMIC);
 if (!nport)
  return nport;

 /* initialize refcount */
 kref_init(&nport->ref);
 nport->release = _efc_nport_free;

 nport->efc = domain->efc;
 snprintf(nport->display_name, sizeof(nport->display_name), "------");
 nport->domain = domain;
 xa_init(&nport->lookup);
 nport->instance_index = domain->nport_count++;
 nport->sm.app = nport;
 nport->enable_ini = enable_ini;
 nport->enable_tgt = enable_tgt;
 nport->enable_rscn = (nport->enable_ini ||
   (nport->enable_tgt && enable_target_rscn(nport->efc)));

 /* Copy service parameters from domain */
 memcpy(nport->service_params, domain->service_params,
        sizeof(struct fc_els_flogi));

 /* Update requested fc_id */
 nport->fc_id = fc_id;

 /* Update the nport's service parameters for the new wwn's */
 nport->wwpn = wwpn;
 nport->wwnn = wwnn;
 snprintf(nport->wwnn_str, sizeof(nport->wwnn_str), "%016llX",
   (unsigned long long)wwnn);

 /*
 * if this is the "first" nport of the domain,
 * then make it the "phys" nport
 */

 if (list_empty(&domain->nport_list))
  domain->nport = nport;

 INIT_LIST_HEAD(&nport->list_entry);
 list_add_tail(&nport->list_entry, &domain->nport_list);

 kref_get(&domain->ref);

 efc_log_debug(domain->efc, "New Nport [%s]\n", nport->display_name);

 return nport;
}

void
efc_nport_free(struct efc_nport *nport)
{
 struct efc_domain *domain;

 if (!nport)
  return;

 domain = nport->domain;
 efc_log_debug(domain->efc, "[%s] free nport\n", nport->display_name);
 list_del(&nport->list_entry);
 /*
 * if this is the physical nport,
 * then clear it out of the domain
 */

 if (nport == domain->nport)
  domain->nport = NULL;

 xa_destroy(&nport->lookup);
 xa_erase(&domain->lookup, nport->fc_id);

 if (list_empty(&domain->nport_list))
  efc_domain_post_event(domain, EFC_EVT_ALL_CHILD_NODES_FREE,
          NULL);

 kref_put(&domain->ref, domain->release);
 kref_put(&nport->ref, nport->release);
}

struct efc_nport *
efc_nport_find(struct efc_domain *domain, u32 d_id)
{
 struct efc_nport *nport;

 /* Find a nport object, given an FC_ID */
 nport = xa_load(&domain->lookup, d_id);
 if (!nport || !kref_get_unless_zero(&nport->ref))
  return NULL;

 return nport;
}

int
efc_nport_attach(struct efc_nport *nport, u32 fc_id)
{
 int rc;
 struct efc_node *node;
 struct efc *efc = nport->efc;
 unsigned long index;

 /* Set our lookup */
 rc = xa_err(xa_store(&nport->domain->lookup, fc_id, nport, GFP_ATOMIC));
 if (rc) {
  efc_log_err(efc, "Sport lookup store failed: %d\n", rc);
  return rc;
 }

 /* Update our display_name */
 efc_node_fcid_display(fc_id, nport->display_name,
         sizeof(nport->display_name));

 xa_for_each(&nport->lookup, index, node) {
  efc_node_update_display_name(node);
 }

 efc_log_debug(nport->efc, "[%s] attach nport: fc_id x%06x\n",
        nport->display_name, fc_id);

 /* Register a nport, given an FC_ID */
 rc = efc_cmd_nport_attach(efc, nport, fc_id);
 if (rc < 0) {
  efc_log_err(nport->efc,
       "efc_hw_port_attach failed: %d\n", rc);
  return -EIO;
 }
 return 0;
}

static void
efc_nport_shutdown(struct efc_nport *nport)
{
 struct efc *efc = nport->efc;
 struct efc_node *node;
 unsigned long index;

 xa_for_each(&nport->lookup, index, node) {
  if (!(node->rnode.fc_id == FC_FID_FLOGI && nport->is_vport)) {
   efc_node_post_event(node, EFC_EVT_SHUTDOWN, NULL);
   continue;
  }

  /*
 * If this is a vport, logout of the fabric
 * controller so that it deletes the vport
 * on the switch.
 */

  /* if link is down, don't send logo */
  if (efc->link_status == EFC_LINK_STATUS_DOWN) {
   efc_node_post_event(node, EFC_EVT_SHUTDOWN, NULL);
   continue;
  }

  efc_log_debug(efc, "[%s] nport shutdown vport, send logo\n",
         node->display_name);

  if (!efc_send_logo(node)) {
   /* sent LOGO, wait for response */
   efc_node_transition(node, __efc_d_wait_logo_rsp, NULL);
   continue;
  }

  /*
 * failed to send LOGO,
 * go ahead and cleanup node anyways
 */

  node_printf(node, "Failed to send LOGO\n");
  efc_node_post_event(node, EFC_EVT_SHUTDOWN_EXPLICIT_LOGO, NULL);
 }
}

static void
efc_vport_link_down(struct efc_nport *nport)
{
 struct efc *efc = nport->efc;
 struct efc_vport *vport;

 /* Clear the nport reference in the vport specification */
 list_for_each_entry(vport, &efc->vport_list, list_entry) {
  if (vport->nport == nport) {
   kref_put(&nport->ref, nport->release);
   vport->nport = NULL;
   break;
  }
 }
}

static void
__efc_nport_common(const char *funcname, struct efc_sm_ctx *ctx,
     enum efc_sm_event evt, void *arg)
{
 struct efc_nport *nport = ctx->app;
 struct efc_domain *domain = nport->domain;
 struct efc *efc = nport->efc;

 switch (evt) {
 case EFC_EVT_ENTER:
 case EFC_EVT_REENTER:
 case EFC_EVT_EXIT:
 case EFC_EVT_ALL_CHILD_NODES_FREE:
  break;
 case EFC_EVT_NPORT_ATTACH_OK:
   efc_sm_transition(ctx, __efc_nport_attached, NULL);
  break;
 case EFC_EVT_SHUTDOWN:
  /* Flag this nport as shutting down */
  nport->shutting_down = true;

  if (nport->is_vport)
   efc_vport_link_down(nport);

  if (xa_empty(&nport->lookup)) {
   /* Remove the nport from the domain's lookup table */
   xa_erase(&domain->lookup, nport->fc_id);
   efc_sm_transition(ctx, __efc_nport_wait_port_free,
       NULL);
   if (efc_cmd_nport_free(efc, nport)) {
    efc_log_debug(nport->efc,
           "efc_hw_port_free failed\n");
    /* Not much we can do, free the nport anyways */
    efc_nport_free(nport);
   }
  } else {
   /* sm: node list is not empty / shutdown nodes */
   efc_sm_transition(ctx,
       __efc_nport_wait_shutdown, NULL);
   efc_nport_shutdown(nport);
  }
  break;
 default:
  efc_log_debug(nport->efc, "[%s] %-20s %-20s not handled\n",
         nport->display_name, funcname,
         efc_sm_event_name(evt));
 }
}

void
__efc_nport_allocated(struct efc_sm_ctx *ctx,
        enum efc_sm_event evt, void *arg)
{
 struct efc_nport *nport = ctx->app;
 struct efc_domain *domain = nport->domain;

 nport_sm_trace(nport);

 switch (evt) {
 /* the physical nport is attached */
 case EFC_EVT_NPORT_ATTACH_OK:
  WARN_ON(nport != domain->nport);
  efc_sm_transition(ctx, __efc_nport_attached, NULL);
  break;

 case EFC_EVT_NPORT_ALLOC_OK:
  /* ignore */
  break;
 default:
  __efc_nport_common(__func__, ctx, evt, arg);
 }
}

void
__efc_nport_vport_init(struct efc_sm_ctx *ctx,
         enum efc_sm_event evt, void *arg)
{
 struct efc_nport *nport = ctx->app;
 struct efc *efc = nport->efc;

 nport_sm_trace(nport);

 switch (evt) {
 case EFC_EVT_ENTER: {
  __be64 be_wwpn = cpu_to_be64(nport->wwpn);

  if (nport->wwpn == 0)
   efc_log_debug(efc, "vport: letting f/w select WWN\n");

  if (nport->fc_id != U32_MAX) {
   efc_log_debug(efc, "vport: hard coding port id: %x\n",
          nport->fc_id);
  }

  efc_sm_transition(ctx, __efc_nport_vport_wait_alloc, NULL);
  /* If wwpn is zero, then we'll let the f/w assign wwpn*/
  if (efc_cmd_nport_alloc(efc, nport, nport->domain,
     nport->wwpn == 0 ? NULL :
     (uint8_t *)&be_wwpn)) {
   efc_log_err(efc, "Can't allocate port\n");
   break;
  }

  break;
 }
 default:
  __efc_nport_common(__func__, ctx, evt, arg);
 }
}

void
__efc_nport_vport_wait_alloc(struct efc_sm_ctx *ctx,
        enum efc_sm_event evt, void *arg)
{
 struct efc_nport *nport = ctx->app;
 struct efc *efc = nport->efc;

 nport_sm_trace(nport);

 switch (evt) {
 case EFC_EVT_NPORT_ALLOC_OK: {
  struct fc_els_flogi *sp;

  sp = (struct fc_els_flogi *)nport->service_params;

  if (nport->wwnn == 0) {
   nport->wwnn = be64_to_cpu(nport->sli_wwnn);
   nport->wwpn = be64_to_cpu(nport->sli_wwpn);
   snprintf(nport->wwnn_str, sizeof(nport->wwnn_str),
     "%016llX", nport->wwpn);
  }

  /* Update the nport's service parameters */
  sp->fl_wwpn = cpu_to_be64(nport->wwpn);
  sp->fl_wwnn = cpu_to_be64(nport->wwnn);

  /*
 * if nport->fc_id is uninitialized,
 * then request that the fabric node use FDISC
 * to find an fc_id.
 * Otherwise we're restoring vports, or we're in
 * fabric emulation mode, so attach the fc_id
 */

  if (nport->fc_id == U32_MAX) {
   struct efc_node *fabric;

   fabric = efc_node_alloc(nport, FC_FID_FLOGI, false,
      false);
   if (!fabric) {
    efc_log_err(efc, "efc_node_alloc() failed\n");
    return;
   }
   efc_node_transition(fabric, __efc_vport_fabric_init,
         NULL);
  } else {
   snprintf(nport->wwnn_str, sizeof(nport->wwnn_str),
     "%016llX", nport->wwpn);
   efc_nport_attach(nport, nport->fc_id);
  }
  efc_sm_transition(ctx, __efc_nport_vport_allocated, NULL);
  break;
 }
 default:
  __efc_nport_common(__func__, ctx, evt, arg);
 }
}

void
__efc_nport_vport_allocated(struct efc_sm_ctx *ctx,
       enum efc_sm_event evt, void *arg)
{
 struct efc_nport *nport = ctx->app;
 struct efc *efc = nport->efc;

 nport_sm_trace(nport);

 /*
 * This state is entered after the nport is allocated;
 * it then waits for a fabric node
 * FDISC to complete, which requests a nport attach.
 * The nport attach complete is handled in this state.
 */

 switch (evt) {
 case EFC_EVT_NPORT_ATTACH_OK: {
  struct efc_node *node;

  /* Find our fabric node, and forward this event */
  node = efc_node_find(nport, FC_FID_FLOGI);
  if (!node) {
   efc_log_debug(efc, "can't find node %06x\n", FC_FID_FLOGI);
   break;
  }
  /* sm: / forward nport attach to fabric node */
  efc_node_post_event(node, evt, NULL);
  efc_sm_transition(ctx, __efc_nport_attached, NULL);
  break;
 }
 default:
  __efc_nport_common(__func__, ctx, evt, arg);
 }
}

static void
efc_vport_update_spec(struct efc_nport *nport)
{
 struct efc *efc = nport->efc;
 struct efc_vport *vport;
 unsigned long flags = 0;

 spin_lock_irqsave(&efc->vport_lock, flags);
 list_for_each_entry(vport, &efc->vport_list, list_entry) {
  if (vport->nport == nport) {
   vport->wwnn = nport->wwnn;
   vport->wwpn = nport->wwpn;
   vport->tgt_data = nport->tgt_data;
   vport->ini_data = nport->ini_data;
   break;
  }
 }
 spin_unlock_irqrestore(&efc->vport_lock, flags);
}

void
__efc_nport_attached(struct efc_sm_ctx *ctx,
       enum efc_sm_event evt, void *arg)
{
 struct efc_nport *nport = ctx->app;
 struct efc *efc = nport->efc;

 nport_sm_trace(nport);

 switch (evt) {
 case EFC_EVT_ENTER: {
  struct efc_node *node;
  unsigned long index;

  efc_log_debug(efc,
         "[%s] NPORT attached WWPN %016llX WWNN %016llX\n",
         nport->display_name,
         nport->wwpn, nport->wwnn);

  xa_for_each(&nport->lookup, index, node)
   efc_node_update_display_name(node);

  efc->tt.new_nport(efc, nport);

  /*
 * Update the vport (if its not the physical nport)
 * parameters
 */

  if (nport->is_vport)
   efc_vport_update_spec(nport);
  break;
 }

 case EFC_EVT_EXIT:
  efc_log_debug(efc,
         "[%s] NPORT deattached WWPN %016llX WWNN %016llX\n",
         nport->display_name,
         nport->wwpn, nport->wwnn);

  efc->tt.del_nport(efc, nport);
  break;
 default:
  __efc_nport_common(__func__, ctx, evt, arg);
 }
}

void
__efc_nport_wait_shutdown(struct efc_sm_ctx *ctx,
     enum efc_sm_event evt, void *arg)
{
 struct efc_nport *nport = ctx->app;
 struct efc_domain *domain = nport->domain;
 struct efc *efc = nport->efc;

 nport_sm_trace(nport);

 switch (evt) {
 case EFC_EVT_NPORT_ALLOC_OK:
 case EFC_EVT_NPORT_ALLOC_FAIL:
 case EFC_EVT_NPORT_ATTACH_OK:
 case EFC_EVT_NPORT_ATTACH_FAIL:
  /* ignore these events - just wait for the all free event */
  break;

 case EFC_EVT_ALL_CHILD_NODES_FREE: {
  /*
 * Remove the nport from the domain's
 * sparse vector lookup table
 */

  xa_erase(&domain->lookup, nport->fc_id);
  efc_sm_transition(ctx, __efc_nport_wait_port_free, NULL);
  if (efc_cmd_nport_free(efc, nport)) {
   efc_log_err(nport->efc, "efc_hw_port_free failed\n");
   /* Not much we can do, free the nport anyways */
   efc_nport_free(nport);
  }
  break;
 }
 default:
  __efc_nport_common(__func__, ctx, evt, arg);
 }
}

void
__efc_nport_wait_port_free(struct efc_sm_ctx *ctx,
      enum efc_sm_event evt, void *arg)
{
 struct efc_nport *nport = ctx->app;

 nport_sm_trace(nport);

 switch (evt) {
 case EFC_EVT_NPORT_ATTACH_OK:
  /* Ignore as we are waiting for the free CB */
  break;
 case EFC_EVT_NPORT_FREE_OK: {
  /* All done, free myself */
  efc_nport_free(nport);
  break;
 }
 default:
  __efc_nport_common(__func__, ctx, evt, arg);
 }
}

static int
efc_vport_nport_alloc(struct efc_domain *domain, struct efc_vport *vport)
{
 struct efc_nport *nport;

 lockdep_assert_held(&domain->efc->lock);

 nport = efc_nport_alloc(domain, vport->wwpn, vport->wwnn, vport->fc_id,
    vport->enable_ini, vport->enable_tgt);
 vport->nport = nport;
 if (!nport)
  return -EIO;

 kref_get(&nport->ref);
 nport->is_vport = true;
 nport->tgt_data = vport->tgt_data;
 nport->ini_data = vport->ini_data;

 efc_sm_transition(&nport->sm, __efc_nport_vport_init, NULL);

 return 0;
}

int
efc_vport_start(struct efc_domain *domain)
{
 struct efc *efc = domain->efc;
 struct efc_vport *vport;
 struct efc_vport *next;
 int rc = 0;
 unsigned long flags = 0;

 /* Use the vport spec to find the associated vports and start them */
 spin_lock_irqsave(&efc->vport_lock, flags);
 list_for_each_entry_safe(vport, next, &efc->vport_list, list_entry) {
  if (!vport->nport) {
   if (efc_vport_nport_alloc(domain, vport))
    rc = -EIO;
  }
 }
 spin_unlock_irqrestore(&efc->vport_lock, flags);

 return rc;
}

int
efc_nport_vport_new(struct efc_domain *domain, uint64_t wwpn, uint64_t wwnn,
      u32 fc_id, bool ini, bool tgt, void *tgt_data,
      void *ini_data)
{
 struct efc *efc = domain->efc;
 struct efc_vport *vport;
 int rc = 0;
 unsigned long flags = 0;

 if (ini && domain->efc->enable_ini == 0) {
  efc_log_debug(efc, "driver initiator mode not enabled\n");
  return -EIO;
 }

 if (tgt && domain->efc->enable_tgt == 0) {
  efc_log_debug(efc, "driver target mode not enabled\n");
  return -EIO;
 }

 /*
 * Create a vport spec if we need to recreate
 * this vport after a link up event
 */

 vport = efc_vport_create_spec(domain->efc, wwnn, wwpn, fc_id, ini, tgt,
          tgt_data, ini_data);
 if (!vport) {
  efc_log_err(efc, "failed to create vport object entry\n");
  return -EIO;
 }

 spin_lock_irqsave(&efc->lock, flags);
 rc = efc_vport_nport_alloc(domain, vport);
 spin_unlock_irqrestore(&efc->lock, flags);

 return rc;
}

int
efc_nport_vport_del(struct efc *efc, struct efc_domain *domain,
      u64 wwpn, uint64_t wwnn)
{
 struct efc_nport *nport;
 struct efc_vport *vport;
 struct efc_vport *next;
 unsigned long flags = 0;

 spin_lock_irqsave(&efc->vport_lock, flags);
 /* walk the efc_vport_list and remove from there */
 list_for_each_entry_safe(vport, next, &efc->vport_list, list_entry) {
  if (vport->wwpn == wwpn && vport->wwnn == wwnn) {
   list_del(&vport->list_entry);
   kfree(vport);
   break;
  }
 }
 spin_unlock_irqrestore(&efc->vport_lock, flags);

 if (!domain) {
  /* No domain means no nport to look for */
  return 0;
 }

 spin_lock_irqsave(&efc->lock, flags);
 list_for_each_entry(nport, &domain->nport_list, list_entry) {
  if (nport->wwpn == wwpn && nport->wwnn == wwnn) {
   /* Shutdown this NPORT */
   efc_sm_post_event(&nport->sm, EFC_EVT_SHUTDOWN, NULL);
   kref_put(&nport->ref, nport->release);
   break;
  }
 }

 spin_unlock_irqrestore(&efc->lock, flags);
 return 0;
}

void
efc_vport_del_all(struct efc *efc)
{
 struct efc_vport *vport;
 struct efc_vport *next;
 unsigned long flags = 0;

 spin_lock_irqsave(&efc->vport_lock, flags);
 list_for_each_entry_safe(vport, next, &efc->vport_list, list_entry) {
  list_del(&vport->list_entry);
  kfree(vport);
 }
 spin_unlock_irqrestore(&efc->vport_lock, flags);
}

struct efc_vport *
efc_vport_create_spec(struct efc *efc, uint64_t wwnn, uint64_t wwpn,
        u32 fc_id, bool enable_ini,
        bool enable_tgt, void *tgt_data, void *ini_data)
{
 struct efc_vport *vport;
 unsigned long flags = 0;

 /*
 * walk the efc_vport_list and return failure
 * if a valid(vport with non zero WWPN and WWNN) vport entry
 * is already created
 */

 spin_lock_irqsave(&efc->vport_lock, flags);
 list_for_each_entry(vport, &efc->vport_list, list_entry) {
  if ((wwpn && vport->wwpn == wwpn) &&
      (wwnn && vport->wwnn == wwnn)) {
   efc_log_err(efc,
        "VPORT %016llX %016llX already allocated\n",
        wwnn, wwpn);
   spin_unlock_irqrestore(&efc->vport_lock, flags);
   return NULL;
  }
 }

 vport = kzalloc(sizeof(*vport), GFP_ATOMIC);
 if (!vport) {
  spin_unlock_irqrestore(&efc->vport_lock, flags);
  return NULL;
 }

 vport->wwnn = wwnn;
 vport->wwpn = wwpn;
 vport->fc_id = fc_id;
 vport->enable_tgt = enable_tgt;
 vport->enable_ini = enable_ini;
 vport->tgt_data = tgt_data;
 vport->ini_data = ini_data;

 INIT_LIST_HEAD(&vport->list_entry);
 list_add_tail(&vport->list_entry, &efc->vport_list);
 spin_unlock_irqrestore(&efc->vport_lock, flags);
 return vport;
}

Messung V0.5
C=95 H=96 G=95

¤ Dauer der Verarbeitung: 0.35 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.