Linux Documentation
 help / color / mirror / Atom feed
* [PATCH v2 1/4] dt-bindings: net: remove obsolete mdio.txt
From: Akash Sukhavasi @ 2026-06-03 20:42 UTC (permalink / raw)
  To: Andrew Lunn, Heiner Kallweit, Russell King, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Mauro Carvalho Chehab,
	Vladimir Oltean, Simon Horman, Jonathan Corbet, Shuah Khan,
	Dmitry Torokhov, Thierry Reding, Jonathan Hunter, Lee Jones
  Cc: netdev, devicetree, linux-kernel, linux-media, linux-doc,
	linux-input, linux-tegra, Akash Sukhavasi
In-Reply-To: <20260603-b4-remove-redirect-stubs-v2-0-c8c19876ab64@gmail.com>

mdio.txt has been a single-line redirect to mdio.yaml since
commit 62d77ff7ecbf ("dt-bindings: net: Add a YAML schemas for the
generic MDIO options"), which introduced the .yaml schema and reduced
the .txt to a stub in the same change. The .yaml has the same filename
in the same directory, making this redirect unnecessary for
discoverability.

No files in the tree reference mdio.txt and it has not been touched
since June 2019. Remove the obsolete stub.

Signed-off-by: Akash Sukhavasi <akash.sukhavasi@gmail.com>
---
 Documentation/devicetree/bindings/net/mdio.txt | 1 -
 1 file changed, 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/net/mdio.txt b/Documentation/devicetree/bindings/net/mdio.txt
deleted file mode 100644
index cf8a0105488e..000000000000
--- a/Documentation/devicetree/bindings/net/mdio.txt
+++ /dev/null
@@ -1 +0,0 @@
-This file has moved to mdio.yaml.

-- 
2.54.0


^ permalink raw reply related

* [PATCH v2 0/4] dt-bindings: remove redundant .txt redirect stubs
From: Akash Sukhavasi @ 2026-06-03 20:42 UTC (permalink / raw)
  To: Andrew Lunn, Heiner Kallweit, Russell King, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Mauro Carvalho Chehab,
	Vladimir Oltean, Simon Horman, Jonathan Corbet, Shuah Khan,
	Dmitry Torokhov, Thierry Reding, Jonathan Hunter, Lee Jones
  Cc: netdev, devicetree, linux-kernel, linux-media, linux-doc,
	linux-input, linux-tegra, Akash Sukhavasi

Several .txt files under Documentation/devicetree/bindings/ contain
only a redirect notice pointing to a .yaml schema with the same base
filename in the same directory. These stubs were useful during the
.txt to .yaml transition but are now redundant, since the .yaml is
discoverable by name. Meanwhile, other documentation still references
some of these stubs, forcing readers through an unnecessary extra hop
to reach the actual schema.

This series removes four such stubs and updates all remaining
cross-references to point directly to the .yaml schemas.

Other redirect stubs in the tree were evaluated and intentionally
kept:

 - Stubs pointing to .yaml files with different names (e.g.,
   spi-bus.txt -> spi-controller.yaml) serve as breadcrumbs for
   the renamed schema.

 - Stubs pointing to multiple .yaml files (e.g., nvmem.txt ->
   nvmem.yaml and nvmem-consumer.yaml) convey that the content
   was split.

 - Stubs pointing to .yaml files in a different directory (e.g.,
   reset/st,stm32-rcc.txt -> clock/st,stm32-rcc.yaml) serve as
   cross-directory pointers.

Two additional same-name, same-directory stubs (leds/common.txt,
regulator/regulator.txt) have significantly more cross references
and will be addressed in a follow-up series.

v2:
- Patch 4/4: corrected commit message (eight references in six files, not
  eight files), Sashiko review.
  https://sashiko.dev/#/patchset/20260529052246.4934-1-akash.sukhavasi@gmail.com?part=4

v1: https://lore.kernel.org/all/20260529052246.4934-1-akash.sukhavasi@gmail.com/

Patch 1 supersedes my earlier standalone submission:
https://lore.kernel.org/all/20260523004223.3045-1-akash.sukhavasi@gmail.com/

Signed-off-by: Akash Sukhavasi <akash.sukhavasi@gmail.com>
---
Akash Sukhavasi (4):
      dt-bindings: net: remove obsolete mdio.txt
      dt-bindings: media: remove obsolete rc.txt
      dt-bindings: net: dsa: remove obsolete dsa.txt
      dt-bindings: input: remove obsolete matrix-keymap.txt

 Documentation/devicetree/bindings/input/brcm,bcm-keypad.txt    | 2 +-
 Documentation/devicetree/bindings/input/clps711x-keypad.txt    | 2 +-
 Documentation/devicetree/bindings/input/matrix-keymap.txt      | 1 -
 Documentation/devicetree/bindings/input/nvidia,tegra20-kbc.txt | 2 +-
 Documentation/devicetree/bindings/input/pxa27x-keypad.txt      | 2 +-
 Documentation/devicetree/bindings/input/st-keyscan.txt         | 2 +-
 Documentation/devicetree/bindings/media/hix5hd2-ir.txt         | 2 +-
 Documentation/devicetree/bindings/media/rc.txt                 | 1 -
 Documentation/devicetree/bindings/mfd/tc3589x.txt              | 6 +++---
 Documentation/devicetree/bindings/net/dsa/dsa.txt              | 4 ----
 Documentation/devicetree/bindings/net/dsa/lan9303.txt          | 2 +-
 Documentation/devicetree/bindings/net/mdio.txt                 | 1 -
 Documentation/networking/dsa/dsa.rst                           | 2 +-
 13 files changed, 11 insertions(+), 18 deletions(-)
---
base-commit: b7bee4ca5688e30ca50fbc87b1b8f7eed7006c17
change-id: 20260603-b4-remove-redirect-stubs-899afc8fa7d6

Best regards,
-- 
Akash Sukhavasi <akash.sukhavasi@gmail.com>


^ permalink raw reply

* Re: [PATCH v2 3/6] alloc_tag: add size-based filtering to ioctl
From: Suren Baghdasaryan @ 2026-06-03 20:40 UTC (permalink / raw)
  To: Hao Ge
  Cc: Abhishek Bapat, Andrew Morton, Kent Overstreet, Shuah Khan,
	Jonathan Corbet, linux-doc, linux-kernel, linux-mm, Sourav Panda
In-Reply-To: <de0f2984-44ea-4098-9d19-c63ee035cdaf@linux.dev>

On Mon, May 25, 2026 at 8:12 PM Hao Ge <hao.ge@linux.dev> wrote:
>
> Hi Abhishek
>
>
> On 2026/5/23 01:45, Abhishek Bapat wrote:
> > Extend the allocinfo filtering mechanism to allow users to filter tags
> > based on the total number of bytes allocated [min_size, max_size]. The
> > size range is inclusive.
> >
> > Filtering by size involves retrieving allocinfo per-CPU counters, which
> > is an expensive operation. Hence, the performance of size-based
> > filtering will be worse than other filters.
> >
> > Signed-off-by: Abhishek Bapat <abhishekbapat@google.com>
> > ---
> >   include/uapi/linux/alloc_tag.h |  8 +++-
> >   lib/alloc_tag.c                | 72 ++++++++++++++++++++++++++++------
> >   2 files changed, 68 insertions(+), 12 deletions(-)
> >
> > diff --git a/include/uapi/linux/alloc_tag.h b/include/uapi/linux/alloc_tag.h
> > index 0cc9db5298c6..45f158bee0a6 100644
> > --- a/include/uapi/linux/alloc_tag.h
> > +++ b/include/uapi/linux/alloc_tag.h
> > @@ -39,13 +39,17 @@ enum {
> >       ALLOCINFO_FILTER_FUNCTION,
> >       ALLOCINFO_FILTER_FILENAME,
> >       ALLOCINFO_FILTER_LINENO,
> > -     __ALLOCINFO_FILTER_LAST = ALLOCINFO_FILTER_LINENO
> > +     ALLOCINFO_FILTER_MIN_SIZE,
> > +     ALLOCINFO_FILTER_MAX_SIZE,
> > +     __ALLOCINFO_FILTER_LAST = ALLOCINFO_FILTER_MAX_SIZE
> >   };
> >
> >   #define ALLOCINFO_FILTER_MASK_MODNAME               (1 << ALLOCINFO_FILTER_MODNAME)
> >   #define ALLOCINFO_FILTER_MASK_FUNCTION              (1 << ALLOCINFO_FILTER_FUNCTION)
> >   #define ALLOCINFO_FILTER_MASK_FILENAME              (1 << ALLOCINFO_FILTER_FILENAME)
> >   #define ALLOCINFO_FILTER_MASK_LINENO                (1 << ALLOCINFO_FILTER_LINENO)
> > +#define ALLOCINFO_FILTER_MASK_MIN_SIZE               (1 << ALLOCINFO_FILTER_MIN_SIZE)
> > +#define ALLOCINFO_FILTER_MASK_MAX_SIZE               (1 << ALLOCINFO_FILTER_MAX_SIZE)
> >
> >   #define ALLOCINFO_FILTER_MASKS \
> >       ((1 << (__ALLOCINFO_FILTER_LAST + 1)) - 1)
> > @@ -53,6 +57,8 @@ enum {
> >   struct allocinfo_filter {
> >       __u64 mask; /* bitmask of the filter fields used */
> >       struct allocinfo_tag fields;
> > +     __u64 min_size;
> > +     __u64 max_size;
> >   };
> >
> >   struct allocinfo_get_at {
> > diff --git a/lib/alloc_tag.c b/lib/alloc_tag.c
> > index 56c394ef721f..6c8743eead2d 100644
> > --- a/lib/alloc_tag.c
> > +++ b/lib/alloc_tag.c
> > @@ -173,11 +173,21 @@ static int allocinfo_cmp_str(const char *str, const char *template)
> >       return strncmp(allocinfo_str(str), template, ALLOCINFO_STR_SIZE);
> >   }
> >
> > +static inline struct alloc_tag_counters allocinfo_prefetch_counters(struct codetag *ct)
> > +{
> > +     return alloc_tag_read(ct_to_alloc_tag(ct));
> > +}
> > +
> >   static void allocinfo_to_params(struct codetag *ct,
> > -                             struct allocinfo_tag_data *data)
> > +                             struct allocinfo_tag_data *data,
> > +                             struct alloc_tag_counters *counters)
> >   {
> > -     struct alloc_tag *tag = ct_to_alloc_tag(ct);
> > -     struct alloc_tag_counters counter = alloc_tag_read(tag);
> > +     struct alloc_tag_counters local_counters;
> > +
> > +     if (!counters) {
> > +             local_counters = allocinfo_prefetch_counters(ct);
> > +             counters = &local_counters;
> > +     }
> >
> >       if (ct->modname)
> >               allocinfo_copy_str(data->tag.modname, ct->modname);
> > @@ -186,9 +196,9 @@ static void allocinfo_to_params(struct codetag *ct,
> >       allocinfo_copy_str(data->tag.function, ct->function);
> >       allocinfo_copy_str(data->tag.filename, ct->filename);
> >       data->tag.lineno = ct->lineno;
> > -     data->counter.bytes = counter.bytes;
> > -     data->counter.calls = counter.calls;
> > -     data->counter.accurate = !alloc_tag_is_inaccurate(tag);
> > +     data->counter.bytes = counters->bytes;
> > +     data->counter.calls = counters->calls;
> > +     data->counter.accurate = !alloc_tag_is_inaccurate(ct_to_alloc_tag(ct));
> >   }
> >
> >   static int allocinfo_ioctl_get_content_id(struct seq_file *m, void __user *arg)
> > @@ -204,7 +214,8 @@ static int allocinfo_ioctl_get_content_id(struct seq_file *m, void __user *arg)
> >       return 0;
> >   }
> >
> > -static bool matches_filter(struct codetag *ct, struct allocinfo_filter *filter)
> > +static bool matches_filter(struct codetag *ct, struct allocinfo_filter *filter,
> > +                        struct alloc_tag_counters *counters)
> >   {
> >       if (!filter || !filter->mask)
> >               return true;
> > @@ -228,6 +239,17 @@ static bool matches_filter(struct codetag *ct, struct allocinfo_filter *filter)
> >           ct->lineno != filter->fields.lineno)
> >               return false;
> >
> > +     if ((filter->mask & ALLOCINFO_FILTER_MASK_MIN_SIZE) ||
> > +         (filter->mask & ALLOCINFO_FILTER_MASK_MAX_SIZE)) {
> > +             /* We assume counters is not NULL here as per caller logic */
> > +             if ((filter->mask & ALLOCINFO_FILTER_MASK_MIN_SIZE) &&
> > +                 counters->bytes < filter->min_size)
> > +                     return false;
> > +             if ((filter->mask & ALLOCINFO_FILTER_MASK_MAX_SIZE) &&
> > +                 counters->bytes > filter->max_size)
> > +                     return false;
> > +     }
> > +
> >       return true;
> >   }
> >
> > @@ -237,6 +259,9 @@ static int allocinfo_ioctl_get_at(struct seq_file *m, void __user *arg)
> >       struct codetag *ct;
> >       struct allocinfo_get_at params = {0};
> >       __u64 skip_count;
> > +     bool sizes_set;
> > +     struct alloc_tag_counters counters;
> > +     struct alloc_tag_counters *counters_ptr = NULL;
> >
> >       if (copy_from_user(&params, arg, sizeof(params)))
> >               return -EFAULT;
> > @@ -244,9 +269,16 @@ static int allocinfo_ioctl_get_at(struct seq_file *m, void __user *arg)
> >       if (params.filter.mask & ~ALLOCINFO_FILTER_MASKS)
> >               return -EINVAL;
> >
> > +     if ((params.filter.mask & ALLOCINFO_FILTER_MASK_MIN_SIZE) &&
> > +         (params.filter.mask & ALLOCINFO_FILTER_MASK_MAX_SIZE) &&
> > +         params.filter.min_size > params.filter.max_size)
> > +             return -EINVAL;
> > +
> >       priv = (struct allocinfo_private *)m->private;
> >
> >       skip_count = params.pos;
> > +     sizes_set = (params.filter.mask &
> > +                  (ALLOCINFO_FILTER_MASK_MIN_SIZE | ALLOCINFO_FILTER_MASK_MAX_SIZE));
> >
> >       mutex_lock(&priv->ioctl_lock);
> >       codetag_lock_module_list(alloc_tag_cttype, true);
> > @@ -261,7 +293,11 @@ static int allocinfo_ioctl_get_at(struct seq_file *m, void __user *arg)
> >       ct = codetag_next_ct(&priv->ioctl_iter);
> >
> >       while (ct) {
> > -             if (matches_filter(ct, &priv->filter)) {
> > +             if (sizes_set) {
> > +                     counters = allocinfo_prefetch_counters(ct);
> > +                     counters_ptr = &counters;
> > +             }
> > +             if (matches_filter(ct, &priv->filter, counters_ptr)) {
>
> alloc_tag_read() walks all per-CPU counters which is not cheap, but here
>
> it's called for every codetag unconditionally when sizes_set is true,
>
> even when the tag would be rejected by modname/function/filename checks
>
> that are plain string comparisons.
>
> For example, say the user filters with MODNAME | MIN_SIZE on a system
>
> with 10000 tags, 100 of which belong to the target module. Today the
>
> code would call alloc_tag_read() 10000 times (once per tag), but only
>
> 100 of those tags pass the modname check — the other 9900 per-CPU walks
>
> are wasted.
>
> Would it make sense to split the filter check so that per-CPU counter reads
>
> only happen after tag-based checks pass? Something like:
>
> static bool allocinfo_match_tag(struct codetag *ct,
>
>                          struct allocinfo_filter *filter) { ... }
>
> static bool allocinfo_match_size(struct alloc_tag_counters *counters,
>
>                                     struct allocinfo_filter *filter) { ... }
>
> And in the caller:
>
> bool match = allocinfo_match_tag(ct, &priv->filter);
>
> /* Add comments to help subsequent developers understand the purpose of
> this modification. */
>
> if (match && sizes_set) {
>
>             counters = allocinfo_prefetch_counters(ct);
>
>             counters_ptr = &counters;
>
>              match = allocinfo_match_size(counters_ptr, &priv->filter);
>
> }
>
> You may find a more elegant approach to resolve this issue.

That's a good point. The counters should be fetched only after all
other filters have passed their checks. Otherwise you lose most of the
performance benefits.

>
> Thanks
>
> Best Regards
>
> Hao
>
> >                       if (skip_count == 0)
> >                               break;
> >                       skip_count--;
> > @@ -270,7 +306,7 @@ static int allocinfo_ioctl_get_at(struct seq_file *m, void __user *arg)
> >       }
> >
> >       if (ct) {
> > -             allocinfo_to_params(ct, &params.data);
> > +             allocinfo_to_params(ct, &params.data, counters_ptr);
> >               priv->positioned = true;
> >       }
> >
> > @@ -292,9 +328,15 @@ static int allocinfo_ioctl_get_next(struct seq_file *m, void __user *arg)
> >       struct codetag *ct;
> >       struct allocinfo_tag_data params = {0};
> >       int ret = 0;
> > +     bool sizes_set;
> > +     struct alloc_tag_counters counters;
> > +     struct alloc_tag_counters *counters_ptr = NULL;
> >
> >       priv = (struct allocinfo_private *)m->private;
> >
> > +     sizes_set = (priv->filter.mask &
> > +                  (ALLOCINFO_FILTER_MASK_MIN_SIZE | ALLOCINFO_FILTER_MASK_MAX_SIZE));
> > +
> >       mutex_lock(&priv->ioctl_lock);
> >       codetag_lock_module_list(alloc_tag_cttype, true);
> >
> > @@ -304,10 +346,18 @@ static int allocinfo_ioctl_get_next(struct seq_file *m, void __user *arg)
> >       }
> >
> >       ct = codetag_next_ct(&priv->ioctl_iter);
> > -     while (ct && !matches_filter(ct, &priv->filter))
> > +     while (ct) {
> > +             if (sizes_set) {
> > +                     counters = allocinfo_prefetch_counters(ct);
> > +                     counters_ptr = &counters;
> > +             }
> > +             if (matches_filter(ct, &priv->filter, counters_ptr))
> > +                     break;
> >               ct = codetag_next_ct(&priv->ioctl_iter);
> > +     }
> > +
> >       if (ct)
> > -             allocinfo_to_params(ct, &params);
> > +             allocinfo_to_params(ct, &params, counters_ptr);
> >
> >       if (!ct) {
> >               priv->positioned = false;

^ permalink raw reply

* Re: [PATCH v3 01/19] perf cs-etm: Queue context packets for frontend
From: Amir Ayupov @ 2026-06-03 20:14 UTC (permalink / raw)
  To: James Clark
  Cc: Suzuki K Poulose, Mike Leach, Leo Yan, Arnaldo Carvalho de Melo,
	Namhyung Kim, Jiri Olsa, Ian Rogers, Jonathan Corbet, Shuah Khan,
	Paschalis Mpeis, coresight, linux-perf-users, linux-kernel,
	Arnaldo Carvalho de Melo, linux-doc
In-Reply-To: <20260603-james-cs-context-tracking-fix-v3-1-c392945d9ed5@linaro.org>

Hi James,

I tested the v2 patch series and it looks good. Compared to my
stamping pid/tid on each packet, there was a minor difference in 2/39
tested perf data files: the number of brstack samples differs by one,
however, there was no loss of binary profile. The resulting BOLT
profile converted from the perf script output was identical, so I'm OK
with v2 patch as-is.

-Amir



On Wed, Jun 3, 2026 at 3:17 AM James Clark <james.clark@linaro.org> wrote:
>
> >
> PE_CONTEXT elements update the context ID and exception level, but the
> decoder may still have prior packets cached for frontend processing.
> Updating the context immediately in the decoder backend can make those
> cached packets get consumed with the wrong thread or EL state.
>
> Add a CS_ETM_CONTEXT packet carrying the TID and EL to the frontend,
> this keeps context changes ordered with the rest of the packet stream
> and avoids mismatches when synthesizing samples from cached packets.
>
> Separate the memory access function into one for the frontend and one
> for decoding. The frontend also needs memory access to attach the
> instruction to samples. Because the frontend does memory access for
> both previous and current packets, change all the frontend memory access
> function signatures to take both a tidq and packet. But backend always
> uses the current backend EL and thread from the tidq.
>
> Treat context packets as a boundary for branch sample generation and
> remove tidq->prev_packet_thread because it's not possible to branch to a
> different thread, so only tracking the current thread is required for
> sample generation.
>
> Fixes: e573e978fb12 ("perf cs-etm: Inject capabilitity for CoreSight traces")
> Reported-by: Amir Ayupov <aaupov@meta.com>
> Closes: https://lore.kernel.org/linux-perf-users/20260515021135.1729028-1-aaupov@meta.com/
> Co-authored-by: James Clark <james.clark@linaro.org>
> Signed-off-by: Leo Yan <leo.yan@arm.com>
> Signed-off-by: James Clark <james.clark@linaro.org>
> ---
>  tools/perf/util/cs-etm-decoder/cs-etm-decoder.c |  21 ++-
>  tools/perf/util/cs-etm.c                        | 234 ++++++++++++++----------
>  tools/perf/util/cs-etm.h                        |   8 +-
>  3 files changed, 162 insertions(+), 101 deletions(-)
>
> diff --git a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
> index dee3020ceaa9..26940f1f1b0b 100644
> --- a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
> +++ b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
> @@ -402,6 +402,8 @@ cs_etm_decoder__buffer_packet(struct cs_etm_queue *etmq,
>         packet_queue->packet_buffer[et].flags = 0;
>         packet_queue->packet_buffer[et].exception_number = UINT32_MAX;
>         packet_queue->packet_buffer[et].trace_chan_id = trace_chan_id;
> +       packet_queue->packet_buffer[et].el = ocsd_EL_unknown;
> +       packet_queue->packet_buffer[et].tid = -1;
>
>         if (packet_queue->packet_count == CS_ETM_PACKET_MAX_BUFFER - 1)
>                 return OCSD_RESP_WAIT;
> @@ -449,6 +451,7 @@ cs_etm_decoder__buffer_range(struct cs_etm_queue *etmq,
>         packet->last_instr_type = elem->last_i_type;
>         packet->last_instr_subtype = elem->last_i_subtype;
>         packet->last_instr_cond = elem->last_instr_cond;
> +       packet->el = elem->context.exception_level;
>
>         if (elem->last_i_type == OCSD_INSTR_BR || elem->last_i_type == OCSD_INSTR_BR_INDIRECT)
>                 packet->last_instr_taken_branch = elem->last_instr_exec;
> @@ -525,7 +528,9 @@ cs_etm_decoder__set_tid(struct cs_etm_queue *etmq,
>                         const ocsd_generic_trace_elem *elem,
>                         const uint8_t trace_chan_id)
>  {
> +       struct cs_etm_packet *packet;
>         pid_t tid = -1;
> +       int ret;
>
>         /*
>          * Process the PE_CONTEXT packets if we have a valid contextID or VMID.
> @@ -546,12 +551,18 @@ cs_etm_decoder__set_tid(struct cs_etm_queue *etmq,
>                 break;
>         }
>
> -       if (cs_etm__etmq_set_tid_el(etmq, tid, trace_chan_id,
> -                                   elem->context.exception_level))
> +       if (cs_etm__etmq_update_decode_context(etmq, trace_chan_id,
> +                               elem->context.exception_level, tid))
>                 return OCSD_RESP_FATAL_SYS_ERR;
>
> -       if (tid == -1)
> -               return OCSD_RESP_CONT;
> +       ret = cs_etm_decoder__buffer_packet(etmq, packet_queue, trace_chan_id,
> +                                           CS_ETM_CONTEXT);
> +       if (ret != OCSD_RESP_CONT && ret != OCSD_RESP_WAIT)
> +               return ret;
> +
> +       packet = &packet_queue->packet_buffer[packet_queue->tail];
> +       packet->tid = tid;
> +       packet->el = elem->context.exception_level;
>
>         /*
>          * A timestamp is generated after a PE_CONTEXT element so make sure
> @@ -559,7 +570,7 @@ cs_etm_decoder__set_tid(struct cs_etm_queue *etmq,
>          */
>         cs_etm_decoder__reset_timestamp(packet_queue);
>
> -       return OCSD_RESP_CONT;
> +       return ret;
>  }
>
>  static ocsd_datapath_resp_t cs_etm_decoder__gen_trace_elem_printer(
> diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
> index 40c6ddfa8c8d..ce570913669c 100644
> --- a/tools/perf/util/cs-etm.c
> +++ b/tools/perf/util/cs-etm.c
> @@ -85,15 +85,22 @@ struct cs_etm_traceid_queue {
>         u64 period_instructions;
>         size_t last_branch_pos;
>         union perf_event *event_buf;
> -       struct thread *thread;
> -       struct thread *prev_packet_thread;
> -       ocsd_ex_level prev_packet_el;
> -       ocsd_ex_level el;
>         struct branch_stack *last_branch;
>         struct branch_stack *last_branch_rb;
>         struct cs_etm_packet *prev_packet;
>         struct cs_etm_packet *packet;
>         struct cs_etm_packet_queue packet_queue;
> +
> +       struct thread *decode_thread;
> +       ocsd_ex_level decode_el;
> +
> +       /*
> +        * The frontend accesses the EL from '[prev_]packet' because it needs
> +        * previous EL for branch and current EL for instruction samples. It's
> +        * not possible to change thread in a single branch sample so no need to
> +        * store or access the thread through the packet.
> +        */
> +       struct thread *frontend_thread;
>  };
>
>  enum cs_etm_format {
> @@ -614,10 +621,11 @@ static int cs_etm__init_traceid_queue(struct cs_etm_queue *etmq,
>
>         queue = &etmq->etm->queues.queue_array[etmq->queue_nr];
>         tidq->trace_chan_id = trace_chan_id;
> -       tidq->el = tidq->prev_packet_el = ocsd_EL_unknown;
> -       tidq->thread = machine__findnew_thread(&etm->session->machines.host, -1,
> +       tidq->decode_el = ocsd_EL_unknown;
> +       tidq->frontend_thread = machine__findnew_thread(&etm->session->machines.host, -1,
> +                                              queue->tid);
> +       tidq->decode_thread = machine__findnew_thread(&etm->session->machines.host, -1,
>                                                queue->tid);
> -       tidq->prev_packet_thread = machine__idle_thread(&etm->session->machines.host);
>
>         tidq->packet = zalloc(sizeof(struct cs_etm_packet));
>         if (!tidq->packet)
> @@ -750,21 +758,10 @@ static void cs_etm__packet_swap(struct cs_etm_auxtrace *etm,
>                 /*
>                  * Swap PACKET with PREV_PACKET: PACKET becomes PREV_PACKET for
>                  * the next incoming packet.
> -                *
> -                * Threads and exception levels are also tracked for both the
> -                * previous and current packets. This is because the previous
> -                * packet is used for the 'from' IP for branch samples, so the
> -                * thread at that time must also be assigned to that sample.
> -                * Across discontinuity packets the thread can change, so by
> -                * tracking the thread for the previous packet the branch sample
> -                * will have the correct info.
>                  */
>                 tmp = tidq->packet;
>                 tidq->packet = tidq->prev_packet;
>                 tidq->prev_packet = tmp;
> -               tidq->prev_packet_el = tidq->el;
> -               thread__put(tidq->prev_packet_thread);
> -               tidq->prev_packet_thread = thread__get(tidq->thread);
>         }
>  }
>
> @@ -937,8 +934,8 @@ static void cs_etm__free_traceid_queues(struct cs_etm_queue *etmq)
>
>                 /* Free this traceid_queue from the array */
>                 tidq = etmq->traceid_queues[idx];
> -               thread__zput(tidq->thread);
> -               thread__zput(tidq->prev_packet_thread);
> +               thread__zput(tidq->frontend_thread);
> +               thread__zput(tidq->decode_thread);
>                 zfree(&tidq->event_buf);
>                 zfree(&tidq->last_branch);
>                 zfree(&tidq->last_branch_rb);
> @@ -1083,47 +1080,43 @@ static u8 cs_etm__cpu_mode(struct cs_etm_queue *etmq, u64 address,
>         }
>  }
>
> -static u32 cs_etm__mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id,
> -                             u64 address, size_t size, u8 *buffer,
> -                             const ocsd_mem_space_acc_t mem_space)
> +static u32 __cs_etm__mem_access(struct cs_etm_queue *etmq,
> +                               u64 address, size_t size, u8 *buffer,
> +                               const ocsd_mem_space_acc_t mem_space,
> +                               ocsd_ex_level el, struct thread *thread)
>  {
>         u8  cpumode;
>         u64 offset;
>         int len;
>         struct addr_location al;
>         struct dso *dso;
> -       struct cs_etm_traceid_queue *tidq;
>         int ret = 0;
>
>         if (!etmq)
>                 return 0;
>
>         addr_location__init(&al);
> -       tidq = cs_etm__etmq_get_traceid_queue(etmq, trace_chan_id);
> -       if (!tidq)
> -               goto out;
>
>         /*
> -        * We've already tracked EL along side the PID in cs_etm__set_thread()
> -        * so double check that it matches what OpenCSD thinks as well. It
> -        * doesn't distinguish between EL0 and EL1 for this mem access callback
> -        * so we had to do the extra tracking. Skip validation if it's any of
> -        * the 'any' values.
> +        * We track EL for the frontend and the backend when receiving context
> +        * and range packets. OpenCSD doesn't distinguish between EL0 and EL1
> +        * for this mem access callback so we had to do the extra tracking. Skip
> +        * validation if it's any of the 'any' values.
>          */
>         if (!(mem_space == OCSD_MEM_SPACE_ANY ||
>               mem_space == OCSD_MEM_SPACE_N || mem_space == OCSD_MEM_SPACE_S)) {
>                 if (mem_space & OCSD_MEM_SPACE_EL1N) {
>                         /* Includes both non secure EL1 and EL0 */
> -                       assert(tidq->el == ocsd_EL1 || tidq->el == ocsd_EL0);
> +                       assert(el == ocsd_EL1 || el == ocsd_EL0);
>                 } else if (mem_space & OCSD_MEM_SPACE_EL2)
> -                       assert(tidq->el == ocsd_EL2);
> +                       assert(el == ocsd_EL2);
>                 else if (mem_space & OCSD_MEM_SPACE_EL3)
> -                       assert(tidq->el == ocsd_EL3);
> +                       assert(el == ocsd_EL3);
>         }
>
> -       cpumode = cs_etm__cpu_mode(etmq, address, tidq->el);
> +       cpumode = cs_etm__cpu_mode(etmq, address, el);
>
> -       if (!thread__find_map(tidq->thread, cpumode, address, &al))
> +       if (!thread__find_map(thread, cpumode, address, &al))
>                 goto out;
>
>         dso = map__dso(al.map);
> @@ -1138,7 +1131,7 @@ static u32 cs_etm__mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id,
>
>         map__load(al.map);
>
> -       len = dso__data_read_offset(dso, maps__machine(thread__maps(tidq->thread)),
> +       len = dso__data_read_offset(dso, maps__machine(thread__maps(thread)),
>                                     offset, buffer, size);
>
>         if (len <= 0) {
> @@ -1158,6 +1151,30 @@ static u32 cs_etm__mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id,
>         return ret;
>  }
>
> +static u32 cs_etm__frontend_mem_access(struct cs_etm_queue *etmq,
> +                                      struct cs_etm_traceid_queue *tidq,
> +                                      struct cs_etm_packet *packet,
> +                                      u64 address, size_t size, u8 *buffer)
> +{
> +       return __cs_etm__mem_access(etmq, address, size, buffer, 0, packet->el,
> +                                   tidq->frontend_thread);
> +}
> +
> +static u32 cs_etm__decoder_mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id,
> +                                     u64 address, size_t size, u8 *buffer,
> +                                     const ocsd_mem_space_acc_t mem_space)
> +{
> +       struct cs_etm_traceid_queue *tidq;
> +
> +       tidq = cs_etm__etmq_get_traceid_queue(etmq, trace_chan_id);
> +       if (!tidq)
> +               return 0;
> +
> +       return __cs_etm__mem_access(etmq, address, size, buffer,
> +                                   mem_space, tidq->decode_el,
> +                                   tidq->decode_thread);
> +}
> +
>  static struct cs_etm_queue *cs_etm__alloc_queue(void)
>  {
>         struct cs_etm_queue *etmq = zalloc(sizeof(*etmq));
> @@ -1333,12 +1350,13 @@ void cs_etm__reset_last_branch_rb(struct cs_etm_traceid_queue *tidq)
>  }
>
>  static inline int cs_etm__t32_instr_size(struct cs_etm_queue *etmq,
> -                                        u8 trace_chan_id, u64 addr)
> +                                        struct cs_etm_traceid_queue *tidq,
> +                                        struct cs_etm_packet *packet, u64 addr)
>  {
>         u8 instrBytes[2];
>
> -       cs_etm__mem_access(etmq, trace_chan_id, addr, ARRAY_SIZE(instrBytes),
> -                          instrBytes, 0);
> +       cs_etm__frontend_mem_access(etmq, tidq, packet, addr,
> +                                   ARRAY_SIZE(instrBytes), instrBytes);
>         /*
>          * T32 instruction size is indicated by bits[15:11] of the first
>          * 16-bit word of the instruction: 0b11101, 0b11110 and 0b11111
> @@ -1371,16 +1389,16 @@ u64 cs_etm__last_executed_instr(const struct cs_etm_packet *packet)
>  }
>
>  static inline u64 cs_etm__instr_addr(struct cs_etm_queue *etmq,
> -                                    u64 trace_chan_id,
> -                                    const struct cs_etm_packet *packet,
> +                                    struct cs_etm_traceid_queue *tidq,
> +                                    struct cs_etm_packet *packet,
>                                      u64 offset)
>  {
>         if (packet->isa == CS_ETM_ISA_T32) {
>                 u64 addr = packet->start_addr;
>
>                 while (offset) {
> -                       addr += cs_etm__t32_instr_size(etmq,
> -                                                      trace_chan_id, addr);
> +                       addr += cs_etm__t32_instr_size(etmq, tidq, packet,
> +                                                      addr);
>                         offset--;
>                 }
>                 return addr;
> @@ -1490,34 +1508,51 @@ cs_etm__get_trace(struct cs_etm_queue *etmq)
>         return etmq->buf_len;
>  }
>
> -static void cs_etm__set_thread(struct cs_etm_queue *etmq,
> -                              struct cs_etm_traceid_queue *tidq, pid_t tid,
> -                              ocsd_ex_level el)
> +/*
> + * Convert a raw thread number to a thread struct and assign it to **thread.
> + */
> +static int cs_etm__etmq_update_thread(struct cs_etm_queue *etmq,
> +                                     ocsd_ex_level el, pid_t tid,
> +                                     struct thread **thread)
>  {
>         struct machine *machine = cs_etm__get_machine(etmq, el);
>
> +       if (!machine || !*thread)
> +               return -EINVAL;
> +
>         if (tid != -1) {
> -               thread__zput(tidq->thread);
> -               tidq->thread = machine__find_thread(machine, -1, tid);
> +               thread__zput(*thread);
> +               *thread = machine__find_thread(machine, -1, tid);
>         }
>
>         /* Couldn't find a known thread */
> -       if (!tidq->thread)
> -               tidq->thread = machine__idle_thread(machine);
> +       if (!*thread)
> +               *thread = machine__idle_thread(machine);
>
> -       tidq->el = el;
> +       return 0;
>  }
>
> -int cs_etm__etmq_set_tid_el(struct cs_etm_queue *etmq, pid_t tid,
> -                           u8 trace_chan_id, ocsd_ex_level el)
> +/*
> + * Set the thread and EL of the decode context which is ahead in time of the
> + * frontend context.
> + */
> +int cs_etm__etmq_update_decode_context(struct cs_etm_queue *etmq,
> +                                      u8 trace_chan_id,
> +                                      ocsd_ex_level el, pid_t tid)
>  {
>         struct cs_etm_traceid_queue *tidq;
> +       int ret;
>
>         tidq = cs_etm__etmq_get_traceid_queue(etmq, trace_chan_id);
>         if (!tidq)
>                 return -EINVAL;
>
> -       cs_etm__set_thread(etmq, tidq, tid, el);
> +       ret = cs_etm__etmq_update_thread(etmq, el, tid,
> +                                        &tidq->decode_thread);
> +       if (ret)
> +               return ret;
> +
> +       tidq->decode_el = el;
>         return 0;
>  }
>
> @@ -1527,8 +1562,8 @@ bool cs_etm__etmq_is_timeless(struct cs_etm_queue *etmq)
>  }
>
>  static void cs_etm__copy_insn(struct cs_etm_queue *etmq,
> -                             u64 trace_chan_id,
> -                             const struct cs_etm_packet *packet,
> +                             struct cs_etm_traceid_queue *tidq,
> +                             struct cs_etm_packet *packet,
>                               struct perf_sample *sample)
>  {
>         /*
> @@ -1545,14 +1580,14 @@ static void cs_etm__copy_insn(struct cs_etm_queue *etmq,
>          * cs_etm__t32_instr_size().
>          */
>         if (packet->isa == CS_ETM_ISA_T32)
> -               sample->insn_len = cs_etm__t32_instr_size(etmq, trace_chan_id,
> +               sample->insn_len = cs_etm__t32_instr_size(etmq, tidq, packet,
>                                                           sample->ip);
>         /* Otherwise, A64 and A32 instruction size are always 32-bit. */
>         else
>                 sample->insn_len = 4;
>
> -       cs_etm__mem_access(etmq, trace_chan_id, sample->ip, sample->insn_len,
> -                          (void *)sample->insn, 0);
> +       cs_etm__frontend_mem_access(etmq, tidq, packet, sample->ip,
> +                                   sample->insn_len, (void *)sample->insn);
>  }
>
>  u64 cs_etm__convert_sample_time(struct cs_etm_queue *etmq, u64 cs_timestamp)
> @@ -1579,6 +1614,7 @@ static inline u64 cs_etm__resolve_sample_time(struct cs_etm_queue *etmq,
>
>  static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq,
>                                             struct cs_etm_traceid_queue *tidq,
> +                                           struct cs_etm_packet *packet,
>                                             u64 addr, u64 period)
>  {
>         int ret = 0;
> @@ -1588,15 +1624,15 @@ static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq,
>
>         perf_sample__init(&sample, /*all=*/true);
>         event->sample.header.type = PERF_RECORD_SAMPLE;
> -       event->sample.header.misc = cs_etm__cpu_mode(etmq, addr, tidq->el);
> +       event->sample.header.misc = cs_etm__cpu_mode(etmq, addr, packet->el);
>         event->sample.header.size = sizeof(struct perf_event_header);
>
>         /* Set time field based on etm auxtrace config. */
>         sample.time = cs_etm__resolve_sample_time(etmq, tidq);
>
>         sample.ip = addr;
> -       sample.pid = thread__pid(tidq->thread);
> -       sample.tid = thread__tid(tidq->thread);
> +       sample.pid = thread__pid(tidq->frontend_thread);
> +       sample.tid = thread__tid(tidq->frontend_thread);
>         sample.id = etmq->etm->instructions_id;
>         sample.stream_id = etmq->etm->instructions_id;
>         sample.period = period;
> @@ -1604,7 +1640,7 @@ static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq,
>         sample.flags = tidq->prev_packet->flags;
>         sample.cpumode = event->sample.header.misc;
>
> -       cs_etm__copy_insn(etmq, tidq->trace_chan_id, tidq->packet, &sample);
> +       cs_etm__copy_insn(etmq, tidq, tidq->packet, &sample);
>
>         if (etm->synth_opts.last_branch)
>                 sample.branch_stack = tidq->last_branch;
> @@ -1649,15 +1685,15 @@ static int cs_etm__synth_branch_sample(struct cs_etm_queue *etmq,
>
>         event->sample.header.type = PERF_RECORD_SAMPLE;
>         event->sample.header.misc = cs_etm__cpu_mode(etmq, ip,
> -                                                    tidq->prev_packet_el);
> +                                                    tidq->prev_packet->el);
>         event->sample.header.size = sizeof(struct perf_event_header);
>
>         /* Set time field based on etm auxtrace config. */
>         sample.time = cs_etm__resolve_sample_time(etmq, tidq);
>
>         sample.ip = ip;
> -       sample.pid = thread__pid(tidq->prev_packet_thread);
> -       sample.tid = thread__tid(tidq->prev_packet_thread);
> +       sample.pid = thread__pid(tidq->frontend_thread);
> +       sample.tid = thread__tid(tidq->frontend_thread);
>         sample.addr = cs_etm__first_executed_instr(tidq->packet);
>         sample.id = etmq->etm->branches_id;
>         sample.stream_id = etmq->etm->branches_id;
> @@ -1666,8 +1702,7 @@ static int cs_etm__synth_branch_sample(struct cs_etm_queue *etmq,
>         sample.flags = tidq->prev_packet->flags;
>         sample.cpumode = event->sample.header.misc;
>
> -       cs_etm__copy_insn(etmq, tidq->trace_chan_id, tidq->prev_packet,
> -                         &sample);
> +       cs_etm__copy_insn(etmq, tidq, tidq->prev_packet, &sample);
>
>         /*
>          * perf report cannot handle events without a branch stack
> @@ -1788,7 +1823,6 @@ static int cs_etm__sample(struct cs_etm_queue *etmq,
>  {
>         struct cs_etm_auxtrace *etm = etmq->etm;
>         int ret;
> -       u8 trace_chan_id = tidq->trace_chan_id;
>         u64 instrs_prev;
>
>         /* Get instructions remainder from previous packet */
> @@ -1874,10 +1908,10 @@ static int cs_etm__sample(struct cs_etm_queue *etmq,
>                          * been executed, but PC has not advanced to next
>                          * instruction)
>                          */
> -                       addr = cs_etm__instr_addr(etmq, trace_chan_id,
> -                                                 tidq->packet, offset - 1);
> +                       addr = cs_etm__instr_addr(etmq, tidq, tidq->packet,
> +                                                 offset - 1);
>                         ret = cs_etm__synth_instruction_sample(
> -                               etmq, tidq, addr,
> +                               etmq, tidq, tidq->packet, addr,
>                                 etm->instructions_sample_period);
>                         if (ret)
>                                 return ret;
> @@ -1959,7 +1993,7 @@ static int cs_etm__flush(struct cs_etm_queue *etmq,
>                 addr = cs_etm__last_executed_instr(tidq->prev_packet);
>
>                 err = cs_etm__synth_instruction_sample(
> -                       etmq, tidq, addr,
> +                       etmq, tidq, tidq->prev_packet, addr,
>                         tidq->period_instructions);
>                 if (err)
>                         return err;
> @@ -2014,7 +2048,7 @@ static int cs_etm__end_block(struct cs_etm_queue *etmq,
>                 addr = cs_etm__last_executed_instr(tidq->prev_packet);
>
>                 err = cs_etm__synth_instruction_sample(
> -                       etmq, tidq, addr,
> +                       etmq, tidq, tidq->prev_packet, addr,
>                         tidq->period_instructions);
>                 if (err)
>                         return err;
> @@ -2051,9 +2085,9 @@ static int cs_etm__get_data_block(struct cs_etm_queue *etmq)
>         return etmq->buf_len;
>  }
>
> -static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id,
> -                                struct cs_etm_packet *packet,
> -                                u64 end_addr)
> +static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq,
> +                                struct cs_etm_traceid_queue *tidq,
> +                                struct cs_etm_packet *packet, u64 end_addr)
>  {
>         /* Initialise to keep compiler happy */
>         u16 instr16 = 0;
> @@ -2075,8 +2109,8 @@ static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id,
>                  * so below only read 2 bytes as instruction size for T32.
>                  */
>                 addr = end_addr - 2;
> -               cs_etm__mem_access(etmq, trace_chan_id, addr, sizeof(instr16),
> -                                  (u8 *)&instr16, 0);
> +               cs_etm__frontend_mem_access(etmq, tidq, packet, addr,
> +                                           sizeof(instr16), (u8 *)&instr16);
>                 if ((instr16 & 0xFF00) == 0xDF00)
>                         return true;
>
> @@ -2091,8 +2125,8 @@ static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id,
>                  * +---------+---------+-------------------------+
>                  */
>                 addr = end_addr - 4;
> -               cs_etm__mem_access(etmq, trace_chan_id, addr, sizeof(instr32),
> -                                  (u8 *)&instr32, 0);
> +               cs_etm__frontend_mem_access(etmq, tidq, packet, addr,
> +                                           sizeof(instr32), (u8 *)&instr32);
>                 if ((instr32 & 0x0F000000) == 0x0F000000 &&
>                     (instr32 & 0xF0000000) != 0xF0000000)
>                         return true;
> @@ -2108,8 +2142,8 @@ static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id,
>                  * +-----------------------+---------+-----------+
>                  */
>                 addr = end_addr - 4;
> -               cs_etm__mem_access(etmq, trace_chan_id, addr, sizeof(instr32),
> -                                  (u8 *)&instr32, 0);
> +               cs_etm__frontend_mem_access(etmq, tidq, packet, addr,
> +                                           sizeof(instr32), (u8 *)&instr32);
>                 if ((instr32 & 0xFFE0001F) == 0xd4000001)
>                         return true;
>
> @@ -2125,7 +2159,6 @@ static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id,
>  static bool cs_etm__is_syscall(struct cs_etm_queue *etmq,
>                                struct cs_etm_traceid_queue *tidq, u64 magic)
>  {
> -       u8 trace_chan_id = tidq->trace_chan_id;
>         struct cs_etm_packet *packet = tidq->packet;
>         struct cs_etm_packet *prev_packet = tidq->prev_packet;
>
> @@ -2140,7 +2173,7 @@ static bool cs_etm__is_syscall(struct cs_etm_queue *etmq,
>          */
>         if (magic == __perf_cs_etmv4_magic) {
>                 if (packet->exception_number == CS_ETMV4_EXC_CALL &&
> -                   cs_etm__is_svc_instr(etmq, trace_chan_id, prev_packet,
> +                   cs_etm__is_svc_instr(etmq, tidq, prev_packet,
>                                          prev_packet->end_addr))
>                         return true;
>         }
> @@ -2178,7 +2211,6 @@ static bool cs_etm__is_sync_exception(struct cs_etm_queue *etmq,
>                                       struct cs_etm_traceid_queue *tidq,
>                                       u64 magic)
>  {
> -       u8 trace_chan_id = tidq->trace_chan_id;
>         struct cs_etm_packet *packet = tidq->packet;
>         struct cs_etm_packet *prev_packet = tidq->prev_packet;
>
> @@ -2204,7 +2236,7 @@ static bool cs_etm__is_sync_exception(struct cs_etm_queue *etmq,
>                  * (SMC, HVC) are taken as sync exceptions.
>                  */
>                 if (packet->exception_number == CS_ETMV4_EXC_CALL &&
> -                   !cs_etm__is_svc_instr(etmq, trace_chan_id, prev_packet,
> +                   !cs_etm__is_svc_instr(etmq, tidq, prev_packet,
>                                           prev_packet->end_addr))
>                         return true;
>
> @@ -2228,7 +2260,6 @@ static int cs_etm__set_sample_flags(struct cs_etm_queue *etmq,
>  {
>         struct cs_etm_packet *packet = tidq->packet;
>         struct cs_etm_packet *prev_packet = tidq->prev_packet;
> -       u8 trace_chan_id = tidq->trace_chan_id;
>         u64 magic;
>         int ret;
>
> @@ -2309,11 +2340,11 @@ static int cs_etm__set_sample_flags(struct cs_etm_queue *etmq,
>                 if (prev_packet->flags == (PERF_IP_FLAG_BRANCH |
>                                            PERF_IP_FLAG_RETURN |
>                                            PERF_IP_FLAG_INTERRUPT) &&
> -                   cs_etm__is_svc_instr(etmq, trace_chan_id,
> -                                        packet, packet->start_addr))
> +                   cs_etm__is_svc_instr(etmq, tidq, packet, packet->start_addr)) {
>                         prev_packet->flags = PERF_IP_FLAG_BRANCH |
>                                              PERF_IP_FLAG_RETURN |
>                                              PERF_IP_FLAG_SYSCALLRET;
> +               }
>                 break;
>         case CS_ETM_DISCONTINUITY:
>                 /*
> @@ -2394,6 +2425,7 @@ static int cs_etm__set_sample_flags(struct cs_etm_queue *etmq,
>                                              PERF_IP_FLAG_RETURN |
>                                              PERF_IP_FLAG_INTERRUPT;
>                 break;
> +       case CS_ETM_CONTEXT:
>         case CS_ETM_EMPTY:
>         default:
>                 break;
> @@ -2469,6 +2501,19 @@ static int cs_etm__process_traceid_queue(struct cs_etm_queue *etmq,
>                          */
>                         cs_etm__sample(etmq, tidq);
>                         break;
> +               case CS_ETM_CONTEXT:
> +                       /*
> +                        * Update context but don't swap packet. Keep the
> +                        * previous one for branch source address info, if
> +                        * tracing the kernel the context packet will be emitted
> +                        * between two ranges.
> +                        */
> +                       ret = cs_etm__etmq_update_thread(etmq, tidq->packet->el,
> +                                                        tidq->packet->tid,
> +                                                        &tidq->frontend_thread);
> +                       if (ret)
> +                               goto out;
> +                       break;
>                 case CS_ETM_EXCEPTION:
>                 case CS_ETM_EXCEPTION_RET:
>                         /*
> @@ -2497,6 +2542,7 @@ static int cs_etm__process_traceid_queue(struct cs_etm_queue *etmq,
>                 }
>         }
>
> +out:
>         return ret;
>  }
>
> @@ -2620,7 +2666,7 @@ static int cs_etm__process_timeless_queues(struct cs_etm_auxtrace *etm,
>                         if (!tidq)
>                                 continue;
>
> -                       if (tid == -1 || thread__tid(tidq->thread) == tid)
> +                       if (tid == -1 || thread__tid(tidq->frontend_thread) == tid)
>                                 cs_etm__run_per_thread_timeless_decoder(etmq);
>                 } else
>                         cs_etm__run_per_cpu_timeless_decoder(etmq);
> @@ -3328,7 +3374,7 @@ static int cs_etm__create_queue_decoders(struct cs_etm_queue *etmq)
>          */
>         if (cs_etm_decoder__add_mem_access_cb(etmq->decoder,
>                                               0x0L, ((u64) -1L),
> -                                             cs_etm__mem_access))
> +                                             cs_etm__decoder_mem_access))
>                 goto out_free_decoder;
>
>         zfree(&t_params);
> diff --git a/tools/perf/util/cs-etm.h b/tools/perf/util/cs-etm.h
> index aa9bb4a32eca..b81099c2b301 100644
> --- a/tools/perf/util/cs-etm.h
> +++ b/tools/perf/util/cs-etm.h
> @@ -158,6 +158,7 @@ enum cs_etm_sample_type {
>         CS_ETM_DISCONTINUITY,
>         CS_ETM_EXCEPTION,
>         CS_ETM_EXCEPTION_RET,
> +       CS_ETM_CONTEXT,
>  };
>
>  enum cs_etm_isa {
> @@ -184,6 +185,8 @@ struct cs_etm_packet {
>         u8 last_instr_size;
>         u8 trace_chan_id;
>         int cpu;
> +       int el;
> +       pid_t tid;
>  };
>
>  #define CS_ETM_PACKET_MAX_BUFFER 1024
> @@ -259,8 +262,9 @@ enum cs_etm_pid_fmt {
>  #include <opencsd/ocsd_if_types.h>
>  int cs_etm__get_cpu(struct cs_etm_queue *etmq, u8 trace_chan_id, int *cpu);
>  enum cs_etm_pid_fmt cs_etm__get_pid_fmt(struct cs_etm_queue *etmq);
> -int cs_etm__etmq_set_tid_el(struct cs_etm_queue *etmq, pid_t tid,
> -                           u8 trace_chan_id, ocsd_ex_level el);
> +int cs_etm__etmq_update_decode_context(struct cs_etm_queue *etmq,
> +                                      u8 trace_chan_id, ocsd_ex_level el,
> +                                      pid_t tid);
>  bool cs_etm__etmq_is_timeless(struct cs_etm_queue *etmq);
>  void cs_etm__etmq_set_traceid_queue_timestamp(struct cs_etm_queue *etmq,
>                                               u8 trace_chan_id);
>
> --
> 2.34.1
>

^ permalink raw reply

* Re: [PATCH net-next] docs: exclude driver and netdevsim bugs
From: Jakub Kicinski @ 2026-06-03 20:13 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms, johannes,
	corbet, skhan, workflows, linux-doc
In-Reply-To: <a0f191f4-3c09-4919-bc79-0b716d1ecc6f@lunn.ch>

On Wed, 3 Jun 2026 21:25:02 +0200 Andrew Lunn wrote:
> > +Additionally, netdev does not consider bugs to be ``net``-worthy
> > +if they fulfill **all** of the following criteria:
> > + - bug is in a hardware device driver;
> > + - bug is either a missing error handling or is part of the error handling flow;
> > + - bug was discovered by a static analysis / AI tool;
> > + - bug was triggered/observed only with kernel changes or fault injection.
> > +Fixes for such bugs should default to ``net-next`` and should **not** contain
> > +a Fixes tag. Networking or driver maintainers may redirect such fixes to ``net``
> > +at their discretion if they consider the condition to be relevant enough.  
> 
> I would also stress what the stable rules say:
> 
> 	It must either fix a real bug that bothers people or ...
> 
> Many of the bug fixes we are currently getting don't meet this
> criteria, so are net-next material.

I decided to leave that out in the end because it's a bit too open
to interpretation for my mind.

^ permalink raw reply

* Re: [PATCH net-next] docs: exclude driver and netdevsim bugs
From: Johannes Berg @ 2026-06-03 20:12 UTC (permalink / raw)
  To: Jakub Kicinski, davem
  Cc: netdev, edumazet, pabeni, andrew+netdev, horms, corbet, skhan,
	workflows, linux-doc
In-Reply-To: <20260603162943.2406080-1-kuba@kernel.org>

On Wed, 2026-06-03 at 09:29 -0700, Jakub Kicinski wrote:
> 
> +Additionally, netdev does not consider bugs to be ``net``-worthy
> +if they fulfill **all** of the following criteria:
> + - bug is in a hardware device driver;
> + - bug is either a missing error handling or is part of the error handling flow;

Do you really want to be this specific?

Take this fix for example that I mentioned the other day:
https://patchwork.kernel.org/project/linux-wireless/patch/20260531145435.701703-1-runyu.xiao@seu.edu.cn/

It doesn't formally fall under that definition, but I think it should,
it's a silly thing to send to stable etc.

This isn't even a USB device where you could reasonably argue that
someone might plug in a random one and it could be programmed to look
like the device in question and misbehave. Sure, you can build PCIe
hardware too that can do that, technically, and there's technically
external PCIe via Thunderbolt, but it's still far harder to actually do
anything with.

> + - bug was discovered by a static analysis / AI tool;

I'm not (yet?) convinced that this bullet point is right.

It risks getting into an argument about how much the LLM did to discover
it, or if the actual discovery was a manual process after the LLM
pointed out issues, or whatever ...

Maybe more importantly, why should that even change the result?

It's true that today the reason to start spelling this out more clearly
is AI related, but that's really because of (a) the scale, and (b) many
of the people running the LLMs not being aware of (and frankly often not
really caring about) the community norms. I'm not convinced that the
"silliness" of a change should be measured by how it originated.

> + - bug was triggered/observed only with kernel changes or fault injection.

Given this fourth bullet point, we'd still accept fixes for such driver
problems that people actually run into, while excluding "theoretical"
things that are discovered by "reading the code".

johannes

^ permalink raw reply

* Re: [RFC PATCH 0/5] mm, swap: Virtual Swap Space (Swap Table Edition)
From: Nhat Pham @ 2026-06-03 20:09 UTC (permalink / raw)
  To: Yosry Ahmed
  Cc: kasong, Liam.Howlett, akpm, apopple, axelrasmussen, baohua,
	baolin.wang, bhe, byungchul, cgroups, chengming.zhou, chrisl,
	corbet, david, dev.jain, gourry, hannes, hughd, jannh,
	joshua.hahnjy, lance.yang, lenb, linux-doc, linux-kernel,
	linux-mm, linux-pm, lorenzo.stoakes, matthew.brost, mhocko,
	muchun.song, npache, pavel, peterx, peterz, pfalcato, rafael,
	rakie.kim, roman.gushchin, rppt, ryan.roberts, shakeel.butt,
	shikemeng, surenb, tglx, vbabka, weixugc, ying.huang, yosry.ahmed,
	yuanchu, zhengqi.arch, ziy, kernel-team, riel, haowenchao22
In-Reply-To: <CAO9r8zP+PkgRzXJcFv+3i2pKFQdLt78Ax1s1DY0qNaiUo7ySqA@mail.gmail.com>

On Wed, Jun 3, 2026 at 12:35 PM Yosry Ahmed <yosry@kernel.org> wrote:
>
> > > > > All that being said, perhaps I am too out of touch with the code to
> > > > > realize it's simply not possible.
> > > > >
> > > > > Honestly, if the main reason we can't have a single swap table for vswap
> > > > > is saving 8 bytes on the reverse mapping, it sounds like a weak-ish
> > > > > argument, even if we can't optimize the reverse mapping away. But maybe
> > > > > I am also out of touch with RAM prices :)
> > > >
> > > > In terms of the space overhead I do agree, FWIW :)
> > > >
> > > > I think the other concern is the indirection overhead with going
> > > > through the xarray for every swap operation, hence the per-CPU vswap
> > > > cluster lookup caching idea:
> > > >
> > > > https://lore.kernel.org/all/20260505153854.1612033-23-nphamcs@gmail.com/
> > >
> > > Right, but we should already avoid the xarray with the swap table
> > > design, right? We just have one swap table pointing to another
> > > essentially?
> >
> > Hmmm, I don't quite follow your suggestion here.
> >
> > For normal swap devices, we organize the space into clusters, and
> > maintain them in various lists (free, nonfull, full etc.). The only
> > difference with a vswap device is we do not have a free list, and have
> > the clusters themselves dynamically allocated.
> >
> > If we're using vswap, we will incur the xarray overhead. There's no
> > avoiding that if we want a dynamic indirection layer. We can of course
> > revisit this data structure design later.
> >
> > So yes, it will be one swap table (vswap cluster) pointing to another
> > swap table (pswap cluster). But to get to the first swap table, you
> > will have to go through xarray still.
>
> Why the xarray? Don't page tables (and shmem page cache) just point
> directly to the vswap entry the same way they point to swap entries
> today?
>
> *looks at the code*
>
> Oh, it's to find the actual cluster because the vswap file can be
> sparse? Hmm yeah I guess we can revisit the data structure here later,

Less sparsity, and more dynamicity :) It might be dense for all we
know - we just don't really know (or want to figure out) the size
statically.

> but IIRC xarrays aren't particularly good for sparse data. Maybe it's
> usually not sprase in practice.
>
> Maybe a maple tree? :)

Maybe :)

>
> > > > If folks like it, what I can do is have CONFIG_ZSWAP depends on
> > > > CONFIG_VSWAP, removes all the non-vswap logic, and call it a day? :)
> > > > Then, on the swap allocation side, if vswap allocation fail and zswap
> > > > writeback is disabled, we can error out early.
> > >
> > > Hmm maybe we can keep it around for now and do that after vswap
> > > stabilizes? It ultimately depend on how much complexity we maintain by
> > > allowing both.
> > >
> > > I think another problem is 32-bit, technically zswap can be used on
> > > 32-bit now, right? So vswap not supporitng 32-bit is a problem.
> >
> > Ah shoot I forgot about that. Hmmm.
> >
> > It's not impossible to make vswap support 32-bit. I did that for v6
> > after all. It just needs extra fields because we have fewer bits to
> > leverage in pointers etc., complicating the logic a bit. Follow-up
> > work? :)
>
> Yeah we can do that, but it's a blocker for zswap only using vswap.

Yeah we can table that. FWIW, if you enable vswap, then zswap should
go through vswap already. It's just code complexity (hopefully for a
short while).

>
> > > General question (for both zswap and general swap code), would a boot
> > > param make implementation simpler? Right now we seem to key off the swap
> > > device having the "vswap" flag, would it help if it was a runtime
> > > constant?
> >
> > Hmmm, even if it's a runtime constant, both branches still have to be
> > there, no? Does the boot param simplify it somehow?
>
> Maybe it doesn't simplify the code, but if the branching causes
> performance overhead we can use static keys. I guess we can still use
> static keys per-swapfile, but it would be more complicated.
>
> Anyway, not super important now.

Ahhh I see what you mean. Yeah we can optimize this later.

^ permalink raw reply

* Re: [PATCH net-next V2 7/7] devlink: Add eswitch mode boot defaults
From: Randy Dunlap @ 2026-06-03 20:06 UTC (permalink / raw)
  To: Mark Bloch, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Andrew Lunn, David S. Miller
  Cc: Jonathan Corbet, Shuah Khan, Jiri Pirko, Simon Horman,
	Sunil Goutham, Linu Cherian, Geetha sowjanya, hariprasad,
	Subbaraya Sundeep, Bharat Bhushan, Saeed Mahameed,
	Leon Romanovsky, Tariq Toukan, Borislav Petkov (AMD),
	Andrew Morton, Peter Zijlstra (Intel), Thomas Gleixner,
	Petr Mladek, Tejun Heo, Vlastimil Babka, Feng Tang, Dave Hansen,
	Christian Brauner, Dapeng Mi, Kees Cook, Marco Elver,
	Eric Biggers, Li RongQing, Paul E. McKenney, Ethan Nelson-Moore,
	linux-doc, linux-kernel, netdev, linux-rdma
In-Reply-To: <20260603193259.3412464-8-mbloch@nvidia.com>

Hi.

On 6/3/26 12:32 PM, Mark Bloch wrote:
> diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
> index 063c11ca33e5..7af9f2898d92 100644
> --- a/Documentation/admin-guide/kernel-parameters.txt
> +++ b/Documentation/admin-guide/kernel-parameters.txt
> @@ -1264,6 +1264,31 @@ Kernel parameters
>  	dell_smm_hwmon.fan_max=
>  			[HW] Maximum configurable fan speed.
>  
> +	devlink_eswitch_mode=
> +			[NET]
> +			Format:
> +			[<selector>]:<mode>

It appears (please correct me if I am mistaken) that the '[' and ']'
above don't mean "optional" but instead they are required characters...

> +
> +			<selector>:
> +			* | <handle>[,<handle>...]

while here they mean "optional".

That is confusing (inconsistent). Also, if the square brackets are
always required around the <selector>, what purpose do they serve?

> +
> +			<handle>:
> +			<bus-name>/<dev-name>
> +
> +			Configure default devlink eswitch mode for matching
> +			devlink instances during device initialization.
> +
> +			<mode>:
> +			legacy | switchdev | switchdev_inactive
> +
> +			Examples:
> +			devlink_eswitch_mode=[*]:switchdev
> +			devlink_eswitch_mode=[pci/0000:08:00.0]:switchdev
> +			devlink_eswitch_mode=[pci/0000:08:00.0,pci/0000:09:00.1]:legacy
> +
> +			See Documentation/networking/devlink/devlink-defaults.rst
> +			for the full syntax.
> +
>  	dfltcc=		[HW,S390]
>  			Format: { on | off | def_only | inf_only | always }
>  			on:       s390 zlib hardware support for compression on


-- 
~Randy


^ permalink raw reply

* Re: [PATCH v2 1/6] alloc_tag: add ioctl to /proc/allocinfo
From: Suren Baghdasaryan @ 2026-06-03 19:59 UTC (permalink / raw)
  To: Hao Ge
  Cc: Abhishek Bapat, Andrew Morton, Kent Overstreet, Shuah Khan,
	Jonathan Corbet, linux-doc, linux-kernel, linux-mm, Sourav Panda
In-Reply-To: <e878da5b-6426-4bc0-924c-ecda72e08813@linux.dev>

On Sun, May 24, 2026 at 7:21 PM Hao Ge <hao.ge@linux.dev> wrote:
>
> Hi Abhishek
>
>
> Thanks for this patch. I had a few questions/comments after going
>
> through it.
>
>
> On 2026/5/23 01:45, Abhishek Bapat wrote:
> > From: Suren Baghdasaryan <surenb@google.com>
> >
> > Add the following ioctl commands for /proc/allocinfo file:
> >
> > ALLOCINFO_IOC_CONTENT_ID - gets content identifier which can be used
> > to check whether the file content has changed specifically due to module
> > load/unload. Every time a module is loaded / unloaded, the returned
> > value will be different. By comparing the identifier value at the
> > beginning and at the end of the content retrieval operation, users can
> > validate retrieved information for consistency.
>
> codetag_get_content_id() does not reflect module unload
>
> codetag_get_content_id() returns cttype->next_mod_seq:
>
> unsigned long codetag_get_content_id(struct codetag_type *cttype)
>
> {
>
>      return cttype->next_mod_seq;
>
> }
>
> However, next_mod_seq is only bumped in codetag_module_init(),
>
> i.e.the module load path:
>
> https://elixir.bootlin.com/linux/v7.1-rc4/source/lib/codetag.c#L204
>
> codetag_unload_module() does not increment next_mod_seq. This means
>
> that if only a module unload happens (without a subsequent load),
>
> content_id stays the same, so users comparing the id before and after
>
> won't detect that the content has changed. The commit message says
>
> "Every time a module is loaded / unloaded" -- I was wondering if this
>
> is intentional? If not, would it make sense to also bump next_mod_seq
>
> in the unload path?

Good point. I overlooked that when I wrote the prototype for this patch.
We should not bump next_mod_seq in the unload path but instead use a
separate seq_count that gets bumped every time
codetag_load_module/codetag_unload_module is called.

>
>
> >
> > ALLOCINFO_IOC_GET_AT - gets the record at the specified position. This
> > is the position of a record in /proc/allocinfo.
> >
> > ALLOCINFO_IOC_GET_NEXT - gets the record next to the last retrieved
> > one. If no records were previously retrieved, returns the first
> > record.
> >
> > Signed-off-by: Suren Baghdasaryan <surenb@google.com>
> > Signed-off-by: Abhishek Bapat <abhishekbapat@google.com>
> > ---
> >   .../userspace-api/ioctl/ioctl-number.rst      |   2 +
> >   MAINTAINERS                                   |   1 +
> >   include/linux/codetag.h                       |   1 +
> >   include/uapi/linux/alloc_tag.h                |  54 +++++
> >   lib/alloc_tag.c                               | 193 +++++++++++++++++-
> >   lib/codetag.c                                 |  11 +
> >   6 files changed, 260 insertions(+), 2 deletions(-)
> >   create mode 100644 include/uapi/linux/alloc_tag.h
> >
> > diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst
> > index 331223761fff..84f6808a8578 100644
> > --- a/Documentation/userspace-api/ioctl/ioctl-number.rst
> > +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
> > @@ -349,6 +349,8 @@ Code  Seq#    Include File                                             Comments
> >                                                                          <mailto:luzmaximilian@gmail.com>
> >   0xA5  20-2F  linux/surface_aggregator/dtx.h                            Microsoft Surface DTX driver
> >                                                                          <mailto:luzmaximilian@gmail.com>
> > +0xA6  00-0F  uapi/linux/alloc_tag.h                                    Memory allocation profiling
> > +                                                                       <mailto:surenb@google.com>
> >   0xAA  00-3F  linux/uapi/linux/userfaultfd.h
> >   0xAB  00-1F  linux/nbd.h
> >   0xAC  00-1F  linux/raw.h
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 46ed0f0e76d8..d176bde8fbfc 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -16709,6 +16709,7 @@ S:    Maintained
> >   F:  Documentation/mm/allocation-profiling.rst
> >   F:  include/linux/alloc_tag.h
> >   F:  include/linux/pgalloc_tag.h
> > +F:   include/uapi/linux/alloc_tag.h
> >   F:  lib/alloc_tag.c
> >
> >   MEMORY CONTROLLER DRIVERS
> > diff --git a/include/linux/codetag.h b/include/linux/codetag.h
> > index 8ea2a5f7c98a..2bcd4e7c809e 100644
> > --- a/include/linux/codetag.h
> > +++ b/include/linux/codetag.h
> > @@ -76,6 +76,7 @@ struct codetag_iterator {
> >
> >   void codetag_lock_module_list(struct codetag_type *cttype, bool lock);
> >   bool codetag_trylock_module_list(struct codetag_type *cttype);
> > +unsigned long codetag_get_content_id(struct codetag_type *cttype);
> >   struct codetag_iterator codetag_get_ct_iter(struct codetag_type *cttype);
> >   struct codetag *codetag_next_ct(struct codetag_iterator *iter);
> >
> > diff --git a/include/uapi/linux/alloc_tag.h b/include/uapi/linux/alloc_tag.h
> > new file mode 100644
> > index 000000000000..e9a5b55fcc7a
> > --- /dev/null
> > +++ b/include/uapi/linux/alloc_tag.h
> > @@ -0,0 +1,54 @@
> > +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> > +/*
> > + *  include/linux/alloc_tag.h
> nit: it should be include/uapi/linux/alloc_tag.h
> > + */
> > +
> > +#ifndef _UAPI_ALLOC_TAG_H
> > +#define _UAPI_ALLOC_TAG_H
> > +
> > +#include <linux/types.h>
> > +
> > +#define ALLOCINFO_STR_SIZE   64
> > +
> > +struct allocinfo_content_id {
> > +     __u64 id;
> > +};
> > +
> > +struct allocinfo_tag {
> > +     /* Longer names are trimmed */
> > +     char modname[ALLOCINFO_STR_SIZE];
> > +     char function[ALLOCINFO_STR_SIZE];
> > +     char filename[ALLOCINFO_STR_SIZE];
> > +     __u64 lineno;
> > +};
> > +
> > +struct allocinfo_counter {
> > +     __u64 bytes;
> > +     __u64 calls;
> > +     __u8 accurate;
> > +     __u8 pad[7]; /* Add alignment to not break the 32-bit compatible interface */
> > +};
> > +
> > +struct allocinfo_tag_data {
> > +     struct allocinfo_tag tag;
> > +     struct allocinfo_counter counter;
> > +};
> > +
> > +struct allocinfo_get_at {
> > +     __u64 pos;      /* input */
> > +     struct allocinfo_tag_data data;
> > +};
> > +
> > +#define _ALLOCINFO_IOC_CONTENT_ID    0
> > +#define _ALLOCINFO_IOC_GET_AT                1
> > +#define _ALLOCINFO_IOC_GET_NEXT              2
> > +
> > +#define ALLOCINFO_IOC_BASE           0xA6
> > +#define ALLOCINFO_IOC_CONTENT_ID     _IOR(ALLOCINFO_IOC_BASE, _ALLOCINFO_IOC_CONTENT_ID,     \
> > +                                          struct allocinfo_content_id)
> > +#define ALLOCINFO_IOC_GET_AT         _IOWR(ALLOCINFO_IOC_BASE, _ALLOCINFO_IOC_GET_AT,        \
> > +                                           struct allocinfo_get_at)
> > +#define ALLOCINFO_IOC_GET_NEXT               _IOR(ALLOCINFO_IOC_BASE, _ALLOCINFO_IOC_GET_NEXT,       \
> > +                                          struct allocinfo_tag_data)
> > +
> > +#endif /* _UAPI_ALLOC_TAG_H */
> > diff --git a/lib/alloc_tag.c b/lib/alloc_tag.c
> > index b9ca95d1f506..3598735b6c93 100644
> > --- a/lib/alloc_tag.c
> > +++ b/lib/alloc_tag.c
> > @@ -5,6 +5,7 @@
> >   #include <linux/gfp.h>
> >   #include <linux/kallsyms.h>
> >   #include <linux/module.h>
> > +#include <linux/mutex.h>
> >   #include <linux/page_ext.h>
> >   #include <linux/pgalloc_tag.h>
> >   #include <linux/proc_fs.h>
> > @@ -14,6 +15,7 @@
> >   #include <linux/string_choices.h>
> >   #include <linux/vmalloc.h>
> >   #include <linux/kmemleak.h>
> > +#include <uapi/linux/alloc_tag.h>
> >
> >   #define ALLOCINFO_FILE_NAME         "allocinfo"
> >   #define MODULE_ALLOC_TAG_VMAP_SIZE  (100000UL * sizeof(struct alloc_tag))
> > @@ -46,6 +48,10 @@ int alloc_tag_ref_offs;
> >   struct allocinfo_private {
> >       struct codetag_iterator iter;
> >       bool print_header;
> > +     /* ioctl uses a separate iterator not to interfere with reads */
> > +     struct codetag_iterator ioctl_iter;
> > +     bool positioned; /* seq_open_private() sets to 0 */
> > +     struct mutex ioctl_lock;
> >   };
> >
> >   static void *allocinfo_start(struct seq_file *m, loff_t *pos)
> > @@ -125,6 +131,190 @@ static const struct seq_operations allocinfo_seq_op = {
> >       .show   = allocinfo_show,
> >   };
> >
> > +static int allocinfo_open(struct inode *inode, struct file *file)
> > +{
> > +     int ret;
> > +
> > +     ret = seq_open_private(file, &allocinfo_seq_op,
> > +                            sizeof(struct allocinfo_private));
> > +     if (!ret) {
> > +             struct seq_file *m = file->private_data;
> > +             struct allocinfo_private *priv = m->private;
> > +
> > +             mutex_init(&priv->ioctl_lock);
> > +     }
> > +     return ret;
> > +}
> > +
> > +static int allocinfo_release(struct inode *inode, struct file *file)
> > +{
> > +     return seq_release_private(inode, file);
> > +}
> > +
> > +static const char *allocinfo_str(const char *str)
> > +{
> > +     size_t len = strlen(str);
> > +
> > +     /* Keep an extra space for the trailing NULL. */
> > +     if (len >= ALLOCINFO_STR_SIZE)
> > +             str += (len - ALLOCINFO_STR_SIZE) + 1;
> > +     return str;
> > +}
> > +
> > +/* Copy a string and trim from the beginning if it's too long */
> > +static void allocinfo_copy_str(char *dest, const char *src)
> > +{
> > +     strscpy(dest, allocinfo_str(src), ALLOCINFO_STR_SIZE);
> > +}
> > +
> > +static void allocinfo_to_params(struct codetag *ct,
> > +                             struct allocinfo_tag_data *data)
> > +{
> > +     struct alloc_tag *tag = ct_to_alloc_tag(ct);
> > +     struct alloc_tag_counters counter = alloc_tag_read(tag);
> > +
> > +     if (ct->modname)
> > +             allocinfo_copy_str(data->tag.modname, ct->modname);
> > +     else
> > +             data->tag.modname[0] = '\0';
> > +     allocinfo_copy_str(data->tag.function, ct->function);
> > +     allocinfo_copy_str(data->tag.filename, ct->filename);
> > +     data->tag.lineno = ct->lineno;
> > +     data->counter.bytes = counter.bytes;
> > +     data->counter.calls = counter.calls;
> > +     data->counter.accurate = !alloc_tag_is_inaccurate(tag);
> > +}
> > +
> > +static int allocinfo_ioctl_get_content_id(struct seq_file *m, void __user *arg)
> > +{
> > +     struct allocinfo_content_id params;
> > +
> > +     codetag_lock_module_list(alloc_tag_cttype, true);
> > +     params.id = codetag_get_content_id(alloc_tag_cttype);
> > +     codetag_lock_module_list(alloc_tag_cttype, false);
> > +     if (copy_to_user(arg, &params, sizeof(params)))
> > +             return -EFAULT;
> > +
> > +     return 0;
> > +}
> > +
> > +static int allocinfo_ioctl_get_at(struct seq_file *m, void __user *arg)
> > +{
> > +     struct allocinfo_private *priv;
> > +     struct codetag *ct;
> > +     __u64 pos;
> > +     struct allocinfo_get_at params = {0};
> > +
> > +     if (copy_from_user(&params, arg, sizeof(params)))
> > +             return -EFAULT;
> > +
> > +     priv = (struct allocinfo_private *)m->private;
> > +     pos = params.pos;
> > +
> > +     mutex_lock(&priv->ioctl_lock);
> > +     codetag_lock_module_list(alloc_tag_cttype, true);
> > +
> > +     /* Find the codetag */
> > +     priv->ioctl_iter = codetag_get_ct_iter(alloc_tag_cttype);
> > +     ct = codetag_next_ct(&priv->ioctl_iter);
> > +     while (ct && pos--)
> > +             ct = codetag_next_ct(&priv->ioctl_iter);
>
> No upper bound check on pos in ALLOCINFO_IOC_GET_AT:
>
> pos comes straight from userspace (__u64) with no validation.
>
> If the system has 10000 tags and someone passes pos=10001,
>
> the loop will still walk all 10000 tags just to return ENOENT
>
> -- all while holding ioctl_lock and mod_lock. It might be worth
>
> checking pos against the total tag count early. struct codetag_type
>
> is not exposed outside codetag.c though, so this would need a small helper.

Ack.

>
>
> Thanks
>
> Best Regards
>
> Hao
>
> > +     if (ct) {
> > +             allocinfo_to_params(ct, &params.data);
> > +             priv->positioned = true;
> > +     }
> > +
> > +     codetag_lock_module_list(alloc_tag_cttype, false);
> > +     mutex_unlock(&priv->ioctl_lock);
> > +
> > +     if (!ct)
> > +             return -ENOENT;
> > +
> > +     if (copy_to_user(arg, &params, sizeof(params)))
> > +             return -EFAULT;
> > +
> > +     return 0;
> > +}
> > +
> > +static int allocinfo_ioctl_get_next(struct seq_file *m, void __user *arg)
> > +{
> > +     struct allocinfo_private *priv;
> > +     struct codetag *ct;
> > +     struct allocinfo_tag_data params = {0};
> > +     int ret = 0;
> > +
> > +     priv = (struct allocinfo_private *)m->private;
> > +
> > +     mutex_lock(&priv->ioctl_lock);
> > +     codetag_lock_module_list(alloc_tag_cttype, true);
> > +
> > +     if (!priv->positioned) {
> > +             priv->ioctl_iter = codetag_get_ct_iter(alloc_tag_cttype);
> > +             priv->positioned = true;
> > +     }
> > +
> > +     ct = codetag_next_ct(&priv->ioctl_iter);
> > +     if (ct)
> > +             allocinfo_to_params(ct, &params);
> > +
> > +     if (!ct) {
> > +             priv->positioned = false;
> > +             ret = -ENOENT;
> > +     }
> > +     codetag_lock_module_list(alloc_tag_cttype, false);
> > +     mutex_unlock(&priv->ioctl_lock);
> > +
> > +     if (ret == 0) {
> > +             if (copy_to_user(arg, &params, sizeof(params)))
> > +                     return -EFAULT;
> > +     }
> > +     return ret;
> > +}
> > +
> > +static long allocinfo_ioctl(struct file *file, unsigned int cmd,
> > +                         unsigned long __arg)
> > +{
> > +     void __user *arg = (void __user *)__arg;
> > +     int ret;
> > +
> > +     switch (cmd) {
> > +     case ALLOCINFO_IOC_CONTENT_ID:
> > +             ret = allocinfo_ioctl_get_content_id(file->private_data, arg);
> > +             break;
> > +     case ALLOCINFO_IOC_GET_AT:
> > +             ret = allocinfo_ioctl_get_at(file->private_data, arg);
> > +             break;
> > +     case ALLOCINFO_IOC_GET_NEXT:
> > +             ret = allocinfo_ioctl_get_next(file->private_data, arg);
> > +             break;
> > +     default:
> > +             ret = -ENOIOCTLCMD;
> > +             break;
> > +     }
> > +
> > +     return ret;
> > +}
> > +
> > +#ifdef CONFIG_COMPAT
> > +static long allocinfo_compat_ioctl(struct file *file, unsigned int cmd,
> > +                                unsigned long arg)
> > +{
> > +     return allocinfo_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
> > +}
> > +#endif
> > +
> > +static const struct proc_ops allocinfo_proc_ops = {
> > +     .proc_open              = allocinfo_open,
> > +     .proc_read_iter         = seq_read_iter,
> > +     .proc_lseek             = seq_lseek,
> > +     .proc_release           = allocinfo_release,
> > +     .proc_ioctl             = allocinfo_ioctl,
> > +#ifdef CONFIG_COMPAT
> > +     .proc_compat_ioctl      = allocinfo_compat_ioctl,
> > +#endif
> > +
> > +};
> > +
> >   size_t alloc_tag_top_users(struct codetag_bytes *tags, size_t count, bool can_sleep)
> >   {
> >       struct codetag_iterator iter;
> > @@ -989,8 +1179,7 @@ static int __init alloc_tag_init(void)
> >               return 0;
> >       }
> >
> > -     if (!proc_create_seq_private(ALLOCINFO_FILE_NAME, 0400, NULL, &allocinfo_seq_op,
> > -                                  sizeof(struct allocinfo_private), NULL)) {
> > +     if (!proc_create(ALLOCINFO_FILE_NAME, 0400, NULL, &allocinfo_proc_ops)) {
> >               pr_err("Failed to create %s file\n", ALLOCINFO_FILE_NAME);
> >               shutdown_mem_profiling(false);
> >               return -ENOMEM;
> > diff --git a/lib/codetag.c b/lib/codetag.c
> > index 304667897ad4..93aa30991563 100644
> > --- a/lib/codetag.c
> > +++ b/lib/codetag.c
> > @@ -48,6 +48,17 @@ bool codetag_trylock_module_list(struct codetag_type *cttype)
> >       return down_read_trylock(&cttype->mod_lock) != 0;
> >   }
> >
> > +unsigned long codetag_get_content_id(struct codetag_type *cttype)
> > +{
> > +     lockdep_assert_held(&cttype->mod_lock);
> > +
> > +     /*
> > +      * next_mod_seq is updated on every load, so can be used to identify
> > +      * content changes.
> > +      */
> > +     return cttype->next_mod_seq;
> > +}
> > +
> >   struct codetag_iterator codetag_get_ct_iter(struct codetag_type *cttype)
> >   {
> >       struct codetag_iterator iter = {

^ permalink raw reply

* Re: [PATCH v2 1/6] alloc_tag: add ioctl to /proc/allocinfo
From: Suren Baghdasaryan @ 2026-06-03 19:53 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Abhishek Bapat, Kent Overstreet, Hao Ge, Shuah Khan,
	Jonathan Corbet, linux-doc, linux-kernel, linux-mm, Sourav Panda
In-Reply-To: <20260522131148.059d7589666e4a35af1430e5@linux-foundation.org>

On Fri, May 22, 2026 at 1:11 PM Andrew Morton <akpm@linux-foundation.org> wrote:
>
> On Fri, 22 May 2026 17:45:33 +0000 Abhishek Bapat <abhishekbapat@google.com> wrote:
>
> > From: Suren Baghdasaryan <surenb@google.com>
> >
> > Add the following ioctl commands for /proc/allocinfo file:
> >
> > ALLOCINFO_IOC_CONTENT_ID - gets content identifier which can be used
> > to check whether the file content has changed specifically due to module
> > load/unload. Every time a module is loaded / unloaded, the returned
> > value will be different. By comparing the identifier value at the
> > beginning and at the end of the content retrieval operation, users can
> > validate retrieved information for consistency.
> >
> > ALLOCINFO_IOC_GET_AT - gets the record at the specified position. This
> > is the position of a record in /proc/allocinfo.
> >
> > ALLOCINFO_IOC_GET_NEXT - gets the record next to the last retrieved
> > one. If no records were previously retrieved, returns the first
> > record.
> >
> > index 000000000000..e9a5b55fcc7a
> > --- /dev/null
> > +++ b/include/uapi/linux/alloc_tag.h
> > @@ -0,0 +1,54 @@
> > +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> > +/*
> > + *  include/linux/alloc_tag.h
> > + */
> > +
> > +#ifndef _UAPI_ALLOC_TAG_H
> > +#define _UAPI_ALLOC_TAG_H
> > +
> > +#include <linux/types.h>
> > +
> > +#define ALLOCINFO_STR_SIZE   64
> > +
> > +struct allocinfo_content_id {
> > +     __u64 id;
> > +};
> > +
> > +struct allocinfo_tag {
> > +     /* Longer names are trimmed */
> > +     char modname[ALLOCINFO_STR_SIZE];
> > +     char function[ALLOCINFO_STR_SIZE];
> > +     char filename[ALLOCINFO_STR_SIZE];
> > +     __u64 lineno;
> > +};
> > +
> > +struct allocinfo_counter {
> > +     __u64 bytes;
> > +     __u64 calls;
> > +     __u8 accurate;
> > +     __u8 pad[7]; /* Add alignment to not break the 32-bit compatible interface */
>
> This seems rather fragile, and makes assumptions about compiler layout?
>
> Can't we use __attribute__((aligned)) in some fashion?

Ack. I think we can.

>
> > +};
> > +
> > +struct allocinfo_tag_data {
> > +     struct allocinfo_tag tag;
> > +     struct allocinfo_counter counter;
> > +};
> > +
> > +struct allocinfo_get_at {
> > +     __u64 pos;      /* input */
> > +     struct allocinfo_tag_data data;
> > +};
> > +
> > +#define _ALLOCINFO_IOC_CONTENT_ID    0
> > +#define _ALLOCINFO_IOC_GET_AT                1
> > +#define _ALLOCINFO_IOC_GET_NEXT              2
> > +
> > +#define ALLOCINFO_IOC_BASE           0xA6
> > +#define ALLOCINFO_IOC_CONTENT_ID     _IOR(ALLOCINFO_IOC_BASE, _ALLOCINFO_IOC_CONTENT_ID,     \
> > +                                          struct allocinfo_content_id)
> > +#define ALLOCINFO_IOC_GET_AT         _IOWR(ALLOCINFO_IOC_BASE, _ALLOCINFO_IOC_GET_AT,        \
> > +                                           struct allocinfo_get_at)
> > +#define ALLOCINFO_IOC_GET_NEXT               _IOR(ALLOCINFO_IOC_BASE, _ALLOCINFO_IOC_GET_NEXT,       \
> > +                                          struct allocinfo_tag_data)
> > +
> > +#endif /* _UAPI_ALLOC_TAG_H */
> > diff --git a/lib/alloc_tag.c b/lib/alloc_tag.c
> > index b9ca95d1f506..3598735b6c93 100644
> > --- a/lib/alloc_tag.c
> > +++ b/lib/alloc_tag.c
> > @@ -5,6 +5,7 @@
> >  #include <linux/gfp.h>
> >  #include <linux/kallsyms.h>
> >  #include <linux/module.h>
> > +#include <linux/mutex.h>
> >  #include <linux/page_ext.h>
> >  #include <linux/pgalloc_tag.h>
> >  #include <linux/proc_fs.h>
> > @@ -14,6 +15,7 @@
> >  #include <linux/string_choices.h>
> >  #include <linux/vmalloc.h>
> >  #include <linux/kmemleak.h>
> > +#include <uapi/linux/alloc_tag.h>
> >
> >  #define ALLOCINFO_FILE_NAME          "allocinfo"
> >  #define MODULE_ALLOC_TAG_VMAP_SIZE   (100000UL * sizeof(struct alloc_tag))
> > @@ -46,6 +48,10 @@ int alloc_tag_ref_offs;
> >  struct allocinfo_private {
> >       struct codetag_iterator iter;
> >       bool print_header;
> > +     /* ioctl uses a separate iterator not to interfere with reads */
> > +     struct codetag_iterator ioctl_iter;
> > +     bool positioned; /* seq_open_private() sets to 0 */
> > +     struct mutex ioctl_lock;
> >  };
> >
> >  static void *allocinfo_start(struct seq_file *m, loff_t *pos)
> > @@ -125,6 +131,190 @@ static const struct seq_operations allocinfo_seq_op = {
> >       .show   = allocinfo_show,
> >  };
> >
> > +static int allocinfo_open(struct inode *inode, struct file *file)
> > +{
> > +     int ret;
> > +
> > +     ret = seq_open_private(file, &allocinfo_seq_op,
> > +                            sizeof(struct allocinfo_private));
> > +     if (!ret) {
> > +             struct seq_file *m = file->private_data;
> > +             struct allocinfo_private *priv = m->private;
> > +
> > +             mutex_init(&priv->ioctl_lock);
> > +     }
> > +     return ret;
> > +}
>
> Generally, the commenting in here is very thin.  Add some explanations
> of what the various functions do and, especially, why they do it?

Ack. Will add.

>
> > +static int allocinfo_release(struct inode *inode, struct file *file)
> > +{
> > +     return seq_release_private(inode, file);
> > +}
> > +
> > +static const char *allocinfo_str(const char *str)
> > +{
> > +     size_t len = strlen(str);
> > +
> > +     /* Keep an extra space for the trailing NULL. */
> > +     if (len >= ALLOCINFO_STR_SIZE)
> > +             str += (len - ALLOCINFO_STR_SIZE) + 1;
> > +     return str;
> > +}
> > +
> > +/* Copy a string and trim from the beginning if it's too long */
> > +static void allocinfo_copy_str(char *dest, const char *src)
> > +{
> > +     strscpy(dest, allocinfo_str(src), ALLOCINFO_STR_SIZE);
> > +}
>
> See, even these two little functions are unnecessarily difficult to
> review when one doesn"t know what they are setting out to do.  One has
> to first reverse engineer their intent from the implementation, then
> check that the implementation implements that intent.

Ack.

>
> > +static int allocinfo_ioctl_get_at(struct seq_file *m, void __user *arg)
> > +{
> > +     struct allocinfo_private *priv;
> > +     struct codetag *ct;
> > +     __u64 pos;
> > +     struct allocinfo_get_at params = {0};
> > +
> > +     if (copy_from_user(&params, arg, sizeof(params)))
> > +             return -EFAULT;
> > +
> > +     priv = (struct allocinfo_private *)m->private;
>
> Unneeded cast.

Ack.

>
> > +     pos = params.pos;
> > +
> > +     mutex_lock(&priv->ioctl_lock);
> > +     codetag_lock_module_list(alloc_tag_cttype, true);
> > +
> > +     /* Find the codetag */
> > +     priv->ioctl_iter = codetag_get_ct_iter(alloc_tag_cttype);
> > +     ct = codetag_next_ct(&priv->ioctl_iter);
> > +     while (ct && pos--)
> > +             ct = codetag_next_ct(&priv->ioctl_iter);
> > +     if (ct) {
> > +             allocinfo_to_params(ct, &params.data);
> > +             priv->positioned = true;
> > +     }
> > +
> > +     codetag_lock_module_list(alloc_tag_cttype, false);
> > +     mutex_unlock(&priv->ioctl_lock);
> > +
> > +     if (!ct)
> > +             return -ENOENT;
> > +
> > +     if (copy_to_user(arg, &params, sizeof(params)))
> > +             return -EFAULT;
> > +
> > +     return 0;
> > +}
> > +
> > +static int allocinfo_ioctl_get_next(struct seq_file *m, void __user *arg)
> > +{
> > +     struct allocinfo_private *priv;
> > +     struct codetag *ct;
> > +     struct allocinfo_tag_data params = {0};
> > +     int ret = 0;
> > +
> > +     priv = (struct allocinfo_private *)m->private;
>
> Ditto.

Ack.

>
> > +     mutex_lock(&priv->ioctl_lock);
> > +     codetag_lock_module_list(alloc_tag_cttype, true);
> > +
> > +     if (!priv->positioned) {
> > +             priv->ioctl_iter = codetag_get_ct_iter(alloc_tag_cttype);
> > +             priv->positioned = true;
> > +     }
> > +
> > +     ct = codetag_next_ct(&priv->ioctl_iter);
> > +     if (ct)
> > +             allocinfo_to_params(ct, &params);
> > +
> > +     if (!ct) {
> > +             priv->positioned = false;
> > +             ret = -ENOENT;
> > +     }
> > +     codetag_lock_module_list(alloc_tag_cttype, false);
> > +     mutex_unlock(&priv->ioctl_lock);
> > +
> > +     if (ret == 0) {
> > +             if (copy_to_user(arg, &params, sizeof(params)))
> > +                     return -EFAULT;
> > +     }
> > +     return ret;
> > +}
> >
> > ...
> >

^ permalink raw reply

* Re: [PATCH v2 0/6] alloc_tag: introduce IOCTL-based filtering for MAP
From: Suren Baghdasaryan @ 2026-06-03 19:51 UTC (permalink / raw)
  To: Hao Ge
  Cc: Andrew Morton, Kent Overstreet, Shuah Khan, Jonathan Corbet,
	linux-doc, linux-kernel, linux-mm, Sourav Panda, Abhishek Bapat
In-Reply-To: <4ae038f0-cc33-4a60-b59b-ae86bb541735@linux.dev>

On Mon, May 25, 2026 at 12:33 AM Hao Ge <hao.ge@linux.dev> wrote:
>
> Hi Andrew and Suren
>
>
> On 2026/5/23 04:11, Andrew Morton wrote:
> > On Fri, 22 May 2026 17:45:32 +0000 Abhishek Bapat <abhishekbapat@google.com> wrote:
> >
> >> Currently, memory allocation profiling data is primarily exposed through
> >> /proc/allocinfo. While useful for manual inspection, this text-based
> >> interface poses challenges for production monitoring and large-scale
> >> analysis:
> >>
> >> 1. Userspace must parse large amounts of text to extract specific
> >> fields.
> >> 2. To find specific tags, userspace must read the entire dataset,
> >> requiring many context switches and high data copying.
> >> 3. The kernel currently aggregates per-CPU counters for every allocation
> >> size, even those the user intends to filter out immediately.
> >>
> >> This series introduces a new IOCTL-based binary interface for allocinfo
> >> that supports kernel-side filtering. By allowing the user to specify a
> >> filter mask, we significantly reduce the work performed in-kernel and
> >> the amount of data transferred to userspace.
> >>
> >> Performance measurements were conducted on an Intel Xeon Platinum 8481C
> >> (224 CPUs) with caches dropped before each run.
> >>
> >> The IOCTL mechanism shows a ~20x performance improvement for
> >> filtered queries. The kernel avoids the expensive per-CPU counter
> >> aggregation (alloc_tag_read) for any tags that fail the initial string
> >> or location filters.
> >>
> >> Scenario 1: Specific File Filtering (arch/x86/events/rapl.c)
> >> 1. Traditional (cat /proc/allocinfo | grep): 22ms (sys)
> >> 2. IOCTL Interface: 1ms (sys)
> >>
> >> Scenario 2: Compound Filtering (Filename + Size)
> >> 1. Traditional: (cat ... | grep | awk): 21ms (sys)
> >> 2. IOCTL Interface: 1ms (sys)
> >>
> >> Scenario 3: Size-Based Filtering (min_size = 1MB)
> >> 1. Traditional: (cat ... | awk): 21ms (sys)
> >> 2. IOCTL Interface: 14ms (sys)
> > Yup, textual interfaces aren't fast.
> >
> > And ioctl-baed interfaces aren't popular.  One would prefer to see an
> > interface which uses read()/lseek(), pread(), etc.  It would be
> > appropriate for this [0/N] to have a discussion of why that approach
> > was not chosen.
> >
> >>   .../userspace-api/ioctl/ioctl-number.rst      |   2 +
> >>   MAINTAINERS                                   |   2 +
> >>   include/linux/codetag.h                       |   1 +
> >>   include/uapi/linux/alloc_tag.h                |  87 +++
> >>   lib/alloc_tag.c                               | 303 ++++++++++-
> >>   lib/codetag.c                                 |  11 +
> >>   tools/testing/selftests/alloc_tag/Makefile    |   9 +
> >>   .../alloc_tag/allocinfo_ioctl_test.c          | 505 ++++++++++++++++++
> >>   8 files changed, 918 insertions(+), 2 deletions(-)
> >>   create mode 100644 include/uapi/linux/alloc_tag.h
> >>   create mode 100644 tools/testing/selftests/alloc_tag/Makefile
> >>   create mode 100644 tools/testing/selftests/alloc_tag/allocinfo_ioctl_test.c
> > At some point this should grow user-facing documentation, please.
> >
> > And the right time for that is now, because such documentation is
> > useful for code review - it makes that review both easier and more
> > useful.
> >
> > Sashiko had a few things to say:
> >
> >       https://sashiko.dev/#/patchset/cover.1779471082.git.abhishekbapat@google.com
>
> I notice that Sashiko has reported a pre-existing issue, as described below:
>
>
>  >  static void *allocinfo_start(struct seq_file *m, loff_t *pos)
> This is a pre-existing issue, but can resuming a sequential read on
> /proc/allocinfo cause a use-after-free if a kernel module is unloaded
> between read() system calls?
> The seq_file read operation updates priv->iter.ct during allocinfo_next(),
> stops iteration, and returns to userspace. If the module containing
> priv->iter.ct is unloaded while the lock is dropped, the module's codetag
> memory is freed.
> On the next read() system call, allocinfo_start() with pos > 0 reacquires
> the lock but returns priv without validating if priv->iter.ct still belongs
> to a valid module. Does allocinfo_show() then dereference this dangling
> pointer?
> [ ... ]
>
> This issue is unrelated to the current patch series and can be resolved
>
> by reverting commit 9f44df50fee4.
>
> Therefore, I have submitted a separate patch addressing this issue,
>
> which is available at the link below:
>
> https://lore.kernel.org/all/20260525072117.112779-1-hao.ge@linux.dev/

Thanks Hao! I commented on your patch, please take a look. I think
there is a better fix.

>
> Thanks
>
> Best Regards
>
> Hao
>

^ permalink raw reply

* Re: [PATCH v2 0/6] alloc_tag: introduce IOCTL-based filtering for MAP
From: Suren Baghdasaryan @ 2026-06-03 19:49 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Abhishek Bapat, Kent Overstreet, Hao Ge, Shuah Khan,
	Jonathan Corbet, linux-doc, linux-kernel, linux-mm, Sourav Panda
In-Reply-To: <20260522131108.f972659717367c67082f3766@linux-foundation.org>

On Fri, May 22, 2026 at 1:11 PM Andrew Morton <akpm@linux-foundation.org> wrote:
>
> On Fri, 22 May 2026 17:45:32 +0000 Abhishek Bapat <abhishekbapat@google.com> wrote:
>
> > Currently, memory allocation profiling data is primarily exposed through
> > /proc/allocinfo. While useful for manual inspection, this text-based
> > interface poses challenges for production monitoring and large-scale
> > analysis:
> >
> > 1. Userspace must parse large amounts of text to extract specific
> > fields.
> > 2. To find specific tags, userspace must read the entire dataset,
> > requiring many context switches and high data copying.
> > 3. The kernel currently aggregates per-CPU counters for every allocation
> > size, even those the user intends to filter out immediately.
> >
> > This series introduces a new IOCTL-based binary interface for allocinfo
> > that supports kernel-side filtering. By allowing the user to specify a
> > filter mask, we significantly reduce the work performed in-kernel and
> > the amount of data transferred to userspace.
> >
> > Performance measurements were conducted on an Intel Xeon Platinum 8481C
> > (224 CPUs) with caches dropped before each run.
> >
> > The IOCTL mechanism shows a ~20x performance improvement for
> > filtered queries. The kernel avoids the expensive per-CPU counter
> > aggregation (alloc_tag_read) for any tags that fail the initial string
> > or location filters.
> >
> > Scenario 1: Specific File Filtering (arch/x86/events/rapl.c)
> > 1. Traditional (cat /proc/allocinfo | grep): 22ms (sys)
> > 2. IOCTL Interface: 1ms (sys)
> >
> > Scenario 2: Compound Filtering (Filename + Size)
> > 1. Traditional: (cat ... | grep | awk): 21ms (sys)
> > 2. IOCTL Interface: 1ms (sys)
> >
> > Scenario 3: Size-Based Filtering (min_size = 1MB)
> > 1. Traditional: (cat ... | awk): 21ms (sys)
> > 2. IOCTL Interface: 14ms (sys)
>
> Yup, textual interfaces aren't fast.
>
> And ioctl-baed interfaces aren't popular.  One would prefer to see an
> interface which uses read()/lseek(), pread(), etc.  It would be
> appropriate for this [0/N] to have a discussion of why that approach
> was not chosen.

We chose ioctl because it allows us to filter data without aggregating
the per-CPU counters, which is the main overhead when reading this
file. That's why we can achieve 20x performance improvement, provided
we do not filter based on the allocation size.
Aside from that, I plan on introducing an additional ioctl command to
enable context capture for specific allocations.

>
> >  .../userspace-api/ioctl/ioctl-number.rst      |   2 +
> >  MAINTAINERS                                   |   2 +
> >  include/linux/codetag.h                       |   1 +
> >  include/uapi/linux/alloc_tag.h                |  87 +++
> >  lib/alloc_tag.c                               | 303 ++++++++++-
> >  lib/codetag.c                                 |  11 +
> >  tools/testing/selftests/alloc_tag/Makefile    |   9 +
> >  .../alloc_tag/allocinfo_ioctl_test.c          | 505 ++++++++++++++++++
> >  8 files changed, 918 insertions(+), 2 deletions(-)
> >  create mode 100644 include/uapi/linux/alloc_tag.h
> >  create mode 100644 tools/testing/selftests/alloc_tag/Makefile
> >  create mode 100644 tools/testing/selftests/alloc_tag/allocinfo_ioctl_test.c
>
> At some point this should grow user-facing documentation, please.
>
> And the right time for that is now, because such documentation is
> useful for code review - it makes that review both easier and more
> useful.

Ack. I believe Abhishek is working on that.

>
> Sashiko had a few things to say:
>
>         https://sashiko.dev/#/patchset/cover.1779471082.git.abhishekbapat@google.com

Ack.

^ permalink raw reply

* Re: [PATCH 1/6] alloc_tag: add ioctl to /proc/allocinfo
From: Suren Baghdasaryan @ 2026-06-03 19:43 UTC (permalink / raw)
  To: Hao Ge
  Cc: Abhishek Bapat, Shuah Khan, Jonathan Corbet, linux-doc,
	linux-kernel, linux-mm, Sourav Panda, Kent Overstreet,
	Andrew Morton
In-Reply-To: <b15d5320-f3ce-4b69-b6e1-b422173acbf0@linux.dev>

On Thu, May 21, 2026 at 1:20 AM Hao Ge <hao.ge@linux.dev> wrote:
>
> On 2026/5/20 01:42, Suren Baghdasaryan wrote:
> > On Mon, May 18, 2026 at 7:53 PM Hao Ge <hao.ge@linux.dev> wrote:
> >> Hi Abhishek
> >>
> >>
> >> Thanks for the follow-up.
> >>
> >>
> >> On 2026/5/19 07:41, Abhishek Bapat wrote:
> >>> On Wed, May 13, 2026 at 9:38 PM Hao Ge<hao.ge@linux.dev>  wrote:
> >>>> Hi Suren and Abhishek
> >>>>
> >>>>
> >>>> Thanks for the patch! A couple of minor comments below.
> >>>>
> >>>>
> >>>> On 2026/5/5 07:36, Abhishek Bapat wrote:
> >>>>> From: Suren Baghdasaryan<surenb@google.com>
> >>>>>
> >>>>> Add the following ioctl commands for /proc/allocinfo file:
> >>>>>
> >>>>> ALLOCINFO_IOC_CONTENT_ID - gets content identifier which can be used
> >>>>> to check whether the file content has changed specifically due to module
> >>>>> load/unload. Every time a module is loaded / unloaded, the returned
> >>>>> value will be different. By comparing the identifier value at the
> >>>>> beginning and at the end of the content retrieval operation, users can
> >>>>> validate retrieved information for consistency.
> >>>>>
> >>>>> ALLOCINFO_IOC_GET_AT - gets the record at the specified position. This
> >>>>> is the position of a record in /proc/allocinfo.
> >>>>>
> >>>>> ALLOCINFO_IOC_GET_NEXT - gets the record next to the last retrieved
> >>>>> one. If no records were previously retrieved, returns the first
> >>>>> record.
> >>>>>
> >>>>> Signed-off-by: Suren Baghdasaryan<surenb@google.com>
> >>>>> Signed-off-by: Abhishek Bapat<abhishekbapat@google.com>
> >>>>> ---
> >>>>>     .../userspace-api/ioctl/ioctl-number.rst      |   2 +
> >>>>>     include/linux/codetag.h                       |   1 +
> >>>>>     include/uapi/linux/alloc_tag.h                |  54 ++++++
> >>>>>     lib/alloc_tag.c                               | 178 +++++++++++++++++-
> >>>>>     lib/codetag.c                                 |  11 ++
> >>>>>     5 files changed, 244 insertions(+), 2 deletions(-)
> >>>>>     create mode 100644 include/uapi/linux/alloc_tag.h
> >>>>>
> >>>>> diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst
> >>>>> index 331223761fff..84f6808a8578 100644
> >>>>> --- a/Documentation/userspace-api/ioctl/ioctl-number.rst
> >>>>> +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
> >>>>> @@ -349,6 +349,8 @@ Code  Seq#    Include File                                             Comments
> >>>>>                                                                            <mailto:luzmaximilian@gmail.com>
> >>>>>     0xA5  20-2F  linux/surface_aggregator/dtx.h                            Microsoft Surface DTX driver
> >>>>>                                                                            <mailto:luzmaximilian@gmail.com>
> >>>>> +0xA6  00-0F  uapi/linux/alloc_tag.h                                    Memory allocation profiling
> >>>>> +<mailto:surenb@google.com>
> >>>>>     0xAA  00-3F  linux/uapi/linux/userfaultfd.h
> >>>>>     0xAB  00-1F  linux/nbd.h
> >>>>>     0xAC  00-1F  linux/raw.h
> >>>>> diff --git a/include/linux/codetag.h b/include/linux/codetag.h
> >>>>> index 8ea2a5f7c98a..2bcd4e7c809e 100644
> >>>>> --- a/include/linux/codetag.h
> >>>>> +++ b/include/linux/codetag.h
> >>>>> @@ -76,6 +76,7 @@ struct codetag_iterator {
> >>>>>
> >>>>>     void codetag_lock_module_list(struct codetag_type *cttype, bool lock);
> >>>>>     bool codetag_trylock_module_list(struct codetag_type *cttype);
> >>>>> +unsigned long codetag_get_content_id(struct codetag_type *cttype);
> >>>>>     struct codetag_iterator codetag_get_ct_iter(struct codetag_type *cttype);
> >>>>>     struct codetag *codetag_next_ct(struct codetag_iterator *iter);
> >>>>>
> >>>>> diff --git a/include/uapi/linux/alloc_tag.h b/include/uapi/linux/alloc_tag.h
> >>>>> new file mode 100644
> >>>>> index 000000000000..e9a5b55fcc7a
> >>>>> --- /dev/null
> >>>>> +++ b/include/uapi/linux/alloc_tag.h
> >>>>> @@ -0,0 +1,54 @@
> >>>>> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> >>>>> +/*
> >>>>> + *  include/linux/alloc_tag.h
> >>>>> + */
> >>>>> +
> >>>>> +#ifndef _UAPI_ALLOC_TAG_H
> >>>>> +#define _UAPI_ALLOC_TAG_H
> >>>>> +
> >>>>> +#include <linux/types.h>
> >>>>> +
> >>>>> +#define ALLOCINFO_STR_SIZE   64
> >>>>> +
> >>>>> +struct allocinfo_content_id {
> >>>>> +     __u64 id;
> >>>>> +};
> >>>>> +
> >>>>> +struct allocinfo_tag {
> >>>>> +     /* Longer names are trimmed */
> >>>>> +     char modname[ALLOCINFO_STR_SIZE];
> >>>>> +     char function[ALLOCINFO_STR_SIZE];
> >>>>> +     char filename[ALLOCINFO_STR_SIZE];
> >>>>> +     __u64 lineno;
> >>>>> +};
> >>>>> +
> >>>>> +struct allocinfo_counter {
> >>>>> +     __u64 bytes;
> >>>>> +     __u64 calls;
> >>>>> +     __u8 accurate;
> >>>>> +     __u8 pad[7]; /* Add alignment to not break the 32-bit compatible interface */
> >>>>> +};
> >>>>> +
> >>>>> +struct allocinfo_tag_data {
> >>>>> +     struct allocinfo_tag tag;
> >>>>> +     struct allocinfo_counter counter;
> >>>>> +};
> >>>>> +
> >>>>> +struct allocinfo_get_at {
> >>>>> +     __u64 pos;      /* input */
> >>>>> +     struct allocinfo_tag_data data;
> >>>>> +};
> >>>>> +
> >>>>> +#define _ALLOCINFO_IOC_CONTENT_ID    0
> >>>>> +#define _ALLOCINFO_IOC_GET_AT                1
> >>>>> +#define _ALLOCINFO_IOC_GET_NEXT              2
> >>>>> +
> >>>>> +#define ALLOCINFO_IOC_BASE           0xA6
> >>>>> +#define ALLOCINFO_IOC_CONTENT_ID     _IOR(ALLOCINFO_IOC_BASE, _ALLOCINFO_IOC_CONTENT_ID,     \
> >>>>> +                                          struct allocinfo_content_id)
> >>>>> +#define ALLOCINFO_IOC_GET_AT         _IOWR(ALLOCINFO_IOC_BASE, _ALLOCINFO_IOC_GET_AT,        \
> >>>>> +                                           struct allocinfo_get_at)
> >>>>> +#define ALLOCINFO_IOC_GET_NEXT               _IOR(ALLOCINFO_IOC_BASE, _ALLOCINFO_IOC_GET_NEXT,       \
> >>>>> +                                          struct allocinfo_tag_data)
> >>>>> +
> >>>>> +#endif /* _UAPI_ALLOC_TAG_H */
> >>>>> diff --git a/lib/alloc_tag.c b/lib/alloc_tag.c
> >>>>> index ed1bdcf1f8ab..5c24d2f954d4 100644
> >>>>> --- a/lib/alloc_tag.c
> >>>>> +++ b/lib/alloc_tag.c
> >>>>> @@ -14,6 +14,7 @@
> >>>>>     #include <linux/string_choices.h>
> >>>>>     #include <linux/vmalloc.h>
> >>>>>     #include <linux/kmemleak.h>
> >>>>> +#include <uapi/linux/alloc_tag.h>
> >>>>>
> >>>>>     #define ALLOCINFO_FILE_NAME         "allocinfo"
> >>>>>     #define MODULE_ALLOC_TAG_VMAP_SIZE  (100000UL * sizeof(struct alloc_tag))
> >>>>> @@ -46,6 +47,9 @@ int alloc_tag_ref_offs;
> >>>>>     struct allocinfo_private {
> >>>>>         struct codetag_iterator iter;
> >>>>>         bool print_header;
> >>>>> +     /* ioctl uses a separate iterator not to interfere with reads */
> >>>>> +     struct codetag_iterator ioctl_iter;
> >>>>> +     bool positioned; /* seq_open_private() sets to 0 */
> >>>>>     };
> >>>>>
> >>>>>     static void *allocinfo_start(struct seq_file *m, loff_t *pos)
> >>>>> @@ -125,6 +129,177 @@ static const struct seq_operations allocinfo_seq_op = {
> >>>>>         .show   = allocinfo_show,
> >>>>>     };
> >>>>>
> >>>>> +static int allocinfo_open(struct inode *inode, struct file *file)
> >>>>> +{
> >>>>> +     return seq_open_private(file, &allocinfo_seq_op,
> >>>>> +                             sizeof(struct allocinfo_private));
> >>>>> +}
> >>>>> +
> >>>>> +static int allocinfo_release(struct inode *inode, struct file *file)
> >>>>> +{
> >>>>> +     return seq_release_private(inode, file);
> >>>>> +}
> >>>>> +
> >>>>> +static const char *allocinfo_str(const char *str)
> >>>>> +{
> >>>>> +     size_t len = strlen(str);
> >>>>> +
> >>>>> +     /* Keep an extra space for the trailing NULL. */
> >>>>> +     if (len >= ALLOCINFO_STR_SIZE)
> >>>>> +             str += (len - ALLOCINFO_STR_SIZE) + 1;
> >>>>> +     return str;
> >>>>> +}
> >>>>> +
> >>>>> +/* Copy a string and trim from the beginning if it's too long */
> >>>>> +static void allocinfo_copy_str(char *dest, const char *src)
> >>>>> +{
> >>>>> +     strscpy(dest, allocinfo_str(src), ALLOCINFO_STR_SIZE);
> >>>>> +}
> >>>>> +
> >>>>> +static void allocinfo_to_params(struct codetag *ct,
> >>>>> +                             struct allocinfo_tag_data *data)
> >>>>> +{
> >>>>> +     struct alloc_tag *tag = ct_to_alloc_tag(ct);
> >>>>> +     struct alloc_tag_counters counter = alloc_tag_read(tag);
> >>>>> +
> >>>>> +     if (ct->modname)
> >>>>> +             allocinfo_copy_str(data->tag.modname, ct->modname);
> >>>>> +     else
> >>>>> +             data->tag.modname[0] = '\0';
> >>>> Minor nit about allocinfo_to_params():
> >>>>
> >>>> When modname is NULL (built-in kernel code), the current code sets it
> >>>>
> >>>> to an empty string:
> >>>>
> >>>>        if (ct->modname)
> >>>>
> >>>>            allocinfo_copy_str(data->tag.modname, ct->modname);
> >>>>
> >>>>        else
> >>>>
> >>>>            data->tag.modname[0] = '\0';
> >>>>
> >>>> This is of course workable in userspace by checking for an empty
> >>>>
> >>>> string, but I was wondering if it would be cleaner to use "vmlinux"
> >>>>
> >>>> as a default:
> >>>>
> >>>> else
> >>>>
> >>>>              allocinfo_copy_str(data->tag.modname, "vmlinux");
> >>>>
> >>>>
> >>>> For some context, in our memory analysis workflow we often group
> >>>>
> >>>> allocations by module to get a quick overview of where memory goes,
> >>>>
> >>>> for example:
> >>>>
> >>>> vmlinux:    2.1 GB    (kernel core)
> >>>>
> >>>> nvidia:     1.2 GB    (GPU driver)
> >>>>
> >>>> iwlwifi:    800 MB    (WiFi driver)
> >>>>
> >>>> ext4:       500 MB    (filesystem)
> >>>>
> >>>> Having a consistent identifier for kernel built-in allocations would
> >>>>
> >>>> avoid each userspace tool needing to handle the empty string as a
> >>>>
> >>>> special case. Totally fine if this is intentional though.
> >>>>
> >>> Thanks for bringing this up, I can certainly make this change.
> >>> However, the information is not currently exposed this way through
> >>> /proc/allocinfo. /proc/allocinfo does not categorize kernel non-module
> >>> allocations as vmlinux, so there will a delta between how IOCTL and
> >>> /proc/allocinfo behave. Suren, could you comment on whether this
> >>> recommendation is fine by you?
> >>>
> >> Right, /proc/allocinfo indeed doesn't categorize them as vmlinux currently.
> >>
> >> It's just that in practice we often group allocations by module, so
> >> having "vmlinux" as a default
> >>
> >> would be convenient. Let's wait for Suren's input.
> > Hi Folks,
> > I would prefer to keep it empty because vmlinux is not really a module
> > and hardcoding this name also seems suboptimal (in case it ever
> > changes). Empty string also aligns with how we output /proc/allocinfo
> > data. If the symbol is in the kernel itself, we do not display the
> > module name at all. So, all in all, unless there is a strong reason
> > against it, I think we should keep it empty.
>
> Hi Suren
>
>
> Thanks for the clarification, that makes sense.
>
> For userspace tools that want to group by module, we can always map an
> empty modname to "vmlinux" at the
>
> presentation layer — no need to hardcode that in the kernel.
>
>
> Hi Abhishek
>
> I noticed the new files (like include/uapi/linux/alloc_tag.h) were added
> in this patchset.
>
> Should they be reflected in the MAINTAINERS file for easier future
> maintenance?

Yes, definitely. Thanks for noticing!

>
> Thanks
>
> Best Regards
>
> Hao
>
> >>>>> +     allocinfo_copy_str(data->tag.function, ct->function);
> >>>>> +     allocinfo_copy_str(data->tag.filename, ct->filename);
> >>>>> +     data->tag.lineno = ct->lineno;
> >>>>> +     data->counter.bytes = counter.bytes;
> >>>>> +     data->counter.calls = counter.calls;
> >>>>> +     data->counter.accurate = !alloc_tag_is_inaccurate(tag);
> >>>>> +}
> >>>>> +
> >>>>> +static int allocinfo_ioctl_get_content_id(struct seq_file *m, void __user *arg)
> >>>>> +{
> >>>>> +     struct allocinfo_content_id params;
> >>>>> +
> >>>>> +     codetag_lock_module_list(alloc_tag_cttype, true);
> >>>>> +     params.id = codetag_get_content_id(alloc_tag_cttype);
> >>>>> +     codetag_lock_module_list(alloc_tag_cttype, false);
> >>>>> +     if (copy_to_user(arg, &params, sizeof(params)))
> >>>>> +             return -EFAULT;
> >>>>> +
> >>>>> +     return 0;
> >>>>> +}
> >>>>> +
> >>>>> +static int allocinfo_ioctl_get_at(struct seq_file *m, void __user *arg)
> >>>>> +{
> >>>>> +     struct allocinfo_private *priv;
> >>>>> +     struct codetag *ct;
> >>>>> +     __u64 pos;
> >>>>> +     struct allocinfo_get_at params = {0};
> >>>>> +
> >>>>> +     if (copy_from_user(&params, arg, sizeof(params)))
> >>>>> +             return -EFAULT;
> >>>>> +
> >>>>> +     priv = (struct allocinfo_private *)m->private;
> >>>>> +     pos = params.pos;
> >>>>> +
> >>>>> +     codetag_lock_module_list(alloc_tag_cttype, true);
> >>>>> +
> >>>>> +     /* Find the codetag */
> >>>>> +     priv->ioctl_iter = codetag_get_ct_iter(alloc_tag_cttype);
> >>>>> +     ct = codetag_next_ct(&priv->ioctl_iter);
> >>>>> +     while (ct && pos--)
> >>>>> +             ct = codetag_next_ct(&priv->ioctl_iter);
> >>>> I noticed that codetag_next_ct(&priv->ioctl_iter) and
> >>>>
> >>>> priv->positioned are accessed without serialization in the ioctl
> >>>>
> >>>> path. Concurrent ioctl calls on the same fd could race on these
> >>>>
> >>>> fields. Just something I spotted while reading the code.
> >>>>
> >>>>
> >>>> Thanks
> >>>>
> >>>> Best Regards
> >>>>
> >>>> Hao
> >>>>
> >>> I believe this should be prevented by `codetag_lock_module_list`; am I
> >>> wrong in my understanding?
> >> Thanks for the explanation! codetag_lock_module_list is designed to
> >> protect the module list from concurrent load/unload, which it does
> >>
> >> correctly. However, it doesn't cover the race between concurrent ioctl
> >> calls on the same fd, since it acquires cttype->mod_lock via
> >>
> >> down_read() and rwsem read locks allow multiple readers to proceed
> >> concurrently:
> >>
> >> Thread A: ALLOCINFO_IOC_GET_AT
> >>
> >> down_read(&cttype->mod_lock)              // read lock acquired
> >>
> >> priv->ioctl_iter = codetag_get_ct_iter(...)
> >>
> >> ct = codetag_next_ct(&priv->ioctl_iter)
> >>
> >> priv->positioned = true;
> >>
> >> Thread B: ALLOCINFO_IOC_GET_NEXT            // concurrent ioctl on same fd
> >>
> >> down_read(&cttype->mod_lock)              // read locks don't exclude
> >> each other
> >>
> >> if (!priv->positioned) {                  // sees partial state from
> >> Thread A
> >>
> >> priv->ioctl_iter = ...                // overwrites Thread A's iterator
> >>
> >> }
> >>
> >> ct = codetag_next_ct(&priv->ioctl_iter)   // corrupted iterator
> >>
> >> priv->ioctl_iter and priv->positioned are per-fd state with no
> >> serialization in the ioctl path.
> > Yep, you are right. codetag_lock_module_list() is not enough here to
> > protect from such races. I guess allocinfo_private would need another
> > lock.
> > Thanks,
> > Suren.
> >
> >
> >> Just something I spotted.
> >>
> >> Thanks
> >>
> >> Best Regards
> >>
> >> Hao
> >>
> >>>>> +     if (ct) {
> >>>>> +             allocinfo_to_params(ct, &params.data);
> >>>>> +             priv->positioned = true;
> >>>>> +     }
> >>>>> +
> >>>>> +     codetag_lock_module_list(alloc_tag_cttype, false);
> >>>>> +
> >>>>> +     if (!ct)
> >>>>> +             return -ENOENT;
> >>>>> +
> >>>>> +     if (copy_to_user(arg, &params, sizeof(params)))
> >>>>> +             return -EFAULT;
> >>>>> +
> >>>>> +     return 0;
> >>>>> +}
> >>>>> +
> >>>>> +static int allocinfo_ioctl_get_next(struct seq_file *m, void __user *arg)
> >>>>> +{
> >>>>> +     struct allocinfo_private *priv;
> >>>>> +     struct codetag *ct;
> >>>>> +     struct allocinfo_tag_data params = {0};
> >>>>> +     int ret = 0;
> >>>>> +
> >>>>> +     priv = (struct allocinfo_private *)m->private;
> >>>>> +
> >>>>> +     codetag_lock_module_list(alloc_tag_cttype, true);
> >>>>> +
> >>>>> +     if (!priv->positioned) {
> >>>>> +             priv->ioctl_iter = codetag_get_ct_iter(alloc_tag_cttype);
> >>>>> +             priv->positioned = true;
> >>>>> +     }
> >>>>> +
> >>>>> +     ct = codetag_next_ct(&priv->ioctl_iter);
> >>>>> +     if (ct)
> >>>>> +             allocinfo_to_params(ct, &params);
> >>>>> +
> >>>>> +     if (!ct) {
> >>>>> +             priv->positioned = false;
> >>>>> +             ret = -ENOENT;
> >>>>> +     }
> >>>>> +     codetag_lock_module_list(alloc_tag_cttype, false);
> >>>>> +
> >>>>> +     if (ret == 0) {
> >>>>> +             if (copy_to_user(arg, &params, sizeof(params)))
> >>>>> +                     return -EFAULT;
> >>>>> +     }
> >>>>> +     return ret;
> >>>>> +}
> >>>>> +
> >>>>> +static long allocinfo_ioctl(struct file *file, unsigned int cmd,
> >>>>> +                         unsigned long __arg)
> >>>>> +{
> >>>>> +     void __user *arg = (void __user *)__arg;
> >>>>> +     int ret;
> >>>>> +
> >>>>> +     switch (cmd) {
> >>>>> +     case ALLOCINFO_IOC_CONTENT_ID:
> >>>>> +             ret = allocinfo_ioctl_get_content_id(file->private_data, arg);
> >>>>> +             break;
> >>>>> +     case ALLOCINFO_IOC_GET_AT:
> >>>>> +             ret = allocinfo_ioctl_get_at(file->private_data, arg);
> >>>>> +             break;
> >>>>> +     case ALLOCINFO_IOC_GET_NEXT:
> >>>>> +             ret = allocinfo_ioctl_get_next(file->private_data, arg);
> >>>>> +             break;
> >>>>> +     default:
> >>>>> +             ret = -ENOIOCTLCMD;
> >>>>> +             break;
> >>>>> +     }
> >>>>> +
> >>>>> +     return ret;
> >>>>> +}
> >>>>> +
> >>>>> +#ifdef CONFIG_COMPAT
> >>>>> +static long allocinfo_compat_ioctl(struct file *file, unsigned int cmd,
> >>>>> +                                unsigned long arg)
> >>>>> +{
> >>>>> +     return allocinfo_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
> >>>>> +}
> >>>>> +#endif
> >>>>> +
> >>>>> +static const struct proc_ops allocinfo_proc_ops = {
> >>>>> +     .proc_open              = allocinfo_open,
> >>>>> +     .proc_read_iter         = seq_read_iter,
> >>>>> +     .proc_lseek             = seq_lseek,
> >>>>> +     .proc_release           = allocinfo_release,
> >>>>> +     .proc_ioctl             = allocinfo_ioctl,
> >>>>> +#ifdef CONFIG_COMPAT
> >>>>> +     .proc_compat_ioctl      = allocinfo_compat_ioctl,
> >>>>> +#endif
> >>>>> +
> >>>>> +};
> >>>>> +
> >>>>>     size_t alloc_tag_top_users(struct codetag_bytes *tags, size_t count, bool can_sleep)
> >>>>>     {
> >>>>>         struct codetag_iterator iter;
> >>>>> @@ -946,8 +1121,7 @@ static int __init alloc_tag_init(void)
> >>>>>                 return 0;
> >>>>>         }
> >>>>>
> >>>>> -     if (!proc_create_seq_private(ALLOCINFO_FILE_NAME, 0400, NULL, &allocinfo_seq_op,
> >>>>> -                                  sizeof(struct allocinfo_private), NULL)) {
> >>>>> +     if (!proc_create(ALLOCINFO_FILE_NAME, 0400, NULL, &allocinfo_proc_ops)) {
> >>>>>                 pr_err("Failed to create %s file\n", ALLOCINFO_FILE_NAME);
> >>>>>                 shutdown_mem_profiling(false);
> >>>>>                 return -ENOMEM;
> >>>>> diff --git a/lib/codetag.c b/lib/codetag.c
> >>>>> index 304667897ad4..93aa30991563 100644
> >>>>> --- a/lib/codetag.c
> >>>>> +++ b/lib/codetag.c
> >>>>> @@ -48,6 +48,17 @@ bool codetag_trylock_module_list(struct codetag_type *cttype)
> >>>>>         return down_read_trylock(&cttype->mod_lock) != 0;
> >>>>>     }
> >>>>>
> >>>>> +unsigned long codetag_get_content_id(struct codetag_type *cttype)
> >>>>> +{
> >>>>> +     lockdep_assert_held(&cttype->mod_lock);
> >>>>> +
> >>>>> +     /*
> >>>>> +      * next_mod_seq is updated on every load, so can be used to identify
> >>>>> +      * content changes.
> >>>>> +      */
> >>>>> +     return cttype->next_mod_seq;
> >>>>> +}
> >>>>> +
> >>>>>     struct codetag_iterator codetag_get_ct_iter(struct codetag_type *cttype)
> >>>>>     {
> >>>>>         struct codetag_iterator iter = {
> >>> Note, I will be following up with a v2 patchset with your feedback
> >>> included. Please bring up any other points you'd want to clarify so
> >>> that I can include all the changes in the v2 patchset. Thanks for
> >>> reviewing!

^ permalink raw reply

* Re: [RFC PATCH 0/5] mm, swap: Virtual Swap Space (Swap Table Edition)
From: Yosry Ahmed @ 2026-06-03 19:35 UTC (permalink / raw)
  To: Nhat Pham
  Cc: kasong, Liam.Howlett, akpm, apopple, axelrasmussen, baohua,
	baolin.wang, bhe, byungchul, cgroups, chengming.zhou, chrisl,
	corbet, david, dev.jain, gourry, hannes, hughd, jannh,
	joshua.hahnjy, lance.yang, lenb, linux-doc, linux-kernel,
	linux-mm, linux-pm, lorenzo.stoakes, matthew.brost, mhocko,
	muchun.song, npache, pavel, peterx, peterz, pfalcato, rafael,
	rakie.kim, roman.gushchin, rppt, ryan.roberts, shakeel.butt,
	shikemeng, surenb, tglx, vbabka, weixugc, ying.huang, yosry.ahmed,
	yuanchu, zhengqi.arch, ziy, kernel-team, riel, haowenchao22
In-Reply-To: <CAKEwX=P95D7wNpWhEAXQpeNPM6eQa2mEZE8Srzfpct=-=Q40tg@mail.gmail.com>

On Wed, Jun 3, 2026 at 12:26 PM Nhat Pham <nphamcs@gmail.com> wrote:
>
> On Wed, Jun 3, 2026 at 11:58 AM Yosry Ahmed <yosry@kernel.org> wrote:
> >
> > > > I assume the main reason here is to avoid the extra overhead if
> > > > everything uses vswap, which would mainly be the reverse mapping
> > > > overhead? I guess there's also some simplicity that comes from reusing
> > > > the swap info infra as a whole, including the swap table.
> > >
> > > Yeah it helps a lot that we don't have to rewrite the whole allocator
> > > and swap entry reference counting logic again :)
> >
> > I specifically meant using a full swap info thing for the physical swap
> > device even when it's behind vswap. That seems like an overkill, and we
> > don't need things like the swap entry reference coutning. We probably
> > just need a bitmap and a reverse mapping.
> >
> > So I am assuming the main reason why we are not doing that (at least for
> > now) is simplicity?
>
> Mostly.
>
> FWIW, we're pretty close to full deduplication. Right now, physical
> swap clusters have a couple of fields that are not needed when they're
> backing a vswap cluster:
>
> 1. The main swap table (which houses swap cache, swap shadow, and
> reference counting): I repurpose it for the rmap :) It's an array of
> unsigned long, which works for rmap.
>
> 2. memcg_table: still duplicated, but I think I can make sure this is
> not allocated if physical swap clusters only back vswap entries. I
> have a prototype that I'm testing for this.
>
> 3. The zeromap field: this is actually not allocated in 64 bit
> architecture, IIUC, which is what I'm gating CONFIG_VSWAP on. If we
> extend vswap to supporting 32 bits, this can also be dynamically
> allocated.
>
> 4. Extend table - this is for the swap count overfills, and already
> dynamically allocated.

I see.

> > > > All that being said, perhaps I am too out of touch with the code to
> > > > realize it's simply not possible.
> > > >
> > > > Honestly, if the main reason we can't have a single swap table for vswap
> > > > is saving 8 bytes on the reverse mapping, it sounds like a weak-ish
> > > > argument, even if we can't optimize the reverse mapping away. But maybe
> > > > I am also out of touch with RAM prices :)
> > >
> > > In terms of the space overhead I do agree, FWIW :)
> > >
> > > I think the other concern is the indirection overhead with going
> > > through the xarray for every swap operation, hence the per-CPU vswap
> > > cluster lookup caching idea:
> > >
> > > https://lore.kernel.org/all/20260505153854.1612033-23-nphamcs@gmail.com/
> >
> > Right, but we should already avoid the xarray with the swap table
> > design, right? We just have one swap table pointing to another
> > essentially?
>
> Hmmm, I don't quite follow your suggestion here.
>
> For normal swap devices, we organize the space into clusters, and
> maintain them in various lists (free, nonfull, full etc.). The only
> difference with a vswap device is we do not have a free list, and have
> the clusters themselves dynamically allocated.
>
> If we're using vswap, we will incur the xarray overhead. There's no
> avoiding that if we want a dynamic indirection layer. We can of course
> revisit this data structure design later.
>
> So yes, it will be one swap table (vswap cluster) pointing to another
> swap table (pswap cluster). But to get to the first swap table, you
> will have to go through xarray still.

Why the xarray? Don't page tables (and shmem page cache) just point
directly to the vswap entry the same way they point to swap entries
today?

*looks at the code*

Oh, it's to find the actual cluster because the vswap file can be
sparse? Hmm yeah I guess we can revisit the data structure here later,
but IIRC xarrays aren't particularly good for sparse data. Maybe it's
usually not sprase in practice.

Maybe a maple tree? :)

> > > If folks like it, what I can do is have CONFIG_ZSWAP depends on
> > > CONFIG_VSWAP, removes all the non-vswap logic, and call it a day? :)
> > > Then, on the swap allocation side, if vswap allocation fail and zswap
> > > writeback is disabled, we can error out early.
> >
> > Hmm maybe we can keep it around for now and do that after vswap
> > stabilizes? It ultimately depend on how much complexity we maintain by
> > allowing both.
> >
> > I think another problem is 32-bit, technically zswap can be used on
> > 32-bit now, right? So vswap not supporitng 32-bit is a problem.
>
> Ah shoot I forgot about that. Hmmm.
>
> It's not impossible to make vswap support 32-bit. I did that for v6
> after all. It just needs extra fields because we have fewer bits to
> leverage in pointers etc., complicating the logic a bit. Follow-up
> work? :)

Yeah we can do that, but it's a blocker for zswap only using vswap.

> > General question (for both zswap and general swap code), would a boot
> > param make implementation simpler? Right now we seem to key off the swap
> > device having the "vswap" flag, would it help if it was a runtime
> > constant?
>
> Hmmm, even if it's a runtime constant, both branches still have to be
> there, no? Does the boot param simplify it somehow?

Maybe it doesn't simplify the code, but if the branching causes
performance overhead we can use static keys. I guess we can still use
static keys per-swapfile, but it would be more complicated.

Anyway, not super important now.

^ permalink raw reply

* [PATCH net-next V2 7/7] devlink: Add eswitch mode boot defaults
From: Mark Bloch @ 2026-06-03 19:32 UTC (permalink / raw)
  To: Eric Dumazet, Jakub Kicinski, Paolo Abeni, Andrew Lunn,
	David S. Miller
  Cc: Jonathan Corbet, Shuah Khan, Jiri Pirko, Simon Horman,
	Sunil Goutham, Linu Cherian, Geetha sowjanya, hariprasad,
	Subbaraya Sundeep, Bharat Bhushan, Saeed Mahameed,
	Leon Romanovsky, Tariq Toukan, Mark Bloch, Borislav Petkov (AMD),
	Andrew Morton, Randy Dunlap, Peter Zijlstra (Intel),
	Thomas Gleixner, Petr Mladek, Tejun Heo, Vlastimil Babka,
	Feng Tang, Dave Hansen, Christian Brauner, Dapeng Mi, Kees Cook,
	Marco Elver, Eric Biggers, Li RongQing, Paul E. McKenney,
	Ethan Nelson-Moore, linux-doc, linux-kernel, netdev, linux-rdma
