* [PATCH v10 7/9] tracing/probes: Add $current variable support
From: Masami Hiramatsu (Google) @ 2026-06-26 2:11 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: <178243982430.790911.17439694390021542101.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 info. e.g.
$current->cpus_ptr
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
Changes in v8:
- Avoid uninitialized ctx->btf issue on $current without typecast.
Changes in v7:
- Fix to use force-typecast for task_struct implicitly.
Changes in v6:
- Rebased on dump fetcharg patch.
- Remove function name/eprobe requirement for $current.
Changes in v5:
- Use s32 for bof_find_btf_id().
Changes in v4:
- Add $current in README when CONFIG_HAVE_FUNCTION_ARG_ACCESS_API=y case.
- Fix to prohibit using $current in eprobes and address based kprobes.
Changes in v3:
- Remove $current support from eprobes (because eprobes is only for event)
- Prohibit uprobes to use $current.
Changes in v2:
- Support to parse $current in parse_btf_arg().
- If no typecast on $current, it automatically casted to task_struct.
- Check error case if $current follows something except for "-".
---
Documentation/trace/fprobetrace.rst | 1 +
Documentation/trace/kprobetrace.rst | 1 +
kernel/trace/trace.c | 4 ++--
kernel/trace/trace_probe.c | 37 ++++++++++++++++++++++++++++++++++-
kernel/trace/trace_probe.h | 1 +
kernel/trace/trace_probe_tmpl.h | 3 +++
6 files changed, 44 insertions(+), 3 deletions(-)
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 5670c4b91dc0..2b0b4f9acb2e 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -4320,13 +4320,13 @@ static const char readme_msg[] =
"\t args: <name>=fetcharg[:type]\n"
"\t fetcharg: (%<register>|$<efield>), @<address>, @<symbol>[+|-<offset>],\n"
#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
- "\t $stack<index>, $stack, $retval, $comm, $arg<N>,\n"
+ "\t $stack<index>, $stack, $retval, $comm, $arg<N>, $current\n"
#ifdef CONFIG_PROBE_EVENTS_BTF_ARGS
"\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"
+ "\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 2d5b2686cc15..eb58b70ae082 100644
--- a/kernel/trace/trace_probe.c
+++ b/kernel/trace/trace_probe.c
@@ -692,7 +692,9 @@ static int parse_btf_arg(char *varname,
int i, is_ptr, ret;
u32 tid;
- if (!ctx->funcname && !(ctx->flags & TPARG_FL_TEVENT))
+ /* Note: field is not separated at this point, so check prefix. */
+ if (!str_has_prefix(varname, "$current") &&
+ !ctx->funcname && !(ctx->flags & TPARG_FL_TEVENT))
return -EINVAL;
is_ptr = split_next_field(varname, &field, ctx);
@@ -705,6 +707,20 @@ static int parse_btf_arg(char *varname,
return -EOPNOTSUPP;
}
+ if (!strcmp(varname, "$current")) {
+ code->op = FETCH_OP_CURRENT;
+ /* If no typecast is specified for $current, use task_struct by default */
+ ret = bpf_find_btf_id("task_struct", BTF_KIND_STRUCT, &ctx->struct_btf);
+ if (ret < 0) {
+ trace_probe_log_err(ctx->offset, NO_BTF_ENTRY);
+ return -ENOENT;
+ }
+ tid = (u32)ret;
+ type = ctx->last_struct =
+ btf_type_skip_modifiers(ctx->struct_btf, tid, NULL);
+ goto found_type;
+ }
+
if (ctx->flags & TPARG_FL_RETURN && !strcmp(varname, "$retval")) {
code->op = FETCH_OP_RETVAL;
/* Check whether the function return type is not void, even with typecast. */
@@ -761,6 +777,7 @@ static int parse_btf_arg(char *varname,
found:
type = btf_type_skip_modifiers(ctx->btf, tid, NULL);
+found_type:
if (!type) {
trace_probe_log_err(ctx->offset, BAD_BTF_TID);
return -EINVAL;
@@ -1270,6 +1287,24 @@ 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 (str_has_prefix(arg, "current")) {
+ /* $current is only supported by kernel probe. */
+ if (!(ctx->flags & TPARG_FL_KERNEL)) {
+ err = TP_ERR_BAD_VAR;
+ goto inval;
+ }
+ arg += strlen("current");
+ if (*arg == '-' && IS_ENABLED(CONFIG_PROBE_EVENTS_BTF_ARGS))
+ return parse_btf_arg(orig_arg, pcode, end, ctx);
+
+ if (*arg != '\0')
+ goto inval;
+
+ 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 e7fcc77f51fc..053f72fdaece 100644
--- a/kernel/trace/trace_probe.h
+++ b/kernel/trace/trace_probe.h
@@ -92,6 +92,7 @@ typedef int (*print_type_func_t)(struct trace_seq *, void *, void *);
FETCH_OP(RETVAL, none), /* Return value */ \
FETCH_OP(IMM, imm), /* Immediate: .immediate */ \
FETCH_OP(COMM, none), /* Current comm */ \
+ FETCH_OP(CURRENT, none), /* Current task_struct address */\
FETCH_OP(ARG, param), /* Argument: .param = index */ \
FETCH_OP(FOFFS, imm), /* File offset: .immediate */ \
FETCH_OP(IMMSTR, string), /* Allocated string: .data */ \
diff --git a/kernel/trace/trace_probe_tmpl.h b/kernel/trace/trace_probe_tmpl.h
index 51436f19083b..d0e9662cde00 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_IMMSTR:
*val = (unsigned long)code->data;
break;
+ case FETCH_OP_CURRENT:
+ *val = (unsigned long)current;
+ break;
default:
return -EILSEQ;
}
^ permalink raw reply related
* [PATCH v10 8/9] tracing/probes: Add this_cpu_read() and this_cpu_ptr() dereference method to fetcharg
From: Masami Hiramatsu (Google) @ 2026-06-26 2:11 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: <178243982430.790911.17439694390021542101.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 this_cpu_read() dereference to access per-cpu
variable for the current CPU (accessing other CPU variable may race with
updates on other CPUs). Also this_cpu_ptr() is for accessing per-cpu
pointer.
Those are working as same as the kernel percpu macro.
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
Changes in v10:
- Prohibit this_cpu_*() for eprobe events.
Changes in v9:
- Prohibit this_cpu_*() for non kernel probes.
Changes in v6:
- Rebased on dump fetcharg patch.
- Fix to fetch static percpu variable with @SYM correctly.
Changes in v5:
- Simplify this_cpu_read() into +0(this_cpu_ptr()).
Changes in v3:
- Remove NULL check for percpu var because it is just an offset, could be 0.
- Simplify process_fetch_insn_bottom() code.
- If the last operation is this_cpu_read(), read only memory of the specific
size (of type).
Changes in v2:
- Drop +CPU/+PCPU and introduce this_cpu_read() and this_cpu_ptr().
- Support these method with BTF typecast.
- Just check the base address is NOT NULL instead of is_kernel_percpu_address().
---
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 | 152 ++++++++++++++++++++++++++---------
kernel/trace/trace_probe.h | 6 +
kernel/trace/trace_probe_tmpl.h | 22 ++++-
7 files changed, 141 insertions(+), 46 deletions(-)
diff --git a/Documentation/trace/eprobetrace.rst b/Documentation/trace/eprobetrace.rst
index 680e0af43d5d..279396951b34 100644
--- a/Documentation/trace/eprobetrace.rst
+++ b/Documentation/trace/eprobetrace.rst
@@ -39,6 +39,8 @@ Synopsis of eprobe_events
@SYM[+|-offs] : Fetch memory at SYM +|- offs (SYM should be a data symbol)
$comm : Fetch current task comm.
+|-[u]OFFS(FETCHARG) : Fetch memory at FETCHARG +|- OFFS address.(\*3)(\*4)
+ this_cpu_read(FETCHARG) : Read the value of the per-CPU variable FETCHARG on the current CPU.
+ this_cpu_ptr(FETCHARG) : Get the address of the per-CPU variable FETCHARG on the current CPU.
\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..3439bc9bd351 100644
--- a/Documentation/trace/fprobetrace.rst
+++ b/Documentation/trace/fprobetrace.rst
@@ -52,6 +52,8 @@ 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)
+ this_cpu_read(FETCHARG) : Read the value of the per-CPU variable FETCHARG on the current CPU.
+ this_cpu_ptr(FETCHARG) : Get the address of the per-CPU variable FETCHARG on the current CPU.
\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..9ae330eb0a52 100644
--- a/Documentation/trace/kprobetrace.rst
+++ b/Documentation/trace/kprobetrace.rst
@@ -55,6 +55,8 @@ 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)
+ this_cpu_read(FETCHARG) : Read the value of the per-CPU variable FETCHARG on the current CPU.
+ this_cpu_ptr(FETCHARG) : Get the address of the per-CPU variable FETCHARG on the current CPU.
\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 2b0b4f9acb2e..c9e182d40059 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -4329,6 +4329,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 this_cpu_read(<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 eb58b70ae082..0bd02bc0ee0f 100644
--- a/kernel/trace/trace_probe.c
+++ b/kernel/trace/trace_probe.c
@@ -345,6 +345,109 @@ static int parse_trace_event(char *arg, struct fetch_insn *code,
return -EINVAL;
}
+/* this_cpu_* parser */
+#define THIS_CPU_PTR_PREFIX "this_cpu_ptr("
+#define THIS_CPU_READ_PREFIX "this_cpu_read("
+#define THIS_CPU_PTR_LEN (sizeof(THIS_CPU_PTR_PREFIX) - 1)
+#define THIS_CPU_READ_LEN (sizeof(THIS_CPU_READ_PREFIX) - 1)
+
+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);
+
+/* handle dereference nested call */
+static inline int handle_dereference(char *arg, struct fetch_insn **pcode,
+ struct fetch_insn *end, struct traceprobe_parse_context *ctx,
+ int deref, long offset)
+{
+ const struct fetch_type *type = find_fetch_type(NULL, ctx->flags);
+ struct fetch_insn *code = *pcode;
+ int cur_offs = ctx->offset;
+ char *tmp;
+ int ret;
+
+ tmp = strrchr(arg, ')');
+ if (!tmp) {
+ trace_probe_log_err(ctx->offset + strlen(arg),
+ DEREF_OPEN_BRACE);
+ return -EINVAL;
+ }
+
+ *tmp = '\0';
+ ret = parse_probe_arg(arg, type, &code, end, ctx);
+ if (ret)
+ return ret;
+ ctx->offset = cur_offs;
+ if (code->op == FETCH_OP_COMM || code->op == FETCH_OP_IMMSTR) {
+ trace_probe_log_err(ctx->offset, COMM_CANT_DEREF);
+ return -EINVAL;
+ }
+
+ /*
+ * this_cpu_ptr(@SYM) does not use SYM value, but use SYM address.
+ * So we overwrite the last FETCH_OP_DEREF with FETCH_OP_CPU_PTR.
+ */
+ if (!(deref == FETCH_OP_CPU_PTR && *arg == '@')) {
+ code++;
+ if (code == end) {
+ trace_probe_log_err(ctx->offset, TOO_MANY_OPS);
+ return -EINVAL;
+ }
+ }
+ *pcode = code;
+
+ code->op = deref;
+ code->offset = offset;
+ /* Reset the last type if used */
+ ctx->last_type = NULL;
+ return 0;
+}
+
+static int parse_this_cpu(char *arg, struct fetch_insn **pcode,
+ struct fetch_insn *end,
+ struct traceprobe_parse_context *ctx)
+{
+ struct fetch_insn *code;
+ bool is_ptr = false;
+ int ret;
+
+ /*
+ * This is only for kernel probes, excluding eprobe, because per-cpu
+ * pointer should not be recorded by events.
+ */
+ if (!(ctx->flags & TPARG_FL_KERNEL) ||
+ (ctx->flags & TPARG_FL_TEVENT)) {
+ trace_probe_log_err(ctx->offset, NOSUP_PERCPU);
+ return -EINVAL;
+ }
+ if (str_has_prefix(arg, THIS_CPU_PTR_PREFIX)) {
+ arg += THIS_CPU_PTR_LEN;
+ ctx->offset += THIS_CPU_PTR_LEN;
+ is_ptr = true;
+ } else if (str_has_prefix(arg, THIS_CPU_READ_PREFIX)) {
+ arg += THIS_CPU_READ_LEN;
+ ctx->offset += THIS_CPU_READ_LEN;
+ } else
+ return -EINVAL;
+
+ ret = handle_dereference(arg, pcode, end, ctx, FETCH_OP_CPU_PTR, 0);
+ if (ret || is_ptr)
+ return ret;
+
+ /* this_cpu_read(VAR) -> +0(this_cpu_ptr(VAR)) */
+ code = *pcode;
+ code++;
+ if (code == end) {
+ trace_probe_log_err(ctx->offset, TOO_MANY_OPS);
+ return -EINVAL;
+ }
+ code->op = FETCH_OP_DEREF;
+ code->offset = 0;
+ *pcode = code;
+ return 0;
+}
+
#ifdef CONFIG_PROBE_EVENTS_BTF_ARGS
static u32 btf_type_int(const struct btf_type *t)
@@ -904,11 +1007,6 @@ static char *find_matched_close_paren(char *s)
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)
@@ -961,7 +1059,9 @@ static int handle_typecast(char *arg, struct fetch_insn **pcode,
/* Skip '(' */
ctx->offset += 1;
tmp++;
- } else if (*tmp == '+' || *tmp == '-') {
+ } else if (*tmp == '+' || *tmp == '-' ||
+ str_has_prefix(tmp, THIS_CPU_PTR_PREFIX) ||
+ str_has_prefix(tmp, THIS_CPU_READ_PREFIX)) {
/* Dereference can have another field access inside it. */
char *open = strchr(tmp + 1, '(');
@@ -1481,36 +1581,9 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
}
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),
- DEREF_OPEN_BRACE);
- return -EINVAL;
- } else {
- const struct fetch_type *t2 = find_fetch_type(NULL, ctx->flags);
- int cur_offs = ctx->offset;
-
- *tmp = '\0';
- ret = parse_probe_arg(arg, t2, &code, end, ctx);
- if (ret)
- break;
- ctx->offset = cur_offs;
- if (code->op == FETCH_OP_COMM ||
- code->op == FETCH_OP_IMMSTR) {
- trace_probe_log_err(ctx->offset, COMM_CANT_DEREF);
- return -EINVAL;
- }
- if (++code == end) {
- trace_probe_log_err(ctx->offset, TOO_MANY_OPS);
- return -EINVAL;
- }
- *pcode = code;
-
- code->op = deref;
- code->offset = offset;
- /* Reset the last type if used */
- ctx->last_type = NULL;
- }
+ ret = handle_dereference(arg, pcode, end, ctx, deref, offset);
+ if (ret < 0)
+ return ret;
break;
case '\\': /* Immediate value */
if (arg[1] == '"') { /* Immediate string */
@@ -1531,7 +1604,10 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
ret = handle_typecast(arg, pcode, end, ctx);
break;
default:
- if (isalpha(arg[0]) || arg[0] == '_') {
+ if (str_has_prefix(arg, THIS_CPU_PTR_PREFIX) ||
+ str_has_prefix(arg, THIS_CPU_READ_PREFIX)) {
+ ret = parse_this_cpu(arg, pcode, end, ctx);
+ } else if (isalpha(arg[0]) || arg[0] == '_') {
/* BTF variable or event field*/
if (ctx->flags & TPARG_FL_TEVENT) {
ret = parse_trace_event(arg, *pcode, ctx);
@@ -1548,8 +1624,8 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
return -EINVAL;
}
ret = parse_btf_arg(arg, pcode, end, ctx);
- break;
}
+ break;
}
if (!ret && code->op == FETCH_OP_NOP) {
/* Parsed, but do not find fetch method */
diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h
index 053f72fdaece..e6268a8dc378 100644
--- a/kernel/trace/trace_probe.h
+++ b/kernel/trace/trace_probe.h
@@ -101,6 +101,7 @@ typedef int (*print_type_func_t)(struct trace_seq *, void *, void *);
/* Stage 2 (dereference) ops */ \
FETCH_OP(DEREF, offset), /* Dereference: .offset */ \
FETCH_OP(UDEREF, offset), /* User-space dereference: .offset */\
+ FETCH_OP(CPU_PTR, none), /* Per-CPU pointer: .offset */ \
/* Stage 3 (store) ops */ \
FETCH_OP(ST_RAW, store), /* Raw value: .size */ \
FETCH_OP(ST_MEM, store), /* Memory: .offset, .size */ \
@@ -596,9 +597,10 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call,
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(TYPECAST_SYM_OFFSET, "@SYM+/-OFFSET with typecast needs parentheses") \
+ C(TYPECAST_SYM_OFFSET, "@SYM+/-OFFSET with typecast needs parentheses"), \
C(TYPECAST_NOT_ALIGNED, "Typecast field option is not byte-aligned"), \
- C(TYPECAST_BAD_ARROW, "Typecast field option does not support -> operator"),
+ C(TYPECAST_BAD_ARROW, "Typecast field option does not support -> operator"), \
+ C(NOSUP_PERCPU, "Per-cpu variable access is only for kernel probes"),
#undef C
#define C(a, b) TP_ERR_##a
diff --git a/kernel/trace/trace_probe_tmpl.h b/kernel/trace/trace_probe_tmpl.h
index d0e9662cde00..8db12f758fda 100644
--- a/kernel/trace/trace_probe_tmpl.h
+++ b/kernel/trace/trace_probe_tmpl.h
@@ -129,25 +129,35 @@ 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_CPU_PTR:
+ val = (unsigned long)this_cpu_ptr((void __percpu *)val);
+ 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
* [PATCH v10 9/9] tracing/probes: Add a new testcase for BTF typecasts
From: Masami Hiramatsu (Google) @ 2026-06-26 2:11 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: <178243982430.790911.17439694390021542101.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
this_cpu_read() 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>
---
Changes in v10:
- Add a check for $current and this_cpu_* for eprobe
Changes in v9:
- Add a testcase for checking new syntax.
Changes in v8:
- Add more test cases.
Changes in v6:
- Update testcase according to changes.
Changes in v5:
- Add more syntax test cases.
Changes in v4:
- Fix uprobe $current test.
Changes in v3:
- Add syntax test case.
- Update testcase to use this_cpu_read()
Changes in v2:
- Use timer_shutdown_sync() instead of timer_delete_sync() for teardown.
---
samples/trace_events/trace-events-sample.c | 40 +++++++
samples/trace_events/trace-events-sample.h | 34 ++++++
.../ftrace/test.d/dynevent/btf_probe_event.tc | 51 ++++++++++
.../test.d/dynevent/btf_typecast_accepted.tc | 107 ++++++++++++++++++++
.../test.d/dynevent/eprobes_syntax_errors.tc | 9 ++
.../ftrace/test.d/dynevent/fprobe_syntax_errors.tc | 12 ++
.../ftrace/test.d/kprobe/kprobe_syntax_errors.tc | 12 ++
.../ftrace/test.d/kprobe/uprobe_syntax_errors.tc | 5 +
8 files changed, 265 insertions(+), 5 deletions(-)
create mode 100644 tools/testing/selftests/ftrace/test.d/dynevent/btf_probe_event.tc
create mode 100644 tools/testing/selftests/ftrace/test.d/dynevent/btf_typecast_accepted.tc
diff --git a/samples/trace_events/trace-events-sample.c b/samples/trace_events/trace-events-sample.c
index 0b7a6efdb247..ca5d98c360cb 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);
@@ -132,9 +146,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))
- return -1;
+ if (IS_ERR(simple_tsk)) {
+ timer_shutdown_sync(&foo_timer_data->timer);
+ free_percpu(foo_timer_data->counter);
+ kfree(foo_timer_data);
+ return PTR_ERR(simple_tsk);
+ }
return 0;
}
@@ -147,6 +179,10 @@ static void __exit trace_event_exit(void)
kthread_stop(simple_tsk_fn);
simple_tsk_fn = NULL;
mutex_unlock(&thread_mutex);
+
+ timer_shutdown_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..96791e120b7d
--- /dev/null
+++ b/tools/testing/selftests/ftrace/test.d/dynevent/btf_probe_event.tc
@@ -0,0 +1,51 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+# description: BTF event with typecast and percpu access
+# requires: dynamic_events "this_cpu_read(<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 this_cpu_read() access.
+# (foo_timer_data,timer)t converts t to struct foo_timer_data * using container_of.
+# data->counter is a per-cpu pointer to int.
+# this_cpu_read(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=this_cpu_read((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
diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/btf_typecast_accepted.tc b/tools/testing/selftests/ftrace/test.d/dynevent/btf_typecast_accepted.tc
new file mode 100644
index 000000000000..acf0b5a917d3
--- /dev/null
+++ b/tools/testing/selftests/ftrace/test.d/dynevent/btf_typecast_accepted.tc
@@ -0,0 +1,107 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+# description: BTF typecast and percpu access syntax validation
+# requires: dynamic_events "this_cpu_read(<fetcharg>)":README "[(structname[,field])]<argname>[->field[->field|.field...]]":README
+
+KPROBES=
+FPROBES=
+
+if grep -qF "p[:[<group>/][<event>]] <place> [<args>]" README ; then
+ KPROBES=yes
+fi
+if grep -qF "f[:[<group>/][<event>]] <func-name>[%return] [<args>]" README ; then
+ FPROBES=yes
+fi
+
+if [ -z "$KPROBES" -a -z "$FPROBES" ] ; then
+ exit_unsupported
+fi
+
+echo 0 > events/enable
+echo > dynamic_events
+
+# Load trace-events-sample module if available to have per-CPU counter structure defined
+if ! lsmod | grep -q trace_events_sample; then
+ modprobe trace-events-sample || true
+fi
+
+if [ "$FPROBES" ] ; then
+ # 1. Test basic typecast on fprobe
+ echo 'f:fpevent1 vfs_read name=(file)file->f_path.dentry->d_name.name:string' >> dynamic_events
+ # 2. Test parenthesized typecast target on fprobe
+ echo 'f:fpevent2 vfs_read name=(file)(file)->f_path.dentry->d_name.name:string' >> dynamic_events
+ # 3. Test nested typecasts on fprobe
+ echo 'f:fpevent3 vfs_read name=(dentry)((file)file->f_path.dentry)->d_name.name:string' >> dynamic_events
+ # 4. Test container_of-style typecast with field option on fprobe
+ echo 'f:fpevent4 vfs_read name=(file,f_path)file->f_mode' >> dynamic_events
+ # 5. Test typecast on return value on fprobe
+ echo 'f:fpevent5 vfs_read%return name=(file)$retval->f_path.dentry->d_name.name:string' >> dynamic_events
+ # 6. Test $current variable support on fprobe
+ echo 'f:fpevent6 vfs_read pid=$current->pid' >> dynamic_events
+ echo 'f:fpevent7 vfs_read pid=(task_struct)$current->pid' >> dynamic_events
+ echo 'f:fpevent8 vfs_read pid=(task_struct,group_leader)$current->pid' >> dynamic_events
+
+ # Test this_cpu_read and this_cpu_ptr on fprobe
+ if lsmod | grep -q trace_events_sample; then
+ echo 'f:fpevent9 sample_timer_cb name=(foo_timer_data,timer)t->name:string count=this_cpu_read((foo_timer_data,timer)t->counter)' >> dynamic_events
+ echo 'f:fpevent10 sample_timer_cb ptr=this_cpu_ptr((foo_timer_data,timer)t->counter)' >> dynamic_events
+ fi
+fi
+
+if [ "$KPROBES" ] ; then
+ # 7. Test basic typecast on kprobe
+ echo 'p:kpevent1 vfs_read name=(file)file->f_path.dentry->d_name.name:string' >> dynamic_events
+ # 8. Test parenthesized typecast target on kprobe
+ echo 'p:kpevent2 vfs_read name=(file)(file)->f_path.dentry->d_name.name:string' >> dynamic_events
+ # 9. Test nested typecasts on kprobe
+ echo 'p:kpevent3 vfs_read name=(dentry)((file)file->f_path.dentry)->d_name.name:string' >> dynamic_events
+ # 10. Test container_of-style typecast with field option on kprobe
+ echo 'p:kpevent4 vfs_read name=(file,f_path)file->f_mode' >> dynamic_events
+ # 11. Test typecast on return value on kretprobe
+ echo 'r:kpevent5 vfs_read name=(file)$retval->f_path.dentry->d_name.name:string' >> dynamic_events
+ # 12. Test $current variable support on kprobe
+ echo 'p:kpevent6 vfs_read pid=$current->pid' >> dynamic_events
+ echo 'p:kpevent7 vfs_read pid=(task_struct)$current->pid' >> dynamic_events
+ echo 'p:kpevent8 vfs_read pid=(task_struct,group_leader)$current->pid' >> dynamic_events
+
+ # Test this_cpu_read and this_cpu_ptr on kprobe
+ if lsmod | grep -q trace_events_sample; then
+ echo 'p:kpevent9 sample_timer_cb name=(foo_timer_data,timer)t->name:string count=this_cpu_read((foo_timer_data,timer)t->counter)' >> dynamic_events
+ echo 'p:kpevent10 sample_timer_cb ptr=this_cpu_ptr((foo_timer_data,timer)t->counter)' >> dynamic_events
+ fi
+fi
+
+# Verify the events exist in dynamic_events
+if [ "$FPROBES" ] ; then
+ grep -q "fpevent1 " dynamic_events
+ grep -q "fpevent2 " dynamic_events
+ grep -q "fpevent3 " dynamic_events
+ grep -q "fpevent4 " dynamic_events
+ grep -q "fpevent5 " dynamic_events
+ grep -q "fpevent6 " dynamic_events
+ grep -q "fpevent7 " dynamic_events
+ grep -q "fpevent8 " dynamic_events
+ if lsmod | grep -q trace_events_sample; then
+ grep -q "fpevent9 " dynamic_events
+ grep -q "fpevent10 " dynamic_events
+ fi
+fi
+
+if [ "$KPROBES" ] ; then
+ grep -q "kpevent1 " dynamic_events
+ grep -q "kpevent2 " dynamic_events
+ grep -q "kpevent3 " dynamic_events
+ grep -q "kpevent4 " dynamic_events
+ grep -q "kpevent5 " dynamic_events
+ grep -q "kpevent6 " dynamic_events
+ grep -q "kpevent7 " dynamic_events
+ grep -q "kpevent8 " dynamic_events
+ if lsmod | grep -q trace_events_sample; then
+ grep -q "kpevent9 " dynamic_events
+ grep -q "kpevent10 " dynamic_events
+ fi
+fi
+
+# Clean up
+echo > dynamic_events
+clear_trace
diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/eprobes_syntax_errors.tc b/tools/testing/selftests/ftrace/test.d/dynevent/eprobes_syntax_errors.tc
index 0e65e787e426..ecfd50187fa7 100644
--- a/tools/testing/selftests/ftrace/test.d/dynevent/eprobes_syntax_errors.tc
+++ b/tools/testing/selftests/ftrace/test.d/dynevent/eprobes_syntax_errors.tc
@@ -21,8 +21,17 @@ check_error 'e:foo/^bar.1 syscalls/sys_enter_openat' # BAD_EVENT_NAME
check_error 'e:foo/bar syscalls/sys_enter_openat arg=^$foo' # BAD_ATTACH_ARG
+check_error 'e:foo/bar syscalls/sys_enter_openat arg=^COMM' # NO_EVENT_FIELD
+if grep -q '\\$current' README; then
+ check_error 'e:foo/bar syscalls/sys_enter_openat arg=^current' # NO_EVENT_FIELD
+fi
+
if grep -q '<attached-group>\.<attached-event>.*\[if <filter>\]' README; then
check_error 'e:foo/bar syscalls/sys_enter_openat if ^' # NO_EP_FILTER
fi
+if grep -q 'this_cpu_read(<fetcharg>)' README; then
+ check_error 'e:foo/bar syscalls/sys_enter_openat arg=^this_cpu_read(file)' # NO_EP_FILTER
+fi
+
exit 0
diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/fprobe_syntax_errors.tc b/tools/testing/selftests/ftrace/test.d/dynevent/fprobe_syntax_errors.tc
index fee479295e2f..e9d7e6919c7f 100644
--- a/tools/testing/selftests/ftrace/test.d/dynevent/fprobe_syntax_errors.tc
+++ b/tools/testing/selftests/ftrace/test.d/dynevent/fprobe_syntax_errors.tc
@@ -112,6 +112,18 @@ check_error 'f vfs_read%return $retval->^foo' # NO_PTR_STRCT
check_error 'f vfs_read file->^foo' # NO_BTF_FIELD
check_error 'f vfs_read file^-.foo' # BAD_HYPHEN
check_error 'f vfs_read ^file:string' # BAD_TYPE4STR
+if grep -qF "[(structname" README ; then
+check_error 'f vfs_read arg1=(task_struct)file^' # TYPECAST_REQ_FIELD
+check_error 'f vfs_read arg1=(a)((b)((c)(^(d)file->d)->c)->b)->a' # TOO_MANY_NESTED
+check_error 'f vfs_read arg1=(task_struct,^in_execve)file->comm' # TYPECAST_NOT_ALIGNED
+check_error 'f vfs_read arg1=(task_struct,^foo_bar)file->pid' # NO_BTF_FIELD
+check_error 'f vfs_read arg1=(^task_struct1234)file->pid' # NO_PTR_STRCT
+check_error 'f vfs_read arg1=(task_struct,se^->group_node)file->comm' # TYPECAST_BAD_ARROW
+check_error 'f vfs_read arg1=(task_struct,^->pid)file->comm' # NO_BTF_FIELD
+check_error 'f vfs_read arg1=(task_struct,^.pid)file->comm' # NO_BTF_FIELD
+check_error 'f vfs_read arg1=(task_struct,^.)file->comm' # NO_BTF_FIELD
+check_error 'f vfs_read arg1=(task_struct)^@symbol+10->comm' # TYPECAST_SYM_OFFSET
+fi
fi
else
diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_syntax_errors.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_syntax_errors.tc
index 8f1c58f0c239..21ce8414459f 100644
--- a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_syntax_errors.tc
+++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_syntax_errors.tc
@@ -115,6 +115,18 @@ check_error 'p vfs_read+20 ^$arg*' # NOFENTRY_ARGS
check_error 'p vfs_read ^hoge' # NO_BTFARG
check_error 'p kfree ^$arg10' # NO_BTFARG (exceed the number of parameters)
check_error 'r kfree ^$retval' # NO_RETVAL
+if grep -qF "[(structname" README ; then
+check_error 'p vfs_read arg1=(task_struct)file^' # TYPECAST_REQ_FIELD
+check_error 'p vfs_read arg1=(a)((b)((c)(^(d)file->d)->c)->b)->a' # TOO_MANY_NESTED
+check_error 'p vfs_read arg1=(task_struct,^in_execve)file->comm' # TYPECAST_NOT_ALIGNED
+check_error 'p vfs_read arg1=(task_struct,^foo_bar)file->pid' # NO_BTF_FIELD
+check_error 'p vfs_read arg1=(^task_struct1234)file->pid' # NO_PTR_STRCT
+check_error 'p vfs_read arg1=(task_struct,se^->group_node)file->comm' # TYPECAST_BAD_ARROW
+check_error 'p vfs_read arg1=(task_struct,^->pid)file->comm' # NO_BTF_FIELD
+check_error 'p vfs_read arg1=(task_struct,^.pid)file->comm' # NO_BTF_FIELD
+check_error 'p vfs_read arg1=(task_struct,^.)file->comm' # NO_BTF_FIELD
+check_error 'p vfs_read arg1=(task_struct)^@symbol+10->comm' # TYPECAST_SYM_OFFSET
+fi
else
check_error 'p vfs_read ^$arg*' # NOSUP_BTFARG
fi
diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/uprobe_syntax_errors.tc b/tools/testing/selftests/ftrace/test.d/kprobe/uprobe_syntax_errors.tc
index c817158b99db..e12dc967ec76 100644
--- a/tools/testing/selftests/ftrace/test.d/kprobe/uprobe_syntax_errors.tc
+++ b/tools/testing/selftests/ftrace/test.d/kprobe/uprobe_syntax_errors.tc
@@ -28,4 +28,9 @@ if grep -q ".*symstr.*" README; then
check_error 'p /bin/sh:10 $stack0:^symstr' # BAD_TYPE
fi
+# $current is not supported by uprobe
+if grep -q "\$current.*" README; then
+check_error 'p /bin/sh:10 ^$current:u8' # BAD_VAR
+fi
+
exit 0
^ permalink raw reply related
* Re: [PATCH v10 2/9] tracing/probes: Support dumping fetcharg program for debugging dynamic events
From: Masami Hiramatsu @ 2026-06-26 4:21 UTC (permalink / raw)
To: Masami Hiramatsu (Google)
Cc: Steven Rostedt, Mathieu Desnoyers, Jonathan Corbet, Shuah Khan,
linux-kernel, linux-trace-kernel, linux-doc, linux-kselftest
In-Reply-To: <178243984380.790911.16958862880276016609.stgit@devnote2>
Sashiko made a good comment:
> commit ce5985c5ce46daa952ae907992e02e403e601c04
> Author: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> tracing/probes: Support dumping fetcharg program for debugging dynamic events
> This commit introduces a new configuration option
> CONFIG_PROBE_EVENTS_DUMP_FETCHARG to debug dynamic events. When enabled, it
> appends commented lines containing the fetch instructions to dynamic events in
> tracefs files. This helps in verifying the compiled fetch instructions for
> each probe argument.
> > diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
> > index a8420e6abb56a..cfa807d8e760f 100644
> > --- a/kernel/trace/trace_kprobe.c
> > +++ b/kernel/trace/trace_kprobe.c
> > @@ -1320,6 +1320,8 @@ static int trace_kprobe_show(struct seq_file *m, struct dyn_event *ev)
> > seq_printf(m, " %s=%s", tk->tp.args[i].name, tk->tp.args[i].comm);
> > seq_putc(m, '\n');
> >
> > + trace_probe_dump_args(m, &tk->tp);
> > +
> Does enabling CONFIG_PROBE_EVENTS_DUMP_FETCHARG break in-tree userspace tools
> that parse kprobe_events?
> For example, perf probe reads the kprobe_events file directly. It passes
> the unexpected # lines to parse_probe_trace_command() in
> tools/perf/util/probe-file.c:__probe_file__get_namelist(), which returns
> -EINVAL and immediately aborts the event listing process.
> Similarly, the ftrace2bconf script does not ignore # lines when reading
> kprobe_events, which leads it to mistakenly emit invalid bootconfig syntax
> in tools/bootconfig/scripts/ftrace2bconf.sh:kprobe_event_options()
> (e.g. ftrace.event.kprobes.#.probes += ...).
Yeah, those tools needs to be updated. Anyway, the tools which reads the
files in tracefs should skip the lines started with #, Those are comment
lines. (e.g. trace file, hist file, etc.)
Thanks,
--
Masami Hiramatsu
^ permalink raw reply
* Re: [PATCH v5 13/24] virt/steal_monitor: Add documentation
From: Shrikanth Hegde @ 2026-06-26 4:30 UTC (permalink / raw)
To: Randy Dunlap, linux-kernel, mingo, peterz, juri.lelli,
vincent.guittot, yury.norov, kprateek.nayak, iii, corbet
Cc: tglx, gregkh, pbonzini, seanjc, vschneid, huschle, rostedt,
dietmar.eggemann, maddy, srikar, hdanton, chleroy, vineeth,
frederic, arighi, pauld, christian.loehle, tj, tommaso.cucinotta,
maz, rafael, kernellwp, linux-doc
In-Reply-To: <0e033f0b-f2b8-4127-b067-b960793bfba3@infradead.org>
Hi Randy, Thanks for checking the patches.
On 6/25/26 10:30 PM, Randy Dunlap wrote:
> Hi,
>
> On 6/25/26 5:46 AM, Shrikanth Hegde wrote:
>> Document this module named steal_monitor and its parameters.
>>
>> Signed-off-by: Shrikanth Hegde <sshegde@linux.ibm.com>
>> ---
>> v4-v5:
>> - new patch
>>
>> Please let me know if the placing is not right.
>>
>> Documentation/driver-api/index.rst | 1 +
>> Documentation/driver-api/steal-monitor.rst | 93 ++++++++++++++++++++++
>> 2 files changed, 94 insertions(+)
>> create mode 100644 Documentation/driver-api/steal-monitor.rst
>
>
>> diff --git a/Documentation/driver-api/steal-monitor.rst b/Documentation/driver-api/steal-monitor.rst
>> new file mode 100644
>> index 000000000000..997a22d0812c
>> --- /dev/null
>> +++ b/Documentation/driver-api/steal-monitor.rst
>> @@ -0,0 +1,93 @@
>> +.. SPDX-License-Identifier: GPL-2.0
>> +=============
>> +Steal Monitor
>> +=============
>> +
>> +:Author: Shrikanth Hegde
>> +
>> +Introduction:
>> +=============
>
> Nit:
> Kernel heading adornment style does not include an ending ':' character
> (4 places).
>
Ok. I will fix it next version.
>> +
>> +Steal monitor is a driver aimed at solving the Noisy Neighbour problem
>> +in virtualized environments. I.e performance of workload
>> +running in one VM gets affected significantly due to other VMs and
>> +combined they make slower forward progress.
>
>
^ permalink raw reply
* [PATCH] docs: fix openSUSE libelf-devel package name
From: David Disseldorp @ 2026-06-26 4:48 UTC (permalink / raw)
To: linux-doc; +Cc: David Disseldorp
The proposed "zypper install ... libelf-dev" invocation results in an
error:
'libelf-dev' not found in package names. Trying capabilities.
No provider of 'libelf-dev' found.
openSUSE and derivitives (Tumbleweed, Leap and SLES) use a "devel"
suffix instead of "dev".
Link: https://build.opensuse.org/projects/openSUSE:Factory/packages/elfutils/files/elfutils.spec
Signed-off-by: David Disseldorp <ddiss@suse.de>
---
Documentation/admin-guide/quickly-build-trimmed-linux.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Documentation/admin-guide/quickly-build-trimmed-linux.rst b/Documentation/admin-guide/quickly-build-trimmed-linux.rst
index cb178e0a62084..f6b31d7207ff6 100644
--- a/Documentation/admin-guide/quickly-build-trimmed-linux.rst
+++ b/Documentation/admin-guide/quickly-build-trimmed-linux.rst
@@ -446,7 +446,7 @@ distributions:
* openSUSE and derivatives::
sudo zypper install bc binutils bison dwarves flex gcc git make perl-base \
- openssl openssl-devel libelf-dev
+ openssl openssl-devel libelf-devel
In case you wonder why these lists include openssl and its development headers:
they are needed for the Secure Boot support, which many distributions enable in
--
2.54.0
^ permalink raw reply related
* Re: [PATCH v9 6/6] selftests/mm: add hwpoison-panic destructive test
From: Miaohe Lin @ 2026-06-26 7:07 UTC (permalink / raw)
To: Breno Leitao
Cc: linux-mm, linux-kernel, linux-doc, linux-kselftest,
linux-trace-kernel, kernel-team, Andrew Morton, David Hildenbrand,
Lorenzo Stoakes, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Shuah Khan, Naoya Horiguchi,
Jonathan Corbet, Shuah Khan, Liam R. Howlett, lance.yang,
Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers
In-Reply-To: <20260609-ecc_panic-v9-6-432a74002e74@debian.org>
On 2026/6/9 18:57, Breno Leitao wrote:
> Add a destructive selftest that verifies
> vm.panic_on_unrecoverable_memory_failure actually panics when a
> hwpoison error hits a kernel-owned page.
>
> Three "kinds" of kernel-owned page can be targeted, selectable via
> the script's first positional argument (default: rodata):
>
> rodata - a PG_reserved page in the kernel rodata range, sourced
> from the "Kernel rodata" sub-resource of "System RAM" in
> /proc/iomem. That entry is reported on every major
> architecture and guarantees the chosen PFN is backed by
> struct page (an online System RAM range, not a firmware
> hole), is PG_reserved, and is read-only -- so even if
> the panic fails to fire for some reason, the resulting
> PG_hwpoison marker on rodata does not corrupt writable
> kernel state.
>
> slab - a slab page found by walking /proc/kpageflags for the
> first PFN with KPF_SLAB set (and KPF_HWPOISON / KPF_NOPAGE
> / KPF_COMPOUND_TAIL clear). Exercises the get_any_page()
> path on a non PG_reserved kernel-owned page and so
> catches regressions where get_any_page() collapses
> kernel-owned pages into a transient -EIO instead of
> -ENOTRECOVERABLE.
>
> pgtable - same as slab, but the PFN is selected via KPF_PGTABLE.
>
> PageLargeKmalloc, the fourth page type matched by
> HWPoisonKernelOwned(), is intentionally not covered: it is a
> PAGE_TYPE_OPS flag with no /proc/kpageflags bit, so selecting such
> a PFN from userspace is not feasible. The slab and pgtable
> variants already exercise the same get_any_page() positive-check
> branch.
>
> The script enables the sysctl and writes the selected physical
> address to /sys/devices/system/memory/hard_offline_page. A
> successful run crashes the kernel with
>
> Memory failure: <pfn>: unrecoverable page
>
> A return from the inject means the panic did not fire and the test
> fails. Test outcome is therefore observed externally (serial
> console, kdump) rather than from the script's own exit code.
>
> The script is intentionally NOT wired into run_vmtests.sh: every
> successful run panics the kernel, which is incompatible with the
> sequential "run each category in the same VM" model that
> run_vmtests.sh assumes. It is also not registered as a TEST_PROGS /
> ksft_* wrapper so a default kselftest run does not opt itself into
> a panic. The script is meant to be executed manually inside a
> disposable VM (e.g. virtme-ng), one variant per VM boot, and
> requires RUN_DESTRUCTIVE=1 in the environment as a safety net.
>
> Signed-off-by: Breno Leitao <leitao@debian.org>
Looks good to me with two comments below.
> ---
> tools/testing/selftests/mm/Makefile | 4 +
> tools/testing/selftests/mm/hwpoison-panic.sh | 208 +++++++++++++++++++++++++++
> 2 files changed, 212 insertions(+)
>
> diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile
> index e6df968f0971..ed321ae709da 100644
> --- a/tools/testing/selftests/mm/Makefile
> +++ b/tools/testing/selftests/mm/Makefile
> @@ -174,6 +174,10 @@ TEST_PROGS += ksft_userfaultfd.sh
> TEST_PROGS += ksft_vma_merge.sh
> TEST_PROGS += ksft_vmalloc.sh
>
> +# Destructive: every successful run panics the kernel. Installed and
> +# kept executable, but not run from a default kselftest invocation.
> +TEST_PROGS_EXTENDED += hwpoison-panic.sh
> +
> TEST_FILES := test_vmalloc.sh
> TEST_FILES += test_hmm.sh
> TEST_FILES += va_high_addr_switch.sh
> diff --git a/tools/testing/selftests/mm/hwpoison-panic.sh b/tools/testing/selftests/mm/hwpoison-panic.sh
> new file mode 100755
> index 000000000000..fe58e7638a8b
> --- /dev/null
> +++ b/tools/testing/selftests/mm/hwpoison-panic.sh
> @@ -0,0 +1,208 @@
> +#!/bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +#
> +# Verify vm.panic_on_unrecoverable_memory_failure by injecting a hwpoison
> +# error on a kernel-owned page and confirming the kernel panics.
> +#
> +# Three "kinds" of kernel-owned page can be targeted, selectable via the
> +# first positional argument (default: rodata):
> +#
> +# rodata - a PG_reserved page in the kernel rodata range
> +# (sourced from /proc/iomem "Kernel rodata"). Exercises
> +# memory_failure() -> get_any_page() on a PageReserved page.
> +#
> +# slab - a slab page found via /proc/kpageflags (KPF_SLAB).
> +# Exercises memory_failure() -> get_any_page() on a non
> +# PG_reserved kernel-owned page. This path is what catches
> +# regressions where get_any_page() collapses kernel-owned
> +# pages into a transient -EIO instead of -ENOTRECOVERABLE.
> +#
> +# pgtable - a page-table page found via /proc/kpageflags (KPF_PGTABLE).
> +# Same path as slab, different page type.
> +#
> +# This test is DESTRUCTIVE: a successful run crashes the kernel. It is
> +# meant to be executed inside a disposable VM (e.g. virtme-ng) with a
> +# serial console captured by the harness. It is skipped unless the
> +# caller opts in via RUN_DESTRUCTIVE=1.
> +#
> +# Test passes externally: the kernel must panic with
> +# "Memory failure: <pfn>: unrecoverable page"
> +# A return from the inject means the panic did not fire and the test
> +# fails.
> +#
> +# Author: Breno Leitao <leitao@debian.org>
> +
> +set -u
> +
> +ksft_skip=4
> +sysctl_path=/proc/sys/vm/panic_on_unrecoverable_memory_failure
> +inject_path=/sys/devices/system/memory/hard_offline_page
> +kpageflags_path=/proc/kpageflags
> +
> +# /proc/kpageflags bit positions (see include/uapi/linux/kernel-page-flags.h)
> +KPF_SLAB=7
> +KPF_COMPOUND_TAIL=16
> +KPF_HWPOISON=19
> +KPF_NOPAGE=20
> +KPF_PGTABLE=26
> +
> +kind=${1:-rodata}
> +
> +ksft_print() { echo "# $*"; }
> +ksft_exit_skip() { ksft_print "$*"; exit "$ksft_skip"; }
> +ksft_exit_fail() { echo "not ok 1 $*"; exit 1; }
> +
> +if [ "$(id -u)" -ne 0 ]; then
> + ksft_exit_skip "must run as root"
> +fi
> +
> +if [ ! -w "$sysctl_path" ]; then
> + ksft_exit_skip "$sysctl_path not present (kernel without the sysctl?)"
> +fi
> +
> +if [ ! -w "$inject_path" ]; then
> + ksft_exit_skip "$inject_path not present (no MEMORY_HOTPLUG?)"
> +fi
> +
> +if [ "${RUN_DESTRUCTIVE:-0}" != "1" ]; then
> + ksft_exit_skip "destructive test; re-run with RUN_DESTRUCTIVE=1 inside a disposable VM"
> +fi
> +
> +# Pick a PFN inside the kernel image rodata region of /proc/iomem.
> +# This is preferred over a top-level "Reserved" entry because top-level
> +# Reserved ranges are often firmware holes that have no backing struct
> +# page; pfn_to_online_page() returns NULL on those and memory_failure()
> +# bails out with -ENXIO before reaching the panic path.
> +#
> +# "Kernel rodata" is reported as a sub-resource of "System RAM" on every
> +# major architecture, which guarantees:
> +# - the PFN is backed by struct page (within an online memory range);
> +# - PG_reserved is set on the page (kernel image area);
> +# - the memory is read-only, so setting PG_hwpoison on it does not
> +# corrupt writable kernel state if the panic somehow does not fire.
> +#
> +# /proc/iomem entries look like (indented for sub-resources):
> +# " 02500000-02ffffff : Kernel rodata"
> +pick_rodata_phys_addr() {
> + awk -v pagesize="$(getconf PAGE_SIZE)" '
> + # Convert a hex string to a number without relying on the gawk-only
> + # strtonum(). mawk lacks it and would otherwise spuriously skip
> + # this test on distros that ship mawk as /usr/bin/awk.
> + function hex2num(s, n, i, c, v) {
> + n = 0
> + for (i = 1; i <= length(s); i++) {
> + c = tolower(substr(s, i, 1))
> + v = index("0123456789abcdef", c) - 1
> + if (v < 0)
> + return -1
> + n = n * 16 + v
> + }
> + return n
> + }
> + /: Kernel rodata[[:space:]]*$/ {
> + sub(/^[[:space:]]+/, "")
> + n = split($0, a, /[- ]/)
> + start = hex2num(a[1])
> + end = hex2num(a[2])
> + if (end <= start)
> + next
> + # Page-align upward and emit the first byte of that page.
> + pfn = int((start + pagesize - 1) / pagesize)
> + printf "0x%x\n", pfn * pagesize
> + exit 0
> + }
> + ' /proc/iomem
> +}
> +
> +# Walk /proc/kpageflags and return the phys addr of the first PFN that
> +# has bit $1 set, with KPF_HWPOISON, KPF_NOPAGE and KPF_COMPOUND_TAIL
> +# all clear (so we attack a real, non-tail, not-already-poisoned page).
> +#
> +# We skip the first 16 MiB of PFNs to step past low-memory special
> +# ranges (BIOS/EFI/ACPI/etc.) that often are PG_reserved and would not
> +# exhibit the slab/pgtable type we are looking for.
> +pick_kpageflags_phys_addr() {
> + local want_bit=$1
> + local pagesize skip_pfn
> +
> + [ -r "$kpageflags_path" ] || return
> +
> + pagesize=$(getconf PAGE_SIZE)
> + skip_pfn=$(((16 * 1024 * 1024) / pagesize))
> +
> + od -An -tx8 -v -w8 -j "$((skip_pfn * 8))" "$kpageflags_path" 2>/dev/null | \
> + awk -v want_bit="$want_bit" \
> + -v hwp_bit="$KPF_HWPOISON" \
> + -v nopage_bit="$KPF_NOPAGE" \
> + -v tail_bit="$KPF_COMPOUND_TAIL" \
> + -v base_pfn="$skip_pfn" \
> + -v pagesize="$pagesize" '
> + # Test whether bit "b" is set in the 16-hex-digit value "hex".
> + # Done with substring + per-digit lookup so we never rely on awk
> + # bitwise operators (mawk lacks them), 64-bit FP precision or the
> + # gawk-only strtonum().
> + function bit_set(hex, b, di, bi, c, v) {
> + di = int(b / 4)
> + bi = b - di * 4
> + c = substr(hex, length(hex) - di, 1)
> + v = index("0123456789abcdef", tolower(c)) - 1
> + if (bi == 0) return (v % 2) == 1
> + if (bi == 1) return int(v / 2) % 2 == 1
> + if (bi == 2) return int(v / 4) % 2 == 1
> + return int(v / 8) % 2 == 1
> + }
> + {
> + gsub(/^[[:space:]]+/, "")
> + h = $1
> + if (bit_set(h, want_bit) &&
> + !bit_set(h, hwp_bit) &&
> + !bit_set(h, nopage_bit) &&
> + !bit_set(h, tail_bit)) {
> + pfn = base_pfn + NR - 1
> + printf "0x%x\n", pfn * pagesize
> + exit 0
> + }
> + }
> + '
> +}
> +
> +case "$kind" in
> +rodata)
> + phys_addr=$(pick_rodata_phys_addr)
> + missing_msg='no "Kernel rodata" entry in /proc/iomem'
> + ;;
> +slab)
> + phys_addr=$(pick_kpageflags_phys_addr "$KPF_SLAB")
> + missing_msg="no usable slab PFN found in $kpageflags_path"
> + ;;
> +pgtable)
> + phys_addr=$(pick_kpageflags_phys_addr "$KPF_PGTABLE")
> + missing_msg="no usable page-table PFN found in $kpageflags_path"
> + ;;
> +*)
> + ksft_exit_fail "unknown kind '$kind' (expected: rodata|slab|pgtable)"
> + ;;
> +esac
> +
> +if [ -z "$phys_addr" ]; then
> + ksft_exit_skip "$missing_msg"
> +fi
> +
> +ksft_print "enabling $sysctl_path"
> +prior=$(cat "$sysctl_path")
> +echo 1 > "$sysctl_path" || ksft_exit_fail "failed to enable sysctl"
> +
> +ksft_print "injecting hwpoison at phys 0x$(printf '%x' "$phys_addr") (kind=$kind)"
> +ksft_print "expecting kernel panic: 'Memory failure: <pfn>: unrecoverable page'"
> +
> +# If this returns, the kernel did not panic → test failed. Restore the
> +# sysctl before reporting so the system is left as we found it.
> +if echo "$phys_addr" > "$inject_path"; then
> + echo "$prior" > "$sysctl_path"
> + ksft_exit_fail "inject returned without panic; sysctl ineffective"
In case of failure, should we recheck the page type? There is a window between
we get the phys_addr and inject the hwpoison.
> +fi
> +
> +# Write failed (e.g. -EINVAL on offlining a non-online region): also a
> +# failure for this test, since we expected the panic path.
> +echo "$prior" > "$sysctl_path"
> +ksft_exit_fail "inject failed before reaching the panic path"
Should we unpoison the pfn in case of failure?
Thanks.
.
^ permalink raw reply
* [PATCH v2 0/9] Support ROHM BD127x0 hot-swap controllers
From: Matti Vaittinen @ 2026-06-26 7:21 UTC (permalink / raw)
To: Matti Vaittinen, Matti Vaittinen, Matti Vaittinen
Cc: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jonathan Corbet, Shuah Khan, Wensheng Wang, Matti Vaittinen,
Ashish Yadav, Vasileios Amoiridis, Kim Seer Paller, ChiShih Tsai,
Chris Packham, Robert Coulson, linux-hwmon, devicetree,
linux-kernel, linux-doc
[-- Attachment #1: Type: text/plain, Size: 1924 bytes --]
Support ROHM BD12780(A) and BD12790
The BD12780 and BD12780A hot-swap controllers are very similar to Analog
Devices ADM1278. There are only some minor differences in the registers.
The BD12790 is largely similar to the ADM1272, with slightly different
coefficients and minor register changes.
This series adds basic support for these ROHM ICs.
Additionally, this series contains couple of fixes, which can be applied
independently from the bd127x0 support patches. Fixes are:
Patch 2/9: Prevent read from uninitialized stack (found by Sashiko)
Patch 3/9: Prevent coefficient overflow with larget shunt resistor (found
by Sashiko)
Patch 4/9: Support module auto-loading when DT is used.
Revision history:
v1 => v2:
dt-bindings:
- Fix compatible list as suggested by Krzysztof
adm1275:
- Add patches to fix issues pointed by Sashiko.
- Add of_device_ids for all supported devices.
- Add own switch 'case's for the new ICs instead of overloading
existing ones
- Drop i2c_device_id for bd12780a
- Don't use IC-wildcard in macro name.
---
Matti Vaittinen (9):
dt-bindings: adm1275: ROHM BD12780 hot-swap controller
hwmon: adm1275: Prevent reading uninitialized stack
hwmon: adm1275: Detect coefficient overflow
hwmon: adm1275: Support module auto-loading
doc: Add ROHM BD12780 and BD12780A
hwmon: adm1275: Support ROHM BD12780
dt-bindings: adm1275: ROHM BD12790 hot-swap controller
doc: adm1275: Add ROHM BD12790
hwmon: adm1275: Support ROHM BD12790
.../bindings/hwmon/adi,adm1275.yaml | 43 +++--
Documentation/hwmon/adm1275.rst | 24 +++
drivers/hwmon/pmbus/Kconfig | 4 +-
drivers/hwmon/pmbus/adm1275.c | 162 ++++++++++++++++--
4 files changed, 208 insertions(+), 25 deletions(-)
base-commit: 254f49634ee16a731174d2ae34bc50bd5f45e731
--
2.54.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* [PATCH v2 1/9] dt-bindings: adm1275: ROHM BD12780 hot-swap controller
From: Matti Vaittinen @ 2026-06-26 7:21 UTC (permalink / raw)
To: Matti Vaittinen, Matti Vaittinen, Matti Vaittinen
Cc: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jonathan Corbet, Shuah Khan, Wensheng Wang, Matti Vaittinen,
Ashish Yadav, Vasileios Amoiridis, Kim Seer Paller, ChiShih Tsai,
Chris Packham, Robert Coulson, linux-hwmon, devicetree,
linux-kernel, linux-doc
In-Reply-To: <cover.1782458224.git.mazziesaccount@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 2599 bytes --]
From: Matti Vaittinen <mazziesaccount@gmail.com>
Support ROHM BD12780 and BD12780A hot-swap controllers, which are largely
compatible with the Analog Devices adm1278. Main difference between
the BD12780 and the BD12780A is, that the BD12780 has one I2C address
configuration pin more (ADDR3) than the BD12780A.
Introduce own compatibles for both variants but require the BD12780A to
always have the BD12780 as a fall-back.
Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
---
Revision history:
v1 => v2:
- Drop extra -items from the compatible list as suggested by Krzysztof
---
.../bindings/hwmon/adi,adm1275.yaml | 38 +++++++++++++------
1 file changed, 27 insertions(+), 11 deletions(-)
diff --git a/Documentation/devicetree/bindings/hwmon/adi,adm1275.yaml b/Documentation/devicetree/bindings/hwmon/adi,adm1275.yaml
index d6a7517f2a50..503e93756584 100644
--- a/Documentation/devicetree/bindings/hwmon/adi,adm1275.yaml
+++ b/Documentation/devicetree/bindings/hwmon/adi,adm1275.yaml
@@ -25,19 +25,34 @@ description: |
https://www.silergy.com/
download/downloadFile?id=5669&type=product&ftype=note
+ The BD12780 and BD12780A are hot-swap controllers from ROHM. They are
+ functionally compatible with the ADM1278. The main difference between
+ the BD12780A and the BD12780 is amount of configurable I2C addresses.
+
+ Datasheets:
+ https://fscdn.rohm.com/en/products/databook/datasheet/ic/power/power_switch/bd12780muv-lb-e.pdf
+ https://fscdn.rohm.com/en/products/databook/datasheet/ic/power/power_switch/bd12780amuv-lb-e.pdf
+
properties:
compatible:
- enum:
- - adi,adm1075
- - adi,adm1272
- - adi,adm1273
- - adi,adm1275
- - adi,adm1276
- - adi,adm1278
- - adi,adm1281
- - adi,adm1293
- - adi,adm1294
- - silergy,mc09c
+ oneOf:
+ - enum:
+ - adi,adm1075
+ - adi,adm1272
+ - adi,adm1273
+ - adi,adm1275
+ - adi,adm1276
+ - adi,adm1278
+ - adi,adm1281
+ - adi,adm1293
+ - adi,adm1294
+ - rohm,bd12780
+ - silergy,mc09c
+
+ # Require BD12780 as a fall-back for BD12780A.
+ - items:
+ - const: rohm,bd12780a
+ - const: rohm,bd12780
reg:
maxItems: 1
@@ -104,6 +119,7 @@ allOf:
- adi,adm1281
- adi,adm1293
- adi,adm1294
+ - rohm,bd12780
- silergy,mc09c
then:
properties:
--
2.54.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply related
* [PATCH v2 2/9] hwmon: adm1275: Prevent reading uninitialized stack
From: Matti Vaittinen @ 2026-06-26 7:22 UTC (permalink / raw)
To: Matti Vaittinen, Matti Vaittinen, Matti Vaittinen
Cc: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jonathan Corbet, Shuah Khan, Wensheng Wang, Matti Vaittinen,
Ashish Yadav, Vasileios Amoiridis, Kim Seer Paller, ChiShih Tsai,
Chris Packham, Robert Coulson, linux-hwmon, devicetree,
linux-kernel, linux-doc
In-Reply-To: <cover.1782458224.git.mazziesaccount@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1975 bytes --]
From: Matti Vaittinen <mazziesaccount@gmail.com>
While adding support for the ROHM BD127X0 hot-swap controllers, sashiko
reported an error in device-name comparison, which can lead to reading
uninitialized stack memory.
Quoting Sashiko:
This is a pre-existing issue, but I noticed that just before this block in
adm1275_probe(), there might be an out-of-bounds stack read:
ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, block_buffer);
if (ret < 0) { ... }
for (mid = adm1275_id; mid->name[0]; mid++) {
if (!strncasecmp(mid->name, block_buffer, strlen(mid->name)))
break;
}
Since i2c_smbus_read_block_data() reads up to 32 bytes into the
uninitialized stack array block_buffer without appending a null
terminator, strncasecmp() could read past the valid bytes returned in ret.
For example, if the device returns a shorter string like "adm12", checking
it against "adm1275" up to the length of "adm1275" will continue reading
into uninitialized stack bounds.
Prevent reading uninitialized memory by zeroing the stack array.
Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
Fixes: 87102808d039 ("hwmon: (pmbus/adm1275) Validate device ID")
---
Revision history:
v1 => v2:
- New patch
---
drivers/hwmon/pmbus/adm1275.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c
index bc2a6a07dc3e..43baa5ded35e 100644
--- a/drivers/hwmon/pmbus/adm1275.c
+++ b/drivers/hwmon/pmbus/adm1275.c
@@ -512,7 +512,7 @@ static int adm1275_enable_vout_temp(struct adm1275_data *data,
static int adm1275_probe(struct i2c_client *client)
{
s32 (*config_read_fn)(const struct i2c_client *client, u8 reg);
- u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1];
+ u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1] = {0};
int config, device_config;
int ret;
struct pmbus_driver_info *info;
--
2.54.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply related
* [PATCH v2 3/9] hwmon: adm1275: Detect coefficient overflow
From: Matti Vaittinen @ 2026-06-26 7:23 UTC (permalink / raw)
To: Matti Vaittinen, Matti Vaittinen, Matti Vaittinen
Cc: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jonathan Corbet, Shuah Khan, Wensheng Wang, Matti Vaittinen,
Ashish Yadav, Vasileios Amoiridis, Kim Seer Paller, ChiShih Tsai,
Chris Packham, Robert Coulson, linux-hwmon, devicetree,
linux-kernel, linux-doc
In-Reply-To: <cover.1782458224.git.mazziesaccount@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 2645 bytes --]
From: Matti Vaittinen <mazziesaccount@gmail.com>
Sashiko detected potential coefficient overflow if large shunt resistor
is used. When going unnoticed it can cause "drastically incorrect
telemetry scaling factors" as Sashiko put it.
I am not convinced such "drastically incorrect telemetry scaling
factors" could have gone unnoticed, so I suspect such large shunt
resistors aren't really used. Well, it shouldn't hurt to detect the
error and abort the probe before Really Wrong current / power -values
are reported to user by the hwmon.
Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
---
Revision history:
v1 => v2:
- New patch
This patch returns -EOVERFLOW with an error print if overflow is
detected. IF there really are systems where the overflow truly occurs,
then this change will cause the probe to fail - which might hurt the
boot process. It might be safer to only print the warning. One could
also try changing the order of the shunt resistor value division (/1000)
and the multiplication and see if overflow goes away - but it'll be
somewhat more complex then. Hence, I just decided to error-out if this
happens, and leave this for the people facing the real overflow to fix
(if needed)... It's still fair to mention this might cause issues.
---
drivers/hwmon/pmbus/adm1275.c | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c
index 43baa5ded35e..ccc3ad21e38e 100644
--- a/drivers/hwmon/pmbus/adm1275.c
+++ b/drivers/hwmon/pmbus/adm1275.c
@@ -839,15 +839,25 @@ static int adm1275_probe(struct i2c_client *client)
info->R[PSC_VOLTAGE_OUT] = coefficients[voindex].R;
}
if (cindex >= 0) {
+ u32 m;
+
/* Scale current with sense resistor value */
- info->m[PSC_CURRENT_OUT] =
- coefficients[cindex].m * shunt / 1000;
+ if (unlikely(check_mul_overflow(coefficients[cindex].m, shunt, &m))) {
+ dev_err(&client->dev, "Current coefficient overflow\n");
+ return -EOVERFLOW;
+ }
+ info->m[PSC_CURRENT_OUT] = m / 1000;
info->b[PSC_CURRENT_OUT] = coefficients[cindex].b;
info->R[PSC_CURRENT_OUT] = coefficients[cindex].R;
}
if (pindex >= 0) {
- info->m[PSC_POWER] =
- coefficients[pindex].m * shunt / 1000;
+ u32 m;
+
+ if (unlikely(check_mul_overflow(coefficients[pindex].m, shunt, &m))) {
+ dev_err(&client->dev, "Power coefficient overflow\n");
+ return -EOVERFLOW;
+ }
+ info->m[PSC_POWER] = m / 1000;
info->b[PSC_POWER] = coefficients[pindex].b;
info->R[PSC_POWER] = coefficients[pindex].R;
}
--
2.54.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply related
* [PATCH v2 4/9] hwmon: adm1275: Support module auto-loading
From: Matti Vaittinen @ 2026-06-26 7:24 UTC (permalink / raw)
To: Matti Vaittinen, Matti Vaittinen, Matti Vaittinen
Cc: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jonathan Corbet, Shuah Khan, Wensheng Wang, Matti Vaittinen,
Ashish Yadav, Vasileios Amoiridis, Kim Seer Paller, ChiShih Tsai,
Chris Packham, Robert Coulson, linux-hwmon, devicetree,
linux-kernel, linux-doc
In-Reply-To: <cover.1782458224.git.mazziesaccount@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1948 bytes --]
From: Matti Vaittinen <mazziesaccount@gmail.com>
Populating the i2c_device_id -table is not enough to make the
driver module automatically load when device-tree node for the
power-monitor is parsed at boot.
Adding the of_device_id tables causes the driver module to be
automatically load at boot. Testing has been done with rather old Debian
system.
When inspecting the generated module-aliases with the insmod, following
entries seem to be the difference:
alias: of:N*T*Cadi,adm1075C*
alias: of:N*T*Cadi,adm1075
I suspect these are required for the module loading to work.
Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
---
Revision history:
v1 => v2:
- New patch as discussed with Guenter here:
https://lore.kernel.org/all/f080e20e-6ec7-4744-9794-0a92d03f48d8@roeck-us.net/
---
drivers/hwmon/pmbus/adm1275.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c
index ccc3ad21e38e..1ea2037711e1 100644
--- a/drivers/hwmon/pmbus/adm1275.c
+++ b/drivers/hwmon/pmbus/adm1275.c
@@ -870,9 +870,25 @@ static int adm1275_probe(struct i2c_client *client)
return pmbus_do_probe(client, info);
}
+static const struct of_device_id adm1275_of_match[] = {
+ { .compatible = "adi,adm1075", },
+ { .compatible = "adi,adm1272", },
+ { .compatible = "adi,adm1273", },
+ { .compatible = "adi,adm1275", },
+ { .compatible = "adi,adm1276", },
+ { .compatible = "adi,adm1278", },
+ { .compatible = "adi,adm1281", },
+ { .compatible = "adi,adm1293", },
+ { .compatible = "adi,adm1294", },
+ { .compatible = "silergy,mc09c", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adm1275_of_match);
+
static struct i2c_driver adm1275_driver = {
.driver = {
.name = "adm1275",
+ .of_match_table = adm1275_of_match,
},
.probe = adm1275_probe,
.id_table = adm1275_id,
--
2.54.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply related
* [PATCH v2 5/9] doc: Add ROHM BD12780 and BD12780A
From: Matti Vaittinen @ 2026-06-26 7:24 UTC (permalink / raw)
To: Matti Vaittinen, Matti Vaittinen, Matti Vaittinen
Cc: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jonathan Corbet, Shuah Khan, Wensheng Wang, Matti Vaittinen,
Ashish Yadav, Vasileios Amoiridis, Kim Seer Paller, ChiShih Tsai,
Chris Packham, Robert Coulson, linux-hwmon, devicetree,
linux-kernel, linux-doc
In-Reply-To: <cover.1782458224.git.mazziesaccount@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1186 bytes --]
From: Matti Vaittinen <mazziesaccount@gmail.com>
Add the ROHM BD12780 and the BD12780A to the list of the ICs supported by
the adm1275 driver.
Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
---
Revision history:
v1 => :
- No changes
---
Documentation/hwmon/adm1275.rst | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/Documentation/hwmon/adm1275.rst b/Documentation/hwmon/adm1275.rst
index cf923f20fa52..8a793dd2b412 100644
--- a/Documentation/hwmon/adm1275.rst
+++ b/Documentation/hwmon/adm1275.rst
@@ -67,6 +67,22 @@ Supported chips:
Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADM1293_1294.pdf
+ * ROHM Semiconductor BD12780
+
+ Prefix: 'bd12780'
+
+ Addresses scanned: -
+
+ Datasheet: https://fscdn.rohm.com/en/products/databook/datasheet/ic/power/power_switch/bd12780muv-lb-e.pdf
+
+ * ROHM Semiconductor BD12780A
+
+ Prefix: 'bd12780'
+
+ Addresses scanned: -
+
+ Datasheet: https://fscdn.rohm.com/en/products/databook/datasheet/ic/power/power_switch/bd12780amuv-lb-e.pdf
+
* Silergy SQ24905C
Prefix: 'mc09c'
--
2.54.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply related
* [PATCH v2 6/9] hwmon: adm1275: Support ROHM BD12780
From: Matti Vaittinen @ 2026-06-26 7:24 UTC (permalink / raw)
To: Matti Vaittinen, Matti Vaittinen, Matti Vaittinen
Cc: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jonathan Corbet, Shuah Khan, Wensheng Wang, Matti Vaittinen,
Ashish Yadav, Vasileios Amoiridis, Kim Seer Paller, ChiShih Tsai,
Chris Packham, Robert Coulson, linux-hwmon, devicetree,
linux-kernel, linux-doc
In-Reply-To: <cover.1782458224.git.mazziesaccount@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 6290 bytes --]
From: Matti Vaittinen <mazziesaccount@gmail.com>
ROHM BD12780 and BD12780A are hot-swap controllers. They are largely
similar to Analog Devices ADM1278. Besides the ID registers and some
added functionality, the BD12780 and BD12780A mark PMON_CONFIG bits
[15:14] as reserved. Hence TSFILT setting must be omitted on these ICs.
The BD12780 has 3 pins usable for configuring the I2C address. The
BD12780A lists the ADDR3-pin as "not connect".
Support ROHM BD12780 and BD12780A controllers.
Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
---
Revision history:
v1 => v2: Changes based on the review by Guenter:
- Drop i2c_device_id for bd12780a
- Add own 'case' for the bd12780 instead of overloading the existing
one and still having an 'if (id == bd12780)' inside the case.
---
drivers/hwmon/pmbus/Kconfig | 2 +-
drivers/hwmon/pmbus/adm1275.c | 55 ++++++++++++++++++++++++++++++-----
2 files changed, 48 insertions(+), 9 deletions(-)
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index 8f4bff375ecb..b3c27f3b2712 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -52,7 +52,7 @@ config SENSORS_ADM1275
help
If you say yes here you get hardware monitoring support for Analog
Devices ADM1075, ADM1272, ADM1273, ADM1275, ADM1276, ADM1278, ADM1281,
- ADM1293, ADM1294 and SQ24905C Hot-Swap Controller and
+ ADM1293, ADM1294, ROHM BD12780, and SQ24905C Hot-Swap Controller and
Digital Power Monitors.
This driver can also be built as a module. If so, the module will
diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c
index 1ea2037711e1..81c50fab7687 100644
--- a/drivers/hwmon/pmbus/adm1275.c
+++ b/drivers/hwmon/pmbus/adm1275.c
@@ -19,7 +19,7 @@
#include "pmbus.h"
enum chips { adm1075, adm1272, adm1273, adm1275, adm1276, adm1278, adm1281,
- adm1293, adm1294, sq24905c };
+ adm1293, adm1294, bd12780, sq24905c };
#define ADM1275_MFR_STATUS_IOUT_WARN2 BIT(0)
#define ADM1293_MFR_STATUS_VAUX_UV_WARN BIT(5)
@@ -47,6 +47,8 @@ enum chips { adm1075, adm1272, adm1273, adm1275, adm1276, adm1278, adm1281,
#define ADM1278_VOUT_EN BIT(1)
#define ADM1278_PMON_DEFCONFIG (ADM1278_VOUT_EN | ADM1278_TEMP1_EN | ADM1278_TSFILT)
+/* The BD12780 data sheets mark TSFILT bit as reserved. */
+#define BD12780_PMON_DEFCONFIG (ADM1278_VOUT_EN | ADM1278_TEMP1_EN)
#define ADM1293_IRANGE_25 0
#define ADM1293_IRANGE_50 BIT(6)
@@ -487,6 +489,7 @@ static const struct i2c_device_id adm1275_id[] = {
{ "adm1281", adm1281 },
{ "adm1293", adm1293 },
{ "adm1294", adm1294 },
+ { "bd12780", bd12780 },
{ "mc09c", sq24905c },
{ }
};
@@ -494,12 +497,13 @@ MODULE_DEVICE_TABLE(i2c, adm1275_id);
/* Enable VOUT & TEMP1 if not enabled (disabled by default) */
static int adm1275_enable_vout_temp(struct adm1275_data *data,
- struct i2c_client *client, int config)
+ struct i2c_client *client, int config,
+ u16 defconfig)
{
int ret;
- if ((config & ADM1278_PMON_DEFCONFIG) != ADM1278_PMON_DEFCONFIG) {
- config |= ADM1278_PMON_DEFCONFIG;
+ if ((config & defconfig) != defconfig) {
+ config |= defconfig;
ret = adm1275_write_pmon_config(data, client, config);
if (ret < 0) {
dev_err(&client->dev, "Failed to enable VOUT/TEMP1 monitoring\n");
@@ -535,7 +539,8 @@ static int adm1275_probe(struct i2c_client *client)
return ret;
}
if ((ret != 3 || strncmp(block_buffer, "ADI", 3)) &&
- (ret != 2 || strncmp(block_buffer, "SY", 2))) {
+ (ret != 2 || strncmp(block_buffer, "SY", 2)) &&
+ (ret != 4 || strncmp(block_buffer, "ROHM", 4))) {
dev_err(&client->dev, "Unsupported Manufacturer ID\n");
return -ENODEV;
}
@@ -562,7 +567,7 @@ static int adm1275_probe(struct i2c_client *client)
if (mid->driver_data == adm1272 || mid->driver_data == adm1273 ||
mid->driver_data == adm1278 || mid->driver_data == adm1281 ||
mid->driver_data == adm1293 || mid->driver_data == adm1294 ||
- mid->driver_data == sq24905c)
+ mid->driver_data == bd12780 || mid->driver_data == sq24905c)
config_read_fn = i2c_smbus_read_word_data;
else
config_read_fn = i2c_smbus_read_byte_data;
@@ -666,7 +671,8 @@ static int adm1275_probe(struct i2c_client *client)
PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
- ret = adm1275_enable_vout_temp(data, client, config);
+ ret = adm1275_enable_vout_temp(data, client, config,
+ ADM1278_PMON_DEFCONFIG);
if (ret)
return ret;
@@ -728,13 +734,45 @@ static int adm1275_probe(struct i2c_client *client)
PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
- ret = adm1275_enable_vout_temp(data, client, config);
+ ret = adm1275_enable_vout_temp(data, client, config,
+ ADM1278_PMON_DEFCONFIG);
if (ret)
return ret;
if (config & ADM1278_VIN_EN)
info->func[0] |= PMBUS_HAVE_VIN;
break;
+
+ /*
+ * The BD12780 is almost functionally identical with the adm1278 above.
+ * Only differences visible to the driver are lack of TSFILT bits and
+ * different identification register contents.
+ */
+ case bd12780:
+ data->have_vout = true;
+ data->have_pin_max = true;
+ data->have_temp_max = true;
+ data->have_power_sampling = true;
+
+ coefficients = adm1278_coefficients;
+ vindex = 0;
+ cindex = 1;
+ pindex = 2;
+ tindex = 3;
+
+ info->func[0] |= PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT |
+ PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
+ PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
+
+ ret = adm1275_enable_vout_temp(data, client, config,
+ BD12780_PMON_DEFCONFIG);
+ if (ret)
+ return ret;
+
+ if (config & ADM1278_VIN_EN)
+ info->func[0] |= PMBUS_HAVE_VIN;
+
+ break;
case adm1293:
case adm1294:
data->have_iout_min = true;
@@ -880,6 +918,7 @@ static const struct of_device_id adm1275_of_match[] = {
{ .compatible = "adi,adm1281", },
{ .compatible = "adi,adm1293", },
{ .compatible = "adi,adm1294", },
+ { .compatible = "rohm,bd12780", },
{ .compatible = "silergy,mc09c", },
{ }
};
--
2.54.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply related
* [PATCH v2 7/9] dt-bindings: adm1275: ROHM BD12790 hot-swap controller
From: Matti Vaittinen @ 2026-06-26 7:25 UTC (permalink / raw)
To: Matti Vaittinen, Matti Vaittinen, Matti Vaittinen
Cc: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jonathan Corbet, Shuah Khan, Wensheng Wang, Matti Vaittinen,
Ashish Yadav, Vasileios Amoiridis, Kim Seer Paller, ChiShih Tsai,
Chris Packham, Robert Coulson, linux-hwmon, devicetree,
linux-kernel, linux-doc
In-Reply-To: <cover.1782458224.git.mazziesaccount@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1581 bytes --]
From: Matti Vaittinen <mazziesaccount@gmail.com>
Support ROHM BD12790 hot-swap controller which is largely compatible
with the Analog Devices adm1272.
Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
Acked-by: Conor Dooley <conor.dooley@microchip.com>
---
Revision history:
v1 => :
- Fixed double space from description
---
Documentation/devicetree/bindings/hwmon/adi,adm1275.yaml | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/Documentation/devicetree/bindings/hwmon/adi,adm1275.yaml b/Documentation/devicetree/bindings/hwmon/adi,adm1275.yaml
index 503e93756584..283cd1662689 100644
--- a/Documentation/devicetree/bindings/hwmon/adi,adm1275.yaml
+++ b/Documentation/devicetree/bindings/hwmon/adi,adm1275.yaml
@@ -33,6 +33,9 @@ description: |
https://fscdn.rohm.com/en/products/databook/datasheet/ic/power/power_switch/bd12780muv-lb-e.pdf
https://fscdn.rohm.com/en/products/databook/datasheet/ic/power/power_switch/bd12780amuv-lb-e.pdf
+ The BD12790 is a ROHM hot-swap controller, functionally similar to the
+ ADM1272.
+
properties:
compatible:
oneOf:
@@ -47,6 +50,7 @@ properties:
- adi,adm1293
- adi,adm1294
- rohm,bd12780
+ - rohm,bd12790
- silergy,mc09c
# Require BD12780 as a fall-back for BD12780A.
@@ -103,6 +107,7 @@ allOf:
enum:
- adi,adm1272
- adi,adm1273
+ - rohm,bd12790
then:
properties:
adi,volt-curr-sample-average:
--
2.54.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply related
* [PATCH v2 8/9] doc: adm1275: Add ROHM BD12790
From: Matti Vaittinen @ 2026-06-26 7:25 UTC (permalink / raw)
To: Matti Vaittinen, Matti Vaittinen, Matti Vaittinen
Cc: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jonathan Corbet, Shuah Khan, Wensheng Wang, Matti Vaittinen,
Ashish Yadav, Vasileios Amoiridis, Kim Seer Paller, ChiShih Tsai,
Chris Packham, Robert Coulson, linux-hwmon, devicetree,
linux-kernel, linux-doc
In-Reply-To: <cover.1782458224.git.mazziesaccount@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 864 bytes --]
From: Matti Vaittinen <mazziesaccount@gmail.com>
Add the ROHM BD12790 to the list of the ICs supported by the adm1275
driver.
Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
---
Revision history:
v1 => :
- No changes
---
Documentation/hwmon/adm1275.rst | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/Documentation/hwmon/adm1275.rst b/Documentation/hwmon/adm1275.rst
index 8a793dd2b412..d8495be313b8 100644
--- a/Documentation/hwmon/adm1275.rst
+++ b/Documentation/hwmon/adm1275.rst
@@ -83,6 +83,14 @@ Supported chips:
Datasheet: https://fscdn.rohm.com/en/products/databook/datasheet/ic/power/power_switch/bd12780amuv-lb-e.pdf
+ * ROHM Semiconductor BD12790
+
+ Prefix: 'bd12790'
+
+ Addresses scanned: -
+
+ Datasheet: -
+
* Silergy SQ24905C
Prefix: 'mc09c'
--
2.54.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply related
* [PATCH v2 9/9] hwmon: adm1275: Support ROHM BD12790
From: Matti Vaittinen @ 2026-06-26 7:26 UTC (permalink / raw)
To: Matti Vaittinen, Matti Vaittinen, Matti Vaittinen
Cc: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jonathan Corbet, Shuah Khan, Wensheng Wang, Matti Vaittinen,
Ashish Yadav, Vasileios Amoiridis, Kim Seer Paller, ChiShih Tsai,
Chris Packham, Robert Coulson, linux-hwmon, devicetree,
linux-kernel, linux-doc
In-Reply-To: <cover.1782458224.git.mazziesaccount@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 7213 bytes --]
From: Matti Vaittinen <mazziesaccount@gmail.com>
Add support for ROHM BD12790 hot-swap controller which is largely
similar to Analog Devices adm1272.
The BD12790 uses the same selectable 60V/100V voltage ranges and
15mV/30mV current-sense ranges as the ADM1272, and the same VRANGE
(bit 5) and IRANGE (bit 0) layout in PMON_CONFIG. It therefore uses
a dedicated coefficient table that mirrors adm1272_coefficients, with
the following differences derived from BD12790 datasheet Table 1 (p.18):
- power 60V/30mV: m=17560 (vs. 17561)
- power 100V/30mV: m=10536 (vs. 10535)
- temperature: b=31880 (vs. 31871, reflecting T[11:0] = 4.2*T + 3188)
Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
Assisted-by: GitHub Copilot:claude-sonnet-4.6
---
Revision history:
v1 => v2: Changes mostly based on the review by Guenter:
- Add own 'case' for the bd12790.
- Don't use wildcard in the macro name.
- Fix the coefficient computation comment for power to take the
shunt-resistor scale into account.
---
drivers/hwmon/pmbus/Kconfig | 4 +-
drivers/hwmon/pmbus/adm1275.c | 79 +++++++++++++++++++++++++++++++++--
2 files changed, 78 insertions(+), 5 deletions(-)
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index b3c27f3b2712..6ebc01e26db3 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -52,8 +52,8 @@ config SENSORS_ADM1275
help
If you say yes here you get hardware monitoring support for Analog
Devices ADM1075, ADM1272, ADM1273, ADM1275, ADM1276, ADM1278, ADM1281,
- ADM1293, ADM1294, ROHM BD12780, and SQ24905C Hot-Swap Controller and
- Digital Power Monitors.
+ ADM1293, ADM1294, ROHM BD12780, ROHM BD12790, and SQ24905C
+ Hot-Swap Controller and Digital Power Monitors.
This driver can also be built as a module. If so, the module will
be called adm1275.
diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c
index 81c50fab7687..406b44852376 100644
--- a/drivers/hwmon/pmbus/adm1275.c
+++ b/drivers/hwmon/pmbus/adm1275.c
@@ -19,7 +19,7 @@
#include "pmbus.h"
enum chips { adm1075, adm1272, adm1273, adm1275, adm1276, adm1278, adm1281,
- adm1293, adm1294, bd12780, sq24905c };
+ adm1293, adm1294, bd12780, bd12790, sq24905c };
#define ADM1275_MFR_STATUS_IOUT_WARN2 BIT(0)
#define ADM1293_MFR_STATUS_VAUX_UV_WARN BIT(5)
@@ -47,7 +47,7 @@ enum chips { adm1075, adm1272, adm1273, adm1275, adm1276, adm1278, adm1281,
#define ADM1278_VOUT_EN BIT(1)
#define ADM1278_PMON_DEFCONFIG (ADM1278_VOUT_EN | ADM1278_TEMP1_EN | ADM1278_TSFILT)
-/* The BD12780 data sheets mark TSFILT bit as reserved. */
+/* The BD127[89]0 data sheets mark TSFILT bit as reserved. */
#define BD12780_PMON_DEFCONFIG (ADM1278_VOUT_EN | ADM1278_TEMP1_EN)
#define ADM1293_IRANGE_25 0
@@ -136,6 +136,30 @@ static const struct coefficients adm1272_coefficients[] = {
};
+/*
+ * BD12790 coefficients derived from preliminary datasheet, Table 1 (p.18)
+ * and the PMBus direct-format relationship X = (Y * 10^(-R) - b) / m.
+ *
+ * Voltage: V[V] = 14.77e-3 * code (60V) / 24.62e-3 * code (100V)
+ * -> m = 6770, R=-2 / m = 4062, R=-2
+ * Current: code = I[A] * RS * 132802.1 + 2048 (15mV) / * 66401.06 + 2048 (30mV)
+ * -> m = 1328, b = 2048 * 10^(-R) = 20480, R=-1 / m = 664, same b and R
+ * Power: code = k * RS * PIN, k = 35119.94 / 17559.97 / 21071.44 / 10535.72
+ * -> m = round(k / 10^(-3-R)), R=-2 for 60V/15mV, R=-3 for the other three
+ * Temperature: code = 4.2 * T + 3188 -> m = 42, b = 3188 * 10 = 31880, R=-1
+ */
+static const struct coefficients bd12790_coefficients[] = {
+ [0] = { 6770, 0, -2 }, /* voltage, vrange 60V */
+ [1] = { 4062, 0, -2 }, /* voltage, vrange 100V */
+ [2] = { 1328, 20480, -1 }, /* current, vsense range 15mV */
+ [3] = { 664, 20480, -1 }, /* current, vsense range 30mV */
+ [4] = { 3512, 0, -2 }, /* power, vrange 60V, irange 15mV */
+ [5] = { 21071, 0, -3 }, /* power, vrange 100V, irange 15mV */
+ [6] = { 17560, 0, -3 }, /* power, vrange 60V, irange 30mV */
+ [7] = { 10536, 0, -3 }, /* power, vrange 100V, irange 30mV */
+ [8] = { 42, 31880, -1 }, /* temperature */
+};
+
static const struct coefficients adm1275_coefficients[] = {
[0] = { 19199, 0, -2 }, /* voltage, vrange set */
[1] = { 6720, 0, -1 }, /* voltage, vrange not set */
@@ -490,6 +514,7 @@ static const struct i2c_device_id adm1275_id[] = {
{ "adm1293", adm1293 },
{ "adm1294", adm1294 },
{ "bd12780", bd12780 },
+ { "bd12790", bd12790 },
{ "mc09c", sq24905c },
{ }
};
@@ -567,7 +592,8 @@ static int adm1275_probe(struct i2c_client *client)
if (mid->driver_data == adm1272 || mid->driver_data == adm1273 ||
mid->driver_data == adm1278 || mid->driver_data == adm1281 ||
mid->driver_data == adm1293 || mid->driver_data == adm1294 ||
- mid->driver_data == bd12780 || mid->driver_data == sq24905c)
+ mid->driver_data == bd12780 || mid->driver_data == bd12790 ||
+ mid->driver_data == sq24905c)
config_read_fn = i2c_smbus_read_word_data;
else
config_read_fn = i2c_smbus_read_byte_data;
@@ -647,6 +673,7 @@ static int adm1275_probe(struct i2c_client *client)
data->have_power_sampling = true;
coefficients = adm1272_coefficients;
+
vindex = (config & ADM1275_VRANGE) ? 1 : 0;
cindex = (config & ADM1272_IRANGE) ? 3 : 2;
/* pindex depends on the combination of the above */
@@ -679,6 +706,51 @@ static int adm1275_probe(struct i2c_client *client)
if (config & ADM1278_VIN_EN)
info->func[0] |= PMBUS_HAVE_VIN;
break;
+
+ /*
+ * The BD12790 is almost identical to the adm1272. Only the defconfig
+ * and coefficients have minor differences.
+ */
+ case bd12790:
+ data->have_vout = true;
+ data->have_pin_max = true;
+ data->have_temp_max = true;
+ data->have_power_sampling = true;
+
+ coefficients = bd12790_coefficients;
+
+ vindex = (config & ADM1275_VRANGE) ? 1 : 0;
+ cindex = (config & ADM1272_IRANGE) ? 3 : 2;
+ /* pindex depends on the combination of the above */
+ switch (config & (ADM1275_VRANGE | ADM1272_IRANGE)) {
+ case 0:
+ default:
+ pindex = 4;
+ break;
+ case ADM1275_VRANGE:
+ pindex = 5;
+ break;
+ case ADM1272_IRANGE:
+ pindex = 6;
+ break;
+ case ADM1275_VRANGE | ADM1272_IRANGE:
+ pindex = 7;
+ break;
+ }
+ tindex = 8;
+
+ info->func[0] |= PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT |
+ PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
+ PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
+
+ ret = adm1275_enable_vout_temp(data, client, config,
+ BD12780_PMON_DEFCONFIG);
+ if (ret)
+ return ret;
+
+ if (config & ADM1278_VIN_EN)
+ info->func[0] |= PMBUS_HAVE_VIN;
+ break;
case adm1275:
if (device_config & ADM1275_IOUT_WARN2_SELECT)
data->have_oc_fault = true;
@@ -919,6 +991,7 @@ static const struct of_device_id adm1275_of_match[] = {
{ .compatible = "adi,adm1293", },
{ .compatible = "adi,adm1294", },
{ .compatible = "rohm,bd12780", },
+ { .compatible = "rohm,bd12790", },
{ .compatible = "silergy,mc09c", },
{ }
};
--
2.54.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply related
* Re: [PATCH] KVM: x86/xen: Add KVM_XEN_VCPU_ATTR_TYPE_WRITE_HYPERCALL_PAGE
From: David Woodhouse @ 2026-06-26 7:45 UTC (permalink / raw)
To: Sean Christopherson, Gerd Hoffmann
Cc: Paolo Bonzini, Jonathan Corbet, Shuah Khan, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
Paul Durrant, kvm, linux-doc, linux-kernel, linux-kselftest
In-Reply-To: <aj21KctIXuf7b_5G@google.com>
[-- Attachment #1: Type: text/plain, Size: 6198 bytes --]
On Thu, 2026-06-25 at 16:09 -0700, Sean Christopherson wrote:
> On Tue, Apr 28, 2026, David Woodhouse wrote:
> > From: David Woodhouse <dwmw@amazon.co.uk>
> >
> > Commit 3617c0ee7decb ("KVM: x86/xen: Only write Xen hypercall page for
> > guest writes to MSR") blocked host-initiated writes from triggering the
> > Xen hypercall page setup, to fix an SRCU usage violation when the
> > hypercall MSR index collides with a real MSR written during vCPU reset.
> >
> > However, some VMMs legitimately need to trigger hypercall page setup
> > from host context. For example, a VMM may intercept the guest's MSR
> > write to track an epoch (for kexec/crash recovery), and then replay the
> > write as a host-initiated KVM_SET_MSRS to populate the hypercall page.
> > The host_initiated check breaks this use case.
> >
> > Add KVM_XEN_VCPU_ATTR_TYPE_WRITE_HYPERCALL_PAGE as a new vcpu attribute
> > that explicitly invokes kvm_xen_write_hypercall_page() under proper
> > locking. This gives userspace a safe interface to trigger hypercall page
> > setup without going through the MSR write path, preserving the
> > host_initiated defence in depth while restoring the lost functionality.
>
> This is all kinda silly. Userspace provides KVM a blob, then userspace intercepts
> the MSR write that triggers doing something with said blob, only to call back into
> KVM to consume the blob that userspace provided in the first place.
>
> Any chance we can deprecate KVM's kvm_xen_write_hypercall_page(), and instead
> rely on userspace to fill the page? This extra bit obviously isn't much code to
> carry, but it's yet one more Xen thing to maintain, and we've accumulated a lot
> of those over the years...
We don't actually use the 'blob' mode. That was added in commit
ffde22ac53b6d in 2009 with a comment saying, "A generic mechanism to
delegate MSR writes to userspace seems overkill and risks encouraging
similar MSR abuse in the future. Thus this patch adds special support
for the Xen HVM MSR."
When João and I came along almost a decade later, in 23200b7a30de3
where we added hypercall interception support we said, "Since this
means KVM owns the ABI, dispense with the facility for the VMM to
provide its own copy of the hypercall pages; just fill them in directly
using VMCALL/VMMCALL as we do for the Hyper-V hypercall page."
I think we could probably rip out the blob mode without any fear of
breaking userspace. Even in 2018 I don't think we could even find the
alleged code from 2009 that used the old support. At least, not in
buildable and usable form?
> > diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c
> > index 91fd3673c09a..c16b4560c9e7 100644
> > --- a/arch/x86/kvm/xen.c
> > +++ b/arch/x86/kvm/xen.c
> > @@ -907,6 +907,13 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
> > {
> > int idx, r = -ENOENT;
> >
> > + /*
> > + * kvm_xen_write_hypercall_page() manages its own locking.
> > + * Handle it before taking xen_lock to avoid a deadlock.
>
> Do we actually want the side effects that necessitate taking xen.xen_lock? From
> a uAPI perspective, it's odd to effectively bundle KVM_XEN_ATTR_TYPE_LONG_MODE
> into KVM_XEN_VCPU_ATTR_TYPE_WRITE_HYPERCALL_PAGE.
That's *guest* ABI, and it's derived from Xen behaviour. Xen will
'latch' its idea of whether a guest VM is 32-bit or 64-bit, for the
purpose of shared data structures (shared_info page, vcpu_info,
runstate).
Xen latches this from the current mode of the running vCPU in *two*
places:
• When the hypercall MSR is invoked
• When the guest sets the event channel GSI (HVM_PARAM_CALLBACK_IRQ).
Thus far, the former has been handled in the kernel (in the code you're
looking at), while the latter is why we have the ioctl to explicitly
latch the guest's long_mode from userspace too, as userspace handles
the HVMOP_set_param calls.
> The other question is, why does kvm_xen_write_hypercall_page() drop xen_lock
> when writing guest memory? That seems odd and unnecessary.
Huh? It takes the lock to do the thing that needs the lock, then drops
it. That is not "odd and unnecessary" at all.
You've been spending too long with these scope-guarded locks. I *hate*
them. I hate the way they slowly spread around the whole kernel, making
every lock holder hold their locks for just a *little* bit longer than
they need to, slowly increasing lock contention "just a little bit; it
doesn't matter" at a time. I hate the way they stop us thinking about
which locks are needed and in which order, and make it unclear whether
some action in the tail of a function actually *needed* the lock, or
was just caught up in it as collateral damage.
> > + */
> > + if (data->type == KVM_XEN_VCPU_ATTR_TYPE_WRITE_HYPERCALL_PAGE)
> > + return kvm_xen_write_hypercall_page(vcpu, data->u.gpa) ? -EIO : 0;
>
> -EIO is rather weird, wouldn't -EINVAL be more appropriate? Ah, and both are
> wrong if copying the blob fails.
-EINVAL is more for "you asked me to do something that doesn't make
sense". -EIO is for "something went wrong when I tried".
Arguably, the thing that's most likely to go wrong is the
kvm_vcpu_write_guest() where it writes instructions[] to the guest, and
maybe that ought to be -EFAULT? But I'm not sure that's quite the right
semantic to return from the ioctl?
> > +
> > mutex_lock(&vcpu->kvm->arch.xen.xen_lock);
> > idx = srcu_read_lock(&vcpu->kvm->srcu);
>
> Speaking of writing memory, kvm_xen_write_hypercall_page() expects the caller
> to be in a read-side SRCU critical section (I didn't actually run this with
> PROVE_LOCKING=y, but I don't think I'm missing anything?)
Yes, good catch. Thanks.
> So, if this uAPI is unavoidable seems like we want something like the below.
> Either that or guard all of kvm_xen_write_hypercall_page() with a lock, and put
> the entire thing in a helper so that KVM_XEN_VCPU_ATTR_TYPE_WRITE_HYPERCALL_PAGE
> can be handled in a case-statement and doesn't need to grab SRCU on its own.
Makes sense (with the test, of course). Want me to put them together
and resend?
[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 5069 bytes --]
^ permalink raw reply
* Re: [PATCH v13 0/4] kunit: Add support for suppressing warning backtraces
From: patchwork-bot+linux-riscv @ 2026-06-26 8:21 UTC (permalink / raw)
To: Albert Esteve
Cc: linux-riscv, arnd, brendan.higgins, david, raemoar63,
maarten.lankhorst, mripard, tzimmermann, airlied, simona, corbet,
skhan, akpm, pjw, palmer, aou, alex, linux-kernel, linux-arch,
linux-kselftest, kunit-dev, dri-devel, workflows, linux-doc,
peterz, acarmina, linux, kees, lkft, mcanal, error27,
simona.vetter
In-Reply-To: <20260515-kunit_add_support-v13-0-18ee42f96e7b@redhat.com>
Hello:
This series was applied to riscv/linux.git (fixes)
by Shuah Khan <skhan@linuxfoundation.org>:
On Fri, 15 May 2026 14:29:31 +0200 you wrote:
> Some unit tests intentionally trigger warning backtraces by passing bad
> parameters to kernel API functions. Such unit tests typically check the
> return value from such calls, not the existence of the warning backtrace.
>
> Such intentionally generated warning backtraces are neither desirable
> nor useful for a number of reasons:
> - They can result in overlooked real problems.
> - A warning that suddenly starts to show up in unit tests needs to be
> investigated and has to be marked to be ignored, for example by
> adjusting filter scripts. Such filters are ad hoc because there is
> no real standard format for warnings. On top of that, such filter
> scripts would require constant maintenance.
>
> [...]
Here is the summary with links:
- [v13,1/4] bug/kunit: Core support for suppressing warning backtraces
(no matching commit)
- [v13,2/4] kunit: Add backtrace suppression self-tests
(no matching commit)
- [v13,3/4] drm: Suppress intentional warning backtraces in scaling unit tests
(no matching commit)
- [v13,4/4] kunit: Add documentation for warning backtrace suppression API
https://git.kernel.org/riscv/c/5c1553dd5db3
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH net-next] Documentation: networking: Add a test plan for ethtool pause validation
From: Maxime Chevallier @ 2026-06-26 8:33 UTC (permalink / raw)
To: Andrew Lunn
Cc: Jakub Kicinski, davem, Eric Dumazet, Paolo Abeni, Simon Horman,
Russell King, Heiner Kallweit, Jonathan Corbet, Shuah Khan,
Oleksij Rempel, Vladimir Oltean, Florian Fainelli,
thomas.petazzoni, netdev, linux-kernel, linux-doc
In-Reply-To: <58f37d6e-973b-4242-be82-0561ccdb1a6f@lunn.ch>
> Sphinx follows pythons object orientate structure. So you could have a
> class test_ethtool_pause_advertising, with class documentation. And
> then methods within the class which are individual tests. The
> commented out section would then be method documentation.
Good point, so maybe something along these lines :
- A class for the test
- methods for indivitual tests
- For readability, I've written what the internal test helper would look
like (_adv_test), and how a test would look like without the helper in
adv_rx_on_tx_on().
I'm already diving into coding, but it helps me a bit in the definition of the
"description" format :)
this is what the class would look like :
class test_ethtool_pause_advertising:
"""Pause advertisement
Validate that changing pause params through the ETHTOOL_MSG_PAUSE command
translates to a change in the advertised pause params, and that these
parameters are correct w.r.t the supported pause params and requested pause
params.
This exercises the .set_pauseparams() ethtool ops for MAC configuration,
as well as the reconfiguration of the PHY's advertising and negociation.
On non-phylink MACs, the MAC should call phy_set_sym_pause() to update the
PHY's advertising, and restart a negotiation with phy_start_aneg() if
need be. Failure to do so will result on the wrong advertising parameters.
Pn phylink-enabled MACs, phylink deals with the PHY reconfiguration provided
the MAC driver calls phylink_ethtool_set_pauseparam().
Failing this test likely means that the PHY driver is not correctly advertising
pause settings, either due to the MAC triggering a PHY reconfiguration,
a misconficonfiguration of the advertising registers by the PHY, or by
mis-handling the phydev->advertising bitfield in the PHY driver directly.
The validation is made by looking at the advertised modes locally, as well as
what the peer's 'lp_advertising' values report.
cfg -- local device's interface configuration
peer -- peer device handle
"""
def _adv_test(cfg, peer, rx, tx, adv, not_adv):
ret = cfg.run(f"ethtool -A ethX rx {rx} tx {tx} autoneg on")
ksft_eq(ret, 0)
linkmodes = cfg.get_advertising()
if adv:
ksft_in(adv, linkmodes, f"rx {rx} tx {tx} must advertise {adv}")
if not_adv:
ksft_not_in(not_adv, linkmodes, f"rx {rx} tx {tx} must not advertise {not_adv}")
remote_linkmodes = peer.get_lp_advertising()
if adv:
ksft_in(adv, linkmodes, f"PHY does not advertise {adv}")
if not_adv:
ksft_not_in(not_adv, linkmodes, f"PHY incorrectly advertises {not_adv}")
@ksft_ethtool_needs_supported_allof([Pause])
def adv_rx_on_tx_on(cfg, peer) -> None:
"""Advertising test with rx on tx on
- run 'ethtool -A ethX rx on tx on autoneg on'
- FAIL if the return isn't 0
- FAIL if ETHTOOL_A_LINKMODES_OURS's advertised values does not contain
"Pause" or contains "Asym_Pause"
- FAIL if peer's lp_advertising doesn't contain "Pause" or contains
"Asym_Pause"
- Succeed otherwise
"""
ret = cfg.run('ethtool -A ethX rx on tx on autoneg on')
ksft_eq(ret, 0)
linkmodes = cfg.get_advertising()
ksft_in('Pause', linkmodes, "rx on tx on must advertise Pause")
ksft_not_in('Asym_Pause', linkmodes, "rx on tx on must not advertise Asym_Pause")
remote_linkmodes = peer.get_lp_advertising()
ksft_in('Pause', linkmodes, "PHY does not advertise Pause")
ksft_not_in('Asym_Pause', linkmodes, "PHY incorrectly advertises Asym_Pause")
@ksft_ethtool_needs_supported_allof([Pause, Asym_Pause])
def adv_rx_on_tx_off(cfg, peer) -> None:
"""Advertising test with rx on tx off
- run 'ethtool -A ethX rx on tx off autoneg on'
- FAIL if the return isn't 0
- FAIL if ETHTOOL_A_LINKMODES_OURS's advertised values does not contain
"Pause" and "Asym_Pause"
- FAIL if peer's lp_advertising doesn't contain "Pause" and "Asym_Pause"
- Succeed otherwise
"""
_adv_test(cfg, peer, 'on', 'off', ["Pause", "Asym_Pause"], [])
@ksft_ethtool_needs_supported_allof([Asym_Pause])
def adv_rx_off_tx_on(cfg, peer) -> None:
"""Advertising test with rx off tx on
- run 'ethtool -A ethX rx off tx on autoneg on'
- FAIL if the return isn't 0
- FAIL if ETHTOOL_A_LINKMODES_OURS's advertised values does not contain
"Asym_Pause" or contains "Pause"
- FAIL if peer's lp_advertising doesn't contain "Pause" and "Asym_Pause"
- Succeed otherwise
"""
_adv_test(cfg, peer, 'off', 'on', ["Asym_Pause"], ["Pause"])
Maxime
^ permalink raw reply
* Re: [PATCH v4 2/2] tracing: Remove trace_printk.h from kernel.h
From: Steven Rostedt @ 2026-06-26 8:51 UTC (permalink / raw)
To: Nathan Chancellor
Cc: linux-kernel, linux-trace-kernel, Masami Hiramatsu, Mark Rutland,
Mathieu Desnoyers, Andrew Morton, Linus Torvalds,
Sebastian Andrzej Siewior, John Ogness, Thomas Gleixner,
Peter Zijlstra, Julia Lawall, Yury Norov, linux-doc, linux-kbuild,
linuxppc-dev, dri-devel, linux-stm32, linux-arm-kernel,
linux-rdma, linux-usb, linux-ext4, linux-nfs, kvm, intel-gfx
In-Reply-To: <20260625234158.GA261868@ax162>
On Thu, 25 Jun 2026 16:41:58 -0700
Nathan Chancellor <nathan@kernel.org> wrote:
> The following diff resolves it for me, should I send it as a separate
> patch or do you want to just fold it in with a note?
>
> diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h
> index 621566345406..2301a701ffbb 100644
> --- a/include/linux/lockdep.h
> +++ b/include/linux/lockdep.h
> @@ -10,6 +10,7 @@
> #ifndef __LINUX_LOCKDEP_H
> #define __LINUX_LOCKDEP_H
>
> +#include <linux/instruction_pointer.h>
Ah, so the reason for this breakage is because lockdep was relying on
instruction_pointer.h, that just happened to be included in kernel.h
via trace_printk.h.
This is a separate issue, so it should be a separate patch. I'll add it
as patch 1 of this series.
Can you send me the config you used. This didn't trigger in my tests.
Thanks,
-- Steve
> #include <linux/lockdep_types.h>
> #include <linux/smp.h>
> #include <asm/percpu.h>
^ permalink raw reply
* Re: [PATCH] Docs: SMP: add an SMP docbook chapter
From: Thomas Gleixner @ 2026-06-26 9:04 UTC (permalink / raw)
To: Randy Dunlap, linux-kernel
Cc: Randy Dunlap, Jonathan Corbet, Shuah Khan, linux-doc,
Peter Zijlstra, Andrew Morton
In-Reply-To: <20260625210331.1050915-1-rdunlap@infradead.org>
TOn Thu, Jun 25 2026 at 14:03, Randy Dunlap wrote:
> Add SMP primitives to the core-api documentation.
>
> Signed-off-by: Randy Dunlap <rdunlap@infradead.org>
Acked-by: Thomas Gleixner <tglx@kernel.org>
^ permalink raw reply
* Re: [PATCH v3 00/11] vfio/pci: Add CXL Type-2 device passthrough support
From: Richard Cheng @ 2026-06-26 9:16 UTC (permalink / raw)
To: mhonap
Cc: djbw, alex, jgg, jic23, dave.jiang, ankita,
alejandro.lucero-palau, alison.schofield, dave, dmatlack, gourry,
ira.weiny, cjia, kjaju, vsethi, zhiw, kvm, linux-cxl, linux-doc,
linux-kernel, linux-kselftest
In-Reply-To: <20260625165407.1769572-1-mhonap@nvidia.com>
On Thu, Jun 25, 2026 at 10:23:56PM +0800, mhonap@nvidia.com wrote:
> From: Manish Honap <mhonap@nvidia.com>
>
> CXL Type-2 accelerators (CXL.mem-capable GPUs and similar) cannot be
> passed through to virtual machines with stock vfio-pci because the
> driver has no concept of HDM decoder management, HDM region exposure,
> or component register virtualization. This series adds those three
> pieces, sufficient for a guest to use the device's firmware-committed
> coherent memory under UVM / ATS.
>
> v3 is a rewrite of the v2 framework form, responding to Dan's request
> in the v2 review for "less emulation, narrower interfaces, and a
> closer mapping to the spec language."
> In this release, cxl-core exposes four EXPORT_SYMBOL_GPL helpers behind
> an opaque handle. vfio-pci becomes a thin transport on top of those.
> Please see "Changes since v2" and "Reviewer feedback addressed" below for
> the per-area summary.
>
Hi Manish,
Thanks for the work, I ran some test with your patches applied on a real
CXL type-2 device, it's a GPU with a FW-committed HDM decoder. I want to
report the result early, the acquire path works, but the first CPU access
to the mapped HDM region crash the host.
So device BDF is 0002:81:00.0 , with CXLCtl: Cache+ IO+ Mem+, HDM decoder firmware-committed.
Binding the device to vfio-pci brought the CXL Type-2 path up cleanly
"""
# modprobe vfio-pci
# echo vfio-pci > /sys/bus/pci/devices/0002:81:00.0/driver_override
# echo 0002:81:00.0 > /sys/bus/pci/drivers_probe
"""
A meme0/endpoint19/region1 appeared, and selftest device_is_cxl() passed.
When running the 9th patch's selftest
"""
# sudo ./vfio_cxl_type2_test 0002:81:00.0
ok 1 cxl_type2.device_is_cxl
# RUN cxl_type2.hdm_region_mmap_rw
"""
At this point, the machine hung and crash.
hdm_region_mmap_rw mmaps the HDM region and does a CPU read/write to it. That =
access never returned. I couldn't capture dmesg or trace before it crashed.
I'm not sure if this is a platform/FW issue or something in how the region
is mapped.
Have you exercised hdm_region_mmap_rw() against your machine? or only cxl_test mock?
If a guest can hang the host just by touching its mapped memory, it needs to be fixed.
Best regards,
Richard Cheng.
> Motivation
> ==========
>
> A CXL Type-2 device exposes its HDM-mapped device memory through HDM
> decoders that BIOS programs and commits at boot. To pass such a
> device to a guest, vfio-pci has to do three things at once:
>
> 1. Surface the firmware-committed HDM-mapped HPA range as a guest-
> mmappable region.
>
> 2. Surface a CXL-spec-compliant view of the CXL Device DVSEC body,
> the HDM Decoder Capability block, and the CXL.cache/mem cap-array
> prefix, so the guest's CXL driver enumerates the same topology
> the host saw.
>
> 3. Keep the host's committed decoder configuration intact (the
> physical decoder is never reprogrammed) while letting the guest
> observe and manage a shadow that follows the per-field write
> semantics in the spec.
>
> The series builds on Alejandro Lucero-Palau's v28 work
> applied on for-7.3/cxl-type2-enabling [1] (sfc is the in-tree consumer
> today). vfio-pci becomes the second consumer.
>
> Architecture
> ============
>
> cxl-core owns the CXL semantics. A new file
> drivers/cxl/core/passthrough.c (gated by hidden Kconfig
> CXL_VFIO_PASSTHROUGH) provides four exported symbols:
>
> struct cxl_passthrough *
> devm_cxl_passthrough_create(struct device *dev,
> struct cxl_dev_state *cxlds);
>
> int cxl_passthrough_dvsec_rw(p, off, val, sz, write);
> int cxl_passthrough_hdm_rw (p, off, val, write);
> int cxl_passthrough_cm_rw (p, off, val, write);
>
> cxl_passthrough is an opaque handle; vfio-pci sees no cxl-internal
> struct pointers. The shadows are snapshotted at create time: the
> DVSEC body from PCI config space dword by dword, the CM cap-array and
> HDM block from the cxl-core MMIO mapping at cxlds->reg_map.base.
> Per-field write semantics follow below:
> CXL r4.0 8.1.3 DVSEC:
> - LOCK is RWO,
> - CONTROL/CONTROL2 are RWL gated on CONFIG_LOCK,
> - STATUS/STATUS2 are RW1C,
> - RANGE1 is HwInit, RANGE2 is RsvdZ
> CXL r4.0 8.2.4.20 HDM:
> - GLOBAL_CTRL RW,
> - decoder CTRL implements COMMIT/COMMITTED,
> - decoder BASE/SIZE RWL gated on COMMITTED or LOCK_ON_COMMIT,
> - cap header HwInit).
>
> vfio-pci becomes a thin transport. The new module
> drivers/vfio/pci/cxl/ exposes two VFIO regions.
>
> VFIO_REGION_SUBTYPE_CXL (HDM region): mmappable view of the
> HDM-mapped HPA. The mmap fault handler calls vmf_insert_pfn() from
> the physical HPA. pread/pwrite go through the memremap_wb() kva
> captured at bind time.
>
> VFIO_REGION_SUBTYPE_CXL_COMP_REGS (component register shadow):
> pread/pwrite only, dword-aligned (-EINVAL on misalignment).
> Each dword dispatches by offset to cxl_passthrough_cm_rw() or
> cxl_passthrough_hdm_rw(). No shadow state on the vfio side; cxl-core
> enforces the spec.
>
> CXL DVSEC config-space accesses use a clipping shim in
> vfio_pci_config_rw_single(). A config-space chunk that crosses the
> DVSEC body boundary is split: header bytes go through the generic
> perm-bits path, body bytes go through cxl_passthrough_dvsec_rw().
> The shim replaces v2's approach of repointing ecap_perms[]
>
> Sparse-mmap is exposed on the component BAR so userspace can mmap the
> non-component portions directly; only the CXL component register
> sub-range goes through pread/pwrite emulation. The CXL sub-range is
> also skipped from vfio_pci-core's request_selected_regions() set
> because cxl-core's devm_cxl_probe_mem() already holds a
> request_mem_region() on it; the asymmetric skip is matched by an
> asymmetric release on disable().
>
> Scope and out-of-scope
> ======================
>
> In scope (rejected at create time with -EOPNOTSUPP otherwise):
>
> - Firmware-committed devices (HOST_FIRMWARE_COMMITTED set).
> - Single HDM decoder (hdm_count == 1).
> - No interleave (IW == 0).
>
> Out of scope, deferred for follow-on work:
>
> - Multi-decoder devices and interleave.
> - Guest-driven (non-firmware-committed) HDM commit.
> - Hotplug, FLR, and sibling-function reset of CXL Type-2 devices.
>
> Changes since v2
> ================
>
> This is a rewrite, not an incremental update. The structure of the
> series changed (20 patches in v2 to 11 in v3) because v3 collapses
> v2 patches 9-15 (detection, HDM emulation, media readiness, region
> management, HDM region, DVSEC emulation) into one cxl-core helper
> file and one vfio-pci consumer.
>
> Framework replaced by narrow opaque-handle helpers (patches 6, 8)
>
> v2 carried a generic register-emulation framework split across four
> state-machine files in cxl-core.
> v3 collapses it into one file: drivers/cxl/core/passthrough.c
> exposing the four EXPORT_SYMBOL_GPL helpers above behind a struct
> cxl_passthrough opaque handle.
>
> Shadow ownership moved into cxl-core (patches 6, 8)
>
> vfio-pci no longer keeps any per-field state. It forwards
> (offset, value) into cxl-core, and cxl-core enforces the spec
> (RWO, RWL, RW1C, HwInit, RsvdZ) with explicit CXL r4.0 section
> references in the switch arms.
>
> DVSEC config-space clipping shim (patch 8)
>
> v2 repointed ecap_perms[] to redirect CXL DVSEC reads and writes.
> v3 keeps ecap_perms[] untouched and clips per-config-access chunks
> at the DVSEC body boundary in vfio_pci_config_rw_single(); header bytes
> go through the generic perm-bits path, body bytes go through
> cxl_passthrough_dvsec_rw(). The shim is local to the per-device
> path.
>
> CONFIG_VFIO_PCI_CXL gates the new module (patch 7)
>
> v2 had a CONFIG_VFIO_CXL_CORE Kconfig stub; v3 renames it to
> CONFIG_VFIO_PCI_CXL to match the vfio-pci naming convention.
> The hidden CXL_VFIO_PASSTHROUGH selects the cxl-core helper file
> on demand. With both disabled, the cxl-core size is unchanged.
>
> UAPI rewritten with named fields (patch 5)
>
> vfio_device_info_cap_cxl in v3 carries:
> flags + HOST_FIRMWARE_COMMITTED bit
> hdm_region_idx
> comp_reg_region_idx
> comp_reg_bar
> comp_reg_offset
> comp_reg_size
> The DPA terminology is renamed to HDM region throughout.
> CACHE_CAPABLE (HDM-DB indicator) is dropped;
> it was informational only in v2 with no caller, and re-adding it
> for an active CXL.cache plumbing series later.
>
> Selftests trimmed (patch 9)
>
> v2 carried selftests for device detection, capability parsing,
> region enumeration, HDM register emulation, HDM mmap with
> page-fault insertion, FLR invalidation, and DVSEC register
> emulation. v3 keeps a smoke-test set of six focused tests:
>
> device_is_cxl GET_INFO advertises FLAGS_CXL
> and a populated CAP_CXL.
> hdm_region_mmap_rw mmap one page, write+read back.
> component_bar_sparse_mmap SPARSE_MMAP cap excludes the
> CXL component register sub-range.
> comp_regs_cm_cap_array_read pread of the CM cap-array
> header at CXL_CM_OFFSET succeeds
> (CAP_ID == 1).
> dvsec_lock_byte_read pread of the DVSEC CONFIG_LOCK
> byte through the clipping shim
> succeeds.
> hdm_decoder_commit_fsm COMMIT / COMMITTED state machine
> and LOCK_ON_COMMIT behaviour.
>
> FLR invalidation, page-fault insertion under load, and full
> DVSEC field-by-field write coverage are deferred to a follow-on
> selftest series. The current six are the minimal set that
> exercises the kernel-side contract end-to-end.
>
> cxl-core prep patches split (patches 1-4)
>
> v3 keeps the cxl-side enablers from v2 patches 1-4 but each as
> a standalone change so the cxl maintainer can review the helper
> API independently of the vfio consumer:
>
> [1/11] cxl_get_hdm_info()
> [2/11] cxl_await_range_active() split from media-ready wait
> [3/11] cxl_register_map records BIR + BAR offset
> [4/11] component/HDM register defines moved to uapi/cxl/cxl_regs.h
>
> Reviewer feedback addressed
> ===========================
>
> Dan
> ---
>
> - VFIO exposes HDM/host-visible region, not raw DPA; docs/UAPI say HDM
> region, DPA only inside cxl-core where appropriate.
> - One vfio-pci device = one HDM region / one decoder, no interleave;
> hdm_count != 1 → -EOPNOTSUPP.
> - Global HDM on DVSEC Range Base treated as legacy; RANGE1/RANGE2
> read-only snapshot, guest writes dropped.
> - No guest/kernel lock games; DVSEC LOCK and HDM LOCK_ON_COMMIT RWO,
> fixed at create from firmware snapshot.
> - Opaque cxl_passthrough handle only; vfio gets HPA via memdev probe +
> layout via cxl_get_hdm_info(), rw via helpers.
> - No multi-region accelerator case in v3; single region enforced,
> multi-region deferred.
> - cxl_await_range_active stays in cxl-core probe; not exported, vfio does
> not call it.
> - No guest LOCK→0 reprogram; guest cannot clear LOCK to remap host HPA;
> kernel uncommit tied to COMMIT, not LOCK alone.
>
> Jason / Gregory / Dan
> ---------------------
>
> - memremap(WB) + request_mem_region on HPA; conflicting direct-map/EFI use
> fails probe with -EBUSY.
>
> Jonathan
> --------
>
> - uapi/cxl/cxl_regs.h for register defines so VMMs need no private
> kernel headers.
> - __free() locals on cxl-core/passthrough error paths instead of
> struct-owned temporaries.
> - No "precommitted at probe" assumption; acquire checks COMMITTED in
> HDM shadow and refuses if missing.
>
> Dave
> ----
>
> - memremap(MEMREMAP_WB) for HDM host mapping (not ioremap_cache).
> - Renamed cap flag to VFIO_CXL_CAP_HOST_FIRMWARE_COMMITTED for clarity.
> - __free() / DEFINE_FREE() cleanup in new passthrough.c create path.
>
> Patch series
> ============
>
> [1/11] cxl: Add cxl_get_hdm_info() helper for HDM decoder metadata
> [2/11] cxl: Split cxl_await_range_active() from media-ready wait
> [3/11] cxl: Record BIR and BAR offset in cxl_register_map
> [4/11] cxl: Move component/HDM register defines to
> uapi/cxl/cxl_regs.h
> [5/11] vfio: UAPI for CXL Type-2 device passthrough
> [6/11] cxl: Add register-virtualization helpers for vfio Type-2
> passthrough
> [7/11] vfio/pci: Add CONFIG_VFIO_PCI_CXL with bind-time CXL Type-2
> acquisition
> [8/11] vfio/pci/cxl: Add HDM + COMP_REGS regions and DVSEC clipping
> shim
> [9/11] selftests/vfio: Add CXL Type-2 device passthrough smoke test
> [10/11] docs: vfio-pci: Document CXL Type-2 device passthrough
> [11/11] vfio/pci: Provide opt-out for CXL Type-2 extensions
>
> Dependencies
> ============
>
> [1] [PATCH v28 0/5] Type2 device basic support
> https://lore.kernel.org/linux-cxl/20260618181806.118745-1-alejandro.lucero-palau@amd.com/
>
> [2] Previous version of this patch series
> [PATCH v2 00/20] vfio/pci: Add CXL Type-2 device passthrough support
> https://lore.kernel.org/linux-cxl/20260401143917.108413-1-mhonap@nvidia.com/
>
> [3] Companion QEMU series
> [RFC 0/9] QEMU: CXL Type-2 device passthrough via vfio-pci
> https://lore.kernel.org/linux-cxl/20260427181235.3003865-1-mhonap@nvidia.com/
>
> Manish Honap (11):
> cxl: Add cxl_get_hdm_info() helper for HDM decoder metadata
> cxl: Split cxl_await_range_active() from media-ready wait
> cxl: Record BIR and BAR offset in cxl_register_map
> cxl: Move component/HDM register defines to uapi/cxl/cxl_regs.h
> vfio: UAPI for CXL Type-2 device passthrough
> cxl: Add register-virtualization helpers for vfio Type-2 passthrough
> vfio/pci: Add CONFIG_VFIO_PCI_CXL with bind-time CXL Type-2
> acquisition
> vfio/pci/cxl: Add HDM + COMP_REGS regions and DVSEC clipping shim
> selftests/vfio: Add CXL Type-2 device passthrough smoke test
> docs: vfio-pci: Document CXL Type-2 device passthrough
> vfio/pci: Provide opt-out for CXL Type-2 extensions
>
> Documentation/driver-api/index.rst | 1 +
> Documentation/driver-api/vfio-pci-cxl.rst | 282 ++++++
> drivers/cxl/Kconfig | 7 +
> drivers/cxl/core/Makefile | 1 +
> drivers/cxl/core/passthrough.c | 590 ++++++++++++
> drivers/cxl/core/pci.c | 70 +-
> drivers/cxl/core/regs.c | 35 +
> drivers/cxl/cxl.h | 52 +-
> drivers/vfio/pci/Kconfig | 2 +
> drivers/vfio/pci/Makefile | 1 +
> drivers/vfio/pci/cxl/Kconfig | 34 +
> drivers/vfio/pci/cxl/Makefile | 2 +
> drivers/vfio/pci/cxl/vfio_cxl_core.c | 889 ++++++++++++++++++
> drivers/vfio/pci/cxl/vfio_cxl_priv.h | 71 ++
> drivers/vfio/pci/vfio_pci.c | 9 +
> drivers/vfio/pci/vfio_pci_config.c | 31 +
> drivers/vfio/pci/vfio_pci_core.c | 68 +-
> drivers/vfio/pci/vfio_pci_priv.h | 93 ++
> drivers/vfio/pci/vfio_pci_rdwr.c | 17 +
> include/cxl/cxl.h | 18 +
> include/cxl/passthrough.h | 121 +++
> include/linux/vfio_pci_core.h | 8 +
> include/uapi/cxl/cxl_regs.h | 63 ++
> include/uapi/linux/vfio.h | 46 +
> tools/testing/selftests/vfio/Makefile | 1 +
> .../selftests/vfio/lib/vfio_pci_device.c | 11 +-
> .../selftests/vfio/vfio_cxl_type2_test.c | 350 +++++++
> 27 files changed, 2821 insertions(+), 52 deletions(-)
> create mode 100644 Documentation/driver-api/vfio-pci-cxl.rst
> create mode 100644 drivers/cxl/core/passthrough.c
> create mode 100644 drivers/vfio/pci/cxl/Kconfig
> create mode 100644 drivers/vfio/pci/cxl/Makefile
> create mode 100644 drivers/vfio/pci/cxl/vfio_cxl_core.c
> create mode 100644 drivers/vfio/pci/cxl/vfio_cxl_priv.h
> create mode 100644 include/cxl/passthrough.h
> create mode 100644 include/uapi/cxl/cxl_regs.h
> create mode 100644 tools/testing/selftests/vfio/vfio_cxl_type2_test.c
>
> base-commit: 90cf2e0d702c8a132ccbe72e7687f33c04c14658
> --
> 2.25.1
>
>
^ permalink raw reply
* Re: [PATCH v5 13/24] virt/steal_monitor: Add documentation
From: Peter Zijlstra @ 2026-06-26 9:28 UTC (permalink / raw)
To: Shrikanth Hegde
Cc: linux-kernel, mingo, juri.lelli, vincent.guittot, yury.norov,
kprateek.nayak, iii, corbet, tglx, gregkh, pbonzini, seanjc,
vschneid, huschle, rostedt, dietmar.eggemann, maddy, srikar,
hdanton, chleroy, vineeth, frederic, arighi, pauld,
christian.loehle, tj, tommaso.cucinotta, maz, rafael, rdunlap,
kernellwp, linux-doc
In-Reply-To: <20260625124648.802832-14-sshegde@linux.ibm.com>
On Thu, Jun 25, 2026 at 06:16:37PM +0530, Shrikanth Hegde wrote:
> +Core idea:
> +==========
> +steal time is an indication available today in Guest which shows contention
> +for underlying physical CPU. Use it as a hint in the guest to fold the
> +workload to a reduced set of vCPUs. When there is contention, steal time
> +will show up in all the guests. When each guest honors the hint and folds
> +the workload to a smaller set of vCPUs(Preferred CPUs), it reduces the
> +contention and thereby reduces vCPU preemption.
> +This is achieved without any cross-guest communication.
> +
> +Steal monitor driver effectively does:
> +
> +1. Periodically computes steal time across the system.
> +
> +2. If steal time is greater than high threshold, reduce the number of
> + preferred CPUs by 1 core. Ensure at least one core is left always.
> + This avoids running into extreme cases.
> +
> +3. If steal time is lower or equal to low threshold, increase the
> + number of preferred CPUs by 1 core. If preferred is same as active,
> + nothing to be done.
> +
> +4. Ensure preferred CPUs is always subset of active CPUs.
> + On feature disable it is same as active CPUs.
So this is very much a co-operative scheme. Perhaps add a few words to
describe the effect of a non cooperative guest. IIRC the result is not
worse than the status quo. That is, if one (or more) guests refuse to
co-operate it will not make things worse, it will just not result in
improvements, right?
^ permalink raw reply
* Re: [PATCH v4 2/5] mm/zswap: Factor writeback loop out of shrink_worker()
From: Hao Jia @ 2026-06-26 9:34 UTC (permalink / raw)
To: Yosry Ahmed, nphamcs
Cc: akpm, tj, hannes, shakeel.butt, mhocko, mkoutny, chengming.zhou,
muchun.song, roman.gushchin, linux-mm, linux-kernel, linux-doc,
Hao Jia
In-Reply-To: <CAO9r8zOYgjbuG5i+LrCcMK764nVpOS+muo-5Q45ZFdiVus-dTA@mail.gmail.com>
On 2026/6/26 01:59, Yosry Ahmed wrote:
>>>> static long zswap_shrink_one(struct mem_cgroup *memcg,
>>>> struct zswap_shrink_state *s)
>>>> {
>>>> long shrunk;
>>>>
>>>> shrunk = shrink_memcg(memcg, NR_ZSWAP_WB_BATCH);
>>>> if (shrunk == -ENOENT)
>>>> return 0;
>>>>
>>>> s->attempts++;
>>>> if (shrunk <= 0 && ++s->failures == MAX_RECLAIM_RETRIES)
>>>> s->stop = true;
>>>
>>> Do we need 'stop' or can we just return a value here to indicate that
>>> we should stop (e.g. -EBUSY)?
>>>
>>
>> Perhaps we could return -EAGAIN instead of -EBUSY? This would align with
>> the semantics of the memory.reclaim interface, which returns -EAGAIN
>> when it reclaims fewer bytes than requested.
>
> Hmm but -EAGAIN tells the caller to try again, while here -EAGAIN
> tells the caller *not* to try again because we exhausted all retries?
>
Okay, let's go with -EBUSY.
>>>
>>> I think splitting the shrink/retry logic over 2 functions makes it
>>> more difficult to follow, so yeah I think fold
>>> zswap_shrink_no_candidate() into zswap_shrink_one(). Then the callers
>>> only need to iterate memcgs (depending on the context) and call
>>> zswap_shrink_one() for each of them.
>>
>> So, something like this?
>
> Yeah, something like this :)
>
>> /* Track progress of a memcg-tree writeback walk. */
>> struct zswap_shrink_state {
>> int attempts;
>
> While at it, I think "attempts" is really the number of scans, right?
> Should we rename it? Maybe "scans" or similar?
>
>> int failures;
>> };
>>
>> /*
>> * Take one step of a memcg-tree writeback walk driven by the caller's
>> * iterator, and fold the result into @s, the retry bookkeeping shared
>> * across steps. @memcg is the iterator's current memcg, or NULL once
>> * it has wrapped around after a full pass over the tree.
>> *
>> * The function returns -EAGAIN to signal the caller to abort the walk
>> * after encountering the following conditions MAX_RECLAIM_RETRIES times:
>> * - No writeback-candidate memcgs were found in a memcg tree walk.
>> * - Shrinking a writeback-candidate memcg failed.
>
> Orthogonal to this patch, but I wonder if this can be simplified. I
> wonder if these two conditions can be replaced with "shrinking a memcg
> that has zswap entries failed". The "no writeback-candidate memcgs in
> the tree" case seems like we should abort right away instead of
> retrying?
>
> Nhat, WDYT?
>
Perhaps something like the following is what you had in mind? I've
drafted the implementation below to make it easier for Nhat to compare
with the previous behavior.
>> *
>> * Return: The number of compressed bytes written back (>= 0), or -EAGAIN
>> * once the retry budget is exhausted and the caller should abort the walk.
>> */
>> static long zswap_shrink_one(struct mem_cgroup *memcg,
>
> Nit: zswap_shrink_one_memcg()
>
> BTW, the existing writeback logic has been broken for a while now when
> memcg is disabled. I think we constantly hit the !memcg case and run
> out of retries. Not sure if your patch changes this in any way, or if
> you want to fix that while you're at it :)
Yes, I'd be happy to do that. However, would it be better to submit a
separate fix patch or combine it with this one?
>
>> struct zswap_shrink_state *s)
>> {
>> long shrunk;
>>
>> /*
>> * If the iterator has completed a full pass, update the shrink state
>> * and check whether we should keep going.
>> */
>> if (!memcg) {
>> /*
>> * Continue shrinking without incrementing failures if we found
>> * candidate memcgs in the last tree walk.
>> */
>> if (!s->attempts && ++s->failures == MAX_RECLAIM_RETRIES)
>> return -EAGAIN;
>> s->attempts = 0;
>> return 0;
>> }
>>
>> shrunk = shrink_memcg(memcg, NR_ZSWAP_WB_BATCH);
>>
>> /*
>> * There are no writeback-candidate pages in the memcg. This is not an
>> * issue as long as we can find another memcg with pages in zswap. Skip
>> * this without incrementing attempts and failures.
>> */
>> if (shrunk == -ENOENT)
>> return 0;
>> s->attempts++;
>>
>> if (shrunk <= 0 && ++s->failures == MAX_RECLAIM_RETRIES)
>> return -EAGAIN;
>>
>> return shrunk;
>> }
>>
>> static void shrink_worker(struct work_struct *w)
>> {
>> struct zswap_shrink_state s = {};
>> unsigned long thr;
>>
>> /* Reclaim down to the accept threshold */
>> thr = zswap_accept_thr_pages();
>>
>> while (zswap_total_pages() > thr) {
>> struct mem_cgroup *memcg;
>> long ret;
>>
>> cond_resched();
>>
>> memcg = zswap_iter_global();
>
> Do we still need this helper? Or should we just keep the memcg
> iteration open-coded?
Done.
>
>> ret = zswap_shrink_one(memcg, &s);
>> /* drop the extra reference taken by zswap_iter_global() */
>> mem_cgroup_put(memcg);
>> if (ret == -EAGAIN)
>> break;
>> }
>> }
/* Track progress of a memcg-tree writeback walk. */
struct zswap_shrink_state {
int scans;
int failures;
};
/*
* Take one step of a memcg-tree writeback walk driven by the caller's
* iterator, and fold the result into @s, the retry bookkeeping shared
* across steps. @memcg is the iterator's current memcg, or NULL once
* it has wrapped around after a full pass over the tree.
*
* The function returns -EBUSY to signal the caller to abort the walk when
* either of the following occurs:
* - A full pass over the tree found no writeback-candidate memcg.
* - Shrinking a writeback-candidate memcg failed MAX_RECLAIM_RETRIES
times.
*
* When memory cgroup is disabled, the iterator always yields NULL. All
* zswap entries then live on the root list_lru, so NULL is treated as the
* root memcg and shrunk directly rather than as a completed tree pass.
*
* Return: The number of compressed bytes written back (>= 0), or -EBUSY
* when the caller should abort the walk.
*/
static long zswap_shrink_one_memcg(struct mem_cgroup *memcg,
struct zswap_shrink_state *s)
{
bool disabled = mem_cgroup_disabled();
long shrunk;
/*
* If the iterator has completed a full pass, update the shrink state
* and check whether we should keep going.
* With memcg disabled the iterator always yields NULL, so fall through
* and shrink the root memcg directly instead.
*/
if (!memcg && !disabled) {
/*
* Abort if no writeback-candidate memcgs in the last tree walk.
* Otherwise reset the scans count and continue.
*/
if (!s->scans)
return -EBUSY;
s->scans = 0;
return 0;
}
shrunk = shrink_memcg(memcg, NR_ZSWAP_WB_BATCH);
/*
* There are no writeback-candidate pages in the memcg. With memcg
* enabled this is not an issue as long as we can find another memcg
* with pages in zswap, so skip without counting it as a candidate.
* With memcg disabled the root LRU is the only target, so we should
* abort if it has no writeback-candidate pages.
*/
if (shrunk == -ENOENT)
return disabled ? -EBUSY : 0;
s->scans++;
if (shrunk <= 0 && ++s->failures == MAX_RECLAIM_RETRIES)
return -EBUSY;
return shrunk;
}
static void shrink_worker(struct work_struct *w)
{
struct zswap_shrink_state s = {};
unsigned long thr;
/* Reclaim down to the accept threshold */
thr = zswap_accept_thr_pages();
/*
* Global reclaim will select cgroup in a round-robin fashion from all
* online memcgs, but memcgs that have no pages in zswap and
* writeback-disabled memcgs (memory.zswap.writeback=0) are not
* candidates for shrinking.
*
* We save iteration cursor memcg into zswap_next_shrink,
* which can be modified by the offline memcg cleaner
* zswap_memcg_offline_cleanup().
*
* Since the offline cleaner is called only once, we cannot leave an
* offline memcg reference in zswap_next_shrink.
* We can rely on the cleaner only if we get online memcg under lock.
*
* If we get an offline memcg, we cannot determine if the cleaner has
* already been called or will be called later. We must put back the
* reference before returning from this function. Otherwise, the
* offline memcg left in zswap_next_shrink will hold the reference
* until the next run of shrink_worker().
*/
while (zswap_total_pages() > thr) {
struct mem_cgroup *memcg;
long ret;
cond_resched();
/*
* Start shrinking from the next memcg after zswap_next_shrink.
* When the offline cleaner has already advanced the cursor,
* advancing the cursor here overlooks one memcg, but this
* should be negligibly rare.
*
* If we get an online memcg, keep the extra reference in case
* the original one obtained by mem_cgroup_iter() is dropped by
* zswap_memcg_offline_cleanup() while we are shrinking the
* memcg.
*/
spin_lock(&zswap_shrink_lock);
do {
memcg = mem_cgroup_iter(NULL, zswap_next_shrink, NULL);
zswap_next_shrink = memcg;
} while (memcg && !mem_cgroup_tryget_online(memcg));
spin_unlock(&zswap_shrink_lock);
ret = zswap_shrink_one_memcg(memcg, &s);
/* drop the extra reference taken above */
mem_cgroup_put(memcg);
if (ret == -EBUSY)
break;
}
}
Thanks,
Hao
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox