aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorAmit Kumar Salecha <amit.salecha@qlogic.com>2010-08-31 13:17:51 -0400
committerDavid S. Miller <davem@davemloft.net>2010-09-01 13:41:57 -0400
commitb5e5492c0d49e2fd6f51961d03b8533435e5e7f5 (patch)
tree6020e160e5b9b84b5d6d94c68f77fb8482d63c71 /drivers/net
parent7373373d100e5aebe751af0b2609a9a01dad5ac1 (diff)
qlcnic: support mac learning
Device eswitch need to configure with VM's mac address. Hypervisor doesn't provide any utility/callbacks to get VM's mac address. Unicast mac address filter improves performance and also provide packet loopback capability i.e communication between VM. Above features is by default off, can be turned on with module parameter 'mac_learn'. Signed-off-by: Amit Kumar Salecha <amit.salecha@qlogic.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/qlcnic/qlcnic.h20
-rw-r--r--drivers/net/qlcnic/qlcnic_hw.c48
-rw-r--r--drivers/net/qlcnic/qlcnic_main.c123
3 files changed, 191 insertions, 0 deletions
diff --git a/drivers/net/qlcnic/qlcnic.h b/drivers/net/qlcnic/qlcnic.h
index cd1a2e6f59d2..4727204a2450 100644
--- a/drivers/net/qlcnic/qlcnic.h
+++ b/drivers/net/qlcnic/qlcnic.h
@@ -926,6 +926,21 @@ struct qlcnic_mac_req {
926#define QLCNIC_INTERRUPT_TEST 1 926#define QLCNIC_INTERRUPT_TEST 1
927#define QLCNIC_LOOPBACK_TEST 2 927#define QLCNIC_LOOPBACK_TEST 2
928 928
929#define QLCNIC_FILTER_AGE 80
930#define QLCNIC_LB_MAX_FILTERS 64
931
932struct qlcnic_filter {
933 struct hlist_node fnode;
934 u8 faddr[ETH_ALEN];
935 unsigned long ftime;
936};
937
938struct qlcnic_filter_hash {
939 struct hlist_head *fhead;
940 u8 fnum;
941 u8 fmax;
942};
943
929struct qlcnic_adapter { 944struct qlcnic_adapter {
930 struct qlcnic_hardware_context ahw; 945 struct qlcnic_hardware_context ahw;
931 946
@@ -934,6 +949,7 @@ struct qlcnic_adapter {
934 struct list_head mac_list; 949 struct list_head mac_list;
935 950
936 spinlock_t tx_clean_lock; 951 spinlock_t tx_clean_lock;
952 spinlock_t mac_learn_lock;
937 953
938 u16 num_txd; 954 u16 num_txd;
939 u16 num_rxd; 955 u16 num_rxd;
@@ -1013,6 +1029,8 @@ struct qlcnic_adapter {
1013 1029
1014 struct qlcnic_nic_intr_coalesce coal; 1030 struct qlcnic_nic_intr_coalesce coal;
1015 1031
1032 struct qlcnic_filter_hash fhash;
1033
1016 unsigned long state; 1034 unsigned long state;
1017 __le32 file_prd_off; /*File fw product offset*/ 1035 __le32 file_prd_off; /*File fw product offset*/
1018 u32 fw_version; 1036 u32 fw_version;
@@ -1211,6 +1229,8 @@ void qlcnic_pcie_sem_unlock(struct qlcnic_adapter *, int);
1211int qlcnic_get_board_info(struct qlcnic_adapter *adapter); 1229int qlcnic_get_board_info(struct qlcnic_adapter *adapter);
1212int qlcnic_wol_supported(struct qlcnic_adapter *adapter); 1230int qlcnic_wol_supported(struct qlcnic_adapter *adapter);
1213int qlcnic_config_led(struct qlcnic_adapter *adapter, u32 state, u32 rate); 1231int qlcnic_config_led(struct qlcnic_adapter *adapter, u32 state, u32 rate);
1232void qlcnic_prune_lb_filters(struct qlcnic_adapter *adapter);
1233void qlcnic_delete_lb_filters(struct qlcnic_adapter *adapter);
1214 1234
1215/* Functions from qlcnic_init.c */ 1235/* Functions from qlcnic_init.c */
1216int qlcnic_load_firmware(struct qlcnic_adapter *adapter); 1236int qlcnic_load_firmware(struct qlcnic_adapter *adapter);
diff --git a/drivers/net/qlcnic/qlcnic_hw.c b/drivers/net/qlcnic/qlcnic_hw.c
index 670a54a84ba6..5b2bce59498d 100644
--- a/drivers/net/qlcnic/qlcnic_hw.c
+++ b/drivers/net/qlcnic/qlcnic_hw.c
@@ -491,6 +491,54 @@ void qlcnic_free_mac_list(struct qlcnic_adapter *adapter)
491 } 491 }
492} 492}
493 493
494void qlcnic_prune_lb_filters(struct qlcnic_adapter *adapter)
495{
496 struct qlcnic_filter *tmp_fil;
497 struct hlist_node *tmp_hnode, *n;
498 struct hlist_head *head;
499 int i;
500
501 for (i = 0; i < adapter->fhash.fmax; i++) {
502 head = &(adapter->fhash.fhead[i]);
503
504 hlist_for_each_entry_safe(tmp_fil, tmp_hnode, n, head, fnode)
505 {
506 if (jiffies >
507 (QLCNIC_FILTER_AGE * HZ + tmp_fil->ftime)) {
508 qlcnic_sre_macaddr_change(adapter,
509 tmp_fil->faddr, QLCNIC_MAC_DEL);
510 spin_lock_bh(&adapter->mac_learn_lock);
511 adapter->fhash.fnum--;
512 hlist_del(&tmp_fil->fnode);
513 spin_unlock_bh(&adapter->mac_learn_lock);
514 kfree(tmp_fil);
515 }
516 }
517 }
518}
519
520void qlcnic_delete_lb_filters(struct qlcnic_adapter *adapter)
521{
522 struct qlcnic_filter *tmp_fil;
523 struct hlist_node *tmp_hnode, *n;
524 struct hlist_head *head;
525 int i;
526
527 for (i = 0; i < adapter->fhash.fmax; i++) {
528 head = &(adapter->fhash.fhead[i]);
529
530 hlist_for_each_entry_safe(tmp_fil, tmp_hnode, n, head, fnode) {
531 qlcnic_sre_macaddr_change(adapter,
532 tmp_fil->faddr, QLCNIC_MAC_DEL);
533 spin_lock_bh(&adapter->mac_learn_lock);
534 adapter->fhash.fnum--;
535 hlist_del(&tmp_fil->fnode);
536 spin_unlock_bh(&adapter->mac_learn_lock);
537 kfree(tmp_fil);
538 }
539 }
540}
541
494#define QLCNIC_CONFIG_INTR_COALESCE 3 542#define QLCNIC_CONFIG_INTR_COALESCE 3
495 543
496/* 544/*
diff --git a/drivers/net/qlcnic/qlcnic_main.c b/drivers/net/qlcnic/qlcnic_main.c
index 130f4dbe2f4b..0fbfb53f2594 100644
--- a/drivers/net/qlcnic/qlcnic_main.c
+++ b/drivers/net/qlcnic/qlcnic_main.c
@@ -50,6 +50,10 @@ static int port_mode = QLCNIC_PORT_MODE_AUTO_NEG;
50/* Default to restricted 1G auto-neg mode */ 50/* Default to restricted 1G auto-neg mode */
51static int wol_port_mode = 5; 51static int wol_port_mode = 5;
52 52
53static int qlcnic_mac_learn;
54module_param(qlcnic_mac_learn, int, 0644);
55MODULE_PARM_DESC(qlcnic_mac_learn, "Mac Filter (0=disabled, 1=enabled)");
56
53static int use_msi = 1; 57static int use_msi = 1;
54module_param(use_msi, int, 0644); 58module_param(use_msi, int, 0644);
55MODULE_PARM_DESC(use_msi, "MSI interrupt (0=disabled, 1=enabled"); 59MODULE_PARM_DESC(use_msi, "MSI interrupt (0=disabled, 1=enabled");
@@ -106,6 +110,8 @@ static struct net_device_stats *qlcnic_get_stats(struct net_device *netdev);
106static void qlcnic_config_indev_addr(struct net_device *dev, unsigned long); 110static void qlcnic_config_indev_addr(struct net_device *dev, unsigned long);
107static int qlcnic_start_firmware(struct qlcnic_adapter *); 111static int qlcnic_start_firmware(struct qlcnic_adapter *);
108 112
113static void qlcnic_alloc_lb_filters_mem(struct qlcnic_adapter *adapter);
114static void qlcnic_free_lb_filters_mem(struct qlcnic_adapter *adapter);
109static void qlcnic_dev_set_npar_ready(struct qlcnic_adapter *); 115static void qlcnic_dev_set_npar_ready(struct qlcnic_adapter *);
110static int qlcnicvf_config_led(struct qlcnic_adapter *, u32, u32); 116static int qlcnicvf_config_led(struct qlcnic_adapter *, u32, u32);
111static int qlcnicvf_config_bridged_mode(struct qlcnic_adapter *, u32); 117static int qlcnicvf_config_bridged_mode(struct qlcnic_adapter *, u32);
@@ -1201,6 +1207,9 @@ __qlcnic_down(struct qlcnic_adapter *adapter, struct net_device *netdev)
1201 1207
1202 qlcnic_free_mac_list(adapter); 1208 qlcnic_free_mac_list(adapter);
1203 1209
1210 if (adapter->fhash.fnum)
1211 qlcnic_delete_lb_filters(adapter);
1212
1204 qlcnic_nic_set_promisc(adapter, QLCNIC_NIU_NON_PROMISC_MODE); 1213 qlcnic_nic_set_promisc(adapter, QLCNIC_NIU_NON_PROMISC_MODE);
1205 1214
1206 qlcnic_napi_disable(adapter); 1215 qlcnic_napi_disable(adapter);
@@ -1596,6 +1605,7 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
1596 break; 1605 break;
1597 } 1606 }
1598 1607
1608 qlcnic_alloc_lb_filters_mem(adapter);
1599 qlcnic_create_diag_entries(adapter); 1609 qlcnic_create_diag_entries(adapter);
1600 1610
1601 return 0; 1611 return 0;
@@ -1647,6 +1657,8 @@ static void __devexit qlcnic_remove(struct pci_dev *pdev)
1647 1657
1648 clear_bit(__QLCNIC_RESETTING, &adapter->state); 1658 clear_bit(__QLCNIC_RESETTING, &adapter->state);
1649 1659
1660 qlcnic_free_lb_filters_mem(adapter);
1661
1650 qlcnic_teardown_intr(adapter); 1662 qlcnic_teardown_intr(adapter);
1651 1663
1652 qlcnic_remove_diag_entries(adapter); 1664 qlcnic_remove_diag_entries(adapter);
@@ -1782,6 +1794,111 @@ static int qlcnic_close(struct net_device *netdev)
1782} 1794}
1783 1795
1784static void 1796static void
1797qlcnic_alloc_lb_filters_mem(struct qlcnic_adapter *adapter)
1798{
1799 void *head;
1800 int i;
1801
1802 if (!qlcnic_mac_learn)
1803 return;
1804
1805 spin_lock_init(&adapter->mac_learn_lock);
1806
1807 head = kcalloc(QLCNIC_LB_MAX_FILTERS, sizeof(struct hlist_head),
1808 GFP_KERNEL);
1809 if (!head)
1810 return;
1811
1812 adapter->fhash.fmax = QLCNIC_LB_MAX_FILTERS;
1813 adapter->fhash.fhead = (struct hlist_head *)head;
1814
1815 for (i = 0; i < adapter->fhash.fmax; i++)
1816 INIT_HLIST_HEAD(&adapter->fhash.fhead[i]);
1817}
1818
1819static void qlcnic_free_lb_filters_mem(struct qlcnic_adapter *adapter)
1820{
1821 if (adapter->fhash.fmax && adapter->fhash.fhead)
1822 kfree(adapter->fhash.fhead);
1823
1824 adapter->fhash.fhead = NULL;
1825 adapter->fhash.fmax = 0;
1826}
1827
1828static void qlcnic_change_filter(struct qlcnic_adapter *adapter,
1829 u64 uaddr, struct qlcnic_host_tx_ring *tx_ring)
1830{
1831 struct cmd_desc_type0 *hwdesc;
1832 struct qlcnic_nic_req *req;
1833 struct qlcnic_mac_req *mac_req;
1834 u32 producer;
1835 u64 word;
1836
1837 producer = tx_ring->producer;
1838 hwdesc = &tx_ring->desc_head[tx_ring->producer];
1839
1840 req = (struct qlcnic_nic_req *)hwdesc;
1841 memset(req, 0, sizeof(struct qlcnic_nic_req));
1842 req->qhdr = cpu_to_le64(QLCNIC_REQUEST << 23);
1843
1844 word = QLCNIC_MAC_EVENT | ((u64)(adapter->portnum) << 16);
1845 req->req_hdr = cpu_to_le64(word);
1846
1847 mac_req = (struct qlcnic_mac_req *)&(req->words[0]);
1848 mac_req->op = QLCNIC_MAC_ADD;
1849 memcpy(mac_req->mac_addr, &uaddr, ETH_ALEN);
1850
1851 tx_ring->producer = get_next_index(producer, tx_ring->num_desc);
1852}
1853
1854#define QLCNIC_MAC_HASH(MAC)\
1855 ((((MAC) & 0x70000) >> 0x10) | (((MAC) & 0x70000000000ULL) >> 0x25))
1856
1857static void
1858qlcnic_send_filter(struct qlcnic_adapter *adapter,
1859 struct qlcnic_host_tx_ring *tx_ring,
1860 struct cmd_desc_type0 *first_desc,
1861 struct sk_buff *skb)
1862{
1863 struct ethhdr *phdr = (struct ethhdr *)(skb->data);
1864 struct qlcnic_filter *fil, *tmp_fil;
1865 struct hlist_node *tmp_hnode, *n;
1866 struct hlist_head *head;
1867 u64 src_addr = 0;
1868 u8 hindex;
1869
1870 if (!compare_ether_addr(phdr->h_source, adapter->mac_addr))
1871 return;
1872
1873 if (adapter->fhash.fnum >= adapter->fhash.fmax)
1874 return;
1875
1876 memcpy(&src_addr, phdr->h_source, ETH_ALEN);
1877 hindex = QLCNIC_MAC_HASH(src_addr) & (QLCNIC_LB_MAX_FILTERS - 1);
1878 head = &(adapter->fhash.fhead[hindex]);
1879
1880 hlist_for_each_entry_safe(tmp_fil, tmp_hnode, n, head, fnode) {
1881 if (!memcmp(tmp_fil->faddr, &src_addr, ETH_ALEN)) {
1882 tmp_fil->ftime = jiffies;
1883 return;
1884 }
1885 }
1886
1887 fil = kzalloc(sizeof(struct qlcnic_filter), GFP_ATOMIC);
1888 if (!fil)
1889 return;
1890
1891 qlcnic_change_filter(adapter, src_addr, tx_ring);
1892
1893 fil->ftime = jiffies;
1894 memcpy(fil->faddr, &src_addr, ETH_ALEN);
1895 spin_lock(&adapter->mac_learn_lock);
1896 hlist_add_head(&(fil->fnode), head);
1897 adapter->fhash.fnum++;
1898 spin_unlock(&adapter->mac_learn_lock);
1899}
1900
1901static void
1785qlcnic_tso_check(struct net_device *netdev, 1902qlcnic_tso_check(struct net_device *netdev,
1786 struct qlcnic_host_tx_ring *tx_ring, 1903 struct qlcnic_host_tx_ring *tx_ring,
1787 struct cmd_desc_type0 *first_desc, 1904 struct cmd_desc_type0 *first_desc,
@@ -2090,6 +2207,9 @@ qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
2090 2207
2091 qlcnic_tso_check(netdev, tx_ring, first_desc, skb); 2208 qlcnic_tso_check(netdev, tx_ring, first_desc, skb);
2092 2209
2210 if (qlcnic_mac_learn)
2211 qlcnic_send_filter(adapter, tx_ring, first_desc, skb);
2212
2093 qlcnic_update_cmd_producer(adapter, tx_ring); 2213 qlcnic_update_cmd_producer(adapter, tx_ring);
2094 2214
2095 adapter->stats.txbytes += skb->len; 2215 adapter->stats.txbytes += skb->len;
@@ -2900,6 +3020,9 @@ qlcnic_fw_poll_work(struct work_struct *work)
2900 if (qlcnic_check_health(adapter)) 3020 if (qlcnic_check_health(adapter))
2901 return; 3021 return;
2902 3022
3023 if (adapter->fhash.fnum)
3024 qlcnic_prune_lb_filters(adapter);
3025
2903reschedule: 3026reschedule:
2904 qlcnic_schedule_work(adapter, qlcnic_fw_poll_work, FW_POLL_DELAY); 3027 qlcnic_schedule_work(adapter, qlcnic_fw_poll_work, FW_POLL_DELAY);
2905} 3028}