diff --git a/src/drivers/net/intelvf.c b/src/drivers/net/intelvf.c index ac6fea745..537e0fbb1 100644 --- a/src/drivers/net/intelvf.c +++ b/src/drivers/net/intelvf.c @@ -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; +} diff --git a/src/drivers/net/intelvf.h b/src/drivers/net/intelvf.h index d2f98d874..ab404698f 100644 --- a/src/drivers/net/intelvf.h +++ b/src/drivers/net/intelvf.h @@ -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]; }; diff --git a/src/drivers/net/intelx.h b/src/drivers/net/intelx.h index 6383dfcad..d7f3b78e8 100644 --- a/src/drivers/net/intelx.h +++ b/src/drivers/net/intelx.h @@ -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 */ diff --git a/src/drivers/net/intelxvf.c b/src/drivers/net/intelxvf.c index 05e34c127..91a10b10f 100644 --- a/src/drivers/net/intelxvf.c +++ b/src/drivers/net/intelxvf.c @@ -30,6 +30,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#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;