diff mbox series

[v9,07/13] lpfc: vmid: Implements ELS commands for appid patch

Message ID 1617750397-26466-8-git-send-email-muneendra.kumar@broadcom.com
State Superseded
Headers show
Series blkcg:Support to track FC storage blk io traffic | expand

Commit Message

Muneendra Kumar April 6, 2021, 11:06 p.m. UTC
From: Gaurav Srivastava <gaurav.srivastava@broadcom.com>

This patch implements ELS command like QFPA and UVEM for the priority
tagging appid support. Other supporting functions are also part of this
patch.

Signed-off-by: Gaurav Srivastava  <gaurav.srivastava@broadcom.com>
Signed-off-by: James Smart <jsmart2021@gmail.com>

---
v9:
Added a lock while accessing a flag

v8:
Added log messages modifications, memory allocation API changes,
return error codes

v7:
No change

v6:
Added Forward declarations, static functions and
removed unused variables

v5:
Changed Return code to non-numeric/Symbol.
Addressed the review comments by Hannes

v4:
No change

v3:
No change

v2:
Ported the patch on top of 5.10/scsi-queue
---
 drivers/scsi/lpfc/lpfc_els.c | 366 ++++++++++++++++++++++++++++++++++-
 1 file changed, 362 insertions(+), 4 deletions(-)

Comments

Benjamin Block April 20, 2021, 12:38 p.m. UTC | #1
Hey,

On Wed, Apr 07, 2021 at 04:36:31AM +0530, Muneendra wrote:
> From: Gaurav Srivastava <gaurav.srivastava@broadcom.com>

> 

> This patch implements ELS command like QFPA and UVEM for the priority

> tagging appid support. Other supporting functions are also part of this

> patch.

> 

> Signed-off-by: Gaurav Srivastava  <gaurav.srivastava@broadcom.com>

> Signed-off-by: James Smart <jsmart2021@gmail.com>

> 

> ---

> v9:

> Added a lock while accessing a flag

> 

> v8:

> Added log messages modifications, memory allocation API changes,

> return error codes

> 

> v7:

> No change

> 

> v6:

> Added Forward declarations, static functions and

> removed unused variables

> 

> v5:

> Changed Return code to non-numeric/Symbol.

> Addressed the review comments by Hannes

> 

> v4:

> No change

> 

> v3:

> No change

> 

> v2:

> Ported the patch on top of 5.10/scsi-queue

> ---

>  drivers/scsi/lpfc/lpfc_els.c | 366 ++++++++++++++++++++++++++++++++++-

>  1 file changed, 362 insertions(+), 4 deletions(-)

> 

> diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c

> index a04546eca18f..22a87559f62d 100644

> --- a/drivers/scsi/lpfc/lpfc_els.c

> +++ b/drivers/scsi/lpfc/lpfc_els.c

> @@ -10260,3 +10309,312 @@ lpfc_sli_abts_recover_port(struct lpfc_vport *vport,

>  	lpfc_unreg_rpi(vport, ndlp);

>  }

>  

> +static void lpfc_init_cs_ctl_bitmap(struct lpfc_vport *vport)

> +{

> +	bitmap_zero(vport->vmid_priority_range, LPFC_VMID_MAX_PRIORITY_RANGE);

> +}

> +

> +static void

> +lpfc_vmid_set_cs_ctl_range(struct lpfc_vport *vport, u32 min, u32 max)

> +{

> +	u32 i;

> +

> +	if ((min > max) || (max > LPFC_VMID_MAX_PRIORITY_RANGE))

> +		return;

> +

> +	for (i = min; i <= max; i++)

> +		set_bit(i, vport->vmid_priority_range);

> +}

> +

> +static void lpfc_vmid_put_cs_ctl(struct lpfc_vport *vport, u32 ctcl_vmid)

> +{

> +	set_bit(ctcl_vmid, vport->vmid_priority_range);

> +}

> +

> +u32 lpfc_vmid_get_cs_ctl(struct lpfc_vport *vport)

> +{

> +	u32 i;

> +

> +	i = find_first_bit(vport->vmid_priority_range,

> +			   LPFC_VMID_MAX_PRIORITY_RANGE);

> +

> +	if (i == LPFC_VMID_MAX_PRIORITY_RANGE)

> +		return 0;

> +

> +	clear_bit(i, vport->vmid_priority_range);

> +	return i;

> +}

> +

> +#define MAX_PRIORITY_DESC	255

> +

> +static void

> +lpfc_cmpl_els_qfpa(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,

> +		   struct lpfc_iocbq *rspiocb)

> +{

> +	struct lpfc_vport *vport = cmdiocb->vport;

> +	struct priority_range_desc *desc;

> +	struct lpfc_dmabuf *prsp = NULL;

> +	struct lpfc_vmid_priority_range *vmid_range = NULL;

> +	u32 *data;

> +	struct lpfc_dmabuf *dmabuf = cmdiocb->context2;

> +	IOCB_t *irsp = &rspiocb->iocb;

> +	u8 *pcmd, max_desc;

> +	u32 len, i;

> +	struct lpfc_nodelist *ndlp = (struct lpfc_nodelist *)cmdiocb->context1;

> +

> +	prsp = list_get_first(&dmabuf->list, struct lpfc_dmabuf, list);

> +	if (!prsp)

> +		goto out;

> +

> +	pcmd = prsp->virt;

> +	data = (u32 *)pcmd;

> +	if (data[0] == ELS_CMD_LS_RJT) {

> +		lpfc_printf_vlog(vport, KERN_WARNING, LOG_SLI,

> +				 "3277 QFPA LS_RJT x%x  x%x\n",

> +				 data[0], data[1]);

> +		goto out;

> +	}

> +	if (irsp->ulpStatus) {

> +		lpfc_printf_vlog(vport, KERN_ERR, LOG_SLI,

> +				 "6529 QFPA failed with status x%x  x%x\n",

> +				 irsp->ulpStatus, irsp->un.ulpWord[4]);

> +		goto out;

> +	}

> +

> +	if (!vport->qfpa_res) {

> +		max_desc = FCELSSIZE / sizeof(*vport->qfpa_res);

> +		vport->qfpa_res = kcalloc(max_desc, sizeof(*vport->qfpa_res),

> +					  GFP_KERNEL);

> +		if (!vport->qfpa_res)

> +			goto out;

> +	}

> +

> +	len = *((u32 *)(pcmd + 4));

> +	len = be32_to_cpu(len);

> +	memcpy(vport->qfpa_res, pcmd, len + 8);

> +	len = len / LPFC_PRIORITY_RANGE_DESC_SIZE;

> +

> +	desc = (struct priority_range_desc *)(pcmd + 8);

> +	vmid_range = vport->vmid_priority.vmid_range;

> +	if (!vmid_range) {

> +		vmid_range = kcalloc(MAX_PRIORITY_DESC, sizeof(*vmid_range),

> +				     GFP_KERNEL);

> +		if (!vmid_range) {

> +			kfree(vport->qfpa_res);

> +			goto out;

> +		}

> +		vport->vmid_priority.vmid_range = vmid_range;

> +	}

> +	vport->vmid_priority.num_descriptors = len;

> +

> +	for (i = 0; i < len; i++, vmid_range++, desc++) {

> +		lpfc_printf_vlog(vport, KERN_DEBUG, LOG_ELS,

> +				 "6539 vmid values low=%d, high=%d, qos=%d, "

> +				 "local ve id=%d\n", desc->lo_range,

> +				 desc->hi_range, desc->qos_priority,

> +				 desc->local_ve_id);

> +

> +		vmid_range->low = desc->lo_range << 1;

> +		if (desc->local_ve_id == QFPA_ODD_ONLY)

> +			vmid_range->low++;

> +		if (desc->qos_priority)

> +			vport->vmid_flag |= LPFC_VMID_QOS_ENABLED;

> +		vmid_range->qos = desc->qos_priority;


I'm curios, if the FC-switch signals it supports QoS for a range here, how
exactly interacts this with the VM IDs that you seem to allocate
dynamically during runtime for cgroups that request specific App IDs?
You don't seem to use `LPFC_VMID_QOS_ENABLED` anywhere else in the
series.

Would different cgroups get different QoS classes/guarantees depending
on the selected VM ID (higher VM ID gets better QoS class, or something
like that?)? Would the tagged traffic be handled differently than the
ordinary traffic in the fabric?

I tried to get something from FC-LS (-5) or FC-FS (-6), but they are extremely
sparse somehow. FC-LS-5 just says "QoS priority provided" for the
field.. and FC-FS doesn't say anything regarding QoS if the tagging
extension in CS_CTL is used.

> +

> +		vmid_range->high = desc->hi_range << 1;

> +		if ((desc->local_ve_id == QFPA_ODD_ONLY) ||

> +		    (desc->local_ve_id == QFPA_EVEN_ODD))

> +			vmid_range->high++;

> +	}

> +	lpfc_init_cs_ctl_bitmap(vport);

> +	for (i = 0; i < vport->vmid_priority.num_descriptors; i++) {

> +		lpfc_vmid_set_cs_ctl_range(vport,

> +				vport->vmid_priority.vmid_range[i].low,

> +				vport->vmid_priority.vmid_range[i].high);

> +	}

> +

> +	vport->vmid_flag |= LPFC_VMID_QFPA_CMPL;

> + out:

> +	lpfc_els_free_iocb(phba, cmdiocb);

> +	lpfc_nlp_put(ndlp);

> +}

> +

> +int lpfc_issue_els_qfpa(struct lpfc_vport *vport)

> +{

> +	struct lpfc_hba *phba = vport->phba;

> +	struct lpfc_nodelist *ndlp;

> +	struct lpfc_iocbq *elsiocb;

> +	u8 *pcmd;

> +	int ret;

> +

> +	ndlp = lpfc_findnode_did(phba->pport, Fabric_DID);

> +	if (!ndlp || ndlp->nlp_state != NLP_STE_UNMAPPED_NODE)

> +		return -ENXIO;

> +

> +	elsiocb = lpfc_prep_els_iocb(vport, 1, LPFC_QFPA_SIZE, 2, ndlp,

> +				     ndlp->nlp_DID, ELS_CMD_QFPA);

> +	if (!elsiocb)

> +		return -ENOMEM;

> +

> +	pcmd = (u8 *)(((struct lpfc_dmabuf *)elsiocb->context2)->virt);

> +

> +	*((u32 *)(pcmd)) = ELS_CMD_QFPA;

> +	pcmd += 4;

> +

> +	elsiocb->iocb_cmpl = lpfc_cmpl_els_qfpa;

> +

> +	elsiocb->context1 = lpfc_nlp_get(ndlp);

> +	if (!elsiocb->context1) {

> +		lpfc_els_free_iocb(vport->phba, elsiocb);

> +		return -ENXIO;

> +	}

> +

> +	ret = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 2);

> +	if (ret != IOCB_SUCCESS) {

> +		lpfc_els_free_iocb(phba, elsiocb);

> +		lpfc_nlp_put(ndlp);

> +		return -EIO;

> +	}

> +	vport->vmid_flag &= ~LPFC_VMID_QOS_ENABLED;

> +	return 0;

> +}


-- 
Best Regards, Benjamin Block  / Linux on IBM Z Kernel Development / IBM Systems
IBM Deutschland Research & Development GmbH    /    https://www.ibm.com/privacy
Vorsitz. AufsR.: Gregor Pillen         /        Geschäftsführung: Dirk Wittkopp
Sitz der Gesellschaft: Böblingen / Registergericht: AmtsG Stuttgart, HRB 243294
James Smart April 21, 2021, 10:55 p.m. UTC | #2
On 4/20/2021 5:38 AM, Benjamin Block wrote:
...
>> +	len = *((u32 *)(pcmd + 4));

>> +	len = be32_to_cpu(len);

>> +	memcpy(vport->qfpa_res, pcmd, len + 8);

>> +	len = len / LPFC_PRIORITY_RANGE_DESC_SIZE;

>> +

>> +	desc = (struct priority_range_desc *)(pcmd + 8);

>> +	vmid_range = vport->vmid_priority.vmid_range;

>> +	if (!vmid_range) {

>> +		vmid_range = kcalloc(MAX_PRIORITY_DESC, sizeof(*vmid_range),

>> +				     GFP_KERNEL);

>> +		if (!vmid_range) {

>> +			kfree(vport->qfpa_res);

>> +			goto out;

>> +		}

>> +		vport->vmid_priority.vmid_range = vmid_range;

>> +	}

>> +	vport->vmid_priority.num_descriptors = len;

>> +

>> +	for (i = 0; i < len; i++, vmid_range++, desc++) {

>> +		lpfc_printf_vlog(vport, KERN_DEBUG, LOG_ELS,

>> +				 "6539 vmid values low=%d, high=%d, qos=%d, "

>> +				 "local ve id=%d\n", desc->lo_range,

>> +				 desc->hi_range, desc->qos_priority,

>> +				 desc->local_ve_id);

>> +

>> +		vmid_range->low = desc->lo_range << 1;

>> +		if (desc->local_ve_id == QFPA_ODD_ONLY)

>> +			vmid_range->low++;

>> +		if (desc->qos_priority)

>> +			vport->vmid_flag |= LPFC_VMID_QOS_ENABLED;

>> +		vmid_range->qos = desc->qos_priority;

> 

> I'm curios, if the FC-switch signals it supports QoS for a range here, how

> exactly interacts this with the VM IDs that you seem to allocate

> dynamically during runtime for cgroups that request specific App IDs?

> You don't seem to use `LPFC_VMID_QOS_ENABLED` anywhere else in the

> series. >

> Would different cgroups get different QoS classes/guarantees depending

> on the selected VM ID (higher VM ID gets better QoS class, or something

> like that?)? Would the tagged traffic be handled differently than the

> ordinary traffic in the fabric?


The simple answer is there is no interaction w/ the cgroup on priority.
And no- we really don't look or use it.  The ranges don't really have 
hard priority values. The way it works is that all values within a range 
is equal; a value in the first range is "higher priority" than a value 
in the second range; and a value in the second range is higher than 
those in the third range, and so on. Doesn't really matter whether the 
range was marked Best Effort or H/M/L. There's no real "weight".

What you see is the driver simply recording the different ranges so that 
it knows what to allocate from later on. The driver creates a flat 
bitmap of all possible values (max of 255) from all ranges - then will 
allocate values on a first bit set basis.  I know at one point we were 
going to only auto-assign if there was 1 range, and if multiple range 
was going to defer a mgmt authority to tell us which range, but this 
obviously doesn't do that.

Also... although this is coded to support the full breadth of what the 
standard allows, it may well be the switch only implements 1 range in 
practice.

> 

> I tried to get something from FC-LS (-5) or FC-FS (-6), but they are extremely

> sparse somehow. FC-LS-5 just says "QoS priority provided" for the

> field.. and FC-FS doesn't say anything regarding QoS if the tagging

> extension in CS_CTL is used.


Yes - most of the discussion on how this form of VMID is used/performed 
was given in the T11 proposals, but as most of that is informational and 
non-normative, very little ends up getting into the spec.

FC-LS-5 section 9 "Priority Tagging" is what you want to look at.

The other form of VMID is the Application Tag (up to 32bits) which is 
described in FC-GS-8 section 6.9 Application Server.  Both forms map a 
value to a uuid and the switch may apply some QoS level to the value 
when it sees it.

The priority tagging method seems to tie in more to qos, but the 
application tag is can equally be done although any qos aspects are 
solely in the switch and not exported to the driver/host.

-- james
Benjamin Block April 22, 2021, 9:28 a.m. UTC | #3
On Wed, Apr 21, 2021 at 03:55:15PM -0700, James Smart wrote:
> On 4/20/2021 5:38 AM, Benjamin Block wrote:

> ...

> > > +	len = *((u32 *)(pcmd + 4));

> > > +	len = be32_to_cpu(len);

> > > +	memcpy(vport->qfpa_res, pcmd, len + 8);

> > > +	len = len / LPFC_PRIORITY_RANGE_DESC_SIZE;

> > > +

> > > +	desc = (struct priority_range_desc *)(pcmd + 8);

> > > +	vmid_range = vport->vmid_priority.vmid_range;

> > > +	if (!vmid_range) {

> > > +		vmid_range = kcalloc(MAX_PRIORITY_DESC, sizeof(*vmid_range),

> > > +				     GFP_KERNEL);

> > > +		if (!vmid_range) {

> > > +			kfree(vport->qfpa_res);

> > > +			goto out;

> > > +		}

> > > +		vport->vmid_priority.vmid_range = vmid_range;

> > > +	}

> > > +	vport->vmid_priority.num_descriptors = len;

> > > +

> > > +	for (i = 0; i < len; i++, vmid_range++, desc++) {

> > > +		lpfc_printf_vlog(vport, KERN_DEBUG, LOG_ELS,

> > > +				 "6539 vmid values low=%d, high=%d, qos=%d, "

> > > +				 "local ve id=%d\n", desc->lo_range,

> > > +				 desc->hi_range, desc->qos_priority,

> > > +				 desc->local_ve_id);

> > > +

> > > +		vmid_range->low = desc->lo_range << 1;

> > > +		if (desc->local_ve_id == QFPA_ODD_ONLY)

> > > +			vmid_range->low++;

> > > +		if (desc->qos_priority)

> > > +			vport->vmid_flag |= LPFC_VMID_QOS_ENABLED;

> > > +		vmid_range->qos = desc->qos_priority;

> > 

> > I'm curios, if the FC-switch signals it supports QoS for a range here, how

> > exactly interacts this with the VM IDs that you seem to allocate

> > dynamically during runtime for cgroups that request specific App IDs?

> > You don't seem to use `LPFC_VMID_QOS_ENABLED` anywhere else in the

> > series. >

> > Would different cgroups get different QoS classes/guarantees depending

> > on the selected VM ID (higher VM ID gets better QoS class, or something

> > like that?)? Would the tagged traffic be handled differently than the

> > ordinary traffic in the fabric?

> 

> The simple answer is there is no interaction w/ the cgroup on priority.

> And no- we really don't look or use it.  The ranges don't really have hard

> priority values. The way it works is that all values within a range is

> equal; a value in the first range is "higher priority" than a value in the

> second range; and a value in the second range is higher than those in the

> third range, and so on. 


Ah. That's interesting. I thought it is like that, but wasn't sure from
the spec. Thanks for clarifying.

> Doesn't really matter whether the range was marked

> Best Effort or H/M/L. There's no real "weight".

> 

> What you see is the driver simply recording the different ranges so that it

> knows what to allocate from later on. The driver creates a flat bitmap of

> all possible values (max of 255) from all ranges - then will allocate values

> on a first bit set basis.  I know at one point we were going to only

> auto-assign if there was 1 range, and if multiple range was going to defer a

> mgmt authority to tell us which range, but this obviously doesn't do that.


I was worrying a bit whether this would create some hard to debug
problems in the wild, when QoS essentially depends on the order in which
Applications/Containers are started and get IDs assigned accordingly -
assuming there is multiple priority ranges.

> Also... although this is coded to support the full breadth of what the

> standard allows, it may well be the switch only implements 1 range in

> practice.

> 


-- 
Best Regards, Benjamin Block  / Linux on IBM Z Kernel Development / IBM Systems
IBM Deutschland Research & Development GmbH    /    https://www.ibm.com/privacy
Vorsitz. AufsR.: Gregor Pillen         /        Geschäftsführung: Dirk Wittkopp
Sitz der Gesellschaft: Böblingen / Registergericht: AmtsG Stuttgart, HRB 243294
diff mbox series

Patch

diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
index a04546eca18f..22a87559f62d 100644
--- a/drivers/scsi/lpfc/lpfc_els.c
+++ b/drivers/scsi/lpfc/lpfc_els.c
@@ -25,6 +25,7 @@ 
 #include <linux/pci.h>
 #include <linux/slab.h>
 #include <linux/interrupt.h>
+#include <linux/delay.h>
 
 #include <scsi/scsi.h>
 #include <scsi/scsi_device.h>
@@ -55,9 +56,15 @@  static int lpfc_issue_els_fdisc(struct lpfc_vport *vport,
 				struct lpfc_nodelist *ndlp, uint8_t retry);
 static int lpfc_issue_fabric_iocb(struct lpfc_hba *phba,
 				  struct lpfc_iocbq *iocb);
+static void lpfc_cmpl_els_uvem(struct lpfc_hba *, struct lpfc_iocbq *,
+			       struct lpfc_iocbq *);
 
 static int lpfc_max_els_tries = 3;
 
+static void lpfc_init_cs_ctl_bitmap(struct lpfc_vport *vport);
+static void lpfc_vmid_set_cs_ctl_range(struct lpfc_vport *vport, u32 min, u32 max);
+static void lpfc_vmid_put_cs_ctl(struct lpfc_vport *vport, u32 ctcl_vmid);
+
 /**
  * lpfc_els_chk_latt - Check host link attention event for a vport
  * @vport: pointer to a host virtual N_Port data structure.
@@ -314,10 +321,10 @@  lpfc_prep_els_iocb(struct lpfc_vport *vport, uint8_t expectRsp,
 		lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
 				 "0116 Xmit ELS command x%x to remote "
 				 "NPORT x%x I/O tag: x%x, port state:x%x "
-				 "rpi x%x fc_flag:x%x\n",
+				 "rpi x%x fc_flag:x%x nlp_flag:x%x vport:x%p\n",
 				 elscmd, did, elsiocb->iotag,
 				 vport->port_state, ndlp->nlp_rpi,
-				 vport->fc_flag);
+				 vport->fc_flag, ndlp->nlp_flag, vport);
 	} else {
 		/* Xmit ELS response <elsCmd> to remote NPORT <did> */
 		lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
@@ -1112,11 +1119,15 @@  lpfc_cmpl_els_flogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
 	/* FLOGI completes successfully */
 	lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
 			 "0101 FLOGI completes successfully, I/O tag:x%x, "
-			 "xri x%x Data: x%x x%x x%x x%x x%x %x\n",
+			 "xri x%x Data: x%x x%x x%x x%x x%x x%x x%x\n",
 			 cmdiocb->iotag, cmdiocb->sli4_xritag,
 			 irsp->un.ulpWord[4], sp->cmn.e_d_tov,
 			 sp->cmn.w2.r_a_tov, sp->cmn.edtovResolution,
-			 vport->port_state, vport->fc_flag);
+			 vport->port_state, vport->fc_flag,
+			 sp->cmn.priority_tagging);
+
+	if (sp->cmn.priority_tagging)
+		vport->vmid_flag |= LPFC_VMID_ISSUE_QFPA;
 
 	if (vport->port_state == LPFC_FLOGI) {
 		/*
@@ -1299,6 +1310,18 @@  lpfc_issue_els_flogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
 	if (sp->cmn.fcphHigh < FC_PH3)
 		sp->cmn.fcphHigh = FC_PH3;
 
+	/* to deterine if switch supports priority tagging */
+	if (phba->cfg_vmid_priority_tagging) {
+		sp->cmn.priority_tagging = 1;
+		/* lpfc_vmid_host_uuid is combination of wwpn and wwnn */
+		if (uuid_is_null((uuid_t *)vport->lpfc_vmid_host_uuid)) {
+			memcpy(vport->lpfc_vmid_host_uuid, phba->wwpn,
+			       sizeof(phba->wwpn));
+			memcpy(&vport->lpfc_vmid_host_uuid[8], phba->wwnn,
+			       sizeof(phba->wwnn));
+		}
+	}
+
 	if  (phba->sli_rev == LPFC_SLI_REV4) {
 		if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) ==
 		    LPFC_SLI_INTF_IF_TYPE_0) {
@@ -1907,6 +1930,7 @@  lpfc_cmpl_els_plogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
 	struct lpfc_nodelist *ndlp, *free_ndlp;
 	struct lpfc_dmabuf *prsp;
 	int disc;
+	struct serv_parm *sp = NULL;
 
 	/* we pass cmdiocb to state machine which needs rspiocb as well */
 	cmdiocb->context_un.rsp_iocb = rspiocb;
@@ -1997,6 +2021,23 @@  lpfc_cmpl_els_plogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
 				   cmdiocb->context2)->list.next,
 				  struct lpfc_dmabuf, list);
 		ndlp = lpfc_plogi_confirm_nport(phba, prsp->virt, ndlp);
+
+		sp = (struct serv_parm *)((u8 *)prsp->virt +
+					  sizeof(u32));
+
+		ndlp->vmid_support = 0;
+		if ((phba->cfg_vmid_app_header && sp->cmn.app_hdr_support) ||
+		    (phba->cfg_vmid_priority_tagging &&
+		     sp->cmn.priority_tagging)) {
+			lpfc_printf_log(phba, KERN_DEBUG, LOG_ELS,
+					"4018 app_hdr_support %d tagging %d DID x%x\n",
+					sp->cmn.app_hdr_support,
+					sp->cmn.priority_tagging,
+					ndlp->nlp_DID);
+			/* if the dest port supports VMID, mark it in ndlp */
+			ndlp->vmid_support = 1;
+		}
+
 		lpfc_disc_state_machine(vport, ndlp, cmdiocb,
 					NLP_EVT_CMPL_PLOGI);
 	}
@@ -2119,6 +2160,14 @@  lpfc_issue_els_plogi(struct lpfc_vport *vport, uint32_t did, uint8_t retry)
 	memset(sp->un.vendorVersion, 0, sizeof(sp->un.vendorVersion));
 	sp->cmn.bbRcvSizeMsb &= 0xF;
 
+	/* check if the destination port supports VMID */
+	ndlp->vmid_support = 0;
+	if (vport->vmid_priority_tagging)
+		sp->cmn.priority_tagging = 1;
+	else if (phba->cfg_vmid_app_header &&
+		 bf_get(lpfc_ftr_ashdr, &phba->sli4_hba.sli4_flags))
+		sp->cmn.app_hdr_support = 1;
+
 	lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
 		"Issue PLOGI:     did:x%x",
 		did, 0, 0);
@@ -10260,3 +10309,312 @@  lpfc_sli_abts_recover_port(struct lpfc_vport *vport,
 	lpfc_unreg_rpi(vport, ndlp);
 }
 
+static void lpfc_init_cs_ctl_bitmap(struct lpfc_vport *vport)
+{
+	bitmap_zero(vport->vmid_priority_range, LPFC_VMID_MAX_PRIORITY_RANGE);
+}
+
+static void
+lpfc_vmid_set_cs_ctl_range(struct lpfc_vport *vport, u32 min, u32 max)
+{
+	u32 i;
+
+	if ((min > max) || (max > LPFC_VMID_MAX_PRIORITY_RANGE))
+		return;
+
+	for (i = min; i <= max; i++)
+		set_bit(i, vport->vmid_priority_range);
+}
+
+static void lpfc_vmid_put_cs_ctl(struct lpfc_vport *vport, u32 ctcl_vmid)
+{
+	set_bit(ctcl_vmid, vport->vmid_priority_range);
+}
+
+u32 lpfc_vmid_get_cs_ctl(struct lpfc_vport *vport)
+{
+	u32 i;
+
+	i = find_first_bit(vport->vmid_priority_range,
+			   LPFC_VMID_MAX_PRIORITY_RANGE);
+
+	if (i == LPFC_VMID_MAX_PRIORITY_RANGE)
+		return 0;
+
+	clear_bit(i, vport->vmid_priority_range);
+	return i;
+}
+
+#define MAX_PRIORITY_DESC	255
+
+static void
+lpfc_cmpl_els_qfpa(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
+		   struct lpfc_iocbq *rspiocb)
+{
+	struct lpfc_vport *vport = cmdiocb->vport;
+	struct priority_range_desc *desc;
+	struct lpfc_dmabuf *prsp = NULL;
+	struct lpfc_vmid_priority_range *vmid_range = NULL;
+	u32 *data;
+	struct lpfc_dmabuf *dmabuf = cmdiocb->context2;
+	IOCB_t *irsp = &rspiocb->iocb;
+	u8 *pcmd, max_desc;
+	u32 len, i;
+	struct lpfc_nodelist *ndlp = (struct lpfc_nodelist *)cmdiocb->context1;
+
+	prsp = list_get_first(&dmabuf->list, struct lpfc_dmabuf, list);
+	if (!prsp)
+		goto out;
+
+	pcmd = prsp->virt;
+	data = (u32 *)pcmd;
+	if (data[0] == ELS_CMD_LS_RJT) {
+		lpfc_printf_vlog(vport, KERN_WARNING, LOG_SLI,
+				 "3277 QFPA LS_RJT x%x  x%x\n",
+				 data[0], data[1]);
+		goto out;
+	}
+	if (irsp->ulpStatus) {
+		lpfc_printf_vlog(vport, KERN_ERR, LOG_SLI,
+				 "6529 QFPA failed with status x%x  x%x\n",
+				 irsp->ulpStatus, irsp->un.ulpWord[4]);
+		goto out;
+	}
+
+	if (!vport->qfpa_res) {
+		max_desc = FCELSSIZE / sizeof(*vport->qfpa_res);
+		vport->qfpa_res = kcalloc(max_desc, sizeof(*vport->qfpa_res),
+					  GFP_KERNEL);
+		if (!vport->qfpa_res)
+			goto out;
+	}
+
+	len = *((u32 *)(pcmd + 4));
+	len = be32_to_cpu(len);
+	memcpy(vport->qfpa_res, pcmd, len + 8);
+	len = len / LPFC_PRIORITY_RANGE_DESC_SIZE;
+
+	desc = (struct priority_range_desc *)(pcmd + 8);
+	vmid_range = vport->vmid_priority.vmid_range;
+	if (!vmid_range) {
+		vmid_range = kcalloc(MAX_PRIORITY_DESC, sizeof(*vmid_range),
+				     GFP_KERNEL);
+		if (!vmid_range) {
+			kfree(vport->qfpa_res);
+			goto out;
+		}
+		vport->vmid_priority.vmid_range = vmid_range;
+	}
+	vport->vmid_priority.num_descriptors = len;
+
+	for (i = 0; i < len; i++, vmid_range++, desc++) {
+		lpfc_printf_vlog(vport, KERN_DEBUG, LOG_ELS,
+				 "6539 vmid values low=%d, high=%d, qos=%d, "
+				 "local ve id=%d\n", desc->lo_range,
+				 desc->hi_range, desc->qos_priority,
+				 desc->local_ve_id);
+
+		vmid_range->low = desc->lo_range << 1;
+		if (desc->local_ve_id == QFPA_ODD_ONLY)
+			vmid_range->low++;
+		if (desc->qos_priority)
+			vport->vmid_flag |= LPFC_VMID_QOS_ENABLED;
+		vmid_range->qos = desc->qos_priority;
+
+		vmid_range->high = desc->hi_range << 1;
+		if ((desc->local_ve_id == QFPA_ODD_ONLY) ||
+		    (desc->local_ve_id == QFPA_EVEN_ODD))
+			vmid_range->high++;
+	}
+	lpfc_init_cs_ctl_bitmap(vport);
+	for (i = 0; i < vport->vmid_priority.num_descriptors; i++) {
+		lpfc_vmid_set_cs_ctl_range(vport,
+				vport->vmid_priority.vmid_range[i].low,
+				vport->vmid_priority.vmid_range[i].high);
+	}
+
+	vport->vmid_flag |= LPFC_VMID_QFPA_CMPL;
+ out:
+	lpfc_els_free_iocb(phba, cmdiocb);
+	lpfc_nlp_put(ndlp);
+}
+
+int lpfc_issue_els_qfpa(struct lpfc_vport *vport)
+{
+	struct lpfc_hba *phba = vport->phba;
+	struct lpfc_nodelist *ndlp;
+	struct lpfc_iocbq *elsiocb;
+	u8 *pcmd;
+	int ret;
+
+	ndlp = lpfc_findnode_did(phba->pport, Fabric_DID);
+	if (!ndlp || ndlp->nlp_state != NLP_STE_UNMAPPED_NODE)
+		return -ENXIO;
+
+	elsiocb = lpfc_prep_els_iocb(vport, 1, LPFC_QFPA_SIZE, 2, ndlp,
+				     ndlp->nlp_DID, ELS_CMD_QFPA);
+	if (!elsiocb)
+		return -ENOMEM;
+
+	pcmd = (u8 *)(((struct lpfc_dmabuf *)elsiocb->context2)->virt);
+
+	*((u32 *)(pcmd)) = ELS_CMD_QFPA;
+	pcmd += 4;
+
+	elsiocb->iocb_cmpl = lpfc_cmpl_els_qfpa;
+
+	elsiocb->context1 = lpfc_nlp_get(ndlp);
+	if (!elsiocb->context1) {
+		lpfc_els_free_iocb(vport->phba, elsiocb);
+		return -ENXIO;
+	}
+
+	ret = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 2);
+	if (ret != IOCB_SUCCESS) {
+		lpfc_els_free_iocb(phba, elsiocb);
+		lpfc_nlp_put(ndlp);
+		return -EIO;
+	}
+	vport->vmid_flag &= ~LPFC_VMID_QOS_ENABLED;
+	return 0;
+}
+
+int
+lpfc_vmid_uvem(struct lpfc_vport *vport,
+	       struct lpfc_vmid *vmid, bool instantiated)
+{
+	struct lpfc_vem_id_desc *vem_id_desc;
+	struct lpfc_nodelist *ndlp;
+	struct lpfc_iocbq *elsiocb;
+	struct instantiated_ve_desc *inst_desc;
+	struct lpfc_vmid_context *vmid_context;
+	u8 *pcmd;
+	u32 *len;
+	int ret = 0;
+
+	ndlp = lpfc_findnode_did(vport, Fabric_DID);
+	if (!ndlp || ndlp->nlp_state != NLP_STE_UNMAPPED_NODE)
+		return -ENXIO;
+
+	vmid_context = kmalloc(sizeof(*vmid_context), GFP_KERNEL);
+	if (!vmid_context)
+		return -ENOMEM;
+	elsiocb = lpfc_prep_els_iocb(vport, 1, LPFC_UVEM_SIZE, 2,
+				     ndlp, Fabric_DID, ELS_CMD_UVEM);
+	if (!elsiocb)
+		goto out;
+
+	lpfc_printf_vlog(vport, KERN_DEBUG, LOG_ELS,
+			 "3427 Host vmid %s %d\n",
+			 vmid->host_vmid, instantiated);
+	vmid_context->vmp = vmid;
+	vmid_context->nlp = ndlp;
+	vmid_context->instantiated = instantiated;
+	elsiocb->vmid_tag.vmid_context = vmid_context;
+	pcmd = (u8 *)(((struct lpfc_dmabuf *)elsiocb->context2)->virt);
+
+	if (uuid_is_null((uuid_t *)vport->lpfc_vmid_host_uuid))
+		memcpy(vport->lpfc_vmid_host_uuid, vmid->host_vmid,
+		       LPFC_COMPRESS_VMID_SIZE);
+
+	*((u32 *)(pcmd)) = ELS_CMD_UVEM;
+	len = (u32 *)(pcmd + 4);
+	*len = cpu_to_be32(LPFC_UVEM_SIZE - 8);
+
+	vem_id_desc = (struct lpfc_vem_id_desc *)(pcmd + 8);
+	vem_id_desc->tag = be32_to_cpu(VEM_ID_DESC_TAG);
+	vem_id_desc->length = be32_to_cpu(LPFC_UVEM_VEM_ID_DESC_SIZE);
+	memcpy(vem_id_desc->vem_id, vport->lpfc_vmid_host_uuid,
+	       LPFC_COMPRESS_VMID_SIZE);
+
+	inst_desc = (struct instantiated_ve_desc *)(pcmd + 32);
+	inst_desc->tag = be32_to_cpu(INSTANTIATED_VE_DESC_TAG);
+	inst_desc->length = be32_to_cpu(LPFC_UVEM_VE_MAP_DESC_SIZE);
+	memcpy(inst_desc->global_vem_id, vmid->host_vmid,
+	       LPFC_COMPRESS_VMID_SIZE);
+
+	bf_set(lpfc_instantiated_nport_id, inst_desc, vport->fc_myDID);
+	bf_set(lpfc_instantiated_local_id, inst_desc,
+	       vmid->un.cs_ctl_vmid);
+	if (instantiated) {
+		inst_desc->tag = be32_to_cpu(INSTANTIATED_VE_DESC_TAG);
+	} else {
+		inst_desc->tag = be32_to_cpu(DEINSTANTIATED_VE_DESC_TAG);
+		lpfc_vmid_put_cs_ctl(vport, vmid->un.cs_ctl_vmid);
+	}
+	inst_desc->word6 = cpu_to_be32(inst_desc->word6);
+
+	elsiocb->iocb_cmpl = lpfc_cmpl_els_uvem;
+
+	elsiocb->context1 = lpfc_nlp_get(ndlp);
+	if (!elsiocb->context1) {
+		lpfc_els_free_iocb(vport->phba, elsiocb);
+		goto out;
+	}
+
+	ret = lpfc_sli_issue_iocb(vport->phba, LPFC_ELS_RING, elsiocb, 0);
+	if (ret != IOCB_SUCCESS) {
+		lpfc_els_free_iocb(vport->phba, elsiocb);
+		lpfc_nlp_put(ndlp);
+		goto out;
+	}
+
+	return 0;
+ out:
+	kfree(vmid_context);
+	return -EIO;
+}
+
+static void
+lpfc_cmpl_els_uvem(struct lpfc_hba *phba, struct lpfc_iocbq *icmdiocb,
+		   struct lpfc_iocbq *rspiocb)
+{
+	struct lpfc_vport *vport = icmdiocb->vport;
+	struct lpfc_dmabuf *prsp = NULL;
+	struct lpfc_vmid_context *vmid_context =
+	    icmdiocb->vmid_tag.vmid_context;
+	struct lpfc_nodelist *ndlp = icmdiocb->context1;
+	u8 *pcmd;
+	u32 *data;
+	IOCB_t *irsp = &rspiocb->iocb;
+	struct lpfc_dmabuf *dmabuf = icmdiocb->context2;
+	struct lpfc_vmid *vmid;
+
+	vmid = vmid_context->vmp;
+	if (!ndlp || ndlp->nlp_state != NLP_STE_UNMAPPED_NODE)
+		ndlp = NULL;
+
+	prsp = list_get_first(&dmabuf->list, struct lpfc_dmabuf, list);
+	if (!prsp)
+		goto out;
+	pcmd = prsp->virt;
+	data = (u32 *)pcmd;
+	if (data[0] == ELS_CMD_LS_RJT) {
+		lpfc_printf_vlog(vport, KERN_WARNING, LOG_SLI,
+				 "4532 UVEM LS_RJT %x %x\n", data[0], data[1]);
+		goto out;
+	}
+	if (irsp->ulpStatus) {
+		lpfc_printf_vlog(vport, KERN_WARNING, LOG_SLI,
+				 "4533 UVEM error status %x: %x\n",
+				 irsp->ulpStatus, irsp->un.ulpWord[4]);
+		goto out;
+	}
+	spin_lock(&phba->hbalock);
+	/* Set IN USE flag */
+	vport->vmid_flag |= LPFC_VMID_IN_USE;
+	phba->pport->vmid_flag |= LPFC_VMID_IN_USE;
+	spin_unlock(&phba->hbalock);
+
+	if (vmid_context->instantiated) {
+		write_lock(&vport->vmid_lock);
+		vmid->flag |= LPFC_VMID_REGISTERED;
+		vmid->flag &= ~LPFC_VMID_REQ_REGISTER;
+		write_unlock(&vport->vmid_lock);
+	}
+
+ out:
+	kfree(vmid_context);
+	lpfc_els_free_iocb(phba, icmdiocb);
+	lpfc_nlp_put(ndlp);
+}