bna: RX Filter Enhancements
authorRasesh Mody <rmody@brocade.com>
Wed, 18 Dec 2013 01:07:34 +0000 (17:07 -0800)
committerDavid S. Miller <davem@davemloft.net>
Wed, 18 Dec 2013 05:30:33 +0000 (00:30 -0500)
Change Details:
 - Added bna_rx_ucast_listset() for synchronous ucast listadd operation.
 - Clear mac->handle before adding it to free_q.
 - bnad_set_rx_mode() rewritten. bnad_set_rx_mode() adds the MACs in uc_list
   to UCAM. If it exceeds the max supported, DEFAULT mode is turned on. If
   MCAM limit is exceeded, ALLMULTI mode is turned on.
 - Clear CF flags, check for the new mode and reprogram the Rx approach.
 - Added bnad_set_rx_ucast_fltr() and bnad_set_rx_mcast_fltr().
 - Check for IFF_PROMISC to set the correct mode.

Signed-off-by: Rasesh Mody <rmody@brocade.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/brocade/bna/bna.h
drivers/net/ethernet/brocade/bna/bna_enet.c
drivers/net/ethernet/brocade/bna/bna_tx_rx.c
drivers/net/ethernet/brocade/bna/bnad.c
drivers/net/ethernet/brocade/bna/bnad.h

index b11fa6c03760f52d951f105df14eec29f4008c03..1f512190d696dca54919ac7896e0cbb82441e86f 100644 (file)
@@ -497,11 +497,17 @@ enum bna_cb_status
 bna_rx_ucast_del(struct bna_rx *rx, u8 *ucmac,
                 void (*cbfn)(struct bnad *, struct bna_rx *));
 enum bna_cb_status
+bna_rx_ucast_listset(struct bna_rx *rx, int count, u8 *uclist,
+                    void (*cbfn)(struct bnad *, struct bna_rx *));
+enum bna_cb_status
 bna_rx_mcast_add(struct bna_rx *rx, u8 *mcmac,
                 void (*cbfn)(struct bnad *, struct bna_rx *));
 enum bna_cb_status
 bna_rx_mcast_listset(struct bna_rx *rx, int count, u8 *mcmac,
                     void (*cbfn)(struct bnad *, struct bna_rx *));
