Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c57ec8fb authored by Nilesh Javali's avatar Nilesh Javali Committed by Martin K. Petersen
Browse files

scsi: qedi: Add support for Boot from SAN over iSCSI offload



This patch adds support for Boot from SAN over iSCSI offload. The iSCSI
boot information in the NVRAM is populated under
/sys/firmware/iscsi_bootX/ using qed NVM-image reading API and further
exported to open-iscsi to perform iSCSI login enabling boot over offload
iSCSI interface in a Boot from SAN environment.

Signed-off-by: default avatarArun Easi <arun.easi@cavium.com>
Signed-off-by: default avatarAndrew Vasquez <andrew.vasquez@cavium.com>
Signed-off-by: default avatarManish Rangankar <manish.rangankar@cavium.com>
Signed-off-by: default avatarNilesh Javali <nilesh.javali@cavium.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 130568d5
Loading
Loading
Loading
Loading
+17 −0
Original line number Original line Diff line number Diff line
@@ -23,11 +23,17 @@
#include <linux/qed/qed_iscsi_if.h>
#include <linux/qed/qed_iscsi_if.h>
#include <linux/qed/qed_ll2_if.h>
#include <linux/qed/qed_ll2_if.h>
#include "qedi_version.h"
#include "qedi_version.h"
#include "qedi_nvm_iscsi_cfg.h"


#define QEDI_MODULE_NAME		"qedi"
#define QEDI_MODULE_NAME		"qedi"


struct qedi_endpoint;
struct qedi_endpoint;


#ifndef GET_FIELD2
#define GET_FIELD2(value, name) \
	(((value) & (name ## _MASK)) >> (name ## _OFFSET))
#endif

/*
/*
 * PCI function probe defines
 * PCI function probe defines
 */
 */
@@ -66,6 +72,11 @@ struct qedi_endpoint;
#define QEDI_HW_DMA_BOUNDARY	0xfff
#define QEDI_HW_DMA_BOUNDARY	0xfff
#define QEDI_PATH_HANDLE	0xFE0000000UL
#define QEDI_PATH_HANDLE	0xFE0000000UL


enum qedi_nvm_tgts {
	QEDI_NVM_TGT_PRI,
	QEDI_NVM_TGT_SEC,
};

struct qedi_uio_ctrl {
struct qedi_uio_ctrl {
	/* meta data */
	/* meta data */
	u32 uio_hsi_version;
	u32 uio_hsi_version;
@@ -283,6 +294,8 @@ struct qedi_ctx {
	void *bdq_pbl_list;
	void *bdq_pbl_list;
	dma_addr_t bdq_pbl_list_dma;
	dma_addr_t bdq_pbl_list_dma;
	u8 bdq_pbl_list_num_entries;
	u8 bdq_pbl_list_num_entries;
	struct nvm_iscsi_cfg *iscsi_cfg;
	dma_addr_t nvm_buf_dma;
	void __iomem *bdq_primary_prod;
	void __iomem *bdq_primary_prod;
	void __iomem *bdq_secondary_prod;
	void __iomem *bdq_secondary_prod;
	u16 bdq_prod_idx;
	u16 bdq_prod_idx;
@@ -337,6 +350,10 @@ struct qedi_ctx {
	bool use_fast_sge;
	bool use_fast_sge;


	atomic_t num_offloads;
	atomic_t num_offloads;
#define SYSFS_FLAG_FW_SEL_BOOT 2
#define IPV6_LEN	41
#define IPV4_LEN	17
	struct iscsi_boot_kset *boot_kset;
};
};


struct qedi_work {
struct qedi_work {
+419 −0
Original line number Original line Diff line number Diff line
@@ -19,6 +19,7 @@
#include <linux/mm.h>
#include <linux/mm.h>
#include <linux/if_vlan.h>
#include <linux/if_vlan.h>
#include <linux/cpu.h>
#include <linux/cpu.h>
#include <linux/iscsi_boot_sysfs.h>


#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_device.h>
@@ -1143,6 +1144,30 @@ static int qedi_setup_int(struct qedi_ctx *qedi)
	return rc;
	return rc;
}
}


static void qedi_free_nvm_iscsi_cfg(struct qedi_ctx *qedi)
{
	if (qedi->iscsi_cfg)
		dma_free_coherent(&qedi->pdev->dev,
				  sizeof(struct nvm_iscsi_cfg),
				  qedi->iscsi_cfg, qedi->nvm_buf_dma);
}

static int qedi_alloc_nvm_iscsi_cfg(struct qedi_ctx *qedi)
{
	qedi->iscsi_cfg = dma_zalloc_coherent(&qedi->pdev->dev,
					     sizeof(struct nvm_iscsi_cfg),
					     &qedi->nvm_buf_dma, GFP_KERNEL);
	if (!qedi->iscsi_cfg) {
		QEDI_ERR(&qedi->dbg_ctx, "Could not allocate NVM BUF.\n");
		return -ENOMEM;
	}
	QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO,
		  "NVM BUF addr=0x%p dma=0x%llx.\n", qedi->iscsi_cfg,
		  qedi->nvm_buf_dma);

	return 0;
}

static void qedi_free_bdq(struct qedi_ctx *qedi)
static void qedi_free_bdq(struct qedi_ctx *qedi)
{
{
	int i;
	int i;
@@ -1183,6 +1208,7 @@ static void qedi_free_global_queues(struct qedi_ctx *qedi)
		kfree(gl[i]);
		kfree(gl[i]);
	}
	}
	qedi_free_bdq(qedi);
	qedi_free_bdq(qedi);
	qedi_free_nvm_iscsi_cfg(qedi);
}
}


static int qedi_alloc_bdq(struct qedi_ctx *qedi)
static int qedi_alloc_bdq(struct qedi_ctx *qedi)
@@ -1309,6 +1335,11 @@ static int qedi_alloc_global_queues(struct qedi_ctx *qedi)
	if (rc)
	if (rc)
		goto mem_alloc_failure;
		goto mem_alloc_failure;


	/* Allocate DMA coherent buffers for NVM_ISCSI_CFG */
	rc = qedi_alloc_nvm_iscsi_cfg(qedi);
	if (rc)
		goto mem_alloc_failure;

	/* Allocate a CQ and an associated PBL for each MSI-X
	/* Allocate a CQ and an associated PBL for each MSI-X
	 * vector.
	 * vector.
	 */
	 */
@@ -1671,6 +1702,387 @@ void qedi_reset_host_mtu(struct qedi_ctx *qedi, u16 mtu)
	qedi_ops->ll2->start(qedi->cdev, &params);
	qedi_ops->ll2->start(qedi->cdev, &params);
}
}


