diff options
Diffstat (limited to 'drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c')
-rw-r--r-- | drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c | 145 |
1 files changed, 134 insertions, 11 deletions
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c index 2990f45df787..6387e0cc3ea9 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c @@ -152,6 +152,89 @@ static inline u32 qlcnic_get_ref_handle(struct qlcnic_adapter *adapter, return handle; } +static inline int qlcnic_82xx_is_lb_pkt(u64 sts_data) +{ + return (qlcnic_get_sts_status(sts_data) == STATUS_CKSUM_LOOP) ? 1 : 0; +} + +void qlcnic_add_lb_filter(struct qlcnic_adapter *adapter, struct sk_buff *skb, + int loopback_pkt, __le16 vlan_id) +{ + struct ethhdr *phdr = (struct ethhdr *)(skb->data); + struct qlcnic_filter *fil, *tmp_fil; + struct hlist_node *tmp_hnode, *n; + struct hlist_head *head; + unsigned long time; + u64 src_addr = 0; + u8 hindex, found = 0, op; + int ret; + + memcpy(&src_addr, phdr->h_source, ETH_ALEN); + + if (loopback_pkt) { + if (adapter->rx_fhash.fnum >= adapter->rx_fhash.fmax) + return; + + hindex = qlcnic_mac_hash(src_addr) & + (adapter->fhash.fbucket_size - 1); + head = &(adapter->rx_fhash.fhead[hindex]); + + hlist_for_each_entry_safe(tmp_fil, tmp_hnode, n, head, fnode) { + if (!memcmp(tmp_fil->faddr, &src_addr, ETH_ALEN) && + tmp_fil->vlan_id == vlan_id) { + time = tmp_fil->ftime; + if (jiffies > (QLCNIC_READD_AGE * HZ + time)) + tmp_fil->ftime = jiffies; + return; + } + } + + fil = kzalloc(sizeof(struct qlcnic_filter), GFP_ATOMIC); + if (!fil) + return; + + fil->ftime = jiffies; + memcpy(fil->faddr, &src_addr, ETH_ALEN); + fil->vlan_id = vlan_id; + spin_lock(&adapter->rx_mac_learn_lock); + hlist_add_head(&(fil->fnode), head); + adapter->rx_fhash.fnum++; + spin_unlock(&adapter->rx_mac_learn_lock); + } else { + hindex = qlcnic_mac_hash(src_addr) & + (adapter->fhash.fbucket_size - 1); + head = &(adapter->rx_fhash.fhead[hindex]); + spin_lock(&adapter->rx_mac_learn_lock); + hlist_for_each_entry_safe(tmp_fil, tmp_hnode, n, head, fnode) { + if (!memcmp(tmp_fil->faddr, &src_addr, ETH_ALEN) && + tmp_fil->vlan_id == vlan_id) { + found = 1; + break; + } + } + + if (!found) { + spin_unlock(&adapter->rx_mac_learn_lock); + return; + } + + op = vlan_id ? QLCNIC_MAC_VLAN_ADD : QLCNIC_MAC_ADD; + ret = qlcnic_sre_macaddr_change(adapter, (u8 *)&src_addr, + vlan_id, op); + if (!ret) { + op = vlan_id ? QLCNIC_MAC_VLAN_DEL : QLCNIC_MAC_DEL; + ret = qlcnic_sre_macaddr_change(adapter, + (u8 *)&src_addr, + vlan_id, op); + if (!ret) { + hlist_del(&(tmp_fil->fnode)); + adapter->rx_fhash.fnum--; + } + } + spin_unlock(&adapter->rx_mac_learn_lock); + } +} + void qlcnic_82xx_change_filter(struct qlcnic_adapter *adapter, u64 *uaddr, __le16 vlan_id) { @@ -207,9 +290,6 @@ static void qlcnic_send_filter(struct qlcnic_adapter *adapter, return; } - /* Only NPAR capable devices support vlan based learning */ - if (adapter->flags & QLCNIC_ESWITCH_ENABLED) - vlan_id = first_desc->vlan_TCI; memcpy(&src_addr, phdr->h_source, ETH_ALEN); hindex = qlcnic_mac_hash(src_addr) & (adapter->fhash.fbucket_size - 1); head = &(adapter->fhash.fhead[hindex]); @@ -920,8 +1000,8 @@ qlcnic_process_rcv(struct qlcnic_adapter *adapter, struct qlcnic_rx_buffer *buffer; struct sk_buff *skb; struct qlcnic_host_rds_ring *rds_ring; - int index, length, cksum, pkt_offset; - u16 vid = 0xffff; + int index, length, cksum, pkt_offset, is_lb_pkt; + u16 vid = 0xffff, t_vid; if (unlikely(ring >= adapter->max_rds_rings)) return NULL; @@ -941,6 +1021,14 @@ qlcnic_process_rcv(struct qlcnic_adapter *adapter, if (!skb) return buffer; + if (adapter->drv_mac_learn && + (adapter->flags & QLCNIC_ESWITCH_ENABLED)) { + t_vid = 0; + is_lb_pkt = qlcnic_82xx_is_lb_pkt(sts_data0); + qlcnic_add_lb_filter(adapter, skb, is_lb_pkt, + cpu_to_le16(t_vid)); + } + if (length > rds_ring->skb_size) skb_put(skb, rds_ring->skb_size); else @@ -985,8 +1073,8 @@ qlcnic_process_lro(struct qlcnic_adapter *adapter, struct ipv6hdr *ipv6h; struct tcphdr *th; bool push, timestamp; - int index, l2_hdr_offset, l4_hdr_offset; - u16 lro_length, length, data_offset, vid = 0xffff; + int index, l2_hdr_offset, l4_hdr_offset, is_lb_pkt; + u16 lro_length, length, data_offset, t_vid, vid = 0xffff; u32 seq_number; if (unlikely(ring > adapter->max_rds_rings)) @@ -1011,6 +1099,14 @@ qlcnic_process_lro(struct qlcnic_adapter *adapter, if (!skb) return buffer; + if (adapter->drv_mac_learn && + (adapter->flags & QLCNIC_ESWITCH_ENABLED)) { + t_vid = 0; + is_lb_pkt = qlcnic_82xx_is_lb_pkt(sts_data0); + qlcnic_add_lb_filter(adapter, skb, is_lb_pkt, + cpu_to_le16(t_vid)); + } + if (timestamp) data_offset = l4_hdr_offset + QLC_TCP_TS_HDR_SIZE; else @@ -1357,6 +1453,17 @@ void qlcnic_82xx_napi_disable(struct qlcnic_adapter *adapter) } } +#define QLC_83XX_NORMAL_LB_PKT (1ULL << 36) +#define QLC_83XX_LRO_LB_PKT (1ULL << 46) + +static inline int qlcnic_83xx_is_lb_pkt(u64 sts_data, int lro_pkt) +{ + if (lro_pkt) + return (sts_data & QLC_83XX_LRO_LB_PKT) ? 1 : 0; + else + return (sts_data & QLC_83XX_NORMAL_LB_PKT) ? 1 : 0; +} + static struct qlcnic_rx_buffer * qlcnic_83xx_process_rcv(struct qlcnic_adapter *adapter, struct qlcnic_host_sds_ring *sds_ring, @@ -1367,8 +1474,8 @@ qlcnic_83xx_process_rcv(struct qlcnic_adapter *adapter, struct qlcnic_rx_buffer *buffer; struct sk_buff *skb; struct qlcnic_host_rds_ring *rds_ring; - int index, length, cksum; - u16 vid = 0xffff; + int index, length, cksum, is_lb_pkt; + u16 vid = 0xffff, t_vid; if (unlikely(ring >= adapter->max_rds_rings)) return NULL; @@ -1386,6 +1493,14 @@ qlcnic_83xx_process_rcv(struct qlcnic_adapter *adapter, if (!skb) return buffer; + if (adapter->drv_mac_learn && + (adapter->flags & QLCNIC_ESWITCH_ENABLED)) { + t_vid = 0; + is_lb_pkt = qlcnic_83xx_is_lb_pkt(sts_data[1], 0); + qlcnic_add_lb_filter(adapter, skb, is_lb_pkt, + cpu_to_le16(t_vid)); + } + if (length > rds_ring->skb_size) skb_put(skb, rds_ring->skb_size); else @@ -1424,9 +1539,9 @@ qlcnic_83xx_process_lro(struct qlcnic_adapter *adapter, struct tcphdr *th; bool push; int l2_hdr_offset, l4_hdr_offset; - int index; + int index, is_lb_pkt; u16 lro_length, length, data_offset, gso_size; - u16 vid = 0xffff; + u16 vid = 0xffff, t_vid; if (unlikely(ring > adapter->max_rds_rings)) return NULL; @@ -1447,6 +1562,14 @@ qlcnic_83xx_process_lro(struct qlcnic_adapter *adapter, skb = qlcnic_process_rxbuf(adapter, rds_ring, index, STATUS_CKSUM_OK); if (!skb) return buffer; + + if (adapter->drv_mac_learn && + (adapter->flags & QLCNIC_ESWITCH_ENABLED)) { + t_vid = 0; + is_lb_pkt = qlcnic_83xx_is_lb_pkt(sts_data[1], 1); + qlcnic_add_lb_filter(adapter, skb, is_lb_pkt, + cpu_to_le16(t_vid)); + } if (qlcnic_83xx_is_tstamp(sts_data[1])) data_offset = l4_hdr_offset + QLCNIC_TCP_TS_HDR_SIZE; else |