* [PATCH 2/2] igc: Cache MAC passthrough address
2026-06-18 7:33 [PATCH 1/2] igc: Wait for MAC passthrough after reset Chia-Lin Kao (AceLan)
@ 2026-06-18 7:33 ` Chia-Lin Kao (AceLan)
2026-06-18 7:55 ` [Intel-wired-lan] [PATCH 1/2] igc: Wait for MAC passthrough after reset Loktionov, Aleksandr
` (3 subsequent siblings)
4 siblings, 0 replies; 8+ messages in thread
From: Chia-Lin Kao (AceLan) @ 2026-06-18 7:33 UTC (permalink / raw)
To: Tony Nguyen, Przemek Kitszel
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, intel-wired-lan, netdev, linux-kernel
Remember the MAC address that firmware writes into RAL0/RAH0 after reset.
On later probes, wait for that cached passthrough address instead of using
any valid register change as the completion condition.
This keeps the first probe conservative and lets later dock replug cycles
use the known passthrough address once the platform has already shown that
firmware can rewrite the address registers.
Signed-off-by: Chia-Lin Kao (AceLan) <acelan.kao@canonical.com>
---
drivers/net/ethernet/intel/igc/igc_main.c | 41 +++++++++++++++++++++--
1 file changed, 39 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c
index fa9752ed8bc5..cf87a82f1bf8 100644
--- a/drivers/net/ethernet/intel/igc/igc_main.c
+++ b/drivers/net/ethernet/intel/igc/igc_main.c
@@ -12,6 +12,7 @@
#include <linux/bpf_trace.h>
#include <net/xdp_sock_drv.h>
#include <linux/etherdevice.h>
+#include <linux/mutex.h>
#include <linux/pci.h>
#include <linux/mdio.h>
@@ -70,6 +71,32 @@ static const struct pci_device_id igc_pci_tbl[] = {
MODULE_DEVICE_TABLE(pci, igc_pci_tbl);
+static bool igc_mac_passthrough_cached;
+static u8 igc_mac_passthrough_addr[ETH_ALEN] __aligned(2);
+static DEFINE_MUTEX(igc_mac_passthrough_lock);
+
+static bool igc_get_mac_passthrough_addr(u8 *addr)
+{
+ mutex_lock(&igc_mac_passthrough_lock);
+ if (!igc_mac_passthrough_cached) {
+ mutex_unlock(&igc_mac_passthrough_lock);
+ return false;
+ }
+
+ ether_addr_copy(addr, igc_mac_passthrough_addr);
+ mutex_unlock(&igc_mac_passthrough_lock);
+
+ return true;
+}
+
+static void igc_cache_mac_passthrough_addr(u8 *addr)
+{
+ mutex_lock(&igc_mac_passthrough_lock);
+ ether_addr_copy(igc_mac_passthrough_addr, addr);
+ igc_mac_passthrough_cached = true;
+ mutex_unlock(&igc_mac_passthrough_lock);
+}
+
static void igc_read_rar0(struct igc_hw *hw, u8 *addr, u32 *ral, u32 *rah)
{
*ral = rd32(IGC_RAL(0));
@@ -97,22 +124,32 @@ static bool igc_is_lmvp_device(struct pci_dev *pdev)
static void igc_wait_for_lmvp_mac_passthrough(struct pci_dev *pdev,
struct igc_hw *hw)
{
+ u8 cached_addr[ETH_ALEN] __aligned(2);
u8 addr[ETH_ALEN] __aligned(2);
u32 orig_ral, orig_rah;
u32 ral, rah;
+ bool cached;
int i;
if (!igc_is_lmvp_device(pdev))
return;
+ cached = igc_get_mac_passthrough_addr(cached_addr);
igc_read_rar0(hw, addr, &orig_ral, &orig_rah);
+ if (cached && ether_addr_equal(addr, cached_addr))
+ return;
for (i = 0; i < 100; i++) {
msleep(100);
igc_read_rar0(hw, addr, &ral, &rah);
- if ((ral != orig_ral || rah != orig_rah) &&
- is_valid_ether_addr(addr))
+ if (cached) {
+ if (ether_addr_equal(addr, cached_addr))
+ return;
+ } else if ((ral != orig_ral || rah != orig_rah) &&
+ is_valid_ether_addr(addr)) {
+ igc_cache_mac_passthrough_addr(addr);
return;
+ }
}
}
--
2.53.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* RE: [Intel-wired-lan] [PATCH 1/2] igc: Wait for MAC passthrough after reset
2026-06-18 7:33 [PATCH 1/2] igc: Wait for MAC passthrough after reset Chia-Lin Kao (AceLan)
2026-06-18 7:33 ` [PATCH 2/2] igc: Cache MAC passthrough address Chia-Lin Kao (AceLan)
@ 2026-06-18 7:55 ` Loktionov, Aleksandr
2026-06-18 8:51 ` Ruinskiy, Dima
2026-06-18 8:08 ` Andrew Lunn
` (2 subsequent siblings)
4 siblings, 1 reply; 8+ messages in thread
From: Loktionov, Aleksandr @ 2026-06-18 7:55 UTC (permalink / raw)
To: kao, acelan, Nguyen, Anthony L, Kitszel, Przemyslaw
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, intel-wired-lan@lists.osuosl.org,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org
> -----Original Message-----
> From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf
> Of Chia-Lin Kao (AceLan) via Intel-wired-lan
> Sent: Thursday, June 18, 2026 9:33 AM
> To: Nguyen, Anthony L <anthony.l.nguyen@intel.com>; Kitszel,
> Przemyslaw <przemyslaw.kitszel@intel.com>
> Cc: Andrew Lunn <andrew+netdev@lunn.ch>; David S. Miller
> <davem@davemloft.net>; Eric Dumazet <edumazet@google.com>; Jakub
> Kicinski <kuba@kernel.org>; Paolo Abeni <pabeni@redhat.com>; intel-
> wired-lan@lists.osuosl.org; netdev@vger.kernel.org; linux-
> kernel@vger.kernel.org
> Subject: [Intel-wired-lan] [PATCH 1/2] igc: Wait for MAC passthrough
> after reset
>
> Some systems support MAC passthrough for dock Ethernet controllers by
> having firmware rewrite the receive address registers after the
> controller reset completes.
>
> igc resets the controller before reading RAL0/RAH0, so that reset can
> restore the controller native MAC address temporarily. If the driver
> reads the registers immediately, it can race the firmware rewrite and
> keep the native dock MAC instead of the host passthrough MAC.
>
> For LMVP devices, poll RAL0/RAH0 after reset and before reading the
> MAC address. Stop once the address registers change to another valid
> Ethernet address, allowing firmware a bounded window to complete the
> passthrough update.
>
Good day, Chia-Lin
It'd be great if you could share more details on how to reproduce the issue.
What exact hardware setup is affected (dock model, NIC, system)?
Which firmware/BIOS version?
How often does the race trigger?
Do you have a way to reliably reproduce it?
Also, what is the observed behavior vs. expected behavior? For example,
which MAC address is seen and which one should be used?
> Signed-off-by: Chia-Lin Kao (AceLan) <acelan.kao@canonical.com>
> ---
> drivers/net/ethernet/intel/igc/igc_main.c | 48
> +++++++++++++++++++++++
> 1 file changed, 48 insertions(+)
>
> diff --git a/drivers/net/ethernet/intel/igc/igc_main.c
> b/drivers/net/ethernet/intel/igc/igc_main.c
> index 2c9e2dfd8499..fa9752ed8bc5 100644
> --- a/drivers/net/ethernet/intel/igc/igc_main.c
> +++ b/drivers/net/ethernet/intel/igc/igc_main.c
> @@ -11,6 +11,7 @@
> #include <net/pkt_sched.h>
> #include <linux/bpf_trace.h>
> #include <net/xdp_sock_drv.h>
> +#include <linux/etherdevice.h>
> #include <linux/pci.h>
> #include <linux/mdio.h>
>
> @@ -69,6 +70,52 @@ static const struct pci_device_id igc_pci_tbl[] = {
>
> MODULE_DEVICE_TABLE(pci, igc_pci_tbl);
>
> +static void igc_read_rar0(struct igc_hw *hw, u8 *addr, u32 *ral, u32
> +*rah) {
> + *ral = rd32(IGC_RAL(0));
> + *rah = rd32(IGC_RAH(0));
> +
> + addr[0] = *ral & 0xff;
> + addr[1] = (*ral >> 8) & 0xff;
> + addr[2] = (*ral >> 16) & 0xff;
> + addr[3] = (*ral >> 24) & 0xff;
> + addr[4] = *rah & 0xff;
> + addr[5] = (*rah >> 8) & 0xff;
> +}
> +
> +static bool igc_is_lmvp_device(struct pci_dev *pdev) {
> + switch (pdev->device) {
> + case IGC_DEV_ID_I225_LMVP:
> + case IGC_DEV_ID_I226_LMVP:
> + return true;
> + default:
> + return false;
> + }
> +}
> +
> +static void igc_wait_for_lmvp_mac_passthrough(struct pci_dev *pdev,
> + struct igc_hw *hw)
> +{
> + u8 addr[ETH_ALEN] __aligned(2);
> + u32 orig_ral, orig_rah;
> + u32 ral, rah;
> + int i;
> +
> + if (!igc_is_lmvp_device(pdev))
> + return;
> +
> + igc_read_rar0(hw, addr, &orig_ral, &orig_rah);
> +
> + for (i = 0; i < 100; i++) {
> + msleep(100);
> + igc_read_rar0(hw, addr, &ral, &rah);
> + if ((ral != orig_ral || rah != orig_rah) &&
> + is_valid_ether_addr(addr))
> + return;
> + }
> +}
> +
> enum latency_range {
> lowest_latency = 0,
> low_latency = 1,
> @@ -7259,6 +7306,7 @@ static int igc_probe(struct pci_dev *pdev,
> * known good starting state
> */
> hw->mac.ops.reset_hw(hw);
> + igc_wait_for_lmvp_mac_passthrough(pdev, hw);
>
> if (igc_get_flash_presence_i225(hw)) {
> if (hw->nvm.ops.validate(hw) < 0) {
> --
> 2.53.0
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [Intel-wired-lan] [PATCH 1/2] igc: Wait for MAC passthrough after reset
2026-06-18 7:55 ` [Intel-wired-lan] [PATCH 1/2] igc: Wait for MAC passthrough after reset Loktionov, Aleksandr
@ 2026-06-18 8:51 ` Ruinskiy, Dima
2026-06-22 1:57 ` Chia-Lin Kao (AceLan)
0 siblings, 1 reply; 8+ messages in thread
From: Ruinskiy, Dima @ 2026-06-18 8:51 UTC (permalink / raw)
To: Loktionov, Aleksandr, kao, acelan, Nguyen, Anthony L,
Kitszel, Przemyslaw
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, intel-wired-lan@lists.osuosl.org,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org
On 18/06/2026 10:55, Loktionov, Aleksandr wrote:
>
>
>> -----Original Message-----
>> From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf
>> Of Chia-Lin Kao (AceLan) via Intel-wired-lan
>> Sent: Thursday, June 18, 2026 9:33 AM
>> To: Nguyen, Anthony L <anthony.l.nguyen@intel.com>; Kitszel,
>> Przemyslaw <przemyslaw.kitszel@intel.com>
>> Cc: Andrew Lunn <andrew+netdev@lunn.ch>; David S. Miller
>> <davem@davemloft.net>; Eric Dumazet <edumazet@google.com>; Jakub
>> Kicinski <kuba@kernel.org>; Paolo Abeni <pabeni@redhat.com>; intel-
>> wired-lan@lists.osuosl.org; netdev@vger.kernel.org; linux-
>> kernel@vger.kernel.org
>> Subject: [Intel-wired-lan] [PATCH 1/2] igc: Wait for MAC passthrough
>> after reset
>>
>> Some systems support MAC passthrough for dock Ethernet controllers by
>> having firmware rewrite the receive address registers after the
>> controller reset completes.
>>
>> igc resets the controller before reading RAL0/RAH0, so that reset can
>> restore the controller native MAC address temporarily. If the driver
>> reads the registers immediately, it can race the firmware rewrite and
>> keep the native dock MAC instead of the host passthrough MAC.
>>
>> For LMVP devices, poll RAL0/RAH0 after reset and before reading the
>> MAC address. Stop once the address registers change to another valid
>> Ethernet address, allowing firmware a bounded window to complete the
>> passthrough update.
>>
> Good day, Chia-Lin
>
> It'd be great if you could share more details on how to reproduce the issue.
>
> What exact hardware setup is affected (dock model, NIC, system)?
> Which firmware/BIOS version?
> How often does the race trigger?
> Do you have a way to reliably reproduce it?
>
> Also, what is the observed behavior vs. expected behavior? For example,
> which MAC address is seen and which one should be used?
>
In addition to that - I would ask - when the race triggers - how much
wait time do you need to reliably resolve it (i.e., for the FW to have
completed the MAC update)?
Because 100 iterations of 100msec each - this translates to up-to 10
seconds, no?
The weak spot here is what if you are on an LMvP system where MAC
passthrough has not been enabled. You will always wait for the full 10
seconds after every reset until you give up and just continue with the
default MAC. Hardly desirable behavior.
We've implemented something like this in another driver at one point,
and the default polling timeout there is 1 second (which does not affect
the UX too much).
A better way may be using a FW interrupt to notify the driver when the
MAC address has been updated. The usability of this approach depends on
whether it is possible to update the MAC address up the stack after the
device has already been initialized. Does the framework support this?
Thanks,
Dima.
>
>> Signed-off-by: Chia-Lin Kao (AceLan) <acelan.kao@canonical.com>
>> ---
>> drivers/net/ethernet/intel/igc/igc_main.c | 48
>> +++++++++++++++++++++++
>> 1 file changed, 48 insertions(+)
>>
>> diff --git a/drivers/net/ethernet/intel/igc/igc_main.c
>> b/drivers/net/ethernet/intel/igc/igc_main.c
>> index 2c9e2dfd8499..fa9752ed8bc5 100644
>> --- a/drivers/net/ethernet/intel/igc/igc_main.c
>> +++ b/drivers/net/ethernet/intel/igc/igc_main.c
>> @@ -11,6 +11,7 @@
>> #include <net/pkt_sched.h>
>> #include <linux/bpf_trace.h>
>> #include <net/xdp_sock_drv.h>
>> +#include <linux/etherdevice.h>
>> #include <linux/pci.h>
>> #include <linux/mdio.h>
>>
>> @@ -69,6 +70,52 @@ static const struct pci_device_id igc_pci_tbl[] = {
>>
>> MODULE_DEVICE_TABLE(pci, igc_pci_tbl);
>>
>> +static void igc_read_rar0(struct igc_hw *hw, u8 *addr, u32 *ral, u32
>> +*rah) {
>> + *ral = rd32(IGC_RAL(0));
>> + *rah = rd32(IGC_RAH(0));
>> +
>> + addr[0] = *ral & 0xff;
>> + addr[1] = (*ral >> 8) & 0xff;
>> + addr[2] = (*ral >> 16) & 0xff;
>> + addr[3] = (*ral >> 24) & 0xff;
>> + addr[4] = *rah & 0xff;
>> + addr[5] = (*rah >> 8) & 0xff;
>> +}
>> +
>> +static bool igc_is_lmvp_device(struct pci_dev *pdev) {
>> + switch (pdev->device) {
>> + case IGC_DEV_ID_I225_LMVP:
>> + case IGC_DEV_ID_I226_LMVP:
>> + return true;
>> + default:
>> + return false;
>> + }
>> +}
>> +
>> +static void igc_wait_for_lmvp_mac_passthrough(struct pci_dev *pdev,
>> + struct igc_hw *hw)
>> +{
>> + u8 addr[ETH_ALEN] __aligned(2);
>> + u32 orig_ral, orig_rah;
>> + u32 ral, rah;
>> + int i;
>> +
>> + if (!igc_is_lmvp_device(pdev))
>> + return;
>> +
>> + igc_read_rar0(hw, addr, &orig_ral, &orig_rah);
>> +
>> + for (i = 0; i < 100; i++) {
>> + msleep(100);
>> + igc_read_rar0(hw, addr, &ral, &rah);
>> + if ((ral != orig_ral || rah != orig_rah) &&
>> + is_valid_ether_addr(addr))
>> + return;
>> + }
>> +}
>> +
>> enum latency_range {
>> lowest_latency = 0,
>> low_latency = 1,
>> @@ -7259,6 +7306,7 @@ static int igc_probe(struct pci_dev *pdev,
>> * known good starting state
>> */
>> hw->mac.ops.reset_hw(hw);
>> + igc_wait_for_lmvp_mac_passthrough(pdev, hw);
>>
>> if (igc_get_flash_presence_i225(hw)) {
>> if (hw->nvm.ops.validate(hw) < 0) {
>> --
>> 2.53.0
>
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [Intel-wired-lan] [PATCH 1/2] igc: Wait for MAC passthrough after reset
2026-06-18 8:51 ` Ruinskiy, Dima
@ 2026-06-22 1:57 ` Chia-Lin Kao (AceLan)
0 siblings, 0 replies; 8+ messages in thread
From: Chia-Lin Kao (AceLan) @ 2026-06-22 1:57 UTC (permalink / raw)
To: Ruinskiy, Dima
Cc: Loktionov, Aleksandr, Nguyen, Anthony L, Kitszel, Przemyslaw,
Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, intel-wired-lan@lists.osuosl.org,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org
On Thu, Jun 18, 2026 at 11:51:35AM +0300, Ruinskiy, Dima wrote:
> On 18/06/2026 10:55, Loktionov, Aleksandr wrote:
> >
> >
> > > -----Original Message-----
> > > From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf
> > > Of Chia-Lin Kao (AceLan) via Intel-wired-lan
> > > Sent: Thursday, June 18, 2026 9:33 AM
> > > To: Nguyen, Anthony L <anthony.l.nguyen@intel.com>; Kitszel,
> > > Przemyslaw <przemyslaw.kitszel@intel.com>
> > > Cc: Andrew Lunn <andrew+netdev@lunn.ch>; David S. Miller
> > > <davem@davemloft.net>; Eric Dumazet <edumazet@google.com>; Jakub
> > > Kicinski <kuba@kernel.org>; Paolo Abeni <pabeni@redhat.com>; intel-
> > > wired-lan@lists.osuosl.org; netdev@vger.kernel.org; linux-
> > > kernel@vger.kernel.org
> > > Subject: [Intel-wired-lan] [PATCH 1/2] igc: Wait for MAC passthrough
> > > after reset
> > >
> > > Some systems support MAC passthrough for dock Ethernet controllers by
> > > having firmware rewrite the receive address registers after the
> > > controller reset completes.
> > >
> > > igc resets the controller before reading RAL0/RAH0, so that reset can
> > > restore the controller native MAC address temporarily. If the driver
> > > reads the registers immediately, it can race the firmware rewrite and
> > > keep the native dock MAC instead of the host passthrough MAC.
> > >
> > > For LMVP devices, poll RAL0/RAH0 after reset and before reading the
> > > MAC address. Stop once the address registers change to another valid
> > > Ethernet address, allowing firmware a bounded window to complete the
> > > passthrough update.
> > >
Hi Aleksandr and Dima,
Let me answer your questions below.
> > Good day, Chia-Lin
> >
> > It'd be great if you could share more details on how to reproduce the issue.
> >
> > What exact hardware setup is affected (dock model, NIC, system)?
We've observed this issue for a long time, and encountered the issue on
Lenovo's P15 Gen 2 (type 20YQ, 20YR) Laptops (ThinkPad) the first
time at 2021 and added 600ms delay.
Recently, we encountered the same issue on Dell, too, and then
increased the delay to 1000ms.
And now, the issue occurs again.
https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1942999
https://lore.kernel.org/lkml/20210702045120.22855-2-aaron.ma@canonical.com/
https://bugs.launchpad.net/ubuntu/+source/linux-oem-6.17/+bug/2143197
> > Which firmware/BIOS version?
It doesn't happen on a single firmware or BIOS, and not a single
hardware or a single brand.
> > How often does the race trigger?
It may happen when re-plug the dock cable.
With the mainline kernel, it's easy to reproduce the issue by
re-plugging the dock cable.
> > Do you have a way to reliably reproduce it?
Yes, I can find some machines to reproduce the issue reliably.
> >
> > Also, what is the observed behavior vs. expected behavior? For example,
> > which MAC address is seen and which one should be used?
Here is the debugging logs, fc:4c:ea:ae:a1:e3 is the MAC
address of the machine, and c4:d6:d3:83:75:d1 is the MAC of the dock.
It gets the correct passthrough MAC address after bootup and the
first re-plug at 40s, and fails to update the MAC address in time
after couple of re-plugs.
[ 0.689873] igc 0000:70:00.0: MAC debug before reset_hw: RAL0=0xaeea4cfc RAH0=0x8000e3a1 RAR0=fc:4c:ea:ae:a1:e3 valid=1
[ 0.755187] igc 0000:70:00.0: MAC debug after reset_hw: RAL0=0x83d3d6c4 RAH0=0x8000d175 RAR0=c4:d6:d3:83:75:d1 valid=1
[ 0.755576] igc 0000:70:00.0: MAC debug: eth_platform_get_mac_address ret=-19, reading RAR0/NVM fallback
[ 0.755582] igc 0000:70:00.0: MAC debug: read_mac_addr ret=0 addr=fc:4c:ea:ae:a1:e3 perm_addr=fc:4c:ea:ae:a1:e3
[ 4.687730] igc 0000:70:00.0: MAC debug firmware: fwnode=<none> props(mac=0 local=0 address=0) fwnode_ret=-19 fwnode_mac=00:00:00:00:00:00 device_ret=-2 device_mac=00:00:00:00:00:00 is_tbt=0 external=0 hotplug_bridge=0
[ 4.687739] igc 0000:70:00.0: MAC debug before reset_hw: RAL0=0xaeea4cfc RAH0=0x8000e3a1 RAR0=fc:4c:ea:ae:a1:e3 valid=1
[ 4.748545] igc 0000:70:00.0: MAC debug after reset_hw: RAL0=0x83d3d6c4 RAH0=0x8000d175 RAR0=c4:d6:d3:83:75:d1 valid=1
[ 4.748937] igc 0000:70:00.0: MAC debug: eth_platform_get_mac_address ret=-19, reading RAR0/NVM fallback
[ 4.748944] igc 0000:70:00.0: MAC debug: read_mac_addr ret=0 addr=fc:4c:ea:ae:a1:e3 perm_addr=fc:4c:ea:ae:a1:e3
[ 40.892715] igc 0000:70:00.0: MAC debug firmware: fwnode=<none> props(mac=0 local=0 address=0) fwnode_ret=-19 fwnode_mac=00:00:00:00:00:00 device_ret=-2 device_mac=00:00:00:00:00:00 is_tbt=0 external=0 hotplug_bridge=0
[ 40.892724] igc 0000:70:00.0: MAC debug before reset_hw: RAL0=0x83d3d6c4 RAH0=0x8000d175 RAR0=c4:d6:d3:83:75:d1 valid=1
[ 40.953524] igc 0000:70:00.0: MAC debug after reset_hw: RAL0=0x83d3d6c4 RAH0=0x8000d175 RAR0=c4:d6:d3:83:75:d1 valid=1
[ 40.953933] igc 0000:70:00.0: MAC debug: eth_platform_get_mac_address ret=-19, reading RAR0/NVM fallback
[ 40.953941] igc 0000:70:00.0: MAC debug: read_mac_addr ret=0 addr=c4:d6:d3:83:75:d1 perm_addr=c4:d6:d3:83:75:d1
...
[ 307.387282] igc 0000:70:00.0: MAC poll change at 700ms: RAL0=0xaeea4cfc RAH0=0x8000e3a1 RAR0=fc:4c:ea:ae:a1:e3 valid=1 prev=c4:d6:d3:83:75:d1
[ 328.826084] igc 0000:38:00.0: MAC poll change at 1000ms: RAL0=0xaeea4cfc RAH0=0x8000e3a1 RAR0=fc:4c:ea:ae:a1:e3 valid=1 prev=c4:d6:d3:83:75:d1
[ 429.070519] igc 0000:38:00.0: MAC poll change at 1100ms: RAL0=0xaeea4cfc RAH0=0x8000e3a1 RAR0=fc:4c:ea:ae:a1:e3 valid=1 prev=c4:d6:d3:83:75:d1
[ 466.509571] igc 0000:70:00.0: MAC poll change at 1000ms: RAL0=0xaeea4cfc RAH0=0x8000e3a1 RAR0=fc:4c:ea:ae:a1:e3 valid=1 prev=c4:d6:d3:83:75:d1
> >
> In addition to that - I would ask - when the race triggers - how much wait
> time do you need to reliably resolve it (i.e., for the FW to have completed
> the MAC update)?
We have tried to unconditionally wait for 1 second while probing, but
it still not enough sometimes. In rare case, it might exceed 2
seconds.
https://git.launchpad.net/~ubuntu-kernel/ubuntu/+source/linux/+git/noble/tree/drivers/net/ethernet/intel/igc/igc_main.c?h=hwe-7.0-next#n7205
if (pci_is_thunderbolt_attached(pdev))
msleep(1000);
>
> Because 100 iterations of 100msec each - this translates to up-to 10
> seconds, no?
Yes, just in case it takes longer.
I think 5 seconds should be enough if you feel this is feasible.
> The weak spot here is what if you are on an LMvP system where MAC
> passthrough has not been enabled. You will always wait for the full 10
> seconds after every reset until you give up and just continue with the
> default MAC. Hardly desirable behavior.
Right, that is the case. But we won't re-plug the dock frequently, so
it is not so bad to wait for a while for the Ethernet to get connected.
>
> We've implemented something like this in another driver at one point, and
> the default polling timeout there is 1 second (which does not affect the UX
> too much).
>
> A better way may be using a FW interrupt to notify the driver when the MAC
> address has been updated. The usability of this approach depends on whether
> it is possible to update the MAC address up the stack after the device has
> already been initialized. Does the framework support this?
I wish we can detect if the MAC passthrough is enabled, so that we
know if we need to poll for the MAC address.
For the FW interrupt mechanism also needs BIOS support, and we don't
have the power to push this.
>
> Thanks,
> Dima.
>
> >
> > > Signed-off-by: Chia-Lin Kao (AceLan) <acelan.kao@canonical.com>
> > > ---
> > > drivers/net/ethernet/intel/igc/igc_main.c | 48
> > > +++++++++++++++++++++++
> > > 1 file changed, 48 insertions(+)
> > >
> > > diff --git a/drivers/net/ethernet/intel/igc/igc_main.c
> > > b/drivers/net/ethernet/intel/igc/igc_main.c
> > > index 2c9e2dfd8499..fa9752ed8bc5 100644
> > > --- a/drivers/net/ethernet/intel/igc/igc_main.c
> > > +++ b/drivers/net/ethernet/intel/igc/igc_main.c
> > > @@ -11,6 +11,7 @@
> > > #include <net/pkt_sched.h>
> > > #include <linux/bpf_trace.h>
> > > #include <net/xdp_sock_drv.h>
> > > +#include <linux/etherdevice.h>
> > > #include <linux/pci.h>
> > > #include <linux/mdio.h>
> > >
> > > @@ -69,6 +70,52 @@ static const struct pci_device_id igc_pci_tbl[] = {
> > >
> > > MODULE_DEVICE_TABLE(pci, igc_pci_tbl);
> > >
> > > +static void igc_read_rar0(struct igc_hw *hw, u8 *addr, u32 *ral, u32
> > > +*rah) {
> > > + *ral = rd32(IGC_RAL(0));
> > > + *rah = rd32(IGC_RAH(0));
> > > +
> > > + addr[0] = *ral & 0xff;
> > > + addr[1] = (*ral >> 8) & 0xff;
> > > + addr[2] = (*ral >> 16) & 0xff;
> > > + addr[3] = (*ral >> 24) & 0xff;
> > > + addr[4] = *rah & 0xff;
> > > + addr[5] = (*rah >> 8) & 0xff;
> > > +}
> > > +
> > > +static bool igc_is_lmvp_device(struct pci_dev *pdev) {
> > > + switch (pdev->device) {
> > > + case IGC_DEV_ID_I225_LMVP:
> > > + case IGC_DEV_ID_I226_LMVP:
> > > + return true;
> > > + default:
> > > + return false;
> > > + }
> > > +}
> > > +
> > > +static void igc_wait_for_lmvp_mac_passthrough(struct pci_dev *pdev,
> > > + struct igc_hw *hw)
> > > +{
> > > + u8 addr[ETH_ALEN] __aligned(2);
> > > + u32 orig_ral, orig_rah;
> > > + u32 ral, rah;
> > > + int i;
> > > +
> > > + if (!igc_is_lmvp_device(pdev))
> > > + return;
> > > +
> > > + igc_read_rar0(hw, addr, &orig_ral, &orig_rah);
> > > +
> > > + for (i = 0; i < 100; i++) {
> > > + msleep(100);
> > > + igc_read_rar0(hw, addr, &ral, &rah);
> > > + if ((ral != orig_ral || rah != orig_rah) &&
> > > + is_valid_ether_addr(addr))
> > > + return;
> > > + }
> > > +}
> > > +
> > > enum latency_range {
> > > lowest_latency = 0,
> > > low_latency = 1,
> > > @@ -7259,6 +7306,7 @@ static int igc_probe(struct pci_dev *pdev,
> > > * known good starting state
> > > */
> > > hw->mac.ops.reset_hw(hw);
> > > + igc_wait_for_lmvp_mac_passthrough(pdev, hw);
> > >
> > > if (igc_get_flash_presence_i225(hw)) {
> > > if (hw->nvm.ops.validate(hw) < 0) {
> > > --
> > > 2.53.0
> >
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 1/2] igc: Wait for MAC passthrough after reset
2026-06-18 7:33 [PATCH 1/2] igc: Wait for MAC passthrough after reset Chia-Lin Kao (AceLan)
2026-06-18 7:33 ` [PATCH 2/2] igc: Cache MAC passthrough address Chia-Lin Kao (AceLan)
2026-06-18 7:55 ` [Intel-wired-lan] [PATCH 1/2] igc: Wait for MAC passthrough after reset Loktionov, Aleksandr
@ 2026-06-18 8:08 ` Andrew Lunn
2026-06-18 8:49 ` [Intel-wired-lan] " Kwapulinski, Piotr
2026-06-18 10:11 ` Paul Menzel
4 siblings, 0 replies; 8+ messages in thread
From: Andrew Lunn @ 2026-06-18 8:08 UTC (permalink / raw)
To: Chia-Lin Kao (AceLan)
Cc: Tony Nguyen, Przemek Kitszel, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, intel-wired-lan,
netdev, linux-kernel
> +static void igc_wait_for_lmvp_mac_passthrough(struct pci_dev *pdev,
> + struct igc_hw *hw)
> +{
> + u8 addr[ETH_ALEN] __aligned(2);
> + u32 orig_ral, orig_rah;
> + u32 ral, rah;
> + int i;
> +
> + if (!igc_is_lmvp_device(pdev))
> + return;
> +
> + igc_read_rar0(hw, addr, &orig_ral, &orig_rah);
> +
> + for (i = 0; i < 100; i++) {
> + msleep(100);
> + igc_read_rar0(hw, addr, &ral, &rah);
> + if ((ral != orig_ral || rah != orig_rah) &&
> + is_valid_ether_addr(addr))
> + return;
> + }
Please use one of the helpers from iopoll.h
Andrew
^ permalink raw reply [flat|nested] 8+ messages in thread* RE: [Intel-wired-lan] [PATCH 1/2] igc: Wait for MAC passthrough after reset
2026-06-18 7:33 [PATCH 1/2] igc: Wait for MAC passthrough after reset Chia-Lin Kao (AceLan)
` (2 preceding siblings ...)
2026-06-18 8:08 ` Andrew Lunn
@ 2026-06-18 8:49 ` Kwapulinski, Piotr
2026-06-18 10:11 ` Paul Menzel
4 siblings, 0 replies; 8+ messages in thread
From: Kwapulinski, Piotr @ 2026-06-18 8:49 UTC (permalink / raw)
To: kao, acelan, Nguyen, Anthony L, Kitszel, Przemyslaw
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, intel-wired-lan@lists.osuosl.org,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org
>-----Original Message-----
>From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf Of Chia-Lin Kao (AceLan) via Intel-wired-lan
>Sent: Thursday, June 18, 2026 9:33 AM
>To: Nguyen, Anthony L <anthony.l.nguyen@intel.com>; Kitszel, Przemyslaw <przemyslaw.kitszel@intel.com>
>Cc: Andrew Lunn <andrew+netdev@lunn.ch>; David S. Miller <davem@davemloft.net>; Eric Dumazet <edumazet@google.com>; Jakub Kicinski <kuba@kernel.org>; Paolo Abeni <pabeni@redhat.com>; intel-wired-lan@lists.osuosl.org; netdev@vger.kernel.org; linux-kernel@vger.kernel.org
>Subject: [Intel-wired-lan] [PATCH 1/2] igc: Wait for MAC passthrough after reset
>
>Some systems support MAC passthrough for dock Ethernet controllers by having firmware rewrite the receive address registers after the controller reset completes.
>
>igc resets the controller before reading RAL0/RAH0, so that reset can restore the controller native MAC address temporarily. If the driver reads the registers immediately, it can race the firmware rewrite and keep the native dock MAC instead of the host passthrough MAC.
>
>For LMVP devices, poll RAL0/RAH0 after reset and before reading the MAC address. Stop once the address registers change to another valid Ethernet address, allowing firmware a bounded window to complete the passthrough update.
>
>Signed-off-by: Chia-Lin Kao (AceLan) <acelan.kao@canonical.com>
>---
> drivers/net/ethernet/intel/igc/igc_main.c | 48 +++++++++++++++++++++++
> 1 file changed, 48 insertions(+)
>
>diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c
>index 2c9e2dfd8499..fa9752ed8bc5 100644
>--- a/drivers/net/ethernet/intel/igc/igc_main.c
>+++ b/drivers/net/ethernet/intel/igc/igc_main.c
>@@ -11,6 +11,7 @@
> #include <net/pkt_sched.h>
> #include <linux/bpf_trace.h>
> #include <net/xdp_sock_drv.h>
>+#include <linux/etherdevice.h>
> #include <linux/pci.h>
> #include <linux/mdio.h>
>
>@@ -69,6 +70,52 @@ static const struct pci_device_id igc_pci_tbl[] = {
>
> MODULE_DEVICE_TABLE(pci, igc_pci_tbl);
>
>+static void igc_read_rar0(struct igc_hw *hw, u8 *addr, u32 *ral, u32
>+*rah) {
>+ *ral = rd32(IGC_RAL(0));
>+ *rah = rd32(IGC_RAH(0));
>+
>+ addr[0] = *ral & 0xff;
>+ addr[1] = (*ral >> 8) & 0xff;
>+ addr[2] = (*ral >> 16) & 0xff;
>+ addr[3] = (*ral >> 24) & 0xff;
>+ addr[4] = *rah & 0xff;
>+ addr[5] = (*rah >> 8) & 0xff;
>+}
>+
>+static bool igc_is_lmvp_device(struct pci_dev *pdev) {
>+ switch (pdev->device) {
>+ case IGC_DEV_ID_I225_LMVP:
>+ case IGC_DEV_ID_I226_LMVP:
>+ return true;
>+ default:
>+ return false;
>+ }
>+}
>+
>+static void igc_wait_for_lmvp_mac_passthrough(struct pci_dev *pdev,
>+ struct igc_hw *hw)
>+{
>+ u8 addr[ETH_ALEN] __aligned(2);
>+ u32 orig_ral, orig_rah;
>+ u32 ral, rah;
>+ int i;
Hello AceLan
Please move ral, rah and 'i' right into the loop.
Thank you.
Piotr
>+
>+ if (!igc_is_lmvp_device(pdev))
>+ return;
>+
>+ igc_read_rar0(hw, addr, &orig_ral, &orig_rah);
>+
>+ for (i = 0; i < 100; i++) {
>+ msleep(100);
>+ igc_read_rar0(hw, addr, &ral, &rah);
>+ if ((ral != orig_ral || rah != orig_rah) &&
>+ is_valid_ether_addr(addr))
>+ return;
>+ }
>+}
>+
> enum latency_range {
> lowest_latency = 0,
> low_latency = 1,
>@@ -7259,6 +7306,7 @@ static int igc_probe(struct pci_dev *pdev,
> * known good starting state
> */
> hw->mac.ops.reset_hw(hw);
>+ igc_wait_for_lmvp_mac_passthrough(pdev, hw);
>
> if (igc_get_flash_presence_i225(hw)) {
> if (hw->nvm.ops.validate(hw) < 0) {
>--
>2.53.0
>
>
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [Intel-wired-lan] [PATCH 1/2] igc: Wait for MAC passthrough after reset
2026-06-18 7:33 [PATCH 1/2] igc: Wait for MAC passthrough after reset Chia-Lin Kao (AceLan)
` (3 preceding siblings ...)
2026-06-18 8:49 ` [Intel-wired-lan] " Kwapulinski, Piotr
@ 2026-06-18 10:11 ` Paul Menzel
4 siblings, 0 replies; 8+ messages in thread
From: Paul Menzel @ 2026-06-18 10:11 UTC (permalink / raw)
To: Chia-Lin Kao (AceLan)
Cc: Tony Nguyen, Przemek Kitszel, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, intel-wired-lan,
netdev, linux-kernel
Dear Chia-Lin,
Thank you for your patch.
Am 18.06.26 um 09:33 schrieb Chia-Lin Kao (AceLan) via Intel-wired-lan:
> Some systems support MAC passthrough for dock Ethernet controllers by
> having firmware rewrite the receive address registers after the controller
> reset completes.
Please give one example system.
> igc resets the controller before reading RAL0/RAH0, so that reset can
> restore the controller native MAC address temporarily. If the driver reads
> the registers immediately, it can race the firmware rewrite and keep the
> native dock MAC instead of the host passthrough MAC.
>
> For LMVP devices, poll RAL0/RAH0 after reset and before reading the MAC
What is LMVP?
> address. Stop once the address registers change to another valid Ethernet
> address, allowing firmware a bounded window to complete the passthrough
> update.
What are the downsides of this approach? Longer reset times?
Please add instructions how to test this.
> Signed-off-by: Chia-Lin Kao (AceLan) <acelan.kao@canonical.com>
> ---
> drivers/net/ethernet/intel/igc/igc_main.c | 48 +++++++++++++++++++++++
> 1 file changed, 48 insertions(+)
>
> diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c
> index 2c9e2dfd8499..fa9752ed8bc5 100644
> --- a/drivers/net/ethernet/intel/igc/igc_main.c
> +++ b/drivers/net/ethernet/intel/igc/igc_main.c
> @@ -11,6 +11,7 @@
> #include <net/pkt_sched.h>
> #include <linux/bpf_trace.h>
> #include <net/xdp_sock_drv.h>
> +#include <linux/etherdevice.h>
> #include <linux/pci.h>
> #include <linux/mdio.h>
>
> @@ -69,6 +70,52 @@ static const struct pci_device_id igc_pci_tbl[] = {
>
> MODULE_DEVICE_TABLE(pci, igc_pci_tbl);
>
> +static void igc_read_rar0(struct igc_hw *hw, u8 *addr, u32 *ral, u32 *rah)
> +{
> + *ral = rd32(IGC_RAL(0));
> + *rah = rd32(IGC_RAH(0));
> +
> + addr[0] = *ral & 0xff;
> + addr[1] = (*ral >> 8) & 0xff;
> + addr[2] = (*ral >> 16) & 0xff;
> + addr[3] = (*ral >> 24) & 0xff;
> + addr[4] = *rah & 0xff;
> + addr[5] = (*rah >> 8) & 0xff;
This looks like a common pattern, but there does not seem to be a
generic Linux implementation. Maybe `igc_read_mac_addr()` in
`drivers/net/ethernet/intel/igc/igc_nvm.c` can be used?
> +}
> +
> +static bool igc_is_lmvp_device(struct pci_dev *pdev)
> +{
> + switch (pdev->device) {
> + case IGC_DEV_ID_I225_LMVP:
> + case IGC_DEV_ID_I226_LMVP:
> + return true;
> + default:
> + return false;
> + }
> +}
> +
> +static void igc_wait_for_lmvp_mac_passthrough(struct pci_dev *pdev,
> + struct igc_hw *hw)
> +{
> + u8 addr[ETH_ALEN] __aligned(2);
> + u32 orig_ral, orig_rah;
> + u32 ral, rah;
> + int i;
> +
> + if (!igc_is_lmvp_device(pdev))
> + return;
> +
> + igc_read_rar0(hw, addr, &orig_ral, &orig_rah);
> +
> + for (i = 0; i < 100; i++) {
> + msleep(100);
Up to ten seconds delay(?) sounds excessive. Please elaborate in the
commit message.
> + igc_read_rar0(hw, addr, &ral, &rah);
> + if ((ral != orig_ral || rah != orig_rah) &&
> + is_valid_ether_addr(addr))
> + return;
> + }
No error in case this didn’t work?
> +}
> +
> enum latency_range {
> lowest_latency = 0,
> low_latency = 1,
> @@ -7259,6 +7306,7 @@ static int igc_probe(struct pci_dev *pdev,
> * known good starting state
> */
> hw->mac.ops.reset_hw(hw);
> + igc_wait_for_lmvp_mac_passthrough(pdev, hw);
>
> if (igc_get_flash_presence_i225(hw)) {
> if (hw->nvm.ops.validate(hw) < 0) {
Kind regards,
Paul
^ permalink raw reply [flat|nested] 8+ messages in thread