/**
 * qedi_get_nvram_block: - Scan through the iSCSI NVRAM block (while accounting
 * for gaps) for the matching absolute-pf-id of the QEDI device.
 */
static struct nvm_iscsi_block *
qedi_get_nvram_block(struct qedi_ctx *qedi)
{
	int i;
	u8 pf;
	u32 flags;
	struct nvm_iscsi_block *block;

	pf = qedi->dev_info.common.abs_pf_id;
	block = &qedi->iscsi_cfg->block[0];
	for (i = 0; i < NUM_OF_ISCSI_PF_SUPPORTED; i++, block++) {
		flags = ((block->id) & NVM_ISCSI_CFG_BLK_CTRL_FLAG_MASK) >>
			NVM_ISCSI_CFG_BLK_CTRL_FLAG_OFFSET;
		if (flags & (NVM_ISCSI_CFG_BLK_CTRL_FLAG_IS_NOT_EMPTY |
				NVM_ISCSI_CFG_BLK_CTRL_FLAG_PF_MAPPED) &&
			(pf == (block->id & NVM_ISCSI_CFG_BLK_MAPPED_PF_ID_MASK)
				>> NVM_ISCSI_CFG_BLK_MAPPED_PF_ID_OFFSET))
			return block;
	}
	return NULL;
}

static ssize_t qedi_show_boot_eth_info(void *data, int type, char *buf)
{
	struct qedi_ctx *qedi = data;
	struct nvm_iscsi_initiator *initiator;
	char *str = buf;
	int rc = 1;
	u32 ipv6_en, dhcp_en, ip_len;
	struct nvm_iscsi_block *block;
	char *fmt, *ip, *sub, *gw;

	block = qedi_get_nvram_block(qedi);
	if (!block)
		return 0;

	initiator = &block->initiator;
	ipv6_en = block->generic.ctrl_flags &
		  NVM_ISCSI_CFG_GEN_IPV6_ENABLED;
	dhcp_en = block->generic.ctrl_flags &
		  NVM_ISCSI_CFG_GEN_DHCP_TCPIP_CONFIG_ENABLED;
	/* Static IP assignments. */
	fmt = ipv6_en ? "%pI6\n" : "%pI4\n";
	ip = ipv6_en ? initiator->ipv6.addr.byte : initiator->ipv4.addr.byte;
	ip_len = ipv6_en ? IPV6_LEN : IPV4_LEN;
	sub = ipv6_en ? initiator->ipv6.subnet_mask.byte :
	      initiator->ipv4.subnet_mask.byte;
	gw = ipv6_en ? initiator->ipv6.gateway.byte :
	     initiator->ipv4.gateway.byte;
	/* DHCP IP adjustments. */
	fmt = dhcp_en ? "%s\n" : fmt;
	if (dhcp_en) {
		ip = ipv6_en ? "0::0" : "0.0.0.0";
		sub = ip;
		gw = ip;
		ip_len = ipv6_en ? 5 : 8;
	}

	switch (type) {
	case ISCSI_BOOT_ETH_IP_ADDR:
		rc = snprintf(str, ip_len, fmt, ip);
		break;
	case ISCSI_BOOT_ETH_SUBNET_MASK:
		rc = snprintf(str, ip_len, fmt, sub);
		break;
	case ISCSI_BOOT_ETH_GATEWAY:
		rc = snprintf(str, ip_len, fmt, gw);
		break;
	case ISCSI_BOOT_ETH_FLAGS:
		rc = snprintf(str, 3, "%hhd\n",
			      SYSFS_FLAG_FW_SEL_BOOT);
		break;
	case ISCSI_BOOT_ETH_INDEX:
		rc = snprintf(str, 3, "0\n");
		break;
	case ISCSI_BOOT_ETH_MAC:
		rc = sysfs_format_mac(str, qedi->mac, ETH_ALEN);
		break;
	case ISCSI_BOOT_ETH_VLAN:
		rc = snprintf(str, 12, "%d\n",
			      GET_FIELD2(initiator->generic_cont0,
					 NVM_ISCSI_CFG_INITIATOR_VLAN));
		break;
	case ISCSI_BOOT_ETH_ORIGIN:
		if (dhcp_en)
			rc = snprintf(str, 3, "3\n");
		break;
	default:
		rc = 0;
		break;
	}

	return rc;
}

static umode_t qedi_eth_get_attr_visibility(void *data, int type)
{
	int rc = 1;

	switch (type) {
	case ISCSI_BOOT_ETH_FLAGS:
	case ISCSI_BOOT_ETH_MAC:
	case ISCSI_BOOT_ETH_INDEX:
	case ISCSI_BOOT_ETH_IP_ADDR:
	case ISCSI_BOOT_ETH_SUBNET_MASK:
	case ISCSI_BOOT_ETH_GATEWAY:
	case ISCSI_BOOT_ETH_ORIGIN:
	case ISCSI_BOOT_ETH_VLAN:
		rc = 0444;
		break;
	default:
		rc = 0;
		break;
	}
	return rc;
}

static ssize_t qedi_show_boot_ini_info(void *data, int type, char *buf)
{
	struct qedi_ctx *qedi = data;
	struct nvm_iscsi_initiator *initiator;
	char *str = buf;
	int rc;
	struct nvm_iscsi_block *block;

	block = qedi_get_nvram_block(qedi);
	if (!block)
		return 0;

	initiator = &block->initiator;

	switch (type) {
	case ISCSI_BOOT_INI_INITIATOR_NAME:
		rc = snprintf(str, NVM_ISCSI_CFG_ISCSI_NAME_MAX_LEN, "%s\n",
			      initiator->initiator_name.byte);
		break;
	default:
		rc = 0;
		break;
	}
	return rc;
}

static umode_t qedi_ini_get_attr_visibility(void *data, int type)
{
	int rc;

	switch (type) {
	case ISCSI_BOOT_INI_INITIATOR_NAME:
		rc = 0444;
		break;
	default:
		rc = 0;
		break;
	}
	return rc;
}

static ssize_t
qedi_show_boot_tgt_info(struct qedi_ctx *qedi, int type,
			char *buf, enum qedi_nvm_tgts idx)
{
	char *str = buf;
	int rc = 1;
	u32 ctrl_flags, ipv6_en, chap_en, mchap_en, ip_len;
	struct nvm_iscsi_block *block;
	char *chap_name, *chap_secret;
	char *mchap_name, *mchap_secret;

	block = qedi_get_nvram_block(qedi);
	if (!block)
		goto exit_show_tgt_info;

	QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_EVT,
		  "Port:%d, tgt_idx:%d\n",
		  GET_FIELD2(block->id, NVM_ISCSI_CFG_BLK_MAPPED_PF_ID), idx);

	ctrl_flags = block->target[idx].ctrl_flags &
		     NVM_ISCSI_CFG_TARGET_ENABLED;

	if (!ctrl_flags) {
		QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_EVT,
			  "Target disabled\n");
		goto exit_show_tgt_info;
	}

	ipv6_en = block->generic.ctrl_flags &
		  NVM_ISCSI_CFG_GEN_IPV6_ENABLED;
	ip_len = ipv6_en ? IPV6_LEN : IPV4_LEN;
	chap_en = block->generic.ctrl_flags &
		  NVM_ISCSI_CFG_GEN_CHAP_ENABLED;
	chap_name = chap_en ? block->initiator.chap_name.byte : NULL;
	chap_secret = chap_en ? block->initiator.chap_password.byte : NULL;

	mchap_en = block->generic.ctrl_flags &
		  NVM_ISCSI_CFG_GEN_CHAP_MUTUAL_ENABLED;
	mchap_name = mchap_en ? block->target[idx].chap_name.byte : NULL;
	mchap_secret = mchap_en ? block->target[idx].chap_password.byte : NULL;

	switch (type) {
	case ISCSI_BOOT_TGT_NAME:
		rc = snprintf(str, NVM_ISCSI_CFG_ISCSI_NAME_MAX_LEN, "%s\n",
			      block->target[idx].target_name.byte);
		break;
	case ISCSI_BOOT_TGT_IP_ADDR:
		if (ipv6_en)
			rc = snprintf(str, ip_len, "%pI6\n",
				      block->target[idx].ipv6_addr.byte);
		else
			rc = snprintf(str, ip_len, "%pI4\n",
				      block->target[idx].ipv4_addr.byte);
		break;
	case ISCSI_BOOT_TGT_PORT:
		rc = snprintf(str, 12, "%d\n",
			      GET_FIELD2(block->target[idx].generic_cont0,
					 NVM_ISCSI_CFG_TARGET_TCP_PORT));
		break;
	case ISCSI_BOOT_TGT_LUN:
		rc = snprintf(str, 22, "%.*d\n",
			      block->target[idx].lun.value[1],
			      block->target[idx].lun.value[0]);
		break;
	case ISCSI_BOOT_TGT_CHAP_NAME:
		rc = snprintf(str, NVM_ISCSI_CFG_CHAP_NAME_MAX_LEN, "%s\n",
			      chap_name);
		break;
	case ISCSI_BOOT_TGT_CHAP_SECRET:
		rc = snprintf(str, NVM_ISCSI_CFG_CHAP_PWD_MAX_LEN, "%s\n",
			      chap_secret);
		break;
	case ISCSI_BOOT_TGT_REV_CHAP_NAME:
		rc = snprintf(str, NVM_ISCSI_CFG_CHAP_NAME_MAX_LEN, "%s\n",
			      mchap_name);
		break;
	case ISCSI_BOOT_TGT_REV_CHAP_SECRET:
		rc = snprintf(str, NVM_ISCSI_CFG_CHAP_PWD_MAX_LEN, "%s\n",
			      mchap_secret);
		break;
	case ISCSI_BOOT_TGT_FLAGS:
		rc = snprintf(str, 3, "%hhd\n", SYSFS_FLAG_FW_SEL_BOOT);
		break;
	case ISCSI_BOOT_TGT_NIC_ASSOC:
		rc = snprintf(str, 3, "0\n");
		break;
	default:
		rc = 0;
		break;
	}

exit_show_tgt_info:
	return rc;
}

static ssize_t qedi_show_boot_tgt_pri_info(void *data, int type, char *buf)
{
	struct qedi_ctx *qedi = data;

	return qedi_show_boot_tgt_info(qedi, type, buf, QEDI_NVM_TGT_PRI);
}

