net: txgbevf: add link update flow
Add link update flow to wangxun 10/25/40G virtual functions. Get link status from pf in mbox, and if it is failed then check the vx_status, because vx_status switching is too slow. Signed-off-by: Mengyuan Lou <mengyuanlou@net-swift.com> Link: https://patch.msgid.link/20250704094923.652-9-mengyuanlou@net-swift.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
committed by
Jakub Kicinski
parent
ce12ba2546
commit
bf68010acc
@@ -1206,6 +1206,8 @@ enum wx_pf_flags {
|
||||
WX_FLAG_PTP_PPS_ENABLED,
|
||||
WX_FLAG_NEED_LINK_CONFIG,
|
||||
WX_FLAG_NEED_SFP_RESET,
|
||||
WX_FLAG_NEED_UPDATE_LINK,
|
||||
WX_FLAG_NEED_DO_RESET,
|
||||
WX_PF_FLAGS_NBITS /* must be last */
|
||||
};
|
||||
|
||||
|
||||
@@ -471,3 +471,129 @@ int wx_get_queues_vf(struct wx *wx, u32 *num_tcs, u32 *default_tc)
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(wx_get_queues_vf);
|
||||
|
||||
static int wx_get_link_status_from_pf(struct wx *wx, u32 *msgbuf)
|
||||
{
|
||||
u32 links_reg = msgbuf[1];
|
||||
|
||||
if (msgbuf[1] & WX_PF_NOFITY_VF_NET_NOT_RUNNING)
|
||||
wx->notify_down = true;
|
||||
else
|
||||
wx->notify_down = false;
|
||||
|
||||
if (wx->notify_down) {
|
||||
wx->link = false;
|
||||
wx->speed = SPEED_UNKNOWN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
wx->link = WX_PFLINK_STATUS(links_reg);
|
||||
wx->speed = WX_PFLINK_SPEED(links_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wx_pf_ping_vf(struct wx *wx, u32 *msgbuf)
|
||||
{
|
||||
if (!(msgbuf[0] & WX_VT_MSGTYPE_CTS))
|
||||
/* msg is not CTS, we need to do reset */
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct wx_link_reg_fields wx_speed_lookup_vf[] = {
|
||||
{wx_mac_unknown},
|
||||
{wx_mac_sp, SPEED_10000, SPEED_1000, SPEED_100, SPEED_UNKNOWN, SPEED_UNKNOWN},
|
||||
{wx_mac_em, SPEED_1000, SPEED_100, SPEED_10, SPEED_UNKNOWN, SPEED_UNKNOWN},
|
||||
{wx_mac_aml, SPEED_40000, SPEED_25000, SPEED_10000, SPEED_1000, SPEED_UNKNOWN},
|
||||
{wx_mac_aml40, SPEED_40000, SPEED_25000, SPEED_10000, SPEED_1000, SPEED_UNKNOWN},
|
||||
};
|
||||
|
||||
static void wx_check_physical_link(struct wx *wx)
|
||||
{
|
||||
u32 val, link_val;
|
||||
int ret;
|
||||
|
||||
/* get link status from hw status reg
|
||||
* for SFP+ modules and DA cables, it can take up to 500usecs
|
||||
* before the link status is correct
|
||||
*/
|
||||
if (wx->mac.type == wx_mac_em)
|
||||
ret = read_poll_timeout_atomic(rd32, val, val & GENMASK(4, 1),
|
||||
100, 500, false, wx, WX_VXSTATUS);
|
||||
else
|
||||
ret = read_poll_timeout_atomic(rd32, val, val & BIT(0), 100,
|
||||
500, false, wx, WX_VXSTATUS);
|
||||
if (ret) {
|
||||
wx->speed = SPEED_UNKNOWN;
|
||||
wx->link = false;
|
||||
return;
|
||||
}
|
||||
|
||||
wx->link = true;
|
||||
link_val = WX_VXSTATUS_SPEED(val);
|
||||
|
||||
if (link_val & BIT(0))
|
||||
wx->speed = wx_speed_lookup_vf[wx->mac.type].bit0_f;
|
||||
else if (link_val & BIT(1))
|
||||
wx->speed = wx_speed_lookup_vf[wx->mac.type].bit1_f;
|
||||
else if (link_val & BIT(2))
|
||||
wx->speed = wx_speed_lookup_vf[wx->mac.type].bit2_f;
|
||||
else if (link_val & BIT(3))
|
||||
wx->speed = wx_speed_lookup_vf[wx->mac.type].bit3_f;
|
||||
else
|
||||
wx->speed = SPEED_UNKNOWN;
|
||||
}
|
||||
|
||||
int wx_check_mac_link_vf(struct wx *wx)
|
||||
{
|
||||
struct wx_mbx_info *mbx = &wx->mbx;
|
||||
u32 msgbuf[2] = {0};
|
||||
int ret = 0;
|
||||
|
||||
if (!mbx->timeout)
|
||||
goto out;
|
||||
|
||||
wx_check_for_rst_vf(wx);
|
||||
if (!wx_check_for_msg_vf(wx))
|
||||
ret = wx_read_mbx_vf(wx, msgbuf, 2);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
switch (msgbuf[0] & GENMASK(8, 0)) {
|
||||
case WX_PF_NOFITY_VF_LINK_STATUS | WX_PF_CONTROL_MSG:
|
||||
ret = wx_get_link_status_from_pf(wx, msgbuf);
|
||||
goto out;
|
||||
case WX_PF_CONTROL_MSG:
|
||||
ret = wx_pf_ping_vf(wx, msgbuf);
|
||||
goto out;
|
||||
case 0:
|
||||
if (msgbuf[0] & WX_VT_MSGTYPE_NACK) {
|
||||
/* msg is NACK, we must have lost CTS status */
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
/* no message, check link status */
|
||||
wx_check_physical_link(wx);
|
||||
goto out;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(msgbuf[0] & WX_VT_MSGTYPE_CTS)) {
|
||||
/* msg is not CTS and is NACK we must have lost CTS status */
|
||||
if (msgbuf[0] & WX_VT_MSGTYPE_NACK)
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* the pf is talking, if we timed out in the past we reinit */
|
||||
if (!mbx->timeout) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -94,6 +94,19 @@
|
||||
#define WX_VXMRQC_RSS_EN BIT(8)
|
||||
#define WX_VXMRQC_RSS_HASH(f) FIELD_PREP(GENMASK(15, 13), f)
|
||||
|
||||
#define WX_PFLINK_STATUS(g) FIELD_GET(BIT(0), g)
|
||||
#define WX_PFLINK_SPEED(g) FIELD_GET(GENMASK(31, 1), g)
|
||||
#define WX_VXSTATUS_SPEED(g) FIELD_GET(GENMASK(4, 1), g)
|
||||
|
||||
struct wx_link_reg_fields {
|
||||
u32 mac_type;
|
||||
u32 bit0_f;
|
||||
u32 bit1_f;
|
||||
u32 bit2_f;
|
||||
u32 bit3_f;
|
||||
u32 bit4_f;
|
||||
};
|
||||
|
||||
void wx_init_hw_vf(struct wx *wx);
|
||||
int wx_reset_hw_vf(struct wx *wx);
|
||||
void wx_get_mac_addr_vf(struct wx *wx, u8 *mac_addr);
|
||||
@@ -109,5 +122,6 @@ int wx_update_xcast_mode_vf(struct wx *wx, int xcast_mode);
|
||||
int wx_get_link_state_vf(struct wx *wx, u16 *link_state);
|
||||
int wx_set_vfta_vf(struct wx *wx, u32 vlan, u32 vind, bool vlan_on,
|
||||
bool vlvf_bypass);
|
||||
int wx_check_mac_link_vf(struct wx *wx);
|
||||
|
||||
#endif /* _WX_VF_H_ */
|
||||
|
||||
@@ -48,6 +48,7 @@ void wxvf_remove(struct pci_dev *pdev)
|
||||
struct wx *wx = pci_get_drvdata(pdev);
|
||||
struct net_device *netdev;
|
||||
|
||||
cancel_work_sync(&wx->service_task);
|
||||
netdev = wx->netdev;
|
||||
unregister_netdev(netdev);
|
||||
kfree(wx->vfinfo);
|
||||
@@ -64,6 +65,7 @@ static irqreturn_t wx_msix_misc_vf(int __always_unused irq, void *data)
|
||||
{
|
||||
struct wx *wx = data;
|
||||
|
||||
set_bit(WX_FLAG_NEED_UPDATE_LINK, wx->flags);
|
||||
/* Clear the interrupt */
|
||||
if (netif_running(wx->netdev))
|
||||
wr32(wx, WX_VXIMC, wx->eims_other);
|
||||
@@ -243,6 +245,24 @@ int wx_set_mac_vf(struct net_device *netdev, void *p)
|
||||
}
|
||||
EXPORT_SYMBOL(wx_set_mac_vf);
|
||||
|
||||
void wxvf_watchdog_update_link(struct wx *wx)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!test_bit(WX_FLAG_NEED_UPDATE_LINK, wx->flags))
|
||||
return;
|
||||
|
||||
spin_lock_bh(&wx->mbx.mbx_lock);
|
||||
err = wx_check_mac_link_vf(wx);
|
||||
spin_unlock_bh(&wx->mbx.mbx_lock);
|
||||
if (err) {
|
||||
wx->link = false;
|
||||
set_bit(WX_FLAG_NEED_DO_RESET, wx->flags);
|
||||
}
|
||||
clear_bit(WX_FLAG_NEED_UPDATE_LINK, wx->flags);
|
||||
}
|
||||
EXPORT_SYMBOL(wxvf_watchdog_update_link);
|
||||
|
||||
static void wxvf_irq_enable(struct wx *wx)
|
||||
{
|
||||
wr32(wx, WX_VXIMC, wx->eims_enable_mask);
|
||||
@@ -250,6 +270,11 @@ static void wxvf_irq_enable(struct wx *wx)
|
||||
|
||||
static void wxvf_up_complete(struct wx *wx)
|
||||
{
|
||||
/* Always set the carrier off */
|
||||
netif_carrier_off(wx->netdev);
|
||||
mod_timer(&wx->service_timer, jiffies + HZ);
|
||||
set_bit(WX_FLAG_NEED_UPDATE_LINK, wx->flags);
|
||||
|
||||
wx_configure_msix_vf(wx);
|
||||
smp_mb__before_atomic();
|
||||
wx_napi_enable_all(wx);
|
||||
@@ -301,8 +326,10 @@ static void wxvf_down(struct wx *wx)
|
||||
{
|
||||
struct net_device *netdev = wx->netdev;
|
||||
|
||||
timer_delete_sync(&wx->service_timer);
|
||||
netif_tx_stop_all_queues(netdev);
|
||||
netif_tx_disable(netdev);
|
||||
netif_carrier_off(netdev);
|
||||
wx_napi_disable_all(wx);
|
||||
wx_reset_vf(wx);
|
||||
|
||||
@@ -310,6 +337,34 @@ static void wxvf_down(struct wx *wx)
|
||||
wx_clean_all_rx_rings(wx);
|
||||
}
|
||||
|
||||
static void wxvf_reinit_locked(struct wx *wx)
|
||||
{
|
||||
while (test_and_set_bit(WX_STATE_RESETTING, wx->state))
|
||||
usleep_range(1000, 2000);
|
||||
wxvf_down(wx);
|
||||
wx_free_irq(wx);
|
||||
wx_configure_vf(wx);
|
||||
wx_request_msix_irqs_vf(wx);
|
||||
wxvf_up_complete(wx);
|
||||
clear_bit(WX_STATE_RESETTING, wx->state);
|
||||
}
|
||||
|
||||
static void wxvf_reset_subtask(struct wx *wx)
|
||||
{
|
||||
if (!test_bit(WX_FLAG_NEED_DO_RESET, wx->flags))
|
||||
return;
|
||||
clear_bit(WX_FLAG_NEED_DO_RESET, wx->flags);
|
||||
|
||||
rtnl_lock();
|
||||
if (test_bit(WX_STATE_RESETTING, wx->state) ||
|
||||
!(netif_running(wx->netdev))) {
|
||||
rtnl_unlock();
|
||||
return;
|
||||
}
|
||||
wxvf_reinit_locked(wx);
|
||||
rtnl_unlock();
|
||||
}
|
||||
|
||||
int wxvf_close(struct net_device *netdev)
|
||||
{
|
||||
struct wx *wx = netdev_priv(netdev);
|
||||
@@ -321,3 +376,39 @@ int wxvf_close(struct net_device *netdev)
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(wxvf_close);
|
||||
|
||||
static void wxvf_link_config_subtask(struct wx *wx)
|
||||
{
|
||||
struct net_device *netdev = wx->netdev;
|
||||
|
||||
wxvf_watchdog_update_link(wx);
|
||||
if (wx->link) {
|
||||
if (netif_carrier_ok(netdev))
|
||||
return;
|
||||
netif_carrier_on(netdev);
|
||||
netdev_info(netdev, "Link is Up - %s\n",
|
||||
phy_speed_to_str(wx->speed));
|
||||
} else {
|
||||
if (!netif_carrier_ok(netdev))
|
||||
return;
|
||||
netif_carrier_off(netdev);
|
||||
netdev_info(netdev, "Link is Down\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void wxvf_service_task(struct work_struct *work)
|
||||
{
|
||||
struct wx *wx = container_of(work, struct wx, service_task);
|
||||
|
||||
wxvf_link_config_subtask(wx);
|
||||
wxvf_reset_subtask(wx);
|
||||
wx_service_event_complete(wx);
|
||||
}
|
||||
|
||||
void wxvf_init_service(struct wx *wx)
|
||||
{
|
||||
timer_setup(&wx->service_timer, wx_service_timer, 0);
|
||||
INIT_WORK(&wx->service_task, wxvf_service_task);
|
||||
clear_bit(WX_STATE_SERVICE_SCHED, wx->state);
|
||||
}
|
||||
EXPORT_SYMBOL(wxvf_init_service);
|
||||
|
||||
@@ -14,7 +14,9 @@ void wx_reset_vf(struct wx *wx);
|
||||
void wx_set_rx_mode_vf(struct net_device *netdev);
|
||||
void wx_configure_vf(struct wx *wx);
|
||||
int wx_set_mac_vf(struct net_device *netdev, void *p);
|
||||
void wxvf_watchdog_update_link(struct wx *wx);
|
||||
int wxvf_open(struct net_device *netdev);
|
||||
int wxvf_close(struct net_device *netdev);
|
||||
void wxvf_init_service(struct wx *wx);
|
||||
|
||||
#endif /* _WX_VF_COMMON_H_ */
|
||||
|
||||
@@ -250,6 +250,7 @@ static int txgbevf_probe(struct pci_dev *pdev,
|
||||
eth_hw_addr_set(netdev, wx->mac.perm_addr);
|
||||
ether_addr_copy(netdev->perm_addr, wx->mac.addr);
|
||||
|
||||
wxvf_init_service(wx);
|
||||
err = wx_init_interrupt_scheme(wx);
|
||||
if (err)
|
||||
goto err_free_sw_init;
|
||||
@@ -266,6 +267,8 @@ static int txgbevf_probe(struct pci_dev *pdev,
|
||||
err_register:
|
||||
wx_clear_interrupt_scheme(wx);
|
||||
err_free_sw_init:
|
||||
timer_delete_sync(&wx->service_timer);
|
||||
cancel_work_sync(&wx->service_task);
|
||||
kfree(wx->vfinfo);
|
||||
kfree(wx->rss_key);
|
||||
kfree(wx->mac_table);
|
||||
|
||||
Reference in New Issue
Block a user