From: Haiyang Zhang <[email protected]> Handle the HW link state events received from HW channel, and set the proper link state, also stop/wake queues accordingly.
Signed-off-by: Haiyang Zhang <[email protected]> --- v2: Updated link up/down to be symmetric, and other minor changes based on comments from Andrew Lunn. --- .../net/ethernet/microsoft/mana/gdma_main.c | 1 + .../net/ethernet/microsoft/mana/hw_channel.c | 19 +++++ drivers/net/ethernet/microsoft/mana/mana_en.c | 76 +++++++++++++++++-- include/net/mana/gdma.h | 4 +- include/net/mana/hw_channel.h | 2 + include/net/mana/mana.h | 6 ++ 6 files changed, 99 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c index 43f034e180c4..effe0a2f207a 100644 --- a/drivers/net/ethernet/microsoft/mana/gdma_main.c +++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c @@ -528,6 +528,7 @@ static void mana_gd_process_eqe(struct gdma_queue *eq) case GDMA_EQE_HWC_INIT_DONE: case GDMA_EQE_HWC_SOC_SERVICE: case GDMA_EQE_RNIC_QP_FATAL: + case GDMA_EQE_HWC_SOC_RECONFIG_DATA: if (!eq->eq.callback) break; diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c index ada6c78a2bef..eae68995492a 100644 --- a/drivers/net/ethernet/microsoft/mana/hw_channel.c +++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c @@ -118,6 +118,7 @@ static void mana_hwc_init_event_handler(void *ctx, struct gdma_queue *q_self, struct gdma_dev *gd = hwc->gdma_dev; union hwc_init_type_data type_data; union hwc_init_eq_id_db eq_db; + struct mana_context *ac; u32 type, val; int ret; @@ -196,6 +197,24 @@ static void mana_hwc_init_event_handler(void *ctx, struct gdma_queue *q_self, hwc->hwc_timeout = val; break; + case HWC_DATA_HW_LINK_CONNECT: + case HWC_DATA_HW_LINK_DISCONNECT: + ac = gd->gdma_context->mana.driver_data; + if (!ac) + break; + + if (ac->mana_removing) { + dev_info(hwc->dev, + "Removing: skip link event %u\n", + type); + break; + } + + ac->link_event = type; + schedule_work(&ac->link_change_work); + + break; + default: dev_warn(hwc->dev, "Received unknown reconfig type %u\n", type); break; diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index 0142fd98392c..51959d37b0a7 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -20,6 +20,7 @@ #include <net/mana/mana.h> #include <net/mana/mana_auxiliary.h> +#include <net/mana/hw_channel.h> static DEFINE_IDA(mana_adev_ida); @@ -84,8 +85,9 @@ static int mana_open(struct net_device *ndev) /* Ensure port state updated before txq state */ smp_wmb(); - netif_carrier_on(ndev); - netif_tx_wake_all_queues(ndev); + if (netif_carrier_ok(ndev)) + netif_tx_wake_all_queues(ndev); + netdev_dbg(ndev, "%s successful\n", __func__); return 0; } @@ -100,6 +102,59 @@ static int mana_close(struct net_device *ndev) return mana_detach(ndev, true); } +static void mana_link_state_handle(struct work_struct *w) +{ + struct mana_port_context *apc; + struct mana_context *ac; + struct net_device *ndev; + bool link_up; + int i; + + ac = container_of(w, struct mana_context, link_change_work); + + if (ac->mana_removing) + return; + + rtnl_lock(); + + if (ac->link_event == HWC_DATA_HW_LINK_CONNECT) + link_up = true; + else if (ac->link_event == HWC_DATA_HW_LINK_DISCONNECT) + link_up = false; + else + goto out; + + /* Process all ports */ + for (i = 0; i < ac->num_ports; i++) { + ndev = ac->ports[i]; + if (!ndev) + continue; + + apc = netdev_priv(ndev); + + if (link_up) { + if (!netif_carrier_ok(ndev)) { + netif_carrier_on(ndev); + + if (apc->port_is_up) + netif_tx_wake_all_queues(ndev); + } + + __netdev_notify_peers(ndev); + } else { + if (netif_carrier_ok(ndev)) { + if (apc->port_is_up) + netif_tx_disable(ndev); + + netif_carrier_off(ndev); + } + } + } + +out: + rtnl_unlock(); +} + static bool mana_can_tx(struct gdma_queue *wq) { return mana_gd_wq_avail_space(wq) >= MAX_TX_WQE_SIZE; @@ -3059,9 +3114,6 @@ int mana_attach(struct net_device *ndev) /* Ensure port state updated before txq state */ smp_wmb(); - if (apc->port_is_up) - netif_carrier_on(ndev); - netif_device_attach(ndev); return 0; @@ -3153,8 +3205,8 @@ int mana_detach(struct net_device *ndev, bool from_close) /* Ensure port state updated before txq state */ smp_wmb(); - netif_tx_disable(ndev); - netif_carrier_off(ndev); + if (netif_carrier_ok(ndev)) + netif_tx_disable(ndev); if (apc->port_st_save) { err = mana_dealloc_queues(ndev); @@ -3212,7 +3264,7 @@ static int mana_probe_port(struct mana_context *ac, int port_idx, netif_set_tso_max_size(ndev, GSO_MAX_SIZE); - netif_carrier_off(ndev); + netif_carrier_on(ndev); netdev_rss_key_fill(apc->hashkey, MANA_HASH_KEY_SIZE); @@ -3431,6 +3483,8 @@ int mana_probe(struct gdma_dev *gd, bool resuming) if (!resuming) { ac->num_ports = num_ports; + + INIT_WORK(&ac->link_change_work, mana_link_state_handle); } else { if (ac->num_ports != num_ports) { dev_err(dev, "The number of vPorts changed: %d->%d\n", @@ -3481,6 +3535,8 @@ int mana_probe(struct gdma_dev *gd, bool resuming) if (err) { mana_remove(gd, false); } else { + ac->mana_removing = false; + dev_dbg(dev, "gd=%p, id=%u, num_ports=%d, type=%u, instance=%u\n", gd, gd->dev_id.as_uint32, ac->num_ports, gd->dev_id.type, gd->dev_id.instance); @@ -3500,6 +3556,10 @@ void mana_remove(struct gdma_dev *gd, bool suspending) int err; int i; + ac->mana_removing = true; + + cancel_work_sync(&ac->link_change_work); + /* adev currently doesn't support suspending, always remove it */ if (gd->adev) remove_adev(gd); diff --git a/include/net/mana/gdma.h b/include/net/mana/gdma.h index 57df78cfbf82..637f42485dba 100644 --- a/include/net/mana/gdma.h +++ b/include/net/mana/gdma.h @@ -590,6 +590,7 @@ enum { /* Driver can self reset on FPGA Reconfig EQE notification */ #define GDMA_DRV_CAP_FLAG_1_HANDLE_RECONFIG_EQE BIT(17) +#define GDMA_DRV_CAP_FLAG_1_HW_VPORT_LINK_AWARE BIT(6) #define GDMA_DRV_CAP_FLAGS1 \ (GDMA_DRV_CAP_FLAG_1_EQ_SHARING_MULTI_VPORT | \ @@ -599,7 +600,8 @@ enum { GDMA_DRV_CAP_FLAG_1_DEV_LIST_HOLES_SUP | \ GDMA_DRV_CAP_FLAG_1_DYNAMIC_IRQ_ALLOC_SUPPORT | \ GDMA_DRV_CAP_FLAG_1_SELF_RESET_ON_EQE | \ - GDMA_DRV_CAP_FLAG_1_HANDLE_RECONFIG_EQE) + GDMA_DRV_CAP_FLAG_1_HANDLE_RECONFIG_EQE | \ + GDMA_DRV_CAP_FLAG_1_HW_VPORT_LINK_AWARE) #define GDMA_DRV_CAP_FLAGS2 0 diff --git a/include/net/mana/hw_channel.h b/include/net/mana/hw_channel.h index 83cf93338eb3..16feb39616c1 100644 --- a/include/net/mana/hw_channel.h +++ b/include/net/mana/hw_channel.h @@ -24,6 +24,8 @@ #define HWC_INIT_DATA_PF_DEST_CQ_ID 11 #define HWC_DATA_CFG_HWC_TIMEOUT 1 +#define HWC_DATA_HW_LINK_CONNECT 2 +#define HWC_DATA_HW_LINK_DISCONNECT 3 #define HW_CHANNEL_WAIT_RESOURCE_TIMEOUT_MS 30000 diff --git a/include/net/mana/mana.h b/include/net/mana/mana.h index 0921485565c0..d59e1f4656ce 100644 --- a/include/net/mana/mana.h +++ b/include/net/mana/mana.h @@ -477,6 +477,12 @@ struct mana_context { struct dentry *mana_eqs_debugfs; struct net_device *ports[MAX_PORTS_IN_MANA_DEV]; + + /* Link state change work */ + struct work_struct link_change_work; + u32 link_event; + + bool mana_removing; }; struct mana_port_context { -- 2.34.1