In-Reply-To: <20260603193259.3412464-1-mbloch@nvidia.com>

Add devlink_eswitch_mode= command line support for setting a default
eswitch mode during device initialization.

The supported syntax selects either all devlink handles or one explicit
comma-separated handle list:

  devlink_eswitch_mode=[*]:<mode>
  devlink_eswitch_mode=[<handle>[,<handle>...]]:<mode>

where <mode> is one of legacy, switchdev or switchdev_inactive. All
selected handles receive the same mode. Assigning different modes to
different handle lists in the same parameter value is not supported.

The default is applied through the existing eswitch_mode_set() devlink
operation, matching the userspace devlink eswitch mode command. devlink
core applies it when a matching devlink instance is registered and after a
successful devlink reload that performed DRIVER_REINIT, so rebuilt device
state returns to the requested boot default.

Document the devlink_eswitch_mode= syntax and duplicate handle handling.

Signed-off-by: Mark Bloch <mbloch@nvidia.com>
---
 .../admin-guide/kernel-parameters.txt         |  25 ++
 .../networking/devlink/devlink-defaults.rst   |  80 ++++++
 Documentation/networking/devlink/index.rst    |   1 +
 net/devlink/core.c                            | 261 ++++++++++++++++++
 net/devlink/dev.c                             |   3 +
 net/devlink/devl_internal.h                   |   1 +
 6 files changed, 371 insertions(+)
 create mode 100644 Documentation/networking/devlink/devlink-defaults.rst

diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 063c11ca33e5..7af9f2898d92 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -1264,6 +1264,31 @@ Kernel parameters
 	dell_smm_hwmon.fan_max=
 			[HW] Maximum configurable fan speed.
 
+	devlink_eswitch_mode=
+			[NET]
+			Format:
+			[<selector>]:<mode>
+
+			<selector>:
+			* | <handle>[,<handle>...]
+
+			<handle>:
+			<bus-name>/<dev-name>
+
+			Configure default devlink eswitch mode for matching
+			devlink instances during device initialization.
+
+			<mode>:
+			legacy | switchdev | switchdev_inactive
+
+			Examples:
+			devlink_eswitch_mode=[*]:switchdev
+			devlink_eswitch_mode=[pci/0000:08:00.0]:switchdev
+			devlink_eswitch_mode=[pci/0000:08:00.0,pci/0000:09:00.1]:legacy
+
+			See Documentation/networking/devlink/devlink-defaults.rst
+			for the full syntax.
+
 	dfltcc=		[HW,S390]
 			Format: { on | off | def_only | inf_only | always }
 			on:       s390 zlib hardware support for compression on
diff --git a/Documentation/networking/devlink/devlink-defaults.rst b/Documentation/networking/devlink/devlink-defaults.rst
new file mode 100644
index 000000000000..b554e75eeeea
--- /dev/null
+++ b/Documentation/networking/devlink/devlink-defaults.rst
@@ -0,0 +1,80 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==============================
+Devlink Eswitch Mode Defaults
+==============================
+
+Devlink eswitch mode defaults allow the eswitch mode to be provided on the
+kernel command line and applied to matching devlink instances during device
+initialization.
+
+The devlink device is selected by its devlink handle. For PCI devices this is
+the same handle shown by ``devlink dev show``, for example
+``pci/0000:08:00.0``.
+
+Kernel command line syntax
+==========================
+
+Defaults are specified with the ``devlink_eswitch_mode=`` kernel command line
+parameter.
+
+The general syntax is::
+
+  devlink_eswitch_mode=[<selector>]:<mode>
+
+``<selector>`` is either ``*`` or one or more devlink handles::
+
+  * | <bus-name>/<dev-name>[,<bus-name>/<dev-name>...]
+
+``*`` applies the mode to every devlink instance. All handles in the same
+``[]`` list receive the same eswitch mode.
+
+``<mode>`` is one of ``legacy``, ``switchdev`` or ``switchdev_inactive``.
+
+Syntax rules
+------------
+
+The following syntax rules apply:
+
+* Specify the default in one ``devlink_eswitch_mode=`` parameter. Repeated
+  ``devlink_eswitch_mode=`` parameters are not accumulated.
+* The ``devlink_eswitch_mode=`` value is limited by the kernel command line
+  size.
+* Whitespace is not allowed within the parameter value.
+* ``<selector>`` must be either ``*`` or a handle list. ``*`` cannot be
+  combined with explicit handles.
+* ``<bus-name>`` and ``<dev-name>`` must not be empty.
+* ``<bus-name>`` must not contain ``:``.
+* ``<dev-name>`` may contain ``:``. This allows PCI names such as
+  ``0000:08:00.0``.
+* Handles must not contain whitespace, ``[``, ``]``, ``*`` or more than one
+  ``/``.
+* A comma inside ``[]`` separates handles.
+* Comma-separated default groups are not supported.
+* Duplicate handles are rejected and the devlink eswitch mode default is
+  ignored.
+
+The eswitch mode default corresponds to the userspace command::
+
+  devlink dev eswitch set <handle> mode <value>
+
+
+Examples
+========
+
+Set all devlink instances to switchdev mode::
+
+  devlink_eswitch_mode=[*]:switchdev
+
+Set one PCI devlink instance to switchdev mode::
+
+  devlink_eswitch_mode=[pci/0000:08:00.0]:switchdev
+
+Set two PCI devlink instances to legacy mode::
+
+  devlink_eswitch_mode=[pci/0000:08:00.0,pci/0000:09:00.1]:legacy
+
+The following is invalid because comma-separated default groups are not
+supported::
+
+  devlink_eswitch_mode=[pci/0000:08:00.0]:switchdev,[pci/0000:09:00.0]:switchdev_inactive
diff --git a/Documentation/networking/devlink/index.rst b/Documentation/networking/devlink/index.rst
index f7ba7dcf477d..0d27a7008b14 100644
--- a/Documentation/networking/devlink/index.rst
+++ b/Documentation/networking/devlink/index.rst
@@ -56,6 +56,7 @@ general.
    :maxdepth: 1
 
    devlink-dpipe
