Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/oovbaapi/ooo/vba/word/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 1 kB image not shown  

Quelle  fdls_disc.c   Sprache: unbekannt

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright 2008 Cisco Systems, Inc.  All rights reserved.
 * Copyright 2007 Nuova Systems, Inc.  All rights reserved.
 */


#include <linux/workqueue.h>
#include "fnic.h"
#include "fdls_fc.h"
#include "fnic_fdls.h"
#include <scsi/fc/fc_fcp.h>
#include <scsi/scsi_transport_fc.h>
#include <linux/utsname.h>

#define FC_FC4_TYPE_SCSI 0x08
#define PORT_SPEED_BIT_8 8
#define PORT_SPEED_BIT_9 9
#define PORT_SPEED_BIT_14 14
#define PORT_SPEED_BIT_15 15

/* FNIC FDMI Register HBA Macros */
#define FNIC_FDMI_NUM_PORTS 1
#define FNIC_FDMI_NUM_HBA_ATTRS 9
#define FNIC_FDMI_TYPE_NODE_NAME 0X1
#define FNIC_FDMI_TYPE_MANUFACTURER 0X2
#define FNIC_FDMI_MANUFACTURER  "Cisco Systems"
#define FNIC_FDMI_TYPE_SERIAL_NUMBER 0X3
#define FNIC_FDMI_TYPE_MODEL  0X4
#define FNIC_FDMI_TYPE_MODEL_DES 0X5
#define FNIC_FDMI_MODEL_DESCRIPTION "Cisco Virtual Interface Card"
#define FNIC_FDMI_TYPE_HARDWARE_VERSION 0X6
#define FNIC_FDMI_TYPE_DRIVER_VERSION 0X7
#define FNIC_FDMI_TYPE_ROM_VERSION 0X8
#define FNIC_FDMI_TYPE_FIRMWARE_VERSION 0X9
#define FNIC_FDMI_NN_LEN 8
#define FNIC_FDMI_MANU_LEN 20
#define FNIC_FDMI_SERIAL_LEN 16
#define FNIC_FDMI_MODEL_LEN 12
#define FNIC_FDMI_MODEL_DES_LEN 56
#define FNIC_FDMI_HW_VER_LEN 16
#define FNIC_FDMI_DR_VER_LEN 28
#define FNIC_FDMI_ROM_VER_LEN 8
#define FNIC_FDMI_FW_VER_LEN 16

/* FNIC FDMI Register PA Macros */
#define FNIC_FDMI_TYPE_FC4_TYPES 0X1
#define FNIC_FDMI_TYPE_SUPPORTED_SPEEDS 0X2
#define FNIC_FDMI_TYPE_CURRENT_SPEED 0X3
#define FNIC_FDMI_TYPE_MAX_FRAME_SIZE 0X4
#define FNIC_FDMI_TYPE_OS_NAME  0X5
#define FNIC_FDMI_TYPE_HOST_NAME 0X6
#define FNIC_FDMI_NUM_PORT_ATTRS 6
#define FNIC_FDMI_FC4_LEN 32
#define FNIC_FDMI_SUPP_SPEED_LEN 4
#define FNIC_FDMI_CUR_SPEED_LEN 4
#define FNIC_FDMI_MFS_LEN 4
#define FNIC_FDMI_MFS 0x800
#define FNIC_FDMI_OS_NAME_LEN 16
#define FNIC_FDMI_HN_LEN 24

#define FDLS_FDMI_PLOGI_PENDING 0x1
#define FDLS_FDMI_REG_HBA_PENDING 0x2
#define FDLS_FDMI_RPA_PENDING 0x4
#define FDLS_FDMI_ABORT_PENDING 0x8
#define FDLS_FDMI_MAX_RETRY 3

#define RETRIES_EXHAUSTED(iport)      \
 (iport->fabric.retry_counter == FABRIC_LOGO_MAX_RETRY)

#define FNIC_TPORT_MAX_NEXUS_RESTART (8)

#define SCHEDULE_OXID_FREE_RETRY_TIME (300)

/* Private Functions */
static void fdls_fdmi_register_hba(struct fnic_iport_s *iport);
static void fdls_fdmi_register_pa(struct fnic_iport_s *iport);
static void fdls_send_rpn_id(struct fnic_iport_s *iport);
static void fdls_process_flogi_rsp(struct fnic_iport_s *iport,
       struct fc_frame_header *fchdr,
       void *rx_frame);
static void fnic_fdls_start_plogi(struct fnic_iport_s *iport);
static void fnic_fdls_start_flogi(struct fnic_iport_s *iport);
static struct fnic_tport_s *fdls_create_tport(struct fnic_iport_s *iport,
       uint32_t fcid,
       uint64_t wwpn);
static void fdls_target_restart_nexus(struct fnic_tport_s *tport);
static void fdls_start_tport_timer(struct fnic_iport_s *iport,
     struct fnic_tport_s *tport, int timeout);
static void fdls_tport_timer_callback(struct timer_list *t);
static void fdls_send_fdmi_plogi(struct fnic_iport_s *iport);
static void fdls_start_fabric_timer(struct fnic_iport_s *iport,
   int timeout);
static void fdls_init_plogi_frame(uint8_t *frame, struct fnic_iport_s *iport);
static void fdls_init_els_acc_frame(uint8_t *frame, struct fnic_iport_s *iport);
static void fdls_init_els_rjt_frame(uint8_t *frame, struct fnic_iport_s *iport);
static void fdls_init_logo_frame(uint8_t *frame, struct fnic_iport_s *iport);
static void fdls_init_fabric_abts_frame(uint8_t *frame,
      struct fnic_iport_s *iport);

uint8_t *fdls_alloc_frame(struct fnic_iport_s *iport)
{
 struct fnic *fnic = iport->fnic;
 uint8_t *frame = NULL;

 frame = mempool_alloc(fnic->frame_pool, GFP_ATOMIC);
 if (frame == NULL) {
  FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
    "Failed to allocate frame");
  return NULL;
 }

 memset(frame, 0, FNIC_FCOE_FRAME_MAXSZ);
 return frame;
}

/**
 * fdls_alloc_oxid - Allocate an oxid from the bitmap based oxid pool
 * @iport: Handle to iport instance
 * @oxid_frame_type: Type of frame to allocate
 * @active_oxid: the oxid which is in use
 *
 * Called with fnic lock held
 */

uint16_t fdls_alloc_oxid(struct fnic_iport_s *iport, int oxid_frame_type,
 uint16_t *active_oxid)
{
 struct fnic *fnic = iport->fnic;
 struct fnic_oxid_pool_s *oxid_pool = &iport->oxid_pool;
 int idx;
 uint16_t oxid;

 lockdep_assert_held(&fnic->fnic_lock);

 /*
 * Allocate next available oxid from bitmap
 */

 idx = find_next_zero_bit(oxid_pool->bitmap, FNIC_OXID_POOL_SZ, oxid_pool->next_idx);
 if (idx == FNIC_OXID_POOL_SZ) {
  FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
   "Alloc oxid: all oxid slots are busy iport state:%d\n",
   iport->state);
  return FNIC_UNASSIGNED_OXID;
 }

 WARN_ON(test_and_set_bit(idx, oxid_pool->bitmap));
 oxid_pool->next_idx = (idx + 1) % FNIC_OXID_POOL_SZ; /* cycle through the bitmap */

 oxid = FNIC_OXID_ENCODE(idx, oxid_frame_type);
 *active_oxid = oxid;

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
    "alloc oxid: 0x%x, iport state: %d\n",
    oxid, iport->state);
 return oxid;
}

/**
 * fdls_free_oxid_idx - Free the oxid using the idx
 * @iport: Handle to iport instance
 * @oxid_idx: The index to free
 *
 * Free the oxid immediately and make it available for new requests
 * Called with fnic lock held
 */

static void fdls_free_oxid_idx(struct fnic_iport_s *iport, uint16_t oxid_idx)
{
 struct fnic *fnic = iport->fnic;
 struct fnic_oxid_pool_s *oxid_pool = &iport->oxid_pool;

 lockdep_assert_held(&fnic->fnic_lock);

  FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
  "free oxid idx: 0x%x\n", oxid_idx);

 WARN_ON(!test_and_clear_bit(oxid_idx, oxid_pool->bitmap));
}

/**
 * fdls_reclaim_oxid_handler - Callback handler for delayed_oxid_work
 * @work: Handle to work_struct
 *
 * Scheduled when an oxid is to be freed later
 * After freeing expired oxid(s), the handler schedules
 * another callback with the remaining time
 * of next unexpired entry in the reclaim list.
 */

void fdls_reclaim_oxid_handler(struct work_struct *work)
{
 struct fnic_oxid_pool_s *oxid_pool = container_of(work,
  struct fnic_oxid_pool_s, oxid_reclaim_work.work);
 struct fnic_iport_s *iport = container_of(oxid_pool,
  struct fnic_iport_s, oxid_pool);
 struct fnic *fnic = iport->fnic;
 struct reclaim_entry_s *reclaim_entry, *next;
 unsigned long delay_j, cur_jiffies;

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
  "Reclaim oxid callback\n");

 spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);

 /* Though the work was scheduled for one entry,
 * walk through and free the expired entries which might have been scheduled
 * at around the same time as the first entry
 */

 list_for_each_entry_safe(reclaim_entry, next,
  &(oxid_pool->oxid_reclaim_list), links) {

  /* The list is always maintained in the order of expiry time */
  cur_jiffies = jiffies;
  if (time_before(cur_jiffies, reclaim_entry->expires))
   break;

  list_del(&reclaim_entry->links);
  fdls_free_oxid_idx(iport, reclaim_entry->oxid_idx);
  kfree(reclaim_entry);
 }

 /* schedule to free up the next entry */
 if (!list_empty(&oxid_pool->oxid_reclaim_list)) {
  reclaim_entry = list_first_entry(&oxid_pool->oxid_reclaim_list,
   struct reclaim_entry_s, links);

  delay_j = reclaim_entry->expires - cur_jiffies;
  schedule_delayed_work(&oxid_pool->oxid_reclaim_work, delay_j);
  FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
   "Scheduling next callback at:%ld jiffies\n", delay_j);
 }

 spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
}

/**
 * fdls_free_oxid - Helper function to free the oxid
 * @iport: Handle to iport instance
 * @oxid: oxid to free
 * @active_oxid: the oxid which is in use
 *
 * Called with fnic lock held
 */

void fdls_free_oxid(struct fnic_iport_s *iport,
  uint16_t oxid, uint16_t *active_oxid)
{
 fdls_free_oxid_idx(iport, FNIC_OXID_IDX(oxid));
 *active_oxid = FNIC_UNASSIGNED_OXID;
}

/**
 * fdls_schedule_oxid_free - Schedule oxid to be freed later
 * @iport: Handle to iport instance
 * @active_oxid: the oxid which is in use
 *
 * Gets called in a rare case scenario when both a command
 * (fdls or target discovery) timed out and the following ABTS
 * timed out as well, without a link change.
 *
 * Called with fnic lock held
 */

void fdls_schedule_oxid_free(struct fnic_iport_s *iport, uint16_t *active_oxid)
{
 struct fnic *fnic = iport->fnic;
 struct fnic_oxid_pool_s *oxid_pool = &iport->oxid_pool;
 struct reclaim_entry_s *reclaim_entry;
 unsigned long delay_j = msecs_to_jiffies(OXID_RECLAIM_TOV(iport));
 int oxid_idx = FNIC_OXID_IDX(*active_oxid);

 lockdep_assert_held(&fnic->fnic_lock);

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
  "Schedule oxid free. oxid: 0x%x\n", *active_oxid);

 *active_oxid = FNIC_UNASSIGNED_OXID;

 reclaim_entry = (struct reclaim_entry_s *)
  kzalloc(sizeof(struct reclaim_entry_s), GFP_ATOMIC);

 if (!reclaim_entry) {
  FNIC_FCS_DBG(KERN_WARNING, fnic->host, fnic->fnic_num,
   "Failed to allocate memory for reclaim struct for oxid idx: %d\n",
   oxid_idx);

  /* Retry the scheduling  */
  WARN_ON(test_and_set_bit(oxid_idx, oxid_pool->pending_schedule_free));
  schedule_delayed_work(&oxid_pool->schedule_oxid_free_retry, 0);
  return;
 }

 reclaim_entry->oxid_idx = oxid_idx;
 reclaim_entry->expires = round_jiffies(jiffies + delay_j);

 list_add_tail(&reclaim_entry->links, &oxid_pool->oxid_reclaim_list);

 schedule_delayed_work(&oxid_pool->oxid_reclaim_work, delay_j);
}

/**
 * fdls_schedule_oxid_free_retry_work - Thread to schedule the
 * oxid to be freed later
 *
 * @work: Handle to the work struct
 */

void fdls_schedule_oxid_free_retry_work(struct work_struct *work)
{
 struct fnic_oxid_pool_s *oxid_pool = container_of(work,
  struct fnic_oxid_pool_s, schedule_oxid_free_retry.work);
 struct fnic_iport_s *iport = container_of(oxid_pool,
  struct fnic_iport_s, oxid_pool);
 struct fnic *fnic = iport->fnic;
 struct reclaim_entry_s *reclaim_entry;
 unsigned long delay_j = msecs_to_jiffies(OXID_RECLAIM_TOV(iport));
 unsigned long flags;
 int idx;

 for_each_set_bit(idx, oxid_pool->pending_schedule_free, FNIC_OXID_POOL_SZ) {

  FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
   "Schedule oxid free. oxid idx: %d\n", idx);

  reclaim_entry = kzalloc(sizeof(*reclaim_entry), GFP_KERNEL);
  if (!reclaim_entry) {
   schedule_delayed_work(&oxid_pool->schedule_oxid_free_retry,
    msecs_to_jiffies(SCHEDULE_OXID_FREE_RETRY_TIME));
   return;
  }

  clear_bit(idx, oxid_pool->pending_schedule_free);
  reclaim_entry->oxid_idx = idx;
  reclaim_entry->expires = round_jiffies(jiffies + delay_j);
  spin_lock_irqsave(&fnic->fnic_lock, flags);
  list_add_tail(&reclaim_entry->links, &oxid_pool->oxid_reclaim_list);
  spin_unlock_irqrestore(&fnic->fnic_lock, flags);
  schedule_delayed_work(&oxid_pool->oxid_reclaim_work, delay_j);
 }
}

static bool fdls_is_oxid_fabric_req(uint16_t oxid)
{
 int oxid_frame_type = FNIC_FRAME_TYPE(oxid);

 switch (oxid_frame_type) {
 case FNIC_FRAME_TYPE_FABRIC_FLOGI:
 case FNIC_FRAME_TYPE_FABRIC_PLOGI:
 case FNIC_FRAME_TYPE_FABRIC_RPN:
 case FNIC_FRAME_TYPE_FABRIC_RFT:
 case FNIC_FRAME_TYPE_FABRIC_RFF:
 case FNIC_FRAME_TYPE_FABRIC_GPN_FT:
 case FNIC_FRAME_TYPE_FABRIC_LOGO:
  break;
 default:
  return false;
 }
 return true;
}

static bool fdls_is_oxid_fdmi_req(uint16_t oxid)
{
 int oxid_frame_type = FNIC_FRAME_TYPE(oxid);

 switch (oxid_frame_type) {
 case FNIC_FRAME_TYPE_FDMI_PLOGI:
 case FNIC_FRAME_TYPE_FDMI_RHBA:
 case FNIC_FRAME_TYPE_FDMI_RPA:
  break;
 default:
  return false;
 }
 return true;
}

static bool fdls_is_oxid_tgt_req(uint16_t oxid)
{
 int oxid_frame_type = FNIC_FRAME_TYPE(oxid);

 switch (oxid_frame_type) {
 case FNIC_FRAME_TYPE_TGT_PLOGI:
 case FNIC_FRAME_TYPE_TGT_PRLI:
 case FNIC_FRAME_TYPE_TGT_ADISC:
 case FNIC_FRAME_TYPE_TGT_LOGO:
  break;
 default:
  return false;
 }
 return true;
}

static void fdls_reset_oxid_pool(struct fnic_iport_s *iport)
{
 struct fnic_oxid_pool_s *oxid_pool = &iport->oxid_pool;

 oxid_pool->next_idx = 0;
}

void fnic_del_fabric_timer_sync(struct fnic *fnic)
{
 fnic->iport.fabric.del_timer_inprogress = 1;
 spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
 timer_delete_sync(&fnic->iport.fabric.retry_timer);
 spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
 fnic->iport.fabric.del_timer_inprogress = 0;
}

void fnic_del_tport_timer_sync(struct fnic *fnic,
      struct fnic_tport_s *tport)
{
 tport->del_timer_inprogress = 1;
 spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
 timer_delete_sync(&tport->retry_timer);
 spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
 tport->del_timer_inprogress = 0;
}

static void
fdls_start_fabric_timer(struct fnic_iport_s *iport, int timeout)
{
 u64 fabric_tov;
 struct fnic *fnic = iport->fnic;

 if (iport->fabric.timer_pending) {
  FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
      "iport fcid: 0x%x: Canceling fabric disc timer\n",
      iport->fcid);
  fnic_del_fabric_timer_sync(fnic);
  iport->fabric.timer_pending = 0;
 }

 if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED))
  iport->fabric.retry_counter++;

 fabric_tov = jiffies + msecs_to_jiffies(timeout);
 mod_timer(&iport->fabric.retry_timer, round_jiffies(fabric_tov));
 iport->fabric.timer_pending = 1;
 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
     "fabric timer is %d ", timeout);
}

static void
fdls_start_tport_timer(struct fnic_iport_s *iport,
        struct fnic_tport_s *tport, int timeout)
{
 u64 fabric_tov;
 struct fnic *fnic = iport->fnic;

 if (tport->timer_pending) {
  FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
      "tport fcid 0x%x: Canceling disc timer\n",
      tport->fcid);
  fnic_del_tport_timer_sync(fnic, tport);
  tport->timer_pending = 0;
 }

 if (!(tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED))
  tport->retry_counter++;

 fabric_tov = jiffies + msecs_to_jiffies(timeout);
 mod_timer(&tport->retry_timer, round_jiffies(fabric_tov));
 tport->timer_pending = 1;
}

void fdls_init_plogi_frame(uint8_t *frame,
  struct fnic_iport_s *iport)
{
 struct fc_std_flogi *pplogi;
 uint8_t s_id[3];

 pplogi = (struct fc_std_flogi *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
 *pplogi = (struct fc_std_flogi) {
  .fchdr = {.fh_r_ctl = FC_RCTL_ELS_REQ, .fh_d_id = {0xFF, 0xFF, 0xFC},
        .fh_type = FC_TYPE_ELS, .fh_f_ctl = {FNIC_ELS_REQ_FCTL, 0, 0},
        .fh_rx_id = cpu_to_be16(FNIC_UNASSIGNED_RXID)},
  .els = {
      .fl_cmd = ELS_PLOGI,
      .fl_csp = {.sp_hi_ver = FNIC_FC_PH_VER_HI,
          .sp_lo_ver = FNIC_FC_PH_VER_LO,
          .sp_bb_cred = cpu_to_be16(FNIC_FC_B2B_CREDIT),
          .sp_features = cpu_to_be16(FC_SP_FT_CIRO),
          .sp_bb_data = cpu_to_be16(FNIC_FC_B2B_RDF_SZ),
          .sp_tot_seq = cpu_to_be16(FNIC_FC_CONCUR_SEQS),
          .sp_rel_off = cpu_to_be16(FNIC_FC_RO_INFO),
          .sp_e_d_tov = cpu_to_be32(FC_DEF_E_D_TOV)},
      .fl_cssp[2].cp_class = cpu_to_be16(FC_CPC_VALID | FC_CPC_SEQ),
      .fl_cssp[2].cp_rdfs = cpu_to_be16(0x800),
      .fl_cssp[2].cp_con_seq = cpu_to_be16(0xFF),
      .fl_cssp[2].cp_open_seq = 1}
 };

 FNIC_STD_SET_NPORT_NAME(&pplogi->els.fl_wwpn, iport->wwpn);
 FNIC_STD_SET_NODE_NAME(&pplogi->els.fl_wwnn, iport->wwnn);
 FNIC_LOGI_SET_RDF_SIZE(pplogi->els, iport->max_payload_size);

 hton24(s_id, iport->fcid);
 FNIC_STD_SET_S_ID(pplogi->fchdr, s_id);
}

static void fdls_init_els_acc_frame(uint8_t *frame,
  struct fnic_iport_s *iport)
{
 struct fc_std_els_acc_rsp *pels_acc;
 uint8_t s_id[3];

 pels_acc = (struct fc_std_els_acc_rsp *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
 *pels_acc = (struct fc_std_els_acc_rsp) {
  .fchdr = {.fh_r_ctl = FC_RCTL_ELS_REP,
     .fh_type = FC_TYPE_ELS, .fh_f_ctl = {FNIC_ELS_REP_FCTL, 0, 0}},
  .acc.la_cmd = ELS_LS_ACC,
 };

 hton24(s_id, iport->fcid);
 FNIC_STD_SET_S_ID(pels_acc->fchdr, s_id);
 FNIC_STD_SET_RX_ID(pels_acc->fchdr, FNIC_UNASSIGNED_RXID);
}

static void fdls_init_els_rjt_frame(uint8_t *frame,
  struct fnic_iport_s *iport)
{
 struct fc_std_els_rjt_rsp *pels_rjt;

 pels_rjt = (struct fc_std_els_rjt_rsp *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
 *pels_rjt = (struct fc_std_els_rjt_rsp) {
  .fchdr = {.fh_r_ctl = FC_RCTL_ELS_REP, .fh_type = FC_TYPE_ELS,
     .fh_f_ctl = {FNIC_ELS_REP_FCTL, 0, 0}},
  .rej.er_cmd = ELS_LS_RJT,
 };

 FNIC_STD_SET_RX_ID(pels_rjt->fchdr, FNIC_UNASSIGNED_RXID);
}

static void fdls_init_logo_frame(uint8_t *frame,
  struct fnic_iport_s *iport)
{
 struct fc_std_logo *plogo;
 uint8_t s_id[3];

 plogo = (struct fc_std_logo *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
 *plogo = (struct fc_std_logo) {
  .fchdr = {.fh_r_ctl = FC_RCTL_ELS_REQ, .fh_type = FC_TYPE_ELS,
        .fh_f_ctl = {FNIC_ELS_REQ_FCTL, 0, 0}},
  .els.fl_cmd = ELS_LOGO,
 };

 hton24(s_id, iport->fcid);
 FNIC_STD_SET_S_ID(plogo->fchdr, s_id);
 memcpy(plogo->els.fl_n_port_id, s_id, 3);

 FNIC_STD_SET_NPORT_NAME(&plogo->els.fl_n_port_wwn,
       iport->wwpn);
}

static void fdls_init_fabric_abts_frame(uint8_t *frame,
  struct fnic_iport_s *iport)
{
 struct fc_frame_header *pfabric_abts;

 pfabric_abts = (struct fc_frame_header *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
 *pfabric_abts = (struct fc_frame_header) {
  .fh_r_ctl = FC_RCTL_BA_ABTS, /* ABTS */
  .fh_s_id = {0x00, 0x00, 0x00},
  .fh_cs_ctl = 0x00, .fh_type = FC_TYPE_BLS,
  .fh_f_ctl = {FNIC_REQ_ABTS_FCTL, 0, 0}, .fh_seq_id = 0x00,
  .fh_df_ctl = 0x00, .fh_seq_cnt = 0x0000,
  .fh_rx_id = cpu_to_be16(FNIC_UNASSIGNED_RXID),
  .fh_parm_offset = 0x00000000, /* bit:0 = 0 Abort a exchange */
 };
}

static void
fdls_send_rscn_resp(struct fnic_iport_s *iport,
      struct fc_frame_header *rscn_fchdr)
{
 uint8_t *frame;
 struct fc_std_els_acc_rsp *pels_acc;
 struct fnic *fnic = iport->fnic;
 uint16_t oxid;
 uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
   sizeof(struct fc_std_els_acc_rsp);

 frame = fdls_alloc_frame(iport);
 if (frame == NULL) {
  FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
    "Failed to allocate frame to send RSCN response");
  return;
 }

 pels_acc = (struct fc_std_els_acc_rsp *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
 fdls_init_els_acc_frame(frame, iport);

 FNIC_STD_SET_D_ID(pels_acc->fchdr, rscn_fchdr->fh_s_id);

 oxid = FNIC_STD_GET_OX_ID(rscn_fchdr);
 FNIC_STD_SET_OX_ID(pels_acc->fchdr, oxid);

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
   "0x%x: FDLS send RSCN response with oxid: 0x%x",
   iport->fcid, oxid);

 fnic_send_fcoe_frame(iport, frame, frame_size);
}

static void
fdls_send_logo_resp(struct fnic_iport_s *iport,
      struct fc_frame_header *req_fchdr)
{
 uint8_t *frame;
 struct fc_std_els_acc_rsp *plogo_resp;
 struct fnic *fnic = iport->fnic;
 uint16_t oxid;
 uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
   sizeof(struct fc_std_els_acc_rsp);

 frame = fdls_alloc_frame(iport);
 if (frame == NULL) {
  FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
    "Failed to allocate frame to send LOGO response");
  return;
 }

 plogo_resp = (struct fc_std_els_acc_rsp *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
 fdls_init_els_acc_frame(frame, iport);

 FNIC_STD_SET_D_ID(plogo_resp->fchdr, req_fchdr->fh_s_id);

 oxid = FNIC_STD_GET_OX_ID(req_fchdr);
 FNIC_STD_SET_OX_ID(plogo_resp->fchdr, oxid);

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
   "0x%x: FDLS send LOGO response with oxid: 0x%x",
   iport->fcid, oxid);

 fnic_send_fcoe_frame(iport, frame, frame_size);
}

void
fdls_send_tport_abts(struct fnic_iport_s *iport,
      struct fnic_tport_s *tport)
{
 uint8_t *frame;
 uint8_t s_id[3];
 uint8_t d_id[3];
 struct fnic *fnic = iport->fnic;
 struct fc_frame_header *ptport_abts;
 uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
   sizeof(struct fc_frame_header);

 frame = fdls_alloc_frame(iport);
 if (frame == NULL) {
  FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
    "Failed to allocate frame to send tport ABTS");
  return;
 }

 ptport_abts = (struct fc_frame_header *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
 *ptport_abts = (struct fc_frame_header) {
  .fh_r_ctl = FC_RCTL_BA_ABTS, /* ABTS */
  .fh_cs_ctl = 0x00, .fh_type = FC_TYPE_BLS,
  .fh_f_ctl = {FNIC_REQ_ABTS_FCTL, 0, 0}, .fh_seq_id = 0x00,
  .fh_df_ctl = 0x00, .fh_seq_cnt = 0x0000,
  .fh_rx_id = cpu_to_be16(FNIC_UNASSIGNED_RXID),
  .fh_parm_offset = 0x00000000, /* bit:0 = 0 Abort a exchange */
 };

 hton24(s_id, iport->fcid);
 hton24(d_id, tport->fcid);
 FNIC_STD_SET_S_ID(*ptport_abts, s_id);
 FNIC_STD_SET_D_ID(*ptport_abts, d_id);
 tport->flags |= FNIC_FDLS_TGT_ABORT_ISSUED;

 FNIC_STD_SET_OX_ID(*ptport_abts, tport->active_oxid);

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
     "0x%x: FDLS send tport abts: tport->state: %d ",
     iport->fcid, tport->state);

 fnic_send_fcoe_frame(iport, frame, frame_size);

 /* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
 fdls_start_tport_timer(iport, tport, 2 * iport->e_d_tov);
}
static void fdls_send_fabric_abts(struct fnic_iport_s *iport)
{
 uint8_t *frame;
 uint8_t s_id[3];
 uint8_t d_id[3];
 struct fnic *fnic = iport->fnic;
 struct fc_frame_header *pfabric_abts;
 uint16_t oxid;
 uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
   sizeof(struct fc_frame_header);

 frame = fdls_alloc_frame(iport);
 if (frame == NULL) {
  FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
    "Failed to allocate frame to send fabric ABTS");
  return;
 }

 pfabric_abts = (struct fc_frame_header *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
 fdls_init_fabric_abts_frame(frame, iport);

 hton24(s_id, iport->fcid);

 switch (iport->fabric.state) {
 case FDLS_STATE_FABRIC_LOGO:
  hton24(d_id, FC_FID_FLOGI);
  FNIC_STD_SET_D_ID(*pfabric_abts, d_id);
  break;

 case FDLS_STATE_FABRIC_FLOGI:
  hton24(d_id, FC_FID_FLOGI);
  FNIC_STD_SET_D_ID(*pfabric_abts, d_id);
  break;

 case FDLS_STATE_FABRIC_PLOGI:
  FNIC_STD_SET_S_ID(*pfabric_abts, s_id);
  hton24(d_id, FC_FID_DIR_SERV);
  FNIC_STD_SET_D_ID(*pfabric_abts, d_id);
  break;

 case FDLS_STATE_RPN_ID:
  FNIC_STD_SET_S_ID(*pfabric_abts, s_id);
  hton24(d_id, FC_FID_DIR_SERV);
  FNIC_STD_SET_D_ID(*pfabric_abts, d_id);
  break;

 case FDLS_STATE_SCR:
  FNIC_STD_SET_S_ID(*pfabric_abts, s_id);
  hton24(d_id, FC_FID_FCTRL);
  FNIC_STD_SET_D_ID(*pfabric_abts, d_id);
  break;

 case FDLS_STATE_REGISTER_FC4_TYPES:
  FNIC_STD_SET_S_ID(*pfabric_abts, s_id);
  hton24(d_id, FC_FID_DIR_SERV);
  FNIC_STD_SET_D_ID(*pfabric_abts, d_id);
  break;

 case FDLS_STATE_REGISTER_FC4_FEATURES:
  FNIC_STD_SET_S_ID(*pfabric_abts, s_id);
  hton24(d_id, FC_FID_DIR_SERV);
  FNIC_STD_SET_D_ID(*pfabric_abts, d_id);
  break;

 case FDLS_STATE_GPN_FT:
  FNIC_STD_SET_S_ID(*pfabric_abts, s_id);
  hton24(d_id, FC_FID_DIR_SERV);
  FNIC_STD_SET_D_ID(*pfabric_abts, d_id);
  break;
 default:
  return;
 }

 oxid = iport->active_oxid_fabric_req;
 FNIC_STD_SET_OX_ID(*pfabric_abts, oxid);

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
   "0x%x: FDLS send fabric abts. iport->fabric.state: %d oxid: 0x%x",
   iport->fcid, iport->fabric.state, oxid);

 iport->fabric.flags |= FNIC_FDLS_FABRIC_ABORT_ISSUED;

 fnic_send_fcoe_frame(iport, frame, frame_size);

 /* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
 fdls_start_fabric_timer(iport, 2 * iport->e_d_tov);
 iport->fabric.timer_pending = 1;
}

static uint8_t *fdls_alloc_init_fdmi_abts_frame(struct fnic_iport_s *iport,
  uint16_t oxid)
{
 struct fc_frame_header *pfdmi_abts;
 uint8_t d_id[3];
 uint8_t *frame;
 struct fnic *fnic = iport->fnic;

 frame = fdls_alloc_frame(iport);
 if (frame == NULL) {
  FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
    "Failed to allocate frame to send FDMI ABTS");
  return NULL;
 }

 pfdmi_abts = (struct fc_frame_header *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
 fdls_init_fabric_abts_frame(frame, iport);

 hton24(d_id, FC_FID_MGMT_SERV);
 FNIC_STD_SET_D_ID(*pfdmi_abts, d_id);
 FNIC_STD_SET_OX_ID(*pfdmi_abts, oxid);

 return frame;
}

static void fdls_send_fdmi_abts(struct fnic_iport_s *iport)
{
 uint8_t *frame;
 struct fnic *fnic = iport->fnic;
 unsigned long fdmi_tov;
 uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
   sizeof(struct fc_frame_header);

 if (iport->fabric.fdmi_pending & FDLS_FDMI_PLOGI_PENDING) {
  frame = fdls_alloc_init_fdmi_abts_frame(iport,
      iport->active_oxid_fdmi_plogi);
  if (frame == NULL)
   return;

  FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
    "0x%x: FDLS send FDMI PLOGI abts. iport->fabric.state: %d oxid: 0x%x",
    iport->fcid, iport->fabric.state, iport->active_oxid_fdmi_plogi);
  fnic_send_fcoe_frame(iport, frame, frame_size);
 } else {
  if (iport->fabric.fdmi_pending & FDLS_FDMI_REG_HBA_PENDING) {
   frame = fdls_alloc_init_fdmi_abts_frame(iport,
      iport->active_oxid_fdmi_rhba);
   if (frame == NULL)
    return;

   FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
     "0x%x: FDLS send FDMI RHBA abts. iport->fabric.state: %d oxid: 0x%x",
     iport->fcid, iport->fabric.state, iport->active_oxid_fdmi_rhba);
   fnic_send_fcoe_frame(iport, frame, frame_size);
  }
  if (iport->fabric.fdmi_pending & FDLS_FDMI_RPA_PENDING) {
   frame = fdls_alloc_init_fdmi_abts_frame(iport,
      iport->active_oxid_fdmi_rpa);
   if (frame == NULL) {
    if (iport->fabric.fdmi_pending & FDLS_FDMI_REG_HBA_PENDING)
     goto arm_timer;
    else
     return;
   }

   FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
     "0x%x: FDLS send FDMI RPA abts. iport->fabric.state: %d oxid: 0x%x",
     iport->fcid, iport->fabric.state, iport->active_oxid_fdmi_rpa);
   fnic_send_fcoe_frame(iport, frame, frame_size);
  }
 }

arm_timer:
 fdmi_tov = jiffies + msecs_to_jiffies(2 * iport->e_d_tov);
 mod_timer(&iport->fabric.fdmi_timer, round_jiffies(fdmi_tov));
 iport->fabric.fdmi_pending |= FDLS_FDMI_ABORT_PENDING;

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
   "0x%x: iport->fabric.fdmi_pending: 0x%x",
   iport->fcid, iport->fabric.fdmi_pending);
}

static void fdls_send_fabric_flogi(struct fnic_iport_s *iport)
{
 uint8_t *frame;
 struct fc_std_flogi *pflogi;
 struct fnic *fnic = iport->fnic;
 uint16_t oxid;
 uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
   sizeof(struct fc_std_flogi);

 frame = fdls_alloc_frame(iport);
 if (frame == NULL) {
  FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
       "Failed to allocate frame to send FLOGI");
  iport->fabric.flags |= FNIC_FDLS_RETRY_FRAME;
  goto err_out;
 }

 pflogi = (struct fc_std_flogi *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
 *pflogi = (struct fc_std_flogi) {
  .fchdr = {.fh_r_ctl = FC_RCTL_ELS_REQ, .fh_d_id = {0xFF, 0xFF, 0xFE},
        .fh_type = FC_TYPE_ELS, .fh_f_ctl = {FNIC_ELS_REQ_FCTL, 0, 0},
        .fh_rx_id = cpu_to_be16(FNIC_UNASSIGNED_RXID)},
  .els.fl_cmd = ELS_FLOGI,
  .els.fl_csp = {.sp_hi_ver = FNIC_FC_PH_VER_HI,
      .sp_lo_ver = FNIC_FC_PH_VER_LO,
      .sp_bb_cred = cpu_to_be16(FNIC_FC_B2B_CREDIT),
      .sp_bb_data = cpu_to_be16(FNIC_FC_B2B_RDF_SZ)},
  .els.fl_cssp[2].cp_class = cpu_to_be16(FC_CPC_VALID | FC_CPC_SEQ)
 };

 FNIC_STD_SET_NPORT_NAME(&pflogi->els.fl_wwpn, iport->wwpn);
 FNIC_STD_SET_NODE_NAME(&pflogi->els.fl_wwnn, iport->wwnn);
 FNIC_LOGI_SET_RDF_SIZE(pflogi->els, iport->max_payload_size);
 FNIC_LOGI_SET_R_A_TOV(pflogi->els, iport->r_a_tov);
 FNIC_LOGI_SET_E_D_TOV(pflogi->els, iport->e_d_tov);

 oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_FABRIC_FLOGI,
  &iport->active_oxid_fabric_req);

 if (oxid == FNIC_UNASSIGNED_OXID) {
  FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
       "0x%x: Failed to allocate OXID to send FLOGI",
    iport->fcid);
  mempool_free(frame, fnic->frame_pool);
  iport->fabric.flags |= FNIC_FDLS_RETRY_FRAME;
  goto err_out;
 }
 FNIC_STD_SET_OX_ID(pflogi->fchdr, oxid);

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
   "0x%x: FDLS send fabric FLOGI with oxid: 0x%x", iport->fcid,
   oxid);

 fnic_send_fcoe_frame(iport, frame, frame_size);
 atomic64_inc(&iport->iport_stats.fabric_flogi_sent);
err_out:
 /* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
 fdls_start_fabric_timer(iport, 2 * iport->e_d_tov);
}

static void fdls_send_fabric_plogi(struct fnic_iport_s *iport)
{
 uint8_t *frame;
 struct fc_std_flogi *pplogi;
 struct fnic *fnic = iport->fnic;
 uint16_t oxid;
 uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
   sizeof(struct fc_std_flogi);

 frame = fdls_alloc_frame(iport);
 if (frame == NULL) {
  FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
       "Failed to allocate frame to send PLOGI");
  iport->fabric.flags |= FNIC_FDLS_RETRY_FRAME;
  goto err_out;
 }

 pplogi = (struct fc_std_flogi *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
 fdls_init_plogi_frame(frame, iport);

 oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_FABRIC_PLOGI,
  &iport->active_oxid_fabric_req);
 if (oxid == FNIC_UNASSIGNED_OXID) {
  FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
       "0x%x: Failed to allocate OXID to send fabric PLOGI",
    iport->fcid);
  mempool_free(frame, fnic->frame_pool);
  iport->fabric.flags |= FNIC_FDLS_RETRY_FRAME;
  goto err_out;
 }
 FNIC_STD_SET_OX_ID(pplogi->fchdr, oxid);

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
   "0x%x: FDLS send fabric PLOGI with oxid: 0x%x", iport->fcid,
   oxid);

 fnic_send_fcoe_frame(iport, frame, frame_size);
 atomic64_inc(&iport->iport_stats.fabric_plogi_sent);

err_out:
 /* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
 fdls_start_fabric_timer(iport, 2 * iport->e_d_tov);
}

static void fdls_send_fdmi_plogi(struct fnic_iport_s *iport)
{
 uint8_t *frame;
 struct fc_std_flogi *pplogi;
 struct fnic *fnic = iport->fnic;
 uint16_t oxid;
 uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
   sizeof(struct fc_std_flogi);
 uint8_t d_id[3];
 u64 fdmi_tov;

 frame = fdls_alloc_frame(iport);
 if (frame == NULL) {
  FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
       "Failed to allocate frame to send FDMI PLOGI");
  goto err_out;
 }

 pplogi = (struct fc_std_flogi *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
 fdls_init_plogi_frame(frame, iport);

 oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_FDMI_PLOGI,
  &iport->active_oxid_fdmi_plogi);

 if (oxid == FNIC_UNASSIGNED_OXID) {
  FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
        "0x%x: Failed to allocate OXID to send FDMI PLOGI",
        iport->fcid);
  mempool_free(frame, fnic->frame_pool);
  goto err_out;
 }
 FNIC_STD_SET_OX_ID(pplogi->fchdr, oxid);

 hton24(d_id, FC_FID_MGMT_SERV);
 FNIC_STD_SET_D_ID(pplogi->fchdr, d_id);

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
       "0x%x: FDLS send FDMI PLOGI with oxid: 0x%x",
       iport->fcid, oxid);

 fnic_send_fcoe_frame(iport, frame, frame_size);

err_out:
 fdmi_tov = jiffies + msecs_to_jiffies(2 * iport->e_d_tov);
 mod_timer(&iport->fabric.fdmi_timer, round_jiffies(fdmi_tov));
 iport->fabric.fdmi_pending = FDLS_FDMI_PLOGI_PENDING;
}

static void fdls_send_rpn_id(struct fnic_iport_s *iport)
{
 uint8_t *frame;
 struct fc_std_rpn_id *prpn_id;
 struct fnic *fnic = iport->fnic;
 uint16_t oxid;
 uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
   sizeof(struct fc_std_rpn_id);
 uint8_t fcid[3];

 frame = fdls_alloc_frame(iport);
 if (frame == NULL) {
  FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
       "Failed to allocate frame to send RPN_ID");
  iport->fabric.flags |= FNIC_FDLS_RETRY_FRAME;
  goto err_out;
 }

 prpn_id = (struct fc_std_rpn_id *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
 *prpn_id = (struct fc_std_rpn_id) {
  .fchdr = {.fh_r_ctl = FC_RCTL_DD_UNSOL_CTL,
        .fh_d_id = {0xFF, 0xFF, 0xFC}, .fh_type = FC_TYPE_CT,
        .fh_f_ctl = {FNIC_ELS_REQ_FCTL, 0, 0},
        .fh_rx_id = cpu_to_be16(FNIC_UNASSIGNED_RXID)},
  .fc_std_ct_hdr = {.ct_rev = FC_CT_REV, .ct_fs_type = FC_FST_DIR,
         .ct_fs_subtype = FC_NS_SUBTYPE,
         .ct_cmd = cpu_to_be16(FC_NS_RPN_ID)}
 };

 hton24(fcid, iport->fcid);
 FNIC_STD_SET_S_ID(prpn_id->fchdr, fcid);

 FNIC_STD_SET_PORT_ID(prpn_id->rpn_id, fcid);
 FNIC_STD_SET_PORT_NAME(prpn_id->rpn_id, iport->wwpn);

 oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_FABRIC_RPN,
  &iport->active_oxid_fabric_req);

 if (oxid == FNIC_UNASSIGNED_OXID) {
  FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
       "0x%x: Failed to allocate OXID to send RPN_ID",
    iport->fcid);
  mempool_free(frame, fnic->frame_pool);
  iport->fabric.flags |= FNIC_FDLS_RETRY_FRAME;
  goto err_out;
 }
 FNIC_STD_SET_OX_ID(prpn_id->fchdr, oxid);

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
   "0x%x: FDLS send RPN ID with oxid: 0x%x", iport->fcid,
   oxid);

 fnic_send_fcoe_frame(iport, frame, frame_size);

err_out:
 /* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
 fdls_start_fabric_timer(iport, 2 * iport->e_d_tov);
}

static void fdls_send_scr(struct fnic_iport_s *iport)
{
 uint8_t *frame;
 struct fc_std_scr *pscr;
 struct fnic *fnic = iport->fnic;
 uint16_t oxid;
 uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
   sizeof(struct fc_std_scr);
 uint8_t fcid[3];

 frame = fdls_alloc_frame(iport);
 if (frame == NULL) {
  FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
       "Failed to allocate frame to send SCR");
  iport->fabric.flags |= FNIC_FDLS_RETRY_FRAME;
  goto err_out;
 }

 pscr = (struct fc_std_scr *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
 *pscr = (struct fc_std_scr) {
  .fchdr = {.fh_r_ctl = FC_RCTL_ELS_REQ,
        .fh_d_id = {0xFF, 0xFF, 0xFD}, .fh_type = FC_TYPE_ELS,
        .fh_f_ctl = {FNIC_ELS_REQ_FCTL, 0, 0},
        .fh_rx_id = cpu_to_be16(FNIC_UNASSIGNED_RXID)},
  .scr = {.scr_cmd = ELS_SCR,
      .scr_reg_func = ELS_SCRF_FULL}
 };

 hton24(fcid, iport->fcid);
 FNIC_STD_SET_S_ID(pscr->fchdr, fcid);

 oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_FABRIC_SCR,
  &iport->active_oxid_fabric_req);
 if (oxid == FNIC_UNASSIGNED_OXID) {
  FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
       "0x%x: Failed to allocate OXID to send SCR",
    iport->fcid);
  mempool_free(frame, fnic->frame_pool);
  iport->fabric.flags |= FNIC_FDLS_RETRY_FRAME;
  goto err_out;
 }
 FNIC_STD_SET_OX_ID(pscr->fchdr, oxid);

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
   "0x%x: FDLS send SCR with oxid: 0x%x", iport->fcid,
   oxid);

 fnic_send_fcoe_frame(iport, frame, frame_size);
 atomic64_inc(&iport->iport_stats.fabric_scr_sent);

err_out:
 /* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
 fdls_start_fabric_timer(iport, 2 * iport->e_d_tov);
}

static void fdls_send_gpn_ft(struct fnic_iport_s *iport, int fdls_state)
{
 uint8_t *frame;
 struct fc_std_gpn_ft *pgpn_ft;
 struct fnic *fnic = iport->fnic;
 uint16_t oxid;
 uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
   sizeof(struct fc_std_gpn_ft);
 uint8_t fcid[3];

 frame = fdls_alloc_frame(iport);
 if (frame == NULL) {
  FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
       "Failed to allocate frame to send GPN FT");
  iport->fabric.flags |= FNIC_FDLS_RETRY_FRAME;
  goto err_out;
 }

 pgpn_ft = (struct fc_std_gpn_ft *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
 *pgpn_ft = (struct fc_std_gpn_ft) {
  .fchdr = {.fh_r_ctl = FC_RCTL_DD_UNSOL_CTL,
        .fh_d_id = {0xFF, 0xFF, 0xFC}, .fh_type = FC_TYPE_CT,
        .fh_f_ctl = {FNIC_ELS_REQ_FCTL, 0, 0},
        .fh_rx_id = cpu_to_be16(FNIC_UNASSIGNED_RXID)},
  .fc_std_ct_hdr = {.ct_rev = FC_CT_REV, .ct_fs_type = FC_FST_DIR,
         .ct_fs_subtype = FC_NS_SUBTYPE,
         .ct_cmd = cpu_to_be16(FC_NS_GPN_FT)},
  .gpn_ft.fn_fc4_type = 0x08
 };

 hton24(fcid, iport->fcid);
 FNIC_STD_SET_S_ID(pgpn_ft->fchdr, fcid);

 oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_FABRIC_GPN_FT,
  &iport->active_oxid_fabric_req);

 if (oxid == FNIC_UNASSIGNED_OXID) {
  FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
       "0x%x: Failed to allocate OXID to send GPN FT",
    iport->fcid);
  mempool_free(frame, fnic->frame_pool);
  iport->fabric.flags |= FNIC_FDLS_RETRY_FRAME;
  goto err_out;
 }
 FNIC_STD_SET_OX_ID(pgpn_ft->fchdr, oxid);

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
   "0x%x: FDLS send GPN FT with oxid: 0x%x", iport->fcid,
   oxid);

 fnic_send_fcoe_frame(iport, frame, frame_size);

err_out:
 /* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
 fdls_start_fabric_timer(iport, 2 * iport->e_d_tov);
 fdls_set_state((&iport->fabric), fdls_state);
}

static void
fdls_send_tgt_adisc(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
{
 uint8_t *frame;
 struct fc_std_els_adisc *padisc;
 uint8_t s_id[3];
 uint8_t d_id[3];
 uint16_t oxid;
 struct fnic *fnic = iport->fnic;
 uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
   sizeof(struct fc_std_els_adisc);

 frame = fdls_alloc_frame(iport);
 if (frame == NULL) {
  FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
    "Failed to allocate frame to send TGT ADISC");
  tport->flags |= FNIC_FDLS_RETRY_FRAME;
  goto err_out;
 }

 padisc = (struct fc_std_els_adisc *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);

 hton24(s_id, iport->fcid);
 hton24(d_id, tport->fcid);
 memcpy(padisc->els.adisc_port_id, s_id, 3);
 FNIC_STD_SET_S_ID(padisc->fchdr, s_id);
 FNIC_STD_SET_D_ID(padisc->fchdr, d_id);

 FNIC_STD_SET_F_CTL(padisc->fchdr, FNIC_ELS_REQ_FCTL << 16);
 FNIC_STD_SET_R_CTL(padisc->fchdr, FC_RCTL_ELS_REQ);
 FNIC_STD_SET_TYPE(padisc->fchdr, FC_TYPE_ELS);

 oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_TGT_ADISC, &tport->active_oxid);
 if (oxid == FNIC_UNASSIGNED_OXID) {
  FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
      "0x%x: Failed to allocate OXID to send TGT ADISC",
      iport->fcid);
  mempool_free(frame, fnic->frame_pool);
  tport->flags |= FNIC_FDLS_RETRY_FRAME;
  goto err_out;
 }
 FNIC_STD_SET_OX_ID(padisc->fchdr, oxid);
 FNIC_STD_SET_RX_ID(padisc->fchdr, FNIC_UNASSIGNED_RXID);

 tport->flags &= ~FNIC_FDLS_TGT_ABORT_ISSUED;

 FNIC_STD_SET_NPORT_NAME(&padisc->els.adisc_wwpn,
    iport->wwpn);
 FNIC_STD_SET_NODE_NAME(&padisc->els.adisc_wwnn,
   iport->wwnn);

 padisc->els.adisc_cmd = ELS_ADISC;

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
     "0x%x: FDLS send ADISC to tgt fcid: 0x%x",
     iport->fcid, tport->fcid);

 atomic64_inc(&iport->iport_stats.tport_adisc_sent);

 fnic_send_fcoe_frame(iport, frame, frame_size);

err_out:
 /* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
 fdls_start_tport_timer(iport, tport, 2 * iport->e_d_tov);
}

bool fdls_delete_tport(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
{
 struct fnic_tport_event_s *tport_del_evt;
 struct fnic *fnic = iport->fnic;

 if ((tport->state == FDLS_TGT_STATE_OFFLINING)
     || (tport->state == FDLS_TGT_STATE_OFFLINE)) {
  FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
        "tport fcid 0x%x: tport state is offlining/offline\n",
        tport->fcid);
  return false;
 }

 fdls_set_tport_state(tport, FDLS_TGT_STATE_OFFLINING);
 /*
 * By setting this flag, the tport will not be seen in a look-up
 * in an RSCN. Even if we move to multithreaded model, this tport
 * will be destroyed and a new RSCN will have to create a new one
 */

 tport->flags |= FNIC_FDLS_TPORT_TERMINATING;

 if (tport->timer_pending) {
  FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
      "tport fcid 0x%x: Canceling disc timer\n",
      tport->fcid);
  fnic_del_tport_timer_sync(fnic, tport);
  tport->timer_pending = 0;
 }

 spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
 fnic_rport_exch_reset(iport->fnic, tport->fcid);
 spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);

 if (tport->flags & FNIC_FDLS_SCSI_REGISTERED) {
  tport_del_evt =
   kzalloc(sizeof(struct fnic_tport_event_s), GFP_ATOMIC);
  if (!tport_del_evt) {
   FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
     "Failed to allocate memory for tport fcid: 0x%0x\n",
     tport->fcid);
   return false;
  }
  tport_del_evt->event = TGT_EV_RPORT_DEL;
  tport_del_evt->arg1 = (void *) tport;
  list_add_tail(&tport_del_evt->links, &fnic->tport_event_list);
  queue_work(fnic_event_queue, &fnic->tport_work);
 } else {
  FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
    "tport 0x%x not reg with scsi_transport. Freeing locally",
    tport->fcid);
  list_del(&tport->links);
  kfree(tport);
 }
 return true;
}

static void
fdls_send_tgt_plogi(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
{
 uint8_t *frame;
 struct fc_std_flogi *pplogi;
 struct fnic *fnic = iport->fnic;
 uint16_t oxid;
 uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
   sizeof(struct fc_std_flogi);
 uint8_t d_id[3];
 uint32_t timeout;

 frame = fdls_alloc_frame(iport);
 if (frame == NULL) {
  FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
    "Failed to allocate frame to send TGT PLOGI");
  tport->flags |= FNIC_FDLS_RETRY_FRAME;
  goto err_out;
 }

 pplogi = (struct fc_std_flogi *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
 fdls_init_plogi_frame(frame, iport);

 oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_TGT_PLOGI, &tport->active_oxid);
 if (oxid == FNIC_UNASSIGNED_OXID) {
  FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
     "0x%x: Failed to allocate oxid to send PLOGI to fcid: 0x%x",
     iport->fcid, tport->fcid);
  mempool_free(frame, fnic->frame_pool);
  tport->flags |= FNIC_FDLS_RETRY_FRAME;
  goto err_out;
 }
 FNIC_STD_SET_OX_ID(pplogi->fchdr, oxid);

 tport->flags &= ~FNIC_FDLS_TGT_ABORT_ISSUED;

 hton24(d_id, tport->fcid);
 FNIC_STD_SET_D_ID(pplogi->fchdr, d_id);

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
     "0x%x: FDLS send tgt PLOGI to tgt: 0x%x with oxid: 0x%x",
     iport->fcid, tport->fcid, oxid);

 fnic_send_fcoe_frame(iport, frame, frame_size);
 atomic64_inc(&iport->iport_stats.tport_plogi_sent);

err_out:
 timeout = max(2 * iport->e_d_tov, iport->plogi_timeout);
 /* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
 fdls_start_tport_timer(iport, tport, timeout);
}

static uint16_t
fnic_fc_plogi_rsp_rdf(struct fnic_iport_s *iport,
        struct fc_std_flogi *plogi_rsp)
{
 uint16_t b2b_rdf_size =
     be16_to_cpu(FNIC_LOGI_RDF_SIZE(plogi_rsp->els));
 uint16_t spc3_rdf_size =
     be16_to_cpu(plogi_rsp->els.fl_cssp[2].cp_rdfs) & FNIC_FC_C3_RDF;
 struct fnic *fnic = iport->fnic;

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
    "MFS: b2b_rdf_size: 0x%x spc3_rdf_size: 0x%x",
    b2b_rdf_size, spc3_rdf_size);

 return min(b2b_rdf_size, spc3_rdf_size);
}

static void fdls_send_register_fc4_types(struct fnic_iport_s *iport)
{
 uint8_t *frame;
 struct fc_std_rft_id *prft_id;
 struct fnic *fnic = iport->fnic;
 uint16_t oxid;
 uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
   sizeof(struct fc_std_rft_id);
 uint8_t fcid[3];

 frame = fdls_alloc_frame(iport);
 if (frame == NULL) {
  FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
       "Failed to allocate frame to send RFT");
  return;
 }

 prft_id = (struct fc_std_rft_id *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
 *prft_id = (struct fc_std_rft_id) {
  .fchdr = {.fh_r_ctl = FC_RCTL_DD_UNSOL_CTL,
        .fh_d_id = {0xFF, 0xFF, 0xFC}, .fh_type = FC_TYPE_CT,
        .fh_f_ctl = {FNIC_ELS_REQ_FCTL, 0, 0},
        .fh_rx_id = cpu_to_be16(FNIC_UNASSIGNED_RXID)},
  .fc_std_ct_hdr = {.ct_rev = FC_CT_REV, .ct_fs_type = FC_FST_DIR,
         .ct_fs_subtype = FC_NS_SUBTYPE,
         .ct_cmd = cpu_to_be16(FC_NS_RFT_ID)}
 };

 hton24(fcid, iport->fcid);
 FNIC_STD_SET_S_ID(prft_id->fchdr, fcid);
 FNIC_STD_SET_PORT_ID(prft_id->rft_id, fcid);

 oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_FABRIC_RFT,
  &iport->active_oxid_fabric_req);

 if (oxid == FNIC_UNASSIGNED_OXID) {
  FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
       "0x%x: Failed to allocate OXID to send RFT",
    iport->fcid);
  mempool_free(frame, fnic->frame_pool);
  return;
 }
 FNIC_STD_SET_OX_ID(prft_id->fchdr, oxid);

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
   "0x%x: FDLS send RFT with oxid: 0x%x", iport->fcid,
   oxid);

 prft_id->rft_id.fr_fts.ff_type_map[0] =
     cpu_to_be32(1 << FC_TYPE_FCP);

 prft_id->rft_id.fr_fts.ff_type_map[1] =
 cpu_to_be32(1 << (FC_TYPE_CT % FC_NS_BPW));

 fnic_send_fcoe_frame(iport, frame, frame_size);

 /* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
 fdls_start_fabric_timer(iport, 2 * iport->e_d_tov);
}

static void fdls_send_register_fc4_features(struct fnic_iport_s *iport)
{
 uint8_t *frame;
 struct fc_std_rff_id *prff_id;
 struct fnic *fnic = iport->fnic;
 uint16_t oxid;
 uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
   sizeof(struct fc_std_rff_id);
 uint8_t fcid[3];

 frame = fdls_alloc_frame(iport);
 if (frame == NULL) {
  FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
       "Failed to allocate frame to send RFF");
  return;
 }

 prff_id = (struct fc_std_rff_id *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
 *prff_id = (struct fc_std_rff_id) {
  .fchdr = {.fh_r_ctl = FC_RCTL_DD_UNSOL_CTL,
        .fh_d_id = {0xFF, 0xFF, 0xFC}, .fh_type = FC_TYPE_CT,
        .fh_f_ctl = {FNIC_ELS_REQ_FCTL, 0, 0},
        .fh_rx_id = cpu_to_be16(FNIC_UNASSIGNED_RXID)},
  .fc_std_ct_hdr = {.ct_rev = FC_CT_REV, .ct_fs_type = FC_FST_DIR,
         .ct_fs_subtype = FC_NS_SUBTYPE,
         .ct_cmd = cpu_to_be16(FC_NS_RFF_ID)},
  .rff_id.fr_feat = 0x2,
  .rff_id.fr_type = FC_TYPE_FCP
 };

 hton24(fcid, iport->fcid);
 FNIC_STD_SET_S_ID(prff_id->fchdr, fcid);
 FNIC_STD_SET_PORT_ID(prff_id->rff_id, fcid);

 oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_FABRIC_RFF,
  &iport->active_oxid_fabric_req);

 if (oxid == FNIC_UNASSIGNED_OXID) {
  FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
        "0x%x: Failed to allocate OXID to send RFF",
     iport->fcid);
  mempool_free(frame, fnic->frame_pool);
  return;
 }
 FNIC_STD_SET_OX_ID(prff_id->fchdr, oxid);

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
   "0x%x: FDLS send RFF with oxid: 0x%x", iport->fcid,
   oxid);

 prff_id->rff_id.fr_type = FC_TYPE_FCP;

 fnic_send_fcoe_frame(iport, frame, frame_size);

 /* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
 fdls_start_fabric_timer(iport, 2 * iport->e_d_tov);
}

static void
fdls_send_tgt_prli(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
{
 uint8_t *frame;
 struct fc_std_els_prli *pprli;
 struct fnic *fnic = iport->fnic;
 uint16_t oxid;
 uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
   sizeof(struct fc_std_els_prli);
 uint8_t s_id[3];
 uint8_t d_id[3];
 uint32_t timeout;

 frame = fdls_alloc_frame(iport);
 if (frame == NULL) {
  FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
    "Failed to allocate frame to send TGT PRLI");
  tport->flags |= FNIC_FDLS_RETRY_FRAME;
  goto err_out;
 }

 pprli = (struct fc_std_els_prli *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
 *pprli = (struct fc_std_els_prli) {
  .fchdr = {.fh_r_ctl = FC_RCTL_ELS_REQ, .fh_type = FC_TYPE_ELS,
     .fh_f_ctl = {FNIC_ELS_REQ_FCTL, 0, 0},
     .fh_rx_id = cpu_to_be16(FNIC_UNASSIGNED_RXID)},
  .els_prli = {.prli_cmd = ELS_PRLI,
        .prli_spp_len = 16,
        .prli_len = cpu_to_be16(0x14)},
  .sp = {.spp_type = 0x08, .spp_flags = 0x0020,
         .spp_params = cpu_to_be32(0xA2)}
 };

 oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_TGT_PRLI, &tport->active_oxid);
 if (oxid == FNIC_UNASSIGNED_OXID) {
  FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
   "0x%x: Failed to allocate OXID to send TGT PRLI to 0x%x",
   iport->fcid, tport->fcid);
  mempool_free(frame, fnic->frame_pool);
  tport->flags |= FNIC_FDLS_RETRY_FRAME;
  goto err_out;
 }

 tport->flags &= ~FNIC_FDLS_TGT_ABORT_ISSUED;

 hton24(s_id, iport->fcid);
 hton24(d_id, tport->fcid);

 FNIC_STD_SET_OX_ID(pprli->fchdr, oxid);
 FNIC_STD_SET_S_ID(pprli->fchdr, s_id);
 FNIC_STD_SET_D_ID(pprli->fchdr, d_id);

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
   "0x%x: FDLS send PRLI to tgt: 0x%x with oxid: 0x%x",
   iport->fcid, tport->fcid, oxid);

 fnic_send_fcoe_frame(iport, frame, frame_size);
 atomic64_inc(&iport->iport_stats.tport_prli_sent);

err_out:
 timeout = max(2 * iport->e_d_tov, iport->plogi_timeout);
 /* Even if fnic_send_fcoe_frame() fails we want to retry after timeout */
 fdls_start_tport_timer(iport, tport, timeout);
}

/**
 * fdls_send_fabric_logo - Send flogo to the fcf
 * @iport: Handle to fnic iport
 *
 * This function does not change or check the fabric state.
 * It the caller's responsibility to set the appropriate iport fabric
 * state when this is called. Normally it is FDLS_STATE_FABRIC_LOGO.
 * Currently this assumes to be called with fnic lock held.
 */

