]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
net: mana: Support HW link state events
authorHaiyang Zhang <haiyangz@microsoft.com>
Wed, 29 Oct 2025 20:43:10 +0000 (13:43 -0700)
committerJakub Kicinski <kuba@kernel.org>
Fri, 31 Oct 2025 22:56:53 +0000 (15:56 -0700)
Handle the NIC hardware link state events received from the HW
channel, then set the proper link state accordingly.

And, add a feature bit, GDMA_DRV_CAP_FLAG_1_HW_VPORT_LINK_AWARE,
to inform the NIC hardware this handler exists.

Our MANA NIC only sends out the link state down/up messages
when we need to let the VM rerun DHCP client and change IP
address. So, add netif_carrier_on() in the probe(), let the NIC
show the right initial state in /sys/class/net/ethX/operstate.

Signed-off-by: Haiyang Zhang <haiyangz@microsoft.com>
Link: https://patch.msgid.link/1761770601-16920-1-git-send-email-haiyangz@linux.microsoft.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/microsoft/mana/gdma_main.c
drivers/net/ethernet/microsoft/mana/hw_channel.c
drivers/net/ethernet/microsoft/mana/mana_en.c
include/net/mana/gdma.h
include/net/mana/hw_channel.h
include/net/mana/mana.h

index 43f034e180c41a595ff570b886569685a56f9fee..effe0a2f207aa19df6cca3409dd248fc55d93708 100644 (file)
@@ -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;
 
index ada6c78a2bef432f39a3bd4fa29b446c0b48b0aa..aa4e2731e2ba7a853fb5cc5e8e1861a2376f8a2a 100644 (file)
@@ -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,17 @@ 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;
+
+                       WRITE_ONCE(ac->link_event, type);
+                       schedule_work(&ac->link_change_work);
+
+                       break;
+
                default:
                        dev_warn(hwc->dev, "Received unknown reconfig type %u\n", type);
                        break;
index 0142fd98392c2ad2d92b13ae3c92ded09e9cbc90..739087081dfde5526c64a0815612b8880e6bdc6b 100644 (file)
@@ -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,7 +85,6 @@ 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);
        netdev_dbg(ndev, "%s successful\n", __func__);
        return 0;
@@ -100,6 +100,46 @@ 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_context *ac;
+       struct net_device *ndev;
+       u32 link_event;
+       bool link_up;
+       int i;
+
+       ac = container_of(w, struct mana_context, link_change_work);
+
+       rtnl_lock();
+
+       link_event = READ_ONCE(ac->link_event);
+
+       if (link_event == HWC_DATA_HW_LINK_CONNECT)
+               link_up = true;
+       else if (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;
+
+               if (link_up) {
+                       netif_carrier_on(ndev);
+
+                       __netdev_notify_peers(ndev);
+               } else {
+                       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 +3099,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;
@@ -3154,7 +3191,6 @@ int mana_detach(struct net_device *ndev, bool from_close)
        smp_wmb();
 
        netif_tx_disable(ndev);
-       netif_carrier_off(ndev);
 
        if (apc->port_st_save) {
                err = mana_dealloc_queues(ndev);
@@ -3243,6 +3279,8 @@ static int mana_probe_port(struct mana_context *ac, int port_idx,
                goto free_indir;
        }
 
+       netif_carrier_on(ndev);
+
        debugfs_create_u32("current_speed", 0400, apc->mana_port_debugfs, &apc->speed);
 
        return 0;
@@ -3431,6 +3469,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",
@@ -3438,6 +3478,8 @@ int mana_probe(struct gdma_dev *gd, bool resuming)
                        err = -EPROTO;
                        goto out;
                }
+
+               enable_work(&ac->link_change_work);
        }
 
        if (ac->num_ports == 0)
@@ -3500,6 +3542,8 @@ void mana_remove(struct gdma_dev *gd, bool suspending)
        int err;
        int i;
 
+       disable_work_sync(&ac->link_change_work);
+
        /* adev currently doesn't support suspending, always remove it */
        if (gd->adev)
                remove_adev(gd);
index 57df78cfbf82cde691d4eed212bd7dade7df9fbf..637f42485dba66f5e42889c9e792ea249d788cd0 100644 (file)
@@ -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
 
index 83cf93338eb38f55a17fb2a6381780f38ac3118f..16feb39616c1bead1a043b3fadc2e18a90651516 100644 (file)
@@ -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
 
index 0921485565c0519a2391ece9d83b640f5e822cfd..8906901535f558bac1923f66acbc360e0c56ecad 100644 (file)
@@ -477,6 +477,10 @@ 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;
 };
 
 struct mana_port_context {