static ssize_t qedi_show_boot_tgt_sec_info(void *data, int type, char *buf)
{
	struct qedi_ctx *qedi = data;

	return qedi_show_boot_tgt_info(qedi, type, buf, QEDI_NVM_TGT_SEC);
}

static umode_t qedi_tgt_get_attr_visibility(void *data, int type)
{
	int rc;

	switch (type) {
	case ISCSI_BOOT_TGT_NAME:
	case ISCSI_BOOT_TGT_IP_ADDR:
	case ISCSI_BOOT_TGT_PORT:
	case ISCSI_BOOT_TGT_LUN:
	case ISCSI_BOOT_TGT_CHAP_NAME:
	case ISCSI_BOOT_TGT_CHAP_SECRET:
	case ISCSI_BOOT_TGT_REV_CHAP_NAME:
	case ISCSI_BOOT_TGT_REV_CHAP_SECRET:
	case ISCSI_BOOT_TGT_NIC_ASSOC:
	case ISCSI_BOOT_TGT_FLAGS:
		rc = 0444;
		break;
	default:
		rc = 0;
		break;
	}
	return rc;
}

static void qedi_boot_release(void *data)
{
	struct qedi_ctx *qedi = data;

	scsi_host_put(qedi->shost);
}

static int qedi_get_boot_info(struct qedi_ctx *qedi)
{
	int ret = 1;
	u16 len;

	len = sizeof(struct nvm_iscsi_cfg);

	QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO,
		  "Get NVM iSCSI CFG image\n");
	ret = qedi_ops->common->nvm_get_image(qedi->cdev,
					      QED_NVM_IMAGE_ISCSI_CFG,
					      (char *)qedi->iscsi_cfg, len);
	if (ret)
		QEDI_ERR(&qedi->dbg_ctx,
			 "Could not get NVM image. ret = %d\n", ret);

	return ret;
}

static int qedi_setup_boot_info(struct qedi_ctx *qedi)
{
	struct iscsi_boot_kobj *boot_kobj;

	if (qedi_get_boot_info(qedi))
		return -EPERM;

	qedi->boot_kset = iscsi_boot_create_host_kset(qedi->shost->host_no);
	if (!qedi->boot_kset)
		goto kset_free;

	if (!scsi_host_get(qedi->shost))
		goto kset_free;

	boot_kobj = iscsi_boot_create_target(qedi->boot_kset, 0, qedi,
					     qedi_show_boot_tgt_pri_info,
					     qedi_tgt_get_attr_visibility,
					     qedi_boot_release);
	if (!boot_kobj)
		goto put_host;

	if (!scsi_host_get(qedi->shost))
		goto kset_free;

	boot_kobj = iscsi_boot_create_target(qedi->boot_kset, 1, qedi,
					     qedi_show_boot_tgt_sec_info,
					     qedi_tgt_get_attr_visibility,
					     qedi_boot_release);
	if (!boot_kobj)
		goto put_host;

	if (!scsi_host_get(qedi->shost))
		goto kset_free;

	boot_kobj = iscsi_boot_create_initiator(qedi->boot_kset, 0, qedi,
						qedi_show_boot_ini_info,
						qedi_ini_get_attr_visibility,
						qedi_boot_release);
	if (!boot_kobj)
		goto put_host;

	if (!scsi_host_get(qedi->shost))
		goto kset_free;

	boot_kobj = iscsi_boot_create_ethernet(qedi->boot_kset, 0, qedi,
					       qedi_show_boot_eth_info,
					       qedi_eth_get_attr_visibility,
					       qedi_boot_release);
	if (!boot_kobj)
		goto put_host;

	return 0;

put_host:
	scsi_host_put(qedi->shost);
kset_free:
	iscsi_boot_destroy_kset(qedi->boot_kset);
	return -ENOMEM;
}

static void __qedi_remove(struct pci_dev *pdev, int mode)
static void __qedi_remove(struct pci_dev *pdev, int mode)
{
{
	struct qedi_ctx *qedi = pci_get_drvdata(pdev);
	struct qedi_ctx *qedi = pci_get_drvdata(pdev);
@@ -1724,6 +2136,9 @@ static void __qedi_remove(struct pci_dev *pdev, int mode)
			qedi->ll2_recv_thread = NULL;
			qedi->ll2_recv_thread = NULL;
		}
		}
		qedi_ll2_free_skbs(qedi);
		qedi_ll2_free_skbs(qedi);

		if (qedi->boot_kset)
			iscsi_boot_destroy_kset(qedi->boot_kset);
	}
	}
}
}


@@ -1967,6 +2382,10 @@ static int __qedi_probe(struct pci_dev *pdev, int mode)
		/* F/w needs 1st task context memory entry for performance */
		/* F/w needs 1st task context memory entry for performance */
		set_bit(QEDI_RESERVE_TASK_ID, qedi->task_idx_map);
		set_bit(QEDI_RESERVE_TASK_ID, qedi->task_idx_map);
		atomic_set(&qedi->num_offloads, 0);
		atomic_set(&qedi->num_offloads, 0);

		if (qedi_setup_boot_info(qedi))
			QEDI_ERR(&qedi->dbg_ctx,
				 "No iSCSI boot target configured\n");
	}
	}


	return 0;
	return 0;
+210 −0
Original line number Original line Diff line number Diff line
/*
 * QLogic iSCSI Offload Driver
 * Copyright (c) 2016 Cavium Inc.
 *
 * This software is available under the terms of the GNU General Public License
 * (GPL) Version 2, available from the file COPYING in the main directory of
 * this source tree.
 */

#ifndef NVM_ISCSI_CFG_H
#define NVM_ISCSI_CFG_H

#define NUM_OF_ISCSI_TARGET_PER_PF    4   /* Defined as per the
					   * ISCSI IBFT constraint
					   */
#define NUM_OF_ISCSI_PF_SUPPORTED     4   /* One PF per Port -
					   * assuming 4 port card
					   */

#define NVM_ISCSI_CFG_DHCP_NAME_MAX_LEN  256

union nvm_iscsi_dhcp_vendor_id {
	u32 value[NVM_ISCSI_CFG_DHCP_NAME_MAX_LEN / 4];
	u8  byte[NVM_ISCSI_CFG_DHCP_NAME_MAX_LEN];
};

#define NVM_ISCSI_IPV4_ADDR_BYTE_LEN 4
union nvm_iscsi_ipv4_addr {
	u32 addr;
	u8  byte[NVM_ISCSI_IPV4_ADDR_BYTE_LEN];
};

#define NVM_ISCSI_IPV6_ADDR_BYTE_LEN 16
union nvm_iscsi_ipv6_addr {
	u32 addr[4];
	u8  byte[NVM_ISCSI_IPV6_ADDR_BYTE_LEN];
};

struct nvm_iscsi_initiator_ipv4 {
	union nvm_iscsi_ipv4_addr addr;				/* 0x0 */
	union nvm_iscsi_ipv4_addr subnet_mask;			/* 0x4 */
	union nvm_iscsi_ipv4_addr gateway;			/* 0x8 */
	union nvm_iscsi_ipv4_addr primary_dns;			/* 0xC */
	union nvm_iscsi_ipv4_addr secondary_dns;		/* 0x10 */
	union nvm_iscsi_ipv4_addr dhcp_addr;			/* 0x14 */

	union nvm_iscsi_ipv4_addr isns_server;			/* 0x18 */
	union nvm_iscsi_ipv4_addr slp_server;			/* 0x1C */
	union nvm_iscsi_ipv4_addr primay_radius_server;		/* 0x20 */
	union nvm_iscsi_ipv4_addr secondary_radius_server;	/* 0x24 */

	union nvm_iscsi_ipv4_addr rsvd[4];			/* 0x28 */
};

struct nvm_iscsi_initiator_ipv6 {
	union nvm_iscsi_ipv6_addr addr;				/* 0x0 */
	union nvm_iscsi_ipv6_addr subnet_mask;			/* 0x10 */
	union nvm_iscsi_ipv6_addr gateway;			/* 0x20 */
	union nvm_iscsi_ipv6_addr primary_dns;			/* 0x30 */
	union nvm_iscsi_ipv6_addr secondary_dns;		/* 0x40 */
	union nvm_iscsi_ipv6_addr dhcp_addr;			/* 0x50 */

	union nvm_iscsi_ipv6_addr isns_server;			/* 0x60 */
	union nvm_iscsi_ipv6_addr slp_server;			/* 0x70 */
	union nvm_iscsi_ipv6_addr primay_radius_server;		/* 0x80 */
	union nvm_iscsi_ipv6_addr secondary_radius_server;	/* 0x90 */

	union nvm_iscsi_ipv6_addr rsvd[3];			/* 0xA0 */

	u32   config;						/* 0xD0 */
#define NVM_ISCSI_CFG_INITIATOR_IPV6_SUBNET_MASK_PREFIX_MASK      0x000000FF
#define NVM_ISCSI_CFG_INITIATOR_IPV6_SUBNET_MASK_PREFIX_OFFSET    0

	u32   rsvd_1[3];
};

#define NVM_ISCSI_CFG_ISCSI_NAME_MAX_LEN  256
union nvm_iscsi_name {
	u32 value[NVM_ISCSI_CFG_ISCSI_NAME_MAX_LEN / 4];
	u8  byte[NVM_ISCSI_CFG_ISCSI_NAME_MAX_LEN];
};

#define NVM_ISCSI_CFG_CHAP_NAME_MAX_LEN  256
union nvm_iscsi_chap_name {
	u32 value[NVM_ISCSI_CFG_CHAP_NAME_MAX_LEN / 4];
	u8  byte[NVM_ISCSI_CFG_CHAP_NAME_MAX_LEN];
};

#define NVM_ISCSI_CFG_CHAP_PWD_MAX_LEN  16 /* md5 need per RFC1996
					    * is 16 octets
					    */
union nvm_iscsi_chap_password {
	u32 value[NVM_ISCSI_CFG_CHAP_PWD_MAX_LEN / 4];
	u8 byte[NVM_ISCSI_CFG_CHAP_PWD_MAX_LEN];
};

union nvm_iscsi_lun {
	u8  byte[8];
	u32 value[2];
};

struct nvm_iscsi_generic {
	u32 ctrl_flags;						/* 0x0 */
#define NVM_ISCSI_CFG_GEN_CHAP_ENABLED                 BIT(0)
#define NVM_ISCSI_CFG_GEN_DHCP_TCPIP_CONFIG_ENABLED    BIT(1)
#define NVM_ISCSI_CFG_GEN_DHCP_ISCSI_CONFIG_ENABLED    BIT(2)
#define NVM_ISCSI_CFG_GEN_IPV6_ENABLED                 BIT(3)
#define NVM_ISCSI_CFG_GEN_IPV4_FALLBACK_ENABLED        BIT(4)
#define NVM_ISCSI_CFG_GEN_ISNS_WORLD_LOGIN             BIT(5)
#define NVM_ISCSI_CFG_GEN_ISNS_SELECTIVE_LOGIN         BIT(6)
#define NVM_ISCSI_CFG_GEN_ADDR_REDIRECT_ENABLED	       BIT(7)
#define NVM_ISCSI_CFG_GEN_CHAP_MUTUAL_ENABLED          BIT(8)

	u32 timeout;						/* 0x4 */
#define NVM_ISCSI_CFG_GEN_DHCP_REQUEST_TIMEOUT_MASK       0x0000FFFF
#define NVM_ISCSI_CFG_GEN_DHCP_REQUEST_TIMEOUT_OFFSET     0
#define NVM_ISCSI_CFG_GEN_PORT_LOGIN_TIMEOUT_MASK         0xFFFF0000
#define NVM_ISCSI_CFG_GEN_PORT_LOGIN_TIMEOUT_OFFSET       16

	union nvm_iscsi_dhcp_vendor_id  dhcp_vendor_id;		/* 0x8  */
	u32 rsvd[62];						/* 0x108 */
};

struct nvm_iscsi_initiator {
	struct nvm_iscsi_initiator_ipv4 ipv4;			/* 0x0 */
	struct nvm_iscsi_initiator_ipv6 ipv6;			/* 0x38 */

	union nvm_iscsi_name           initiator_name;		/* 0x118 */
	union nvm_iscsi_chap_name      chap_name;		/* 0x218 */
	union nvm_iscsi_chap_password  chap_password;		/* 0x318 */

