162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright(c) 2013 - 2019 Intel Corporation. */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include "fm10k.h" 562306a36Sopenharmony_ci#include "fm10k_vf.h" 662306a36Sopenharmony_ci#include "fm10k_pf.h" 762306a36Sopenharmony_ci 862306a36Sopenharmony_cistatic s32 fm10k_iov_msg_error(struct fm10k_hw *hw, u32 **results, 962306a36Sopenharmony_ci struct fm10k_mbx_info *mbx) 1062306a36Sopenharmony_ci{ 1162306a36Sopenharmony_ci struct fm10k_vf_info *vf_info = (struct fm10k_vf_info *)mbx; 1262306a36Sopenharmony_ci struct fm10k_intfc *interface = hw->back; 1362306a36Sopenharmony_ci struct pci_dev *pdev = interface->pdev; 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci dev_err(&pdev->dev, "Unknown message ID %u on VF %d\n", 1662306a36Sopenharmony_ci **results & FM10K_TLV_ID_MASK, vf_info->vf_idx); 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci return fm10k_tlv_msg_error(hw, results, mbx); 1962306a36Sopenharmony_ci} 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/** 2262306a36Sopenharmony_ci * fm10k_iov_msg_queue_mac_vlan - Message handler for MAC/VLAN request from VF 2362306a36Sopenharmony_ci * @hw: Pointer to hardware structure 2462306a36Sopenharmony_ci * @results: Pointer array to message, results[0] is pointer to message 2562306a36Sopenharmony_ci * @mbx: Pointer to mailbox information structure 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * This function is a custom handler for MAC/VLAN requests from the VF. The 2862306a36Sopenharmony_ci * assumption is that it is acceptable to directly hand off the message from 2962306a36Sopenharmony_ci * the VF to the PF's switch manager. However, we use a MAC/VLAN message 3062306a36Sopenharmony_ci * queue to avoid overloading the mailbox when a large number of requests 3162306a36Sopenharmony_ci * come in. 3262306a36Sopenharmony_ci **/ 3362306a36Sopenharmony_cistatic s32 fm10k_iov_msg_queue_mac_vlan(struct fm10k_hw *hw, u32 **results, 3462306a36Sopenharmony_ci struct fm10k_mbx_info *mbx) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci struct fm10k_vf_info *vf_info = (struct fm10k_vf_info *)mbx; 3762306a36Sopenharmony_ci struct fm10k_intfc *interface = hw->back; 3862306a36Sopenharmony_ci u8 mac[ETH_ALEN]; 3962306a36Sopenharmony_ci u32 *result; 4062306a36Sopenharmony_ci int err = 0; 4162306a36Sopenharmony_ci bool set; 4262306a36Sopenharmony_ci u16 vlan; 4362306a36Sopenharmony_ci u32 vid; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci /* we shouldn't be updating rules on a disabled interface */ 4662306a36Sopenharmony_ci if (!FM10K_VF_FLAG_ENABLED(vf_info)) 4762306a36Sopenharmony_ci err = FM10K_ERR_PARAM; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci if (!err && !!results[FM10K_MAC_VLAN_MSG_VLAN]) { 5062306a36Sopenharmony_ci result = results[FM10K_MAC_VLAN_MSG_VLAN]; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci /* record VLAN id requested */ 5362306a36Sopenharmony_ci err = fm10k_tlv_attr_get_u32(result, &vid); 5462306a36Sopenharmony_ci if (err) 5562306a36Sopenharmony_ci return err; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci set = !(vid & FM10K_VLAN_CLEAR); 5862306a36Sopenharmony_ci vid &= ~FM10K_VLAN_CLEAR; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci /* if the length field has been set, this is a multi-bit 6162306a36Sopenharmony_ci * update request. For multi-bit requests, simply disallow 6262306a36Sopenharmony_ci * them when the pf_vid has been set. In this case, the PF 6362306a36Sopenharmony_ci * should have already cleared the VLAN_TABLE, and if we 6462306a36Sopenharmony_ci * allowed them, it could allow a rogue VF to receive traffic 6562306a36Sopenharmony_ci * on a VLAN it was not assigned. In the single-bit case, we 6662306a36Sopenharmony_ci * need to modify requests for VLAN 0 to use the default PF or 6762306a36Sopenharmony_ci * SW vid when assigned. 6862306a36Sopenharmony_ci */ 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (vid >> 16) { 7162306a36Sopenharmony_ci /* prevent multi-bit requests when PF has 7262306a36Sopenharmony_ci * administratively set the VLAN for this VF 7362306a36Sopenharmony_ci */ 7462306a36Sopenharmony_ci if (vf_info->pf_vid) 7562306a36Sopenharmony_ci return FM10K_ERR_PARAM; 7662306a36Sopenharmony_ci } else { 7762306a36Sopenharmony_ci err = fm10k_iov_select_vid(vf_info, (u16)vid); 7862306a36Sopenharmony_ci if (err < 0) 7962306a36Sopenharmony_ci return err; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci vid = err; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* update VSI info for VF in regards to VLAN table */ 8562306a36Sopenharmony_ci err = hw->mac.ops.update_vlan(hw, vid, vf_info->vsi, set); 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (!err && !!results[FM10K_MAC_VLAN_MSG_MAC]) { 8962306a36Sopenharmony_ci result = results[FM10K_MAC_VLAN_MSG_MAC]; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* record unicast MAC address requested */ 9262306a36Sopenharmony_ci err = fm10k_tlv_attr_get_mac_vlan(result, mac, &vlan); 9362306a36Sopenharmony_ci if (err) 9462306a36Sopenharmony_ci return err; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci /* block attempts to set MAC for a locked device */ 9762306a36Sopenharmony_ci if (is_valid_ether_addr(vf_info->mac) && 9862306a36Sopenharmony_ci !ether_addr_equal(mac, vf_info->mac)) 9962306a36Sopenharmony_ci return FM10K_ERR_PARAM; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci set = !(vlan & FM10K_VLAN_CLEAR); 10262306a36Sopenharmony_ci vlan &= ~FM10K_VLAN_CLEAR; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci err = fm10k_iov_select_vid(vf_info, vlan); 10562306a36Sopenharmony_ci if (err < 0) 10662306a36Sopenharmony_ci return err; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci vlan = (u16)err; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci /* Add this request to the MAC/VLAN queue */ 11162306a36Sopenharmony_ci err = fm10k_queue_mac_request(interface, vf_info->glort, 11262306a36Sopenharmony_ci mac, vlan, set); 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if (!err && !!results[FM10K_MAC_VLAN_MSG_MULTICAST]) { 11662306a36Sopenharmony_ci result = results[FM10K_MAC_VLAN_MSG_MULTICAST]; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci /* record multicast MAC address requested */ 11962306a36Sopenharmony_ci err = fm10k_tlv_attr_get_mac_vlan(result, mac, &vlan); 12062306a36Sopenharmony_ci if (err) 12162306a36Sopenharmony_ci return err; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* verify that the VF is allowed to request multicast */ 12462306a36Sopenharmony_ci if (!(vf_info->vf_flags & FM10K_VF_FLAG_MULTI_ENABLED)) 12562306a36Sopenharmony_ci return FM10K_ERR_PARAM; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci set = !(vlan & FM10K_VLAN_CLEAR); 12862306a36Sopenharmony_ci vlan &= ~FM10K_VLAN_CLEAR; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci err = fm10k_iov_select_vid(vf_info, vlan); 13162306a36Sopenharmony_ci if (err < 0) 13262306a36Sopenharmony_ci return err; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci vlan = (u16)err; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* Add this request to the MAC/VLAN queue */ 13762306a36Sopenharmony_ci err = fm10k_queue_mac_request(interface, vf_info->glort, 13862306a36Sopenharmony_ci mac, vlan, set); 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci return err; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic const struct fm10k_msg_data iov_mbx_data[] = { 14562306a36Sopenharmony_ci FM10K_TLV_MSG_TEST_HANDLER(fm10k_tlv_msg_test), 14662306a36Sopenharmony_ci FM10K_VF_MSG_MSIX_HANDLER(fm10k_iov_msg_msix_pf), 14762306a36Sopenharmony_ci FM10K_VF_MSG_MAC_VLAN_HANDLER(fm10k_iov_msg_queue_mac_vlan), 14862306a36Sopenharmony_ci FM10K_VF_MSG_LPORT_STATE_HANDLER(fm10k_iov_msg_lport_state_pf), 14962306a36Sopenharmony_ci FM10K_TLV_MSG_ERROR_HANDLER(fm10k_iov_msg_error), 15062306a36Sopenharmony_ci}; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cis32 fm10k_iov_event(struct fm10k_intfc *interface) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci struct fm10k_hw *hw = &interface->hw; 15562306a36Sopenharmony_ci struct fm10k_iov_data *iov_data; 15662306a36Sopenharmony_ci s64 vflre; 15762306a36Sopenharmony_ci int i; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci /* if there is no iov_data then there is no mailbox to process */ 16062306a36Sopenharmony_ci if (!READ_ONCE(interface->iov_data)) 16162306a36Sopenharmony_ci return 0; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci rcu_read_lock(); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci iov_data = interface->iov_data; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci /* check again now that we are in the RCU block */ 16862306a36Sopenharmony_ci if (!iov_data) 16962306a36Sopenharmony_ci goto read_unlock; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if (!(fm10k_read_reg(hw, FM10K_EICR) & FM10K_EICR_VFLR)) 17262306a36Sopenharmony_ci goto read_unlock; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* read VFLRE to determine if any VFs have been reset */ 17562306a36Sopenharmony_ci vflre = fm10k_read_reg(hw, FM10K_PFVFLRE(1)); 17662306a36Sopenharmony_ci vflre <<= 32; 17762306a36Sopenharmony_ci vflre |= fm10k_read_reg(hw, FM10K_PFVFLRE(0)); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci i = iov_data->num_vfs; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci for (vflre <<= 64 - i; vflre && i--; vflre += vflre) { 18262306a36Sopenharmony_ci struct fm10k_vf_info *vf_info = &iov_data->vf_info[i]; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (vflre >= 0) 18562306a36Sopenharmony_ci continue; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci hw->iov.ops.reset_resources(hw, vf_info); 18862306a36Sopenharmony_ci vf_info->mbx.ops.connect(hw, &vf_info->mbx); 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ciread_unlock: 19262306a36Sopenharmony_ci rcu_read_unlock(); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci return 0; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cis32 fm10k_iov_mbx(struct fm10k_intfc *interface) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci struct fm10k_hw *hw = &interface->hw; 20062306a36Sopenharmony_ci struct fm10k_iov_data *iov_data; 20162306a36Sopenharmony_ci int i; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* if there is no iov_data then there is no mailbox to process */ 20462306a36Sopenharmony_ci if (!READ_ONCE(interface->iov_data)) 20562306a36Sopenharmony_ci return 0; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci rcu_read_lock(); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci iov_data = interface->iov_data; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* check again now that we are in the RCU block */ 21262306a36Sopenharmony_ci if (!iov_data) 21362306a36Sopenharmony_ci goto read_unlock; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* lock the mailbox for transmit and receive */ 21662306a36Sopenharmony_ci fm10k_mbx_lock(interface); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* Most VF messages sent to the PF cause the PF to respond by 21962306a36Sopenharmony_ci * requesting from the SM mailbox. This means that too many VF 22062306a36Sopenharmony_ci * messages processed at once could cause a mailbox timeout on the PF. 22162306a36Sopenharmony_ci * To prevent this, store a pointer to the next VF mbx to process. Use 22262306a36Sopenharmony_ci * that as the start of the loop so that we don't starve whichever VF 22362306a36Sopenharmony_ci * got ignored on the previous run. 22462306a36Sopenharmony_ci */ 22562306a36Sopenharmony_ciprocess_mbx: 22662306a36Sopenharmony_ci for (i = iov_data->next_vf_mbx ? : iov_data->num_vfs; i--;) { 22762306a36Sopenharmony_ci struct fm10k_vf_info *vf_info = &iov_data->vf_info[i]; 22862306a36Sopenharmony_ci struct fm10k_mbx_info *mbx = &vf_info->mbx; 22962306a36Sopenharmony_ci u16 glort = vf_info->glort; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* process the SM mailbox first to drain outgoing messages */ 23262306a36Sopenharmony_ci hw->mbx.ops.process(hw, &hw->mbx); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* verify port mapping is valid, if not reset port */ 23562306a36Sopenharmony_ci if (vf_info->vf_flags && !fm10k_glort_valid_pf(hw, glort)) { 23662306a36Sopenharmony_ci hw->iov.ops.reset_lport(hw, vf_info); 23762306a36Sopenharmony_ci fm10k_clear_macvlan_queue(interface, glort, false); 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* reset VFs that have mailbox timed out */ 24162306a36Sopenharmony_ci if (!mbx->timeout) { 24262306a36Sopenharmony_ci hw->iov.ops.reset_resources(hw, vf_info); 24362306a36Sopenharmony_ci mbx->ops.connect(hw, mbx); 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* guarantee we have free space in the SM mailbox */ 24762306a36Sopenharmony_ci if (hw->mbx.state == FM10K_STATE_OPEN && 24862306a36Sopenharmony_ci !hw->mbx.ops.tx_ready(&hw->mbx, FM10K_VFMBX_MSG_MTU)) { 24962306a36Sopenharmony_ci /* keep track of how many times this occurs */ 25062306a36Sopenharmony_ci interface->hw_sm_mbx_full++; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* make sure we try again momentarily */ 25362306a36Sopenharmony_ci fm10k_service_event_schedule(interface); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci break; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* cleanup mailbox and process received messages */ 25962306a36Sopenharmony_ci mbx->ops.process(hw, mbx); 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci /* if we stopped processing mailboxes early, update next_vf_mbx. 26362306a36Sopenharmony_ci * Otherwise, reset next_vf_mbx, and restart loop so that we process 26462306a36Sopenharmony_ci * the remaining mailboxes we skipped at the start. 26562306a36Sopenharmony_ci */ 26662306a36Sopenharmony_ci if (i >= 0) { 26762306a36Sopenharmony_ci iov_data->next_vf_mbx = i + 1; 26862306a36Sopenharmony_ci } else if (iov_data->next_vf_mbx) { 26962306a36Sopenharmony_ci iov_data->next_vf_mbx = 0; 27062306a36Sopenharmony_ci goto process_mbx; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* free the lock */ 27462306a36Sopenharmony_ci fm10k_mbx_unlock(interface); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ciread_unlock: 27762306a36Sopenharmony_ci rcu_read_unlock(); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci return 0; 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_civoid fm10k_iov_suspend(struct pci_dev *pdev) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci struct fm10k_intfc *interface = pci_get_drvdata(pdev); 28562306a36Sopenharmony_ci struct fm10k_iov_data *iov_data = interface->iov_data; 28662306a36Sopenharmony_ci struct fm10k_hw *hw = &interface->hw; 28762306a36Sopenharmony_ci int num_vfs, i; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci /* pull out num_vfs from iov_data */ 29062306a36Sopenharmony_ci num_vfs = iov_data ? iov_data->num_vfs : 0; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci /* shut down queue mapping for VFs */ 29362306a36Sopenharmony_ci fm10k_write_reg(hw, FM10K_DGLORTMAP(fm10k_dglort_vf_rss), 29462306a36Sopenharmony_ci FM10K_DGLORTMAP_NONE); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* Stop any active VFs and reset their resources */ 29762306a36Sopenharmony_ci for (i = 0; i < num_vfs; i++) { 29862306a36Sopenharmony_ci struct fm10k_vf_info *vf_info = &iov_data->vf_info[i]; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci hw->iov.ops.reset_resources(hw, vf_info); 30162306a36Sopenharmony_ci hw->iov.ops.reset_lport(hw, vf_info); 30262306a36Sopenharmony_ci fm10k_clear_macvlan_queue(interface, vf_info->glort, false); 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic void fm10k_mask_aer_comp_abort(struct pci_dev *pdev) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci u32 err_mask; 30962306a36Sopenharmony_ci int pos; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR); 31262306a36Sopenharmony_ci if (!pos) 31362306a36Sopenharmony_ci return; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci /* Mask the completion abort bit in the ERR_UNCOR_MASK register, 31662306a36Sopenharmony_ci * preventing the device from reporting these errors to the upstream 31762306a36Sopenharmony_ci * PCIe root device. This avoids bringing down platforms which upgrade 31862306a36Sopenharmony_ci * non-fatal completer aborts into machine check exceptions. Completer 31962306a36Sopenharmony_ci * aborts can occur whenever a VF reads a queue it doesn't own. 32062306a36Sopenharmony_ci */ 32162306a36Sopenharmony_ci pci_read_config_dword(pdev, pos + PCI_ERR_UNCOR_MASK, &err_mask); 32262306a36Sopenharmony_ci err_mask |= PCI_ERR_UNC_COMP_ABORT; 32362306a36Sopenharmony_ci pci_write_config_dword(pdev, pos + PCI_ERR_UNCOR_MASK, err_mask); 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ciint fm10k_iov_resume(struct pci_dev *pdev) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci struct fm10k_intfc *interface = pci_get_drvdata(pdev); 32962306a36Sopenharmony_ci struct fm10k_iov_data *iov_data = interface->iov_data; 33062306a36Sopenharmony_ci struct fm10k_dglort_cfg dglort = { 0 }; 33162306a36Sopenharmony_ci struct fm10k_hw *hw = &interface->hw; 33262306a36Sopenharmony_ci int num_vfs, i; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci /* pull out num_vfs from iov_data */ 33562306a36Sopenharmony_ci num_vfs = iov_data ? iov_data->num_vfs : 0; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* return error if iov_data is not already populated */ 33862306a36Sopenharmony_ci if (!iov_data) 33962306a36Sopenharmony_ci return -ENOMEM; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* Lower severity of completer abort error reporting as 34262306a36Sopenharmony_ci * the VFs can trigger this any time they read a queue 34362306a36Sopenharmony_ci * that they don't own. 34462306a36Sopenharmony_ci */ 34562306a36Sopenharmony_ci fm10k_mask_aer_comp_abort(pdev); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci /* allocate hardware resources for the VFs */ 34862306a36Sopenharmony_ci hw->iov.ops.assign_resources(hw, num_vfs, num_vfs); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* configure DGLORT mapping for RSS */ 35162306a36Sopenharmony_ci dglort.glort = hw->mac.dglort_map & FM10K_DGLORTMAP_NONE; 35262306a36Sopenharmony_ci dglort.idx = fm10k_dglort_vf_rss; 35362306a36Sopenharmony_ci dglort.inner_rss = 1; 35462306a36Sopenharmony_ci dglort.rss_l = fls(fm10k_queues_per_pool(hw) - 1); 35562306a36Sopenharmony_ci dglort.queue_b = fm10k_vf_queue_index(hw, 0); 35662306a36Sopenharmony_ci dglort.vsi_l = fls(hw->iov.total_vfs - 1); 35762306a36Sopenharmony_ci dglort.vsi_b = 1; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci hw->mac.ops.configure_dglort_map(hw, &dglort); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* assign resources to the device */ 36262306a36Sopenharmony_ci for (i = 0; i < num_vfs; i++) { 36362306a36Sopenharmony_ci struct fm10k_vf_info *vf_info = &iov_data->vf_info[i]; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci /* allocate all but the last GLORT to the VFs */ 36662306a36Sopenharmony_ci if (i == (~hw->mac.dglort_map >> FM10K_DGLORTMAP_MASK_SHIFT)) 36762306a36Sopenharmony_ci break; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* assign GLORT to VF, and restrict it to multicast */ 37062306a36Sopenharmony_ci hw->iov.ops.set_lport(hw, vf_info, i, 37162306a36Sopenharmony_ci FM10K_VF_FLAG_MULTI_CAPABLE); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci /* mailbox is disconnected so we don't send a message */ 37462306a36Sopenharmony_ci hw->iov.ops.assign_default_mac_vlan(hw, vf_info); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci /* now we are ready so we can connect */ 37762306a36Sopenharmony_ci vf_info->mbx.ops.connect(hw, &vf_info->mbx); 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci return 0; 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cis32 fm10k_iov_update_pvid(struct fm10k_intfc *interface, u16 glort, u16 pvid) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci struct fm10k_iov_data *iov_data = interface->iov_data; 38662306a36Sopenharmony_ci struct fm10k_hw *hw = &interface->hw; 38762306a36Sopenharmony_ci struct fm10k_vf_info *vf_info; 38862306a36Sopenharmony_ci u16 vf_idx = (glort - hw->mac.dglort_map) & FM10K_DGLORTMAP_NONE; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci /* no IOV support, not our message to process */ 39162306a36Sopenharmony_ci if (!iov_data) 39262306a36Sopenharmony_ci return FM10K_ERR_PARAM; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci /* glort outside our range, not our message to process */ 39562306a36Sopenharmony_ci if (vf_idx >= iov_data->num_vfs) 39662306a36Sopenharmony_ci return FM10K_ERR_PARAM; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* determine if an update has occurred and if so notify the VF */ 39962306a36Sopenharmony_ci vf_info = &iov_data->vf_info[vf_idx]; 40062306a36Sopenharmony_ci if (vf_info->sw_vid != pvid) { 40162306a36Sopenharmony_ci vf_info->sw_vid = pvid; 40262306a36Sopenharmony_ci hw->iov.ops.assign_default_mac_vlan(hw, vf_info); 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci return 0; 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistatic void fm10k_iov_free_data(struct pci_dev *pdev) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci struct fm10k_intfc *interface = pci_get_drvdata(pdev); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci if (!interface->iov_data) 41362306a36Sopenharmony_ci return; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci /* reclaim hardware resources */ 41662306a36Sopenharmony_ci fm10k_iov_suspend(pdev); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci /* drop iov_data from interface */ 41962306a36Sopenharmony_ci kfree_rcu(interface->iov_data, rcu); 42062306a36Sopenharmony_ci interface->iov_data = NULL; 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistatic s32 fm10k_iov_alloc_data(struct pci_dev *pdev, int num_vfs) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci struct fm10k_intfc *interface = pci_get_drvdata(pdev); 42662306a36Sopenharmony_ci struct fm10k_iov_data *iov_data = interface->iov_data; 42762306a36Sopenharmony_ci struct fm10k_hw *hw = &interface->hw; 42862306a36Sopenharmony_ci size_t size; 42962306a36Sopenharmony_ci int i; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci /* return error if iov_data is already populated */ 43262306a36Sopenharmony_ci if (iov_data) 43362306a36Sopenharmony_ci return -EBUSY; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci /* The PF should always be able to assign resources */ 43662306a36Sopenharmony_ci if (!hw->iov.ops.assign_resources) 43762306a36Sopenharmony_ci return -ENODEV; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci /* nothing to do if no VFs are requested */ 44062306a36Sopenharmony_ci if (!num_vfs) 44162306a36Sopenharmony_ci return 0; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci /* allocate memory for VF storage */ 44462306a36Sopenharmony_ci size = offsetof(struct fm10k_iov_data, vf_info[num_vfs]); 44562306a36Sopenharmony_ci iov_data = kzalloc(size, GFP_KERNEL); 44662306a36Sopenharmony_ci if (!iov_data) 44762306a36Sopenharmony_ci return -ENOMEM; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci /* record number of VFs */ 45062306a36Sopenharmony_ci iov_data->num_vfs = num_vfs; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci /* loop through vf_info structures initializing each entry */ 45362306a36Sopenharmony_ci for (i = 0; i < num_vfs; i++) { 45462306a36Sopenharmony_ci struct fm10k_vf_info *vf_info = &iov_data->vf_info[i]; 45562306a36Sopenharmony_ci int err; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci /* Record VF VSI value */ 45862306a36Sopenharmony_ci vf_info->vsi = i + 1; 45962306a36Sopenharmony_ci vf_info->vf_idx = i; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci /* initialize mailbox memory */ 46262306a36Sopenharmony_ci err = fm10k_pfvf_mbx_init(hw, &vf_info->mbx, iov_mbx_data, i); 46362306a36Sopenharmony_ci if (err) { 46462306a36Sopenharmony_ci dev_err(&pdev->dev, 46562306a36Sopenharmony_ci "Unable to initialize SR-IOV mailbox\n"); 46662306a36Sopenharmony_ci kfree(iov_data); 46762306a36Sopenharmony_ci return err; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci /* assign iov_data to interface */ 47262306a36Sopenharmony_ci interface->iov_data = iov_data; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci /* allocate hardware resources for the VFs */ 47562306a36Sopenharmony_ci fm10k_iov_resume(pdev); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci return 0; 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_civoid fm10k_iov_disable(struct pci_dev *pdev) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci if (pci_num_vf(pdev) && pci_vfs_assigned(pdev)) 48362306a36Sopenharmony_ci dev_err(&pdev->dev, 48462306a36Sopenharmony_ci "Cannot disable SR-IOV while VFs are assigned\n"); 48562306a36Sopenharmony_ci else 48662306a36Sopenharmony_ci pci_disable_sriov(pdev); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci fm10k_iov_free_data(pdev); 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ciint fm10k_iov_configure(struct pci_dev *pdev, int num_vfs) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci int current_vfs = pci_num_vf(pdev); 49462306a36Sopenharmony_ci int err = 0; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci if (current_vfs && pci_vfs_assigned(pdev)) { 49762306a36Sopenharmony_ci dev_err(&pdev->dev, 49862306a36Sopenharmony_ci "Cannot modify SR-IOV while VFs are assigned\n"); 49962306a36Sopenharmony_ci num_vfs = current_vfs; 50062306a36Sopenharmony_ci } else { 50162306a36Sopenharmony_ci pci_disable_sriov(pdev); 50262306a36Sopenharmony_ci fm10k_iov_free_data(pdev); 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci /* allocate resources for the VFs */ 50662306a36Sopenharmony_ci err = fm10k_iov_alloc_data(pdev, num_vfs); 50762306a36Sopenharmony_ci if (err) 50862306a36Sopenharmony_ci return err; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci /* allocate VFs if not already allocated */ 51162306a36Sopenharmony_ci if (num_vfs && num_vfs != current_vfs) { 51262306a36Sopenharmony_ci err = pci_enable_sriov(pdev, num_vfs); 51362306a36Sopenharmony_ci if (err) { 51462306a36Sopenharmony_ci dev_err(&pdev->dev, 51562306a36Sopenharmony_ci "Enable PCI SR-IOV failed: %d\n", err); 51662306a36Sopenharmony_ci return err; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci return num_vfs; 52162306a36Sopenharmony_ci} 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci/** 52462306a36Sopenharmony_ci * fm10k_iov_update_stats - Update stats for all VFs 52562306a36Sopenharmony_ci * @interface: device private structure 52662306a36Sopenharmony_ci * 52762306a36Sopenharmony_ci * Updates the VF statistics for all enabled VFs. Expects to be called by 52862306a36Sopenharmony_ci * fm10k_update_stats and assumes that locking via the __FM10K_UPDATING_STATS 52962306a36Sopenharmony_ci * bit is already handled. 53062306a36Sopenharmony_ci */ 53162306a36Sopenharmony_civoid fm10k_iov_update_stats(struct fm10k_intfc *interface) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci struct fm10k_iov_data *iov_data = interface->iov_data; 53462306a36Sopenharmony_ci struct fm10k_hw *hw = &interface->hw; 53562306a36Sopenharmony_ci int i; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci if (!iov_data) 53862306a36Sopenharmony_ci return; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci for (i = 0; i < iov_data->num_vfs; i++) 54162306a36Sopenharmony_ci hw->iov.ops.update_stats(hw, iov_data->vf_info[i].stats, i); 54262306a36Sopenharmony_ci} 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_cistatic inline void fm10k_reset_vf_info(struct fm10k_intfc *interface, 54562306a36Sopenharmony_ci struct fm10k_vf_info *vf_info) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci struct fm10k_hw *hw = &interface->hw; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci /* assigning the MAC address will send a mailbox message */ 55062306a36Sopenharmony_ci fm10k_mbx_lock(interface); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci /* disable LPORT for this VF which clears switch rules */ 55362306a36Sopenharmony_ci hw->iov.ops.reset_lport(hw, vf_info); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci fm10k_clear_macvlan_queue(interface, vf_info->glort, false); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci /* assign new MAC+VLAN for this VF */ 55862306a36Sopenharmony_ci hw->iov.ops.assign_default_mac_vlan(hw, vf_info); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci /* re-enable the LPORT for this VF */ 56162306a36Sopenharmony_ci hw->iov.ops.set_lport(hw, vf_info, vf_info->vf_idx, 56262306a36Sopenharmony_ci FM10K_VF_FLAG_MULTI_CAPABLE); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci fm10k_mbx_unlock(interface); 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ciint fm10k_ndo_set_vf_mac(struct net_device *netdev, int vf_idx, u8 *mac) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci struct fm10k_intfc *interface = netdev_priv(netdev); 57062306a36Sopenharmony_ci struct fm10k_iov_data *iov_data = interface->iov_data; 57162306a36Sopenharmony_ci struct fm10k_vf_info *vf_info; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci /* verify SR-IOV is active and that vf idx is valid */ 57462306a36Sopenharmony_ci if (!iov_data || vf_idx >= iov_data->num_vfs) 57562306a36Sopenharmony_ci return -EINVAL; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci /* verify MAC addr is valid */ 57862306a36Sopenharmony_ci if (!is_zero_ether_addr(mac) && !is_valid_ether_addr(mac)) 57962306a36Sopenharmony_ci return -EINVAL; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci /* record new MAC address */ 58262306a36Sopenharmony_ci vf_info = &iov_data->vf_info[vf_idx]; 58362306a36Sopenharmony_ci ether_addr_copy(vf_info->mac, mac); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci fm10k_reset_vf_info(interface, vf_info); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci return 0; 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ciint fm10k_ndo_set_vf_vlan(struct net_device *netdev, int vf_idx, u16 vid, 59162306a36Sopenharmony_ci u8 qos, __be16 vlan_proto) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci struct fm10k_intfc *interface = netdev_priv(netdev); 59462306a36Sopenharmony_ci struct fm10k_iov_data *iov_data = interface->iov_data; 59562306a36Sopenharmony_ci struct fm10k_hw *hw = &interface->hw; 59662306a36Sopenharmony_ci struct fm10k_vf_info *vf_info; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci /* verify SR-IOV is active and that vf idx is valid */ 59962306a36Sopenharmony_ci if (!iov_data || vf_idx >= iov_data->num_vfs) 60062306a36Sopenharmony_ci return -EINVAL; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci /* QOS is unsupported and VLAN IDs accepted range 0-4094 */ 60362306a36Sopenharmony_ci if (qos || (vid > (VLAN_VID_MASK - 1))) 60462306a36Sopenharmony_ci return -EINVAL; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci /* VF VLAN Protocol part to default is unsupported */ 60762306a36Sopenharmony_ci if (vlan_proto != htons(ETH_P_8021Q)) 60862306a36Sopenharmony_ci return -EPROTONOSUPPORT; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci vf_info = &iov_data->vf_info[vf_idx]; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci /* exit if there is nothing to do */ 61362306a36Sopenharmony_ci if (vf_info->pf_vid == vid) 61462306a36Sopenharmony_ci return 0; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci /* record default VLAN ID for VF */ 61762306a36Sopenharmony_ci vf_info->pf_vid = vid; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci /* Clear the VLAN table for the VF */ 62062306a36Sopenharmony_ci hw->mac.ops.update_vlan(hw, FM10K_VLAN_ALL, vf_info->vsi, false); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci fm10k_reset_vf_info(interface, vf_info); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci return 0; 62562306a36Sopenharmony_ci} 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ciint fm10k_ndo_set_vf_bw(struct net_device *netdev, int vf_idx, 62862306a36Sopenharmony_ci int __always_unused min_rate, int max_rate) 62962306a36Sopenharmony_ci{ 63062306a36Sopenharmony_ci struct fm10k_intfc *interface = netdev_priv(netdev); 63162306a36Sopenharmony_ci struct fm10k_iov_data *iov_data = interface->iov_data; 63262306a36Sopenharmony_ci struct fm10k_hw *hw = &interface->hw; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci /* verify SR-IOV is active and that vf idx is valid */ 63562306a36Sopenharmony_ci if (!iov_data || vf_idx >= iov_data->num_vfs) 63662306a36Sopenharmony_ci return -EINVAL; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci /* rate limit cannot be less than 10Mbs or greater than link speed */ 63962306a36Sopenharmony_ci if (max_rate && 64062306a36Sopenharmony_ci (max_rate < FM10K_VF_TC_MIN || max_rate > FM10K_VF_TC_MAX)) 64162306a36Sopenharmony_ci return -EINVAL; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci /* store values */ 64462306a36Sopenharmony_ci iov_data->vf_info[vf_idx].rate = max_rate; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci /* update hardware configuration */ 64762306a36Sopenharmony_ci hw->iov.ops.configure_tc(hw, vf_idx, max_rate); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci return 0; 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ciint fm10k_ndo_get_vf_config(struct net_device *netdev, 65362306a36Sopenharmony_ci int vf_idx, struct ifla_vf_info *ivi) 65462306a36Sopenharmony_ci{ 65562306a36Sopenharmony_ci struct fm10k_intfc *interface = netdev_priv(netdev); 65662306a36Sopenharmony_ci struct fm10k_iov_data *iov_data = interface->iov_data; 65762306a36Sopenharmony_ci struct fm10k_vf_info *vf_info; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci /* verify SR-IOV is active and that vf idx is valid */ 66062306a36Sopenharmony_ci if (!iov_data || vf_idx >= iov_data->num_vfs) 66162306a36Sopenharmony_ci return -EINVAL; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci vf_info = &iov_data->vf_info[vf_idx]; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci ivi->vf = vf_idx; 66662306a36Sopenharmony_ci ivi->max_tx_rate = vf_info->rate; 66762306a36Sopenharmony_ci ivi->min_tx_rate = 0; 66862306a36Sopenharmony_ci ether_addr_copy(ivi->mac, vf_info->mac); 66962306a36Sopenharmony_ci ivi->vlan = vf_info->pf_vid; 67062306a36Sopenharmony_ci ivi->qos = 0; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci return 0; 67362306a36Sopenharmony_ci} 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ciint fm10k_ndo_get_vf_stats(struct net_device *netdev, 67662306a36Sopenharmony_ci int vf_idx, struct ifla_vf_stats *stats) 67762306a36Sopenharmony_ci{ 67862306a36Sopenharmony_ci struct fm10k_intfc *interface = netdev_priv(netdev); 67962306a36Sopenharmony_ci struct fm10k_iov_data *iov_data = interface->iov_data; 68062306a36Sopenharmony_ci struct fm10k_hw *hw = &interface->hw; 68162306a36Sopenharmony_ci struct fm10k_hw_stats_q *hw_stats; 68262306a36Sopenharmony_ci u32 idx, qpp; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci /* verify SR-IOV is active and that vf idx is valid */ 68562306a36Sopenharmony_ci if (!iov_data || vf_idx >= iov_data->num_vfs) 68662306a36Sopenharmony_ci return -EINVAL; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci qpp = fm10k_queues_per_pool(hw); 68962306a36Sopenharmony_ci hw_stats = iov_data->vf_info[vf_idx].stats; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci for (idx = 0; idx < qpp; idx++) { 69262306a36Sopenharmony_ci stats->rx_packets += hw_stats[idx].rx_packets.count; 69362306a36Sopenharmony_ci stats->tx_packets += hw_stats[idx].tx_packets.count; 69462306a36Sopenharmony_ci stats->rx_bytes += hw_stats[idx].rx_bytes.count; 69562306a36Sopenharmony_ci stats->tx_bytes += hw_stats[idx].tx_bytes.count; 69662306a36Sopenharmony_ci stats->rx_dropped += hw_stats[idx].rx_drops.count; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci return 0; 70062306a36Sopenharmony_ci} 701