summaryrefslogtreecommitdiffstats
path: root/sys/dev/em
diff options
context:
space:
mode:
authorrwatson <rwatson@FreeBSD.org>2004-11-12 11:03:07 +0000
committerrwatson <rwatson@FreeBSD.org>2004-11-12 11:03:07 +0000
commit56c8fde970f28b986c87f945a96e3ec8dee0b33c (patch)
treeedcc570a8ec04acc58fd5fe35c2e17694ab8b033 /sys/dev/em
parent488ffe7864b0ea2c3de649d6253be13fe9a7e67b (diff)
downloadFreeBSD-src-56c8fde970f28b986c87f945a96e3ec8dee0b33c.zip
FreeBSD-src-56c8fde970f28b986c87f945a96e3ec8dee0b33c.tar.gz
Correct a bug in the if_em driver relating to the use of vlans with
promiscuous mode introduced in 1.45, which programs the em card not to strip or prepend tags when in promiscuous mode without also modifying behavior to manually prepend a vlan header in the event that the card isn't doing it on transmit. Due to a feature of card operation, if the global VLAN prepend/strip register isn't set, setting the VLAN tag flag on individual packet descriptors will cause the packet to be transmitted using ISL encapsulation rather than 802.1Q VLAN encapsulation. This fix causes em_encap() to prepend the header by tracking whether the card is configured to temporarily disable prepending/stripping due to promiscuous mode. As a result, entering promiscuous mode on the parent em interface no longer causes vlans to appear to "wedge" or transmit ISL-encapsulated frames, which typically will not be configured/spoken by the other endpoints on the VLAN trunk. This bug may also exist in other drivers, and the additional vlan encapsulation logic should be abstracted and centralized in if_vlan.c if so. RELENG_5_3 candidate. MFC after: 1 week Tested by: pjd, rwatson Reported by: astesin at ukrtelecom dot net Reported by: Mike Tancsa <mike at sentex dot net> Reported by: Iasen Kostov <tbyte at OTEL dot net>
Diffstat (limited to 'sys/dev/em')
-rw-r--r--sys/dev/em/if_em.c49
-rw-r--r--sys/dev/em/if_em.h1
2 files changed, 44 insertions, 6 deletions
diff --git a/sys/dev/em/if_em.c b/sys/dev/em/if_em.c
index 389915b..51a574e 100644
--- a/sys/dev/em/if_em.c
+++ b/sys/dev/em/if_em.c
@@ -161,7 +161,7 @@ static void em_print_link_status(struct adapter *);
static int em_get_buf(int i, struct adapter *,
struct mbuf *);
static void em_enable_vlans(struct adapter *);
-static int em_encap(struct adapter *, struct mbuf *);
+static int em_encap(struct adapter *, struct mbuf **);
static void em_smartspeed(struct adapter *);
static int em_82547_fifo_workaround(struct adapter *, int);
static void em_82547_update_fifo_head(struct adapter *, int);
@@ -599,7 +599,7 @@ em_start_locked(struct ifnet *ifp)
if (m_head == NULL) break;
- if (em_encap(adapter, m_head)) {
+ if (em_encap(adapter, &m_head)) {
ifp->if_flags |= IFF_OACTIVE;
IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
break;
@@ -1159,13 +1159,15 @@ em_tx_cb(void *arg, bus_dma_segment_t *seg, int nsegs, bus_size_t mapsize, int e
* return 0 on success, positive on failure
**********************************************************************/
static int
-em_encap(struct adapter *adapter, struct mbuf *m_head)
+em_encap(struct adapter *adapter, struct mbuf **m_headp)
{
u_int32_t txd_upper;
u_int32_t txd_lower, txd_used = 0, txd_saved = 0;
int i, j, error;
u_int64_t address;
+ struct mbuf *m_head;
+
/* For 82544 Workaround */
DESC_ARRAY desc_array;
u_int32_t array_elements;
@@ -1181,6 +1183,8 @@ em_encap(struct adapter *adapter, struct mbuf *m_head)
struct em_tx_desc *current_tx_desc = NULL;
struct ifnet *ifp = &adapter->interface_data.ac_if;
+ m_head = *m_headp;
+
/*
* Force a cleanup if number of TX descriptors
* available hits the threshold
@@ -1233,6 +1237,36 @@ em_encap(struct adapter *adapter, struct mbuf *m_head)
mtag = VLAN_OUTPUT_TAG(ifp, m_head);
#endif
+ /*
+ * When operating in promiscuous mode, hardware encapsulation for
+ * packets is disabled. This means we have to add the vlan
+ * encapsulation in the driver, since it will have come down from the
+ * VLAN layer with a tag instead of a VLAN header.
+ */
+ if (mtag != NULL && adapter->em_insert_vlan_header) {
+ struct ether_vlan_header *evl;
+ struct ether_header eh;
+
+ m_head = m_pullup(m_head, sizeof(eh));
+ if (m_head == NULL)
+ return (ENOBUFS);
+ eh = *mtod(m_head, struct ether_header *);
+ M_PREPEND(m_head, sizeof(*evl), M_DONTWAIT);
+ if (m_head == NULL)
+ return (ENOBUFS);
+ m_head = m_pullup(m_head, sizeof(*evl));
+ if (m_head == NULL)
+ return (ENOBUFS);
+ evl = mtod(m_head, struct ether_vlan_header *);
+ bcopy(&eh, evl, sizeof(*evl));
+ evl->evl_proto = evl->evl_encap_proto;
+ evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
+ evl->evl_tag = htons(VLAN_TAG_VALUE(mtag));
+ m_tag_delete(m_head, mtag);
+ mtag = NULL;
+ *m_headp = m_head;
+ }
+
i = adapter->next_avail_tx_desc;
if (adapter->pcix_82544) {
txd_saved = i;
@@ -1480,19 +1514,20 @@ em_set_promisc(struct adapter * adapter)
if (ifp->if_flags & IFF_PROMISC) {
reg_rctl |= (E1000_RCTL_UPE | E1000_RCTL_MPE);
E1000_WRITE_REG(&adapter->hw, RCTL, reg_rctl);
-
/* Disable VLAN stripping in promiscous mode
* This enables bridging of vlan tagged frames to occur
* and also allows vlan tags to be seen in tcpdump
*/
ctrl &= ~E1000_CTRL_VME;
E1000_WRITE_REG(&adapter->hw, CTRL, ctrl);
-
+ adapter->em_insert_vlan_header = 1;
} else if (ifp->if_flags & IFF_ALLMULTI) {
reg_rctl |= E1000_RCTL_MPE;
reg_rctl &= ~E1000_RCTL_UPE;
E1000_WRITE_REG(&adapter->hw, RCTL, reg_rctl);
- }
+ adapter->em_insert_vlan_header = 0;
+ } else
+ adapter->em_insert_vlan_header = 0;
return;
}
@@ -1509,6 +1544,8 @@ em_disable_promisc(struct adapter * adapter)
E1000_WRITE_REG(&adapter->hw, RCTL, reg_rctl);
em_enable_vlans(adapter);
+ adapter->em_insert_vlan_header = 0;
+
return;
}
diff --git a/sys/dev/em/if_em.h b/sys/dev/em/if_em.h
index 9c85b2a..5d3609e 100644
--- a/sys/dev/em/if_em.h
+++ b/sys/dev/em/if_em.h
@@ -346,6 +346,7 @@ struct adapter {
int io_rid;
u_int8_t unit;
struct mtx mtx;
+ int em_insert_vlan_header;
/* Info about the board itself */
u_int32_t part_num;
OpenPOWER on IntegriCloud