+   devlink-defaults
    devlink-eswitch-attr
    devlink-flash
    devlink-health
diff --git a/net/devlink/core.c b/net/devlink/core.c
index eeb6a71f5f56..3e1b26459894 100644
--- a/net/devlink/core.c
+++ b/net/devlink/core.c
@@ -4,6 +4,10 @@
  * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
  */
 
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/string.h>
 #include <net/genetlink.h>
 #define CREATE_TRACE_POINTS
 #include <trace/events/devlink.h>
@@ -16,6 +20,234 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_trap_report);
 
 DEFINE_XARRAY_FLAGS(devlinks, XA_FLAGS_ALLOC);
 
+static char *devlink_default_esw_mode_param;
+static bool devlink_default_esw_mode_match_all;
+static enum devlink_eswitch_mode devlink_default_esw_mode;
+static LIST_HEAD(devlink_default_esw_mode_nodes);
+
+struct devlink_default_esw_mode_node {
+	struct list_head list;
+	char *bus_name;
+	char *dev_name;
+};
+
+static int __init
+devlink_default_esw_mode_to_value(const char *str,
+				  enum devlink_eswitch_mode *mode)
+{
+	if (!strcmp(str, "legacy")) {
+		*mode = DEVLINK_ESWITCH_MODE_LEGACY;
+		return 0;
+	}
+	if (!strcmp(str, "switchdev")) {
+		*mode = DEVLINK_ESWITCH_MODE_SWITCHDEV;
+		return 0;
+	}
+	if (!strcmp(str, "switchdev_inactive")) {
+		*mode = DEVLINK_ESWITCH_MODE_SWITCHDEV_INACTIVE;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int __init
+devlink_default_esw_mode_handle_parse(char *handle, char **bus_name,
+				      char **dev_name)
+{
+	char *slash;
+	char *p;
+
+	if (!handle || !*handle)
+		return -EINVAL;
+
+	for (p = handle; *p; p++) {
+		if (*p == '[' || *p == ']' || *p == '*')
+			return -EINVAL;
+	}
+
+	slash = strchr(handle, '/');
+	if (!slash || slash == handle || !slash[1])
+		return -EINVAL;
+	if (strchr(slash + 1, '/'))
+		return -EINVAL;
+
+	*slash = '\0';
+	if (strchr(handle, ':'))
+		return -EINVAL;
+
+	*bus_name = handle;
+	*dev_name = slash + 1;
+	return 0;
+}
+
+static struct devlink_default_esw_mode_node *
+devlink_default_esw_mode_node_find(const char *bus_name, const char *dev_name)
+{
+	struct devlink_default_esw_mode_node *node;
+
+	list_for_each_entry(node, &devlink_default_esw_mode_nodes, list) {
+		if (!strcmp(node->bus_name, bus_name) &&
+		    !strcmp(node->dev_name, dev_name))
+			return node;
+	}
+
+	return NULL;
+}
+
+static int __init
+devlink_default_esw_mode_node_add(const char *bus_name, const char *dev_name)
+{
+	struct devlink_default_esw_mode_node *node;
+
+	if (devlink_default_esw_mode_node_find(bus_name, dev_name))
+		return -EEXIST;
+
+	node = kzalloc_obj(*node);
+	if (!node)
+		return -ENOMEM;
+
+	INIT_LIST_HEAD(&node->list);
+	node->bus_name = kstrdup(bus_name, GFP_KERNEL);
+	node->dev_name = kstrdup(dev_name, GFP_KERNEL);
+	if (!node->bus_name || !node->dev_name) {
+		kfree(node->bus_name);
+		kfree(node->dev_name);
+		kfree(node);
+		return -ENOMEM;
+	}
+
+	list_add_tail(&node->list, &devlink_default_esw_mode_nodes);
+	return 0;
+}
+
+static int __init devlink_default_esw_mode_handles_parse(char *handles)
+{
+	char *handle;
+	int err;
+
+	if (!strcmp(handles, "*")) {
+		devlink_default_esw_mode_match_all = true;
+		return 0;
+	}
+
+	while ((handle = strsep(&handles, ",")) != NULL) {
+		char *bus_name;
+		char *dev_name;
+
+		err = devlink_default_esw_mode_handle_parse(handle, &bus_name,
+							    &dev_name);
+		if (err)
+			return err;
+
+		err = devlink_default_esw_mode_node_add(bus_name, dev_name);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+static void __init
+devlink_default_esw_mode_node_free(struct devlink_default_esw_mode_node *node)
+{
+	kfree(node->bus_name);
+	kfree(node->dev_name);
+	kfree(node);
+}
+
+static void __init devlink_default_esw_mode_nodes_clear(void)
+{
+	struct devlink_default_esw_mode_node *node;
+	struct devlink_default_esw_mode_node *node_tmp;
+
+	list_for_each_entry_safe(node, node_tmp,
+				 &devlink_default_esw_mode_nodes, list) {
+		list_del(&node->list);
+		devlink_default_esw_mode_node_free(node);
+	}
+
+	devlink_default_esw_mode_match_all = false;
+}
+
+static int __init devlink_default_esw_mode_parse(char *str)
+{
+	char *handles_end;
+	char *handles;
+	char *mode;
+	enum devlink_eswitch_mode esw_mode;
+	int err;
+
+	if (!str || *str != '[')
+		return -EINVAL;
+
+	handles = str + 1;
+	handles_end = strchr(handles, ']');
+	if (!handles_end || handles_end[1] != ':' || !handles_end[2])
+		return -EINVAL;
+
+	*handles_end = '\0';
+	mode = handles_end + 2;
+	if (!*handles)
+		return -EINVAL;
+
+	err = devlink_default_esw_mode_to_value(mode, &esw_mode);
+	if (err)
+		return err;
+
+	err = devlink_default_esw_mode_handles_parse(handles);
+	if (err)
+		devlink_default_esw_mode_nodes_clear();
+	else
+		devlink_default_esw_mode = esw_mode;
+
+	return err;
+}
+
+static bool devlink_default_esw_mode_match(struct devlink *devlink)
+{
+	const char *bus_name = devlink_bus_name(devlink);
+	const char *dev_name = devlink_dev_name(devlink);
+	struct devlink_default_esw_mode_node *node;
+
+	if (devlink_default_esw_mode_match_all)
+		return true;
+
+	node = devlink_default_esw_mode_node_find(bus_name, dev_name);
+	return !!node;
+}
+
+void devlink_apply_default_esw_mode(struct devlink *devlink)
+{
+	const struct devlink_ops *ops = devlink->ops;
+	int err;
+
+	devl_assert_locked(devlink);
+
+	if (!devlink_default_esw_mode_match(devlink))
+		return;
+
+	if (!ops->eswitch_mode_set) {
+		if (!devlink_default_esw_mode_match_all)
+			devl_warn(devlink,
+				  "devlink_eswitch_mode= selected this device but eswitch mode setting is not supported\n");
+		return;
+	}
+
+	err = ops->eswitch_mode_set(devlink, devlink_default_esw_mode, NULL);
+	if (err)
+		devl_warn(devlink,
+			  "Couldn't apply default eswitch mode, err %d\n",
+			  err);
+}
+
+static int __init devlink_default_esw_mode_setup(char *str)
+{
+	devlink_default_esw_mode_param = str;
+	return 1;
+}
+__setup("devlink_eswitch_mode=", devlink_default_esw_mode_setup);
+
 static struct devlink *devlinks_xa_get(unsigned long index)
 {
 	struct devlink *devlink;
@@ -391,6 +623,7 @@ int devl_register(struct devlink *devlink)
 	xa_set_mark(&devlinks, devlink->index, DEVLINK_REGISTERED);
 	devlink_notify_register(devlink);
 	devlink_rel_nested_in_notify(devlink);
+	devlink_apply_default_esw_mode(devlink);
 
 	return 0;
 }
@@ -578,6 +811,31 @@ static int __init devlink_init(void)
 {
 	int err;
 
+	if (devlink_default_esw_mode_param) {
+		char *def;
+
+		def = kstrdup(devlink_default_esw_mode_param, GFP_KERNEL);
+		if (!def) {
+			devlink_default_esw_mode_param = NULL;
+			pr_warn("devlink: devlink_eswitch_mode parameter ignored, failed to allocate memory\n");
+		} else {
+			err = devlink_default_esw_mode_parse(def);
+			kfree(def);
+			if (err == -EEXIST) {
+				devlink_default_esw_mode_param = NULL;
+				pr_warn("devlink: duplicate eswitch mode handles ignored\n");
+			} else if (err == -EINVAL) {
+				devlink_default_esw_mode_param = NULL;
+				pr_warn("devlink: invalid devlink_eswitch_mode parameter ignored\n");
+			} else if (err == -ENOMEM) {
+				devlink_default_esw_mode_param = NULL;
+				pr_warn("devlink: devlink_eswitch_mode parameter ignored, failed to allocate memory\n");
+			} else if (err) {
+				goto out;
+			}
+		}
+	}
+
 	err = register_pernet_subsys(&devlink_pernet_ops);
 	if (err)
 		goto out;
@@ -593,7 +851,10 @@ static int __init devlink_init(void)
 out_unreg_pernet_subsys:
 	unregister_pernet_subsys(&devlink_pernet_ops);
 out:
+	if (err)
+		devlink_default_esw_mode_nodes_clear();
 	WARN_ON(err);
+
 	return err;
 }
 
diff --git a/net/devlink/dev.c b/net/devlink/dev.c
index 57b2b8f03543..0b4a831465e8 100644
--- a/net/devlink/dev.c
+++ b/net/devlink/dev.c
@@ -478,6 +478,9 @@ int devlink_reload(struct devlink *devlink, struct net *dest_net,
 		return err;
 
 	WARN_ON(!(*actions_performed & BIT(action)));
+	if (*actions_performed & BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT))
+		devlink_apply_default_esw_mode(devlink);
+
 	/* Catch driver on updating the remote action within devlink reload */
 	WARN_ON(memcmp(remote_reload_stats, devlink->stats.remote_reload_stats,
 		       sizeof(remote_reload_stats)));
diff --git a/net/devlink/devl_internal.h b/net/devlink/devl_internal.h
index e4e48ee2da5a..12557b65248d 100644
--- a/net/devlink/devl_internal.h
+++ b/net/devlink/devl_internal.h
@@ -71,6 +71,7 @@ extern struct genl_family devlink_nl_family;
 struct devlink *__devlink_alloc(const struct devlink_ops *ops, size_t priv_size,
 				struct net *net, struct device *dev,
 				const struct device_driver *dev_driver);
+void devlink_apply_default_esw_mode(struct devlink *devlink);
 
 #define devl_warn(devlink, format, args...)				\
 	do {								\
-- 
2.34.1


^ permalink raw reply related

* [PATCH net-next V2 6/7] octeontx2-pf: Register devlink after SR-IOV state init
From: Mark Bloch @ 2026-06-03 19:32 UTC (permalink / raw)
  To: Eric Dumazet, Jakub Kicinski, Paolo Abeni, Andrew Lunn,
	David S. Miller
  Cc: Jonathan Corbet, Shuah Khan, Jiri Pirko, Simon Horman,
	Sunil Goutham, Linu Cherian, Geetha sowjanya, hariprasad,
	Subbaraya Sundeep, Bharat Bhushan, Saeed Mahameed,
	Leon Romanovsky, Tariq Toukan, Mark Bloch, Borislav Petkov (AMD),
	Andrew Morton, Randy Dunlap, Peter Zijlstra (Intel),
	Thomas Gleixner, Petr Mladek, Tejun Heo, Vlastimil Babka,
	Feng Tang, Dave Hansen, Christian Brauner, Dapeng Mi, Kees Cook,
	Marco Elver, Eric Biggers, Li RongQing, Paul E. McKenney,
	Ethan Nelson-Moore, linux-doc, linux-kernel, netdev, linux-rdma
In-Reply-To: <20260603193259.3412464-1-mbloch@nvidia.com>

A later patch makes devlink registration the point where devlink core may
call eswitch_mode_set() to apply a boot-time default eswitch mode.

Move octeontx2 PF devlink registration after PF SR-IOV configuration state
is initialized, so representor creation has the state it needs.

Add a separate unwind label so failures after devlink registration
unregister devlink before cleaning up SR-IOV state.

Signed-off-by: Mark Bloch <mbloch@nvidia.com>
---
 .../ethernet/marvell/octeontx2/nic/otx2_pf.c    | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
index ee623476e5ff..9afe6cf0ea01 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
@@ -3278,14 +3278,14 @@ static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	if (err)
 		goto err_mcam_flow_del;
 
-	err = otx2_register_dl(pf);
-	if (err)
-		goto err_mcam_flow_del;
-
 	/* Initialize SR-IOV resources */
 	err = otx2_sriov_vfcfg_init(pf);
 	if (err)
-		goto err_pf_sriov_init;
+		goto err_shutdown_tc;
+
+	err = otx2_register_dl(pf);
+	if (err)
+		goto err_sriov_cleannup;
 
 	/* Enable link notifications */
 	otx2_cgx_config_linkevents(pf, true);
@@ -3293,7 +3293,7 @@ static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	pf->af_xdp_zc_qidx = bitmap_zalloc(qcount, GFP_KERNEL);
 	if (!pf->af_xdp_zc_qidx) {
 		err = -ENOMEM;
-		goto err_sriov_cleannup;
+		goto err_dl_unregister;
 	}
 
 #ifdef CONFIG_DCB
@@ -3310,10 +3310,11 @@ static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 err_free_zc_bmap:
 	bitmap_free(pf->af_xdp_zc_qidx);
 #endif
+err_dl_unregister:
+	otx2_unregister_dl(pf);
 err_sriov_cleannup:
 	otx2_sriov_vfcfg_cleanup(pf);
-err_pf_sriov_init:
-	otx2_unregister_dl(pf);
+err_shutdown_tc:
 	otx2_shutdown_tc(pf);
 err_mcam_flow_del:
 	otx2_mcam_flow_del(pf);
-- 
2.34.1


^ permalink raw reply related

* [PATCH] docs/zh_TW: replace 接口 with 介面 in stable-api-nonsense.rst
From: panzhipop @ 2026-06-03 19:34 UTC (permalink / raw)
  To: Hu Haowen; +Cc: Jonathan Corbet, Shuah Khan, linux-doc, linux-kernel, panzhipop

In Taiwan's standard terminology, as defined by the National Academy
for Educational Research (NAER) term bank (https://terms.naer.edu.tw/),
the correct Traditional Chinese translation for "interface" is "介面",
not "接口" (which is used in Simplified Chinese/Mainland China).

Update the zh_TW translation of stable-api-nonsense.rst to use
the proper Taiwanese terminology.

Signed-off-by: panzhipop <kipp455187@gmail.com>
---
 .../zh_TW/process/stable-api-nonsense.rst     | 62 +++++++++----------
 1 file changed, 31 insertions(+), 31 deletions(-)

diff --git a/Documentation/translations/zh_TW/process/stable-api-nonsense.rst b/Documentation/translations/zh_TW/process/stable-api-nonsense.rst
index 4b8597fed5ae..a21daf29da10 100644
--- a/Documentation/translations/zh_TW/process/stable-api-nonsense.rst
+++ b/Documentation/translations/zh_TW/process/stable-api-nonsense.rst
@@ -14,21 +14,21 @@
         中文版校譯者: 李陽  Li Yang <leoyang.li@nxp.com>
                       胡皓文 Hu Haowen <2023002089@link.tyut.edu.cn>
 
-Linux 內核驅動接口
+Linux 內核驅動介面
 ==================
 
-寫作本文檔的目的,是爲了解釋爲什麼Linux既沒有二進制內核接口,也沒有穩定
-的內核接口。這裏所說的內核接口,是指內核裏的接口,而不是內核和用戶空間
-的接口。內核到用戶空間的接口,是提供給應用程序使用的系統調用,系統調用
+寫作本文檔的目的,是爲了解釋爲什麼Linux既沒有二進制內核介面,也沒有穩定
+的內核介面。這裏所說的內核介面,是指內核裏的介面,而不是內核和用戶空間
+的介面。內核到用戶空間的介面,是提供給應用程序使用的系統調用,系統調用
 在歷史上幾乎沒有過變化,將來也不會有變化。我有一些老應用程序是在0.9版本
 或者更早版本的內核上編譯的,在使用2.6版本內核的Linux發佈上依然用得很好
-。用戶和應用程序作者可以將這個接口看成是穩定的。
+。用戶和應用程序作者可以將這個介面看成是穩定的。
 
 
 執行綱要
 --------
 
-你也許以爲自己想要穩定的內核接口,但是你不清楚你要的實際上不是它。你需
+你也許以爲自己想要穩定的內核介面,但是你不清楚你要的實際上不是它。你需
 要的其實是穩定的驅動程序,而你只有將驅動程序放到公版內核的源代碼樹裏,
 纔有可能達到這個目的。而且這樣做還有很多其它好處,正是因爲這些好處使得
 Linux能成爲強壯,穩定,成熟的操作系統,這也是你最開始選擇Linux的原因。
@@ -37,8 +37,8 @@ Linux能成爲強壯,穩定,成熟的操作系統,這也是你最開始選
 入門
 -----
 
-只有那些寫驅動程序的“怪人”纔會擔心內核接口的改變,對廣大用戶來說,既
-看不到內核接口,也不需要去關心它。
+只有那些寫驅動程序的“怪人”纔會擔心內核介面的改變,對廣大用戶來說,既
+看不到內核介面,也不需要去關心它。
 
 首先,我不打算討論關於任何非GPL許可的內核驅動的法律問題,這些非GPL許可
 的驅動程序包括不公開源代碼,隱藏源代碼,二進制或者是用源代碼包裝,或者
@@ -46,14 +46,14 @@ Linux能成爲強壯,穩定,成熟的操作系統,這也是你最開始選
 詢律師,我只是一個程序員,所以我只打算探討技術問題(不是小看法律問題,
 法律問題很實際,並且需要一直關注)。
 
-既然只談技術問題,我們就有了下面兩個主題:二進制內核接口和穩定的內核源
-代碼接口。這兩個問題是互相關聯的,讓我們先解決掉二進制接口的問題。
+既然只談技術問題,我們就有了下面兩個主題:二進制內核介面和穩定的內核源
+代碼介面。這兩個問題是互相關聯的,讓我們先解決掉二進制介面的問題。
 
 
-二進制內核接口
+二進制內核介面
 --------------
-假如我們有一個穩定的內核源代碼接口,那麼自然而然的,我們就擁有了穩定的
-二進制接口,是這樣的嗎?錯。讓我們看看關於Linux內核的幾點事實:
+假如我們有一個穩定的內核源代碼介面,那麼自然而然的,我們就擁有了穩定的
+二進制介面,是這樣的嗎?錯。讓我們看看關於Linux內核的幾點事實:
 
     - 取決於所用的C編譯器的版本,不同的內核數據結構裏的結構體的對齊方
       式會有差別,代碼中不同函數的表現形式也不一樣(函數是不是被inline
@@ -84,18 +84,18 @@ Linux能成爲強壯,穩定,成熟的操作系統,這也是你最開始選
 深刻的教訓...
 
 
-穩定的內核源代碼接口
+穩定的內核源代碼介面
 --------------------
 
 如果有人不將他的內核驅動程序,放入公版內核的源代碼樹,而又想讓驅動程序
 一直保持在最新的內核中可用,那麼這個話題將會變得沒完沒了。
-內核開發是持續而且快節奏的,從來都不會慢下來。內核開發人員在當前接口中
+內核開發是持續而且快節奏的,從來都不會慢下來。內核開發人員在當前介面中
 找到bug,或者找到更好的實現方式。一旦發現這些,他們就很快會去修改當前的
-接口。修改接口意味着,函數名可能會改變,結構體可能被擴充或者刪減,函數
-的參數也可能發生改變。一旦接口被修改,內核中使用這些接口的地方需要同時
+介面。修改介面意味着,函數名可能會改變,結構體可能被擴充或者刪減,函數
+的參數也可能發生改變。一旦介面被修改,內核中使用這些介面的地方需要同時
 修正,這樣才能保證所有的東西繼續工作。
 
-舉一個例子,內核的USB驅動程序接口在USB子系統的整個生命週期中,至少經歷
+舉一個例子,內核的USB驅動程序介面在USB子系統的整個生命週期中,至少經歷
 了三次重寫。這些重寫解決以下問題:
 
     - 把數據流從同步模式改成非同步模式,這個改動減少了一些驅動程序的
@@ -105,22 +105,22 @@ Linux能成爲強壯,穩定,成熟的操作系統,這也是你最開始選
       需要提供更多的參數給USB核心,以修正了很多已經被記錄在案的死鎖。
 
 這和一些封閉源代碼的操作系統形成鮮明的對比,在那些操作系統上,不得不額
-外的維護舊的USB接口。這導致了一個可能性,新的開發者依然會不小心使用舊的
-接口,以不恰當的方式編寫代碼,進而影響到操作系統的穩定性。
+外的維護舊的USB介面。這導致了一個可能性,新的開發者依然會不小心使用舊的
+介面,以不恰當的方式編寫代碼,進而影響到操作系統的穩定性。
 在上面的例子中,所有的開發者都同意這些重要的改動,在這樣的情況下修改代
-價很低。如果Linux保持一個穩定的內核源代碼接口,那麼就得創建一個新的接口
-;舊的,有問題的接口必須一直維護,給Linux USB開發者帶來額外的工作。既然
+價很低。如果Linux保持一個穩定的內核源代碼介面,那麼就得創建一個新的介面
+;舊的,有問題的介面必須一直維護,給Linux USB開發者帶來額外的工作。既然
 所有的Linux USB驅動的作者都是利用自己的時間工作,那麼要求他們去做毫無意
 義的免費額外工作,是不可能的。
 安全問題對Linux來說十分重要。一個安全問題被發現,就會在短時間內得到修
-正。在很多情況下,這將導致Linux內核中的一些接口被重寫,以從根本上避免安
-全問題。一旦接口被重寫,所有使用這些接口的驅動程序,必須同時得到修正,
+正。在很多情況下,這將導致Linux內核中的一些介面被重寫,以從根本上避免安
+全問題。一旦介面被重寫,所有使用這些介面的驅動程序,必須同時得到修正,
 以確定安全問題已經得到修復並且不可能在未來還有同樣的安全問題。如果內核
-內部接口不允許改變,那麼就不可能修復這樣的安全問題,也不可能確認這樣的
+內部介面不允許改變,那麼就不可能修復這樣的安全問題,也不可能確認這樣的
 安全問題以後不會發生。
-開發者一直在清理內核接口。如果一個接口沒有人在使用了,它就會被刪除。這
-樣可以確保內核儘可能的小,而且所有潛在的接口都會得到儘可能完整的測試
-(沒有人使用的接口是不可能得到良好的測試的)。
+開發者一直在清理內核介面。如果一個介面沒有人在使用了,它就會被刪除。這
+樣可以確保內核儘可能的小,而且所有潛在的介面都會得到儘可能完整的測試
+(沒有人使用的介面是不可能得到良好的測試的)。
 
 
 要做什麼
@@ -128,11 +128,11 @@ Linux能成爲強壯,穩定,成熟的操作系統,這也是你最開始選
 
 如果你寫了一個Linux內核驅動,但是它還不在Linux源代碼樹裏,作爲一個開發
 者,你應該怎麼做?爲每個發佈的每個版本提供一個二進制驅動,那簡直是一個
-噩夢,要跟上永遠處於變化之中的內核接口,也是一件辛苦活。
+噩夢,要跟上永遠處於變化之中的內核介面,也是一件辛苦活。
 很簡單,讓你的驅動進入內核源代碼樹(要記得我們在談論的是以GPL許可發行
 的驅動,如果你的代碼不符合GPL,那麼祝你好運,你只能自己解決這個問題了,
 你這個吸血鬼<把Andrew和Linus對吸血鬼的定義鏈接到這裏>)。當你的代碼加入
-公版內核源代碼樹之後,如果一個內核接口改變,你的驅動會直接被修改接口的
+公版內核源代碼樹之後,如果一個內核介面改變,你的驅動會直接被修改介面的
 那個人修改。保證你的驅動永遠都可以編譯通過,並且一直工作,你幾乎不需要
 做什麼事情。
 
@@ -142,7 +142,7 @@ Linux能成爲強壯,穩定,成熟的操作系統,這也是你最開始選
     - 其他人會給驅動添加新特性。
     - 其他人會找到驅動中的bug並修復。
     - 其他人會在驅動中找到性能優化的機會。
-    - 當外部的接口的改變需要修改驅動程序的時候,其他人會修改驅動程序
+    - 當外部的介面的改變需要修改驅動程序的時候,其他人會修改驅動程序
     - 不需要聯繫任何發行商,這個驅動會自動的隨着所有的Linux發佈一起發
       布。
 
-- 
2.34.1


^ permalink raw reply related

* [PATCH net-next V2 5/7] octeontx2-af: Register devlink after SR-IOV init
From: Mark Bloch @ 2026-06-03 19:32 UTC (permalink / raw)
  To: Eric Dumazet, Jakub Kicinski, Paolo Abeni, Andrew Lunn,
	David S. Miller
  Cc: Jonathan Corbet, Shuah Khan, Jiri Pirko, Simon Horman,
	Sunil Goutham, Linu Cherian, Geetha sowjanya, hariprasad,
	Subbaraya Sundeep, Bharat Bhushan, Saeed Mahameed,
	Leon Romanovsky, Tariq Toukan, Mark Bloch, Borislav Petkov (AMD),
	Andrew Morton, Randy Dunlap, Peter Zijlstra (Intel),
	Thomas Gleixner, Petr Mladek, Tejun Heo, Vlastimil Babka,
	Feng Tang, Dave Hansen, Christian Brauner, Dapeng Mi, Kees Cook,
	Marco Elver, Eric Biggers, Li RongQing, Paul E. McKenney,
	Ethan Nelson-Moore, linux-doc, linux-kernel, netdev, linux-rdma
In-Reply-To: <20260603193259.3412464-1-mbloch@nvidia.com>

A later patch makes devlink registration the point where devlink core may
call eswitch_mode_set() to apply a boot-time default eswitch mode.

Move octeontx2 AF devlink registration after SR-IOV is enabled and the
representor switch lock is initialized, so the AF eswitch mode set path
sees the state it depends on.

If devlink registration fails after SR-IOV setup, unregister interrupts
before disabling SR-IOV. This keeps the AF-VF mailbox IRQ handlers
synchronized before the AF-VF mailbox workqueue is destroyed.

Signed-off-by: Mark Bloch <mbloch@nvidia.com>
---
 .../net/ethernet/marvell/octeontx2/af/rvu.c   | 24 ++++++++++---------
 1 file changed, 13 insertions(+), 11 deletions(-)

diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
index 3cf131508ecf..c2b52eb4ffab 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
@@ -3545,6 +3545,7 @@ static void rvu_update_module_params(struct rvu *rvu)
 static int rvu_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
 	struct device *dev = &pdev->dev;
+	bool sriov_done = false;
 	struct rvu *rvu;
 	int    err;
 
@@ -3634,26 +3635,27 @@ static int rvu_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 		goto err_flr;
 	}
 
-	err = rvu_register_dl(rvu);
-	if (err) {
-		dev_err(dev, "%s: Failed to register devlink\n", __func__);
-		goto err_irq;
-	}
-
 	rvu_setup_rvum_blk_revid(rvu);
 
 	/* Enable AF's VFs (if any) */
 	err = rvu_enable_sriov(rvu);
 	if (err) {
 		dev_err(dev, "%s: Failed to enable sriov\n", __func__);
-		goto err_dl;
+		goto err_irq;
+	}
+	sriov_done = true;
+
+	mutex_init(&rvu->rswitch.switch_lock);
+
+	err = rvu_register_dl(rvu);
+	if (err) {
+		dev_err(dev, "%s: Failed to register devlink\n", __func__);
+		goto err_irq;
 	}
 
 	/* Initialize debugfs */
 	rvu_dbg_init(rvu);
 
-	mutex_init(&rvu->rswitch.switch_lock);
-
 	if (rvu->fwdata)
 		ptp_start(rvu, rvu->fwdata->sclk, rvu->fwdata->ptp_ext_clk_rate,
 			  rvu->fwdata->ptp_ext_tstamp);
@@ -3662,10 +3664,10 @@ static int rvu_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	rvu_alloc_cint_qint_mem(rvu, &rvu->pf[RVU_AFPF], BLKADDR_NIX0,
 				(rvu->hw->block[BLKADDR_NIX0].lf.max));
 	return 0;
-err_dl:
-	rvu_unregister_dl(rvu);
 err_irq:
 	rvu_unregister_interrupts(rvu);
+	if (sriov_done)
+		rvu_disable_sriov(rvu);
 err_flr:
 	rvu_flr_wq_destroy(rvu);
 err_mbox:
-- 
2.34.1


^ permalink raw reply related

* [PATCH net-next V2 4/7] net/mlx5: Register devlink after device init
From: Mark Bloch @ 2026-06-03 19:32 UTC (permalink / raw)
  To: Eric Dumazet, Jakub Kicinski, Paolo Abeni, Andrew Lunn,
	David S. Miller
  Cc: Jonathan Corbet, Shuah Khan, Jiri Pirko, Simon Horman,
	Sunil Goutham, Linu Cherian, Geetha sowjanya, hariprasad,
	Subbaraya Sundeep, Bharat Bhushan, Saeed Mahameed,
	Leon Romanovsky, Tariq Toukan, Mark Bloch, Borislav Petkov (AMD),
	Andrew Morton, Randy Dunlap, Peter Zijlstra (Intel),
	Thomas Gleixner, Petr Mladek, Tejun Heo, Vlastimil Babka,
	Feng Tang, Dave Hansen, Christian Brauner, Dapeng Mi, Kees Cook,
	Marco Elver, Eric Biggers, Li RongQing, Paul E. McKenney,
	Ethan Nelson-Moore, linux-doc, linux-kernel, netdev, linux-rdma,
	Shay Drori
In-Reply-To: <20260603193259.3412464-1-mbloch@nvidia.com>

devl_register() makes the devlink instance visible to userspace. A later
patch also makes registration the point where devlink core may call
eswitch_mode_set() to apply a boot-time default eswitch mode.

Move mlx5 devlink registration after mlx5 device initialization completes,
including the lightweight init path, so registration-time devlink
operations see initialized driver state.

Move devl_unregister() before the matching teardown paths, so unregister
notifications are emitted from devl_unregister() before mlx5 removes the
devlink objects.

Add a devl-locked uninit helper so failed nested devlink setup can unwind
the initialized device before the instance is registered.

Reviewed-by: Shay Drori <shayd@nvidia.com>
Signed-off-by: Mark Bloch <mbloch@nvidia.com>
---
 .../net/ethernet/mellanox/mlx5/core/main.c    | 34 ++++++++++++++-----
 1 file changed, 25 insertions(+), 9 deletions(-)

diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c
index 0c6e4efe38c8..ab3d3ff10f1a 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c
@@ -1455,31 +1455,40 @@ int mlx5_init_one_devl_locked(struct mlx5_core_dev *dev)
 	return err;
 }
 
+static void mlx5_uninit_one_devl_locked(struct mlx5_core_dev *dev);
+
 int mlx5_init_one(struct mlx5_core_dev *dev)
 {
 	struct devlink *devlink = priv_to_devlink(dev);
 	int err;
 
 	devl_lock(devlink);
+	err = mlx5_init_one_devl_locked(dev);
+	if (err)
+		goto unlock;
+
 	if (dev->shd) {
 		err = devl_nested_devlink_set(dev->shd, devlink);
 		if (err)
-			goto unlock;
+			goto err_uninit;
 	}
+
 	devl_register(devlink);
-	err = mlx5_init_one_devl_locked(dev);
-	if (err)
-		devl_unregister(devlink);
+	devl_unlock(devlink);
+	return 0;
+
+err_uninit:
+	mlx5_uninit_one_devl_locked(dev);
 unlock:
 	devl_unlock(devlink);
 	return err;
 }
 
-void mlx5_uninit_one(struct mlx5_core_dev *dev)
+static void mlx5_uninit_one_devl_locked(struct mlx5_core_dev *dev)
 {
 	struct devlink *devlink = priv_to_devlink(dev);
 
-	devl_lock(devlink);
+	devl_assert_locked(devlink);
 	mutex_lock(&dev->intf_state_mutex);
 
 	mlx5_hwmon_dev_unregister(dev);
@@ -1501,7 +1510,15 @@ void mlx5_uninit_one(struct mlx5_core_dev *dev)
 	mlx5_function_teardown(dev, true);
 out:
 	mutex_unlock(&dev->intf_state_mutex);
+}
+
+void mlx5_uninit_one(struct mlx5_core_dev *dev)
+{
+	struct devlink *devlink = priv_to_devlink(dev);
+
+	devl_lock(devlink);
 	devl_unregister(devlink);
+	mlx5_uninit_one_devl_locked(dev);
 	devl_unlock(devlink);
 }
 
@@ -1635,7 +1652,6 @@ int mlx5_init_one_light(struct mlx5_core_dev *dev)
 	int err;
 
 	devl_lock(devlink);
-	devl_register(devlink);
 	dev->state = MLX5_DEVICE_STATE_UP;
 	err = mlx5_function_enable(dev, true, mlx5_tout_ms(dev, FW_PRE_INIT_TIMEOUT));
 	if (err) {
@@ -1655,6 +1671,7 @@ int mlx5_init_one_light(struct mlx5_core_dev *dev)
 		goto query_hca_caps_err;
 	}
 
+	devl_register(devlink);
 	devl_unlock(devlink);
 	return 0;
 
@@ -1662,7 +1679,6 @@ int mlx5_init_one_light(struct mlx5_core_dev *dev)
 	mlx5_function_disable(dev, true);
 out:
 	dev->state = MLX5_DEVICE_STATE_INTERNAL_ERROR;
-	devl_unregister(devlink);
 	devl_unlock(devlink);
 	return err;
 }
@@ -1672,8 +1688,8 @@ void mlx5_uninit_one_light(struct mlx5_core_dev *dev)
 	struct devlink *devlink = priv_to_devlink(dev);
 
 	devl_lock(devlink);
-	mlx5_devlink_params_unregister(priv_to_devlink(dev));
 	devl_unregister(devlink);
+	mlx5_devlink_params_unregister(priv_to_devlink(dev));
 	devl_unlock(devlink);
 	if (dev->state != MLX5_DEVICE_STATE_UP)
 		return;
-- 
2.34.1


^ permalink raw reply related

* [PATCH net-next V2 2/7] netdevsim: Register devlink after device init
From: Mark Bloch @ 2026-06-03 19:32 UTC (permalink / raw)
  To: Eric Dumazet, Jakub Kicinski, Paolo Abeni, Andrew Lunn,
	David S. Miller
  Cc: Jonathan Corbet, Shuah Khan, Jiri Pirko, Simon Horman,
	Sunil Goutham, Linu Cherian, Geetha sowjanya, hariprasad,
	Subbaraya Sundeep, Bharat Bhushan, Saeed Mahameed,
	Leon Romanovsky, Tariq Toukan, Mark Bloch, Borislav Petkov (AMD),
	Andrew Morton, Randy Dunlap, Peter Zijlstra (Intel),
	Thomas Gleixner, Petr Mladek, Tejun Heo, Vlastimil Babka,
	Feng Tang, Dave Hansen, Christian Brauner, Dapeng Mi, Kees Cook,
	Marco Elver, Eric Biggers, Li RongQing, Paul E. McKenney,
	Ethan Nelson-Moore, linux-doc, linux-kernel, netdev, linux-rdma
In-Reply-To: <20260603193259.3412464-1-mbloch@nvidia.com>

devl_register() makes the devlink instance visible to userspace. A later
patch also makes registration the point where devlink core may call
eswitch_mode_set() to apply a boot-time default eswitch mode.

Move netdevsim registration after all objects (resources, params, regions,
traps, debugfs etc) are initialized, and after the initial eswitch mode is
set to legacy.

Move devl_unregister() to the beginning of nsim_drv_remove(), before those
devlink objects are torn down. This keeps devlink register/unregister as
the notification barrier and makes the later object teardown paths run
after devlink is no longer registered, so they do not emit their own
netlink DEL notifications.

Signed-off-by: Mark Bloch <mbloch@nvidia.com>
---
 drivers/net/netdevsim/dev.c | 15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/drivers/net/netdevsim/dev.c b/drivers/net/netdevsim/dev.c
index aed9ad5f1b43..7cf4102b049e 100644
--- a/drivers/net/netdevsim/dev.c
+++ b/drivers/net/netdevsim/dev.c
@@ -1680,13 +1680,9 @@ int nsim_drv_probe(struct nsim_bus_dev *nsim_bus_dev)
 		goto err_devlink_unlock;
 	}
 
-	err = devl_register(devlink);
-	if (err)
-		goto err_vfc_free;
-
 	err = nsim_dev_resources_register(devlink);
 	if (err)
-		goto err_dl_unregister;
+		goto err_vfc_free;
 
 	err = devl_params_register(devlink, nsim_devlink_params,
 				   ARRAY_SIZE(nsim_devlink_params));
@@ -1733,9 +1729,14 @@ int nsim_drv_probe(struct nsim_bus_dev *nsim_bus_dev)
 		goto err_hwstats_exit;
 
 	nsim_dev->esw_mode = DEVLINK_ESWITCH_MODE_LEGACY;
+	err = devl_register(devlink);
+	if (err)
+		goto err_port_del_all;
 	devl_unlock(devlink);
 	return 0;
 
+err_port_del_all:
+	nsim_dev_port_del_all(nsim_dev);
 err_hwstats_exit:
 	nsim_dev_hwstats_exit(nsim_dev);
 err_psample_exit:
@@ -1757,8 +1758,6 @@ int nsim_drv_probe(struct nsim_bus_dev *nsim_bus_dev)
 			       ARRAY_SIZE(nsim_devlink_params));
 err_resource_unregister:
 	devl_resources_unregister(devlink);
-err_dl_unregister:
-	devl_unregister(devlink);
 err_vfc_free:
 	kfree(nsim_dev->vfconfigs);
 err_devlink_unlock:
@@ -1797,6 +1796,7 @@ void nsim_drv_remove(struct nsim_bus_dev *nsim_bus_dev)
 	struct devlink *devlink = priv_to_devlink(nsim_dev);
 
 	devl_lock(devlink);
+	devl_unregister(devlink);
 	nsim_dev_reload_destroy(nsim_dev);
 
 	nsim_bpf_dev_exit(nsim_dev);
@@ -1804,7 +1804,6 @@ void nsim_drv_remove(struct nsim_bus_dev *nsim_bus_dev)
 	devl_params_unregister(devlink, nsim_devlink_params,
 			       ARRAY_SIZE(nsim_devlink_params));
 	devl_resources_unregister(devlink);
-	devl_unregister(devlink);
 	kfree(nsim_dev->vfconfigs);
 	kfree(nsim_dev->fa_cookie);
 	mutex_destroy(&nsim_dev->progs_list_lock);
-- 
2.34.1


^ permalink raw reply related

* [PATCH net-next V2 3/7] net/mlx5: Clear FW reset-in-progress bit before reload
From: Mark Bloch @ 2026-06-03 19:32 UTC (permalink / raw)
  To: Eric Dumazet, Jakub Kicinski, Paolo Abeni, Andrew Lunn,
	David S. Miller
  Cc: Jonathan Corbet, Shuah Khan, Jiri Pirko, Simon Horman,
	Sunil Goutham, Linu Cherian, Geetha sowjanya, hariprasad,
	Subbaraya Sundeep, Bharat Bhushan, Saeed Mahameed,
	Leon Romanovsky, Tariq Toukan, Mark Bloch, Borislav Petkov (AMD),
	Andrew Morton, Randy Dunlap, Peter Zijlstra (Intel),
	Thomas Gleixner, Petr Mladek, Tejun Heo, Vlastimil Babka,
	Feng Tang, Dave Hansen, Christian Brauner, Dapeng Mi, Kees Cook,
	Marco Elver, Eric Biggers, Li RongQing, Paul E. McKenney,
	Ethan Nelson-Moore, linux-doc, linux-kernel, netdev, linux-rdma,
	Shay Drori, Moshe Shemesh
In-Reply-To: <20260603193259.3412464-1-mbloch@nvidia.com>

mlx5 sets MLX5_FW_RESET_FLAGS_RESET_IN_PROGRESS when acknowledging a sync
reset request. This bit blocks devlink reload and other devlink operations
while the firmware reset is running, but it was kept set until after the
driver reload finished.

Clear the reset-in-progress bit once the reset unload flow is done and PCI
access is back, before reloading the device. For a reset initiated through
devlink, clear it before completing the reload waiter. For a reset reported
through an asynchronous firmware event, keep the unload flow outside
devl_lock, then take devl_lock before clearing the bit and reloading
through the devl-locked load helper.

Reviewed-by: Shay Drori <shayd@nvidia.com>
Reviewed-by: Moshe Shemesh <moshe@nvidia.com>
Signed-off-by: Mark Bloch <mbloch@nvidia.com>
---
 .../ethernet/mellanox/mlx5/core/fw_reset.c    | 28 +++++++++++--------
 1 file changed, 17 insertions(+), 11 deletions(-)

diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c b/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c
index 07440c58713a..7283e5b49eed 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c
@@ -238,24 +238,30 @@ static void mlx5_fw_reset_complete_reload(struct mlx5_core_dev *dev)
 {
 	struct mlx5_fw_reset *fw_reset = dev->priv.fw_reset;
 	struct devlink *devlink = priv_to_devlink(dev);
+	int err;
 
 	/* if this is the driver that initiated the fw reset, devlink completed the reload */
 	if (test_bit(MLX5_FW_RESET_FLAGS_PENDING_COMP, &fw_reset->reset_flags)) {
+		clear_bit(MLX5_FW_RESET_FLAGS_RESET_IN_PROGRESS,
+			  &fw_reset->reset_flags);
 		complete(&fw_reset->done);
-	} else {
-		mlx5_sync_reset_unload_flow(dev, false);
-		if (mlx5_health_wait_pci_up(dev))
-			mlx5_core_err(dev, "reset reload flow aborted, PCI reads still not working\n");
-		else
-			mlx5_load_one(dev, true);
-		devl_lock(devlink);
-		devlink_remote_reload_actions_performed(devlink, 0,
-							BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT) |
-							BIT(DEVLINK_RELOAD_ACTION_FW_ACTIVATE));
-		devl_unlock(devlink);
+		return;
 	}
 
+	mlx5_sync_reset_unload_flow(dev, false);
+	err = mlx5_health_wait_pci_up(dev);
+
+	devl_lock(devlink);
 	clear_bit(MLX5_FW_RESET_FLAGS_RESET_IN_PROGRESS, &fw_reset->reset_flags);
+	if (err)
+		mlx5_core_err(dev, "reset reload flow aborted, PCI reads still not working\n");
+	else
+		mlx5_load_one_devl_locked(dev, true);
+
+	devlink_remote_reload_actions_performed(devlink, 0,
+						BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT) |
+						BIT(DEVLINK_RELOAD_ACTION_FW_ACTIVATE));
+	devl_unlock(devlink);
 }
 
 static void mlx5_stop_sync_reset_poll(struct mlx5_core_dev *dev)
-- 
2.34.1


^ permalink raw reply related

* [PATCH net-next V2 1/7] devlink: Skip health recover notifications before register
From: Mark Bloch @ 2026-06-03 19:32 UTC (permalink / raw)
  To: Eric Dumazet, Jakub Kicinski, Paolo Abeni, Andrew Lunn,
	David S. Miller
  Cc: Jonathan Corbet, Shuah Khan, Jiri Pirko, Simon Horman,
	Sunil Goutham, Linu Cherian, Geetha sowjanya, hariprasad,
	Subbaraya Sundeep, Bharat Bhushan, Saeed Mahameed,
	Leon Romanovsky, Tariq Toukan, Mark Bloch, Borislav Petkov (AMD),
	Andrew Morton, Randy Dunlap, Peter Zijlstra (Intel),
	Thomas Gleixner, Petr Mladek, Tejun Heo, Vlastimil Babka,
	Feng Tang, Dave Hansen, Christian Brauner, Dapeng Mi, Kees Cook,
	Marco Elver, Eric Biggers, Li RongQing, Paul E. McKenney,
	Ethan Nelson-Moore, linux-doc, linux-kernel, netdev, linux-rdma
In-Reply-To: <20260603193259.3412464-1-mbloch@nvidia.com>

devlink health reports can be generated before the devlink instance is
registered. This can happen during driver initialization when a driver
creates health reporters early and its health polling detects an error
before devlink_register() is reached.

