* Re: [PATCH v7 02/12] cpumask: Introduce cpu_preferred_mask
From: Shrikanth Hegde @ 2026-07-14 6:30 UTC (permalink / raw)
To: Yury Norov
Cc: linux-kernel, mingo, peterz, juri.lelli, vincent.guittot,
yury.norov, kprateek.nayak, iii, corbet, tglx, gregkh, pbonzini,
seanjc, vschneid, huschle, rostedt, dietmar.eggemann, maddy,
srikar, hdanton, chleroy, vineeth, frederic, arighi, pauld,
christian.loehle, tj, tommaso.cucinotta, maz, rafael, rdunlap,
kernellwp, linux-doc
In-Reply-To: <alT78Nzt3xa-7G5Y@yury>
Hi Yury,
On 7/13/26 8:23 PM, Yury Norov wrote:
> On Fri, Jul 10, 2026 at 03:26:38AM +0530, Shrikanth Hegde wrote:
>> Provide cpu_preferred_mask infrastructure. Define get/set macros
>> which could be used to get/set CPU state as preferred.
>>
>> PREFERRED_CPU config will be selected by the driver which handles
>> steal time values. It is going to set/clear preferred CPU state.
>> This driver will be called steal_monitor and it is introduced in
>> subsequent patches. It periodically samples the steal time and
>> decides on preferred CPU state.
>>
>> A CPU is set to preferred when it becomes active. Later it may be
>> marked as non-preferred depending on steal time values with
>> steal_monitor being enabled.
>>
>> Always maintain design construct of preferred is subset of active.
>> i.e. preferred ⊆ active ⊆ online ⊆ present ⊆ possible
>>
>> With PREFERRED_CPU=n, ensure set_cpu_preferred is a nop and get
>> method returns the active state in that case.
>>
>> Signed-off-by: Shrikanth Hegde <sshegde@linux.ibm.com>
>> ---
>> v6->v7:
>> - removed CONFIG_PREFERRED_CPU as user option.
>> - Use do { } while (0) for nop
>>
>> include/linux/cpumask.h | 24 ++++++++++++++++++++++++
>> kernel/Kconfig.preempt | 3 +++
>> kernel/cpu.c | 6 ++++++
>> kernel/sched/core.c | 5 +++++
>> 4 files changed, 38 insertions(+)
>>
>> diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h
>> index d3cda0544954..34d08a3d80e1 100644
>> --- a/include/linux/cpumask.h
>> +++ b/include/linux/cpumask.h
>> @@ -122,12 +122,20 @@ extern struct cpumask __cpu_enabled_mask;
>> extern struct cpumask __cpu_present_mask;
>> extern struct cpumask __cpu_active_mask;
>> extern struct cpumask __cpu_dying_mask;
>> +
>> +#ifdef CONFIG_PREFERRED_CPU
>> +extern struct cpumask __cpu_preferred_mask;
>> +#else
>> +#define __cpu_preferred_mask __cpu_active_mask
>> +#endif
>> +
>> #define cpu_possible_mask ((const struct cpumask *)&__cpu_possible_mask)
>> #define cpu_online_mask ((const struct cpumask *)&__cpu_online_mask)
>> #define cpu_enabled_mask ((const struct cpumask *)&__cpu_enabled_mask)
>> #define cpu_present_mask ((const struct cpumask *)&__cpu_present_mask)
>> #define cpu_active_mask ((const struct cpumask *)&__cpu_active_mask)
>> #define cpu_dying_mask ((const struct cpumask *)&__cpu_dying_mask)
>> +#define cpu_preferred_mask ((const struct cpumask *)&__cpu_preferred_mask)
>>
>> extern atomic_t __num_online_cpus;
>> extern unsigned int __num_possible_cpus;
>> @@ -1164,6 +1172,12 @@ void init_cpu_possible(const struct cpumask *src);
>> #define set_cpu_active(cpu, active) assign_cpu((cpu), &__cpu_active_mask, (active))
>> #define set_cpu_dying(cpu, dying) assign_cpu((cpu), &__cpu_dying_mask, (dying))
>>
>> +#ifdef CONFIG_PREFERRED_CPU
>> +#define set_cpu_preferred(cpu, preferred) assign_cpu((cpu), &__cpu_preferred_mask, (preferred))
>> +#else
>> +#define set_cpu_preferred(cpu, preferred) do { } while (0)
>> +#endif
>> +
>> void set_cpu_online(unsigned int cpu, bool online);
>> void set_cpu_possible(unsigned int cpu, bool possible);
>>
>> @@ -1258,6 +1272,11 @@ static __always_inline bool cpu_dying(unsigned int cpu)
>> return cpumask_test_cpu(cpu, cpu_dying_mask);
>> }
>>
>> +static __always_inline bool cpu_preferred(unsigned int cpu)
>> +{
>> + return cpumask_test_cpu(cpu, cpu_preferred_mask);
>> +}
>> +
>> #else
>>
>> #define num_online_cpus() 1U
>> @@ -1296,6 +1315,11 @@ static __always_inline bool cpu_dying(unsigned int cpu)
>> return false;
>> }
>>
>> +static __always_inline bool cpu_preferred(unsigned int cpu)
>> +{
>> + return cpu == 0;
>> +}
>> +
>> #endif /* NR_CPUS > 1 */
>>
>> #define cpu_is_offline(cpu) unlikely(!cpu_online(cpu))
>> diff --git a/kernel/Kconfig.preempt b/kernel/Kconfig.preempt
>> index 88c594c6d7fc..ed02e4431230 100644
>> --- a/kernel/Kconfig.preempt
>> +++ b/kernel/Kconfig.preempt
>> @@ -192,3 +192,6 @@ config SCHED_CLASS_EXT
>> For more information:
>> Documentation/scheduler/sched-ext.rst
>> https://github.com/sched-ext/scx
>> +
>> +config PREFERRED_CPU
>> + bool
>
> This still should depend on PARAVIRT and SMP. And maybe to enforce it
> even stronger, your driver should fail to build if PREFERRED_CPU is
> disabled. Imagine a scenario when someone makes PREFERRED_CPU
> depending on some other config, but doesn't modify your driver. That
> way you'll build the STEAL_MONITOR successfully, but because
> PREFERRED_CPU is off, you'll end up with non-working functionality at
> best, or corrupted cpu_active_mask at worst.
>
Sorry, i may not understand all the intricacies of kconfigs.
But, Since driver selects PREFERRED_CPU, and PREFERRED_CPU can't be enabled
individually, driver again can't depend on PREFERRED_CPU right?
As per previous discussion, it is probably better that driver selects PREFERRED_CPU.
Keeping them both independent and selectable brings too many variations.
No?
I guess you meant below.
In kernel/Kconfig.preempt:
config PREFERRED_CPU
bool
depends on SMP && PARAVIRT
Driver's Kconfig (this is there already)
config VIRT_STEAL_GOVERNOR
tristate "Virtual Steal Time Governor"
depends on SMP && PARAVIRT
select PREFERRED_CPU
> Also, the name 'steal monitor' implies monitoring, while in fact
> you're actively affecting the scheduling process.
>
> Maybe 'steal governor'?
>
Make sense. Will do.
> Thanks,
> Yury
^ permalink raw reply
* Re: [PATCH v5 4/6] mm/zswap: Implement proactive writeback
From: Hao Jia @ 2026-07-14 6:29 UTC (permalink / raw)
To: Yosry Ahmed
Cc: akpm, tj, hannes, shakeel.butt, mhocko, mkoutny, nphamcs,
chengming.zhou, muchun.song, roman.gushchin, linux-mm,
linux-kernel, linux-doc, Hao Jia
In-Reply-To: <CAO9r8zNu=JPZG4be9beJUdBEGRgX6YaD_cpZw2P8WieDY=c06g@mail.gmail.com>
On 2026/7/13 23:53, Yosry Ahmed wrote:
> On Fri, Jul 10, 2026 at 3:04 AM Hao Jia <jiahao.kernel@gmail.com> wrote:
>>
>>
>>
>> On 2026/7/10 04:44, Yosry Ahmed wrote:
>>> On Wed, Jul 8, 2026 at 7:15 AM Hao Jia <jiahao.kernel@gmail.com> wrote:
>>>>
>>>>
>>>>
>>>> On 2026/7/7 03:33, Yosry Ahmed wrote:
>>>>> On Thu, Jul 2, 2026 at 5:32 AM Hao Jia <jiahao.kernel@gmail.com> wrote:
>>>>>>
>>>>>>
>>>>>>
>>>>>> On 2026/7/1 19:45, Hao Jia wrote:
>>>>>>>
>>>>>>>
>>>>>>> On 2026/7/1 00:10, Yosry Ahmed wrote:
>>>>>>>>>> Before going through more versions we need to figure out if this will
>>>>>>>>>> pivot to be a proactive demotion interfcae for swap tiering.
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Yes. Should I drop patches 4-6 in the next version and wait for swap
>>>>>>>>> tiering to be finalized?
>>>>>>>>> We can try to get the non-memcg parts (patches 1-3) merged upstream
>>>>>>>>> first. This would also give them plenty of time to bake and catch any
>>>>>>>>> potential regressions. Thoughts?
>>>>>>>>
>>>>>>>> Patches 1-2 can be sent and merged separately, yes. For patch 2,
>>>>>>>> please include some numbers for the writeback performance before and
>>>>>>>> after batching.
>>>>>>>
>>>>>>> I'd love to collect some performance data. Do you have any recommended
>>>>>>> benchmarks for this?
>>>>>>>
>>>>>>
>>>>>> Perhaps the following test case could work?
>>>>>>
>>>>>> Test Setup:
>>>>>> - Total memory: 32 GB
>>>>>> - zswap settings: max_pool_percent=1, accept_threshold_percent=50,
>>>>>> shrinker_enabled=N
>>>>>> - cgroup constraint: memory.max=1G
>>>>>> - Workload: Run the following stress-ng command inside the cgroup for
>>>>>> 120s to
>>>>>> continuously force zswap store failures and trigger shrink_worker():
>>>>>>
>>>>>> bash -c 'echo $$ > /sys/fs/cgroup/zswaptest/cgroup.procs ; \
>>>>>> exec stress-ng --vm 4 --vm-bytes 4G --vm-keep --vm-method rand-set -t
>>>>>> 120s -q'
>>>>>>
>>>>>> The following comparison results were collected over multiple runs via
>>>>>> bpftrace
>>>>>> and the 'written_back_pages' sysfs interface:
>>>>>>
>>>>>> Baseline Patched
>>>>>> ---------------------------------------------------
>>>>>> shrink_worker wakeups 5,587 878
>>>>>> shrink_memcg calls 7,823,853 2,347,320
>>>>>> written_back 257 781,214
>>>>>>
>>>>>> Conclusion:
>>>>>> Under the same workload and duration, the patched kernel shows a
>>>>>> significant reduction
>>>>>> in both shrink_worker wakeups and shrink_memcg calls, while successfully
>>>>>> executing a
>>>>>> much higher volume of page writebacks.
>>>>>
>>>>> Hmm this is actually a bit concerning. Yes, we are invoking the
>>>>> shrinker less, but we're writing back *a lot* more memory, orders of
>>>>> magnitude more. We are using a batch size of 64, and making ~1/3 of
>>>>> the calls to shrink_memcg(), so the number of written back pages
>>>>> should be ~20x more, not 3000x more? I think I am missing something.
>>>>>
>>>>> Also, ideally, the batching wouldn't result in significantly more
>>>>> writeback, but a similar amount of writeback over less shrinker
>>>>> invocations. If we are writing back significantly more pages then the
>>>>> batching logic is probably too aggressive?
>>>>
>>>> Apologies, I think the test I constructed has a bit of a problem. This
>>>> test has very, very heavy memory pressure and is already a very abnormal
>>>> case.
>>>>
>>>> The zswap entry returns the first time because of "second chance" after
>>>> setting referenced to false. For the baseline, it scans 1 page per node
>>>> each time for 16 loops. During the test, shrink_worker() basically exits
>>>> at about 16 pages each time.
>>>>
>>>> Since stress-ng periodically and randomly writes to this 4G memory, it
>>>> keeps triggering zswapin and then waiting to zswapout new zswap entries
>>>> after falling below the pool threshold. When the speed of zswapin/out is
>>>> far greater than the scanning speed of shrink_worker(), a large number
>>>> of zswap entries cannot wait until the second scan for writeback. New
>>>> entries are stored on the zswap LRU list again, and the referenced of
>>>> the new zswap entries is set to true again. During the test, it was
>>>> found that 99.21% of the return values of shrink_memcg_cb() in the
>>>> baseline kernel were LRU_ROTATE.
>>>
>>> Hmm if I understand correctly, you are saying that the current
>>> upstream code is actually failing to writeback when it should in the
>>> previous test case with very high memory pressure, but it is with
>>> batching? If that's the case, I think it's actually really good data
>>> to include. However, we should make sure that's what's actually
>>> happening. If the current shrinker is not keeping up and failing to
>>> writeback, we should observe:
>>> 1. shrink_worker() hitting MAX_RECLAIM_RETRIES continuously and bailing.
>>> 2. zswap usage consistently remains at/near the limit, and not going
>>> down to the acceptance threshold.
>>> 3. zswap_store() failing to accept pages and the pages going directly
>>> to disk, causing an LRU inversion (hotter pages on disk, colder pages
>>> in zswap).
>>>
>>> Can you confirm that this is what's observed with the high pressure test case?
>>>
>>
>> Apologies, my previous explanation might not have been very clear.
>>
>> For an entry to be written back, the shrinker must scan the *same* entry
>> twice: the first scan sets referenced to false and returns ROTATE, and
>> only during the second scan can it be written back.
>>
>> If a swap entry is zswapin'd between the first and second scan (meaning
>> the entry is no longer on the zswap LRU), then this swap entry will not
>> be written back by the shrinker. Therefore, the second scan must occur
>> before this entry is zswapin'd for it to be possible to be written back.
>> So, if the baseline scanning speed is far slower than the lifecycle
>> speed of the swap entries, it results in only scanning once. In the
>> baseline kernel, 99.21% of the return values of shrink_memcg_cb() are
>> LRU_ROTATE, while the patched kernel's shrink_worker() scans at least 64
>> * 16 entries in a single pass, resulting in only 58.7% of the return
>> values of shrink_memcg_cb() being LRU_ROTATE.
>
> Right, my question is, is the high rate of LRU_ROTATE leading to
> failure to writeback in a way that causes zswap store failures (and
> pages skipping zswap and going directly to swap)?
Yes, I observed that the number of zswap_store() failures (returning
false) is very high.
Thanks,
Hao
^ permalink raw reply
* Re: [PATCH v2] docs: zh_TW: process: localize terminologies and improve fluency in 8.Conclusion
From: Alex Shi @ 2026-07-14 6:22 UTC (permalink / raw)
To: 葉宸佑, Weijie Yuan
Cc: Dongliang Mu, Hu Haowen, Jonathan Corbet, Shuah Khan,
Dongliang Mu, linux-doc, linux-kernel, Yuchen Tian, Alex Shi,
Yanteng Si
In-Reply-To: <CAKspUhJTGXzM=UeKTTZYX69NttBuCnAezz=ZOM9imPWLSP5g9A@mail.gmail.com>
On 2026/7/14 03:47, 葉宸佑 wrote:
> Weijie: agreed, a zh_TW how-to (mirroring the zh_CN one, with the
> glossary referenced) looks like the natural follow-up once the first
> series settles the terminology. Adding it to the list.
>
> So, to keep everything in one place, my understanding of the plan:
>
> - Chen-Yu: terminology series for process/ (14 files), folding in the
> pending 8.Conclusion changes, glossary included; adopt the
> "update to commit HASH" convention from now on
> - Chen-Yu: read Jon's advice for new-language efforts (Spanish thread)
> - later: a zh_TW how-to document
> - Weijie: investigate which documents may not need translation;
> monitor the CN/TW lists during the trial period
> - Dongliang: review; patches routed through Alex's tree (pending
> Alex's confirmation)
It's ok. I will pick up tested and reviewed commit into my Chinese
documents tree.
>
> If I got anything wrong, please correct me -- otherwise I will get
> started on the series.
>
> Thanks,
^ permalink raw reply
* [PATCH net-next V6 0/4] devlink: Add boot-time eswitch mode defaults
From: Mark Bloch @ 2026-07-14 6:17 UTC (permalink / raw)
To: Jiri Pirko, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman
Cc: Saeed Mahameed, Leon Romanovsky, Tariq Toukan, Andrew Lunn,
Jonathan Corbet, Shuah Khan, netdev, linux-rdma, linux-doc,
Mark Bloch
This series adds a devlink_eswitch_mode= kernel command line parameter
for setting a default devlink eswitch mode during boot.
Following the discussion with Jakub[1] and the feedback on the RFC
postings, this version keeps the scope limited to a boot-time devlink
eswitch mode default only.
The option selects either all devlink handles or an explicit
comma-separated handle list:
devlink_eswitch_mode=*=switchdev
devlink_eswitch_mode=pci/0000:08:00.0,pci/0000:09:00.1=switchdev_inactive
The supported modes are legacy, switchdev and switchdev_inactive. The
selected mode is applied through the existing eswitch_mode_set() devlink
operation, the same operation used by the devlink eswitch mode command.
Registration may happen while a driver holds the devlink lock and
continues device initialization. Devlink core marks the default as
pending and applies it from devl_unlock() once the instance is
registered, before releasing the lock. This prevents userspace from
racing with the boot default.
After a successful reload that performed DRIVER_REINIT, devlink core
already holds the devlink instance lock and the driver completed
reload_up(), so the default is applied directly from the reload path.
Patch 1 clears the mlx5 FW reset-in-progress bit before reload.
Patch 2 factors the common eswitch mode set validation into a helper.
Patch 3 adds the devlink_eswitch_mode= parser and documentation.
Patch 4 applies parsed defaults from devlink core.
Changelog:
v5 -> v6:
- Dropped regular work and applied a pending default directly from
devl_unlock() while the devlink instance lock is still held.
- Dropped the driver API and mlx5-specific default application patches.
v4 -> v5:
- Moved the default eswitch mode code into a separate file, per Jiri's
comment.
- Dropped the delayed workqueue and switched to regular work triggered
via devl_unlock(), per Jiri's comment.
- Renamed some functions to better align with devlink code.
v3 -> v4:
- Rework registration time apply to use per devlink work queued from
devl_unlock(), instead of calling eswitch_mode_set() directly from
devl_register().
- Apply the default directly after successful DRIVER_REINIT devlink reload,
where the devlink lock is already held and reload_up() has completed.
- Add devl_apply_default_esw_mode() for drivers that know their exact ready
point.
- Drop the driver registration-ordering preparation patches that are no
longer needed with the async registration apply path.
v2 -> v3:
- Change the devlink_eswitch_mode= API syntax to use <selector>=<mode>
instead of [<selector>]:<mode>, following a comment from Randy Dunlap.
v1 -> v2:
- Move default eswitch mode application into devlink core. The default is
now applied during devlink registration and after a successful devlink
reload that performed DRIVER_REINIT.
- Remove the exported devl_apply_default_esw_mode() driver API and the mlx5
driver-side call to it.
- Skip devlink health recovery notifications while the devlink instance is
not registered, so drivers can move registration later without early
health work hitting registration assertions.
- Move mlx5 devlink registration after device initialization, including the
lightweight init path, so the core can apply the default through the
normal registration flow.
- Move the matching netdevsim and mlx5 unregister paths before object
teardown, so unregister notifications come from devl_unregister() and the
later object teardown paths run while the devlink instance is no longer
registered.
- Add registration-ordering preparation patches for netdevsim and octeontx2
AF/PF, so their eswitch state is ready before registration-time defaults
may call eswitch_mode_set().
[1] lore.kernel.org/r/20260502184153.4fd8d06f@kernel.org/
RFC v1: lore.kernel.org/r/20260506123739.1959770-1-mbloch@nvidia.com/
RFC v2: lore.kernel.org/r/20260510185424.2041415-1-mbloch@nvidia.com/
v1: lore.kernel.org/r/20260521072434.362624-1-tariqt@nvidia.com/
v2: lore.kernel.org/all/20260603193259.3412464-1-mbloch@nvidia.com/
v3: lore.kernel.org/all/20260605181030.3486619-1-mbloch@nvidia.com/
v4: lore.kernel.org/all/20260629182102.245150-1-mbloch@nvidia.com/
v5: lore.kernel.org/all/20260707174527.425134-1-mbloch@nvidia.com/
Mark Bloch (4):
net/mlx5: Clear FW reset-in-progress bit before reload
devlink: Factor out eswitch mode setting
devlink: Parse eswitch mode boot defaults
devlink: Apply eswitch mode boot defaults
.../admin-guide/kernel-parameters.txt | 25 ++
.../networking/devlink/devlink-defaults.rst | 78 +++++
Documentation/networking/devlink/index.rst | 1 +
.../ethernet/mellanox/mlx5/core/fw_reset.c | 28 +-
net/devlink/Makefile | 2 +-
net/devlink/core.c | 10 +
net/devlink/default.c | 303 ++++++++++++++++++
net/devlink/dev.c | 33 +-
net/devlink/devl_internal.h | 10 +
9 files changed, 471 insertions(+), 19 deletions(-)
create mode 100644 Documentation/networking/devlink/devlink-defaults.rst
create mode 100644 net/devlink/default.c
base-commit: f6f3b36c15ed44de1fbb44e645e4fae8c4a4453e
--
2.43.0
^ permalink raw reply
* [PATCH net-next V6 4/4] devlink: Apply eswitch mode boot defaults
From: Mark Bloch @ 2026-07-14 6:17 UTC (permalink / raw)
To: Jiri Pirko, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman
Cc: Saeed Mahameed, Leon Romanovsky, Tariq Toukan, Andrew Lunn,
Jonathan Corbet, Shuah Khan, netdev, linux-rdma, linux-doc,
Mark Bloch
In-Reply-To: <20260714061731.531849-1-mbloch@nvidia.com>
Apply parsed devlink_eswitch_mode= defaults after devlink registration
and after successful reload.
Mark the default mode as pending when a devlink instance is allocated.
Before devl_unlock() releases the instance lock, apply a pending default
when the instance is registered.
Clear the pending state before calling into the driver so the boot
default remains a one-shot operation even if the mode change fails.
For successful reloads that performed DRIVER_REINIT, devlink_reload()
already holds the devlink instance lock and the driver has completed
reload_up(). Clear the pending state and apply the default directly from
the reload path.
Treat an explicit user eswitch mode request as consuming the pending
default mode.
Signed-off-by: Mark Bloch <mbloch@nvidia.com>
---
net/devlink/core.c | 3 ++
net/devlink/default.c | 70 +++++++++++++++++++++++++++++++++++--
net/devlink/dev.c | 6 ++++
net/devlink/devl_internal.h | 5 +++
4 files changed, 82 insertions(+), 2 deletions(-)
diff --git a/net/devlink/core.c b/net/devlink/core.c
index fc14ee5d9dcf..cea4afc27dd9 100644
--- a/net/devlink/core.c
+++ b/net/devlink/core.c
@@ -317,6 +317,7 @@ EXPORT_SYMBOL_GPL(devl_trylock);
void devl_unlock(struct devlink *devlink)
{
+ devlink_default_esw_mode_apply_pending(devlink);
mutex_unlock(&devlink->lock);
}
EXPORT_SYMBOL_GPL(devl_unlock);
@@ -429,6 +430,7 @@ void devl_unregister(struct devlink *devlink)
ASSERT_DEVLINK_REGISTERED(devlink);
devl_assert_locked(devlink);
+ devlink_default_esw_mode_apply_pending_clear(devlink);
devlink_notify_unregister(devlink);
xa_clear_mark(&devlinks, devlink->index, DEVLINK_REGISTERED);
devlink_rel_put(devlink);
@@ -490,6 +492,7 @@ struct devlink *__devlink_alloc(const struct devlink_ops *ops, size_t priv_size,
INIT_LIST_HEAD(&devlink->trap_group_list);
INIT_LIST_HEAD(&devlink->trap_policer_list);
INIT_RCU_WORK(&devlink->rwork, devlink_release);
+ devlink_default_esw_mode_instance_init(devlink);
lockdep_register_key(&devlink->lock_key);
mutex_init(&devlink->lock);
lockdep_set_class(&devlink->lock, &devlink->lock_key);
diff --git a/net/devlink/default.c b/net/devlink/default.c
index 8434af83ea69..77cc356dfac9 100644
--- a/net/devlink/default.c
+++ b/net/devlink/default.c
@@ -10,6 +10,7 @@
static char *devlink_default_esw_mode_param;
static bool devlink_default_esw_mode_match_all;
+static bool devlink_default_esw_mode_enabled;
static enum devlink_eswitch_mode devlink_default_esw_mode;
static LIST_HEAD(devlink_default_esw_mode_nodes);
@@ -154,6 +155,7 @@ static void __init devlink_default_esw_mode_nodes_clear(void)
}
devlink_default_esw_mode_match_all = false;
+ devlink_default_esw_mode_enabled = false;
}
static int __init devlink_default_esw_mode_parse(char *str)
@@ -180,14 +182,78 @@ static int __init devlink_default_esw_mode_parse(char *str)
return err;
err = devlink_default_esw_mode_handles_parse(handles);
- if (err)
+ if (err) {
devlink_default_esw_mode_nodes_clear();
- else
+ } else {
devlink_default_esw_mode = esw_mode;
+ devlink_default_esw_mode_enabled = true;
+ }
return err;
}
+static bool devlink_default_esw_mode_match(struct devlink *devlink)
+{
+ const char *bus_name = devlink_bus_name(devlink);
+ const char *dev_name = devlink_dev_name(devlink);
+ struct devlink_default_esw_mode_node *node;
+
+ if (devlink_default_esw_mode_match_all)
+ return true;
+
+ node = devlink_default_esw_mode_node_find(bus_name, dev_name);
+ return !!node;
+}
+
+void devlink_default_esw_mode_apply_locked(struct devlink *devlink)
+{
+ const struct devlink_ops *ops = devlink->ops;
+ int err;
+
+ devl_assert_locked(devlink);
+
+ if (!devlink_default_esw_mode_match(devlink))
+ return;
+
+ if (!ops->eswitch_mode_set) {
+ if (!devlink_default_esw_mode_match_all)
+ devl_warn(devlink,
+ "devlink_eswitch_mode= selected this device but eswitch mode setting is not supported\n");
+ return;
+ }
+
+ err = devlink_eswitch_mode_set(devlink, devlink_default_esw_mode, NULL);
+ if (err)
+ devl_warn(devlink,
+ "Couldn't apply default eswitch mode, err %d\n",
+ err);
+}
+
+void devlink_default_esw_mode_apply_pending(struct devlink *devlink)
+{
+ devl_assert_locked(devlink);
+
+ if (!devlink->default_esw_mode_apply_pending ||
+ !__devl_is_registered(devlink))
+ return;
+
+ devlink->default_esw_mode_apply_pending = false;
+ devlink_default_esw_mode_apply_locked(devlink);
+}
+
+void devlink_default_esw_mode_instance_init(struct devlink *devlink)
+{
+ devlink->default_esw_mode_apply_pending =
+ devlink_default_esw_mode_enabled;
+}
+
+void devlink_default_esw_mode_apply_pending_clear(struct devlink *devlink)
+{
+ devl_assert_locked(devlink);
+
+ devlink->default_esw_mode_apply_pending = false;
+}
+
static int __init devlink_default_esw_mode_setup(char *str)
{
devlink_default_esw_mode_param = str;
diff --git a/net/devlink/dev.c b/net/devlink/dev.c
index 119ef105d0a7..611bb6bfd492 100644
--- a/net/devlink/dev.c
+++ b/net/devlink/dev.c
@@ -478,6 +478,11 @@ int devlink_reload(struct devlink *devlink, struct net *dest_net,
return err;
WARN_ON(!(*actions_performed & BIT(action)));
+ if (*actions_performed & BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT)) {
+ devlink_default_esw_mode_apply_pending_clear(devlink);
+ devlink_default_esw_mode_apply_locked(devlink);
+ }
+
/* Catch driver on updating the remote action within devlink reload */
WARN_ON(memcmp(remote_reload_stats, devlink->stats.remote_reload_stats,
sizeof(remote_reload_stats)));
@@ -731,6 +736,7 @@ int devlink_nl_eswitch_set_doit(struct sk_buff *skb, struct genl_info *info)
u16 mode;
if (info->attrs[DEVLINK_ATTR_ESWITCH_MODE]) {
+ devlink_default_esw_mode_apply_pending_clear(devlink);
mode = nla_get_u16(info->attrs[DEVLINK_ATTR_ESWITCH_MODE]);
err = devlink_eswitch_mode_set(devlink, mode, info->extack);
if (err)
diff --git a/net/devlink/devl_internal.h b/net/devlink/devl_internal.h
index fe9ad58515d4..97f53394b1c0 100644
--- a/net/devlink/devl_internal.h
+++ b/net/devlink/devl_internal.h
@@ -58,6 +58,7 @@ struct devlink {
struct mutex lock;
struct lock_class_key lock_key;
u8 reload_failed:1;
+ u8 default_esw_mode_apply_pending:1;
refcount_t refcount;
struct rcu_work rwork;
struct devlink_rel *rel;
@@ -73,6 +74,10 @@ struct devlink *__devlink_alloc(const struct devlink_ops *ops, size_t priv_size,
const struct device_driver *dev_driver);
int devlink_default_esw_mode_init(void);
void devlink_default_esw_mode_cleanup(void);
+void devlink_default_esw_mode_instance_init(struct devlink *devlink);
+void devlink_default_esw_mode_apply_locked(struct devlink *devlink);
+void devlink_default_esw_mode_apply_pending(struct devlink *devlink);
+void devlink_default_esw_mode_apply_pending_clear(struct devlink *devlink);
#define devl_warn(devlink, format, args...) \
do { \
--
2.43.0
^ permalink raw reply related
* [PATCH net-next V6 3/4] devlink: Parse eswitch mode boot defaults
From: Mark Bloch @ 2026-07-14 6:17 UTC (permalink / raw)
To: Jiri Pirko, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman
Cc: Saeed Mahameed, Leon Romanovsky, Tariq Toukan, Andrew Lunn,
Jonathan Corbet, Shuah Khan, netdev, linux-rdma, linux-doc,
Mark Bloch
In-Reply-To: <20260714061731.531849-1-mbloch@nvidia.com>
Add devlink_eswitch_mode= kernel command line parsing for a default
eswitch mode.
The supported syntax selects either all devlink handles or one explicit
comma-separated handle list:
devlink_eswitch_mode=*=<mode>
devlink_eswitch_mode=<handle>[,<handle>...]=<mode>
where <mode> is one of legacy, switchdev or switchdev_inactive. All
selected handles receive the same mode. Assigning different modes to
different handle lists in the same parameter value is not supported.
Store the parsed selector and mode in devlink core so the default can be
applied by a downstream patch.
Document the devlink_eswitch_mode= syntax and duplicate handle handling.
Signed-off-by: Mark Bloch <mbloch@nvidia.com>
---
.../admin-guide/kernel-parameters.txt | 25 ++
.../networking/devlink/devlink-defaults.rst | 78 ++++++
Documentation/networking/devlink/index.rst | 1 +
net/devlink/Makefile | 2 +-
net/devlink/core.c | 7 +
net/devlink/default.c | 237 ++++++++++++++++++
net/devlink/devl_internal.h | 2 +
7 files changed, 351 insertions(+), 1 deletion(-)
create mode 100644 Documentation/networking/devlink/devlink-defaults.rst
create mode 100644 net/devlink/default.c
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index b5493a7f8f22..117300dd589c 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -1249,6 +1249,31 @@ Kernel parameters
dell_smm_hwmon.fan_max=
[HW] Maximum configurable fan speed.
+ devlink_eswitch_mode=
+ [NET]
+ Format:
+ <selector>=<mode>
+
+ <selector>:
+ * | <handle>[,<handle>...]
+
+ <handle>:
+ <bus-name>/<dev-name>
+
+ Configure default devlink eswitch mode for matching
+ devlink instances during device initialization.
+
+ <mode>:
+ legacy | switchdev | switchdev_inactive
+
+ Examples:
+ devlink_eswitch_mode=*=switchdev
+ devlink_eswitch_mode=pci/0000:08:00.0=switchdev
+ devlink_eswitch_mode=pci/0000:08:00.0,pci/0000:09:00.1=switchdev_inactive
+
+ See Documentation/networking/devlink/devlink-defaults.rst
+ for the full syntax.
+
dfltcc= [HW,S390]
Format: { on | off | def_only | inf_only | always }
on: s390 zlib hardware support for compression on
diff --git a/Documentation/networking/devlink/devlink-defaults.rst b/Documentation/networking/devlink/devlink-defaults.rst
new file mode 100644
index 000000000000..380c9e99210e
--- /dev/null
+++ b/Documentation/networking/devlink/devlink-defaults.rst
@@ -0,0 +1,78 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==============================
+Devlink Eswitch Mode Defaults
+==============================
+
+Devlink eswitch mode defaults allow the eswitch mode to be provided on the
+kernel command line and applied to matching devlink instances during device
+initialization.
+
+The devlink device is selected by its devlink handle. For PCI devices this is
+the same handle shown by ``devlink dev show``, for example
+``pci/0000:08:00.0``.
+
+Kernel command line syntax
+==========================
+
+Defaults are specified with the ``devlink_eswitch_mode=`` kernel command line
+parameter.
+
+The general syntax is::
+
+ devlink_eswitch_mode=<selector>=<mode>
+
+``<selector>`` is either ``*`` or one or more devlink handles::
+
+ * | <bus-name>/<dev-name>[,<bus-name>/<dev-name>...]
+
+``*`` applies the mode to every devlink instance. All handles in the same
+selector receive the same eswitch mode.
+
+``<mode>`` is one of ``legacy``, ``switchdev`` or ``switchdev_inactive``.
+
+Syntax rules
+------------
+
+The following syntax rules apply:
+
+* Specify the default in one ``devlink_eswitch_mode=`` parameter. Repeated
+ ``devlink_eswitch_mode=`` parameters are not accumulated.
+* The ``devlink_eswitch_mode=`` value is limited by the kernel command line
+ size.
+* Whitespace is not allowed within the parameter value.
+* ``<selector>`` must be either ``*`` or a handle list. ``*`` cannot be
+ combined with explicit handles.
+* ``<bus-name>`` and ``<dev-name>`` must not be empty.
+* ``<dev-name>`` may contain ``:``. This allows PCI names such as
+ ``0000:08:00.0``.
+* Handles must not contain whitespace, ``*``, ``=`` or more than one ``/``.
+* A comma separates handles.
+* Comma-separated default assignments are not supported.
+* Duplicate handles are rejected and the devlink eswitch mode default is
+ ignored.
+
+The eswitch mode default corresponds to the userspace command::
+
+ devlink dev eswitch set <handle> mode <value>
+
+
+Examples
+========
+
+Set all devlink instances to switchdev mode::
+
+ devlink_eswitch_mode=*=switchdev
+
+Set one PCI devlink instance to switchdev mode::
+
+ devlink_eswitch_mode=pci/0000:08:00.0=switchdev
+
+Set two PCI devlink instances to switchdev inactive mode::
+
+ devlink_eswitch_mode=pci/0000:08:00.0,pci/0000:09:00.1=switchdev_inactive
+
+The following is invalid because comma-separated default assignments are not
+supported::
+
+ devlink_eswitch_mode=pci/0000:08:00.0=switchdev,pci/0000:09:00.0=switchdev_inactive
diff --git a/Documentation/networking/devlink/index.rst b/Documentation/networking/devlink/index.rst
index 4745148fecf4..134d2f319922 100644
--- a/Documentation/networking/devlink/index.rst
+++ b/Documentation/networking/devlink/index.rst
@@ -56,6 +56,7 @@ general.
:maxdepth: 1
devlink-dpipe
+ devlink-defaults
devlink-eswitch-attr
devlink-flash
devlink-health
diff --git a/net/devlink/Makefile b/net/devlink/Makefile
index 8f2adb5e5836..99ca0ef7cf1e 100644
--- a/net/devlink/Makefile
+++ b/net/devlink/Makefile
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
-obj-y := core.o netlink.o netlink_gen.o dev.o port.o sb.o dpipe.o \
+obj-y := core.o netlink.o netlink_gen.o dev.o default.o port.o sb.o dpipe.o \
resource.o param.o region.o health.o trap.o rate.o linecard.o sh_dev.o
diff --git a/net/devlink/core.c b/net/devlink/core.c
index c53a42e17a58..fc14ee5d9dcf 100644
--- a/net/devlink/core.c
+++ b/net/devlink/core.c
@@ -598,6 +598,10 @@ static int __init devlink_init(void)
{
int err;
+ err = devlink_default_esw_mode_init();
+ if (err)
+ goto out;
+
err = register_pernet_subsys(&devlink_pernet_ops);
if (err)
goto out;
@@ -613,7 +617,10 @@ static int __init devlink_init(void)
out_unreg_pernet_subsys:
unregister_pernet_subsys(&devlink_pernet_ops);
out:
+ if (err)
+ devlink_default_esw_mode_cleanup();
WARN_ON(err);
+
return err;
}
diff --git a/net/devlink/default.c b/net/devlink/default.c
new file mode 100644
index 000000000000..8434af83ea69
--- /dev/null
+++ b/net/devlink/default.c
@@ -0,0 +1,237 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include "devl_internal.h"
+
+static char *devlink_default_esw_mode_param;
+static bool devlink_default_esw_mode_match_all;
+static enum devlink_eswitch_mode devlink_default_esw_mode;
+static LIST_HEAD(devlink_default_esw_mode_nodes);
+
+struct devlink_default_esw_mode_node {
+ struct list_head list;
+ char *bus_name;
+ char *dev_name;
+};
+
+static int __init
+devlink_default_esw_mode_to_value(const char *str,
+ enum devlink_eswitch_mode *mode)
+{
+ if (!strcmp(str, "legacy")) {
+ *mode = DEVLINK_ESWITCH_MODE_LEGACY;
+ return 0;
+ }
+ if (!strcmp(str, "switchdev")) {
+ *mode = DEVLINK_ESWITCH_MODE_SWITCHDEV;
+ return 0;
+ }
+ if (!strcmp(str, "switchdev_inactive")) {
+ *mode = DEVLINK_ESWITCH_MODE_SWITCHDEV_INACTIVE;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int __init
+devlink_default_esw_mode_handle_parse(char *handle, char **bus_name,
+ char **dev_name)
+{
+ char *slash;
+ char *p;
+
+ if (!*handle)
+ return -EINVAL;
+
+ for (p = handle; *p; p++) {
+ if (*p == '*' || *p == '=')
+ return -EINVAL;
+ }
+
+ slash = strchr(handle, '/');
+ if (!slash || slash == handle || !slash[1])
+ return -EINVAL;
+ if (strchr(slash + 1, '/'))
+ return -EINVAL;
+
+ *slash = '\0';
+
+ *bus_name = handle;
+ *dev_name = slash + 1;
+ return 0;
+}
+
+static struct devlink_default_esw_mode_node *
+devlink_default_esw_mode_node_find(const char *bus_name, const char *dev_name)
+{
+ struct devlink_default_esw_mode_node *node;
+
+ list_for_each_entry(node, &devlink_default_esw_mode_nodes, list) {
+ if (!strcmp(node->bus_name, bus_name) &&
+ !strcmp(node->dev_name, dev_name))
+ return node;
+ }
+
+ return NULL;
+}
+
+static int __init
+devlink_default_esw_mode_node_add(const char *bus_name, const char *dev_name)
+{
+ struct devlink_default_esw_mode_node *node;
+
+ if (devlink_default_esw_mode_node_find(bus_name, dev_name))
+ return -EEXIST;
+
+ node = kzalloc_obj(*node);
+ if (!node)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&node->list);
+ node->bus_name = kstrdup(bus_name, GFP_KERNEL);
+ node->dev_name = kstrdup(dev_name, GFP_KERNEL);
+ if (!node->bus_name || !node->dev_name) {
+ kfree(node->bus_name);
+ kfree(node->dev_name);
+ kfree(node);
+ return -ENOMEM;
+ }
+
+ list_add_tail(&node->list, &devlink_default_esw_mode_nodes);
+ return 0;
+}
+
+static int __init devlink_default_esw_mode_handles_parse(char *handles)
+{
+ char *handle;
+ int err;
+
+ if (!strcmp(handles, "*")) {
+ devlink_default_esw_mode_match_all = true;
+ return 0;
+ }
+
+ while ((handle = strsep(&handles, ",")) != NULL) {
+ char *bus_name;
+ char *dev_name;
+
+ err = devlink_default_esw_mode_handle_parse(handle, &bus_name,
+ &dev_name);
+ if (err)
+ return err;
+
+ err = devlink_default_esw_mode_node_add(bus_name, dev_name);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static void __init
+devlink_default_esw_mode_node_free(struct devlink_default_esw_mode_node *node)
+{
+ kfree(node->bus_name);
+ kfree(node->dev_name);
+ kfree(node);
+}
+
+static void __init devlink_default_esw_mode_nodes_clear(void)
+{
+ struct devlink_default_esw_mode_node *node_tmp;
+ struct devlink_default_esw_mode_node *node;
+
+ list_for_each_entry_safe(node, node_tmp,
+ &devlink_default_esw_mode_nodes, list) {
+ list_del(&node->list);
+ devlink_default_esw_mode_node_free(node);
+ }
+
+ devlink_default_esw_mode_match_all = false;
+}
+
+static int __init devlink_default_esw_mode_parse(char *str)
+{
+ enum devlink_eswitch_mode esw_mode;
+ char *separator;
+ char *handles;
+ char *mode;
+ int err;
+
+ if (!*str)
+ return -EINVAL;
+
+ separator = strrchr(str, '=');
+ if (!separator || separator == str || !separator[1])
+ return -EINVAL;
+
+ *separator = '\0';
+ handles = str;
+ mode = separator + 1;
+
+ err = devlink_default_esw_mode_to_value(mode, &esw_mode);
+ if (err)
+ return err;
+
+ err = devlink_default_esw_mode_handles_parse(handles);
+ if (err)
+ devlink_default_esw_mode_nodes_clear();
+ else
+ devlink_default_esw_mode = esw_mode;
+
+ return err;
+}
+
+static int __init devlink_default_esw_mode_setup(char *str)
+{
+ devlink_default_esw_mode_param = str;
+ return 1;
+}
+__setup("devlink_eswitch_mode=", devlink_default_esw_mode_setup);
+
+int __init devlink_default_esw_mode_init(void)
+{
+ char *def;
+ int err;
+
+ if (!devlink_default_esw_mode_param)
+ return 0;
+
+ def = kstrdup(devlink_default_esw_mode_param, GFP_KERNEL);
+ if (!def) {
+ devlink_default_esw_mode_param = NULL;
+ pr_warn("devlink: devlink_eswitch_mode parameter ignored, failed to allocate memory\n");
+ return 0;
+ }
+
+ err = devlink_default_esw_mode_parse(def);
+ kfree(def);
+ if (err == -EEXIST) {
+ devlink_default_esw_mode_param = NULL;
+ pr_warn("devlink: duplicate eswitch mode handles ignored\n");
+ return 0;
+ } else if (err == -EINVAL) {
+ devlink_default_esw_mode_param = NULL;
+ pr_warn("devlink: invalid devlink_eswitch_mode parameter ignored\n");
+ return 0;
+ } else if (err == -ENOMEM) {
+ devlink_default_esw_mode_param = NULL;
+ pr_warn("devlink: devlink_eswitch_mode parameter ignored, failed to allocate memory\n");
+ return 0;
+ } else if (err) {
+ return err;
+ }
+
+ return 0;
+}
+
+void __init devlink_default_esw_mode_cleanup(void)
+{
+ devlink_default_esw_mode_nodes_clear();
+}
diff --git a/net/devlink/devl_internal.h b/net/devlink/devl_internal.h
index af43b7163f78..fe9ad58515d4 100644
--- a/net/devlink/devl_internal.h
+++ b/net/devlink/devl_internal.h
@@ -71,6 +71,8 @@ extern struct genl_family devlink_nl_family;
struct devlink *__devlink_alloc(const struct devlink_ops *ops, size_t priv_size,
struct net *net, struct device *dev,
const struct device_driver *dev_driver);
+int devlink_default_esw_mode_init(void);
+void devlink_default_esw_mode_cleanup(void);
#define devl_warn(devlink, format, args...) \
do { \
--
2.43.0
^ permalink raw reply related
* [PATCH net-next V6 2/4] devlink: Factor out eswitch mode setting
From: Mark Bloch @ 2026-07-14 6:17 UTC (permalink / raw)
To: Jiri Pirko, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman
Cc: Saeed Mahameed, Leon Romanovsky, Tariq Toukan, Andrew Lunn,
Jonathan Corbet, Shuah Khan, netdev, linux-rdma, linux-doc,
Mark Bloch
In-Reply-To: <20260714061731.531849-1-mbloch@nvidia.com>
Move the common eswitch mode set checks into a small helper and use it
from the netlink eswitch set command. This makes the same validation
available to the devlink core path that applies eswitch mode defaults.
Signed-off-by: Mark Bloch <mbloch@nvidia.com>
---
net/devlink/dev.c | 27 ++++++++++++++++++++-------
net/devlink/devl_internal.h | 3 +++
2 files changed, 23 insertions(+), 7 deletions(-)
diff --git a/net/devlink/dev.c b/net/devlink/dev.c
index bcf001554e84..119ef105d0a7 100644
--- a/net/devlink/dev.c
+++ b/net/devlink/dev.c
@@ -702,6 +702,25 @@ int devlink_nl_eswitch_get_doit(struct sk_buff *skb, struct genl_info *info)
return genlmsg_reply(msg, info);
}
+int devlink_eswitch_mode_set(struct devlink *devlink,
+ enum devlink_eswitch_mode mode,
+ struct netlink_ext_ack *extack)
+{
+ const struct devlink_ops *ops = devlink->ops;
+ int err;
+
+ devl_assert_locked(devlink);
+
+ if (!ops->eswitch_mode_set)
+ return -EOPNOTSUPP;
+
+ err = devlink_rates_check(devlink, devlink_rate_is_node, extack);
+ if (err)
+ return err;
+
+ return ops->eswitch_mode_set(devlink, mode, extack);
+}
+
int devlink_nl_eswitch_set_doit(struct sk_buff *skb, struct genl_info *info)
{
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
@@ -712,14 +731,8 @@ int devlink_nl_eswitch_set_doit(struct sk_buff *skb, struct genl_info *info)
u16 mode;
if (info->attrs[DEVLINK_ATTR_ESWITCH_MODE]) {
- if (!ops->eswitch_mode_set)
- return -EOPNOTSUPP;
- err = devlink_rates_check(devlink, devlink_rate_is_node,
- info->extack);
- if (err)
- return err;
mode = nla_get_u16(info->attrs[DEVLINK_ATTR_ESWITCH_MODE]);
- err = ops->eswitch_mode_set(devlink, mode, info->extack);
+ err = devlink_eswitch_mode_set(devlink, mode, info->extack);
if (err)
return err;
}
diff --git a/net/devlink/devl_internal.h b/net/devlink/devl_internal.h
index cdf894ba5a9d..af43b7163f78 100644
--- a/net/devlink/devl_internal.h
+++ b/net/devlink/devl_internal.h
@@ -348,6 +348,9 @@ bool devlink_rate_is_node(const struct devlink_rate *devlink_rate);
int devlink_rates_check(struct devlink *devlink,
bool (*rate_filter)(const struct devlink_rate *),
struct netlink_ext_ack *extack);
+int devlink_eswitch_mode_set(struct devlink *devlink,
+ enum devlink_eswitch_mode mode,
+ struct netlink_ext_ack *extack);
/* Linecards */
unsigned int devlink_linecard_index(struct devlink_linecard *linecard);
--
2.43.0
^ permalink raw reply related
* [PATCH net-next V6 1/4] net/mlx5: Clear FW reset-in-progress bit before reload
From: Mark Bloch @ 2026-07-14 6:17 UTC (permalink / raw)
To: Jiri Pirko, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman
Cc: Saeed Mahameed, Leon Romanovsky, Tariq Toukan, Andrew Lunn,
Jonathan Corbet, Shuah Khan, netdev, linux-rdma, linux-doc,
Mark Bloch, Shay Drori, Moshe Shemesh
In-Reply-To: <20260714061731.531849-1-mbloch@nvidia.com>
mlx5 sets MLX5_FW_RESET_FLAGS_RESET_IN_PROGRESS when acknowledging a sync
reset request. This bit blocks devlink reload and other devlink operations
while the firmware reset is running, but it was kept set until after the
driver reload finished.
Clear the reset-in-progress bit once the reset unload flow is done and PCI
access is back, before reloading the device. For a reset initiated through
devlink, clear it before completing the reload waiter. For a reset reported
through an asynchronous firmware event, keep the unload flow outside
devl_lock, then take devl_lock before clearing the bit and reloading
through the devl-locked load helper.
Reviewed-by: Shay Drori <shayd@nvidia.com>
Reviewed-by: Moshe Shemesh <moshe@nvidia.com>
Signed-off-by: Mark Bloch <mbloch@nvidia.com>
---
.../ethernet/mellanox/mlx5/core/fw_reset.c | 28 +++++++++++--------
1 file changed, 17 insertions(+), 11 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c b/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c
index 07440c58713a..7283e5b49eed 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c
@@ -238,24 +238,30 @@ static void mlx5_fw_reset_complete_reload(struct mlx5_core_dev *dev)
{
struct mlx5_fw_reset *fw_reset = dev->priv.fw_reset;
struct devlink *devlink = priv_to_devlink(dev);
+ int err;
/* if this is the driver that initiated the fw reset, devlink completed the reload */
if (test_bit(MLX5_FW_RESET_FLAGS_PENDING_COMP, &fw_reset->reset_flags)) {
+ clear_bit(MLX5_FW_RESET_FLAGS_RESET_IN_PROGRESS,
+ &fw_reset->reset_flags);
complete(&fw_reset->done);
- } else {
- mlx5_sync_reset_unload_flow(dev, false);
- if (mlx5_health_wait_pci_up(dev))
- mlx5_core_err(dev, "reset reload flow aborted, PCI reads still not working\n");
- else
- mlx5_load_one(dev, true);
- devl_lock(devlink);
- devlink_remote_reload_actions_performed(devlink, 0,
- BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT) |
- BIT(DEVLINK_RELOAD_ACTION_FW_ACTIVATE));
- devl_unlock(devlink);
+ return;
}
+ mlx5_sync_reset_unload_flow(dev, false);
+ err = mlx5_health_wait_pci_up(dev);
+
+ devl_lock(devlink);
clear_bit(MLX5_FW_RESET_FLAGS_RESET_IN_PROGRESS, &fw_reset->reset_flags);
+ if (err)
+ mlx5_core_err(dev, "reset reload flow aborted, PCI reads still not working\n");
+ else
+ mlx5_load_one_devl_locked(dev, true);
+
+ devlink_remote_reload_actions_performed(devlink, 0,
+ BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT) |
+ BIT(DEVLINK_RELOAD_ACTION_FW_ACTIVATE));
+ devl_unlock(devlink);
}
static void mlx5_stop_sync_reset_poll(struct mlx5_core_dev *dev)
--
2.43.0
^ permalink raw reply related
* Dear linux-doc, project info
From: Harry Schofield ESQ @ 2026-07-14 5:24 UTC (permalink / raw)
To: linux-doc
Re:Good day linux-doc,
Please let me know if this is best email to send you the project
info.
Kind regards,
Harry Schofield, ceMBA
^ permalink raw reply
* Re: [PATCH v6 2/3] iommu/arm-smmu-v3: Introduce CFGI/TLBI-repeat workaround infrastructure
From: Ashish Mhetre @ 2026-07-14 4:59 UTC (permalink / raw)
To: Nicolin Chen
Cc: catalin.marinas, will, corbet, skhan, robin.murphy, joro, jgg,
linux-arm-kernel, linux-doc, linux-kernel, iommu, linux-tegra
In-Reply-To: <alUtYqO4HFOmVke5@nvidia.com>
On 7/13/2026 11:54 PM, Nicolin Chen wrote:
> On Mon, Jul 13, 2026 at 11:15:41AM +0000, Ashish Mhetre wrote:
>> Tegra264 SMMU instances need every CFGI/TLBI command sequence issued
>> twice, with the second issue executing only after the first issue's
>> CMD_SYNC has completed:
>>
>> TLBI/CFGI ... CMD_SYNC TLBI/CFGI ... CMD_SYNC
>>
>> ATC_INV is not affected and must never be doubled.
>>
>> Add arm_smmu_erratum_repeat_tlbi_cfgi_key and an
>> arm_smmu_erratum_cmd_needs_repeating() helper that gates on the static
>> key first and then range-checks the opcode (CFGI_STE .. ATC_INV), so
>> subsequent changes wiring the workaround into the CMDQ submission and
>> iommufd batching paths can share a single predicate.
>>
>> Rename the existing arm_smmu_cmdq_issue_cmdlist() to
>> __arm_smmu_cmdq_issue_cmdlist() and add a thin wrapper that re-issues
>> the same cmdlist a second time when the predicate fires. Register the
>> new condition with arm_smmu_cmdq_batch_force_sync() and add
>> arm_vsmmu_can_batch_cmd() so iommufd batches split at every "needs
>> repeating" transition.
>>
>> No callers enable the static key yet, so there is no functional change.
>> A subsequent change will enable the key on affected instances.
> Maybe add a small note (better in patch-3).
>
> Note: since guest-level VCMDQs issue commands directly to the HW, a guest
> kernel enabling the cmdqv feature on NVIDIA Tegra264 must apply this WAR.
Ack.
>> Suggested-by: Nicolin Chen <nicolinc@nvidia.com>
>> Signed-off-by: Ashish Mhetre <amhetre@nvidia.com>
> Reviewed-by: Nicolin Chen <nicolinc@nvidia.com>
>
> Some small issues; please fix:
Sure, will respin v7 fixing these.
>> +static bool arm_vsmmu_can_batch_cmd(struct arm_smmu_device *smmu,
>> + struct arm_vsmmu_invalidation_cmd *last,
>> + struct arm_vsmmu_invalidation_cmd *next)
> @smmu is unused here.
Ack.
>> diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
>> index dd7475c50afc..eb8374cfce2a 100644
>> --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
>> +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
>> @@ -42,6 +42,14 @@ MODULE_PARM_DESC(disable_msipolling,
>> static const struct iommu_ops arm_smmu_ops;
>> static struct iommu_dirty_ops arm_smmu_dirty_ops;
>>
>> +/*
>> + * Repeat every {CFGI,TLBI};CMD_SYNC command sequence so that the second
>> + * issue executes only after the first issue's CMD_SYNC has completed.
>> + * Does not apply to ATC_INV. The key is global and is enabled from DT
>> + * probe on affected hardware (currently Tegra264 only).
>> + */
>> +static DEFINE_STATIC_KEY_FALSE(arm_smmu_erratum_repeat_tlbi_cfgi_key);
> Since we defined a static key, it would be better explicitly add:
>
> #include <linux/jump_label.h>
Ack.
>> @@ -860,6 +900,11 @@ static bool arm_smmu_cmdq_batch_force_sync(struct arm_smmu_device *smmu,
>> (smmu->options & ARM_SMMU_OPT_CMDQ_FORCE_SYNC))
>> return true;
>>
>> + /* See the description at arm_smmu_erratum_repeat_tlbi_cfgi_key */
>> + if (cmds->num == CMDQ_BATCH_ENTRIES &&
>> + arm_smmu_erratum_cmd_needs_repeating(&cmds->cmds[0]))
>> + return true;
> /*
> * See the description at arm_smmu_erratum_repeat_tlbi_cfgi_key. Batches
> * never mix CFGI/TLBI with others, so checking cmds[0] alone is enough.
> */
>
> Nicolin
Ack.
Thanks,
Ashish Mhetre
^ permalink raw reply
* Re: What's cooking in zh_CN (Jul 2026)
From: Weijie Yuan @ 2026-07-14 4:47 UTC (permalink / raw)
To: Jiandong Qiu
Cc: linux-doc, Alex Shi, Yanteng Si, Dongliang Mu, Ben Guo, Gary Guo,
Yan Zhu, Doehyun Baek, chengyaqiang
In-Reply-To: <CAAJ18eFxjotuhggQ=wkSuXfyz5yVHE0LiP26STdcJwfdwAfF5w@mail.gmail.com>
On Tue, Jul 14, 2026 at 10:30:24AM +0800, Jiandong Qiu wrote:
> Just a thought: zh_CN/how-to.rst currently focuses mainly on submitting
> patches. If we want to encourage newcomers to review too, maybe we could
> add some guidance there on how to review, what to check and how to give
> feedback? Curious what others think.
Yeah, Although the current situation indicates that the pressure of
review is not very high at present, gradually distributing the review
work to the community would definitely not be a bad thing. Finally, it
would be best to have Alex, Dongliang and Yanteng handle the last pass.
I see that most of the patches are currently reviewed by Dongliang. This
is definitely fine, but having more pairs of eyes would be even better,
just as Linus Torvalds said.
For how-to.rst, maybe we can add a section?
btw, Git has a doc especially for review:
https://git-scm.com/docs/ReviewingGuidelines
we can have a look, then let's see what we can do.
^ permalink raw reply
* RE: [PATCH v3 07/11] vfio/pci: Add CONFIG_VFIO_PCI_CXL with bind-time CXL Type-2 acquisition
From: Dan Williams (nvidia) @ 2026-07-14 4:32 UTC (permalink / raw)
To: Manish Honap, Alex Williamson
Cc: djbw@kernel.org, jgg@ziepe.ca, jic23@kernel.org,
dave.jiang@intel.com, Ankit Agrawal,
alejandro.lucero-palau@amd.com, alison.schofield@intel.com,
dave@stgolabs.net, dmatlack@google.com, gourry@gourry.net,
ira.weiny@intel.com, Neo Jia, Krishnakant Jaju, Vikram Sethi,
Zhi Wang, kvm@vger.kernel.org, linux-cxl@vger.kernel.org,
linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-kselftest@vger.kernel.org, Manish Honap
In-Reply-To: <IA1PR12MB90309355D97FE886A1DA77BABDFA2@IA1PR12MB9030.namprd12.prod.outlook.com>
Manish Honap wrote:
[..]
> > > vfio_pci_cxl_acquire() implements the bind sequence:
> > >
> > > - pcie_is_cxl() and CXL Device DVSEC discovery (-ENODEV if absent
> > > or if MEM_CAPABLE clear — caller falls back to plain vfio-pci)
> > > - devm_cxl_dev_state_create() with struct vfio_pci_cxl_state
> > > embedding cxl_dev_state at offset 0 (required by the 7-arg
> > > macro's static_assert in include/cxl/cxl.h)
> > > - pci_enable_device_mem(), cxl_pci_setup_regs(), cxl_get_hdm_info()
> > > (rejecting hdm_count != 1), cxl_regblock_get_bar_info(),
> > > cxl_await_range_active()
> >
> > The cover letter claims otherwise:
> >
> > "- cxl_await_range_active stays in cxl-core probe; not exported, vfio
> > does not call it."
> >
> > It's exported in 2/ and called below.
This feels like a gap on the CXL side. devm_cxl_probe_mem() should take
care to only succeed after memory is active, or fail with a timeout.
Recall that the current cxl_await_range_active() export is for the
"internal" cxl_pci driver. Now that there are external consumers
devm_cxl_probe_mem() should return a fully validated and awaited memory
range. Do not make every driver remember this part of the setup flow.
> > > - devm_cxl_passthrough_create() to snapshot the DVSEC body, HDM
> > > block, and CM cap-array shadows owned by cxl-core
> > > - pci_disable_device() — clears PCI_COMMAND_MASTER but NOT
> > > PCI_COMMAND_MEMORY, so cxl-core MMIO accesses from the next step
> > > still succeed
> > > - devm_cxl_probe_mem() to register the cxl_memdev, enumerate the
> > > endpoint port, and attach the firmware-committed autoregion
> > > - request_mem_region() + memremap_wb() of the autoregion's HPA so
> > > the HDM VFIO region can serve guest accesses through it
> >
> > How does this interact with:
> >
> > - The device making use of low power states while idle
> > - Repeatability per tenant instance
> > - Protection of tenant data per instance
> >
> > The culmination of all of these, plus the basic housekeeping of
> > maintaining the lightest touch on the device, including keeping the
> > device in the minimum state of functionality outside of an actual user,
> > is why I would expect to perform acquire/release as part of open/close.
>
> The cover letter description is wrong. During my earlier thought process,
> creating a region during probe seemed a correct option as any later requirement
> where region needs to be already created at probe emerged, we will have easier
> way to handle it. I will give some more thought to update v4 to acquire
> CXL state in open_device() and release it in close_device(), matching zpci.
This is going to run up against a current design wart of
devm_cxl_probe_mem() that does not have a teardown flow implemented
outside of the typical ->remove() to devres_release_all() flow. Even if
that was fixed I do not see it affecting the listed concerns.
devm_cxl_probe_mem() for firmware committed decoders is mostly just
reading resource values.
For power state management the power management that vfio-pci performs
only informs the CXL.io portion of device power. CXL.cache and CXL.mem
are dynamically managed by the device.
For repeatability I do think discarding and reinitializing the shadow
configuration makes sense, but the base HDM configuration does not
require teardown.
For tenant data protection that arises from the data retention after
reset policy, not impacted by unloading the HDM configuration.
I assume VFIO likely would want to build a capability to zero HDM memory
if the device leaves the state ambiguous / implementation defined?
That is a good note for the CXL Reset series as well. If VFIO issued
resets are for tenant handoff the make sure that CXL Reset requests
memory clearing.
^ permalink raw reply
* Re: [PATCH v2 2/4] docs/zh_CN: Update rust/general-information.rst translation
From: Ben Guo @ 2026-07-14 3:39 UTC (permalink / raw)
To: Dongliang Mu, Alex Shi, Yanteng Si, Jonathan Corbet
Cc: Gary Guo, linux-doc, linux-kernel, rust-for-linux,
hust-os-kernel-patches
In-Reply-To: <17094968-1385-4dba-aae8-5d93a2aaf59e@hust.edu.cn>
On 7/13/26 10:37 AM, Dongliang Mu wrote:
>> Signed-off-by: Ben Guo <ben.guo@openatom.club>
>> ---
>> .../zh_CN/rust/general-information.rst | 82 ++++++++++++++++++-
>> 1 file changed, 79 insertions(+), 3 deletions(-)
>>
>> diff --git a/Documentation/translations/zh_CN/rust/general-
>> information.rst b/Documentation/translations/zh_CN/rust/general-
>> information.rst
>> index 9b5e37e13f3..ff9355cb8c8 100644
>> --- a/Documentation/translations/zh_CN/rust/general-information.rst
>> +++ b/Documentation/translations/zh_CN/rust/general-information.rst
>> @@ -13,6 +13,14 @@
>> 本文档包含了在内核中使用Rust支持时需要了解的有用信息。
>> +``no_std``
>> +----------
>> +
>> +内核中的 Rust 支持只能链接 `core <https://doc.rust-lang.org/core/>`_,
>> +而不能链接 `std <https://doc.rust-lang.org/std/>`_。供内核使用的 crate
>> +必须使用 ``#![no_std]`` 属性选择这种行为。
>> +
>> +
>> .. _rust_code_documentation_zh_cn:
>> 代码文档
>> @@ -20,10 +28,18 @@
>> Rust内核代码使用其内置的文档生成器 ``rustdoc`` 进行记录。
>> -生成的HTML文档包括集成搜索、链接项(如类型、函数、常量)、源代码等。
>> 它们可以在以下地址阅读
>> -(TODO:当在主线中时链接,与其他文档一起生成):
>> +生成的HTML文档包括集成搜索、链接项(如类型、函数、常量)、源代码等。
> Add spaces before and after HTML
Dongliang,
Thanks, I will check for similar spacing issues and fix them in the next
version.
Best,
Ben
^ permalink raw reply
* Re: [PATCH 1/3] delaytop: refactor repetitive delay fields into array with enum
From: xu.xin16 @ 2026-07-14 3:07 UTC (permalink / raw)
To: wang.yaxin, akpm
Cc: wang.yaxin, fan.yu9, yang.yang29, corbet, linux-kernel, linux-doc
In-Reply-To: <20260711173256542-prxp-gSW6zPAMKAp8GOM@zte.com.cn>
> Replace the 9 groups of (count, delay_total, delay_max, delay_max_ts)
> named fields in struct task_info with a struct delay_metrics array
> indexed by enum delay_type. This eliminates all unsafe pointer
> arithmetic via offsetof() from compare_tasks(),
> field_delay_max_and_ts(), and get_field_delay_values().
>
> The struct field_desc now stores an enum delay_type index instead of
> four separate unsigned long offset values.
>
> Signed-off-by: Wang Yaxin <wang.yaxin@zte.com.cn>
Acked-by: Xu Xin <xu.xin16@zte.com.cn>
^ permalink raw reply
* Re: [PATCH 0/3] tools/accounting: refactor delay fields and share format_timespec()
From: Andrew Morton @ 2026-07-14 2:48 UTC (permalink / raw)
To: wang.yaxin
Cc: fan.yu9, yang.yang29, corbet, linux-kernel, linux-doc, xu.xin16
In-Reply-To: <20260711173112482SCQEM08VED2PT1pxUYOXk@zte.com.cn>
On Sat, 11 Jul 2026 17:31:12 +0800 (CST) <wang.yaxin@zte.com.cn> wrote:
> - Convert per-field delay members in struct task_info to an array indexed
> by enum delay_type, eliminating offsetof() pointer arithmetic.
>
> - Factor out a common format_timespec() implementation shared by getdelays
> and delaytop, using strftime for cleaner timestamp formatting.
>
> - Replace the complex sizeof/ULL/shift Y2038 guard with a direct narrowing
> truncation check ((long long)time_sec != ts->tv_sec).
Sounds great.
AI review might have found a couple of things. Please check it out?
https://sashiko.dev/#/patchset/20260711173112482SCQEM08VED2PT1pxUYOXk@zte.com.cn
^ permalink raw reply
* Re: [PATCH v7 06/10] ACPI: APEI: GHES: move CXL CPER helpers
From: Alison Schofield @ 2026-07-14 2:45 UTC (permalink / raw)
To: Ahmed Tiba
Cc: Rafael J. Wysocki, Tony Luck, Borislav Petkov, Hanjun Guo,
Mauro Carvalho Chehab, Shuai Xue, Len Brown, Saket Dumbre,
Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Vishal Verma,
Dan Williams, Ira Weiny, Li Ming, Mahesh J Salgaonkar,
Oliver O'Halloran, Bjorn Helgaas, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet, Shuah Khan,
linux-kernel, linux-acpi, acpica-devel, linux-cxl, linuxppc-dev,
linux-pci, devicetree, linux-edac, linux-doc, Dmitry.Lamerov
In-Reply-To: <20260708-topics-ahmtib01-ras_ffh_arm_internal_review-v7-6-8b3a85216cef@arm.com>
On Wed, Jul 08, 2026 at 02:59:05PM +0100, Ahmed Tiba wrote:
> Move the CXL CPER handling paths out of ghes.c and into ghes_cper.c so the
> helpers can be reused. The code is moved as-is, with the public
> prototypes updated so GHES keeps calling into the new translation unit.
>
> While moving this code, also add CXL CPER section length checks and use
> spinlock_irqsave() in CXL register/unregister paths for locking
> consistency.
NAK on moving and changing in the same patch, and esp in a patch
whose subject only says MOVE.
>
> Reviewed-by: Jonathan Cameron <jic23@kernel.org>
> Signed-off-by: Ahmed Tiba <ahmed.tiba@arm.com>
> ---
> drivers/acpi/apei/ghes.c | 180 +++++++++++-------------------------------
> drivers/acpi/apei/ghes_cper.c | 135 +++++++++++++++++++++++++++++++
> include/acpi/ghes_cper.h | 11 +++
> 3 files changed, 194 insertions(+), 132 deletions(-)
>
> diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
> index 07e4001ea8d7..2a83d326e692 100644
> --- a/drivers/acpi/apei/ghes.c
> +++ b/drivers/acpi/apei/ghes.c
> @@ -383,138 +383,6 @@ static void ghes_handle_aer(struct acpi_hest_generic_data *gdata)
> #endif
> }
>
> -/* Room for 8 entries */
> -#define CXL_CPER_PROT_ERR_FIFO_DEPTH 8
> -static DEFINE_KFIFO(cxl_cper_prot_err_fifo, struct cxl_cper_prot_err_work_data,
> - CXL_CPER_PROT_ERR_FIFO_DEPTH);
> -
> -/* Synchronize schedule_work() with cxl_cper_prot_err_work changes */
> -static DEFINE_SPINLOCK(cxl_cper_prot_err_work_lock);
> -struct work_struct *cxl_cper_prot_err_work;
> -
> -static void cxl_cper_post_prot_err(struct cxl_cper_sec_prot_err *prot_err,
> - int severity)
> -{
> -#ifdef CONFIG_ACPI_APEI_PCIEAER
> - struct cxl_cper_prot_err_work_data wd;
> -
> - if (cxl_cper_sec_prot_err_valid(prot_err))
> - return;
> -
> - guard(spinlock_irqsave)(&cxl_cper_prot_err_work_lock);
> -
> - if (!cxl_cper_prot_err_work)
> - return;
> -
> - if (cxl_cper_setup_prot_err_work_data(&wd, prot_err, severity))
> - return;
> -
> - if (!kfifo_put(&cxl_cper_prot_err_fifo, wd)) {
> - pr_err_ratelimited("CXL CPER kfifo overflow\n");
> - return;
> - }
> -
> - schedule_work(cxl_cper_prot_err_work);
> -#endif
> -}
> -
> -int cxl_cper_register_prot_err_work(struct work_struct *work)
> -{
> - if (cxl_cper_prot_err_work)
> - return -EINVAL;
> -
> - guard(spinlock)(&cxl_cper_prot_err_work_lock);
> - cxl_cper_prot_err_work = work;
> - return 0;
> -}
> -EXPORT_SYMBOL_NS_GPL(cxl_cper_register_prot_err_work, "CXL");
> -
> -int cxl_cper_unregister_prot_err_work(struct work_struct *work)
> -{
> - if (cxl_cper_prot_err_work != work)
> - return -EINVAL;
> -
> - guard(spinlock)(&cxl_cper_prot_err_work_lock);
> - cxl_cper_prot_err_work = NULL;
> - return 0;
> -}
> -EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_prot_err_work, "CXL");
> -
> -int cxl_cper_prot_err_kfifo_get(struct cxl_cper_prot_err_work_data *wd)
> -{
> - return kfifo_get(&cxl_cper_prot_err_fifo, wd);
> -}
> -EXPORT_SYMBOL_NS_GPL(cxl_cper_prot_err_kfifo_get, "CXL");
> -
> -/* Room for 8 entries for each of the 4 event log queues */
> -#define CXL_CPER_FIFO_DEPTH 32
> -DEFINE_KFIFO(cxl_cper_fifo, struct cxl_cper_work_data, CXL_CPER_FIFO_DEPTH);
> -
> -/* Synchronize schedule_work() with cxl_cper_work changes */
> -static DEFINE_SPINLOCK(cxl_cper_work_lock);
> -struct work_struct *cxl_cper_work;
> -
> -static void cxl_cper_post_event(enum cxl_event_type event_type,
> - struct cxl_cper_event_rec *rec)
> -{
> - struct cxl_cper_work_data wd;
> -
> - if (rec->hdr.length <= sizeof(rec->hdr) ||
> - rec->hdr.length > sizeof(*rec)) {
> - pr_err(FW_WARN "CXL CPER Invalid section length (%u)\n",
> - rec->hdr.length);
> - return;
> - }
> -
> - if (!(rec->hdr.validation_bits & CPER_CXL_COMP_EVENT_LOG_VALID)) {
> - pr_err(FW_WARN "CXL CPER invalid event\n");
> - return;
> - }
> -
> - guard(spinlock_irqsave)(&cxl_cper_work_lock);
> -
> - if (!cxl_cper_work)
> - return;
> -
> - wd.event_type = event_type;
> - memcpy(&wd.rec, rec, sizeof(wd.rec));
> -
> - if (!kfifo_put(&cxl_cper_fifo, wd)) {
> - pr_err_ratelimited("CXL CPER kfifo overflow\n");
> - return;
> - }
> -
> - schedule_work(cxl_cper_work);
> -}
> -
> -int cxl_cper_register_work(struct work_struct *work)
> -{
> - if (cxl_cper_work)
> - return -EINVAL;
> -
> - guard(spinlock)(&cxl_cper_work_lock);
> - cxl_cper_work = work;
> - return 0;
> -}
> -EXPORT_SYMBOL_NS_GPL(cxl_cper_register_work, "CXL");
> -
> -int cxl_cper_unregister_work(struct work_struct *work)
> -{
> - if (cxl_cper_work != work)
> - return -EINVAL;
> -
> - guard(spinlock)(&cxl_cper_work_lock);
> - cxl_cper_work = NULL;
> - return 0;
> -}
> -EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_work, "CXL");
> -
> -int cxl_cper_kfifo_get(struct cxl_cper_work_data *wd)
> -{
> - return kfifo_get(&cxl_cper_fifo, wd);
> -}
> -EXPORT_SYMBOL_NS_GPL(cxl_cper_kfifo_get, "CXL");
> -
> static void ghes_log_hwerr(int sev, guid_t *sec_type)
> {
> if (sev != CPER_SEV_RECOVERABLE)
> @@ -549,6 +417,42 @@ static void ghes_log_hwerr(int sev, guid_t *sec_type)
> hwerr_log_error_type(HWERR_RECOV_OTHERS);
> }
>
> +static bool ghes_cxl_event_len_valid(struct acpi_hest_generic_data *gdata,
> + struct cxl_cper_event_rec *rec)
> +{
> + if (gdata->error_data_length < sizeof(*rec) ||
> + rec->hdr.length <= sizeof(rec->hdr) ||
> + rec->hdr.length > gdata->error_data_length ||
> + rec->hdr.length > sizeof(*rec)) {
> + pr_err(FW_WARN "CXL CPER Invalid section length (%u)\n",
> + rec->hdr.length);
> + return false;
> + }
> +
> + return true;
> +}
> +
> +static bool ghes_cxl_prot_err_len_valid(struct acpi_hest_generic_data *gdata,
> + struct cxl_cper_sec_prot_err *prot_err)
> +{
> + if (gdata->error_data_length < sizeof(*prot_err) +
> + sizeof(struct cxl_ras_capability_regs)) {
> + pr_err(FW_WARN "CXL CPER Invalid protocol error length (%u)\n",
> + gdata->error_data_length);
> + return false;
> + }
> +
> + if (prot_err->dvsec_len >
> + gdata->error_data_length - sizeof(*prot_err) -
> + sizeof(struct cxl_ras_capability_regs)) {
> + pr_err(FW_WARN "CXL CPER invalid DVSEC length (%u)\n",
> + prot_err->dvsec_len);
> + return false;
> + }
> +
> + return true;
> +}
> +
> static void ghes_do_proc(struct ghes *ghes,
> const struct acpi_hest_generic_status *estatus)
> {
> @@ -585,18 +489,30 @@ static void ghes_do_proc(struct ghes *ghes,
> } else if (guid_equal(sec_type, &CPER_SEC_CXL_PROT_ERR)) {
> struct cxl_cper_sec_prot_err *prot_err = acpi_hest_get_payload(gdata);
>
> + if (!ghes_cxl_prot_err_len_valid(gdata, prot_err))
> + continue;
> +
> cxl_cper_post_prot_err(prot_err, gdata->error_severity);
> } else if (guid_equal(sec_type, &CPER_SEC_CXL_GEN_MEDIA_GUID)) {
> struct cxl_cper_event_rec *rec = acpi_hest_get_payload(gdata);
>
> + if (!ghes_cxl_event_len_valid(gdata, rec))
> + continue;
> +
> cxl_cper_post_event(CXL_CPER_EVENT_GEN_MEDIA, rec);
> } else if (guid_equal(sec_type, &CPER_SEC_CXL_DRAM_GUID)) {
> struct cxl_cper_event_rec *rec = acpi_hest_get_payload(gdata);
>
> + if (!ghes_cxl_event_len_valid(gdata, rec))
> + continue;
> +
> cxl_cper_post_event(CXL_CPER_EVENT_DRAM, rec);
> } else if (guid_equal(sec_type, &CPER_SEC_CXL_MEM_MODULE_GUID)) {
> struct cxl_cper_event_rec *rec = acpi_hest_get_payload(gdata);
>
> + if (!ghes_cxl_event_len_valid(gdata, rec))
> + continue;
> +
> cxl_cper_post_event(CXL_CPER_EVENT_MEM_MODULE, rec);
> } else {
> void *err = acpi_hest_get_payload(gdata);
> diff --git a/drivers/acpi/apei/ghes_cper.c b/drivers/acpi/apei/ghes_cper.c
> index 7e4a66b788b8..b59e3ed3eab3 100644
> --- a/drivers/acpi/apei/ghes_cper.c
> +++ b/drivers/acpi/apei/ghes_cper.c
> @@ -12,9 +12,12 @@
> * Author: Huang Ying <ying.huang@intel.com>
> */
>
> +#include <linux/aer.h>
> +#include <linux/cleanup.h>
> #include <linux/err.h>
> #include <linux/genalloc.h>
> #include <linux/io.h>
> +#include <linux/kfifo.h>
> #include <linux/kernel.h>
> #include <linux/math64.h>
> #include <linux/mm.h>
> @@ -350,6 +353,138 @@ void ghes_defer_non_standard_event(struct acpi_hest_generic_data *gdata,
> schedule_work(&entry->work);
> }
>
> +/* Room for 8 entries */
> +#define CXL_CPER_PROT_ERR_FIFO_DEPTH 8
> +static DEFINE_KFIFO(cxl_cper_prot_err_fifo, struct cxl_cper_prot_err_work_data,
> + CXL_CPER_PROT_ERR_FIFO_DEPTH);
> +
> +/* Synchronize schedule_work() with cxl_cper_prot_err_work changes */
> +static DEFINE_SPINLOCK(cxl_cper_prot_err_work_lock);
> +struct work_struct *cxl_cper_prot_err_work;
> +
> +void cxl_cper_post_prot_err(struct cxl_cper_sec_prot_err *prot_err,
> + int severity)
> +{
> +#ifdef CONFIG_ACPI_APEI_PCIEAER
> + struct cxl_cper_prot_err_work_data wd;
> +
> + if (cxl_cper_sec_prot_err_valid(prot_err))
> + return;
> +
> + guard(spinlock_irqsave)(&cxl_cper_prot_err_work_lock);
> +
> + if (!cxl_cper_prot_err_work)
> + return;
> +
> + if (cxl_cper_setup_prot_err_work_data(&wd, prot_err, severity))
> + return;
> +
> + if (!kfifo_put(&cxl_cper_prot_err_fifo, wd)) {
> + pr_err_ratelimited("CXL CPER kfifo overflow\n");
> + return;
> + }
> +
> + schedule_work(cxl_cper_prot_err_work);
> +#endif
> +}
> +
> +int cxl_cper_register_prot_err_work(struct work_struct *work)
> +{
> + if (cxl_cper_prot_err_work)
> + return -EINVAL;
> +
> + guard(spinlock_irqsave)(&cxl_cper_prot_err_work_lock);
> + cxl_cper_prot_err_work = work;
> + return 0;
> +}
> +EXPORT_SYMBOL_NS_GPL(cxl_cper_register_prot_err_work, "CXL");
> +
> +int cxl_cper_unregister_prot_err_work(struct work_struct *work)
> +{
> + if (cxl_cper_prot_err_work != work)
> + return -EINVAL;
> +
> + guard(spinlock_irqsave)(&cxl_cper_prot_err_work_lock);
> + cxl_cper_prot_err_work = NULL;
> + return 0;
> +}
> +EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_prot_err_work, "CXL");
> +
> +int cxl_cper_prot_err_kfifo_get(struct cxl_cper_prot_err_work_data *wd)
> +{
> + return kfifo_get(&cxl_cper_prot_err_fifo, wd);
> +}
> +EXPORT_SYMBOL_NS_GPL(cxl_cper_prot_err_kfifo_get, "CXL");
> +
> +/* Room for 8 entries for each of the 4 event log queues */
> +#define CXL_CPER_FIFO_DEPTH 32
> +static DEFINE_KFIFO(cxl_cper_fifo, struct cxl_cper_work_data, CXL_CPER_FIFO_DEPTH);
> +
> +/* Synchronize schedule_work() with cxl_cper_work changes */
> +static DEFINE_SPINLOCK(cxl_cper_work_lock);
> +struct work_struct *cxl_cper_work;
> +
> +void cxl_cper_post_event(enum cxl_event_type event_type,
> + struct cxl_cper_event_rec *rec)
> +{
> + struct cxl_cper_work_data wd;
> +
> + if (rec->hdr.length <= sizeof(rec->hdr) ||
> + rec->hdr.length > sizeof(*rec)) {
> + pr_err(FW_WARN "CXL CPER Invalid section length (%u)\n",
> + rec->hdr.length);
> + return;
> + }
> +
> + if (!(rec->hdr.validation_bits & CPER_CXL_COMP_EVENT_LOG_VALID)) {
> + pr_err(FW_WARN "CXL CPER invalid event\n");
> + return;
> + }
> +
> + guard(spinlock_irqsave)(&cxl_cper_work_lock);
> +
> + if (!cxl_cper_work)
> + return;
> +
> + wd.event_type = event_type;
> + memcpy(&wd.rec, rec, sizeof(wd.rec));
> +
> + if (!kfifo_put(&cxl_cper_fifo, wd)) {
> + pr_err_ratelimited("CXL CPER kfifo overflow\n");
> + return;
> + }
> +
> + schedule_work(cxl_cper_work);
> +}
> +
> +int cxl_cper_register_work(struct work_struct *work)
> +{
> + if (cxl_cper_work)
> + return -EINVAL;
> +
> + guard(spinlock_irqsave)(&cxl_cper_work_lock);
> + cxl_cper_work = work;
> + return 0;
> +}
> +EXPORT_SYMBOL_NS_GPL(cxl_cper_register_work, "CXL");
> +
> +int cxl_cper_unregister_work(struct work_struct *work)
> +{
> + if (cxl_cper_work != work)
> + return -EINVAL;
> +
> + guard(spinlock_irqsave)(&cxl_cper_work_lock);
> + cxl_cper_work = NULL;
> + return 0;
> +}
> +EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_work, "CXL");
> +
> +int cxl_cper_kfifo_get(struct cxl_cper_work_data *wd)
> +{
> + return kfifo_get(&cxl_cper_fifo, wd);
> +}
> +EXPORT_SYMBOL_NS_GPL(cxl_cper_kfifo_get, "CXL");
> +
> /*
> * GHES error status reporting throttle, to report more kinds of
> * errors, instead of just most frequently occurred errors.
> diff --git a/include/acpi/ghes_cper.h b/include/acpi/ghes_cper.h
> index d9f9253d8de9..a853a5996cdf 100644
> --- a/include/acpi/ghes_cper.h
> +++ b/include/acpi/ghes_cper.h
> @@ -20,6 +20,7 @@
>
> #include <acpi/ghes.h>
> #include <asm/fixmap.h>
> +#include <cxl/event.h>
>
> #define GHES_PFX "GHES: "
>
> @@ -106,5 +107,15 @@ void ghes_estatus_cache_add(struct acpi_hest_generic *generic,
> struct acpi_hest_generic_status *estatus);
> void ghes_defer_non_standard_event(struct acpi_hest_generic_data *gdata,
> int sev);
> +void cxl_cper_post_prot_err(struct cxl_cper_sec_prot_err *prot_err,
> + int severity);
> +int cxl_cper_register_prot_err_work(struct work_struct *work);
> +int cxl_cper_unregister_prot_err_work(struct work_struct *work);
> +int cxl_cper_prot_err_kfifo_get(struct cxl_cper_prot_err_work_data *wd);
> +void cxl_cper_post_event(enum cxl_event_type event_type,
> + struct cxl_cper_event_rec *rec);
> +int cxl_cper_register_work(struct work_struct *work);
> +int cxl_cper_unregister_work(struct work_struct *work);
> +int cxl_cper_kfifo_get(struct cxl_cper_work_data *wd);
>
> #endif /* ACPI_APEI_GHES_CPER_H */
>
> --
> 2.43.0
>
^ permalink raw reply
* Re: What's cooking in zh_CN (Jul 2026)
From: Jiandong Qiu @ 2026-07-14 2:30 UTC (permalink / raw)
To: Weijie Yuan, linux-doc
Cc: Alex Shi, Yanteng Si, Dongliang Mu, Ben Guo, Gary Guo, Yan Zhu,
Doehyun Baek, chengyaqiang
In-Reply-To: <alUXH8qRRjno2eZG@wyuan.org>
Hi,
This is great! As a newcomer, I often wondered what was going on, and
worried about duplicating others' work.
This kind of summary would really help newcomers get up to speed.
> More importantly, it gives newcomers an overview of the current state of
> the project. New contributors can begin not only by submitting patches,
> but also by reviewing patches already posted to the mailing list,
> thereby learning how our workflow operates. This may also help reduce
> the review burden on our friendly maintainers.
Just a thought: zh_CN/how-to.rst currently focuses mainly on submitting
patches. If we want to encourage newcomers to review too, maybe we could
add some guidance there on how to review, what to check and how to give
feedback? Curious what others think.
Thanks for starting this!
Best,
Jiandong
^ permalink raw reply
* Re: [PATCH] docs/zh_CN: fix KASAN SW_TAGS mode description
From: Alex Shi @ 2026-07-14 2:16 UTC (permalink / raw)
To: Zenghui Yu, chengyaqiang
Cc: alexs, si.yanteng, dzm91, corbet, skhan, linux-doc, linux-kernel,
chengyaqiang
In-Reply-To: <05ee738e-37ea-4697-99de-4cec914066b4@linux.dev>
On 2026/5/24 18:44, Zenghui Yu wrote:
> On 5/22/26 3:57 PM, chengyaqiang wrote:
>> From: chengyaqiang<chengyaqiang@tsinghua.edu.cn>
>>
>> CONFIG_KASAN_SW_TAGS enables Software Tag-Based KASAN mode, not Hardware
>> Tag-Based mode. Fix the incorrect translation in the Chinese documentation.
>>
>> The original text incorrectly described both CONFIG_KASAN_SW_TAGS and
>> CONFIG_KASAN_HW_TAGS as "基于硬件标签" (hardware tag-based). Correct
>> CONFIG_KASAN_SW_TAGS to "基于软件标签" (software tag-based).
>>
>> Signed-off-by: chengyaqiang<chengyaqiang@tsinghua.edu.cn>
Applied, thanks!
^ permalink raw reply
* [PATCH v4 net-next 7/7] net: ena: Implement gettimexattrs64 callback for PTP attributes
From: Arthur Kiyanovski @ 2026-07-14 2:03 UTC (permalink / raw)
To: David Miller, Jakub Kicinski, netdev
Cc: Arthur Kiyanovski, Richard Cochran, Eric Dumazet, Paolo Abeni,
David Woodhouse, Thomas Gleixner, Miroslav Lichvar, Andrew Lunn,
Wen Gu, Xuan Zhuo, David Woodhouse, Yonatan Sarna,
Zorik Machulsky, Alexander Matushevsky, Saeed Bshara, Matt Wilson,
Anthony Liguori, Nafea Bshara, Evgeny Schmeilin, Netanel Belgazal,
Ali Saidi, Benjamin Herrenschmidt, Noam Dagan, David Arinzon,
Evgeny Ostrovsky, Ofir Tabachnik, Amit Bernstein, linux-kselftest,
linux-doc, shuah, Jonathan Corbet, Shuah Khan, Simon Horman,
vadim.fedorenko
In-Reply-To: <20260714020340.25014-1-akiyano@amazon.com>
Implement the gettimexattrs64 callback in the ENA driver to support
the PTP_SYS_OFFSET_EXTENDED_ATTRS ioctl.
This enables applications to retrieve PHC timestamps with quality
attributes through the standard PTP ioctl interface.
The ENA device currently reports only error_bound (valid bit set).
Other attributes are not reported (valid bits unset).
Signed-off-by: Amit Bernstein <amitbern@amazon.com>
Signed-off-by: Arthur Kiyanovski <akiyano@amazon.com>
---
drivers/net/ethernet/amazon/ena/ena_phc.c | 58 +++++++++++++++++++----
1 file changed, 48 insertions(+), 10 deletions(-)
diff --git a/drivers/net/ethernet/amazon/ena/ena_phc.c b/drivers/net/ethernet/amazon/ena/ena_phc.c
index 2bcb5af564e2..725c36fe3f6e 100644
--- a/drivers/net/ethernet/amazon/ena/ena_phc.c
+++ b/drivers/net/ethernet/amazon/ena/ena_phc.c
@@ -25,6 +25,43 @@ static int ena_phc_feature_enable(struct ptp_clock_info *clock_info,
return -EOPNOTSUPP;
}
+static int ena_phc_gettimexattrs64(struct ptp_clock_info *clock_info,
+ struct timespec64 *ts,
+ struct ptp_system_timestamp *sts,
+ struct ptp_clock_attrs *att)
+{
+ struct ena_phc_info *phc_info =
+ container_of(clock_info, struct ena_phc_info, clock_info);
+ u32 error_bound_nsec;
+ unsigned long flags;
+ u64 timestamp_nsec;
+ int rc;
+
+ spin_lock_irqsave(&phc_info->lock, flags);
+
+ ptp_read_system_prets(sts);
+
+ rc = ena_com_phc_get_timestamp(phc_info->adapter->ena_dev,
+ ×tamp_nsec,
+ &error_bound_nsec);
+
+ ptp_read_system_postts(sts);
+
+ spin_unlock_irqrestore(&phc_info->lock, flags);
+
+ if (rc)
+ return rc;
+
+ *ts = ns_to_timespec64(timestamp_nsec);
+
+ if (att) {
+ att->error_bound = error_bound_nsec;
+ att->valid |= PTP_ATTRS_VALID_ERROR_BOUND;
+ }
+
+ return 0;
+}
+
static int ena_phc_gettimex64(struct ptp_clock_info *clock_info,
struct timespec64 *ts,
struct ptp_system_timestamp *sts)
@@ -62,16 +99,17 @@ static int ena_phc_settime64(struct ptp_clock_info *clock_info,
}
static struct ptp_clock_info ena_ptp_clock_info = {
- .owner = THIS_MODULE,
- .n_alarm = 0,
- .n_ext_ts = 0,
- .n_per_out = 0,
- .pps = 0,
- .adjtime = ena_phc_adjtime,
- .adjfine = ena_phc_adjfine,
- .gettimex64 = ena_phc_gettimex64,
- .settime64 = ena_phc_settime64,
- .enable = ena_phc_feature_enable,
+ .owner = THIS_MODULE,
+ .n_alarm = 0,
+ .n_ext_ts = 0,
+ .n_per_out = 0,
+ .pps = 0,
+ .adjtime = ena_phc_adjtime,
+ .adjfine = ena_phc_adjfine,
+ .gettimexattrs64 = ena_phc_gettimexattrs64,
+ .gettimex64 = ena_phc_gettimex64,
+ .settime64 = ena_phc_settime64,
+ .enable = ena_phc_feature_enable,
};
/* Enable/Disable PHC by the kernel, affects on the next init flow */
--
2.47.3
^ permalink raw reply related
* [PATCH v4 net-next 6/7] net: ena: Add error bound to PHC communication layer
From: Arthur Kiyanovski @ 2026-07-14 2:03 UTC (permalink / raw)
To: David Miller, Jakub Kicinski, netdev
Cc: Arthur Kiyanovski, Richard Cochran, Eric Dumazet, Paolo Abeni,
David Woodhouse, Thomas Gleixner, Miroslav Lichvar, Andrew Lunn,
Wen Gu, Xuan Zhuo, David Woodhouse, Yonatan Sarna,
Zorik Machulsky, Alexander Matushevsky, Saeed Bshara, Matt Wilson,
Anthony Liguori, Nafea Bshara, Evgeny Schmeilin, Netanel Belgazal,
Ali Saidi, Benjamin Herrenschmidt, Noam Dagan, David Arinzon,
Evgeny Ostrovsky, Ofir Tabachnik, Amit Bernstein, linux-kselftest,
linux-doc, shuah, Jonathan Corbet, Shuah Khan, Simon Horman,
vadim.fedorenko
In-Reply-To: <20260714020340.25014-1-akiyano@amazon.com>
Extend the ENA PHC communication layer to retrieve error bound from
the device.
Update ena_com_phc_get_timestamp() to retrieve error_bound alongside
timestamps.
Add error handling and statistics for error_bound retrieval failures.
Signed-off-by: Amit Bernstein <amitbern@amazon.com>
Signed-off-by: Arthur Kiyanovski <akiyano@amazon.com>
---
.../device_drivers/ethernet/amazon/ena.rst | 2 +
drivers/net/ethernet/amazon/ena/ena_com.c | 40 ++++++++++++-------
drivers/net/ethernet/amazon/ena/ena_com.h | 5 ++-
drivers/net/ethernet/amazon/ena/ena_debugfs.c | 3 ++
drivers/net/ethernet/amazon/ena/ena_phc.c | 3 +-
5 files changed, 36 insertions(+), 17 deletions(-)
diff --git a/Documentation/networking/device_drivers/ethernet/amazon/ena.rst b/Documentation/networking/device_drivers/ethernet/amazon/ena.rst
index 14784a0a6a8a..ce9ba84bfd01 100644
--- a/Documentation/networking/device_drivers/ethernet/amazon/ena.rst
+++ b/Documentation/networking/device_drivers/ethernet/amazon/ena.rst
@@ -306,6 +306,8 @@ PHC errors must remain below 1% of all PHC requests to maintain the desired leve
**phc_err_dv** | Number of failed get time attempts due to device errors (entering into block state).
**phc_err_ts** | Number of failed get time attempts due to timestamp errors (entering into block state),
| This occurs if driver exceeded the request limit or device received an invalid timestamp.
+**phc_err_eb** | Number of failed get time attempts due to error bound errors (entering into block state),
+ | This occurs if device received an excessively high or invalid error bound.
================= ======================================================
PHC timeouts:
diff --git a/drivers/net/ethernet/amazon/ena/ena_com.c b/drivers/net/ethernet/amazon/ena/ena_com.c
index 40d1fd64bc34..353cb88e880e 100644
--- a/drivers/net/ethernet/amazon/ena/ena_com.c
+++ b/drivers/net/ethernet/amazon/ena/ena_com.c
@@ -45,7 +45,8 @@
#define ENA_PHC_DEFAULT_EXPIRE_TIMEOUT_USEC 10
#define ENA_PHC_DEFAULT_BLOCK_TIMEOUT_USEC 1000
#define ENA_PHC_REQ_ID_OFFSET 0xDEAD
-#define ENA_PHC_ERROR_FLAGS (ENA_ADMIN_PHC_ERROR_FLAG_TIMESTAMP)
+#define ENA_PHC_ERROR_FLAGS (ENA_ADMIN_PHC_ERROR_FLAG_TIMESTAMP | \
+ ENA_ADMIN_PHC_ERROR_FLAG_ERROR_BOUND)
/*****************************************************************************/
/*****************************************************************************/
@@ -1726,7 +1727,7 @@ int ena_com_phc_config(struct ena_com_dev *ena_dev)
if (phc->expire_timeout_usec > phc->block_timeout_usec)
phc->expire_timeout_usec = phc->block_timeout_usec;
- /* Prepare PHC feature command */
+ /* Prepare PHC config feature command */
memset(&set_feat_cmd, 0x0, sizeof(set_feat_cmd));
set_feat_cmd.aq_common_descriptor.opcode = ENA_ADMIN_SET_FEATURE;
set_feat_cmd.feat_common.feature_id = ENA_ADMIN_PHC_CONFIG;
@@ -1781,7 +1782,8 @@ void ena_com_phc_destroy(struct ena_com_dev *ena_dev)
phc->virt_addr = NULL;
}
-int ena_com_phc_get_timestamp(struct ena_com_dev *ena_dev, u64 *timestamp)
+int ena_com_phc_get_timestamp(struct ena_com_dev *ena_dev, u64 *timestamp,
+ u32 *error_bound)
{
const ktime_t zero_system_time = ktime_set(0, 0);
struct ena_com_phc_info *phc = &ena_dev->phc;
@@ -1828,6 +1830,8 @@ int ena_com_phc_get_timestamp(struct ena_com_dev *ena_dev, u64 *timestamp)
* a PHC error, this occurs if device:
* - exceeded the get time request limit
* - received an invalid timestamp
+ * - received an excessively high error bound
+ * - received an invalid error bound
*/
netdev_err(ena_dev->net_device,
"PHC get time request 0x%x failed (error 0x%x)\n",
@@ -1835,9 +1839,11 @@ int ena_com_phc_get_timestamp(struct ena_com_dev *ena_dev, u64 *timestamp)
resp->error_flags);
phc->stats.phc_err_ts += !!(resp->error_flags &
ENA_ADMIN_PHC_ERROR_FLAG_TIMESTAMP);
+ phc->stats.phc_err_eb += !!(resp->error_flags &
+ ENA_ADMIN_PHC_ERROR_FLAG_ERROR_BOUND);
} else {
/* Device updated req_id during blocking time
- * with valid timestamp
+ * with valid timestamp and error bound
*/
phc->stats.phc_exp++;
}
@@ -1864,9 +1870,9 @@ int ena_com_phc_get_timestamp(struct ena_com_dev *ena_dev, u64 *timestamp)
/* Stalling until the device updates req_id */
while (1) {
if (unlikely(ktime_after(ktime_get(), expire_time))) {
- /* Gave up waiting for updated req_id, PHC enters into
- * blocked state until passing blocking time,
- * during this time any get PHC timestamp will fail with
+ /* Gave up waiting for updated req_id,
+ * PHC enters into blocked state until passing blocking
+ * time, during this time, any request will fail with
* device busy error
*/
ret = -EBUSY;
@@ -1881,20 +1887,21 @@ int ena_com_phc_get_timestamp(struct ena_com_dev *ena_dev, u64 *timestamp)
continue;
}
- /* Ensure PHC payload (timestamp, error_flags) is read
- * after req_id update is observed
+ /* Ensure PHC payload (timestamp, error_bound, error_flags)
+ * is read after req_id update is observed
*/
dma_rmb();
/* req_id was updated by the device which indicates that
- * PHC timestamp and error_flags are updated too,
- * checking errors before retrieving timestamp
+ * PHC timestamp, error_bound and error_flags are updated too,
+ * checking error flags before retrieving timestamp and
+ * error_bound values
*/
if (unlikely(resp->error_flags & ENA_PHC_ERROR_FLAGS)) {
- /* Retrieved invalid PHC timestamp, PHC enters into
- * blocked state until passing blocking time,
- * during this time any get PHC timestamp requests
- * will fail with device busy error
+ /* Retrieved timestamp or error bound errors,
+ * PHC enters into blocked state until passing blocking
+ * time, during this time, any request will fail with
+ * device busy error
*/
ret = -EBUSY;
break;
@@ -1902,12 +1909,15 @@ int ena_com_phc_get_timestamp(struct ena_com_dev *ena_dev, u64 *timestamp)
/* PHC timestamp value is returned to the caller */
*timestamp = resp->timestamp;
+ if (error_bound)
+ *error_bound = resp->error_bound;
/* Update statistic on valid PHC timestamp retrieval */
phc->stats.phc_cnt++;
/* This indicates PHC state is active */
phc->system_time = zero_system_time;
+
break;
}
diff --git a/drivers/net/ethernet/amazon/ena/ena_com.h b/drivers/net/ethernet/amazon/ena/ena_com.h
index 64df2c48c9a6..fcbff1a9eb7a 100644
--- a/drivers/net/ethernet/amazon/ena/ena_com.h
+++ b/drivers/net/ethernet/amazon/ena/ena_com.h
@@ -216,6 +216,7 @@ struct ena_com_stats_phc {
u64 phc_skp;
u64 phc_err_dv;
u64 phc_err_ts;
+ u64 phc_err_eb;
};
struct ena_com_admin_queue {
@@ -462,9 +463,11 @@ void ena_com_phc_destroy(struct ena_com_dev *ena_dev);
/* ena_com_phc_get_timestamp - Retrieve PHC timestamp
* @ena_dev: ENA communication layer struct
* @timestamp: Retrieved PHC timestamp
+ * @error_bound: maximum possible deviation of the timestamp (nanosecond)
* @return - 0 on success, negative value on failure
*/
-int ena_com_phc_get_timestamp(struct ena_com_dev *ena_dev, u64 *timestamp);
+int ena_com_phc_get_timestamp(struct ena_com_dev *ena_dev, u64 *timestamp,
+ u32 *error_bound);
/* ena_com_set_mmio_read_mode - Enable/disable the indirect mmio reg read mechanism
* @ena_dev: ENA communication layer struct
diff --git a/drivers/net/ethernet/amazon/ena/ena_debugfs.c b/drivers/net/ethernet/amazon/ena/ena_debugfs.c
index 46ed80986724..db9d1843996b 100644
--- a/drivers/net/ethernet/amazon/ena/ena_debugfs.c
+++ b/drivers/net/ethernet/amazon/ena/ena_debugfs.c
@@ -32,6 +32,9 @@ static int phc_stats_show(struct seq_file *file, void *priv)
seq_printf(file,
"phc_err_ts: %llu\n",
adapter->ena_dev->phc.stats.phc_err_ts);
+ seq_printf(file,
+ "phc_err_eb: %llu\n",
+ adapter->ena_dev->phc.stats.phc_err_eb);
return 0;
}
diff --git a/drivers/net/ethernet/amazon/ena/ena_phc.c b/drivers/net/ethernet/amazon/ena/ena_phc.c
index c2a3ff1ef645..2bcb5af564e2 100644
--- a/drivers/net/ethernet/amazon/ena/ena_phc.c
+++ b/drivers/net/ethernet/amazon/ena/ena_phc.c
@@ -40,7 +40,8 @@ static int ena_phc_gettimex64(struct ptp_clock_info *clock_info,
ptp_read_system_prets(sts);
rc = ena_com_phc_get_timestamp(phc_info->adapter->ena_dev,
- ×tamp_nsec);
+ ×tamp_nsec,
+ NULL);
ptp_read_system_postts(sts);
--
2.47.3
^ permalink raw reply related
* [PATCH v4 net-next 5/7] net: ena: Update PHC admin interface for error bound support
From: Arthur Kiyanovski @ 2026-07-14 2:03 UTC (permalink / raw)
To: David Miller, Jakub Kicinski, netdev
Cc: Arthur Kiyanovski, Richard Cochran, Eric Dumazet, Paolo Abeni,
David Woodhouse, Thomas Gleixner, Miroslav Lichvar, Andrew Lunn,
Wen Gu, Xuan Zhuo, David Woodhouse, Yonatan Sarna,
Zorik Machulsky, Alexander Matushevsky, Saeed Bshara, Matt Wilson,
Anthony Liguori, Nafea Bshara, Evgeny Schmeilin, Netanel Belgazal,
Ali Saidi, Benjamin Herrenschmidt, Noam Dagan, David Arinzon,
Evgeny Ostrovsky, Ofir Tabachnik, Amit Bernstein, linux-kselftest,
linux-doc, shuah, Jonathan Corbet, Shuah Khan, Simon Horman,
vadim.fedorenko
In-Reply-To: <20260714020340.25014-1-akiyano@amazon.com>
Extend the ENA admin interface to support error bound.
Add error_bound to the PHC response structure.
Introduce a feature version mechanism to indicate device supports
error_bound, and add an error flag for error_bound retrieval failures.
This enables the driver to retrieve error_bound information from the
device alongside timestamps.
Signed-off-by: Amit Bernstein <amitbern@amazon.com>
Signed-off-by: Arthur Kiyanovski <akiyano@amazon.com>
---
.../net/ethernet/amazon/ena/ena_admin_defs.h | 17 +++++++++++------
drivers/net/ethernet/amazon/ena/ena_com.c | 11 ++++++-----
2 files changed, 17 insertions(+), 11 deletions(-)
diff --git a/drivers/net/ethernet/amazon/ena/ena_admin_defs.h b/drivers/net/ethernet/amazon/ena/ena_admin_defs.h
index 898ecd96b96a..2d132c4bc590 100644
--- a/drivers/net/ethernet/amazon/ena/ena_admin_defs.h
+++ b/drivers/net/ethernet/amazon/ena/ena_admin_defs.h
@@ -128,12 +128,14 @@ enum ena_admin_get_stats_scope {
ENA_ADMIN_ETH_TRAFFIC = 1,
};
-enum ena_admin_phc_type {
- ENA_ADMIN_PHC_TYPE_READLESS = 0,
+enum ena_admin_phc_feature_version {
+ /* Readless with error_bound */
+ ENA_ADMIN_PHC_FEATURE_VERSION_0 = 0,
};
enum ena_admin_phc_error_flags {
ENA_ADMIN_PHC_ERROR_FLAG_TIMESTAMP = BIT(0),
+ ENA_ADMIN_PHC_ERROR_FLAG_ERROR_BOUND = BIT(1),
};
/* ENA SRD configuration for ENI */
@@ -1035,10 +1037,10 @@ struct ena_admin_queue_ext_feature_desc {
};
struct ena_admin_feature_phc_desc {
- /* PHC type as defined in enum ena_admin_get_phc_type,
- * used only for GET command.
+ /* PHC version as defined in enum ena_admin_phc_feature_version,
+ * used only for GET command as max supported PHC version by the device.
*/
- u8 type;
+ u8 version;
/* Reserved - MBZ */
u8 reserved1[3];
@@ -1224,7 +1226,10 @@ struct ena_admin_phc_resp {
/* PHC timestamp (nsec) */
u64 timestamp;
- u8 reserved2[12];
+ u8 reserved2[8];
+
+ /* Timestamp error limit (nsec) */
+ u32 error_bound;
/* Bit field of enum ena_admin_phc_error_flags */
u32 error_flags;
diff --git a/drivers/net/ethernet/amazon/ena/ena_com.c b/drivers/net/ethernet/amazon/ena/ena_com.c
index 297fb36ab8c1..40d1fd64bc34 100644
--- a/drivers/net/ethernet/amazon/ena/ena_com.c
+++ b/drivers/net/ethernet/amazon/ena/ena_com.c
@@ -1682,11 +1682,11 @@ int ena_com_phc_config(struct ena_com_dev *ena_dev)
struct ena_admin_set_feat_cmd set_feat_cmd;
int ret = 0;
- /* Get device PHC default configuration */
+ /* Get default device PHC configuration */
ret = ena_com_get_feature(ena_dev,
&get_feat_resp,
ENA_ADMIN_PHC_CONFIG,
- 0);
+ ENA_ADMIN_PHC_FEATURE_VERSION_0);
if (unlikely(ret)) {
netdev_err(ena_dev->net_device,
"Failed to get PHC feature configuration, error: %d\n",
@@ -1694,10 +1694,11 @@ int ena_com_phc_config(struct ena_com_dev *ena_dev)
return ret;
}
- /* Supporting only readless PHC retrieval */
- if (get_feat_resp.u.phc.type != ENA_ADMIN_PHC_TYPE_READLESS) {
+ /* Supporting only PHC V0 (readless mode with error bound) */
+ if (get_feat_resp.u.phc.version != ENA_ADMIN_PHC_FEATURE_VERSION_0) {
netdev_err(ena_dev->net_device,
- "Unsupported PHC type, error: %d\n",
+ "Unsupported PHC version (0x%X), error: %d\n",
+ get_feat_resp.u.phc.version,
-EOPNOTSUPP);
return -EOPNOTSUPP;
}
--
2.47.3
^ permalink raw reply related
* [PATCH v4 net-next 4/7] ptp: ptp_vmclock: Implement attributes ioctls
From: Arthur Kiyanovski @ 2026-07-14 2:03 UTC (permalink / raw)
To: David Miller, Jakub Kicinski, netdev
Cc: Arthur Kiyanovski, Richard Cochran, Eric Dumazet, Paolo Abeni,
David Woodhouse, Thomas Gleixner, Miroslav Lichvar, Andrew Lunn,
Wen Gu, Xuan Zhuo, David Woodhouse, Yonatan Sarna,
Zorik Machulsky, Alexander Matushevsky, Saeed Bshara, Matt Wilson,
Anthony Liguori, Nafea Bshara, Evgeny Schmeilin, Netanel Belgazal,
Ali Saidi, Benjamin Herrenschmidt, Noam Dagan, David Arinzon,
Evgeny Ostrovsky, Ofir Tabachnik, Amit Bernstein, linux-kselftest,
linux-doc, shuah, Jonathan Corbet, Shuah Khan, Simon Horman,
vadim.fedorenko
In-Reply-To: <20260714020340.25014-1-akiyano@amazon.com>
Implement the gettimexattrs64 and getcrosststampattrs callbacks in the
ptp_vmclock driver to provide clock quality attributes through the new
PTP_SYS_OFFSET_EXTENDED_ATTRS and PTP_SYS_OFFSET_PRECISE_ATTRS ioctls.
The ptp_vmclock device exposes:
- error_bound: Derived from time_maxerror_nanosec, accumulated with
counter frequency error (counter_period_maxerror_rate_frac_sec) over
elapsed counter ticks
- clock_status: Mapped from the device's clock_status field
- timescale: Always reports TAI (UTC sources are converted by tai_adjust()
before the attributes are populated)
The legacy ioctls return -EINVAL when clock_status is UNRELIABLE since
they have no way to communicate clock state to userspace. The attrs
ioctls have a status field for this purpose, so they treat UNRELIABLE
as success and let userspace check the status field.
To avoid a race where the hypervisor could update clock_status between
the timestamp call and the UNRELIABLE check, the clock state is captured
inside the seq_count loop for a consistent snapshot with the timestamp.
Signed-off-by: Arthur Kiyanovski <akiyano@amazon.com>
---
drivers/ptp/ptp_vmclock.c | 197 ++++++++++++++++++++++++++++++++++----
1 file changed, 181 insertions(+), 16 deletions(-)
diff --git a/drivers/ptp/ptp_vmclock.c b/drivers/ptp/ptp_vmclock.c
index eebdcd5ebc08..16b728c0f591 100644
--- a/drivers/ptp/ptp_vmclock.c
+++ b/drivers/ptp/ptp_vmclock.c
@@ -53,6 +53,17 @@ struct vmclock_state {
char *name;
};
+/**
+ * struct vmclock_crosststamp_ctx - context for get_device_system_crosststamp()
+ * @st: vmclock device state
+ * @attrs: optional output for PTP clock attributes, populated inside the
+ * seq_count loop for a consistent snapshot with the timestamp
+ */
+struct vmclock_crosststamp_ctx {
+ struct vmclock_state *st;
+ struct ptp_clock_attrs *attrs;
+};
+
#define VMCLOCK_MAX_WAIT ms_to_ktime(100)
/* Require at least the flags field to be present. All else can be optional. */
@@ -95,13 +106,111 @@ static bool tai_adjust(struct vmclock_abi *clk, uint64_t *sec)
return false;
}
+static uint8_t vmclock_get_ptp_timescale(uint8_t vmclock_time_type)
+{
+ switch (vmclock_time_type) {
+ case VMCLOCK_TIME_UTC:
+ return PTP_TIMESCALE_UTC;
+ case VMCLOCK_TIME_TAI:
+ return PTP_TIMESCALE_TAI;
+ case VMCLOCK_TIME_MONOTONIC:
+ return PTP_TIMESCALE_MONOTONIC;
+ default:
+ return PTP_TIMESCALE_UNKNOWN;
+ }
+}
+
+static uint8_t vmclock_get_ptp_status(uint8_t vmclock_status)
+{
+ switch (vmclock_status) {
+ case VMCLOCK_STATUS_UNKNOWN:
+ return PTP_CLOCK_STATUS_UNKNOWN;
+ case VMCLOCK_STATUS_INITIALIZING:
+ return PTP_CLOCK_STATUS_INITIALIZING;
+ case VMCLOCK_STATUS_SYNCHRONIZED:
+ return PTP_CLOCK_STATUS_SYNCED;
+ case VMCLOCK_STATUS_FREERUNNING:
+ return PTP_CLOCK_STATUS_FREE_RUNNING;
+ case VMCLOCK_STATUS_UNRELIABLE:
+ return PTP_CLOCK_STATUS_UNRELIABLE;
+ default:
+ return PTP_CLOCK_STATUS_UNKNOWN;
+ }
+}
+
+static void vmclock_populate_ptp_attributes(struct vmclock_state *st,
+ struct ptp_clock_attrs *att,
+ uint64_t delta)
+{
+ uint64_t maxerror_ns = UINT_MAX;
+
+ if (!att)
+ return;
+
+ /* Only calculate if the base error is flagged as valid
+ * by the hypervisor.
+ */
+ if (VMCLOCK_FIELD_PRESENT(st->clk, time_maxerror_nanosec) &&
+ (le64_to_cpu(st->clk->flags) & VMCLOCK_FLAG_TIME_MAXERROR_VALID)) {
+ maxerror_ns = le64_to_cpu(st->clk->time_maxerror_nanosec);
+
+ /* If frequency error is also valid, accumulate it
+ * over the delta.
+ */
+ if (VMCLOCK_FIELD_PRESENT(st->clk, counter_period_maxerror_rate_frac_sec) &&
+ (le64_to_cpu(st->clk->flags) & VMCLOCK_FLAG_PERIOD_MAXERROR_VALID)) {
+ uint64_t maxerror_rate, err_hi, err_frac, growth_ns;
+
+ if (st->clk->counter_period_shift >= 128) {
+ maxerror_ns = U64_MAX;
+ goto saturate;
+ }
+
+ maxerror_rate = le64_to_cpu(st->clk->counter_period_maxerror_rate_frac_sec);
+ err_frac = mul_u64_u64_shr_add_u64(&err_hi, delta,
+ maxerror_rate,
+ st->clk->counter_period_shift,
+ 0);
+
+ if (err_hi >= U64_MAX / NSEC_PER_SEC) {
+ maxerror_ns = U64_MAX;
+ goto saturate;
+ }
+
+ growth_ns = (err_hi * NSEC_PER_SEC) +
+ mul_u64_u64_shr(err_frac, NSEC_PER_SEC, 64);
+
+ /* Guard against overflow */
+ if (U64_MAX - growth_ns < maxerror_ns)
+ maxerror_ns = U64_MAX;
+ else
+ maxerror_ns += growth_ns;
+ }
+ }
+
+saturate:
+ /* PTP UAPI error_bound is 32-bit nanoseconds */
+ att->error_bound = (maxerror_ns > UINT_MAX) ?
+ UINT_MAX : (uint32_t)maxerror_ns;
+ att->valid |= PTP_ATTRS_VALID_ERROR_BOUND;
+ att->timescale = vmclock_get_ptp_timescale(st->clk->time_type);
+ /* tai_adjust() already converted UTC to TAI before we're called */
+ if (st->clk->time_type == VMCLOCK_TIME_UTC)
+ att->timescale = PTP_TIMESCALE_TAI;
+ att->valid |= PTP_ATTRS_VALID_TIMESCALE;
+ att->status = vmclock_get_ptp_status(st->clk->clock_status);
+ att->valid |= PTP_ATTRS_VALID_STATUS;
+}
+
static int vmclock_get_crosststamp(struct vmclock_state *st,
struct ptp_system_timestamp *sts,
struct system_counterval_t *system_counter,
- struct timespec64 *tspec)
+ struct timespec64 *tspec,
+ struct ptp_clock_attrs *attrs)
{
ktime_t deadline = ktime_add(ktime_get(), VMCLOCK_MAX_WAIT);
uint64_t cycle, delta, seq, frac_sec;
+ uint8_t clock_status = VMCLOCK_STATUS_UNKNOWN;
#ifdef CONFIG_X86
/*
@@ -121,9 +230,6 @@ static int vmclock_get_crosststamp(struct vmclock_state *st,
*/
virt_rmb();
- if (st->clk->clock_status == VMCLOCK_STATUS_UNRELIABLE)
- return -EINVAL;
-
/*
* When invoked for gettimex64(), fill in the pre/post system
* times. The simple case is when system time is based on the
@@ -164,6 +270,17 @@ static int vmclock_get_crosststamp(struct vmclock_state *st,
if (!tai_adjust(st->clk, &tspec->tv_sec))
return -EINVAL;
+ /*
+ * Capture clock state inside the seq_count loop for a
+ * consistent snapshot with the timestamp. The attrs path
+ * reports it to userspace via the status field; the legacy
+ * path saves it for the UNRELIABLE check after the loop.
+ */
+ if (attrs)
+ vmclock_populate_ptp_attributes(st, attrs, delta);
+ else
+ clock_status = st->clk->clock_status;
+
/*
* This pairs with a write barrier in the hypervisor
* which populates this structure.
@@ -181,6 +298,17 @@ static int vmclock_get_crosststamp(struct vmclock_state *st,
system_counter->cs_id = st->cs_id;
}
+ /*
+ * If attrs is set, attributes were already populated inside the
+ * seq_count loop. Return success even for UNRELIABLE — the attrs
+ * ioctl can report the status to userspace.
+ */
+ if (attrs)
+ return 0;
+
+ if (clock_status == VMCLOCK_STATUS_UNRELIABLE)
+ return -EINVAL;
+
return 0;
}
@@ -193,7 +321,8 @@ static int vmclock_get_crosststamp(struct vmclock_state *st,
static int vmclock_get_crosststamp_kvmclock(struct vmclock_state *st,
struct ptp_system_timestamp *sts,
struct system_counterval_t *system_counter,
- struct timespec64 *tspec)
+ struct timespec64 *tspec,
+ struct ptp_clock_attrs *attrs)
{
struct pvclock_vcpu_time_info *pvti = this_cpu_pvti();
unsigned int pvti_ver;
@@ -204,7 +333,8 @@ static int vmclock_get_crosststamp_kvmclock(struct vmclock_state *st,
do {
pvti_ver = pvclock_read_begin(pvti);
- ret = vmclock_get_crosststamp(st, sts, system_counter, tspec);
+ ret = vmclock_get_crosststamp(st, sts, system_counter, tspec,
+ attrs);
if (ret)
break;
@@ -233,17 +363,19 @@ static int ptp_vmclock_get_time_fn(ktime_t *device_time,
struct system_counterval_t *system_counter,
void *ctx)
{
- struct vmclock_state *st = ctx;
+ struct vmclock_crosststamp_ctx *vctx = ctx;
+ struct vmclock_state *st = vctx->st;
struct timespec64 tspec;
int ret;
#ifdef SUPPORT_KVMCLOCK
if (READ_ONCE(st->sys_cs_id) == CSID_X86_KVM_CLK)
ret = vmclock_get_crosststamp_kvmclock(st, NULL, system_counter,
- &tspec);
+ &tspec, vctx->attrs);
else
#endif
- ret = vmclock_get_crosststamp(st, NULL, system_counter, &tspec);
+ ret = vmclock_get_crosststamp(st, NULL, system_counter, &tspec,
+ vctx->attrs);
if (!ret)
*device_time = timespec64_to_ktime(tspec);
@@ -251,12 +383,11 @@ static int ptp_vmclock_get_time_fn(ktime_t *device_time,
return ret;
}
-static int ptp_vmclock_getcrosststamp(struct ptp_clock_info *ptp,
- struct system_device_crosststamp *xtstamp)
+static int ptp_vmclock_do_getcrosststamp(struct vmclock_crosststamp_ctx *vctx,
+ struct system_device_crosststamp *xtstamp)
{
- struct vmclock_state *st = container_of(ptp, struct vmclock_state,
- ptp_clock_info);
- int ret = get_device_system_crosststamp(ptp_vmclock_get_time_fn, st,
+ struct vmclock_state *st = vctx->st;
+ int ret = get_device_system_crosststamp(ptp_vmclock_get_time_fn, vctx,
NULL, xtstamp);
#ifdef SUPPORT_KVMCLOCK
/*
@@ -273,13 +404,23 @@ static int ptp_vmclock_getcrosststamp(struct ptp_clock_info *ptp,
systime_snapshot.cs_id == CSID_X86_KVM_CLK) {
WRITE_ONCE(st->sys_cs_id, systime_snapshot.cs_id);
ret = get_device_system_crosststamp(ptp_vmclock_get_time_fn,
- st, NULL, xtstamp);
+ vctx, NULL, xtstamp);
}
}
#endif
return ret;
}
+static int ptp_vmclock_getcrosststamp(struct ptp_clock_info *ptp,
+ struct system_device_crosststamp *xtstamp)
+{
+ struct vmclock_state *st = container_of(ptp, struct vmclock_state,
+ ptp_clock_info);
+ struct vmclock_crosststamp_ctx vctx = { .st = st };
+
+ return ptp_vmclock_do_getcrosststamp(&vctx, xtstamp);
+}
+
/*
* PTP clock operations
*/
@@ -306,7 +447,29 @@ static int ptp_vmclock_gettimex(struct ptp_clock_info *ptp, struct timespec64 *t
struct vmclock_state *st = container_of(ptp, struct vmclock_state,
ptp_clock_info);
- return vmclock_get_crosststamp(st, sts, NULL, ts);
+ return vmclock_get_crosststamp(st, sts, NULL, ts, NULL);
+}
+
+static int ptp_vmclock_gettimexattrs(struct ptp_clock_info *ptp,
+ struct timespec64 *ts,
+ struct ptp_system_timestamp *sts,
+ struct ptp_clock_attrs *att)
+{
+ struct vmclock_state *st = container_of(ptp, struct vmclock_state,
+ ptp_clock_info);
+
+ return vmclock_get_crosststamp(st, sts, NULL, ts, att);
+}
+
+static int ptp_vmclock_getcrosststampattrs(struct ptp_clock_info *ptp,
+ struct system_device_crosststamp *xtstamp,
+ struct ptp_clock_attrs *att)
+{
+ struct vmclock_state *st = container_of(ptp, struct vmclock_state,
+ ptp_clock_info);
+ struct vmclock_crosststamp_ctx vctx = { .st = st, .attrs = att };
+
+ return ptp_vmclock_do_getcrosststamp(&vctx, xtstamp);
}
static int ptp_vmclock_enable(struct ptp_clock_info *ptp,
@@ -324,9 +487,11 @@ static const struct ptp_clock_info ptp_vmclock_info = {
.adjfine = ptp_vmclock_adjfine,
.adjtime = ptp_vmclock_adjtime,
.gettimex64 = ptp_vmclock_gettimex,
+ .gettimexattrs64 = ptp_vmclock_gettimexattrs,
.settime64 = ptp_vmclock_settime,
.enable = ptp_vmclock_enable,
.getcrosststamp = ptp_vmclock_getcrosststamp,
+ .getcrosststampattrs = ptp_vmclock_getcrosststampattrs,
};
static struct ptp_clock *vmclock_ptp_register(struct device *dev,
--
2.47.3
^ permalink raw reply related
* [PATCH v4 net-next 3/7] selftests/ptp: Add testptp support for attributes ioctls
From: Arthur Kiyanovski @ 2026-07-14 2:03 UTC (permalink / raw)
To: David Miller, Jakub Kicinski, netdev
Cc: Arthur Kiyanovski, Richard Cochran, Eric Dumazet, Paolo Abeni,
David Woodhouse, Thomas Gleixner, Miroslav Lichvar, Andrew Lunn,
Wen Gu, Xuan Zhuo, David Woodhouse, Yonatan Sarna,
Zorik Machulsky, Alexander Matushevsky, Saeed Bshara, Matt Wilson,
Anthony Liguori, Nafea Bshara, Evgeny Schmeilin, Netanel Belgazal,
Ali Saidi, Benjamin Herrenschmidt, Noam Dagan, David Arinzon,
Evgeny Ostrovsky, Ofir Tabachnik, Amit Bernstein, linux-kselftest,
linux-doc, shuah, Jonathan Corbet, Shuah Khan, Simon Horman,
vadim.fedorenko
In-Reply-To: <20260714020340.25014-1-akiyano@amazon.com>
Add support for testing the new PTP_SYS_OFFSET_EXTENDED_ATTRS and
PTP_SYS_OFFSET_PRECISE_ATTRS ioctls in the testptp utility.
New command-line options:
-a: Get extended offset with attributes (error_bound, clock_status,
timescale)
-A: Get precise cross-timestamp with attributes
These options allow testing and validation of PHC devices that provide
clock quality information alongside timestamps.
Also display the new clock_attrs capability in the -c output, and
update print_system_timestamp to print unrecognized clock types instead
of silently dropping them.
Signed-off-by: Amit Bernstein <amitbern@amazon.com>
Signed-off-by: Arthur Kiyanovski <akiyano@amazon.com>
---
tools/testing/selftests/ptp/testptp.c | 111 +++++++++++++++++++++++++-
1 file changed, 109 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/ptp/testptp.c b/tools/testing/selftests/ptp/testptp.c
index d3bcfd03fce4..99607da01b22 100644
--- a/tools/testing/selftests/ptp/testptp.c
+++ b/tools/testing/selftests/ptp/testptp.c
@@ -147,10 +147,13 @@ static void usage(char *progname)
" -t val shift the ptp clock time by 'val' seconds\n"
" -T val set the ptp clock time to 'val' seconds\n"
" -x val get an extended ptp clock time with the desired number of samples (up to %d)\n"
+ " -a val get extended timestamps with attributes (error_bound,\n"
+ " clock_status, timescale, counter), up to %d samples\n"
" -X get a ptp clock cross timestamp\n"
+ " -A get a precise cross timestamp with attributes\n"
" -y val pre/post tstamp timebase to use {realtime|monotonic|monotonic-raw}\n"
" -z test combinations of rising/falling external time stamp flags\n",
- progname, PTP_MAX_SAMPLES);
+ progname, PTP_MAX_SAMPLES, PTP_MAX_SAMPLES);
}
static void print_system_timestamp(int sample_num, __kernel_clockid_t clockid,
@@ -171,6 +174,8 @@ static void print_system_timestamp(int sample_num, __kernel_clockid_t clockid,
sample_num, when, sec, nsec);
break;
default:
+ printf("sample #%2d: unknown clock %d %s: %lld.%09u\n",
+ sample_num, clockid, when, sec, nsec);
break;
}
}
@@ -188,6 +193,7 @@ int main(int argc, char *argv[])
struct ptp_sys_offset *sysoff;
struct ptp_sys_offset_extended *soe;
struct ptp_sys_offset_precise *xts;
+ struct ptp_sys_offset_attrs *attrs_data;
char *progname;
unsigned int i;
@@ -208,7 +214,9 @@ int main(int argc, char *argv[])
int list_pins = 0;
int pct_offset = 0;
int getextended = 0;
+ int getextendedattrs = 0;
int getcross = 0;
+ int getcrossattrs = 0;
int n_samples = 0;
int pin_index = -1, pin_func;
int pps = -1;
@@ -226,7 +234,8 @@ int main(int argc, char *argv[])
progname = strrchr(argv[0], '/');
progname = progname ? 1+progname : argv[0];
- while (EOF != (c = getopt(argc, argv, "cd:e:E:f:F:ghH:i:k:lL:n:o:p:P:rsSt:T:w:x:Xy:z"))) {
+ while (EOF != (c = getopt(argc, argv,
+ "a:Acd:e:E:f:F:ghH:i:k:lL:n:o:p:P:rsSt:T:w:x:Xy:z"))) {
switch (c) {
case 'c':
capabilities = 1;
@@ -311,9 +320,22 @@ int main(int argc, char *argv[])
return -1;
}
break;
+ case 'a':
+ getextendedattrs = atoi(optarg);
+ if (getextendedattrs < 1 ||
+ getextendedattrs > PTP_MAX_SAMPLES) {
+ fprintf(stderr,
+ "number of extended attrs timestamp samples must be between 1 and %d; was asked for %d\n",
+ PTP_MAX_SAMPLES, getextendedattrs);
+ return -1;
+ }
+ break;
case 'X':
getcross = 1;
break;
+ case 'A':
+ getcrossattrs = 1;
+ break;
case 'y':
if (!strcasecmp(optarg, "realtime"))
ext_clockid = CLOCK_REALTIME;
@@ -367,6 +389,8 @@ int main(int argc, char *argv[])
" %d programmable pins\n"
" %d cross timestamping\n"
" %d adjust_phase\n"
+ " %d extended_attrs\n"
+ " %d precise_attrs\n"
" %d maximum phase adjustment (ns)\n",
caps.max_adj,
caps.n_alarm,
@@ -376,6 +400,8 @@ int main(int argc, char *argv[])
caps.n_pins,
caps.cross_timestamping,
caps.adjust_phase,
+ caps.extended_attrs,
+ caps.precise_attrs,
caps.max_phase_adj);
}
}
@@ -648,6 +674,49 @@ int main(int argc, char *argv[])
free(soe);
}
+ if (getextendedattrs) {
+ attrs_data = calloc(1, sizeof(*attrs_data) +
+ getextendedattrs * sizeof(struct ptp_timestamp));
+ if (!attrs_data) {
+ perror("calloc");
+ return -1;
+ }
+
+ attrs_data->request.num_samples = getextendedattrs;
+ attrs_data->request.clock_id = ext_clockid;
+
+ if (ioctl(fd, PTP_SYS_OFFSET_EXTENDED_ATTRS, attrs_data)) {
+ perror("PTP_SYS_OFFSET_EXTENDED_ATTRS");
+ } else {
+ printf("extended attrs timestamp request returned %d samples\n",
+ getextendedattrs);
+
+ for (i = 0; i < getextendedattrs; i++) {
+ struct ptp_timestamp *ts = &attrs_data->timestamps[i];
+
+ printf(" sample #%u:\n", i);
+ printf(" sys before: %lld ns\n",
+ (long long)ts->pre_systime.sys_time);
+ printf(" phc time: %lld.%09u\n",
+ ts->devtime.device_time.sec,
+ ts->devtime.device_time.nsec);
+ if (ts->devtime.attrs.valid & PTP_ATTRS_VALID_ERROR_BOUND)
+ printf(" error_bound: %u ns\n",
+ ts->devtime.attrs.error_bound);
+ if (ts->devtime.attrs.valid & PTP_ATTRS_VALID_STATUS)
+ printf(" status: %u\n",
+ ts->devtime.attrs.status);
+ if (ts->devtime.attrs.valid & PTP_ATTRS_VALID_TIMESCALE)
+ printf(" timescale: %u\n",
+ ts->devtime.attrs.timescale);
+ printf(" sys after: %lld ns\n",
+ (long long)ts->post_systime.sys_time);
+ }
+ }
+
+ free(attrs_data);
+ }
+
if (getcross) {
xts = calloc(1, sizeof(*xts));
if (!xts) {
@@ -671,6 +740,44 @@ int main(int argc, char *argv[])
free(xts);
}
+ if (getcrossattrs) {
+ attrs_data = calloc(1, sizeof(*attrs_data) +
+ sizeof(struct ptp_timestamp));
+ if (!attrs_data) {
+ perror("calloc");
+ return -1;
+ }
+
+ attrs_data->request.num_samples = 1;
+ attrs_data->request.clock_id = ext_clockid;
+
+ if (ioctl(fd, PTP_SYS_OFFSET_PRECISE_ATTRS, attrs_data)) {
+ perror("PTP_SYS_OFFSET_PRECISE_ATTRS");
+ } else {
+ struct ptp_timestamp *ts = &attrs_data->timestamps[0];
+
+ puts("precise attrs crosstimestamp request okay");
+ printf("device time: %lld.%09u\n",
+ ts->devtime.device_time.sec,
+ ts->devtime.device_time.nsec);
+ printf("system time: %lld ns\n",
+ (long long)ts->systime.sys_time);
+ printf("raw time: %lld ns\n",
+ (long long)ts->systime.sys_rawtime);
+ if (ts->devtime.attrs.valid & PTP_ATTRS_VALID_ERROR_BOUND)
+ printf("error_bound: %u ns\n",
+ ts->devtime.attrs.error_bound);
+ if (ts->devtime.attrs.valid & PTP_ATTRS_VALID_STATUS)
+ printf("status: %u\n",
+ ts->devtime.attrs.status);
+ if (ts->devtime.attrs.valid & PTP_ATTRS_VALID_TIMESCALE)
+ printf("timescale: %u\n",
+ ts->devtime.attrs.timescale);
+ }
+
+ free(attrs_data);
+ }
+
if (channel >= 0) {
if (ioctl(fd, PTP_MASK_CLEAR_ALL)) {
perror("PTP_MASK_CLEAR_ALL");
--
2.47.3
^ permalink raw reply related
* [PATCH v4 net-next 2/7] selftests/ptp: Extract print_system_timestamp helper in testptp
From: Arthur Kiyanovski @ 2026-07-14 2:03 UTC (permalink / raw)
To: David Miller, Jakub Kicinski, netdev
Cc: Arthur Kiyanovski, Richard Cochran, Eric Dumazet, Paolo Abeni,
David Woodhouse, Thomas Gleixner, Miroslav Lichvar, Andrew Lunn,
Wen Gu, Xuan Zhuo, David Woodhouse, Yonatan Sarna,
Zorik Machulsky, Alexander Matushevsky, Saeed Bshara, Matt Wilson,
Anthony Liguori, Nafea Bshara, Evgeny Schmeilin, Netanel Belgazal,
Ali Saidi, Benjamin Herrenschmidt, Noam Dagan, David Arinzon,
Evgeny Ostrovsky, Ofir Tabachnik, Amit Bernstein, linux-kselftest,
linux-doc, shuah, Jonathan Corbet, Shuah Khan, Simon Horman,
vadim.fedorenko
In-Reply-To: <20260714020340.25014-1-akiyano@amazon.com>
Extract the repeated switch-on-clockid pattern used for printing
system timestamps into a reusable helper function. This removes
code duplication in the -x (PTP_SYS_OFFSET_EXTENDED) output path
and prepares for additional callers.
The "after" timestamp lines now include the sample number prefix
for consistency with the "before" lines, slightly changing the
output format.
Signed-off-by: Arthur Kiyanovski <akiyano@amazon.com>
---
tools/testing/selftests/ptp/testptp.c | 70 ++++++++++++---------------
1 file changed, 32 insertions(+), 38 deletions(-)
diff --git a/tools/testing/selftests/ptp/testptp.c b/tools/testing/selftests/ptp/testptp.c
index ed1e2886ba3c..d3bcfd03fce4 100644
--- a/tools/testing/selftests/ptp/testptp.c
+++ b/tools/testing/selftests/ptp/testptp.c
@@ -153,6 +153,28 @@ static void usage(char *progname)
progname, PTP_MAX_SAMPLES);
}
+static void print_system_timestamp(int sample_num, __kernel_clockid_t clockid,
+ long long sec, unsigned int nsec,
+ const char *when)
+{
+ switch (clockid) {
+ case CLOCK_REALTIME:
+ printf("sample #%2d: real time %s: %lld.%09u\n",
+ sample_num, when, sec, nsec);
+ break;
+ case CLOCK_MONOTONIC:
+ printf("sample #%2d: monotonic time %s: %lld.%09u\n",
+ sample_num, when, sec, nsec);
+ break;
+ case CLOCK_MONOTONIC_RAW:
+ printf("sample #%2d: monotonic-raw time %s: %lld.%09u\n",
+ sample_num, when, sec, nsec);
+ break;
+ default:
+ break;
+ }
+}
+
int main(int argc, char *argv[])
{
struct ptp_clock_caps caps;
@@ -608,46 +630,18 @@ int main(int argc, char *argv[])
getextended);
for (i = 0; i < getextended; i++) {
- switch (ext_clockid) {
- case CLOCK_REALTIME:
- printf("sample #%2d: real time before: %lld.%09u\n",
- i, soe->ts[i][0].sec,
- soe->ts[i][0].nsec);
- break;
- case CLOCK_MONOTONIC:
- printf("sample #%2d: monotonic time before: %lld.%09u\n",
- i, soe->ts[i][0].sec,
- soe->ts[i][0].nsec);
- break;
- case CLOCK_MONOTONIC_RAW:
- printf("sample #%2d: monotonic-raw time before: %lld.%09u\n",
- i, soe->ts[i][0].sec,
- soe->ts[i][0].nsec);
- break;
- default:
- break;
- }
+ print_system_timestamp(i, ext_clockid,
+ soe->ts[i][0].sec,
+ soe->ts[i][0].nsec,
+ "before");
+
printf(" phc time: %lld.%09u\n",
soe->ts[i][1].sec, soe->ts[i][1].nsec);
- switch (ext_clockid) {
- case CLOCK_REALTIME:
- printf(" real time after: %lld.%09u\n",
- soe->ts[i][2].sec,
- soe->ts[i][2].nsec);
- break;
- case CLOCK_MONOTONIC:
- printf(" monotonic time after: %lld.%09u\n",
- soe->ts[i][2].sec,
- soe->ts[i][2].nsec);
- break;
- case CLOCK_MONOTONIC_RAW:
- printf(" monotonic-raw time after: %lld.%09u\n",
- soe->ts[i][2].sec,
- soe->ts[i][2].nsec);
- break;
- default:
- break;
- }
+
+ print_system_timestamp(i, ext_clockid,
+ soe->ts[i][2].sec,
+ soe->ts[i][2].nsec,
+ "after");
}
}
--
2.47.3
^ permalink raw reply related
* [PATCH v4 net-next 1/7] ptp: Add ioctls for PHC timestamps with quality attributes
From: Arthur Kiyanovski @ 2026-07-14 2:03 UTC (permalink / raw)
To: David Miller, Jakub Kicinski, netdev
Cc: Arthur Kiyanovski, Richard Cochran, Eric Dumazet, Paolo Abeni,
David Woodhouse, Thomas Gleixner, Miroslav Lichvar, Andrew Lunn,
Wen Gu, Xuan Zhuo, David Woodhouse, Yonatan Sarna,
Zorik Machulsky, Alexander Matushevsky, Saeed Bshara, Matt Wilson,
Anthony Liguori, Nafea Bshara, Evgeny Schmeilin, Netanel Belgazal,
Ali Saidi, Benjamin Herrenschmidt, Noam Dagan, David Arinzon,
Evgeny Ostrovsky, Ofir Tabachnik, Amit Bernstein, linux-kselftest,
linux-doc, shuah, Jonathan Corbet, Shuah Khan, Simon Horman,
vadim.fedorenko
In-Reply-To: <20260714020340.25014-1-akiyano@amazon.com>
Introduce two new ioctls that extend existing PTP timestamp interfaces
with clock quality information:
- PTP_SYS_OFFSET_EXTENDED_ATTRS: Extends PTP_SYS_OFFSET_EXTENDED
- PTP_SYS_OFFSET_PRECISE_ATTRS: Extends PTP_SYS_OFFSET_PRECISE
These ioctls provide quality attributes alongside timestamps:
1. error_bound: Maximum deviation from true time (nanoseconds), based
on device's internal clock state
2. clock_status: Synchronization state (unknown, initializing,
synchronized, free-running, unreliable)
3. timescale: Time reference (TAI, UTC, etc.)
4. counter_value: Raw system counter (e.g. TSC ticks) captured by the
timekeeping core alongside each system timestamp
5. counter_id: Identifies the counter source (e.g. TSC, ARM arch counter)
This supports three use cases:
1. Managed PHC devices (e.g., ENA, vmclock) that maintain their own
synchronization and can report quality metrics directly to userspace
without requiring ptp4l
2. Applications that need complete time quality information in a single
call, regardless of how the PHC is synchronized
3. VMMs that need raw system counter values paired
with PTP timestamps for feed-forward clock calibration, avoiding the
feedback loop inherent in NTP-style synchronization
Timescale definitions use a Continuity/Discipline framework to describe
timeline properties and steering behavior consistently across all
entries.
This implementation is based on the original RFC and the UAPI design
discussion linked below.
Link: https://lore.kernel.org/netdev/20250724115657.150-1-darinzon@amazon.com/
Link: https://lore.kernel.org/all/87se7ht25o.ffs@tglx/
Signed-off-by: Amit Bernstein <amitbern@amazon.com>
Signed-off-by: Arthur Kiyanovski <akiyano@amazon.com>
---
drivers/ptp/ptp_chardev.c | 166 ++++++++++++++++++--
drivers/ptp/ptp_clock.c | 4 +-
include/linux/ptp_clock_kernel.h | 30 ++++
include/uapi/linux/ptp_clock.h | 254 ++++++++++++++++++++++++++++++-
4 files changed, 439 insertions(+), 15 deletions(-)
diff --git a/drivers/ptp/ptp_chardev.c b/drivers/ptp/ptp_chardev.c
index dc23cd708cfe..be33f8ead727 100644
--- a/drivers/ptp/ptp_chardev.c
+++ b/drivers/ptp/ptp_chardev.c
@@ -190,6 +190,8 @@ static long ptp_clock_getcaps(struct ptp_clock *ptp, void __user *arg)
.cross_timestamping = ptp->info->getcrosststamp != NULL,
.adjust_phase = ptp->info->adjphase != NULL &&
ptp->info->getmaxphase != NULL,
+ .extended_attrs = ptp->info->gettimexattrs64 != NULL,
+ .precise_attrs = ptp->info->getcrosststampattrs != NULL,
};
if (caps.adjust_phase)
@@ -347,11 +349,28 @@ typedef int (*ptp_gettimex_fn)(struct ptp_clock_info *,
struct timespec64 *,
struct ptp_system_timestamp *);
+static int ptp_validate_sys_offset_clockid(__kernel_clockid_t clockid)
+{
+ switch (clockid) {
+ case CLOCK_REALTIME:
+ case CLOCK_MONOTONIC:
+ case CLOCK_MONOTONIC_RAW:
+ return 0;
+ case CLOCK_AUX ... CLOCK_AUX_LAST:
+ if (IS_ENABLED(CONFIG_POSIX_AUX_CLOCKS))
+ return 0;
+ fallthrough;
+ default:
+ return -EINVAL;
+ }
+}
+
static long ptp_sys_offset_extended(struct ptp_clock *ptp, void __user *arg,
ptp_gettimex_fn gettimex_fn)
{
struct ptp_sys_offset_extended *extoff __free(kfree) = NULL;
struct ptp_system_timestamp sts;
+ int err;
if (!gettimex_fn)
return -EOPNOTSUPP;
@@ -363,23 +382,13 @@ static long ptp_sys_offset_extended(struct ptp_clock *ptp, void __user *arg,
if (extoff->n_samples > PTP_MAX_SAMPLES || extoff->rsv[0] || extoff->rsv[1])
return -EINVAL;
- switch (extoff->clockid) {
- case CLOCK_REALTIME:
- case CLOCK_MONOTONIC:
- case CLOCK_MONOTONIC_RAW:
- break;
- case CLOCK_AUX ... CLOCK_AUX_LAST:
- if (IS_ENABLED(CONFIG_POSIX_AUX_CLOCKS))
- break;
- fallthrough;
- default:
- return -EINVAL;
- }
+ err = ptp_validate_sys_offset_clockid(extoff->clockid);
+ if (err)
+ return err;
sts.clockid = extoff->clockid;
for (unsigned int i = 0; i < extoff->n_samples; i++) {
struct timespec64 ts;
- int err;
err = gettimex_fn(ptp->info, &ts, &sts);
if (err)
@@ -404,6 +413,131 @@ static long ptp_sys_offset_extended(struct ptp_clock *ptp, void __user *arg,
return copy_to_user(arg, extoff, sizeof(*extoff)) ? -EFAULT : 0;
}
+static long ptp_sys_offset_extended_attrs(struct ptp_clock *ptp, void __user *arg)
+{
+ struct ptp_sys_offset_attrs *data __free(kfree) = NULL;
+ struct ptp_system_timestamp sts;
+ unsigned int n_samples;
+ int err;
+
+ data = memdup_user(arg, sizeof(*data));
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ if (data->request.valid ||
+ data->request.num_samples > PTP_MAX_SAMPLES ||
+ data->request.num_samples == 0)
+ return -EINVAL;
+
+ err = ptp_validate_sys_offset_clockid(data->request.clock_id);
+ if (err)
+ return err;
+
+ n_samples = data->request.num_samples;
+ sts.clockid = data->request.clock_id;
+ kfree(data);
+ data = kzalloc(struct_size(data, timestamps, n_samples), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->request.num_samples = n_samples;
+
+ for (unsigned int i = 0; i < n_samples; i++) {
+ struct ptp_clock_attrs att = {};
+ struct timespec64 ts;
+
+ if (ptp->info->gettimexattrs64)
+ err = ptp->info->gettimexattrs64(ptp->info, &ts,
+ &sts, &att);
+ else if (ptp->info->gettimex64)
+ err = ptp->info->gettimex64(ptp->info, &ts, &sts);
+ else
+ return -EOPNOTSUPP;
+
+ if (err)
+ return err;
+
+ /* Filter out disabled or unavailable clocks */
+ if (!sts.pre_sts.valid || !sts.post_sts.valid)
+ return -EINVAL;
+
+ data->timestamps[i].pre_systime.sys_time =
+ ktime_to_ns(sts.pre_sts.systime);
+ data->timestamps[i].pre_systime.sys_rawtime =
+ ktime_to_ns(sts.pre_sts.monoraw);
+ data->timestamps[i].pre_systime.sys_counter =
+ sts.pre_sts.cycles;
+ data->timestamps[i].pre_systime.sys_counter_id =
+ sts.pre_sts.cs_id;
+ data->timestamps[i].devtime.device_time.sec = ts.tv_sec;
+ data->timestamps[i].devtime.device_time.nsec = ts.tv_nsec;
+ data->timestamps[i].devtime.attrs = att;
+ data->timestamps[i].post_systime.sys_time =
+ ktime_to_ns(sts.post_sts.systime);
+ data->timestamps[i].post_systime.sys_rawtime =
+ ktime_to_ns(sts.post_sts.monoraw);
+ data->timestamps[i].post_systime.sys_counter =
+ sts.post_sts.cycles;
+ data->timestamps[i].post_systime.sys_counter_id =
+ sts.post_sts.cs_id;
+ }
+
+ return copy_to_user(arg, data,
+ struct_size(data, timestamps, n_samples)) ? -EFAULT : 0;
+}
+
+static long ptp_sys_offset_precise_attrs(struct ptp_clock *ptp, void __user *arg)
+{
+ struct ptp_sys_offset_attrs *data __free(kfree) = NULL;
+ struct system_device_crosststamp xtstamp;
+ struct ptp_clock_attrs att = {};
+ struct timespec64 ts;
+ int err;
+
+ data = memdup_user(arg, sizeof(*data));
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ if (data->request.valid ||
+ data->request.num_samples != 1)
+ return -EINVAL;
+
+ err = ptp_validate_sys_offset_clockid(data->request.clock_id);
+ if (err)
+ return err;
+
+ kfree(data);
+ data = kzalloc(struct_size(data, timestamps, 1), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ if (ptp->info->getcrosststampattrs)
+ err = ptp->info->getcrosststampattrs(ptp->info, &xtstamp, &att);
+ else if (ptp->info->getcrosststamp)
+ err = ptp->info->getcrosststamp(ptp->info, &xtstamp);
+ else
+ return -EOPNOTSUPP;
+
+ if (err)
+ return err;
+
+ ts = ktime_to_timespec64(xtstamp.device);
+ data->timestamps[0].systime.sys_time =
+ ktime_to_ns(xtstamp.sys_systime);
+ data->timestamps[0].systime.sys_rawtime =
+ ktime_to_ns(xtstamp.sys_monoraw);
+ data->timestamps[0].systime.sys_counter =
+ xtstamp.sys_counter.cycles;
+ data->timestamps[0].systime.sys_counter_id =
+ xtstamp.sys_counter.cs_id;
+ data->timestamps[0].devtime.device_time.sec = ts.tv_sec;
+ data->timestamps[0].devtime.device_time.nsec = ts.tv_nsec;
+ data->timestamps[0].devtime.attrs = att;
+
+ return copy_to_user(arg, data,
+ struct_size(data, timestamps, 1)) ? -EFAULT : 0;
+}
+
static long ptp_sys_offset(struct ptp_clock *ptp, void __user *arg)
{
struct ptp_sys_offset *sysoff __free(kfree) = NULL;
@@ -539,11 +673,17 @@ long ptp_ioctl(struct posix_clock_context *pccontext, unsigned int cmd,
return ptp_sys_offset_precise(ptp, argptr,
ptp->info->getcrosststamp);
+ case PTP_SYS_OFFSET_PRECISE_ATTRS:
+ return ptp_sys_offset_precise_attrs(ptp, argptr);
+
case PTP_SYS_OFFSET_EXTENDED:
case PTP_SYS_OFFSET_EXTENDED2:
return ptp_sys_offset_extended(ptp, argptr,
ptp->info->gettimex64);
+ case PTP_SYS_OFFSET_EXTENDED_ATTRS:
+ return ptp_sys_offset_extended_attrs(ptp, argptr);
+
case PTP_SYS_OFFSET:
case PTP_SYS_OFFSET2:
return ptp_sys_offset(ptp, argptr);
diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c
index d6f54ccaf93b..849aef8191c5 100644
--- a/drivers/ptp/ptp_clock.c
+++ b/drivers/ptp/ptp_clock.c
@@ -112,7 +112,9 @@ static int ptp_clock_gettime(struct posix_clock *pc, struct timespec64 *tp)
struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
int err;
- if (ptp->info->gettimex64)
+ if (ptp->info->gettimexattrs64)
+ err = ptp->info->gettimexattrs64(ptp->info, tp, NULL, NULL);
+ else if (ptp->info->gettimex64)
err = ptp->info->gettimex64(ptp->info, tp, NULL);
else
err = ptp->info->gettime64(ptp->info, tp);
diff --git a/include/linux/ptp_clock_kernel.h b/include/linux/ptp_clock_kernel.h
index 36a27a910595..b2a418081687 100644
--- a/include/linux/ptp_clock_kernel.h
+++ b/include/linux/ptp_clock_kernel.h
@@ -123,11 +123,34 @@ struct ptp_system_timestamp {
* reading the lowest bits of the PHC timestamp and the second
* reading immediately follows that.
*
+ * @gettimexattrs64: Reads the current time from the hardware clock and
+ * optionally also the system clock with additional clock
+ * attributes.
+ * parameter ts: Holds the PHC timestamp.
+ * parameter sts: If not NULL, it holds a pair of
+ * timestamps from the system clock. The first reading is
+ * made right before reading the lowest bits of the PHC
+ * timestamp and the second reading immediately follows that.
+ * parameter att: If not NULL, it holds the maximum error
+ * bound for the returned PHC timestamp in nanoseconds,
+ * the timescale for the returned PHC timestamp and the
+ * clock's qualitative synchronization status.
+ *
* @getcrosststamp: Reads the current time from the hardware clock and
* system clock simultaneously.
* parameter cts: Contains timestamp (device,system) pair,
* where system time is realtime and monotonic.
*
+ * @getcrosststampattrs: Reads the current time from the hardware clock and
+ * system clock simultaneously with additional data on
+ * hardware clock accuracy and reliability.
+ * parameter cts: Contains timestamp (device,system)
+ * pair, where system time is realtime and monotonic.
+ * parameter att: If not NULL, it holds the maximum error
+ * bound for the returned PHC timestamp in nanoseconds,
+ * the timescale for the returned PHC timestamp and the
+ * clock's qualitative synchronization status.
+ *
* @settime64: Set the current time on the hardware clock.
* parameter ts: Time value to set.
*
@@ -209,8 +232,15 @@ struct ptp_clock_info {
int (*gettime64)(struct ptp_clock_info *ptp, struct timespec64 *ts);
int (*gettimex64)(struct ptp_clock_info *ptp, struct timespec64 *ts,
struct ptp_system_timestamp *sts);
+ int (*gettimexattrs64)(struct ptp_clock_info *ptp,
+ struct timespec64 *ts,
+ struct ptp_system_timestamp *sts,
+ struct ptp_clock_attrs *att);
int (*getcrosststamp)(struct ptp_clock_info *ptp,
struct system_device_crosststamp *cts);
+ int (*getcrosststampattrs)(struct ptp_clock_info *ptp,
+ struct system_device_crosststamp *cts,
+ struct ptp_clock_attrs *att);
int (*settime64)(struct ptp_clock_info *p, const struct timespec64 *ts);
int (*getcycles64)(struct ptp_clock_info *ptp, struct timespec64 *ts);
int (*getcyclesx64)(struct ptp_clock_info *ptp, struct timespec64 *ts,
diff --git a/include/uapi/linux/ptp_clock.h b/include/uapi/linux/ptp_clock.h
index 46d45f902486..88c2da6bc8c6 100644
--- a/include/uapi/linux/ptp_clock.h
+++ b/include/uapi/linux/ptp_clock.h
@@ -79,6 +79,137 @@
*/
#define PTP_PEROUT_V1_VALID_FLAGS (0)
+/*
+ * Clock status values for struct ptp_clock_attrs.status
+ */
+enum ptp_clock_status {
+ /* Clock synchronization status cannot be reliably determined */
+ PTP_CLOCK_STATUS_UNKNOWN = 0,
+
+ /* Clock is acquiring synchronization */
+ PTP_CLOCK_STATUS_INITIALIZING = 1,
+
+ /* Clock is synchronized and maintained accurately by the device */
+ PTP_CLOCK_STATUS_SYNCED = 2,
+
+ /* Clock is drifting but remains within acceptable error bounds */
+ PTP_CLOCK_STATUS_HOLDOVER = 3,
+
+ /* Clock is drifting without adjustments or synchronization */
+ PTP_CLOCK_STATUS_FREE_RUNNING = 4,
+
+ /* Clock is unreliable, the error_bound value cannot be trusted */
+ PTP_CLOCK_STATUS_UNRELIABLE = 5
+};
+
+/*
+ * Clock timescale values for struct ptp_clock_attrs.timescale.
+ *
+ * These definitions describe the mathematical properties and reference
+ * epochs of the timescale provided by the PHC.
+ *
+ * Discipline: Describes the frequency/phase steering behavior.
+ * Continuity: Describes whether the timeline is uninterrupted.
+ */
+enum ptp_clock_timescale {
+ /* Unknown or unspecified timescale */
+ PTP_TIMESCALE_UNKNOWN = 0,
+
+ /********************* Absolute Atomic Timescales *********************
+ * These timescales are continuous, monotonic standards based on atomic
+ * physics. They do not experience phase jumps.
+ **********************************************************************/
+
+ /**
+ * International Atomic Time (TAI)
+ * Epoch: 1958-01-01 00:00:00.
+ * Continuity: Strictly monotonic and continuous; no leap seconds.
+ * Discipline: Primary atomic reference; no phase jumps.
+ */
+ PTP_TIMESCALE_TAI = 1,
+
+ /**
+ * Terrestrial Time (TT)
+ * Epoch: 1958-01-01 00:00:00.
+ * Continuity: Strictly monotonic and continuous; no leap seconds.
+ * Discipline: Defined as TAI + 32.184s constant offset.
+ */
+ PTP_TIMESCALE_TT = 2,
+
+ /**
+ * Global Positioning System (GPS) Time
+ * Epoch: 1980-01-06 00:00:00.
+ * Continuity: Strictly monotonic and continuous; no leap seconds.
+ * Discipline: Defined by the GPS constellation; fixed offset from TAI.
+ */
+ PTP_TIMESCALE_GPS = 3,
+
+ /****************** UTC-Based Timescales (Civil Time) *****************
+ * These timescales are derived from TAI but adjusted to align with
+ * the Earth's rotation, primarily through leap seconds.
+ **********************************************************************/
+
+ /**
+ * Coordinated Universal Time (UTC) - Wall-clock (CLOCK_REALTIME)
+ * Epoch: 1970-01-01 00:00:00 (Unix epoch).
+ * Continuity: Discontinuous; subject to 1-second leap second
+ * phase jumps.
+ * Discipline: Frequency steered; incorporates leap second corrections.
+ *
+ * Note: Leap-smeared UTC MUST NOT be advertised as PTP_TIMESCALE_UTC.
+ * Smear algorithms are not standardized and the resulting timescale
+ * is ambiguous. Implementations using smeared UTC MUST advertise
+ * PTP_TIMESCALE_UNKNOWN or PTP_TIMESCALE_PROPRIETARY instead.
+ */
+ PTP_TIMESCALE_UTC = 4,
+
+ /**
+ * POSIX Time (Unix Time)
+ * Epoch: 1970-01-01 00:00:00.
+ * Continuity: Discontinuous; leap seconds handled by
+ * repeating/skipping values.
+ * Discipline: Follows UTC frequency steering and phase jumps.
+ */
+ PTP_TIMESCALE_POSIX = 5,
+
+ /****************** System-Relative Monotonic Clocks ******************
+ * These timescales are relative to a system event (like boot)
+ * and are not synchronized to an external atomic standard.
+ **********************************************************************/
+
+ /**
+ * Monotonic System Clock (CLOCK_MONOTONIC)
+ * Epoch: Arbitrary (System boot time).
+ * Continuity: Strictly monotonic; no leap seconds.
+ * Discipline: Frequency steered to match system reference;
+ * does not advance during suspend.
+ */
+ PTP_TIMESCALE_MONOTONIC = 6,
+
+ /**
+ * Raw Monotonic System Clock (CLOCK_MONOTONIC_RAW)
+ * Epoch: Arbitrary (System boot time).
+ * Continuity: Strictly monotonic; no leap seconds.
+ * Discipline: Raw hardware oscillator; no frequency steering
+ * or discipline.
+ */
+ PTP_TIMESCALE_MONOTONIC_RAW = 7,
+
+ /**
+ * Boot Time System Clock (CLOCK_BOOTTIME)
+ * Epoch: Arbitrary (System boot time).
+ * Continuity: Strictly monotonic and continuous; no leap seconds.
+ * Discipline: Frequency steered to match system reference;
+ * advances during suspend.
+ */
+ PTP_TIMESCALE_BOOTTIME = 8,
+
+ /********************** Vendor-Specific Timescale *********************/
+
+ /* A proprietary or vendor-specific timescale with custom rules. */
+ PTP_TIMESCALE_PROPRIETARY = 9,
+};
+
/*
* struct ptp_clock_time - represents a time value
*
@@ -94,6 +225,119 @@ struct ptp_clock_time {
__u32 reserved;
};
+/*
+ * Hardware counter identifiers for struct ptp_sys_time.sys_counter_id
+ */
+enum ptp_counter_id {
+ /* Counter value not available or type not specified */
+ PTP_COUNTER_UNKNOWN = 0,
+
+ /* x86 Time Stamp Counter (TSC) */
+ PTP_COUNTER_X86_TSC = 1,
+
+ /* ARM Generic Timer virtual counter */
+ PTP_COUNTER_ARM_ARCH = 2,
+};
+
+/* Valid flags for struct ptp_clock_attrs.valid */
+#define PTP_ATTRS_VALID_ERROR_BOUND (1 << 0)
+#define PTP_ATTRS_VALID_TIMESCALE (1 << 1)
+#define PTP_ATTRS_VALID_STATUS (1 << 2)
+
+/**
+ * struct ptp_clock_attrs - quality attributes for a PHC timestamp
+ *
+ * @valid: Bitmask of PTP_ATTRS_VALID_* indicating which fields
+ * are populated. Zero means no attributes available.
+ * @error_bound: Maximum error in nanoseconds. Valid only when
+ * PTP_ATTRS_VALID_ERROR_BOUND is set.
+ * @timescale: Clock timescale (enum ptp_clock_timescale). Valid only
+ * when PTP_ATTRS_VALID_TIMESCALE is set.
+ * @status: Synchronization status (enum ptp_clock_status). Valid
+ * only when PTP_ATTRS_VALID_STATUS is set.
+ * @rsv: Reserved for future use, must be zero.
+ */
+struct ptp_clock_attrs {
+ __u32 valid;
+ __u32 error_bound;
+ __u32 timescale;
+ __u32 status;
+ __u32 rsv[4];
+};
+
+/**
+ * struct ptp_sys_time - system time snapshot with counter value
+ *
+ * @sys_time: System time in nanoseconds (clock selected by request).
+ * @sys_rawtime: CLOCK_MONOTONIC_RAW time in nanoseconds.
+ * @sys_counter: Raw clocksource counter value (0 = unavailable).
+ * @sys_counter_id: Identifies the counter (enum ptp_counter_id).
+ * @rsv: Reserved for future use, must be zero.
+ */
+struct ptp_sys_time {
+ __s64 sys_time;
+ __s64 sys_rawtime;
+ __u64 sys_counter;
+ __u32 sys_counter_id;
+ __u32 rsv;
+};
+
+/**
+ * struct ptp_dev_time - device timestamp with quality attributes
+ *
+ * @device_time: PHC timestamp value.
+ * @attrs: Quality attributes for this timestamp.
+ */
+struct ptp_dev_time {
+ struct ptp_clock_time device_time;
+ struct ptp_clock_attrs attrs;
+};
+
+/**
+ * struct ptp_timestamp - a complete timestamp sample
+ *
+ * For PTP_SYS_OFFSET_EXTENDED_ATTRS: pre_systime and post_systime bracket
+ * the device read (ABA sandwich).
+ * For PTP_SYS_OFFSET_PRECISE_ATTRS: only systime (union with pre_systime)
+ * is meaningful; post_systime is zeroed.
+ */
+struct ptp_timestamp {
+ union {
+ struct ptp_sys_time systime;
+ struct ptp_sys_time pre_systime;
+ };
+ struct ptp_dev_time devtime;
+ struct ptp_sys_time post_systime;
+};
+
+/**
+ * struct ptp_attrs_request - request parameters for attrs ioctls
+ *
+ * @valid: Bitmask for future request extensions. Must be zero for now.
+ * @clock_id: Clock base for system timestamps (CLOCK_REALTIME, etc).
+ * @num_samples: Number of timestamp samples requested.
+ * For PTP_SYS_OFFSET_PRECISE_ATTRS must be 1.
+ * @rsv: Reserved for future use, must be zero.
+ */
+struct ptp_attrs_request {
+ __u32 valid;
+ __kernel_clockid_t clock_id;
+ __u32 num_samples;
+ __u32 rsv[3];
+};
+
+/**
+ * struct ptp_sys_offset_attrs - unified data structure for attrs ioctls
+ *
+ * Used by both PTP_SYS_OFFSET_EXTENDED_ATTRS and
+ * PTP_SYS_OFFSET_PRECISE_ATTRS. Userspace allocates space for
+ * request.num_samples entries in the timestamps array.
+ */
+struct ptp_sys_offset_attrs {
+ struct ptp_attrs_request request;
+ struct ptp_timestamp timestamps[];
+};
+
struct ptp_clock_caps {
int max_adj; /* Maximum frequency adjustment in parts per billon. */
int n_alarm; /* Number of programmable alarms. */
@@ -106,7 +350,11 @@ struct ptp_clock_caps {
/* Whether the clock supports adjust phase */
int adjust_phase;
int max_phase_adj; /* Maximum phase adjustment in nanoseconds. */
- int rsv[11]; /* Reserved for future use. */
+ /* Whether the clock supports extended timestamps with attributes */
+ int extended_attrs;
+ /* Whether the clock supports precise cross-timestamps with attributes */
+ int precise_attrs;
+ int rsv[9]; /* Reserved for future use. */
};
struct ptp_extts_request {
@@ -252,6 +500,10 @@ struct ptp_pin_desc {
_IOWR(PTP_CLK_MAGIC, 21, struct ptp_sys_offset_precise)
#define PTP_SYS_OFFSET_EXTENDED_CYCLES \
_IOWR(PTP_CLK_MAGIC, 22, struct ptp_sys_offset_extended)
+#define PTP_SYS_OFFSET_PRECISE_ATTRS \
+ _IOWR(PTP_CLK_MAGIC, 23, struct ptp_sys_offset_attrs)
+#define PTP_SYS_OFFSET_EXTENDED_ATTRS \
+ _IOWR(PTP_CLK_MAGIC, 24, struct ptp_sys_offset_attrs)
struct ptp_extts_event {
struct ptp_clock_time t; /* Time event occurred. */
--
2.47.3
^ 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