* Re: [PATCH net-next] selftests/net: packetdrill: add tcp_syncookies_ip6_9k
From: Neal Cardwell @ 2026-04-29 20:47 UTC (permalink / raw)
To: Eric Dumazet
Cc: Jakub Kicinski, David S . Miller, Paolo Abeni, Simon Horman,
Kuniyuki Iwashima, netdev, eric.dumazet
In-Reply-To: <CANn89iJrvSvw2XKvB7LfXc2egeaqC+seiCiKN2USMw9gicqM1w@mail.gmail.com>
On Wed, Apr 29, 2026 at 1:15 PM Eric Dumazet <edumazet@google.com> wrote:
>
> On Wed, Apr 29, 2026 at 10:09 AM Eric Dumazet <edumazet@google.com> wrote:
> >
> > On Wed, Apr 29, 2026 at 9:48 AM Jakub Kicinski <kuba@kernel.org> wrote:
> > >
> > > On Wed, 29 Apr 2026 08:00:36 +0000 Eric Dumazet wrote:
> > > > This test checks syncookie mode is able to reconstruct some
> > > > client options when TCP TS are used:
> > > >
> > > > - wscale option.
> > > > - sackOK.
> > > > - MSS (in a limited way).
> > > > - ECN (not tested, because of limited value).
> > >
> > > We're getting:
> > >
> > > # TAP version 13
> > > # 1..1
> > > # File "/tmp/code_oyWax9", line 223
> > > # assert tcpi_snd_wscale = 10, tcpi_snd_wscale
> > > # ^
> > > # SyntaxError: invalid syntax
> > > # tcp_syncookies_ip6_9k.pkt: error executing code: 'python3' returned non-zero status 1
> > >
> > > do we need to update packetdrill ?
> >
> > No, this should have been
> >
> > assert tcpi_snd_wscale == 10, tcpi_snd_wscale
> >
> > I initially had a value of 8, and decided going to 10 just to make it
> > different than the output wscale value.
> >
> > Sorry for the confusion.
>
> Neal, can you take care of adding TCPI_OPT_SACK to packetdrill ?
>
> Thanks!
>
> diff --git a/gtests/net/packetdrill/code.c b/gtests/net/packetdrill/code.c
> index d90bffabcfe6..6d8d20e64d8c 100644
> --- a/gtests/net/packetdrill/code.c
> +++ b/gtests/net/packetdrill/code.c
> @@ -129,6 +129,7 @@ static void write_symbols(struct code_state *code)
> emit_var(code, "TCPI_OPT_WSCALE", TCPI_OPT_WSCALE);
> emit_var(code, "TCPI_OPT_ECN", TCPI_OPT_ECN);
> emit_var(code, "TCPI_OPT_SYN_DATA", TCPI_OPT_SYN_DATA);
> + emit_var(code, "TCPI_OPT_SACK", TCPI_OPT_SACK);
> #endif /* linux */
> }
Sure! Done. I added all the missing bit values in the following commit:
https://github.com/google/packetdrill/commit/faa0dfb54065118625e169d3111ce09c65b20229
This can be picked up with the following, or similar:
git clone https://github.com/google/packetdrill.git
neal
^ permalink raw reply
* [PATCH net 0/7] ionic: Various bugfixes
From: Eric Joyner @ 2026-04-29 21:00 UTC (permalink / raw)
To: netdev
Cc: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Eric Joyner
Brett's patches mostly fix issues around communication and configuration
between the firmware and the driver; the last two are related and were
found after an incorrectly operating firmware caused Admin Queue
commands to time out. This issue and the rest are fixes to issues that
we've observed internally.
Prabu's patch is critical for enabling PTP/HW timestamping to work
correctly when the completion queue entries are double-sized; the offset
into the descriptor to read the HW timestamp is incorrect when in that
mode.
Brett Creeley (6):
ionic: Allow the first devcmd to trigger deferred probe
ionic: Handle failures from ionic_reset() when relevant
ionic: Fix unexpected dev_cmd failures
ionic: Fix check in ionic_get_link_ext_stats
ionic: fix adminq use-after-free on command timeout
ionic: service adminq CQ before cancelling to avoid false timeouts
Prabu Thayalan (1):
ionic: fix completion descriptor access with 2x desc size
.../ethernet/pensando/ionic/ionic_bus_pci.c | 8 +-
.../ethernet/pensando/ionic/ionic_ethtool.c | 6 +-
.../net/ethernet/pensando/ionic/ionic_lif.c | 4 +-
.../net/ethernet/pensando/ionic/ionic_main.c | 74 +++++++++++++++++--
.../net/ethernet/pensando/ionic/ionic_txrx.c | 27 +++----
5 files changed, 96 insertions(+), 23 deletions(-)
base-commit: e728258debd553c95d2e70f9cd97c9fde27c7130
--
2.17.1
^ permalink raw reply
* [PATCH net 2/7] ionic: Handle failures from ionic_reset() when relevant
From: Eric Joyner @ 2026-04-29 21:00 UTC (permalink / raw)
To: netdev
Cc: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Eric Joyner
In-Reply-To: <20260429210007.40015-1-eric.joyner@amd.com>
From: Brett Creeley <brett.creeley@amd.com>
If ionic_reset() fails, then the device either wasn't ready to be
communicated with, the firmware is down, and/or the devcmd path is
already torn down. For teardown/remove cases we can ignore the
result of ionic_reset(). However, for any setup cases, we should
take the result seriously.
Note, older firmware always returns success for IONIC_CMD_RESET, so this
change will not break those. However, newer firmware may return failure
if the IONIC_CMD_RESET dev cmd fails.
Fixes: 8097a2f3d21a ("ionic: Reset LIF device while restarting LIF")
Signed-off-by: Brett Creeley <brett.creeley@amd.com>
Signed-off-by: Eric Joyner <eric.joyner@amd.com>
---
drivers/net/ethernet/pensando/ionic/ionic_lif.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
index 637e635bbf03..db4bbeda0b29 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
@@ -3473,7 +3473,9 @@ static void ionic_lif_handle_fw_up(struct ionic_lif *lif)
* just need to reanimate it.
*/
ionic_init_devinfo(ionic);
- ionic_reset(ionic);
+ err = ionic_reset(ionic);
+ if (err)
+ goto err_out;
err = ionic_identify(ionic);
if (err)
goto err_out;
--
2.17.1
^ permalink raw reply related
* [PATCH net 3/7] ionic: Fix unexpected dev_cmd failures
From: Eric Joyner @ 2026-04-29 21:00 UTC (permalink / raw)
To: netdev
Cc: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Eric Joyner
In-Reply-To: <20260429210007.40015-1-eric.joyner@amd.com>
From: Brett Creeley <brett.creeley@amd.com>
When polling for a devcmd completion it's possible for the driver to
timeout the command even if the dev_cmd has completed. This can cause
unexpected failures and device probe to fail. Fix this by reading
the dev_cmd's done bit one last time after breaking out of the poll
loop.
Fixes: fbfb8031533c ("ionic: Add hardware init and device commands")
Suggested-by: Neel Patel <neel.patel@amd.com>
Signed-off-by: Brett Creeley <brett.creeley@amd.com>
Signed-off-by: Eric Joyner <eric.joyner@amd.com>
---
drivers/net/ethernet/pensando/ionic/ionic_main.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_main.c b/drivers/net/ethernet/pensando/ionic/ionic_main.c
index 91f89b9ff807..810cef0fec93 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_main.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_main.c
@@ -541,6 +541,10 @@ static int __ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds,
}
duration = jiffies - start_time;
+ /* one final check to prevent unexpected timeout */
+ if (!done)
+ done = ionic_dev_cmd_done(idev);
+
dev_dbg(ionic->dev, "DEVCMD %s (%d) done=%d took %ld secs (%ld jiffies)\n",
ionic_opcode_to_str(opcode), opcode,
done, duration / HZ, duration);
--
2.17.1
^ permalink raw reply related
* [PATCH net 1/7] ionic: Allow the first devcmd to trigger deferred probe
From: Eric Joyner @ 2026-04-29 21:00 UTC (permalink / raw)
To: netdev
Cc: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Eric Joyner
In-Reply-To: <20260429210007.40015-1-eric.joyner@amd.com>
From: Brett Creeley <brett.creeley@amd.com>
The BAR signature can become visible before firmware is ready to process
device commands. In that window the first devcmd may fail even though
the device is present.
Treat a failure of the first devcmd as deferred probe and return
-EPROBE_DEFER, so probe is retried after firmware initialization
completes.
Also reduce log severity for reset-devcmd failures in this path: these
early failures are expected during firmware bring-up and should not emit
the standard devcmd failure messages.
A possible follow-up is to rework ionic_reset() to retry on -EAGAIN and
-EAGAIN/ETIMEDOUT style transient failures, but this change keeps the
current reset flow unchanged.
Fixes: fbfb8031533c ("ionic: Add hardware init and device commands")
Signed-off-by: Brett Creeley <brett.creeley@amd.com>
Signed-off-by: Eric Joyner <eric.joyner@amd.com>
---
drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c | 8 ++++++--
drivers/net/ethernet/pensando/ionic/ionic_main.c | 8 ++++++--
2 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c b/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
index 05f19489ec5c..59ce35404e53 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
@@ -260,7 +260,8 @@ static int ionic_setup_one(struct ionic *ionic)
/* Configure the device */
err = ionic_setup(ionic);
if (err) {
- dev_err(dev, "Cannot setup device: %d, aborting\n", err);
+ if (err != -EPROBE_DEFER)
+ dev_err(dev, "Cannot setup device: %d, aborting\n", err);
goto err_out_clear_pci;
}
pci_set_master(pdev);
@@ -335,8 +336,11 @@ static int ionic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
#endif
err = ionic_setup_one(ionic);
- if (err)
+ if (err) {
+ if (err == -EPROBE_DEFER)
+ dev_info(dev, "Device isn't ready, deferring probe\n");
goto err_out;
+ }
/* Allocate and init the LIF */
err = ionic_lif_size(ionic);
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_main.c b/drivers/net/ethernet/pensando/ionic/ionic_main.c
index 3c5200e2fdb7..91f89b9ff807 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_main.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_main.c
@@ -603,7 +603,11 @@ int ionic_setup(struct ionic *ionic)
err = ionic_dev_setup(ionic);
if (err)
return err;
- ionic_reset(ionic);
+
+ err = ionic_reset(ionic);
+ /* firmware may not be ready to respond yet */
+ if (err == -EAGAIN || err == -ETIMEDOUT)
+ return -EPROBE_DEFER;
return 0;
}
@@ -687,7 +691,7 @@ int ionic_reset(struct ionic *ionic)
mutex_lock(&ionic->dev_cmd_lock);
ionic_dev_cmd_reset(idev);
- err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
+ err = ionic_dev_cmd_wait_nomsg(ionic, DEVCMD_TIMEOUT);
mutex_unlock(&ionic->dev_cmd_lock);
return err;
--
2.17.1
^ permalink raw reply related
* [PATCH net 4/7] ionic: Fix check in ionic_get_link_ext_stats
From: Eric Joyner @ 2026-04-29 21:00 UTC (permalink / raw)
To: netdev
Cc: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Eric Joyner
In-Reply-To: <20260429210007.40015-1-eric.joyner@amd.com>
From: Brett Creeley <brett.creeley@amd.com>
The current check will fail if SR-IOV is not initialized for the
physical function; this is because is_physfn is 0 if sriov_init() isn't
run or fails. Change the check that prevents getting the link down count
to use is_virtfn instead so that VFs don't get this functionality, which
was the original intent.
Fixes: 132b4ebfa090 ("ionic: add support for ethtool extended stat link_down_count")
Signed-off-by: Brett Creeley <brett.creeley@amd.com>
Signed-off-by: Eric Joyner <eric.joyner@amd.com>
---
drivers/net/ethernet/pensando/ionic/ionic_ethtool.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
index 78a802eb159f..296f831a514d 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
@@ -116,8 +116,10 @@ static void ionic_get_link_ext_stats(struct net_device *netdev,
{
struct ionic_lif *lif = netdev_priv(netdev);
- if (lif->ionic->pdev->is_physfn)
- stats->link_down_events = lif->link_down_count;
+ if (lif->ionic->pdev->is_virtfn)
+ return;
+
+ stats->link_down_events = lif->link_down_count;
}
static int ionic_get_link_ksettings(struct net_device *netdev,
--
2.17.1
^ permalink raw reply related
* [PATCH net 5/7] ionic: fix adminq use-after-free on command timeout
From: Eric Joyner @ 2026-04-29 21:00 UTC (permalink / raw)
To: netdev
Cc: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Eric Joyner
In-Reply-To: <20260429210007.40015-1-eric.joyner@amd.com>
From: Brett Creeley <brett.creeley@amd.com>
When ionic_adminq_wait() times out or detects FW reset, it
returns an error to the caller, whose ionic_admin_ctx is typically
on the stack. However, desc_info->ctx in the adminq still points
to that ctx. If ionic_adminq_service() later runs in NAPI context,
it dereferences the stale pointer to copy the completion and call
complete_all(), causing a use-after-free.
The timeout path partially addressed this via ionic_adminq_flush()
in ionic_adminq_check_err(), which NULLs all pending desc_info->ctx
entries. But there is a race window between the timeout detection
and the flush where NAPI could fire and access the stale ctx. The
FW reset path had no protection at all and returned directly
without clearing desc_info->ctx.
Add ionic_adminq_cancel() which takes adminq_lock and NULLs
desc_info->ctx for the specific context being cancelled. This
coordinates with ionic_adminq_service() which also runs under the
same lock. Call it from both error paths in ionic_adminq_wait()
before returning.
Fixes: 938962d55229 ("ionic: Add adminq action")
Assisted-by: Claude:claude-opus-4.6
Signed-off-by: Brett Creeley <brett.creeley@amd.com>
Signed-off-by: Eric Joyner <eric.joyner@amd.com>
---
.../net/ethernet/pensando/ionic/ionic_main.c | 30 +++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_main.c b/drivers/net/ethernet/pensando/ionic/ionic_main.c
index 810cef0fec93..0971ca4d6650 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_main.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_main.c
@@ -190,6 +190,32 @@ static const char *ionic_opcode_to_str(enum ionic_cmd_opcode opcode)
}
}
+static void ionic_adminq_cancel(struct ionic_lif *lif,
+ struct ionic_admin_ctx *ctx)
+{
+ struct ionic_admin_desc_info *desc_info;
+ unsigned long irqflags;
+ struct ionic_queue *q;
+ int i;
+
+ spin_lock_irqsave(&lif->adminq_lock, irqflags);
+ if (!lif->adminqcq) {
+ spin_unlock_irqrestore(&lif->adminq_lock, irqflags);
+ return;
+ }
+
+ q = &lif->adminqcq->q;
+
+ for (i = 0; i < q->num_descs; i++) {
+ desc_info = &q->admin_info[i];
+ if (desc_info->ctx == ctx) {
+ desc_info->ctx = NULL;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&lif->adminq_lock, irqflags);
+}
+
static void ionic_adminq_flush(struct ionic_lif *lif)
{
struct ionic_admin_desc_info *desc_info;
@@ -448,6 +474,7 @@ int ionic_adminq_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx,
if (do_msg)
netdev_warn(netdev, "%s (%d) interrupted, FW in reset\n",
name, ctx->cmd.cmd.opcode);
+ ionic_adminq_cancel(lif, ctx);
ctx->comp.comp.status = IONIC_RC_ERROR;
return -ENXIO;
}
@@ -458,6 +485,9 @@ int ionic_adminq_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx,
dev_dbg(lif->ionic->dev, "%s: elapsed %d msecs\n",
__func__, jiffies_to_msecs(time_done - time_start));
+ if (time_after_eq(time_done, time_limit))
+ ionic_adminq_cancel(lif, ctx);
+
return ionic_adminq_check_err(lif, ctx,
time_after_eq(time_done, time_limit),
do_msg);
--
2.17.1
^ permalink raw reply related
* [PATCH net 6/7] ionic: service adminq CQ before cancelling to avoid false timeouts
From: Eric Joyner @ 2026-04-29 21:00 UTC (permalink / raw)
To: netdev
Cc: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Eric Joyner
In-Reply-To: <20260429210007.40015-1-eric.joyner@amd.com>
From: Brett Creeley <brett.creeley@amd.com>
When ionic_adminq_wait() hits its timeout, it's possible the
firmware has already written the completion to the CQ but
ionic_adminq_service() was never scheduled via NAPI to process it.
In this case the command is falsely reported as timed out even
though it completed successfully.
Fix this by renaming ionic_adminq_cancel() to
ionic_adminq_service_or_cancel() and having it call
ionic_cq_service() under the adminq_lock before checking whether
the context needs to be cancelled. ionic_cq_service() invokes
ionic_adminq_service() which will process any pending CQ entries,
copy the completion data, call complete_all(), and clear
desc_info->ctx for completed commands.
After servicing, check completion_done() on the ctx. If the
completion was found and processed, return false (not cancelled)
so ionic_adminq_wait() can use the actual firmware result instead
of reporting a false timeout. If the completion was not found,
fall through to the existing cancel path that NULLs desc_info->ctx
and return true (cancelled).
Fixes: 938962d55229 ("ionic: Add adminq action")
Assisted-by: Claude:claude-opus-4.6
Signed-off-by: Brett Creeley <brett.creeley@amd.com>
Signed-off-by: Eric Joyner <eric.joyner@amd.com>
---
.../net/ethernet/pensando/ionic/ionic_main.c | 42 +++++++++++++++----
1 file changed, 34 insertions(+), 8 deletions(-)
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_main.c b/drivers/net/ethernet/pensando/ionic/ionic_main.c
index 0971ca4d6650..708c7e4c578b 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_main.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_main.c
@@ -190,8 +190,8 @@ static const char *ionic_opcode_to_str(enum ionic_cmd_opcode opcode)
}
}
-static void ionic_adminq_cancel(struct ionic_lif *lif,
- struct ionic_admin_ctx *ctx)
+static bool ionic_adminq_service_or_cancel(struct ionic_lif *lif,
+ struct ionic_admin_ctx *ctx)
{
struct ionic_admin_desc_info *desc_info;
unsigned long irqflags;
@@ -201,9 +201,29 @@ static void ionic_adminq_cancel(struct ionic_lif *lif,
spin_lock_irqsave(&lif->adminq_lock, irqflags);
if (!lif->adminqcq) {
spin_unlock_irqrestore(&lif->adminq_lock, irqflags);
- return;
+ return true;
+ }
+
+ /* Service the CQ to pick up any completions that the FW has
+ * sent but NAPI hasn't processed yet. This will call
+ * complete_all() on any matching contexts, including ours.
+ */
+ ionic_cq_service(&lif->adminqcq->cq, lif->adminqcq->cq.num_descs,
+ ionic_adminq_service, NULL, NULL);
+
+ /* If the completion was serviced above, the ctx will have been
+ * completed and its desc_info->ctx cleared by
+ * ionic_adminq_service(). Check and return not-cancelled.
+ */
+ if (completion_done(&ctx->work)) {
+ spin_unlock_irqrestore(&lif->adminq_lock, irqflags);
+ return false;
}
+ /* The command is still pending, cancel it by clearing
+ * desc_info->ctx so ionic_adminq_service() won't touch
+ * the caller's ctx after we return.
+ */
q = &lif->adminqcq->q;
for (i = 0; i < q->num_descs; i++) {
@@ -214,6 +234,8 @@ static void ionic_adminq_cancel(struct ionic_lif *lif,
}
}
spin_unlock_irqrestore(&lif->adminq_lock, irqflags);
+
+ return true;
}
static void ionic_adminq_flush(struct ionic_lif *lif)
@@ -444,6 +466,7 @@ int ionic_adminq_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx,
unsigned long time_start;
unsigned long time_done;
unsigned long remaining;
+ bool timed_out = false;
const char *name;
name = ionic_opcode_to_str(ctx->cmd.cmd.opcode);
@@ -474,7 +497,7 @@ int ionic_adminq_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx,
if (do_msg)
netdev_warn(netdev, "%s (%d) interrupted, FW in reset\n",
name, ctx->cmd.cmd.opcode);
- ionic_adminq_cancel(lif, ctx);
+ ionic_adminq_service_or_cancel(lif, ctx);
ctx->comp.comp.status = IONIC_RC_ERROR;
return -ENXIO;
}
@@ -485,12 +508,15 @@ int ionic_adminq_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx,
dev_dbg(lif->ionic->dev, "%s: elapsed %d msecs\n",
__func__, jiffies_to_msecs(time_done - time_start));
+ /* If the wait timed out, attempt to service the CQ and cancel
+ * the ctx. If ionic_adminq_service() completed the ctx between
+ * timeout detection and taking the lock, cancel returns false
+ * and we avoid a false timeout.
+ */
if (time_after_eq(time_done, time_limit))
- ionic_adminq_cancel(lif, ctx);
+ timed_out = ionic_adminq_service_or_cancel(lif, ctx);
- return ionic_adminq_check_err(lif, ctx,
- time_after_eq(time_done, time_limit),
- do_msg);
+ return ionic_adminq_check_err(lif, ctx, timed_out, do_msg);
}
static int __ionic_adminq_post_wait(struct ionic_lif *lif,
--
2.17.1
^ permalink raw reply related
* [PATCH net 7/7] ionic: fix completion descriptor access with 2x desc size
From: Eric Joyner @ 2026-04-29 21:00 UTC (permalink / raw)
To: netdev
Cc: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Eric Joyner, Prabu Thayalan
In-Reply-To: <20260429210007.40015-1-eric.joyner@amd.com>
From: Prabu Thayalan <prabu.ponrajthayalan@amd.com>
The old ionic_rx_service() and ionic_tx_service() used array
indexing to access completion descriptors:
comp = &((struct ionic_rxq_comp *)cq->base)[cq->tail_idx];
This assumes the stride is sizeof(struct ionic_rxq_comp) = 16 bytes.
However, when the IONIC_Q_F_2X_CQ_DESC flag is set, the actual
completion descriptor size is 32 bytes (2 * sizeof(comp)), and the
completion itself is located at the end of that 32-byte slot. Array
indexing with a 16-byte stride would access the wrong offset.
Use pointer arithmetic that accounts for the actual descriptor size
from cq->desc_size:
comp = cq->base +
cq->desc_size * cq->tail_idx +
cq->desc_size - sizeof(*comp);
This correctly calculates the completion location regardless of
descriptor size. For the common case where desc_size equals
sizeof(*comp), use array indexing in a likely() fast path to avoid
performance regression.
Fixes: 0ec9f6669a7d ("ionic: add handling of larger descriptors")
Signed-off-by: Prabu Thayalan <prabu.ponrajthayalan@amd.com>
Signed-off-by: Eric Joyner <eric.joyner@amd.com>
---
.../net/ethernet/pensando/ionic/ionic_txrx.c | 27 ++++++++++---------
1 file changed, 14 insertions(+), 13 deletions(-)
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c
index 301ebee2fdc5..27a113d63d28 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c
@@ -701,11 +701,7 @@ static void ionic_rx_clean(struct ionic_queue *q,
__le64 *cq_desc_hwstamp;
u64 hwstamp;
- cq_desc_hwstamp =
- (void *)comp +
- qcq->cq.desc_size -
- sizeof(struct ionic_rxq_comp) -
- IONIC_HWSTAMP_CQ_NEGOFFSET;
+ cq_desc_hwstamp = (void *)comp - IONIC_HWSTAMP_CQ_NEGOFFSET;
hwstamp = le64_to_cpu(*cq_desc_hwstamp);
@@ -729,7 +725,12 @@ static bool __ionic_rx_service(struct ionic_cq *cq, struct bpf_prog *xdp_prog)
struct ionic_queue *q = cq->bound_q;
struct ionic_rxq_comp *comp;
- comp = &((struct ionic_rxq_comp *)cq->base)[cq->tail_idx];
+ if (likely(cq->desc_size == sizeof(*comp)))
+ comp = &((struct ionic_rxq_comp *)cq->base)[cq->tail_idx];
+ else
+ comp = cq->base +
+ cq->desc_size * cq->tail_idx +
+ cq->desc_size - sizeof(*comp);
if (!color_match(comp->pkt_type_color, cq->done_color))
return false;
@@ -1180,7 +1181,6 @@ static void ionic_tx_clean(struct ionic_queue *q,
bool in_napi)
{
struct ionic_tx_stats *stats = q_to_tx_stats(q);
- struct ionic_qcq *qcq = q_to_qcq(q);
struct sk_buff *skb;
if (desc_info->xdpf) {
@@ -1205,11 +1205,7 @@ static void ionic_tx_clean(struct ionic_queue *q,
__le64 *cq_desc_hwstamp;
u64 hwstamp;
- cq_desc_hwstamp =
- (void *)comp +
- qcq->cq.desc_size -
- sizeof(struct ionic_txq_comp) -
- IONIC_HWSTAMP_CQ_NEGOFFSET;
+ cq_desc_hwstamp = (void *)comp - IONIC_HWSTAMP_CQ_NEGOFFSET;
hwstamp = le64_to_cpu(*cq_desc_hwstamp);
@@ -1244,7 +1240,12 @@ static bool ionic_tx_service(struct ionic_cq *cq,
unsigned int pkts = 0;
u16 index;
- comp = &((struct ionic_txq_comp *)cq->base)[cq->tail_idx];
+ if (likely(cq->desc_size == sizeof(*comp)))
+ comp = &((struct ionic_txq_comp *)cq->base)[cq->tail_idx];
+ else
+ comp = cq->base +
+ cq->desc_size * cq->tail_idx +
+ cq->desc_size - sizeof(*comp);
if (!color_match(comp->color, cq->done_color))
return false;
--
2.17.1
^ permalink raw reply related
* Re: [PATCH 0/2] netfilter: fix NULL ops race in iptable lazy init
From: Tristan Madani @ 2026-04-29 21:03 UTC (permalink / raw)
To: Phil Sutter
Cc: Pablo Neira Ayuso, Florian Westphal, netfilter-devel, netdev,
stable, linux-kernel
In-Reply-To: <20260429175613.1459342-1-tristmd@gmail.com>
On Wed, 30 Apr 2026 Phil Sutter wrote:
> Is this true? Your patch moves the ops allocation, but new_table->ops is
> still assigned after xt_register_table() has returned. AIUI, the race
> window is just reduced, not eliminated.
You are right -- I missed that new_table->ops is assigned after
xt_register_table() returns. The table becomes visible via list_add()
inside xt_register_table(), but the ops pointer is still NULL at that
point. Moving the allocation alone does not close the window.
We cannot assign ops before xt_register_table() because we need the
returned new_table pointer to set ops[i].priv.
Would a V2 that guards the pre_exit path instead be acceptable?
Something like:
void ipt_unregister_table_pre_exit(struct net *net, const char *name)
{
struct xt_table *table = xt_find_table(net, NFPROTO_IPV4, name);
if (table && table->ops)
nf_unregister_net_hooks(net, table->ops,
hweight32(table->valid_hooks));
}
This way cleanup_net simply skips the table if ops has not been assigned
yet. The register path will either complete and call
nf_register_net_hooks() normally, or fail and clean up via
__ipt_unregister_table().
Thanks,
Tristan
^ permalink raw reply
* Re: [PATCH net-next v9 0/4] tun/tap & vhost-net: apply qdisc backpressure on full ptr_ring to reduce TX drops
From: Simon Schippers @ 2026-04-29 21:04 UTC (permalink / raw)
To: willemdebruijn.kernel, jasowang, andrew+netdev, davem, edumazet,
kuba, pabeni, mst, eperezma, leiyang, stephen, jon, tim.gebauer,
netdev, linux-kernel, kvm, virtualization
In-Reply-To: <20260428123859.19578-1-simon.schippers@tu-dortmund.de>
I just saw that the Sashiko AI review found some valid regressions.
I will update and test the patch set accordingly and send a new
version...
^ permalink raw reply
* Re: [PATCH net v1 1/3] net: introduce helper to resolve hardware timestamps from skb
From: Willem de Bruijn @ 2026-04-29 21:04 UTC (permalink / raw)
To: Kohei Enju, netdev
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Kuniyuki Iwashima, Willem de Bruijn, David Ahern,
Neal Cardwell, Gerhard Engleder, Jonathan Lemon, Richard Cochran,
Kohei Enju
In-Reply-To: <20260429091632.26509-2-kohei@enjuk.jp>
Kohei Enju wrote:
> Move the logic that resolves a hardware timestamp from an skb, including
> late timestamp resolution via netdev_get_tstamp(), from net/socket.c to
> a common helper.
>
> Let's allow other networking code to reuse the same resolution path.
>
> Signed-off-by: Kohei Enju <kohei@enjuk.jp>
Thanks for the fix series.
Fixes require a Fixes tag. See also https://www.kernel.org/doc/html/latest/process/maintainer-netdev.html.
I suggest merging this and the second patch, as this is not a
standalone fix, and the other is a one-line change.
> ---
> include/linux/skbuff.h | 11 +++++++++++
> net/core/skbuff.c | 27 +++++++++++++++++++++++++++
> net/socket.c | 27 +++------------------------
> 3 files changed, 41 insertions(+), 24 deletions(-)
>
> diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
> index 2bcf78a4de7b..651a5ae8b11c 100644
> --- a/include/linux/skbuff.h
> +++ b/include/linux/skbuff.h
> @@ -4731,6 +4731,17 @@ void __skb_tstamp_tx(struct sk_buff *orig_skb, const struct sk_buff *ack_skb,
> void skb_tstamp_tx(struct sk_buff *orig_skb,
> struct skb_shared_hwtstamps *hwtstamps);
>
> +/**
> + * skb_get_hwtstamp - resolve a hardware timestamp from an skb
> + * @skb: skb carrying the timestamp
> + * @cycles: true to request the free-running cycle-based timestamp
> + * @if_index: optional return pointer for the originating netdev ifindex
> + *
> + * Return: resolved hardware timestamp, or the stored skb hwtstamp when no
> + * device-specific late timestamp resolution is needed.
> + */
> +ktime_t skb_get_hwtstamp(struct sk_buff *skb, bool cycles, int *if_index);
> +
> /**
> * skb_tx_timestamp() - Driver hook for transmit timestamping
> *
> diff --git a/net/core/skbuff.c b/net/core/skbuff.c
> index 7dad68e3b518..d11f4e2e9391 100644
> --- a/net/core/skbuff.c
> +++ b/net/core/skbuff.c
> @@ -5729,6 +5729,33 @@ void skb_tstamp_tx(struct sk_buff *orig_skb,
> }
> EXPORT_SYMBOL_GPL(skb_tstamp_tx);
>
> +ktime_t skb_get_hwtstamp(struct sk_buff *skb, bool cycles, int *if_index)
> +{
> + struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb);
> + struct net_device *orig_dev;
> + ktime_t hwtstamp;
> +
> + if (if_index)
> + *if_index = 0;
> +
> + if (!(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP_NETDEV))
> + return shhwtstamps->hwtstamp;
> +
> + rcu_read_lock();
> + orig_dev = dev_get_by_napi_id(skb_napi_id(skb));
> + if (orig_dev) {
> + if (if_index)
> + *if_index = orig_dev->ifindex;
> + hwtstamp = netdev_get_tstamp(orig_dev, shhwtstamps, cycles);
> + } else {
> + hwtstamp = shhwtstamps->hwtstamp;
> + }
> + rcu_read_unlock();
> +
> + return hwtstamp;
> +}
> +EXPORT_SYMBOL_GPL(skb_get_hwtstamp);
> +
> #ifdef CONFIG_WIRELESS
> void skb_complete_wifi_ack(struct sk_buff *skb, bool acked)
> {
> diff --git a/net/socket.c b/net/socket.c
> index 22a412fdec07..95b21b16a0fc 100644
> --- a/net/socket.c
> +++ b/net/socket.c
> @@ -876,21 +876,7 @@ static bool skb_is_swtx_tstamp(const struct sk_buff *skb, int false_tstamp)
> static ktime_t get_timestamp(struct sock *sk, struct sk_buff *skb, int *if_index)
> {
> bool cycles = READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_BIND_PHC;
> - struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb);
> - struct net_device *orig_dev;
> - ktime_t hwtstamp;
> -
> - rcu_read_lock();
> - orig_dev = dev_get_by_napi_id(skb_napi_id(skb));
> - if (orig_dev) {
> - *if_index = orig_dev->ifindex;
> - hwtstamp = netdev_get_tstamp(orig_dev, shhwtstamps, cycles);
> - } else {
> - hwtstamp = shhwtstamps->hwtstamp;
> - }
> - rcu_read_unlock();
> -
> - return hwtstamp;
> + return skb_get_hwtstamp(skb, cycles, if_index);
> }
At this point simpler to remove get_timestamp entirely. It's an
unnecessary layer of indirection.
Perhaps pass sk_tsflags rather than bool to skb_get_hwtstamp.
> static void put_ts_pktinfo(struct msghdr *msg, struct sk_buff *skb,
> @@ -940,7 +926,6 @@ int skb_get_tx_timestamp(struct sk_buff *skb, struct sock *sk,
> {
> u32 tsflags = READ_ONCE(sk->sk_tsflags);
> ktime_t hwtstamp;
> - int if_index = 0;
>
> if ((tsflags & SOF_TIMESTAMPING_SOFTWARE) &&
> ktime_to_timespec64_cond(skb->tstamp, ts))
> @@ -950,10 +935,7 @@ int skb_get_tx_timestamp(struct sk_buff *skb, struct sock *sk,
> skb_is_swtx_tstamp(skb, false))
> return -ENOENT;
>
> - if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP_NETDEV)
> - hwtstamp = get_timestamp(sk, skb, &if_index);
> - else
> - hwtstamp = skb_hwtstamps(skb)->hwtstamp;
> + hwtstamp = get_timestamp(sk, skb, NULL);
>
> if (tsflags & SOF_TIMESTAMPING_BIND_PHC)
> hwtstamp = ptp_convert_timestamp(&hwtstamp,
> @@ -1033,10 +1015,7 @@ void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk,
> !(tsflags & SOF_TIMESTAMPING_OPT_RX_FILTER))) &&
> !skb_is_swtx_tstamp(skb, false_tstamp)) {
> if_index = 0;
> - if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP_NETDEV)
> - hwtstamp = get_timestamp(sk, skb, &if_index);
> - else
> - hwtstamp = shhwtstamps->hwtstamp;
> + hwtstamp = get_timestamp(sk, skb, &if_index);
>
> if (tsflags & SOF_TIMESTAMPING_BIND_PHC)
> hwtstamp = ptp_convert_timestamp(&hwtstamp,
> --
> 2.53.0
>
^ permalink raw reply
* Re: [PATCH net v1 3/3] tcp: use skb_get_hwtstamp() for hardware timestamps
From: Willem de Bruijn @ 2026-04-29 21:09 UTC (permalink / raw)
To: Kohei Enju, netdev
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Kuniyuki Iwashima, Willem de Bruijn, David Ahern,
Neal Cardwell, Gerhard Engleder, Jonathan Lemon, Richard Cochran,
Kohei Enju
In-Reply-To: <20260429091632.26509-4-kohei@enjuk.jp>
Kohei Enju wrote:
> Since commit 97dc7cd92ac6 ("ptp: Support late timestamp determination"),
> skb_shared_hwtstamps may contain netdev_data instead of hwtstamp. TCP
> receive timestamping can then interpret the stored value as a ktime_t
> and report bogus hardware timestamps to userspace.
>
> Use skb_get_hwtstamp() instead of reading hwtstamp directly, so TCP
> sockets follow the same hardware timestamp resolution path as the socket
> layer. When coalescing SKBs, resolve late timestamps before copying them
> to the merged skb.
Why? Does this preclude supporting SOF_TIMESTAMPING_BIND_PHC for such
sockets at a later time?
It is just as easy to coalesce the cookie as the htwtstamp.
That also avoids the need to mask out SKBTX_HW_TSTAMP_NETDEV.
> Additionally, recognize SKBTX_HW_TSTAMP_NETDEV as
> indicating a receive timestamp is present.
What is the reason for this extra condition?
> Note that skb_get_hwtstamp() is called with cycles == false, since TCP
> hasn't honored SOF_TIMESTAMPING_BIND_PHC so far, and this patch doesn't
> change that behavior.
>
> Fixes: 97dc7cd92ac6 ("ptp: Support late timestamp determination")
> Signed-off-by: Kohei Enju <kohei@enjuk.jp>
> ---
> include/net/tcp.h | 2 +-
> net/ipv4/tcp_input.c | 3 ++-
> net/ipv4/tcp_ipv4.c | 6 ++++--
> net/ipv6/tcp_ipv6.c | 3 ++-
> 4 files changed, 9 insertions(+), 5 deletions(-)
>
> diff --git a/include/net/tcp.h b/include/net/tcp.h
> index ecbadcb3a744..7b5fcee97079 100644
> --- a/include/net/tcp.h
> +++ b/include/net/tcp.h
> @@ -524,7 +524,7 @@ tcp_update_recv_tstamps(struct sk_buff *skb,
> struct scm_timestamping_internal *tss)
> {
> tss->ts[0] = skb->tstamp;
> - tss->ts[2] = skb_hwtstamps(skb)->hwtstamp;
> + tss->ts[2] = skb_get_hwtstamp(skb, false, NULL);
> }
>
> void tcp_recv_timestamp(struct msghdr *msg, const struct sock *sk,
> diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
> index d5c9e65d9760..9fd473559b58 100644
> --- a/net/ipv4/tcp_input.c
> +++ b/net/ipv4/tcp_input.c
> @@ -5237,7 +5237,8 @@ static bool tcp_try_coalesce(struct sock *sk,
> if (TCP_SKB_CB(from)->has_rxtstamp) {
> TCP_SKB_CB(to)->has_rxtstamp = true;
> to->tstamp = from->tstamp;
> - skb_hwtstamps(to)->hwtstamp = skb_hwtstamps(from)->hwtstamp;
> + skb_hwtstamps(to)->hwtstamp = skb_get_hwtstamp(from, false, NULL);
> + skb_shinfo(to)->tx_flags &= ~SKBTX_HW_TSTAMP_NETDEV;
> }
>
> return true;
> diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
> index 8fc24c3743c5..c35d82317764 100644
> --- a/net/ipv4/tcp_ipv4.c
> +++ b/net/ipv4/tcp_ipv4.c
> @@ -1993,7 +1993,8 @@ enum skb_drop_reason tcp_add_backlog(struct sock *sk, struct sk_buff *skb)
> if (TCP_SKB_CB(skb)->has_rxtstamp) {
> TCP_SKB_CB(tail)->has_rxtstamp = true;
> tail->tstamp = skb->tstamp;
> - skb_hwtstamps(tail)->hwtstamp = skb_hwtstamps(skb)->hwtstamp;
> + skb_hwtstamps(tail)->hwtstamp = skb_get_hwtstamp(skb, false, NULL);
> + skb_shinfo(tail)->tx_flags &= ~SKBTX_HW_TSTAMP_NETDEV;
> }
>
> /* Not as strict as GRO. We only need to carry mss max value */
> @@ -2062,7 +2063,8 @@ static void tcp_v4_fill_cb(struct sk_buff *skb, const struct iphdr *iph,
> TCP_SKB_CB(skb)->ip_dsfield = ipv4_get_dsfield(iph);
> TCP_SKB_CB(skb)->sacked = 0;
> TCP_SKB_CB(skb)->has_rxtstamp =
> - skb->tstamp || skb_hwtstamps(skb)->hwtstamp;
> + skb->tstamp || skb_hwtstamps(skb)->hwtstamp ||
> + (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP_NETDEV);
> }
>
> /*
> diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
> index 2c3f7a739709..3361ed7f94de 100644
> --- a/net/ipv6/tcp_ipv6.c
> +++ b/net/ipv6/tcp_ipv6.c
> @@ -1704,7 +1704,8 @@ static void tcp_v6_fill_cb(struct sk_buff *skb, const struct ipv6hdr *hdr,
> TCP_SKB_CB(skb)->ip_dsfield = ipv6_get_dsfield(hdr);
> TCP_SKB_CB(skb)->sacked = 0;
> TCP_SKB_CB(skb)->has_rxtstamp =
> - skb->tstamp || skb_hwtstamps(skb)->hwtstamp;
> + skb->tstamp || skb_hwtstamps(skb)->hwtstamp ||
> + (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP_NETDEV);
> }
>
> INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb)
> --
> 2.53.0
>
^ permalink raw reply
* [PATCH net-next] Documentation/tcp_ao: Document the supported MAC algorithms and lengths
From: Eric Biggers @ 2026-04-29 21:08 UTC (permalink / raw)
To: netdev
Cc: linux-crypto, linux-kernel, Eric Dumazet, Neal Cardwell,
Kuniyuki Iwashima, David S . Miller, David Ahern, Jakub Kicinski,
Paolo Abeni, Simon Horman, Ard Biesheuvel, Jason A . Donenfeld,
Herbert Xu, Dmitry Safonov, Eric Biggers
Update the TCP-AO documentation to fix some incorrect terminology and
claims regarding the MAC algorithms, and document which MAC algorithms
and lengths the Linux implementation supports.
Signed-off-by: Eric Biggers <ebiggers@kernel.org>
---
Documentation/networking/tcp_ao.rst | 38 ++++++++++++++++++++---------
1 file changed, 27 insertions(+), 11 deletions(-)
diff --git a/Documentation/networking/tcp_ao.rst b/Documentation/networking/tcp_ao.rst
index d5b6d0df63c3..55304037aa81 100644
--- a/Documentation/networking/tcp_ao.rst
+++ b/Documentation/networking/tcp_ao.rst
@@ -5,32 +5,34 @@ TCP Authentication Option Linux implementation (RFC5925)
========================================================
TCP Authentication Option (TCP-AO) provides a TCP extension aimed at verifying
segments between trusted peers. It adds a new TCP header option with
a Message Authentication Code (MAC). MACs are produced from the content
-of a TCP segment using a hashing function with a password known to both peers.
+of a TCP segment using a key known to both peers.
The intent of TCP-AO is to deprecate TCP-MD5 providing better security,
-key rotation and support for a variety of hashing algorithms.
+key rotation and support for a variety of MAC algorithms.
1. Introduction
===============
.. table:: Short and Limited Comparison of TCP-AO and TCP-MD5
+----------------------+------------------------+-----------------------+
| | TCP-MD5 | TCP-AO |
+======================+========================+=======================+
- |Supported hashing |MD5 |Must support HMAC-SHA1 |
- |algorithms |(cryptographically weak)|(chosen-prefix attacks)|
- | | |and CMAC-AES-128 (only |
- | | |side-channel attacks). |
- | | |May support any hashing|
- | | |algorithm. |
+ |Supported MAC |MD5 of data and key |HMAC-SHA-1-96 and |
+ |algorithms |(cryptographically weak)|AES-128-CMAC-96. |
+ | | |Implementations are |
+ | | |permitted to support |
+ | | |additional algorithms. |
+----------------------+------------------------+-----------------------+
- |Length of MACs (bytes)|16 |Typically 12-16. |
- | | |Other variants that fit|
- | | |TCP header permitted. |
+ |Length of MACs (bytes)|16 |12 for HMAC-SHA-1-96 |
+ | | |and AES-128-CMAC-96. |
+ | | |Implementations are |
+ | | |permitted to support |
+ | | |any MAC length that |
+ | | |fits in the TCP header.|
+----------------------+------------------------+-----------------------+
|Number of keys per |1 |Many |
|TCP connection | | |
+----------------------+------------------------+-----------------------+
|Possibility to change |Non-practical (both |Supported by protocol |
@@ -294,10 +296,24 @@ Linux provides a set of ``setsockopt()s`` and ``getsockopt()s`` that let
userspace manage TCP-AO on a per-socket basis. In order to add/delete MKTs
``TCP_AO_ADD_KEY`` and ``TCP_AO_DEL_KEY`` TCP socket options must be used.
It is not allowed to add a key on an established non-TCP-AO connection
as well as to remove the last key from TCP-AO connection.
+``TCP_AO_ADD_KEY`` allows the MAC algorithm and MAC length to be selected.
+Linux supports the mandatory-to-implement algorithms HMAC-SHA-1-96 and
+AES-128-CMAC-96. In addition, as Linux extensions, it supports:
+
+- HMAC-SHA256. Linux uses HMAC-SHA256 in the same way as HMAC-SHA1; this
+ includes omitting an explicit entropy extraction step. To work around the
+ missing entropy extraction, users should provide keys with full entropy. The
+ implementation is interoperable with other implementations of HMAC-SHA256 for
+ TCP-AO only when they have implemented the key derivation the same way (and
+ also the same MAC length is selected on each side).
+
+- Any MAC length for any of the supported MAC algorithms, provided it fits in
+ the TCP header and is at least 4 bytes.
+
``setsockopt(TCP_AO_DEL_KEY)`` command may specify ``tcp_ao_del::current_key``
+ ``tcp_ao_del::set_current`` and/or ``tcp_ao_del::rnext``
+ ``tcp_ao_del::set_rnext`` which makes such delete "forced": it
provides userspace a way to delete a key that's being used and atomically set
another one instead. This is not intended for normal use and should be used
base-commit: 09942ddedcb960f9e78fd817ec33f501d1040c5b
--
2.54.0.545.g6539524ca2-goog
^ permalink raw reply related
* Re: [PATCH net-next v2 1/5] net/tcp-ao: Drop support for most non-RFC-specified algorithms
From: Eric Biggers @ 2026-04-29 21:11 UTC (permalink / raw)
To: Simon Horman
Cc: netdev, linux-crypto, linux-kernel, edumazet, ncardwell, kuniyu,
davem, dsahern, kuba, pabeni, ardb, Jason, herbert, 0x7f454c46
In-Reply-To: <20260429194456.GA621449@google.com>
On Wed, Apr 29, 2026 at 07:44:56PM +0000, Eric Biggers wrote:
> > One more question, on the commit message and documentation rather than the
> > diff: Documentation/networking/tcp_ao.rst still describes TCP-AO as "May
> > support any hashing algorithm"
>
> That "May support any hashing algorithm" statement has always been
> incorrect, so I wouldn't pay much attention to it. It also appears in a
> table describing TCP-AO as a protocol, not the kernel's implementation.
>
> > and does not mention the newly enforced
> > whitelist or the -ENOENT failure mode. Should tcp_ao.rst be updated in
> > this patch to list the accepted algorithm strings and the rationale (e.g.
> > the 20-byte TCP option MAC cap), so userspace has a documented contract?
>
> As stated in the commit message, the list of MAC algorithms supported by
> the kernel's implementation of TCP-AO has always been undocumented. It
> should be documented, but I would suggest documentation improvements
> belong in a separate patch.
The missing documentation is added in
https://lore.kernel.org/netdev/20260429210856.725667-1-ebiggers@kernel.org/
- Eric
^ permalink raw reply
* [PATCH net-next v3] net: phy: realtek: support MDI swapping for RTL8226-CG
From: Jan Hoffmann @ 2026-04-29 21:11 UTC (permalink / raw)
To: Andrew Lunn, Heiner Kallweit, Russell King, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Daniel Golle,
Markus Stockhausen, Damien Dejean
Cc: netdev, linux-kernel, Jan Hoffmann
Add support for configuring swapping of MDI pairs (ABCD->DCBA) when the
property "enet-phy-pair-order" is specified.
Unfortunately, no documentation about this feature is available, but
this implementation still tries to avoid magic numbers and raw register
numbers where it seems clear what is going on.
As it is unknown whether the patching step can be safely reversed, only
enabling MDI swapping is fully supported. A value of "0" for the "enet-
phy-pair-order" property is not accepted if the PHY has already been
patched for MDI swapping (however, this should not occur in practice).
Some other Realtek PHYs also support similar mechanisms:
- RTL8221B-VB-CG allows to configure MDI swapping via the same register,
but does not need the additional patching step. However, it is unclear
whether a driver implementation for that PHY is necessary, as it is
known to support configuration via strapping pins (which is working
fine at least in Zyxel XGS1210-12 rev B1).
- The patching step seems to match the one for the integrated PHYs of
some Realtek PCIe/USB NICs (see for example the r8152 driver).
For now, only implement this for the RTL8226-CG PHY, where it is needed
for the switches Zyxel XGS1010-12 rev A1 and XGS1210-12 rev A1.
Signed-off-by: Jan Hoffmann <jan@3e8.eu>
---
v3:
- Accept value of "0" for "enet-phy-pair-order" property.
- Do configuration in .probe instead of .config_init, as it only needs
to be done once and survives soft reset.
v2:
- Added register/field definitions where possible. For some registers,
it seems hard to find a proper name, but I still defined them where
some of the fields are known.
- Introduced new helper function to swap RG_LPF_CAP and RG_LPF_CAP_XG
values, in order to reduce duplication.
drivers/net/phy/realtek/realtek_main.c | 154 +++++++++++++++++++++++++
1 file changed, 154 insertions(+)
diff --git a/drivers/net/phy/realtek/realtek_main.c b/drivers/net/phy/realtek/realtek_main.c
index 79c867ef64da..57c01ecdaabc 100644
--- a/drivers/net/phy/realtek/realtek_main.c
+++ b/drivers/net/phy/realtek/realtek_main.c
@@ -189,6 +189,21 @@
#define RTL8224_VND1_MDI_PAIR_SWAP 0xa90
#define RTL8224_VND1_MDI_POLARITY_SWAP 0xa94
+#define RTL8226_VND1_UNKNOWN_6A21 0x6a21
+#define RTL8226_VND1_UNKNOWN_6A21_MDI_SWAP_EN BIT(5)
+
+#define RTL8226_VND2_UNKNOWN_D068 0xd068
+#define RTL8226_VND2_UNKNOWN_D068_MDI_SWAP_FLAG BIT(1)
+#define RTL8226_VND2_UNKNOWN_D068_PAIR_SEL GENMASK(4, 3)
+#define RTL8226_VND2_ADCCAL_OFFSET 0xd06a
+
+#define RTL8226_VND2_RG_LPF_CAP_XG_P0_P1 0xbd5a
+#define RTL8226_VND2_RG_LPF_CAP_XG_P2_P3 0xbd5c
+#define RTL8226_VND2_RG_LPF_CAP_P0_P1 0xbc18
+#define RTL8226_VND2_RG_LPF_CAP_P2_P3 0xbc1a
+#define RTL8226_RG_LPF_CAP_PAIR_A_MASK GENMASK(4, 0)
+#define RTL8226_RG_LPF_CAP_PAIR_B_MASK GENMASK(12, 8)
+
#define RTL8366RB_POWER_SAVE 0x15
#define RTL8366RB_POWER_SAVE_ON BIT(12)
@@ -1468,6 +1483,144 @@ static int rtl822xb_write_mmd(struct phy_device *phydev, int devnum, u16 reg,
return write_ret;
}
+static int rtl8226_set_mdi_swap(struct phy_device *phydev, bool swap_enable)
+{
+ u16 val = swap_enable ? RTL8226_VND1_UNKNOWN_6A21_MDI_SWAP_EN : 0;
+
+ return phy_modify_mmd(phydev, MDIO_MMD_VEND1, RTL8226_VND1_UNKNOWN_6A21,
+ RTL8226_VND1_UNKNOWN_6A21_MDI_SWAP_EN, val);
+}
+
+static int rtl8226_swap_rg_lpf_cap(struct phy_device *phydev, u32 reg_p0_p1, u32 reg_p2_p3)
+{
+ u16 val_p0, val_p1, val_p2, val_p3;
+ int ret;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, reg_p0_p1);
+ if (ret < 0)
+ return ret;
+
+ val_p0 = FIELD_GET(RTL8226_RG_LPF_CAP_PAIR_A_MASK, ret);
+ val_p1 = FIELD_GET(RTL8226_RG_LPF_CAP_PAIR_B_MASK, ret);
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, reg_p2_p3);
+ if (ret < 0)
+ return ret;
+
+ val_p2 = FIELD_GET(RTL8226_RG_LPF_CAP_PAIR_A_MASK, ret);
+ val_p3 = FIELD_GET(RTL8226_RG_LPF_CAP_PAIR_B_MASK, ret);
+
+ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, reg_p0_p1,
+ RTL8226_RG_LPF_CAP_PAIR_A_MASK | RTL8226_RG_LPF_CAP_PAIR_B_MASK,
+ FIELD_PREP(RTL8226_RG_LPF_CAP_PAIR_A_MASK, val_p3) |
+ FIELD_PREP(RTL8226_RG_LPF_CAP_PAIR_B_MASK, val_p2));
+ if (ret < 0)
+ return ret;
+
+ return phy_modify_mmd(phydev, MDIO_MMD_VEND2, reg_p2_p3,
+ RTL8226_RG_LPF_CAP_PAIR_A_MASK | RTL8226_RG_LPF_CAP_PAIR_B_MASK,
+ FIELD_PREP(RTL8226_RG_LPF_CAP_PAIR_A_MASK, val_p1) |
+ FIELD_PREP(RTL8226_RG_LPF_CAP_PAIR_B_MASK, val_p0));
+}
+
+static int rtl8226_patch_mdi_swap(struct phy_device *phydev, bool swap_enable)
+{
+ u16 adccal_offset[4];
+ bool is_patched;
+ int ret;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8226_VND2_UNKNOWN_D068);
+ if (ret < 0)
+ return ret;
+
+ is_patched = !(ret & RTL8226_VND2_UNKNOWN_D068_MDI_SWAP_FLAG);
+
+ if (is_patched == swap_enable) {
+ /* Nothing to do */
+ return 0;
+ }
+
+ if (!swap_enable) {
+ /* Patching is only implemented one-way, see next comment. */
+ phydev_warn(phydev, "MDI swapping disabled, but PHY is already patched.\n");
+ return -EINVAL;
+ }
+
+ /* The exact meaning of these bits is unknown. We only know that bit 1
+ * is used as a flag that swapping is already done.
+ */
+ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, RTL8226_VND2_UNKNOWN_D068, 0x7, 0x1);
+ if (ret < 0)
+ return ret;
+
+ for (int i = 0; i < 4; i++) {
+ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, RTL8226_VND2_UNKNOWN_D068,
+ RTL8226_VND2_UNKNOWN_D068_PAIR_SEL,
+ FIELD_PREP(RTL8226_VND2_UNKNOWN_D068_PAIR_SEL, i));
+ if (ret < 0)
+ return ret;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8226_VND2_ADCCAL_OFFSET);
+ if (ret < 0)
+ return ret;
+
+ adccal_offset[i] = ret;
+ }
+
+ for (int i = 0; i < 4; i++) {
+ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, RTL8226_VND2_UNKNOWN_D068,
+ RTL8226_VND2_UNKNOWN_D068_PAIR_SEL,
+ FIELD_PREP(RTL8226_VND2_UNKNOWN_D068_PAIR_SEL, i));
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8226_VND2_ADCCAL_OFFSET,
+ adccal_offset[3 - i]);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = rtl8226_swap_rg_lpf_cap(phydev, RTL8226_VND2_RG_LPF_CAP_XG_P0_P1,
+ RTL8226_VND2_RG_LPF_CAP_XG_P2_P3);
+ if (ret < 0)
+ return ret;
+
+ return rtl8226_swap_rg_lpf_cap(phydev, RTL8226_VND2_RG_LPF_CAP_P0_P1,
+ RTL8226_VND2_RG_LPF_CAP_P2_P3);
+}
+
+static int rtl8226_config_mdi_order(struct phy_device *phydev)
+{
+ u32 order;
+ bool swap_enable;
+ int ret;
+
+ ret = of_property_read_u32(phydev->mdio.dev.of_node, "enet-phy-pair-order", &order);
+
+ /* Property not present, nothing to do */
+ if (ret == -EINVAL || ret == -ENOSYS)
+ return 0;
+
+ if (ret)
+ return ret;
+
+ if (order & ~1)
+ return -EINVAL;
+
+ swap_enable = !!(order & 1);
+
+ ret = rtl8226_set_mdi_swap(phydev, swap_enable);
+ if (ret)
+ return ret;
+
+ return rtl8226_patch_mdi_swap(phydev, swap_enable);
+}
+
+static int rtl8226_probe(struct phy_device *phydev)
+{
+ return rtl8226_config_mdi_order(phydev);
+}
+
static int rtl822x_set_serdes_option_mode(struct phy_device *phydev, bool gen1)
{
bool has_2500, has_sgmii;
@@ -2522,6 +2675,7 @@ static struct phy_driver realtek_drvs[] = {
.soft_reset = rtl822x_c45_soft_reset,
.get_features = rtl822x_c45_get_features,
.config_aneg = rtl822x_c45_config_aneg,
+ .probe = rtl8226_probe,
.config_init = rtl822x_config_init,
.inband_caps = rtl822x_inband_caps,
.config_inband = rtl822x_config_inband,
--
2.53.0
^ permalink raw reply related
* [pull-request] mlx5-next updates 2026-04-29
From: Tariq Toukan @ 2026-04-29 21:27 UTC (permalink / raw)
To: Eric Dumazet, Jakub Kicinski, Paolo Abeni, Andrew Lunn,
David S. Miller
Cc: Leon Romanovsky, Jason Gunthorpe, Saeed Mahameed, Tariq Toukan,
Mark Bloch, Moshe Shemesh, Shay Drori, Parav Pandit,
Daniel Jurgens, Kees Cook, Adithya Jayachandran, Jiri Pirko,
Or Har-Toov, Simon Horman, linux-rdma, linux-kernel, netdev,
Gal Pressman, Dragos Tatulea
Hi,
The following pull-request contains common mlx5 updates
for your *net-next* tree.
Please pull and let me know of any problem.
Regards,
Tariq
----------------------------------------------------------------
The following changes since commit 254f49634ee16a731174d2ae34bc50bd5f45e731:
Linux 7.1-rc1 (2026-04-26 14:19:00 -0700)
are available in the Git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/mellanox/linux.git mlx5-next
for you to fetch changes up to 02c54621e81ccdc1907e2d735bcda751f2caade1:
net/mlx5: Extend query_esw_functions output for multi-function support (2026-04-29 16:28:30 -0400)
----------------------------------------------------------------
Moshe Shemesh (4):
mlx5: Rename the vport number enums for host PF and VF
net/mlx5: Add function_id_type for enable/disable_hca cmds
net/mlx5: Remove unused host_sf_enable field
net/mlx5: Extend query_esw_functions output for multi-function support
drivers/infiniband/hw/mlx5/counters.c | 4 +-
.../ethernet/mellanox/mlx5/core/esw/devlink_port.c | 7 ++-
.../net/ethernet/mellanox/mlx5/core/esw/ipsec.c | 2 +-
drivers/net/ethernet/mellanox/mlx5/core/eswitch.c | 36 ++++++-----
drivers/net/ethernet/mellanox/mlx5/core/eswitch.h | 2 +-
.../ethernet/mellanox/mlx5/core/eswitch_offloads.c | 42 ++++++++-----
.../mellanox/mlx5/core/sf/mlx5_ifc_vhca_event.h | 8 ---
drivers/net/ethernet/mellanox/mlx5/core/sriov.c | 7 ++-
.../mellanox/mlx5/core/steering/hws/vport.c | 2 +-
include/linux/mlx5/eswitch.h | 2 +-
include/linux/mlx5/mlx5_ifc.h | 73 +++++++++++++++++++---
include/linux/mlx5/vport.h | 4 +-
12 files changed, 130 insertions(+), 59 deletions(-)
^ permalink raw reply
* [PATCH net-next] net: tls: reshuffle the device ops check
From: Jakub Kicinski @ 2026-04-29 21:30 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, Jakub Kicinski,
john.fastabend, sd
We try to validate during registration that the netdev
has ops if it has features. This is currently somewhat sillily
written because we have a dereference before a NULL check
on the ops struct. Straighten this out.
No functional change intended other than saving ourselves
the very theoretical crash with a bad driver.
Note that we check earlier in the function that either ops
or TLS features are set for the device in question.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
CC: john.fastabend@gmail.com
CC: sd@queasysnail.net
---
net/tls/tls_device.c | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/net/tls/tls_device.c b/net/tls/tls_device.c
index 99c8eff9783e..741aef09bfd3 100644
--- a/net/tls/tls_device.c
+++ b/net/tls/tls_device.c
@@ -1387,16 +1387,15 @@ static int tls_dev_event(struct notifier_block *this, unsigned long event,
case NETDEV_FEAT_CHANGE:
if (netif_is_bond_master(dev))
return NOTIFY_DONE;
+ if (!dev->tlsdev_ops ||
+ !dev->tlsdev_ops->tls_dev_add ||
+ !dev->tlsdev_ops->tls_dev_del)
+ return NOTIFY_BAD;
if ((dev->features & NETIF_F_HW_TLS_RX) &&
!dev->tlsdev_ops->tls_dev_resync)
return NOTIFY_BAD;
- if (dev->tlsdev_ops &&
- dev->tlsdev_ops->tls_dev_add &&
- dev->tlsdev_ops->tls_dev_del)
- return NOTIFY_DONE;
- else
- return NOTIFY_BAD;
+ return NOTIFY_DONE;
case NETDEV_DOWN:
return tls_device_down(dev);
}
--
2.54.0
^ permalink raw reply related
* [PATCH net-next v5 0/2] net: pse-pd: add poll path and LED trigger support
From: Carlo Szelinsky @ 2026-04-29 21:32 UTC (permalink / raw)
To: Oleksij Rempel, Kory Maincent
Cc: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Krzysztof Kozlowski, netdev, linux-kernel,
linux-leds, Carlo Szelinsky
In-Reply-To: <20260410124428.809943-1-github@szelinsky.de>
Thanks to Kory, Oleksij, Krzysztof, Andrew and Jakub for all the
helpful feedback on earlier versions:-) I really appreciate the time
you put into reviewing this. I hope this is now the last round ;-)
This series adds poll-based event detection and LED trigger support
to the PSE core subsystem.
Patch 1 introduces the poll path independently of LED support,
so it can be tested in isolation on boards with and without IRQ
configured.
Patch 2 adds LED triggers that hook into the shared event handling
path introduced by patch 1.
Note: while reworking the poll teardown for v5, I noticed
pse_release_pis() (which kfree()s the pi[] array) runs before
disable_irq() / cancel_delayed_work_sync() in
pse_controller_unregister(). A concurrent IRQ or poll worker that
fires in that window would read through a freed pi[]. This is
pre-existing for the IRQ path (since fc0e6db30941, "Add support for
reporting events"); the v5 poll cancel inherits the same placement
for symmetry. Does it make sense that I send a separate fix that disables
both async sources before pse_release_pis() or am I wrong here?
Changes since v4:
- Rebased on top of "net: pse-pd: fix out-of-bounds bitmap access in
pse_isr() on 32-bit" (5099807f335c, merged via net). Extended the
same bitmap-pointer pattern to the new pse_handle_events() and
pse_poll_worker() code paths so for_each_set_bit() does not read
past a single unsigned long when nr_lines > BITS_PER_LONG (Jakub,
Kory)
- Cancel the poll work explicitly in pse_controller_unregister(),
next to the existing disable_irq() for the IRQ path, instead of
relying on devm_add_action_or_reset() LIFO ordering. Makes the
helper safe even when a driver registers it in the standard order
(helper before devm_pse_controller_register()) (Jakub)
- struct pse_pi_led_triggers no longer wrapped in
#if IS_ENABLED(CONFIG_LEDS_TRIGGERS); only the function bodies
remain ifdef'd, so reference sites no longer need their own
ifdefs (Jakub)
- Added .activate callbacks for both LED triggers. Without these,
an LED bound to a trigger after pse_controller_register() (e.g.
via sysfs) would stay dark until the next hardware event toggled
state (Jakub)
- Run the post-registration initial-state pass under pcdev->lock,
matching pse_led_update()'s documented locking contract and
avoiding races with concurrent regulator_enable() that share
last_delivering / last_enabled and the hardware ops (Jakub)
- On partial pse_led_triggers_register() failure, NULL out
pcdev->pi_led_trigs so the existing early-return guard in
pse_led_update() short-circuits any later calls onto
partially-registered triggers (Jakub)
Changes since v3:
- Dropped the dt-bindings poll-interval-ms patch: the poll interval
is a driver decision, not a hardware property (Krzysztof)
- Removed of_property_read_u32() for poll-interval-ms from
devm_pse_poll_helper(); the 500ms default is now hardcoded but
drivers can override pcdev->poll_interval_ms before calling the
helper
- Rebased on net-next/main
Changes since v2:
- Based on net-next/main, added net-next subject prefix
- Added --base tree information
- Added CC for devicetree list and DT maintainers
- Collected Reviewed-by from Kory Maincent on patch 1/3
- Fixed build error when CONFIG_LEDS_TRIGGERS is disabled:
moved LED registration before list_add(), removing the
pcdev->pi_led_trigs = NULL assignment on conditionally
compiled struct member (reported by kernel test robot)
- Fixed use-after-free on device unbind: poll work is now
cancelled via devm_add_action_or_reset() to ensure correct
devres teardown ordering (poll_work cancelled before
poll_notifs is freed)
- Used system_freezable_wq for poll worker to prevent hardware
access during system suspend
- Added PoDL power status and admin state checks to LED triggers
so they work for both C33 and PoDL controller types
- Used dev_name(dev) for LED trigger names to ensure uniqueness
across multiple PSE controllers (of_node->name can be generic)
- Added initial LED state query at registration so already-active
ports are reflected immediately
- Added pse_led_update() calls in regulator enable/disable paths
so ethtool admin state changes are reflected in LEDs
- Moved LED trigger registration before list_add() to prevent
race where IRQ/poll could invoke pse_led_update() on partially
initialized triggers
Changes since v1:
- Split single patch into 3 separate patches
- Extracted pse_handle_events() and devm_pse_poll_helper() as a
standalone poll path (patches 1-2), testable without LED code
- Added DT binding for poll-interval-ms as a separate patch
- Renamed led-poll-interval-ms to poll-interval-ms for generic use
- Fire LED triggers from the notification path rather than a
separate poll loop
Tested on Realtek RTL9303 with HS104 PoE chip, poll path only
(without IRQ configured). Verified PD connect/disconnect notifications
and LED trigger state changes.
Link: https://lore.kernel.org/all/20260410124428.809943-1-github@szelinsky.de/
Link: https://lore.kernel.org/all/20260329153124.2823980-1-github@szelinsky.de/
Link: https://lore.kernel.org/all/20260323201225.1836561-1-github@szelinsky.de/
Carlo Szelinsky (2):
net: pse-pd: add devm_pse_poll_helper()
net: pse-pd: add LED trigger support via notification path
drivers/net/pse-pd/pse_core.c | 355 +++++++++++++++++++++++++++++++---
include/linux/pse-pd/pse.h | 32 +++
2 files changed, 356 insertions(+), 31 deletions(-)
base-commit: 09942ddedcb960f9e78fd817ec33f501d1040c5b
--
2.43.0
^ permalink raw reply
* [PATCH net-next v5 1/2] net: pse-pd: add devm_pse_poll_helper()
From: Carlo Szelinsky @ 2026-04-29 21:32 UTC (permalink / raw)
To: Oleksij Rempel, Kory Maincent
Cc: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Krzysztof Kozlowski, netdev, linux-kernel,
linux-leds, Carlo Szelinsky
In-Reply-To: <20260429213224.1747410-1-github@szelinsky.de>
Extract the common event handling loop from pse_isr() into a shared
pse_handle_events() function, and add a generic poll-based alternative
to the IRQ path for PSE controllers that lack interrupt support or
have IRQ lines not wired on the board.
The new devm_pse_poll_helper() function sets up a delayed work that
periodically calls the driver's map_event callback to detect state
changes, feeding events into the existing ntf_fifo / pse_send_ntf_worker
notification pipeline. This reuses the same pse_irq_desc interface as
the IRQ path: the driver provides a map_event callback that populates
per-PI notification arrays.
The poll worker uses system_freezable_wq to avoid running during system
suspend when the underlying hardware (e.g. I2C bus) may be inaccessible.
Cancel the poll work explicitly in pse_controller_unregister() (next to
the existing disable_irq() for the IRQ path) so it cannot outlive the
controller it pushes events into. This avoids relying on devres LIFO
ordering between devm_pse_controller_register() and devm_pse_poll_helper()
when a driver follows the standard pattern of setting up the helper
before registering the controller.
The notifs_mask is allocated as a real bitmap via devm_bitmap_zalloc()
and passed to pse_handle_events() and the driver's map_event callback
as a pointer (mirroring the pattern from commit 5099807f335c
("net: pse-pd: fix out-of-bounds bitmap access in pse_isr() on 32-bit"))
so for_each_set_bit() does not read past a single unsigned long when
nr_lines > BITS_PER_LONG.
The poll interval defaults to 500ms, balancing responsiveness against
bus load (e.g. I2C).
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
---
drivers/net/pse-pd/pse_core.c | 157 +++++++++++++++++++++++++++-------
include/linux/pse-pd/pse.h | 14 +++
2 files changed, 140 insertions(+), 31 deletions(-)
diff --git a/drivers/net/pse-pd/pse_core.c b/drivers/net/pse-pd/pse_core.c
index 87aa4f4e9724..b7ffec0c942c 100644
--- a/drivers/net/pse-pd/pse_core.c
+++ b/drivers/net/pse-pd/pse_core.c
@@ -14,10 +14,17 @@
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/rtnetlink.h>
+#include <linux/workqueue.h>
#include <net/net_trackers.h>
#define PSE_PW_D_LIMIT INT_MAX
+/* Default poll interval for controllers without IRQ support.
+ * 500ms provides a reasonable trade-off between responsiveness
+ * (event detection, PD detection) and I2C bus utilization.
+ */
+#define PSE_DEFAULT_POLL_INTERVAL_MS 500
+
static DEFINE_MUTEX(pse_list_mutex);
static LIST_HEAD(pse_controller_list);
static DEFINE_XARRAY_ALLOC(pse_pw_d_map);
@@ -1118,6 +1125,8 @@ void pse_controller_unregister(struct pse_controller_dev *pcdev)
pse_release_pis(pcdev);
if (pcdev->irq)
disable_irq(pcdev->irq);
+ if (pcdev->polling)
+ cancel_delayed_work_sync(&pcdev->poll_work);
cancel_work_sync(&pcdev->ntf_work);
kfifo_free(&pcdev->ntf_fifo);
mutex_lock(&pse_list_mutex);
@@ -1239,66 +1248,104 @@ static int pse_set_config_isr(struct pse_controller_dev *pcdev, int id,
}
/**
- * pse_isr - IRQ handler for PSE
- * @irq: irq number
- * @data: pointer to user interrupt structure
+ * pse_handle_events - Process PSE events for all PIs
+ * @pcdev: a pointer to the PSE controller device
+ * @notifs: per-PI notification array
+ * @notifs_mask: bitmap of PIs with events (sized for pcdev->nr_lines)
*
- * Return: irqreturn_t - status of IRQ
+ * Common event handling shared between IRQ and poll paths.
+ * Caller must hold pcdev->lock.
*/
-static irqreturn_t pse_isr(int irq, void *data)
+static void pse_handle_events(struct pse_controller_dev *pcdev,
+ unsigned long *notifs,
+ unsigned long *notifs_mask)
{
- struct pse_controller_dev *pcdev;
- struct pse_irq_desc *desc;
- struct pse_irq *h = data;
- int ret, i;
-
- desc = &h->desc;
- pcdev = h->pcdev;
-
- /* Clear notifs mask */
- memset(h->notifs, 0, pcdev->nr_lines * sizeof(*h->notifs));
- bitmap_zero(h->notifs_mask, pcdev->nr_lines);
- mutex_lock(&pcdev->lock);
- ret = desc->map_event(irq, pcdev, h->notifs, h->notifs_mask);
- if (ret || bitmap_empty(h->notifs_mask, pcdev->nr_lines)) {
- mutex_unlock(&pcdev->lock);
- return IRQ_NONE;
- }
+ int i;
- for_each_set_bit(i, h->notifs_mask, pcdev->nr_lines) {
- unsigned long notifs, rnotifs;
+ for_each_set_bit(i, notifs_mask, pcdev->nr_lines) {
+ unsigned long pi_notifs, rnotifs;
struct pse_ntf ntf = {};
+ int ret;
/* Do nothing PI not described */
if (!pcdev->pi[i].rdev)
continue;
- notifs = h->notifs[i];
+ pi_notifs = notifs[i];
if (pse_pw_d_is_sw_pw_control(pcdev, pcdev->pi[i].pw_d)) {
- ret = pse_set_config_isr(pcdev, i, notifs);
+ ret = pse_set_config_isr(pcdev, i, pi_notifs);
if (ret)
- notifs |= ETHTOOL_PSE_EVENT_SW_PW_CONTROL_ERROR;
+ pi_notifs |= ETHTOOL_PSE_EVENT_SW_PW_CONTROL_ERROR;
}
- dev_dbg(h->pcdev->dev,
- "Sending PSE notification EVT 0x%lx\n", notifs);
+ dev_dbg(pcdev->dev,
+ "Sending PSE notification EVT 0x%lx\n", pi_notifs);
- ntf.notifs = notifs;
+ ntf.notifs = pi_notifs;
ntf.id = i;
kfifo_in_spinlocked(&pcdev->ntf_fifo, &ntf, 1,
&pcdev->ntf_fifo_lock);
schedule_work(&pcdev->ntf_work);
- rnotifs = pse_to_regulator_notifs(notifs);
+ rnotifs = pse_to_regulator_notifs(pi_notifs);
regulator_notifier_call_chain(pcdev->pi[i].rdev, rnotifs,
NULL);
}
+}
+
+/**
+ * pse_isr - IRQ handler for PSE
+ * @irq: irq number
+ * @data: pointer to user interrupt structure
+ *
+ * Return: irqreturn_t - status of IRQ
+ */
+static irqreturn_t pse_isr(int irq, void *data)
+{
+ struct pse_controller_dev *pcdev;
+ struct pse_irq *h = data;
+ int ret;
+
+ pcdev = h->pcdev;
+ /* Clear notifs mask */
+ memset(h->notifs, 0, pcdev->nr_lines * sizeof(*h->notifs));
+ bitmap_zero(h->notifs_mask, pcdev->nr_lines);
+ mutex_lock(&pcdev->lock);
+ ret = h->desc.map_event(irq, pcdev, h->notifs, h->notifs_mask);
+ if (ret || bitmap_empty(h->notifs_mask, pcdev->nr_lines)) {
+ mutex_unlock(&pcdev->lock);
+ return IRQ_NONE;
+ }
+
+ pse_handle_events(pcdev, h->notifs, h->notifs_mask);
mutex_unlock(&pcdev->lock);
return IRQ_HANDLED;
}
+static void pse_poll_worker(struct work_struct *work)
+{
+ struct pse_controller_dev *pcdev =
+ container_of(work, struct pse_controller_dev,
+ poll_work.work);
+ int ret;
+
+ memset(pcdev->poll_notifs, 0,
+ pcdev->nr_lines * sizeof(*pcdev->poll_notifs));
+ bitmap_zero(pcdev->poll_notifs_mask, pcdev->nr_lines);
+ mutex_lock(&pcdev->lock);
+ ret = pcdev->poll_desc.map_event(0, pcdev, pcdev->poll_notifs,
+ pcdev->poll_notifs_mask);
+ if (!ret && !bitmap_empty(pcdev->poll_notifs_mask, pcdev->nr_lines))
+ pse_handle_events(pcdev, pcdev->poll_notifs,
+ pcdev->poll_notifs_mask);
+ mutex_unlock(&pcdev->lock);
+
+ queue_delayed_work(system_freezable_wq, &pcdev->poll_work,
+ msecs_to_jiffies(pcdev->poll_interval_ms));
+}
+
/**
* devm_pse_irq_helper - Register IRQ based PSE event notifier
* @pcdev: a pointer to the PSE
@@ -1356,6 +1403,54 @@ int devm_pse_irq_helper(struct pse_controller_dev *pcdev, int irq,
}
EXPORT_SYMBOL_GPL(devm_pse_irq_helper);
+/**
+ * devm_pse_poll_helper - Register poll-based PSE event notifier
+ * @pcdev: a pointer to the PSE controller device
+ * @d: PSE event description (uses same pse_irq_desc as IRQ path)
+ *
+ * For PSE controllers without IRQ support or with IRQ not wired. Sets
+ * up a delayed work that periodically calls the driver's map_event
+ * callback to detect state changes, feeding events into the standard
+ * notification pipeline.
+ *
+ * The poll worker uses system_freezable_wq to ensure it does not run
+ * during system suspend while the hardware may be inaccessible.
+ *
+ * Return: 0 on success and errno on failure
+ */
+int devm_pse_poll_helper(struct pse_controller_dev *pcdev,
+ const struct pse_irq_desc *d)
+{
+ struct device *dev = pcdev->dev;
+
+ if (!d || !d->map_event || !d->name)
+ return -EINVAL;
+
+ pcdev->poll_desc = *d;
+ pcdev->poll_notifs = devm_kcalloc(dev, pcdev->nr_lines,
+ sizeof(*pcdev->poll_notifs),
+ GFP_KERNEL);
+ if (!pcdev->poll_notifs)
+ return -ENOMEM;
+
+ pcdev->poll_notifs_mask = devm_bitmap_zalloc(dev, pcdev->nr_lines,
+ GFP_KERNEL);
+ if (!pcdev->poll_notifs_mask)
+ return -ENOMEM;
+
+ if (!pcdev->poll_interval_ms)
+ pcdev->poll_interval_ms = PSE_DEFAULT_POLL_INTERVAL_MS;
+
+ INIT_DELAYED_WORK(&pcdev->poll_work, pse_poll_worker);
+ pcdev->polling = true;
+
+ queue_delayed_work(system_freezable_wq, &pcdev->poll_work,
+ msecs_to_jiffies(pcdev->poll_interval_ms));
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(devm_pse_poll_helper);
+
/* PSE control section */
static void __pse_control_release(struct kref *kref)
diff --git a/include/linux/pse-pd/pse.h b/include/linux/pse-pd/pse.h
index 4e5696cfade7..4b5d5b11a084 100644
--- a/include/linux/pse-pd/pse.h
+++ b/include/linux/pse-pd/pse.h
@@ -292,6 +292,12 @@ struct pse_ntf {
* @pi: table of PSE PIs described in this controller device
* @no_of_pse_pi: flag set if the pse_pis devicetree node is not used
* @irq: PSE interrupt
+ * @polling: flag indicating poll-based event detection is active
+ * @poll_interval_ms: poll interval in milliseconds
+ * @poll_work: delayed work for poll-based event detection
+ * @poll_desc: copy of the driver's event descriptor for polling
+ * @poll_notifs: per-PI notification scratch space for poll worker
+ * @poll_notifs_mask: bitmap of PIs with events for poll worker
* @pis_prio_max: Maximum value allowed for the PSE PIs priority
* @supp_budget_eval_strategies: budget evaluation strategies supported
* by the PSE
@@ -312,6 +318,12 @@ struct pse_controller_dev {
struct pse_pi *pi;
bool no_of_pse_pi;
int irq;
+ bool polling;
+ unsigned int poll_interval_ms;
+ struct delayed_work poll_work;
+ struct pse_irq_desc poll_desc;
+ unsigned long *poll_notifs;
+ unsigned long *poll_notifs_mask;
unsigned int pis_prio_max;
u32 supp_budget_eval_strategies;
struct work_struct ntf_work;
@@ -345,6 +357,8 @@ int devm_pse_controller_register(struct device *dev,
struct pse_controller_dev *pcdev);
int devm_pse_irq_helper(struct pse_controller_dev *pcdev, int irq,
int irq_flags, const struct pse_irq_desc *d);
+int devm_pse_poll_helper(struct pse_controller_dev *pcdev,
+ const struct pse_irq_desc *d);
struct pse_control *of_pse_control_get(struct device_node *node,
struct phy_device *phydev);
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v5 2/2] net: pse-pd: add LED trigger support via notification path
From: Carlo Szelinsky @ 2026-04-29 21:32 UTC (permalink / raw)
To: Oleksij Rempel, Kory Maincent
Cc: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Krzysztof Kozlowski, netdev, linux-kernel,
linux-leds, Carlo Szelinsky
In-Reply-To: <20260429213224.1747410-1-github@szelinsky.de>
Add per-PI "delivering" and "enabled" LED triggers to the PSE core
subsystem. LED state is updated from the shared pse_handle_events()
function whenever the IRQ or poll path detects a state change, as well
as from the regulator enable/disable paths so that host-initiated
admin state changes via ethtool are immediately reflected.
Each trigger registers an .activate callback that syncs a freshly-bound
LED to the cached state, so an LED bound after pse_controller_register()
(e.g. via sysfs) does not stay dark until the next hardware event.
The post-registration initial-state pass is run under pcdev->lock,
matching pse_led_update()'s documented locking contract and avoiding
races with concurrent regulator_enable() paths that share last_delivering
/ last_enabled and the hardware ops.
If pse_led_triggers_register() fails partway through, the partially-
registered triggers are detached by clearing pcdev->pi_led_trigs, so the
existing early-return guard in pse_led_update() is the single point of
truth and a stale led_trigger.name cannot fool the dereference path
into touching an unregistered trigger.
The per-PI trigger struct lives outside the CONFIG_LEDS_TRIGGERS ifdef
to avoid forcing every reference site to be wrapped; the LED-specific
code paths remain ifdef'd.
Link: https://lore.kernel.org/oe-kbuild-all/202603251254.o5PqMBRU-lkp@intel.com/
Link: https://lore.kernel.org/oe-kbuild-all/202603251250.cuMCk5Yv-lkp@intel.com/
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
---
drivers/net/pse-pd/pse_core.c | 200 +++++++++++++++++++++++++++++++++-
include/linux/pse-pd/pse.h | 18 +++
2 files changed, 217 insertions(+), 1 deletion(-)
diff --git a/drivers/net/pse-pd/pse_core.c b/drivers/net/pse-pd/pse_core.c
index b7ffec0c942c..bff97211ae22 100644
--- a/drivers/net/pse-pd/pse_core.c
+++ b/drivers/net/pse-pd/pse_core.c
@@ -8,6 +8,7 @@
#include <linux/device.h>
#include <linux/ethtool.h>
#include <linux/ethtool_netlink.h>
+#include <linux/leds.h>
#include <linux/of.h>
#include <linux/phy.h>
#include <linux/pse-pd/pse.h>
@@ -669,6 +670,168 @@ static int _pse_pi_delivery_power_sw_pw_ctrl(struct pse_controller_dev *pcdev,
return 0;
}
+#if IS_ENABLED(CONFIG_LEDS_TRIGGERS)
+/**
+ * pse_pi_get_states - Fetch current delivering/enabled state for a PI
+ * @pcdev: PSE controller device
+ * @id: PI index
+ * @delivering: out, set to true if PI is currently delivering power
+ * @enabled: out, set to true if PI is administratively enabled
+ *
+ * Queries hardware via the controller ops. Caller must hold pcdev->lock.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+static int pse_pi_get_states(struct pse_controller_dev *pcdev, int id,
+ bool *delivering, bool *enabled)
+{
+ struct pse_pw_status pw_status = {};
+ struct pse_admin_state admin_state = {};
+ int ret;
+
+ ret = pcdev->ops->pi_get_pw_status(pcdev, id, &pw_status);
+ if (ret)
+ return ret;
+ ret = pcdev->ops->pi_get_admin_state(pcdev, id, &admin_state);
+ if (ret)
+ return ret;
+
+ *delivering = pw_status.c33_pw_status ==
+ ETHTOOL_C33_PSE_PW_D_STATUS_DELIVERING ||
+ pw_status.podl_pw_status ==
+ ETHTOOL_PODL_PSE_PW_D_STATUS_DELIVERING;
+ *enabled = admin_state.c33_admin_state ==
+ ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED ||
+ admin_state.podl_admin_state ==
+ ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED;
+
+ return 0;
+}
+
+/**
+ * pse_led_update - Update LED triggers for a PI based on current state
+ * @pcdev: PSE controller device
+ * @id: PI index
+ *
+ * Queries the current power status and admin state of the PI and
+ * fires LED trigger events on state changes. Called from the
+ * notification path and the regulator enable/disable paths.
+ *
+ * Must be called with pcdev->lock held.
+ */
+static void pse_led_update(struct pse_controller_dev *pcdev, int id)
+{
+ struct pse_pi_led_triggers *trigs;
+ bool delivering, enabled;
+
+ if (!pcdev->pi_led_trigs)
+ return;
+
+ trigs = &pcdev->pi_led_trigs[id];
+ if (!trigs->delivering.name)
+ return;
+
+ if (pse_pi_get_states(pcdev, id, &delivering, &enabled))
+ return;
+
+ if (trigs->last_delivering != delivering) {
+ trigs->last_delivering = delivering;
+ led_trigger_event(&trigs->delivering,
+ delivering ? LED_FULL : LED_OFF);
+ }
+
+ if (trigs->last_enabled != enabled) {
+ trigs->last_enabled = enabled;
+ led_trigger_event(&trigs->enabled,
+ enabled ? LED_FULL : LED_OFF);
+ }
+}
+
+/* Sync a freshly-bound LED to the cached trigger state. Without these
+ * .activate callbacks, an LED bound to the trigger after
+ * pse_controller_register() (e.g. via sysfs) would stay dark until the
+ * next hardware event toggles state.
+ */
+static int pse_led_delivering_activate(struct led_classdev *led_cdev)
+{
+ struct pse_pi_led_triggers *trigs =
+ container_of(led_cdev->trigger, struct pse_pi_led_triggers,
+ delivering);
+
+ led_set_brightness(led_cdev,
+ trigs->last_delivering ? LED_FULL : LED_OFF);
+ return 0;
+}
+
+static int pse_led_enabled_activate(struct led_classdev *led_cdev)
+{
+ struct pse_pi_led_triggers *trigs =
+ container_of(led_cdev->trigger, struct pse_pi_led_triggers,
+ enabled);
+
+ led_set_brightness(led_cdev,
+ trigs->last_enabled ? LED_FULL : LED_OFF);
+ return 0;
+}
+
+static int pse_led_triggers_register(struct pse_controller_dev *pcdev)
+{
+ struct device *dev = pcdev->dev;
+ const char *dev_id;
+ int i, ret;
+
+ dev_id = dev_name(dev);
+
+ pcdev->pi_led_trigs = devm_kcalloc(dev, pcdev->nr_lines,
+ sizeof(*pcdev->pi_led_trigs),
+ GFP_KERNEL);
+ if (!pcdev->pi_led_trigs)
+ return -ENOMEM;
+
+ for (i = 0; i < pcdev->nr_lines; i++) {
+ struct pse_pi_led_triggers *trigs = &pcdev->pi_led_trigs[i];
+
+ /* Skip PIs not described in device tree */
+ if (!pcdev->no_of_pse_pi && !pcdev->pi[i].np)
+ continue;
+
+ trigs->delivering.name = devm_kasprintf(dev, GFP_KERNEL,
+ "pse-%s:port%d:delivering",
+ dev_id, i);
+ if (!trigs->delivering.name)
+ return -ENOMEM;
+ trigs->delivering.activate = pse_led_delivering_activate;
+
+ ret = devm_led_trigger_register(dev, &trigs->delivering);
+ if (ret) {
+ trigs->delivering.name = NULL;
+ return ret;
+ }
+
+ trigs->enabled.name = devm_kasprintf(dev, GFP_KERNEL,
+ "pse-%s:port%d:enabled",
+ dev_id, i);
+ if (!trigs->enabled.name)
+ return -ENOMEM;
+ trigs->enabled.activate = pse_led_enabled_activate;
+
+ ret = devm_led_trigger_register(dev, &trigs->enabled);
+ if (ret) {
+ trigs->enabled.name = NULL;
+ return ret;
+ }
+ }
+
+ return 0;
+}
+#else
+static inline void pse_led_update(struct pse_controller_dev *pcdev, int id) {}
+static int pse_led_triggers_register(struct pse_controller_dev *pcdev)
+{
+ return 0;
+}
+#endif /* CONFIG_LEDS_TRIGGERS */
+
static int pse_pi_enable(struct regulator_dev *rdev)
{
struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev);
@@ -694,6 +857,7 @@ static int pse_pi_enable(struct regulator_dev *rdev)
pcdev->pi[id].admin_state_enabled = 1;
ret = 0;
}
+ pse_led_update(pcdev, id);
mutex_unlock(&pcdev->lock);
return ret;
}
@@ -701,6 +865,7 @@ static int pse_pi_enable(struct regulator_dev *rdev)
ret = ops->pi_enable(pcdev, id);
if (!ret)
pcdev->pi[id].admin_state_enabled = 1;
+ pse_led_update(pcdev, id);
mutex_unlock(&pcdev->lock);
return ret;
@@ -718,6 +883,7 @@ static int pse_pi_disable(struct regulator_dev *rdev)
ret = _pse_pi_disable(pcdev, id);
if (!ret)
pi->admin_state_enabled = 0;
+ pse_led_update(pcdev, id);
mutex_unlock(&pcdev->lock);
return 0;
@@ -1107,6 +1273,31 @@ int pse_controller_register(struct pse_controller_dev *pcdev)
if (ret)
return ret;
+ ret = pse_led_triggers_register(pcdev);
+ if (ret) {
+ /* LED triggers are non-essential for power delivery; warn
+ * and continue. NULL out the array so pse_led_update()'s
+ * early-return guard short-circuits any later calls onto
+ * partially-registered triggers.
+ */
+ dev_warn(pcdev->dev, "Failed to register LED triggers: %d\n",
+ ret);
+ pcdev->pi_led_trigs = NULL;
+ }
+
+ /* Query initial LED state for all PIs so already-active ports
+ * are reflected immediately without waiting for a hardware event.
+ * Hold pcdev->lock: regulators are already exposed and a
+ * concurrent regulator_enable() would race on the hw callbacks
+ * and on last_delivering / last_enabled.
+ */
+ mutex_lock(&pcdev->lock);
+ for (i = 0; i < pcdev->nr_lines; i++) {
+ if (pcdev->no_of_pse_pi || pcdev->pi[i].np)
+ pse_led_update(pcdev, i);
+ }
+ mutex_unlock(&pcdev->lock);
+
mutex_lock(&pse_list_mutex);
list_add(&pcdev->list, &pse_controller_list);
mutex_unlock(&pse_list_mutex);
@@ -1267,7 +1458,14 @@ static void pse_handle_events(struct pse_controller_dev *pcdev,
struct pse_ntf ntf = {};
int ret;
- /* Do nothing PI not described */
+ /* Update LEDs for described PIs regardless of consumer state.
+ * LED triggers are registered at controller init, before any
+ * PHY claims a PSE control, so rdev may still be NULL here.
+ */
+ if (pcdev->no_of_pse_pi || pcdev->pi[i].np)
+ pse_led_update(pcdev, i);
+
+ /* Skip regulator/netlink path for PIs without consumers */
if (!pcdev->pi[i].rdev)
continue;
diff --git a/include/linux/pse-pd/pse.h b/include/linux/pse-pd/pse.h
index 4b5d5b11a084..ba321e669642 100644
--- a/include/linux/pse-pd/pse.h
+++ b/include/linux/pse-pd/pse.h
@@ -10,6 +10,7 @@
#include <linux/kfifo.h>
#include <uapi/linux/ethtool.h>
#include <uapi/linux/ethtool_netlink_generated.h>
+#include <linux/leds.h>
#include <linux/regulator/driver.h>
/* Maximum current in uA according to IEEE 802.3-2022 Table 145-1 */
@@ -266,6 +267,21 @@ struct pse_pi {
int pw_allocated_mW;
};
+/**
+ * struct pse_pi_led_triggers - LED trigger state for a PSE PI
+ *
+ * @delivering: LED trigger for power delivering state
+ * @enabled: LED trigger for admin enabled state
+ * @last_delivering: cached delivering state for change detection
+ * @last_enabled: cached enabled state for change detection
+ */
+struct pse_pi_led_triggers {
+ struct led_trigger delivering;
+ struct led_trigger enabled;
+ bool last_delivering;
+ bool last_enabled;
+};
+
/**
* struct pse_ntf - PSE notification element
*
@@ -304,6 +320,7 @@ struct pse_ntf {
* @ntf_work: workqueue for PSE notification management
* @ntf_fifo: PSE notifications FIFO
* @ntf_fifo_lock: protect @ntf_fifo writer
+ * @pi_led_trigs: per-PI LED trigger state array
*/
struct pse_controller_dev {
const struct pse_controller_ops *ops;
@@ -329,6 +346,7 @@ struct pse_controller_dev {
struct work_struct ntf_work;
DECLARE_KFIFO_PTR(ntf_fifo, struct pse_ntf);
spinlock_t ntf_fifo_lock; /* Protect @ntf_fifo writer */
+ struct pse_pi_led_triggers *pi_led_trigs;
};
/**
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v3 3/3] p54spi: convert to devicetree
From: Arnd Bergmann @ 2026-04-29 21:35 UTC (permalink / raw)
To: Krzysztof Kozlowski, Arnd Bergmann
Cc: Aaro Koskinen, Andreas Kemnade, Bartosz Golaszewski,
Benoît Cousson, David S . Miller, Dmitry Torokhov,
Eric Dumazet, Felipe Balbi, Jakub Kicinski, Johannes Berg,
Kevin Hilman, Krzysztof Kozlowski, Linus Walleij, Paolo Abeni,
Rob Herring, Roger Quadros, Tony Lindgren, linux-wireless, Netdev,
devicetree, linux-kernel, linux-arm-kernel,
open list:GPIO SUBSYSTEM, Linux-OMAP, Christian Lamparter
In-Reply-To: <e4a7e9d8-7091-4520-a634-ff0a44eb5139@kernel.org>
On Wed, Apr 29, 2026, at 10:07, Krzysztof Kozlowski wrote:
> On 27/04/2026 16:23, Arnd Bergmann wrote:
>>
>> - ret = gpio_request(p54spi_gpio_power, "p54spi power");
>> - if (ret < 0) {
>> - dev_err(&priv->spi->dev, "power GPIO request failed: %d", ret);
>> + priv->gpio_powerdown = gpiod_get(&spi->dev, "powerdown", GPIOD_OUT_HIGH);
>> + if (IS_ERR(priv->gpio_powerdown)) {
>> + ret = PTR_ERR(priv->gpio_powerdown);
>> + dev_err(&priv->spi->dev, "powerdown GPIO request failed: %d", ret);
>
> Binding said it is optional, so this cannot be a failure.
>
> Also, please use ret = dev_err_probe syntax.
Ok, fixed both.
>> @@ -686,10 +659,19 @@ static void p54spi_remove(struct spi_device *spi)
>> p54_free_common(priv->hw);
>> }
>>
>> +struct of_device_id p54spi_of_ids[] = {
>
> static const
I would have expected that to trigger a compile-time warning for a
missing declaration, not sure what happened here. Fixed now.
>> + { .compatible = "cnxt,3110x", },
>> + { .compatible = "isil,p54spi", },
>> + { .compatible = "st,stlc4550", },
>> + { .compatible = "st,stlc4560", },
>
> At least last two devices are then compatible, so this should be
> expressed in the binding with fallback and drop stlc4560 here. Maybe all
> of them are compatible.
The driver doesn't know the difference, so I assume they are
either all compatible, or the other ones don't actually work.
I've dropped everything except "st,stlc4550" now, as that is the
one I used in the dts file. I kept the other identifiers
in the binding as:
compatible:
oneOf:
- const: st,stlc4560
- items:
- enum:
- cnxt,3110x
- st,stlc4550
- isil,p54spi
- const: st,stlc4560
Not sure if that's the best way to express this.
Arnd
^ permalink raw reply
* [PATCH net-next v9 0/5] TLS read_sock performance scalability
From: Chuck Lever @ 2026-04-29 21:48 UTC (permalink / raw)
To: John Fastabend, Jakub Kicinski, Sabrina Dubroca
Cc: Eric Dumazet, Simon Horman, Paolo Abeni, netdev,
kernel-tls-handshake, Chuck Lever, Hannes Reinecke,
Alistair Francis
I'd like to encourage in-kernel kTLS consumers (i.e., NFS and
NVMe/TCP) to coalesce on the use of read_sock. When I suggested
this to Hannes, he reported a few performance scalability issues
with read_sock. However, batch async decryption and its
submit/deliver scaffolding were dropped from this series because
async_capable is always false for TLS 1.3, the TLS version that
NFS and NVMe/TCP both require. Async crypto support for TLS 1.3
is a prerequisite for revisiting that work.
This series is now only a set of clean-ups. Support for async
has been deferred until after TLS KeyUpdate has been merged.
---
Changes since v8:
- Address review comments from sashiko
- Patch 2: Requeue partially consumed skb to prevent leak
- Patch 5: Re-check sk_err so RST during flush surfaces as
-ECONNRESET instead of EOF
- Address review comments from gpt-5.5
- Patch 4: Restore msg_ready early-return in tls_strp_check_rcv()
so the queued strp_work doesn't double-wake the consumer
- Patch 4: Add tls_strparser msg_announced bit so the recvmsg
exit-point handoff doesn't re-fire saved_data_ready() for a
record BH or the worker already announced (rx_list-only drain
path)
Changes since v7:
- Rebased on net-next (v7.1-rc1)
Changes since v6:
- Rebased on net-next, v5's 1/6 was merged upstream
Changes since v5:
- Patch 6: Set released = true when sk_flush_backlog() returns
true, so tls_strp_msg_load() knows the socket lock was
released (Sabrina)
- Patch 6: Drop Fixes tag; submit bug fix separately via net
if warranted (Sabrina)
- Patch 6: Note redundant flush on cold path in commit message
(Sabrina)
Changes since v4:
- Drop batch async decryption and submit/deliver restructure:
async_capable is always false for TLS 1.3, so the new code
was unreachable for NFS and NVMe/TCP
- Purge async_hold directly in tls_decrypt_async_wait() and drop
the tls_decrypt_async_drain() wrapper
- Merge tls_strp_check_rcv_quiet() into tls_strp_check_rcv() with
a bool wake parameter; fix lost wakeup on the recvmsg exit path
Changes since v3:
- Clarify why tls_decrypt_async_drain() is separate from _wait()
- Fold tls_err_abort() into tls_rx_one_record(), drop tls_rx_decrypt_record()
- Move backlog flush into tls_rx_rec_wait() so all RX paths benefit
Changes since v2:
- Fix short read self tests
Changes since v1:
- Add C11 reference
- Extend data_ready reduction to recvmsg and splice
- Restructure read_sock and recvmsg using shared helpers
---
Chuck Lever (5):
tls: Abort the connection on decrypt failure
tls: Fix dangling skb pointer in tls_sw_read_sock()
tls: Factor tls_strp_msg_release() from tls_strp_msg_done()
tls: Suppress spurious saved_data_ready on all receive paths
tls: Flush backlog before waiting for a new record
include/net/tls.h | 4 +++
net/tls/tls.h | 4 +--
net/tls/tls_main.c | 2 +-
net/tls/tls_strp.c | 36 ++++++++++++++++++++------
net/tls/tls_sw.c | 76 ++++++++++++++++++++++++++++++++++++------------------
5 files changed, 86 insertions(+), 36 deletions(-)
---
base-commit: 09942ddedcb960f9e78fd817ec33f501d1040c5b
change-id: 20260317-tls-read-sock-a0022c9df265
Best regards,
--
Chuck Lever
^ permalink raw reply
* [PATCH net-next v9 1/5] tls: Abort the connection on decrypt failure
From: Chuck Lever @ 2026-04-29 21:48 UTC (permalink / raw)
To: John Fastabend, Jakub Kicinski, Sabrina Dubroca
Cc: Eric Dumazet, Simon Horman, Paolo Abeni, netdev,
kernel-tls-handshake, Chuck Lever, Hannes Reinecke
In-Reply-To: <20260429-tls-read-sock-v9-0-39e71aa7810f@oracle.com>
From: Chuck Lever <chuck.lever@oracle.com>
recvmsg, read_sock, and splice_read each open-code a
tls_err_abort() call after tls_rx_one_record() fails. Move
the abort into tls_rx_one_record() so each receive path
shares a single decrypt-and-abort sequence.
A tls_check_pending_rekey() failure after successful
decryption no longer triggers tls_err_abort(). That path
fires only when skb_copy_bits() fails on a valid skb,
which is not a realistic scenario.
Suggested-by: Sabrina Dubroca <sd@queasysnail.net>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
net/tls/tls_sw.c | 19 +++++++++----------
1 file changed, 9 insertions(+), 10 deletions(-)
diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
index 94d2ae0daa8c..244ac8ed4b01 100644
--- a/net/tls/tls_sw.c
+++ b/net/tls/tls_sw.c
@@ -1809,6 +1809,9 @@ static int tls_check_pending_rekey(struct sock *sk, struct tls_context *ctx,
return 0;
}
+/* Decrypt and return one TLS record. On decrypt failure the connection is
+ * aborted (sk_err set) before returning a negative errno.
+ */
static int tls_rx_one_record(struct sock *sk, struct msghdr *msg,
struct tls_decrypt_arg *darg)
{
@@ -1820,8 +1823,10 @@ static int tls_rx_one_record(struct sock *sk, struct msghdr *msg,
err = tls_decrypt_device(sk, msg, tls_ctx, darg);
if (!err)
err = tls_decrypt_sw(sk, tls_ctx, msg, darg);
- if (err < 0)
+ if (err < 0) {
+ tls_err_abort(sk, -EBADMSG);
return err;
+ }
rxm = strp_msg(darg->skb);
rxm->offset += prot->prepend_size;
@@ -2132,10 +2137,8 @@ int tls_sw_recvmsg(struct sock *sk,
darg.async = false;
err = tls_rx_one_record(sk, msg, &darg);
- if (err < 0) {
- tls_err_abort(sk, -EBADMSG);
+ if (err < 0)
goto recv_end;
- }
async |= darg.async;
@@ -2294,10 +2297,8 @@ ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos,
memset(&darg.inargs, 0, sizeof(darg.inargs));
err = tls_rx_one_record(sk, NULL, &darg);
- if (err < 0) {
- tls_err_abort(sk, -EBADMSG);
+ if (err < 0)
goto splice_read_end;
- }
tls_rx_rec_done(ctx);
skb = darg.skb;
@@ -2380,10 +2381,8 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc,
memset(&darg.inargs, 0, sizeof(darg.inargs));
err = tls_rx_one_record(sk, NULL, &darg);
- if (err < 0) {
- tls_err_abort(sk, -EBADMSG);
+ if (err < 0)
goto read_sock_end;
- }
released = tls_read_flush_backlog(sk, prot, INT_MAX,
0, decrypted,
--
2.53.0
^ permalink raw reply related
* [PATCH net-next v9 2/5] tls: Fix dangling skb pointer in tls_sw_read_sock()
From: Chuck Lever @ 2026-04-29 21:48 UTC (permalink / raw)
To: John Fastabend, Jakub Kicinski, Sabrina Dubroca
Cc: Eric Dumazet, Simon Horman, Paolo Abeni, netdev,
kernel-tls-handshake, Chuck Lever, Hannes Reinecke,
Alistair Francis
In-Reply-To: <20260429-tls-read-sock-v9-0-39e71aa7810f@oracle.com>
From: Chuck Lever <chuck.lever@oracle.com>
Two related defects in the receive loop of tls_sw_read_sock()
share a single fix.
Per ISO/IEC 9899:2011 section 6.2.4p2, a pointer value becomes
indeterminate when the object it points to reaches the end of
its lifetime; Annex J.2 classifies the use of such a value as
undefined behavior. consume_skb(skb) in the fully-consumed path
frees the skb, but the "do { } while (skb)" loop condition then
evaluates that freed pointer. Although the value is never
dereferenced -- the loop either continues and overwrites skb,
or exits -- any future change that adds a dereference between
consume_skb() and the loop condition would produce a silent
use-after-free.
Separately, when read_actor() consumes only part of a record
(used < rxm->full_len) but desc->count is still non-zero, the
existing code updates rxm in place and falls through to the
next loop iteration. The next iteration then unconditionally
overwrites skb without freeing or requeuing the partially
consumed buffer, leaking the skb and silently dropping stream
data.
A read_actor returning fewer bytes than offered is in every
case a backpressure signal; the only correct response is to
requeue and exit. Replace the do/while with an explicit
for(;;), requeue unconditionally on partial consume, and break
on exhausted desc->count after a full consume.
Fixes: 662fbcec32f4 ("net/tls: implement ->read_sock()")
Reviewed-by: Hannes Reinecke <hare@suse.de>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
net/tls/tls_sw.c | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
index 244ac8ed4b01..c58d3b0b0a8a 100644
--- a/net/tls/tls_sw.c
+++ b/net/tls/tls_sw.c
@@ -2366,7 +2366,7 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc,
goto read_sock_end;
decrypted = 0;
- do {
+ for (;;) {
if (!skb_queue_empty(&ctx->rx_list)) {
skb = __skb_dequeue(&ctx->rx_list);
rxm = strp_msg(skb);
@@ -2411,14 +2411,13 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc,
if (used < rxm->full_len) {
rxm->offset += used;
rxm->full_len -= used;
- if (!desc->count)
- goto read_sock_requeue;
- } else {
- consume_skb(skb);
- if (!desc->count)
- skb = NULL;
+ goto read_sock_requeue;
}
- } while (skb);
+ consume_skb(skb);
+ skb = NULL;
+ if (!desc->count)
+ break;
+ }
read_sock_end:
tls_rx_reader_release(sk, ctx);
--
2.53.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox