struct qlcnic_filter_hash fhash;
struct qlcnic_filter_hash rx_fhash;
+ struct list_head vf_mc_list;
spinlock_t tx_clean_lock;
spinlock_t mac_learn_lock;
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);
+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_83xx_configure_opmode(struct qlcnic_adapter *adapter);
int qlcnic_read_mac_addr(struct qlcnic_adapter *);
int qlcnic_setup_netdev(struct qlcnic_adapter *, struct net_device *, int);
+void qlcnic_sriov_vf_schedule_multi(struct net_device *);
+void qlcnic_vf_add_mc_list(struct net_device *);
/*
* QLOGIC Board information
return 0;
}
-void qlcnic_set_multi(struct net_device *netdev)
+void __qlcnic_set_multi(struct net_device *netdev)
{
struct qlcnic_adapter *adapter = netdev_priv(netdev);
struct netdev_hw_addr *ha;
if (!test_bit(__QLCNIC_FW_ATTACHED, &adapter->state))
return;
- qlcnic_nic_add_mac(adapter, adapter->mac_addr);
+ if (!qlcnic_sriov_vf_check(adapter))
+ qlcnic_nic_add_mac(adapter, adapter->mac_addr);
qlcnic_nic_add_mac(adapter, bcast_addr);
if (netdev->flags & IFF_PROMISC) {
goto send_fw_cmd;
}
- if (!netdev_mc_empty(netdev)) {
+ if (!netdev_mc_empty(netdev) && !qlcnic_sriov_vf_check(adapter)) {
netdev_for_each_mc_addr(ha, netdev) {
qlcnic_nic_add_mac(adapter, ha->addr);
}
}
+ if (qlcnic_sriov_vf_check(adapter))
+ qlcnic_vf_add_mc_list(netdev);
+
send_fw_cmd:
- if (mode == VPORT_MISS_MODE_ACCEPT_ALL && !adapter->fdb_mac_learn) {
- qlcnic_alloc_lb_filters_mem(adapter);
- adapter->drv_mac_learn = true;
- } else {
- adapter->drv_mac_learn = false;
+ if (!qlcnic_sriov_vf_check(adapter)) {
+ if (mode == VPORT_MISS_MODE_ACCEPT_ALL &&
+ !adapter->fdb_mac_learn) {
+ qlcnic_alloc_lb_filters_mem(adapter);
+ adapter->drv_mac_learn = true;
+ } else {
+ adapter->drv_mac_learn = false;
+ }
}
qlcnic_nic_set_promisc(adapter, mode);
}
+void qlcnic_set_multi(struct net_device *netdev)
+{
+ struct qlcnic_adapter *adapter = netdev_priv(netdev);
+ struct netdev_hw_addr *ha;
+ struct qlcnic_mac_list_s *cur;
+
+ if (!test_bit(__QLCNIC_FW_ATTACHED, &adapter->state))
+ return;
+ if (qlcnic_sriov_vf_check(adapter)) {
+ if (!netdev_mc_empty(netdev)) {
+ netdev_for_each_mc_addr(ha, netdev) {
+ cur = kzalloc(sizeof(struct qlcnic_mac_list_s),
+ GFP_ATOMIC);
+ memcpy(cur->mac_addr,
+ ha->addr, ETH_ALEN);
+ list_add_tail(&cur->list, &adapter->vf_mc_list);
+ }
+ }
+ qlcnic_sriov_vf_schedule_multi(adapter->netdev);
+ return;
+ }
+ __qlcnic_set_multi(netdev);
+}
+
int qlcnic_82xx_nic_set_promisc(struct qlcnic_adapter *adapter, u32 mode)
{
struct qlcnic_nic_req req;
bc->bc_trans_wq = wq;
+ wq = create_singlethread_workqueue("async");
+ if (wq == NULL) {
+ err = -ENOMEM;
+ dev_err(&adapter->pdev->dev, "Cannot create async workqueue\n");
+ goto qlcnic_destroy_trans_wq;
+ }
+
+ bc->bc_async_wq = wq;
+ INIT_LIST_HEAD(&bc->async_list);
+
for (i = 0; i < num_vfs; i++) {
vf = &sriov->vf_info[i];
vf->adapter = adapter;
vp = kzalloc(sizeof(struct qlcnic_vport), GFP_KERNEL);
if (!vp) {
err = -ENOMEM;
- goto qlcnic_destroy_trans_wq;
+ goto qlcnic_destroy_async_wq;
}
sriov->vf_info[i].vp = vp;
random_ether_addr(vp->mac);
return 0;
+qlcnic_destroy_async_wq:
+ destroy_workqueue(bc->bc_async_wq);
+
qlcnic_destroy_trans_wq:
destroy_workqueue(bc->bc_trans_wq);
if (!qlcnic_sriov_enable_check(adapter))
return;
+ qlcnic_sriov_cleanup_async_list(bc);
+ destroy_workqueue(bc->bc_async_wq);
destroy_workqueue(bc->bc_trans_wq);
for (i = 0; i < sriov->num_vfs; i++)
{
int err;
+ INIT_LIST_HEAD(&adapter->vf_mc_list);
if (!qlcnic_use_msi_x && !!qlcnic_use_msi)
dev_warn(&adapter->pdev->dev,
"83xx adapter do not support MSI interrupts\n");
qlcnic_free_mbx_args(&cmd);
return ret;
}
+
+void qlcnic_vf_add_mc_list(struct net_device *netdev)
+{
+ struct qlcnic_adapter *adapter = netdev_priv(netdev);
+ struct qlcnic_mac_list_s *cur;
+ struct list_head *head, tmp_list;
+
+ INIT_LIST_HEAD(&tmp_list);
+ head = &adapter->vf_mc_list;
+ netif_addr_lock_bh(netdev);
+
+ while (!list_empty(head)) {
+ cur = list_entry(head->next, struct qlcnic_mac_list_s, list);
+ list_move(&cur->list, &tmp_list);
+ }
+
+ netif_addr_unlock_bh(netdev);
+
+ while (!list_empty(&tmp_list)) {
+ cur = list_entry((&tmp_list)->next,
+ struct qlcnic_mac_list_s, list);
+ qlcnic_nic_add_mac(adapter, cur->mac_addr);
+ list_del(&cur->list);
+ kfree(cur);
+ }
+}
+
+void qlcnic_sriov_cleanup_async_list(struct qlcnic_back_channel *bc)
+{
+ struct list_head *head = &bc->async_list;
+ struct qlcnic_async_work_list *entry;
+
+ while (!list_empty(head)) {
+ entry = list_entry(head->next, struct qlcnic_async_work_list,
+ list);
+ cancel_work_sync(&entry->work);
+ list_del(&entry->list);
+ kfree(entry);
+ }
+}
+
+static void qlcnic_sriov_vf_set_multi(struct net_device *netdev)
+{
+ struct qlcnic_adapter *adapter = netdev_priv(netdev);
+
+ if (!test_bit(__QLCNIC_FW_ATTACHED, &adapter->state))
+ return;
+
+ __qlcnic_set_multi(netdev);
+}
+
+static void qlcnic_sriov_handle_async_multi(struct work_struct *work)
+{
+ struct qlcnic_async_work_list *entry;
+ struct net_device *netdev;
+
+ entry = container_of(work, struct qlcnic_async_work_list, work);
+ netdev = (struct net_device *)entry->ptr;
+
+ qlcnic_sriov_vf_set_multi(netdev);
+ return;
+}
+
+static struct qlcnic_async_work_list *
+qlcnic_sriov_get_free_node_async_work(struct qlcnic_back_channel *bc)
+{
+ struct list_head *node;
+ struct qlcnic_async_work_list *entry = NULL;
+ u8 empty = 0;
+
+ list_for_each(node, &bc->async_list) {
+ entry = list_entry(node, struct qlcnic_async_work_list, list);
+ if (!work_pending(&entry->work)) {
+ empty = 1;
+ break;
+ }
+ }
+
+ if (!empty) {
+ entry = kzalloc(sizeof(struct qlcnic_async_work_list),
+ GFP_ATOMIC);
+ if (entry == NULL)
+ return NULL;
+ list_add_tail(&entry->list, &bc->async_list);
+ }
+
+ return entry;
+}
+
+static void qlcnic_sriov_schedule_bc_async_work(struct qlcnic_back_channel *bc,
+ work_func_t func, void *data)
+{
+ struct qlcnic_async_work_list *entry = NULL;
+
+ entry = qlcnic_sriov_get_free_node_async_work(bc);
+ if (!entry)
+ return;
+
+ entry->ptr = data;
+ INIT_WORK(&entry->work, func);
+ queue_work(bc->bc_async_wq, &entry->work);
+}
+
+void qlcnic_sriov_vf_schedule_multi(struct net_device *netdev)
+{
+
+ struct qlcnic_adapter *adapter = netdev_priv(netdev);
+ struct qlcnic_back_channel *bc = &adapter->ahw->sriov->bc;
+
+ qlcnic_sriov_schedule_bc_async_work(bc, qlcnic_sriov_handle_async_multi,
+ netdev);
+}