+void
+bna_rx_mcast_delall(struct bna_rx *rx,
+                   void (*cbfn)(struct bnad *, struct bna_rx *));
 enum bna_cb_status
 bna_rx_mode_set(struct bna_rx *rx, enum bna_rxmode rxmode,
                enum bna_rxmode bitmask,
@@ -509,6 +515,8 @@ bna_rx_mode_set(struct bna_rx *rx, enum bna_rxmode rxmode,
 void bna_rx_vlan_add(struct bna_rx *rx, int vlan_id);
 void bna_rx_vlan_del(struct bna_rx *rx, int vlan_id);
 void bna_rx_vlanfilter_enable(struct bna_rx *rx);
+void bna_rx_vlan_strip_enable(struct bna_rx *rx);
+void bna_rx_vlan_strip_disable(struct bna_rx *rx);
 /* ENET */
 
 /* API for RX */
index 79afa11f64fa0087377bb1f9c5cd4ed10b7377f8..13f9636cdba73fdd7bc7f4d148f35a9948f4e04b 100644 (file)
@@ -1825,11 +1825,16 @@ static void
 bna_ucam_mod_uninit(struct bna_ucam_mod *ucam_mod)
 {
        struct list_head *qe;
-       int i = 0;
+       int i;
 
+       i = 0;
        list_for_each(qe, &ucam_mod->free_q)
                i++;
 
+       i = 0;
+       list_for_each(qe, &ucam_mod->del_q)
+               i++;
+
        ucam_mod->bna = NULL;
 }
 
@@ -1877,6 +1882,9 @@ bna_mcam_mod_uninit(struct bna_mcam_mod *mcam_mod)
        i = 0;
        list_for_each(qe, &mcam_mod->free_q) i++;
 
+       i = 0;
+       list_for_each(qe, &mcam_mod->del_q) i++;
+
        i = 0;
        list_for_each(qe, &mcam_mod->free_handle_q) i++;
 
index 40558b643b6ec7c36a52f38907ef656e05726ed3..e02c1265e3231a2ba721372dc61d8dfb3f4c1f5b 100644 (file)
@@ -915,6 +915,75 @@ bna_rx_mcast_add(struct bna_rx *rx, u8 *addr,
        return BNA_CB_SUCCESS;
 }
 
+enum bna_cb_status
+bna_rx_ucast_listset(struct bna_rx *rx, int count, u8 *uclist,
+                    void (*cbfn)(struct bnad *, struct bna_rx *))
+{
+       struct bna_ucam_mod *ucam_mod = &rx->bna->ucam_mod;
+       struct bna_rxf *rxf = &rx->rxf;
+       struct list_head list_head;
+       struct list_head *qe;
+       u8 *mcaddr;
+       struct bna_mac *mac, *del_mac;
+       int i;
+
+       /* Purge the pending_add_q */
+       while (!list_empty(&rxf->ucast_pending_add_q)) {
+               bfa_q_deq(&rxf->ucast_pending_add_q, &qe);
+               bfa_q_qe_init(qe);
+               mac = (struct bna_mac *)qe;
+               bna_cam_mod_mac_put(&ucam_mod->free_q, mac);
+       }
+
+       /* Schedule active_q entries for deletion */
+       while (!list_empty(&rxf->ucast_active_q)) {
+               bfa_q_deq(&rxf->ucast_active_q, &qe);
+               mac = (struct bna_mac *)qe;
+               bfa_q_qe_init(&mac->qe);
+
+               del_mac = bna_cam_mod_mac_get(&ucam_mod->del_q);
+               memcpy(del_mac, mac, sizeof(*del_mac));
+               list_add_tail(&del_mac->qe, &rxf->ucast_pending_del_q);
+               bna_cam_mod_mac_put(&ucam_mod->free_q, mac);
+       }
+
+       /* Allocate nodes */
+       INIT_LIST_HEAD(&list_head);
+       for (i = 0, mcaddr = uclist; i < count; i++) {
+               mac = bna_cam_mod_mac_get(&ucam_mod->free_q);
+               if (mac == NULL)
+                       goto err_return;
+               bfa_q_qe_init(&mac->qe);
+               memcpy(mac->addr, mcaddr, ETH_ALEN);
+               list_add_tail(&mac->qe, &list_head);
+               mcaddr += ETH_ALEN;
+       }
+
+       /* Add the new entries */
+       while (!list_empty(&list_head)) {
+               bfa_q_deq(&list_head, &qe);
+               mac = (struct bna_mac *)qe;
+               bfa_q_qe_init(&mac->qe);
+               list_add_tail(&mac->qe, &rxf->ucast_pending_add_q);
+       }
+
+       rxf->cam_fltr_cbfn = cbfn;
+       rxf->cam_fltr_cbarg = rx->bna->bnad;
+       bfa_fsm_send_event(rxf, RXF_E_CONFIG);
+
+       return BNA_CB_SUCCESS;
+
+err_return:
+       while (!list_empty(&list_head)) {
+               bfa_q_deq(&list_head, &qe);
+               mac = (struct bna_mac *)qe;
+               bfa_q_qe_init(&mac->qe);
+               bna_cam_mod_mac_put(&ucam_mod->free_q, mac);
+       }
+
+       return BNA_CB_UCAST_CAM_FULL;
+}
+
 enum bna_cb_status
 bna_rx_mcast_listset(struct bna_rx *rx, int count, u8 *mclist,
                     void (*cbfn)(struct bnad *, struct bna_rx *))
@@ -943,7 +1012,7 @@ bna_rx_mcast_listset(struct bna_rx *rx, int count, u8 *mclist,
 
                del_mac = bna_cam_mod_mac_get(&mcam_mod->del_q);
 
-               memcpy(del_mac, mac, sizeof(*mac));
+               memcpy(del_mac, mac, sizeof(*del_mac));
                list_add_tail(&del_mac->qe, &rxf->mcast_pending_del_q);
                mac->handle = NULL;
                bna_cam_mod_mac_put(&mcam_mod->free_q, mac);
@@ -987,6 +1056,49 @@ err_return:
        return BNA_CB_MCAST_LIST_FULL;
 }
 
+void
+bna_rx_mcast_delall(struct bna_rx *rx,
+                   void (*cbfn)(struct bnad *, struct bna_rx *))
+{
+       struct bna_rxf *rxf = &rx->rxf;
+       struct list_head *qe;
+       struct bna_mac *mac, *del_mac;
+       int need_hw_config = 0;
+
+       /* Purge all entries from pending_add_q */
+       while (!list_empty(&rxf->mcast_pending_add_q)) {
+               bfa_q_deq(&rxf->mcast_pending_add_q, &qe);
+               mac = (struct bna_mac *)qe;
+               bfa_q_qe_init(&mac->qe);
+               bna_cam_mod_mac_put(bna_mcam_mod_free_q(rxf->rx->bna), mac);
+       }
+
+       /* Schedule all entries in active_q for deletion */
+       while (!list_empty(&rxf->mcast_active_q)) {
+               bfa_q_deq(&rxf->mcast_active_q, &qe);
+               mac = (struct bna_mac *)qe;
+               bfa_q_qe_init(&mac->qe);
+
+               del_mac = bna_cam_mod_mac_get(bna_mcam_mod_del_q(rxf->rx->bna));
+
+               memcpy(del_mac, mac, sizeof(*del_mac));
+               list_add_tail(&del_mac->qe, &rxf->mcast_pending_del_q);
+               mac->handle = NULL;
+               bna_cam_mod_mac_put(bna_mcam_mod_free_q(rxf->rx->bna), mac);
+               need_hw_config = 1;
+       }
+
+       if (need_hw_config) {
+               rxf->cam_fltr_cbfn = cbfn;
+               rxf->cam_fltr_cbarg = rx->bna->bnad;
+               bfa_fsm_send_event(rxf, RXF_E_CONFIG);
+               return;
+       }
+
+       if (cbfn)
+               (*cbfn)(rx->bna->bnad, rx);
+}
+
 void
 bna_rx_vlan_add(struct bna_rx *rx, int vlan_id)
 {
@@ -2679,6 +2791,30 @@ bna_rx_cleanup_complete(struct bna_rx *rx)
        bfa_fsm_send_event(rx, RX_E_CLEANUP_DONE);
 }
 
+void
+bna_rx_vlan_strip_enable(struct bna_rx *rx)
+{
+       struct bna_rxf *rxf = &rx->rxf;
+
+       if (rxf->vlan_strip_status == BNA_STATUS_T_DISABLED) {
+               rxf->vlan_strip_status = BNA_STATUS_T_ENABLED;
+               rxf->vlan_strip_pending = true;
+               bfa_fsm_send_event(rxf, RXF_E_CONFIG);
+       }
+}
+
+void
+bna_rx_vlan_strip_disable(struct bna_rx *rx)
+{
+       struct bna_rxf *rxf = &rx->rxf;
+
+       if (rxf->vlan_strip_status != BNA_STATUS_T_DISABLED) {
+               rxf->vlan_strip_status = BNA_STATUS_T_DISABLED;
+               rxf->vlan_strip_pending = true;
+               bfa_fsm_send_event(rxf, RXF_E_CONFIG);
+       }
+}
+
 enum bna_cb_status
 bna_rx_mode_set(struct bna_rx *rx, enum bna_rxmode new_mode,
                enum bna_rxmode bitmask,
index 1790959c2b733d0919ddac7d8db7f0e7c2659d40..c1357eac35008ad94baf76889a97d337b0b46568 100644 (file)
@@ -2624,9 +2624,6 @@ bnad_stop(struct net_device *netdev)
        bnad_destroy_tx(bnad, 0);
        bnad_destroy_rx(bnad, 0);
 
-       /* These config flags are cleared in the hardware */
-       bnad->cfg_flags &= ~(BNAD_CF_ALLMULTI | BNAD_CF_PROMISC);
-
        /* Synchronize mailbox IRQ */
        bnad_mbox_irq_sync(bnad);
 
@@ -2939,73 +2936,133 @@ bnad_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats)
        return stats;
 }
 
+static void
+bnad_set_rx_ucast_fltr(struct bnad *bnad)
+{
+       struct net_device *netdev = bnad->netdev;
+       int uc_count = netdev_uc_count(netdev);
+       enum bna_cb_status ret;
+       u8 *mac_list;
+       struct netdev_hw_addr *ha;
+       int entry;
+
+       if (netdev_uc_empty(bnad->netdev)) {
+               bna_rx_ucast_listset(bnad->rx_info[0].rx, 0, NULL, NULL);
+               return;
+       }
+
+       if (uc_count > bna_attr(&bnad->bna)->num_ucmac)
+               goto mode_default;
+
+       mac_list = kzalloc(uc_count * ETH_ALEN, GFP_ATOMIC);
+       if (mac_list == NULL)
+               goto mode_default;
+
+       entry = 0;
+       netdev_for_each_uc_addr(ha, netdev) {
+               memcpy(&mac_list[entry * ETH_ALEN],
+                      &ha->addr[0], ETH_ALEN);
+               entry++;
+       }
+
+       ret = bna_rx_ucast_listset(bnad->rx_info[0].rx, entry,
+                       mac_list, NULL);
+       kfree(mac_list);
+
+       if (ret != BNA_CB_SUCCESS)
+               goto mode_default;
+
+       return;
+
+       /* ucast packets not in UCAM are routed to default function */
+mode_default:
+       bnad->cfg_flags |= BNAD_CF_DEFAULT;
+       bna_rx_ucast_listset(bnad->rx_info[0].rx, 0, NULL, NULL);
+}
+
+static void
+bnad_set_rx_mcast_fltr(struct bnad *bnad)
+{
+       struct net_device *netdev = bnad->netdev;
+       int mc_count = netdev_mc_count(netdev);
+       enum bna_cb_status ret;
+       u8 *mac_list;
+
+       if (netdev->flags & IFF_ALLMULTI)
+               goto mode_allmulti;
+
+       if (netdev_mc_empty(netdev))
+               return;
+
+       if (mc_count > bna_attr(&bnad->bna)->num_mcmac)
+               goto mode_allmulti;
+
+       mac_list = kzalloc((mc_count + 1) * ETH_ALEN, GFP_ATOMIC);
+
+       if (mac_list == NULL)
+               goto mode_allmulti;
+
+       memcpy(&mac_list[0], &bnad_bcast_addr[0], ETH_ALEN);
+
+       /* copy rest of the MCAST addresses */
+       bnad_netdev_mc_list_get(netdev, mac_list);
+       ret = bna_rx_mcast_listset(bnad->rx_info[0].rx, mc_count + 1,
+                       mac_list, NULL);
+       kfree(mac_list);
+
+       if (ret != BNA_CB_SUCCESS)
+               goto mode_allmulti;
+
+       return;
+
+mode_allmulti:
+       bnad->cfg_flags |= BNAD_CF_ALLMULTI;
+       bna_rx_mcast_delall(bnad->rx_info[0].rx, NULL);
+}
+
 void
 bnad_set_rx_mode(struct net_device *netdev)
 {
        struct bnad *bnad = netdev_priv(netdev);
-       u32     new_mask, valid_mask;
+       enum bna_rxmode new_mode, mode_mask;
        unsigned long flags;
 
        spin_lock_irqsave(&bnad->bna_lock, flags);
 
-       new_mask = valid_mask = 0;
-
-       if (netdev->flags & IFF_PROMISC) {
-               if (!(bnad->cfg_flags & BNAD_CF_PROMISC)) {
-                       new_mask = BNAD_RXMODE_PROMISC_DEFAULT;
-                       valid_mask = BNAD_RXMODE_PROMISC_DEFAULT;
-                       bnad->cfg_flags |= BNAD_CF_PROMISC;
-               }
-       } else {
-               if (bnad->cfg_flags & BNAD_CF_PROMISC) {
-                       new_mask = ~BNAD_RXMODE_PROMISC_DEFAULT;
-                       valid_mask = BNAD_RXMODE_PROMISC_DEFAULT;
-                       bnad->cfg_flags &= ~BNAD_CF_PROMISC;
-               }
-       }
-
-       if (netdev->flags & IFF_ALLMULTI) {
-               if (!(bnad->cfg_flags & BNAD_CF_ALLMULTI)) {
-                       new_mask |= BNA_RXMODE_ALLMULTI;
-                       valid_mask |= BNA_RXMODE_ALLMULTI;
-                       bnad->cfg_flags |= BNAD_CF_ALLMULTI;
-               }
-       } else {
-               if (bnad->cfg_flags & BNAD_CF_ALLMULTI) {
-                       new_mask &= ~BNA_RXMODE_ALLMULTI;
-                       valid_mask |= BNA_RXMODE_ALLMULTI;
-                       bnad->cfg_flags &= ~BNAD_CF_ALLMULTI;
-               }
+       if (bnad->rx_info[0].rx == NULL) {
+               spin_unlock_irqrestore(&bnad->bna_lock, flags);
+               return;
        }
 
-       if (bnad->rx_info[0].rx == NULL)
-               goto unlock;
+       /* clear bnad flags to update it with new settings */
+       bnad->cfg_flags &= ~(BNAD_CF_PROMISC | BNAD_CF_DEFAULT |
+                       BNAD_CF_ALLMULTI);
 
-       bna_rx_mode_set(bnad->rx_info[0].rx, new_mask, valid_mask, NULL);
+       new_mode = 0;
+       if (netdev->flags & IFF_PROMISC) {
+               new_mode |= BNAD_RXMODE_PROMISC_DEFAULT;
+               bnad->cfg_flags |= BNAD_CF_PROMISC;
+       } else {
+               bnad_set_rx_mcast_fltr(bnad);
 
-       if (!netdev_mc_empty(netdev)) {
-               u8 *mcaddr_list;
-               int mc_count = netdev_mc_count(netdev);
+               if (bnad->cfg_flags & BNAD_CF_ALLMULTI)
+                       new_mode |= BNA_RXMODE_ALLMULTI;
 
-               /* Index 0 holds the broadcast address */
-               mcaddr_list =
-                       kzalloc((mc_count + 1) * ETH_ALEN,
-                               GFP_ATOMIC);
-               if (!mcaddr_list)
-                       goto unlock;
+               bnad_set_rx_ucast_fltr(bnad);
 
-               memcpy(&mcaddr_list[0], &bnad_bcast_addr[0], ETH_ALEN);
+               if (bnad->cfg_flags & BNAD_CF_DEFAULT)
+                       new_mode |= BNA_RXMODE_DEFAULT;
+       }
 
-               /* Copy rest of the MC addresses */
-               bnad_netdev_mc_list_get(netdev, mcaddr_list);
+       mode_mask = BNA_RXMODE_PROMISC | BNA_RXMODE_DEFAULT |
+                       BNA_RXMODE_ALLMULTI;
+       bna_rx_mode_set(bnad->rx_info[0].rx, new_mode, mode_mask, NULL);
 
-               bna_rx_mcast_listset(bnad->rx_info[0].rx, mc_count + 1,
-                                       mcaddr_list, NULL);
+       if (bnad->cfg_flags & BNAD_CF_PROMISC)
+               bna_rx_vlan_strip_disable(bnad->rx_info[0].rx);
+       else
+               bna_rx_vlan_strip_enable(bnad->rx_info[0].rx);
 
-               /* Should we enable BNAD_CF_ALLMULTI for err != 0 ? */
-               kfree(mcaddr_list);
-       }
-unlock:
        spin_unlock_irqrestore(&bnad->bna_lock, flags);
 }
 
index 4137aaad3ab1d155602c4f01266692e61034a2fd..9d10c69f58890192fbdf0d911efd16a352b0e260 100644 (file)
@@ -260,7 +260,8 @@ struct bnad_rx_unmap_q {
 #define        BNAD_CF_DIM_ENABLED             0x01    /* DIM */
 #define        BNAD_CF_PROMISC                 0x02
 #define BNAD_CF_ALLMULTI               0x04
-#define        BNAD_CF_MSIX                    0x08    /* If in MSIx mode */
+#define        BNAD_CF_DEFAULT                 0x08
+#define        BNAD_CF_MSIX                    0x10    /* If in MSIx mode */
 
 /* Defines for run_flags bit-mask */
 /* Set, tested & cleared using xxx_bit() functions */