diff options
Diffstat (limited to 'drivers/net/qlcnic/qlcnic_main.c')
-rw-r--r-- | drivers/net/qlcnic/qlcnic_main.c | 123 |
1 files changed, 123 insertions, 0 deletions
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 */ |
51 | static int wol_port_mode = 5; | 51 | static int wol_port_mode = 5; |
52 | 52 | ||
53 | static int qlcnic_mac_learn; | ||
54 | module_param(qlcnic_mac_learn, int, 0644); | ||
55 | MODULE_PARM_DESC(qlcnic_mac_learn, "Mac Filter (0=disabled, 1=enabled)"); | ||
56 | |||
53 | static int use_msi = 1; | 57 | static int use_msi = 1; |
54 | module_param(use_msi, int, 0644); | 58 | module_param(use_msi, int, 0644); |
55 | MODULE_PARM_DESC(use_msi, "MSI interrupt (0=disabled, 1=enabled"); | 59 | MODULE_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); | |||
106 | static void qlcnic_config_indev_addr(struct net_device *dev, unsigned long); | 110 | static void qlcnic_config_indev_addr(struct net_device *dev, unsigned long); |
107 | static int qlcnic_start_firmware(struct qlcnic_adapter *); | 111 | static int qlcnic_start_firmware(struct qlcnic_adapter *); |
108 | 112 | ||
113 | static void qlcnic_alloc_lb_filters_mem(struct qlcnic_adapter *adapter); | ||
114 | static void qlcnic_free_lb_filters_mem(struct qlcnic_adapter *adapter); | ||
109 | static void qlcnic_dev_set_npar_ready(struct qlcnic_adapter *); | 115 | static void qlcnic_dev_set_npar_ready(struct qlcnic_adapter *); |
110 | static int qlcnicvf_config_led(struct qlcnic_adapter *, u32, u32); | 116 | static int qlcnicvf_config_led(struct qlcnic_adapter *, u32, u32); |
111 | static int qlcnicvf_config_bridged_mode(struct qlcnic_adapter *, u32); | 117 | static 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 | ||
1784 | static void | 1796 | static void |
1797 | qlcnic_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 | |||
1819 | static 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 | |||
1828 | static 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 | |||
1857 | static void | ||
1858 | qlcnic_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 | |||
1901 | static void | ||
1785 | qlcnic_tso_check(struct net_device *netdev, | 1902 | qlcnic_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 | |||
2903 | reschedule: | 3026 | reschedule: |
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 | } |