david/ipxe
david
/
ipxe
Archived
1
0
Fork 0

[intel] Strip spurious VLAN tags received by virtual function NICs

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>
This commit is contained in:
Michael Brown 2016-07-11 17:14:14 +01:00
parent 45dd627689
commit db3443608f
4 changed files with 144 additions and 0 deletions

View 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;
}

View 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];
};

View 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 */

View 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;