|
@@ -39,6 +39,8 @@ static int qlcnic_sriov_channel_cfg_cmd(struct qlcnic_adapter *, u8);
|
|
|
static void qlcnic_sriov_process_bc_cmd(struct work_struct *);
|
|
|
static int qlcnic_sriov_vf_shutdown(struct pci_dev *);
|
|
|
static int qlcnic_sriov_vf_resume(struct qlcnic_adapter *);
|
|
|
+static int qlcnic_sriov_async_issue_cmd(struct qlcnic_adapter *,
|
|
|
+ struct qlcnic_cmd_args *);
|
|
|
|
|
|
static struct qlcnic_hardware_ops qlcnic_sriov_vf_hw_ops = {
|
|
|
.read_crb = qlcnic_83xx_read_crb,
|
|
@@ -181,7 +183,7 @@ int qlcnic_sriov_init(struct qlcnic_adapter *adapter, int num_vfs)
|
|
|
vf->adapter = adapter;
|
|
|
vf->pci_func = qlcnic_sriov_virtid_fn(adapter, i);
|
|
|
mutex_init(&vf->send_cmd_lock);
|
|
|
- mutex_init(&vf->vlan_list_lock);
|
|
|
+ spin_lock_init(&vf->vlan_list_lock);
|
|
|
INIT_LIST_HEAD(&vf->rcv_act.wait_list);
|
|
|
INIT_LIST_HEAD(&vf->rcv_pend.wait_list);
|
|
|
spin_lock_init(&vf->rcv_act.lock);
|
|
@@ -197,8 +199,9 @@ int qlcnic_sriov_init(struct qlcnic_adapter *adapter, int num_vfs)
|
|
|
goto qlcnic_destroy_async_wq;
|
|
|
}
|
|
|
sriov->vf_info[i].vp = vp;
|
|
|
+ vp->vlan_mode = QLC_GUEST_VLAN_MODE;
|
|
|
vp->max_tx_bw = MAX_BW;
|
|
|
- vp->spoofchk = true;
|
|
|
+ vp->spoofchk = false;
|
|
|
random_ether_addr(vp->mac);
|
|
|
dev_info(&adapter->pdev->dev,
|
|
|
"MAC Address %pM is configured for VF %d\n",
|
|
@@ -515,6 +518,8 @@ static int qlcnic_sriov_setup_vf(struct qlcnic_adapter *adapter,
|
|
|
{
|
|
|
int err;
|
|
|
|
|
|
+ adapter->flags |= QLCNIC_VLAN_FILTERING;
|
|
|
+ adapter->ahw->total_nic_func = 1;
|
|
|
INIT_LIST_HEAD(&adapter->vf_mc_list);
|
|
|
if (!qlcnic_use_msi_x && !!qlcnic_use_msi)
|
|
|
dev_warn(&adapter->pdev->dev,
|
|
@@ -770,6 +775,7 @@ static int qlcnic_sriov_prepare_bc_hdr(struct qlcnic_bc_trans *trans,
|
|
|
cmd->req.arg = (u32 *)trans->req_pay;
|
|
|
cmd->rsp.arg = (u32 *)trans->rsp_pay;
|
|
|
cmd_op = cmd->req.arg[0] & 0xff;
|
|
|
+ cmd->cmd_op = cmd_op;
|
|
|
remainder = (trans->rsp_pay_size) % (bc_pay_sz);
|
|
|
num_frags = (trans->rsp_pay_size) / (bc_pay_sz);
|
|
|
if (remainder)
|
|
@@ -1356,7 +1362,7 @@ static int qlcnic_sriov_retry_bc_cmd(struct qlcnic_adapter *adapter,
|
|
|
return -EIO;
|
|
|
}
|
|
|
|
|
|
-static int qlcnic_sriov_issue_cmd(struct qlcnic_adapter *adapter,
|
|
|
+static int __qlcnic_sriov_issue_cmd(struct qlcnic_adapter *adapter,
|
|
|
struct qlcnic_cmd_args *cmd)
|
|
|
{
|
|
|
struct qlcnic_hardware_context *ahw = adapter->ahw;
|
|
@@ -1408,12 +1414,17 @@ retry:
|
|
|
(mbx_err_code == QLCNIC_MBX_PORT_RSP_OK)) {
|
|
|
rsp = QLCNIC_RCODE_SUCCESS;
|
|
|
} else {
|
|
|
- rsp = mbx_err_code;
|
|
|
- if (!rsp)
|
|
|
- rsp = 1;
|
|
|
- dev_err(dev,
|
|
|
- "MBX command 0x%x failed with err:0x%x for VF %d\n",
|
|
|
- opcode, mbx_err_code, func);
|
|
|
+ if (cmd->type == QLC_83XX_MBX_CMD_NO_WAIT) {
|
|
|
+ rsp = QLCNIC_RCODE_SUCCESS;
|
|
|
+ } else {
|
|
|
+ rsp = mbx_err_code;
|
|
|
+ if (!rsp)
|
|
|
+ rsp = 1;
|
|
|
+
|
|
|
+ dev_err(dev,
|
|
|
+ "MBX command 0x%x failed with err:0x%x for VF %d\n",
|
|
|
+ opcode, mbx_err_code, func);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
err_out:
|
|
@@ -1428,6 +1439,16 @@ cleanup_transaction:
|
|
|
return rsp;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+static int qlcnic_sriov_issue_cmd(struct qlcnic_adapter *adapter,
|
|
|
+ struct qlcnic_cmd_args *cmd)
|
|
|
+{
|
|
|
+ if (cmd->type == QLC_83XX_MBX_CMD_NO_WAIT)
|
|
|
+ return qlcnic_sriov_async_issue_cmd(adapter, cmd);
|
|
|
+ else
|
|
|
+ return __qlcnic_sriov_issue_cmd(adapter, cmd);
|
|
|
+}
|
|
|
+
|
|
|
static int qlcnic_sriov_channel_cfg_cmd(struct qlcnic_adapter *adapter, u8 cmd_op)
|
|
|
{
|
|
|
struct qlcnic_cmd_args cmd;
|
|
@@ -1458,58 +1479,28 @@ out:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-static void qlcnic_vf_add_mc_list(struct net_device *netdev)
|
|
|
+static void qlcnic_vf_add_mc_list(struct net_device *netdev, const u8 *mac)
|
|
|
{
|
|
|
struct qlcnic_adapter *adapter = netdev_priv(netdev);
|
|
|
struct qlcnic_sriov *sriov = adapter->ahw->sriov;
|
|
|
- struct qlcnic_mac_vlan_list *cur;
|
|
|
- struct list_head *head, tmp_list;
|
|
|
struct qlcnic_vf_info *vf;
|
|
|
u16 vlan_id;
|
|
|
int i;
|
|
|
|
|
|
- static const u8 bcast_addr[ETH_ALEN] = {
|
|
|
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
|
|
- };
|
|
|
-
|
|
|
vf = &adapter->ahw->sriov->vf_info[0];
|
|
|
- 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_vlan_list, 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_vlan_list, list);
|
|
|
- if (!qlcnic_sriov_check_any_vlan(vf)) {
|
|
|
- qlcnic_nic_add_mac(adapter, bcast_addr, 0);
|
|
|
- qlcnic_nic_add_mac(adapter, cur->mac_addr, 0);
|
|
|
- } else {
|
|
|
- mutex_lock(&vf->vlan_list_lock);
|
|
|
- for (i = 0; i < sriov->num_allowed_vlans; i++) {
|
|
|
- vlan_id = vf->sriov_vlans[i];
|
|
|
- if (vlan_id) {
|
|
|
- qlcnic_nic_add_mac(adapter, bcast_addr,
|
|
|
- vlan_id);
|
|
|
- qlcnic_nic_add_mac(adapter,
|
|
|
- cur->mac_addr,
|
|
|
- vlan_id);
|
|
|
- }
|
|
|
- }
|
|
|
- mutex_unlock(&vf->vlan_list_lock);
|
|
|
- if (qlcnic_84xx_check(adapter)) {
|
|
|
- qlcnic_nic_add_mac(adapter, bcast_addr, 0);
|
|
|
- qlcnic_nic_add_mac(adapter, cur->mac_addr, 0);
|
|
|
- }
|
|
|
+ if (!qlcnic_sriov_check_any_vlan(vf)) {
|
|
|
+ qlcnic_nic_add_mac(adapter, mac, 0);
|
|
|
+ } else {
|
|
|
+ spin_lock(&vf->vlan_list_lock);
|
|
|
+ for (i = 0; i < sriov->num_allowed_vlans; i++) {
|
|
|
+ vlan_id = vf->sriov_vlans[i];
|
|
|
+ if (vlan_id)
|
|
|
+ qlcnic_nic_add_mac(adapter, mac, vlan_id);
|
|
|
}
|
|
|
- list_del(&cur->list);
|
|
|
- kfree(cur);
|
|
|
+ spin_unlock(&vf->vlan_list_lock);
|
|
|
+ if (qlcnic_84xx_check(adapter))
|
|
|
+ qlcnic_nic_add_mac(adapter, mac, 0);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -1518,6 +1509,7 @@ void qlcnic_sriov_cleanup_async_list(struct qlcnic_back_channel *bc)
|
|
|
struct list_head *head = &bc->async_list;
|
|
|
struct qlcnic_async_work_list *entry;
|
|
|
|
|
|
+ flush_workqueue(bc->bc_async_wq);
|
|
|
while (!list_empty(head)) {
|
|
|
entry = list_entry(head->next, struct qlcnic_async_work_list,
|
|
|
list);
|
|
@@ -1527,10 +1519,14 @@ void qlcnic_sriov_cleanup_async_list(struct qlcnic_back_channel *bc)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static void qlcnic_sriov_vf_set_multi(struct net_device *netdev)
|
|
|
+void qlcnic_sriov_vf_set_multi(struct net_device *netdev)
|
|
|
{
|
|
|
struct qlcnic_adapter *adapter = netdev_priv(netdev);
|
|
|
struct qlcnic_hardware_context *ahw = adapter->ahw;
|
|
|
+ static const u8 bcast_addr[ETH_ALEN] = {
|
|
|
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
|
|
+ };
|
|
|
+ struct netdev_hw_addr *ha;
|
|
|
u32 mode = VPORT_MISS_MODE_DROP;
|
|
|
|
|
|
if (!test_bit(__QLCNIC_FW_ATTACHED, &adapter->state))
|
|
@@ -1542,23 +1538,49 @@ static void qlcnic_sriov_vf_set_multi(struct net_device *netdev)
|
|
|
} else if ((netdev->flags & IFF_ALLMULTI) ||
|
|
|
(netdev_mc_count(netdev) > ahw->max_mc_count)) {
|
|
|
mode = VPORT_MISS_MODE_ACCEPT_MULTI;
|
|
|
+ } else {
|
|
|
+ qlcnic_vf_add_mc_list(netdev, bcast_addr);
|
|
|
+ if (!netdev_mc_empty(netdev)) {
|
|
|
+ netdev_for_each_mc_addr(ha, netdev)
|
|
|
+ qlcnic_vf_add_mc_list(netdev, ha->addr);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- if (qlcnic_sriov_vf_check(adapter))
|
|
|
- qlcnic_vf_add_mc_list(netdev);
|
|
|
+ /* configure unicast MAC address, if there is not sufficient space
|
|
|
+ * to store all the unicast addresses then enable promiscuous mode
|
|
|
+ */
|
|
|
+ if (netdev_uc_count(netdev) > ahw->max_uc_count) {
|
|
|
+ mode = VPORT_MISS_MODE_ACCEPT_ALL;
|
|
|
+ } else if (!netdev_uc_empty(netdev)) {
|
|
|
+ netdev_for_each_uc_addr(ha, netdev)
|
|
|
+ qlcnic_vf_add_mc_list(netdev, ha->addr);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (adapter->pdev->is_virtfn) {
|
|
|
+ if (mode == VPORT_MISS_MODE_ACCEPT_ALL &&
|
|
|
+ !adapter->fdb_mac_learn) {
|
|
|
+ qlcnic_alloc_lb_filters_mem(adapter);
|
|
|
+ adapter->drv_mac_learn = 1;
|
|
|
+ adapter->rx_mac_learn = true;
|
|
|
+ } else {
|
|
|
+ adapter->drv_mac_learn = 0;
|
|
|
+ adapter->rx_mac_learn = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
qlcnic_nic_set_promisc(adapter, mode);
|
|
|
}
|
|
|
|
|
|
-static void qlcnic_sriov_handle_async_multi(struct work_struct *work)
|
|
|
+static void qlcnic_sriov_handle_async_issue_cmd(struct work_struct *work)
|
|
|
{
|
|
|
struct qlcnic_async_work_list *entry;
|
|
|
- struct net_device *netdev;
|
|
|
+ struct qlcnic_adapter *adapter;
|
|
|
+ struct qlcnic_cmd_args *cmd;
|
|
|
|
|
|
entry = container_of(work, struct qlcnic_async_work_list, work);
|
|
|
- netdev = (struct net_device *)entry->ptr;
|
|
|
-
|
|
|
- qlcnic_sriov_vf_set_multi(netdev);
|
|
|
+ adapter = entry->ptr;
|
|
|
+ cmd = entry->cmd;
|
|
|
+ __qlcnic_sriov_issue_cmd(adapter, cmd);
|
|
|
return;
|
|
|
}
|
|
|
|
|
@@ -1588,8 +1610,9 @@ qlcnic_sriov_get_free_node_async_work(struct qlcnic_back_channel *bc)
|
|
|
return entry;
|
|
|
}
|
|
|
|
|
|
-static void qlcnic_sriov_schedule_bc_async_work(struct qlcnic_back_channel *bc,
|
|
|
- work_func_t func, void *data)
|
|
|
+static void qlcnic_sriov_schedule_async_cmd(struct qlcnic_back_channel *bc,
|
|
|
+ work_func_t func, void *data,
|
|
|
+ struct qlcnic_cmd_args *cmd)
|
|
|
{
|
|
|
struct qlcnic_async_work_list *entry = NULL;
|
|
|
|
|
@@ -1598,21 +1621,23 @@ static void qlcnic_sriov_schedule_bc_async_work(struct qlcnic_back_channel *bc,
|
|
|
return;
|
|
|
|
|
|
entry->ptr = data;
|
|
|
+ entry->cmd = cmd;
|
|
|
INIT_WORK(&entry->work, func);
|
|
|
queue_work(bc->bc_async_wq, &entry->work);
|
|
|
}
|
|
|
|
|
|
-void qlcnic_sriov_vf_schedule_multi(struct net_device *netdev)
|
|
|
+static int qlcnic_sriov_async_issue_cmd(struct qlcnic_adapter *adapter,
|
|
|
+ struct qlcnic_cmd_args *cmd)
|
|
|
{
|
|
|
|
|
|
- struct qlcnic_adapter *adapter = netdev_priv(netdev);
|
|
|
struct qlcnic_back_channel *bc = &adapter->ahw->sriov->bc;
|
|
|
|
|
|
if (adapter->need_fw_reset)
|
|
|
- return;
|
|
|
+ return -EIO;
|
|
|
|
|
|
- qlcnic_sriov_schedule_bc_async_work(bc, qlcnic_sriov_handle_async_multi,
|
|
|
- netdev);
|
|
|
+ qlcnic_sriov_schedule_async_cmd(bc, qlcnic_sriov_handle_async_issue_cmd,
|
|
|
+ adapter, cmd);
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
static int qlcnic_sriov_vf_reinit_driver(struct qlcnic_adapter *adapter)
|
|
@@ -1836,6 +1861,12 @@ static int qlcnic_sriov_vf_idc_unknown_state(struct qlcnic_adapter *adapter)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static void qlcnic_sriov_vf_periodic_tasks(struct qlcnic_adapter *adapter)
|
|
|
+{
|
|
|
+ if (adapter->fhash.fnum)
|
|
|
+ qlcnic_prune_lb_filters(adapter);
|
|
|
+}
|
|
|
+
|
|
|
static void qlcnic_sriov_vf_poll_dev_state(struct work_struct *work)
|
|
|
{
|
|
|
struct qlcnic_adapter *adapter;
|
|
@@ -1867,6 +1898,8 @@ static void qlcnic_sriov_vf_poll_dev_state(struct work_struct *work)
|
|
|
}
|
|
|
|
|
|
idc->prev_state = idc->curr_state;
|
|
|
+ qlcnic_sriov_vf_periodic_tasks(adapter);
|
|
|
+
|
|
|
if (!ret && test_bit(QLC_83XX_MODULE_LOADED, &idc->status))
|
|
|
qlcnic_schedule_work(adapter, qlcnic_sriov_vf_poll_dev_state,
|
|
|
idc->delay);
|
|
@@ -1890,7 +1923,7 @@ static int qlcnic_sriov_check_vlan_id(struct qlcnic_sriov *sriov,
|
|
|
if (!vf->sriov_vlans)
|
|
|
return err;
|
|
|
|
|
|
- mutex_lock(&vf->vlan_list_lock);
|
|
|
+ spin_lock_bh(&vf->vlan_list_lock);
|
|
|
|
|
|
for (i = 0; i < sriov->num_allowed_vlans; i++) {
|
|
|
if (vf->sriov_vlans[i] == vlan_id) {
|
|
@@ -1899,7 +1932,7 @@ static int qlcnic_sriov_check_vlan_id(struct qlcnic_sriov *sriov,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- mutex_unlock(&vf->vlan_list_lock);
|
|
|
+ spin_unlock_bh(&vf->vlan_list_lock);
|
|
|
return err;
|
|
|
}
|
|
|
|
|
@@ -1908,12 +1941,12 @@ static int qlcnic_sriov_validate_num_vlans(struct qlcnic_sriov *sriov,
|
|
|
{
|
|
|
int err = 0;
|
|
|
|
|
|
- mutex_lock(&vf->vlan_list_lock);
|
|
|
+ spin_lock_bh(&vf->vlan_list_lock);
|
|
|
|
|
|
if (vf->num_vlan >= sriov->num_allowed_vlans)
|
|
|
err = -EINVAL;
|
|
|
|
|
|
- mutex_unlock(&vf->vlan_list_lock);
|
|
|
+ spin_unlock_bh(&vf->vlan_list_lock);
|
|
|
return err;
|
|
|
}
|
|
|
|
|
@@ -1966,7 +1999,7 @@ static void qlcnic_sriov_vlan_operation(struct qlcnic_vf_info *vf, u16 vlan_id,
|
|
|
if (!vf->sriov_vlans)
|
|
|
return;
|
|
|
|
|
|
- mutex_lock(&vf->vlan_list_lock);
|
|
|
+ spin_lock_bh(&vf->vlan_list_lock);
|
|
|
|
|
|
switch (opcode) {
|
|
|
case QLC_VLAN_ADD:
|
|
@@ -1979,7 +2012,7 @@ static void qlcnic_sriov_vlan_operation(struct qlcnic_vf_info *vf, u16 vlan_id,
|
|
|
netdev_err(adapter->netdev, "Invalid VLAN operation\n");
|
|
|
}
|
|
|
|
|
|
- mutex_unlock(&vf->vlan_list_lock);
|
|
|
+ spin_unlock_bh(&vf->vlan_list_lock);
|
|
|
return;
|
|
|
}
|
|
|
|
|
@@ -1987,6 +2020,7 @@ int qlcnic_sriov_cfg_vf_guest_vlan(struct qlcnic_adapter *adapter,
|
|
|
u16 vid, u8 enable)
|
|
|
{
|
|
|
struct qlcnic_sriov *sriov = adapter->ahw->sriov;
|
|
|
+ struct net_device *netdev = adapter->netdev;
|
|
|
struct qlcnic_vf_info *vf;
|
|
|
struct qlcnic_cmd_args cmd;
|
|
|
int ret;
|
|
@@ -2012,14 +2046,18 @@ int qlcnic_sriov_cfg_vf_guest_vlan(struct qlcnic_adapter *adapter,
|
|
|
dev_err(&adapter->pdev->dev,
|
|
|
"Failed to configure guest VLAN, err=%d\n", ret);
|
|
|
} else {
|
|
|
+ netif_addr_lock_bh(netdev);
|
|
|
qlcnic_free_mac_list(adapter);
|
|
|
+ netif_addr_unlock_bh(netdev);
|
|
|
|
|
|
if (enable)
|
|
|
qlcnic_sriov_vlan_operation(vf, vid, QLC_VLAN_ADD);
|
|
|
else
|
|
|
qlcnic_sriov_vlan_operation(vf, vid, QLC_VLAN_DELETE);
|
|
|
|
|
|
- qlcnic_set_multi(adapter->netdev);
|
|
|
+ netif_addr_lock_bh(netdev);
|
|
|
+ qlcnic_set_multi(netdev);
|
|
|
+ netif_addr_unlock_bh(netdev);
|
|
|
}
|
|
|
|
|
|
qlcnic_free_mbx_args(&cmd);
|
|
@@ -2150,11 +2188,11 @@ bool qlcnic_sriov_check_any_vlan(struct qlcnic_vf_info *vf)
|
|
|
{
|
|
|
bool err = false;
|
|
|
|
|
|
- mutex_lock(&vf->vlan_list_lock);
|
|
|
+ spin_lock_bh(&vf->vlan_list_lock);
|
|
|
|
|
|
if (vf->num_vlan)
|
|
|
err = true;
|
|
|
|
|
|
- mutex_unlock(&vf->vlan_list_lock);
|
|
|
+ spin_unlock_bh(&vf->vlan_list_lock);
|
|
|
return err;
|
|
|
}
|