* Re: [PATCH v2] scsi: st: fix typo in documentation
From: Martin K. Petersen @ 2026-05-15 1:59 UTC (permalink / raw)
To: Wang Zihan; +Cc: Kai.Makisara, linux-scsi, linux-doc
In-Reply-To: <tencent_818C822F215676B9B14011B88848609BD309@qq.com>
Wang,
> Correct "form" to "from" in drive buffers description.
Applied to 7.2/scsi-staging, thanks!
--
Martin K. Petersen
^ permalink raw reply
* Re: kernel-doc no longer warns about leftover argument doc?
From: Randy Dunlap @ 2026-05-15 1:58 UTC (permalink / raw)
To: Jakub Kicinski, Mauro Carvalho Chehab; +Cc: linux-doc
In-Reply-To: <20260514184948.3fa705e1@kernel.org>
On 5/14/26 6:49 PM, Jakub Kicinski wrote:
> Hi!
>
> Looks like the new python kernel-doc does not warn when there are
> stray arguments in function kdoc. Eg
>
> /**
> * ksz_wol_pre_shutdown - Prepares the switch device for shutdown while
> * considering Wake-on-LAN (WoL) settings.
> * @dev: The switch device structure.
> * @wol_enabled: Pointer to a boolean which will be set to true if WoL is
> * enabled on any port.
> *
> * This function prepares the switch device for a safe shutdown while taking
> * into account the Wake-on-LAN (WoL) settings on the user ports. It updates
> * the wol_enabled flag accordingly to reflect whether WoL is active on any
> * port.
> */
> static void ksz_wol_pre_shutdown(struct ksz_device *dev)
>
>
> AIs seem to catch it but that's not ideal..
>
Yes, hopefully we can have this patch merged soon:
https://lore.kernel.org/all/20260507023232.4108680-1-shuicheng.lin@intel.com/
giving:
Warning: init/test001.c:13 Excess function parameter 'wol_enabled' description in 'ksz_wol_pre_shutdown'
--
~Randy
^ permalink raw reply
* kernel-doc no longer warns about leftover argument doc?
From: Jakub Kicinski @ 2026-05-15 1:49 UTC (permalink / raw)
To: Mauro Carvalho Chehab; +Cc: linux-doc
Hi!
Looks like the new python kernel-doc does not warn when there are
stray arguments in function kdoc. Eg
/**
* ksz_wol_pre_shutdown - Prepares the switch device for shutdown while
* considering Wake-on-LAN (WoL) settings.
* @dev: The switch device structure.
* @wol_enabled: Pointer to a boolean which will be set to true if WoL is
* enabled on any port.
*
* This function prepares the switch device for a safe shutdown while taking
* into account the Wake-on-LAN (WoL) settings on the user ports. It updates
* the wol_enabled flag accordingly to reflect whether WoL is active on any
* port.
*/
static void ksz_wol_pre_shutdown(struct ksz_device *dev)
AIs seem to catch it but that's not ideal..
^ permalink raw reply
* [RFC PATCH v2.2 28/28] Docs/admin-guide/mm/damon/usage: update for memcg damon filter
From: SeongJae Park @ 2026-05-15 0:44 UTC (permalink / raw)
Cc: SeongJae Park, Liam R. Howlett, Andrew Morton, David Hildenbrand,
Jonathan Corbet, Lorenzo Stoakes, Michal Hocko, Mike Rapoport,
Shuah Khan, Suren Baghdasaryan, Vlastimil Babka, damon, linux-doc,
linux-kernel, linux-mm
In-Reply-To: <20260515004433.128933-1-sj@kernel.org>
Update DAMON usage document for the newly added belonging memory cgroup
attribute monitoring feature.
Signed-off-by: SeongJae Park <sj@kernel.org>
---
Documentation/admin-guide/mm/damon/usage.rst | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/Documentation/admin-guide/mm/damon/usage.rst b/Documentation/admin-guide/mm/damon/usage.rst
index 44224da62533e..4b1d77f7175ab 100644
--- a/Documentation/admin-guide/mm/damon/usage.rst
+++ b/Documentation/admin-guide/mm/damon/usage.rst
@@ -73,7 +73,7 @@ comma (",").
│ │ │ │ │ │ nr_regions/min,max
│ │ │ │ │ │ :ref:`probes <damon_usage_sysfs_probes>`/nr_probes
│ │ │ │ │ │ │ 0/filters/nr_filters
- │ │ │ │ │ │ │ │ │ 0/type,matching,allow
+ │ │ │ │ │ │ │ │ │ 0/type,matching,allow,path
│ │ │ │ │ │ │ │ │ ...
│ │ │ │ │ │ │ │ ...
│ │ │ │ │ :ref:`targets <sysfs_targets>`/nr_targets
@@ -283,7 +283,9 @@ the data attribute for the probe.
In the beginning, ``filters`` directory has only one file, ``nr_filters``.
Writing a number (``N``) to the file creates the number of child directories
named ``0`` to ``N-1``. Each directory represents each filter and work in a
-way similar to that for :ref:`DAMOS filter <sysfs_filters>`.
+way similar to that for :ref:`DAMOS filter <sysfs_filters>`. When the filter
+``type`` is ``memcg``, ``path`` file works the role of ``memcg_path`` for
+:ref:`DAMOS filter <sysfs_filters>`.
.. _sysfs_targets:
--
2.47.3
^ permalink raw reply related
* [RFC PATCH v2.2 27/28] Docs/mm/damon/design: update for memcg damon filter
From: SeongJae Park @ 2026-05-15 0:44 UTC (permalink / raw)
Cc: SeongJae Park, Liam R. Howlett, Andrew Morton, David Hildenbrand,
Jonathan Corbet, Lorenzo Stoakes, Michal Hocko, Mike Rapoport,
Shuah Khan, Suren Baghdasaryan, Vlastimil Babka, damon, linux-doc,
linux-kernel, linux-mm
In-Reply-To: <20260515004433.128933-1-sj@kernel.org>
Update DAMON design document for the newly added belonging memory cgroup
attribute monitoring feature.
Signed-off-by: SeongJae Park <sj@kernel.org>
---
Documentation/mm/damon/design.rst | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Documentation/mm/damon/design.rst b/Documentation/mm/damon/design.rst
index 85d668e929194..8a2d68cbcefca 100644
--- a/Documentation/mm/damon/design.rst
+++ b/Documentation/mm/damon/design.rst
@@ -286,8 +286,8 @@ registration is made by specifying a probe per attribute. Each of the probe
specifies a rule to determine if a given memory region has the related
attribute. The rule is constructed with multiple filters. The filters work
same to :ref:`DAMOS filters <damon_design_damos_filters>` except the supported
-filter types. Currently only ``anon`` filter type is supported for data
-attributes monitoring.
+filter types. Currently only ``anon`` and ``memcg`` filter types are supported
+for data attributes monitoring.
If such probes are registered, DAMON executes the probes for each region's
sampling memory when it does the access :ref:`sampling
--
2.47.3
^ permalink raw reply related
* [RFC PATCH v2.2 20/28] Docs/mm/damon/design: document data attributes monitoring
From: SeongJae Park @ 2026-05-15 0:44 UTC (permalink / raw)
Cc: SeongJae Park, Liam R. Howlett, Andrew Morton, David Hildenbrand,
Jonathan Corbet, Lorenzo Stoakes, Michal Hocko, Mike Rapoport,
Shuah Khan, Suren Baghdasaryan, Vlastimil Babka, damon, linux-doc,
linux-kernel, linux-mm
In-Reply-To: <20260515004433.128933-1-sj@kernel.org>
Update DAMON design document for newly added data attributes monitoring
feature.
Signed-off-by: SeongJae Park <sj@kernel.org>
---
Documentation/mm/damon/design.rst | 37 +++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)
diff --git a/Documentation/mm/damon/design.rst b/Documentation/mm/damon/design.rst
index afc7d52bda2f7..aa08c899a3e5b 100644
--- a/Documentation/mm/damon/design.rst
+++ b/Documentation/mm/damon/design.rst
@@ -269,6 +269,43 @@ interval``, DAMON checks if the region's size and access frequency
(``nr_accesses``) has significantly changed. If so, the counter is reset to
zero. Otherwise, the counter is increased.
+Data Attributes Monitoring
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Data access pattern is only one type of data attributes. In some use cases,
+users need to know more data attributes information. For example, users may
+need to know how much of a given hot or cold memory region is backed by
+anonymous pages, or belong to a specific cgroup. For such use case, data
+attributes monitoring feature is provided.
+
+Using the feature, users can register data attributes of their interest to the
+DAMON :ref:`context <damon_design_execution_model_and_data_structures>`. The
+registration is made by specifying a probe per attribute. Each of the probe
+specifies a rule to determine if a given memory region has the related
+attribute. The rule is constructed with multiple filters. The filters work
+same to :ref:`DAMOS filters <damon_design_damos_filters>` except the supported
+filter types. Currently only ``anon`` filter type is supported for data
+attributes monitoring.
+
+If such probes are registered, DAMON executes the probes for each region's
+sampling memory when it does the access :ref:`sampling
+<damon_design_region_based_sampling>`. The number of samples that identified
+as having the data attribute (hitting the probe) per :ref:`aggregation interval
+<damon_design_monitoring>` is accounted in a per-region per-probe counter.
+Users can therefore know how much of a given DAMON region has a specific data
+attribute by reading the per-region per-probe probe hits counter after each
+aggregation interval.
+
+This is a sampling based mechanism. Hence, it is lightweight but the output
+may include some measurement errors. The output should be used with good
+understanding of statistics.
+
+Another way to do this for higher accuracy is using :ref:`DAMOS filter
+<damon_design_damos_filters>` with ``stat`` :ref:`action
+<damon_design_damos_action>` and ``sz_ops_filter_passed`` :ref:`stat
+<damon_design_damos_stat>`. This approach provides the data attributes
+information in page level. But, because it is operated in page level, the
+overhead is proportional to the size of the memory.
Dynamic Target Space Updates Handling
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--
2.47.3
^ permalink raw reply related
* [RFC PATCH v2.2 21/28] Docs/admin-guide/mm/damon/usage: document data attributes monitoring
From: SeongJae Park @ 2026-05-15 0:44 UTC (permalink / raw)
Cc: SeongJae Park, Liam R. Howlett, Andrew Morton, David Hildenbrand,
Jonathan Corbet, Lorenzo Stoakes, Michal Hocko, Mike Rapoport,
Shuah Khan, Suren Baghdasaryan, Vlastimil Babka, damon, linux-doc,
linux-kernel, linux-mm
In-Reply-To: <20260515004433.128933-1-sj@kernel.org>
Update DAMON usage document for the newly added data attributes
monitoring feature.
Signed-off-by: SeongJae Park <sj@kernel.org>
---
Documentation/admin-guide/mm/damon/usage.rst | 46 +++++++++++++++++---
Documentation/mm/damon/design.rst | 2 +
2 files changed, 41 insertions(+), 7 deletions(-)
diff --git a/Documentation/admin-guide/mm/damon/usage.rst b/Documentation/admin-guide/mm/damon/usage.rst
index 534e1199cf091..44224da62533e 100644
--- a/Documentation/admin-guide/mm/damon/usage.rst
+++ b/Documentation/admin-guide/mm/damon/usage.rst
@@ -71,6 +71,11 @@ comma (",").
│ │ │ │ │ │ intervals/sample_us,aggr_us,update_us
│ │ │ │ │ │ │ intervals_goal/access_bp,aggrs,min_sample_us,max_sample_us
│ │ │ │ │ │ nr_regions/min,max
+ │ │ │ │ │ │ :ref:`probes <damon_usage_sysfs_probes>`/nr_probes
+ │ │ │ │ │ │ │ 0/filters/nr_filters
+ │ │ │ │ │ │ │ │ │ 0/type,matching,allow
+ │ │ │ │ │ │ │ │ │ ...
+ │ │ │ │ │ │ │ │ ...
│ │ │ │ │ :ref:`targets <sysfs_targets>`/nr_targets
│ │ │ │ │ │ :ref:`0 <sysfs_target>`/pid_target,obsolete_target
│ │ │ │ │ │ │ :ref:`regions <sysfs_regions>`/nr_regions
@@ -94,7 +99,10 @@ comma (",").
│ │ │ │ │ │ │ │ 0/id,weight
│ │ │ │ │ │ │ :ref:`stats <sysfs_schemes_stats>`/nr_tried,sz_tried,nr_applied,sz_applied,sz_ops_filter_passed,qt_exceeds,nr_snapshots,max_nr_snapshots
│ │ │ │ │ │ │ :ref:`tried_regions <sysfs_schemes_tried_regions>`/total_bytes
- │ │ │ │ │ │ │ │ 0/start,end,nr_accesses,age,sz_filter_passed
+ │ │ │ │ │ │ │ │ 0/start,end,nr_accesses,age,sz_filter_passed,
+ │ │ │ │ │ │ │ │ │ probes
+ │ │ │ │ │ │ │ │ │ │ 0/hits
+ │ │ │ │ │ │ │ │ │ │ ...
│ │ │ │ │ │ │ │ ...
│ │ │ │ │ │ ...
│ │ │ │ ...
@@ -221,8 +229,8 @@ contexts/<N>/monitoring_attrs/
Files for specifying attributes of the monitoring including required quality
and efficiency of the monitoring are in ``monitoring_attrs`` directory.
-Specifically, two directories, ``intervals`` and ``nr_regions`` exist in this
-directory.
+Specifically, two directories, ``intervals``, ``nr_regions`` and ``probes``
+exist in this directory.
Under ``intervals`` directory, three files for DAMON's sampling interval
(``sample_us``), aggregation interval (``aggr_us``), and update interval
@@ -256,6 +264,27 @@ tuning-applied current values of the two intervals can be read from the
``sample_us`` and ``aggr_us`` files after writing ``update_tuned_intervals`` to
the ``state`` file.
+.. _damon_usage_sysfs_probes:
+
+contexts/<N>/monitoring_attrs/probes/
+-------------------------------------
+
+A directory for registering :ref:`data attributes monitoring
+<damon_design_data_attrs_monitoring>` probes.
+
+In the beginning, this directory has only one file, ``nr_probes``. Writing a
+number (``N``) to the file creates the number of child directories named ``0``
+to ``N-1``. Each directory represents each monitoring probe.
+
+In each probe directory, one directory, ``filters`` exist. The directory
+contains files for installingt filters for the probe, that is used to determine
+the data attribute for the probe.
+
+In the beginning, ``filters`` directory has only one file, ``nr_filters``.
+Writing a number (``N``) to the file creates the number of child directories
+named ``0`` to ``N-1``. Each directory represents each filter and work in a
+way similar to that for :ref:`DAMOS filter <sysfs_filters>`.
+
.. _sysfs_targets:
contexts/<N>/targets/
@@ -600,10 +629,13 @@ set the ``access pattern`` as their interested pattern that they want to query.
tried_regions/<N>/
------------------
-In each region directory, you will find five files (``start``, ``end``,
-``nr_accesses``, ``age``, and ``sz_filter_passed``). Reading the files will
-show the properties of the region that corresponding DAMON-based operation
-scheme ``action`` has tried to be applied.
+In each region directory, you will find six files (``start``, ``end``,
+``nr_accesses``, ``age``, ``sz_filter_passed`` and ``probe_hits``). Reading
+the files will show the properties of the region that corresponding DAMON-based
+operation scheme ``action`` has tried to be applied.
+
+Reading ``probe_hists`` shows the number of data attributes monitoring
+probe-hit positive samples of the region.
Example
~~~~~~~
diff --git a/Documentation/mm/damon/design.rst b/Documentation/mm/damon/design.rst
index aa08c899a3e5b..85d668e929194 100644
--- a/Documentation/mm/damon/design.rst
+++ b/Documentation/mm/damon/design.rst
@@ -269,6 +269,8 @@ interval``, DAMON checks if the region's size and access frequency
(``nr_accesses``) has significantly changed. If so, the counter is reset to
zero. Otherwise, the counter is increased.
+.. _damon_design_data_attrs_monitoring:
+
Data Attributes Monitoring
~~~~~~~~~~~~~~~~~~~~~~~~~~
--
2.47.3
^ permalink raw reply related
* [RFC PATCH v2.2 00/28] mm/damon: introduce data attributes monitoring
From: SeongJae Park @ 2026-05-15 0:44 UTC (permalink / raw)
Cc: SeongJae Park, Liam R. Howlett, Andrew Morton, David Hildenbrand,
Jonathan Corbet, Lorenzo Stoakes, Masami Hiramatsu,
Mathieu Desnoyers, Michal Hocko, Mike Rapoport, Shuah Khan,
Shuah Khan, Steven Rostedt, Suren Baghdasaryan, Vlastimil Babka,
damon, linux-doc, linux-kernel, linux-kselftest, linux-mm,
linux-trace-kernel
TL; DR
======
Extend DAMON for monitoring general data attributes other than accesses.
The short term motivation is lightweight page type (e.g., belonging
cgroup) aware monitoring. In long term, this will help extending DAMON
for multiple access events capture primitives (e.g., page faults and
PMU) and eventually pivotting DAMON to a "Data Attributes Monitoring and
Operations eNgine" in long term.
Background: High Cost of Page Level Properties Monitoring
=========================================================
DAMON is initially introduced as a Data Access MONitor. It has been
extended for not only access monitoring but also data access-aware
system operations (DAMOS). But still the monitoring part is only for
data accesses.
Data access patterns is good information, but some users need more
holistic views. Particularly, users want to show the access pattern
information together with the types of the memory. For example, users
who work for making huge pages efficiently want to know how much of
DAMON-found hot/cold regions are backed by huge pages. Users who run
multiple workloads with different cgroups want to know how much of
DAMON-found hot/cold regions belong to specific cgroups.
For the user demand, we developed a DAMOS extension for page level
properties based monitoring [1], which has landed on 6.14. Using the
feature, users can inform the page level data properties that they are
interested in, in a flexible format that uses DAMOS filters. Then,
DAMON applies the filters to each folio of the entire DAMON region and
lets users know how many bytes of memory in each DAMON region passed the
given filters.
This gives page level detailed and deterministic information to users.
But, because the operation is done at page level, the overhead is
proportional to the memory size. It was useful for test or debugging
purposes on a small number of machines. But it was obviously too heavy
to be enabled always on all machines running the real user workloads.
For real world workloads, it was recommended to use the feature with
user-space controlled sampling approaches. For example, users could do
the page level monitoring only once per hour, on randomly selected one
percent of machines of their fleet. If the runtime and the size of the
fleet is long and big enough, it should provide statistically meaningful
data.
But users are too busy to implement such controls on their own.
Data Attributes Monitoring
==========================
Extend DAMON to monitor not only data accesses, but also general data
attributes. Do the extension while keeping the main promise of DAMON,
the bounded and best-effort minimum overhead.
Allow users to specify what data attributes in addition to the data
access they want to monitor. Users can install one 'data probe' per
data attribute of their interest for this purpose. The 'data probe'
should be able to be applied to any memory, and determine if the given
memory has the appropriate data attribute. E.g., if memory of physical
address 42 belongs to cgroup A. Each 'data probe' is configured with
filters that are very similar to the DAMOS filters.
When DAMON checks if each sampling address memory of each region is
accessed since the last check, it applies data probes if registered.
Same to the number of access check-positive samples accounting
(nr_accesses), it accounts the number of each data probe-positive
samples in another per-region counters array, namely 'probe_hits'. When
DAMON resets nr_accesses every aggregation interval, it resets
'probe_hits' together.
Users can read 'probe_hits' just before the values are reset. In this
way, users can know how many hot/cold memory regions have data
attributes of their interest. E.g., 30 percent of this system's hot
memory is belonging to cgroup A, and 80 percent of the cgroup
A-belonging hot memory is backed by huge pages.
Patches Sequence
================
First eight patches implement the core feature, interface and the
working support. Patch 1 introduces data probe data structure, namely
damon_probe. Patch 2 extends damon_ctx for installing data probes.
Patch 3 introduces another data structure for filters of each data
probe, namely damon_filter. Patch 4 updates damon_ctx commit function
to handle the probes. Patch 5 extends damon_region for the per-region
per-probe positive samples counter, namely probe_hits. Patch 6 extends
damon_operations for applying probes on the underlying DAMON operations
implementation. Patch 7 updates kdamond_fn() to invoke the probes
applying callback. Patch 8 finally implements the probes support on
paddr ops.
Ten changes for user interface (patches 9-18) come next. Patches 9-13
implements sysfs directories and files for setting data probes, namely
probes directory, probe directory, filters directory, filter directory
and filter directory internal files, respectively. Patch 14 connects
the user inputs that are made via the sysfs files to DAMON core.
Following three patches (patches 15-17) implement sysfs directories and
files for showing the probe_hits to users, namely probes directory,
probe directory and hits files, respectively. Patch 18 introduces a new
tracepoint for showing the probe_hits via tracefs.
Patch 19 adds a selftest for the sysfs files.
Patches 20 and 21 documents the design and usage of the new feature,
respectively.
Seven additional patches (patches 22-28) for monitoring belonging memory
cgroup follow. Depending on the feedback, this part might be separated
to another series in future. Patch 22 defines the DAMON filter type for
the new attribute, namely DAMON_FILTER_TYPE_MEMCG. Patch 23 add the
support on paddr ops. Patch 24 updates the sysfs interface for setup of
the target memcg. Patch 25 move code for easy reuse of the filter
target memcg setup. Patch 26 connects the user input to the core layer.
Finally, patches 27 and 28 update the design and usage documents for the
memcg attribute monitoring support.
Discussions
===========
This allows the page properties monitoring with overhead that is low
enough to be enabled always on real world workloads. Because the
sampling time for access check is reused for data attributes check, the
upper-bounded and best-effort minimum overhead of DAMON is kept.
Because the sampling memory for access check is reused for data
attributes check, additional overhead is minimum.
Still DAMOS-based page level properties monitoring should be useful,
because it provides a deterministic page level information. When in
doubt of the sampling based information, running DAMOS-based one
together and comparing the results would be useful, for debugging and
tuning.
Plan for Dropping RFC tag
=========================
I'm considering renaming the tracepoint for exposing probe_hits
(damon_aggregated_v2).
Making changes for feedback from myself, humans and Sashiko should be
the major remaining work.
I'm currently hoping to drop the RFC tag by 7.2-rc1.
Future Works: Mid Term
========================
This version of implementation is limiting the maximum number of data
probes to four. I will try to find a way to remove the limit in future.
I personally think it should be enough for common use cases, though, and
therefore not giving high priority at the moment.
Future Works: Long Term
=======================
There are user requests for extending DAMON with detailed access
information, for example, per-CPUs/threads/read/writes monitoring. For
that, I was working [2] on extending DAMON to use page fault events as
another access check primitives, and making the infrastructure flexible
for future use of yet another access check primitive. Actually there is
another ongoing work [3] for extending DAMON with PMU events. The
motivation of the work is reducing the overhead, though.
In my work [2], I was introducing a new interface for access sampling
primitives control. Now I think this data probe interface can be used
for that, too. That is, data access becomes just one type of data
attribute. Also, pg_idle-confirmed access, page fault-confirmed access,
and PMU event-confirmed access will be different types of data
attributes.
The regions adjustment mechanism is currently working based on the
access information. That's because DAMON is designed for data access
monitoring. That is, data access information is the primary interest,
and therefore DAMON adjusts regions in a way that can best-present the
information.
Once data access becomes just one of data attributes, there is no reason
to think data access that special. There might be some users not
interested in access at all but want to know the location of memory of
specific type. Data probes interface will allow doing that. Further,
we could extend the interface to let users set any data attribute as the
'primary' attribute. Then, DAMON will split and merge regions in a way
that can best-present the 'primary' attributes.
DAMOS will also be extended, to specify targets based on not only the
data access pattern, but all user-registered data attributes. From this
stage, we may be able to call DAMON as a "Data Attributes Monitoring and
Operations eNgine".
[1] https://lore.kernel.org/20250106193401.109161-1-sj@kernel.org
[2] https://lore.kernel.org/20251208062943.68824-1-sj@kernel.org/
[3] https://lore.kernel.org/20260423004211.7037-1-akinobu.mita@gmail.com
Changes from RFC v2.1
- rfc v2.1: https://lore.kernel.org/20260514140904.119781-1-sj@kernel.org
- Rebase to mm-stable (7.1-rc3) to avoid Sashiko patch apply failure.
Changes from RFC v2
- rfc v2: https://lore.kernel.org/20260512143645.113201-1-sj@kernel.org
- Optimize nr_probes calculation for probe_hits tracepoint.
- Use TRACE_EVENT_CONDITION() for probe_hits tracepoint.
- Rebase to latest mm-new.
Changes from RFC
- rfc: https://lore.kernel.org/all/20260426205222.93895-1-sj@kernel.org/
- Support memcg DAMON filter.
- Use per-probe probe_hits sysfs file.
- Use dynamic_array for probe_hits tracing.
- Fix filter matching field.
- Fix folio leaking in damon_pa_filter_pass().
- Move nr_regions of damon_aggregated_v2 tracepoint after end.
- Rename DAMON_TEST_TYPE_ANON to DAMON_FILTER_TYPE_ANON.
SeongJae Park (28):
mm/damon/core: introduce struct damon_probe
mm/damon/core: embed damon_probe objects in damon_ctx
mm/damon/core: introduce damon_filter
mm/damon/core: commit probes
mm/damon/core: introduce damon_region->probe_hits
mm/damon/core: introduce damon_ops->apply_probes
mm/damon/core: do data attributes monitoring
mm/damon/paddr: support data attributes monitoring
mm/damon/sysfs: implement probes dir
mm/damon/sysfs: implement probe dir
mm/damon/sysfs: implement filters directory
mm/damon/sysfs: implement filter dir
mm/damon/sysfs: implement filter dir files
mm/damon/sysfs: setup probes on DAMON core API parameters
mm/damon/sysfs-schemes: implement tried_regions/<r>/probes/
mm/damon/sysfs-schemes: implement probe dir
mm/damon/sysfs-schemes: implement probe/hits file
mm/damon: trace probe_hits
selftests/damon/sysfs.sh: test probes dir
Docs/mm/damon/design: document data attributes monitoring
Docs/admin-guide/mm/damon/usage: document data attributes monitoring
mm/damon/core: introduce DAMON_FILTER_TYPE_MEMCG
mm/damon/paddr: support DAMON_FILTER_TYPE_MEMCG
mm/damon/sysfs: add filters/<F>/path file
mm/damon/sysfs-schemes: move memcg_path_to_id() to sysfs-common
mm/damon/sysfs: setup damon_filter->memcg_id from path
Docs/mm/damon/design: update for memcg damon filter
Docs/admin-guide/mm/damon/usage: update for memcg damon filter
Documentation/admin-guide/mm/damon/usage.rst | 48 +-
Documentation/mm/damon/design.rst | 39 ++
include/linux/damon.h | 67 +++
include/trace/events/damon.h | 38 ++
mm/damon/core.c | 197 +++++++
mm/damon/paddr.c | 76 +++
mm/damon/sysfs-common.c | 41 ++
mm/damon/sysfs-common.h | 2 +
mm/damon/sysfs-schemes.c | 221 ++++++--
mm/damon/sysfs.c | 557 +++++++++++++++++++
tools/testing/selftests/damon/sysfs.sh | 48 ++
11 files changed, 1284 insertions(+), 50 deletions(-)
base-commit: 5d6919055dec134de3c40167a490f33c74c12581
--
2.47.3
^ permalink raw reply
* Re: [RFC PATCH v2.1 00/28] mm/damon: introduce data attributes monitoring
From: SeongJae Park @ 2026-05-15 0:41 UTC (permalink / raw)
To: SeongJae Park
Cc: Liam R. Howlett, Andrew Morton, David Hildenbrand,
Jonathan Corbet, Lorenzo Stoakes, Masami Hiramatsu,
Mathieu Desnoyers, Michal Hocko, Mike Rapoport, Shuah Khan,
Shuah Khan, Steven Rostedt, Suren Baghdasaryan, Vlastimil Babka,
damon, linux-doc, linux-kernel, linux-kselftest, linux-mm,
linux-trace-kernel
In-Reply-To: <20260514140904.119781-1-sj@kernel.org>
On Thu, 14 May 2026 07:08:33 -0700 SeongJae Park <sj@kernel.org> wrote:
> TL; DR
> ======
>
> Extend DAMON for monitoring general data attributes other than accesses.
> The short term motivation is lightweight page type (e.g., belonging
> cgroup) aware monitoring. In long term, this will help extending DAMON
> for multiple access events capture primitives (e.g., page faults and
> PMU) and eventually pivotting DAMON to a "Data Attributes Monitoring and
> Operations eNgine" in long term.
Sashiko failed [1] reviewing this due to a problem at finding a fresh baseline
commit. I will shortly post the next version (RFC v2.2) after rebasing to
mm-stable (7.1-rc3) for avoiding the issue.
[1] https://lore.kernel.org/20260514205555.51653-1-sj@kernel.org
Thanks,
SJ
[...]
^ permalink raw reply
* Re: [PATCH v2 04/14] mm: add VM_UFFD_RWP VMA flag
From: SeongJae Park @ 2026-05-15 0:29 UTC (permalink / raw)
To: Kiryl Shutsemau (Meta)
Cc: SeongJae Park, akpm, rppt, peterx, david, ljs, surenb, vbabka,
Liam.Howlett, ziy, corbet, skhan, seanjc, pbonzini, jthoughton,
aarcange, usama.arif, linux-mm, linux-kernel, linux-doc,
linux-kselftest, kvm, kernel-team
In-Reply-To: <b47d8d5681fd8730f6090ebfdb72f219a8edef1e.1778254670.git.kas@kernel.org>
On Fri, 8 May 2026 16:55:16 +0100 "Kiryl Shutsemau (Meta)" <kas@kernel.org> wrote:
> Preparatory patch for userfaultfd read-write protection (RWP). RWP
> extends userfaultfd protection from plain write-protection (WP) to
> full read-write protection: accesses to an RWP-protected range --
> reads as well as writes -- trap through userfaultfd.
>
> RWP marks ranges by combining PAGE_NONE with the uffd PTE bit, so
> the flag is only meaningful when both primitives exist. A new
> CONFIG_USERFAULTFD_RWP Kconfig symbol auto-selects when CONFIG_64BIT,
> CONFIG_ARCH_HAS_PTE_PROTNONE, and CONFIG_HAVE_ARCH_USERFAULTFD_WP
> are all set; call sites that gate on the flag depend on the symbol.
> Elsewhere VM_UFFD_RWP aliases VM_NONE and every downstream check
> folds to dead code.
>
> Nothing sets the flag yet.
>
> Signed-off-by: Kiryl Shutsemau <kas@kernel.org>
> Assisted-by: Claude:claude-opus-4-6
A silly but loud thought. Would it make more sense to put Signed-off-by: after
Assisted-by: ?
> ---
> Documentation/filesystems/proc.rst | 1 +
> fs/proc/task_mmu.c | 3 +++
> include/linux/mm.h | 28 +++++++++++++++++----------
> include/linux/userfaultfd_k.h | 31 +++++++++++++++++++++++++-----
> include/trace/events/mmflags.h | 7 +++++++
> mm/Kconfig | 9 +++++++++
> 6 files changed, 64 insertions(+), 15 deletions(-)
>
> diff --git a/Documentation/filesystems/proc.rst b/Documentation/filesystems/proc.rst
> index db6167befb7b..db28207c5290 100644
> --- a/Documentation/filesystems/proc.rst
> +++ b/Documentation/filesystems/proc.rst
> @@ -607,6 +607,7 @@ encoded manner. The codes are the following:
> um userfaultfd missing tracking
> uw userfaultfd wr-protect tracking
> ui userfaultfd minor fault
> + ur userfaultfd read-write-protect tracking
Yet another silly but loud thought. My first feeling on this was that this
reads like 'u'serfaultfd 'r'ead-protect. And was further thinking 'uf' for
just 'u'seffaultfd 'f'ault or 'up' for 'u'serfault-'p'rotect might make sense.
But ended up thinking this is too trivial and ain't really matter.
[...]
> diff --git a/mm/Kconfig b/mm/Kconfig
> index e8bf1e9e6ad9..ccf534a8cbc9 100644
> --- a/mm/Kconfig
> +++ b/mm/Kconfig
> @@ -1347,6 +1347,15 @@ config HAVE_ARCH_USERFAULTFD_MINOR
> help
> Arch has userfaultfd minor fault support
>
> +config USERFAULTFD_RWP
> + def_bool y
> + depends on 64BIT && ARCH_HAS_PTE_PROTNONE && HAVE_ARCH_USERFAULTFD_WP
> + help
> + Userfaultfd read-write protection (UFFDIO_RWPROTECT) delivers a
Seems UFFDIO_RWPROTECT will be introduced later. Would it make more sense to
add this config together with the patch?
> + userfaultfd notification on every access -- read or write -- to a
> + protected range, letting userspace observe the working set of a
> + process.
> +
> menuconfig USERFAULTFD
> bool "Enable userfaultfd() system call"
> depends on MMU
> --
> 2.51.2
>
>
None of my comments is a blocker.
Reviewed-by: SeongJae Park <sj@kernel.org>
Thanks,
SJ
^ permalink raw reply
* Re: [PATCH net-next] Documentation: networking: devlink: stmmac: fix typo in phc_coarse_adj
From: patchwork-bot+netdevbpf @ 2026-05-15 0:20 UTC (permalink / raw)
To: Avinash Duduskar
Cc: netdev, davem, kuba, pabeni, edumazet, horms, corbet, jiri,
mcoquelin.stm32, alexandre.torgue, linux-doc, linux-stm32,
linux-arm-kernel, linux-kernel
In-Reply-To: <20260512133214.1773502-1-avinash.duduskar@gmail.com>
Hello:
This patch was applied to netdev/net-next.git (main)
by Jakub Kicinski <kuba@kernel.org>:
On Tue, 12 May 2026 19:02:14 +0530 you wrote:
> "Functionnal" should be "Functional".
>
> Signed-off-by: Avinash Duduskar <avinash.duduskar@gmail.com>
> ---
> Documentation/networking/devlink/stmmac.rst | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
Here is the summary with links:
- [net-next] Documentation: networking: devlink: stmmac: fix typo in phc_coarse_adj
https://git.kernel.org/netdev/net-next/c/e54fe8200a1c
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH net-next] Documentation: networking: ip-sysctl: fix typo in tcp_ecn_option
From: patchwork-bot+netdevbpf @ 2026-05-15 0:20 UTC (permalink / raw)
To: Avinash Duduskar
Cc: netdev, davem, kuba, pabeni, edumazet, horms, corbet, linux-doc,
linux-kernel
In-Reply-To: <20260512133125.1772658-1-avinash.duduskar@gmail.com>
Hello:
This patch was applied to netdev/net-next.git (main)
by Jakub Kicinski <kuba@kernel.org>:
On Tue, 12 May 2026 19:01:25 +0530 you wrote:
> "regarless" should be "regardless".
>
> Signed-off-by: Avinash Duduskar <avinash.duduskar@gmail.com>
> ---
> Documentation/networking/ip-sysctl.rst | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
Here is the summary with links:
- [net-next] Documentation: networking: ip-sysctl: fix typo in tcp_ecn_option
https://git.kernel.org/netdev/net-next/c/d2dc0c5c4c42
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH] docs: hwmon: sy7636a: fix temperature sysfs attribute name
From: Guenter Roeck @ 2026-05-14 22:37 UTC (permalink / raw)
To: Chen-Shi-Hong
Cc: Jonathan Corbet, Shuah Khan, linux-hwmon, linux-doc, linux-kernel
In-Reply-To: <20260514154108.1937-1-eric039eric@gmail.com>
On Thu, May 14, 2026 at 11:39:13PM +0800, Chen-Shi-Hong wrote:
> The hwmon sysfs naming convention uses
> temp[1-*]_input for temperature channels.
>
> Documentation/hwmon/sy7636a-hwmon.rst currently documents
> temp0_input, while the driver uses the standard hwmon
> temperature channel interface.
>
> Update the documentation to use temp1_input.
>
> Signed-off-by: Chen-Shi-Hong <eric039eric@gmail.com>
Applied.
Thanks,
Guenter
> ---
> Documentation/hwmon/sy7636a-hwmon.rst | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/Documentation/hwmon/sy7636a-hwmon.rst b/Documentation/hwmon/sy7636a-hwmon.rst
> index 0143ce0e5db7..03d866aba6e8 100644
> --- a/Documentation/hwmon/sy7636a-hwmon.rst
> +++ b/Documentation/hwmon/sy7636a-hwmon.rst
> @@ -22,5 +22,5 @@ The following sensors are supported
> sysfs-Interface
> ---------------
>
> -temp0_input
> +temp1_input
> - Temperature of external NTC (milli-degree C)
^ permalink raw reply
* [PATCH v2 10/10] selftests/liveupdate: Add stress-files kexec test
From: Pasha Tatashin @ 2026-05-14 22:26 UTC (permalink / raw)
To: linux-kselftest, rppt, shuah, akpm, linux-mm, skhan, linux-doc,
linux-kernel, corbet, pasha.tatashin, dmatlack, kexec, pratyush,
skhawaja, graf
In-Reply-To: <20260514222628.931312-1-pasha.tatashin@soleen.com>
Add a new luo_stress_files kexec test that verifies preserving and
retrieving 500 files across a kexec reboot.
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
Reviewed-by: Pratyush Yadav (Google) <pratyush@kernel.org>
---
tools/testing/selftests/liveupdate/Makefile | 1 +
.../selftests/liveupdate/luo_stress_files.c | 101 ++++++++++++++++++
2 files changed, 102 insertions(+)
create mode 100644 tools/testing/selftests/liveupdate/luo_stress_files.c
diff --git a/tools/testing/selftests/liveupdate/Makefile b/tools/testing/selftests/liveupdate/Makefile
index ed7534468386..30689d22cb02 100644
--- a/tools/testing/selftests/liveupdate/Makefile
+++ b/tools/testing/selftests/liveupdate/Makefile
@@ -7,6 +7,7 @@ TEST_GEN_PROGS += liveupdate
TEST_GEN_PROGS_EXTENDED += luo_kexec_simple
TEST_GEN_PROGS_EXTENDED += luo_multi_session
TEST_GEN_PROGS_EXTENDED += luo_stress_sessions
+TEST_GEN_PROGS_EXTENDED += luo_stress_files
TEST_FILES += do_kexec.sh
diff --git a/tools/testing/selftests/liveupdate/luo_stress_files.c b/tools/testing/selftests/liveupdate/luo_stress_files.c
new file mode 100644
index 000000000000..b6a0c3899890
--- /dev/null
+++ b/tools/testing/selftests/liveupdate/luo_stress_files.c
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ * Copyright (c) 2026, Google LLC.
+ * Pasha Tatashin <pasha.tatashin@soleen.com>
+ *
+ * Validate that LUO can handle a large number of files per session across
+ * a kexec reboot.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include "luo_test_utils.h"
+
+#define NUM_FILES 500
+#define STATE_SESSION_NAME "kexec_many_files_state"
+#define STATE_MEMFD_TOKEN 9999
+#define TEST_SESSION_NAME "many_files_session"
+
+/* Stage 1: Executed before the kexec reboot. */
+static void run_stage_1(int luo_fd)
+{
+ int session_fd, ret, i;
+
+ ksft_print_msg("[STAGE 1] Increasing ulimit for open files...\n");
+ ret = luo_ensure_nofile_limit(NUM_FILES);
+ if (ret == -EPERM)
+ ksft_exit_skip("Insufficient privileges to set RLIMIT_NOFILE\n");
+ if (ret < 0)
+ ksft_exit_fail_msg("luo_ensure_nofile_limit failed: %s\n", strerror(-ret));
+
+ ksft_print_msg("[STAGE 1] Creating state file for next stage (2)...\n");
+ create_state_file(luo_fd, STATE_SESSION_NAME, STATE_MEMFD_TOKEN, 2);
+
+ ksft_print_msg("[STAGE 1] Creating test session '%s'...\n", TEST_SESSION_NAME);
+ session_fd = luo_create_session(luo_fd, TEST_SESSION_NAME);
+ if (session_fd < 0)
+ fail_exit("luo_create_session");
+
+ ksft_print_msg("[STAGE 1] Preserving %d files...\n", NUM_FILES);
+ for (i = 0; i < NUM_FILES; i++) {
+ char data[64];
+
+ snprintf(data, sizeof(data), "file-data-%d", i);
+ if (create_and_preserve_memfd(session_fd, i, data) < 0)
+ fail_exit("create_and_preserve_memfd for index %d", i);
+ }
+
+ ksft_print_msg("[STAGE 1] Successfully preserved %d files.\n", NUM_FILES);
+
+ close(luo_fd);
+ daemonize_and_wait();
+}
+
+/* Stage 2: Executed after the kexec reboot. */
+static void run_stage_2(int luo_fd, int state_session_fd)
+{
+ int session_fd;
+ int i, stage;
+
+ ksft_print_msg("[STAGE 2] Starting post-kexec verification...\n");
+
+ restore_and_read_stage(state_session_fd, STATE_MEMFD_TOKEN, &stage);
+ if (stage != 2) {
+ fail_exit("Expected stage 2, but state file contains %d",
+ stage);
+ }
+
+ ksft_print_msg("[STAGE 2] Retrieving test session '%s'...\n", TEST_SESSION_NAME);
+ session_fd = luo_retrieve_session(luo_fd, TEST_SESSION_NAME);
+ if (session_fd < 0)
+ fail_exit("luo_retrieve_session");
+
+ ksft_print_msg("[STAGE 2] Verifying %d files...\n", NUM_FILES);
+ for (i = 0; i < NUM_FILES; i++) {
+ char data[64];
+
+ snprintf(data, sizeof(data), "file-data-%d", i);
+ if (restore_and_verify_memfd(session_fd, i, data) < 0)
+ fail_exit("restore_and_verify_memfd for index %d", i);
+ }
+
+ ksft_print_msg("[STAGE 2] Finishing test session...\n");
+ if (luo_session_finish(session_fd) < 0)
+ fail_exit("luo_session_finish for test session");
+ close(session_fd);
+
+ ksft_print_msg("[STAGE 2] Finalizing state session...\n");
+ if (luo_session_finish(state_session_fd) < 0)
+ fail_exit("luo_session_finish for state session");
+ close(state_session_fd);
+
+ ksft_print_msg("\n--- MANY-FILES KEXEC TEST PASSED (%d files) ---\n",
+ NUM_FILES);
+}
+
+int main(int argc, char *argv[])
+{
+ return luo_test(argc, argv, STATE_SESSION_NAME,
+ run_stage_1, run_stage_2);
+}
--
2.53.0
^ permalink raw reply related
* [PATCH v2 09/10] selftests/liveupdate: Add stress-sessions kexec test
From: Pasha Tatashin @ 2026-05-14 22:26 UTC (permalink / raw)
To: linux-kselftest, rppt, shuah, akpm, linux-mm, skhan, linux-doc,
linux-kernel, corbet, pasha.tatashin, dmatlack, kexec, pratyush,
skhawaja, graf
In-Reply-To: <20260514222628.931312-1-pasha.tatashin@soleen.com>
Add a new test that creates 2000 LUO sessions before a kexec
reboot and verifies their presence after the reboot. This ensures
that the linked-block serialization mechanism works correctly for
a large number of sessions.
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
tools/testing/selftests/liveupdate/Makefile | 1 +
.../liveupdate/luo_stress_sessions.c | 102 ++++++++++++++++++
2 files changed, 103 insertions(+)
create mode 100644 tools/testing/selftests/liveupdate/luo_stress_sessions.c
diff --git a/tools/testing/selftests/liveupdate/Makefile b/tools/testing/selftests/liveupdate/Makefile
index 080754787ede..ed7534468386 100644
--- a/tools/testing/selftests/liveupdate/Makefile
+++ b/tools/testing/selftests/liveupdate/Makefile
@@ -6,6 +6,7 @@ TEST_GEN_PROGS += liveupdate
TEST_GEN_PROGS_EXTENDED += luo_kexec_simple
TEST_GEN_PROGS_EXTENDED += luo_multi_session
+TEST_GEN_PROGS_EXTENDED += luo_stress_sessions
TEST_FILES += do_kexec.sh
diff --git a/tools/testing/selftests/liveupdate/luo_stress_sessions.c b/tools/testing/selftests/liveupdate/luo_stress_sessions.c
new file mode 100644
index 000000000000..f201b1839d1d
--- /dev/null
+++ b/tools/testing/selftests/liveupdate/luo_stress_sessions.c
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ * Copyright (c) 2026, Google LLC.
+ * Pasha Tatashin <pasha.tatashin@soleen.com>
+ *
+ * Validate that LUO can handle a large number of sessions across a kexec
+ * reboot.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include "luo_test_utils.h"
+
+#define NUM_SESSIONS 2000
+#define STATE_SESSION_NAME "kexec_many_state"
+#define STATE_MEMFD_TOKEN 999
+
+/* Stage 1: Executed before the kexec reboot. */
+static void run_stage_1(int luo_fd)
+{
+ int ret, i;
+
+ ksft_print_msg("[STAGE 1] Increasing ulimit for open files...\n");
+ ret = luo_ensure_nofile_limit(NUM_SESSIONS);
+ if (ret == -EPERM)
+ ksft_exit_skip("Insufficient privileges to set RLIMIT_NOFILE\n");
+ if (ret < 0)
+ ksft_exit_fail_msg("luo_ensure_nofile_limit failed: %s\n", strerror(-ret));
+
+ ksft_print_msg("[STAGE 1] Creating state file for next stage (2)...\n");
+ create_state_file(luo_fd, STATE_SESSION_NAME, STATE_MEMFD_TOKEN, 2);
+
+ ksft_print_msg("[STAGE 1] Creating %d sessions...\n", NUM_SESSIONS);
+
+ for (i = 0; i < NUM_SESSIONS; i++) {
+ char name[LIVEUPDATE_SESSION_NAME_LENGTH];
+ int s_fd;
+
+ snprintf(name, sizeof(name), "many-test-%d", i);
+ s_fd = luo_create_session(luo_fd, name);
+ if (s_fd < 0) {
+ fail_exit("luo_create_session for '%s' at index %d",
+ name, i);
+ }
+ }
+
+ ksft_print_msg("[STAGE 1] Successfully created %d sessions.\n",
+ NUM_SESSIONS);
+
+ close(luo_fd);
+ daemonize_and_wait();
+}
+
+/* Stage 2: Executed after the kexec reboot. */
+static void run_stage_2(int luo_fd, int state_session_fd)
+{
+ int i, stage;
+
+ ksft_print_msg("[STAGE 2] Starting post-kexec verification...\n");
+
+ restore_and_read_stage(state_session_fd, STATE_MEMFD_TOKEN, &stage);
+ if (stage != 2) {
+ fail_exit("Expected stage 2, but state file contains %d",
+ stage);
+ }
+
+ ksft_print_msg("[STAGE 2] Retrieving and finishing %d sessions...\n",
+ NUM_SESSIONS);
+
+ for (i = 0; i < NUM_SESSIONS; i++) {
+ char name[LIVEUPDATE_SESSION_NAME_LENGTH];
+ int s_fd;
+
+ snprintf(name, sizeof(name), "many-test-%d", i);
+ s_fd = luo_retrieve_session(luo_fd, name);
+ if (s_fd < 0) {
+ fail_exit("luo_retrieve_session for '%s' at index %d",
+ name, i);
+ }
+
+ if (luo_session_finish(s_fd) < 0) {
+ fail_exit("luo_session_finish for '%s' at index %d",
+ name, i);
+ }
+ close(s_fd);
+ }
+
+ ksft_print_msg("[STAGE 2] Finalizing state session...\n");
+ if (luo_session_finish(state_session_fd) < 0)
+ fail_exit("luo_session_finish for state session");
+ close(state_session_fd);
+
+ ksft_print_msg("\n--- MANY-SESSIONS KEXEC TEST PASSED (%d sessions) ---\n",
+ NUM_SESSIONS);
+}
+
+int main(int argc, char *argv[])
+{
+ return luo_test(argc, argv, STATE_SESSION_NAME,
+ run_stage_1, run_stage_2);
+}
--
2.53.0
^ permalink raw reply related
* [PATCH v2 08/10] selftests/liveupdate: Test session and file limit removal
From: Pasha Tatashin @ 2026-05-14 22:26 UTC (permalink / raw)
To: linux-kselftest, rppt, shuah, akpm, linux-mm, skhan, linux-doc,
linux-kernel, corbet, pasha.tatashin, dmatlack, kexec, pratyush,
skhawaja, graf
In-Reply-To: <20260514222628.931312-1-pasha.tatashin@soleen.com>
With the removal of static limits on the number of sessions and files per
session, the orchestrator now uses dynamic allocation.
Add new test cases to verify that the system can handle a large number of
sessions and files. These tests ensure that the dynamic block allocation
and reuse logic for session metadata and outgoing files work correctly
beyond the previous static limits.
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
.../testing/selftests/liveupdate/liveupdate.c | 75 +++++++++++++++++++
.../selftests/liveupdate/luo_test_utils.c | 24 ++++++
.../selftests/liveupdate/luo_test_utils.h | 2 +
3 files changed, 101 insertions(+)
diff --git a/tools/testing/selftests/liveupdate/liveupdate.c b/tools/testing/selftests/liveupdate/liveupdate.c
index c7d94b9181e1..502fb3567e38 100644
--- a/tools/testing/selftests/liveupdate/liveupdate.c
+++ b/tools/testing/selftests/liveupdate/liveupdate.c
@@ -26,6 +26,7 @@
#include <linux/liveupdate.h>
+#include "luo_test_utils.h"
#include "../kselftest.h"
#include "../kselftest_harness.h"
@@ -499,4 +500,78 @@ TEST_F(liveupdate_device, get_session_name_max_length)
ASSERT_EQ(close(session_fd), 0);
}
+/*
+ * Test Case: Manage Many Sessions
+ *
+ * Verifies that a large number of sessions can be created and then
+ * destroyed during normal system operation. This specifically tests the
+ * dynamic block allocation and reuse logic for session metadata management
+ * without preserving any files.
+ */
+TEST_F(liveupdate_device, preserve_many_sessions)
+{
+#define MANY_SESSIONS 2000
+ int session_fds[MANY_SESSIONS];
+ int ret, i;
+
+ self->fd1 = open(LIVEUPDATE_DEV, O_RDWR);
+ if (self->fd1 < 0 && errno == ENOENT)
+ SKIP(return, "%s does not exist", LIVEUPDATE_DEV);
+ ASSERT_GE(self->fd1, 0);
+
+ ret = luo_ensure_nofile_limit(MANY_SESSIONS);
+ if (ret == -EPERM)
+ SKIP(return, "Insufficient privileges to set RLIMIT_NOFILE");
+ ASSERT_EQ(ret, 0);
+
+ for (i = 0; i < MANY_SESSIONS; i++) {
+ char name[64];
+
+ snprintf(name, sizeof(name), "many-session-%d", i);
+ session_fds[i] = create_session(self->fd1, name);
+ ASSERT_GE(session_fds[i], 0);
+ }
+
+ for (i = 0; i < MANY_SESSIONS; i++)
+ ASSERT_EQ(close(session_fds[i]), 0);
+}
+
+/*
+ * Test Case: Preserve Many Files
+ *
+ * Verifies that a large number of files can be preserved in a single session
+ * and then destroyed during normal system operation. This tests the dynamic
+ * block allocation and management for outgoing files.
+ */
+TEST_F(liveupdate_device, preserve_many_files)
+{
+#define MANY_FILES 500
+ int mem_fds[MANY_FILES];
+ int session_fd, ret, i;
+
+ self->fd1 = open(LIVEUPDATE_DEV, O_RDWR);
+ if (self->fd1 < 0 && errno == ENOENT)
+ SKIP(return, "%s does not exist", LIVEUPDATE_DEV);
+ ASSERT_GE(self->fd1, 0);
+
+ session_fd = create_session(self->fd1, "many-files-test");
+ ASSERT_GE(session_fd, 0);
+
+ ret = luo_ensure_nofile_limit(MANY_FILES + 10);
+ if (ret == -EPERM)
+ SKIP(return, "Insufficient privileges to set RLIMIT_NOFILE");
+ ASSERT_EQ(ret, 0);
+
+ for (i = 0; i < MANY_FILES; i++) {
+ mem_fds[i] = memfd_create("test-memfd", 0);
+ ASSERT_GE(mem_fds[i], 0);
+ ASSERT_EQ(preserve_fd(session_fd, mem_fds[i], i), 0);
+ }
+
+ for (i = 0; i < MANY_FILES; i++)
+ ASSERT_EQ(close(mem_fds[i]), 0);
+
+ ASSERT_EQ(close(session_fd), 0);
+}
+
TEST_HARNESS_MAIN
diff --git a/tools/testing/selftests/liveupdate/luo_test_utils.c b/tools/testing/selftests/liveupdate/luo_test_utils.c
index 3c8721c505df..333a3530051b 100644
--- a/tools/testing/selftests/liveupdate/luo_test_utils.c
+++ b/tools/testing/selftests/liveupdate/luo_test_utils.c
@@ -17,6 +17,7 @@
#include <sys/syscall.h>
#include <sys/mman.h>
#include <sys/types.h>
+#include <sys/resource.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdarg.h>
@@ -28,6 +29,29 @@ int luo_open_device(void)
return open(LUO_DEVICE, O_RDWR);
}
+int luo_ensure_nofile_limit(long min_limit)
+{
+ struct rlimit hl;
+
+ /* Allow to extra files to be used by test itself */
+ min_limit += 32;
+
+ if (getrlimit(RLIMIT_NOFILE, &hl) < 0)
+ return -errno;
+
+ if (hl.rlim_cur >= min_limit)
+ return 0;
+
+ hl.rlim_cur = min_limit;
+ if (hl.rlim_cur > hl.rlim_max)
+ hl.rlim_max = hl.rlim_cur;
+
+ if (setrlimit(RLIMIT_NOFILE, &hl) < 0)
+ return -errno;
+
+ return 0;
+}
+
int luo_create_session(int luo_fd, const char *name)
{
struct liveupdate_ioctl_create_session arg = { .size = sizeof(arg) };
diff --git a/tools/testing/selftests/liveupdate/luo_test_utils.h b/tools/testing/selftests/liveupdate/luo_test_utils.h
index 90099bf49577..6a0d85386613 100644
--- a/tools/testing/selftests/liveupdate/luo_test_utils.h
+++ b/tools/testing/selftests/liveupdate/luo_test_utils.h
@@ -26,6 +26,8 @@ int luo_create_session(int luo_fd, const char *name);
int luo_retrieve_session(int luo_fd, const char *name);
int luo_session_finish(int session_fd);
+int luo_ensure_nofile_limit(long min_limit);
+
int create_and_preserve_memfd(int session_fd, int token, const char *data);
int restore_and_verify_memfd(int session_fd, int token, const char *expected_data);
--
2.53.0
^ permalink raw reply related
* [PATCH v2 07/10] liveupdate: Remove limit on the number of files per session
From: Pasha Tatashin @ 2026-05-14 22:26 UTC (permalink / raw)
To: linux-kselftest, rppt, shuah, akpm, linux-mm, skhan, linux-doc,
linux-kernel, corbet, pasha.tatashin, dmatlack, kexec, pratyush,
skhawaja, graf
In-Reply-To: <20260514222628.931312-1-pasha.tatashin@soleen.com>
To remove the fixed limit on the number of preserved files per session,
transition the file metadata serialization from a single contiguous
memory block to a chain of linked blocks.
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
include/linux/kho/abi/luo.h | 13 +--
kernel/liveupdate/luo_file.c | 142 +++++++++++++++----------------
kernel/liveupdate/luo_internal.h | 5 +-
3 files changed, 77 insertions(+), 83 deletions(-)
diff --git a/include/linux/kho/abi/luo.h b/include/linux/kho/abi/luo.h
index a643193a379c..59677dc9cdd1 100644
--- a/include/linux/kho/abi/luo.h
+++ b/include/linux/kho/abi/luo.h
@@ -56,8 +56,8 @@
*
* - struct luo_session_ser:
* Metadata for a single session, including its name and a physical pointer
- * to another preserved memory block containing an array of
- * `struct luo_file_ser` for all files in that session.
+ * to the first `struct luo_block_header_ser` for all files in that session.
+ * Multiple blocks are linked via the `next` field in the header.
*
* - struct luo_file_ser:
* Metadata for a single preserved file. Contains the `compatible` string to
@@ -85,7 +85,7 @@
*/
#define LUO_FDT_SIZE PAGE_SIZE
#define LUO_FDT_KHO_ENTRY_NAME "LUO"
-#define LUO_FDT_COMPATIBLE "luo-v3"
+#define LUO_FDT_COMPATIBLE "luo-v4"
#define LUO_FDT_ABI_HEADER "luo-abi-header"
/**
@@ -138,9 +138,10 @@ struct luo_file_ser {
/**
* struct luo_file_set_ser - Represents the serialized metadata for file set
- * @files: The physical address of a contiguous memory block that holds
- * the serialized state of files (array of luo_file_ser) in this file
- * set.
+ * @files: The physical address of the first `struct luo_block_header_ser`.
+ * This structure is the header for a block of memory containing
+ * an array of `struct luo_file_ser` entries. Multiple blocks are
+ * linked via the `next` field in the header.
* @count: The total number of files that were part of this session during
* serialization. Used for iteration and validation during
* restoration.
diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c
index a2510563469a..e2ab7834963d 100644
--- a/kernel/liveupdate/luo_file.c
+++ b/kernel/liveupdate/luo_file.c
@@ -118,11 +118,6 @@ static LIST_HEAD(luo_file_handler_list);
/* Keep track of files being preserved by LUO */
static DEFINE_XARRAY(luo_preserved_files);
-/* 2 4K pages, give space for 128 files per file_set */
-#define LUO_FILE_PGCNT 2ul
-#define LUO_FILE_MAX \
- ((LUO_FILE_PGCNT << PAGE_SHIFT) / sizeof(struct luo_file_ser))
-
/**
* struct luo_file - Represents a single preserved file instance.
* @fh: Pointer to the &struct liveupdate_file_handler that manages
@@ -174,39 +169,6 @@ struct luo_file {
u64 token;
};
-static int luo_alloc_files_mem(struct luo_file_set *file_set)
-{
- size_t size;
- void *mem;
-
- if (file_set->files)
- return 0;
-
- WARN_ON_ONCE(file_set->count);
-
- size = LUO_FILE_PGCNT << PAGE_SHIFT;
- mem = kho_alloc_preserve(size);
- if (IS_ERR(mem))
- return PTR_ERR(mem);
-
- file_set->files = mem;
-
- return 0;
-}
-
-static void luo_free_files_mem(struct luo_file_set *file_set)
-{
- /* If file_set has files, no need to free preservation memory */
- if (file_set->count)
- return;
-
- if (!file_set->files)
- return;
-
- kho_unpreserve_free(file_set->files);
- file_set->files = NULL;
-}
-
static unsigned long luo_get_id(struct liveupdate_file_handler *fh,
struct file *file)
{
@@ -276,16 +238,15 @@ int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd)
if (luo_token_is_used(file_set, token))
return -EEXIST;
- if (file_set->count == LUO_FILE_MAX)
- return -ENOSPC;
+ err = luo_block_grow(&file_set->block_set, file_set->count);
+ if (err)
+ return err;
file = fget(fd);
- if (!file)
- return -EBADF;
-
- err = luo_alloc_files_mem(file_set);
- if (err)
- goto err_fput;
+ if (!file) {
+ err = -EBADF;
+ goto err_shrink;
+ }
err = -ENOENT;
down_read(&luo_register_rwlock);
@@ -300,7 +261,7 @@ int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd)
/* err is still -ENOENT if no handler was found */
if (err)
- goto err_free_files_mem;
+ goto err_fput;
err = xa_insert(&luo_preserved_files, luo_get_id(fh, file),
file, GFP_KERNEL);
@@ -343,10 +304,10 @@ int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd)
xa_erase(&luo_preserved_files, luo_get_id(fh, file));
err_module_put:
module_put(fh->ops->owner);
-err_free_files_mem:
- luo_free_files_mem(file_set);
err_fput:
fput(file);
+err_shrink:
+ luo_block_shrink(&file_set->block_set, file_set->count);
return err;
}
@@ -391,13 +352,14 @@ void luo_file_unpreserve_files(struct luo_file_set *file_set)
luo_get_id(luo_file->fh, luo_file->file));
list_del(&luo_file->list);
file_set->count--;
+ luo_block_shrink(&file_set->block_set, file_set->count);
fput(luo_file->file);
mutex_destroy(&luo_file->mutex);
kfree(luo_file);
}
- luo_free_files_mem(file_set);
+ luo_block_destroy(&file_set->block_set);
}
static int luo_file_freeze_one(struct luo_file_set *file_set,
@@ -453,7 +415,7 @@ static void __luo_file_unfreeze(struct luo_file_set *file_set,
luo_file_unfreeze_one(file_set, luo_file);
}
- memset(file_set->files, 0, LUO_FILE_PGCNT << PAGE_SHIFT);
+ luo_block_set_clear(&file_set->block_set);
}
/**
@@ -492,19 +454,23 @@ static void __luo_file_unfreeze(struct luo_file_set *file_set,
int luo_file_freeze(struct luo_file_set *file_set,
struct luo_file_set_ser *file_set_ser)
{
- struct luo_file_ser *file_ser = file_set->files;
struct luo_file *luo_file;
+ struct luo_block_it it;
int err;
- int i;
if (!file_set->count)
return 0;
- if (WARN_ON(!file_ser))
- return -EINVAL;
+ luo_block_it_init(&it, &file_set->block_set);
- i = 0;
list_for_each_entry(luo_file, &file_set->files_list, list) {
+ struct luo_file_ser *file_ser = luo_block_it_next(&it);
+
+ if (!file_ser) {
+ err = -ENOSPC;
+ goto err_unfreeze;
+ }
+
err = luo_file_freeze_one(file_set, luo_file);
if (err < 0) {
pr_warn("Freeze failed for token[%#0llx] handler[%s] err[%pe]\n",
@@ -513,16 +479,21 @@ int luo_file_freeze(struct luo_file_set *file_set,
goto err_unfreeze;
}
- strscpy(file_ser[i].compatible, luo_file->fh->compatible,
- sizeof(file_ser[i].compatible));
- file_ser[i].data = luo_file->serialized_data;
- file_ser[i].token = luo_file->token;
- i++;
+ strscpy(file_ser->compatible, luo_file->fh->compatible,
+ sizeof(file_ser->compatible));
+ file_ser->data = luo_file->serialized_data;
+ file_ser->token = luo_file->token;
}
+ luo_block_it_finalize(&it);
file_set_ser->count = file_set->count;
- if (file_set->files)
- file_set_ser->files = virt_to_phys(file_set->files);
+ if (!list_empty(&file_set->block_set.blocks)) {
+ struct luo_block *block;
+
+ block = list_first_entry(&file_set->block_set.blocks,
+ struct luo_block, list);
+ file_set_ser->files = virt_to_phys(block->ser);
+ }
return 0;
@@ -740,14 +711,12 @@ int luo_file_finish(struct luo_file_set *file_set)
}
list_del(&luo_file->list);
file_set->count--;
+ luo_block_shrink(&file_set->block_set, file_set->count);
mutex_destroy(&luo_file->mutex);
kfree(luo_file);
}
- if (file_set->files) {
- kho_restore_free(file_set->files);
- file_set->files = NULL;
- }
+ luo_block_destroy(&file_set->block_set);
return 0;
}
@@ -821,16 +790,18 @@ int luo_file_deserialize(struct luo_file_set *file_set,
struct luo_file_set_ser *file_set_ser)
{
struct luo_file_ser *file_ser;
+ struct luo_block_it it;
int err;
- u64 i;
if (!file_set_ser->files) {
WARN_ON(file_set_ser->count);
return 0;
}
- file_set->count = file_set_ser->count;
- file_set->files = phys_to_virt(file_set_ser->files);
+ file_set->count = 0;
+ err = luo_block_restore(&file_set->block_set, file_set_ser->files);
+ if (err)
+ return err;
/*
* Note on error handling:
@@ -847,25 +818,48 @@ int luo_file_deserialize(struct luo_file_set *file_set,
* userspace to detect the failure and trigger a reboot, which will
* reliably reset devices and reclaim memory.
*/
- file_ser = file_set->files;
- for (i = 0; i < file_set->count; i++) {
- err = luo_file_deserialize_one(file_set, &file_ser[i]);
+ luo_block_it_init(&it, &file_set->block_set);
+ while ((file_ser = luo_block_it_read(&it))) {
+ err = luo_file_deserialize_one(file_set, file_ser);
if (err)
- return err;
+ goto err_destroy_blocks;
+ file_set->count++;
+ }
+
+ if (file_set->count != file_set_ser->count) {
+ pr_warn("File count mismatch: expected %llu, found %ld\n",
+ file_set_ser->count, file_set->count);
}
return 0;
+
+err_destroy_blocks:
+ while (!list_empty(&file_set->files_list)) {
+ struct luo_file *luo_file;
+
+ luo_file = list_first_entry(&file_set->files_list,
+ struct luo_file, list);
+ list_del(&luo_file->list);
+ module_put(luo_file->fh->ops->owner);
+ mutex_destroy(&luo_file->mutex);
+ kfree(luo_file);
+ }
+ file_set->count = 0;
+ luo_block_destroy(&file_set->block_set);
+ return err;
}
void luo_file_set_init(struct luo_file_set *file_set)
{
INIT_LIST_HEAD(&file_set->files_list);
+ luo_block_set_init(&file_set->block_set, sizeof(struct luo_file_ser));
}
void luo_file_set_destroy(struct luo_file_set *file_set)
{
WARN_ON(file_set->count);
WARN_ON(!list_empty(&file_set->files_list));
+ WARN_ON(!list_empty(&file_set->block_set.blocks));
}
/**
diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_internal.h
index 090078ad771c..901483e6e4ad 100644
--- a/kernel/liveupdate/luo_internal.h
+++ b/kernel/liveupdate/luo_internal.h
@@ -89,14 +89,13 @@ struct luo_block_it {
* struct luo_file_set - A set of files that belong to the same sessions.
* @files_list: An ordered list of files associated with this session, it is
* ordered by preservation time.
- * @files: The physically contiguous memory block that holds the serialized
- * state of files.
+ * @block_set: The set of serialization blocks.
* @count: A counter tracking the number of files currently stored in the
* @files_list for this session.
*/
struct luo_file_set {
struct list_head files_list;
- struct luo_file_ser *files;
+ struct luo_block_set block_set;
long count;
};
--
2.53.0
^ permalink raw reply related
* [PATCH v2 06/10] liveupdate: Remove limit on the number of sessions
From: Pasha Tatashin @ 2026-05-14 22:26 UTC (permalink / raw)
To: linux-kselftest, rppt, shuah, akpm, linux-mm, skhan, linux-doc,
linux-kernel, corbet, pasha.tatashin, dmatlack, kexec, pratyush,
skhawaja, graf
In-Reply-To: <20260514222628.931312-1-pasha.tatashin@soleen.com>
Currently, the number of LUO sessions is limited by a fixed number of
pre-allocated pages for serialization (16 pages, allowing for ~819
sessions).
This limitation is problematic if LUO is used to support things such as
systemd file descriptor store, and would be used not just as VM memory
but to save other states on the machine.
Remove this limit by transitioning to a linked-block approach for
session metadata serialization. Instead of a single contiguous block,
session metadata is now stored in a chain of 16-page blocks. Each block
starts with a header containing the physical address of the next block
and the number of session entries in the current block.
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
include/linux/kho/abi/luo.h | 24 +-------
kernel/liveupdate/luo_session.c | 105 +++++++++++++++-----------------
2 files changed, 50 insertions(+), 79 deletions(-)
diff --git a/include/linux/kho/abi/luo.h b/include/linux/kho/abi/luo.h
index 2ba447c0c1ba..a643193a379c 100644
--- a/include/linux/kho/abi/luo.h
+++ b/include/linux/kho/abi/luo.h
@@ -36,7 +36,7 @@
*
* Main LUO Node (/):
*
- * - compatible: "luo-v2"
+ * - compatible: "luo-v3"
* Identifies the overall LUO ABI version.
* - luo-abi-header: u64
* The physical address of `struct luo_ser`.
@@ -54,11 +54,6 @@
* of the next data block and the number of entries that follow this
* header in the current block.
*
- * - struct luo_session_header_ser:
- * Header for the session array. Contains the total page count of the
- * preserved memory block and the number of `struct luo_session_ser`
- * entries that follow.
- *
* - struct luo_session_ser:
* Metadata for a single session, including its name and a physical pointer
* to another preserved memory block containing an array of
@@ -90,7 +85,7 @@
*/
#define LUO_FDT_SIZE PAGE_SIZE
#define LUO_FDT_KHO_ENTRY_NAME "LUO"
-#define LUO_FDT_COMPATIBLE "luo-v2"
+#define LUO_FDT_COMPATIBLE "luo-v3"
#define LUO_FDT_ABI_HEADER "luo-abi-header"
/**
@@ -155,21 +150,6 @@ struct luo_file_set_ser {
u64 count;
} __packed;
-/**
- * struct luo_session_header_ser - Header for the serialized session data block.
- * @count: The number of `struct luo_session_ser` entries that immediately
- * follow this header in the memory block.
- *
- * This structure is located at the beginning of a contiguous block of
- * physical memory preserved across the kexec. It provides the necessary
- * metadata to interpret the array of session entries that follow.
- *
- * If this structure is modified, `LUO_FDT_COMPATIBLE` must be updated.
- */
-struct luo_session_header_ser {
- u64 count;
-} __packed;
-
/**
* struct luo_session_ser - Represents the serialized metadata for a LUO session.
* @name: The unique name of the session, provided by the userspace at
diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c
index 339514e8122c..064e83fabe62 100644
--- a/kernel/liveupdate/luo_session.c
+++ b/kernel/liveupdate/luo_session.c
@@ -69,21 +69,13 @@
#include <uapi/linux/liveupdate.h>
#include "luo_internal.h"
-/* 16 4K pages, give space for 744 sessions */
-#define LUO_SESSION_PGCNT 16ul
-#define LUO_SESSION_MAX (((LUO_SESSION_PGCNT << PAGE_SHIFT) - \
- sizeof(struct luo_session_header_ser)) / \
- sizeof(struct luo_session_ser))
-
/**
* struct luo_session_header - Header struct for managing LUO sessions.
* @count: The number of sessions currently tracked in the @list.
* @list: The head of the linked list of `struct luo_session` instances.
* @rwsem: A read-write semaphore providing synchronized access to the
* session list and other fields in this structure.
- * @header_ser: The header data of serialization array.
- * @ser: The serialized session data (an array of
- * `struct luo_session_ser`).
+ * @block_set: The set of serialization blocks.
* @sessions_pa: Points to the location of sessions_pa within struct luo_ser.
* @active: Set to true when first initialized. If previous kernel did not
* send session data, active stays false for incoming.
@@ -92,8 +84,7 @@ struct luo_session_header {
long count;
struct list_head list;
struct rw_semaphore rwsem;
- struct luo_session_header_ser *header_ser;
- struct luo_session_ser *ser;
+ struct luo_block_set block_set;
u64 *sessions_pa;
bool active;
};
@@ -112,10 +103,14 @@ static struct luo_session_global luo_session_global = {
.incoming = {
.list = LIST_HEAD_INIT(luo_session_global.incoming.list),
.rwsem = __RWSEM_INITIALIZER(luo_session_global.incoming.rwsem),
+ .block_set = LUO_BLOCK_SET_INIT(luo_session_global.incoming.block_set,
+ sizeof(struct luo_session_ser)),
},
.outgoing = {
.list = LIST_HEAD_INIT(luo_session_global.outgoing.list),
.rwsem = __RWSEM_INITIALIZER(luo_session_global.outgoing.rwsem),
+ .block_set = LUO_BLOCK_SET_INIT(luo_session_global.outgoing.block_set,
+ sizeof(struct luo_session_ser)),
},
};
@@ -142,25 +137,6 @@ static void luo_session_free(struct luo_session *session)
kfree(session);
}
-static int luo_session_grow_ser(struct luo_session_header *sh)
-{
- struct luo_session_header_ser *header_ser;
-
- if (sh->count == LUO_SESSION_MAX)
- return -ENOMEM;
-
- if (sh->header_ser)
- return 0;
-
- header_ser = kho_alloc_preserve(LUO_SESSION_PGCNT << PAGE_SHIFT);
- if (IS_ERR(header_ser))
- return PTR_ERR(header_ser);
-
- sh->header_ser = header_ser;
- sh->ser = (void *)(header_ser + 1);
- return 0;
-}
-
static int luo_session_insert(struct luo_session_header *sh,
struct luo_session *session)
{
@@ -174,7 +150,7 @@ static int luo_session_insert(struct luo_session_header *sh,
* for new session.
*/
if (sh == &luo_session_global.outgoing) {
- err = luo_session_grow_ser(sh);
+ err = luo_block_grow(&sh->block_set, sh->count);
if (err)
return err;
}
@@ -201,6 +177,8 @@ static void luo_session_remove(struct luo_session_header *sh,
guard(rwsem_write)(&sh->rwsem);
list_del(&session->list);
sh->count--;
+ if (sh == &luo_session_global.outgoing)
+ luo_block_shrink(&sh->block_set, sh->count);
}
static int luo_session_finish_one(struct luo_session *session)
@@ -489,15 +467,17 @@ void __init luo_session_setup_outgoing(u64 *sessions_pa)
int __init luo_session_setup_incoming(u64 sessions_pa)
{
- struct luo_session_header_ser *header_ser;
+ struct luo_session_header *sh = &luo_session_global.incoming;
+ int err;
- if (sessions_pa) {
- header_ser = phys_to_virt(sessions_pa);
- luo_session_global.incoming.header_ser = header_ser;
- luo_session_global.incoming.ser = (void *)(header_ser + 1);
- luo_session_global.incoming.active = true;
- }
+ if (!sessions_pa)
+ return 0;
+ err = luo_block_restore(&sh->block_set, sessions_pa);
+ if (err)
+ return err;
+
+ sh->active = true;
return 0;
}
@@ -539,6 +519,8 @@ int luo_session_deserialize(void)
{
struct luo_session_header *sh = &luo_session_global.incoming;
static bool is_deserialized;
+ struct luo_session_ser *ser;
+ struct luo_block_it it;
static int err;
/* If has been deserialized, always return the same error code */
@@ -564,51 +546,60 @@ int luo_session_deserialize(void)
* userspace to detect the failure and trigger a reboot, which will
* reliably reset devices and reclaim memory.
*/
- for (int i = 0; i < sh->header_ser->count; i++) {
- err = luo_session_deserialize_one(sh, &sh->ser[i]);
+ luo_block_it_init(&it, &sh->block_set);
+ while ((ser = luo_block_it_read(&it))) {
+ err = luo_session_deserialize_one(sh, ser);
if (err)
- return err;
+ goto err_destroy_blocks;
}
- kho_restore_free(sh->header_ser);
- sh->header_ser = NULL;
- sh->ser = NULL;
+err_destroy_blocks:
+ luo_block_destroy(&sh->block_set);
- return 0;
+ return err;
}
int luo_session_serialize(void)
{
struct luo_session_header *sh = &luo_session_global.outgoing;
struct luo_session *session;
- int i = 0;
+ struct luo_block_it it;
int err;
guard(rwsem_write)(&sh->rwsem);
*sh->sessions_pa = 0;
+ luo_block_it_init(&it, &sh->block_set);
+
list_for_each_entry(session, &sh->list, list) {
- err = luo_session_freeze_one(session, &sh->ser[i]);
+ struct luo_session_ser *ser = luo_block_it_next(&it);
+
+ if (!ser) {
+ err = -ENOSPC;
+ goto err_undo;
+ }
+
+ err = luo_session_freeze_one(session, ser);
if (err)
goto err_undo;
- strscpy(sh->ser[i].name, session->name,
- sizeof(sh->ser[i].name));
- i++;
+ strscpy(ser->name, session->name, sizeof(ser->name));
}
- if (sh->header_ser && sh->count > 0) {
- sh->header_ser->count = sh->count;
- *sh->sessions_pa = virt_to_phys(sh->header_ser);
- }
+ luo_block_it_finalize(&it);
+
+ if (sh->sessions_pa && sh->count > 0)
+ *sh->sessions_pa = sh->block_set.head_pa;
return 0;
err_undo:
+ luo_block_it_prev(&it);
list_for_each_entry_continue_reverse(session, &sh->list, list) {
- i--;
- luo_session_unfreeze_one(session, &sh->ser[i]);
- memset(sh->ser[i].name, 0, sizeof(sh->ser[i].name));
+ struct luo_session_ser *ser = luo_block_it_prev(&it);
+
+ luo_session_unfreeze_one(session, ser);
+ memset(ser->name, 0, sizeof(ser->name));
}
return err;
--
2.53.0
^ permalink raw reply related
* [PATCH v2 05/10] liveupdate: defer session block allocation and PA setting
From: Pasha Tatashin @ 2026-05-14 22:26 UTC (permalink / raw)
To: linux-kselftest, rppt, shuah, akpm, linux-mm, skhan, linux-doc,
linux-kernel, corbet, pasha.tatashin, dmatlack, kexec, pratyush,
skhawaja, graf
In-Reply-To: <20260514222628.931312-1-pasha.tatashin@soleen.com>
Currently, luo_session_setup_outgoing() allocates the session block and
sets its physical address in the header immediately. With upcoming
dynamic block-based session management, this makes the first block
different from the rest. Move the allocation to where it is first needed.
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
kernel/liveupdate/luo_core.c | 4 +-
kernel/liveupdate/luo_internal.h | 2 +-
kernel/liveupdate/luo_session.c | 68 ++++++++++++++++++++------------
3 files changed, 45 insertions(+), 29 deletions(-)
diff --git a/kernel/liveupdate/luo_core.c b/kernel/liveupdate/luo_core.c
index 9bd649b22029..5dd7f1e7dddb 100644
--- a/kernel/liveupdate/luo_core.c
+++ b/kernel/liveupdate/luo_core.c
@@ -189,9 +189,7 @@ static int __init luo_fdt_setup(void)
if (err)
goto exit_free_luo_ser;
- err = luo_session_setup_outgoing(&luo_ser->sessions_pa);
- if (err)
- goto exit_free_luo_ser;
+ luo_session_setup_outgoing(&luo_ser->sessions_pa);
err = luo_flb_setup_outgoing(&luo_ser->flbs_pa);
if (err)
diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_internal.h
index 04f31319ccdf..090078ad771c 100644
--- a/kernel/liveupdate/luo_internal.h
+++ b/kernel/liveupdate/luo_internal.h
@@ -126,7 +126,7 @@ extern struct rw_semaphore luo_register_rwlock;
int luo_session_create(const char *name, struct file **filep);
int luo_session_retrieve(const char *name, struct file **filep);
-int __init luo_session_setup_outgoing(u64 *sessions_pa);
+void __init luo_session_setup_outgoing(u64 *sessions_pa);
int __init luo_session_setup_incoming(u64 sessions_pa);
int luo_session_serialize(void);
int luo_session_deserialize(void);
diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c
index c08733a328bc..339514e8122c 100644
--- a/kernel/liveupdate/luo_session.c
+++ b/kernel/liveupdate/luo_session.c
@@ -77,15 +77,16 @@
/**
* struct luo_session_header - Header struct for managing LUO sessions.
- * @count: The number of sessions currently tracked in the @list.
- * @list: The head of the linked list of `struct luo_session` instances.
- * @rwsem: A read-write semaphore providing synchronized access to the
- * session list and other fields in this structure.
- * @header_ser: The header data of serialization array.
- * @ser: The serialized session data (an array of
- * `struct luo_session_ser`).
- * @active: Set to true when first initialized. If previous kernel did not
- * send session data, active stays false for incoming.
+ * @count: The number of sessions currently tracked in the @list.
+ * @list: The head of the linked list of `struct luo_session` instances.
+ * @rwsem: A read-write semaphore providing synchronized access to the
+ * session list and other fields in this structure.
+ * @header_ser: The header data of serialization array.
+ * @ser: The serialized session data (an array of
+ * `struct luo_session_ser`).
+ * @sessions_pa: Points to the location of sessions_pa within struct luo_ser.
+ * @active: Set to true when first initialized. If previous kernel did not
+ * send session data, active stays false for incoming.
*/
struct luo_session_header {
long count;
@@ -93,6 +94,7 @@ struct luo_session_header {
struct rw_semaphore rwsem;
struct luo_session_header_ser *header_ser;
struct luo_session_ser *ser;
+ u64 *sessions_pa;
bool active;
};
@@ -140,10 +142,30 @@ static void luo_session_free(struct luo_session *session)
kfree(session);
}
+static int luo_session_grow_ser(struct luo_session_header *sh)
+{
+ struct luo_session_header_ser *header_ser;
+
+ if (sh->count == LUO_SESSION_MAX)
+ return -ENOMEM;
+
+ if (sh->header_ser)
+ return 0;
+
+ header_ser = kho_alloc_preserve(LUO_SESSION_PGCNT << PAGE_SHIFT);
+ if (IS_ERR(header_ser))
+ return PTR_ERR(header_ser);
+
+ sh->header_ser = header_ser;
+ sh->ser = (void *)(header_ser + 1);
+ return 0;
+}
+
static int luo_session_insert(struct luo_session_header *sh,
struct luo_session *session)
{
struct luo_session *it;
+ int err;
guard(rwsem_write)(&sh->rwsem);
@@ -152,8 +174,9 @@ static int luo_session_insert(struct luo_session_header *sh,
* for new session.
*/
if (sh == &luo_session_global.outgoing) {
- if (sh->count == LUO_SESSION_MAX)
- return -ENOMEM;
+ err = luo_session_grow_ser(sh);
+ if (err)
+ return err;
}
/*
@@ -458,21 +481,10 @@ int luo_session_retrieve(const char *name, struct file **filep)
return err;
}
-int __init luo_session_setup_outgoing(u64 *sessions_pa)
+void __init luo_session_setup_outgoing(u64 *sessions_pa)
{
- struct luo_session_header_ser *header_ser;
-
- header_ser = kho_alloc_preserve(LUO_SESSION_PGCNT << PAGE_SHIFT);
- if (IS_ERR(header_ser))
- return PTR_ERR(header_ser);
-
- *sessions_pa = virt_to_phys(header_ser);
-
- luo_session_global.outgoing.header_ser = header_ser;
- luo_session_global.outgoing.ser = (void *)(header_ser + 1);
+ luo_session_global.outgoing.sessions_pa = sessions_pa;
luo_session_global.outgoing.active = true;
-
- return 0;
}
int __init luo_session_setup_incoming(u64 sessions_pa)
@@ -573,6 +585,8 @@ int luo_session_serialize(void)
int err;
guard(rwsem_write)(&sh->rwsem);
+ *sh->sessions_pa = 0;
+
list_for_each_entry(session, &sh->list, list) {
err = luo_session_freeze_one(session, &sh->ser[i]);
if (err)
@@ -582,7 +596,11 @@ int luo_session_serialize(void)
sizeof(sh->ser[i].name));
i++;
}
- sh->header_ser->count = sh->count;
+
+ if (sh->header_ser && sh->count > 0) {
+ sh->header_ser->count = sh->count;
+ *sh->sessions_pa = virt_to_phys(sh->header_ser);
+ }
return 0;
--
2.53.0
^ permalink raw reply related
* [PATCH v2 04/10] liveupdate: add support for linked-block serialization
From: Pasha Tatashin @ 2026-05-14 22:26 UTC (permalink / raw)
To: linux-kselftest, rppt, shuah, akpm, linux-mm, skhan, linux-doc,
linux-kernel, corbet, pasha.tatashin, dmatlack, kexec, pratyush,
skhawaja, graf
In-Reply-To: <20260514222628.931312-1-pasha.tatashin@soleen.com>
Introduce a linked-block serialization mechanism for LUO state.
Previously, LUO used contiguous memory blocks for serializing sessions
and files, which imposed limits on the total number of items that could
be preserved across a live update.
This commit adds the infrastructure for a more flexible, block-based
approach where serialized data is stored in a chain of linked blocks.
This is a preparatory step to allow an unlimited number of
luo_sessions and luo_files to be preserved.
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
Documentation/core-api/liveupdate.rst | 8 +
include/linux/kho/abi/luo.h | 22 ++
kernel/liveupdate/Makefile | 1 +
kernel/liveupdate/luo_block.c | 388 ++++++++++++++++++++++++++
kernel/liveupdate/luo_internal.h | 57 ++++
5 files changed, 476 insertions(+)
create mode 100644 kernel/liveupdate/luo_block.c
diff --git a/Documentation/core-api/liveupdate.rst b/Documentation/core-api/liveupdate.rst
index 5a292d0f3706..978beb02292f 100644
--- a/Documentation/core-api/liveupdate.rst
+++ b/Documentation/core-api/liveupdate.rst
@@ -23,6 +23,11 @@ LUO File Lifecycle Bound Global Data
.. kernel-doc:: kernel/liveupdate/luo_flb.c
:doc: LUO File Lifecycle Bound Global Data
+LUO Serialization Blocks
+========================
+.. kernel-doc:: kernel/liveupdate/luo_block.c
+ :doc: LUO Serialization Blocks
+
Live Update Orchestrator ABI
============================
.. kernel-doc:: include/linux/kho/abi/luo.h
@@ -65,6 +70,9 @@ Internal API
.. kernel-doc:: kernel/liveupdate/luo_file.c
:internal:
+.. kernel-doc:: kernel/liveupdate/luo_block.c
+ :internal:
+
See Also
========
diff --git a/include/linux/kho/abi/luo.h b/include/linux/kho/abi/luo.h
index 1b2f865a771a..2ba447c0c1ba 100644
--- a/include/linux/kho/abi/luo.h
+++ b/include/linux/kho/abi/luo.h
@@ -49,6 +49,11 @@
* The central ABI structure that contains the overall state of the LUO.
* It includes the liveupdate-number and pointers to sessions and FLBs.
*
+ * - struct luo_block_header_ser:
+ * Header for the session or file data block. Contains the physical address
+ * of the next data block and the number of entries that follow this
+ * header in the current block.
+ *
* - struct luo_session_header_ser:
* Header for the session array. Contains the total page count of the
* preserved memory block and the number of `struct luo_session_ser`
@@ -105,6 +110,23 @@ struct luo_ser {
#define LIVEUPDATE_HNDL_COMPAT_LENGTH 48
+/**
+ * struct luo_block_header_ser - Header for the serialized data block.
+ * @next: Physical address of the next struct luo_block_header_ser.
+ * @count: The number of entries that immediately follow this header in the
+ * memory block.
+ *
+ * This structure is located at the beginning of a block of
+ * physical memory preserved across the kexec. It provides the necessary
+ * metadata to interpret the array of entries that follow.
+ *
+ * If this structure is modified, `LUO_FDT_COMPATIBLE` must be updated.
+ */
+struct luo_block_header_ser {
+ u64 next;
+ u64 count;
+} __packed;
+
/**
* struct luo_file_ser - Represents the serialized preserves files.
* @compatible: File handler compatible string.
diff --git a/kernel/liveupdate/Makefile b/kernel/liveupdate/Makefile
index d2f779cbe279..9f8f6fb1b8e5 100644
--- a/kernel/liveupdate/Makefile
+++ b/kernel/liveupdate/Makefile
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
luo-y := \
+ luo_block.o \
luo_core.o \
luo_file.o \
luo_flb.o \
diff --git a/kernel/liveupdate/luo_block.c b/kernel/liveupdate/luo_block.c
new file mode 100644
index 000000000000..0be0669b5cac
--- /dev/null
+++ b/kernel/liveupdate/luo_block.c
@@ -0,0 +1,388 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (c) 2026, Google LLC.
+ * Pasha Tatashin <pasha.tatashin@soleen.com>
+ */
+
+/**
+ * DOC: LUO Serialization Blocks
+ *
+ * LUO provides a mechanism to preserve stateful data across a kexec-based live
+ * update by serializing it into contiguous memory blocks. This file provides
+ * the common infrastructure for managing these blocks.
+ *
+ * Each block consists of a header (struct luo_block_header_ser) followed by an
+ * array of serialized entries. Multiple blocks are linked together via a
+ * physical pointer in the header, forming a linked list that can be easily
+ * traversed in both the current and the next kernel.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/io.h>
+#include <linux/kexec_handover.h>
+#include <linux/slab.h>
+#include "luo_internal.h"
+
+/* 2 4K pages, give space for 170 sessions or 127 files per block */
+#define LUO_BLOCK_PGCNT 2ul
+#define LUO_BLOCK_SIZE (LUO_BLOCK_PGCNT << PAGE_SHIFT)
+
+/*
+ * Safeguard limit for the number of serialization blocks. This is used to
+ * prevent infinite loops and excessive memory allocation in case of memory
+ * corruption in the preserved state.
+ *
+ * This limit allows for 1.7 million sessions and 1.27 million files per
+ * session, which is more than enough for all realistic use cases.
+ */
+#define LUO_MAX_BLOCKS 10000
+
+/**
+ * luo_block_set_init - Initialize a block set.
+ * @bs: The block set to initialize.
+ * @entry_size: The size of each entry in the blocks.
+ */
+void luo_block_set_init(struct luo_block_set *bs, size_t entry_size)
+{
+ *bs = (struct luo_block_set)LUO_BLOCK_SET_INIT(*bs, entry_size);
+}
+
+static inline u64 luo_block_count_per_block(struct luo_block_set *bs)
+{
+ if (unlikely(!bs->count_per_block)) {
+ bs->count_per_block = (LUO_BLOCK_SIZE -
+ sizeof(struct luo_block_header_ser)) /
+ bs->entry_size;
+ WARN_ON(!bs->count_per_block);
+ }
+ return bs->count_per_block;
+}
+
+/* Free serialzied data */
+static void luo_block_free_ser(struct luo_block_set *bs,
+ struct luo_block_header_ser *ser)
+{
+ if (bs->incoming)
+ kho_restore_free(ser);
+ else
+ kho_unpreserve_free(ser);
+}
+
+static struct luo_block_header_ser *luo_block_alloc_ser(struct luo_block_set *bs)
+{
+ WARN_ON(bs->incoming);
+ return kho_alloc_preserve(LUO_BLOCK_SIZE);
+}
+
+static int luo_block_add(struct luo_block_set *bs,
+ struct luo_block_header_ser *ser)
+{
+ struct luo_block *block, *last;
+
+ if (bs->nblocks >= LUO_MAX_BLOCKS)
+ return -ENOSPC;
+
+ block = kzalloc_obj(*block);
+ if (!block)
+ return -ENOMEM;
+
+ block->ser = ser;
+ last = list_last_entry_or_null(&bs->blocks, struct luo_block, list);
+ list_add_tail(&block->list, &bs->blocks);
+ bs->nblocks++;
+
+ if (last)
+ last->ser->next = virt_to_phys(ser);
+ else
+ bs->head_pa = virt_to_phys(ser);
+
+ return 0;
+}
+
+/**
+ * luo_block_grow - Create a new block if the current capacity is reached.
+ * @bs: The block set.
+ * @count: The current number of entries.
+ *
+ * This function handles the dynamic expansion of a block set. It allocates
+ * and links a new serialization block if the provided entry count matches
+ * the current total capacity of the set.
+ *
+ * Return: 0 on success, or a negative errno on failure.
+ */
+int luo_block_grow(struct luo_block_set *bs, u64 count)
+{
+ struct luo_block_header_ser *ser;
+ int err;
+
+ if (WARN_ON(bs->incoming))
+ return -EINVAL;
+
+ if (count != bs->nblocks * luo_block_count_per_block(bs))
+ return 0;
+
+ ser = luo_block_alloc_ser(bs);
+ if (IS_ERR(ser))
+ return PTR_ERR(ser);
+
+ err = luo_block_add(bs, ser);
+ if (err) {
+ luo_block_free_ser(bs, ser);
+ return err;
+ }
+
+ return 0;
+}
+
+/**
+ * luo_block_shrink - Conditionally destroy the last block in a block set.
+ * @bs: The block set.
+ * @count: The current number of entries across all blocks.
+ *
+ * This function checks if the last block in the set is redundant based on the
+ * total entry count and the capacity of the preceding blocks. If the entry
+ * count can be accommodated by the blocks that come before the last one, the
+ * last block is destroyed and removed from the set.
+ */
+void luo_block_shrink(struct luo_block_set *bs, u64 count)
+{
+ struct luo_block *last, *new_last;
+
+ if (count > (bs->nblocks - 1) * luo_block_count_per_block(bs))
+ return;
+
+ if (list_empty(&bs->blocks))
+ return;
+
+ last = list_last_entry(&bs->blocks, struct luo_block, list);
+ list_del(&last->list);
+ bs->nblocks--;
+ luo_block_free_ser(bs, last->ser);
+ kfree(last);
+
+ new_last = list_last_entry_or_null(&bs->blocks, struct luo_block, list);
+ if (new_last)
+ new_last->ser->next = 0;
+ else
+ bs->head_pa = 0;
+}
+
+/*
+ * luo_cyclic_blocks_check - Check for cycles in a linked list of blocks.
+ * Uses Floyd's cycle-finding algorithm to ensure sanity of the incoming list.
+ */
+static bool luo_cyclic_blocks_check(struct luo_block_set *bs)
+{
+ struct luo_block_header_ser *fast;
+ struct luo_block_header_ser *slow;
+ int count = 0;
+
+ fast = phys_to_virt(bs->head_pa);
+ slow = fast;
+
+ while (fast) {
+ if (count++ >= LUO_MAX_BLOCKS) {
+ pr_err("Linked list too long\n");
+ return false;
+ }
+
+ if (!fast->next)
+ break;
+
+ fast = phys_to_virt(fast->next);
+ if (!fast->next)
+ break;
+
+ fast = phys_to_virt(fast->next);
+ slow = phys_to_virt(slow->next);
+
+ if (slow == fast) {
+ pr_err("Cyclic list detected\n");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/**
+ * luo_block_restore - Restore a block set from a physical address.
+ * @bs: The block set to restore.
+ * @head_pa: Physical address of the first block header.
+ *
+ * Return: 0 on success, or a negative errno on failure.
+ */
+int luo_block_restore(struct luo_block_set *bs, u64 head_pa)
+{
+ struct luo_block_header_ser *ser;
+ u64 next_pa = head_pa;
+ int err;
+
+ /* Restored block sets use size from the previous kernel */
+ bs->incoming = true;
+ if (!head_pa)
+ return 0;
+
+ bs->head_pa = head_pa;
+ if (!luo_cyclic_blocks_check(bs))
+ return -EINVAL;
+
+ while (next_pa) {
+ ser = phys_to_virt(next_pa);
+ if (ser->count > luo_block_count_per_block(bs)) {
+ pr_warn("Block contains too many entries: %llu\n",
+ ser->count);
+ err = -EINVAL;
+ goto err_destroy;
+ }
+ err = luo_block_add(bs, ser);
+ if (err)
+ goto err_destroy;
+ next_pa = ser->next;
+ }
+
+ return 0;
+
+err_destroy:
+ luo_block_destroy(bs);
+ return err;
+}
+
+/**
+ * luo_block_destroy - Destroy all blocks in a block set.
+ * @bs: The block set.
+ */
+void luo_block_destroy(struct luo_block_set *bs)
+{
+ u64 head_pa = bs->head_pa;
+ struct luo_block *block;
+
+ while (!list_empty(&bs->blocks)) {
+ block = list_first_entry(&bs->blocks, struct luo_block, list);
+ list_del(&block->list);
+ kfree(block);
+ }
+ bs->nblocks = 0;
+ bs->head_pa = 0;
+
+ while (head_pa) {
+ struct luo_block_header_ser *ser = phys_to_virt(head_pa);
+
+ head_pa = ser->next;
+ luo_block_free_ser(bs, ser);
+ }
+}
+
+/**
+ * luo_block_set_clear - Clear all serialized data in a block set.
+ * @bs: The block set to clear.
+ */
+void luo_block_set_clear(struct luo_block_set *bs)
+{
+ struct luo_block *block;
+
+ list_for_each_entry(block, &bs->blocks, list) {
+ block->ser->count = 0;
+ memset(block->ser + 1, 0, LUO_BLOCK_SIZE - sizeof(*block->ser));
+ }
+}
+
+/**
+ * luo_block_it_init - Initialize a block set iterator.
+ * @it: The iterator to initialize.
+ * @bs: The block set to iterate over.
+ */
+void luo_block_it_init(struct luo_block_it *it, struct luo_block_set *bs)
+{
+ it->bs = bs;
+ it->block = list_first_entry_or_null(&bs->blocks, struct luo_block, list);
+ it->i = 0;
+}
+
+/**
+ * luo_block_it_next - Return the next entry slot in the block set.
+ * @it: The block iterator.
+ *
+ * If the current block is full, it automatically advances to the next block
+ * in the set.
+ *
+ * Return: A pointer to the next entry slot, or NULL if no more slots are
+ * available.
+ */
+void *luo_block_it_next(struct luo_block_it *it)
+{
+ if (!it->block)
+ return NULL;
+
+ if (it->i == luo_block_count_per_block(it->bs)) {
+ it->block->ser->count = it->i;
+ if (list_is_last(&it->block->list, &it->bs->blocks))
+ return NULL;
+ it->block = list_next_entry(it->block, list);
+ it->i = 0;
+ }
+
+ return (void *)(it->block->ser + 1) + (it->i++ * it->bs->entry_size);
+}
+
+/**
+ * luo_block_it_read - Return the next entry slot for reading.
+ * @it: The block iterator.
+ *
+ * This function iterates through entries that were previously serialized,
+ * respecting the count stored in each block's header.
+ *
+ * Return: A pointer to the next entry slot, or NULL if no more entries are
+ * available.
+ */
+void *luo_block_it_read(struct luo_block_it *it)
+{
+ if (!it->block)
+ return NULL;
+
+ while (it->i == it->block->ser->count) {
+ if (list_is_last(&it->block->list, &it->bs->blocks))
+ return NULL;
+ it->block = list_next_entry(it->block, list);
+ it->i = 0;
+ }
+
+ return (void *)(it->block->ser + 1) + (it->i++ * it->bs->entry_size);
+}
+
+/**
+ * luo_block_it_prev - Return the previous entry slot in the block set.
+ * @it: The block iterator.
+ *
+ * If the current index is at the start of a block, it automatically moves to
+ * the end of the previous block.
+ *
+ * Return: A pointer to the previous entry slot, or NULL if at the very
+ * beginning of the block set.
+ */
+void *luo_block_it_prev(struct luo_block_it *it)
+{
+ if (!it->block)
+ return NULL;
+
+ if (it->i == 0) {
+ if (list_is_first(&it->block->list, &it->bs->blocks))
+ return NULL;
+ it->block = list_prev_entry(it->block, list);
+ it->i = luo_block_count_per_block(it->bs);
+ }
+
+ return (void *)(it->block->ser + 1) + (--it->i * it->bs->entry_size);
+}
+
+/**
+ * luo_block_it_finalize - Finalize the current block by setting its entry count.
+ * @it: The block iterator.
+ */
+void luo_block_it_finalize(struct luo_block_it *it)
+{
+ if (it->block)
+ it->block->ser->count = it->i;
+}
diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_internal.h
index 4b6c18348d9b..04f31319ccdf 100644
--- a/kernel/liveupdate/luo_internal.h
+++ b/kernel/liveupdate/luo_internal.h
@@ -40,6 +40,51 @@ static inline int luo_ucmd_respond(struct luo_ucmd *ucmd,
*/
#define luo_restore_fail(__fmt, ...) panic(__fmt, ##__VA_ARGS__)
+/**
+ * struct luo_block - Internal representation of a serialization block.
+ * @list: List head for linking blocks in memory.
+ * @ser: Pointer to the serialized header in preserved memory.
+ */
+struct luo_block {
+ struct list_head list;
+ struct luo_block_header_ser *ser;
+};
+
+/**
+ * struct luo_block_set - A set of blocks that belong to the same object.
+ * @blocks: The list of serialization blocks (struct luo_block).
+ * @nblocks: The number of allocated serialization blocks.
+ * @head_pa: Physical address of the first block header.
+ * @entry_size: The size of each entry in the blocks.
+ * @count_per_block: The maximum number of entries each block can hold.
+ * @incoming: True if this block set was restored from the previous kernel.
+ */
+struct luo_block_set {
+ struct list_head blocks;
+ long nblocks;
+ u64 head_pa;
+ size_t entry_size;
+ u64 count_per_block;
+ bool incoming;
+};
+
+/**
+ * struct luo_block_it - Iterator for serializing entries into blocks.
+ * @bs: The block set being iterated.
+ * @block: The current block.
+ * @i: The current entry index within @block.
+ */
+struct luo_block_it {
+ struct luo_block_set *bs;
+ struct luo_block *block;
+ u64 i;
+};
+
+#define LUO_BLOCK_SET_INIT(name, _entry_size) { \
+ .blocks = LIST_HEAD_INIT((name).blocks), \
+ .entry_size = _entry_size, \
+}
+
/**
* struct luo_file_set - A set of files that belong to the same sessions.
* @files_list: An ordered list of files associated with this session, it is
@@ -100,6 +145,18 @@ int luo_file_deserialize(struct luo_file_set *file_set,
void luo_file_set_init(struct luo_file_set *file_set);
void luo_file_set_destroy(struct luo_file_set *file_set);
+void luo_block_set_init(struct luo_block_set *bs, size_t entry_size);
+int luo_block_grow(struct luo_block_set *bs, u64 count);
+void luo_block_shrink(struct luo_block_set *bs, u64 count);
+int luo_block_restore(struct luo_block_set *bs, u64 head_pa);
+void luo_block_destroy(struct luo_block_set *bs);
+void luo_block_set_clear(struct luo_block_set *bs);
+void luo_block_it_init(struct luo_block_it *it, struct luo_block_set *bs);
+void *luo_block_it_next(struct luo_block_it *it);
+void *luo_block_it_read(struct luo_block_it *it);
+void *luo_block_it_prev(struct luo_block_it *it);
+void luo_block_it_finalize(struct luo_block_it *it);
+
int luo_flb_file_preserve(struct liveupdate_file_handler *fh);
void luo_flb_file_unpreserve(struct liveupdate_file_handler *fh);
void luo_flb_file_finish(struct liveupdate_file_handler *fh);
--
2.53.0
^ permalink raw reply related
* [PATCH v2 03/10] liveupdate: Extract luo_session_deserialize_one helper
From: Pasha Tatashin @ 2026-05-14 22:26 UTC (permalink / raw)
To: linux-kselftest, rppt, shuah, akpm, linux-mm, skhan, linux-doc,
linux-kernel, corbet, pasha.tatashin, dmatlack, kexec, pratyush,
skhawaja, graf
In-Reply-To: <20260514222628.931312-1-pasha.tatashin@soleen.com>
Extract the logic for deserializing single entries for sessions into
separate helper functions. In preparation to a linked-block
serialization for sessions.
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
kernel/liveupdate/luo_session.c | 62 +++++++++++++++++++--------------
1 file changed, 36 insertions(+), 26 deletions(-)
diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c
index 915ab9ce0f34..c08733a328bc 100644
--- a/kernel/liveupdate/luo_session.c
+++ b/kernel/liveupdate/luo_session.c
@@ -489,6 +489,40 @@ int __init luo_session_setup_incoming(u64 sessions_pa)
return 0;
}
+static int luo_session_deserialize_one(struct luo_session_header *sh,
+ struct luo_session_ser *ser)
+{
+ struct luo_session *session;
+ int err;
+
+ session = luo_session_alloc(ser->name);
+ if (IS_ERR(session)) {
+ pr_warn("Failed to allocate session [%.*s] during deserialization %pe\n",
+ (int)sizeof(ser->name), ser->name, session);
+ return PTR_ERR(session);
+ }
+
+ err = luo_session_insert(sh, session);
+ if (err) {
+ pr_warn("Failed to insert session [%s] %pe\n",
+ session->name, ERR_PTR(err));
+ luo_session_free(session);
+ return err;
+ }
+
+ scoped_guard(mutex, &session->mutex) {
+ err = luo_file_deserialize(&session->file_set,
+ &ser->file_set_ser);
+ }
+ if (err) {
+ pr_warn("Failed to deserialize files for session [%s] %pe\n",
+ session->name, ERR_PTR(err));
+ return err;
+ }
+
+ return 0;
+}
+
int luo_session_deserialize(void)
{
struct luo_session_header *sh = &luo_session_global.incoming;
@@ -519,33 +553,9 @@ int luo_session_deserialize(void)
* reliably reset devices and reclaim memory.
*/
for (int i = 0; i < sh->header_ser->count; i++) {
- struct luo_session *session;
-
- session = luo_session_alloc(sh->ser[i].name);
- if (IS_ERR(session)) {
- pr_warn("Failed to allocate session [%.*s] during deserialization %pe\n",
- (int)sizeof(sh->ser[i].name),
- sh->ser[i].name, session);
- return PTR_ERR(session);
- }
-
- err = luo_session_insert(sh, session);
- if (err) {
- pr_warn("Failed to insert session [%s] %pe\n",
- session->name, ERR_PTR(err));
- luo_session_free(session);
- return err;
- }
-
- scoped_guard(mutex, &session->mutex) {
- err = luo_file_deserialize(&session->file_set,
- &sh->ser[i].file_set_ser);
- }
- if (err) {
- pr_warn("Failed to deserialize files for session [%s] %pe\n",
- session->name, ERR_PTR(err));
+ err = luo_session_deserialize_one(sh, &sh->ser[i]);
+ if (err)
return err;
- }
}
kho_restore_free(sh->header_ser);
--
2.53.0
^ permalink raw reply related
* [PATCH v2 02/10] liveupdate: Extract luo_file_deserialize_one helper
From: Pasha Tatashin @ 2026-05-14 22:26 UTC (permalink / raw)
To: linux-kselftest, rppt, shuah, akpm, linux-mm, skhan, linux-doc,
linux-kernel, corbet, pasha.tatashin, dmatlack, kexec, pratyush,
skhawaja, graf
In-Reply-To: <20260514222628.931312-1-pasha.tatashin@soleen.com>
Extract the logic for deserializing single entries for files into
separate helper functions. In preparation to a linked-block
serialization for files.
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
kernel/liveupdate/luo_file.c | 77 ++++++++++++++++++++----------------
1 file changed, 44 insertions(+), 33 deletions(-)
diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c
index a0a419085e28..a2510563469a 100644
--- a/kernel/liveupdate/luo_file.c
+++ b/kernel/liveupdate/luo_file.c
@@ -752,6 +752,46 @@ int luo_file_finish(struct luo_file_set *file_set)
return 0;
}
+static int luo_file_deserialize_one(struct luo_file_set *file_set,
+ struct luo_file_ser *ser)
+{
+ struct liveupdate_file_handler *fh;
+ bool handler_found = false;
+ struct luo_file *luo_file;
+
+ down_read(&luo_register_rwlock);
+ list_private_for_each_entry(fh, &luo_file_handler_list, list) {
+ if (!strcmp(fh->compatible, ser->compatible)) {
+ if (try_module_get(fh->ops->owner))
+ handler_found = true;
+ break;
+ }
+ }
+ up_read(&luo_register_rwlock);
+
+ if (!handler_found) {
+ pr_warn("No registered handler for compatible '%.*s'\n",
+ (int)sizeof(ser->compatible),
+ ser->compatible);
+ return -ENOENT;
+ }
+
+ luo_file = kzalloc_obj(*luo_file);
+ if (!luo_file) {
+ module_put(fh->ops->owner);
+ return -ENOMEM;
+ }
+
+ luo_file->fh = fh;
+ luo_file->file = NULL;
+ luo_file->serialized_data = ser->data;
+ luo_file->token = ser->token;
+ mutex_init(&luo_file->mutex);
+ list_add_tail(&luo_file->list, &file_set->files_list);
+
+ return 0;
+}
+
/**
* luo_file_deserialize - Reconstructs the list of preserved files in the new kernel.
* @file_set: The incoming file_set to fill with deserialized data.
@@ -781,6 +821,7 @@ int luo_file_deserialize(struct luo_file_set *file_set,
struct luo_file_set_ser *file_set_ser)
{
struct luo_file_ser *file_ser;
+ int err;
u64 i;
if (!file_set_ser->files) {
@@ -808,39 +849,9 @@ int luo_file_deserialize(struct luo_file_set *file_set,
*/
file_ser = file_set->files;
for (i = 0; i < file_set->count; i++) {
- struct liveupdate_file_handler *fh;
- bool handler_found = false;
- struct luo_file *luo_file;
-
- down_read(&luo_register_rwlock);
- list_private_for_each_entry(fh, &luo_file_handler_list, list) {
- if (!strcmp(fh->compatible, file_ser[i].compatible)) {
- if (try_module_get(fh->ops->owner))
- handler_found = true;
- break;
- }
- }
- up_read(&luo_register_rwlock);
-
- if (!handler_found) {
- pr_warn("No registered handler for compatible '%.*s'\n",
- (int)sizeof(file_ser[i].compatible),
- file_ser[i].compatible);
- return -ENOENT;
- }
-
- luo_file = kzalloc_obj(*luo_file);
- if (!luo_file) {
- module_put(fh->ops->owner);
- return -ENOMEM;
- }
-
- luo_file->fh = fh;
- luo_file->file = NULL;
- luo_file->serialized_data = file_ser[i].data;
- luo_file->token = file_ser[i].token;
- mutex_init(&luo_file->mutex);
- list_add_tail(&luo_file->list, &file_set->files_list);
+ err = luo_file_deserialize_one(file_set, &file_ser[i]);
+ if (err)
+ return err;
}
return 0;
--
2.53.0
^ permalink raw reply related
* [PATCH v2 01/10] liveupdate: centralize state management into struct luo_ser
From: Pasha Tatashin @ 2026-05-14 22:26 UTC (permalink / raw)
To: linux-kselftest, rppt, shuah, akpm, linux-mm, skhan, linux-doc,
linux-kernel, corbet, pasha.tatashin, dmatlack, kexec, pratyush,
skhawaja, graf
In-Reply-To: <20260514222628.931312-1-pasha.tatashin@soleen.com>
Transition the LUO to ABI v2, which centralizes state management into a
single struct luo_ser header.
Previously, LUO state was spread across multiple FDT properties and
subnodes. ABI v2 simplifies this by placing all core state, including
the liveupdate number and physical addresses for sessions and FLB
headers into a centralized struct luo_ser.
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
include/linux/kho/abi/luo.h | 91 +++++++++++---------------------
kernel/liveupdate/luo_core.c | 59 ++++++++++++++-------
kernel/liveupdate/luo_flb.c | 65 ++++-------------------
kernel/liveupdate/luo_internal.h | 8 +--
kernel/liveupdate/luo_session.c | 57 +++-----------------
5 files changed, 93 insertions(+), 187 deletions(-)
diff --git a/include/linux/kho/abi/luo.h b/include/linux/kho/abi/luo.h
index 46750a0ddf88..1b2f865a771a 100644
--- a/include/linux/kho/abi/luo.h
+++ b/include/linux/kho/abi/luo.h
@@ -30,52 +30,25 @@
* .. code-block:: none
*
* / {
- * compatible = "luo-v1";
- * liveupdate-number = <...>;
- *
- * luo-session {
- * compatible = "luo-session-v1";
- * luo-session-header = <phys_addr_of_session_header_ser>;
- * };
- *
- * luo-flb {
- * compatible = "luo-flb-v1";
- * luo-flb-header = <phys_addr_of_flb_header_ser>;
- * };
+ * compatible = "luo-v2";
+ * luo-abi-header = <phys_addr_of_luo_ser>;
* };
*
* Main LUO Node (/):
*
- * - compatible: "luo-v1"
+ * - compatible: "luo-v2"
* Identifies the overall LUO ABI version.
- * - liveupdate-number: u64
- * A counter tracking the number of successful live updates performed.
- *
- * Session Node (luo-session):
- * This node describes all preserved user-space sessions.
- *
- * - compatible: "luo-session-v1"
- * Identifies the session ABI version.
- * - luo-session-header: u64
- * The physical address of a `struct luo_session_header_ser`. This structure
- * is the header for a contiguous block of memory containing an array of
- * `struct luo_session_ser`, one for each preserved session.
- *
- * File-Lifecycle-Bound Node (luo-flb):
- * This node describes all preserved global objects whose lifecycle is bound
- * to that of the preserved files (e.g., shared IOMMU state).
- *
- * - compatible: "luo-flb-v1"
- * Identifies the FLB ABI version.
- * - luo-flb-header: u64
- * The physical address of a `struct luo_flb_header_ser`. This structure is
- * the header for a contiguous block of memory containing an array of
- * `struct luo_flb_ser`, one for each preserved global object.
+ * - luo-abi-header: u64
+ * The physical address of `struct luo_ser`.
*
* Serialization Structures:
* The FDT properties point to memory regions containing arrays of simple,
* `__packed` structures. These structures contain the actual preserved state.
*
+ * - struct luo_ser:
+ * The central ABI structure that contains the overall state of the LUO.
+ * It includes the liveupdate-number and pointers to sessions and FLBs.
+ *
* - struct luo_session_header_ser:
* Header for the session array. Contains the total page count of the
* preserved memory block and the number of `struct luo_session_ser`
@@ -109,13 +82,26 @@
/*
* The LUO FDT hooks all LUO state for sessions, fds, etc.
- * In the root it also carries "liveupdate-number" 64-bit property that
- * corresponds to the number of live-updates performed on this machine.
*/
#define LUO_FDT_SIZE PAGE_SIZE
#define LUO_FDT_KHO_ENTRY_NAME "LUO"
-#define LUO_FDT_COMPATIBLE "luo-v1"
-#define LUO_FDT_LIVEUPDATE_NUM "liveupdate-number"
+#define LUO_FDT_COMPATIBLE "luo-v2"
+#define LUO_FDT_ABI_HEADER "luo-abi-header"
+
+/**
+ * struct luo_ser - Centralized LUO ABI header.
+ * @liveupdate_num: A counter tracking the number of successful live updates.
+ * @sessions_pa: Physical address of the first session block header.
+ * @flbs_pa: Physical address of the FLB header.
+ *
+ * This structure is the root of all preserved LUO state. It is pointed to by
+ * the "luo-abi-header" property in the LUO FDT.
+ */
+struct luo_ser {
+ u64 liveupdate_num;
+ u64 sessions_pa;
+ u64 flbs_pa;
+} __packed;
#define LIVEUPDATE_HNDL_COMPAT_LENGTH 48
@@ -147,15 +133,6 @@ struct luo_file_set_ser {
u64 count;
} __packed;
-/*
- * LUO FDT session node
- * LUO_FDT_SESSION_HEADER: is a u64 physical address of struct
- * luo_session_header_ser
- */
-#define LUO_FDT_SESSION_NODE_NAME "luo-session"
-#define LUO_FDT_SESSION_COMPATIBLE "luo-session-v2"
-#define LUO_FDT_SESSION_HEADER "luo-session-header"
-
/**
* struct luo_session_header_ser - Header for the serialized session data block.
* @count: The number of `struct luo_session_ser` entries that immediately
@@ -165,7 +142,7 @@ struct luo_file_set_ser {
* physical memory preserved across the kexec. It provides the necessary
* metadata to interpret the array of session entries that follow.
*
- * If this structure is modified, `LUO_FDT_SESSION_COMPATIBLE` must be updated.
+ * If this structure is modified, `LUO_FDT_COMPATIBLE` must be updated.
*/
struct luo_session_header_ser {
u64 count;
@@ -182,7 +159,7 @@ struct luo_session_header_ser {
* session) is created and passed to the new kernel, allowing it to reconstruct
* the session context.
*
- * If this structure is modified, `LUO_FDT_SESSION_COMPATIBLE` must be updated.
+ * If this structure is modified, `LUO_FDT_COMPATIBLE` must be updated.
*/
struct luo_session_ser {
char name[LIVEUPDATE_SESSION_NAME_LENGTH];
@@ -192,10 +169,6 @@ struct luo_session_ser {
/* The max size is set so it can be reliably used during in serialization */
#define LIVEUPDATE_FLB_COMPAT_LENGTH 48
-#define LUO_FDT_FLB_NODE_NAME "luo-flb"
-#define LUO_FDT_FLB_COMPATIBLE "luo-flb-v1"
-#define LUO_FDT_FLB_HEADER "luo-flb-header"
-
/**
* struct luo_flb_header_ser - Header for the serialized FLB data block.
* @pgcnt: The total number of pages occupied by the entire preserved memory
@@ -205,11 +178,9 @@ struct luo_session_ser {
* in the memory block.
*
* This structure is located at the physical address specified by the
- * `LUO_FDT_FLB_HEADER` FDT property. It provides the new kernel with the
- * necessary information to find and iterate over the array of preserved
- * File-Lifecycle-Bound objects and to manage the underlying memory.
+ * flbs_pa in luo_ser.
*
- * If this structure is modified, LUO_FDT_FLB_COMPATIBLE must be updated.
+ * If this structure is modified, `LUO_FDT_COMPATIBLE` must be updated.
*/
struct luo_flb_header_ser {
u64 pgcnt;
@@ -231,7 +202,7 @@ struct luo_flb_header_ser {
* passed to the new kernel. Each entry allows the LUO core to restore one
* global, shared object.
*
- * If this structure is modified, LUO_FDT_FLB_COMPATIBLE must be updated.
+ * If this structure is modified, `LUO_FDT_COMPATIBLE` must be updated.
*/
struct luo_flb_ser {
char name[LIVEUPDATE_FLB_COMPAT_LENGTH];
diff --git a/kernel/liveupdate/luo_core.c b/kernel/liveupdate/luo_core.c
index 803f51c84275..9bd649b22029 100644
--- a/kernel/liveupdate/luo_core.c
+++ b/kernel/liveupdate/luo_core.c
@@ -82,9 +82,11 @@ early_param("liveupdate", early_liveupdate_param);
static int __init luo_early_startup(void)
{
+ struct luo_ser *luo_ser;
+ int err, header_size;
phys_addr_t fdt_phys;
- int err, ln_size;
const void *ptr;
+ u64 luo_ser_pa;
if (!kho_is_enabled()) {
if (liveupdate_enabled())
@@ -115,27 +117,29 @@ static int __init luo_early_startup(void)
return -EINVAL;
}
- ln_size = 0;
- ptr = fdt_getprop(luo_global.fdt_in, 0, LUO_FDT_LIVEUPDATE_NUM,
- &ln_size);
- if (!ptr || ln_size != sizeof(luo_global.liveupdate_num)) {
- pr_err("Unable to get live update number '%s' [%d]\n",
- LUO_FDT_LIVEUPDATE_NUM, ln_size);
+ header_size = 0;
+ ptr = fdt_getprop(luo_global.fdt_in, 0, LUO_FDT_ABI_HEADER, &header_size);
+ if (!ptr || header_size != sizeof(u64)) {
+ pr_err("Unable to get ABI header '%s' [%d]\n",
+ LUO_FDT_ABI_HEADER, header_size);
return -EINVAL;
}
- luo_global.liveupdate_num = get_unaligned((u64 *)ptr);
+ luo_ser_pa = get_unaligned((u64 *)ptr);
+ luo_ser = phys_to_virt(luo_ser_pa);
+
+ luo_global.liveupdate_num = luo_ser->liveupdate_num;
pr_info("Retrieved live update data, liveupdate number: %lld\n",
luo_global.liveupdate_num);
- err = luo_session_setup_incoming(luo_global.fdt_in);
+ err = luo_session_setup_incoming(luo_ser->sessions_pa);
if (err)
return err;
- err = luo_flb_setup_incoming(luo_global.fdt_in);
+ luo_flb_setup_incoming(luo_ser->flbs_pa);
- return err;
+ return 0;
}
static int __init liveupdate_early_init(void)
@@ -156,7 +160,8 @@ early_initcall(liveupdate_early_init);
/* Called during boot to create outgoing LUO fdt tree */
static int __init luo_fdt_setup(void)
{
- const u64 ln = luo_global.liveupdate_num + 1;
+ struct luo_ser *luo_ser;
+ u64 luo_ser_pa;
void *fdt_out;
int err;
@@ -166,27 +171,45 @@ static int __init luo_fdt_setup(void)
return PTR_ERR(fdt_out);
}
+ luo_ser = kho_alloc_preserve(sizeof(*luo_ser));
+ if (IS_ERR(luo_ser)) {
+ err = PTR_ERR(luo_ser);
+ goto exit_free_fdt;
+ }
+ luo_ser_pa = virt_to_phys(luo_ser);
+
err = fdt_create(fdt_out, LUO_FDT_SIZE);
err |= fdt_finish_reservemap(fdt_out);
err |= fdt_begin_node(fdt_out, "");
err |= fdt_property_string(fdt_out, "compatible", LUO_FDT_COMPATIBLE);
- err |= fdt_property(fdt_out, LUO_FDT_LIVEUPDATE_NUM, &ln, sizeof(ln));
- err |= luo_session_setup_outgoing(fdt_out);
- err |= luo_flb_setup_outgoing(fdt_out);
+ err |= fdt_property(fdt_out, LUO_FDT_ABI_HEADER, &luo_ser_pa,
+ sizeof(luo_ser_pa));
err |= fdt_end_node(fdt_out);
err |= fdt_finish(fdt_out);
if (err)
- goto exit_free;
+ goto exit_free_luo_ser;
+
+ err = luo_session_setup_outgoing(&luo_ser->sessions_pa);
+ if (err)
+ goto exit_free_luo_ser;
+
+ err = luo_flb_setup_outgoing(&luo_ser->flbs_pa);
+ if (err)
+ goto exit_free_luo_ser;
+
+ luo_ser->liveupdate_num = luo_global.liveupdate_num + 1;
err = kho_add_subtree(LUO_FDT_KHO_ENTRY_NAME, fdt_out,
fdt_totalsize(fdt_out));
if (err)
- goto exit_free;
+ goto exit_free_luo_ser;
luo_global.fdt_out = fdt_out;
return 0;
-exit_free:
+exit_free_luo_ser:
+ kho_unpreserve_free(luo_ser);
+exit_free_fdt:
kho_unpreserve_free(fdt_out);
pr_err("failed to prepare LUO FDT: %d\n", err);
diff --git a/kernel/liveupdate/luo_flb.c b/kernel/liveupdate/luo_flb.c
index 8f5c5dd01cd0..7ccc59981297 100644
--- a/kernel/liveupdate/luo_flb.c
+++ b/kernel/liveupdate/luo_flb.c
@@ -159,8 +159,8 @@ static void luo_flb_file_unpreserve_one(struct liveupdate_flb *flb)
static int luo_flb_retrieve_one(struct liveupdate_flb *flb)
{
- struct luo_flb_private *private = luo_flb_get_private(flb);
struct luo_flb_header *fh = &luo_flb_global.incoming;
+ struct luo_flb_private *private = luo_flb_get_private(flb);
struct liveupdate_flb_op_args args = {0};
bool found = false;
int err;
@@ -551,27 +551,15 @@ int liveupdate_flb_get_outgoing(struct liveupdate_flb *flb, void **objp)
return 0;
}
-int __init luo_flb_setup_outgoing(void *fdt_out)
+int __init luo_flb_setup_outgoing(u64 *flbs_pa)
{
struct luo_flb_header_ser *header_ser;
- u64 header_ser_pa;
- int err;
header_ser = kho_alloc_preserve(LUO_FLB_PGCNT << PAGE_SHIFT);
if (IS_ERR(header_ser))
return PTR_ERR(header_ser);
- header_ser_pa = virt_to_phys(header_ser);
-
- err = fdt_begin_node(fdt_out, LUO_FDT_FLB_NODE_NAME);
- err |= fdt_property_string(fdt_out, "compatible",
- LUO_FDT_FLB_COMPATIBLE);
- err |= fdt_property(fdt_out, LUO_FDT_FLB_HEADER, &header_ser_pa,
- sizeof(header_ser_pa));
- err |= fdt_end_node(fdt_out);
-
- if (err)
- goto err_unpreserve;
+ *flbs_pa = virt_to_phys(header_ser);
header_ser->pgcnt = LUO_FLB_PGCNT;
luo_flb_global.outgoing.header_ser = header_ser;
@@ -579,53 +567,18 @@ int __init luo_flb_setup_outgoing(void *fdt_out)
luo_flb_global.outgoing.active = true;
return 0;
-
-err_unpreserve:
- kho_unpreserve_free(header_ser);
-
- return err;
}
-int __init luo_flb_setup_incoming(void *fdt_in)
+void __init luo_flb_setup_incoming(u64 flbs_pa)
{
struct luo_flb_header_ser *header_ser;
- int err, header_size, offset;
- const void *ptr;
- u64 header_ser_pa;
-
- offset = fdt_subnode_offset(fdt_in, 0, LUO_FDT_FLB_NODE_NAME);
- if (offset < 0) {
- pr_err("Unable to get FLB node [%s]\n", LUO_FDT_FLB_NODE_NAME);
- return -ENOENT;
+ if (flbs_pa) {
+ header_ser = phys_to_virt(flbs_pa);
+ luo_flb_global.incoming.header_ser = header_ser;
+ luo_flb_global.incoming.ser = (void *)(header_ser + 1);
+ luo_flb_global.incoming.active = true;
}
-
- err = fdt_node_check_compatible(fdt_in, offset,
- LUO_FDT_FLB_COMPATIBLE);
- if (err) {
- pr_err("FLB node is incompatible with '%s' [%d]\n",
- LUO_FDT_FLB_COMPATIBLE, err);
-
- return -EINVAL;
- }
-
- header_size = 0;
- ptr = fdt_getprop(fdt_in, offset, LUO_FDT_FLB_HEADER, &header_size);
- if (!ptr || header_size != sizeof(u64)) {
- pr_err("Unable to get FLB header property '%s' [%d]\n",
- LUO_FDT_FLB_HEADER, header_size);
-
- return -EINVAL;
- }
-
- header_ser_pa = get_unaligned((u64 *)ptr);
- header_ser = phys_to_virt(header_ser_pa);
-
- luo_flb_global.incoming.header_ser = header_ser;
- luo_flb_global.incoming.ser = (void *)(header_ser + 1);
- luo_flb_global.incoming.active = true;
-
- return 0;
}
/**
diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_internal.h
index 875844d7a41d..4b6c18348d9b 100644
--- a/kernel/liveupdate/luo_internal.h
+++ b/kernel/liveupdate/luo_internal.h
@@ -81,8 +81,8 @@ extern struct rw_semaphore luo_register_rwlock;
int luo_session_create(const char *name, struct file **filep);
int luo_session_retrieve(const char *name, struct file **filep);
-int __init luo_session_setup_outgoing(void *fdt);
-int __init luo_session_setup_incoming(void *fdt);
+int __init luo_session_setup_outgoing(u64 *sessions_pa);
+int __init luo_session_setup_incoming(u64 sessions_pa);
int luo_session_serialize(void);
int luo_session_deserialize(void);
@@ -104,8 +104,8 @@ int luo_flb_file_preserve(struct liveupdate_file_handler *fh);
void luo_flb_file_unpreserve(struct liveupdate_file_handler *fh);
void luo_flb_file_finish(struct liveupdate_file_handler *fh);
void luo_flb_unregister_all(struct liveupdate_file_handler *fh);
-int __init luo_flb_setup_outgoing(void *fdt);
-int __init luo_flb_setup_incoming(void *fdt);
+int __init luo_flb_setup_outgoing(u64 *flbs_pa);
+void __init luo_flb_setup_incoming(u64 flbs_pa);
void luo_flb_serialize(void);
#ifdef CONFIG_LIVEUPDATE_TEST
diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c
index 1caaa7886ec6..915ab9ce0f34 100644
--- a/kernel/liveupdate/luo_session.c
+++ b/kernel/liveupdate/luo_session.c
@@ -458,75 +458,34 @@ int luo_session_retrieve(const char *name, struct file **filep)
return err;
}
-int __init luo_session_setup_outgoing(void *fdt_out)
+int __init luo_session_setup_outgoing(u64 *sessions_pa)
{
struct luo_session_header_ser *header_ser;
- u64 header_ser_pa;
- int err;
header_ser = kho_alloc_preserve(LUO_SESSION_PGCNT << PAGE_SHIFT);
if (IS_ERR(header_ser))
return PTR_ERR(header_ser);
- header_ser_pa = virt_to_phys(header_ser);
-
- err = fdt_begin_node(fdt_out, LUO_FDT_SESSION_NODE_NAME);
- err |= fdt_property_string(fdt_out, "compatible",
- LUO_FDT_SESSION_COMPATIBLE);
- err |= fdt_property(fdt_out, LUO_FDT_SESSION_HEADER, &header_ser_pa,
- sizeof(header_ser_pa));
- err |= fdt_end_node(fdt_out);
- if (err)
- goto err_unpreserve;
+ *sessions_pa = virt_to_phys(header_ser);
luo_session_global.outgoing.header_ser = header_ser;
luo_session_global.outgoing.ser = (void *)(header_ser + 1);
luo_session_global.outgoing.active = true;
return 0;
-
-err_unpreserve:
- kho_unpreserve_free(header_ser);
- return err;
}
-int __init luo_session_setup_incoming(void *fdt_in)
+int __init luo_session_setup_incoming(u64 sessions_pa)
{
struct luo_session_header_ser *header_ser;
- int err, header_size, offset;
- u64 header_ser_pa;
- const void *ptr;
-
- offset = fdt_subnode_offset(fdt_in, 0, LUO_FDT_SESSION_NODE_NAME);
- if (offset < 0) {
- pr_err("Unable to get session node: [%s]\n",
- LUO_FDT_SESSION_NODE_NAME);
- return -EINVAL;
- }
- err = fdt_node_check_compatible(fdt_in, offset,
- LUO_FDT_SESSION_COMPATIBLE);
- if (err) {
- pr_err("Session node incompatible [%s]\n",
- LUO_FDT_SESSION_COMPATIBLE);
- return -EINVAL;
- }
-
- header_size = 0;
- ptr = fdt_getprop(fdt_in, offset, LUO_FDT_SESSION_HEADER, &header_size);
- if (!ptr || header_size != sizeof(u64)) {
- pr_err("Unable to get session header '%s' [%d]\n",
- LUO_FDT_SESSION_HEADER, header_size);
- return -EINVAL;
+ if (sessions_pa) {
+ header_ser = phys_to_virt(sessions_pa);
+ luo_session_global.incoming.header_ser = header_ser;
+ luo_session_global.incoming.ser = (void *)(header_ser + 1);
+ luo_session_global.incoming.active = true;
}
- header_ser_pa = get_unaligned((u64 *)ptr);
- header_ser = phys_to_virt(header_ser_pa);
-
- luo_session_global.incoming.header_ser = header_ser;
- luo_session_global.incoming.ser = (void *)(header_ser + 1);
- luo_session_global.incoming.active = true;
-
return 0;
}
--
2.53.0
^ permalink raw reply related
* [PATCH v2 00/10] liveupdate: Remove limits on the number of files and sessions
From: Pasha Tatashin @ 2026-05-14 22:26 UTC (permalink / raw)
To: linux-kselftest, rppt, shuah, akpm, linux-mm, skhan, linux-doc,
linux-kernel, corbet, pasha.tatashin, dmatlack, kexec, pratyush,
skhawaja, graf
This series removes the fixed limits on the number of files that can
be preserved within a single session, and the total number of sessions
managed by the Live Update Orchestrator.
The core of the change is a transition from single contiguous memory
blocks for metadata serialization to a chain of linked blocks. This
allows LUO to scale dynamically.
1. ABI Evolution:
- Introduced linked-block headers for both file and session
serialization.
- Bumped session ABI version to v4.
2. Memory Management & Security:
- Implemented a dynamic block allocation and reuse strategy. Blocks
are allocated only when existing ones are exhausted and are reused
during session/file removal cycles.
- Introduced LUO_MAX_BLOCKS (10000) as a safeguard against stupid
excessive allocations or corrupted cyclic lists during restore.
3. Expanded Selftests:
- Added new kexec-based tests verifying preservation of
2000 sessions and 500 files per session.
- Added self-tests for many sessions and many files management.
Changes v2:
- Addressed all comments from Pratyush
- Consolidated LUO state management from FDT into struct luo_ser.
- Extracted luo_file_deserialize_one and luo_session_deserialize_one
helpers to improve modularity, and later review (Pratyush).
- Added dedicated luo_block.c for unified linked-block serialization
and management.
Pasha Tatashin (10):
liveupdate: centralize state management into struct luo_ser
liveupdate: Extract luo_file_deserialize_one helper
liveupdate: Extract luo_session_deserialize_one helper
liveupdate: add support for linked-block serialization
liveupdate: defer session block allocation and PA setting
liveupdate: Remove limit on the number of sessions
liveupdate: Remove limit on the number of files per session
selftests/liveupdate: Test session and file limit removal
selftests/liveupdate: Add stress-sessions kexec test
selftests/liveupdate: Add stress-files kexec test
Documentation/core-api/liveupdate.rst | 8 +
include/linux/kho/abi/luo.h | 140 +++----
kernel/liveupdate/Makefile | 1 +
kernel/liveupdate/luo_block.c | 388 ++++++++++++++++++
kernel/liveupdate/luo_core.c | 57 ++-
kernel/liveupdate/luo_file.c | 209 +++++-----
kernel/liveupdate/luo_flb.c | 65 +--
kernel/liveupdate/luo_internal.h | 70 +++-
kernel/liveupdate/luo_session.c | 202 ++++-----
tools/testing/selftests/liveupdate/Makefile | 2 +
.../testing/selftests/liveupdate/liveupdate.c | 75 ++++
.../selftests/liveupdate/luo_stress_files.c | 101 +++++
.../liveupdate/luo_stress_sessions.c | 102 +++++
.../selftests/liveupdate/luo_test_utils.c | 24 ++
.../selftests/liveupdate/luo_test_utils.h | 2 +
15 files changed, 1068 insertions(+), 378 deletions(-)
create mode 100644 kernel/liveupdate/luo_block.c
create mode 100644 tools/testing/selftests/liveupdate/luo_stress_files.c
create mode 100644 tools/testing/selftests/liveupdate/luo_stress_sessions.c
base-commit: 34e8f02817e31826e76bb2ded48bf28fe921f20b
--
2.53.0
^ permalink raw reply
* [RESEND/FOLLOW-UP] Documentation/translations/pt_BR patches
From: Daniel Pereira @ 2026-05-14 22:02 UTC (permalink / raw)
To: Jonathan Corbet, linux-doc
Hi Corbet,
I hope you’re doing well.
I’m reaching out to follow up on the status of the patches I submitted
about 12 days ago regarding the Portuguese (pt_BR) documentation
translations.
Please let me know if you’ve had a chance to review them or if there
are any adjustments needed on my end to move forward with the merge.
Thanks for your time and assistance.
Best regards,
Daniel Pereira
^ permalink raw reply
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