devlink health still records the report state and counters in that case,
but userspace cannot observe the devlink instance yet and there is no
registered handle to notify through. Skip the netlink notification while
the devlink instance is not registered instead of asserting registration.

This keeps later userspace queries useful after registration while avoiding
a warning from early health reports.

Signed-off-by: Mark Bloch <mbloch@nvidia.com>
---
 net/devlink/health.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/net/devlink/health.c b/net/devlink/health.c
index ea7a334e939b..376e79497771 100644
--- a/net/devlink/health.c
+++ b/net/devlink/health.c
@@ -513,9 +513,8 @@ static void devlink_recover_notify(struct devlink_health_reporter *reporter,
 	int err;
 
 	WARN_ON(cmd != DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
-	ASSERT_DEVLINK_REGISTERED(devlink);
 
-	if (!devlink_nl_notify_need(devlink))
+	if (!__devl_is_registered(devlink) || !devlink_nl_notify_need(devlink))
 		return;
 
 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
-- 
2.34.1


^ permalink raw reply related

* [PATCH net-next V2 0/7] devlink: Add boot-time eswitch mode defaults
From: Mark Bloch @ 2026-06-03 19:32 UTC (permalink / raw)
  To: Eric Dumazet, Jakub Kicinski, Paolo Abeni, Andrew Lunn,
	David S. Miller
  Cc: Jonathan Corbet, Shuah Khan, Jiri Pirko, Simon Horman,
	Sunil Goutham, Linu Cherian, Geetha sowjanya, hariprasad,
	Subbaraya Sundeep, Bharat Bhushan, Saeed Mahameed,
	Leon Romanovsky, Tariq Toukan, Mark Bloch, Borislav Petkov (AMD),
	Andrew Morton, Randy Dunlap, Peter Zijlstra (Intel),
	Thomas Gleixner, Petr Mladek, Tejun Heo, Vlastimil Babka,
	Feng Tang, Dave Hansen, Christian Brauner, Dapeng Mi, Kees Cook,
	Marco Elver, Eric Biggers, Li RongQing, Paul E. McKenney,
	Ethan Nelson-Moore, linux-doc, linux-kernel, netdev, linux-rdma

This series adds a devlink_eswitch_mode= kernel command line parameter for
applying a default devlink eswitch mode during device initialization.

Following the discussion with Jakub[1] and the feedback on the RFC
postings, this version keeps the scope limited to a boot-time devlink
eswitch mode default only.

The option selects either all devlink handles or an explicit comma
separated handle list:

devlink_eswitch_mode=[*]:switchdev
devlink_eswitch_mode=[pci/0000:08:00.0,pci/0000:09:00.1]:switchdev_inactive

The supported modes are legacy, switchdev and switchdev_inactive. The
selected mode is applied through the existing eswitch_mode_set() devlink
operation, the same operation used by the devlink eswitch mode command.

The preparatory patches move registration points that expose the devlink
instance before the driver is ready for a registration-time eswitch mode
change. Where registration is moved later, the matching unregister path is
moved earlier so unregister notifications are sent from devl_unregister()
before object teardown. The final patch adds the parser and applies the
default from devlink core when a matching instance is registered and after
a successful devlink reload that performed DRIVER_REINIT.

Patch 1 skips devlink health recovery notifications while a devlink
instance is not registered. Health state and counters are still updated,
but there is no registered instance for userspace to observe or receive
notifications from yet. This lets drivers move registration later without
hitting health notification registration assertions during early
initialization.

Patch 2 moves netdevsim devlink registration after device initialization,
so registration-time defaults can call eswitch_mode_set() after simulator
state is ready. It also unregisters devlink before netdevsim tears down the
objects that were registered before devlink became visible.

Patch 3 clears the mlx5 FW reset-in-progress bit before reloading after a
firmware reset.

Patch 4 moves mlx5 devlink registration after device initialization,
including the lightweight init path, and moves unregister before the
matching teardown.

Patch 5 moves octeontx2 AF devlink registration after SR-IOV setup and
switch lock initialization.

Patch 6 moves octeontx2 PF devlink registration after PF SR-IOV state
setup.

Patch 7 adds the devlink_eswitch_mode= parser, documentation,
registration-time default application and successful reload default
application.

Changelog:

v1 -> v2:

- Move default eswitch mode application into devlink core. The default is
  now applied during devlink registration and after a successful devlink
  reload that performed DRIVER_REINIT.

- Remove the exported devl_apply_default_esw_mode() driver API and the mlx5
  driver-side call to it.

- Skip devlink health recovery notifications while the devlink instance is
  not registered, so drivers can move registration later without early
  health work hitting registration assertions.

- Move mlx5 devlink registration after device initialization, including the
  lightweight init path, so the core can apply the default through the
  normal registration flow.

- Move the matching netdevsim and mlx5 unregister paths before object
  teardown, so unregister notifications come from devl_unregister() and the
  later object teardown paths run while the devlink instance is no longer
  registered.

- Add registration-ordering preparation patches for netdevsim and octeontx2
  AF/PF, so their eswitch state is ready before registration-time defaults
  may call eswitch_mode_set().

[1] https://lore.kernel.org/all/20260502184153.4fd8d06f@kernel.org/
RFC V1 : https://lore.kernel.org/all/20260506123739.1959770-1-mbloch@nvidia.com/
RFC V2 : https://lore.kernel.org/all/20260510185424.2041415-1-mbloch@nvidia.com/
v1     : https://lore.kernel.org/all/20260521072434.362624-1-tariqt@nvidia.com/

Signed-off-by: Mark Bloch <mbloch@nvidia.com>

Mark Bloch (7):
  devlink: Skip health recover notifications before register
  netdevsim: Register devlink after device init
  net/mlx5: Clear FW reset-in-progress bit before reload
  net/mlx5: Register devlink after device init
  octeontx2-af: Register devlink after SR-IOV init
  octeontx2-pf: Register devlink after SR-IOV state init
  devlink: Add eswitch mode boot defaults

 .../admin-guide/kernel-parameters.txt         |  25 ++
 .../networking/devlink/devlink-defaults.rst   |  80 ++++++
 Documentation/networking/devlink/index.rst    |   1 +
 .../net/ethernet/marvell/octeontx2/af/rvu.c   |  24 +-
 .../ethernet/marvell/octeontx2/nic/otx2_pf.c  |  17 +-
 .../ethernet/mellanox/mlx5/core/fw_reset.c    |  28 +-
 .../net/ethernet/mellanox/mlx5/core/main.c    |  34 ++-
 drivers/net/netdevsim/dev.c                   |  15 +-
 net/devlink/core.c                            | 261 ++++++++++++++++++
 net/devlink/dev.c                             |   3 +
 net/devlink/devl_internal.h                   |   1 +
 net/devlink/health.c                          |   3 +-
 12 files changed, 443 insertions(+), 49 deletions(-)
 create mode 100644 Documentation/networking/devlink/devlink-defaults.rst


base-commit: dfcc2ff12925d99e858eaf539eaa4aaaf81fe2a6
-- 
2.34.1


^ permalink raw reply

* Re: [RFC PATCH 0/5] mm, swap: Virtual Swap Space (Swap Table Edition)
From: Nhat Pham @ 2026-06-03 19:26 UTC (permalink / raw)
  To: Yosry Ahmed
  Cc: kasong, Liam.Howlett, akpm, apopple, axelrasmussen, baohua,
	baolin.wang, bhe, byungchul, cgroups, chengming.zhou, chrisl,
	corbet, david, dev.jain, gourry, hannes, hughd, jannh,
	joshua.hahnjy, lance.yang, lenb, linux-doc, linux-kernel,
	linux-mm, linux-pm, lorenzo.stoakes, matthew.brost, mhocko,
	muchun.song, npache, pavel, peterx, peterz, pfalcato, rafael,
	rakie.kim, roman.gushchin, rppt, ryan.roberts, shakeel.butt,
	shikemeng, surenb, tglx, vbabka, weixugc, ying.huang, yosry.ahmed,
	yuanchu, zhengqi.arch, ziy, kernel-team, riel, haowenchao22
In-Reply-To: <aiB2sHqxcBAJrTkP@google.com>

On Wed, Jun 3, 2026 at 11:58 AM Yosry Ahmed <yosry@kernel.org> wrote:
>
> > > I assume the main reason here is to avoid the extra overhead if
> > > everything uses vswap, which would mainly be the reverse mapping
> > > overhead? I guess there's also some simplicity that comes from reusing
> > > the swap info infra as a whole, including the swap table.
> >
> > Yeah it helps a lot that we don't have to rewrite the whole allocator
> > and swap entry reference counting logic again :)
>
> I specifically meant using a full swap info thing for the physical swap
> device even when it's behind vswap. That seems like an overkill, and we
> don't need things like the swap entry reference coutning. We probably
> just need a bitmap and a reverse mapping.
>
> So I am assuming the main reason why we are not doing that (at least for
> now) is simplicity?

Mostly.

FWIW, we're pretty close to full deduplication. Right now, physical
swap clusters have a couple of fields that are not needed when they're
backing a vswap cluster:

1. The main swap table (which houses swap cache, swap shadow, and
reference counting): I repurpose it for the rmap :) It's an array of
unsigned long, which works for rmap.

2. memcg_table: still duplicated, but I think I can make sure this is
not allocated if physical swap clusters only back vswap entries. I
have a prototype that I'm testing for this.

3. The zeromap field: this is actually not allocated in 64 bit
architecture, IIUC, which is what I'm gating CONFIG_VSWAP on. If we
extend vswap to supporting 32 bits, this can also be dynamically
allocated.

4. Extend table - this is for the swap count overfills, and already
dynamically allocated.

>
> > >
> > > I don't like that the code bifurcates for vswap vs. normal swap entries
> > > though. Not sure if this is an issue that can be fixed with proper
> > > abstractions to hide it, or if the design needs modifications. I was
> > > honestly really hoping we don't end up with this. I was hoping that the
> > > physical swap device no longer uses a full swap table and all, and
> > > everything goes through vswap.
> > >
> > > I hoping that if redirection isn't needed (e.g. zswap is disabled),
> > > vswap can directly encode the physical swap slot so that the reverse
> > > mapping isn't needed -- so we avoid the overhead without keeping the
> > > physical swap device using a fully-fledged swap table.
> >
> > Can you expand on "vswap can directly encode the physical swap slot"?
> > I'm not sure I follow here.
>
> I meant that if redirection is not needed (e.g. zswap is disabled), then
> instead of having a vswap device pointing at a physical swap device, we
> can just the data (e.g. phyiscal swap slot) in the vswap device
> directly. Then we don't need a full swap info thing and swap table for
> the physical swap device.
>
> This directly ties into my question above, about why we have a
> fully-fledged swap info thing for the physical swap device when using
> vswap.

See above.

>
> > >
> > > All that being said, perhaps I am too out of touch with the code to
> > > realize it's simply not possible.
> > >
> > > Honestly, if the main reason we can't have a single swap table for vswap
> > > is saving 8 bytes on the reverse mapping, it sounds like a weak-ish
> > > argument, even if we can't optimize the reverse mapping away. But maybe
> > > I am also out of touch with RAM prices :)
> >
> > In terms of the space overhead I do agree, FWIW :)
> >
> > I think the other concern is the indirection overhead with going
> > through the xarray for every swap operation, hence the per-CPU vswap
> > cluster lookup caching idea:
> >
> > https://lore.kernel.org/all/20260505153854.1612033-23-nphamcs@gmail.com/
>
> Right, but we should already avoid the xarray with the swap table
> design, right? We just have one swap table pointing to another
> essentially?

Hmmm, I don't quite follow your suggestion here.

For normal swap devices, we organize the space into clusters, and
maintain them in various lists (free, nonfull, full etc.). The only
difference with a vswap device is we do not have a free list, and have
the clusters themselves dynamically allocated.

If we're using vswap, we will incur the xarray overhead. There's no
avoiding that if we want a dynamic indirection layer. We can of course
revisit this data structure design later.

So yes, it will be one swap table (vswap cluster) pointing to another
swap table (pswap cluster). But to get to the first swap table, you
will have to go through xarray still.

>
> > >
> > > I at least hope that, the current design is not painting us into a
> > > corner (e.g. through userspace interfaces), and we can still achieve a
> > > vswap-for-all implementation in the future (maybe that's what you have
> > > in mind already?).
> >
> > That's still my plan. Operationally speaking, I want to make this
> > completely transparent to users, with minimal to no performance
> > overhead.
>
> So if CONFIG_VSWAP is set all swap devices are vswap by default, right?
> Would it help with testing if it's controlled by a boot param?
>
> >
> > The next action item is to optimize for vswap-on-fast-swapfile case -
> > that was Kairui's main concerns regarding performance. I spent a lot
> > of time perfing and fixing issues for this case in v6. The issues with
> > the most egregious effects and simplest fix (vswap-less
> > swap-cache-only check for e.g) are already fixed in this new design,
> > and eventually I will move the rest (lookup caching) and more to here.
>
> So is the end goal to have vswap be the default rather than a special
> swap device? It would certainly help to include some details about that.

That is my preference - I did allude to it in "Runtime enable/disable
of the vswap device" follow-up section :) I'm a sucker for unified
paths. It kinda depends on whether we can optimize most of vswap
overhead away though - if not, then we have to maintain both paths.
Kairui, how do you feel about this?

>
> > >
> > > Aside from the swap code, the only sticking point for me is the logic
> > > bifurcation in zswap. Why does zswap need to handle vswap vs. not vswap?
> > > I thought the point of the design is to use vswap when zswap is used,
> > > and otherwise use a normal swap table. In a way, one of the goals is to
> > > make zswap a first class swap citizen, but it doesn't seem like we are
> > > achieving that?
> >
> > We already have all the machinery to make zswap completely
> > independent. Right now, if you use vswap, you'll skip the zswap's
> > internal xarray entirely, and just store a zswap entry in the virtual
> > swap cluster's vtable.
> >
> > I just haven't removed the old code for 2 reasons:
> >
> > 1. Reduce the delta on this RFC, to ease the burden for reviewers (and
> > definitely not because I'm lazy :P)
> >
> > 2. The only other practical reason is so that we can let users compile
> > with !CONFIG_VSWAP and still uses zswap on top of the old swapfile
> > setup during the transition/experimentation period for now.
> >
> > But logically and conceptually speaking, there is no reason I can come
> > up with to use zswap on without vswap. The CPU indirection overhead is
> > already partially there (since zswap uses an xarray) and further
> > optimized (cluster loopup caching etc.), as well as the space overhead
> > (vswap replaces the zswap xarray). I actually wrote a whole paragraph
> > about how we should always go for vswap if we're using zswap, but then
> > decide to remove it since there's no code for it yet.
> >
> > If folks like it, what I can do is have CONFIG_ZSWAP depends on
> > CONFIG_VSWAP, removes all the non-vswap logic, and call it a day? :)
> > Then, on the swap allocation side, if vswap allocation fail and zswap
> > writeback is disabled, we can error out early.
>
> Hmm maybe we can keep it around for now and do that after vswap
> stabilizes? It ultimately depend on how much complexity we maintain by
> allowing both.
>
> I think another problem is 32-bit, technically zswap can be used on
> 32-bit now, right? So vswap not supporitng 32-bit is a problem.

Ah shoot I forgot about that. Hmmm.

It's not impossible to make vswap support 32-bit. I did that for v6
after all. It just needs extra fields because we have fewer bits to
leverage in pointers etc., complicating the logic a bit. Follow-up
work? :)

>
> General question (for both zswap and general swap code), would a boot
> param make implementation simpler? Right now we seem to key off the swap
> device having the "vswap" flag, would it help if it was a runtime
> constant?

Hmmm, even if it's a runtime constant, both branches still have to be
there, no? Does the boot param simplify it somehow?

^ permalink raw reply

* Re: [PATCH net-next] docs: exclude driver and netdevsim bugs
From: Andrew Lunn @ 2026-06-03 19:25 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms, johannes,
	corbet, skhan, workflows, linux-doc
In-Reply-To: <20260603162943.2406080-1-kuba@kernel.org>

> +Additionally, netdev does not consider bugs to be ``net``-worthy
> +if they fulfill **all** of the following criteria:
> + - bug is in a hardware device driver;
> + - bug is either a missing error handling or is part of the error handling flow;
> + - bug was discovered by a static analysis / AI tool;
> + - bug was triggered/observed only with kernel changes or fault injection.
> +Fixes for such bugs should default to ``net-next`` and should **not** contain
> +a Fixes tag. Networking or driver maintainers may redirect such fixes to ``net``
> +at their discretion if they consider the condition to be relevant enough.

I would also stress what the stable rules say:

	It must either fix a real bug that bothers people or ...

Many of the bug fixes we are currently getting don't meet this
criteria, so are net-next material.

   Andrew

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox