diff options
author | Alexander Duyck <alexander.h.duyck@intel.com> | 2009-02-19 20:40:07 -0800 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-02-20 00:22:54 -0800 |
commit | 4ae196dfd61d06b061c069edcdd7c73121e60a21 (patch) | |
tree | 68111629e73751b6200cc9bdd8b769246fe0d540 /drivers/net/igb/igb_main.c | |
parent | e173952257d7a3d3c64de3039d9fc02d1fbf49c3 (diff) |
igb: Add support for enabling VFs to PF driver.
This patch adds the support to handle requests from the VF to perform
operations such as completing resets, setting/reading mac address, adding
vlans, adding multicast addresses, setting rlpml, and general
communications between the PF and all VFs.
Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/igb/igb_main.c')
-rw-r--r-- | drivers/net/igb/igb_main.c | 429 |
1 files changed, 405 insertions, 24 deletions
diff --git a/drivers/net/igb/igb_main.c b/drivers/net/igb/igb_main.c index c7c7eeba336..b9e7980e3f4 100644 --- a/drivers/net/igb/igb_main.c +++ b/drivers/net/igb/igb_main.c @@ -122,10 +122,16 @@ static void igb_vlan_rx_register(struct net_device *, struct vlan_group *); static void igb_vlan_rx_add_vid(struct net_device *, u16); static void igb_vlan_rx_kill_vid(struct net_device *, u16); static void igb_restore_vlan(struct igb_adapter *); +static void igb_ping_all_vfs(struct igb_adapter *); +static void igb_msg_task(struct igb_adapter *); +static int igb_rcv_msg_from_vf(struct igb_adapter *, u32); static inline void igb_set_rah_pool(struct e1000_hw *, int , int); static void igb_set_mc_list_pools(struct igb_adapter *, int, u16); +static void igb_vmm_control(struct igb_adapter *); static inline void igb_set_vmolr(struct e1000_hw *, int); -static inline void igb_set_vf_rlpml(struct igb_adapter *, int, int); +static inline int igb_set_vf_rlpml(struct igb_adapter *, int, int); +static int igb_set_vf_mac(struct igb_adapter *adapter, int, unsigned char *); +static void igb_restore_vf_multicasts(struct igb_adapter *adapter); static int igb_suspend(struct pci_dev *, pm_message_t); #ifdef CONFIG_PM @@ -768,7 +774,10 @@ static void igb_irq_enable(struct igb_adapter *adapter) wr32(E1000_EIAC, adapter->eims_enable_mask); wr32(E1000_EIAM, adapter->eims_enable_mask); wr32(E1000_EIMS, adapter->eims_enable_mask); - wr32(E1000_IMS, E1000_IMS_LSC | E1000_IMS_DOUTSYNC); + if (adapter->vfs_allocated_count) + wr32(E1000_MBVFIMR, 0xFF); + wr32(E1000_IMS, (E1000_IMS_LSC | E1000_IMS_VMMB | + E1000_IMS_DOUTSYNC)); } else { wr32(E1000_IMS, IMS_ENABLE_MASK); wr32(E1000_IAM, IMS_ENABLE_MASK); @@ -892,6 +901,7 @@ int igb_up(struct igb_adapter *adapter) if (adapter->msix_entries) igb_configure_msix(adapter); + igb_vmm_control(adapter); igb_set_rah_pool(hw, adapter->vfs_allocated_count, 0); igb_set_vmolr(hw, adapter->vfs_allocated_count); @@ -1047,6 +1057,20 @@ void igb_reset(struct igb_adapter *adapter) fc->send_xon = 1; fc->type = fc->original_type; + /* disable receive for all VFs and wait one second */ + if (adapter->vfs_allocated_count) { + int i; + for (i = 0 ; i < adapter->vfs_allocated_count; i++) + adapter->vf_data[i].clear_to_send = false; + + /* ping all the active vfs to let them know we are going down */ + igb_ping_all_vfs(adapter); + + /* disable transmits and receives */ + wr32(E1000_VFRE, 0); + wr32(E1000_VFTE, 0); + } + /* Allow time for pending master requests to run */ adapter->hw.mac.ops.reset_hw(&adapter->hw); wr32(E1000_WUC, 0); @@ -1624,6 +1648,7 @@ static int igb_open(struct net_device *netdev) * clean_rx handler before we do so. */ igb_configure(adapter); + igb_vmm_control(adapter); igb_set_rah_pool(hw, adapter->vfs_allocated_count, 0); igb_set_vmolr(hw, adapter->vfs_allocated_count); @@ -2456,6 +2481,8 @@ static void igb_set_multi(struct net_device *netdev) mac->rar_entry_count); igb_set_mc_list_pools(adapter, i, mac->rar_entry_count); + igb_restore_vf_multicasts(adapter); + kfree(mta_list); } @@ -2571,6 +2598,8 @@ static void igb_watchdog_task(struct work_struct *work) netif_carrier_on(netdev); netif_tx_wake_all_queues(netdev); + igb_ping_all_vfs(adapter); + /* link state has changed, schedule phy info update */ if (!test_bit(__IGB_DOWN, &adapter->state)) mod_timer(&adapter->phy_info_timer, @@ -2586,6 +2615,8 @@ static void igb_watchdog_task(struct work_struct *work) netif_carrier_off(netdev); netif_tx_stop_all_queues(netdev); + igb_ping_all_vfs(adapter); + /* link state has changed, schedule phy info update */ if (!test_bit(__IGB_DOWN, &adapter->state)) mod_timer(&adapter->phy_info_timer, @@ -3523,15 +3554,19 @@ static irqreturn_t igb_msix_other(int irq, void *data) /* HW is reporting DMA is out of sync */ adapter->stats.doosync++; } - if (!(icr & E1000_ICR_LSC)) - goto no_link_interrupt; - hw->mac.get_link_status = 1; - /* guard against interrupt when we're going down */ - if (!test_bit(__IGB_DOWN, &adapter->state)) - mod_timer(&adapter->watchdog_timer, jiffies + 1); -no_link_interrupt: - wr32(E1000_IMS, E1000_IMS_LSC | E1000_IMS_DOUTSYNC); + /* Check for a mailbox event */ + if (icr & E1000_ICR_VMMB) + igb_msg_task(adapter); + + if (icr & E1000_ICR_LSC) { + hw->mac.get_link_status = 1; + /* guard against interrupt when we're going down */ + if (!test_bit(__IGB_DOWN, &adapter->state)) + mod_timer(&adapter->watchdog_timer, jiffies + 1); + } + + wr32(E1000_IMS, E1000_IMS_LSC | E1000_IMS_DOUTSYNC | E1000_IMS_VMMB); wr32(E1000_EIMS, adapter->eims_other); return IRQ_HANDLED; @@ -3719,6 +3754,317 @@ static int igb_notify_dca(struct notifier_block *nb, unsigned long event, } #endif /* CONFIG_IGB_DCA */ +static void igb_ping_all_vfs(struct igb_adapter *adapter) +{ + struct e1000_hw *hw = &adapter->hw; + u32 ping; + int i; + + for (i = 0 ; i < adapter->vfs_allocated_count; i++) { + ping = E1000_PF_CONTROL_MSG; + if (adapter->vf_data[i].clear_to_send) + ping |= E1000_VT_MSGTYPE_CTS; + igb_write_mbx(hw, &ping, 1, i); + } +} + +static int igb_set_vf_multicasts(struct igb_adapter *adapter, + u32 *msgbuf, u32 vf) +{ + int n = (msgbuf[0] & E1000_VT_MSGINFO_MASK) >> E1000_VT_MSGINFO_SHIFT; + u16 *hash_list = (u16 *)&msgbuf[1]; + struct vf_data_storage *vf_data = &adapter->vf_data[vf]; + int i; + + /* only up to 30 hash values supported */ + if (n > 30) + n = 30; + + /* salt away the number of multi cast addresses assigned + * to this VF for later use to restore when the PF multi cast + * list changes + */ + vf_data->num_vf_mc_hashes = n; + + /* VFs are limited to using the MTA hash table for their multicast + * addresses */ + for (i = 0; i < n; i++) + vf_data->vf_mc_hashes[i] = hash_list[i];; + + /* Flush and reset the mta with the new values */ + igb_set_multi(adapter->netdev); + + return 0; +} + +static void igb_restore_vf_multicasts(struct igb_adapter *adapter) +{ + struct e1000_hw *hw = &adapter->hw; + struct vf_data_storage *vf_data; + int i, j; + + for (i = 0; i < adapter->vfs_allocated_count; i++) { + vf_data = &adapter->vf_data[i]; + for (j = 0; j < vf_data[i].num_vf_mc_hashes; j++) + igb_mta_set(hw, vf_data->vf_mc_hashes[j]); + } +} + +static void igb_clear_vf_vfta(struct igb_adapter *adapter, u32 vf) +{ + struct e1000_hw *hw = &adapter->hw; + u32 pool_mask, reg, vid; + int i; + + pool_mask = 1 << (E1000_VLVF_POOLSEL_SHIFT + vf); + + /* Find the vlan filter for this id */ + for (i = 0; i < E1000_VLVF_ARRAY_SIZE; i++) { + reg = rd32(E1000_VLVF(i)); + + /* remove the vf from the pool */ + reg &= ~pool_mask; + + /* if pool is empty then remove entry from vfta */ + if (!(reg & E1000_VLVF_POOLSEL_MASK) && + (reg & E1000_VLVF_VLANID_ENABLE)) { + reg = 0; + vid = reg & E1000_VLVF_VLANID_MASK; + igb_vfta_set(hw, vid, false); + } + + wr32(E1000_VLVF(i), reg); + } +} + +static s32 igb_vlvf_set(struct igb_adapter *adapter, u32 vid, bool add, u32 vf) +{ + struct e1000_hw *hw = &adapter->hw; + u32 reg, i; + + /* It is an error to call this function when VFs are not enabled */ + if (!adapter->vfs_allocated_count) + return -1; + + /* Find the vlan filter for this id */ + for (i = 0; i < E1000_VLVF_ARRAY_SIZE; i++) { + reg = rd32(E1000_VLVF(i)); + if ((reg & E1000_VLVF_VLANID_ENABLE) && + vid == (reg & E1000_VLVF_VLANID_MASK)) + break; + } + + if (add) { + if (i == E1000_VLVF_ARRAY_SIZE) { + /* Did not find a matching VLAN ID entry that was + * enabled. Search for a free filter entry, i.e. + * one without the enable bit set + */ + for (i = 0; i < E1000_VLVF_ARRAY_SIZE; i++) { + reg = rd32(E1000_VLVF(i)); + if (!(reg & E1000_VLVF_VLANID_ENABLE)) + break; + } + } + if (i < E1000_VLVF_ARRAY_SIZE) { + /* Found an enabled/available entry */ + reg |= 1 << (E1000_VLVF_POOLSEL_SHIFT + vf); + + /* if !enabled we need to set this up in vfta */ + if (!(reg & E1000_VLVF_VLANID_ENABLE)) { + /* add VID to filter table */ + igb_vfta_set(hw, vid, true); + reg |= E1000_VLVF_VLANID_ENABLE; + } + + wr32(E1000_VLVF(i), reg); + return 0; + } + } else { + if (i < E1000_VLVF_ARRAY_SIZE) { + /* remove vf from the pool */ + reg &= ~(1 << (E1000_VLVF_POOLSEL_SHIFT + vf)); + /* if pool is empty then remove entry from vfta */ + if (!(reg & E1000_VLVF_POOLSEL_MASK)) { + reg = 0; + igb_vfta_set(hw, vid, false); + } + wr32(E1000_VLVF(i), reg); + return 0; + } + } + return -1; +} + +static int igb_set_vf_vlan(struct igb_adapter *adapter, u32 *msgbuf, u32 vf) +{ + int add = (msgbuf[0] & E1000_VT_MSGINFO_MASK) >> E1000_VT_MSGINFO_SHIFT; + int vid = (msgbuf[1] & E1000_VLVF_VLANID_MASK); + + return igb_vlvf_set(adapter, vid, add, vf); +} + +static inline void igb_vf_reset_event(struct igb_adapter *adapter, u32 vf) +{ + struct e1000_hw *hw = &adapter->hw; + + /* disable mailbox functionality for vf */ + adapter->vf_data[vf].clear_to_send = false; + + /* reset offloads to defaults */ + igb_set_vmolr(hw, vf); + + /* reset vlans for device */ + igb_clear_vf_vfta(adapter, vf); + + /* reset multicast table array for vf */ + adapter->vf_data[vf].num_vf_mc_hashes = 0; + + /* Flush and reset the mta with the new values */ + igb_set_multi(adapter->netdev); +} + +static inline void igb_vf_reset_msg(struct igb_adapter *adapter, u32 vf) +{ + struct e1000_hw *hw = &adapter->hw; + unsigned char *vf_mac = adapter->vf_data[vf].vf_mac_addresses; + u32 reg, msgbuf[3]; + u8 *addr = (u8 *)(&msgbuf[1]); + + /* process all the same items cleared in a function level reset */ + igb_vf_reset_event(adapter, vf); + + /* set vf mac address */ + igb_rar_set(hw, vf_mac, vf + 1); + igb_set_rah_pool(hw, vf, vf + 1); + + /* enable transmit and receive for vf */ + reg = rd32(E1000_VFTE); + wr32(E1000_VFTE, reg | (1 << vf)); + reg = rd32(E1000_VFRE); + wr32(E1000_VFRE, reg | (1 << vf)); + + /* enable mailbox functionality for vf */ + adapter->vf_data[vf].clear_to_send = true; + + /* reply to reset with ack and vf mac address */ + msgbuf[0] = E1000_VF_RESET | E1000_VT_MSGTYPE_ACK; + memcpy(addr, vf_mac, 6); + igb_write_mbx(hw, msgbuf, 3, vf); +} + +static int igb_set_vf_mac_addr(struct igb_adapter *adapter, u32 *msg, int vf) +{ + unsigned char *addr = (char *)&msg[1]; + int err = -1; + + if (is_valid_ether_addr(addr)) + err = igb_set_vf_mac(adapter, vf, addr); + + return err; + +} + +static void igb_rcv_ack_from_vf(struct igb_adapter *adapter, u32 vf) +{ + struct e1000_hw *hw = &adapter->hw; + u32 msg = E1000_VT_MSGTYPE_NACK; + + /* if device isn't clear to send it shouldn't be reading either */ + if (!adapter->vf_data[vf].clear_to_send) + igb_write_mbx(hw, &msg, 1, vf); +} + + +static void igb_msg_task(struct igb_adapter *adapter) +{ + struct e1000_hw *hw = &adapter->hw; + u32 vf; + + for (vf = 0; vf < adapter->vfs_allocated_count; vf++) { + /* process any reset requests */ + if (!igb_check_for_rst(hw, vf)) { + adapter->vf_data[vf].clear_to_send = false; + igb_vf_reset_event(adapter, vf); + } + + /* process any messages pending */ + if (!igb_check_for_msg(hw, vf)) + igb_rcv_msg_from_vf(adapter, vf); + + /* process any acks */ + if (!igb_check_for_ack(hw, vf)) + igb_rcv_ack_from_vf(adapter, vf); + + } +} + +static int igb_rcv_msg_from_vf(struct igb_adapter *adapter, u32 vf) +{ + u32 mbx_size = E1000_VFMAILBOX_SIZE; + u32 msgbuf[mbx_size]; + struct e1000_hw *hw = &adapter->hw; + s32 retval; + + retval = igb_read_mbx(hw, msgbuf, mbx_size, vf); + + if (retval) + dev_err(&adapter->pdev->dev, + "Error receiving message from VF\n"); + + /* this is a message we already processed, do nothing */ + if (msgbuf[0] & (E1000_VT_MSGTYPE_ACK | E1000_VT_MSGTYPE_NACK)) + return retval; + + /* + * until the vf completes a reset it should not be + * allowed to start any configuration. + */ + + if (msgbuf[0] == E1000_VF_RESET) { + igb_vf_reset_msg(adapter, vf); + + return retval; + } + + if (!adapter->vf_data[vf].clear_to_send) { + msgbuf[0] |= E1000_VT_MSGTYPE_NACK; + igb_write_mbx(hw, msgbuf, 1, vf); + return retval; + } + + switch ((msgbuf[0] & 0xFFFF)) { + case E1000_VF_SET_MAC_ADDR: + retval = igb_set_vf_mac_addr(adapter, msgbuf, vf); + break; + case E1000_VF_SET_MULTICAST: + retval = igb_set_vf_multicasts(adapter, msgbuf, vf); + break; + case E1000_VF_SET_LPE: + retval = igb_set_vf_rlpml(adapter, msgbuf[1], vf); + break; + case E1000_VF_SET_VLAN: + retval = igb_set_vf_vlan(adapter, msgbuf, vf); + break; + default: + dev_err(&adapter->pdev->dev, "Unhandled Msg %08x\n", msgbuf[0]); + retval = -1; + break; + } + + /* notify the VF of the results of what it sent us */ + if (retval) + msgbuf[0] |= E1000_VT_MSGTYPE_NACK; + else + msgbuf[0] |= E1000_VT_MSGTYPE_ACK; + + msgbuf[0] |= E1000_VT_MSGTYPE_CTS; + + igb_write_mbx(hw, msgbuf, 1, vf); + + return retval; +} + /** * igb_intr_msi - Interrupt Handler * @irq: interrupt number @@ -4582,24 +4928,25 @@ static void igb_vlan_rx_add_vid(struct net_device *netdev, u16 vid) { struct igb_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; - u32 vfta, index; + int pf_id = adapter->vfs_allocated_count; if ((hw->mng_cookie.status & E1000_MNG_DHCP_COOKIE_STATUS_VLAN) && (vid == adapter->mng_vlan_id)) return; - /* add VID to filter table */ - index = (vid >> 5) & 0x7F; - vfta = array_rd32(E1000_VFTA, index); - vfta |= (1 << (vid & 0x1F)); - igb_write_vfta(&adapter->hw, index, vfta); + + /* add vid to vlvf if sr-iov is enabled, + * if that fails add directly to filter table */ + if (igb_vlvf_set(adapter, vid, true, pf_id)) + igb_vfta_set(hw, vid, true); + } static void igb_vlan_rx_kill_vid(struct net_device *netdev, u16 vid) { struct igb_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; - u32 vfta, index; + int pf_id = adapter->vfs_allocated_count; igb_irq_disable(adapter); vlan_group_set_device(adapter->vlgrp, vid, NULL); @@ -4615,11 +4962,10 @@ static void igb_vlan_rx_kill_vid(struct net_device *netdev, u16 vid) return; } - /* remove VID from filter table */ - index = (vid >> 5) & 0x7F; - vfta = array_rd32(E1000_VFTA, index); - vfta &= ~(1 << (vid & 0x1F)); - igb_write_vfta(&adapter->hw, index, vfta); + /* remove vid from vlvf if sr-iov is enabled, + * if not in vlvf remove from vfta */ + if (igb_vlvf_set(adapter, vid, false, pf_id)) + igb_vfta_set(hw, vid, false); } static void igb_restore_vlan(struct igb_adapter *adapter) @@ -4950,8 +5296,8 @@ static inline void igb_set_vmolr(struct e1000_hw *hw, int vfn) wr32(E1000_VMOLR(vfn), reg_data); } -static inline void igb_set_vf_rlpml(struct igb_adapter *adapter, int size, - int vfn) +static inline int igb_set_vf_rlpml(struct igb_adapter *adapter, int size, + int vfn) { struct e1000_hw *hw = &adapter->hw; u32 vmolr; @@ -4960,6 +5306,8 @@ static inline void igb_set_vf_rlpml(struct igb_adapter *adapter, int size, vmolr &= ~E1000_VMOLR_RLPML_MASK; vmolr |= size | E1000_VMOLR_LPE; wr32(E1000_VMOLR(vfn), vmolr); + + return 0; } static inline void igb_set_rah_pool(struct e1000_hw *hw, int pool, int entry) @@ -4985,4 +5333,37 @@ static void igb_set_mc_list_pools(struct igb_adapter *adapter, igb_set_rah_pool(hw, adapter->vfs_allocated_count, i); } +static int igb_set_vf_mac(struct igb_adapter *adapter, + int vf, unsigned char *mac_addr) +{ + struct e1000_hw *hw = &adapter->hw; + int rar_entry = vf + 1; /* VF MAC addresses start at entry 1 */ + + igb_rar_set(hw, mac_addr, rar_entry); + + memcpy(adapter->vf_data[vf].vf_mac_addresses, mac_addr, 6); + + igb_set_rah_pool(hw, vf, rar_entry); + + return 0; +} + +static void igb_vmm_control(struct igb_adapter *adapter) +{ + struct e1000_hw *hw = &adapter->hw; + u32 reg_data; + + if (!adapter->vfs_allocated_count) + return; + + /* VF's need PF reset indication before they + * can send/receive mail */ + reg_data = rd32(E1000_CTRL_EXT); + reg_data |= E1000_CTRL_EXT_PFRSTD; + wr32(E1000_CTRL_EXT, reg_data); + + igb_vmdq_set_loopback_pf(hw, true); + igb_vmdq_set_replication_pf(hw, true); +} + /* igb_main.c */ |