* Re: [PATCH v3 1/3] PM: core: Rename module parameters prefix to "power"
From: Rafael J. Wysocki @ 2026-06-08 14:11 UTC (permalink / raw)
To: Tzung-Bi Shih
Cc: Jonathan Corbet, Rafael J. Wysocki, Greg Kroah-Hartman,
Danilo Krummrich, Shuah Khan, Pavel Machek, Len Brown, linux-doc,
linux-kernel, linux-pm, driver-core, tfiga, senozhatsky,
Randy Dunlap
In-Reply-To: <20260608021526.1023248-2-tzungbi@kernel.org>
On Mon, Jun 8, 2026 at 4:16 AM Tzung-Bi Shih <tzungbi@kernel.org> wrote:
>
> Currently, the module parameters defined in drivers/base/power/main.c
> use the default prefix "main" (derived from the filename). The prefix
> "main" is too generic and non-descriptive for power management
> parameters.
>
> Redefine MODULE_PARAM_PREFIX to "power." at the beginning of the file
> to group the module parameters under the "power" namespace instead.
> This makes the parameters more descriptive.
>
> Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
> ---
> v3:
> - No changes.
>
> v2: https://lore.kernel.org/all/20260604090756.2884671-2-tzungbi@kernel.org
> - New to the series.
>
> v1: Doesn't exist.
>
> drivers/base/power/main.c | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
> index ed48c292f575..cd864f3a2799 100644
> --- a/drivers/base/power/main.c
> +++ b/drivers/base/power/main.c
> @@ -40,6 +40,9 @@
> #include "../base.h"
> #include "power.h"
>
> +#undef MODULE_PARAM_PREFIX
> +#define MODULE_PARAM_PREFIX "power."
"power" may be confused with the power supply support, so I'd rather
use "pm" or even "pm_sleep" (in which case the "dpm_" prefix could be
dropped from the new module param name in the next patch).
> +
> typedef int (*pm_callback_t)(struct device *);
>
> /*
> --
^ permalink raw reply
* Re: [PATCH v3 2/3] PM: dpm_watchdog: Allow disabling DPM watchdog by default
From: Rafael J. Wysocki @ 2026-06-08 14:14 UTC (permalink / raw)
To: Tzung-Bi Shih
Cc: Jonathan Corbet, Rafael J. Wysocki, Greg Kroah-Hartman,
Danilo Krummrich, Shuah Khan, Pavel Machek, Len Brown, linux-doc,
linux-kernel, linux-pm, driver-core, tfiga, senozhatsky,
Randy Dunlap
In-Reply-To: <20260608021526.1023248-3-tzungbi@kernel.org>
On Mon, Jun 8, 2026 at 4:16 AM Tzung-Bi Shih <tzungbi@kernel.org> wrote:
>
> Introduce the CONFIG_DPM_WATCHDOG_DEFAULT_ENABLED Kconfig option to
> allow the device suspend/resume watchdog (DPM watchdog) to be disabled
> by default at compile time.
>
> Additionally, introduce the "dpm_watchdog_enabled" module parameter to
> allow the watchdog to be enabled or disabled at boot time (via
> "power.dpm_watchdog_enabled") and at runtime (via sysfs).
I think that the new module param is more important because the new
config option is just its default value, so I'd rearrange the
changelog.
Also, I think that the "DEFAULT_" part of the new config option name
doesn't provide any additional value, so I'd just drop it.
> This provides flexibility for systems that want the watchdog code
> compiled in but inactive by default, allowing it to be enabled only when
> needed.
>
> Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
> ---
> v3:
> - Add "PM" tag (was missing).
> - Update the format and specify dependencies in kernel-parameters.txt.
> - Update the help message in Kconfig to reflect that dpm_watchdog_enabled
> can be set at runtime as well.
>
> v2: https://lore.kernel.org/all/20260604090756.2884671-3-tzungbi@kernel.org
> - Use module parameter and bool for dpm_watchdog_enabled.
> - Use IS_ENABLED().
>
> v1: https://lore.kernel.org/all/20260528103215.505795-1-tzungbi@kernel.org
>
> Documentation/admin-guide/kernel-parameters.txt | 8 ++++++++
> drivers/base/power/main.c | 11 +++++++++++
> kernel/power/Kconfig | 10 ++++++++++
> 3 files changed, 29 insertions(+)
>
> diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
> index 00375193bd26..f2620764c28e 100644
> --- a/Documentation/admin-guide/kernel-parameters.txt
> +++ b/Documentation/admin-guide/kernel-parameters.txt
> @@ -47,6 +47,7 @@
> PCI PCI bus support is enabled.
> PCIE PCI Express support is enabled.
> PCMCIA The PCMCIA subsystem is enabled.
> + PM Power Management support is enabled.
> PNP Plug & Play support is enabled.
> PPC PowerPC architecture is enabled.
> PPT Parallel port support is enabled.
> @@ -5399,6 +5400,13 @@ Kernel parameters
> function to NULL. On Idle the CPU just reduces
> execution priority.
>
> + power.dpm_watchdog_enabled=
> + [PM] Enable or disable the device suspend/resume
> + watchdog (DPM watchdog). Requires CONFIG_PM_SLEEP and
> + CONFIG_DPM_WATCHDOG enabled.
> + Format: <bool>
> + Default value is set by CONFIG_DPM_WATCHDOG_DEFAULT_ENABLED.
> +
> ppc_strict_facility_enable
> [PPC,ENABLE] This option catches any kernel floating point,
> Altivec, VSX and SPE outside of regions specifically
> diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
> index cd864f3a2799..7822c29b7c8d 100644
> --- a/drivers/base/power/main.c
> +++ b/drivers/base/power/main.c
> @@ -534,6 +534,11 @@ module_param(dpm_watchdog_all_cpu_backtrace, bool, 0644);
> MODULE_PARM_DESC(dpm_watchdog_all_cpu_backtrace,
> "Backtrace all CPUs on DPM watchdog timeout");
>
> +static bool __read_mostly dpm_watchdog_enabled =
> + IS_ENABLED(CONFIG_DPM_WATCHDOG_DEFAULT_ENABLED);
> +module_param(dpm_watchdog_enabled, bool, 0644);
> +MODULE_PARM_DESC(dpm_watchdog_enabled, "Enable DPM watchdog");
> +
> /**
> * dpm_watchdog_handler - Driver suspend / resume watchdog handler.
> * @t: The timer that PM watchdog depends on.
> @@ -577,6 +582,9 @@ static void dpm_watchdog_set(struct dpm_watchdog *wd, struct device *dev)
> {
> struct timer_list *timer = &wd->timer;
>
> + if (!dpm_watchdog_enabled)
> + return;
> +
> wd->dev = dev;
> wd->tsk = current;
> wd->fatal = CONFIG_DPM_WATCHDOG_TIMEOUT == CONFIG_DPM_WATCHDOG_WARNING_TIMEOUT;
> @@ -595,6 +603,9 @@ static void dpm_watchdog_clear(struct dpm_watchdog *wd)
> {
> struct timer_list *timer = &wd->timer;
>
> + if (!dpm_watchdog_enabled)
> + return;
> +
> timer_delete_sync(timer);
> timer_destroy_on_stack(timer);
> }
> diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
> index 530c897311d4..b16bd159074a 100644
> --- a/kernel/power/Kconfig
> +++ b/kernel/power/Kconfig
> @@ -268,6 +268,16 @@ config DPM_WATCHDOG
> captured in pstore device for inspection in subsequent
> boot session.
>
> +config DPM_WATCHDOG_DEFAULT_ENABLED
> + bool "Enable DPM watchdog by default"
> + depends on DPM_WATCHDOG
> + default y
> + help
> + If you say Y here, the DPM watchdog will be enabled by default.
> + If you say N, it will be compiled in but disabled. It can be
> + enabled at boot time via the "power.dpm_watchdog_enabled" kernel
> + parameter or at runtime via sysfs.
> +
> config DPM_WATCHDOG_TIMEOUT
> int "Watchdog timeout to panic in seconds"
> range 1 120
> --
> 2.54.0.1099.g489fc7bff1-goog
>
^ permalink raw reply
* Re: [PATCH v8 2/6] mm/memory-failure: surface unhandlable kernel pages as -ENOTRECOVERABLE
From: Breno Leitao @ 2026-06-08 14:15 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: Miaohe Lin, linux-mm, linux-kernel, linux-doc, linux-kselftest,
linux-trace-kernel, kernel-team, Lance Yang, Andrew Morton,
Lorenzo Stoakes, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Shuah Khan, Naoya Horiguchi,
Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers,
Jonathan Corbet, Shuah Khan, Liam R. Howlett
In-Reply-To: <f1a742be-80cb-4256-b1f9-e50a0f83cb15@kernel.org>
On Fri, Jun 05, 2026 at 11:42:53AM +0200, David Hildenbrand (Arm) wrote:
> On 6/5/26 11:35, Breno Leitao wrote:
> > On Wed, Jun 03, 2026 at 10:33:04AM +0800, Miaohe Lin wrote:
> >> On 2026/6/2 17:41, David Hildenbrand (Arm) wrote:
> >>>
> >>> Races are fine. We might miss some pages, but that can happen on races either way.
> >>>
> >>>
> >>> I'd just do something like
> >>>
> >>> if (PageReserved(page))
> >>> return true;
> >>>
> >>> head = compound_head(page);
> >>
> >> If @head is split just after compound_head. And then @head is freed into buddy and re-allocated as slab
> >> page while @page is still in the buddy. We would panic on this scene as @head is PageSlab. But we were
> >> supposed to successfully handle @page. Or am I miss something?
> >
> > You're right that it is racy, but I think it is an acceptable race here.
> >
>
> I mean, any such races can currently already happen one way or the other?
>
> Really, the only way to not get races is to tryget the (compound)page,
> revalidate that the page is still part of the compound page.
>
> I'm not sure if that's really a good idea.
>
> But my memory is a bit vague in which scenarios we already hold a page reference
> here to prevent any concurrent freeing?
No, we don't hold one here in the case that matters.
HWPoisonKernelOwned() runs at the very top of get_any_page(), before
try_again: and before __get_hwpoison_page(). The first refcount taken in
the whole path is the folio_try_get() inside __get_hwpoison_page(), which
runs *after* the short-circuit.
So get_any_page() itself never holds a reference at the check -- the only way
one exists is if the caller passed MF_COUNT_INCREASED (count_increased ==
true).
So on the MCE/GHES path -- the one this panic option exists for -- no
reference is held when HWPoisonKernelOwned() does its compound_head() +
PageSlab()/PageTable()/PageLargeKmalloc() checks.
Given that, I'd rather keep it racy and take no refcount than add a
tryget + revalidate purely for this check. As I've said earleir, an operator
who enabled it has chosen to crash rather than run on corrupted memory;
mis-attributing one such rare, genuinely-poisoned page is within that contract.
^ permalink raw reply
* Re: [PATCH v3 3/3] PM: dpm_watchdog: Add sysctl interface for DPM watchdog timeouts
From: Rafael J. Wysocki @ 2026-06-08 14:22 UTC (permalink / raw)
To: Tzung-Bi Shih
Cc: Jonathan Corbet, Rafael J. Wysocki, Greg Kroah-Hartman,
Danilo Krummrich, Shuah Khan, Pavel Machek, Len Brown, linux-doc,
linux-kernel, linux-pm, driver-core, tfiga, senozhatsky,
Randy Dunlap
In-Reply-To: <20260608021526.1023248-4-tzungbi@kernel.org>
On Mon, Jun 8, 2026 at 4:16 AM Tzung-Bi Shih <tzungbi@kernel.org> wrote:
>
> Introduce sysctl knobs to allow configuring DPM watchdog timeouts at
> runtime.
>
> Currently, these timeouts are fixed at compile time via
> CONFIG_DPM_WATCHDOG_TIMEOUT and CONFIG_DPM_WATCHDOG_WARNING_TIMEOUT.
> This limits flexibility if the timeouts need to be adjusted for
> different testing scenarios or hardware behaviors without rebuilding
> the kernel.
>
> Add the following sysctl files under /proc/sys/kernel/:
> - dpm_watchdog_timeout_secs: The total timeout before panic. The
> maximum value is capped at CONFIG_DPM_WATCHDOG_TIMEOUT to prevent
> unreasonably large timeouts.
> - dpm_watchdog_warning_timeout_secs: The warning timeout. The maximum
> value is capped at the current dpm_watchdog_timeout_secs.
> Both sysctls have a minimum value of 1.
>
> Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
> ---
> v3:
> - No changes.
>
> v2: https://lore.kernel.org/all/20260604090756.2884671-4-tzungbi@kernel.org
> - New to the series.
>
> v1: Doesn't exist.
>
> drivers/base/power/main.c | 61 ++++++++++++++++++++++++++++++++++++---
> 1 file changed, 57 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
> index 7822c29b7c8d..c1a4b30fafb2 100644
> --- a/drivers/base/power/main.c
> +++ b/drivers/base/power/main.c
> @@ -28,6 +28,7 @@
> #include <linux/interrupt.h>
> #include <linux/sched.h>
> #include <linux/sched/debug.h>
> +#include <linux/sysctl.h>
> #include <linux/async.h>
> #include <linux/suspend.h>
> #include <trace/events/power.h>
> @@ -539,6 +540,58 @@ static bool __read_mostly dpm_watchdog_enabled =
> module_param(dpm_watchdog_enabled, bool, 0644);
> MODULE_PARM_DESC(dpm_watchdog_enabled, "Enable DPM watchdog");
>
> +static unsigned int __read_mostly dpm_watchdog_timeout = CONFIG_DPM_WATCHDOG_TIMEOUT;
> +static unsigned int __read_mostly dpm_watchdog_warning_timeout =
> + CONFIG_DPM_WATCHDOG_WARNING_TIMEOUT;
> +static const unsigned int dpm_watchdog_timeout_max = CONFIG_DPM_WATCHDOG_TIMEOUT;
> +
> +static int proc_dodpm_watchdog_timeout_secs(const struct ctl_table *table,
> + int write, void *buffer,
> + size_t *lenp, loff_t *ppos)
> +{
> + struct ctl_table ctl = *table;
> + unsigned int val = dpm_watchdog_timeout;
> + int ret;
> +
> + ctl.data = &val;
> + ret = proc_douintvec_minmax(&ctl, write, buffer, lenp, ppos);
> + if (ret || !write)
> + return ret;
> +
> + if (val < dpm_watchdog_warning_timeout)
> + dpm_watchdog_warning_timeout = val;
> + dpm_watchdog_timeout = val;
> +
> + return 0;
> +}
> +
> +static const struct ctl_table dpm_watchdog_sysctls[] = {
> + {
> + .procname = "dpm_watchdog_timeout_secs",
> + .maxlen = sizeof(unsigned int),
> + .mode = 0644,
> + .proc_handler = proc_dodpm_watchdog_timeout_secs,
> + .extra1 = SYSCTL_ONE,
> + .extra2 = (void *)&dpm_watchdog_timeout_max,
> + },
> + {
> + .procname = "dpm_watchdog_warning_timeout_secs",
> + .data = &dpm_watchdog_warning_timeout,
> + .maxlen = sizeof(unsigned int),
> + .mode = 0644,
> + .proc_handler = proc_douintvec_minmax,
> + .extra1 = SYSCTL_ONE,
> + .extra2 = (void *)&dpm_watchdog_timeout,
> + },
> +};
> +
> +static int __init dpm_watchdog_sysctl_init(void)
> +{
> + register_sysctl_init("kernel", dpm_watchdog_sysctls);
> + return 0;
> +}
> +subsys_initcall(dpm_watchdog_sysctl_init);
> +
> /**
> * dpm_watchdog_handler - Driver suspend / resume watchdog handler.
> * @t: The timer that PM watchdog depends on.
> @@ -564,9 +617,9 @@ static void dpm_watchdog_handler(struct timer_list *t)
> dev_driver_string(wd->dev), dev_name(wd->dev));
> }
>
> - time_left = CONFIG_DPM_WATCHDOG_TIMEOUT - CONFIG_DPM_WATCHDOG_WARNING_TIMEOUT;
> + time_left = dpm_watchdog_timeout - dpm_watchdog_warning_timeout;
> dev_warn(wd->dev, "**** DPM device timeout after %u seconds; %u seconds until panic ****\n",
> - CONFIG_DPM_WATCHDOG_WARNING_TIMEOUT, time_left);
> + dpm_watchdog_warning_timeout, time_left);
> show_stack(wd->tsk, NULL, KERN_WARNING);
>
> wd->fatal = true;
> @@ -587,11 +640,11 @@ static void dpm_watchdog_set(struct dpm_watchdog *wd, struct device *dev)
>
> wd->dev = dev;
> wd->tsk = current;
> - wd->fatal = CONFIG_DPM_WATCHDOG_TIMEOUT == CONFIG_DPM_WATCHDOG_WARNING_TIMEOUT;
> + wd->fatal = dpm_watchdog_timeout == dpm_watchdog_warning_timeout;
>
> timer_setup_on_stack(timer, dpm_watchdog_handler, 0);
> /* use same timeout value for both suspend and resume */
> - timer->expires = jiffies + HZ * CONFIG_DPM_WATCHDOG_WARNING_TIMEOUT;
> + timer->expires = jiffies + HZ * dpm_watchdog_warning_timeout;
> add_timer(timer);
> }
>
> --
I think that this can be applied without the other two patches in the
series, so please let me know if you want me to apply it separately.
Thanks!
^ permalink raw reply
* [RFC PATCH 0/7] tracing/probes: Add more typecast features
From: Masami Hiramatsu (Google) @ 2026-06-08 14:24 UTC (permalink / raw)
To: Steven Rostedt, Mathieu Desnoyers
Cc: Jonathan Corbet, Shuah Khan, Masami Hiramatsu, linux-kernel,
linux-trace-kernel, linux-doc, linux-kselftest
Hi,
Here is a series of patches to introduce more typecast features
to probe events, which includes 1. expanding BTF typecast to
fprobe and kprobe events, 2. introducing container_of like typecst
option, 3. supporting nested typecast, 4. adding $current special
variable support, 5. adding per-cpu dereference support, 6. adding
a testcase to check typecasts.
Steve introduced BTF typecast feature for eprobe[1].
This series extends it and add more options:
1. Expanding BTF typecast to kprobe and fprobe.
(currently only function entry/exit)
2. Introduce container_of like typecast. This adds a "assigned
member" option to the typecast.
(STRUCT,MEMBER)VAR->ANOTHER_MEMBER
This casts VAR to STRUCT type but the VAR is as the address
of STRUCT.MEMBER. In C, it is:
container_of(VAR, STRUCT, MEMBER)->ANOTHER_MEMBER
3. Support nested typecast, e.g.
(STRUCT)((STRUCT2)VAR->MEMBER2)->MEMBER
the nest level must be smaller than 3.
4. Add $current variable to point "current" task_struct.
This is useful with typecast, e.g.
(task_struct)$current->pid
5. per-cpu dereference support.
+CPU(VAR) is the same as this_cpu_read(VAR), and
+PCPU(VAR) is the same as this_cpu_ptr(VAR).
Also, "this_cpu_ptr(VAR)" is available. This is good
with nesting expression.
(STRUCT)(this_cpu_ptr(VAR))->MEMBER
(However, it might be better to allow a special way to omit
parentheses for thi_cpu_ptr())
And added a test script to test part of them.
[1] https://lore.kernel.org/all/20260601130746.2139d926@gandalf.local.home/
---
Masami Hiramatsu (Google) (7):
tracing/probes: Support typecast for various probe events
tracing/probes: Support nested typecast
tracing/probes: Support field specifier option for typecast
tracing/probes: Add $current variable support
tracing/probes: Add +CPU() and +PCPU() dereference method to fetcharg
tracing/probes: Support reserved this_cpu_ptr() method
tracing/probes: Add a new testcase for BTF typecasts
Documentation/trace/eprobetrace.rst | 11 +
Documentation/trace/fprobetrace.rst | 11 +
Documentation/trace/kprobetrace.rst | 12 +
kernel/trace/trace.c | 6
kernel/trace/trace_probe.c | 312 +++++++++++++++-----
kernel/trace/trace_probe.h | 12 +
kernel/trace/trace_probe_tmpl.h | 33 ++
samples/trace_events/trace-events-sample.c | 38 ++
samples/trace_events/trace-events-sample.h | 34 ++
.../ftrace/test.d/dynevent/btf_probe_event.tc | 52 +++
10 files changed, 422 insertions(+), 99 deletions(-)
create mode 100644 tools/testing/selftests/ftrace/test.d/dynevent/btf_probe_event.tc
--
Signature
^ permalink raw reply
* [RFC PATCH 1/7] tracing/probes: Support typecast for various probe events
From: Masami Hiramatsu (Google) @ 2026-06-08 14:24 UTC (permalink / raw)
To: Steven Rostedt, Mathieu Desnoyers
Cc: Jonathan Corbet, Shuah Khan, Masami Hiramatsu, linux-kernel,
linux-trace-kernel, linux-doc, linux-kselftest
In-Reply-To: <178092865666.163648.10457567771536160909.stgit@devnote2>
From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Support BTF typecast feature on other probe events (but only if it is
kernel function entry or return.)
To support other probe events, we just need to use last_struct type
when we find a function parameter in parse_btf_arg().
This also update <tracefs>/README file to show struct typecast.
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
Documentation/trace/fprobetrace.rst | 3 +++
Documentation/trace/kprobetrace.rst | 4 ++++
kernel/trace/trace.c | 2 +-
kernel/trace/trace_probe.c | 12 +++++++-----
4 files changed, 15 insertions(+), 6 deletions(-)
diff --git a/Documentation/trace/fprobetrace.rst b/Documentation/trace/fprobetrace.rst
index b4c2ca3d02c1..7435ded2d66d 100644
--- a/Documentation/trace/fprobetrace.rst
+++ b/Documentation/trace/fprobetrace.rst
@@ -57,6 +57,9 @@ Synopsis of fprobe-events
(u8/u16/u32/u64/s8/s16/s32/s64), hexadecimal types
(x8/x16/x32/x64), "char", "string", "ustring", "symbol", "symstr"
and bitfield are supported.
+ (STRUCT)FIELD->MEMBER[->MEMBER] : If BTF is supported, typecast FIELD to
+ a pointer to STRUCT and then derference the pointer defined by
+ ->MEMBER.
(\*1) This is available only when BTF is enabled.
(\*2) only for the probe on function entry (offs == 0). Note, this argument access
diff --git a/Documentation/trace/kprobetrace.rst b/Documentation/trace/kprobetrace.rst
index 3b6791c17e9b..f73614997d52 100644
--- a/Documentation/trace/kprobetrace.rst
+++ b/Documentation/trace/kprobetrace.rst
@@ -61,6 +61,10 @@ Synopsis of kprobe_events
(x8/x16/x32/x64), VFS layer common type(%pd/%pD), "char",
"string", "ustring", "symbol", "symstr" and bitfield are
supported.
+ (STRUCT)FIELD->MEMBER[->MEMBER] : If BTF is supported, typecast FIELD to
+ a pointer to STRUCT and then derference the pointer defined by
+ ->MEMBER. Note that this is available only when the probe is
+ on function entry.
(\*1) only for the probe on function entry (offs == 0). Note, this argument access
is best effort, because depending on the argument type, it may be passed on
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 6eb4d3097a4d..aa93e7b01146 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -4325,7 +4325,7 @@ static const char readme_msg[] =
#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
"\t $stack<index>, $stack, $retval, $comm, $arg<N>,\n"
#ifdef CONFIG_PROBE_EVENTS_BTF_ARGS
- "\t <argname>[->field[->field|.field...]],\n"
+ "\t [(structname)]<argname>[->field[->field|.field...]],\n"
#endif
#else
"\t $stack<index>, $stack, $retval, $comm,\n"
diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c
index fd1caa1f9723..609b156986c5 100644
--- a/kernel/trace/trace_probe.c
+++ b/kernel/trace/trace_probe.c
@@ -759,7 +759,10 @@ static int parse_btf_arg(char *varname,
return -ENOENT;
found:
- type = btf_type_skip_modifiers(ctx->btf, tid, &tid);
+ if (ctx->struct_btf)
+ type = ctx->last_struct;
+ else
+ type = btf_type_skip_modifiers(ctx->btf, tid, &tid);
found_type:
if (!type) {
trace_probe_log_err(ctx->offset, BAD_BTF_TID);
@@ -836,10 +839,9 @@ static int handle_typecast(char *arg, struct fetch_insn **pcode,
char *tmp;
int ret;
- /* Currently this only works for eprobes */
- if (!(ctx->flags & TPARG_FL_TEVENT)) {
- trace_probe_log_err(ctx->offset, TYPECAST_NOT_EVENT);
- return -EINVAL;
+ if (!(tparg_is_function_entry(ctx->flags) || tparg_is_function_return(ctx->flags))) {
+ trace_probe_log_err(ctx->offset, NOSUP_BTFARG);
+ return -EOPNOTSUPP;
}
tmp = strchr(arg, ')');
^ permalink raw reply related
* [RFC PATCH 2/7] tracing/probes: Support nested typecast
From: Masami Hiramatsu (Google) @ 2026-06-08 14:24 UTC (permalink / raw)
To: Steven Rostedt, Mathieu Desnoyers
Cc: Jonathan Corbet, Shuah Khan, Masami Hiramatsu, linux-kernel,
linux-trace-kernel, linux-doc, linux-kselftest
In-Reply-To: <178092865666.163648.10457567771536160909.stgit@devnote2>
From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
When we hit an open parenthesis right after typecast closing
parenthesis, it means we have nested typecast. This allows us to
typecast a generic data member in a structure to a pointer to
another structure.
For example, to cast a DATA_MEMBER of VAR structure to STRUCT pointer
and get MEMBER value.
(STRUCT)(VAR->DATA_MEMBER)->MEMBER
Also, we can nest typecast.
(STRUCT1)((STRUCT2)$ARG->FIELD2)->FIELD1
Currently the max nest level is limited to 3.
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
Documentation/trace/eprobetrace.rst | 2 +
Documentation/trace/fprobetrace.rst | 2 +
Documentation/trace/kprobetrace.rst | 2 +
kernel/trace/trace.c | 1
kernel/trace/trace_probe.c | 76 ++++++++++++++++++++++++++++++++---
kernel/trace/trace_probe.h | 7 +++
6 files changed, 82 insertions(+), 8 deletions(-)
diff --git a/Documentation/trace/eprobetrace.rst b/Documentation/trace/eprobetrace.rst
index fe3602540569..cd0b4aa7f896 100644
--- a/Documentation/trace/eprobetrace.rst
+++ b/Documentation/trace/eprobetrace.rst
@@ -50,6 +50,8 @@ Synopsis of eprobe_events
a pointer to STRUCT and then derference the pointer defined by
->MEMBER. Note that when this is used, the FIELD name does not
need to be prefixed with a '$'.
+ (STRUCT)(FETCHARG)->MEMBER[->MEMBER] : typecast can nest, so the above can
+ also be used with another FETCHARG instead of FIELD.
Types
-----
diff --git a/Documentation/trace/fprobetrace.rst b/Documentation/trace/fprobetrace.rst
index 7435ded2d66d..6b8bb27bb62d 100644
--- a/Documentation/trace/fprobetrace.rst
+++ b/Documentation/trace/fprobetrace.rst
@@ -60,6 +60,8 @@ Synopsis of fprobe-events
(STRUCT)FIELD->MEMBER[->MEMBER] : If BTF is supported, typecast FIELD to
a pointer to STRUCT and then derference the pointer defined by
->MEMBER.
+ (STRUCT)(FETCHARG)->MEMBER[->MEMBER] : typecast can nest, so the above can
+ also be used with another FETCHARG instead of FIELD.
(\*1) This is available only when BTF is enabled.
(\*2) only for the probe on function entry (offs == 0). Note, this argument access
diff --git a/Documentation/trace/kprobetrace.rst b/Documentation/trace/kprobetrace.rst
index f73614997d52..c4382765d5b2 100644
--- a/Documentation/trace/kprobetrace.rst
+++ b/Documentation/trace/kprobetrace.rst
@@ -65,6 +65,8 @@ Synopsis of kprobe_events
a pointer to STRUCT and then derference the pointer defined by
->MEMBER. Note that this is available only when the probe is
on function entry.
+ (STRUCT)(FETCHARG)->MEMBER[->MEMBER] : typecast can nest, so the above can
+ also be used with another FETCHARG instead of FIELD.
(\*1) only for the probe on function entry (offs == 0). Note, this argument access
is best effort, because depending on the argument type, it may be passed on
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index aa93e7b01146..4f70318918c2 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -4326,6 +4326,7 @@ static const char readme_msg[] =
"\t $stack<index>, $stack, $retval, $comm, $arg<N>,\n"
#ifdef CONFIG_PROBE_EVENTS_BTF_ARGS
"\t [(structname)]<argname>[->field[->field|.field...]],\n"
+ "\t [(structname)](fetcharg)->field[->field|.field...],\n"
#endif
#else
"\t $stack<index>, $stack, $retval, $comm,\n"
diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c
index 609b156986c5..ddd9b1b63a17 100644
--- a/kernel/trace/trace_probe.c
+++ b/kernel/trace/trace_probe.c
@@ -832,10 +832,35 @@ static int query_btf_struct(const char *sname, struct traceprobe_parse_context *
return 0;
}
+/* Find the matching closing parenthesis for a given opening parenthesis. */
+static char *find_matched_close_paren(char *s)
+{
+ char *p = s;
+ int count = 0;
+
+ while (*p) {
+ if (*p == '(')
+ count++;
+ else if (*p == ')') {
+ if (--count == 0)
+ return p;
+ }
+ p++;
+ }
+ return NULL;
+}
+
+static int
+parse_probe_arg(char *arg, const struct fetch_type *type,
+ struct fetch_insn **pcode, struct fetch_insn *end,
+ struct traceprobe_parse_context *ctx);
+
static int handle_typecast(char *arg, struct fetch_insn **pcode,
struct fetch_insn *end,
struct traceprobe_parse_context *ctx)
{
+ int orig_offset = ctx->offset;
+ bool nested = false;
char *tmp;
int ret;
@@ -850,19 +875,56 @@ static int handle_typecast(char *arg, struct fetch_insn **pcode,
DEREF_OPEN_BRACE);
return -EINVAL;
}
- *tmp = '\0';
- ret = query_btf_struct(arg + 1, ctx);
- *tmp = ')';
+ *tmp++ = '\0';
+
+ /* Handle the nested structure like (STRUCT)(VAR->FIELD)->... */
+ if (*tmp == '(') {
+ char *close = find_matched_close_paren(tmp);
+
+ ctx->offset += tmp - arg;
+ if (!close) {
+ trace_probe_log_err(ctx->offset, DEREF_OPEN_BRACE);
+ return -EINVAL;
+ }
+ /* We expect a field access for typecast */
+ if (close[1] != '-' || close[2] != '>') {
+ trace_probe_log_err(ctx->offset + close - tmp + 1,
+ TYPECAST_REQ_FIELD);
+ return -EINVAL;
+ }
+ ctx->nested_level++;
+ if (ctx->nested_level > TRACEPROBE_MAX_NESTED_LEVEL) {
+ trace_probe_log_err(ctx->offset, TOO_MANY_NESTED);
+ return -E2BIG;
+ }
+ *close = '\0';
+
+ ctx->offset += 1; /* for the '(' */
+ /* We need to parse the nested one */
+ ret = parse_probe_arg(tmp + 1, find_fetch_type(NULL, ctx->flags),
+ pcode, end, ctx);
+ if (ret < 0)
+ return ret;
+ ctx->nested_level--;
+ clear_struct_btf(ctx);
+
+ tmp = close + 1;
+ nested = true;
+ }
+
+ ret = query_btf_struct(arg + 1, ctx);
if (ret < 0) {
trace_probe_log_err(ctx->offset + 1, NO_PTR_STRCT);
return -EINVAL;
}
- tmp++;
-
- ctx->offset += tmp - arg;
- ret = parse_btf_arg(tmp, pcode, end, ctx);
+ ctx->offset = orig_offset + tmp - arg;
+ /* If it is nested, tmp points to the field name. */
+ if (nested)
+ ret = parse_btf_field(tmp, ctx->last_struct, pcode, end, ctx);
+ else
+ ret = parse_btf_arg(tmp, pcode, end, ctx);
return ret;
}
diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h
index 15758cc11fc6..8dcc65e4e1db 100644
--- a/kernel/trace/trace_probe.h
+++ b/kernel/trace/trace_probe.h
@@ -430,8 +430,11 @@ struct traceprobe_parse_context {
struct trace_probe *tp;
unsigned int flags;
int offset;
+ int nested_level;
};
+#define TRACEPROBE_MAX_NESTED_LEVEL 3
+
extern int traceprobe_parse_probe_arg(struct trace_probe *tp, int i,
const char *argv,
struct traceprobe_parse_context *ctx);
@@ -566,7 +569,9 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call,
C(TOO_MANY_ARGS, "Too many arguments are specified"), \
C(TOO_MANY_EARGS, "Too many entry arguments specified"), \
C(EVENT_TOO_BIG, "Event too big (too many fields?)"), \
- C(TYPECAST_NOT_EVENT, "Typecasts are only for eprobe fields"),
+ C(TYPECAST_NOT_EVENT, "Typecasts are only for eprobe fields"), \
+ C(TYPECAST_REQ_FIELD, "Typecast requires a field access"), \
+ C(TOO_MANY_NESTED, "Too many nested typecasts/dereferences"),
#undef C
#define C(a, b) TP_ERR_##a
^ permalink raw reply related
* [RFC PATCH 3/7] tracing/probes: Support field specifier option for typecast
From: Masami Hiramatsu (Google) @ 2026-06-08 14:24 UTC (permalink / raw)
To: Steven Rostedt, Mathieu Desnoyers
Cc: Jonathan Corbet, Shuah Khan, Masami Hiramatsu, linux-kernel,
linux-trace-kernel, linux-doc, linux-kselftest
In-Reply-To: <178092865666.163648.10457567771536160909.stgit@devnote2>
From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Add a field specifier option for the typecast. This works like
container_of() macro.
(STRUCT[,FIELD[.FIELD2...]])VAR
This is equivalent to :
container_of(VAR, struct STRUCT, FIELD[.FIELD2...])
For example:
echo "f tick_nohz_handler next_tick=(tick_sched,sched_timer)timer->next_tick" >> dynamic_events
This will trace tick_nohz_handler() with its tick_sched::next_tick which
is converted from @timer by contianer_of(tick, struct tick_sched, sched_timer).
So, if you enabkle both fprobes:tick_nohz_handler__entry and
timer:hrtimer_expire_entry events, we will see something like:
<idle>-0 [002] d.h1. 3778.087272: hrtimer_expire_entry: hrtimer=00000000d63db328 f
unction=tick_nohz_handler now=3777450051040
<idle>-0 [002] d.h1. 3778.087281: tick_nohz_handler__entry: (tick_nohz_handler+0x4
/0x140) next_tick=3777450000000
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
Documentation/trace/eprobetrace.rst | 5 +
Documentation/trace/fprobetrace.rst | 8 +-
Documentation/trace/kprobetrace.rst | 8 +-
kernel/trace/trace.c | 4 -
kernel/trace/trace_probe.c | 169 +++++++++++++++++++++++------------
kernel/trace/trace_probe.h | 4 +
6 files changed, 131 insertions(+), 67 deletions(-)
diff --git a/Documentation/trace/eprobetrace.rst b/Documentation/trace/eprobetrace.rst
index cd0b4aa7f896..680e0af43d5d 100644
--- a/Documentation/trace/eprobetrace.rst
+++ b/Documentation/trace/eprobetrace.rst
@@ -49,7 +49,10 @@ Synopsis of eprobe_events
(STRUCT)FIELD->MEMBER[->MEMBER] : If BTF is supported, typecast FIELD to
a pointer to STRUCT and then derference the pointer defined by
->MEMBER. Note that when this is used, the FIELD name does not
- need to be prefixed with a '$'.
+ need to be prefixed with a '$'. ASGN can be specified optionally.
+ If ASGN is specified, FIELD will be cast to the same offset
+ position as the ASGN member, rather than to the beginning of
+ the STRUCT.
(STRUCT)(FETCHARG)->MEMBER[->MEMBER] : typecast can nest, so the above can
also be used with another FETCHARG instead of FIELD.
diff --git a/Documentation/trace/fprobetrace.rst b/Documentation/trace/fprobetrace.rst
index 6b8bb27bb62d..290a9e6f7491 100644
--- a/Documentation/trace/fprobetrace.rst
+++ b/Documentation/trace/fprobetrace.rst
@@ -57,10 +57,12 @@ Synopsis of fprobe-events
(u8/u16/u32/u64/s8/s16/s32/s64), hexadecimal types
(x8/x16/x32/x64), "char", "string", "ustring", "symbol", "symstr"
and bitfield are supported.
- (STRUCT)FIELD->MEMBER[->MEMBER] : If BTF is supported, typecast FIELD to
+ (STRUCT[,ASGN])FIELD->MEMBER[->MEMBER] : If BTF is supported, typecast FIELD to
a pointer to STRUCT and then derference the pointer defined by
- ->MEMBER.
- (STRUCT)(FETCHARG)->MEMBER[->MEMBER] : typecast can nest, so the above can
+ ->MEMBER. ASGN can be specified optionally. If ASGN is specified,
+ FIELD will be cast to the same offset position as the ASGN member,
+ rather than to the beginning of the STRUCT.
+ (STRUCT[,ASGN])(FETCHARG)->MEMBER[->MEMBER] : typecast can nest, so the above can
also be used with another FETCHARG instead of FIELD.
(\*1) This is available only when BTF is enabled.
diff --git a/Documentation/trace/kprobetrace.rst b/Documentation/trace/kprobetrace.rst
index c4382765d5b2..a62707e6a9f2 100644
--- a/Documentation/trace/kprobetrace.rst
+++ b/Documentation/trace/kprobetrace.rst
@@ -61,11 +61,13 @@ Synopsis of kprobe_events
(x8/x16/x32/x64), VFS layer common type(%pd/%pD), "char",
"string", "ustring", "symbol", "symstr" and bitfield are
supported.
- (STRUCT)FIELD->MEMBER[->MEMBER] : If BTF is supported, typecast FIELD to
+ (STRUCT[,ASGN])FIELD->MEMBER[->MEMBER] : If BTF is supported, typecast FIELD to
a pointer to STRUCT and then derference the pointer defined by
->MEMBER. Note that this is available only when the probe is
- on function entry.
- (STRUCT)(FETCHARG)->MEMBER[->MEMBER] : typecast can nest, so the above can
+ on function entry. ASGN can be specified optionally. If ASGN
+ is specified, FIELD will be cast to the same offset position
+ as the ASGN member, rather than to the beginning of the STRUCT.
+ (STRUCT[,ASGN])(FETCHARG)->MEMBER[->MEMBER] : typecast can nest, so the above can
also be used with another FETCHARG instead of FIELD.
(\*1) only for the probe on function entry (offs == 0). Note, this argument access
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 4f70318918c2..0e36af853199 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -4325,8 +4325,8 @@ static const char readme_msg[] =
#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
"\t $stack<index>, $stack, $retval, $comm, $arg<N>,\n"
#ifdef CONFIG_PROBE_EVENTS_BTF_ARGS
- "\t [(structname)]<argname>[->field[->field|.field...]],\n"
- "\t [(structname)](fetcharg)->field[->field|.field...],\n"
+ "\t [(structname[,field])]<argname>[->field[->field|.field...]],\n"
+ "\t [(structname[,field])](fetcharg)->field[->field|.field...],\n"
#endif
#else
"\t $stack<index>, $stack, $retval, $comm,\n"
diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c
index ddd9b1b63a17..ff0b619e9a90 100644
--- a/kernel/trace/trace_probe.c
+++ b/kernel/trace/trace_probe.c
@@ -574,6 +574,65 @@ static int split_next_field(char *varname, char **next_field,
return ret;
}
+/* Inner loop for solving dot operator ('.'). Return bit-offset of the given field */
+static int get_bitoffset_of_field(char **pfieldname, const struct btf_type **ptype,
+ struct traceprobe_parse_context *ctx)
+{
+ const struct btf_type *type = *ptype;
+ const struct btf_member *field;
+ struct btf *btf = ctx_btf(ctx);
+ char *fieldname = *pfieldname;
+ int bitoffs = 0;
+ u32 anon_offs;
+ char *next;
+ int is_ptr;
+ s32 tid;
+
+ do {
+ next = NULL;
+ is_ptr = split_next_field(fieldname, &next, ctx);
+ if (is_ptr < 0)
+ return is_ptr;
+
+ anon_offs = 0;
+ field = btf_find_struct_member(btf, type, fieldname,
+ &anon_offs);
+ if (IS_ERR(field)) {
+ trace_probe_log_err(ctx->offset, BAD_BTF_TID);
+ return PTR_ERR(field);
+ }
+ if (!field) {
+ trace_probe_log_err(ctx->offset, NO_BTF_FIELD);
+ return -ENOENT;
+ }
+ /* Add anonymous structure/union offset */
+ bitoffs += anon_offs;
+
+ /* Accumulate the bit-offsets of the dot-connected fields */
+ if (btf_type_kflag(type)) {
+ bitoffs += BTF_MEMBER_BIT_OFFSET(field->offset);
+ ctx->last_bitsize = BTF_MEMBER_BITFIELD_SIZE(field->offset);
+ } else {
+ bitoffs += field->offset;
+ ctx->last_bitsize = 0;
+ }
+
+ type = btf_type_skip_modifiers(btf, field->type, &tid);
+ if (!type) {
+ trace_probe_log_err(ctx->offset, BAD_BTF_TID);
+ return -EINVAL;
+ }
+
+ if (next)
+ ctx->offset += next - fieldname;
+ fieldname = next;
+ } while (!is_ptr && fieldname);
+
+ *pfieldname = fieldname;
+ *ptype = type;
+
+ return bitoffs;
+}
/*
* Parse the field of data structure. The @type must be a pointer type
* pointing the target data structure type.
@@ -583,16 +642,14 @@ static int parse_btf_field(char *fieldname, const struct btf_type *type,
struct traceprobe_parse_context *ctx)
{
struct fetch_insn *code = *pcode;
- const struct btf_member *field;
- u32 bitoffs, anon_offs;
- bool is_struct = ctx->struct_btf != NULL;
struct btf *btf = ctx_btf(ctx);
- char *next;
- int is_ptr;
+ bool is_first_field = true;
+ int bitoffs;
s32 tid;
do {
- if (!is_struct) {
+ /* For the first field of typecast, @type will be the target structure type. */
+ if (!(is_first_field && ctx->struct_btf)) {
/* Outer loop for solving arrow operator ('->') */
if (BTF_INFO_KIND(type->info) != BTF_KIND_PTR) {
trace_probe_log_err(ctx->offset, NO_PTR_STRCT);
@@ -606,60 +663,25 @@ static int parse_btf_field(char *fieldname, const struct btf_type *type,
return -EINVAL;
}
}
- /* Only the first type can skip being a pointer */
- is_struct = false;
-
- bitoffs = 0;
- do {
- /* Inner loop for solving dot operator ('.') */
- next = NULL;
- is_ptr = split_next_field(fieldname, &next, ctx);
- if (is_ptr < 0)
- return is_ptr;
-
- anon_offs = 0;
- field = btf_find_struct_member(btf, type, fieldname,
- &anon_offs);
- if (IS_ERR(field)) {
- trace_probe_log_err(ctx->offset, BAD_BTF_TID);
- return PTR_ERR(field);
- }
- if (!field) {
- trace_probe_log_err(ctx->offset, NO_BTF_FIELD);
- return -ENOENT;
- }
- /* Add anonymous structure/union offset */
- bitoffs += anon_offs;
-
- /* Accumulate the bit-offsets of the dot-connected fields */
- if (btf_type_kflag(type)) {
- bitoffs += BTF_MEMBER_BIT_OFFSET(field->offset);
- ctx->last_bitsize = BTF_MEMBER_BITFIELD_SIZE(field->offset);
- } else {
- bitoffs += field->offset;
- ctx->last_bitsize = 0;
- }
-
- type = btf_type_skip_modifiers(btf, field->type, &tid);
- if (!type) {
- trace_probe_log_err(ctx->offset, BAD_BTF_TID);
- return -EINVAL;
- }
-
- ctx->offset += next - fieldname;
- fieldname = next;
- } while (!is_ptr && fieldname);
+ bitoffs = get_bitoffset_of_field(&fieldname, &type, ctx);
+ if (bitoffs < 0)
+ return bitoffs;
if (++code == end) {
trace_probe_log_err(ctx->offset, TOO_MANY_OPS);
return -EINVAL;
}
code->op = FETCH_OP_DEREF; /* TODO: user deref support */
+ if (is_first_field && ctx->struct_btf) {
+ /* The first field can be typecasted with field option. */
+ bitoffs -= ctx->prefix_bitoffs;
+ }
code->offset = bitoffs / 8;
*pcode = code;
ctx->last_bitoffs = bitoffs % 8;
ctx->last_type = type;
+ is_first_field = false;
} while (fieldname);
return 0;
@@ -700,8 +722,7 @@ static int parse_btf_arg(char *varname,
/* TEVENT is only here via a typecast */
if (WARN_ON_ONCE(ctx->struct_btf == NULL))
return -EINVAL;
- type = ctx->last_struct;
- goto found_type;
+ goto found;
}
if (ctx->flags & TPARG_FL_RETURN && !strcmp(varname, "$retval")) {
@@ -763,7 +784,6 @@ static int parse_btf_arg(char *varname,
type = ctx->last_struct;
else
type = btf_type_skip_modifiers(ctx->btf, tid, &tid);
-found_type:
if (!type) {
trace_probe_log_err(ctx->offset, BAD_BTF_TID);
return -EINVAL;
@@ -832,6 +852,41 @@ static int query_btf_struct(const char *sname, struct traceprobe_parse_context *
return 0;
}
+static int parse_btf_casttype(char *casttype, struct traceprobe_parse_context *ctx)
+{
+ char *field;
+ int ret;
+
+ /* Field option - evaluated later. */
+ field = strchr(casttype, ',');
+ if (field)
+ *field++ = '\0';
+
+ ret = query_btf_struct(casttype, ctx);
+ if (ret < 0) {
+ trace_probe_log_err(ctx->offset, NO_PTR_STRCT);
+ return -EINVAL;
+ }
+
+ if (field) {
+ struct btf_type *type = (struct btf_type *)ctx->last_struct;
+
+ ctx->offset += field - casttype;
+ ret = get_bitoffset_of_field(&field, &ctx->last_struct, ctx);
+ if (ret < 0)
+ return ret;
+ if (ret % 8) {
+ trace_probe_log_err(ctx->offset, TYPECAST_NOT_ALIGNED);
+ return -EINVAL;
+ }
+ ctx->prefix_bitoffs = ret;
+ /* Restore the original struct type (overwritten by get_bitoffset_of_field) */
+ ctx->last_struct = type;
+ }
+
+ return ret;
+}
+
/* Find the matching closing parenthesis for a given opening parenthesis. */
static char *find_matched_close_paren(char *s)
{
@@ -913,11 +968,10 @@ static int handle_typecast(char *arg, struct fetch_insn **pcode,
nested = true;
}
- ret = query_btf_struct(arg + 1, ctx);
- if (ret < 0) {
- trace_probe_log_err(ctx->offset + 1, NO_PTR_STRCT);
- return -EINVAL;
- }
+ ctx->offset = orig_offset + 1; /* for the '(' */
+ ret = parse_btf_casttype(arg + 1, ctx);
+ if (ret < 0)
+ return ret;
ctx->offset = orig_offset + tmp - arg;
/* If it is nested, tmp points to the field name. */
@@ -925,6 +979,7 @@ static int handle_typecast(char *arg, struct fetch_insn **pcode,
ret = parse_btf_field(tmp, ctx->last_struct, pcode, end, ctx);
else
ret = parse_btf_arg(tmp, pcode, end, ctx);
+ ctx->prefix_bitoffs = 0;
return ret;
}
diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h
index 8dcc65e4e1db..b1a54da3c761 100644
--- a/kernel/trace/trace_probe.h
+++ b/kernel/trace/trace_probe.h
@@ -431,6 +431,7 @@ struct traceprobe_parse_context {
unsigned int flags;
int offset;
int nested_level;
+ int prefix_bitoffs; /* The bit offset of the prefix field of typecast */
};
#define TRACEPROBE_MAX_NESTED_LEVEL 3
@@ -571,7 +572,8 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call,
C(EVENT_TOO_BIG, "Event too big (too many fields?)"), \
C(TYPECAST_NOT_EVENT, "Typecasts are only for eprobe fields"), \
C(TYPECAST_REQ_FIELD, "Typecast requires a field access"), \
- C(TOO_MANY_NESTED, "Too many nested typecasts/dereferences"),
+ C(TOO_MANY_NESTED, "Too many nested typecasts/dereferences"), \
+ C(TYPECAST_NOT_ALIGNED, "Typecast field option is not byte-aligned"),
#undef C
#define C(a, b) TP_ERR_##a
^ permalink raw reply related
* [RFC PATCH 4/7] tracing/probes: Add $current variable support
From: Masami Hiramatsu (Google) @ 2026-06-08 14:24 UTC (permalink / raw)
To: Steven Rostedt, Mathieu Desnoyers
Cc: Jonathan Corbet, Shuah Khan, Masami Hiramatsu, linux-kernel,
linux-trace-kernel, linux-doc, linux-kselftest
In-Reply-To: <178092865666.163648.10457567771536160909.stgit@devnote2>
From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Since we can use the BTF to cast value to a structure pointer type,
it is useful to introduce "$current" special variable support to
fetcharg.
User can define a fetcharg to access current task_struct properties
using BTF typecast (or dereference - but this may be complicated) e.g.
(task_struct)$current->cpus_ptr
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
Documentation/trace/eprobetrace.rst | 1 +
Documentation/trace/fprobetrace.rst | 1 +
Documentation/trace/kprobetrace.rst | 1 +
kernel/trace/trace.c | 2 +-
kernel/trace/trace_probe.c | 6 ++++++
kernel/trace/trace_probe.h | 1 +
kernel/trace/trace_probe_tmpl.h | 3 +++
7 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/Documentation/trace/eprobetrace.rst b/Documentation/trace/eprobetrace.rst
index 680e0af43d5d..dcf92d5b4175 100644
--- a/Documentation/trace/eprobetrace.rst
+++ b/Documentation/trace/eprobetrace.rst
@@ -38,6 +38,7 @@ Synopsis of eprobe_events
@ADDR : Fetch memory at ADDR (ADDR should be in kernel)
@SYM[+|-offs] : Fetch memory at SYM +|- offs (SYM should be a data symbol)
$comm : Fetch current task comm.
+ $current : Fetch the address of the current task_struct.
+|-[u]OFFS(FETCHARG) : Fetch memory at FETCHARG +|- OFFS address.(\*3)(\*4)
\IMM : Store an immediate value to the argument.
NAME=FETCHARG : Set NAME as the argument name of FETCHARG.
diff --git a/Documentation/trace/fprobetrace.rst b/Documentation/trace/fprobetrace.rst
index 290a9e6f7491..3392cab016b3 100644
--- a/Documentation/trace/fprobetrace.rst
+++ b/Documentation/trace/fprobetrace.rst
@@ -50,6 +50,7 @@ Synopsis of fprobe-events
$argN : Fetch the Nth function argument. (N >= 1) (\*2)
$retval : Fetch return value.(\*3)
$comm : Fetch current task comm.
+ $current : Fetch the address of the current task_struct.
+|-[u]OFFS(FETCHARG) : Fetch memory at FETCHARG +|- OFFS address.(\*4)(\*5)
\IMM : Store an immediate value to the argument.
NAME=FETCHARG : Set NAME as the argument name of FETCHARG.
diff --git a/Documentation/trace/kprobetrace.rst b/Documentation/trace/kprobetrace.rst
index a62707e6a9f2..81e4fe38791d 100644
--- a/Documentation/trace/kprobetrace.rst
+++ b/Documentation/trace/kprobetrace.rst
@@ -53,6 +53,7 @@ Synopsis of kprobe_events
$argN : Fetch the Nth function argument. (N >= 1) (\*1)
$retval : Fetch return value.(\*2)
$comm : Fetch current task comm.
+ $current : Fetch the address of the current task_struct.
+|-[u]OFFS(FETCHARG) : Fetch memory at FETCHARG +|- OFFS address.(\*3)(\*4)
\IMM : Store an immediate value to the argument.
NAME=FETCHARG : Set NAME as the argument name of FETCHARG.
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 0e36af853199..e185a006cb08 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -4329,7 +4329,7 @@ static const char readme_msg[] =
"\t [(structname[,field])](fetcharg)->field[->field|.field...],\n"
#endif
#else
- "\t $stack<index>, $stack, $retval, $comm,\n"
+ "\t $stack<index>, $stack, $retval, $comm, $current\n"
#endif
"\t +|-[u]<offset>(<fetcharg>), \\imm-value, \\\"imm-string\"\n"
"\t kernel return probes support: $retval, $arg<N>, $comm\n"
diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c
index ff0b619e9a90..2c5deb1e1463 100644
--- a/kernel/trace/trace_probe.c
+++ b/kernel/trace/trace_probe.c
@@ -1235,6 +1235,12 @@ static int parse_probe_vars(char *orig_arg, const struct fetch_type *t,
return 0;
}
+ /* $current returns the address of the current task_struct. */
+ if (strcmp(arg, "current") == 0) {
+ code->op = FETCH_OP_CURRENT;
+ return 0;
+ }
+
#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
len = str_has_prefix(arg, "arg");
if (len) {
diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h
index b1a54da3c761..f2b31089779c 100644
--- a/kernel/trace/trace_probe.h
+++ b/kernel/trace/trace_probe.h
@@ -96,6 +96,7 @@ enum fetch_op {
FETCH_OP_FOFFS, /* File offset: .immediate */
FETCH_OP_DATA, /* Allocated data: .data */
FETCH_OP_EDATA, /* Entry data: .offset */
+ FETCH_OP_CURRENT, /* Current task_struct address */
// Stage 2 (dereference) op
FETCH_OP_DEREF, /* Dereference: .offset */
FETCH_OP_UDEREF, /* User-space Dereference: .offset */
diff --git a/kernel/trace/trace_probe_tmpl.h b/kernel/trace/trace_probe_tmpl.h
index f39b37fcdb3b..f630930288d2 100644
--- a/kernel/trace/trace_probe_tmpl.h
+++ b/kernel/trace/trace_probe_tmpl.h
@@ -112,6 +112,9 @@ process_common_fetch_insn(struct fetch_insn *code, unsigned long *val)
case FETCH_OP_DATA:
*val = (unsigned long)code->data;
break;
+ case FETCH_OP_CURRENT:
+ *val = (unsigned long)current;
+ break;
default:
return -EILSEQ;
}
^ permalink raw reply related
* [RFC PATCH 5/7] tracing/probes: Add +CPU() and +PCPU() dereference method to fetcharg
From: Masami Hiramatsu (Google) @ 2026-06-08 14:25 UTC (permalink / raw)
To: Steven Rostedt, Mathieu Desnoyers
Cc: Jonathan Corbet, Shuah Khan, Masami Hiramatsu, linux-kernel,
linux-trace-kernel, linux-doc, linux-kselftest
In-Reply-To: <178092865666.163648.10457567771536160909.stgit@devnote2>
From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
When tracing the kernel local variables, sometimes we need to get the
CPU local variables. To access it, current simple dereference is not
enough.
Thus, introduce a special +CPU() dereference to access per-cpu variable
for the current CPU (accessing other CPU variable may race with
updates on other CPUs). Also +PCPU() is for accessing per-cpu pointer.
+CPU(pcp)
is equal to
this_cpu_read(pcp)
And
+PCPU(pcp)
is equal to
this_cpu_ptr(pcp)
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
Documentation/trace/eprobetrace.rst | 3 ++
Documentation/trace/fprobetrace.rst | 3 ++
Documentation/trace/kprobetrace.rst | 3 ++
kernel/trace/trace.c | 1 +
kernel/trace/trace_probe.c | 48 +++++++++++++++++++++--------------
kernel/trace/trace_probe.h | 2 +
kernel/trace/trace_probe_tmpl.h | 30 ++++++++++++++++++----
7 files changed, 65 insertions(+), 25 deletions(-)
diff --git a/Documentation/trace/eprobetrace.rst b/Documentation/trace/eprobetrace.rst
index dcf92d5b4175..0c7878df02f6 100644
--- a/Documentation/trace/eprobetrace.rst
+++ b/Documentation/trace/eprobetrace.rst
@@ -40,6 +40,9 @@ Synopsis of eprobe_events
$comm : Fetch current task comm.
$current : Fetch the address of the current task_struct.
+|-[u]OFFS(FETCHARG) : Fetch memory at FETCHARG +|- OFFS address.(\*3)(\*4)
+ +CPU(FETCHARG) : Fetch memory at FETCHARG address on the CPU specified by CPU.
+ This is useful for fetching per-CPU variables.
+ +PCPU(FETCHARG) : Fetch memory address at FETCHARG address on the per-CPU area.
\IMM : Store an immediate value to the argument.
NAME=FETCHARG : Set NAME as the argument name of FETCHARG.
FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types
diff --git a/Documentation/trace/fprobetrace.rst b/Documentation/trace/fprobetrace.rst
index 3392cab016b3..c851f98bb310 100644
--- a/Documentation/trace/fprobetrace.rst
+++ b/Documentation/trace/fprobetrace.rst
@@ -52,6 +52,9 @@ Synopsis of fprobe-events
$comm : Fetch current task comm.
$current : Fetch the address of the current task_struct.
+|-[u]OFFS(FETCHARG) : Fetch memory at FETCHARG +|- OFFS address.(\*4)(\*5)
+ +CPU(FETCHARG) : Fetch memory at FETCHARG address on the CPU specified by CPU.
+ This is useful for fetching per-CPU variables.
+ +PCPU(FETCHARG) : Fetch memory address at FETCHARG address on the per-CPU area.
\IMM : Store an immediate value to the argument.
NAME=FETCHARG : Set NAME as the argument name of FETCHARG.
FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types
diff --git a/Documentation/trace/kprobetrace.rst b/Documentation/trace/kprobetrace.rst
index 81e4fe38791d..bc806fd82a91 100644
--- a/Documentation/trace/kprobetrace.rst
+++ b/Documentation/trace/kprobetrace.rst
@@ -55,6 +55,9 @@ Synopsis of kprobe_events
$comm : Fetch current task comm.
$current : Fetch the address of the current task_struct.
+|-[u]OFFS(FETCHARG) : Fetch memory at FETCHARG +|- OFFS address.(\*3)(\*4)
+ +CPU(FETCHARG) : Fetch memory at FETCHARG address on the CPU specified by CPU.
+ This is useful for fetching per-CPU variables.
+ +PCPU(FETCHARG) : Fetch memory address at FETCHARG address on the per-CPU area.
\IMM : Store an immediate value to the argument.
NAME=FETCHARG : Set NAME as the argument name of FETCHARG.
FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index e185a006cb08..2b8c8ac4036a 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -4332,6 +4332,7 @@ static const char readme_msg[] =
"\t $stack<index>, $stack, $retval, $comm, $current\n"
#endif
"\t +|-[u]<offset>(<fetcharg>), \\imm-value, \\\"imm-string\"\n"
+ "\t +CPU(<fetcharg>), +PCPU(<fetcharg>)\n"
"\t kernel return probes support: $retval, $arg<N>, $comm\n"
"\t type: s8/16/32/64, u8/16/32/64, x8/16/32/64, char, string, symbol,\n"
"\t b<bit-width>@<bit-offset>/<container-size>, ustring,\n"
diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c
index 2c5deb1e1463..fa6757222fe6 100644
--- a/kernel/trace/trace_probe.c
+++ b/kernel/trace/trace_probe.c
@@ -1396,26 +1396,36 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
case '+': /* deref memory */
case '-':
- if (arg[1] == 'u') {
- deref = FETCH_OP_UDEREF;
- arg[1] = arg[0];
- arg++;
- }
- if (arg[0] == '+')
- arg++; /* Skip '+', because kstrtol() rejects it. */
- tmp = strchr(arg, '(');
- if (!tmp) {
- trace_probe_log_err(ctx->offset, DEREF_NEED_BRACE);
- return -EINVAL;
- }
- *tmp = '\0';
- ret = kstrtol(arg, 0, &offset);
- if (ret) {
- trace_probe_log_err(ctx->offset, BAD_DEREF_OFFS);
- break;
+ if (str_has_prefix(arg, "+CPU(")) {
+ deref = FETCH_OP_DEREF_CPU;
+ arg += 5;
+ ctx->offset += 5;
+ } else if (str_has_prefix(arg, "+PCPU(")) {
+ deref = FETCH_OP_CPU_PTR;
+ arg += 6;
+ ctx->offset += 6;
+ } else {
+ if (arg[1] == 'u') {
+ deref = FETCH_OP_UDEREF;
+ arg[1] = arg[0];
+ arg++;
+ }
+ if (arg[0] == '+')
+ arg++; /* Skip '+', because kstrtol() rejects it. */
+ tmp = strchr(arg, '(');
+ if (!tmp) {
+ trace_probe_log_err(ctx->offset, DEREF_NEED_BRACE);
+ return -EINVAL;
+ }
+ *tmp = '\0';
+ ret = kstrtol(arg, 0, &offset);
+ if (ret) {
+ trace_probe_log_err(ctx->offset, BAD_DEREF_OFFS);
+ break;
+ }
+ ctx->offset += (tmp + 1 - arg) + (arg[0] != '-' ? 1 : 0);
+ arg = tmp + 1;
}
- ctx->offset += (tmp + 1 - arg) + (arg[0] != '-' ? 1 : 0);
- arg = tmp + 1;
tmp = strrchr(arg, ')');
if (!tmp) {
trace_probe_log_err(ctx->offset + strlen(arg),
diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h
index f2b31089779c..bec04bcc4226 100644
--- a/kernel/trace/trace_probe.h
+++ b/kernel/trace/trace_probe.h
@@ -100,6 +100,8 @@ enum fetch_op {
// Stage 2 (dereference) op
FETCH_OP_DEREF, /* Dereference: .offset */
FETCH_OP_UDEREF, /* User-space Dereference: .offset */
+ FETCH_OP_DEREF_CPU, /* Per-CPU Dereference for this CPU */
+ FETCH_OP_CPU_PTR, /* Per-CPU pointer for this CPU */
// Stage 3 (store) ops
FETCH_OP_ST_RAW, /* Raw: .size */
FETCH_OP_ST_MEM, /* Mem: .offset, .size */
diff --git a/kernel/trace/trace_probe_tmpl.h b/kernel/trace/trace_probe_tmpl.h
index f630930288d2..82d753decf48 100644
--- a/kernel/trace/trace_probe_tmpl.h
+++ b/kernel/trace/trace_probe_tmpl.h
@@ -129,25 +129,43 @@ process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val,
struct fetch_insn *s3 = NULL;
int total = 0, ret = 0, i = 0;
u32 loc = 0;
- unsigned long lval = val;
+ unsigned long lval, llval = val;
stage2:
/* 2nd stage: dereference memory if needed */
do {
- if (code->op == FETCH_OP_DEREF) {
- lval = val;
+ lval = val;
+ switch (code->op) {
+ case FETCH_OP_DEREF:
ret = probe_mem_read(&val, (void *)val + code->offset,
sizeof(val));
- } else if (code->op == FETCH_OP_UDEREF) {
- lval = val;
+ break;
+ case FETCH_OP_UDEREF:
ret = probe_mem_read_user(&val,
(void *)val + code->offset, sizeof(val));
- } else
break;
+ case FETCH_OP_DEREF_CPU:
+ case FETCH_OP_CPU_PTR:
+ if (!is_kernel_percpu_address(val)) {
+ ret = -EFAULT;
+ break;
+ }
+ val = (unsigned long)this_cpu_ptr((void __percpu *)val);
+ if (code->op == FETCH_OP_DEREF_CPU)
+ ret = probe_mem_read(&val, (void *)val, sizeof(val));
+ else
+ ret = 0;
+ break;
+ default:
+ lval = llval;
+ goto out;
+ }
if (ret)
return ret;
+ llval = lval;
code++;
} while (1);
+out:
s3 = code;
stage3:
^ permalink raw reply related
* [RFC PATCH 6/7] tracing/probes: Support reserved this_cpu_ptr() method
From: Masami Hiramatsu (Google) @ 2026-06-08 14:25 UTC (permalink / raw)
To: Steven Rostedt, Mathieu Desnoyers
Cc: Jonathan Corbet, Shuah Khan, Masami Hiramatsu, linux-kernel,
linux-trace-kernel, linux-doc, linux-kselftest
In-Reply-To: <178092865666.163648.10457567771536160909.stgit@devnote2>
From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
The +PCPU() dereference operator was introduced in trace probes to
access a per-CPU pointer of a CPU local variable. However, kernel
developers are more familiar with the "this_cpu_ptr()" macro.
To make trace probe syntax more intuitive and aligned with standard
kernel macros, introduce support for "this_cpu_ptr(<fetcharg>)" as a
reserved method.
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
kernel/trace/trace.c | 2 +-
kernel/trace/trace_probe.c | 7 +++++++
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 2b8c8ac4036a..60ab839d0867 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -4332,7 +4332,7 @@ static const char readme_msg[] =
"\t $stack<index>, $stack, $retval, $comm, $current\n"
#endif
"\t +|-[u]<offset>(<fetcharg>), \\imm-value, \\\"imm-string\"\n"
- "\t +CPU(<fetcharg>), +PCPU(<fetcharg>)\n"
+ "\t +CPU(<fetcharg>), +PCPU(<fetcharg>), this_cpu_ptr(<fetcharg>)\n"
"\t kernel return probes support: $retval, $arg<N>, $comm\n"
"\t type: s8/16/32/64, u8/16/32/64, x8/16/32/64, char, string, symbol,\n"
"\t b<bit-width>@<bit-offset>/<container-size>, ustring,\n"
diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c
index fa6757222fe6..27be0664cdf3 100644
--- a/kernel/trace/trace_probe.c
+++ b/kernel/trace/trace_probe.c
@@ -1315,6 +1315,7 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
struct fetch_insn **pcode, struct fetch_insn *end,
struct traceprobe_parse_context *ctx)
{
+ static const char *THIS_CPU_PTR_STR = "this_cpu_ptr(";
struct fetch_insn *code = *pcode;
unsigned long param;
int deref = FETCH_OP_DEREF;
@@ -1426,6 +1427,7 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
ctx->offset += (tmp + 1 - arg) + (arg[0] != '-' ? 1 : 0);
arg = tmp + 1;
}
+handle_deref:
tmp = strrchr(arg, ')');
if (!tmp) {
trace_probe_log_err(ctx->offset + strlen(arg),
@@ -1476,6 +1478,11 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
ret = handle_typecast(arg, pcode, end, ctx);
break;
default:
+ if (str_has_prefix(arg, THIS_CPU_PTR_STR)) {
+ arg += strlen(THIS_CPU_PTR_STR);
+ deref = FETCH_OP_CPU_PTR;
+ goto handle_deref;
+ }
if (isalpha(arg[0]) || arg[0] == '_') { /* BTF variable */
if (!tparg_is_function_entry(ctx->flags) &&
!tparg_is_function_return(ctx->flags)) {
^ permalink raw reply related
* [RFC PATCH 7/7] tracing/probes: Add a new testcase for BTF typecasts
From: Masami Hiramatsu (Google) @ 2026-06-08 14:25 UTC (permalink / raw)
To: Steven Rostedt, Mathieu Desnoyers
Cc: Jonathan Corbet, Shuah Khan, Masami Hiramatsu, linux-kernel,
linux-trace-kernel, linux-doc, linux-kselftest
In-Reply-To: <178092865666.163648.10457567771536160909.stgit@devnote2>
From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
With the introduction of container_of-style BTF typecasting and
per-CPU variable access support in trace probes, we need a way to
verify their functionality and prevent regressions.
Add a new ftrace kselftest and update the trace event sample module to
test and validate these features.
Specifically, update the trace-events-sample module to set up a periodic
timer whose callback accesses a per-CPU counter. Introduce a new sample
trace event, foo_timer_fn, to trace this callback and log the current
counter value.
Then, add a new test case, btf_probe_event.tc, which defines a dynamic
probe on the timer callback. The probe uses BTF typecasting to recover
the parent structure from the timer argument and +CPU() to fetch the
per-CPU counter. The test verifies the integrity of the implementation
by ensuring the values recorded by the dynamic probe match those from
the static tracepoint.
Assisted-by: Antigravity:gemini-3.5-flash
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
samples/trace_events/trace-events-sample.c | 38 ++++++++++++++-
samples/trace_events/trace-events-sample.h | 34 ++++++++++++-
.../ftrace/test.d/dynevent/btf_probe_event.tc | 52 ++++++++++++++++++++
3 files changed, 120 insertions(+), 4 deletions(-)
create mode 100644 tools/testing/selftests/ftrace/test.d/dynevent/btf_probe_event.tc
diff --git a/samples/trace_events/trace-events-sample.c b/samples/trace_events/trace-events-sample.c
index ecc7db237f2e..770315812218 100644
--- a/samples/trace_events/trace-events-sample.c
+++ b/samples/trace_events/trace-events-sample.c
@@ -94,6 +94,20 @@ static int simple_thread_fn(void *arg)
static DEFINE_MUTEX(thread_mutex);
static int simple_thread_cnt;
+static struct foo_timer_data *foo_timer_data;
+
+static void sample_timer_cb(struct timer_list *t)
+{
+ struct foo_timer_data *data = container_of(t, struct foo_timer_data, timer);
+
+ get_cpu();
+ trace_foo_timer_fn(data);
+ (*this_cpu_ptr(data->counter))++;
+ put_cpu();
+
+ mod_timer(t, jiffies + HZ);
+}
+
int foo_bar_reg(void)
{
mutex_lock(&thread_mutex);
@@ -128,9 +142,27 @@ void foo_bar_unreg(void)
static int __init trace_event_init(void)
{
+ foo_timer_data = kzalloc_obj(*foo_timer_data, GFP_KERNEL);
+ if (!foo_timer_data)
+ return -ENOMEM;
+
+ foo_timer_data->name = "sample_timer_counter";
+ foo_timer_data->counter = alloc_percpu(int);
+ if (!foo_timer_data->counter) {
+ kfree(foo_timer_data);
+ return -ENOMEM;
+ }
+
+ timer_setup(&foo_timer_data->timer, sample_timer_cb, 0);
+ mod_timer(&foo_timer_data->timer, jiffies + HZ);
+
simple_tsk = kthread_run(simple_thread, NULL, "event-sample");
- if (IS_ERR(simple_tsk))
+ if (IS_ERR(simple_tsk)) {
+ timer_delete_sync(&foo_timer_data->timer);
+ free_percpu(foo_timer_data->counter);
+ kfree(foo_timer_data);
return -1;
+ }
return 0;
}
@@ -143,6 +175,10 @@ static void __exit trace_event_exit(void)
kthread_stop(simple_tsk_fn);
simple_tsk_fn = NULL;
mutex_unlock(&thread_mutex);
+
+ timer_delete_sync(&foo_timer_data->timer);
+ free_percpu(foo_timer_data->counter);
+ kfree(foo_timer_data);
}
module_init(trace_event_init);
diff --git a/samples/trace_events/trace-events-sample.h b/samples/trace_events/trace-events-sample.h
index 1a05fc153353..816848a456a2 100644
--- a/samples/trace_events/trace-events-sample.h
+++ b/samples/trace_events/trace-events-sample.h
@@ -247,12 +247,14 @@
*/
/*
- * It is OK to have helper functions in the file, but they need to be protected
- * from being defined more than once. Remember, this file gets included more
- * than once.
+ * It is OK to have helper functions and data structures in the file, but they
+ * need to be protected from being defined more than once. Remember, this file
+ * gets included more than once.
*/
#ifndef __TRACE_EVENT_SAMPLE_HELPER_FUNCTIONS
#define __TRACE_EVENT_SAMPLE_HELPER_FUNCTIONS
+#include <linux/timer.h>
+
static inline int __length_of(const int *list)
{
int i;
@@ -270,6 +272,13 @@ enum {
TRACE_SAMPLE_BAR = 4,
TRACE_SAMPLE_ZOO = 8,
};
+
+struct foo_timer_data {
+ const char *name;
+ struct timer_list timer;
+ int __percpu *counter;
+};
+
#endif
/*
@@ -595,6 +604,25 @@ TRACE_EVENT(foo_rel_loc,
__get_rel_bitmask(bitmask),
__get_rel_cpumask(cpumask))
);
+
+TRACE_EVENT(foo_timer_fn,
+
+ TP_PROTO(struct foo_timer_data *data),
+
+ TP_ARGS(data),
+
+ TP_STRUCT__entry(
+ __string( name, data->name )
+ __field( int, count )
+ ),
+
+ TP_fast_assign(
+ __assign_str(name);
+ __entry->count = *this_cpu_ptr(data->counter);
+ ),
+
+ TP_printk("name=%s count=%d", __get_str(name), __entry->count)
+);
#endif
/***** NOTICE! The #if protection ends here. *****/
diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/btf_probe_event.tc b/tools/testing/selftests/ftrace/test.d/dynevent/btf_probe_event.tc
new file mode 100644
index 000000000000..f1980650dbe2
--- /dev/null
+++ b/tools/testing/selftests/ftrace/test.d/dynevent/btf_probe_event.tc
@@ -0,0 +1,52 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+# description: BTF event with typecast and percpu access
+# requires: dynamic_events " +CPU(<fetcharg>)":README "[(structname[,field])]<argname>[->field[->field|.field...]]":README
+
+# Check if the sample module is loaded
+if ! lsmod | grep -q trace_events_sample; then
+ modprobe trace-events-sample || exit_unsupported
+fi
+
+echo 0 > events/enable
+echo > dynamic_events
+
+# The sample_timer_cb(struct timer_list *t) is called.
+# We want to check (STRUCT,FIELD)VAR typecast and +PCPU() dereference.
+# (foo_timer_data,timer)t converts t to struct foo_timer_data * using container_of.
+# data->counter is a per-cpu pointer to int.
+# +PCPU(data->counter) should give the per-cpu address of the counter.
+# *+PCPU(data->counter) should give the value of the counter.
+
+echo 'f:mysample/myevent sample_timer_cb name=(foo_timer_data,timer)t->name:string count=+CPU((foo_timer_data,timer)t->counter)' >> dynamic_events
+
+echo 1 > events/mysample/myevent/enable
+echo 1 > events/sample-trace/foo_timer_fn/enable
+
+sleep 2
+
+echo 0 > events/mysample/myevent/enable
+echo 0 > events/sample-trace/foo_timer_fn/enable
+
+# Compare the values.
+MATCH=0
+while read line; do
+ if echo $line | grep -q "foo_timer_fn:"; then
+ NAME=`echo $line | sed 's/.*name=\([^ ]*\) .*/\1/'`
+ COUNT=`echo $line | sed 's/.*count=\([^ ]*\).*/\1/'`
+ if grep -q "myevent:.*name=\"${NAME}\" count=$COUNT" trace; then
+ MATCH=$((MATCH+1))
+ fi
+ fi
+done < trace
+
+if [ $MATCH -eq 0 ]; then
+ echo "No matching events found"
+ exit_fail
+fi
+
+# Clean up
+echo 0 > events/mysample/myevent/enable
+echo 0 > events/sample-trace/foo_timer_fn/enable
+echo > dynamic_events
+clear_trace
^ permalink raw reply related
* [PATCH iwl-next v8 00/15] Introduce iXD driver
From: Larysa Zaremba @ 2026-06-08 14:41 UTC (permalink / raw)
To: intel-wired-lan, Tony Nguyen
Cc: aleksander.lobakin, sridhar.samudrala, Michal Swiatkowski,
Larysa Zaremba, Fijalkowski, Maciej, Emil Tantilov, Madhu Chittim,
Josh Hay, Keller, Jacob E, jayaprakash.shanmugam, Jiri Pirko,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Jonathan Corbet, Richard Cochran, Przemek Kitszel,
Andrew Lunn, netdev, linux-doc, linux-kernel
This patch series adds the iXD driver, which supports the Intel(R)
Control Plane PCI Function on Intel E2100 and later IPUs and FNICs.
It facilitates a centralized control over multiple IDPF PFs/VFs/SFs
exposed by the same card. The reason for the separation is to be able
to offload the control plane to the host different from where the data
plane is running.
This is the first phase in the release of this driver where we implement the
initialization of the core PCI driver. Subsequent phases will implement
advanced features like usage of idpf ethernet aux device, link management,
NVM update via devlink, switchdev port representors, data and exception path,
flow rule programming, etc.
The first phase entails the following aspects:
1. Additional libie functionalities:
Patches 1-5 introduce additional common library API for drivers to
communicate with the control plane through mailbox communication.
A control queue is a hardware interface which is used by the driver
to interact with other subsystems (like firmware). The library APIs
allow the driver to setup and configure the control queues to send and
receive virtchnl messages. The library has an internal bookkeeping
(XN API) mechanism to keep track of the send messages. It supports both
synchronous as well as asynchronous way of handling the messages. The
library also handles the timeout internally for synchronous messages
using events. This reduces the driver's overhead in handling the timeout
error cases.
The current patch series supports only APIs that are needed for device
initialization. These include APIs in the libie_pci module:
* Allocating/freeing the DMA memory and mapping the MMIO regions for
BAR0, read/write APIs for drivers to access the MMIO memory
and libie_cp module:
* Control queue initialization and configuration
* Transport initialization for bookkeeping
* Blocking and asynchronous mailbox transactions
Once the mailbox is initialized, the drivers can send and receive virtchnl
messages to/from the control plane.
The modules above are not supposed to be linked witn the main libie library,
but do share the folder with it.
2. idpf :
Patches 6-11 refactor the idpf driver to use the libie APIs for control
queue configuration, virtchnl transaction, device initialization and
reset and adjust related code accordingly.
3. ixd:
Patches 12-15 add the ixd driver and implement multiple pieces of the
initialization flow as follows:
* Add the ability to load
* A reset is issued to ensure a clean device state, followed by
initialization of the mailbox
* Device capabilities:
As part of initialization, the driver has to determine what the device is
capable of (ex. max queues, vports, etc). This information is obtained from
the firmware and stored by the driver.
* Enable initial support for the devlink interface
v7->v8:
* roll back linux/{net/intel -> intel}/* change (1st patch)
* use pci_iomap_range() to map regions in libie_pci
* improve libie_pci commit message
* remove broken dead code for getting RSS from idpf (new patch!)
* use maximum alignment for control queue stack buffer in ixd
* fix ixd_ctlq_clean_sq() usage
* do not register ixd devlink prematurely
* improve receive buffer size validation in idpf
v6->v7:
* lock libie ctlq transaction earlier on Rx path
* improve ctlq Rx buffer size validation both in idpf and libie
* fix RSS LUT buf size (will also go to stable separately)
* improve xnm shutdown handling
* skip zero-sized BAR regions instread of returning error in idpf
* unify treatment of initialized and uninitialized ctlq messages
v5->v6:
* improved failure handling and input validation
* removed cp version from devlink in ixd
* patch 8 requires retest
v4->v5:
* rebase on top of queue resources refactor
* overwrite idpf's memory regions management that was merged in the meantime
* introduce libie_pci_unmap_fltr_regs()
* rebase on top on RDMA patchset - patches 8 & 9
* use pcim_* helpers in libie_pci (patch 3)
* fix issue with reset not being triggered (reported by Emil) - patch 8
* introduce idpf_send_mb_msg_kfree function to handle cases, where
idpf allocates buffers on different size for the same command - patch 8
v2->v3:
* non-trivial rebase affecting idpf refactoring patches
* add include/linux/intel under both Tony and NETWORKING DRIVERS
* due to rebase, in libie account for libeth_rx now using netmem instead
of plain pages
* make libie_ctlq_release_rx_buf() take only one argument, as the producing
queue is not actually needed to release a page pool buffer
* fix return value not being set in idpf_send_get_rx_ptype_msg()
* fix kdoc comments, so libie and ixd generate it cleanly
* separate idpf refactoring into 2 patches: pci+mmio and ctlq+xn
* suplement idpf refactoring commit message with information about module size
and resource usage changes
* reformat commit messages to reduce the number of wasted lines
v1->v2:
* rename libeth_cp and libeth_pci to libie_cp and libie_pci respectively,
move them into an appropriate folder
* rebase on top of recent PTP changes, this alters idpf refactor
* update maintainers after moving headers
* cast resource_size_t to unsigned long long when printing
* add ixd devlink documentation into index
* fix xn system kdoc problems
* fix indentation in libeth_ctlq_xn_deinit()
* fix extra kdoc member vcxn_mngr in idpf_adapter
Amritha Nambiar (1):
ixd: add devlink support
Larysa Zaremba (6):
idpf: remove unused code for getting RSS info from device
idpf: make mbx_task queueing and cancelling more consistent
idpf: print a debug message and bail in case of non-event ctlq message
ixd: add basic driver framework for Intel(R) Control Plane Function
ixd: add reset checks and initialize the mailbox
ixd: add the core initialization
Pavan Kumar Linga (4):
libeth: allow to create fill queues without NAPI
idpf: remove 'vport_params_reqd' field
idpf: refactor idpf to use libie_pci APIs
idpf: refactor idpf to use libie control queues
Phani R Burra (3):
libie: add PCI device initialization helpers to libie
libie: add control queue support
libie: add bookkeeping support for control queue messages
Victor Raj (1):
virtchnl: move virtchnl and virtchnl2 headers to
'include/linux/net/intel'
.../device_drivers/ethernet/index.rst | 1 +
.../device_drivers/ethernet/intel/ixd.rst | 39 +
Documentation/networking/devlink/index.rst | 1 +
Documentation/networking/devlink/ixd.rst | 30 +
MAINTAINERS | 1 -
drivers/net/ethernet/intel/Kconfig | 2 +
drivers/net/ethernet/intel/Makefile | 1 +
drivers/net/ethernet/intel/i40e/i40e.h | 2 +-
drivers/net/ethernet/intel/i40e/i40e_common.c | 2 +-
.../net/ethernet/intel/i40e/i40e_prototype.h | 2 +-
.../ethernet/intel/i40e/i40e_virtchnl_pf.h | 2 +-
drivers/net/ethernet/intel/iavf/iavf.h | 2 +-
drivers/net/ethernet/intel/iavf/iavf_common.c | 2 +-
.../net/ethernet/intel/iavf/iavf_prototype.h | 3 +-
drivers/net/ethernet/intel/iavf/iavf_types.h | 4 +-
drivers/net/ethernet/intel/ice/ice.h | 2 +-
drivers/net/ethernet/intel/ice/ice_common.h | 2 +-
drivers/net/ethernet/intel/ice/ice_vf_lib.h | 2 +-
.../net/ethernet/intel/ice/virt/virtchnl.h | 2 +-
drivers/net/ethernet/intel/idpf/Kconfig | 1 +
drivers/net/ethernet/intel/idpf/Makefile | 2 -
drivers/net/ethernet/intel/idpf/idpf.h | 99 +-
.../net/ethernet/intel/idpf/idpf_controlq.c | 621 ------
.../net/ethernet/intel/idpf/idpf_controlq.h | 144 --
.../ethernet/intel/idpf/idpf_controlq_api.h | 177 --
.../ethernet/intel/idpf/idpf_controlq_setup.c | 169 --
drivers/net/ethernet/intel/idpf/idpf_dev.c | 101 +-
.../net/ethernet/intel/idpf/idpf_ethtool.c | 28 +-
drivers/net/ethernet/intel/idpf/idpf_idc.c | 38 +-
drivers/net/ethernet/intel/idpf/idpf_lib.c | 69 +-
drivers/net/ethernet/intel/idpf/idpf_main.c | 117 +-
drivers/net/ethernet/intel/idpf/idpf_mem.h | 20 -
drivers/net/ethernet/intel/idpf/idpf_txrx.c | 4 +-
drivers/net/ethernet/intel/idpf/idpf_txrx.h | 4 +-
drivers/net/ethernet/intel/idpf/idpf_vf_dev.c | 99 +-
.../net/ethernet/intel/idpf/idpf_virtchnl.c | 1879 +++++++----------
.../net/ethernet/intel/idpf/idpf_virtchnl.h | 106 +-
.../ethernet/intel/idpf/idpf_virtchnl_ptp.c | 309 ++-
drivers/net/ethernet/intel/ixd/Kconfig | 15 +
drivers/net/ethernet/intel/ixd/Makefile | 13 +
drivers/net/ethernet/intel/ixd/ixd.h | 59 +
drivers/net/ethernet/intel/ixd/ixd_ctlq.c | 147 ++
drivers/net/ethernet/intel/ixd/ixd_ctlq.h | 34 +
drivers/net/ethernet/intel/ixd/ixd_dev.c | 89 +
drivers/net/ethernet/intel/ixd/ixd_devlink.c | 97 +
drivers/net/ethernet/intel/ixd/ixd_devlink.h | 50 +
drivers/net/ethernet/intel/ixd/ixd_lan_regs.h | 68 +
drivers/net/ethernet/intel/ixd/ixd_lib.c | 173 ++
drivers/net/ethernet/intel/ixd/ixd_main.c | 145 ++
drivers/net/ethernet/intel/ixd/ixd_virtchnl.c | 178 ++
drivers/net/ethernet/intel/ixd/ixd_virtchnl.h | 12 +
drivers/net/ethernet/intel/libeth/rx.c | 12 +-
drivers/net/ethernet/intel/libie/Kconfig | 14 +
drivers/net/ethernet/intel/libie/Makefile | 8 +
drivers/net/ethernet/intel/libie/controlq.c | 1213 +++++++++++
drivers/net/ethernet/intel/libie/pci.c | 207 ++
include/linux/net/intel/libie/controlq.h | 426 ++++
include/linux/net/intel/libie/pci.h | 56 +
include/linux/{avf => net/intel}/virtchnl.h | 0
.../linux/net/intel}/virtchnl2.h | 0
.../linux/net/intel}/virtchnl2_lan_desc.h | 0
include/net/libeth/rx.h | 4 +-
62 files changed, 4201 insertions(+), 2908 deletions(-)
create mode 100644 Documentation/networking/device_drivers/ethernet/intel/ixd.rst
create mode 100644 Documentation/networking/devlink/ixd.rst
delete mode 100644 drivers/net/ethernet/intel/idpf/idpf_controlq.c
delete mode 100644 drivers/net/ethernet/intel/idpf/idpf_controlq.h
delete mode 100644 drivers/net/ethernet/intel/idpf/idpf_controlq_api.h
delete mode 100644 drivers/net/ethernet/intel/idpf/idpf_controlq_setup.c
delete mode 100644 drivers/net/ethernet/intel/idpf/idpf_mem.h
create mode 100644 drivers/net/ethernet/intel/ixd/Kconfig
create mode 100644 drivers/net/ethernet/intel/ixd/Makefile
create mode 100644 drivers/net/ethernet/intel/ixd/ixd.h
create mode 100644 drivers/net/ethernet/intel/ixd/ixd_ctlq.c
create mode 100644 drivers/net/ethernet/intel/ixd/ixd_ctlq.h
create mode 100644 drivers/net/ethernet/intel/ixd/ixd_dev.c
create mode 100644 drivers/net/ethernet/intel/ixd/ixd_devlink.c
create mode 100644 drivers/net/ethernet/intel/ixd/ixd_devlink.h
create mode 100644 drivers/net/ethernet/intel/ixd/ixd_lan_regs.h
create mode 100644 drivers/net/ethernet/intel/ixd/ixd_lib.c
create mode 100644 drivers/net/ethernet/intel/ixd/ixd_main.c
create mode 100644 drivers/net/ethernet/intel/ixd/ixd_virtchnl.c
create mode 100644 drivers/net/ethernet/intel/ixd/ixd_virtchnl.h
create mode 100644 drivers/net/ethernet/intel/libie/controlq.c
create mode 100644 drivers/net/ethernet/intel/libie/pci.c
create mode 100644 include/linux/net/intel/libie/controlq.h
create mode 100644 include/linux/net/intel/libie/pci.h
rename include/linux/{avf => net/intel}/virtchnl.h (100%)
rename {drivers/net/ethernet/intel/idpf => include/linux/net/intel}/virtchnl2.h (100%)
rename {drivers/net/ethernet/intel/idpf => include/linux/net/intel}/virtchnl2_lan_desc.h (100%)
--
2.47.0
^ permalink raw reply
* [PATCH iwl-next v8 01/15] virtchnl: move virtchnl and virtchnl2 headers to 'include/linux/net/intel'
From: Larysa Zaremba @ 2026-06-08 14:41 UTC (permalink / raw)
To: intel-wired-lan, Tony Nguyen
Cc: aleksander.lobakin, sridhar.samudrala, Michal Swiatkowski,
Larysa Zaremba, Fijalkowski, Maciej, Emil Tantilov, Madhu Chittim,
Josh Hay, Keller, Jacob E, jayaprakash.shanmugam, Jiri Pirko,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Jonathan Corbet, Richard Cochran, Przemek Kitszel,
Andrew Lunn, netdev, linux-doc, linux-kernel, Samuel Salin
In-Reply-To: <20260608144127.2751230-1-larysa.zaremba@intel.com>
From: Victor Raj <victor.raj@intel.com>
virtchnl2 headers will be used by both idpf and ixd drivers, so they have
to be moved to an include directory. On top of that, it would be useful to
place all iavf headers together with other intel networking headers.
Move abovementioned intel header files into 'include/linux/net/intel'.
Suggested-by: Alexander Lobakin <aleksander.lobakin@intel.com>
Reviewed-by: Sridhar Samudrala <sridhar.samudrala@intel.com>
Signed-off-by: Victor Raj <victor.raj@intel.com>
Tested-by: Samuel Salin <Samuel.salin@intel.com>
Signed-off-by: Larysa Zaremba <larysa.zaremba@intel.com>
---
MAINTAINERS | 1 -
drivers/net/ethernet/intel/i40e/i40e.h | 2 +-
drivers/net/ethernet/intel/i40e/i40e_common.c | 2 +-
drivers/net/ethernet/intel/i40e/i40e_prototype.h | 2 +-
drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h | 2 +-
drivers/net/ethernet/intel/iavf/iavf.h | 2 +-
drivers/net/ethernet/intel/iavf/iavf_common.c | 2 +-
drivers/net/ethernet/intel/iavf/iavf_prototype.h | 3 ++-
drivers/net/ethernet/intel/iavf/iavf_types.h | 4 +---
drivers/net/ethernet/intel/ice/ice.h | 2 +-
drivers/net/ethernet/intel/ice/ice_common.h | 2 +-
drivers/net/ethernet/intel/ice/ice_vf_lib.h | 2 +-
drivers/net/ethernet/intel/ice/virt/virtchnl.h | 2 +-
drivers/net/ethernet/intel/idpf/idpf.h | 2 +-
drivers/net/ethernet/intel/idpf/idpf_txrx.h | 2 +-
drivers/net/ethernet/intel/idpf/idpf_virtchnl.h | 2 +-
include/linux/{avf => net/intel}/virtchnl.h | 0
.../intel/idpf => include/linux/net/intel}/virtchnl2.h | 0
.../idpf => include/linux/net/intel}/virtchnl2_lan_desc.h | 0
19 files changed, 16 insertions(+), 18 deletions(-)
rename include/linux/{avf => net/intel}/virtchnl.h (100%)
rename {drivers/net/ethernet/intel/idpf => include/linux/net/intel}/virtchnl2.h (100%)
rename {drivers/net/ethernet/intel/idpf => include/linux/net/intel}/virtchnl2_lan_desc.h (100%)
diff --git a/MAINTAINERS b/MAINTAINERS
index eb8cdcc76324..952f09b40711 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12917,7 +12917,6 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/next-queue.git
F: Documentation/networking/device_drivers/ethernet/intel/
F: drivers/net/ethernet/intel/
F: drivers/net/ethernet/intel/*/
-F: include/linux/avf/virtchnl.h
F: include/linux/net/intel/*/
INTEL ETHERNET PROTOCOL DRIVER FOR RDMA
diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
index 83e780919ac9..1b6a8fbaa648 100644
--- a/drivers/net/ethernet/intel/i40e/i40e.h
+++ b/drivers/net/ethernet/intel/i40e/i40e.h
@@ -8,8 +8,8 @@
#include <linux/pci.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/types.h>
-#include <linux/avf/virtchnl.h>
#include <linux/net/intel/i40e_client.h>
+#include <linux/net/intel/virtchnl.h>
#include <net/devlink.h>
#include <net/pkt_cls.h>
#include <net/udp_tunnel.h>
diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c
index 59f5c1e810eb..8dadfef2c09f 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_common.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_common.c
@@ -1,10 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2013 - 2021 Intel Corporation. */
-#include <linux/avf/virtchnl.h>
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/etherdevice.h>
+#include <linux/net/intel/virtchnl.h>
#include <linux/pci.h>
#include "i40e_adminq_cmd.h"
#include "i40e_devids.h"
diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
index 26bb7bffe361..e3d57550090e 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
@@ -5,7 +5,7 @@
#define _I40E_PROTOTYPE_H_
#include <linux/ethtool.h>
-#include <linux/avf/virtchnl.h>
+#include <linux/net/intel/virtchnl.h>
#include "i40e_debug.h"
#include "i40e_type.h"
diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h
index f558b45725c8..4e119c0502f3 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h
@@ -4,7 +4,7 @@
#ifndef _I40E_VIRTCHNL_PF_H_
#define _I40E_VIRTCHNL_PF_H_
-#include <linux/avf/virtchnl.h>
+#include <linux/net/intel/virtchnl.h>
#include <linux/netdevice.h>
#include "i40e_type.h"
diff --git a/drivers/net/ethernet/intel/iavf/iavf.h b/drivers/net/ethernet/intel/iavf/iavf.h
index 050f8241ef5e..dc31202b2a94 100644
--- a/drivers/net/ethernet/intel/iavf/iavf.h
+++ b/drivers/net/ethernet/intel/iavf/iavf.h
@@ -27,6 +27,7 @@
#include <linux/etherdevice.h>
#include <linux/socket.h>
#include <linux/jiffies.h>
+#include <linux/net/intel/virtchnl.h>
#include <net/ip6_checksum.h>
#include <net/pkt_cls.h>
#include <net/pkt_sched.h>
@@ -37,7 +38,6 @@
#include <net/net_shaper.h>
#include "iavf_type.h"
-#include <linux/avf/virtchnl.h>
#include "iavf_txrx.h"
#include "iavf_fdir.h"
#include "iavf_adv_rss.h"
diff --git a/drivers/net/ethernet/intel/iavf/iavf_common.c b/drivers/net/ethernet/intel/iavf/iavf_common.c
index 614a886bca99..277193a97d91 100644
--- a/drivers/net/ethernet/intel/iavf/iavf_common.c
+++ b/drivers/net/ethernet/intel/iavf/iavf_common.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2013 - 2018 Intel Corporation. */
-#include <linux/avf/virtchnl.h>
+#include <linux/net/intel/virtchnl.h>
#include <linux/bitfield.h>
#include "iavf_type.h"
#include "iavf_adminq.h"
diff --git a/drivers/net/ethernet/intel/iavf/iavf_prototype.h b/drivers/net/ethernet/intel/iavf/iavf_prototype.h
index 7f9f9dbf959a..1b1f6ede3920 100644
--- a/drivers/net/ethernet/intel/iavf/iavf_prototype.h
+++ b/drivers/net/ethernet/intel/iavf/iavf_prototype.h
@@ -4,9 +4,10 @@
#ifndef _IAVF_PROTOTYPE_H_
#define _IAVF_PROTOTYPE_H_
+#include <linux/net/intel/virtchnl.h>
+
#include "iavf_type.h"
#include "iavf_alloc.h"
-#include <linux/avf/virtchnl.h>
/* Prototypes for shared code functions that are not in
* the standard function pointer structures. These are
diff --git a/drivers/net/ethernet/intel/iavf/iavf_types.h b/drivers/net/ethernet/intel/iavf/iavf_types.h
index a095855122bf..35d6d8fcca04 100644
--- a/drivers/net/ethernet/intel/iavf/iavf_types.h
+++ b/drivers/net/ethernet/intel/iavf/iavf_types.h
@@ -4,9 +4,7 @@
#ifndef _IAVF_TYPES_H_
#define _IAVF_TYPES_H_
-#include "iavf_types.h"
-
-#include <linux/avf/virtchnl.h>
+#include <linux/net/intel/virtchnl.h>
#include <linux/ptp_clock_kernel.h>
/* structure used to queue PTP commands for processing */
diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
index 725b130dd3a2..0027b93847c7 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -36,7 +36,7 @@
#include <linux/bpf.h>
#include <linux/btf.h>
#include <linux/auxiliary_bus.h>
-#include <linux/avf/virtchnl.h>
+#include <linux/net/intel/virtchnl.h>
#include <linux/cpu_rmap.h>
#include <linux/dim.h>
#include <linux/gnss.h>
diff --git a/drivers/net/ethernet/intel/ice/ice_common.h b/drivers/net/ethernet/intel/ice/ice_common.h
index e700ac0dc347..d9e7861e8ce0 100644
--- a/drivers/net/ethernet/intel/ice/ice_common.h
+++ b/drivers/net/ethernet/intel/ice/ice_common.h
@@ -5,13 +5,13 @@
#define _ICE_COMMON_H_
#include <linux/bitfield.h>
+#include <linux/net/intel/virtchnl.h>
#include "ice.h"
#include "ice_type.h"
#include "ice_nvm.h"
#include "ice_flex_pipe.h"
#include "ice_parser.h"
-#include <linux/avf/virtchnl.h>
#include "ice_switch.h"
#include "ice_fdir.h"
diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib.h b/drivers/net/ethernet/intel/ice/ice_vf_lib.h
index 7a9c75d1d07c..fa436b3b1eac 100644
--- a/drivers/net/ethernet/intel/ice/ice_vf_lib.h
+++ b/drivers/net/ethernet/intel/ice/ice_vf_lib.h
@@ -8,9 +8,9 @@
#include <linux/hashtable.h>
#include <linux/bitmap.h>
#include <linux/mutex.h>
+#include <linux/net/intel/virtchnl.h>
#include <linux/pci.h>
#include <net/devlink.h>
-#include <linux/avf/virtchnl.h>
#include "ice_type.h"
#include "ice_flow.h"
#include "virt/fdir.h"
diff --git a/drivers/net/ethernet/intel/ice/virt/virtchnl.h b/drivers/net/ethernet/intel/ice/virt/virtchnl.h
index 71bb456e2d71..d11789b3ae1f 100644
--- a/drivers/net/ethernet/intel/ice/virt/virtchnl.h
+++ b/drivers/net/ethernet/intel/ice/virt/virtchnl.h
@@ -7,7 +7,7 @@
#include <linux/types.h>
#include <linux/bitops.h>
#include <linux/if_ether.h>
-#include <linux/avf/virtchnl.h>
+#include <linux/net/intel/virtchnl.h>
#include "ice_vf_lib.h"
/* Restrict number of MAC Addr and VLAN that non-trusted VF can programmed */
diff --git a/drivers/net/ethernet/intel/idpf/idpf.h b/drivers/net/ethernet/intel/idpf/idpf.h
index ec1b75f039bb..984944bab28b 100644
--- a/drivers/net/ethernet/intel/idpf/idpf.h
+++ b/drivers/net/ethernet/intel/idpf/idpf.h
@@ -23,8 +23,8 @@ struct idpf_rss_data;
#include <linux/net/intel/iidc_rdma.h>
#include <linux/net/intel/iidc_rdma_idpf.h>
+#include <linux/net/intel/virtchnl2.h>
-#include "virtchnl2.h"
#include "idpf_txrx.h"
#include "idpf_controlq.h"
diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.h b/drivers/net/ethernet/intel/idpf/idpf_txrx.h
index 4be5b3b6d3ed..08b048664029 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_txrx.h
+++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.h
@@ -5,6 +5,7 @@
#define _IDPF_TXRX_H_
#include <linux/dim.h>
+#include <linux/net/intel/virtchnl2_lan_desc.h>
#include <net/libeth/cache.h>
#include <net/libeth/types.h>
@@ -13,7 +14,6 @@
#include <net/xdp.h>
#include "idpf_lan_txrx.h"
-#include "virtchnl2_lan_desc.h"
#define IDPF_LARGE_MAX_Q 256
#define IDPF_MAX_Q 16
diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h
index 6876e3ed9d1b..972dd88cf3d2 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h
+++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h
@@ -4,7 +4,7 @@
#ifndef _IDPF_VIRTCHNL_H_
#define _IDPF_VIRTCHNL_H_
-#include "virtchnl2.h"
+#include <linux/net/intel/virtchnl2.h>
#define IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC (60 * 1000)
#define IDPF_VC_XN_IDX_M GENMASK(7, 0)
diff --git a/include/linux/avf/virtchnl.h b/include/linux/net/intel/virtchnl.h
similarity index 100%
rename from include/linux/avf/virtchnl.h
rename to include/linux/net/intel/virtchnl.h
diff --git a/drivers/net/ethernet/intel/idpf/virtchnl2.h b/include/linux/net/intel/virtchnl2.h
similarity index 100%
rename from drivers/net/ethernet/intel/idpf/virtchnl2.h
rename to include/linux/net/intel/virtchnl2.h
diff --git a/drivers/net/ethernet/intel/idpf/virtchnl2_lan_desc.h b/include/linux/net/intel/virtchnl2_lan_desc.h
similarity index 100%
rename from drivers/net/ethernet/intel/idpf/virtchnl2_lan_desc.h
rename to include/linux/net/intel/virtchnl2_lan_desc.h
--
2.47.0
^ permalink raw reply related
* [PATCH iwl-next v8 02/15] libie: add PCI device initialization helpers to libie
From: Larysa Zaremba @ 2026-06-08 14:41 UTC (permalink / raw)
To: intel-wired-lan, Tony Nguyen
Cc: aleksander.lobakin, sridhar.samudrala, Michal Swiatkowski,
Larysa Zaremba, Fijalkowski, Maciej, Emil Tantilov, Madhu Chittim,
Josh Hay, Keller, Jacob E, jayaprakash.shanmugam, Jiri Pirko,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Jonathan Corbet, Richard Cochran, Przemek Kitszel,
Andrew Lunn, netdev, linux-doc, linux-kernel, Phani R Burra,
Bharath R, Samuel Salin
In-Reply-To: <20260608144127.2751230-1-larysa.zaremba@intel.com>
From: Phani R Burra <phani.r.burra@intel.com>
idpf and ixd drivers serve different PCI functions on the same device,
therefore their PCI configuration flow is very similar.
Add support functions for idpf and ixd to configure PCI functionality and
access MMIO space. Add a mapping list which can be traversed by a driver,
e.g. to pass certain I/O mappings to the auxbus devices. Such list is also
traversed by the libie_pci_get_mmio_addr() helper, which allows for easier
memory access.
Reviewed-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
Signed-off-by: Phani R Burra <phani.r.burra@intel.com>
Co-developed-by: Victor Raj <victor.raj@intel.com>
Signed-off-by: Victor Raj <victor.raj@intel.com>
Co-developed-by: Sridhar Samudrala <sridhar.samudrala@intel.com>
Signed-off-by: Sridhar Samudrala <sridhar.samudrala@intel.com>
Co-developed-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Tested-by: Bharath R <bharath.r@intel.com>
Tested-by: Samuel Salin <Samuel.salin@intel.com>
Co-developed-by: Larysa Zaremba <larysa.zaremba@intel.com>
Signed-off-by: Larysa Zaremba <larysa.zaremba@intel.com>
---
drivers/net/ethernet/intel/libie/Kconfig | 6 +
drivers/net/ethernet/intel/libie/Makefile | 4 +
drivers/net/ethernet/intel/libie/pci.c | 207 ++++++++++++++++++++++
include/linux/net/intel/libie/pci.h | 56 ++++++
4 files changed, 273 insertions(+)
create mode 100644 drivers/net/ethernet/intel/libie/pci.c
create mode 100644 include/linux/net/intel/libie/pci.h
diff --git a/drivers/net/ethernet/intel/libie/Kconfig b/drivers/net/ethernet/intel/libie/Kconfig
index 70831c7e336e..500a95c944a8 100644
--- a/drivers/net/ethernet/intel/libie/Kconfig
+++ b/drivers/net/ethernet/intel/libie/Kconfig
@@ -23,3 +23,9 @@ config LIBIE_FWLOG
for it. Firmware logging is using admin queue interface to communicate
with the device. Debugfs is a user interface used to config logging
and dump all collected logs.
+
+config LIBIE_PCI
+ tristate
+ help
+ Helper functions for management of PCI resources belonging
+ to networking devices.
diff --git a/drivers/net/ethernet/intel/libie/Makefile b/drivers/net/ethernet/intel/libie/Makefile
index db57fc6780ea..a28509cb9086 100644
--- a/drivers/net/ethernet/intel/libie/Makefile
+++ b/drivers/net/ethernet/intel/libie/Makefile
@@ -12,3 +12,7 @@ libie_adminq-y := adminq.o
obj-$(CONFIG_LIBIE_FWLOG) += libie_fwlog.o
libie_fwlog-y := fwlog.o
+
+obj-$(CONFIG_LIBIE_PCI) += libie_pci.o
+
+libie_pci-y := pci.o
diff --git a/drivers/net/ethernet/intel/libie/pci.c b/drivers/net/ethernet/intel/libie/pci.c
new file mode 100644
index 000000000000..9dd5859d2834
--- /dev/null
+++ b/drivers/net/ethernet/intel/libie/pci.c
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (C) 2025 Intel Corporation */
+
+#include <linux/net/intel/libie/pci.h>
+
+/**
+ * libie_find_mmio_region - find MMIO region containing a range
+ * @mmio_list: list that contains MMIO region info
+ * @offset: range start offset
+ * @size: range size
+ * @bar_idx: BAR index containing the range to search
+ *
+ * Return: pointer to a MMIO region overlapping with the range in any way or
+ * NULL if no such region is mapped.
+ */
+static struct libie_pci_mmio_region *
+libie_find_mmio_region(const struct list_head *mmio_list,
+ resource_size_t offset, resource_size_t size,
+ int bar_idx)
+{
+ resource_size_t end_offset = offset + size;
+ struct libie_pci_mmio_region *mr;
+
+ list_for_each_entry(mr, mmio_list, list) {
+ resource_size_t mr_end = mr->offset + mr->size;
+ resource_size_t mr_start = mr->offset;
+
+ if (mr->bar_idx != bar_idx)
+ continue;
+ if (offset < mr_end && end_offset > mr_start)
+ return mr;
+ }
+
+ return NULL;
+}
+
+/**
+ * __libie_pci_get_mmio_addr - get the MMIO virtual address
+ * @mmio_info: contains list of MMIO regions
+ * @offset: register offset to find
+ * @num_args: number of additional arguments present
+ *
+ * This function finds the virtual address of a register offset by iterating
+ * through the non-linear MMIO regions that are mapped by the driver.
+ *
+ * Return: valid MMIO virtual address or NULL.
+ */
+void __iomem *__libie_pci_get_mmio_addr(struct libie_mmio_info *mmio_info,
+ resource_size_t offset,
+ int num_args, ...)
+{
+ struct libie_pci_mmio_region *mr;
+ int bar_idx = 0;
+ va_list args;
+
+ if (num_args) {
+ va_start(args, num_args);
+ bar_idx = va_arg(args, int);
+ va_end(args);
+ }
+
+ list_for_each_entry(mr, &mmio_info->mmio_list, list)
+ if (bar_idx == mr->bar_idx && offset >= mr->offset &&
+ offset < mr->offset + mr->size) {
+ offset -= mr->offset;
+
+ return mr->addr + offset;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_NS_GPL(__libie_pci_get_mmio_addr, "LIBIE_PCI");
+
+/**
+ * __libie_pci_map_mmio_region - map PCI device MMIO region
+ * @mmio_info: struct to store the mapped MMIO region
+ * @offset: MMIO region start offset
+ * @size: MMIO region size
+ * @num_args: number of additional arguments present
+ *
+ * Return: true on success, false on memory map failure.
+ */
+bool __libie_pci_map_mmio_region(struct libie_mmio_info *mmio_info,
+ resource_size_t offset,
+ resource_size_t size, int num_args, ...)
+{
+ struct pci_dev *pdev = mmio_info->pdev;
+ struct libie_pci_mmio_region *mr;
+ void __iomem *va;
+ int bar_idx = 0;
+ va_list args;
+
+ if (num_args) {
+ va_start(args, num_args);
+ bar_idx = va_arg(args, int);
+ va_end(args);
+ }
+
+ /* pci_iomap_range() would silently map less in such case */
+ if (size_add(offset, size) > pci_resource_len(pdev, bar_idx))
+ return false;
+
+ mr = libie_find_mmio_region(&mmio_info->mmio_list, offset, size,
+ bar_idx);
+ if (mr) {
+ pci_warn(pdev,
+ "Mapping of BAR%u (offset=%llu, size=%llu) intersecting region (offset=%llu, size=%llu) already exists\n",
+ bar_idx, (unsigned long long)mr->offset,
+ (unsigned long long)mr->size,
+ (unsigned long long)offset, (unsigned long long)size);
+ return mr->offset <= offset &&
+ mr->offset + mr->size >= offset + size;
+ }
+
+ va = pci_iomap_range(mmio_info->pdev, bar_idx, offset, size);
+ if (!va) {
+ pci_err(pdev, "Failed to map BAR%u region\n", bar_idx);
+ return false;
+ }
+
+ mr = kvzalloc_obj(*mr);
+ if (!mr) {
+ iounmap(va);
+ return false;
+ }
+
+ mr->addr = va;
+ mr->offset = offset;
+ mr->size = size;
+ mr->bar_idx = bar_idx;
+
+ list_add_tail(&mr->list, &mmio_info->mmio_list);
+
+ return true;
+}
+EXPORT_SYMBOL_NS_GPL(__libie_pci_map_mmio_region, "LIBIE_PCI");
+
+/**
+ * libie_pci_unmap_fltr_regs - unmap selected PCI device MMIO regions
+ * @mmio_info: contains list of MMIO regions to unmap
+ * @fltr: returns true, if region is to be unmapped
+ */
+void libie_pci_unmap_fltr_regs(struct libie_mmio_info *mmio_info,
+ bool (*fltr)(struct libie_mmio_info *mmio_info,
+ struct libie_pci_mmio_region *reg))
+{
+ struct libie_pci_mmio_region *mr, *tmp;
+
+ list_for_each_entry_safe(mr, tmp, &mmio_info->mmio_list, list) {
+ if (!fltr(mmio_info, mr))
+ continue;
+ iounmap(mr->addr);
+ list_del(&mr->list);
+ kvfree(mr);
+ }
+}
+EXPORT_SYMBOL_NS_GPL(libie_pci_unmap_fltr_regs, "LIBIE_PCI");
+
+/**
+ * libie_pci_unmap_all_mmio_regions - unmap all PCI device MMIO regions
+ * @mmio_info: contains list of MMIO regions to unmap
+ */
+void libie_pci_unmap_all_mmio_regions(struct libie_mmio_info *mmio_info)
+{
+ struct libie_pci_mmio_region *mr, *tmp;
+
+ list_for_each_entry_safe(mr, tmp, &mmio_info->mmio_list, list) {
+ iounmap(mr->addr);
+ list_del(&mr->list);
+ kvfree(mr);
+ }
+}
+EXPORT_SYMBOL_NS_GPL(libie_pci_unmap_all_mmio_regions, "LIBIE_PCI");
+
+/**
+ * libie_pci_init_dev - enable and reserve PCI regions of the device
+ * @pdev: PCI device information
+ *
+ * Return: %0 on success, -%errno on failure.
+ */
+int libie_pci_init_dev(struct pci_dev *pdev)
+{
+ int err;
+
+ err = pcim_enable_device(pdev);
+ if (err)
+ return err;
+
+ for (int bar = 0; bar < PCI_STD_NUM_BARS; bar++)
+ if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) {
+ err = pcim_request_region(pdev, bar, pci_name(pdev));
+ if (err)
+ return err;
+ }
+
+ err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+ if (err)
+ return err;
+
+ pci_set_master(pdev);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(libie_pci_init_dev, "LIBIE_PCI");
+
+MODULE_DESCRIPTION("Common Ethernet PCI library");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/net/intel/libie/pci.h b/include/linux/net/intel/libie/pci.h
new file mode 100644
index 000000000000..effd072c55c8
--- /dev/null
+++ b/include/linux/net/intel/libie/pci.h
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2025 Intel Corporation */
+
+#ifndef __LIBIE_PCI_H
+#define __LIBIE_PCI_H
+
+#include <linux/pci.h>
+
+/**
+ * struct libie_pci_mmio_region - structure for MMIO region info
+ * @list: used to add a MMIO region to the list of MMIO regions in
+ * libie_mmio_info
+ * @addr: virtual address of MMIO region start
+ * @offset: start offset of the MMIO region
+ * @size: size of the MMIO region
+ * @bar_idx: BAR index to which the MMIO region belongs to
+ */
+struct libie_pci_mmio_region {
+ struct list_head list;
+ void __iomem *addr;
+ resource_size_t offset;
+ resource_size_t size;
+ u16 bar_idx;
+};
+
+/**
+ * struct libie_mmio_info - contains list of MMIO regions
+ * @pdev: PCI device pointer
+ * @mmio_list: list of MMIO regions
+ */
+struct libie_mmio_info {
+ struct pci_dev *pdev;
+ struct list_head mmio_list;
+};
+
+#define libie_pci_map_mmio_region(mmio_info, offset, size, ...) \
+ __libie_pci_map_mmio_region(mmio_info, offset, size, \
+ COUNT_ARGS(__VA_ARGS__), ##__VA_ARGS__)
+
+#define libie_pci_get_mmio_addr(mmio_info, offset, ...) \
+ __libie_pci_get_mmio_addr(mmio_info, offset, \
+ COUNT_ARGS(__VA_ARGS__), ##__VA_ARGS__)
+
+bool __libie_pci_map_mmio_region(struct libie_mmio_info *mmio_info,
+ resource_size_t offset, resource_size_t size,
+ int num_args, ...);
+void __iomem *__libie_pci_get_mmio_addr(struct libie_mmio_info *mmio_info,
+ resource_size_t offset,
+ int num_args, ...);
+void libie_pci_unmap_all_mmio_regions(struct libie_mmio_info *mmio_info);
+void libie_pci_unmap_fltr_regs(struct libie_mmio_info *mmio_info,
+ bool (*fltr)(struct libie_mmio_info *mmio_info,
+ struct libie_pci_mmio_region *reg));
+int libie_pci_init_dev(struct pci_dev *pdev);
+
+#endif /* __LIBIE_PCI_H */
--
2.47.0
^ permalink raw reply related
* [PATCH iwl-next v8 03/15] libeth: allow to create fill queues without NAPI
From: Larysa Zaremba @ 2026-06-08 14:41 UTC (permalink / raw)
To: intel-wired-lan, Tony Nguyen
Cc: aleksander.lobakin, sridhar.samudrala, Michal Swiatkowski,
Larysa Zaremba, Fijalkowski, Maciej, Emil Tantilov, Madhu Chittim,
Josh Hay, Keller, Jacob E, jayaprakash.shanmugam, Jiri Pirko,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Jonathan Corbet, Richard Cochran, Przemek Kitszel,
Andrew Lunn, netdev, linux-doc, linux-kernel, Bharath R,
Samuel Salin
In-Reply-To: <20260608144127.2751230-1-larysa.zaremba@intel.com>
From: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Control queues can utilize libeth_rx fill queues, despite working outside
of NAPI context. The only problem is standard fill queues requiring NAPI
that provides them with the device pointer.
Introduce a way to provide the device directly without using NAPI.
Suggested-by: Alexander Lobakin <aleksander.lobakin@intel.com>
Reviewed-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Tested-by: Bharath R <bharath.r@intel.com>
Tested-by: Samuel Salin <Samuel.salin@intel.com>
Signed-off-by: Larysa Zaremba <larysa.zaremba@intel.com>
---
drivers/net/ethernet/intel/libeth/rx.c | 12 ++++++++----
include/net/libeth/rx.h | 4 +++-
2 files changed, 11 insertions(+), 5 deletions(-)
diff --git a/drivers/net/ethernet/intel/libeth/rx.c b/drivers/net/ethernet/intel/libeth/rx.c
index 62521a1f4ec9..0c1a565a1b3a 100644
--- a/drivers/net/ethernet/intel/libeth/rx.c
+++ b/drivers/net/ethernet/intel/libeth/rx.c
@@ -145,25 +145,29 @@ static bool libeth_rx_page_pool_params_zc(struct libeth_fq *fq,
/**
* libeth_rx_fq_create - create a PP with the default libeth settings
* @fq: buffer queue struct to fill
- * @napi: &napi_struct covering this PP (no usage outside its poll loops)
+ * @napi_dev: &napi_struct for NAPI (data) queues, &device for others
*
* Return: %0 on success, -%errno on failure.
*/
-int libeth_rx_fq_create(struct libeth_fq *fq, struct napi_struct *napi)
+int libeth_rx_fq_create(struct libeth_fq *fq, void *napi_dev)
{
+ struct napi_struct *napi = fq->no_napi ? NULL : napi_dev;
struct page_pool_params pp = {
.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV,
.order = LIBETH_RX_PAGE_ORDER,
.pool_size = fq->count,
.nid = fq->nid,
- .dev = napi->dev->dev.parent,
- .netdev = napi->dev,
+ .dev = napi ? napi->dev->dev.parent : napi_dev,
+ .netdev = napi ? napi->dev : NULL,
.napi = napi,
};
struct libeth_fqe *fqes;
struct page_pool *pool;
int ret;
+ if (!pp.netdev && fq->type == LIBETH_FQE_MTU)
+ return -EINVAL;
+
pp.dma_dir = fq->xdp ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE;
if (!fq->hsplit)
diff --git a/include/net/libeth/rx.h b/include/net/libeth/rx.h
index 5d991404845e..0e736846c5e8 100644
--- a/include/net/libeth/rx.h
+++ b/include/net/libeth/rx.h
@@ -69,6 +69,7 @@ enum libeth_fqe_type {
* @type: type of the buffers this queue has
* @hsplit: flag whether header split is enabled
* @xdp: flag indicating whether XDP is enabled
+ * @no_napi: the queue is not a data queue and does not have NAPI
* @buf_len: HW-writeable length per each buffer
* @nid: ID of the closest NUMA node with memory
*/
@@ -85,12 +86,13 @@ struct libeth_fq {
enum libeth_fqe_type type:2;
bool hsplit:1;
bool xdp:1;
+ bool no_napi:1;
u32 buf_len;
int nid;
};
-int libeth_rx_fq_create(struct libeth_fq *fq, struct napi_struct *napi);
+int libeth_rx_fq_create(struct libeth_fq *fq, void *napi_dev);
void libeth_rx_fq_destroy(struct libeth_fq *fq);
/**
--
2.47.0
^ permalink raw reply related
* [PATCH iwl-next v8 04/15] libie: add control queue support
From: Larysa Zaremba @ 2026-06-08 14:41 UTC (permalink / raw)
To: intel-wired-lan, Tony Nguyen
Cc: aleksander.lobakin, sridhar.samudrala, Michal Swiatkowski,
Larysa Zaremba, Fijalkowski, Maciej, Emil Tantilov, Madhu Chittim,
Josh Hay, Keller, Jacob E, jayaprakash.shanmugam, Jiri Pirko,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Jonathan Corbet, Richard Cochran, Przemek Kitszel,
Andrew Lunn, netdev, linux-doc, linux-kernel, Phani R Burra,
Samuel Salin, Bharath R
In-Reply-To: <20260608144127.2751230-1-larysa.zaremba@intel.com>
From: Phani R Burra <phani.r.burra@intel.com>
Libie will now support control queue setup and configuration APIs. These
are mainly used for mailbox communication between drivers and control
plane.
Make use of the libeth_rx page pool support for managing controlq buffers.
Reviewed-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
Signed-off-by: Phani R Burra <phani.r.burra@intel.com>
Co-developed-by: Victor Raj <victor.raj@intel.com>
Signed-off-by: Victor Raj <victor.raj@intel.com>
Co-developed-by: Sridhar Samudrala <sridhar.samudrala@intel.com>
Signed-off-by: Sridhar Samudrala <sridhar.samudrala@intel.com>
Co-developed-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Tested-by: Samuel Salin <Samuel.salin@intel.com>
Tested-by: Bharath R <bharath.r@intel.com>
Co-developed-by: Larysa Zaremba <larysa.zaremba@intel.com>
Signed-off-by: Larysa Zaremba <larysa.zaremba@intel.com>
---
drivers/net/ethernet/intel/libie/Kconfig | 8 +
drivers/net/ethernet/intel/libie/Makefile | 4 +
drivers/net/ethernet/intel/libie/controlq.c | 614 ++++++++++++++++++++
include/linux/net/intel/libie/controlq.h | 249 ++++++++
4 files changed, 875 insertions(+)
create mode 100644 drivers/net/ethernet/intel/libie/controlq.c
create mode 100644 include/linux/net/intel/libie/controlq.h
diff --git a/drivers/net/ethernet/intel/libie/Kconfig b/drivers/net/ethernet/intel/libie/Kconfig
index 500a95c944a8..9c5fdebb6766 100644
--- a/drivers/net/ethernet/intel/libie/Kconfig
+++ b/drivers/net/ethernet/intel/libie/Kconfig
@@ -15,6 +15,14 @@ config LIBIE_ADMINQ
Helper functions used by Intel Ethernet drivers for administration
queue command interface (aka adminq).
+config LIBIE_CP
+ tristate
+ select LIBETH
+ select LIBIE_PCI
+ help
+ Common helper routines to communicate with the device Control Plane
+ using virtchnl2 or related mailbox protocols.
+
config LIBIE_FWLOG
tristate
select LIBIE_ADMINQ
diff --git a/drivers/net/ethernet/intel/libie/Makefile b/drivers/net/ethernet/intel/libie/Makefile
index a28509cb9086..3065aa057798 100644
--- a/drivers/net/ethernet/intel/libie/Makefile
+++ b/drivers/net/ethernet/intel/libie/Makefile
@@ -9,6 +9,10 @@ obj-$(CONFIG_LIBIE_ADMINQ) += libie_adminq.o
libie_adminq-y := adminq.o
+obj-$(CONFIG_LIBIE_CP) += libie_cp.o
+
+libie_cp-y := controlq.o
+
obj-$(CONFIG_LIBIE_FWLOG) += libie_fwlog.o
libie_fwlog-y := fwlog.o
diff --git a/drivers/net/ethernet/intel/libie/controlq.c b/drivers/net/ethernet/intel/libie/controlq.c
new file mode 100644
index 000000000000..f926fc0a0f1a
--- /dev/null
+++ b/drivers/net/ethernet/intel/libie/controlq.c
@@ -0,0 +1,614 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (C) 2025 Intel Corporation */
+
+#include <linux/bitfield.h>
+#include <net/libeth/rx.h>
+
+#include <linux/net/intel/libie/controlq.h>
+
+#define LIBIE_CTLQ_DESC_QWORD0(sz) \
+ (LIBIE_CTLQ_DESC_FLAG_BUF | \
+ LIBIE_CTLQ_DESC_FLAG_RD | \
+ FIELD_PREP(LIBIE_CTLQ_DESC_DATA_LEN, sz))
+
+/**
+ * libie_ctlq_free_fq - free fill queue resources, including buffers
+ * @ctlq: Rx control queue whose resources need to be freed
+ */
+static void libie_ctlq_free_fq(struct libie_ctlq_info *ctlq)
+{
+ struct libeth_fq fq = {
+ .fqes = ctlq->rx_fqes,
+ .pp = ctlq->pp,
+ };
+
+ for (u32 ntc = ctlq->next_to_clean; ntc != ctlq->next_to_post; ) {
+ page_pool_put_full_netmem(fq.pp, fq.fqes[ntc].netmem, false);
+
+ if (++ntc >= ctlq->ring_len)
+ ntc = 0;
+ }
+
+ libeth_rx_fq_destroy(&fq);
+}
+
+/**
+ * libie_ctlq_init_fq - initialize fill queue for an Rx controlq
+ * @ctlq: control queue that needs a Rx buffer allocation
+ *
+ * Return: %0 on success, -%errno on failure
+ */
+static int libie_ctlq_init_fq(struct libie_ctlq_info *ctlq)
+{
+ struct libeth_fq fq = {
+ .count = ctlq->ring_len,
+ .truesize = LIBIE_CTLQ_MAX_BUF_LEN,
+ .nid = NUMA_NO_NODE,
+ .type = LIBETH_FQE_SHORT,
+ .hsplit = true,
+ .no_napi = true,
+ };
+ int err;
+
+ err = libeth_rx_fq_create(&fq, ctlq->dev);
+ if (err)
+ return err;
+
+ ctlq->pp = fq.pp;
+ ctlq->rx_fqes = fq.fqes;
+ ctlq->truesize = fq.truesize;
+
+ return 0;
+}
+
+/**
+ * libie_ctlq_reset_rx_desc - reset the descriptor with a new address
+ * @desc: descriptor to (re)initialize
+ * @addr: physical address to put into descriptor
+ * @mem_truesize: size of the accessible memory
+ */
+static void libie_ctlq_reset_rx_desc(struct libie_ctlq_desc *desc,
+ dma_addr_t addr, u32 mem_truesize)
+{
+ u64 qword;
+
+ *desc = (struct libie_ctlq_desc) {};
+ qword = LIBIE_CTLQ_DESC_QWORD0(mem_truesize);
+ desc->qword0 = cpu_to_le64(qword);
+
+ qword = FIELD_PREP(LIBIE_CTLQ_DESC_DATA_ADDR_HIGH,
+ upper_32_bits(addr)) |
+ FIELD_PREP(LIBIE_CTLQ_DESC_DATA_ADDR_LOW,
+ lower_32_bits(addr));
+ desc->qword3 = cpu_to_le64(qword);
+}
+
+/**
+ * libie_ctlq_post_rx_buffs - post buffers to descriptor ring
+ * @ctlq: control queue that requires Rx descriptor ring to be initialized with
+ * new Rx buffers
+ *
+ * The caller must make sure that calls to libie_ctlq_post_rx_buffs()
+ * and libie_ctlq_recv() for each queue are either serialized
+ * or used under ctlq->lock.
+ *
+ * Return: %0 on success, -%ENOMEM if any buffer could not be allocated
+ */
+int libie_ctlq_post_rx_buffs(struct libie_ctlq_info *ctlq)
+{
+ u32 ntp = ctlq->next_to_post, ntc = ctlq->next_to_clean, num_to_post;
+ const struct libeth_fq_fp fq = {
+ .pp = ctlq->pp,
+ .fqes = ctlq->rx_fqes,
+ .truesize = ctlq->truesize,
+ .count = ctlq->ring_len,
+ };
+ int ret = 0;
+
+ num_to_post = (ntc > ntp ? 0 : ctlq->ring_len) + ntc - ntp - 1;
+
+ while (num_to_post--) {
+ dma_addr_t addr;
+
+ addr = libeth_rx_alloc(&fq, ntp);
+ if (unlikely(addr == DMA_MAPPING_ERROR)) {
+ ret = -ENOMEM;
+ goto post_bufs;
+ }
+
+ libie_ctlq_reset_rx_desc(&ctlq->descs[ntp], addr, fq.truesize);
+
+ if (unlikely(++ntp == ctlq->ring_len))
+ ntp = 0;
+ }
+
+post_bufs:
+ if (likely(ctlq->next_to_post != ntp)) {
+ ctlq->next_to_post = ntp;
+
+ writel(ntp, ctlq->reg.tail);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(libie_ctlq_post_rx_buffs, "LIBIE_CP");
+
+/**
+ * libie_ctlq_free_tx_msgs - Free Tx control queue messages
+ * @ctlq: Tx control queue being destroyed
+ * @num_msgs: number of messages allocated so far
+ */
+static void libie_ctlq_free_tx_msgs(struct libie_ctlq_info *ctlq,
+ u32 num_msgs)
+{
+ for (u32 i = 0; i < num_msgs; i++)
+ kfree(ctlq->tx_msg[i]);
+
+ kvfree(ctlq->tx_msg);
+}
+
+/**
+ * libie_ctlq_alloc_tx_msgs - Allocate Tx control queue messages
+ * @ctlq: Tx control queue being created
+ *
+ * Return: %0 on success, -%ENOMEM on allocation error
+ */
+static int libie_ctlq_alloc_tx_msgs(struct libie_ctlq_info *ctlq)
+{
+ ctlq->tx_msg = kvzalloc_objs(*ctlq->tx_msg, ctlq->ring_len,
+ GFP_KERNEL);
+ if (!ctlq->tx_msg)
+ return -ENOMEM;
+
+ for (u32 i = 0; i < ctlq->ring_len; i++) {
+ ctlq->tx_msg[i] = kzalloc_obj(*ctlq->tx_msg[i]);
+ if (!ctlq->tx_msg[i]) {
+ libie_ctlq_free_tx_msgs(ctlq, i);
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * libie_cp_free_dma_mem - Free the previously allocated DMA memory
+ * @dev: device information
+ * @mem: DMA memory information
+ */
+static void libie_cp_free_dma_mem(struct device *dev,
+ struct libie_cp_dma_mem *mem)
+{
+ dma_free_coherent(dev, mem->size, mem->va, mem->pa);
+ mem->va = NULL;
+}
+
+/**
+ * libie_ctlq_dealloc_ring_res - Free memory allocated for control queue
+ * @ctlq: control queue that requires its ring memory to be freed
+ *
+ * Free the memory used by the ring, buffers and other related structures.
+ */
+static void libie_ctlq_dealloc_ring_res(struct libie_ctlq_info *ctlq)
+{
+ struct libie_cp_dma_mem *dma = &ctlq->ring_mem;
+
+ if (ctlq->type == LIBIE_CTLQ_TYPE_TX)
+ libie_ctlq_free_tx_msgs(ctlq, ctlq->ring_len);
+ else
+ libie_ctlq_free_fq(ctlq);
+
+ libie_cp_free_dma_mem(ctlq->dev, dma);
+}
+
+/**
+ * libie_cp_alloc_dma_mem - Allocate a DMA memory
+ * @dev: device information
+ * @mem: memory for DMA information to be stored
+ * @size: size of the memory to allocate
+ *
+ * Return: virtual address of DMA memory or NULL.
+ */
+static void *libie_cp_alloc_dma_mem(struct device *dev,
+ struct libie_cp_dma_mem *mem, u32 size)
+{
+ size = ALIGN(size, SZ_4K);
+
+ mem->va = dma_alloc_coherent(dev, size, &mem->pa, GFP_KERNEL);
+ mem->size = size;
+
+ return mem->va;
+}
+
+/**
+ * libie_ctlq_alloc_queue_res - allocate memory for descriptor ring and bufs
+ * @ctlq: control queue that requires its ring resources to be allocated
+ *
+ * Return: %0 on success, -%errno on failure
+ */
+static int libie_ctlq_alloc_queue_res(struct libie_ctlq_info *ctlq)
+{
+ size_t size = array_size(ctlq->ring_len, sizeof(*ctlq->descs));
+ struct libie_cp_dma_mem *dma = &ctlq->ring_mem;
+ int err = -ENOMEM;
+
+ if (!libie_cp_alloc_dma_mem(ctlq->dev, dma, size))
+ return -ENOMEM;
+
+ ctlq->descs = dma->va;
+
+ if (ctlq->type == LIBIE_CTLQ_TYPE_TX) {
+ if (libie_ctlq_alloc_tx_msgs(ctlq))
+ goto free_dma_mem;
+ } else {
+ err = libie_ctlq_init_fq(ctlq);
+ if (err)
+ goto free_dma_mem;
+
+ err = libie_ctlq_post_rx_buffs(ctlq);
+ if (err) {
+ libie_ctlq_free_fq(ctlq);
+ goto free_dma_mem;
+ }
+ }
+
+ return 0;
+
+free_dma_mem:
+ libie_cp_free_dma_mem(ctlq->dev, dma);
+
+ return err;
+}
+
+/**
+ * libie_ctlq_init_regs - Initialize control queue registers
+ * @ctlq: control queue that needs to be initialized
+ *
+ * Initialize registers. The caller is expected to have already initialized the
+ * descriptor ring memory and buffer memory.
+ */
+static void libie_ctlq_init_regs(struct libie_ctlq_info *ctlq)
+{
+ u32 dword;
+
+ if (ctlq->type == LIBIE_CTLQ_TYPE_RX)
+ writel(ctlq->ring_len - 1, ctlq->reg.tail);
+
+ writel(0, ctlq->reg.head);
+ writel(lower_32_bits(ctlq->ring_mem.pa), ctlq->reg.addr_low);
+ writel(upper_32_bits(ctlq->ring_mem.pa), ctlq->reg.addr_high);
+
+ dword = FIELD_PREP(LIBIE_CTLQ_MBX_ATQ_LEN, ctlq->ring_len) |
+ ctlq->reg.len_ena_mask;
+ writel(dword, ctlq->reg.len);
+}
+
+/**
+ * libie_find_ctlq - find the controlq for the given id and type
+ * @ctx: controlq context structure
+ * @type: type of controlq to find
+ * @id: controlq id to find
+ *
+ * Return: control queue info pointer on success, NULL on failure
+ */
+struct libie_ctlq_info *libie_find_ctlq(struct libie_ctlq_ctx *ctx,
+ enum virtchnl2_queue_type type,
+ int id)
+{
+ struct libie_ctlq_info *cq;
+
+ guard(spinlock)(&ctx->ctlqs_lock);
+
+ list_for_each_entry(cq, &ctx->ctlqs, list)
+ if (cq->qid == id && cq->type == type)
+ return cq;
+
+ return NULL;
+}
+EXPORT_SYMBOL_NS_GPL(libie_find_ctlq, "LIBIE_CP");
+
+/**
+ * libie_ctlq_add - add one control queue
+ * @ctx: controlq context information
+ * @qinfo: information that requires for queue creation
+ *
+ * Allocate and initialize a control queue and add it to the control queue list.
+ * The ctlq parameter will be allocated/initialized and passed back to the
+ * caller if no errors occur.
+ *
+ * Note: libie_ctlq_init must be called prior to any calls to libie_ctlq_add.
+ *
+ * Return: added control queue info pointer on success, error pointer on failure
+ */
+static struct libie_ctlq_info *
+libie_ctlq_add(struct libie_ctlq_ctx *ctx,
+ const struct libie_ctlq_create_info *qinfo)
+{
+ struct libie_ctlq_info *ctlq;
+
+ if (qinfo->id != LIBIE_CTLQ_MBX_ID)
+ return ERR_PTR(-EOPNOTSUPP);
+
+ /* libie_ctlq_init was not called */
+ scoped_guard(spinlock, &ctx->ctlqs_lock)
+ if (!ctx->ctlqs.next)
+ return ERR_PTR(-EINVAL);
+
+ ctlq = kvzalloc_obj(*ctlq);
+ if (!ctlq)
+ return ERR_PTR(-ENOMEM);
+
+ ctlq->type = qinfo->type;
+ ctlq->qid = qinfo->id;
+ ctlq->ring_len = qinfo->len;
+ ctlq->dev = &ctx->mmio_info.pdev->dev;
+ ctlq->reg = qinfo->reg;
+
+ if (libie_ctlq_alloc_queue_res(ctlq)) {
+ kvfree(ctlq);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ libie_ctlq_init_regs(ctlq);
+
+ spin_lock_init(&ctlq->lock);
+
+ scoped_guard(spinlock, &ctx->ctlqs_lock)
+ list_add(&ctlq->list, &ctx->ctlqs);
+
+ return ctlq;
+}
+
+/**
+ * libie_ctlq_remove - deallocate and remove specified control queue
+ * @ctx: libie context information
+ * @ctlq: specific control queue that needs to be removed
+ */
+static void libie_ctlq_remove(struct libie_ctlq_ctx *ctx,
+ struct libie_ctlq_info *ctlq)
+{
+ scoped_guard(spinlock, &ctx->ctlqs_lock)
+ list_del(&ctlq->list);
+
+ libie_ctlq_dealloc_ring_res(ctlq);
+ kvfree(ctlq);
+}
+
+/**
+ * libie_ctlq_init - main initialization routine for all control queues
+ * @ctx: libie context information
+ * @qinfo: array of structs containing info for each queue to be initialized
+ * @numq: number of queues to initialize
+ *
+ * This initializes queue list and adds any number and any type of control
+ * queues. This is an all or nothing routine; if one fails, all previously
+ * allocated queues will be destroyed. This must be called prior to using
+ * the individual add/remove APIs.
+ *
+ * Return: %0 on success, -%errno on failure
+ */
+int libie_ctlq_init(struct libie_ctlq_ctx *ctx,
+ const struct libie_ctlq_create_info *qinfo,
+ u32 numq)
+{
+ INIT_LIST_HEAD(&ctx->ctlqs);
+ spin_lock_init(&ctx->ctlqs_lock);
+
+ for (u32 i = 0; i < numq; i++) {
+ struct libie_ctlq_info *ctlq;
+
+ ctlq = libie_ctlq_add(ctx, &qinfo[i]);
+ if (IS_ERR(ctlq)) {
+ libie_ctlq_deinit(ctx);
+ return PTR_ERR(ctlq);
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(libie_ctlq_init, "LIBIE_CP");
+
+/**
+ * libie_ctlq_deinit - destroy all control queues
+ * @ctx: libie CP context information
+ */
+void libie_ctlq_deinit(struct libie_ctlq_ctx *ctx)
+{
+ struct libie_ctlq_info *ctlq, *tmp;
+
+ list_for_each_entry_safe(ctlq, tmp, &ctx->ctlqs, list)
+ libie_ctlq_remove(ctx, ctlq);
+}
+EXPORT_SYMBOL_NS_GPL(libie_ctlq_deinit, "LIBIE_CP");
+
+/**
+ * libie_ctlq_tx_desc_from_msg - initialize a Tx descriptor from a message
+ * @desc: descriptor to be initialized
+ * @msg: filled control queue message
+ */
+static void libie_ctlq_tx_desc_from_msg(struct libie_ctlq_desc *desc,
+ const struct libie_ctlq_msg *msg)
+{
+ const struct libie_cp_dma_mem *dma = &msg->send_mem;
+ u64 qword;
+
+ qword = FIELD_PREP(LIBIE_CTLQ_DESC_FLAGS, msg->flags) |
+ FIELD_PREP(LIBIE_CTLQ_DESC_INFRA_OPCODE, msg->opcode) |
+ FIELD_PREP(LIBIE_CTLQ_DESC_PFID_VFID, msg->func_id);
+ desc->qword0 = cpu_to_le64(qword);
+
+ qword = FIELD_PREP(LIBIE_CTLQ_DESC_VIRTCHNL_OPCODE,
+ msg->chnl_opcode) |
+ FIELD_PREP(LIBIE_CTLQ_DESC_VIRTCHNL_MSG_RET_VAL,
+ msg->chnl_retval);
+ desc->qword1 = cpu_to_le64(qword);
+
+ qword = FIELD_PREP(LIBIE_CTLQ_DESC_MSG_PARAM0, msg->param0) |
+ FIELD_PREP(LIBIE_CTLQ_DESC_SW_COOKIE,
+ msg->sw_cookie) |
+ FIELD_PREP(LIBIE_CTLQ_DESC_VIRTCHNL_FLAGS,
+ msg->virt_flags);
+ desc->qword2 = cpu_to_le64(qword);
+
+ if (likely(msg->data_len)) {
+ desc->qword0 |=
+ cpu_to_le64(LIBIE_CTLQ_DESC_QWORD0(msg->data_len));
+ qword = FIELD_PREP(LIBIE_CTLQ_DESC_DATA_ADDR_HIGH,
+ upper_32_bits(dma->pa)) |
+ FIELD_PREP(LIBIE_CTLQ_DESC_DATA_ADDR_LOW,
+ lower_32_bits(dma->pa));
+ } else {
+ qword = msg->addr_param;
+ }
+
+ desc->qword3 = cpu_to_le64(qword);
+}
+
+/**
+ * libie_ctlq_send_desc_avail - get number of free descriptors on a Tx ctlq
+ * @ctlq: specific control queue which is going be used for sending messages
+ *
+ * The caller must hold ctlq->lock. Any dependent sending must be done
+ * in the same critical section.
+ *
+ * Return: number of available descriptors/messages on a given control queue.
+ */
+u32 libie_ctlq_send_desc_avail(const struct libie_ctlq_info *ctlq)
+{
+ u32 ntu = ctlq->next_to_use, ntc = ctlq->next_to_clean;
+
+ return (ntc > ntu ? 0 : ctlq->ring_len) + ntc - ntu - 1;
+}
+EXPORT_SYMBOL_NS_GPL(libie_ctlq_send_desc_avail, "LIBIE_CP");
+
+/**
+ * libie_ctlq_send - send a message to Control Plane or Peer
+ * @ctlq: specific control queue which is used for sending a message
+ * @num_q_msg: number of messages present to send on @ctlq,
+ * positive and no greater than the number of available descriptors
+ *
+ * The caller must fill in @num_q_msg Tx messages staring at ntu beforehand.
+ *
+ * The caller must hold ctlq->lock. The intended pattern is to first check
+ * the number of descriptors available, then fill in the messages and perform
+ * send within a single critical section.
+ *
+ * Return: %0 on success, -%errno on failure.
+ */
+void libie_ctlq_send(struct libie_ctlq_info *ctlq, u32 num_q_msg)
+{
+ u32 ntu = ctlq->next_to_use;
+
+ for (int i = 0; i < num_q_msg; i++) {
+ struct libie_ctlq_msg *msg = ctlq->tx_msg[ntu];
+ struct libie_ctlq_desc *desc;
+
+ desc = &ctlq->descs[ntu];
+ libie_ctlq_tx_desc_from_msg(desc, msg);
+
+ if (unlikely(++ntu == ctlq->ring_len))
+ ntu = 0;
+ }
+ writel(ntu, ctlq->reg.tail);
+ ctlq->next_to_use = ntu;
+}
+EXPORT_SYMBOL_NS_GPL(libie_ctlq_send, "LIBIE_CP");
+
+/**
+ * libie_ctlq_fill_rx_msg - fill in a message from Rx descriptor and buffer
+ * @msg: message to be filled in
+ * @desc: received descriptor
+ * @rx_buf: fill queue buffer associated with the descriptor
+ */
+static void libie_ctlq_fill_rx_msg(struct libie_ctlq_msg *msg,
+ const struct libie_ctlq_desc *desc,
+ struct libeth_fqe *rx_buf)
+{
+ u64 qword = le64_to_cpu(desc->qword0);
+
+ msg->flags = FIELD_GET(LIBIE_CTLQ_DESC_FLAGS, qword);
+ msg->opcode = FIELD_GET(LIBIE_CTLQ_DESC_INFRA_OPCODE, qword);
+ msg->data_len = FIELD_GET(LIBIE_CTLQ_DESC_DATA_LEN, qword);
+ msg->hw_retval = FIELD_GET(LIBIE_CTLQ_DESC_HW_RETVAL, qword);
+
+ qword = le64_to_cpu(desc->qword1);
+ msg->chnl_opcode =
+ FIELD_GET(LIBIE_CTLQ_DESC_VIRTCHNL_OPCODE, qword);
+ msg->chnl_retval =
+ FIELD_GET(LIBIE_CTLQ_DESC_VIRTCHNL_MSG_RET_VAL, qword);
+
+ qword = le64_to_cpu(desc->qword2);
+ msg->param0 =
+ FIELD_GET(LIBIE_CTLQ_DESC_MSG_PARAM0, qword);
+ msg->sw_cookie =
+ FIELD_GET(LIBIE_CTLQ_DESC_SW_COOKIE, qword);
+ msg->virt_flags =
+ FIELD_GET(LIBIE_CTLQ_DESC_VIRTCHNL_FLAGS, qword);
+
+ if (likely(msg->data_len)) {
+ if (unlikely(msg->data_len > LIBIE_CTLQ_MAX_BUF_LEN)) {
+ msg->data_len = LIBIE_CTLQ_MAX_BUF_LEN;
+ msg->chnl_retval = U32_MAX;
+ }
+ msg->recv_mem = (struct kvec) {
+ .iov_base = netmem_address(rx_buf->netmem),
+ .iov_len = msg->data_len,
+ };
+ libeth_rx_sync_for_cpu(rx_buf, msg->data_len);
+ } else {
+ msg->recv_mem = (struct kvec) {};
+ msg->addr_param = le64_to_cpu(desc->qword3);
+ page_pool_put_full_netmem(netmem_get_pp(rx_buf->netmem),
+ rx_buf->netmem, false);
+ }
+}
+
+/**
+ * libie_ctlq_recv - receive control queue message call back
+ * @ctlq: control queue that needs to processed for receive
+ * @msg: array of received control queue messages on this q;
+ * needs to be pre-allocated by caller for as many messages as requested
+ * @num_q_msg: number of messages that can be stored in msg buffer
+ *
+ * Called by interrupt handler or polling mechanism. Caller is expected
+ * to free buffers.
+ *
+ * The caller must make sure that calls to libie_ctlq_post_rx_buffs()
+ * and libie_ctlq_recv() for each queue are either serialized
+ * or used under ctlq->lock.
+ *
+ * Return: number of messages received
+ */
+u32 libie_ctlq_recv(struct libie_ctlq_info *ctlq, struct libie_ctlq_msg *msg,
+ u32 num_q_msg)
+{
+ u32 ntc, i;
+
+ ntc = ctlq->next_to_clean;
+
+ for (i = 0; i < num_q_msg; i++) {
+ const struct libie_ctlq_desc *desc = &ctlq->descs[ntc];
+ struct libeth_fqe *rx_buf = &ctlq->rx_fqes[ntc];
+ u64 qword;
+
+ qword = le64_to_cpu(desc->qword0);
+ if (!FIELD_GET(LIBIE_CTLQ_DESC_FLAG_DD, qword))
+ break;
+
+ dma_rmb();
+
+ libie_ctlq_fill_rx_msg(&msg[i], desc, rx_buf);
+
+ if (unlikely(++ntc == ctlq->ring_len))
+ ntc = 0;
+ }
+
+ ctlq->next_to_clean = ntc;
+
+ return i;
+}
+EXPORT_SYMBOL_NS_GPL(libie_ctlq_recv, "LIBIE_CP");
+
+MODULE_DESCRIPTION("Control Plane communication API");
+MODULE_IMPORT_NS("LIBETH");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/net/intel/libie/controlq.h b/include/linux/net/intel/libie/controlq.h
new file mode 100644
index 000000000000..a176a0d2ba33
--- /dev/null
+++ b/include/linux/net/intel/libie/controlq.h
@@ -0,0 +1,249 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2025 Intel Corporation */
+
+#ifndef __LIBIE_CONTROLQ_H
+#define __LIBIE_CONTROLQ_H
+
+#include <net/libeth/rx.h>
+
+#include <linux/net/intel/libie/pci.h>
+#include <linux/net/intel/virtchnl2.h>
+
+/* Default mailbox control queue */
+#define LIBIE_CTLQ_MBX_ID -1
+#define LIBIE_CTLQ_MAX_BUF_LEN SZ_4K
+
+#define LIBIE_CTLQ_TYPE_TX 0
+#define LIBIE_CTLQ_TYPE_RX 1
+
+/* Opcode used to send controlq message to the control plane */
+#define LIBIE_CTLQ_SEND_MSG_TO_CP 0x801
+#define LIBIE_CTLQ_SEND_MSG_TO_PEER 0x804
+
+/**
+ * struct libie_ctlq_ctx - contains controlq info and MMIO region info
+ * @mmio_info: MMIO region info structure
+ * @ctlqs: list that stores all the control queues
+ * @ctlqs_lock: lock for control queue list
+ */
+struct libie_ctlq_ctx {
+ struct libie_mmio_info mmio_info;
+ struct list_head ctlqs;
+ spinlock_t ctlqs_lock; /* protects the ctlqs list */
+};
+
+/**
+ * struct libie_ctlq_reg - structure representing virtual addresses of the
+ * controlq registers and masks
+ * @head: controlq head register address
+ * @tail: controlq tail register address
+ * @len: register address to write controlq length and enable bit
+ * @addr_high: register address to write the upper 32b of ring physical address
+ * @addr_low: register address to write the lower 32b of ring physical address
+ * @len_mask: mask to read the controlq length
+ * @len_ena_mask: mask to write the controlq enable bit
+ * @head_mask: mask to read the head value
+ */
+struct libie_ctlq_reg {
+ void __iomem *head;
+ void __iomem *tail;
+ void __iomem *len;
+ void __iomem *addr_high;
+ void __iomem *addr_low;
+ u32 len_mask;
+ u32 len_ena_mask;
+ u32 head_mask;
+};
+
+/**
+ * struct libie_cp_dma_mem - structure for DMA memory
+ * @va: virtual address
+ * @pa: physical address
+ * @size: memory size
+ */
+struct libie_cp_dma_mem {
+ void *va;
+ dma_addr_t pa;
+ size_t size;
+};
+
+/**
+ * struct libie_ctlq_msg - control queue message data
+ * @flags: refer to 'Flags sub-structure' definitions
+ * @opcode: infrastructure message opcode
+ * @data_len: size of the payload
+ * @func_id: queue id for the secondary mailbox queue, 0 for default mailbox
+ * @hw_retval: execution status from the HW
+ * @chnl_opcode: virtchnl message opcode
+ * @chnl_retval: virtchnl return value
+ * @param0: indirect message raw parameter0
+ * @sw_cookie: used to verify the response of the sent virtchnl message
+ * @virt_flags: virtchnl capability flags
+ * @addr_param: additional parameters in place of the address, given no buffer
+ * @recv_mem: virtual address and size of the buffer that contains
+ * the indirect response
+ * @send_mem: physical and virtual address of the DMA buffer,
+ * used for sending
+ */
+struct libie_ctlq_msg {
+ u16 flags;
+ u16 opcode;
+ u16 data_len;
+ union {
+ u16 func_id;
+ u16 hw_retval;
+ };
+ u32 chnl_opcode;
+ u32 chnl_retval;
+ u32 param0;
+ u16 sw_cookie;
+ u16 virt_flags;
+ u64 addr_param;
+ union {
+ struct kvec recv_mem;
+ struct libie_cp_dma_mem send_mem;
+ };
+};
+
+/**
+ * struct libie_ctlq_create_info - control queue create information
+ * @type: control queue type (Rx or Tx)
+ * @id: queue offset passed as input, -1 for default mailbox
+ * @reg: registers accessed by control queue
+ * @len: controlq length
+ */
+struct libie_ctlq_create_info {
+ enum virtchnl2_queue_type type;
+ int id;
+ struct libie_ctlq_reg reg;
+ u16 len;
+};
+
+/**
+ * struct libie_ctlq_info - control queue information
+ * @list: used to add a controlq to the list of queues in libie_ctlq_ctx
+ * @type: control queue type
+ * @qid: queue identifier
+ * @lock: control queue lock
+ * @ring_mem: descriptor ring DMA memory
+ * @descs: array of descriptors
+ * @rx_fqes: array of controlq Rx buffers
+ * @tx_msg: Tx messages sent to hardware
+ * @reg: registers used by control queue
+ * @dev: device that owns this control queue
+ * @pp: page pool for controlq Rx buffers
+ * @truesize: size to allocate per buffer
+ * @next_to_use: next available slot to send buffer
+ * @next_to_clean: next descriptor to be cleaned
+ * @next_to_post: next available slot to post buffers to after receive
+ * @ring_len: length of the descriptor ring
+ */
+struct libie_ctlq_info {
+ struct list_head list;
+ enum virtchnl2_queue_type type;
+ int qid;
+ spinlock_t lock; /* for concurrent processing */
+ struct libie_cp_dma_mem ring_mem;
+ struct libie_ctlq_desc *descs;
+ union {
+ struct libeth_fqe *rx_fqes;
+ struct libie_ctlq_msg **tx_msg;
+ };
+ struct libie_ctlq_reg reg;
+ struct device *dev;
+ struct page_pool *pp;
+ u32 truesize;
+ u32 next_to_clean;
+ union {
+ u32 next_to_use;
+ u32 next_to_post;
+ };
+ u32 ring_len;
+};
+
+#define LIBIE_CTLQ_MBX_ATQ_LEN GENMASK(9, 0)
+
+/* Flags sub-structure
+ * |0 |1 |2 |3 |4 |5 |6 |7 |8 |9 |10 |11 |12 |13 |14 |15 |
+ * |DD |CMP|ERR| * RSV * |FTYPE | *RSV* |RD |VFC|BUF| HOST_ID |
+ */
+ /* libie controlq descriptor qword0 details */
+#define LIBIE_CTLQ_DESC_FLAG_DD BIT(0)
+#define LIBIE_CTLQ_DESC_FLAG_CMP BIT(1)
+#define LIBIE_CTLQ_DESC_FLAG_ERR BIT(2)
+#define LIBIE_CTLQ_DESC_FLAG_FTYPE_VM BIT(6)
+#define LIBIE_CTLQ_DESC_FLAG_FTYPE_PF BIT(7)
+#define LIBIE_CTLQ_DESC_FLAG_FTYPE GENMASK(7, 6)
+#define LIBIE_CTLQ_DESC_FLAG_RD BIT(10)
+#define LIBIE_CTLQ_DESC_FLAG_VFC BIT(11)
+#define LIBIE_CTLQ_DESC_FLAG_BUF BIT(12)
+#define LIBIE_CTLQ_DESC_FLAG_HOST_ID GENMASK(15, 13)
+
+#define LIBIE_CTLQ_DESC_FLAGS GENMASK(15, 0)
+#define LIBIE_CTLQ_DESC_INFRA_OPCODE GENMASK_ULL(31, 16)
+#define LIBIE_CTLQ_DESC_DATA_LEN GENMASK_ULL(47, 32)
+#define LIBIE_CTLQ_DESC_HW_RETVAL GENMASK_ULL(63, 48)
+
+#define LIBIE_CTLQ_DESC_PFID_VFID GENMASK_ULL(63, 48)
+
+/* libie controlq descriptor qword1 details */
+#define LIBIE_CTLQ_DESC_VIRTCHNL_OPCODE GENMASK(27, 0)
+#define LIBIE_CTLQ_DESC_VIRTCHNL_DESC_TYPE GENMASK_ULL(31, 28)
+#define LIBIE_CTLQ_DESC_VIRTCHNL_MSG_RET_VAL GENMASK_ULL(63, 32)
+
+/* libie controlq descriptor qword2 details */
+#define LIBIE_CTLQ_DESC_MSG_PARAM0 GENMASK_ULL(31, 0)
+#define LIBIE_CTLQ_DESC_SW_COOKIE GENMASK_ULL(47, 32)
+#define LIBIE_CTLQ_DESC_VIRTCHNL_FLAGS GENMASK_ULL(63, 48)
+
+/* libie controlq descriptor qword3 details */
+#define LIBIE_CTLQ_DESC_DATA_ADDR_HIGH GENMASK_ULL(31, 0)
+#define LIBIE_CTLQ_DESC_DATA_ADDR_LOW GENMASK_ULL(63, 32)
+
+/**
+ * struct libie_ctlq_desc - control queue descriptor format
+ * @qword0: flags, message opcode, data length etc
+ * @qword1: virtchnl opcode, descriptor type and return value
+ * @qword2: indirect message parameters
+ * @qword3: indirect message buffer address
+ */
+struct libie_ctlq_desc {
+ __le64 qword0;
+ __le64 qword1;
+ __le64 qword2;
+ __le64 qword3;
+};
+
+/**
+ * libie_ctlq_release_rx_buf - Release Rx buffer for a specific control queue
+ * @rx_buf: Rx buffer to be freed
+ *
+ * Driver uses this function to post back the Rx buffer after the usage.
+ */
+static inline void libie_ctlq_release_rx_buf(struct kvec *rx_buf)
+{
+ netmem_ref netmem;
+
+ if (!rx_buf->iov_base)
+ return;
+
+ netmem = virt_to_netmem(rx_buf->iov_base);
+ page_pool_put_full_netmem(netmem_get_pp(netmem), netmem, false);
+}
+
+int libie_ctlq_init(struct libie_ctlq_ctx *ctx,
+ const struct libie_ctlq_create_info *qinfo, u32 numq);
+void libie_ctlq_deinit(struct libie_ctlq_ctx *ctx);
+
+struct libie_ctlq_info *libie_find_ctlq(struct libie_ctlq_ctx *ctx,
+ enum virtchnl2_queue_type type,
+ int id);
+
+u32 libie_ctlq_send_desc_avail(const struct libie_ctlq_info *ctlq);
+void libie_ctlq_send(struct libie_ctlq_info *ctlq, u32 num_q_msg);
+u32 libie_ctlq_recv(struct libie_ctlq_info *ctlq, struct libie_ctlq_msg *msg,
+ u32 num_q_msg);
+
+int libie_ctlq_post_rx_buffs(struct libie_ctlq_info *ctlq);
+
+#endif /* __LIBIE_CONTROLQ_H */
--
2.47.0
^ permalink raw reply related
* [PATCH iwl-next v8 06/15] idpf: remove 'vport_params_reqd' field
From: Larysa Zaremba @ 2026-06-08 14:41 UTC (permalink / raw)
To: intel-wired-lan, Tony Nguyen
Cc: aleksander.lobakin, sridhar.samudrala, Michal Swiatkowski,
Larysa Zaremba, Fijalkowski, Maciej, Emil Tantilov, Madhu Chittim,
Josh Hay, Keller, Jacob E, jayaprakash.shanmugam, Jiri Pirko,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Jonathan Corbet, Richard Cochran, Przemek Kitszel,
Andrew Lunn, netdev, linux-doc, linux-kernel, Samuel Salin
In-Reply-To: <20260608144127.2751230-1-larysa.zaremba@intel.com>
From: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
While sending a create vport message to the device control plane, a create
vport virtchnl message is prepared with all the required info to initialize
the vport. This info is stored in the adapter struct but never used
thereafter. So, remove the said field.
Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Reviewed-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
Reviewed-by: Madhu Chittim <madhu.chittim@intel.com>
Tested-by: Samuel Salin <Samuel.salin@intel.com>
Signed-off-by: Larysa Zaremba <larysa.zaremba@intel.com>
---
drivers/net/ethernet/intel/idpf/idpf.h | 2 --
drivers/net/ethernet/intel/idpf/idpf_lib.c | 2 --
.../net/ethernet/intel/idpf/idpf_virtchnl.c | 30 +++++++------------
3 files changed, 10 insertions(+), 24 deletions(-)
diff --git a/drivers/net/ethernet/intel/idpf/idpf.h b/drivers/net/ethernet/intel/idpf/idpf.h
index 984944bab28b..c5e47e79a641 100644
--- a/drivers/net/ethernet/intel/idpf/idpf.h
+++ b/drivers/net/ethernet/intel/idpf/idpf.h
@@ -638,7 +638,6 @@ struct idpf_vc_xn_manager;
* @avail_queues: Device given queue limits
* @vports: Array to store vports created by the driver
* @netdevs: Associated Vport netdevs
- * @vport_params_reqd: Vport params requested
* @vport_params_recvd: Vport params received
* @vport_ids: Array of device given vport identifiers
* @singleq_pt_lkup: Lookup table for singleq RX ptypes
@@ -697,7 +696,6 @@ struct idpf_adapter {
struct idpf_avail_queue_info avail_queues;
struct idpf_vport **vports;
struct net_device **netdevs;
- struct virtchnl2_create_vport **vport_params_reqd;
struct virtchnl2_create_vport **vport_params_recvd;
u32 *vport_ids;
diff --git a/drivers/net/ethernet/intel/idpf/idpf_lib.c b/drivers/net/ethernet/intel/idpf/idpf_lib.c
index cf966fe6c759..d88ca59edf97 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_lib.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_lib.c
@@ -1109,8 +1109,6 @@ static void idpf_vport_rel(struct idpf_vport *vport)
kfree(adapter->vport_params_recvd[idx]);
adapter->vport_params_recvd[idx] = NULL;
- kfree(adapter->vport_params_reqd[idx]);
- adapter->vport_params_reqd[idx] = NULL;
kfree(vport);
adapter->num_alloc_vports--;
diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
index be66f9b2e101..d4546d62cca9 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
@@ -1557,14 +1557,10 @@ int idpf_send_create_vport_msg(struct idpf_adapter *adapter,
ssize_t reply_sz;
buf_size = sizeof(struct virtchnl2_create_vport);
- if (!adapter->vport_params_reqd[idx]) {
- adapter->vport_params_reqd[idx] = kzalloc(buf_size,
- GFP_KERNEL);
- if (!adapter->vport_params_reqd[idx])
- return -ENOMEM;
- }
+ vport_msg = kzalloc(buf_size, GFP_KERNEL);
+ if (!vport_msg)
+ return -ENOMEM;
- vport_msg = adapter->vport_params_reqd[idx];
vport_msg->vport_type = cpu_to_le16(VIRTCHNL2_VPORT_TYPE_DEFAULT);
vport_msg->vport_index = cpu_to_le16(idx);
@@ -1581,8 +1577,7 @@ int idpf_send_create_vport_msg(struct idpf_adapter *adapter,
err = idpf_vport_calc_total_qs(adapter, idx, vport_msg, max_q);
if (err) {
dev_err(&adapter->pdev->dev, "Enough queues are not available");
-
- return err;
+ goto rel_buf;
}
if (!adapter->vport_params_recvd[idx]) {
@@ -1590,7 +1585,7 @@ int idpf_send_create_vport_msg(struct idpf_adapter *adapter,
GFP_KERNEL);
if (!adapter->vport_params_recvd[idx]) {
err = -ENOMEM;
- goto free_vport_params;
+ goto rel_buf;
}
}
@@ -1606,13 +1601,15 @@ int idpf_send_create_vport_msg(struct idpf_adapter *adapter,
goto free_vport_params;
}
+ kfree(vport_msg);
+
return 0;
free_vport_params:
kfree(adapter->vport_params_recvd[idx]);
adapter->vport_params_recvd[idx] = NULL;
- kfree(adapter->vport_params_reqd[idx]);
- adapter->vport_params_reqd[idx] = NULL;
+rel_buf:
+ kfree(vport_msg);
return err;
}
@@ -3418,8 +3415,6 @@ static void idpf_vport_params_buf_rel(struct idpf_adapter *adapter)
{
kfree(adapter->vport_params_recvd);
adapter->vport_params_recvd = NULL;
- kfree(adapter->vport_params_reqd);
- adapter->vport_params_reqd = NULL;
kfree(adapter->vport_ids);
adapter->vport_ids = NULL;
}
@@ -3434,15 +3429,10 @@ static int idpf_vport_params_buf_alloc(struct idpf_adapter *adapter)
{
u16 num_max_vports = idpf_get_max_vports(adapter);
- adapter->vport_params_reqd = kzalloc_objs(*adapter->vport_params_reqd,
- num_max_vports);
- if (!adapter->vport_params_reqd)
- return -ENOMEM;
-
adapter->vport_params_recvd = kzalloc_objs(*adapter->vport_params_recvd,
num_max_vports);
if (!adapter->vport_params_recvd)
- goto err_mem;
+ return -ENOMEM;
adapter->vport_ids = kcalloc(num_max_vports, sizeof(u32), GFP_KERNEL);
if (!adapter->vport_ids)
--
2.47.0
^ permalink raw reply related
* [PATCH iwl-next v8 05/15] libie: add bookkeeping support for control queue messages
From: Larysa Zaremba @ 2026-06-08 14:41 UTC (permalink / raw)
To: intel-wired-lan, Tony Nguyen
Cc: aleksander.lobakin, sridhar.samudrala, Michal Swiatkowski,
Larysa Zaremba, Fijalkowski, Maciej, Emil Tantilov, Madhu Chittim,
Josh Hay, Keller, Jacob E, jayaprakash.shanmugam, Jiri Pirko,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Jonathan Corbet, Richard Cochran, Przemek Kitszel,
Andrew Lunn, netdev, linux-doc, linux-kernel, Phani R Burra,
Bharath R, Samuel Salin
In-Reply-To: <20260608144127.2751230-1-larysa.zaremba@intel.com>
From: Phani R Burra <phani.r.burra@intel.com>
All send control queue messages are allocated/freed in libie itself and
tracked with the unique transaction (Xn) ids until they receive response or
time out. Responses can be received out of order, therefore transactions
are stored in an array and tracked though a bitmap.
Pre-allocated DMA memory is used where possible. It reduces the driver
overhead in handling memory allocation/free and message timeouts.
Reviewed-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
Signed-off-by: Phani R Burra <phani.r.burra@intel.com>
Co-developed-by: Victor Raj <victor.raj@intel.com>
Signed-off-by: Victor Raj <victor.raj@intel.com>
Co-developed-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Tested-by: Bharath R <bharath.r@intel.com>
Tested-by: Samuel Salin <Samuel.salin@intel.com>
Co-developed-by: Larysa Zaremba <larysa.zaremba@intel.com>
Signed-off-by: Larysa Zaremba <larysa.zaremba@intel.com>
---
drivers/net/ethernet/intel/libie/controlq.c | 599 ++++++++++++++++++++
include/linux/net/intel/libie/controlq.h | 177 ++++++
2 files changed, 776 insertions(+)
diff --git a/drivers/net/ethernet/intel/libie/controlq.c b/drivers/net/ethernet/intel/libie/controlq.c
index f926fc0a0f1a..571d7db99ef6 100644
--- a/drivers/net/ethernet/intel/libie/controlq.c
+++ b/drivers/net/ethernet/intel/libie/controlq.c
@@ -609,6 +609,605 @@ u32 libie_ctlq_recv(struct libie_ctlq_info *ctlq, struct libie_ctlq_msg *msg,
}
EXPORT_SYMBOL_NS_GPL(libie_ctlq_recv, "LIBIE_CP");
+/**
+ * libie_ctlq_xn_pop_free - get a free Xn entry from the free list
+ * @xnm: Xn transaction manager
+ *
+ * Retrieve a free Xn entry from the free list.
+ *
+ * Return: valid Xn entry pointer or NULL if there are no free Xn entries.
+ */
+static struct libie_ctlq_xn *
+libie_ctlq_xn_pop_free(struct libie_ctlq_xn_manager *xnm)
+{
+ struct libie_ctlq_xn *xn;
+ u32 free_idx;
+
+ guard(spinlock)(&xnm->free_xns_bm_lock);
+
+ if (unlikely(xnm->shutdown))
+ return NULL;
+
+ free_idx = find_next_bit(xnm->free_xns_bm, LIBIE_CTLQ_MAX_XN_ENTRIES,
+ 0);
+ if (free_idx == LIBIE_CTLQ_MAX_XN_ENTRIES)
+ return NULL;
+
+ __clear_bit(free_idx, xnm->free_xns_bm);
+ xn = &xnm->ring[free_idx];
+ xn->cookie = xnm->cookie++;
+
+ return xn;
+}
+
+/**
+ * __libie_ctlq_xn_push_free - unsafely push a Xn entry into the free list
+ * @xnm: Xn transaction manager
+ * @xn: xn entry to be added into the free list
+ */
+static void __libie_ctlq_xn_push_free(struct libie_ctlq_xn_manager *xnm,
+ struct libie_ctlq_xn *xn)
+{
+ __set_bit(xn->index, xnm->free_xns_bm);
+
+ if (likely(!xnm->shutdown))
+ return;
+
+ if (bitmap_full(xnm->free_xns_bm, LIBIE_CTLQ_MAX_XN_ENTRIES))
+ complete(&xnm->can_destroy);
+}
+
+/**
+ * libie_ctlq_xn_push_free - push a Xn entry into the free list
+ * @xnm: Xn transaction manager
+ * @xn: xn entry to be added into the free list, not locked
+ *
+ * Safely add a used Xn entry back to the free list.
+ */
+static void libie_ctlq_xn_push_free(struct libie_ctlq_xn_manager *xnm,
+ struct libie_ctlq_xn *xn)
+{
+ guard(spinlock)(&xnm->free_xns_bm_lock);
+
+ __libie_ctlq_xn_push_free(xnm, xn);
+}
+
+/**
+ * libie_ctlq_xn_deinit_dma - free the DMA memory allocated for send messages
+ * @dev: device pointer
+ * @xnm: pointer to the transaction manager
+ * @num_entries: number of Xn entries to free the DMA for
+ */
+static void libie_ctlq_xn_deinit_dma(struct device *dev,
+ struct libie_ctlq_xn_manager *xnm,
+ u32 num_entries)
+{
+ for (u32 i = 0; i < num_entries; i++) {
+ struct libie_ctlq_xn *xn = &xnm->ring[i];
+
+ libie_cp_free_dma_mem(dev, xn->dma_mem);
+ kfree(xn->dma_mem);
+ }
+}
+
+/**
+ * libie_ctlq_xn_init_dma - pre-allocate DMA memory for send messages that use
+ * stack variables
+ * @dev: device pointer
+ * @xnm: pointer to transaction manager
+ *
+ * Return: %0 on success or error if memory allocation fails
+ */
+static int libie_ctlq_xn_init_dma(struct device *dev,
+ struct libie_ctlq_xn_manager *xnm)
+{
+ u32 i;
+
+ for (i = 0; i < LIBIE_CTLQ_MAX_XN_ENTRIES; i++) {
+ struct libie_ctlq_xn *xn = &xnm->ring[i];
+ struct libie_cp_dma_mem *dma_mem;
+
+ dma_mem = kzalloc_obj(*dma_mem);
+ if (!dma_mem)
+ goto dealloc_dma;
+
+ dma_mem->va = libie_cp_alloc_dma_mem(dev, dma_mem,
+ LIBIE_CTLQ_MAX_BUF_LEN);
+ if (!dma_mem->va) {
+ kfree(dma_mem);
+ goto dealloc_dma;
+ }
+
+ xn->dma_mem = dma_mem;
+ }
+
+ return 0;
+
+dealloc_dma:
+ libie_ctlq_xn_deinit_dma(dev, xnm, i);
+
+ return -ENOMEM;
+}
+
+/**
+ * libie_ctlq_xn_process_recv - process Xn data in receive message
+ * @params: Xn receive param information to handle a receive message
+ * @ctlq_msg: received control queue message
+ *
+ * Process a control queue receive message and send a complete event
+ * notification.
+ *
+ * Return: true if a message has been processed, false otherwise.
+ */
+static bool
+libie_ctlq_xn_process_recv(struct libie_ctlq_xn_recv_params *params,
+ struct libie_ctlq_msg *ctlq_msg)
+{
+ struct libie_ctlq_xn_manager *xnm = params->xnm;
+ struct libie_ctlq_xn *xn;
+ u16 msg_cookie, xn_index;
+ struct kvec *response;
+ int status;
+ u16 data;
+
+ data = ctlq_msg->sw_cookie;
+ xn_index = FIELD_GET(LIBIE_CTLQ_XN_INDEX_M, data);
+ msg_cookie = FIELD_GET(LIBIE_CTLQ_XN_COOKIE_M, data);
+ status = ctlq_msg->chnl_retval ? -EFAULT : 0;
+
+ xn = &xnm->ring[xn_index];
+ spin_lock(&xn->xn_lock);
+ if (ctlq_msg->chnl_opcode != xn->virtchnl_opcode ||
+ msg_cookie != xn->cookie) {
+ spin_unlock(&xn->xn_lock);
+ return false;
+ }
+
+ if (xn->state != LIBIE_CTLQ_XN_ASYNC &&
+ xn->state != LIBIE_CTLQ_XN_WAITING) {
+ spin_unlock(&xn->xn_lock);
+ return false;
+ }
+
+ response = &ctlq_msg->recv_mem;
+ if (xn->state == LIBIE_CTLQ_XN_ASYNC) {
+ xn->resp_cb(xn->send_ctx, response, status);
+ libie_ctlq_release_rx_buf(response);
+ xn->state = LIBIE_CTLQ_XN_IDLE;
+ spin_unlock(&xn->xn_lock);
+ libie_ctlq_xn_push_free(xnm, xn);
+
+ return true;
+ }
+
+ xn->recv_mem = *response;
+ xn->state = status ? LIBIE_CTLQ_XN_COMPLETED_FAILED :
+ LIBIE_CTLQ_XN_COMPLETED_SUCCESS;
+
+ complete(&xn->cmd_completion_event);
+ spin_unlock(&xn->xn_lock);
+
+ return true;
+}
+
+/**
+ * libie_xn_check_async_timeout - Check for asynchronous message timeouts
+ * @xnm: Xn transaction manager
+ *
+ * Call the corresponding callback to notify the caller about the timeout.
+ */
+static void libie_xn_check_async_timeout(struct libie_ctlq_xn_manager *xnm)
+{
+ u32 idx;
+
+ for_each_clear_bit(idx, xnm->free_xns_bm, LIBIE_CTLQ_MAX_XN_ENTRIES) {
+ struct libie_ctlq_xn *xn = &xnm->ring[idx];
+ u64 timeout_ms;
+
+ spin_lock(&xn->xn_lock);
+
+ timeout_ms = ktime_ms_delta(ktime_get(), xn->timestamp);
+ if (xn->state != LIBIE_CTLQ_XN_ASYNC ||
+ timeout_ms < xn->timeout_ms) {
+ spin_unlock(&xn->xn_lock);
+ continue;
+ }
+
+ xn->resp_cb(xn->send_ctx, NULL, -ETIMEDOUT);
+ xn->state = LIBIE_CTLQ_XN_IDLE;
+ spin_unlock(&xn->xn_lock);
+ libie_ctlq_xn_push_free(xnm, xn);
+ }
+}
+
+/**
+ * libie_ctlq_xn_recv - process control queue receive message
+ * @params: Xn receive param information to handle a receive message
+ *
+ * Process a receive message and update the receive queue buffer.
+ *
+ * Return: remaining budget.
+ */
+u32 libie_ctlq_xn_recv(struct libie_ctlq_xn_recv_params *params)
+{
+ struct libie_ctlq_msg ctlq_msg;
+ u32 budget = params->budget;
+
+ while (budget && libie_ctlq_recv(params->ctlq, &ctlq_msg, 1)) {
+ budget--;
+ if (!libie_ctlq_xn_process_recv(params, &ctlq_msg))
+ params->ctlq_msg_handler(params->xnm->ctx, &ctlq_msg);
+ }
+
+ libie_ctlq_post_rx_buffs(params->ctlq);
+ libie_xn_check_async_timeout(params->xnm);
+
+ return budget;
+}
+EXPORT_SYMBOL_NS_GPL(libie_ctlq_xn_recv, "LIBIE_CP");
+
+/**
+ * libie_cp_map_dma_mem - map a given virtual address for DMA
+ * @dev: device information
+ * @va: virtual address to be mapped
+ * @size: size of the memory
+ * @direction: DMA direction either from/to device
+ * @dma_mem: memory for DMA information to be stored
+ *
+ * Return: true on success, false on DMA map failure.
+ */
+static bool libie_cp_map_dma_mem(struct device *dev, void *va, size_t size,
+ int direction,
+ struct libie_cp_dma_mem *dma_mem)
+{
+ dma_mem->pa = dma_map_single(dev, va, size, direction);
+
+ return dma_mapping_error(dev, dma_mem->pa) ? false : true;
+}
+
+/**
+ * libie_cp_unmap_dma_mem - unmap previously mapped DMA address
+ * @dev: device information
+ * @dma_mem: DMA memory information
+ */
+static void libie_cp_unmap_dma_mem(struct device *dev,
+ const struct libie_cp_dma_mem *dma_mem)
+{
+ dma_unmap_single(dev, dma_mem->pa, dma_mem->size,
+ dma_mem->direction);
+}
+
+/**
+ * libie_ctlq_xn_process_send - process and send a control queue message
+ * @params: Xn send param information for sending a control queue message
+ * @xn: Assigned Xn entry for tracking the control queue message
+ *
+ * Return: %0 on success, -%errno on failure.
+ */
+static
+int libie_ctlq_xn_process_send(struct libie_ctlq_xn_send_params *params,
+ struct libie_ctlq_xn *xn)
+{
+ size_t buf_len = params->send_buf.iov_len;
+ struct device *dev = params->ctlq->dev;
+ void *buf = params->send_buf.iov_base;
+ struct libie_cp_dma_mem *dma_mem;
+ u16 cookie;
+
+ if (!buf || !buf_len)
+ return -EOPNOTSUPP;
+
+ if (libie_cp_can_send_onstack(buf_len)) {
+ dma_mem = xn->dma_mem;
+ memcpy(dma_mem->va, buf, buf_len);
+ } else {
+ dma_mem = &xn->send_dma_mem;
+ dma_mem->va = buf;
+ dma_mem->size = buf_len;
+ dma_mem->direction = DMA_TO_DEVICE;
+
+ if (!libie_cp_map_dma_mem(dev, buf, buf_len, DMA_TO_DEVICE,
+ dma_mem))
+ return -ENOMEM;
+ }
+
+ cookie = FIELD_PREP(LIBIE_CTLQ_XN_COOKIE_M, xn->cookie) |
+ FIELD_PREP(LIBIE_CTLQ_XN_INDEX_M, xn->index);
+
+ scoped_guard(spinlock, ¶ms->ctlq->lock) {
+ struct libie_ctlq_info *ctlq = params->ctlq;
+ struct libie_ctlq_msg *ctlq_msg;
+
+ if (!libie_ctlq_send_desc_avail(ctlq)) {
+ if (!libie_cp_can_send_onstack(buf_len))
+ libie_cp_unmap_dma_mem(dev, dma_mem);
+
+ return -EBUSY;
+ }
+
+ ctlq_msg = ctlq->tx_msg[ctlq->next_to_use];
+ if (params->ctlq_msg)
+ *ctlq_msg = *params->ctlq_msg;
+ else
+ /* Unused ctlq messages are already zeroed */
+ ctlq_msg->opcode = LIBIE_CTLQ_SEND_MSG_TO_CP;
+
+ ctlq_msg->sw_cookie = cookie;
+ ctlq_msg->send_mem = *dma_mem;
+ ctlq_msg->data_len = buf_len;
+ ctlq_msg->chnl_opcode = params->chnl_opcode;
+ libie_ctlq_send(params->ctlq, 1);
+ }
+
+ return 0;
+}
+
+/**
+ * libie_ctlq_xn_send - Function to send a control queue message
+ * @params: Xn send param information for sending a control queue message
+ *
+ * Send a control queue (mailbox or config) message.
+ * Based on the params value, the call can be completed synchronously or
+ * asynchronously.
+ *
+ * Return: %0 on success, -%errno on failure.
+ */
+int libie_ctlq_xn_send(struct libie_ctlq_xn_send_params *params)
+{
+ bool free_send = !libie_cp_can_send_onstack(params->send_buf.iov_len);
+ struct libie_ctlq_xn *xn;
+ int ret;
+
+ if (params->send_buf.iov_len > LIBIE_CTLQ_MAX_BUF_LEN) {
+ ret = -EINVAL;
+ goto free_buf;
+ }
+
+ xn = libie_ctlq_xn_pop_free(params->xnm);
+ /* no free transactions available */
+ if (unlikely(!xn)) {
+ ret = -EAGAIN;
+ goto free_buf;
+ }
+
+ spin_lock(&xn->xn_lock);
+ if (xn->state == LIBIE_CTLQ_XN_SHUTDOWN) {
+ ret = -ENXIO;
+ goto unlock_xn;
+ }
+
+ xn->state = params->resp_cb ? LIBIE_CTLQ_XN_ASYNC :
+ LIBIE_CTLQ_XN_WAITING;
+ xn->ctlq = params->ctlq;
+ xn->virtchnl_opcode = params->chnl_opcode;
+
+ if (params->resp_cb) {
+ xn->send_ctx = params->send_ctx;
+ xn->resp_cb = params->resp_cb;
+ xn->timeout_ms = params->timeout_ms;
+ xn->timestamp = ktime_get();
+ }
+
+ ret = libie_ctlq_xn_process_send(params, xn);
+ if (ret)
+ goto release_xn;
+ else
+ free_send = false;
+
+ spin_unlock(&xn->xn_lock);
+
+ if (params->resp_cb)
+ return 0;
+
+ wait_for_completion_timeout(&xn->cmd_completion_event,
+ msecs_to_jiffies(params->timeout_ms));
+
+ spin_lock(&xn->xn_lock);
+ switch (xn->state) {
+ case LIBIE_CTLQ_XN_WAITING:
+ ret = -ETIMEDOUT;
+ break;
+ case LIBIE_CTLQ_XN_COMPLETED_SUCCESS:
+ params->recv_mem = xn->recv_mem;
+ break;
+ default:
+ ret = -EBADMSG;
+ break;
+ }
+
+ /* Free the receive buffer in case of failure. On timeout, receive
+ * buffer is not allocated.
+ */
+ if (ret && ret != -ETIMEDOUT)
+ libie_ctlq_release_rx_buf(&xn->recv_mem);
+
+release_xn:
+ xn->state = LIBIE_CTLQ_XN_IDLE;
+ reinit_completion(&xn->cmd_completion_event);
+unlock_xn:
+ spin_unlock(&xn->xn_lock);
+ libie_ctlq_xn_push_free(params->xnm, xn);
+free_buf:
+ if (free_send)
+ params->rel_tx_buf(params->send_buf.iov_base);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(libie_ctlq_xn_send, "LIBIE_CP");
+
+/**
+ * libie_ctlq_xn_send_clean - cleanup the send control queue message buffers
+ * @params: Xn clean param information for send complete handling
+ *
+ * Cleanup the send buffers for the given control queue, if force is set, then
+ * clear all the outstanding send messages irrespective their send status.
+ * Force should be used during deinit or reset.
+ *
+ * Return: number of send buffers cleaned.
+ */
+u32 libie_ctlq_xn_send_clean(const struct libie_ctlq_xn_clean_params *params)
+{
+ struct libie_ctlq_info *ctlq = params->ctlq;
+ struct device *dev = ctlq->dev;
+ u32 ntc, i;
+
+ spin_lock(&ctlq->lock);
+ ntc = ctlq->next_to_clean;
+
+ for (i = 0; i < params->num_msgs; i++) {
+ struct libie_ctlq_msg *msg = ctlq->tx_msg[ntc];
+ struct libie_ctlq_desc *desc;
+ u64 qword;
+
+ desc = &ctlq->descs[ntc];
+ qword = le64_to_cpu(desc->qword0);
+
+ if (!FIELD_GET(LIBIE_CTLQ_DESC_FLAG_DD, qword) &&
+ !(unlikely(params->force) && msg->data_len))
+ break;
+
+ dma_rmb();
+
+ if (!libie_cp_can_send_onstack(msg->data_len)) {
+ libie_cp_unmap_dma_mem(dev, &msg->send_mem);
+ params->rel_tx_buf(msg->send_mem.va);
+ }
+
+ memset(msg, 0, sizeof(*msg));
+ desc->qword0 = 0;
+
+ if (unlikely(++ntc == ctlq->ring_len))
+ ntc = 0;
+ }
+
+ ctlq->next_to_clean = ntc;
+ spin_unlock(&ctlq->lock);
+
+ return i;
+}
+EXPORT_SYMBOL_NS_GPL(libie_ctlq_xn_send_clean, "LIBIE_CP");
+
+/**
+ * libie_ctlq_xn_shutdown - terminate control queue transactions
+ * @xnm: pointer to the transaction manager
+ *
+ * Synchronously terminate existing transactions and stop accepting new ones.
+ */
+void libie_ctlq_xn_shutdown(struct libie_ctlq_xn_manager *xnm)
+{
+ bool must_wait = false;
+ u32 i;
+
+ /* Should be no new clear bits after this */
+ spin_lock(&xnm->free_xns_bm_lock);
+ xnm->shutdown = true;
+
+ for_each_clear_bit(i, xnm->free_xns_bm, LIBIE_CTLQ_MAX_XN_ENTRIES) {
+ struct libie_ctlq_xn *xn = &xnm->ring[i];
+
+ spin_lock(&xn->xn_lock);
+
+ switch (xn->state) {
+ /* if an idle xn is not free, it is about to be either
+ * freed or initialized, prevent the latter and wait
+ */
+ case LIBIE_CTLQ_XN_IDLE:
+ xn->state = LIBIE_CTLQ_XN_SHUTDOWN;
+ fallthrough;
+ /* waiting thread possibly needs a push to return the xn,
+ * transaction will be reported as timed out
+ */
+ case LIBIE_CTLQ_XN_WAITING:
+ complete(&xn->cmd_completion_event);
+ fallthrough;
+ /* these states will return the xn soon */
+ case LIBIE_CTLQ_XN_COMPLETED_SUCCESS:
+ case LIBIE_CTLQ_XN_COMPLETED_FAILED:
+ case LIBIE_CTLQ_XN_SHUTDOWN:
+ must_wait = true;
+ break;
+ /* no thread should reference async xns at this point */
+ case LIBIE_CTLQ_XN_ASYNC:
+ __libie_ctlq_xn_push_free(xnm, xn);
+ break;
+ }
+
+ spin_unlock(&xn->xn_lock);
+ }
+
+ spin_unlock(&xnm->free_xns_bm_lock);
+
+ if (must_wait)
+ wait_for_completion(&xnm->can_destroy);
+}
+EXPORT_SYMBOL_NS_GPL(libie_ctlq_xn_shutdown, "LIBIE_CP");
+
+/**
+ * libie_ctlq_xn_deinit - deallocate and free the transaction manager resources
+ * @xnm: pointer to the transaction manager
+ * @ctx: controlq context structure
+ *
+ * All Rx processing must be stopped beforehand.
+ */
+void libie_ctlq_xn_deinit(struct libie_ctlq_xn_manager *xnm,
+ struct libie_ctlq_ctx *ctx)
+{
+ libie_ctlq_xn_shutdown(xnm);
+ libie_ctlq_xn_deinit_dma(&ctx->mmio_info.pdev->dev, xnm,
+ LIBIE_CTLQ_MAX_XN_ENTRIES);
+ kfree(xnm);
+ libie_ctlq_deinit(ctx);
+}
+EXPORT_SYMBOL_NS_GPL(libie_ctlq_xn_deinit, "LIBIE_CP");
+
+/**
+ * libie_ctlq_xn_init - initialize the Xn transaction manager
+ * @params: Xn init param information for allocating Xn manager resources
+ *
+ * Return: %0 on success, -%errno on failure.
+ */
+int libie_ctlq_xn_init(struct libie_ctlq_xn_init_params *params)
+{
+ struct libie_ctlq_xn_manager *xnm;
+ int ret;
+
+ ret = libie_ctlq_init(params->ctx, params->cctlq_info, params->num_qs);
+ if (ret)
+ return ret;
+
+ xnm = kzalloc_obj(*xnm);
+ if (!xnm)
+ goto ctlq_deinit;
+
+ ret = libie_ctlq_xn_init_dma(¶ms->ctx->mmio_info.pdev->dev, xnm);
+ if (ret)
+ goto free_xnm;
+
+ spin_lock_init(&xnm->free_xns_bm_lock);
+ init_completion(&xnm->can_destroy);
+ bitmap_fill(xnm->free_xns_bm, LIBIE_CTLQ_MAX_XN_ENTRIES);
+
+ for (u32 i = 0; i < LIBIE_CTLQ_MAX_XN_ENTRIES; i++) {
+ struct libie_ctlq_xn *xn = &xnm->ring[i];
+
+ xn->index = i;
+ init_completion(&xn->cmd_completion_event);
+ spin_lock_init(&xn->xn_lock);
+ }
+ xnm->ctx = params->ctx;
+ params->xnm = xnm;
+
+ return 0;
+
+free_xnm:
+ kfree(xnm);
+ctlq_deinit:
+ libie_ctlq_deinit(params->ctx);
+
+ return -ENOMEM;
+}
+EXPORT_SYMBOL_NS_GPL(libie_ctlq_xn_init, "LIBIE_CP");
+
MODULE_DESCRIPTION("Control Plane communication API");
MODULE_IMPORT_NS("LIBETH");
MODULE_LICENSE("GPL");
diff --git a/include/linux/net/intel/libie/controlq.h b/include/linux/net/intel/libie/controlq.h
index a176a0d2ba33..bbeff7630b23 100644
--- a/include/linux/net/intel/libie/controlq.h
+++ b/include/linux/net/intel/libie/controlq.h
@@ -20,6 +20,8 @@
#define LIBIE_CTLQ_SEND_MSG_TO_CP 0x801
#define LIBIE_CTLQ_SEND_MSG_TO_PEER 0x804
+#define LIBIE_CP_TX_COPYBREAK 128
+
/**
* struct libie_ctlq_ctx - contains controlq info and MMIO region info
* @mmio_info: MMIO region info structure
@@ -60,11 +62,13 @@ struct libie_ctlq_reg {
* @va: virtual address
* @pa: physical address
* @size: memory size
+ * @direction: memory to device or device to memory
*/
struct libie_cp_dma_mem {
void *va;
dma_addr_t pa;
size_t size;
+ int direction;
};
/**
@@ -246,4 +250,177 @@ u32 libie_ctlq_recv(struct libie_ctlq_info *ctlq, struct libie_ctlq_msg *msg,
int libie_ctlq_post_rx_buffs(struct libie_ctlq_info *ctlq);
+/* Only 8 bits are available in descriptor for Xn index */
+#define LIBIE_CTLQ_MAX_XN_ENTRIES 256
+#define LIBIE_CTLQ_XN_COOKIE_M GENMASK(15, 8)
+#define LIBIE_CTLQ_XN_INDEX_M GENMASK(7, 0)
+
+/**
+ * enum libie_ctlq_xn_state - Transaction state of a virtchnl message
+ * @LIBIE_CTLQ_XN_IDLE: transaction is available to use
+ * @LIBIE_CTLQ_XN_WAITING: waiting for transaction to complete
+ * @LIBIE_CTLQ_XN_COMPLETED_SUCCESS: transaction completed with success
+ * @LIBIE_CTLQ_XN_COMPLETED_FAILED: transaction completed with failure
+ * @LIBIE_CTLQ_XN_ASYNC: asynchronous virtchnl message transaction type
+ * @LIBIE_CTLQ_XN_SHUTDOWN: transaction cannot be used anymore
+ */
+enum libie_ctlq_xn_state {
+ LIBIE_CTLQ_XN_IDLE = 0,
+ LIBIE_CTLQ_XN_WAITING,
+ LIBIE_CTLQ_XN_COMPLETED_SUCCESS,
+ LIBIE_CTLQ_XN_COMPLETED_FAILED,
+ LIBIE_CTLQ_XN_ASYNC,
+ LIBIE_CTLQ_XN_SHUTDOWN,
+};
+
+/**
+ * struct libie_ctlq_xn - structure representing a virtchnl transaction entry
+ * @resp_cb: callback to handle the response of an asynchronous virtchnl message
+ * @xn_lock: lock to protect the transaction entry state
+ * @ctlq: send control queue information
+ * @cmd_completion_event: signal when a reply is available
+ * @dma_mem: DMA memory of send buffer that use stack variable
+ * @send_dma_mem: DMA memory of send buffer
+ * @recv_mem: receive buffer
+ * @send_ctx: context for callback function
+ * @timeout_ms: Xn transaction timeout in msecs
+ * @timestamp: timestamp to record the Xn send
+ * @virtchnl_opcode: virtchnl command opcode used for Xn transaction
+ * @state: transaction state of a virtchnl message
+ * @cookie: unique message identifier
+ * @index: index of the transaction entry
+ */
+struct libie_ctlq_xn {
+ void (*resp_cb)(void *ctx, struct kvec *mem, int status);
+ spinlock_t xn_lock; /* protects state */
+ struct libie_ctlq_info *ctlq;
+ struct completion cmd_completion_event;
+ struct libie_cp_dma_mem *dma_mem;
+ struct libie_cp_dma_mem send_dma_mem;
+ struct kvec recv_mem;
+ void *send_ctx;
+ u64 timeout_ms;
+ ktime_t timestamp;
+ u32 virtchnl_opcode;
+ enum libie_ctlq_xn_state state;
+ u8 cookie;
+ u8 index;
+};
+
+/**
+ * struct libie_ctlq_xn_manager - structure representing the array of virtchnl
+ * transaction entries
+ * @ctx: pointer to controlq context structure
+ * @free_xns_bm_lock: lock to protect the free Xn entries bit map
+ * @free_xns_bm: bitmap that represents the free Xn entries
+ * @ring: array of Xn entries
+ * @can_destroy: completion triggered by the last returned transaction
+ * @shutdown: shows the transactions the xnm shutdown is waiting for them
+ * @cookie: unique message identifier
+ */
+struct libie_ctlq_xn_manager {
+ struct libie_ctlq_ctx *ctx;
+ spinlock_t free_xns_bm_lock; /* get/check entries */
+ DECLARE_BITMAP(free_xns_bm, LIBIE_CTLQ_MAX_XN_ENTRIES);
+ struct libie_ctlq_xn ring[LIBIE_CTLQ_MAX_XN_ENTRIES];
+ struct completion can_destroy;
+ bool shutdown;
+ u8 cookie;
+};
+
+/**
+ * struct libie_ctlq_xn_send_params - structure representing send Xn entry
+ * @resp_cb: callback to handle the response of an asynchronous virtchnl message
+ * @rel_tx_buf: driver entry point for freeing the send buffer after send
+ * @xnm: Xn manager to process Xn entries
+ * @ctlq: send control queue information
+ * @ctlq_msg: control queue message information
+ * @send_buf: represents the buffer that carries outgoing information
+ * @recv_mem: receive buffer
+ * @send_ctx: context for call back function
+ * @timeout_ms: virtchnl transaction timeout in msecs
+ * @chnl_opcode: virtchnl message opcode
+ */
+struct libie_ctlq_xn_send_params {
+ void (*resp_cb)(void *ctx, struct kvec *mem, int status);
+ void (*rel_tx_buf)(const void *buf_va);
+ struct libie_ctlq_xn_manager *xnm;
+ struct libie_ctlq_info *ctlq;
+ struct libie_ctlq_msg *ctlq_msg;
+ struct kvec send_buf;
+ struct kvec recv_mem;
+ void *send_ctx;
+ u64 timeout_ms;
+ u32 chnl_opcode;
+};
+
+/**
+ * libie_cp_can_send_onstack - can a message be sent using a stack variable
+ * @size: ctlq data buffer size
+ *
+ * Return: %true if the message size is small enough for caller to pass
+ * an on-stack buffer, %false if kmalloc is needed
+ */
+static inline bool libie_cp_can_send_onstack(u32 size)
+{
+ return size <= LIBIE_CP_TX_COPYBREAK;
+}
+
+/**
+ * struct libie_ctlq_xn_recv_params - structure representing receive Xn entry
+ * @ctlq_msg_handler: callback to handle a message originated from the peer
+ * @xnm: Xn manager to process Xn entries
+ * @ctlq: control queue information
+ * @budget: maximum number of messages to process
+ */
+struct libie_ctlq_xn_recv_params {
+ void (*ctlq_msg_handler)(struct libie_ctlq_ctx *ctx,
+ struct libie_ctlq_msg *msg);
+ struct libie_ctlq_xn_manager *xnm;
+ struct libie_ctlq_info *ctlq;
+ u32 budget;
+};
+
+/**
+ * struct libie_ctlq_xn_clean_params - Data structure used for cleaning the
+ * control queue messages
+ * @rel_tx_buf: driver entry point for freeing the send buffer after send
+ * @ctx: pointer to context structure
+ * @ctlq: control queue information
+ * @send_ctx: context for call back function
+ * @num_msgs: number of messages to be cleaned
+ * @force: clean even if DD is not yet set
+ */
+struct libie_ctlq_xn_clean_params {
+ void (*rel_tx_buf)(const void *buf_va);
+ struct libie_ctlq_ctx *ctx;
+ struct libie_ctlq_info *ctlq;
+ void *send_ctx;
+ u16 num_msgs;
+ bool force;
+};
+
+/**
+ * struct libie_ctlq_xn_init_params - Data structure used for initializing the
+ * Xn transaction manager
+ * @cctlq_info: control queue information
+ * @ctx: pointer to controlq context structure
+ * @xnm: Xn manager to process Xn entries
+ * @num_qs: number of control queues needs to initialized
+ */
+struct libie_ctlq_xn_init_params {
+ struct libie_ctlq_create_info *cctlq_info;
+ struct libie_ctlq_ctx *ctx;
+ struct libie_ctlq_xn_manager *xnm;
+ u32 num_qs;
+};
+
+int libie_ctlq_xn_init(struct libie_ctlq_xn_init_params *params);
+void libie_ctlq_xn_deinit(struct libie_ctlq_xn_manager *xnm,
+ struct libie_ctlq_ctx *ctx);
+void libie_ctlq_xn_shutdown(struct libie_ctlq_xn_manager *xnm);
+int libie_ctlq_xn_send(struct libie_ctlq_xn_send_params *params);
+u32 libie_ctlq_xn_recv(struct libie_ctlq_xn_recv_params *params);
+u32 libie_ctlq_xn_send_clean(const struct libie_ctlq_xn_clean_params *params);
+
#endif /* __LIBIE_CONTROLQ_H */
--
2.47.0
^ permalink raw reply related
* [PATCH iwl-next v8 07/15] idpf: remove unused code for getting RSS info from device
From: Larysa Zaremba @ 2026-06-08 14:41 UTC (permalink / raw)
To: intel-wired-lan, Tony Nguyen
Cc: aleksander.lobakin, sridhar.samudrala, Michal Swiatkowski,
Larysa Zaremba, Fijalkowski, Maciej, Emil Tantilov, Madhu Chittim,
Josh Hay, Keller, Jacob E, jayaprakash.shanmugam, Jiri Pirko,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Jonathan Corbet, Richard Cochran, Przemek Kitszel,
Andrew Lunn, netdev, linux-doc, linux-kernel
In-Reply-To: <20260608144127.2751230-1-larysa.zaremba@intel.com>
idpf_send_get_set_rss_lut_msg() and idpf_send_get_set_rss_key_msg() do not
handle the get=true path properly. Response validation is insufficient,
memcpy size is wrong, LE-to-CPU conversion is missing. Fortunately, those
functions are never used with get=true. Given how broken this dead code is,
it is unlikely to be useful in the future.
Rename idpf_send_get_set_rss_lut_msg() to idpf_send_set_rss_lut_msg(),
idpf_send_get_set_rss_key_msg() to idpf_send_set_rss_key_msg(), remove the
get parameter and remove all get=true cases from the function.
Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
Signed-off-by: Larysa Zaremba <larysa.zaremba@intel.com>
---
drivers/net/ethernet/intel/idpf/idpf_txrx.c | 4 +-
.../net/ethernet/intel/idpf/idpf_virtchnl.c | 107 +++---------------
.../net/ethernet/intel/idpf/idpf_virtchnl.h | 10 +-
3 files changed, 22 insertions(+), 99 deletions(-)
diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.c b/drivers/net/ethernet/intel/idpf/idpf_txrx.c
index f6b3b15364ff..d744db0efd3f 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_txrx.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.c
@@ -4679,11 +4679,11 @@ int idpf_config_rss(struct idpf_vport *vport, struct idpf_rss_data *rss_data)
u32 vport_id = vport->vport_id;
int err;
- err = idpf_send_get_set_rss_key_msg(adapter, rss_data, vport_id, false);
+ err = idpf_send_set_rss_key_msg(adapter, rss_data, vport_id);
if (err)
return err;
- return idpf_send_get_set_rss_lut_msg(adapter, rss_data, vport_id, false);
+ return idpf_send_set_rss_lut_msg(adapter, rss_data, vport_id);
}
/**
diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
index d4546d62cca9..a3c17f0e14f3 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
@@ -2847,29 +2847,26 @@ int idpf_send_get_stats_msg(struct idpf_netdev_priv *np,
}
/**
- * idpf_send_get_set_rss_lut_msg - Send virtchnl get or set RSS lut message
+ * idpf_send_set_rss_lut_msg - Send virtchnl set RSS lut message
* @adapter: adapter pointer used to send virtchnl message
* @rss_data: pointer to RSS key and lut info
* @vport_id: vport identifier used while preparing the virtchnl message
- * @get: flag to set or get RSS look up table
*
- * When rxhash is disabled, RSS LUT will be configured with zeros. If rxhash
+ * When rxhash is disabled, RSS LUT will be configured with zeros. If rxhash
* is enabled, the LUT values stored in driver's soft copy will be used to setup
* the HW.
*
* Return: 0 on success, negative on failure.
*/
-int idpf_send_get_set_rss_lut_msg(struct idpf_adapter *adapter,
- struct idpf_rss_data *rss_data,
- u32 vport_id, bool get)
+int idpf_send_set_rss_lut_msg(struct idpf_adapter *adapter,
+ struct idpf_rss_data *rss_data, u32 vport_id)
{
- struct virtchnl2_rss_lut *recv_rl __free(kfree) = NULL;
struct virtchnl2_rss_lut *rl __free(kfree) = NULL;
struct idpf_vc_xn_params xn_params = {};
- int buf_size, lut_buf_size;
struct idpf_vport *vport;
ssize_t reply_sz;
bool rxhash_ena;
+ int buf_size;
int i;
vport = idpf_vid_to_vport(adapter, vport_id);
@@ -2888,72 +2885,34 @@ int idpf_send_get_set_rss_lut_msg(struct idpf_adapter *adapter,
xn_params.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC;
xn_params.send_buf.iov_base = rl;
xn_params.send_buf.iov_len = buf_size;
+ xn_params.vc_op = VIRTCHNL2_OP_SET_RSS_LUT;
- if (get) {
- recv_rl = kzalloc(IDPF_CTLQ_MAX_BUF_LEN, GFP_KERNEL);
- if (!recv_rl)
- return -ENOMEM;
- xn_params.vc_op = VIRTCHNL2_OP_GET_RSS_LUT;
- xn_params.recv_buf.iov_base = recv_rl;
- xn_params.recv_buf.iov_len = IDPF_CTLQ_MAX_BUF_LEN;
- } else {
- rl->lut_entries = cpu_to_le16(rss_data->rss_lut_size);
- for (i = 0; i < rss_data->rss_lut_size; i++)
- rl->lut[i] = rxhash_ena ?
- cpu_to_le32(rss_data->rss_lut[i]) : 0;
+ rl->lut_entries = cpu_to_le16(rss_data->rss_lut_size);
+ for (i = 0; i < rss_data->rss_lut_size; i++)
+ rl->lut[i] = rxhash_ena ? cpu_to_le32(rss_data->rss_lut[i]) : 0;
- xn_params.vc_op = VIRTCHNL2_OP_SET_RSS_LUT;
- }
reply_sz = idpf_vc_xn_exec(adapter, &xn_params);
if (reply_sz < 0)
return reply_sz;
- if (!get)
- return 0;
- if (reply_sz < sizeof(struct virtchnl2_rss_lut))
- return -EIO;
-
- lut_buf_size = le16_to_cpu(recv_rl->lut_entries) * sizeof(u32);
- if (reply_sz < lut_buf_size)
- return -EIO;
-
- /* size didn't change, we can reuse existing lut buf */
- if (rss_data->rss_lut_size == le16_to_cpu(recv_rl->lut_entries))
- goto do_memcpy;
-
- rss_data->rss_lut_size = le16_to_cpu(recv_rl->lut_entries);
- kfree(rss_data->rss_lut);
-
- rss_data->rss_lut = kzalloc(lut_buf_size, GFP_KERNEL);
- if (!rss_data->rss_lut) {
- rss_data->rss_lut_size = 0;
- return -ENOMEM;
- }
-
-do_memcpy:
- memcpy(rss_data->rss_lut, recv_rl->lut, rss_data->rss_lut_size);
return 0;
}
/**
- * idpf_send_get_set_rss_key_msg - Send virtchnl get or set RSS key message
+ * idpf_send_set_rss_key_msg - Send virtchnl set RSS key message
* @adapter: adapter pointer used to send virtchnl message
* @rss_data: pointer to RSS key and lut info
* @vport_id: vport identifier used while preparing the virtchnl message
- * @get: flag to set or get RSS look up table
*
* Return: 0 on success, negative on failure
*/
-int idpf_send_get_set_rss_key_msg(struct idpf_adapter *adapter,
- struct idpf_rss_data *rss_data,
- u32 vport_id, bool get)
+int idpf_send_set_rss_key_msg(struct idpf_adapter *adapter,
+ struct idpf_rss_data *rss_data, u32 vport_id)
{
- struct virtchnl2_rss_key *recv_rk __free(kfree) = NULL;
struct virtchnl2_rss_key *rk __free(kfree) = NULL;
struct idpf_vc_xn_params xn_params = {};
ssize_t reply_sz;
int i, buf_size;
- u16 key_size;
buf_size = struct_size(rk, key_flex, rss_data->rss_key_size);
rk = kzalloc(buf_size, GFP_KERNEL);
@@ -2964,49 +2923,15 @@ int idpf_send_get_set_rss_key_msg(struct idpf_adapter *adapter,
xn_params.send_buf.iov_base = rk;
xn_params.send_buf.iov_len = buf_size;
xn_params.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC;
- if (get) {
- recv_rk = kzalloc(IDPF_CTLQ_MAX_BUF_LEN, GFP_KERNEL);
- if (!recv_rk)
- return -ENOMEM;
-
- xn_params.vc_op = VIRTCHNL2_OP_GET_RSS_KEY;
- xn_params.recv_buf.iov_base = recv_rk;
- xn_params.recv_buf.iov_len = IDPF_CTLQ_MAX_BUF_LEN;
- } else {
- rk->key_len = cpu_to_le16(rss_data->rss_key_size);
- for (i = 0; i < rss_data->rss_key_size; i++)
- rk->key_flex[i] = rss_data->rss_key[i];
+ xn_params.vc_op = VIRTCHNL2_OP_SET_RSS_KEY;
- xn_params.vc_op = VIRTCHNL2_OP_SET_RSS_KEY;
- }
+ rk->key_len = cpu_to_le16(rss_data->rss_key_size);
+ for (i = 0; i < rss_data->rss_key_size; i++)
+ rk->key_flex[i] = rss_data->rss_key[i];
reply_sz = idpf_vc_xn_exec(adapter, &xn_params);
if (reply_sz < 0)
return reply_sz;
- if (!get)
- return 0;
- if (reply_sz < sizeof(struct virtchnl2_rss_key))
- return -EIO;
-
- key_size = min_t(u16, NETDEV_RSS_KEY_LEN,
- le16_to_cpu(recv_rk->key_len));
- if (reply_sz < key_size)
- return -EIO;
-
- /* key len didn't change, reuse existing buf */
- if (rss_data->rss_key_size == key_size)
- goto do_memcpy;
-
- rss_data->rss_key_size = key_size;
- kfree(rss_data->rss_key);
- rss_data->rss_key = kzalloc(key_size, GFP_KERNEL);
- if (!rss_data->rss_key) {
- rss_data->rss_key_size = 0;
- return -ENOMEM;
- }
-
-do_memcpy:
- memcpy(rss_data->rss_key, recv_rk->key_flex, rss_data->rss_key_size);
return 0;
}
diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h
index 972dd88cf3d2..7210dc7b426b 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h
+++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h
@@ -208,12 +208,10 @@ int idpf_send_ena_dis_loopback_msg(struct idpf_adapter *adapter, u32 vport_id,
int idpf_send_get_stats_msg(struct idpf_netdev_priv *np,
struct idpf_port_stats *port_stats);
int idpf_send_set_sriov_vfs_msg(struct idpf_adapter *adapter, u16 num_vfs);
-int idpf_send_get_set_rss_key_msg(struct idpf_adapter *adapter,
- struct idpf_rss_data *rss_data,
- u32 vport_id, bool get);
-int idpf_send_get_set_rss_lut_msg(struct idpf_adapter *adapter,
- struct idpf_rss_data *rss_data,
- u32 vport_id, bool get);
+int idpf_send_set_rss_key_msg(struct idpf_adapter *adapter,
+ struct idpf_rss_data *rss_data, u32 vport_id);
+int idpf_send_set_rss_lut_msg(struct idpf_adapter *adapter,
+ struct idpf_rss_data *rss_data, u32 vport_id);
void idpf_vc_xn_shutdown(struct idpf_vc_xn_manager *vcxn_mngr);
int idpf_idc_rdma_vc_send_sync(struct iidc_rdma_core_dev_info *cdev_info,
u8 *send_msg, u16 msg_size,
--
2.47.0
^ permalink raw reply related
* [PATCH iwl-next v8 10/15] idpf: make mbx_task queueing and cancelling more consistent
From: Larysa Zaremba @ 2026-06-08 14:41 UTC (permalink / raw)
To: intel-wired-lan, Tony Nguyen
Cc: aleksander.lobakin, sridhar.samudrala, Michal Swiatkowski,
Larysa Zaremba, Fijalkowski, Maciej, Emil Tantilov, Madhu Chittim,
Josh Hay, Keller, Jacob E, jayaprakash.shanmugam, Jiri Pirko,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Jonathan Corbet, Richard Cochran, Przemek Kitszel,
Andrew Lunn, netdev, linux-doc, linux-kernel, Samuel Salin
In-Reply-To: <20260608144127.2751230-1-larysa.zaremba@intel.com>
As a consequence of refactoring idpf code to use libeth APIs,
idpf_vc_xn_shutdown was merged with and replaced by idpf_deinit_dflt_mbx.
This does not affect the Tx path, as it checked for a presence of an xn
manager anyway. Rx processing is handled by the mbx_task that is not always
cancelled before calling the new consolidated mailbox deinit function.
Moreover, in the reset path idpf_intr_rel() reschedules it after the deinit
is done. This leads to mbx_task referencing the freed mailbox and causing
KASAN warnings.
To remedy this, in the init path, do the first queueing of mbx_task in
idpf_init_dflt_mbx(), in deinit and reset, always cancel the task in
idpf_deinit_dflt_mbx() and in every flow first call idpf_mb_intr_rel_irq().
Reviewed-by: Emil Tantilov <emil.s.tantilov@intel.com>
Reviewed-by: Michal Kubiak <michal.kubiak@intel.com>
Tested-by: Samuel Salin <Samuel.salin@intel.com>
Signed-off-by: Larysa Zaremba <larysa.zaremba@intel.com>
---
drivers/net/ethernet/intel/idpf/idpf.h | 1 +
drivers/net/ethernet/intel/idpf/idpf_lib.c | 9 ++++-----
drivers/net/ethernet/intel/idpf/idpf_virtchnl.c | 6 +++++-
3 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/drivers/net/ethernet/intel/idpf/idpf.h b/drivers/net/ethernet/intel/idpf/idpf.h
index d7d751e2a781..470bc23c844c 100644
--- a/drivers/net/ethernet/intel/idpf/idpf.h
+++ b/drivers/net/ethernet/intel/idpf/idpf.h
@@ -984,6 +984,7 @@ void idpf_vc_event_task(struct work_struct *work);
void idpf_dev_ops_init(struct idpf_adapter *adapter);
void idpf_vf_dev_ops_init(struct idpf_adapter *adapter);
int idpf_intr_req(struct idpf_adapter *adapter);
+void idpf_mb_intr_rel_irq(struct idpf_adapter *adapter);
void idpf_intr_rel(struct idpf_adapter *adapter);
u16 idpf_get_max_tx_hdr_size(struct idpf_adapter *adapter);
int idpf_initiate_soft_reset(struct idpf_vport *vport,
diff --git a/drivers/net/ethernet/intel/idpf/idpf_lib.c b/drivers/net/ethernet/intel/idpf/idpf_lib.c
index 0d131bf0993e..7988836fbae0 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_lib.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_lib.c
@@ -68,9 +68,11 @@ static void idpf_deinit_vector_stack(struct idpf_adapter *adapter)
* This will also disable interrupt mode and queue up mailbox task. Mailbox
* task will reschedule itself if not in interrupt mode.
*/
-static void idpf_mb_intr_rel_irq(struct idpf_adapter *adapter)
+void idpf_mb_intr_rel_irq(struct idpf_adapter *adapter)
{
- clear_bit(IDPF_MB_INTR_MODE, adapter->flags);
+ if (!test_and_clear_bit(IDPF_MB_INTR_MODE, adapter->flags))
+ return;
+
kfree(free_irq(adapter->msix_entries[0].vector, adapter));
queue_delayed_work(adapter->mbx_wq, &adapter->mbx_task, 0);
}
@@ -1938,14 +1940,11 @@ static void idpf_init_hard_reset(struct idpf_adapter *adapter)
goto unlock_mutex;
}
- queue_delayed_work(adapter->mbx_wq, &adapter->mbx_task, 0);
-
/* Initialize the state machine, also allocate memory and request
* resources
*/
err = idpf_vc_core_init(adapter);
if (err) {
- cancel_delayed_work_sync(&adapter->mbx_task);
idpf_deinit_dflt_mbx(adapter);
goto unlock_mutex;
}
diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
index dff719c4b1a2..c6c036edb918 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
@@ -2910,6 +2910,8 @@ int idpf_init_dflt_mbx(struct idpf_adapter *adapter)
adapter->xnm = params.xnm;
adapter->state = __IDPF_VER_CHECK;
+ queue_delayed_work(adapter->mbx_wq, &adapter->mbx_task, 0);
+
return 0;
}
@@ -2919,6 +2921,9 @@ int idpf_init_dflt_mbx(struct idpf_adapter *adapter)
*/
void idpf_deinit_dflt_mbx(struct idpf_adapter *adapter)
{
+ idpf_mb_intr_rel_irq(adapter);
+ cancel_delayed_work_sync(&adapter->mbx_task);
+
if (adapter->xnm) {
idpf_mb_clean(adapter, adapter->asq, true);
libie_ctlq_xn_deinit(adapter->xnm, &adapter->ctlq_ctx);
@@ -3178,7 +3183,6 @@ void idpf_vc_core_deinit(struct idpf_adapter *adapter)
idpf_deinit_dflt_mbx(adapter);
cancel_delayed_work_sync(&adapter->serv_task);
- cancel_delayed_work_sync(&adapter->mbx_task);
idpf_vport_params_buf_rel(adapter);
--
2.47.0
^ permalink raw reply related
* [PATCH iwl-next v8 08/15] idpf: refactor idpf to use libie_pci APIs
From: Larysa Zaremba @ 2026-06-08 14:41 UTC (permalink / raw)
To: intel-wired-lan, Tony Nguyen
Cc: aleksander.lobakin, sridhar.samudrala, Michal Swiatkowski,
Larysa Zaremba, Fijalkowski, Maciej, Emil Tantilov, Madhu Chittim,
Josh Hay, Keller, Jacob E, jayaprakash.shanmugam, Jiri Pirko,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Jonathan Corbet, Richard Cochran, Przemek Kitszel,
Andrew Lunn, netdev, linux-doc, linux-kernel, Samuel Salin
In-Reply-To: <20260608144127.2751230-1-larysa.zaremba@intel.com>
From: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Use libie_pci init and MMIO APIs where possible, struct idpf_hw cannot be
deleted for now as it also houses control queues that will be refactored
later. Use libie_cp header for libie_ctlq_ctx that contains mmio info from
the start in order to not increase the diff later.
Reviewed-by: Madhu Chittim <madhu.chittim@intel.com>
Reviewed-by: Sridhar Samudrala <sridhar.samudrala@intel.com>
Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Tested-by: Samuel Salin <Samuel.salin@intel.com>
Co-developed-by: Larysa Zaremba <larysa.zaremba@intel.com>
Signed-off-by: Larysa Zaremba <larysa.zaremba@intel.com>
---
drivers/net/ethernet/intel/idpf/Kconfig | 1 +
drivers/net/ethernet/intel/idpf/idpf.h | 70 +-------
.../net/ethernet/intel/idpf/idpf_controlq.c | 26 ++-
.../net/ethernet/intel/idpf/idpf_controlq.h | 2 -
drivers/net/ethernet/intel/idpf/idpf_dev.c | 61 ++++---
drivers/net/ethernet/intel/idpf/idpf_idc.c | 38 ++--
drivers/net/ethernet/intel/idpf/idpf_lib.c | 7 +-
drivers/net/ethernet/intel/idpf/idpf_main.c | 114 ++++++------
drivers/net/ethernet/intel/idpf/idpf_vf_dev.c | 57 +++---
.../net/ethernet/intel/idpf/idpf_virtchnl.c | 169 +++++++++---------
.../ethernet/intel/idpf/idpf_virtchnl_ptp.c | 58 +++---
11 files changed, 288 insertions(+), 315 deletions(-)
diff --git a/drivers/net/ethernet/intel/idpf/Kconfig b/drivers/net/ethernet/intel/idpf/Kconfig
index adab2154125b..586df3a4afe9 100644
--- a/drivers/net/ethernet/intel/idpf/Kconfig
+++ b/drivers/net/ethernet/intel/idpf/Kconfig
@@ -6,6 +6,7 @@ config IDPF
depends on PCI_MSI
depends on PTP_1588_CLOCK_OPTIONAL
select DIMLIB
+ select LIBIE_CP
select LIBETH_XDP
help
This driver supports Intel(R) Infrastructure Data Path Function
diff --git a/drivers/net/ethernet/intel/idpf/idpf.h b/drivers/net/ethernet/intel/idpf/idpf.h
index c5e47e79a641..92a120aadfcd 100644
--- a/drivers/net/ethernet/intel/idpf/idpf.h
+++ b/drivers/net/ethernet/intel/idpf/idpf.h
@@ -23,6 +23,7 @@ struct idpf_rss_data;
#include <linux/net/intel/iidc_rdma.h>
#include <linux/net/intel/iidc_rdma_idpf.h>
+#include <linux/net/intel/libie/controlq.h>
#include <linux/net/intel/virtchnl2.h>
#include "idpf_txrx.h"
@@ -625,6 +626,7 @@ struct idpf_vc_xn_manager;
* @flags: See enum idpf_flags
* @reset_reg: See struct idpf_reset_reg
* @hw: Device access data
+ * @ctlq_ctx: controlq context
* @num_avail_msix: Available number of MSIX vectors
* @num_msix_entries: Number of entries in MSIX table
* @msix_entries: MSIX table
@@ -682,6 +684,7 @@ struct idpf_adapter {
DECLARE_BITMAP(flags, IDPF_FLAGS_NBITS);
struct idpf_reset_reg reset_reg;
struct idpf_hw hw;
+ struct libie_ctlq_ctx ctlq_ctx;
u16 num_avail_msix;
u16 num_msix_entries;
struct msix_entry *msix_entries;
@@ -870,70 +873,6 @@ static inline u8 idpf_get_min_tx_pkt_len(struct idpf_adapter *adapter)
return pkt_len ? pkt_len : IDPF_TX_MIN_PKT_LEN;
}
-/**
- * idpf_get_mbx_reg_addr - Get BAR0 mailbox register address
- * @adapter: private data struct
- * @reg_offset: register offset value
- *
- * Return: BAR0 mailbox register address based on register offset.
- */
-static inline void __iomem *idpf_get_mbx_reg_addr(struct idpf_adapter *adapter,
- resource_size_t reg_offset)
-{
- return adapter->hw.mbx.vaddr + reg_offset;
-}
-
-/**
- * idpf_get_rstat_reg_addr - Get BAR0 rstat register address
- * @adapter: private data struct
- * @reg_offset: register offset value
- *
- * Return: BAR0 rstat register address based on register offset.
- */
-static inline void __iomem *idpf_get_rstat_reg_addr(struct idpf_adapter *adapter,
- resource_size_t reg_offset)
-{
- reg_offset -= adapter->dev_ops.static_reg_info[1].start;
-
- return adapter->hw.rstat.vaddr + reg_offset;
-}
-
-/**
- * idpf_get_reg_addr - Get BAR0 register address
- * @adapter: private data struct
- * @reg_offset: register offset value
- *
- * Based on the register offset, return the actual BAR0 register address
- */
-static inline void __iomem *idpf_get_reg_addr(struct idpf_adapter *adapter,
- resource_size_t reg_offset)
-{
- struct idpf_hw *hw = &adapter->hw;
-
- for (int i = 0; i < hw->num_lan_regs; i++) {
- struct idpf_mmio_reg *region = &hw->lan_regs[i];
-
- if (reg_offset >= region->addr_start &&
- reg_offset < (region->addr_start + region->addr_len)) {
- /* Convert the offset so that it is relative to the
- * start of the region. Then add the base address of
- * the region to get the final address.
- */
- reg_offset -= region->addr_start;
-
- return region->vaddr + reg_offset;
- }
- }
-
- /* It's impossible to hit this case with offsets from the CP. But if we
- * do for any other reason, the kernel will panic on that register
- * access. Might as well do it here to make it clear what's happening.
- */
- BUG();
-
- return NULL;
-}
-
/**
* idpf_is_reset_detected - check if we were reset at some point
* @adapter: driver specific private structure
@@ -945,7 +884,8 @@ static inline bool idpf_is_reset_detected(struct idpf_adapter *adapter)
if (!adapter->hw.arq)
return true;
- return !(readl(idpf_get_mbx_reg_addr(adapter, adapter->hw.arq->reg.len)) &
+ return !(readl(libie_pci_get_mmio_addr(&adapter->ctlq_ctx.mmio_info,
+ adapter->hw.arq->reg.len)) &
adapter->hw.arq->reg.len_mask);
}
diff --git a/drivers/net/ethernet/intel/idpf/idpf_controlq.c b/drivers/net/ethernet/intel/idpf/idpf_controlq.c
index d2dde43269e9..020b08367e18 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_controlq.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_controlq.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2023 Intel Corporation */
-#include "idpf_controlq.h"
+#include "idpf.h"
/**
* idpf_ctlq_setup_regs - initialize control queue registers
@@ -34,21 +34,27 @@ static void idpf_ctlq_setup_regs(struct idpf_ctlq_info *cq,
static void idpf_ctlq_init_regs(struct idpf_hw *hw, struct idpf_ctlq_info *cq,
bool is_rxq)
{
+ struct libie_mmio_info *mmio = &hw->back->ctlq_ctx.mmio_info;
+
/* Update tail to post pre-allocated buffers for rx queues */
if (is_rxq)
- idpf_mbx_wr32(hw, cq->reg.tail, (u32)(cq->ring_size - 1));
+ writel((u32)(cq->ring_size - 1),
+ libie_pci_get_mmio_addr(mmio, cq->reg.tail));
/* For non-Mailbox control queues only TAIL need to be set */
if (cq->q_id != -1)
return;
/* Clear Head for both send or receive */
- idpf_mbx_wr32(hw, cq->reg.head, 0);
+ writel(0, libie_pci_get_mmio_addr(mmio, cq->reg.head));
/* set starting point */
- idpf_mbx_wr32(hw, cq->reg.bal, lower_32_bits(cq->desc_ring.pa));
- idpf_mbx_wr32(hw, cq->reg.bah, upper_32_bits(cq->desc_ring.pa));
- idpf_mbx_wr32(hw, cq->reg.len, (cq->ring_size | cq->reg.len_ena_mask));
+ writel(lower_32_bits(cq->desc_ring.pa),
+ libie_pci_get_mmio_addr(mmio, cq->reg.bal));
+ writel(upper_32_bits(cq->desc_ring.pa),
+ libie_pci_get_mmio_addr(mmio, cq->reg.bah));
+ writel((cq->ring_size | cq->reg.len_ena_mask),
+ libie_pci_get_mmio_addr(mmio, cq->reg.len));
}
/**
@@ -326,7 +332,9 @@ int idpf_ctlq_send(struct idpf_hw *hw, struct idpf_ctlq_info *cq,
*/
dma_wmb();
- idpf_mbx_wr32(hw, cq->reg.tail, cq->next_to_use);
+ writel(cq->next_to_use,
+ libie_pci_get_mmio_addr(&hw->back->ctlq_ctx.mmio_info,
+ cq->reg.tail));
err_unlock:
spin_unlock(&cq->cq_lock);
@@ -518,7 +526,9 @@ int idpf_ctlq_post_rx_buffs(struct idpf_hw *hw, struct idpf_ctlq_info *cq,
dma_wmb();
- idpf_mbx_wr32(hw, cq->reg.tail, cq->next_to_post);
+ writel(cq->next_to_post,
+ libie_pci_get_mmio_addr(&hw->back->ctlq_ctx.mmio_info,
+ cq->reg.tail));
}
spin_unlock(&cq->cq_lock);
diff --git a/drivers/net/ethernet/intel/idpf/idpf_controlq.h b/drivers/net/ethernet/intel/idpf/idpf_controlq.h
index de4ece40c2ff..acf595e9265f 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_controlq.h
+++ b/drivers/net/ethernet/intel/idpf/idpf_controlq.h
@@ -109,8 +109,6 @@ struct idpf_mmio_reg {
* Align to ctlq_hw_info
*/
struct idpf_hw {
- struct idpf_mmio_reg mbx;
- struct idpf_mmio_reg rstat;
/* Array of remaining LAN BAR regions */
int num_lan_regs;
struct idpf_mmio_reg *lan_regs;
diff --git a/drivers/net/ethernet/intel/idpf/idpf_dev.c b/drivers/net/ethernet/intel/idpf/idpf_dev.c
index 1a0c71c95ef1..e36b0017186f 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_dev.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_dev.c
@@ -16,7 +16,6 @@
static void idpf_ctlq_reg_init(struct idpf_adapter *adapter,
struct idpf_ctlq_create_info *cq)
{
- resource_size_t mbx_start = adapter->dev_ops.static_reg_info[0].start;
int i;
for (i = 0; i < IDPF_NUM_DFLT_MBX_Q; i++) {
@@ -25,22 +24,22 @@ static void idpf_ctlq_reg_init(struct idpf_adapter *adapter,
switch (ccq->type) {
case IDPF_CTLQ_TYPE_MAILBOX_TX:
/* set head and tail registers in our local struct */
- ccq->reg.head = PF_FW_ATQH - mbx_start;
- ccq->reg.tail = PF_FW_ATQT - mbx_start;
- ccq->reg.len = PF_FW_ATQLEN - mbx_start;
- ccq->reg.bah = PF_FW_ATQBAH - mbx_start;
- ccq->reg.bal = PF_FW_ATQBAL - mbx_start;
+ ccq->reg.head = PF_FW_ATQH;
+ ccq->reg.tail = PF_FW_ATQT;
+ ccq->reg.len = PF_FW_ATQLEN;
+ ccq->reg.bah = PF_FW_ATQBAH;
+ ccq->reg.bal = PF_FW_ATQBAL;
ccq->reg.len_mask = PF_FW_ATQLEN_ATQLEN_M;
ccq->reg.len_ena_mask = PF_FW_ATQLEN_ATQENABLE_M;
ccq->reg.head_mask = PF_FW_ATQH_ATQH_M;
break;
case IDPF_CTLQ_TYPE_MAILBOX_RX:
/* set head and tail registers in our local struct */
- ccq->reg.head = PF_FW_ARQH - mbx_start;
- ccq->reg.tail = PF_FW_ARQT - mbx_start;
- ccq->reg.len = PF_FW_ARQLEN - mbx_start;
- ccq->reg.bah = PF_FW_ARQBAH - mbx_start;
- ccq->reg.bal = PF_FW_ARQBAL - mbx_start;
+ ccq->reg.head = PF_FW_ARQH;
+ ccq->reg.tail = PF_FW_ARQT;
+ ccq->reg.len = PF_FW_ARQLEN;
+ ccq->reg.bah = PF_FW_ARQBAH;
+ ccq->reg.bal = PF_FW_ARQBAL;
ccq->reg.len_mask = PF_FW_ARQLEN_ARQLEN_M;
ccq->reg.len_ena_mask = PF_FW_ARQLEN_ARQENABLE_M;
ccq->reg.head_mask = PF_FW_ARQH_ARQH_M;
@@ -57,13 +56,14 @@ static void idpf_ctlq_reg_init(struct idpf_adapter *adapter,
*/
static void idpf_mb_intr_reg_init(struct idpf_adapter *adapter)
{
+ struct libie_mmio_info *mmio = &adapter->ctlq_ctx.mmio_info;
struct idpf_intr_reg *intr = &adapter->mb_vector.intr_reg;
u32 dyn_ctl = le32_to_cpu(adapter->caps.mailbox_dyn_ctl);
- intr->dyn_ctl = idpf_get_reg_addr(adapter, dyn_ctl);
+ intr->dyn_ctl = libie_pci_get_mmio_addr(mmio, dyn_ctl);
intr->dyn_ctl_intena_m = PF_GLINT_DYN_CTL_INTENA_M;
intr->dyn_ctl_itridx_m = PF_GLINT_DYN_CTL_ITR_INDX_M;
- intr->icr_ena = idpf_get_reg_addr(adapter, PF_INT_DIR_OICR_ENA);
+ intr->icr_ena = libie_pci_get_mmio_addr(mmio, PF_INT_DIR_OICR_ENA);
intr->icr_ena_ctlq_m = PF_INT_DIR_OICR_ENA_M;
}
@@ -78,6 +78,7 @@ static int idpf_intr_reg_init(struct idpf_vport *vport,
struct idpf_adapter *adapter = vport->adapter;
u16 num_vecs = rsrc->num_q_vectors;
struct idpf_vec_regs *reg_vals;
+ struct libie_mmio_info *mmio;
int num_regs, i, err = 0;
u32 rx_itr, tx_itr, val;
u16 total_vecs;
@@ -93,14 +94,17 @@ static int idpf_intr_reg_init(struct idpf_vport *vport,
goto free_reg_vals;
}
+ mmio = &adapter->ctlq_ctx.mmio_info;
+
for (i = 0; i < num_vecs; i++) {
struct idpf_q_vector *q_vector = &rsrc->q_vectors[i];
u16 vec_id = rsrc->q_vector_idxs[i] - IDPF_MBX_Q_VEC;
struct idpf_intr_reg *intr = &q_vector->intr_reg;
+ struct idpf_vec_regs *reg = ®_vals[vec_id];
u32 spacing;
- intr->dyn_ctl = idpf_get_reg_addr(adapter,
- reg_vals[vec_id].dyn_ctl_reg);
+ intr->dyn_ctl = libie_pci_get_mmio_addr(mmio,
+ reg->dyn_ctl_reg);
intr->dyn_ctl_intena_m = PF_GLINT_DYN_CTL_INTENA_M;
intr->dyn_ctl_intena_msk_m = PF_GLINT_DYN_CTL_INTENA_MSK_M;
intr->dyn_ctl_itridx_s = PF_GLINT_DYN_CTL_ITR_INDX_S;
@@ -110,22 +114,21 @@ static int idpf_intr_reg_init(struct idpf_vport *vport,
intr->dyn_ctl_sw_itridx_ena_m =
PF_GLINT_DYN_CTL_SW_ITR_INDX_ENA_M;
- spacing = IDPF_ITR_IDX_SPACING(reg_vals[vec_id].itrn_index_spacing,
+ spacing = IDPF_ITR_IDX_SPACING(reg->itrn_index_spacing,
IDPF_PF_ITR_IDX_SPACING);
rx_itr = PF_GLINT_ITR_ADDR(VIRTCHNL2_ITR_IDX_0,
- reg_vals[vec_id].itrn_reg,
- spacing);
+ reg->itrn_reg, spacing);
tx_itr = PF_GLINT_ITR_ADDR(VIRTCHNL2_ITR_IDX_1,
- reg_vals[vec_id].itrn_reg,
- spacing);
- intr->rx_itr = idpf_get_reg_addr(adapter, rx_itr);
- intr->tx_itr = idpf_get_reg_addr(adapter, tx_itr);
+ reg->itrn_reg, spacing);
+ intr->rx_itr = libie_pci_get_mmio_addr(mmio, rx_itr);
+ intr->tx_itr = libie_pci_get_mmio_addr(mmio, tx_itr);
}
/* Data vector for NOIRQ queues */
val = reg_vals[rsrc->q_vector_idxs[i] - IDPF_MBX_Q_VEC].dyn_ctl_reg;
- rsrc->noirq_dyn_ctl = idpf_get_reg_addr(adapter, val);
+ rsrc->noirq_dyn_ctl =
+ libie_pci_get_mmio_addr(&adapter->ctlq_ctx.mmio_info, val);
val = PF_GLINT_DYN_CTL_WB_ON_ITR_M | PF_GLINT_DYN_CTL_INTENA_MSK_M |
FIELD_PREP(PF_GLINT_DYN_CTL_ITR_INDX_M, IDPF_NO_ITR_UPDATE_IDX);
@@ -143,7 +146,9 @@ static int idpf_intr_reg_init(struct idpf_vport *vport,
*/
static void idpf_reset_reg_init(struct idpf_adapter *adapter)
{
- adapter->reset_reg.rstat = idpf_get_rstat_reg_addr(adapter, PFGEN_RSTAT);
+ adapter->reset_reg.rstat =
+ libie_pci_get_mmio_addr(&adapter->ctlq_ctx.mmio_info,
+ PFGEN_RSTAT);
adapter->reset_reg.rstat_m = PFGEN_RSTAT_PFR_STATE_M;
}
@@ -155,11 +160,11 @@ static void idpf_reset_reg_init(struct idpf_adapter *adapter)
static void idpf_trigger_reset(struct idpf_adapter *adapter,
enum idpf_flags __always_unused trig_cause)
{
- u32 reset_reg;
+ void __iomem *addr;
- reset_reg = readl(idpf_get_rstat_reg_addr(adapter, PFGEN_CTRL));
- writel(reset_reg | PFGEN_CTRL_PFSWR,
- idpf_get_rstat_reg_addr(adapter, PFGEN_CTRL));
+ addr = libie_pci_get_mmio_addr(&adapter->ctlq_ctx.mmio_info,
+ PFGEN_CTRL);
+ writel(readl(addr) | PFGEN_CTRL_PFSWR, addr);
}
/**
diff --git a/drivers/net/ethernet/intel/idpf/idpf_idc.c b/drivers/net/ethernet/intel/idpf/idpf_idc.c
index b7d6b08fc89e..0a7edb783758 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_idc.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_idc.c
@@ -416,9 +416,12 @@ idpf_idc_init_msix_data(struct idpf_adapter *adapter)
int idpf_idc_init_aux_core_dev(struct idpf_adapter *adapter,
enum iidc_function_type ftype)
{
+ struct libie_mmio_info *mmio = &adapter->ctlq_ctx.mmio_info;
struct iidc_rdma_core_dev_info *cdev_info;
struct iidc_rdma_priv_dev_info *privd;
- int err, i;
+ struct libie_pci_mmio_region *mr;
+ size_t num_mem_regions;
+ int err, i = 0;
adapter->cdev_info = kzalloc_obj(*cdev_info);
if (!adapter->cdev_info)
@@ -436,22 +439,37 @@ int idpf_idc_init_aux_core_dev(struct idpf_adapter *adapter,
cdev_info->rdma_protocol = IIDC_RDMA_PROTOCOL_ROCEV2;
privd->ftype = ftype;
+ num_mem_regions = list_count_nodes(&mmio->mmio_list);
+ if (num_mem_regions <= IDPF_MMIO_REG_NUM_STATIC) {
+ err = -EINVAL;
+ goto err_plug_aux_dev;
+ }
+
+ num_mem_regions -= IDPF_MMIO_REG_NUM_STATIC;
privd->mapped_mem_regions =
kzalloc_objs(struct iidc_rdma_lan_mapped_mem_region,
- adapter->hw.num_lan_regs);
+ num_mem_regions);
if (!privd->mapped_mem_regions) {
err = -ENOMEM;
goto err_plug_aux_dev;
}
- privd->num_memory_regions = cpu_to_le16(adapter->hw.num_lan_regs);
- for (i = 0; i < adapter->hw.num_lan_regs; i++) {
- privd->mapped_mem_regions[i].region_addr =
- adapter->hw.lan_regs[i].vaddr;
- privd->mapped_mem_regions[i].size =
- cpu_to_le64(adapter->hw.lan_regs[i].addr_len);
- privd->mapped_mem_regions[i].start_offset =
- cpu_to_le64(adapter->hw.lan_regs[i].addr_start);
+ privd->num_memory_regions = cpu_to_le16(num_mem_regions);
+ list_for_each_entry(mr, &mmio->mmio_list, list) {
+ struct resource *static_regs = adapter->dev_ops.static_reg_info;
+ bool is_static = false;
+
+ for (uint j = 0; j < IDPF_MMIO_REG_NUM_STATIC; j++)
+ if (mr->offset == static_regs[j].start)
+ is_static = true;
+
+ if (is_static)
+ continue;
+
+ privd->mapped_mem_regions[i].region_addr = mr->addr;
+ privd->mapped_mem_regions[i].size = cpu_to_le64(mr->size);
+ privd->mapped_mem_regions[i++].start_offset =
+ cpu_to_le64(mr->offset);
}
idpf_idc_init_msix_data(adapter);
diff --git a/drivers/net/ethernet/intel/idpf/idpf_lib.c b/drivers/net/ethernet/intel/idpf/idpf_lib.c
index d88ca59edf97..875472ae77fd 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_lib.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_lib.c
@@ -1847,15 +1847,14 @@ void idpf_deinit_task(struct idpf_adapter *adapter)
/**
* idpf_check_reset_complete - check that reset is complete
- * @hw: pointer to hw struct
+ * @adapter: adapter to check
* @reset_reg: struct with reset registers
*
* Returns 0 if device is ready to use, or -EBUSY if it's in reset.
**/
-static int idpf_check_reset_complete(struct idpf_hw *hw,
+static int idpf_check_reset_complete(struct idpf_adapter *adapter,
struct idpf_reset_reg *reset_reg)
{
- struct idpf_adapter *adapter = hw->back;
int i;
for (i = 0; i < 2000; i++) {
@@ -1918,7 +1917,7 @@ static void idpf_init_hard_reset(struct idpf_adapter *adapter)
}
/* Wait for reset to complete */
- err = idpf_check_reset_complete(&adapter->hw, &adapter->reset_reg);
+ err = idpf_check_reset_complete(adapter, &adapter->reset_reg);
if (err) {
dev_err(dev, "The driver was unable to contact the device's firmware. Check that the FW is running. Driver state= 0x%x\n",
adapter->state);
diff --git a/drivers/net/ethernet/intel/idpf/idpf_main.c b/drivers/net/ethernet/intel/idpf/idpf_main.c
index ab3c409e587b..93b11fb1609f 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_main.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_main.c
@@ -15,6 +15,8 @@
MODULE_DESCRIPTION(DRV_SUMMARY);
MODULE_IMPORT_NS("LIBETH");
+MODULE_IMPORT_NS("LIBIE_CP");
+MODULE_IMPORT_NS("LIBIE_PCI");
MODULE_IMPORT_NS("LIBETH_XDP");
MODULE_LICENSE("GPL");
@@ -56,8 +58,16 @@ static int idpf_get_device_type(struct pci_dev *pdev)
static int idpf_dev_init(struct idpf_adapter *adapter,
const struct pci_device_id *ent)
{
+ struct libie_mmio_info *mmio_info = &adapter->ctlq_ctx.mmio_info;
int ret;
+ ret = libie_pci_init_dev(adapter->pdev);
+ if (ret)
+ return ret;
+
+ mmio_info->pdev = adapter->pdev;
+ INIT_LIST_HEAD(&mmio_info->mmio_list);
+
if (ent->class == IDPF_CLASS_NETWORK_ETHERNET_PROGIF) {
ret = idpf_get_device_type(adapter->pdev);
switch (ret) {
@@ -90,6 +100,15 @@ static int idpf_dev_init(struct idpf_adapter *adapter,
return 0;
}
+/**
+ * idpf_decfg_device - deconfigure device and device specific resources
+ * @adapter: driver specific private structure
+ */
+static void idpf_decfg_device(struct idpf_adapter *adapter)
+{
+ libie_pci_unmap_all_mmio_regions(&adapter->ctlq_ctx.mmio_info);
+}
+
/**
* idpf_remove - Device removal routine
* @pdev: PCI device information struct
@@ -159,6 +178,7 @@ static void idpf_remove(struct pci_dev *pdev)
mutex_destroy(&adapter->queue_lock);
mutex_destroy(&adapter->vc_buf_lock);
+ idpf_decfg_device(adapter);
pci_set_drvdata(pdev, NULL);
kfree(adapter);
}
@@ -181,46 +201,45 @@ static void idpf_shutdown(struct pci_dev *pdev)
}
/**
- * idpf_cfg_hw - Initialize HW struct
- * @adapter: adapter to setup hw struct for
+ * idpf_cfg_device - configure device and device specific resources
+ * @adapter: driver specific private structure
*
- * Returns 0 on success, negative on failure
+ * Return: %0 on success, -%errno on failure.
*/
-static int idpf_cfg_hw(struct idpf_adapter *adapter)
+static int idpf_cfg_device(struct idpf_adapter *adapter)
{
- resource_size_t res_start, mbx_start, rstat_start;
+ struct libie_mmio_info *mmio_info = &adapter->ctlq_ctx.mmio_info;
struct pci_dev *pdev = adapter->pdev;
- struct idpf_hw *hw = &adapter->hw;
- struct device *dev = &pdev->dev;
- long len;
-
- res_start = pci_resource_start(pdev, 0);
+ struct resource *region;
+ bool mapped = false;
+ int err;
/* Map mailbox space for virtchnl communication */
- mbx_start = res_start + adapter->dev_ops.static_reg_info[0].start;
- len = resource_size(&adapter->dev_ops.static_reg_info[0]);
- hw->mbx.vaddr = devm_ioremap(dev, mbx_start, len);
- if (!hw->mbx.vaddr) {
- pci_err(pdev, "failed to allocate BAR0 mbx region\n");
-
+ region = &adapter->dev_ops.static_reg_info[0];
+ mapped = libie_pci_map_mmio_region(mmio_info, region->start,
+ resource_size(region));
+ if (!mapped) {
+ pci_err(pdev, "failed to map BAR0 mbx region\n");
return -ENOMEM;
}
- hw->mbx.addr_start = adapter->dev_ops.static_reg_info[0].start;
- hw->mbx.addr_len = len;
/* Map rstat space for resets */
- rstat_start = res_start + adapter->dev_ops.static_reg_info[1].start;
- len = resource_size(&adapter->dev_ops.static_reg_info[1]);
- hw->rstat.vaddr = devm_ioremap(dev, rstat_start, len);
- if (!hw->rstat.vaddr) {
- pci_err(pdev, "failed to allocate BAR0 rstat region\n");
+ region = &adapter->dev_ops.static_reg_info[1];
+ mapped = libie_pci_map_mmio_region(mmio_info, region->start,
+ resource_size(region));
+ if (!mapped) {
+ pci_err(pdev, "failed to map BAR0 rstat region\n");
+ libie_pci_unmap_all_mmio_regions(mmio_info);
return -ENOMEM;
}
- hw->rstat.addr_start = adapter->dev_ops.static_reg_info[1].start;
- hw->rstat.addr_len = len;
- hw->back = adapter;
+ err = pci_enable_ptm(pdev);
+ if (err)
+ pci_dbg(pdev, "PCIe PTM is not supported by PCIe bus/controller\n");
+
+ pci_set_drvdata(pdev, adapter);
+ adapter->hw.back = adapter;
return 0;
}
@@ -246,32 +265,21 @@ static int idpf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
adapter->req_rx_splitq = true;
adapter->pdev = pdev;
- err = pcim_enable_device(pdev);
- if (err)
- goto err_free;
- err = pcim_request_region(pdev, 0, pci_name(pdev));
+ err = idpf_dev_init(adapter, ent);
if (err) {
- pci_err(pdev, "pcim_request_region failed %pe\n", ERR_PTR(err));
-
+ dev_err(&pdev->dev, "Unexpected dev ID 0x%x in idpf probe\n",
+ ent->device);
goto err_free;
}
- err = pci_enable_ptm(pdev);
- if (err)
- pci_dbg(pdev, "PCIe PTM is not supported by PCIe bus/controller\n");
-
- /* set up for high or low dma */
- err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
+ err = idpf_cfg_device(adapter);
if (err) {
- pci_err(pdev, "DMA configuration failed: %pe\n", ERR_PTR(err));
-
+ pci_err(pdev, "Failed to configure device specific resources: %pe\n",
+ ERR_PTR(err));
goto err_free;
}
- pci_set_master(pdev);
- pci_set_drvdata(pdev, adapter);
-
adapter->init_wq = alloc_workqueue("%s-%s-init",
WQ_UNBOUND | WQ_MEM_RECLAIM, 0,
dev_driver_string(dev),
@@ -279,7 +287,7 @@ static int idpf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (!adapter->init_wq) {
dev_err(dev, "Failed to allocate init workqueue\n");
err = -ENOMEM;
- goto err_free;
+ goto err_init_wq;
}
adapter->serv_wq = alloc_workqueue("%s-%s-service",
@@ -324,20 +332,6 @@ static int idpf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
/* setup msglvl */
adapter->msg_enable = netif_msg_init(-1, IDPF_AVAIL_NETIF_M);
- err = idpf_dev_init(adapter, ent);
- if (err) {
- dev_err(&pdev->dev, "Unexpected dev ID 0x%x in idpf probe\n",
- ent->device);
- goto destroy_vc_event_wq;
- }
-
- err = idpf_cfg_hw(adapter);
- if (err) {
- dev_err(dev, "Failed to configure HW structure for adapter: %d\n",
- err);
- goto destroy_vc_event_wq;
- }
-
mutex_init(&adapter->vport_ctrl_lock);
mutex_init(&adapter->vector_lock);
mutex_init(&adapter->queue_lock);
@@ -356,8 +350,6 @@ static int idpf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
return 0;
-destroy_vc_event_wq:
- destroy_workqueue(adapter->vc_event_wq);
err_vc_event_wq_alloc:
destroy_workqueue(adapter->stats_wq);
err_stats_wq_alloc:
@@ -366,6 +358,8 @@ static int idpf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
destroy_workqueue(adapter->serv_wq);
err_serv_wq_alloc:
destroy_workqueue(adapter->init_wq);
+err_init_wq:
+ idpf_decfg_device(adapter);
err_free:
kfree(adapter);
return err;
diff --git a/drivers/net/ethernet/intel/idpf/idpf_vf_dev.c b/drivers/net/ethernet/intel/idpf/idpf_vf_dev.c
index a07d7e808ca9..98b8f678bd9a 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_vf_dev.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_vf_dev.c
@@ -15,31 +15,28 @@
static void idpf_vf_ctlq_reg_init(struct idpf_adapter *adapter,
struct idpf_ctlq_create_info *cq)
{
- resource_size_t mbx_start = adapter->dev_ops.static_reg_info[0].start;
- int i;
-
- for (i = 0; i < IDPF_NUM_DFLT_MBX_Q; i++) {
+ for (int i = 0; i < IDPF_NUM_DFLT_MBX_Q; i++) {
struct idpf_ctlq_create_info *ccq = cq + i;
switch (ccq->type) {
case IDPF_CTLQ_TYPE_MAILBOX_TX:
/* set head and tail registers in our local struct */
- ccq->reg.head = VF_ATQH - mbx_start;
- ccq->reg.tail = VF_ATQT - mbx_start;
- ccq->reg.len = VF_ATQLEN - mbx_start;
- ccq->reg.bah = VF_ATQBAH - mbx_start;
- ccq->reg.bal = VF_ATQBAL - mbx_start;
+ ccq->reg.head = VF_ATQH;
+ ccq->reg.tail = VF_ATQT;
+ ccq->reg.len = VF_ATQLEN;
+ ccq->reg.bah = VF_ATQBAH;
+ ccq->reg.bal = VF_ATQBAL;
ccq->reg.len_mask = VF_ATQLEN_ATQLEN_M;
ccq->reg.len_ena_mask = VF_ATQLEN_ATQENABLE_M;
ccq->reg.head_mask = VF_ATQH_ATQH_M;
break;
case IDPF_CTLQ_TYPE_MAILBOX_RX:
/* set head and tail registers in our local struct */
- ccq->reg.head = VF_ARQH - mbx_start;
- ccq->reg.tail = VF_ARQT - mbx_start;
- ccq->reg.len = VF_ARQLEN - mbx_start;
- ccq->reg.bah = VF_ARQBAH - mbx_start;
- ccq->reg.bal = VF_ARQBAL - mbx_start;
+ ccq->reg.head = VF_ARQH;
+ ccq->reg.tail = VF_ARQT;
+ ccq->reg.len = VF_ARQLEN;
+ ccq->reg.bah = VF_ARQBAH;
+ ccq->reg.bal = VF_ARQBAL;
ccq->reg.len_mask = VF_ARQLEN_ARQLEN_M;
ccq->reg.len_ena_mask = VF_ARQLEN_ARQENABLE_M;
ccq->reg.head_mask = VF_ARQH_ARQH_M;
@@ -56,13 +53,14 @@ static void idpf_vf_ctlq_reg_init(struct idpf_adapter *adapter,
*/
static void idpf_vf_mb_intr_reg_init(struct idpf_adapter *adapter)
{
+ struct libie_mmio_info *mmio = &adapter->ctlq_ctx.mmio_info;
struct idpf_intr_reg *intr = &adapter->mb_vector.intr_reg;
u32 dyn_ctl = le32_to_cpu(adapter->caps.mailbox_dyn_ctl);
- intr->dyn_ctl = idpf_get_reg_addr(adapter, dyn_ctl);
+ intr->dyn_ctl = libie_pci_get_mmio_addr(mmio, dyn_ctl);
intr->dyn_ctl_intena_m = VF_INT_DYN_CTL0_INTENA_M;
intr->dyn_ctl_itridx_m = VF_INT_DYN_CTL0_ITR_INDX_M;
- intr->icr_ena = idpf_get_reg_addr(adapter, VF_INT_ICR0_ENA1);
+ intr->icr_ena = libie_pci_get_mmio_addr(mmio, VF_INT_ICR0_ENA1);
intr->icr_ena_ctlq_m = VF_INT_ICR0_ENA1_ADMINQ_M;
}
@@ -77,6 +75,7 @@ static int idpf_vf_intr_reg_init(struct idpf_vport *vport,
struct idpf_adapter *adapter = vport->adapter;
u16 num_vecs = rsrc->num_q_vectors;
struct idpf_vec_regs *reg_vals;
+ struct libie_mmio_info *mmio;
int num_regs, i, err = 0;
u32 rx_itr, tx_itr, val;
u16 total_vecs;
@@ -92,14 +91,17 @@ static int idpf_vf_intr_reg_init(struct idpf_vport *vport,
goto free_reg_vals;
}
+ mmio = &adapter->ctlq_ctx.mmio_info;
+
for (i = 0; i < num_vecs; i++) {
struct idpf_q_vector *q_vector = &rsrc->q_vectors[i];
u16 vec_id = rsrc->q_vector_idxs[i] - IDPF_MBX_Q_VEC;
struct idpf_intr_reg *intr = &q_vector->intr_reg;
+ struct idpf_vec_regs *reg = ®_vals[vec_id];
u32 spacing;
- intr->dyn_ctl = idpf_get_reg_addr(adapter,
- reg_vals[vec_id].dyn_ctl_reg);
+ intr->dyn_ctl = libie_pci_get_mmio_addr(mmio,
+ reg->dyn_ctl_reg);
intr->dyn_ctl_intena_m = VF_INT_DYN_CTLN_INTENA_M;
intr->dyn_ctl_intena_msk_m = VF_INT_DYN_CTLN_INTENA_MSK_M;
intr->dyn_ctl_itridx_s = VF_INT_DYN_CTLN_ITR_INDX_S;
@@ -109,22 +111,21 @@ static int idpf_vf_intr_reg_init(struct idpf_vport *vport,
intr->dyn_ctl_sw_itridx_ena_m =
VF_INT_DYN_CTLN_SW_ITR_INDX_ENA_M;
- spacing = IDPF_ITR_IDX_SPACING(reg_vals[vec_id].itrn_index_spacing,
+ spacing = IDPF_ITR_IDX_SPACING(reg->itrn_index_spacing,
IDPF_VF_ITR_IDX_SPACING);
rx_itr = VF_INT_ITRN_ADDR(VIRTCHNL2_ITR_IDX_0,
- reg_vals[vec_id].itrn_reg,
- spacing);
+ reg->itrn_reg, spacing);
tx_itr = VF_INT_ITRN_ADDR(VIRTCHNL2_ITR_IDX_1,
- reg_vals[vec_id].itrn_reg,
- spacing);
- intr->rx_itr = idpf_get_reg_addr(adapter, rx_itr);
- intr->tx_itr = idpf_get_reg_addr(adapter, tx_itr);
+ reg->itrn_reg, spacing);
+ intr->rx_itr = libie_pci_get_mmio_addr(mmio, rx_itr);
+ intr->tx_itr = libie_pci_get_mmio_addr(mmio, tx_itr);
}
/* Data vector for NOIRQ queues */
val = reg_vals[rsrc->q_vector_idxs[i] - IDPF_MBX_Q_VEC].dyn_ctl_reg;
- rsrc->noirq_dyn_ctl = idpf_get_reg_addr(adapter, val);
+ rsrc->noirq_dyn_ctl =
+ libie_pci_get_mmio_addr(&adapter->ctlq_ctx.mmio_info, val);
val = VF_INT_DYN_CTLN_WB_ON_ITR_M | VF_INT_DYN_CTLN_INTENA_MSK_M |
FIELD_PREP(VF_INT_DYN_CTLN_ITR_INDX_M, IDPF_NO_ITR_UPDATE_IDX);
@@ -142,7 +143,9 @@ static int idpf_vf_intr_reg_init(struct idpf_vport *vport,
*/
static void idpf_vf_reset_reg_init(struct idpf_adapter *adapter)
{
- adapter->reset_reg.rstat = idpf_get_rstat_reg_addr(adapter, VFGEN_RSTAT);
+ adapter->reset_reg.rstat =
+ libie_pci_get_mmio_addr(&adapter->ctlq_ctx.mmio_info,
+ VFGEN_RSTAT);
adapter->reset_reg.rstat_m = VFGEN_RSTAT_VFR_STATE_M;
}
diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
index a3c17f0e14f3..357ff3e86b2e 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
@@ -2,6 +2,7 @@
/* Copyright (C) 2023 Intel Corporation */
#include <linux/export.h>
+#include <linux/net/intel/libie/pci.h>
#include <net/libeth/rx.h>
#include "idpf.h"
@@ -1020,12 +1021,46 @@ static int idpf_send_get_caps_msg(struct idpf_adapter *adapter)
}
/**
- * idpf_send_get_lan_memory_regions - Send virtchnl get LAN memory regions msg
+ * idpf_mmio_region_non_static - Check if region is not static
+ * @mmio_info: PCI resources info
+ * @reg: region to check
+ *
+ * Return: %true if region can be received though virtchnl command,
+ * %false if region is related to mailbox or resetting
+ */
+static bool idpf_mmio_region_non_static(struct libie_mmio_info *mmio_info,
+ struct libie_pci_mmio_region *reg)
+{
+ struct idpf_adapter *adapter =
+ container_of(mmio_info, struct idpf_adapter,
+ ctlq_ctx.mmio_info);
+
+ for (uint i = 0; i < IDPF_MMIO_REG_NUM_STATIC; i++) {
+ if (reg->bar_idx == 0 &&
+ reg->offset == adapter->dev_ops.static_reg_info[i].start)
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * idpf_decfg_lan_memory_regions - Unmap non-static memory regions
+ * @adapter: Driver specific private structure
+ */
+static void idpf_decfg_lan_memory_regions(struct idpf_adapter *adapter)
+{
+ libie_pci_unmap_fltr_regs(&adapter->ctlq_ctx.mmio_info,
+ idpf_mmio_region_non_static);
+}
+
+/**
+ * idpf_cfg_lan_memory_regions - Send virtchnl get LAN memory regions msg
* @adapter: Driver specific private struct
*
* Return: 0 on success or error code on failure.
*/
-static int idpf_send_get_lan_memory_regions(struct idpf_adapter *adapter)
+static int idpf_cfg_lan_memory_regions(struct idpf_adapter *adapter)
{
struct virtchnl2_get_lan_memory_regions *rcvd_regions __free(kfree);
struct idpf_vc_xn_params xn_params = {
@@ -1037,7 +1072,6 @@ static int idpf_send_get_lan_memory_regions(struct idpf_adapter *adapter)
.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
};
int num_regions, size;
- struct idpf_hw *hw;
ssize_t reply_sz;
int err = 0;
@@ -1060,86 +1094,56 @@ static int idpf_send_get_lan_memory_regions(struct idpf_adapter *adapter)
if (size > IDPF_CTLQ_MAX_BUF_LEN)
return -EINVAL;
- hw = &adapter->hw;
- hw->lan_regs = kzalloc_objs(*hw->lan_regs, num_regions);
- if (!hw->lan_regs)
- return -ENOMEM;
-
for (int i = 0; i < num_regions; i++) {
- hw->lan_regs[i].addr_len =
- le64_to_cpu(rcvd_regions->mem_reg[i].size);
- hw->lan_regs[i].addr_start =
- le64_to_cpu(rcvd_regions->mem_reg[i].start_offset);
+ struct libie_mmio_info *mmio = &adapter->ctlq_ctx.mmio_info;
+ resource_size_t offset, len;
+
+ offset = le64_to_cpu(rcvd_regions->mem_reg[i].start_offset);
+ len = le64_to_cpu(rcvd_regions->mem_reg[i].size);
+ if (len && !libie_pci_map_mmio_region(mmio, offset, len)) {
+ idpf_decfg_lan_memory_regions(adapter);
+ return -EIO;
+ }
}
- hw->num_lan_regs = num_regions;
return err;
}
/**
- * idpf_calc_remaining_mmio_regs - calculate MMIO regions outside mbx and rstat
+ * idpf_map_remaining_mmio_regs - map MMIO regions outside mbx and rstat
* @adapter: Driver specific private structure
*
- * Called when idpf_send_get_lan_memory_regions is not supported. This will
+ * Called when idpf_cfg_lan_memory_regions is not supported. This will
* calculate the offsets and sizes for the regions before, in between, and
* after the mailbox and rstat MMIO mappings.
*
* Return: 0 on success or error code on failure.
*/
-static int idpf_calc_remaining_mmio_regs(struct idpf_adapter *adapter)
+static int idpf_map_remaining_mmio_regs(struct idpf_adapter *adapter)
{
struct resource *rstat_reg = &adapter->dev_ops.static_reg_info[1];
struct resource *mbx_reg = &adapter->dev_ops.static_reg_info[0];
- struct idpf_hw *hw = &adapter->hw;
-
- hw->num_lan_regs = IDPF_MMIO_MAP_FALLBACK_MAX_REMAINING;
- hw->lan_regs = kzalloc_objs(*hw->lan_regs, hw->num_lan_regs);
- if (!hw->lan_regs)
- return -ENOMEM;
+ struct libie_mmio_info *mmio = &adapter->ctlq_ctx.mmio_info;
+ resource_size_t reg_start, size;
+ bool ok = true;
/* Region preceding mailbox */
- hw->lan_regs[0].addr_start = 0;
- hw->lan_regs[0].addr_len = mbx_reg->start;
- /* Region between mailbox and rstat */
- hw->lan_regs[1].addr_start = mbx_reg->end + 1;
- hw->lan_regs[1].addr_len = rstat_reg->start -
- hw->lan_regs[1].addr_start;
- /* Region after rstat */
- hw->lan_regs[2].addr_start = rstat_reg->end + 1;
- hw->lan_regs[2].addr_len = pci_resource_len(adapter->pdev, 0) -
- hw->lan_regs[2].addr_start;
-
- return 0;
-}
+ size = mbx_reg->start;
+ ok &= !size || libie_pci_map_mmio_region(mmio, 0, size);
-/**
- * idpf_map_lan_mmio_regs - map remaining LAN BAR regions
- * @adapter: Driver specific private structure
- *
- * Return: 0 on success or error code on failure.
- */
-static int idpf_map_lan_mmio_regs(struct idpf_adapter *adapter)
-{
- struct pci_dev *pdev = adapter->pdev;
- struct idpf_hw *hw = &adapter->hw;
- resource_size_t res_start;
-
- res_start = pci_resource_start(pdev, 0);
-
- for (int i = 0; i < hw->num_lan_regs; i++) {
- resource_size_t start;
- long len;
+ /* Region between mailbox and rstat */
+ reg_start = mbx_reg->end + 1;
+ size = rstat_reg->start - reg_start;
+ ok &= !size || libie_pci_map_mmio_region(mmio, reg_start, size);
- len = hw->lan_regs[i].addr_len;
- if (!len)
- continue;
- start = hw->lan_regs[i].addr_start + res_start;
+ /* Region after rstat */
+ reg_start = rstat_reg->end + 1;
+ size = pci_resource_len(adapter->pdev, 0) - reg_start;
+ ok &= !size || libie_pci_map_mmio_region(mmio, reg_start, size);
- hw->lan_regs[i].vaddr = devm_ioremap(&pdev->dev, start, len);
- if (!hw->lan_regs[i].vaddr) {
- pci_err(pdev, "failed to allocate BAR0 region\n");
- return -ENOMEM;
- }
+ if (!ok) {
+ idpf_decfg_lan_memory_regions(adapter);
+ return -ENOMEM;
}
return 0;
@@ -1413,7 +1417,7 @@ static int __idpf_queue_reg_init(struct idpf_vport *vport,
struct idpf_q_vec_rsrc *rsrc, u32 *reg_vals,
int num_regs, u32 q_type)
{
- struct idpf_adapter *adapter = vport->adapter;
+ struct libie_mmio_info *mmio = &vport->adapter->ctlq_ctx.mmio_info;
int i, j, k = 0;
switch (q_type) {
@@ -1423,7 +1427,8 @@ static int __idpf_queue_reg_init(struct idpf_vport *vport,
for (j = 0; j < tx_qgrp->num_txq && k < num_regs; j++, k++)
tx_qgrp->txqs[j]->tail =
- idpf_get_reg_addr(adapter, reg_vals[k]);
+ libie_pci_get_mmio_addr(mmio,
+ reg_vals[k]);
}
break;
case VIRTCHNL2_QUEUE_TYPE_RX:
@@ -1435,8 +1440,8 @@ static int __idpf_queue_reg_init(struct idpf_vport *vport,
struct idpf_rx_queue *q;
q = rx_qgrp->singleq.rxqs[j];
- q->tail = idpf_get_reg_addr(adapter,
- reg_vals[k]);
+ q->tail = libie_pci_get_mmio_addr(mmio,
+ reg_vals[k]);
}
}
break;
@@ -1449,8 +1454,8 @@ static int __idpf_queue_reg_init(struct idpf_vport *vport,
struct idpf_buf_queue *q;
q = &rx_qgrp->splitq.bufq_sets[j].bufq;
- q->tail = idpf_get_reg_addr(adapter,
- reg_vals[k]);
+ q->tail = libie_pci_get_mmio_addr(mmio,
+ reg_vals[k]);
}
}
break;
@@ -3445,35 +3450,30 @@ int idpf_vc_core_init(struct idpf_adapter *adapter)
}
if (idpf_is_cap_ena(adapter, IDPF_OTHER_CAPS, VIRTCHNL2_CAP_LAN_MEMORY_REGIONS)) {
- err = idpf_send_get_lan_memory_regions(adapter);
+ err = idpf_cfg_lan_memory_regions(adapter);
if (err) {
- dev_err(&adapter->pdev->dev, "Failed to get LAN memory regions: %d\n",
+ dev_err(&adapter->pdev->dev, "Failed to configure LAN memory regions: %d\n",
err);
return -EINVAL;
}
} else {
/* Fallback to mapping the remaining regions of the entire BAR */
- err = idpf_calc_remaining_mmio_regs(adapter);
+ err = idpf_map_remaining_mmio_regs(adapter);
if (err) {
- dev_err(&adapter->pdev->dev, "Failed to allocate BAR0 region(s): %d\n",
+ dev_err(&adapter->pdev->dev, "Failed to configure BAR0 region(s): %d\n",
err);
- return -ENOMEM;
+ return err;
}
}
- err = idpf_map_lan_mmio_regs(adapter);
- if (err) {
- dev_err(&adapter->pdev->dev, "Failed to map BAR0 region(s): %d\n",
- err);
- return -ENOMEM;
- }
-
pci_sriov_set_totalvfs(adapter->pdev, idpf_get_max_vfs(adapter));
num_max_vports = idpf_get_max_vports(adapter);
adapter->max_vports = num_max_vports;
adapter->vports = kzalloc_objs(*adapter->vports, num_max_vports);
- if (!adapter->vports)
- return -ENOMEM;
+ if (!adapter->vports) {
+ err = -ENOMEM;
+ goto decfg_regions;
+ }
if (!adapter->netdevs) {
adapter->netdevs = kzalloc_objs(struct net_device *,
@@ -3539,6 +3539,8 @@ int idpf_vc_core_init(struct idpf_adapter *adapter)
err_netdev_alloc:
kfree(adapter->vports);
adapter->vports = NULL;
+decfg_regions:
+ idpf_decfg_lan_memory_regions(adapter);
return err;
init_failed:
@@ -3572,7 +3574,6 @@ int idpf_vc_core_init(struct idpf_adapter *adapter)
*/
void idpf_vc_core_deinit(struct idpf_adapter *adapter)
{
- struct idpf_hw *hw = &adapter->hw;
bool remove_in_prog;
if (!test_bit(IDPF_VC_CORE_INIT, adapter->flags))
@@ -3597,12 +3598,10 @@ void idpf_vc_core_deinit(struct idpf_adapter *adapter)
idpf_vport_params_buf_rel(adapter);
- kfree(hw->lan_regs);
- hw->lan_regs = NULL;
-
kfree(adapter->vports);
adapter->vports = NULL;
+ idpf_decfg_lan_memory_regions(adapter);
clear_bit(IDPF_VC_CORE_INIT, adapter->flags);
}
diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c
index d9bcc3f61c65..8d8fb498e092 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c
@@ -31,6 +31,7 @@ int idpf_ptp_get_caps(struct idpf_adapter *adapter)
.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
};
struct virtchnl2_ptp_cross_time_reg_offsets cross_tstamp_offsets;
+ struct libie_mmio_info *mmio = &adapter->ctlq_ctx.mmio_info;
struct virtchnl2_ptp_clk_adj_reg_offsets clk_adj_offsets;
struct virtchnl2_ptp_clk_reg_offsets clock_offsets;
struct idpf_ptp_secondary_mbx *scnd_mbx;
@@ -76,19 +77,20 @@ int idpf_ptp_get_caps(struct idpf_adapter *adapter)
clock_offsets = recv_ptp_caps_msg->clk_offsets;
temp_offset = le32_to_cpu(clock_offsets.dev_clk_ns_l);
- ptp->dev_clk_regs.dev_clk_ns_l = idpf_get_reg_addr(adapter,
- temp_offset);
+ ptp->dev_clk_regs.dev_clk_ns_l =
+ libie_pci_get_mmio_addr(mmio, temp_offset);
temp_offset = le32_to_cpu(clock_offsets.dev_clk_ns_h);
- ptp->dev_clk_regs.dev_clk_ns_h = idpf_get_reg_addr(adapter,
- temp_offset);
+ ptp->dev_clk_regs.dev_clk_ns_h =
+ libie_pci_get_mmio_addr(mmio, temp_offset);
temp_offset = le32_to_cpu(clock_offsets.phy_clk_ns_l);
- ptp->dev_clk_regs.phy_clk_ns_l = idpf_get_reg_addr(adapter,
- temp_offset);
+ ptp->dev_clk_regs.phy_clk_ns_l =
+ libie_pci_get_mmio_addr(mmio, temp_offset);
temp_offset = le32_to_cpu(clock_offsets.phy_clk_ns_h);
- ptp->dev_clk_regs.phy_clk_ns_h = idpf_get_reg_addr(adapter,
- temp_offset);
+ ptp->dev_clk_regs.phy_clk_ns_h =
+ libie_pci_get_mmio_addr(mmio, temp_offset);
temp_offset = le32_to_cpu(clock_offsets.cmd_sync_trigger);
- ptp->dev_clk_regs.cmd_sync = idpf_get_reg_addr(adapter, temp_offset);
+ ptp->dev_clk_regs.cmd_sync =
+ libie_pci_get_mmio_addr(mmio, temp_offset);
cross_tstamp:
access_type = ptp->get_cross_tstamp_access;
@@ -98,13 +100,14 @@ int idpf_ptp_get_caps(struct idpf_adapter *adapter)
cross_tstamp_offsets = recv_ptp_caps_msg->cross_time_offsets;
temp_offset = le32_to_cpu(cross_tstamp_offsets.sys_time_ns_l);
- ptp->dev_clk_regs.sys_time_ns_l = idpf_get_reg_addr(adapter,
- temp_offset);
+ ptp->dev_clk_regs.sys_time_ns_l =
+ libie_pci_get_mmio_addr(mmio, temp_offset);
temp_offset = le32_to_cpu(cross_tstamp_offsets.sys_time_ns_h);
- ptp->dev_clk_regs.sys_time_ns_h = idpf_get_reg_addr(adapter,
- temp_offset);
+ ptp->dev_clk_regs.sys_time_ns_h =
+ libie_pci_get_mmio_addr(mmio, temp_offset);
temp_offset = le32_to_cpu(cross_tstamp_offsets.cmd_sync_trigger);
- ptp->dev_clk_regs.cmd_sync = idpf_get_reg_addr(adapter, temp_offset);
+ ptp->dev_clk_regs.cmd_sync =
+ libie_pci_get_mmio_addr(mmio, temp_offset);
discipline_clock:
access_type = ptp->adj_dev_clk_time_access;
@@ -115,29 +118,32 @@ int idpf_ptp_get_caps(struct idpf_adapter *adapter)
/* Device clock offsets */
temp_offset = le32_to_cpu(clk_adj_offsets.dev_clk_cmd_type);
- ptp->dev_clk_regs.cmd = idpf_get_reg_addr(adapter, temp_offset);
+ ptp->dev_clk_regs.cmd = libie_pci_get_mmio_addr(mmio, temp_offset);
temp_offset = le32_to_cpu(clk_adj_offsets.dev_clk_incval_l);
- ptp->dev_clk_regs.incval_l = idpf_get_reg_addr(adapter, temp_offset);
+ ptp->dev_clk_regs.incval_l = libie_pci_get_mmio_addr(mmio, temp_offset);
temp_offset = le32_to_cpu(clk_adj_offsets.dev_clk_incval_h);
- ptp->dev_clk_regs.incval_h = idpf_get_reg_addr(adapter, temp_offset);
+ ptp->dev_clk_regs.incval_h = libie_pci_get_mmio_addr(mmio, temp_offset);
temp_offset = le32_to_cpu(clk_adj_offsets.dev_clk_shadj_l);
- ptp->dev_clk_regs.shadj_l = idpf_get_reg_addr(adapter, temp_offset);
+ ptp->dev_clk_regs.shadj_l = libie_pci_get_mmio_addr(mmio, temp_offset);
temp_offset = le32_to_cpu(clk_adj_offsets.dev_clk_shadj_h);
- ptp->dev_clk_regs.shadj_h = idpf_get_reg_addr(adapter, temp_offset);
+ ptp->dev_clk_regs.shadj_h = libie_pci_get_mmio_addr(mmio, temp_offset);
/* PHY clock offsets */
temp_offset = le32_to_cpu(clk_adj_offsets.phy_clk_cmd_type);
- ptp->dev_clk_regs.phy_cmd = idpf_get_reg_addr(adapter, temp_offset);
+ ptp->dev_clk_regs.phy_cmd =
+ libie_pci_get_mmio_addr(mmio, temp_offset);
temp_offset = le32_to_cpu(clk_adj_offsets.phy_clk_incval_l);
- ptp->dev_clk_regs.phy_incval_l = idpf_get_reg_addr(adapter,
- temp_offset);
+ ptp->dev_clk_regs.phy_incval_l =
+ libie_pci_get_mmio_addr(mmio, temp_offset);
temp_offset = le32_to_cpu(clk_adj_offsets.phy_clk_incval_h);
- ptp->dev_clk_regs.phy_incval_h = idpf_get_reg_addr(adapter,
- temp_offset);
+ ptp->dev_clk_regs.phy_incval_h =
+ libie_pci_get_mmio_addr(mmio, temp_offset);
temp_offset = le32_to_cpu(clk_adj_offsets.phy_clk_shadj_l);
- ptp->dev_clk_regs.phy_shadj_l = idpf_get_reg_addr(adapter, temp_offset);
+ ptp->dev_clk_regs.phy_shadj_l =
+ libie_pci_get_mmio_addr(mmio, temp_offset);
temp_offset = le32_to_cpu(clk_adj_offsets.phy_clk_shadj_h);
- ptp->dev_clk_regs.phy_shadj_h = idpf_get_reg_addr(adapter, temp_offset);
+ ptp->dev_clk_regs.phy_shadj_h =
+ libie_pci_get_mmio_addr(mmio, temp_offset);
return 0;
}
--
2.47.0
^ permalink raw reply related
* [PATCH iwl-next v8 11/15] idpf: print a debug message and bail in case of non-event ctlq message
From: Larysa Zaremba @ 2026-06-08 14:41 UTC (permalink / raw)
To: intel-wired-lan, Tony Nguyen
Cc: aleksander.lobakin, sridhar.samudrala, Michal Swiatkowski,
Larysa Zaremba, Fijalkowski, Maciej, Emil Tantilov, Madhu Chittim,
Josh Hay, Keller, Jacob E, jayaprakash.shanmugam, Jiri Pirko,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Jonathan Corbet, Richard Cochran, Przemek Kitszel,
Andrew Lunn, netdev, linux-doc, linux-kernel, Aleksandr Loktionov,
Samuel Salin
In-Reply-To: <20260608144127.2751230-1-larysa.zaremba@intel.com>
Unlike previous internal idpf ctlq implementation, idpf calls the default
message handler for all received messages that do not have a matching xn
transaction, not only for VIRTCHNL2_OP_EVENT. This leads to many error
messages printing garbage, because the parsing expected a valid event
message, but got e.g. a delayed response for a timed-out transaction.
The information about timed-out transactions and otherwise unhandleable
messages can still be valuable for developers, so print the information
with dynamic debug and exit the function, so the following functions can
parse valid events in peace.
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Reviewed-by: Michal Kubiak <michal.kubiak@intel.com>
Tested-by: Samuel Salin <Samuel.salin@intel.com>
Signed-off-by: Larysa Zaremba <larysa.zaremba@intel.com>
---
drivers/net/ethernet/intel/idpf/idpf_virtchnl.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
index c6c036edb918..03087b63660d 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
@@ -84,6 +84,13 @@ void idpf_recv_event_msg(struct libie_ctlq_ctx *ctx,
u32 event;
adapter = container_of(ctx, struct idpf_adapter, ctlq_ctx);
+ if (ctlq_msg->chnl_opcode != VIRTCHNL2_OP_EVENT) {
+ dev_dbg(&adapter->pdev->dev,
+ "Unhandled message with opcode %u from CP\n",
+ ctlq_msg->chnl_opcode);
+ goto free_rx_buf;
+ }
+
if (payload_size < sizeof(*v2e)) {
dev_err_ratelimited(&adapter->pdev->dev, "Failed to receive valid payload for event msg (op %d len %d)\n",
ctlq_msg->chnl_opcode,
--
2.47.0
^ permalink raw reply related
* [PATCH iwl-next v8 12/15] ixd: add basic driver framework for Intel(R) Control Plane Function
From: Larysa Zaremba @ 2026-06-08 14:41 UTC (permalink / raw)
To: intel-wired-lan, Tony Nguyen
Cc: aleksander.lobakin, sridhar.samudrala, Michal Swiatkowski,
Larysa Zaremba, Fijalkowski, Maciej, Emil Tantilov, Madhu Chittim,
Josh Hay, Keller, Jacob E, jayaprakash.shanmugam, Jiri Pirko,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Jonathan Corbet, Richard Cochran, Przemek Kitszel,
Andrew Lunn, netdev, linux-doc, linux-kernel, Bharath R
In-Reply-To: <20260608144127.2751230-1-larysa.zaremba@intel.com>
Add module register and probe functionality. Add the required support to
register IXD PCI driver, as well as probe and remove call backs. Enable the
PCI device and request the kernel to reserve the memory resources that will
be used by the driver. Finally map the BAR0 address space.
For now, use devm_alloc() to allocate adapter, as it requires the least
amount of code. In a later commit, it will be replaced with a devlink
alternative.
Co-developed-by: Amritha Nambiar <amritha.nambiar@intel.com>
Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
Reviewed-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
Tested-by: Bharath R <Bharath.r@intel.com>
Signed-off-by: Larysa Zaremba <larysa.zaremba@intel.com>
---
.../device_drivers/ethernet/index.rst | 1 +
.../device_drivers/ethernet/intel/ixd.rst | 39 ++++++
drivers/net/ethernet/intel/Kconfig | 2 +
drivers/net/ethernet/intel/Makefile | 1 +
drivers/net/ethernet/intel/ixd/Kconfig | 13 ++
drivers/net/ethernet/intel/ixd/Makefile | 8 ++
drivers/net/ethernet/intel/ixd/ixd.h | 28 +++++
drivers/net/ethernet/intel/ixd/ixd_lan_regs.h | 28 +++++
drivers/net/ethernet/intel/ixd/ixd_main.c | 112 ++++++++++++++++++
9 files changed, 232 insertions(+)
create mode 100644 Documentation/networking/device_drivers/ethernet/intel/ixd.rst
create mode 100644 drivers/net/ethernet/intel/ixd/Kconfig
create mode 100644 drivers/net/ethernet/intel/ixd/Makefile
create mode 100644 drivers/net/ethernet/intel/ixd/ixd.h
create mode 100644 drivers/net/ethernet/intel/ixd/ixd_lan_regs.h
create mode 100644 drivers/net/ethernet/intel/ixd/ixd_main.c
diff --git a/Documentation/networking/device_drivers/ethernet/index.rst b/Documentation/networking/device_drivers/ethernet/index.rst
index 786a23c84b90..afaa1de55b50 100644
--- a/Documentation/networking/device_drivers/ethernet/index.rst
+++ b/Documentation/networking/device_drivers/ethernet/index.rst
@@ -37,6 +37,7 @@ Contents:
intel/igbvf
intel/ixgbe
intel/ixgbevf
+ intel/ixd
intel/i40e
intel/iavf
intel/ice
diff --git a/Documentation/networking/device_drivers/ethernet/intel/ixd.rst b/Documentation/networking/device_drivers/ethernet/intel/ixd.rst
new file mode 100644
index 000000000000..1387626e5d20
--- /dev/null
+++ b/Documentation/networking/device_drivers/ethernet/intel/ixd.rst
@@ -0,0 +1,39 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+==========================================================================
+iXD Linux* Base Driver for the Intel(R) Control Plane Function
+==========================================================================
+
+Intel iXD Linux driver.
+Copyright(C) 2025 Intel Corporation.
+
+.. contents::
+
+For questions related to hardware requirements, refer to the documentation
+supplied with your Intel adapter. All hardware requirements listed apply to use
+with Linux.
+
+
+Identifying Your Adapter
+========================
+For information on how to identify your adapter, and for the latest Intel
+network drivers, refer to the Intel Support website:
+http://www.intel.com/support
+
+
+Support
+=======
+For general information, go to the Intel support website at:
+http://www.intel.com/support/
+
+If an issue is identified with the released source code on a supported kernel
+with a supported adapter, email the specific information related to the issue
+to intel-wired-lan@lists.osuosl.org.
+
+
+Trademarks
+==========
+Intel is a trademark or registered trademark of Intel Corporation or its
+subsidiaries in the United States and/or other countries.
+
+* Other names and brands may be claimed as the property of others.
diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig
index 288fa8ce53af..780f113986ea 100644
--- a/drivers/net/ethernet/intel/Kconfig
+++ b/drivers/net/ethernet/intel/Kconfig
@@ -398,4 +398,6 @@ config IGC_LEDS
source "drivers/net/ethernet/intel/idpf/Kconfig"
+source "drivers/net/ethernet/intel/ixd/Kconfig"
+
endif # NET_VENDOR_INTEL
diff --git a/drivers/net/ethernet/intel/Makefile b/drivers/net/ethernet/intel/Makefile
index 9a37dc76aef0..08b29f3b6801 100644
--- a/drivers/net/ethernet/intel/Makefile
+++ b/drivers/net/ethernet/intel/Makefile
@@ -19,3 +19,4 @@ obj-$(CONFIG_IAVF) += iavf/
obj-$(CONFIG_FM10K) += fm10k/
obj-$(CONFIG_ICE) += ice/
obj-$(CONFIG_IDPF) += idpf/
+obj-$(CONFIG_IXD) += ixd/
diff --git a/drivers/net/ethernet/intel/ixd/Kconfig b/drivers/net/ethernet/intel/ixd/Kconfig
new file mode 100644
index 000000000000..f5594efe292c
--- /dev/null
+++ b/drivers/net/ethernet/intel/ixd/Kconfig
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0-only
+# Copyright (C) 2025 Intel Corporation
+
+config IXD
+ tristate "Intel(R) Control Plane Function Support"
+ depends on PCI_MSI
+ select LIBETH
+ select LIBIE_PCI
+ help
+ This driver supports Intel(R) Control Plane PCI Function
+ of Intel E2100 and later IPUs and FNICs.
+ It facilitates a centralized control over multiple IDPF PFs/VFs/SFs
+ exposed by the same card.
diff --git a/drivers/net/ethernet/intel/ixd/Makefile b/drivers/net/ethernet/intel/ixd/Makefile
new file mode 100644
index 000000000000..3849bc240600
--- /dev/null
+++ b/drivers/net/ethernet/intel/ixd/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0-only
+# Copyright (C) 2025 Intel Corporation
+
+# Intel(R) Control Plane Function Linux Driver
+
+obj-$(CONFIG_IXD) += ixd.o
+
+ixd-y := ixd_main.o
diff --git a/drivers/net/ethernet/intel/ixd/ixd.h b/drivers/net/ethernet/intel/ixd/ixd.h
new file mode 100644
index 000000000000..1b918c5d31cd
--- /dev/null
+++ b/drivers/net/ethernet/intel/ixd/ixd.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2025 Intel Corporation */
+
+#ifndef _IXD_H_
+#define _IXD_H_
+
+#include <linux/net/intel/libie/pci.h>
+
+/**
+ * struct ixd_adapter - Data structure representing a CPF
+ * @hw: Device access data
+ */
+struct ixd_adapter {
+ struct libie_mmio_info hw;
+};
+
+/**
+ * ixd_to_dev - Get the corresponding device struct from an adapter
+ * @adapter: PCI device driver-specific private data
+ *
+ * Return: struct device corresponding to the given adapter
+ */
+static inline struct device *ixd_to_dev(struct ixd_adapter *adapter)
+{
+ return &adapter->hw.pdev->dev;
+}
+
+#endif /* _IXD_H_ */
diff --git a/drivers/net/ethernet/intel/ixd/ixd_lan_regs.h b/drivers/net/ethernet/intel/ixd/ixd_lan_regs.h
new file mode 100644
index 000000000000..fbb88929d0de
--- /dev/null
+++ b/drivers/net/ethernet/intel/ixd/ixd_lan_regs.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2025 Intel Corporation */
+
+#ifndef _IXD_LAN_REGS_H_
+#define _IXD_LAN_REGS_H_
+
+/* Control Plane Function PCI ID */
+#define IXD_DEV_ID_CPF 0x1efe
+
+/* Control Queue (Mailbox) */
+#define PF_FW_MBX_REG_LEN 4096
+#define PF_FW_MBX 0x08400000
+
+/* Reset registers */
+#define PFGEN_RTRIG_REG_LEN 2048
+#define PFGEN_RTRIG 0x08407000 /* Device resets */
+
+/**
+ * struct ixd_bar_region - BAR region description
+ * @offset: BAR region offset
+ * @size: BAR region size
+ */
+struct ixd_bar_region {
+ resource_size_t offset;
+ resource_size_t size;
+};
+
+#endif /* _IXD_LAN_REGS_H_ */
diff --git a/drivers/net/ethernet/intel/ixd/ixd_main.c b/drivers/net/ethernet/intel/ixd/ixd_main.c
new file mode 100644
index 000000000000..75ee53152e61
--- /dev/null
+++ b/drivers/net/ethernet/intel/ixd/ixd_main.c
@@ -0,0 +1,112 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (C) 2025 Intel Corporation */
+
+#include "ixd.h"
+#include "ixd_lan_regs.h"
+
+MODULE_DESCRIPTION("Intel(R) Control Plane Function Device Driver");
+MODULE_IMPORT_NS("LIBIE_PCI");
+MODULE_LICENSE("GPL");
+
+/**
+ * ixd_remove - remove a CPF PCI device
+ * @pdev: PCI device being removed
+ */
+static void ixd_remove(struct pci_dev *pdev)
+{
+ struct ixd_adapter *adapter = pci_get_drvdata(pdev);
+
+ libie_pci_unmap_all_mmio_regions(&adapter->hw);
+}
+
+/**
+ * ixd_shutdown - shut down a CPF PCI device
+ * @pdev: PCI device being shut down
+ */
+static void ixd_shutdown(struct pci_dev *pdev)
+{
+ ixd_remove(pdev);
+
+ if (system_state == SYSTEM_POWER_OFF)
+ pci_set_power_state(pdev, PCI_D3hot);
+}
+
+/**
+ * ixd_iomap_regions - iomap PCI BARs
+ * @adapter: adapter to map memory regions for
+ *
+ * Returns: %0 on success, negative on failure
+ */
+static int ixd_iomap_regions(struct ixd_adapter *adapter)
+{
+ const struct ixd_bar_region regions[] = {
+ {
+ .offset = PFGEN_RTRIG,
+ .size = PFGEN_RTRIG_REG_LEN,
+ },
+ {
+ .offset = PF_FW_MBX,
+ .size = PF_FW_MBX_REG_LEN,
+ },
+ };
+
+ for (int i = 0; i < ARRAY_SIZE(regions); i++) {
+ struct libie_mmio_info *mmio_info = &adapter->hw;
+ bool map_ok;
+
+ map_ok = libie_pci_map_mmio_region(mmio_info,
+ regions[i].offset,
+ regions[i].size);
+ if (!map_ok) {
+ dev_err(ixd_to_dev(adapter),
+ "Failed to map PCI device MMIO region\n");
+
+ libie_pci_unmap_all_mmio_regions(mmio_info);
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * ixd_probe - probe a CPF PCI device
+ * @pdev: corresponding PCI device
+ * @ent: entry in ixd_pci_tbl
+ *
+ * Returns: %0 on success, negative errno code on failure
+ */
+static int ixd_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ struct ixd_adapter *adapter;
+ int err;
+
+ adapter = devm_kzalloc(&pdev->dev, sizeof(*adapter), GFP_KERNEL);
+ if (!adapter)
+ return -ENOMEM;
+ adapter->hw.pdev = pdev;
+ INIT_LIST_HEAD(&adapter->hw.mmio_list);
+
+ err = libie_pci_init_dev(pdev);
+ if (err)
+ return err;
+
+ pci_set_drvdata(pdev, adapter);
+
+ return ixd_iomap_regions(adapter);
+}
+
+static const struct pci_device_id ixd_pci_tbl[] = {
+ { PCI_VDEVICE(INTEL, IXD_DEV_ID_CPF) },
+ { }
+};
+MODULE_DEVICE_TABLE(pci, ixd_pci_tbl);
+
+static struct pci_driver ixd_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = ixd_pci_tbl,
+ .probe = ixd_probe,
+ .remove = ixd_remove,
+ .shutdown = ixd_shutdown,
+};
+module_pci_driver(ixd_driver);
--
2.47.0
^ permalink raw reply related
* [PATCH iwl-next v8 13/15] ixd: add reset checks and initialize the mailbox
From: Larysa Zaremba @ 2026-06-08 14:41 UTC (permalink / raw)
To: intel-wired-lan, Tony Nguyen
Cc: aleksander.lobakin, sridhar.samudrala, Michal Swiatkowski,
Larysa Zaremba, Fijalkowski, Maciej, Emil Tantilov, Madhu Chittim,
Josh Hay, Keller, Jacob E, jayaprakash.shanmugam, Jiri Pirko,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Jonathan Corbet, Richard Cochran, Przemek Kitszel,
Andrew Lunn, netdev, linux-doc, linux-kernel, Aleksandr Loktionov,
Bharath R
In-Reply-To: <20260608144127.2751230-1-larysa.zaremba@intel.com>
At the end of the probe, trigger hard reset, initialize and schedule the
after-reset task. If the reset is complete in a pre-determined time,
initialize the default mailbox, through which other resources will be
negotiated.
Co-developed-by: Amritha Nambiar <amritha.nambiar@intel.com>
Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
Reviewed-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Tested-by: Bharath R <Bharath.r@intel.com>
Signed-off-by: Larysa Zaremba <larysa.zaremba@intel.com>
---
drivers/net/ethernet/intel/ixd/Kconfig | 1 +
drivers/net/ethernet/intel/ixd/Makefile | 2 +
drivers/net/ethernet/intel/ixd/ixd.h | 28 +++-
drivers/net/ethernet/intel/ixd/ixd_dev.c | 89 +++++++++++
drivers/net/ethernet/intel/ixd/ixd_lan_regs.h | 40 +++++
drivers/net/ethernet/intel/ixd/ixd_lib.c | 143 ++++++++++++++++++
drivers/net/ethernet/intel/ixd/ixd_main.c | 32 +++-
7 files changed, 326 insertions(+), 9 deletions(-)
create mode 100644 drivers/net/ethernet/intel/ixd/ixd_dev.c
create mode 100644 drivers/net/ethernet/intel/ixd/ixd_lib.c
diff --git a/drivers/net/ethernet/intel/ixd/Kconfig b/drivers/net/ethernet/intel/ixd/Kconfig
index f5594efe292c..24510c50070e 100644
--- a/drivers/net/ethernet/intel/ixd/Kconfig
+++ b/drivers/net/ethernet/intel/ixd/Kconfig
@@ -5,6 +5,7 @@ config IXD
tristate "Intel(R) Control Plane Function Support"
depends on PCI_MSI
select LIBETH
+ select LIBIE_CP
select LIBIE_PCI
help
This driver supports Intel(R) Control Plane PCI Function
diff --git a/drivers/net/ethernet/intel/ixd/Makefile b/drivers/net/ethernet/intel/ixd/Makefile
index 3849bc240600..164b2c86952f 100644
--- a/drivers/net/ethernet/intel/ixd/Makefile
+++ b/drivers/net/ethernet/intel/ixd/Makefile
@@ -6,3 +6,5 @@
obj-$(CONFIG_IXD) += ixd.o
ixd-y := ixd_main.o
+ixd-y += ixd_dev.o
+ixd-y += ixd_lib.o
diff --git a/drivers/net/ethernet/intel/ixd/ixd.h b/drivers/net/ethernet/intel/ixd/ixd.h
index 1b918c5d31cd..c86c2b05c5b4 100644
--- a/drivers/net/ethernet/intel/ixd/ixd.h
+++ b/drivers/net/ethernet/intel/ixd/ixd.h
@@ -4,14 +4,25 @@
#ifndef _IXD_H_
#define _IXD_H_
-#include <linux/net/intel/libie/pci.h>
+#include <linux/net/intel/libie/controlq.h>
/**
* struct ixd_adapter - Data structure representing a CPF
- * @hw: Device access data
+ * @cp_ctx: Control plane communication context
+ * @init_task: Delayed initialization after reset
+ * @xnm: virtchnl transaction manager
+ * @asq: Send control queue info
+ * @arq: Receive control queue info
*/
struct ixd_adapter {
- struct libie_mmio_info hw;
+ struct libie_ctlq_ctx cp_ctx;
+ struct {
+ struct delayed_work init_work;
+ u8 reset_retries;
+ } init_task;
+ struct libie_ctlq_xn_manager *xnm;
+ struct libie_ctlq_info *asq;
+ struct libie_ctlq_info *arq;
};
/**
@@ -22,7 +33,16 @@ struct ixd_adapter {
*/
static inline struct device *ixd_to_dev(struct ixd_adapter *adapter)
{
- return &adapter->hw.pdev->dev;
+ return &adapter->cp_ctx.mmio_info.pdev->dev;
}
+void ixd_ctlq_reg_init(struct ixd_adapter *adapter,
+ struct libie_ctlq_reg *ctlq_reg_tx,
+ struct libie_ctlq_reg *ctlq_reg_rx);
+void ixd_trigger_reset(struct ixd_adapter *adapter);
+bool ixd_check_reset_complete(struct ixd_adapter *adapter);
+void ixd_init_task(struct work_struct *work);
+int ixd_init_dflt_mbx(struct ixd_adapter *adapter);
+void ixd_deinit_dflt_mbx(struct ixd_adapter *adapter);
+
#endif /* _IXD_H_ */
diff --git a/drivers/net/ethernet/intel/ixd/ixd_dev.c b/drivers/net/ethernet/intel/ixd/ixd_dev.c
new file mode 100644
index 000000000000..cdd5477cc1f4
--- /dev/null
+++ b/drivers/net/ethernet/intel/ixd/ixd_dev.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (C) 2025 Intel Corporation */
+
+#include "ixd.h"
+#include "ixd_lan_regs.h"
+
+/**
+ * ixd_ctlq_reg_init - Initialize default mailbox registers
+ * @adapter: PCI device driver-specific private data
+ * @ctlq_reg_tx: Transmit queue registers info to be filled
+ * @ctlq_reg_rx: Receive queue registers info to be filled
+ */
+void ixd_ctlq_reg_init(struct ixd_adapter *adapter,
+ struct libie_ctlq_reg *ctlq_reg_tx,
+ struct libie_ctlq_reg *ctlq_reg_rx)
+{
+ struct libie_mmio_info *mmio_info = &adapter->cp_ctx.mmio_info;
+ *ctlq_reg_tx = (struct libie_ctlq_reg) {
+ .head = libie_pci_get_mmio_addr(mmio_info, PF_FW_ATQH),
+ .tail = libie_pci_get_mmio_addr(mmio_info, PF_FW_ATQT),
+ .len = libie_pci_get_mmio_addr(mmio_info, PF_FW_ATQLEN),
+ .addr_high = libie_pci_get_mmio_addr(mmio_info, PF_FW_ATQBAH),
+ .addr_low = libie_pci_get_mmio_addr(mmio_info, PF_FW_ATQBAL),
+ .len_mask = PF_FW_ATQLEN_ATQLEN_M,
+ .len_ena_mask = PF_FW_ATQLEN_ATQENABLE_M,
+ .head_mask = PF_FW_ATQH_ATQH_M,
+ };
+
+ *ctlq_reg_rx = (struct libie_ctlq_reg) {
+ .head = libie_pci_get_mmio_addr(mmio_info, PF_FW_ARQH),
+ .tail = libie_pci_get_mmio_addr(mmio_info, PF_FW_ARQT),
+ .len = libie_pci_get_mmio_addr(mmio_info, PF_FW_ARQLEN),
+ .addr_high = libie_pci_get_mmio_addr(mmio_info, PF_FW_ARQBAH),
+ .addr_low = libie_pci_get_mmio_addr(mmio_info, PF_FW_ARQBAL),
+ .len_mask = PF_FW_ARQLEN_ARQLEN_M,
+ .len_ena_mask = PF_FW_ARQLEN_ARQENABLE_M,
+ .head_mask = PF_FW_ARQH_ARQH_M,
+ };
+}
+
+static const struct ixd_reset_reg ixd_reset_reg = {
+ .rstat = PFGEN_RSTAT,
+ .rstat_m = PFGEN_RSTAT_PFR_STATE_M,
+ .rstat_ok_v = 0b01,
+ .rtrigger = PFGEN_CTRL,
+ .rtrigger_m = PFGEN_CTRL_PFSWR,
+};
+
+/**
+ * ixd_trigger_reset - Trigger PFR reset
+ * @adapter: the device with mapped reset register
+ */
+void ixd_trigger_reset(struct ixd_adapter *adapter)
+{
+ void __iomem *addr;
+ u32 reg_val;
+
+ addr = libie_pci_get_mmio_addr(&adapter->cp_ctx.mmio_info,
+ ixd_reset_reg.rtrigger);
+ reg_val = readl(addr);
+ writel(reg_val | ixd_reset_reg.rtrigger_m, addr);
+}
+
+/**
+ * ixd_check_reset_complete - Check if the PFR reset is completed
+ * @adapter: CPF being reset
+ *
+ * Return: %true if the register read indicates reset has been finished,
+ * %false otherwise
+ */
+bool ixd_check_reset_complete(struct ixd_adapter *adapter)
+{
+ u32 reg_val, reset_status;
+ void __iomem *addr;
+
+ addr = libie_pci_get_mmio_addr(&adapter->cp_ctx.mmio_info,
+ ixd_reset_reg.rstat);
+ reg_val = readl(addr);
+ reset_status = reg_val & ixd_reset_reg.rstat_m;
+
+ /* 0xFFFFFFFF might be read if the other side hasn't cleared
+ * the register for us yet.
+ */
+ if (reg_val != GENMASK(31, 0) &&
+ reset_status == ixd_reset_reg.rstat_ok_v)
+ return true;
+
+ return false;
+}
diff --git a/drivers/net/ethernet/intel/ixd/ixd_lan_regs.h b/drivers/net/ethernet/intel/ixd/ixd_lan_regs.h
index fbb88929d0de..58e58c75981b 100644
--- a/drivers/net/ethernet/intel/ixd/ixd_lan_regs.h
+++ b/drivers/net/ethernet/intel/ixd/ixd_lan_regs.h
@@ -11,9 +11,33 @@
#define PF_FW_MBX_REG_LEN 4096
#define PF_FW_MBX 0x08400000
+#define PF_FW_ARQBAL (PF_FW_MBX)
+#define PF_FW_ARQBAH (PF_FW_MBX + 0x4)
+#define PF_FW_ARQLEN (PF_FW_MBX + 0x8)
+#define PF_FW_ARQLEN_ARQLEN_M GENMASK(12, 0)
+#define PF_FW_ARQLEN_ARQENABLE_S 31
+#define PF_FW_ARQLEN_ARQENABLE_M BIT(PF_FW_ARQLEN_ARQENABLE_S)
+#define PF_FW_ARQH_ARQH_M GENMASK(12, 0)
+#define PF_FW_ARQH (PF_FW_MBX + 0xC)
+#define PF_FW_ARQT (PF_FW_MBX + 0x10)
+
+#define PF_FW_ATQBAL (PF_FW_MBX + 0x14)
+#define PF_FW_ATQBAH (PF_FW_MBX + 0x18)
+#define PF_FW_ATQLEN (PF_FW_MBX + 0x1C)
+#define PF_FW_ATQLEN_ATQLEN_M GENMASK(9, 0)
+#define PF_FW_ATQLEN_ATQENABLE_S 31
+#define PF_FW_ATQLEN_ATQENABLE_M BIT(PF_FW_ATQLEN_ATQENABLE_S)
+#define PF_FW_ATQH_ATQH_M GENMASK(9, 0)
+#define PF_FW_ATQH (PF_FW_MBX + 0x20)
+#define PF_FW_ATQT (PF_FW_MBX + 0x24)
+
/* Reset registers */
#define PFGEN_RTRIG_REG_LEN 2048
#define PFGEN_RTRIG 0x08407000 /* Device resets */
+#define PFGEN_RSTAT 0x08407008 /* PFR status */
+#define PFGEN_RSTAT_PFR_STATE_M GENMASK(1, 0)
+#define PFGEN_CTRL 0x0840700C /* PFR trigger */
+#define PFGEN_CTRL_PFSWR BIT(0)
/**
* struct ixd_bar_region - BAR region description
@@ -25,4 +49,20 @@ struct ixd_bar_region {
resource_size_t size;
};
+/**
+ * struct ixd_reset_reg - structure for reset registers
+ * @rstat: offset of status in register
+ * @rstat_m: status mask
+ * @rstat_ok_v: value that indicates PFR completed status
+ * @rtrigger: offset of reset trigger in register
+ * @rtrigger_m: reset trigger mask
+ */
+struct ixd_reset_reg {
+ u32 rstat;
+ u32 rstat_m;
+ u32 rstat_ok_v;
+ u32 rtrigger;
+ u32 rtrigger_m;
+};
+
#endif /* _IXD_LAN_REGS_H_ */
diff --git a/drivers/net/ethernet/intel/ixd/ixd_lib.c b/drivers/net/ethernet/intel/ixd/ixd_lib.c
new file mode 100644
index 000000000000..afc413d3650f
--- /dev/null
+++ b/drivers/net/ethernet/intel/ixd/ixd_lib.c
@@ -0,0 +1,143 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (C) 2025 Intel Corporation */
+
+#include "ixd.h"
+
+#define IXD_DFLT_MBX_Q_LEN 64
+
+/**
+ * ixd_init_ctlq_create_info - Initialize control queue info for creation
+ * @info: destination
+ * @type: type of the queue to create
+ * @ctlq_reg: register assigned to the control queue
+ */
+static void ixd_init_ctlq_create_info(struct libie_ctlq_create_info *info,
+ enum virtchnl2_queue_type type,
+ const struct libie_ctlq_reg *ctlq_reg)
+{
+ *info = (struct libie_ctlq_create_info) {
+ .type = type,
+ .id = -1,
+ .reg = *ctlq_reg,
+ .len = IXD_DFLT_MBX_Q_LEN,
+ };
+}
+
+/**
+ * ixd_init_libie_xn_params - Initialize xn transaction manager creation info
+ * @params: destination
+ * @adapter: adapter info struct
+ * @ctlqs: list of the managed queues to create
+ * @num_queues: length of the queue list
+ */
+static void ixd_init_libie_xn_params(struct libie_ctlq_xn_init_params *params,
+ struct ixd_adapter *adapter,
+ struct libie_ctlq_create_info *ctlqs,
+ uint num_queues)
+{
+ *params = (struct libie_ctlq_xn_init_params){
+ .cctlq_info = ctlqs,
+ .ctx = &adapter->cp_ctx,
+ .num_qs = num_queues,
+ };
+}
+
+/**
+ * ixd_adapter_fill_dflt_ctlqs - Find default control queues and store them
+ * @adapter: adapter info struct
+ */
+static void ixd_adapter_fill_dflt_ctlqs(struct ixd_adapter *adapter)
+{
+ guard(spinlock)(&adapter->cp_ctx.ctlqs_lock);
+ struct libie_ctlq_info *cq;
+
+ list_for_each_entry(cq, &adapter->cp_ctx.ctlqs, list) {
+ if (cq->qid != -1)
+ continue;
+ if (cq->type == LIBIE_CTLQ_TYPE_RX)
+ adapter->arq = cq;
+ else if (cq->type == LIBIE_CTLQ_TYPE_TX)
+ adapter->asq = cq;
+ }
+}
+
+/**
+ * ixd_deinit_dflt_mbx - Deinitialize default mailbox
+ * @adapter: adapter info struct
+ */
+void ixd_deinit_dflt_mbx(struct ixd_adapter *adapter)
+{
+ if (adapter->xnm)
+ libie_ctlq_xn_deinit(adapter->xnm, &adapter->cp_ctx);
+
+ adapter->arq = NULL;
+ adapter->asq = NULL;
+ adapter->xnm = NULL;
+}
+
+/**
+ * ixd_init_dflt_mbx - Setup default mailbox parameters and make request
+ * @adapter: adapter info struct
+ *
+ * Return: %0 on success, negative errno code on failure
+ */
+int ixd_init_dflt_mbx(struct ixd_adapter *adapter)
+{
+ struct libie_ctlq_create_info ctlqs_info[2];
+ struct libie_ctlq_xn_init_params xn_params;
+ struct libie_ctlq_reg ctlq_reg_tx;
+ struct libie_ctlq_reg ctlq_reg_rx;
+ int err;
+
+ ixd_ctlq_reg_init(adapter, &ctlq_reg_tx, &ctlq_reg_rx);
+ ixd_init_ctlq_create_info(&ctlqs_info[0], LIBIE_CTLQ_TYPE_TX,
+ &ctlq_reg_tx);
+ ixd_init_ctlq_create_info(&ctlqs_info[1], LIBIE_CTLQ_TYPE_RX,
+ &ctlq_reg_rx);
+ ixd_init_libie_xn_params(&xn_params, adapter, ctlqs_info,
+ ARRAY_SIZE(ctlqs_info));
+ err = libie_ctlq_xn_init(&xn_params);
+ if (err)
+ return err;
+ adapter->xnm = xn_params.xnm;
+
+ ixd_adapter_fill_dflt_ctlqs(adapter);
+
+ if (!adapter->asq || !adapter->arq) {
+ ixd_deinit_dflt_mbx(adapter);
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+/**
+ * ixd_init_task - Initialize after reset
+ * @work: init work struct
+ */
+void ixd_init_task(struct work_struct *work)
+{
+ struct ixd_adapter *adapter;
+ int err;
+
+ adapter = container_of(work, struct ixd_adapter,
+ init_task.init_work.work);
+
+ if (!ixd_check_reset_complete(adapter)) {
+ if (++adapter->init_task.reset_retries < 10)
+ queue_delayed_work(system_unbound_wq,
+ &adapter->init_task.init_work,
+ msecs_to_jiffies(500));
+ else
+ dev_err(ixd_to_dev(adapter),
+ "Device reset failed. The driver was unable to contact the device's firmware. Check that the FW is running.\n");
+ return;
+ }
+
+ adapter->init_task.reset_retries = 0;
+ err = ixd_init_dflt_mbx(adapter);
+ if (err)
+ dev_err(ixd_to_dev(adapter),
+ "Failed to initialize the default mailbox: %pe\n",
+ ERR_PTR(err));
+}
diff --git a/drivers/net/ethernet/intel/ixd/ixd_main.c b/drivers/net/ethernet/intel/ixd/ixd_main.c
index 75ee53152e61..b4d4000b63ed 100644
--- a/drivers/net/ethernet/intel/ixd/ixd_main.c
+++ b/drivers/net/ethernet/intel/ixd/ixd_main.c
@@ -5,6 +5,7 @@
#include "ixd_lan_regs.h"
MODULE_DESCRIPTION("Intel(R) Control Plane Function Device Driver");
+MODULE_IMPORT_NS("LIBIE_CP");
MODULE_IMPORT_NS("LIBIE_PCI");
MODULE_LICENSE("GPL");
@@ -16,7 +17,13 @@ static void ixd_remove(struct pci_dev *pdev)
{
struct ixd_adapter *adapter = pci_get_drvdata(pdev);
- libie_pci_unmap_all_mmio_regions(&adapter->hw);
+ /* Do not mix removal with (re)initialization */
+ cancel_delayed_work_sync(&adapter->init_task.init_work);
+ /* Leave the device clean on exit */
+ ixd_trigger_reset(adapter);
+ ixd_deinit_dflt_mbx(adapter);
+
+ libie_pci_unmap_all_mmio_regions(&adapter->cp_ctx.mmio_info);
}
/**
@@ -51,7 +58,7 @@ static int ixd_iomap_regions(struct ixd_adapter *adapter)
};
for (int i = 0; i < ARRAY_SIZE(regions); i++) {
- struct libie_mmio_info *mmio_info = &adapter->hw;
+ struct libie_mmio_info *mmio_info = &adapter->cp_ctx.mmio_info;
bool map_ok;
map_ok = libie_pci_map_mmio_region(mmio_info,
@@ -81,11 +88,15 @@ static int ixd_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
struct ixd_adapter *adapter;
int err;
+ if (WARN_ON(ent->device != IXD_DEV_ID_CPF))
+ return -EINVAL;
+
adapter = devm_kzalloc(&pdev->dev, sizeof(*adapter), GFP_KERNEL);
if (!adapter)
return -ENOMEM;
- adapter->hw.pdev = pdev;
- INIT_LIST_HEAD(&adapter->hw.mmio_list);
+
+ adapter->cp_ctx.mmio_info.pdev = pdev;
+ INIT_LIST_HEAD(&adapter->cp_ctx.mmio_info.mmio_list);
err = libie_pci_init_dev(pdev);
if (err)
@@ -93,7 +104,18 @@ static int ixd_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
pci_set_drvdata(pdev, adapter);
- return ixd_iomap_regions(adapter);
+ err = ixd_iomap_regions(adapter);
+ if (err)
+ return err;
+
+ INIT_DELAYED_WORK(&adapter->init_task.init_work,
+ ixd_init_task);
+
+ ixd_trigger_reset(adapter);
+ queue_delayed_work(system_unbound_wq, &adapter->init_task.init_work,
+ msecs_to_jiffies(500));
+
+ return 0;
}
static const struct pci_device_id ixd_pci_tbl[] = {
--
2.47.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox