qlcnic: add support for FDB netdevice ops.
authorJitendra Kalsaria <jitendra.kalsaria@qlogic.com>
Fri, 25 Jan 2013 10:20:40 +0000 (10:20 +0000)
committerDavid S. Miller <davem@davemloft.net>
Sun, 27 Jan 2013 06:00:08 +0000 (01:00 -0500)
Providing communication channel between KVM and e-Switch so that it
can be informed when hypervisor configures a MAC address and VLAN.

qlcnic_mac_learn module param usage will be changed to:
0 = MAC learning is disable
1 = Driver learning is enable
2 = FDB learning is enable

Signed-off-by: Jitendra Kalsaria <jitendra.kalsaria@qlogic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c

index 35f345739753e0020b3ffe6dc50e7d08ae197aaa..3260fb32c873f740c2cc45ab14a5e9c1d93d8298 100644 (file)
@@ -746,6 +746,11 @@ struct qlcnic_mac_list_s {
        uint8_t mac_addr[ETH_ALEN+2];
 };
 
+/* MAC Learn */
+#define NO_MAC_LEARN           0
+#define DRV_MAC_LEARN          1
+#define FDB_MAC_LEARN          2
+
 #define QLCNIC_HOST_REQUEST    0x13
 #define QLCNIC_REQUEST         0x14
 
@@ -981,7 +986,8 @@ struct qlcnic_adapter {
        u8 mac_addr[ETH_ALEN];
 
        u64 dev_rst_time;
-       u8 mac_learn;
+       bool drv_mac_learn;
+       bool fdb_mac_learn;
        unsigned long vlans[BITS_TO_LONGS(VLAN_N_VID)];
        u8 flash_mfg_id;
        struct qlcnic_npar_info *npars;
@@ -1421,6 +1427,8 @@ void qlcnic_post_rx_buffers(struct qlcnic_adapter *adapter,
                struct qlcnic_host_rds_ring *rds_ring, u8 ring_id);
 int qlcnic_process_rcv_ring(struct qlcnic_host_sds_ring *sds_ring, int max);
 void qlcnic_set_multi(struct net_device *netdev);
+int qlcnic_nic_add_mac(struct qlcnic_adapter *, const u8 *);
+int qlcnic_nic_del_mac(struct qlcnic_adapter *, const u8 *);
 void qlcnic_free_mac_list(struct qlcnic_adapter *adapter);
 
 int qlcnic_fw_cmd_set_mtu(struct qlcnic_adapter *adapter, int mtu);
index 30be026102c8d7407266c0313f5b22a4c5bbc867..6c6ecfc152b8b27fb6283a1b47e22152b9ea72b9 100644 (file)
@@ -446,7 +446,29 @@ int qlcnic_82xx_sre_macaddr_change(struct qlcnic_adapter *adapter, u8 *addr,
        return qlcnic_send_cmd_descs(adapter, (struct cmd_desc_type0 *)&req, 1);
 }
 
-static int qlcnic_nic_add_mac(struct qlcnic_adapter *adapter, const u8 *addr)
+int qlcnic_nic_del_mac(struct qlcnic_adapter *adapter, const u8 *addr)
+{
+       struct list_head *head;
+       struct qlcnic_mac_list_s *cur;
+       int err = -EINVAL;
+
+       /* Delete MAC from the existing list */
+       list_for_each(head, &adapter->mac_list) {
+               cur = list_entry(head, struct qlcnic_mac_list_s, list);
+               if (memcmp(addr, cur->mac_addr, ETH_ALEN) == 0) {
+                       err = qlcnic_sre_macaddr_change(adapter, cur->mac_addr,
+                                                       0, QLCNIC_MAC_DEL);
+                       if (err)
+                               return err;
+                       list_del(&cur->list);
+                       kfree(cur);
+                       return err;
+               }
+       }
+       return err;
+}
+
+int qlcnic_nic_add_mac(struct qlcnic_adapter *adapter, const u8 *addr)
 {
        struct list_head *head;
        struct qlcnic_mac_list_s *cur;
@@ -510,11 +532,11 @@ void qlcnic_set_multi(struct net_device *netdev)
        }
 
 send_fw_cmd:
-       if (mode == VPORT_MISS_MODE_ACCEPT_ALL) {
+       if (mode == VPORT_MISS_MODE_ACCEPT_ALL && !adapter->fdb_mac_learn) {
                qlcnic_alloc_lb_filters_mem(adapter);
-               adapter->mac_learn = 1;
+               adapter->drv_mac_learn = true;
        } else {
-               adapter->mac_learn = 0;
+               adapter->drv_mac_learn = false;
        }
 
        qlcnic_nic_set_promisc(adapter, mode);
index 0033971b5b2b7197595fcaeaecd98ee4ae17d2f4..fdf34836ef41a224a7b3141cec5c1c217bf83003 100644 (file)
@@ -521,7 +521,7 @@ netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
        if (unlikely(qlcnic_tx_pkt(adapter, first_desc, skb)))
                goto unwind_buff;
 
-       if (adapter->mac_learn)
+       if (adapter->drv_mac_learn)
                qlcnic_send_filter(adapter, first_desc, skb);
 
        adapter->stats.txbytes += skb->len;
index 8b0f47a71964f91fc92d38043fdfa07c34fbe6fb..e6b363a7664f907c86d723ac44f0b8fbc76589b1 100644 (file)
@@ -32,7 +32,8 @@ static const char qlcnic_driver_string[] = "QLogic 1/10 GbE "
 
 static int qlcnic_mac_learn;
 module_param(qlcnic_mac_learn, int, 0444);
-MODULE_PARM_DESC(qlcnic_mac_learn, "Mac Filter (0=disabled, 1=enabled)");
+MODULE_PARM_DESC(qlcnic_mac_learn,
+                "Mac Filter (0=learning is disabled, 1=Driver learning is enabled, 2=FDB learning is enabled)");
 
 int qlcnic_use_msi = 1;
 MODULE_PARM_DESC(use_msi, "MSI interrupt (0=disabled, 1=enabled");
@@ -246,6 +247,77 @@ static int qlcnic_set_mac(struct net_device *netdev, void *p)
        return 0;
 }
 
+static int qlcnic_fdb_del(struct ndmsg *ndm, struct net_device *netdev,
+                       const unsigned char *addr)
+{
+       struct qlcnic_adapter *adapter = netdev_priv(netdev);
+       int err = -EOPNOTSUPP;
+
+       if (!adapter->fdb_mac_learn) {
+               pr_info("%s: Driver mac learn is enabled, FDB operation not allowed\n",
+                       __func__);
+               return err;
+       }
+
+       if (adapter->flags & QLCNIC_ESWITCH_ENABLED) {
+               if (is_unicast_ether_addr(addr))
+                       err = qlcnic_nic_del_mac(adapter, addr);
+               else if (is_multicast_ether_addr(addr))
+                       err = dev_mc_del(netdev, addr);
+               else
+                       err =  -EINVAL;
+       }
+       return err;
+}
+
+static int qlcnic_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
+                       struct net_device *netdev,
+                       const unsigned char *addr, u16 flags)
+{
+       struct qlcnic_adapter *adapter = netdev_priv(netdev);
+       int err = 0;
+
+       if (!adapter->fdb_mac_learn) {
+               pr_info("%s: Driver mac learn is enabled, FDB operation not allowed\n",
+                       __func__);
+               return -EOPNOTSUPP;
+       }
+
+       if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED)) {
+               pr_info("%s: FDB e-switch is not enabled\n", __func__);
+               return -EOPNOTSUPP;
+       }
+
+       if (ether_addr_equal(addr, adapter->mac_addr))
+               return err;
+
+       if (is_unicast_ether_addr(addr))
+               err = qlcnic_nic_add_mac(adapter, addr);
+       else if (is_multicast_ether_addr(addr))
+               err = dev_mc_add_excl(netdev, addr);
+       else
+               err = -EINVAL;
+
+       return err;
+}
+
+static int qlcnic_fdb_dump(struct sk_buff *skb, struct netlink_callback *ncb,
+                       struct net_device *netdev, int idx)
+{
+       struct qlcnic_adapter *adapter = netdev_priv(netdev);
+
+       if (!adapter->fdb_mac_learn) {
+               pr_info("%s: Driver mac learn is enabled, FDB operation not allowed\n",
+                       __func__);
+               return -EOPNOTSUPP;
+       }
+
+       if (adapter->flags & QLCNIC_ESWITCH_ENABLED)
+               idx = ndo_dflt_fdb_dump(skb, ncb, netdev, idx);
+
+       return idx;
+}
+
 static void qlcnic_82xx_cancel_idc_work(struct qlcnic_adapter *adapter)
 {
        while (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state))
@@ -268,6 +340,9 @@ static const struct net_device_ops qlcnic_netdev_ops = {
        .ndo_tx_timeout    = qlcnic_tx_timeout,
        .ndo_vlan_rx_add_vid    = qlcnic_vlan_rx_add,
        .ndo_vlan_rx_kill_vid   = qlcnic_vlan_rx_del,
+       .ndo_fdb_add            = qlcnic_fdb_add,
+       .ndo_fdb_del            = qlcnic_fdb_del,
+       .ndo_fdb_dump           = qlcnic_fdb_dump,
 #ifdef CONFIG_NET_POLL_CONTROLLER
        .ndo_poll_controller = qlcnic_poll_controller,
 #endif
@@ -1802,7 +1877,10 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        adapter->dev_rst_time = jiffies;
        adapter->ahw->revision_id = pdev->revision;
-       adapter->mac_learn = qlcnic_mac_learn;
+       if (qlcnic_mac_learn == FDB_MAC_LEARN)
+               adapter->fdb_mac_learn = true;
+       else if (qlcnic_mac_learn == DRV_MAC_LEARN)
+               adapter->drv_mac_learn = true;
        adapter->max_drv_tx_rings = 1;
 
        rwlock_init(&adapter->ahw->crb_lock);
@@ -1893,7 +1971,7 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (qlcnic_get_act_pci_func(adapter))
                goto err_out_disable_mbx_intr;
 
-       if (adapter->mac_learn)
+       if (adapter->drv_mac_learn)
                qlcnic_alloc_lb_filters_mem(adapter);
 
        qlcnic_add_sysfs(adapter);