void fdls_send_fabric_logo(struct fnic_iport_s *iport)
{
 uint8_t *frame;
 struct fc_std_logo *plogo;
 struct fnic *fnic = iport->fnic;
 uint8_t d_id[3];
 uint16_t oxid;
 uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
   sizeof(struct fc_std_logo);

 frame = fdls_alloc_frame(iport);
 if (frame == NULL) {
  FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
       "Failed to allocate frame to send fabric LOGO");
  return;
 }

 plogo = (struct fc_std_logo *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
 fdls_init_logo_frame(frame, iport);

 oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_FABRIC_LOGO,
  &iport->active_oxid_fabric_req);

 if (oxid == FNIC_UNASSIGNED_OXID) {
  FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
       "0x%x: Failed to allocate OXID to send fabric LOGO",
    iport->fcid);
  mempool_free(frame, fnic->frame_pool);
  return;
 }
 FNIC_STD_SET_OX_ID(plogo->fchdr, oxid);

 hton24(d_id, FC_FID_FLOGI);
 FNIC_STD_SET_D_ID(plogo->fchdr, d_id);

 iport->fabric.flags &= ~FNIC_FDLS_FABRIC_ABORT_ISSUED;

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
       "0x%x: FDLS send fabric LOGO with oxid: 0x%x",
       iport->fcid, oxid);

 fnic_send_fcoe_frame(iport, frame, frame_size);

 fdls_start_fabric_timer(iport, 2 * iport->e_d_tov);
}

/**
 * fdls_tgt_logout - Send plogo to the remote port
 * @iport: Handle to fnic iport
 * @tport: Handle to remote port
 *
 * This function does not change or check the fabric/tport state.
 * It the caller's responsibility to set the appropriate tport/fabric
 * state when this is called. Normally that is fdls_tgt_state_plogo.
 * This could be used to send plogo to nameserver process
 * also not just target processes
 */

void fdls_tgt_logout(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
{
 uint8_t *frame;
 struct fc_std_logo *plogo;
 struct fnic *fnic = iport->fnic;
 uint8_t d_id[3];
 uint16_t oxid;
 uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
   sizeof(struct fc_std_logo);

 frame = fdls_alloc_frame(iport);
 if (frame == NULL) {
  FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
       "Failed to allocate frame to send fabric LOGO");
  return;
 }

 plogo = (struct fc_std_logo *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
 fdls_init_logo_frame(frame, iport);

 oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_TGT_LOGO, &tport->active_oxid);
 if (oxid == FNIC_UNASSIGNED_OXID) {
  FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
       "0x%x: Failed to allocate OXID to send tgt LOGO",
       iport->fcid);
  mempool_free(frame, fnic->frame_pool);
  return;
 }
 FNIC_STD_SET_OX_ID(plogo->fchdr, oxid);

 hton24(d_id, tport->fcid);
 FNIC_STD_SET_D_ID(plogo->fchdr, d_id);

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
   "0x%x: FDLS send tgt LOGO with oxid: 0x%x",
   iport->fcid, oxid);

 fnic_send_fcoe_frame(iport, frame, frame_size);

 atomic64_inc(&iport->iport_stats.tport_logo_sent);
}

static void fdls_tgt_discovery_start(struct fnic_iport_s *iport)
{
 struct fnic_tport_s *tport, *next;
 u32 old_link_down_cnt = iport->fnic->link_down_cnt;
 struct fnic *fnic = iport->fnic;

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
     "0x%x: Starting FDLS target discovery", iport->fcid);

 list_for_each_entry_safe(tport, next, &iport->tport_list, links) {
  if ((old_link_down_cnt != iport->fnic->link_down_cnt)
   || (iport->state != FNIC_IPORT_STATE_READY)) {
   break;
  }
  /* if we marked the tport as deleted due to GPN_FT
 * We should not send ADISC anymore
 */

  if ((tport->state == FDLS_TGT_STATE_OFFLINING) ||
   (tport->state == FDLS_TGT_STATE_OFFLINE))
   continue;

  /* For tports which have received RSCN */
  if (tport->flags & FNIC_FDLS_TPORT_SEND_ADISC) {
   tport->retry_counter = 0;
   fdls_set_tport_state(tport, FDLS_TGT_STATE_ADISC);
   tport->flags &= ~FNIC_FDLS_TPORT_SEND_ADISC;
   fdls_send_tgt_adisc(iport, tport);
   continue;
  }
  if (fdls_get_tport_state(tport) != FDLS_TGT_STATE_INIT) {
   /* Not a new port, skip  */
   continue;
  }
  tport->retry_counter = 0;
  fdls_set_tport_state(tport, FDLS_TGT_STATE_PLOGI);
  fdls_send_tgt_plogi(iport, tport);
 }
 fdls_set_state((&iport->fabric), FDLS_STATE_TGT_DISCOVERY);
}

/*
 * Function to restart the IT nexus if we received any out of
 * sequence PLOGI/PRLI  response from the target.
 * The memory for the new tport structure is allocated
 * inside fdls_create_tport and added to the iport's tport list.
 * This will get freed later during tport_offline/linkdown
 * or module unload. The new_tport pointer will go out of scope
 * safely since the memory it is
 * pointing to it will be freed later
 */

static void fdls_target_restart_nexus(struct fnic_tport_s *tport)
{
 struct fnic_iport_s *iport = tport->iport;
 struct fnic_tport_s *new_tport = NULL;
 uint32_t fcid;
 uint64_t wwpn;
 int nexus_restart_count;
 struct fnic *fnic = iport->fnic;
 bool retval = true;

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
     "tport fcid: 0x%x state: %d restart_count: %d",
     tport->fcid, tport->state, tport->nexus_restart_count);

 fcid = tport->fcid;
 wwpn = tport->wwpn;
 nexus_restart_count = tport->nexus_restart_count;

 retval = fdls_delete_tport(iport, tport);
 if (retval != true) {
  FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
        "Error deleting tport: 0x%x", fcid);
  return;
 }

 if (nexus_restart_count >= FNIC_TPORT_MAX_NEXUS_RESTART) {
  FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
        "Exceeded nexus restart retries tport: 0x%x",
        fcid);
  return;
 }

 /*
 * Allocate memory for the new tport and add it to
 * iport's tport list.
 * This memory will be freed during tport_offline/linkdown
 * or module unload. The pointer new_tport is safe to go
 * out of scope when this function returns, since the memory
 * it is pointing to is guaranteed to be freed later
 * as mentioned above.
 */

 new_tport = fdls_create_tport(iport, fcid, wwpn);
 if (!new_tport) {
  FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
      "Error creating new tport: 0x%x", fcid);
  return;
 }

 new_tport->nexus_restart_count = nexus_restart_count + 1;
 fdls_send_tgt_plogi(iport, new_tport);
 fdls_set_tport_state(new_tport, FDLS_TGT_STATE_PLOGI);
}

struct fnic_tport_s *fnic_find_tport_by_fcid(struct fnic_iport_s *iport,
          uint32_t fcid)
{
 struct fnic_tport_s *tport, *next;

 list_for_each_entry_safe(tport, next, &(iport->tport_list), links) {
  if ((tport->fcid == fcid)
   && !(tport->flags & FNIC_FDLS_TPORT_TERMINATING))
   return tport;
 }
 return NULL;
}

static struct fnic_tport_s *fdls_create_tport(struct fnic_iport_s *iport,
          uint32_t fcid, uint64_t wwpn)
{
 struct fnic_tport_s *tport;
 struct fnic *fnic = iport->fnic;

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
    "FDLS create tport: fcid: 0x%x wwpn: 0x%llx", fcid, wwpn);

 tport = kzalloc(sizeof(struct fnic_tport_s), GFP_ATOMIC);
 if (!tport) {
  FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
    "Memory allocation failure while creating tport: 0x%x\n",
    fcid);
  return NULL;
 }

 tport->max_payload_size = FNIC_FCOE_MAX_FRAME_SZ;
 tport->r_a_tov = FC_DEF_R_A_TOV;
 tport->e_d_tov = FC_DEF_E_D_TOV;
 tport->fcid = fcid;
 tport->wwpn = wwpn;
 tport->iport = iport;

 FNIC_FCS_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
     "Need to setup tport timer callback");

 timer_setup(&tport->retry_timer, fdls_tport_timer_callback, 0);

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
     "Added tport 0x%x", tport->fcid);
 fdls_set_tport_state(tport, FDLS_TGT_STATE_INIT);
 list_add_tail(&tport->links, &iport->tport_list);
 atomic_set(&tport->in_flight, 0);
 return tport;
}

struct fnic_tport_s *fnic_find_tport_by_wwpn(struct fnic_iport_s *iport,
          uint64_t wwpn)
{
 struct fnic_tport_s *tport, *next;

 list_for_each_entry_safe(tport, next, &(iport->tport_list), links) {
  if ((tport->wwpn == wwpn)
   && !(tport->flags & FNIC_FDLS_TPORT_TERMINATING))
   return tport;
 }
 return NULL;
}

static void
fnic_fdmi_attr_set(void *attr_start, u16 type, u16 len,
  void *data, u32 *off)
{
 u16 size = len + FC_FDMI_ATTR_ENTRY_HEADER_LEN;
 struct fc_fdmi_attr_entry *fdmi_attr = (struct fc_fdmi_attr_entry *)
  ((u8 *)attr_start + *off);

 put_unaligned_be16(type, &fdmi_attr->type);
 put_unaligned_be16(size, &fdmi_attr->len);
 memcpy(fdmi_attr->value, data, len);
 *off += size;
}

