]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[intel] Strip spurious VLAN tags received by virtual function NICs
authorMichael Brown <mcb30@ipxe.org>
Mon, 11 Jul 2016 16:14:14 +0000 (17:14 +0100)
committerMichael Brown <mcb30@ipxe.org>
Tue, 12 Jul 2016 08:14:41 +0000 (09:14 +0100)
The physical function may be configured to transparently insert a VLAN
tag into all transmitted packets.  Unfortunately, it does not
equivalently strip this same VLAN tag from all received packets.  This
behaviour may be observed in some Amazon EC2 instances with Enhanced
Networking enabled: transmissions work as expected but all packets
received by iPXE appear to have a spurious VLAN tag.

We can configure the receive queue to strip VLAN tags via the
RXDCTL.VME bit.  We need to find out from the PF driver whether or not
we should do so.

There exists a "get queue configuration" mailbox message which
contains a field labelled IXGBE_VF_TRANS_VLAN in the Linux driver.

A comment in the Linux PF driver describes this field as "notify VF of
need for VLAN tag stripping, and correct queue".  It will be filled
with a non-zero value if the PF is enforcing the use of a single VLAN
tag.  It will also be filled with a non-zero value if the PF is using
multiple traffic classes.

The Linux VF driver seems to treat this field as being simply the
number of traffic classes, and gives it no VLAN-related
interpretation.  The Linux VF driver instead handles the VLAN tag
stripping by simply assuming that any unrecognised VLAN tag ought to
be silently dropped.

We choose to strip and ignore the VLAN tag if the IXGBE_VF_TRANS_VLAN
field has a non-zero value.

Reported-by: Leonid Vasetsky <leonidv@velostrata.com>
Tested-by: Leonid Vasetsky <leonidv@velostrata.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/drivers/net/intelvf.c
src/drivers/net/intelvf.h
src/drivers/net/intelx.h
src/drivers/net/intelxvf.c

index ac6fea745457863544edf66581380f369bf2adc2..537e0fbb1828ed2d9324dd77f26adf09d6dda936 100644 (file)
@@ -338,3 +338,44 @@ int intelvf_mbox_set_mtu ( struct intel_nic *intel, size_t mtu ) {
 
        return 0;
 }
+
+/**
+ * Get queue configuration
+ *
+ * @v intel            Intel device
+ * @v vlan_thing       VLAN hand-waving thing to fill in
+ * @ret rc             Return status code
+ */
+int intelvf_mbox_queues ( struct intel_nic *intel, int *vlan_thing ) {
+       union intelvf_msg msg;
+       int rc;
+
+       /* Send queue configuration message */
+       memset ( &msg, 0, sizeof ( msg ) );
+       msg.hdr = INTELVF_MSG_TYPE_GET_QUEUES;
+       if ( ( rc = intelvf_mbox_msg ( intel, &msg ) ) != 0 ) {
+               DBGC ( intel, "INTEL %p get queue configuration failed: %s\n",
+                      intel, strerror ( rc ) );
+               return rc;
+       }
+
+       /* Check response */
+       if ( ( msg.hdr & INTELVF_MSG_TYPE_MASK ) !=INTELVF_MSG_TYPE_GET_QUEUES){
+               DBGC ( intel, "INTEL %p get queue configuration unexpected "
+                      "response:\n", intel );
+               DBGC_HDA ( intel, 0, &msg, sizeof ( msg ) );
+               return -EPROTO;
+       }
+
+       /* Check that we were allowed to get the queue configuration */
+       if ( ! ( msg.hdr & INTELVF_MSG_ACK ) ) {
+               DBGC ( intel, "INTEL %p get queue configuration refused\n",
+                      intel );
+               return -EPERM;
+       }
+
+       /* Extract VLAN hand-waving thing */
+       *vlan_thing = msg.queues.vlan_thing;
+
+       return 0;
+}
index d2f98d874028cd92312f3f21e50030b5ce2dccd0..ab404698fe6de9f48370931fdf5664c1a2569e2e 100644 (file)
@@ -37,6 +37,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 /** Set MTU mailbox message */
 #define INTELVF_MSG_TYPE_SET_MTU 0x00000005UL
 
+/** Get queue configuration message */
+#define INTELVF_MSG_TYPE_GET_QUEUES 0x00000009UL
+
 /** Control ("ping") mailbox message */
 #define INTELVF_MSG_TYPE_CONTROL 0x00000100UL
 
@@ -78,6 +81,44 @@ struct intelvf_msg_mtu {
        uint32_t mtu;
 } __attribute__ (( packed ));
 
+/** Queue configuration mailbox message (API v1.1+ only) */
+struct intelvf_msg_queues {
+       /** Message header */
+       uint32_t hdr;
+       /** Maximum number of transmit queues */
+       uint32_t tx;
+       /** Maximum number of receive queues */
+       uint32_t rx;
+       /** VLAN hand-waving thing
+        *
+        * This is labelled IXGBE_VF_TRANS_VLAN in the Linux driver.
+        *
+        * A comment in the Linux PF driver describes it as "notify VF
+        * of need for VLAN tag stripping, and correct queue".  It
+        * will be filled with a non-zero value if the PF is enforcing
+        * the use of a single VLAN tag.  It will also be filled with
+        * a non-zero value if the PF is using multiple traffic
+        * classes.
+        *
+        * The Linux VF driver seems to treat this field as being
+        * simply the number of traffic classes, and gives it no
+        * VLAN-related interpretation.
+        *
+        * If the PF is enforcing the use of a single VLAN tag for the
+        * VF, then the VLAN tag will be transparently inserted in
+        * transmitted packets (via the PFVMVIR register) but will
+        * still be visible in received packets.  The Linux VF driver
+        * handles this unexpected VLAN tag by simply ignoring any
+        * unrecognised VLAN tags.
+        *
+        * We choose to strip and ignore the VLAN tag if this field
+        * has a non-zero value.
+        */
+       uint32_t vlan_thing;
+       /** Default queue */
+       uint32_t dflt;
+} __attribute__ (( packed ));
+
 /** Mailbox message */
 union intelvf_msg {
        /** Message header */
@@ -88,6 +129,8 @@ union intelvf_msg {
        struct intelvf_msg_version version;
        /** MTU message */
        struct intelvf_msg_mtu mtu;
+       /** Queue configuration message */
+       struct intelvf_msg_queues queues;
        /** Raw dwords */
        uint32_t dword[0];
 };
index 6383dfcadb7a2eb54b6722a2a90feb990c1365af..d7f3b78e81cc0cbcd8234c7d10d307e514286349 100644 (file)
@@ -71,6 +71,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 /** Receive Descriptor register block */
 #define INTELX_RD 0x01000UL
 
+/** Receive Descriptor Control Register */
+#define INTELX_RXDCTL_VME      0x40000000UL    /**< Strip VLAN tag */
+
 /** Split Receive Control Register */
 #define INTELX_SRRCTL 0x02100UL
 #define INTELX_SRRCTL_BSIZE(kb)        ( (kb) << 0 )   /**< Receive buffer size */
index 05e34c12760d1f7d7549b44fb7367999187c4519..91a10b10f7d6ea01401a2160e696485715a3e4d0 100644 (file)
@@ -30,6 +30,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 #include <ipxe/pci.h>
 #include <ipxe/netdevice.h>
 #include <ipxe/ethernet.h>
+#include "intelx.h"
 #include "intelxvf.h"
 
 /** @file
@@ -156,6 +157,47 @@ static int intelxvf_mbox_version ( struct intel_nic *intel,
        return 0;
 }
 
+/**
+ * Get queue configuration
+ *
+ * @v intel            Intel device
+ * @v vlan_thing       VLAN hand-waving thing to fill in
+ * @ret rc             Return status code
+ */
+static int intelxvf_mbox_queues ( struct intel_nic *intel, int *vlan_thing ) {
+       union intelvf_msg msg;
+       int rc;
+
+       /* Send queue configuration message */
+       memset ( &msg, 0, sizeof ( msg ) );
+       msg.hdr = INTELVF_MSG_TYPE_GET_QUEUES;
+       if ( ( rc = intelvf_mbox_msg ( intel, &msg ) ) != 0 ) {
+               DBGC ( intel, "INTEL %p get queue configuration failed: %s\n",
+                      intel, strerror ( rc ) );
+               return rc;
+       }
+
+       /* Check response */
+       if ( ( msg.hdr & INTELVF_MSG_TYPE_MASK ) !=INTELVF_MSG_TYPE_GET_QUEUES){
+               DBGC ( intel, "INTEL %p get queue configuration unexpected "
+                      "response:\n", intel );
+               DBGC_HDA ( intel, 0, &msg, sizeof ( msg ) );
+               return -EPROTO;
+       }
+
+       /* Check that we were allowed to get the queue configuration */
+       if ( ! ( msg.hdr & INTELVF_MSG_ACK ) ) {
+               DBGC ( intel, "INTEL %p get queue configuration refused\n",
+                      intel );
+               return -EPERM;
+       }
+
+       /* Extract VLAN hand-waving thing */
+       *vlan_thing = msg.queues.vlan_thing;
+
+       return 0;
+}
+
 /******************************************************************************
  *
  * Network device interface
@@ -171,8 +213,10 @@ static int intelxvf_mbox_version ( struct intel_nic *intel,
  */
 static int intelxvf_open ( struct net_device *netdev ) {
        struct intel_nic *intel = netdev->priv;
+       uint32_t rxdctl;
        uint32_t srrctl;
        uint32_t dca_rxctrl;
+       int vlan_thing;
        int rc;
 
        /* Reset the function */
@@ -208,6 +252,19 @@ static int intelxvf_open ( struct net_device *netdev ) {
                goto err_mbox_set_mtu;
        }
 
+       /* Get queue configuration.  Ignore failures, since the host
+        * may not support this message.
+        */
+       vlan_thing = 0;
+       intelxvf_mbox_queues ( intel, &vlan_thing );
+       if ( vlan_thing ) {
+               DBGC ( intel, "INTEL %p stripping VLAN tags (thing=%d)\n",
+                      intel, vlan_thing );
+               rxdctl = readl ( intel->regs + INTELXVF_RD + INTEL_xDCTL );
+               rxdctl |= INTELX_RXDCTL_VME;
+               writel ( rxdctl, intel->regs + INTELXVF_RD + INTEL_xDCTL );
+       }
+
        /* Create transmit descriptor ring */
        if ( ( rc = intel_create_ring ( intel, &intel->tx ) ) != 0 )
                goto err_create_tx;