	u32 generic_cont0;					/* 0x398 */
#define NVM_ISCSI_CFG_INITIATOR_VLAN_MASK		0x0000FFFF
#define NVM_ISCSI_CFG_INITIATOR_VLAN_OFFSET		0
#define NVM_ISCSI_CFG_INITIATOR_IP_VERSION_MASK		0x00030000
#define NVM_ISCSI_CFG_INITIATOR_IP_VERSION_OFFSET	16
#define NVM_ISCSI_CFG_INITIATOR_IP_VERSION_4		1
#define NVM_ISCSI_CFG_INITIATOR_IP_VERSION_6		2
#define NVM_ISCSI_CFG_INITIATOR_IP_VERSION_4_AND_6	3

	u32 ctrl_flags;
#define NVM_ISCSI_CFG_INITIATOR_IP_VERSION_PRIORITY_V6     BIT(0)
#define NVM_ISCSI_CFG_INITIATOR_VLAN_ENABLED               BIT(1)

	u32 rsvd[116];						/* 0x32C */
};

struct nvm_iscsi_target {
	u32 ctrl_flags;						/* 0x0 */
#define NVM_ISCSI_CFG_TARGET_ENABLED            BIT(0)
#define NVM_ISCSI_CFG_BOOT_TIME_LOGIN_STATUS    BIT(1)

	u32 generic_cont0;					/* 0x4 */
#define NVM_ISCSI_CFG_TARGET_TCP_PORT_MASK      0x0000FFFF
#define NVM_ISCSI_CFG_TARGET_TCP_PORT_OFFSET    0

	u32 ip_ver;
#define NVM_ISCSI_CFG_IPv4       4
#define NVM_ISCSI_CFG_IPv6       6

	u32 rsvd_1[7];						/* 0x24 */
	union nvm_iscsi_ipv4_addr ipv4_addr;			/* 0x28 */
	union nvm_iscsi_ipv6_addr ipv6_addr;			/* 0x2C */
	union nvm_iscsi_lun lun;				/* 0x3C */

	union nvm_iscsi_name           target_name;		/* 0x44 */
	union nvm_iscsi_chap_name      chap_name;		/* 0x144 */
	union nvm_iscsi_chap_password  chap_password;		/* 0x244 */

	u32 rsvd_2[107];					/* 0x2C4 */
};

struct nvm_iscsi_block {
	u32 id;							/* 0x0 */
#define NVM_ISCSI_CFG_BLK_MAPPED_PF_ID_MASK         0x0000000F
#define NVM_ISCSI_CFG_BLK_MAPPED_PF_ID_OFFSET       0
#define NVM_ISCSI_CFG_BLK_CTRL_FLAG_MASK            0x00000FF0
#define NVM_ISCSI_CFG_BLK_CTRL_FLAG_OFFSET          4
#define NVM_ISCSI_CFG_BLK_CTRL_FLAG_IS_NOT_EMPTY    BIT(0)
#define NVM_ISCSI_CFG_BLK_CTRL_FLAG_PF_MAPPED       BIT(1)

	u32 rsvd_1[5];						/* 0x4 */

	struct nvm_iscsi_generic     generic;			/* 0x18 */
	struct nvm_iscsi_initiator   initiator;			/* 0x218 */
	struct nvm_iscsi_target      target[NUM_OF_ISCSI_TARGET_PER_PF];
								/* 0x718 */

	u32 rsvd_2[58];						/* 0x1718 */
	/* total size - 0x1800 - 6K block */
};

struct nvm_iscsi_cfg {
	u32 id;							/* 0x0 */
#define NVM_ISCSI_CFG_BLK_VERSION_MINOR_MASK     0x000000FF
#define NVM_ISCSI_CFG_BLK_VERSION_MAJOR_MASK     0x0000FF00
#define NVM_ISCSI_CFG_BLK_SIGNATURE_MASK         0xFFFF0000
#define NVM_ISCSI_CFG_BLK_SIGNATURE              0x49430000 /* IC - Iscsi
							     * Config
							     */

#define NVM_ISCSI_CFG_BLK_VERSION_MAJOR          0
#define NVM_ISCSI_CFG_BLK_VERSION_MINOR          10
#define NVM_ISCSI_CFG_BLK_VERSION ((NVM_ISCSI_CFG_BLK_VERSION_MAJOR << 8) | \
				   NVM_ISCSI_CFG_BLK_VERSION_MINOR)

	struct nvm_iscsi_block	block[NUM_OF_ISCSI_PF_SUPPORTED]; /* 0x4 */
};

#endif