static void fdls_fdmi_register_hba(struct fnic_iport_s *iport)
{
 uint8_t *frame;
 struct fc_std_fdmi_rhba *prhba;
 struct fc_fdmi_attr_entry *fdmi_attr;
 uint8_t fcid[3];
 int err;
 struct fnic *fnic = iport->fnic;
 struct vnic_devcmd_fw_info *fw_info = NULL;
 uint16_t oxid;
 u32 attr_off_bytes, len;
 u8 data[64];
 uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET;

 frame = fdls_alloc_frame(iport);
 if (frame == NULL) {
  FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
       "Failed to allocate frame to send FDMI RHBA");
  return;
 }

 prhba = (struct fc_std_fdmi_rhba *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
 *prhba = (struct fc_std_fdmi_rhba) {
  .fchdr = {
   .fh_r_ctl = FC_RCTL_DD_UNSOL_CTL,
   .fh_d_id = {0xFF, 0XFF, 0XFA},
   .fh_type = FC_TYPE_CT,
   .fh_f_ctl = {FNIC_ELS_REQ_FCTL, 0, 0},
   .fh_rx_id = cpu_to_be16(FNIC_UNASSIGNED_RXID)
  },
  .fc_std_ct_hdr = {
   .ct_rev = FC_CT_REV, .ct_fs_type = FC_FST_MGMT,
   .ct_fs_subtype = FC_FDMI_SUBTYPE,
   .ct_cmd = cpu_to_be16(FC_FDMI_RHBA)
  },
 };

 hton24(fcid, iport->fcid);
 FNIC_STD_SET_S_ID(prhba->fchdr, fcid);

 oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_FDMI_RHBA,
  &iport->active_oxid_fdmi_rhba);

 if (oxid == FNIC_UNASSIGNED_OXID) {
  FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
        "0x%x: Failed to allocate OXID to send FDMI RHBA",
       iport->fcid);
  mempool_free(frame, fnic->frame_pool);
  return;
 }
 FNIC_STD_SET_OX_ID(prhba->fchdr, oxid);

 put_unaligned_be64(iport->wwpn, &prhba->rhba.hbaid.id);
 put_unaligned_be32(FNIC_FDMI_NUM_PORTS, &prhba->rhba.port.numport);
 put_unaligned_be64(iport->wwpn, &prhba->rhba.port.port[0].portname);
 put_unaligned_be32(FNIC_FDMI_NUM_HBA_ATTRS,
   &prhba->rhba.hba_attrs.numattrs);

 fdmi_attr = prhba->rhba.hba_attrs.attr;
 attr_off_bytes = 0;

 put_unaligned_be64(iport->wwnn, data);
 fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_NODE_NAME,
  FNIC_FDMI_NN_LEN, data, &attr_off_bytes);

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
   "NN set, off=%d", attr_off_bytes);

 strscpy_pad(data, FNIC_FDMI_MANUFACTURER, FNIC_FDMI_MANU_LEN);
 fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_MANUFACTURER,
  FNIC_FDMI_MANU_LEN, data, &attr_off_bytes);

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
   "MFG set <%s>, off=%d", data, attr_off_bytes);

 err = vnic_dev_fw_info(fnic->vdev, &fw_info);
 if (!err) {
  strscpy_pad(data, fw_info->hw_serial_number,
    FNIC_FDMI_SERIAL_LEN);
  fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_SERIAL_NUMBER,
   FNIC_FDMI_SERIAL_LEN, data, &attr_off_bytes);
  FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
    "SERIAL set <%s>, off=%d", data, attr_off_bytes);

 }

 if (fnic->subsys_desc_len >= FNIC_FDMI_MODEL_LEN)
  fnic->subsys_desc_len = FNIC_FDMI_MODEL_LEN - 1;
 strscpy_pad(data, fnic->subsys_desc, FNIC_FDMI_MODEL_LEN);
 fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_MODEL, FNIC_FDMI_MODEL_LEN,
  data, &attr_off_bytes);

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
   "MODEL set <%s>, off=%d", data, attr_off_bytes);

 strscpy_pad(data, FNIC_FDMI_MODEL_DESCRIPTION, FNIC_FDMI_MODEL_DES_LEN);
 fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_MODEL_DES,
  FNIC_FDMI_MODEL_DES_LEN, data, &attr_off_bytes);

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
   "MODEL_DESC set <%s>, off=%d", data, attr_off_bytes);

 if (!err) {
  strscpy_pad(data, fw_info->hw_version, FNIC_FDMI_HW_VER_LEN);
  fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_HARDWARE_VERSION,
   FNIC_FDMI_HW_VER_LEN, data, &attr_off_bytes);
  FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
    "HW_VER set <%s>, off=%d", data, attr_off_bytes);

 }

 strscpy_pad(data, DRV_VERSION, FNIC_FDMI_DR_VER_LEN);
 fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_DRIVER_VERSION,
  FNIC_FDMI_DR_VER_LEN, data, &attr_off_bytes);

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
   "DRV_VER set <%s>, off=%d", data, attr_off_bytes);

 strscpy_pad(data, "N/A", FNIC_FDMI_ROM_VER_LEN);
 fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_ROM_VERSION,
  FNIC_FDMI_ROM_VER_LEN, data, &attr_off_bytes);

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
   "ROM_VER set <%s>, off=%d", data, attr_off_bytes);

 if (!err) {
  strscpy_pad(data, fw_info->fw_version, FNIC_FDMI_FW_VER_LEN);
  fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_FIRMWARE_VERSION,
   FNIC_FDMI_FW_VER_LEN, data, &attr_off_bytes);

  FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
    "FW_VER set <%s>, off=%d", data, attr_off_bytes);
 }

 len = sizeof(struct fc_std_fdmi_rhba) + attr_off_bytes;
 frame_size += len;

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
   "0x%x: FDLS send FDMI RHBA with oxid: 0x%x fs: %d", iport->fcid,
   oxid, frame_size);

 fnic_send_fcoe_frame(iport, frame, frame_size);
 iport->fabric.fdmi_pending |= FDLS_FDMI_REG_HBA_PENDING;
}

static void fdls_fdmi_register_pa(struct fnic_iport_s *iport)
{
 uint8_t *frame;
 struct fc_std_fdmi_rpa *prpa;
 struct fc_fdmi_attr_entry *fdmi_attr;
 uint8_t fcid[3];
 struct fnic *fnic = iport->fnic;
 u32 port_speed_bm;
 u32 port_speed = vnic_dev_port_speed(fnic->vdev);
 uint16_t oxid;
 u32 attr_off_bytes, len;
 u8 tmp_data[16], data[64];
 uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET;

 frame = fdls_alloc_frame(iport);
 if (frame == NULL) {
  FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
       "Failed to allocate frame to send FDMI RPA");
  return;
 }

 prpa = (struct fc_std_fdmi_rpa *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
 *prpa = (struct fc_std_fdmi_rpa) {
  .fchdr = {
   .fh_r_ctl = FC_RCTL_DD_UNSOL_CTL,
   .fh_d_id = {0xFF, 0xFF, 0xFA},
   .fh_type = FC_TYPE_CT,
   .fh_f_ctl = {FNIC_ELS_REQ_FCTL, 0, 0},
   .fh_rx_id = cpu_to_be16(FNIC_UNASSIGNED_RXID)
  },
  .fc_std_ct_hdr = {
   .ct_rev = FC_CT_REV, .ct_fs_type = FC_FST_MGMT,
   .ct_fs_subtype = FC_FDMI_SUBTYPE,
   .ct_cmd = cpu_to_be16(FC_FDMI_RPA)
  },
 };

 hton24(fcid, iport->fcid);
 FNIC_STD_SET_S_ID(prpa->fchdr, fcid);

 oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_FDMI_RPA,
  &iport->active_oxid_fdmi_rpa);

 if (oxid == FNIC_UNASSIGNED_OXID) {
  FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
        "0x%x: Failed to allocate OXID to send FDMI RPA",
        iport->fcid);
  mempool_free(frame, fnic->frame_pool);
  return;
 }
 FNIC_STD_SET_OX_ID(prpa->fchdr, oxid);

 put_unaligned_be64(iport->wwpn, &prpa->rpa.port.portname);
 put_unaligned_be32(FNIC_FDMI_NUM_PORT_ATTRS,
    &prpa->rpa.hba_attrs.numattrs);

 /* MDS does not support GIGE speed.
 * Bit shift standard definitions from scsi_transport_fc.h to
 * match FC spec.
 */

 switch (port_speed) {
 case DCEM_PORTSPEED_10G:
 case DCEM_PORTSPEED_20G:
  /* There is no bit for 20G */
  port_speed_bm = FC_PORTSPEED_10GBIT << PORT_SPEED_BIT_14;
  break;
 case DCEM_PORTSPEED_25G:
  port_speed_bm = FC_PORTSPEED_25GBIT << PORT_SPEED_BIT_8;
  break;
 case DCEM_PORTSPEED_40G:
 case DCEM_PORTSPEED_4x10G:
  port_speed_bm = FC_PORTSPEED_40GBIT << PORT_SPEED_BIT_9;
  break;
 case DCEM_PORTSPEED_100G:
  port_speed_bm = FC_PORTSPEED_100GBIT << PORT_SPEED_BIT_8;
  break;
 default:
  port_speed_bm = FC_PORTSPEED_1GBIT << PORT_SPEED_BIT_15;
  break;
 }
 attr_off_bytes = 0;

 fdmi_attr = prpa->rpa.hba_attrs.attr;

 put_unaligned_be64(iport->wwnn, data);

 memset(data, 0, FNIC_FDMI_FC4_LEN);
 data[2] = 1;
 fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_FC4_TYPES,
  FNIC_FDMI_FC4_LEN, data, &attr_off_bytes);

 put_unaligned_be32(port_speed_bm, data);
 fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_SUPPORTED_SPEEDS,
  FNIC_FDMI_SUPP_SPEED_LEN, data, &attr_off_bytes);

 put_unaligned_be32(port_speed_bm, data);
 fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_CURRENT_SPEED,
  FNIC_FDMI_CUR_SPEED_LEN, data, &attr_off_bytes);

 put_unaligned_be32(FNIC_FDMI_MFS, data);
 fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_MAX_FRAME_SIZE,
  FNIC_FDMI_MFS_LEN, data, &attr_off_bytes);

 snprintf(tmp_data, FNIC_FDMI_OS_NAME_LEN - 1, "host%d",
   fnic->host->host_no);
 strscpy_pad(data, tmp_data, FNIC_FDMI_OS_NAME_LEN);
 fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_OS_NAME,
  FNIC_FDMI_OS_NAME_LEN, data, &attr_off_bytes);

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
   "OS name set <%s>, off=%d", data, attr_off_bytes);

 sprintf(fc_host_system_hostname(fnic->host), "%s", utsname()->nodename);
 strscpy_pad(data, fc_host_system_hostname(fnic->host),
     FNIC_FDMI_HN_LEN);
 fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_HOST_NAME,
  FNIC_FDMI_HN_LEN, data, &attr_off_bytes);

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
   "Host name set <%s>, off=%d", data, attr_off_bytes);

 len = sizeof(struct fc_std_fdmi_rpa) + attr_off_bytes;
 frame_size += len;

 FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
   "0x%x: FDLS send FDMI RPA with oxid: 0x%x fs: %d", iport->fcid,
   oxid, frame_size);

 fnic_send_fcoe_frame(iport, frame, frame_size);
 iport->fabric.fdmi_pending |= FDLS_FDMI_RPA_PENDING;
}

void fdls_fabric_timer_callback(struct timer_list *t)
{
 struct fnic_fdls_fabric_s *fabric = timer_container_of(fabric, t,
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=98 H=94 G=95

[ zur Elbe Produktseite wechseln0.39Quellennavigators  Analyse erneut starten  ]