diff mbox series

[v7,07/15] scsi: fnic: Add and integrate support for FDMI

Message ID 20241212020312.4786-8-kartilak@cisco.com
State New
Headers show
Series Introduce support for Fabric Discovery and... | expand

Commit Message

Karan Tilak Kumar (kartilak) Dec. 12, 2024, 2:03 a.m. UTC
Add support for Fabric-Device Management Interface
(FDMI) by introducing PCI device IDs for Cisco
Hardware.
Introduce a module parameter to enable/disable
FDMI support.
Integrate support for FDMI.

Reported-by: kernel test robot <lkp@intel.com>
Closes:
https://lore.kernel.org/oe-kbuild-all/202406110734.p2v8dq9v-lkp
@intel.com/

Reviewed-by: Sesidhar Baddela <sebaddel@cisco.com>
Co-developed-by: Gian Carlo Boffa <gcboffa@cisco.com>
Signed-off-by: Gian Carlo Boffa <gcboffa@cisco.com>
Co-developed-by: Arulprabhu Ponnusamy <arulponn@cisco.com>
Signed-off-by: Arulprabhu Ponnusamy <arulponn@cisco.com>
Co-developed-by: Arun Easi <aeasi@cisco.com>
Signed-off-by: Arun Easi <aeasi@cisco.com>
Co-developed-by: Karan Tilak Kumar <kartilak@cisco.com>
Signed-off-by: Karan Tilak Kumar <kartilak@cisco.com>
---
Changes between v5 and v6:
    Incorporate review comments from Martin:
        Remove GCC 13.3 warnings.
    Incorporate review comments from Hannes:
	Split patch into PCI changes and FDMI changes.
	Allocate OXID from a pool.
	Modify frame initialization.

Changes between v4 and v5:
    Incorporate review comments from Martin:
	Modify attribution appropriately.
	Remove spurious newline at the end of fdls_disc.c.
	Remove spurious newline at the end of fnic_main.c.

Changes between v2 and v3:
    Incorporate review comments from Hannes:
	Replace redundant definitions with standard definitions.

Changes between v1 and v2:
    Incorporate review comments from Hannes from other patches:
	Replace pr_info with dev_info.
	Replace htonll() with get_unaligned_be64().
	Replace definitions with standard definitions from fc_els.h.
	Use standard definitions from scsi_transport_fc.h for
	port speeds.
	Refactor definitions in struct fnic to avoid cache holes.
	Replace memcmp with not equal to operator.
    Fix warning from kernel test robot:
	Remove version.h
---
 drivers/scsi/fnic/fdls_disc.c | 658 ++++++++++++++++++++++++++++++++++
 drivers/scsi/fnic/fnic.h      |   1 +
 drivers/scsi/fnic/fnic_main.c |  19 +-
 3 files changed, 676 insertions(+), 2 deletions(-)

Comments

Dan Carpenter Jan. 7, 2025, 12:30 p.m. UTC | #1
On Wed, Dec 11, 2024 at 06:03:04PM -0800, Karan Tilak Kumar wrote:
> @@ -612,6 +615,7 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
>  	unsigned long flags;
>  	int hwq;
>  	char *desc, *subsys_desc;
> +	int len;

Do not introduce unnecessary levels of indirection.  Get rid of this len
variable.

>  
>  	/*
>  	 * Allocate SCSI Host and set up association between host,
> @@ -646,9 +650,17 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
>  	fnic_stats_debugfs_init(fnic);
>  
>  	/* Find model name from PCIe subsys ID */
> -	if (fnic_get_desc_by_devid(pdev, &desc, &subsys_desc) == 0)
> +	if (fnic_get_desc_by_devid(pdev, &desc, &subsys_desc) == 0) {
>  		dev_info(&fnic->pdev->dev, "Model: %s\n", subsys_desc);
> -	else {
> +
> +		/* Update FDMI model */

This comment adds no information.  Delete it.

> +		fnic->subsys_desc_len = strlen(subsys_desc);

Keep in mind that strlen() does not count the NUL-terminator.

> +		len = ARRAY_SIZE(fnic->subsys_desc);

Use sizeof() when you are talking about bytes or chars.  For snprintf() and
other string functions, it's always sizeof() and never ARRAY_SIZE().

> +		if (fnic->subsys_desc_len > len)
> +			fnic->subsys_desc_len = len;
> +		memcpy(fnic->subsys_desc, subsys_desc, fnic->subsys_desc_len);

So this is an 0-14 character buffer.  If fnic->subsys_desc_len is set to 14,
then the string is not NUL terminated.  This is how the buffer is used in
fdls_fdmi_register_hba()

	strscpy_pad(data, fnic->subsys_desc, FNIC_FDMI_MODEL_LEN);
	data[FNIC_FDMI_MODEL_LEN - 1] = 0;

This suggests that fnic->subsys_desc is expected to be NUL-terminated.
However FNIC_FDMI_MODEL_LEN is 12.  So in that case the last 3 characters
are removed.  LOL.  It's harmless but so very annoying.

Also strscpy_pad() will ensure that data[FNIC_FDMI_MODEL_LEN - 1] is set
to zero so that line could be deleted.

regards,
dan carpenter
Karan Tilak Kumar (kartilak) Jan. 8, 2025, 9:47 p.m. UTC | #2
On Tuesday, January 7, 2025 4:30 AM, Dan Carpenter <dan.carpenter@linaro.org> wrote:
>
> On Wed, Dec 11, 2024 at 06:03:04PM -0800, Karan Tilak Kumar wrote:
> > @@ -612,6 +615,7 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
> >     unsigned long flags;
> >     int hwq;
> >     char *desc, *subsys_desc;
> > +   int len;
>
> Do not introduce unnecessary levels of indirection.  Get rid of this len
> variable.

Thanks Dan. 
We will address this in a future patch.

> >
> >     /*
> >      * Allocate SCSI Host and set up association between host,
> > @@ -646,9 +650,17 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
> >     fnic_stats_debugfs_init(fnic);
> >
> >     /* Find model name from PCIe subsys ID */
> > -   if (fnic_get_desc_by_devid(pdev, &desc, &subsys_desc) == 0)
> > +   if (fnic_get_desc_by_devid(pdev, &desc, &subsys_desc) == 0) {
> >             dev_info(&fnic->pdev->dev, "Model: %s\n", subsys_desc);
> > -   else {
> > +
> > +           /* Update FDMI model */
>
> This comment adds no information.  Delete it.

Thanks Dan. We will address this in a future patch.

> > +           fnic->subsys_desc_len = strlen(subsys_desc);
>
> Keep in mind that strlen() does not count the NUL-terminator.
>
> > +           len = ARRAY_SIZE(fnic->subsys_desc);
>
> Use sizeof() when you are talking about bytes or chars.  For snprintf() and
> other string functions, it's always sizeof() and never ARRAY_SIZE().
>
> > +           if (fnic->subsys_desc_len > len)
> > +                   fnic->subsys_desc_len = len;
> > +           memcpy(fnic->subsys_desc, subsys_desc, fnic->subsys_desc_len);
>
> So this is an 0-14 character buffer.  If fnic->subsys_desc_len is set to 14,
> then the string is not NUL terminated.  This is how the buffer is used in
> fdls_fdmi_register_hba()
>
> strscpy_pad(data, fnic->subsys_desc, FNIC_FDMI_MODEL_LEN);
> data[FNIC_FDMI_MODEL_LEN - 1] = 0;
>
> This suggests that fnic->subsys_desc is expected to be NUL-terminated.
> However FNIC_FDMI_MODEL_LEN is 12.  So in that case the last 3 characters
> are removed.  LOL.  It's harmless but so very annoying.
>
> Also strscpy_pad() will ensure that data[FNIC_FDMI_MODEL_LEN - 1] is set
> to zero so that line could be deleted.

Thanks Dan. Cisco VIC model names do not exceed 9 characters.
I understand your point about the theoretical case though.
The data init line can be removed in a future patch.

> regards,
> dan carpenter
>

Thanks for your time and effort in reviewing this patch Dan.
Appreciate your help.

Regards,
Karan
Karan Tilak Kumar (kartilak) Jan. 13, 2025, 8:28 p.m. UTC | #3
On Monday, January 13, 2025 9:35 AM, John Meneghini <jmeneghi@redhat.com> wrote:
>
> Just a note to say that these patches are important to Red Hat and we are actively engaged in back porting and testing these patches in to RHEL-9 and RHEL-10.
>
> I think these issues that Dan has pointed out are all issues which can be addressed in a follow up patch.
>
> /John
>
> On 1/7/25 07:30, Dan Carpenter wrote:
> > On Wed, Dec 11, 2024 at 06:03:04PM -0800, Karan Tilak Kumar wrote:
> >> @@ -612,6 +615,7 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
> >>    unsigned long flags;
> >>    int hwq;
> >>    char *desc, *subsys_desc;
> >> +  int len;
> >
> > Do not introduce unnecessary levels of indirection.  Get rid of this len
> > variable.
> >
> >>
> >>    /*
> >>     * Allocate SCSI Host and set up association between host,
> >> @@ -646,9 +650,17 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
> >>    fnic_stats_debugfs_init(fnic);
> >>
> >>    /* Find model name from PCIe subsys ID */
> >> -  if (fnic_get_desc_by_devid(pdev, &desc, &subsys_desc) == 0)
> >> +  if (fnic_get_desc_by_devid(pdev, &desc, &subsys_desc) == 0) {
> >>            dev_info(&fnic->pdev->dev, "Model: %s\n", subsys_desc);
> >> -  else {
> >> +
> >> +          /* Update FDMI model */
> >
> > This comment adds no information.  Delete it.
> >
> >> +          fnic->subsys_desc_len = strlen(subsys_desc);
> >
> > Keep in mind that strlen() does not count the NUL-terminator.
> >
> >> +          len = ARRAY_SIZE(fnic->subsys_desc);
> >
> > Use sizeof() when you are talking about bytes or chars.  For snprintf() and
> > other string functions, it's always sizeof() and never ARRAY_SIZE().
> >
> >> +          if (fnic->subsys_desc_len > len)
> >> +                  fnic->subsys_desc_len = len;
> >> +          memcpy(fnic->subsys_desc, subsys_desc, fnic->subsys_desc_len);
> >
> > So this is an 0-14 character buffer.  If fnic->subsys_desc_len is set to 14,
> > then the string is not NUL terminated.  This is how the buffer is used in
> > fdls_fdmi_register_hba()
> >
> >     strscpy_pad(data, fnic->subsys_desc, FNIC_FDMI_MODEL_LEN);
> >     data[FNIC_FDMI_MODEL_LEN - 1] = 0;
> >
> > This suggests that fnic->subsys_desc is expected to be NUL-terminated.
> > However FNIC_FDMI_MODEL_LEN is 12.  So in that case the last 3 characters
> > are removed.  LOL.  It's harmless but so very annoying.
> >
> > Also strscpy_pad() will ensure that data[FNIC_FDMI_MODEL_LEN - 1] is set
> > to zero so that line could be deleted.
> >
> > regards,
> > dan carpenter
> >
>
>

Thanks for your inputs, John.

Regards,
Karan
Dan Carpenter Jan. 14, 2025, 9:59 a.m. UTC | #4
On Mon, Jan 13, 2025 at 12:35:03PM -0500, John Meneghini wrote:
> Just a note to say that these patches are important to Red Hat and we
> are actively engaged in back porting and testing these patches in to
> RHEL-9 and RHEL-10.
> 
> I think these issues that Dan has pointed out are all issues which
> can be addressed in a follow up patch.

I mean we already merged this.  I only got involved because of static
checker issues in linux-next.

What I'm complaining about is not so much any specific issue but just
that the process was not followed.  Normally this patch would not
be merged.  If anyone sent a patch like this to drivers/staging it
would have triggered Greg's patch-bot automated response:

- Your patch did many different things all at once, making it difficult
  to review.  All Linux kernel patches need to only do one thing at a
  time.  If you need to do multiple things (such as clean up all coding
  style issues in a file/driver), do it in a sequence of patches, each
  one doing only one thing.  This will make it easier to review the
  patches to ensure that they are correct, and to help alleviate any
  merge issues that larger patches can cause.

This rule is the same in every subsystem.  No one wants to merge a patch
like this.  But what happened is the patch sat on the list and only
Hannes and Martin were doing any review.  Karan was left doing all the
work with no help or guidance.  Eventually Martin has to give up right?
The patchset isn't up to normal standards but it's basically okay and
Martin can't do every single thing by himself and eventually it's pretty
clear no one else is coming to help.  It is what it is etc.

Please understand this in the gentlest way.  Next time if something is
important then assign an engineer to help out.  It would have taken a day
to prepare this patchset for merging.  You had seven months.  It's not
fair to show up five days before the merge window asking for special
exemptions from the review process.  Maintainers and reviewers are
already overworked, they shouldn't have to work around your deadlines.
John Meneghini Jan. 14, 2025, 3:15 p.m. UTC | #5
Hi Dan.

I absolutely agree with all of your comments and I appreciate your review.
I agree that all of the issues you've pointed out, with the the exception of
one, need to be addressed. The issues pointed out - especially the string
manipulation issues - can turn into CVEs. We don't want to be checking bugs
like this into Linux. Certainly, nothing should be merged that does not
pass the static checker, et al, automated tools we have.

My comment here was only to say that I don't think it's reasonable to
ask Karan to break this change into a series of 100 small, reviewable changes.

To explain: the fnic driver is Cisco's driver. Cisco has always taken responsibility
for this driver and Karan is one of the many Cisco Maintainers. What's happening is,
after a long period of time where the code has been somewhat neglected, Cisco has
decided to do a major update of their upstream driver. These changes
are really more like a new driver, with some major new features, than a
series of bug fixes or updates. This is happening because - at the insistence
of some of the Distros, like Red Hat - Cisco is being told they need to bring
their upstream drivers in line with their out-of-box drivers.

Karan can confirm if this is the case. My question for Karan is: are there any more
major driver changes coming for fnic, or is this the majority of it?

If this were simply a new feature or a series of bug fixes I agree the changes
need to be organized into a series of small, reviewable patches. However,
under the circumstances, I am willing to hold my nose and say: just get your driver
updated and into order, and then we will hold the Maintainers (not Martin) accountable.

So I guess I am recommending an exception to the rule, just this once, in regard to breaking the
overall change into a smaller and smaller patch series. I would prefer, instead, that any and all
changes needed to address further review comments be presented as a small series of patches, broken
down into reviewable chunks, on top of the exiting patch series. It can be decided later if these
patches can or should be squashed into the respective 17 commits.

Anyways, those are my thoughts.

Thanks again for your help with this review. I agree Karan needs help and support,
and I will try to be more public about they ways I am doing that - which have been
mostly in the back-ground.

/John

On 1/14/25 04:59, Dan Carpenter wrote:
> On Mon, Jan 13, 2025 at 12:35:03PM -0500, John Meneghini wrote:
>> Just a note to say that these patches are important to Red Hat and we
>> are actively engaged in back porting and testing these patches in to
>> RHEL-9 and RHEL-10.
>>
>> I think these issues that Dan has pointed out are all issues which
>> can be addressed in a follow up patch.
> 
> I mean we already merged this.  I only got involved because of static
> checker issues in linux-next.
> 
> What I'm complaining about is not so much any specific issue but just
> that the process was not followed.  Normally this patch would not
> be merged.  If anyone sent a patch like this to drivers/staging it
> would have triggered Greg's patch-bot automated response:
> 
> - Your patch did many different things all at once, making it difficult
>    to review.  All Linux kernel patches need to only do one thing at a
>    time.  If you need to do multiple things (such as clean up all coding
>    style issues in a file/driver), do it in a sequence of patches, each
>    one doing only one thing.  This will make it easier to review the
>    patches to ensure that they are correct, and to help alleviate any
>    merge issues that larger patches can cause.
> 
> This rule is the same in every subsystem.  No one wants to merge a patch
> like this.  But what happened is the patch sat on the list and only
> Hannes and Martin were doing any review.  Karan was left doing all the
> work with no help or guidance.  Eventually Martin has to give up right?
> The patchset isn't up to normal standards but it's basically okay and
> Martin can't do every single thing by himself and eventually it's pretty
> clear no one else is coming to help.  It is what it is etc.
> 
> Please understand this in the gentlest way.  Next time if something is
> important then assign an engineer to help out.  It would have taken a day
> to prepare this patchset for merging.  You had seven months.  It's not
> fair to show up five days before the merge window asking for special
> exemptions from the review process.  Maintainers and reviewers are
> already overworked, they shouldn't have to work around your deadlines.
> 
>  From what I've seen Karan is absolutely doing a good job addressing the
> problems I reported so it's fine.  But normally this is not how it works.
> 
> regards,
> dan carpenter
>
Karan Tilak Kumar (kartilak) Jan. 15, 2025, 1:03 a.m. UTC | #6
On Tuesday, January 14, 2025 7:15 AM, John Meneghini <jmeneghi@redhat.com> wrote:
>
> Hi Dan.
>
> I absolutely agree with all of your comments and I appreciate your review.
> I agree that all of the issues you've pointed out, with the the exception of
> one, need to be addressed. The issues pointed out - especially the string
> manipulation issues - can turn into CVEs. We don't want to be checking bugs
> like this into Linux. Certainly, nothing should be merged that does not
> pass the static checker, et al, automated tools we have.
>
> My comment here was only to say that I don't think it's reasonable to
> ask Karan to break this change into a series of 100 small, reviewable changes.
>
> To explain: the fnic driver is Cisco's driver. Cisco has always taken responsibility
> for this driver and Karan is one of the many Cisco Maintainers. What's happening is,
> after a long period of time where the code has been somewhat neglected, Cisco has
> decided to do a major update of their upstream driver. These changes
> are really more like a new driver, with some major new features, than a
> series of bug fixes or updates. This is happening because - at the insistence
> of some of the Distros, like Red Hat - Cisco is being told they need to bring
> their upstream drivers in line with their out-of-box drivers.
>
> Karan can confirm if this is the case. My question for Karan is: are there any more
> major driver changes coming for fnic, or is this the majority of it?
>

This current set of changes is the biggest set.
We have a few more small features planned for the future, not for 6.14.
They are quite limited in their scope.

Regards,
Karan
Dan Carpenter Jan. 16, 2025, 6:29 a.m. UTC | #7
On Tue, Jan 14, 2025 at 10:15:12AM -0500, John Meneghini wrote:
> Hi Dan.
> 
> I absolutely agree with all of your comments and I appreciate your review.
> I agree that all of the issues you've pointed out, with the the exception of
> one, need to be addressed. The issues pointed out - especially the string
> manipulation issues - can turn into CVEs. We don't want to be checking bugs
> like this into Linux. Certainly, nothing should be merged that does not
> pass the static checker, et al, automated tools we have.
> 
> My comment here was only to say that I don't think it's reasonable to
> ask Karan to break this change into a series of 100 small, reviewable changes.

100 small changes is hyperbole.  It would have made this set of 15
patches into probably 23 patches.  A day's work perhaps.

Creating reviewable patches is part of the process because it forces
you to review the code yourself.  It makes reviewing the code easier
and safer and faster.

I feel like if people had asked in Jun last year, you have two options:
Option A: Would you rather do one day's work cleaning this code up to
make it easy to review?  Option B: Would you rather resend it as-is
every month for seven months?  I feel like most people would choose
option A.

But the real problem is that we don't have enough SCSI maintainers...
Even a quite junior maintainer would help.  In drivers/staging, we have
a bunch of random volunteers who chime in on stuff like this.  It is
what it is.

regards,
dan carpenter
diff mbox series

Patch

diff --git a/drivers/scsi/fnic/fdls_disc.c b/drivers/scsi/fnic/fdls_disc.c
index e08cf343bad7..35f117d23f90 100644
--- a/drivers/scsi/fnic/fdls_disc.c
+++ b/drivers/scsi/fnic/fdls_disc.c
@@ -18,6 +18,52 @@ 
 #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)
 
@@ -26,6 +72,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,
@@ -39,6 +87,7 @@  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);
@@ -721,6 +770,52 @@  static void fdls_send_fabric_abts(struct fnic_iport_s *iport)
 	iport->fabric.timer_pending = 1;
 }
 
+static void fdls_send_fdmi_abts(struct fnic_iport_s *iport)
+{
+	uint8_t *frame;
+	uint8_t d_id[3];
+	struct fnic *fnic = iport->fnic;
+	struct fc_frame_header *pfabric_abts;
+	unsigned long fdmi_tov;
+	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->lport->host, fnic->fnic_num,
+				"Failed to allocate frame to send FDMI ABTS");
+		return;
+	}
+
+	pfabric_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(*pfabric_abts, d_id);
+
+	if (iport->fabric.fdmi_pending & FDLS_FDMI_PLOGI_PENDING) {
+		oxid = iport->active_oxid_fdmi_plogi;
+		FNIC_STD_SET_OX_ID(*pfabric_abts, oxid);
+		fnic_send_fcoe_frame(iport, frame, frame_size);
+	} else {
+		if (iport->fabric.fdmi_pending & FDLS_FDMI_REG_HBA_PENDING) {
+			oxid = iport->active_oxid_fdmi_rhba;
+			FNIC_STD_SET_OX_ID(*pfabric_abts, oxid);
+			fnic_send_fcoe_frame(iport, frame, frame_size);
+		}
+		if (iport->fabric.fdmi_pending & FDLS_FDMI_RPA_PENDING) {
+			oxid = iport->active_oxid_fdmi_rpa;
+			FNIC_STD_SET_OX_ID(*pfabric_abts, oxid);
+			fnic_send_fcoe_frame(iport, frame, frame_size);
+		}
+	}
+
+	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;
+}
+
 static void fdls_send_fabric_flogi(struct fnic_iport_s *iport)
 {
 	uint8_t *frame;
@@ -823,6 +918,54 @@  static void fdls_send_fabric_plogi(struct fnic_iport_s *iport)
 	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->lport->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->lport->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->lport->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;
@@ -1644,6 +1787,300 @@  struct fnic_tport_s *fnic_find_tport_by_wwpn(struct fnic_iport_s *iport,
 	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->lport->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->lport->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->lport->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->lport->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->lport->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);
+	data[FNIC_FDMI_MODEL_LEN - 1] = 0;
+	fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_MODEL, FNIC_FDMI_MODEL_LEN,
+		data, &attr_off_bytes);
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->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->lport->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->lport->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->lport->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->lport->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->lport->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->lport->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->lport->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->lport->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->lport->host->host_no);
+	strscpy_pad(data, tmp_data, FNIC_FDMI_OS_NAME_LEN);
+	data[FNIC_FDMI_OS_NAME_LEN - 1] = 0;
+	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->lport->host, fnic->fnic_num,
+			"OS name set <%s>, off=%d", data, attr_off_bytes);
+
+	sprintf(fc_host_system_hostname(fnic->lport->host), "%s", utsname()->nodename);
+	strscpy_pad(data, fc_host_system_hostname(fnic->lport->host),
+					FNIC_FDMI_HN_LEN);
+	data[FNIC_FDMI_HN_LEN - 1] = 0;
+	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->lport->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->lport->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 = from_timer(fabric, t, retry_timer);
@@ -1817,6 +2254,64 @@  void fdls_fabric_timer_callback(struct timer_list *t)
 	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 }
 
+void fdls_fdmi_timer_callback(struct timer_list *t)
+{
+	struct fnic_fdls_fabric_s *fabric = from_timer(fabric, t, fdmi_timer);
+	struct fnic_iport_s *iport =
+		container_of(fabric, struct fnic_iport_s, fabric);
+	struct fnic *fnic = iport->fnic;
+	unsigned long flags;
+
+	spin_lock_irqsave(&fnic->fnic_lock, flags);
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+		"fdmi timer callback : 0x%x\n", iport->fabric.fdmi_pending);
+
+	if (!iport->fabric.fdmi_pending) {
+		/* timer expired after fdmi responses received. */
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		return;
+	}
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+		"fdmi timer callback : 0x%x\n", iport->fabric.fdmi_pending);
+
+	/* if not abort pending, send an abort */
+	if (!(iport->fabric.fdmi_pending & FDLS_FDMI_ABORT_PENDING)) {
+		fdls_send_fdmi_abts(iport);
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		return;
+	}
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+		"fdmi timer callback : 0x%x\n", iport->fabric.fdmi_pending);
+
+	/* ABTS pending for an active fdmi request that is pending.
+	 * That means FDMI ABTS timed out
+	 * Schedule to free the OXID after 2*r_a_tov and proceed
+	 */
+	if (iport->fabric.fdmi_pending & FDLS_FDMI_PLOGI_PENDING) {
+		fdls_schedule_oxid_free(iport, &iport->active_oxid_fdmi_plogi);
+	} else {
+		if (iport->fabric.fdmi_pending & FDLS_FDMI_REG_HBA_PENDING)
+			fdls_schedule_oxid_free(iport, &iport->active_oxid_fdmi_rhba);
+		if (iport->fabric.fdmi_pending & FDLS_FDMI_RPA_PENDING)
+			fdls_schedule_oxid_free(iport, &iport->active_oxid_fdmi_rpa);
+	}
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+		"fdmi timer callback : 0x%x\n", iport->fabric.fdmi_pending);
+
+	iport->fabric.fdmi_pending = 0;
+	/* If max retries not exhaused, start over from fdmi plogi */
+	if (iport->fabric.fdmi_retry < FDLS_FDMI_MAX_RETRY) {
+		iport->fabric.fdmi_retry++;
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "retry fdmi timer %d", iport->fabric.fdmi_retry);
+		fdls_send_fdmi_plogi(iport);
+	}
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+		"fdmi timer callback : 0x%x\n", iport->fabric.fdmi_pending);
+	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+}
+
 static void fdls_send_delete_tport_msg(struct fnic_tport_s *tport)
 {
 	struct fnic_iport_s *iport = (struct fnic_iport_s *) tport->iport;
@@ -1956,6 +2451,15 @@  static void fnic_fdls_start_plogi(struct fnic_iport_s *iport)
 	fdls_send_fabric_plogi(iport);
 	fdls_set_state((&iport->fabric), FDLS_STATE_FABRIC_PLOGI);
 	iport->fabric.flags &= ~FNIC_FDLS_FABRIC_ABORT_ISSUED;
+
+	if ((fnic_fdmi_support == 1) && (!(iport->flags & FNIC_FDMI_ACTIVE))) {
+		/* we can do FDMI at the same time */
+		iport->fabric.fdmi_retry = 0;
+		timer_setup(&iport->fabric.fdmi_timer, fdls_fdmi_timer_callback,
+					0);
+		fdls_send_fdmi_plogi(iport);
+		iport->flags |= FNIC_FDMI_ACTIVE;
+	}
 }
 static void
 fdls_process_tgt_adisc_rsp(struct fnic_iport_s *iport,
@@ -3090,6 +3594,144 @@  fdls_process_fabric_plogi_rsp(struct fnic_iport_s *iport,
 	}
 }
 
+static void fdls_process_fdmi_plogi_rsp(struct fnic_iport_s *iport,
+					struct fc_frame_header *fchdr)
+{
+	struct fc_std_flogi *plogi_rsp = (struct fc_std_flogi *)fchdr;
+	struct fc_std_els_rjt_rsp *els_rjt = (struct fc_std_els_rjt_rsp *)fchdr;
+	struct fnic_fdls_fabric_s *fdls = &iport->fabric;
+	struct fnic *fnic = iport->fnic;
+	u64 fdmi_tov;
+	uint16_t oxid = FNIC_STD_GET_OX_ID(fchdr);
+
+	if (iport->active_oxid_fdmi_plogi != oxid) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			"Incorrect OXID in response. state: %d, oxid recvd: 0x%x, active oxid: 0x%x\n",
+			fdls_get_state(fdls), oxid, iport->active_oxid_fdmi_plogi);
+		return;
+	}
+
+	iport->fabric.fdmi_pending &= ~FDLS_FDMI_PLOGI_PENDING;
+	fdls_free_oxid(iport, oxid, &iport->active_oxid_fdmi_plogi);
+
+	if (ntoh24(fchdr->fh_s_id) == FC_FID_MGMT_SERV) {
+		del_timer_sync(&iport->fabric.fdmi_timer);
+		iport->fabric.fdmi_pending = 0;
+		switch (plogi_rsp->els.fl_cmd) {
+		case ELS_LS_ACC:
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "FDLS process fdmi PLOGI response status: ELS_LS_ACC\n");
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "Sending fdmi registration for port 0x%x\n",
+				 iport->fcid);
+
+			fdls_fdmi_register_hba(iport);
+			fdls_fdmi_register_pa(iport);
+			fdmi_tov = jiffies + msecs_to_jiffies(5000);
+			mod_timer(&iport->fabric.fdmi_timer,
+				  round_jiffies(fdmi_tov));
+			break;
+		case ELS_LS_RJT:
+			FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+				 "Fabric FDMI PLOGI returned ELS_LS_RJT reason: 0x%x",
+				     els_rjt->rej.er_reason);
+
+			if (((els_rjt->rej.er_reason == ELS_RJT_BUSY)
+			     || (els_rjt->rej.er_reason == ELS_RJT_UNAB))
+				&& (iport->fabric.fdmi_retry < 7)) {
+				iport->fabric.fdmi_retry++;
+				fdls_send_fdmi_plogi(iport);
+			}
+			break;
+		default:
+			break;
+		}
+	}
+}
+static void fdls_process_fdmi_reg_ack(struct fnic_iport_s *iport,
+				      struct fc_frame_header *fchdr,
+				      int rsp_type)
+{
+	struct fnic *fnic = iport->fnic;
+	uint16_t oxid;
+
+	if (!iport->fabric.fdmi_pending) {
+		FNIC_FCS_DBG(KERN_ERR, fnic->lport->host, fnic->fnic_num,
+			     "Received FDMI ack while not waiting: 0x%x\n",
+			     FNIC_STD_GET_OX_ID(fchdr));
+		return;
+	}
+
+	oxid =  FNIC_STD_GET_OX_ID(fchdr);
+
+	if ((iport->active_oxid_fdmi_rhba != oxid) &&
+		(iport->active_oxid_fdmi_rpa != oxid))  {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			"Incorrect OXID in response. oxid recvd: 0x%x, active oxids(rhba,rpa): 0x%x, 0x%x\n",
+			oxid, iport->active_oxid_fdmi_rhba, iport->active_oxid_fdmi_rpa);
+		return;
+	}
+	if (FNIC_FRAME_TYPE(oxid) == FNIC_FRAME_TYPE_FDMI_RHBA) {
+		iport->fabric.fdmi_pending &= ~FDLS_FDMI_REG_HBA_PENDING;
+		fdls_free_oxid(iport, oxid, &iport->active_oxid_fdmi_rhba);
+	} else {
+		iport->fabric.fdmi_pending &= ~FDLS_FDMI_RPA_PENDING;
+		fdls_free_oxid(iport, oxid, &iport->active_oxid_fdmi_rpa);
+	}
+
+	FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+		"iport fcid: 0x%x: Received FDMI registration ack\n",
+		 iport->fcid);
+
+	if (!iport->fabric.fdmi_pending) {
+		del_timer_sync(&iport->fabric.fdmi_timer);
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+					 "iport fcid: 0x%x: Canceling FDMI timer\n",
+					 iport->fcid);
+	}
+}
+
+static void fdls_process_fdmi_abts_rsp(struct fnic_iport_s *iport,
+				       struct fc_frame_header *fchdr)
+{
+	uint32_t s_id;
+	struct fnic *fnic = iport->fnic;
+	uint16_t oxid;
+
+	s_id = ntoh24(FNIC_STD_GET_S_ID(fchdr));
+
+	if (!(s_id != FC_FID_MGMT_SERV)) {
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			     "Received abts rsp with invalid SID: 0x%x. Dropping frame",
+			     s_id);
+		return;
+	}
+
+	oxid =  FNIC_STD_GET_OX_ID(fchdr);
+
+	switch (FNIC_FRAME_TYPE(oxid)) {
+	case FNIC_FRAME_TYPE_FDMI_PLOGI:
+		fdls_free_oxid(iport, oxid, &iport->active_oxid_fdmi_plogi);
+		break;
+	case FNIC_FRAME_TYPE_FDMI_RHBA:
+		fdls_free_oxid(iport, oxid, &iport->active_oxid_fdmi_rhba);
+		break;
+	case FNIC_FRAME_TYPE_FDMI_RPA:
+		fdls_free_oxid(iport, oxid, &iport->active_oxid_fdmi_rpa);
+		break;
+	default:
+		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
+			"Received abts rsp with invalid oxid: 0x%x. Dropping frame",
+			oxid);
+		break;
+	}
+
+	del_timer_sync(&iport->fabric.fdmi_timer);
+	iport->fabric.fdmi_pending &= ~FDLS_FDMI_ABORT_PENDING;
+
+	fdls_send_fdmi_plogi(iport);
+}
+
 static void
 fdls_process_fabric_abts_rsp(struct fnic_iport_s *iport,
 			     struct fc_frame_header *fchdr)
@@ -4094,6 +4736,12 @@  fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport,
 
 	case FNIC_FRAME_TYPE_FABRIC_LOGO:
 		return FNIC_FABRIC_LOGO_RSP;
+	case FNIC_FRAME_TYPE_FDMI_PLOGI:
+		return FNIC_FDMI_PLOGI_RSP;
+	case FNIC_FRAME_TYPE_FDMI_RHBA:
+		return FNIC_FDMI_REG_HBA_RSP;
+	case FNIC_FRAME_TYPE_FDMI_RPA:
+		return FNIC_FDMI_RPA_RSP;
 	case FNIC_FRAME_TYPE_TGT_PLOGI:
 		return FNIC_TPORT_PLOGI_RSP;
 	case FNIC_FRAME_TYPE_TGT_PRLI:
@@ -4148,6 +4796,9 @@  void fnic_fdls_recv_frame(struct fnic_iport_s *iport, void *rx_frame,
 	case FNIC_FABRIC_PLOGI_RSP:
 		fdls_process_fabric_plogi_rsp(iport, fchdr);
 		break;
+	case FNIC_FDMI_PLOGI_RSP:
+		fdls_process_fdmi_plogi_rsp(iport, fchdr);
+		break;
 	case FNIC_FABRIC_RPN_RSP:
 		fdls_process_rpn_id_rsp(iport, fchdr);
 		break;
@@ -4187,6 +4838,9 @@  void fnic_fdls_recv_frame(struct fnic_iport_s *iport, void *rx_frame,
 	case FNIC_FABRIC_BLS_ABTS_RSP:
 			fdls_process_fabric_abts_rsp(iport, fchdr);
 		break;
+	case FNIC_FDMI_BLS_ABTS_RSP:
+		fdls_process_fdmi_abts_rsp(iport, fchdr);
+		break;
 	case FNIC_BLS_ABTS_REQ:
 		fdls_process_abts_req(iport, fchdr);
 		break;
@@ -4212,6 +4866,10 @@  void fnic_fdls_recv_frame(struct fnic_iport_s *iport, void *rx_frame,
 	case FNIC_ELS_RLS:
 		fdls_process_rls_req(iport, fchdr);
 		break;
+	case FNIC_FDMI_REG_HBA_RSP:
+	case FNIC_FDMI_RPA_RSP:
+		fdls_process_fdmi_reg_ack(iport, fchdr, frame_type);
+		break;
 	default:
 		FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, fnic->fnic_num,
 			 "s_id: 0x%x d_did: 0x%x", s_id, d_id);
diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h
index c2978c5c6e8f..c4f4b2fe192a 100644
--- a/drivers/scsi/fnic/fnic.h
+++ b/drivers/scsi/fnic/fnic.h
@@ -205,6 +205,7 @@  static inline u64 fnic_flags_and_state(struct scsi_cmnd *cmd)
 #define fnic_clear_state_flags(fnicp, st_flags)  \
 	__fnic_set_state_flags(fnicp, st_flags, 1)
 
+extern unsigned int fnic_fdmi_support;
 extern unsigned int fnic_log_level;
 extern unsigned int io_completions;
 extern struct workqueue_struct *fnic_event_queue;
diff --git a/drivers/scsi/fnic/fnic_main.c b/drivers/scsi/fnic/fnic_main.c
index c1c10731906f..c75716856417 100644
--- a/drivers/scsi/fnic/fnic_main.c
+++ b/drivers/scsi/fnic/fnic_main.c
@@ -64,6 +64,9 @@  unsigned int fnic_log_level;
 module_param(fnic_log_level, int, S_IRUGO|S_IWUSR);
 MODULE_PARM_DESC(fnic_log_level, "bit mask of fnic logging levels");
 
+unsigned int fnic_fdmi_support = 1;
+module_param(fnic_fdmi_support, int, 0644);
+MODULE_PARM_DESC(fnic_fdmi_support, "FDMI support");
 
 unsigned int io_completions = FNIC_DFLT_IO_COMPLETIONS;
 module_param(io_completions, int, S_IRUGO|S_IWUSR);
@@ -612,6 +615,7 @@  static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 	unsigned long flags;
 	int hwq;
 	char *desc, *subsys_desc;
+	int len;
 
 	/*
 	 * Allocate SCSI Host and set up association between host,
@@ -646,9 +650,17 @@  static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 	fnic_stats_debugfs_init(fnic);
 
 	/* Find model name from PCIe subsys ID */
-	if (fnic_get_desc_by_devid(pdev, &desc, &subsys_desc) == 0)
+	if (fnic_get_desc_by_devid(pdev, &desc, &subsys_desc) == 0) {
 		dev_info(&fnic->pdev->dev, "Model: %s\n", subsys_desc);
-	else {
+
+		/* Update FDMI model */
+		fnic->subsys_desc_len = strlen(subsys_desc);
+		len = ARRAY_SIZE(fnic->subsys_desc);
+		if (fnic->subsys_desc_len > len)
+			fnic->subsys_desc_len = len;
+		memcpy(fnic->subsys_desc, subsys_desc, fnic->subsys_desc_len);
+		dev_info(&fnic->pdev->dev, "FDMI Model: %s\n", fnic->subsys_desc);
+	} else {
 		fnic->subsys_desc_len = 0;
 		dev_info(&fnic->pdev->dev, "Model: %s subsys_id: 0x%04x\n", "Unknown",
 				pdev->subsystem_device);
@@ -1051,6 +1063,9 @@  static void fnic_remove(struct pci_dev *pdev)
 		fnic_fcoe_evlist_free(fnic);
 	}
 
+	if ((fnic_fdmi_support == 1) && (fnic->iport.fabric.fdmi_pending > 0))
+		del_timer_sync(&fnic->iport.fabric.fdmi_timer);
+
 	/*
 	 * Log off the fabric. This stops all remote ports, dns port,
 	 * logs off the fabric. This flushes all rport, disc, lport work