* [PATCH v3 1/6] printk: sysctl: use unsigned int for printk_delay
From: Andrew Murray @ 2026-07-12 10:20 UTC (permalink / raw)
To: Petr Mladek, Steven Rostedt, John Ogness, Sergey Senozhatsky,
Jonathan Corbet, Shuah Khan, Russell King, Florian Fainelli,
Broadcom internal kernel review list, Ray Jui, Scott Branden,
Andrew Morton, Greg Kroah-Hartman, Sebastian Andrzej Siewior,
Clark Williams
Cc: linux-kernel, linux-doc, linux-arm-kernel, linux-rpi-kernel,
linux-rt-devel, Andrew Murray
In-Reply-To: <20260712-printkcleanup-v3-0-574547b8f71b@thegoodpenguin.co.uk>
As the printk_delay sysctl represents a duration in milliseconds,
let's set its type to be unsigned int.
Signed-off-by: Andrew Murray <amurray@thegoodpenguin.co.uk>
---
include/linux/printk.h | 2 +-
kernel/printk/printk.c | 4 ++--
kernel/printk/sysctl.c | 4 ++--
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/include/linux/printk.h b/include/linux/printk.h
index f594c1266bfd411f2238b45374e8a71222f0407c..ea52dc72b73b64898f82a0319b01c27323b18dee 100644
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -188,7 +188,7 @@ extern int __printk_ratelimit(const char *func);
extern bool printk_timed_ratelimit(unsigned long *caller_jiffies,
unsigned int interval_msec);
-extern int printk_delay_msec;
+extern unsigned int printk_delay_msec;
extern int dmesg_restrict;
extern void wake_up_klogd(void);
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 2fe9a963c823a41e7df10c29939a2abb55462859..31aabdf8248cc39c54ee11685d4a37deac1c174c 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -2117,14 +2117,14 @@ static u8 *__printk_recursion_counter(void)
local_irq_restore(flags); \
} while (0)
-int printk_delay_msec __read_mostly;
+unsigned int printk_delay_msec __read_mostly;
static inline void printk_delay(int level)
{
boot_delay_msec(level);
if (unlikely(printk_delay_msec)) {
- int m = printk_delay_msec;
+ unsigned int m = printk_delay_msec;
while (m--) {
mdelay(1);
diff --git a/kernel/printk/sysctl.c b/kernel/printk/sysctl.c
index f15732e93c2e9c0865c42e4af9cb6458d4402c0a..56c3db63b3f8c6c5fddc4e4de1f41c6102f0f4b1 100644
--- a/kernel/printk/sysctl.c
+++ b/kernel/printk/sysctl.c
@@ -44,9 +44,9 @@ static const struct ctl_table printk_sysctls[] = {
{
.procname = "printk_delay",
.data = &printk_delay_msec,
- .maxlen = sizeof(int),
+ .maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = proc_dointvec_minmax,
+ .proc_handler = proc_douintvec_minmax,
.extra1 = SYSCTL_ZERO,
.extra2 = (void *)&ten_thousand,
},
--
2.34.1
^ permalink raw reply related
* [PATCH v3 0/6] printk: nbcon: deprecate boot_delay in favour of printk_delay
From: Andrew Murray @ 2026-07-12 10:20 UTC (permalink / raw)
To: Petr Mladek, Steven Rostedt, John Ogness, Sergey Senozhatsky,
Jonathan Corbet, Shuah Khan, Russell King, Florian Fainelli,
Broadcom internal kernel review list, Ray Jui, Scott Branden,
Andrew Morton, Greg Kroah-Hartman, Sebastian Andrzej Siewior,
Clark Williams
Cc: linux-kernel, linux-doc, linux-arm-kernel, linux-rpi-kernel,
linux-rt-devel, Andrew Murray
The boot_delay (BOOT_PRINTK_DELAY) kernel parameter and printk_delay
sysctl are two distinct mechanisms for providing similar functionality
which add a delay prior to each printed printk message.
boot_delay provides a kernel parameter for delaying printk output from
kernel start through to boot (SYSTEM_RUNNING), whereas printk_delay is
configurable only via sysctl and thus is only used post boot.
However, since the introduction of nbcon and the legacy printer thread
for PREEMPT_RT kernels, printk records are now emited to the console
asynchronously to the caller of printk. Thus, any printk delay added by
boot_delay/printk_delay continues to slow down the calling process but
may not have any impact to the rate in which records are emited to the
console, especially for slow consoles.
To address these issues, let's deprecate boot_delay, extend printk_delay
to be useable from kernel start and ensure that delays occur at the point
where console messages are printed rather than queued.
Please note that this patchset results in delays occuring after a message
is printed rather than, as it is now, before.
Signed-off-by: Andrew Murray <amurray@thegoodpenguin.co.uk>
---
Please see the following related work for additional context:
- https://lore.kernel.org/all/20260503214214.3475670-1-rdunlap@infradead.org/
- https://lore.kernel.org/all/20260505-printk_delay-v1-1-5dba51d7f17c@thegoodpenguin.co.uk/
---
Changes in v1:
- Rebased onto v7.2-rc2
- Moved emitted field from nbcon_write_context to nbcon_context
- Fixed comparison of char field that used > operator
- Used unsigned int and bounds checking for printk/boot delay
- Moved delays prior to allowing handover
- Added additional information into commit messages
- Link to v2: https://lore.kernel.org/lkml/20260630-deprecate_boot_delay-v2-0-f9883d36aa4b@thegoodpenguin.co.uk
Changes in v2:
- Rebased onto v7.2-rc1
- Correctly handle negative values for printk_delay_msec (patch 2)
- Add missing newline in pr_warn (patch 2)
- Improved patch descriptions for (patches 2 and 3)
- Use new emitted flag in nbcon_context/nbcon_write_context in place of backlog && wctxt.len checks (patch 3)
- Use unsigned char for unsafe_takeover field instead of bool in nbcon_write_context (patch 3)
- Move printk_delay_msec and printk_delay from printk.h to internal.h (patch 3)
- Revert regression added in v1 to __nbcon_atomic_flush_pending_con (patch 3)
- Move printk_delay later in console_emit_next_record ensuring delay is always after emit (across nbcon/legacy) (patch 3)
- Fixed typo in documentation s/boot_delay/printk_delay/g in printk_delay= section (patch 4)
- Link to v1: https://lore.kernel.org/r/20260601-deprecate_boot_delay-v1-0-c34c187142a6@thegoodpenguin.co.uk
---
Andrew Murray (6):
printk: sysctl: use unsigned int for printk_delay
printk: add bounds checking to boot_delay
printk: remove BOOT_PRINTK_DELAY config option
printk: deprecate boot_delay in favour of printk_delay
printk: nbcon: move printk_delay to console emiting code
Documentation/kernel-parameters: add/update printk_delay/boot_delay
Documentation/admin-guide/kernel-parameters.txt | 31 +++++++--
arch/arm/configs/bcm2835_defconfig | 1 -
include/linux/console.h | 5 +-
include/linux/printk.h | 1 -
kernel/printk/internal.h | 6 ++
kernel/printk/nbcon.c | 13 ++++
kernel/printk/printk.c | 89 ++++++++++++++++---------
kernel/printk/sysctl.c | 4 +-
lib/Kconfig.debug | 18 -----
9 files changed, 108 insertions(+), 60 deletions(-)
---
base-commit: 8cdeaa50eae8dad34885515f62559ee83e7e8dda
change-id: 20260711-printkcleanup-cbe3fda4a2bc
Best regards,
--
Andrew Murray <amurray@thegoodpenguin.co.uk>
^ permalink raw reply
* Re: [PATCH v2] docs/ja_JP: translate submitting-patches.rst (sign-off)
From: weibu @ 2026-07-12 9:53 UTC (permalink / raw)
To: Akira Yokosawa; +Cc: linux-doc, linux-kernel, corbet
In-Reply-To: <c8c36d99-9c0a-4767-8a4e-a5ad28093530@gmail.com>
Akira-san,
Thank you for pointing this out.
I agree with your concern. I will not translate the DCO text itself in
the next revision.
In v3, I will keep the DCO 1.1 text in English as the original verbatim
text, and add a Japanese explanation that the sign-off refers to the
English DCO text, not to a translated version.
I will prepare v3 after checking the wording again.
Thanks,
Akiyoshi Kurita
2026-07-12 17:11 に Akira Yokosawa さんは書きました:
> Hi,
>
> On Sun, 12 Jul 2026 06:55:10 +0900, Akiyoshi Kurita wrote:
>> Translate the "Include PATCH in the subject" and "Sign your work -
>> the Developer's Certificate of Origin" sections into Japanese.
>>
>> Keep the DCO text as a literal block to match commit 999161066dc5
>> ("docs: submitting-patches: Fix section structure around DCO").
>>
>> Signed-off-by: Akiyoshi Kurita <weibu@redadmin.org>
>>
>> ---
>> Changes in v2:
>>
>> - Added the Japanese translation of the "Include PATCH in the subject"
>> section.
>> - Updated the DCO translation to match the current English text and
>> structure.
>> - Kept the DCO statement in a literal block following commit
>> 999161066dc5.
>
> You didn't address my concern expressed in v1 review.
> This is what I said:
>
> What you need to agree in signing off is the English certificate.
> Not the translated one. So this can confuse people.
>
> I don't have any good idea.
>
> Please convince me you can translate the certificate
> without any concern of confusion.
>
> You said in your reply:
>
> I agree that translating the DCO text itself could be confusing,
> because the sign-off refers to the English certificate, not to a
> translated version.
>
>>
>>
>> .../ja_JP/process/submitting-patches.rst | 66
>> +++++++++++++++++++
>> 1 file changed, 66 insertions(+)
>>
> [...]
>
>> +作業への署名 - Developer's Certificate of Origin
>> +--------------------------------------------------
>> +
>> +誰が何を行ったのかを追跡しやすくするため、特にパッチが複数階層の
>> +メンテナーを経由して最終的にカーネルへ取り込まれる場合に備えて、
>> +メールでやり取りされるパッチには sign-off の手続きが導入されています。
>> +
>> +sign-off は、パッチの説明の末尾に追加する単純な一行です。これは、
>> +そのパッチを自分で作成したか、オープンソースのパッチとして提出する
>> +権利を持っていることを証明します。以下を証明できる場合、規則は単純です::
>> +
>> + Developer's Certificate of Origin 1.1
>> +
>> + このプロジェクトへ貢献することにより、私は以下を証明します:
>> +
>> + (a) この貢献の全部または一部を私が作成し、ファイルに示された
>> + オープンソースライセンスの下で提出する権利を有していること。
>> + または、
>> +
>> + (b) 私の知る限り、この貢献は適切なオープンソースライセンスの
>> + 対象となる以前の成果物に基づいており、そのライセンスに従って、
>> + 私が全部または一部を変更した成果物を、ファイルに示された
>> + 同じオープンソースライセンスの下で提出する権利を有していること。
>> + ただし、別のライセンスで提出することを許可されている場合を除く。
>> + または、
>> +
>> + (c) この貢献は、(a)、(b)、または (c) を証明した別の人物から
>> + 私へ直接提供されたものであり、私が変更を加えていないこと。
>> +
>> + (d) このプロジェクトおよび貢献が公開されること、ならびに私が
>> + 提出したすべての個人情報と sign-off を含む貢献の記録が
>> + 無期限に保存され、このプロジェクトまたは関連する
>> + オープンソースライセンスに従って再配布される可能性があることを
>> + 理解し、同意すること。
>> +
>
> This will confuse people, as you have agreed.
>
> So, I think you need to do something more to prevent any confusion.
>
> I guess you have another hurdle to clear.
>
> Text of DCO 1.1 is available at: https://developercertificate.org/
>
> It carries this legal statement:
>
> Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
>
> Everyone is permitted to copy and distribute verbatim copies of
> this
> license document, but changing it is not allowed.
>
> There is no mention of translation, so I can't figure out if
> translation
> is allowed.
>
> To be clear, I don't have sufficient background for reviewing
> legal (contract/agreement/certificate/...) text. So there is a high
> chance of me being confused.
>
> Thanks,
> Akira
>
>> +上記を証明できる場合は、次のような行を追加します::
>> +
>> + Signed-off-by: Random J Developer
>> <random@developer.example.org>
>> +
> [...]
^ permalink raw reply
* Re: [PATCH v7 4/5] iio: osf: add authenticated stream parser
From: Andy Shevchenko @ 2026-07-12 9:51 UTC (permalink / raw)
To: Kim Jinseob
Cc: Jonathan Cameron, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
David Lechner, Nuno Sa, Andy Shevchenko, Jonathan Corbet,
Shuah Khan, linux-iio, devicetree, linux-doc, linux-kernel
In-Reply-To: <CALMSewLACs7+QEq=3Pp=Wo6dvmEu0hFjmuOx8oe0AGoZHPmADw@mail.gmail.com>
On Sat, Jul 11, 2026 at 12:24:12AM +0900, Kim Jinseob wrote:
What happened to your email? Regular MUAs add the line to see the quoting legend,
and in your email it's gone?
...
> > Seems like reinvention of min() from minmax.h.
>
> Agreed. The open-coded minimum calculations are now replaced with min().
You too eagerly removed the context... But okay, it seems you agreed on
the suggested changes.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* Re: [PATCH] docs/zh_CN: add docs-next checkout workaround
From: Dongliang Mu @ 2026-07-12 8:35 UTC (permalink / raw)
To: Weijie Yuan
Cc: Alex Shi, Yanteng Si, Jonathan Corbet, Shuah Khan, linux-kernel,
linux-doc
In-Reply-To: <alMp5-UPJks0xEvS@wyuan.org>
On 7/12/26 1:45 PM, Weijie Yuan wrote:
> On Sun, Jul 12, 2026 at 12:47:16PM +0800, Dongliang Mu wrote:
>> Hi Weijie,
>> Could you please try cloning
>> https://mirror.nju.edu.cn/git/kernel-doc-zh.git/ <https://link.wtturl.cn/?target=https%3A%2F%2Fmirror.nju.edu.cn%2Fgit%2Fkernel-doc-zh.git%2F&scene=im&aid=582478&lang=zh>
>> and verify whether this mirror is stable?
> OK, I just did a quick test on my debian 13 (local physical server):
>
> $ git clone https://mirror.nju.edu.cn/git/kernel-doc-zh.git/
> Cloning into 'kernel-doc-zh'...
> remote: Enumerating objects: 11675982, done.
> remote: Counting objects: 100% (11675982/11675982), done.
> remote: Compressing objects: 100% (2008049/2008049), done.
> error: RPC failed; curl 92 HTTP/2 stream 5 was not closed cleanly: INTERNAL_ERROR (err 2)
> error: 7397 bytes of body are still expected
> fetch-pack: unexpected disconnect while reading sideband packet
> fatal: early EOF
> fatal: fetch-pack: invalid index-pack output
>
> then again:
>
> GIT_TRACE=1 \
> GIT_TRACE_CURL=1 \
> GIT_CURL_VERBOSE=1 \
> git clone https://mirror.nju.edu.cn/git/kernel-doc-zh.git/
>
> [...many verbose outputs]
> remote: Enumerating objects: 11675982, done.
> remote: Counting objects: 100% (11675982/11675982), done.
> remote: Compressing objects: 100% (2008049/2008049), done.
> 13:24:10.505880 http.c:994 == Info: HTTP/2 stream 5 was not closed cleanly: INTERNAL_ERROR (err 2)
> 13:24:10.505973 http.c:994 == Info: Connection #0 to host mirror.nju.edu.cn left intact
> error: RPC failed; curl 92 HTTP/2 stream 5 was not closed cleanly: INTERNAL_ERROR (err 2)
> error: 6191 bytes of body are still expected
> fetch-pack: unexpected disconnect while reading sideband packet
> fatal: early EOF
> fatal: fetch-pack: invalid index-pack output
It seems like the same issue with our mirror site. I've forward this
email thread to NJU mirror maintainers.
> From the output, it seems that there was an error in the mirror of NJU's
> nginx, the Git HTTP backend behind nginx, or the upstream connection
> between them, but I'm not 100% sure.
>
> I will conduct several more tests immediately to troubleshoot the
> problem, to see where exactly the problem lies.
>
> In addition, cloning with a cloud server in Hong Kong:
>
> Cloning into 'kernel-doc-zh'...
> remote: Enumerating objects: 11675982, done.
> remote: Counting objects: 100% (11675982/11675982), done.
> remote: Compressing objects: 100% (2008049/2008049), done.
> error: RPC failed; curl 92 HTTP/2 stream 5 was not closed cleanly: INTERNAL_ERROR (err 2)
> error: 3965 bytes of body are still expected
> fetch-pack: unexpected disconnect while reading sideband packet
> fatal: early EOF
> fatal: fetch-pack: invalid index-pack output
I tried the NJU mirror in my laptop, and successfully clone the
repository from NJU mirror. There must exist some unknown reason to lead
to this failure.
>
>> TUNA has limited storage capacity and has asked the NJU mirror site to host
>> this Git repository.
> Fully understand, easy to notice that TUNA is under big preesure these days.
> Thanks, NJU!
>
>> We are currently diagnosing network issues on our mirror site. The classic
>> architecture - Nginx (serving static frontend, HTTPS, and caching) -> Apache
>> (reverse proxy + CGI execution) -> cgit / git-http-backend - appears to be
>> functional in principle.
>> The clone failures with large Git repositories are likely caused by
>> insufficient or inappropriate parameter configurations rather than the
>> architecture itself.
> Agreed, it is very likely that the transmission problem is caused by the
> large size of the warehouse.
>
>> P.S., I am the mentor of HUST OpenAtom Club, and our club is maintaining the
>> hust mirror site.
> Thanks very much.
^ permalink raw reply
* Re: [PATCH v2] docs/ja_JP: translate submitting-patches.rst (sign-off)
From: Akira Yokosawa @ 2026-07-12 8:11 UTC (permalink / raw)
To: Akiyoshi Kurita, linux-doc; +Cc: linux-kernel, corbet
In-Reply-To: <20260711215510.1708416-1-weibu@redadmin.org>
Hi,
On Sun, 12 Jul 2026 06:55:10 +0900, Akiyoshi Kurita wrote:
> Translate the "Include PATCH in the subject" and "Sign your work -
> the Developer's Certificate of Origin" sections into Japanese.
>
> Keep the DCO text as a literal block to match commit 999161066dc5
> ("docs: submitting-patches: Fix section structure around DCO").
>
> Signed-off-by: Akiyoshi Kurita <weibu@redadmin.org>
>
> ---
> Changes in v2:
>
> - Added the Japanese translation of the "Include PATCH in the subject" section.
> - Updated the DCO translation to match the current English text and structure.
> - Kept the DCO statement in a literal block following commit 999161066dc5.
You didn't address my concern expressed in v1 review.
This is what I said:
What you need to agree in signing off is the English certificate.
Not the translated one. So this can confuse people.
I don't have any good idea.
Please convince me you can translate the certificate
without any concern of confusion.
You said in your reply:
I agree that translating the DCO text itself could be confusing,
because the sign-off refers to the English certificate, not to a
translated version.
>
>
> .../ja_JP/process/submitting-patches.rst | 66 +++++++++++++++++++
> 1 file changed, 66 insertions(+)
>
[...]
> +作業への署名 - Developer's Certificate of Origin
> +--------------------------------------------------
> +
> +誰が何を行ったのかを追跡しやすくするため、特にパッチが複数階層の
> +メンテナーを経由して最終的にカーネルへ取り込まれる場合に備えて、
> +メールでやり取りされるパッチには sign-off の手続きが導入されています。
> +
> +sign-off は、パッチの説明の末尾に追加する単純な一行です。これは、
> +そのパッチを自分で作成したか、オープンソースのパッチとして提出する
> +権利を持っていることを証明します。以下を証明できる場合、規則は単純です::
> +
> + Developer's Certificate of Origin 1.1
> +
> + このプロジェクトへ貢献することにより、私は以下を証明します:
> +
> + (a) この貢献の全部または一部を私が作成し、ファイルに示された
> + オープンソースライセンスの下で提出する権利を有していること。
> + または、
> +
> + (b) 私の知る限り、この貢献は適切なオープンソースライセンスの
> + 対象となる以前の成果物に基づいており、そのライセンスに従って、
> + 私が全部または一部を変更した成果物を、ファイルに示された
> + 同じオープンソースライセンスの下で提出する権利を有していること。
> + ただし、別のライセンスで提出することを許可されている場合を除く。
> + または、
> +
> + (c) この貢献は、(a)、(b)、または (c) を証明した別の人物から
> + 私へ直接提供されたものであり、私が変更を加えていないこと。
> +
> + (d) このプロジェクトおよび貢献が公開されること、ならびに私が
> + 提出したすべての個人情報と sign-off を含む貢献の記録が
> + 無期限に保存され、このプロジェクトまたは関連する
> + オープンソースライセンスに従って再配布される可能性があることを
> + 理解し、同意すること。
> +
This will confuse people, as you have agreed.
So, I think you need to do something more to prevent any confusion.
I guess you have another hurdle to clear.
Text of DCO 1.1 is available at: https://developercertificate.org/
It carries this legal statement:
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
There is no mention of translation, so I can't figure out if translation
is allowed.
To be clear, I don't have sufficient background for reviewing
legal (contract/agreement/certificate/...) text. So there is a high
chance of me being confused.
Thanks,
Akira
> +上記を証明できる場合は、次のような行を追加します::
> +
> + Signed-off-by: Random J Developer <random@developer.example.org>
> +
[...]
^ permalink raw reply
* Re: [PATCH v2] docs: zh_TW: process: localize terminologies and improve fluency in 8.Conclusion
From: Weijie Yuan @ 2026-07-12 7:23 UTC (permalink / raw)
To: Alex Shi
Cc: 葉宸佑, Dongliang Mu, Hu Haowen,
Jonathan Corbet, Shuah Khan, Dongliang Mu, linux-doc,
linux-kernel, Yuchen Tian, Alex Shi, Yanteng Si
In-Reply-To: <40155119-b6c1-40cc-bbf2-5d1bbe5743e8@gmail.com>
On Sun, Jul 12, 2026 at 12:33:36PM +0800, Alex Shi wrote:
> On 2026/7/12 01:21, Weijie Yuan wrote:
> > > For what it's worth, I am from Taiwan and a native zh_TW speaker.
> > > That is actually what motivated this patch: much of the current text
> > > reads like converted zh_CN rather than natural Taiwanese Mandarin,
> > So, back to my confusion again, and quote myself:
> >
> > How exactly we define the position of Traditional Chinese or zh_TW?
> >
> > 1. Simple conversion between simplified and traditional Chinese
> > characters
> > 2. Taiwanese localized traditional Chinese
> >
> > This issue needs to be confirmed by the senior maintenance personnel.
> > (I will review the archives to confirm. If there is already a clear
> > definition, please forgive me.)
>
> Hi Weijie,
>
> Regarding this issue, we also have Hong Kong and Macau Traditional
> Chinese. While they are mutually intelligible with Taiwanese Chinese,
> there are slight differences. If a Taiwan-specific Traditional Chinese
> translation is required, does this imply that we would also need other
> corresponding localized translations? This is similar to English-the
> English used in the UK, the US, Australia, and so on all differ
> slightly, yet the kernel documentation does not maintain separate
> versions for different countries.
Hi Alex,
Agreed, so I can understand that your point is that Traditional Chinese
(Taiwan) actually doesn't have much practical use?
> Furthermore, aside from a few differences in computing terminology,
> there are no significant differences between Taiwanese Chinese and
> Mainland Chinese that would lead to misunderstandings.
Agreed.
> In fact, many of the current Simplified Chinese translations were
> contributed by people from Taiwan, like Haowen and others.
Sorry, but I guess he is very likely not from Taiwan.
1. Based on the recent several patches, his initial translation was
merely a simple conversion between simplified and traditional Chinese
characters, without taking into account the local language expressions
specific to Taiwan. If he were from Taiwan, many obvious linguistic
habits would surely have been noticed, such as "软件" vs. "軟體".
2. His previous email domain shows he is studying in the mainland.
3. His personal blog is written in simplified chinese.
Of course, I have no intention of intruding on others' privacy. I just
merely made a brief observation.
> To avoid scattering our efforts, I suggest we minimize fragmentation
> as much as possible. When it comes to technical documentation
> translation, not literary translation, a straightforward, unadorned,
> and free from misunderstandings is the best translation and easy to
> maintain. Let's keep thing simple, unless sth is really necessary.
Absolutely agreed. These minor issues in expression habits, of course,
will not have much impact on reading.
Given that this document has not been maintained for ~2 years and these
patches to the terminology actually don't have much significance, it
might be more appropriate to directly declare the status of Traditional
Chinese as "Orphan" provisionally for now, and remove it directly in the
near future, until Hao Wen's return and opinion. Or maybe, waiting for a
new good soul to take over, which is unpredictable.
Thanks,
Weijie
^ permalink raw reply
* Re: [PATCH] docs/zh_CN: add docs-next checkout workaround
From: Weijie Yuan @ 2026-07-12 6:53 UTC (permalink / raw)
To: Dongliang Mu
Cc: Alex Shi, Yanteng Si, Jonathan Corbet, Shuah Khan, linux-kernel,
linux-doc
In-Reply-To: <alMp5-UPJks0xEvS@wyuan.org>
On Sun, Jul 12, 2026 at 01:45:36PM +0800, Weijie Yuan wrote:
> > We are currently diagnosing network issues on our mirror site. The classic
> > architecture - Nginx (serving static frontend, HTTPS, and caching) -> Apache
> > (reverse proxy + CGI execution) -> cgit / git-http-backend - appears to be
> > functional in principle.
> > The clone failures with large Git repositories are likely caused by
> > insufficient or inappropriate parameter configurations rather than the
> > architecture itself.
>
> Agreed, it is very likely that the transmission problem is caused by the
> large size of the warehouse.
Oops, I mean repo of course.
^ permalink raw reply
* Re: [PATCH] docs/zh_CN: add docs-next checkout workaround
From: Weijie Yuan @ 2026-07-12 5:45 UTC (permalink / raw)
To: Dongliang Mu
Cc: Alex Shi, Yanteng Si, Jonathan Corbet, Shuah Khan, linux-kernel,
linux-doc
In-Reply-To: <91ea31b9-4154-4769-a620-43fea87cb949@hust.edu.cn>
On Sun, Jul 12, 2026 at 12:47:16PM +0800, Dongliang Mu wrote:
> Hi Weijie,
> Could you please try cloning
> https://mirror.nju.edu.cn/git/kernel-doc-zh.git/ <https://link.wtturl.cn/?target=https%3A%2F%2Fmirror.nju.edu.cn%2Fgit%2Fkernel-doc-zh.git%2F&scene=im&aid=582478&lang=zh>
> and verify whether this mirror is stable?
OK, I just did a quick test on my debian 13 (local physical server):
$ git clone https://mirror.nju.edu.cn/git/kernel-doc-zh.git/
Cloning into 'kernel-doc-zh'...
remote: Enumerating objects: 11675982, done.
remote: Counting objects: 100% (11675982/11675982), done.
remote: Compressing objects: 100% (2008049/2008049), done.
error: RPC failed; curl 92 HTTP/2 stream 5 was not closed cleanly: INTERNAL_ERROR (err 2)
error: 7397 bytes of body are still expected
fetch-pack: unexpected disconnect while reading sideband packet
fatal: early EOF
fatal: fetch-pack: invalid index-pack output
then again:
GIT_TRACE=1 \
GIT_TRACE_CURL=1 \
GIT_CURL_VERBOSE=1 \
git clone https://mirror.nju.edu.cn/git/kernel-doc-zh.git/
[...many verbose outputs]
remote: Enumerating objects: 11675982, done.
remote: Counting objects: 100% (11675982/11675982), done.
remote: Compressing objects: 100% (2008049/2008049), done.
13:24:10.505880 http.c:994 == Info: HTTP/2 stream 5 was not closed cleanly: INTERNAL_ERROR (err 2)
13:24:10.505973 http.c:994 == Info: Connection #0 to host mirror.nju.edu.cn left intact
error: RPC failed; curl 92 HTTP/2 stream 5 was not closed cleanly: INTERNAL_ERROR (err 2)
error: 6191 bytes of body are still expected
fetch-pack: unexpected disconnect while reading sideband packet
fatal: early EOF
fatal: fetch-pack: invalid index-pack output
From the output, it seems that there was an error in the mirror of NJU's
nginx, the Git HTTP backend behind nginx, or the upstream connection
between them, but I'm not 100% sure.
I will conduct several more tests immediately to troubleshoot the
problem, to see where exactly the problem lies.
In addition, cloning with a cloud server in Hong Kong:
Cloning into 'kernel-doc-zh'...
remote: Enumerating objects: 11675982, done.
remote: Counting objects: 100% (11675982/11675982), done.
remote: Compressing objects: 100% (2008049/2008049), done.
error: RPC failed; curl 92 HTTP/2 stream 5 was not closed cleanly: INTERNAL_ERROR (err 2)
error: 3965 bytes of body are still expected
fetch-pack: unexpected disconnect while reading sideband packet
fatal: early EOF
fatal: fetch-pack: invalid index-pack output
> TUNA has limited storage capacity and has asked the NJU mirror site to host
> this Git repository.
Fully understand, easy to notice that TUNA is under big preesure these days.
Thanks, NJU!
> We are currently diagnosing network issues on our mirror site. The classic
> architecture - Nginx (serving static frontend, HTTPS, and caching) -> Apache
> (reverse proxy + CGI execution) -> cgit / git-http-backend - appears to be
> functional in principle.
> The clone failures with large Git repositories are likely caused by
> insufficient or inappropriate parameter configurations rather than the
> architecture itself.
Agreed, it is very likely that the transmission problem is caused by the
large size of the warehouse.
> P.S., I am the mentor of HUST OpenAtom Club, and our club is maintaining the
> hust mirror site.
Thanks very much.
^ permalink raw reply
* Re: [PATCH v2] docs: zh_TW: process: localize terminologies and improve fluency in 8.Conclusion
From: Weijie Yuan @ 2026-07-12 5:15 UTC (permalink / raw)
To: Jonathan Corbet
Cc: 葉宸佑, Dongliang Mu, Hu Haowen, Shuah Khan,
Dongliang Mu, linux-doc, linux-kernel, Yuchen Tian, Alex Shi,
Yanteng Si
In-Reply-To: <87wlv1i985.fsf@trenco.lwn.net>
On Sat, Jul 11, 2026 at 02:04:26PM -0600, Jonathan Corbet wrote:
> Weijie Yuan <wy@wyuan.org> writes:
>
> > How exactly we define the position of Traditional Chinese or zh_TW?
> >
> > 1. Simple conversion between simplified and traditional Chinese
> > characters
> > 2. Taiwanese localized traditional Chinese
> >
> > This issue needs to be confirmed by the senior maintenance personnel.
> > (I will review the archives to confirm. If there is already a clear
> > definition, please forgive me.)
>
> "Senior maintenance personnel" in this case is the people who actually
> step up to maintain this translation. There is no higher level of
> authority that needs to somehow sign off on it.
Yeah, so, due to the current special circumstances, I actually mean that
I want to seek the opinions of the Simplified Chinese team, and then
finally have you Ack.
> The existing translation is essentially abandoned; if somebody wants
> to carry it forward -- and stay with it -- with a shift in focus, I
> think that is just fine.
Yes, hope that the following discussion will lead to a consensus.
Thanks,
Weijie
^ permalink raw reply
* Re: [PATCH] docs/zh_CN: add docs-next checkout workaround
From: Dongliang Mu @ 2026-07-12 4:47 UTC (permalink / raw)
To: Weijie Yuan
Cc: Alex Shi, Yanteng Si, Jonathan Corbet, Shuah Khan, linux-kernel,
linux-doc
In-Reply-To: <alD6UJw1Y2VNK3x1@wyuan.org>
On 7/10/26 9:57 PM, Weijie Yuan wrote:
> On Fri, Jul 10, 2026 at 09:44:45PM +0800, Dongliang Mu wrote:
>>>>> --- >8 ---
>>>> Is this a special mark for LKML? If I understand correctly, the following
>>>> should be the patch content.
>>> Yes, the following is the commit message body.
>>>
>>> You can apply this patch by "git am -c" or "git am --scissors", which
>>> means: Remove everything in body before a scissors line (see git-mailinfo[1]).
>>>
>>> https://git-scm.com/docs/git-am#Documentation/git-am.txt---scissors
>>>
>>> Git community sometimes use it, for the cases if you want to swap the
>>> actual commit message and the supplementary information.
>> Understood.
> Sorry for any confusion.
>
>>>> This would make the envionment preparation more difficult. My suggestion is
>>>> to ask maintainers of some mirror sites to sync this repo like us.
>>>>
>>>> Dongliang Mu
>>> Makes sense. Could I kindly ask, for example, Tsinghua tuna team to
>>> mirror our repo, on behalf of our Chinese document team? I'm afraid they
>>> are unlikely to consider my request in my individual capacity. ;-)
>> I can help contact TUNA maintainers.
>>
>> Dongliang Mu
> That would be great! Apparently your words carry more weight than mine
> ;-) So do you mean that I just need to wait quietly for your good news?
Hi Weijie,
Could you please try cloning
https://mirror.nju.edu.cn/git/kernel-doc-zh.git/
<https://link.wtturl.cn/?target=https%3A%2F%2Fmirror.nju.edu.cn%2Fgit%2Fkernel-doc-zh.git%2F&scene=im&aid=582478&lang=zh>
and verify whether this mirror is stable?
TUNA has limited storage capacity and has asked the NJU mirror site to
host this Git repository.
We are currently diagnosing network issues on our mirror site. The
classic architecture — Nginx (serving static frontend, HTTPS, and
caching) → Apache (reverse proxy + CGI execution) → cgit /
git-http-backend — appears to be functional in principle.
The clone failures with large Git repositories are likely caused by
insufficient or inappropriate parameter configurations rather than the
architecture itself.
P.S., I am the mentor of HUST OpenAtom Club, and our club is maintaining
the hust mirror site.
Dongliang Mu
>
> Appreciate it!
>
> Thanks,
> Weijie
^ permalink raw reply
* Re: [PATCH v2] docs: zh_TW: process: localize terminologies and improve fluency in 8.Conclusion
From: Alex Shi @ 2026-07-12 4:33 UTC (permalink / raw)
To: Weijie Yuan, 葉宸佑
Cc: Dongliang Mu, Hu Haowen, Jonathan Corbet, Shuah Khan,
Dongliang Mu, linux-doc, linux-kernel, Yuchen Tian, Alex Shi,
Yanteng Si
In-Reply-To: <alJ7ocaqtpUkCGrd@wyuan.org>
On 2026/7/12 01:21, Weijie Yuan wrote:
>> For what it's worth, I am from Taiwan and a native zh_TW speaker.
>> That is actually what motivated this patch: much of the current text
>> reads like converted zh_CN rather than natural Taiwanese Mandarin,
> So, back to my confusion again, and quote myself:
>
> How exactly we define the position of Traditional Chinese or zh_TW?
>
> 1. Simple conversion between simplified and traditional Chinese
> characters
> 2. Taiwanese localized traditional Chinese
>
> This issue needs to be confirmed by the senior maintenance personnel.
> (I will review the archives to confirm. If there is already a clear
> definition, please forgive me.)
Hi Weijie,
Regarding this issue, we also have Hong Kong and Macau Traditional
Chinese. While they are mutually intelligible with Taiwanese Chinese,
there are slight differences. If a Taiwan-specific Traditional Chinese
translation is required, does this imply that we would also need other
corresponding localized translations? This is similar to English—the
English used in the UK, the US, Australia, and so on all differ
slightly, yet the kernel documentation does not maintain separate
versions for different countries.
Furthermore, aside from a few differences in computing terminology,
there are no significant differences between Taiwanese Chinese and
Mainland Chinese that would lead to misunderstandings. In fact, many of
the current Simplified Chinese translations were contributed by people
from Taiwan, like Haowen and others. To avoid scattering our efforts, I
suggest we minimize fragmentation as much as possible. When it comes to
technical documentation translation, not literary translation, a
straightforward, unadorned, and free from misunderstandings is the best
translation and easy to maintain.
Let's keep thing simple, unless sth is really necessary.
Thanks
Alex
^ permalink raw reply
* [PATCH v2 5/5] selftests/exec: add binfmt_misc bpf-backed handler test
From: Farid Zakaria @ 2026-07-12 4:08 UTC (permalink / raw)
To: Christian Brauner, Alexei Starovoitov, Daniel Borkmann,
Martin KaFai Lau, Shuah Khan
Cc: Andrii Nakryiko, Kees Cook, Alexander Viro, Jan Kara,
Jonathan Corbet, Jann Horn, John Ericson, linux-fsdevel, linux-mm,
linux-kernel, bpf, linux-doc, linux-kselftest, Farid Zakaria
In-Reply-To: <20260711-binfmt-misc-bpf-v2-v2-0-d6591ceaf207@gmail.com>
Exercise the bpf-backed ('B') binfmt_misc handlers end to end. A handler
is a struct binfmt_misc_ops struct_ops map; the test loads and attaches
it (which publishes it by name), activates it with a 'B' entry, and
checks that a matched binary is routed to the interpreter the program
selected via bpf_binprm_set_interp().
Two self-contained cases are covered:
- bpf_interp: match a synthetic aarch64 ELF header from bprm->buf and
route it to a fixed interpreter chosen by the program.
- nix_origin: resolve a "$ORIGIN/..."-relative PT_INTERP to an
interpreter co-located with the binary -- the relocatable-loader
case the kernel ELF loader cannot express. The relocatable binary is
linked with PT_INTERP set to the literal "$ORIGIN/binfmt_bpf_interp"
(-Wl,--dynamic-linker), which the kernel cannot resolve on its own.
Both route to a small test interpreter that prints a marker, proving the
program-selected interpreter actually ran.
The bpf objects are compiled against the running kernel's BTF: the
Makefile generates vmlinux.h with bpftool and the harness links libbpf.
Override CLANG/BPFTOOL/VMLINUX_BTF/LIBBPF_CFLAGS/LIBBPF_LDLIBS as needed.
Signed-off-by: Farid Zakaria <farid.m.zakaria@gmail.com>
Assisted-by: Claude:Opus-4.8
---
tools/testing/selftests/exec/Makefile | 37 ++++
tools/testing/selftests/exec/binfmt_bpf_app.c | 12 ++
tools/testing/selftests/exec/binfmt_bpf_interp.c | 15 ++
tools/testing/selftests/exec/binfmt_misc_bpf.c | 260 +++++++++++++++++++++++
tools/testing/selftests/exec/bpf_interp.bpf.c | 52 +++++
tools/testing/selftests/exec/nix_origin.bpf.c | 179 ++++++++++++++++
6 files changed, 555 insertions(+)
diff --git a/tools/testing/selftests/exec/Makefile b/tools/testing/selftests/exec/Makefile
index 45a3cfc43..5423e3f7f 100644
--- a/tools/testing/selftests/exec/Makefile
+++ b/tools/testing/selftests/exec/Makefile
@@ -21,6 +21,12 @@ TEST_GEN_PROGS += recursion-depth
TEST_GEN_PROGS += null-argv
TEST_GEN_PROGS += check-exec
+# binfmt_misc bpf-backed ('B') handler test: a libbpf harness plus its
+# struct_ops objects and the test interpreter/app it routes between.
+TEST_GEN_PROGS += binfmt_misc_bpf
+TEST_GEN_FILES += bpf_interp.bpf.o nix_origin.bpf.o
+TEST_GEN_FILES += binfmt_bpf_interp binfmt_bpf_app
+
EXTRA_CLEAN := $(OUTPUT)/subdir.moved $(OUTPUT)/execveat.moved $(OUTPUT)/xxxxx* \
$(OUTPUT)/S_I*.test
@@ -55,3 +61,34 @@ $(OUTPUT)/script-exec.inc: $(CHECK_EXEC_SAMPLES)/script-exec.inc
cp $< $@
$(OUTPUT)/script-noexec.inc: $(CHECK_EXEC_SAMPLES)/script-noexec.inc
cp $< $@
+
+# --- binfmt_misc bpf ('B') handler test ---------------------------------
+# The struct_ops bpf objects are compiled against the running kernel's BTF.
+# Override CLANG/BPFTOOL/VMLINUX_BTF if they aren't on PATH or the vmlinux
+# BTF lives elsewhere, and LIBBPF_CFLAGS/LDLIBS to point at a libbpf install.
+CLANG ?= clang
+BPFTOOL ?= bpftool
+VMLINUX_BTF ?= /sys/kernel/btf/vmlinux
+BPF_CFLAGS ?= -I$(OUTPUT)
+LIBBPF_CFLAGS ?=
+LIBBPF_LDLIBS ?= -lbpf -lelf -lz
+
+$(OUTPUT)/vmlinux.h:
+ $(BPFTOOL) btf dump file $(VMLINUX_BTF) format c > $@
+ sed -i '/__ksym;$$/d' $@
+
+$(OUTPUT)/%.bpf.o: %.bpf.c $(OUTPUT)/vmlinux.h
+ $(CLANG) -g -O2 -target bpf -mcpu=v3 $(BPF_CFLAGS) -c $< -o $@
+
+$(OUTPUT)/binfmt_misc_bpf: binfmt_misc_bpf.c
+ $(CC) $(CFLAGS) $(LIBBPF_CFLAGS) $(LDFLAGS) $< $(LIBBPF_LDLIBS) -o $@
+
+$(OUTPUT)/binfmt_bpf_interp: binfmt_bpf_interp.c
+ $(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
+
+# PT_INTERP is set to the literal "$ORIGIN/binfmt_bpf_interp"; the nix_origin
+# handler resolves it relative to the binary at run time.
+$(OUTPUT)/binfmt_bpf_app: binfmt_bpf_app.c
+ $(CC) $(CFLAGS) $(LDFLAGS) -Wl,--dynamic-linker,'$$ORIGIN/binfmt_bpf_interp' $< -o $@
+
+EXTRA_CLEAN += $(OUTPUT)/vmlinux.h $(OUTPUT)/bpf_interp.bpf.o $(OUTPUT)/nix_origin.bpf.o
diff --git a/tools/testing/selftests/exec/binfmt_bpf_app.c b/tools/testing/selftests/exec/binfmt_bpf_app.c
new file mode 100644
index 000000000..472270f14
--- /dev/null
+++ b/tools/testing/selftests/exec/binfmt_bpf_app.c
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * A relocatable binary for the binfmt_misc_bpf $ORIGIN case. The Makefile
+ * links it with PT_INTERP set to the literal "$ORIGIN/binfmt_bpf_interp"
+ * (-Wl,--dynamic-linker), which the kernel ELF loader cannot resolve. The
+ * nix_origin bpf handler resolves it relative to this binary's directory and
+ * routes execution to the co-located interpreter.
+ */
+int main(void)
+{
+ return 0;
+}
diff --git a/tools/testing/selftests/exec/binfmt_bpf_interp.c b/tools/testing/selftests/exec/binfmt_bpf_interp.c
new file mode 100644
index 000000000..2db205f09
--- /dev/null
+++ b/tools/testing/selftests/exec/binfmt_bpf_interp.c
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Test interpreter for the binfmt_misc_bpf selftest. A bpf-backed 'B' handler
+ * routes a matched binary here; printing this marker proves the program's
+ * chosen interpreter actually ran.
+ */
+#include <unistd.h>
+
+int main(int argc, char **argv)
+{
+ (void)argc;
+ (void)argv;
+ write(1, "BPF_INTERP_RAN\n", 15);
+ return 0;
+}
diff --git a/tools/testing/selftests/exec/binfmt_misc_bpf.c b/tools/testing/selftests/exec/binfmt_misc_bpf.c
new file mode 100644
index 000000000..b3e017ade
--- /dev/null
+++ b/tools/testing/selftests/exec/binfmt_misc_bpf.c
@@ -0,0 +1,260 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Selftest for binfmt_misc bpf-backed ('B') handlers.
+ *
+ * A handler is a struct binfmt_misc_ops struct_ops map. Attaching it publishes
+ * it by name in the caller's user namespace; a 'B' entry activates it:
+ *
+ * echo ':name:B:<handler>::::' > /proc/sys/fs/binfmt_misc/register
+ *
+ * Two self-contained cases are exercised:
+ *
+ * 1. bpf_interp: the handler matches a synthetic aarch64 ELF header and
+ * routes it to a fixed interpreter chosen by the program.
+ * 2. nix_origin: the handler resolves a "$ORIGIN/..."-relative PT_INTERP to
+ * an interpreter co-located with the binary (the relocatable-loader case
+ * the kernel ELF loader cannot express).
+ *
+ * Both route to a test interpreter that prints BPF_INTERP_RAN, proving the
+ * program's chosen interpreter actually ran.
+ */
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <bpf/libbpf.h>
+
+#define INTERP_PATH "/tmp/binfmt_bpf_interp"
+#define AARCH64_PATH "/tmp/binfmt_bpf_aarch64"
+#define RELOC_DIR "/tmp/binfmt_reloc"
+#define BINFMT_REG "/proc/sys/fs/binfmt_misc/register"
+#define EXPECT "BPF_INTERP_RAN"
+
+static char testdir[512]; /* directory holding this test's built artifacts */
+
+static int copy_file(const char *src, const char *dst)
+{
+ char buf[4096];
+ int in, out;
+ ssize_t n;
+
+ in = open(src, O_RDONLY);
+ if (in < 0)
+ return -1;
+ out = open(dst, O_WRONLY | O_CREAT | O_TRUNC, 0755);
+ if (out < 0) {
+ close(in);
+ return -1;
+ }
+ while ((n = read(in, buf, sizeof(buf))) > 0) {
+ if (write(out, buf, n) != n) {
+ close(in);
+ close(out);
+ return -1;
+ }
+ }
+ close(in);
+ close(out);
+ return n < 0 ? -1 : 0;
+}
+
+/* A minimal 64-bit little-endian aarch64 ELF header, padded to the read size. */
+static int create_fake_aarch64(const char *path)
+{
+ unsigned char hdr[256] = {0};
+ int fd;
+
+ hdr[0] = 0x7f; hdr[1] = 'E'; hdr[2] = 'L'; hdr[3] = 'F';
+ hdr[4] = 2; /* ELFCLASS64 */
+ hdr[5] = 1; /* ELFDATA2LSB */
+ hdr[6] = 1; /* EV_CURRENT */
+ hdr[16] = 2; /* e_type = ET_EXEC */
+ hdr[18] = 183 & 0xff; /* e_machine = EM_AARCH64 */
+ hdr[19] = (183 >> 8) & 0xff;
+ hdr[20] = 1; /* e_version */
+
+ fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0755);
+ if (fd < 0)
+ return -1;
+ if (write(fd, hdr, sizeof(hdr)) != (ssize_t)sizeof(hdr)) {
+ close(fd);
+ return -1;
+ }
+ close(fd);
+ return 0;
+}
+
+static int register_entry(const char *name, const char *handler)
+{
+ char rule[128];
+ int fd;
+ ssize_t n;
+
+ snprintf(rule, sizeof(rule), ":%s:B:%s::::", name, handler);
+ fd = open(BINFMT_REG, O_WRONLY);
+ if (fd < 0)
+ return -1;
+ n = write(fd, rule, strlen(rule));
+ close(fd);
+ return n < 0 ? -1 : 0;
+}
+
+static void unregister_entry(const char *name)
+{
+ char path[128];
+ int fd;
+
+ snprintf(path, sizeof(path), "/proc/sys/fs/binfmt_misc/%s", name);
+ fd = open(path, O_WRONLY);
+ if (fd >= 0) {
+ if (write(fd, "-1", 2) < 0)
+ ; /* best effort */
+ close(fd);
+ }
+}
+
+static int check_output(const char *cmd, const char *expected)
+{
+ char buf[128];
+ FILE *fp;
+
+ fp = popen(cmd, "r");
+ if (!fp)
+ return -1;
+ if (!fgets(buf, sizeof(buf), fp)) {
+ pclose(fp);
+ return -1;
+ }
+ pclose(fp);
+ return strncmp(buf, expected, strlen(expected)) ? -1 : 0;
+}
+
+/*
+ * Load @objfile, attach its struct_ops map @handler (which publishes the
+ * handler), activate a 'B' entry named @entry that references it, run @target
+ * and check it produced @expect.
+ */
+static int run_case(const char *objfile, const char *handler,
+ const char *entry, const char *target, const char *expect)
+{
+ struct bpf_object *obj;
+ struct bpf_map *map;
+ struct bpf_link *link;
+ int ret = -1;
+
+ obj = bpf_object__open_file(objfile, NULL);
+ if (!obj || libbpf_get_error(obj)) {
+ fprintf(stderr, "open %s failed\n", objfile);
+ return -1;
+ }
+ if (bpf_object__load(obj)) {
+ fprintf(stderr, "load %s failed (check dmesg for the verifier log)\n",
+ objfile);
+ goto close;
+ }
+ map = bpf_object__find_map_by_name(obj, handler);
+ if (!map) {
+ fprintf(stderr, "no struct_ops map '%s' in %s\n", handler, objfile);
+ goto close;
+ }
+ link = bpf_map__attach_struct_ops(map);
+ if (!link || libbpf_get_error(link)) {
+ fprintf(stderr, "attach struct_ops '%s' failed\n", handler);
+ goto close;
+ }
+ if (register_entry(entry, handler)) {
+ fprintf(stderr, "register 'B' entry '%s' failed\n", entry);
+ goto detach;
+ }
+ ret = check_output(target, expect);
+ unregister_entry(entry);
+detach:
+ bpf_link__destroy(link);
+close:
+ bpf_object__close(obj);
+ return ret;
+}
+
+int main(void)
+{
+ char src[600], obj[600], appdst[600], interpdst[600];
+ char exe[512];
+ ssize_t n;
+ int fail = 0;
+ struct stat st;
+
+ if (getuid() != 0) {
+ fprintf(stderr, "Skipping: test must be run as root\n");
+ return 4; /* KSFT_SKIP */
+ }
+
+ n = readlink("/proc/self/exe", exe, sizeof(exe) - 1);
+ if (n < 0) {
+ perror("readlink");
+ return 1;
+ }
+ exe[n] = '\0';
+ snprintf(testdir, sizeof(testdir), "%s", dirname(exe));
+
+ if (stat("/sys/fs/bpf", &st) < 0)
+ mkdir("/sys/fs/bpf", 0755);
+ mount("bpf", "/sys/fs/bpf", "bpf", 0, NULL);
+ if (access(BINFMT_REG, F_OK) < 0)
+ mount("binfmt_misc", "/proc/sys/fs/binfmt_misc", "binfmt_misc", 0, NULL);
+
+ /* Shared test interpreter. */
+ snprintf(src, sizeof(src), "%s/binfmt_bpf_interp", testdir);
+ if (copy_file(src, INTERP_PATH)) {
+ fprintf(stderr, "cannot install %s\n", INTERP_PATH);
+ return 1;
+ }
+
+ /* Case 1: match a synthetic aarch64 header -> fixed interpreter. */
+ printf("[*] case 1: match aarch64 header -> program-chosen interpreter\n");
+ if (create_fake_aarch64(AARCH64_PATH)) {
+ fprintf(stderr, "cannot create %s\n", AARCH64_PATH);
+ return 1;
+ }
+ snprintf(obj, sizeof(obj), "%s/bpf_interp.bpf.o", testdir);
+ if (run_case(obj, "bpf_interp", "test_bpf_interp", AARCH64_PATH, EXPECT) == 0)
+ printf("[+] case 1 passed\n");
+ else {
+ printf("[-] case 1 FAILED\n");
+ fail = 1;
+ }
+ unlink(AARCH64_PATH);
+
+ /* Case 2: $ORIGIN-relative PT_INTERP -> co-located interpreter. */
+ printf("[*] case 2: $ORIGIN interpreter resolved relative to the binary\n");
+ mkdir(RELOC_DIR, 0755);
+ snprintf(appdst, sizeof(appdst), "%s/app", RELOC_DIR);
+ snprintf(interpdst, sizeof(interpdst), "%s/binfmt_bpf_interp", RELOC_DIR);
+ snprintf(src, sizeof(src), "%s/binfmt_bpf_app", testdir);
+ if (copy_file(src, appdst) ||
+ copy_file(INTERP_PATH, interpdst)) {
+ fprintf(stderr, "cannot set up %s\n", RELOC_DIR);
+ fail = 1;
+ } else {
+ snprintf(obj, sizeof(obj), "%s/nix_origin.bpf.o", testdir);
+ if (run_case(obj, "nix_origin", "test_bpf_origin", appdst, EXPECT) == 0)
+ printf("[+] case 2 passed\n");
+ else {
+ printf("[-] case 2 FAILED\n");
+ fail = 1;
+ }
+ }
+ unlink(appdst);
+ unlink(interpdst);
+ rmdir(RELOC_DIR);
+ unlink(INTERP_PATH);
+
+ if (!fail)
+ printf("[*] all binfmt_misc bpf cases passed\n");
+ return fail;
+}
diff --git a/tools/testing/selftests/exec/bpf_interp.bpf.c b/tools/testing/selftests/exec/bpf_interp.bpf.c
new file mode 100644
index 000000000..ca98c3716
--- /dev/null
+++ b/tools/testing/selftests/exec/bpf_interp.bpf.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * binfmt_misc_ops handler for the selftest's fixed-interpreter case: match a
+ * 64-bit aarch64 ELF header from the prefetched buffer and route it to a fixed
+ * interpreter chosen by the program. This is the portable, self-contained
+ * equivalent of routing a foreign binary to an emulator: it matches
+ * programmatically and computes the interpreter, but points at a test binary
+ * the harness installs rather than a system emulator.
+ */
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+
+char _license[] SEC("license") = "GPL";
+
+#define EI_CLASS 4
+#define ELFCLASS64 2
+#define EM_AARCH64 183
+
+extern int bpf_binprm_set_interp(struct linux_binprm *bprm, const char *path,
+ size_t path__sz) __ksym;
+
+SEC("struct_ops.s/load")
+int BPF_PROG(bpf_interp_load, struct linux_binprm *bprm)
+{
+ /*
+ * Keep the path on the (writable) stack: bpf_binprm_set_interp() takes
+ * a sized memory arg and the verifier rejects a read-only .rodata
+ * buffer for it. The harness installs the interpreter at this path.
+ */
+ char interp[] = "/tmp/binfmt_bpf_interp";
+ __u16 machine;
+
+ if (bprm->buf[0] != 0x7f || bprm->buf[1] != 'E' ||
+ bprm->buf[2] != 'L' || bprm->buf[3] != 'F' ||
+ bprm->buf[EI_CLASS] != ELFCLASS64)
+ return 0;
+
+ /* e_machine is a 16-bit little-endian field at offset 18. */
+ machine = (__u8)bprm->buf[18] | ((__u16)(__u8)bprm->buf[19] << 8);
+ if (machine != EM_AARCH64)
+ return 0;
+
+ /* @path__sz includes the terminating NUL. */
+ return bpf_binprm_set_interp(bprm, interp, sizeof(interp)) ?: 1;
+}
+
+SEC(".struct_ops.link")
+struct binfmt_misc_ops bpf_interp = {
+ .load = (void *)bpf_interp_load,
+ .name = "bpf_interp",
+};
diff --git a/tools/testing/selftests/exec/nix_origin.bpf.c b/tools/testing/selftests/exec/nix_origin.bpf.c
new file mode 100644
index 000000000..a853d64f4
--- /dev/null
+++ b/tools/testing/selftests/exec/nix_origin.bpf.c
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * nix_origin.bpf.c - $ORIGIN-relative PT_INTERP resolution
+ *
+ * A binfmt_misc_ops handler that makes relocatable (Nix-style) ELF
+ * binaries work: if PT_INTERP starts with "$ORIGIN/", the loader is
+ * resolved relative to the directory of the binary being executed and
+ * selected via bpf_binprm_set_interp(). Anything else is declined so
+ * regular binaries pass through untouched.
+ *
+ * Activate with:
+ * bpftool struct_ops register nix_origin.bpf.o /sys/fs/bpf
+ * echo ':nix-origin:B:nix_origin::::' > /proc/sys/fs/binfmt_misc/register
+ */
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+
+char _license[] SEC("license") = "GPL";
+
+#define PATH_MAX 4096
+#define EI_CLASS 4
+#define ELFCLASSXX 2 /* ELFCLASS64; flip to 1 for 32-bit */
+#define PT_INTERP 3
+#define MAX_PHDRS 64
+
+#define ORIGIN "$ORIGIN"
+#define ORIGIN_LEN (sizeof(ORIGIN) - 1)
+
+#define ENOENT 2
+#define ENAMETOOLONG 36
+
+extern int bpf_dynptr_from_file(struct file *file, __u32 flags,
+ struct bpf_dynptr *ptr__uninit) __ksym;
+extern int bpf_dynptr_file_discard(struct bpf_dynptr *dynptr) __ksym;
+extern int bpf_path_d_path(const struct path *path, char *buf,
+ size_t buf__sz) __ksym;
+extern int bpf_binprm_set_interp(struct linux_binprm *bprm, const char *path,
+ size_t path__sz) __ksym;
+
+struct scratch {
+ char interp[PATH_MAX]; /* PT_INTERP as embedded in the binary */
+ char path[PATH_MAX]; /* d_path of the binary, becomes the result */
+};
+
+/* Keyed by pid: execs run concurrently and the program can sleep. */
+struct {
+ __uint(type, BPF_MAP_TYPE_HASH);
+ __uint(max_entries, 512);
+ __type(key, __u64);
+ __type(value, struct scratch);
+} scratch_map SEC(".maps");
+
+static const struct scratch zero_scratch;
+
+SEC("struct_ops.s/load")
+int BPF_PROG(nix_origin_load, struct linux_binprm *bprm)
+{
+ __u32 isz, sfx, rsz, slash;
+ struct elf64_phdr phdr;
+ struct elf64_hdr ehdr;
+ struct bpf_dynptr dp;
+ struct scratch *sc;
+ bool found = false;
+ __u64 id;
+ int ret = 0, len, i;
+
+ /* Cheap reject from the prefetched header. */
+ if (bprm->buf[0] != 0x7f || bprm->buf[1] != 'E' ||
+ bprm->buf[2] != 'L' || bprm->buf[3] != 'F' ||
+ bprm->buf[EI_CLASS] != ELFCLASSXX)
+ return 0;
+
+ if (bpf_dynptr_from_file(bprm->file, 0, &dp))
+ goto out;
+
+ if (bpf_dynptr_read(&ehdr, sizeof(ehdr), &dp, 0, 0))
+ goto out;
+ if (ehdr.e_phentsize != sizeof(struct elf64_phdr))
+ goto out;
+
+ bpf_for(i, 0, ehdr.e_phnum) {
+ if (i >= MAX_PHDRS)
+ break;
+ if (bpf_dynptr_read(&phdr, sizeof(phdr), &dp,
+ ehdr.e_phoff + i * sizeof(phdr), 0))
+ goto out;
+ if (phdr.p_type == PT_INTERP) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ goto out;
+
+ isz = phdr.p_filesz;
+ if (isz <= ORIGIN_LEN + 1 || isz >= sizeof(sc->interp))
+ goto out;
+ /*
+ * The range check above compiles to a test on a zero-extended copy of
+ * the u64 p_filesz, so the verifier does not carry the bound to the
+ * dynptr_read() length below ("unbounded memory access"). Mask isz to
+ * the buffer size (a power of two) and force the masked value to be
+ * materialized with a barrier so the read uses the bounded register.
+ */
+ isz &= sizeof(sc->interp) - 1;
+ barrier_var(isz);
+
+ id = bpf_get_current_pid_tgid();
+ if (bpf_map_update_elem(&scratch_map, &id, &zero_scratch, BPF_ANY))
+ goto out;
+ sc = bpf_map_lookup_elem(&scratch_map, &id);
+ if (!sc)
+ goto out_del;
+
+ if (bpf_dynptr_read(sc->interp, isz, &dp, phdr.p_offset, 0))
+ goto out_del;
+ if (sc->interp[isz - 1] != '\0')
+ goto out_del;
+
+ /* Not "$ORIGIN/..."? Decline, the elf loader owns it. */
+ if (sc->interp[0] != '$' || sc->interp[1] != 'O' ||
+ sc->interp[2] != 'R' || sc->interp[3] != 'I' ||
+ sc->interp[4] != 'G' || sc->interp[5] != 'I' ||
+ sc->interp[6] != 'N' || sc->interp[7] != '/')
+ goto out_del;
+
+ /*
+ * From here on the binary is ours: resolution failures fail the
+ * exec instead of falling back to binfmt_elf, which would resolve
+ * the literal "$ORIGIN/..." relative to the caller's cwd.
+ */
+ ret = -ENOENT;
+ len = bpf_path_d_path(&bprm->file->f_path, sc->path, sizeof(sc->path));
+ if (len <= 0 || len > sizeof(sc->path))
+ goto out_del;
+ /* Unreachable or unlinked ("... (deleted)") binaries can't resolve. */
+ if (sc->path[0] != '/')
+ goto out_del;
+
+ /* $ORIGIN = dirname of the binary. */
+ slash = 0;
+ bpf_for(i, 1, len - 1) {
+ if (i >= sizeof(sc->path))
+ break;
+ if (sc->path[i] == '/')
+ slash = i;
+ }
+
+ /* Splice the suffix (leading '/' and NUL included) onto the dir. */
+ sfx = isz - ORIGIN_LEN;
+ rsz = slash + sfx;
+ if (rsz > sizeof(sc->path)) {
+ ret = -ENAMETOOLONG;
+ goto out_del;
+ }
+ bpf_for(i, 0, sfx) {
+ __u32 s = ORIGIN_LEN + i, d = slash + i;
+
+ if (s >= sizeof(sc->interp) || d >= sizeof(sc->path))
+ break;
+ sc->path[d] = sc->interp[s];
+ }
+
+ ret = bpf_binprm_set_interp(bprm, sc->path, rsz);
+ if (!ret)
+ ret = 1;
+out_del:
+ bpf_map_delete_elem(&scratch_map, &id);
+out:
+ bpf_dynptr_file_discard(&dp);
+ return ret;
+}
+
+SEC(".struct_ops.link")
+struct binfmt_misc_ops nix_origin = {
+ .load = (void *)nix_origin_load,
+ .name = "nix_origin",
+};
--
2.51.2
^ permalink raw reply related
* [PATCH v2 4/5] bpf: allow fs kfuncs for binfmt_misc_ops programs
From: Farid Zakaria @ 2026-07-12 4:08 UTC (permalink / raw)
To: Christian Brauner, Alexei Starovoitov, Daniel Borkmann,
Martin KaFai Lau, Shuah Khan
Cc: Andrii Nakryiko, Kees Cook, Alexander Viro, Jan Kara,
Jonathan Corbet, Jann Horn, John Ericson, linux-fsdevel, linux-mm,
linux-kernel, bpf, linux-doc, linux-kselftest, Farid Zakaria
In-Reply-To: <20260711-binfmt-misc-bpf-v2-v2-0-d6591ceaf207@gmail.com>
From: Christian Brauner <brauner@kernel.org>
The fs kfuncs are currently exclusive to LSM programs. A binfmt_misc
load program needs a subset of them to do anything interesting: to
compute an interpreter relative to the binary's location it wants
bpf_path_d_path() on bprm->file->f_path, and matching on per-binary
metadata wants bpf_get_file_xattr() and friends.
Register the fs kfunc set for struct_ops programs as well and extend
the filter to admit binfmt_misc_ops programs. The xattr setters stay
exclusive to LSM programs: a binary type handler decides how to run a
binary, it has no business modifying filesystem state.
This only takes effect in builds that have the fs kfunc set at all,
i.e. CONFIG_BPF_LSM. Without it a binfmt_misc handler is limited to
bprm fields and the file-backed dynptr, which are provided by the
common kfunc set.
Link: https://lore.kernel.org/20260704211409.1978485-1-farid.m.zakaria@gmail.com
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
fs/bpf_fs_kfuncs.c | 23 ++++++++++++++++++++---
1 file changed, 20 insertions(+), 3 deletions(-)
diff --git a/fs/bpf_fs_kfuncs.c b/fs/bpf_fs_kfuncs.c
index 768aca2dc..aa1fe988b 100644
--- a/fs/bpf_fs_kfuncs.c
+++ b/fs/bpf_fs_kfuncs.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2024 Google LLC. */
+#include <linux/binfmt_misc.h>
#include <linux/bpf.h>
#include <linux/bpf_lsm.h>
#include <linux/btf.h>
@@ -387,10 +388,20 @@ BTF_ID_FLAGS(func, bpf_remove_dentry_xattr, KF_SLEEPABLE)
BTF_ID_FLAGS(func, bpf_real_inode, KF_SLEEPABLE | KF_RET_NULL)
BTF_KFUNCS_END(bpf_fs_kfunc_set_ids)
+/* Side-effecting kfuncs that stay exclusive to LSM programs. */
+BTF_SET_START(bpf_fs_kfunc_lsm_only_ids)
+BTF_ID(func, bpf_set_dentry_xattr)
+BTF_ID(func, bpf_remove_dentry_xattr)
+BTF_SET_END(bpf_fs_kfunc_lsm_only_ids)
+
static int bpf_fs_kfuncs_filter(const struct bpf_prog *prog, u32 kfunc_id)
{
- if (!btf_id_set8_contains(&bpf_fs_kfunc_set_ids, kfunc_id) ||
- prog->type == BPF_PROG_TYPE_LSM)
+ if (!btf_id_set8_contains(&bpf_fs_kfunc_set_ids, kfunc_id))
+ return 0;
+ if (prog->type == BPF_PROG_TYPE_LSM)
+ return 0;
+ if (bpf_prog_is_binfmt_misc_ops(prog) &&
+ !btf_id_set_contains(&bpf_fs_kfunc_lsm_only_ids, kfunc_id))
return 0;
return -EACCES;
}
@@ -433,7 +444,13 @@ static const struct btf_kfunc_id_set bpf_fs_kfunc_set = {
static int __init bpf_fs_kfuncs_init(void)
{
- return register_btf_kfunc_id_set(BPF_PROG_TYPE_LSM, &bpf_fs_kfunc_set);
+ int ret;
+
+ ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_LSM, &bpf_fs_kfunc_set);
+ if (ret || !IS_ENABLED(CONFIG_BINFMT_MISC_BPF))
+ return ret;
+ return register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS,
+ &bpf_fs_kfunc_set);
}
late_initcall(bpf_fs_kfuncs_init);
--
2.51.2
^ permalink raw reply related
* [PATCH v2 3/5] binfmt_misc: wire up bpf-backed 'B' entries
From: Farid Zakaria @ 2026-07-12 4:08 UTC (permalink / raw)
To: Christian Brauner, Alexei Starovoitov, Daniel Borkmann,
Martin KaFai Lau, Shuah Khan
Cc: Andrii Nakryiko, Kees Cook, Alexander Viro, Jan Kara,
Jonathan Corbet, Jann Horn, John Ericson, linux-fsdevel, linux-mm,
linux-kernel, bpf, linux-doc, linux-kselftest, Farid Zakaria
In-Reply-To: <20260711-binfmt-misc-bpf-v2-v2-0-d6591ceaf207@gmail.com>
From: Christian Brauner <brauner@kernel.org>
Activate a registered binfmt_misc_ops handler through the existing text
interface with the new 'B' entry type:
echo ':name:B:<handler-name>::::' > <binfmt_misc>/register
The offset field carries the handler name; magic, mask, and interpreter
must be empty since the program supplies both the matching and the
interpreter. Reusing the register file keeps the existing permission
model intact: activating a handler requires the same write access to a
binfmt_misc instance as any other registration, and the per user
namespace instance semantics apply unchanged. A 'B' entry in a
container's own instance shadows the host's handlers just like any
other entry, and the privilege needed to shadow e.g. all ELF binaries
is the same as for a static 'M' entry matching \x7fELF today; the only
novelty is that matching becomes programmable.
The entry takes its own reference on the ops for its whole lifetime.
It is dropped in put_binfmt_handler() next to the MISC_FMT_OPEN_FILE
interp_file cleanup, which the existing users refcount already defers
past any concurrent load_misc_binary(), and explicitly on the
registration failure path where the users refcount is not live yet.
The program runs from load_misc_binary(), never from the matching walk
under entries_lock: the read_lock disables preemption while the load
program must be able to sleep to read file content. search_binfmt_handler()
therefore only nominates the next enabled 'B' entry and the program
decides outside the lock. A declining program (returning 0) falls
through to handlers registered after it via a skip cursor and a rescan;
entries registered or removed between rescans can shift the cursor, so
a program may be consulted twice in that window, which is harmless
since matching must be free of side effects. Returning a positive value
without having selected an interpreter terminates the binfmt_misc scan
with -ENOEXEC so the remaining binary formats still get a shot.
The 'C' and 'F' flags are rejected for 'B' entries. 'F' exists to
pre-open a fixed interpreter at registration time in the registrar's
context which is meaningless for a per-exec computed path. 'C' honors
the suid bits of the matched binary while executing the interpreter;
combined with a program-chosen interpreter that would let a user
namespace root pick what runs with a setuid binary's credentials. The
computed path itself cannot widen access: it is opened with open_exec()
under the caller's credentials with the usual LSM and noexec checks,
identical to a statically registered interpreter.
Link: https://lore.kernel.org/20260704211409.1978485-1-farid.m.zakaria@gmail.com
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Co-developed-by: Farid Zakaria <farid.m.zakaria@gmail.com>
Signed-off-by: Farid Zakaria <farid.m.zakaria@gmail.com>
Assisted-by: Claude:Opus-4.8
---
Documentation/admin-guide/binfmt-misc.rst | 40 +++++++-
fs/binfmt_misc.c | 149 +++++++++++++++++++++++++++---
2 files changed, 175 insertions(+), 14 deletions(-)
diff --git a/Documentation/admin-guide/binfmt-misc.rst b/Documentation/admin-guide/binfmt-misc.rst
index 306ef48f5..de948dae7 100644
--- a/Documentation/admin-guide/binfmt-misc.rst
+++ b/Documentation/admin-guide/binfmt-misc.rst
@@ -26,11 +26,13 @@ Here is what the fields mean:
name below ``/proc/sys/fs/binfmt_misc``; cannot contain slashes ``/`` for
obvious reasons.
- ``type``
- is the type of recognition. Give ``M`` for magic and ``E`` for extension.
+ is the type of recognition. Give ``M`` for magic, ``E`` for extension and
+ ``B`` for a bpf-backed handler (see below).
- ``offset``
is the offset of the magic/mask in the file, counted in bytes. This
defaults to 0 if you omit it (i.e. you write ``:name:type::magic...``).
- Ignored when using filename extension matching.
+ Ignored when using filename extension matching. For ``B`` entries this
+ field carries the name of the bpf handler instead.
- ``magic``
is the byte sequence binfmt_misc is matching for. The magic string
may contain hex-encoded characters like ``\x0a`` or ``\xA4``. Note that you
@@ -97,6 +99,40 @@ There are some restrictions:
offset+size(magic) has to be less than 128
- the interpreter string may not exceed 127 characters
+
+bpf-backed handlers
+-------------------
+
+With ``CONFIG_BINFMT_MISC_BPF`` both the matching and the interpreter
+selection can be delegated to a bpf program. A handler is an instance of the
+``binfmt_misc_ops`` struct_ops with a sleepable ``load`` program and a
+``name``. Once the struct_ops map is registered the handler can be activated
+with a ``B`` entry that references it by name and carries neither magic,
+mask, nor interpreter::
+
+ echo ':qemu:B:my_handler::::' > register
+
+At exec time the ``load`` program receives the ``linux_binprm`` of the
+binary. It can match on the header in ``bprm->buf``, read the file itself,
+e.g. to parse ELF program headers, and derive the interpreter from the
+binary's location. It selects the interpreter by calling the
+``bpf_binprm_set_interp()`` kfunc with an absolute path and returning a
+positive value. Returning ``0`` falls through to the handlers registered
+after this one, a negative errno fails the exec with that error;
+``-ENOEXEC`` ends the binfmt_misc search but lets the remaining binary
+formats have a go. The interpreter is opened with the credentials of the
+task doing the exec, exactly as a statically registered interpreter would
+be.
+
+Handlers are looked up in the user namespace the struct_ops map was
+registered in, falling back to ancestor namespaces, mirroring how
+binfmt_misc instances themselves are looked up. The entry keeps the handler
+alive; deleting the struct_ops map only prevents new registrations.
+
+The ``C`` and ``F`` flags cannot be combined with ``B`` entries: there is no
+fixed interpreter to pre-open and a program-selected interpreter must never
+inherit the credentials of a setuid binary.
+
To use binfmt_misc you have to mount it first. You can mount it with
``mount -t binfmt_misc none /proc/sys/fs/binfmt_misc`` command, or you can add
a line ``none /proc/sys/fs/binfmt_misc binfmt_misc defaults 0 0`` to your
diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c
index fcaad14f8..4ece75f95 100644
--- a/fs/binfmt_misc.c
+++ b/fs/binfmt_misc.c
@@ -11,6 +11,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/binfmts.h>
+#include <linux/binfmt_misc.h>
#include <linux/bitops.h>
#include <linux/bits.h>
#include <linux/bug.h>
@@ -38,6 +39,7 @@
enum binfmt_misc_entry_bits {
MISC_FMT_ENABLED_BIT = 0,
MISC_FMT_MAGIC_BIT = 1,
+ MISC_FMT_BPF_BIT = 2,
};
/* Entry behavior flags, fixed at registration time. */
@@ -59,6 +61,8 @@ struct binfmt_misc_entry {
char *name;
struct dentry *dentry;
struct file *interp_file;
+ const struct binfmt_misc_ops *bpf_ops; /* bpf-backed handler ('B') */
+ const char *bpf_ops_name;
refcount_t users; /* sync removal with load_misc_binary() */
struct rcu_head rcu;
char buf[]; /* register string, fields point in here */
@@ -109,16 +113,21 @@ static bool entry_matches_extension(const struct binfmt_misc_entry *e,
* search_binfmt_handler - search for a binary handler for @bprm
* @misc: handle to binfmt_misc instance
* @bprm: binary for which we are looking for a handler
+ * @bpf_skip: number of bpf-backed handlers to skip over
*
* Search for a binary type handler for @bprm in the list of registered binary
- * type handlers.
+ * type handlers. A bpf-backed handler cannot be matched here as its program
+ * must run in sleepable context; it is returned as a candidate and the
+ * program decides in load_misc_binary(). @bpf_skip resumes the search after
+ * the first @bpf_skip candidates declined.
*
* The caller must hold the RCU read lock.
*
* Return: binary type list entry on success, NULL on failure
*/
static struct binfmt_misc_entry *
-search_binfmt_handler(struct binfmt_misc *misc, struct linux_binprm *bprm)
+search_binfmt_handler(struct binfmt_misc *misc, struct linux_binprm *bprm,
+ unsigned int bpf_skip)
{
char *dot = strrchr(bprm->interp, '.');
const char *ext = dot ? dot + 1 : NULL;
@@ -130,6 +139,15 @@ search_binfmt_handler(struct binfmt_misc *misc, struct linux_binprm *bprm)
if (!test_bit(MISC_FMT_ENABLED_BIT, &e->flags))
continue;
+ /* A bpf handler is decided in load_misc_binary(). */
+ if (test_bit(MISC_FMT_BPF_BIT, &e->flags)) {
+ if (bpf_skip) {
+ bpf_skip--;
+ continue;
+ }
+ return e;
+ }
+
if (test_bit(MISC_FMT_MAGIC_BIT, &e->flags)) {
if (entry_matches_magic(e, bprm))
return e;
@@ -146,6 +164,7 @@ search_binfmt_handler(struct binfmt_misc *misc, struct linux_binprm *bprm)
* get_binfmt_handler - try to find a binary type handler
* @misc: handle to binfmt_misc instance
* @bprm: binary for which we are looking for a handler
+ * @bpf_skip: number of bpf-backed handlers to skip over
*
* Try to find a binfmt handler for the binary type. If one is found take a
* reference to protect against removal via bm_{entry,status}_write(). The
@@ -156,13 +175,14 @@ search_binfmt_handler(struct binfmt_misc *misc, struct linux_binprm *bprm)
* Return: binary type list entry on success, NULL on failure
*/
static struct binfmt_misc_entry *get_binfmt_handler(struct binfmt_misc *misc,
- struct linux_binprm *bprm)
+ struct linux_binprm *bprm,
+ unsigned int bpf_skip)
{
struct binfmt_misc_entry *e;
guard(rcu)();
do {
- e = search_binfmt_handler(misc, bprm);
+ e = search_binfmt_handler(misc, bprm, bpf_skip);
} while (e && !refcount_inc_not_zero(&e->users));
return e;
}
@@ -182,6 +202,8 @@ static void put_binfmt_handler(struct binfmt_misc_entry *e)
exe_file_allow_write_access(e->interp_file);
filp_close(e->interp_file, NULL);
}
+ if (e->bpf_ops)
+ binfmt_misc_put_ops(e->bpf_ops);
/* Lockless walkers may still dereference this entry. */
kfree_rcu(e, rcu);
}
@@ -224,13 +246,16 @@ static int load_misc_binary(struct linux_binprm *bprm)
struct binfmt_misc_entry *fmt __free(put_binfmt_handler) = NULL;
struct file *interp_file;
struct binfmt_misc *misc;
+ const char *interpreter;
+ unsigned int bpf_skip = 0;
int retval;
misc = current_binfmt_misc();
if (!READ_ONCE(misc->enabled))
return -ENOEXEC;
- fmt = get_binfmt_handler(misc, bprm);
+retry:
+ fmt = get_binfmt_handler(misc, bprm, bpf_skip);
if (!fmt)
return -ENOEXEC;
@@ -238,6 +263,37 @@ static int load_misc_binary(struct linux_binprm *bprm)
if (bprm->interp_flags & BINPRM_FLAGS_PATH_INACCESSIBLE)
return -ENOENT;
+ if (test_bit(MISC_FMT_BPF_BIT, &fmt->flags)) {
+ /*
+ * A bpf-backed handler matches and picks the interpreter in
+ * sleepable context: > 0 means it handled the binary, 0 falls
+ * through to the handlers after this one and a negative errno
+ * fails the exec.
+ */
+ retval = fmt->bpf_ops->load(bprm);
+ if (retval < 0) {
+ /* Keep a program-supplied error within errno range. */
+ if (retval < -MAX_ERRNO)
+ retval = -ENOEXEC;
+ return retval;
+ }
+ if (!retval) {
+ /* Declined: move on to the handlers after this one. */
+ kfree(bprm->bpf_interp);
+ bprm->bpf_interp = NULL;
+ put_binfmt_handler(fmt);
+ fmt = NULL;
+ bpf_skip++;
+ goto retry;
+ }
+ /* Selecting an interpreter is part of the contract. */
+ if (!bprm->bpf_interp)
+ return -ENOEXEC;
+ interpreter = bprm->bpf_interp;
+ } else {
+ interpreter = fmt->interpreter;
+ }
+
if (fmt->flags & MISC_FMT_PRESERVE_ARGV0) {
bprm->interp_flags |= BINPRM_FLAGS_PRESERVE_ARGV0;
} else {
@@ -256,13 +312,13 @@ static int load_misc_binary(struct linux_binprm *bprm)
bprm->argc++;
/* add the interp as argv[0] */
- retval = copy_string_kernel(fmt->interpreter, bprm);
+ retval = copy_string_kernel(interpreter, bprm);
if (retval < 0)
return retval;
bprm->argc++;
/* Update interp in case binfmt_script needs it. */
- retval = bprm_change_interp(fmt->interpreter, bprm);
+ retval = bprm_change_interp(interpreter, bprm);
if (retval < 0)
return retval;
@@ -277,7 +333,7 @@ static int load_misc_binary(struct linux_binprm *bprm)
}
}
} else {
- interp_file = open_exec(fmt->interpreter);
+ interp_file = open_exec(interpreter);
}
if (IS_ERR(interp_file))
return PTR_ERR(interp_file);
@@ -428,6 +484,38 @@ static char *parse_extension_fields(struct binfmt_misc_entry *e, char *p,
return p;
}
+/* Parse the 'offset' field of a 'B' entry: the bpf handler name. */
+static char *parse_bpf_fields(struct binfmt_misc_entry *e, char *p, char del)
+{
+ char *s;
+
+ /* The 'offset' field carries the bpf handler name. */
+ s = strchr(p, del);
+ if (!s)
+ return NULL;
+ *s++ = '\0';
+ e->bpf_ops_name = p;
+ if (!e->bpf_ops_name[0] ||
+ strlen(e->bpf_ops_name) >= BINFMT_MISC_OPS_NAME_MAX)
+ return NULL;
+ p = s;
+ pr_debug("register: bpf handler: {%s}\n", e->bpf_ops_name);
+
+ /* The 'magic' field must be empty. */
+ s = strchr(p, del);
+ if (!s || s != p)
+ return NULL;
+ *s++ = '\0';
+ p = s;
+
+ /* The 'mask' field must be empty. */
+ s = strchr(p, del);
+ if (!s || s != p)
+ return NULL;
+ *s++ = '\0';
+ return s;
+}
+
/*
* This registers a new binary format, it recognises the syntax
* ':name:type:offset:magic:mask:interpreter:flags'
@@ -492,13 +580,21 @@ static struct binfmt_misc_entry *create_entry(const char __user *buffer,
pr_debug("register: type: M (magic)\n");
e->flags = BIT(MISC_FMT_ENABLED_BIT) | BIT(MISC_FMT_MAGIC_BIT);
break;
+ case 'B':
+ if (!IS_ENABLED(CONFIG_BINFMT_MISC_BPF))
+ return ERR_PTR(-EINVAL);
+ pr_debug("register: type: B (bpf)\n");
+ e->flags = BIT(MISC_FMT_ENABLED_BIT) | BIT(MISC_FMT_BPF_BIT);
+ break;
default:
return ERR_PTR(-EINVAL);
}
if (*p++ != del)
return ERR_PTR(-EINVAL);
- if (test_bit(MISC_FMT_MAGIC_BIT, &e->flags))
+ if (test_bit(MISC_FMT_BPF_BIT, &e->flags))
+ p = parse_bpf_fields(e, p, del);
+ else if (test_bit(MISC_FMT_MAGIC_BIT, &e->flags))
p = parse_magic_fields(e, p, del);
else
p = parse_extension_fields(e, p, del);
@@ -511,8 +607,13 @@ static struct binfmt_misc_entry *create_entry(const char __user *buffer,
if (!p)
return ERR_PTR(-EINVAL);
*p++ = '\0';
- if (!e->interpreter[0])
+ if (test_bit(MISC_FMT_BPF_BIT, &e->flags)) {
+ /* The program selects the interpreter at exec time. */
+ if (e->interpreter[0])
+ return ERR_PTR(-EINVAL);
+ } else if (!e->interpreter[0]) {
return ERR_PTR(-EINVAL);
+ }
pr_debug("register: interpreter: {%s}\n", e->interpreter);
/* Parse the 'flags' field. */
@@ -522,6 +623,14 @@ static struct binfmt_misc_entry *create_entry(const char __user *buffer,
if (p != buf + count)
return ERR_PTR(-EINVAL);
+ /*
+ * A program-selected interpreter cannot be pre-opened and must not
+ * inherit the credentials of a setuid binary it was chosen for.
+ */
+ if (test_bit(MISC_FMT_BPF_BIT, &e->flags) &&
+ (e->flags & (MISC_FMT_CREDENTIALS | MISC_FMT_OPEN_FILE)))
+ return ERR_PTR(-EINVAL);
+
return no_free_ptr(e);
}
@@ -575,7 +684,10 @@ static int bm_entry_show(struct seq_file *m, void *unused)
else
seq_puts(m, "disabled\n");
- seq_printf(m, "interpreter %s\n", e->interpreter);
+ if (test_bit(MISC_FMT_BPF_BIT, &e->flags))
+ seq_printf(m, "bpf %s\n", e->bpf_ops_name);
+ else
+ seq_printf(m, "interpreter %s\n", e->interpreter);
/* print the special flags */
seq_puts(m, "flags: ");
@@ -589,7 +701,9 @@ static int bm_entry_show(struct seq_file *m, void *unused)
seq_putc(m, 'F');
seq_putc(m, '\n');
- if (!test_bit(MISC_FMT_MAGIC_BIT, &e->flags)) {
+ if (test_bit(MISC_FMT_BPF_BIT, &e->flags)) {
+ /* No magic or extension to print for a bpf handler. */
+ } else if (!test_bit(MISC_FMT_MAGIC_BIT, &e->flags)) {
seq_printf(m, "extension .%s\n", e->magic);
} else {
seq_printf(m, "offset %i\nmagic ", e->offset);
@@ -840,6 +954,15 @@ static ssize_t bm_register_write(struct file *file, const char __user *buffer,
if (IS_ERR(e))
return PTR_ERR(e);
+ if (test_bit(MISC_FMT_BPF_BIT, &e->flags)) {
+ e->bpf_ops = binfmt_misc_get_ops(sb->s_user_ns, e->bpf_ops_name);
+ if (!e->bpf_ops) {
+ pr_notice("register: no bpf handler named %s\n",
+ e->bpf_ops_name);
+ return -ENOENT;
+ }
+ }
+
if (e->flags & MISC_FMT_OPEN_FILE) {
/*
* Now that we support unprivileged binfmt_misc mounts make
@@ -864,6 +987,8 @@ static ssize_t bm_register_write(struct file *file, const char __user *buffer,
exe_file_allow_write_access(f);
filp_close(f, NULL);
}
+ if (e->bpf_ops)
+ binfmt_misc_put_ops(e->bpf_ops);
return err;
}
--
2.51.2
^ permalink raw reply related
* [PATCH v2 2/5] binfmt_misc: add binfmt_misc_ops bpf struct_ops
From: Farid Zakaria @ 2026-07-12 4:08 UTC (permalink / raw)
To: Christian Brauner, Alexei Starovoitov, Daniel Borkmann,
Martin KaFai Lau, Shuah Khan
Cc: Andrii Nakryiko, Kees Cook, Alexander Viro, Jan Kara,
Jonathan Corbet, Jann Horn, John Ericson, linux-fsdevel, linux-mm,
linux-kernel, bpf, linux-doc, linux-kselftest, Farid Zakaria
In-Reply-To: <20260711-binfmt-misc-bpf-v2-v2-0-d6591ceaf207@gmail.com>
From: Christian Brauner <brauner@kernel.org>
Add the bpf plumbing for binary type handlers whose matching and
interpreter selection are implemented by a bpf program instead of a
fixed magic/extension and a fixed interpreter string recorded at
registration time. This serves relocatable binary formats where the
interpreter must be computed per binary, e.g. relative to the location
of the binary itself, as discussed for hermetic Nix-style executables.
A handler is an instance of the new binfmt_misc_ops struct_ops with a
single op:
int (*load)(struct linux_binprm *bprm);
and a name that binfmt_misc entries reference it by. struct_ops is the
sanctioned mechanism for this kind of user-supplied policy callback:
program types, attach types, and the uapi helper list are frozen, and
every recently added subsystem hook (bpf qdisc, SMC handshake control,
io_uring loop ops, sched_ext) is a struct_ops user. The op receives the
bprm as a trusted BTF pointer, so a program can match on the header in
bprm->buf, read arbitrary file content via bpf_dynptr_from_file() to
parse e.g. ELF program headers, and inspect the binary's location. No
dedicated program type, ctx blob, or uapi helper is needed.
The load program is required to be sleepable. Reliable file reads at
exec time fault in the file's pages; a non-sleepable program would be
limited to whatever happens to be resident in the page cache and would
fail sporadically on cold caches. This also constrains the caller:
binfmt_misc must invoke the op from sleepable context, which a later
patch takes care of.
The interpreter is selected through the new kfunc:
int bpf_binprm_set_interp(struct linux_binprm *bprm,
const char *path, size_t path__sz);
which enforces an absolute path shorter than PATH_MAX and stages a copy
in the bprm. The bprm is exclusively owned by the task doing the exec,
so no shared or per-CPU state is involved and nothing here can race.
The kfunc is registered for struct_ops programs with a filter limiting
it to binfmt_misc_ops programs.
Registering an ops instance (updating the struct_ops map or attaching
its link) publishes the handler under its name in a registry keyed by
the registering task's user namespace. Lookups walk the user namespace
hierarchy upwards, mirroring how binfmt_misc instances themselves are
resolved in load_binfmt_misc(). Consumers take a reference on the ops
via bpf_struct_ops_get() which pins the underlying map and programs, so
an activated handler keeps working even if the map is deleted or the
registering container goes away; deregistration only prevents new
activations, exactly like unregistering a tcp congestion ops with live
users.
Link: https://lore.kernel.org/20260704211409.1978485-1-farid.m.zakaria@gmail.com
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
fs/Kconfig.binfmt | 14 +++
fs/Makefile | 1 +
fs/binfmt_misc_bpf.c | 275 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/binfmt_misc.h | 49 ++++++++
4 files changed, 339 insertions(+)
diff --git a/fs/Kconfig.binfmt b/fs/Kconfig.binfmt
index 1949e25c7..daeac4889 100644
--- a/fs/Kconfig.binfmt
+++ b/fs/Kconfig.binfmt
@@ -168,6 +168,20 @@ config BINFMT_MISC
you have use for it; the module is called binfmt_misc. If you
don't know what to answer at this point, say Y.
+config BINFMT_MISC_BPF
+ bool "BPF-selected interpreters for misc binaries"
+ depends on BINFMT_MISC=y
+ depends on BPF_SYSCALL && BPF_JIT && DEBUG_INFO_BTF
+ help
+ Allow binfmt_misc binary type handlers to be implemented as bpf
+ struct_ops programs. Instead of matching a fixed magic and
+ redirecting to a fixed interpreter recorded at registration time
+ such handlers match binaries programmatically and compute the
+ interpreter to use per binary, e.g. relative to the location of
+ the binary itself.
+
+ If you don't know what to answer at this point, say N.
+
config COREDUMP
bool "Enable core dump support" if EXPERT
default y
diff --git a/fs/Makefile b/fs/Makefile
index 89a8a9d20..499c6670f 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_FS_ENCRYPTION) += crypto/
obj-$(CONFIG_FS_VERITY) += verity/
obj-$(CONFIG_FILE_LOCKING) += locks.o
obj-$(CONFIG_BINFMT_MISC) += binfmt_misc.o
+obj-$(CONFIG_BINFMT_MISC_BPF) += binfmt_misc_bpf.o
obj-$(CONFIG_BINFMT_SCRIPT) += binfmt_script.o
obj-$(CONFIG_BINFMT_ELF) += binfmt_elf.o
obj-$(CONFIG_COMPAT_BINFMT_ELF) += compat_binfmt_elf.o
diff --git a/fs/binfmt_misc_bpf.c b/fs/binfmt_misc_bpf.c
new file mode 100644
index 000000000..72da0964d
--- /dev/null
+++ b/fs/binfmt_misc_bpf.c
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * BPF-backed binary type handlers for binfmt_misc.
+ *
+ * A handler is a struct binfmt_misc_ops struct_ops map. Loading and
+ * registering it makes the handler available under its name in the user
+ * namespace it was registered in. A binfmt_misc 'B' entry activates it:
+ *
+ * echo ':entry:B:<handler-name>::::' > <binfmt_misc>/register
+ */
+
+#include <linux/binfmt_misc.h>
+#include <linux/binfmts.h>
+#include <linux/bpf.h>
+#include <linux/bpf_verifier.h>
+#include <linux/btf.h>
+#include <linux/btf_ids.h>
+#include <linux/cred.h>
+#include <linux/init.h>
+#include <linux/limits.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/user_namespace.h>
+
+struct bm_bpf_ops_reg {
+ struct list_head list;
+ const struct binfmt_misc_ops *ops;
+ struct bpf_link *link;
+ struct user_namespace *user_ns;
+};
+
+static DEFINE_MUTEX(bm_bpf_ops_lock);
+static LIST_HEAD(bm_bpf_ops_list);
+
+static struct bpf_struct_ops bpf_binfmt_misc_ops;
+
+static struct bm_bpf_ops_reg *bm_bpf_ops_find(const struct user_namespace *user_ns,
+ const char *name)
+{
+ struct bm_bpf_ops_reg *reg;
+
+ lockdep_assert_held(&bm_bpf_ops_lock);
+
+ list_for_each_entry(reg, &bm_bpf_ops_list, list) {
+ if (reg->user_ns == user_ns && !strcmp(reg->ops->name, name))
+ return reg;
+ }
+ return NULL;
+}
+
+/**
+ * binfmt_misc_get_ops - look up a bpf binary type handler by name
+ * @user_ns: user namespace of the binfmt_misc instance
+ * @name: name the handler was registered under
+ *
+ * Search @user_ns and its ancestors for a handler named @name, mirroring
+ * the instance lookup in load_binfmt_misc(). The returned handler stays
+ * callable until binfmt_misc_put_ops() even if the backing struct_ops map
+ * is detached or deleted in the meantime.
+ *
+ * Return: the handler on success, NULL on failure
+ */
+const struct binfmt_misc_ops *binfmt_misc_get_ops(struct user_namespace *user_ns,
+ const char *name)
+{
+ const struct user_namespace *ns;
+ struct bm_bpf_ops_reg *reg;
+
+ guard(mutex)(&bm_bpf_ops_lock);
+
+ for (ns = user_ns; ns; ns = ns->parent) {
+ reg = bm_bpf_ops_find(ns, name);
+ if (!reg)
+ continue;
+ if (!bpf_struct_ops_get(reg->ops))
+ return NULL;
+ return reg->ops;
+ }
+ return NULL;
+}
+
+void binfmt_misc_put_ops(const struct binfmt_misc_ops *ops)
+{
+ bpf_struct_ops_put(ops);
+}
+
+bool bpf_prog_is_binfmt_misc_ops(const struct bpf_prog *prog)
+{
+ return prog->type == BPF_PROG_TYPE_STRUCT_OPS &&
+ prog->aux->st_ops == &bpf_binfmt_misc_ops;
+}
+
+__bpf_kfunc_start_defs();
+
+/**
+ * bpf_binprm_set_interp - select the interpreter for the current exec
+ * @bprm: binary that is being executed
+ * @path: absolute path to the interpreter
+ * @path__sz: size of the @path buffer, including the terminating NUL
+ *
+ * To be called from the load program of a struct binfmt_misc_ops handler
+ * before returning a positive value. The path is opened with the
+ * credentials of the task doing the exec after the program returns.
+ *
+ * Return: 0 on success, a negative errno on failure
+ */
+__bpf_kfunc int bpf_binprm_set_interp(struct linux_binprm *bprm,
+ const char *path, size_t path__sz)
+{
+ size_t len;
+ char *interp;
+
+ if (!path__sz)
+ return -EINVAL;
+ len = strnlen(path, path__sz);
+ if (len == path__sz)
+ return -EINVAL;
+ if (path[0] != '/')
+ return -EINVAL;
+ if (len >= PATH_MAX)
+ return -ENAMETOOLONG;
+
+ interp = kmemdup_nul(path, len, GFP_KERNEL);
+ if (!interp)
+ return -ENOMEM;
+
+ kfree(bprm->bpf_interp);
+ bprm->bpf_interp = interp;
+ return 0;
+}
+
+__bpf_kfunc_end_defs();
+
+BTF_KFUNCS_START(bm_bpf_kfunc_ids)
+BTF_ID_FLAGS(func, bpf_binprm_set_interp, KF_SLEEPABLE)
+BTF_KFUNCS_END(bm_bpf_kfunc_ids)
+
+static int bm_bpf_kfunc_filter(const struct bpf_prog *prog, u32 kfunc_id)
+{
+ if (!btf_id_set8_contains(&bm_bpf_kfunc_ids, kfunc_id))
+ return 0;
+ if (bpf_prog_is_binfmt_misc_ops(prog))
+ return 0;
+ return -EACCES;
+}
+
+static const struct btf_kfunc_id_set bm_bpf_kfunc_set = {
+ .owner = THIS_MODULE,
+ .set = &bm_bpf_kfunc_ids,
+ .filter = bm_bpf_kfunc_filter,
+};
+
+static int bm_bpf_ops__load(struct linux_binprm *bprm)
+{
+ return 0;
+}
+
+static struct binfmt_misc_ops bm_bpf_ops_stubs = {
+ .load = bm_bpf_ops__load,
+};
+
+static int bm_bpf_init(struct btf *btf)
+{
+ return register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS,
+ &bm_bpf_kfunc_set);
+}
+
+static int bm_bpf_check_member(const struct btf_type *t,
+ const struct btf_member *member,
+ const struct bpf_prog *prog)
+{
+ u32 moff = __btf_member_bit_offset(t, member) / 8;
+
+ switch (moff) {
+ case offsetof(struct binfmt_misc_ops, load):
+ /* Reliable file reads at exec time require sleeping. */
+ if (!prog->sleepable)
+ return -EINVAL;
+ break;
+ }
+ return 0;
+}
+
+static int bm_bpf_init_member(const struct btf_type *t,
+ const struct btf_member *member,
+ void *kdata, const void *udata)
+{
+ const struct binfmt_misc_ops *uops = udata;
+ struct binfmt_misc_ops *ops = kdata;
+ u32 moff = __btf_member_bit_offset(t, member) / 8;
+
+ switch (moff) {
+ case offsetof(struct binfmt_misc_ops, name):
+ if (bpf_obj_name_cpy(ops->name, uops->name,
+ sizeof(ops->name)) <= 0)
+ return -EINVAL;
+ return 1;
+ }
+ return 0;
+}
+
+static int bm_bpf_validate(void *kdata)
+{
+ struct binfmt_misc_ops *ops = kdata;
+
+ if (!ops->load)
+ return -EINVAL;
+ return 0;
+}
+
+static int bm_bpf_reg(void *kdata, struct bpf_link *link)
+{
+ struct binfmt_misc_ops *ops = kdata;
+ struct bm_bpf_ops_reg *reg;
+
+ reg = kzalloc_obj(*reg, GFP_KERNEL_ACCOUNT);
+ if (!reg)
+ return -ENOMEM;
+
+ reg->ops = ops;
+ reg->link = link;
+ reg->user_ns = get_user_ns(current_user_ns());
+
+ guard(mutex)(&bm_bpf_ops_lock);
+
+ if (bm_bpf_ops_find(reg->user_ns, ops->name)) {
+ put_user_ns(reg->user_ns);
+ kfree(reg);
+ return -EEXIST;
+ }
+
+ list_add(®->list, &bm_bpf_ops_list);
+ return 0;
+}
+
+static void bm_bpf_unreg(void *kdata, struct bpf_link *link)
+{
+ struct bm_bpf_ops_reg *reg;
+
+ guard(mutex)(&bm_bpf_ops_lock);
+
+ list_for_each_entry(reg, &bm_bpf_ops_list, list) {
+ if (reg->ops == kdata && reg->link == link) {
+ list_del(®->list);
+ put_user_ns(reg->user_ns);
+ kfree(reg);
+ return;
+ }
+ }
+}
+
+static const struct bpf_verifier_ops bm_bpf_verifier_ops = {
+ .get_func_proto = bpf_base_func_proto,
+ .is_valid_access = bpf_tracing_btf_ctx_access,
+};
+
+static struct bpf_struct_ops bpf_binfmt_misc_ops = {
+ .verifier_ops = &bm_bpf_verifier_ops,
+ .init = bm_bpf_init,
+ .check_member = bm_bpf_check_member,
+ .init_member = bm_bpf_init_member,
+ .validate = bm_bpf_validate,
+ .reg = bm_bpf_reg,
+ .unreg = bm_bpf_unreg,
+ .cfi_stubs = &bm_bpf_ops_stubs,
+ .name = "binfmt_misc_ops",
+ .owner = THIS_MODULE,
+};
+
+static int __init bm_bpf_struct_ops_init(void)
+{
+ return register_bpf_struct_ops(&bpf_binfmt_misc_ops, binfmt_misc_ops);
+}
+late_initcall(bm_bpf_struct_ops_init);
diff --git a/include/linux/binfmt_misc.h b/include/linux/binfmt_misc.h
new file mode 100644
index 000000000..e1d26c430
--- /dev/null
+++ b/include/linux/binfmt_misc.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_BINFMT_MISC_H
+#define _LINUX_BINFMT_MISC_H
+
+#include <linux/types.h>
+
+struct bpf_prog;
+struct linux_binprm;
+struct user_namespace;
+
+#define BINFMT_MISC_OPS_NAME_MAX 16
+
+/**
+ * struct binfmt_misc_ops - bpf-backed binary type handler
+ * @load: match @bprm and select an interpreter via bpf_binprm_set_interp();
+ * returns > 0 if the binary was handled, 0 to fall through to the
+ * handlers registered after this one, a negative errno to fail the
+ * exec; -ENOEXEC does not fail the exec but moves on to the
+ * remaining binary formats
+ * @name: name that 'B' entries reference the handler by
+ */
+struct binfmt_misc_ops {
+ int (*load)(struct linux_binprm *bprm);
+ char name[BINFMT_MISC_OPS_NAME_MAX];
+};
+
+#ifdef CONFIG_BINFMT_MISC_BPF
+const struct binfmt_misc_ops *binfmt_misc_get_ops(struct user_namespace *user_ns,
+ const char *name);
+void binfmt_misc_put_ops(const struct binfmt_misc_ops *ops);
+bool bpf_prog_is_binfmt_misc_ops(const struct bpf_prog *prog);
+#else
+static inline const struct binfmt_misc_ops *
+binfmt_misc_get_ops(struct user_namespace *user_ns, const char *name)
+{
+ return NULL;
+}
+
+static inline void binfmt_misc_put_ops(const struct binfmt_misc_ops *ops)
+{
+}
+
+static inline bool bpf_prog_is_binfmt_misc_ops(const struct bpf_prog *prog)
+{
+ return false;
+}
+#endif /* CONFIG_BINFMT_MISC_BPF */
+
+#endif /* _LINUX_BINFMT_MISC_H */
--
2.51.2
^ permalink raw reply related
* [PATCH v2 1/5] exec: stash a bpf-selected interpreter in struct linux_binprm
From: Farid Zakaria @ 2026-07-12 4:08 UTC (permalink / raw)
To: Christian Brauner, Alexei Starovoitov, Daniel Borkmann,
Martin KaFai Lau, Shuah Khan
Cc: Andrii Nakryiko, Kees Cook, Alexander Viro, Jan Kara,
Jonathan Corbet, Jann Horn, John Ericson, linux-fsdevel, linux-mm,
linux-kernel, bpf, linux-doc, linux-kselftest, Farid Zakaria
In-Reply-To: <20260711-binfmt-misc-bpf-v2-v2-0-d6591ceaf207@gmail.com>
From: Christian Brauner <brauner@kernel.org>
The upcoming bpf-backed binfmt_misc handlers select the interpreter for
a binary programmatically at exec time. The selection runs before
load_misc_binary() has copied the binary path from bprm->interp into
the argument vector, so the selecting program cannot go through
bprm_change_interp() directly without clobbering argv[1].
Stage the selected path in the bprm instead. The bprm is exclusively
owned by the task doing the exec so no synchronization is needed. The
consumer frees and clears the field once the exec attempt that set it
is finished; free_bprm() covers all error paths.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
fs/exec.c | 1 +
include/linux/binfmts.h | 1 +
2 files changed, 2 insertions(+)
diff --git a/fs/exec.c b/fs/exec.c
index b92fe7db1..7c9e28f54 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1418,6 +1418,7 @@ static void free_bprm(struct linux_binprm *bprm)
/* If a binfmt changed the interp, free it. */
if (bprm->interp != bprm->filename)
kfree(bprm->interp);
+ kfree(bprm->bpf_interp);
kfree(bprm->fdpath);
kfree(bprm);
}
diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
index 7e7333b7b..1dbec6905 100644
--- a/include/linux/binfmts.h
+++ b/include/linux/binfmts.h
@@ -65,6 +65,7 @@ struct linux_binprm {
of the time same as filename, but could be
different for binfmt_{misc,script} */
const char *fdpath; /* generated filename for execveat */
+ const char *bpf_interp; /* interpreter selected by a bpf handler */
unsigned interp_flags;
int execfd; /* File descriptor of the executable */
unsigned long exec;
--
2.51.2
^ permalink raw reply related
* [PATCH v2 0/5] binfmt_misc: bpf-backed binary type handlers
From: Farid Zakaria @ 2026-07-12 4:08 UTC (permalink / raw)
To: Christian Brauner, Alexei Starovoitov, Daniel Borkmann,
Martin KaFai Lau, Shuah Khan
Cc: Andrii Nakryiko, Kees Cook, Alexander Viro, Jan Kara,
Jonathan Corbet, Jann Horn, John Ericson, linux-fsdevel, linux-mm,
linux-kernel, bpf, linux-doc, linux-kselftest, Farid Zakaria
This is a continuation of Christian's bpf-backed binfmt_misc POC [1], which he offered
to hand off to me. I am carrying it forward. The kernel design is his and is
unchanged. This series rebases it onto his binfmt_misc locking/cleanup series [2]
and adds selftests (+ fixed from the one's shared), and therefore does
not apply to mainline alone.
As for motivation for this whole change, Christian did a great VL;MR;
write-up [1].
TL;DR: binfmt_misc can match a binary and hand it to a fixed interpreter,
but it can't match programmatically or compute the interpreter per binary.
The Nix community would love to support relocatable binaries, where the
correct loader can only be found relative to the binary itself.
This adds a 'B' handler type: a binfmt_misc_ops struct_ops program that
inspects the binary and picks the interpreter with one new kfunc,
bpf_binprm_set_interp().
bpftool struct_ops register nix_origin.bpf.o /sys/fs/bpf
echo ':nix-origin:B:nix_origin::::' > /proc/sys/fs/binfmt_misc/register
Patch 5's nix_origin.bpf.c is Christian's example program with a small fix
so it passes the verifier. Christian, happy to add a Co-developed-by: and your
Signed-off-by: if you would like the credit in the trailer.
For those that want to validate this functionality on a NixOS machine,
you may find my flake.nix handy [3]. It builds the kernel, compiles the
bpf objects against its BTF, and runs the selftest on boot,
# be sure to change virtualisation.sharedDirectories in flake.nix to your checkout
$ nix build .#nixosConfigurations.micro-vm.config.system.build.vm -o /tmp/vm
$ NIXPKGS_QEMU_KERNEL_micro_vm=$PWD/arch/x86/boot/bzImage \
$ NIX_DISK_IMAGE=/tmp/vm.qcow2 \
$ QEMU_KERNEL_PARAMS="binfmt_autotest console=ttyS0" \
$ QEMU_OPTS="-nographic -no-reboot" \
/tmp/vm/bin/run-micro-vm-vm
[1] https://lore.kernel.org/r/20260707-work-bpf-binfmt_misc-v1-0-74b995c84ec1@kernel.org
[2] https://lore.kernel.org/all/20260710-work-binfmt_misc-locking-v3-0-a162f7cb58d6@kernel.org/
[3] https://gist.github.com/fzakaria/0155e11a0882bd3d6e63f4070e7fac0c
To: Christian Brauner <brauner@kernel.org>
To: Alexei Starovoitov <ast@kernel.org>
To: Daniel Borkmann <daniel@iogearbox.net>
To: Martin KaFai Lau <martin.lau@linux.dev>
To: Shuah Khan <shuah@kernel.org>
Cc: Andrii Nakryiko <andrii@kernel.org>
Cc: Kees Cook <kees@kernel.org>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: Jan Kara <jack@suse.cz>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Jann Horn <jannh@google.com>
Cc: John Ericson <mail@johnericson.me>
Cc: linux-fsdevel@vger.kernel.org
Cc: linux-mm@kvack.org
Cc: linux-kernel@vger.kernel.org
Cc: bpf@vger.kernel.org
Cc: linux-doc@vger.kernel.org
Cc: linux-kselftest@vger.kernel.org
Signed-off-by: Farid Zakaria <farid.m.zakaria@gmail.com>
---
Changes in v2:
- Patch 5 adds new tests that validate the functionality:
- a bpf handler matches a synthetic aarch64 header and routes it to a fixed
interpreter the program chooses;
- a handler resolves a "$ORIGIN/..."-relative PT_INTERP to an
interpreter co-located with the binary (i.e, Nix usecase);
- Patch 3 is rebased to fit the new binfmt_misc code style: the 'B' field parsing is now a
parse_bpf_fields() helper matching the new parse_{magic,extension}_fields()
split, and load_misc_binary() keeps the bpf retry loop in the
__free() cleanup style.
- Link to v1: https://lore.kernel.org/r/20260707-work-bpf-binfmt_misc-v1-0-74b995c84ec1@kernel.org
---
Christian Brauner (4):
exec: stash a bpf-selected interpreter in struct linux_binprm
binfmt_misc: add binfmt_misc_ops bpf struct_ops
binfmt_misc: wire up bpf-backed 'B' entries
bpf: allow fs kfuncs for binfmt_misc_ops programs
Farid Zakaria (1):
selftests/exec: add binfmt_misc bpf-backed handler test
Documentation/admin-guide/binfmt-misc.rst | 40 +++-
fs/Kconfig.binfmt | 14 ++
fs/Makefile | 1 +
fs/binfmt_misc.c | 149 +++++++++++-
fs/binfmt_misc_bpf.c | 275 +++++++++++++++++++++++
fs/bpf_fs_kfuncs.c | 23 +-
fs/exec.c | 1 +
include/linux/binfmt_misc.h | 49 ++++
include/linux/binfmts.h | 1 +
tools/testing/selftests/exec/Makefile | 37 +++
tools/testing/selftests/exec/binfmt_bpf_app.c | 12 +
tools/testing/selftests/exec/binfmt_bpf_interp.c | 15 ++
tools/testing/selftests/exec/binfmt_misc_bpf.c | 260 +++++++++++++++++++++
tools/testing/selftests/exec/bpf_interp.bpf.c | 52 +++++
tools/testing/selftests/exec/nix_origin.bpf.c | 179 +++++++++++++++
15 files changed, 1091 insertions(+), 17 deletions(-)
---
base-commit: 6203a47c1d78f948cd5e9ad1a4c089745e466870
change-id: 20260711-binfmt-misc-bpf-v2-0f5c1f99b9ed
Best regards,
--
Farid Zakaria <farid.m.zakaria@gmail.com>
^ permalink raw reply
* Re: [PATCH 1/6] dt-bindings: iio: adc: Add AD7768
From: Jonathan Cameron @ 2026-07-12 1:39 UTC (permalink / raw)
To: David Lechner
Cc: Janani Sunil, Nuno Sá, Michael Hennerich, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Olivier Moysan,
Philipp Zabel, Linus Walleij, Bartosz Golaszewski,
Jonathan Corbet, Shuah Khan, linux, linux-iio, devicetree,
linux-kernel, linux-gpio, linux-doc, jananisunil.dev
In-Reply-To: <a96caaaf-5cac-4bc0-9e74-e48b6fe0afc1@baylibre.com>
On Sat, 11 Jul 2026 09:40:32 -0500
David Lechner <dlechner@baylibre.com> wrote:
> On 7/9/26 7:33 PM, Jonathan Cameron wrote:
> >>> + adi,common-mode-output:
> >>> + $ref: /schemas/types.yaml#/definitions/string
> >>> + enum:
> >>> + - avdd-avss-half
> >>> + - 1.65V
> >>> + - 2.5V
> >>> + - 2.14V
> >>> + description:
> >>> + Common mode voltage output selection.
> >>
> >> Why not using standard regulator provider bindings for this?
> >
> > Interesting question. If that was done there would need to be
> > a consumer which means explicit modelling of any analog circuit.
> > We do that in a few cases but so far (and yup this is a driver thing
> > in a dt-binding) I don't think we have any way to consumer data when
> > a backend is involved.
>
> There is also the regulator-always-on property, so strictly speaking,
> a consumer is not required.
Makes some sense I guess.
How would the voltage be controlled? Set min and max to same value?
J
>
> >
> >>
> >>> +
> >>> + adi,vcm-power-down:
> >>> + type: boolean
> >>> + description: Power down the common mode output buffer
> >>
> >> Is the buffer separate from the output? In that case I would expect
> >> buffer to be in the property name, otherwise this should just be
> >> part of the enum options above (and the default one at that).
> >>
>
^ permalink raw reply
* Re: [PATCH v7 17/17] docs: iio: add documentation for ad9910 driver
From: Jonathan Cameron @ 2026-07-12 1:35 UTC (permalink / raw)
To: Rodrigo Alencar via B4 Relay
Cc: rodrigo.alencar, linux-iio, devicetree, linux-kernel, linux-doc,
linux-hardening, Lars-Peter Clausen, Michael Hennerich,
David Lechner, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel, Jonathan Corbet, Shuah Khan,
Kees Cook, Gustavo A. R. Silva
In-Reply-To: <20260707-ad9910-iio-driver-v7-17-a4ec30f63700@analog.com>
On Tue, 07 Jul 2026 15:04:38 +0100
Rodrigo Alencar via B4 Relay <devnull+rodrigo.alencar.analog.com@kernel.org> wrote:
> From: Rodrigo Alencar <rodrigo.alencar@analog.com>
>
> Add documentation for the AD9910 DDS IIO driver, which describes channels,
> DDS modes, attributes and ABI usage examples.
>
> Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
Based on my slightly tired Saturday afternoon reading I'm happy with how all this
looks now. Did find one thing to comment on though ;)
> ---
> Documentation/iio/ad9910.rst | 792 +++++++++++++++++++++++++++++++++++++++++++
> Documentation/iio/index.rst | 1 +
> MAINTAINERS | 1 +
> 3 files changed, 794 insertions(+)
>
> diff --git a/Documentation/iio/ad9910.rst b/Documentation/iio/ad9910.rst
> new file mode 100644
> index 000000000000..ab5223c70c01
> --- /dev/null
> +++ b/Documentation/iio/ad9910.rst
> +
> + echo 1 > /sys/bus/iio/devices/iio\:device0/out_altcurrent100_powerdown
> \ No newline at end of file
Add one.
There were a few really trivial things sashiko called out in earlier
patches such as %lu for long unsigned prints. I'll just assume you'll
tidy any of those up for v8 + the stuff Andy pointed out.
Otherwise I think this is all looking good. Nice work on such a complex
device.
Thanks,
Jonathan
^ permalink raw reply
* Re: [PATCH v7 16/17] iio: ABI: add docs for ad9910 sysfs and debugfs entries
From: Jonathan Cameron @ 2026-07-12 1:19 UTC (permalink / raw)
To: Rodrigo Alencar via B4 Relay
Cc: rodrigo.alencar, linux-iio, devicetree, linux-kernel, linux-doc,
linux-hardening, Lars-Peter Clausen, Michael Hennerich,
David Lechner, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel, Jonathan Corbet, Shuah Khan,
Kees Cook, Gustavo A. R. Silva
In-Reply-To: <20260707-ad9910-iio-driver-v7-16-a4ec30f63700@analog.com>
On Tue, 07 Jul 2026 15:04:37 +0100
Rodrigo Alencar via B4 Relay <devnull+rodrigo.alencar.analog.com@kernel.org> wrote:
> From: Rodrigo Alencar <rodrigo.alencar@analog.com>
>
> Add custom ABI documentation files for the DDS AD9910 with sysfs and
> debugfs entries. The sysfs ones control parameters from the Digital Ramp
> Generator and OSK engine. The debugfs attributes are read-only and
> evalutate into source channel labels for each DDS core parameter.
>
> Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
Sashiko called out on earlier patch adding IIO_FREQUENCY that it might
need ABI docs in the main ABI file. Now we only add that when a driver
first uses, it but I do see some standard stuff being added in patch 11
for offset and scale, so I'd expect to see a patch to
sysfs-bus-iio
Key being to define the units. That may mean adding a _raw entry even
though you don't actually use it in that patch.
> ---
> Documentation/ABI/testing/debugfs-iio-ad9910 | 60 ++++++++++++++++++++++
> .../ABI/testing/sysfs-bus-iio-frequency-ad9910 | 31 +++++++++++
> MAINTAINERS | 2 +
> 3 files changed, 93 insertions(+)
>
> diff --git a/Documentation/ABI/testing/debugfs-iio-ad9910 b/Documentation/ABI/testing/debugfs-iio-ad9910
> new file mode 100644
> index 000000000000..b4a125bc1007
> --- /dev/null
> +++ b/Documentation/ABI/testing/debugfs-iio-ad9910
> @@ -0,0 +1,60 @@
> +What: /sys/kernel/debug/iio/iio:deviceX/frequency_source
> +KernelVersion: 7.3
> +Contact: linux-iio@vger.kernel.org
> +Description:
> + Read-only. Reports the channel label that corresponds to which of the
Maybe reference label via iio:deviceX/*_label or something like that to
make the point it is in the related per channel label sysfs file.
> + on-chip signal generators is currently driving the output frequency.
> +
> + Resolution order (first match wins):
> +
I wonder if this is correct place to provide this info.
Sooner or later I'd expect the attribute to be used in another driver and
annoyingly we can only document them in one place or the docs build goes
wrong. So maybe just he bit above should be here and this priority stuff
belongs in the per driver main doc? (I'm wondering if an earlier me
told you to put it here - sorry if so!)
> + - ``ram`` — RAM playback is enabled and its destination is
> + frequency, phase (polar), amplitude, or polar.
> + - ``drg_frequency`` — the Digital Ramp Generator is enabled
> + and its destination is frequency.
> + - ``parallel_frequency`` — the parallel data port is enabled
> + and the frequency scan element is active.
> + - ``profileN`` (N = 0..7) — none of the above modulation
> + sources are driving frequency, so the currently selected
> + single-tone profile register is in effect.
> +
^ permalink raw reply
* Re: [PATCH v7 07/17] iio: test: add kunit tests for channel prefix naming generation
From: Jonathan Cameron @ 2026-07-12 1:09 UTC (permalink / raw)
To: Rodrigo Alencar via B4 Relay
Cc: rodrigo.alencar, linux-iio, devicetree, linux-kernel, linux-doc,
linux-hardening, Lars-Peter Clausen, Michael Hennerich,
David Lechner, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel, Jonathan Corbet, Shuah Khan,
Kees Cook, Gustavo A. R. Silva
In-Reply-To: <20260707-ad9910-iio-driver-v7-7-a4ec30f63700@analog.com>
On Tue, 07 Jul 2026 15:04:28 +0100
Rodrigo Alencar via B4 Relay <devnull+rodrigo.alencar.analog.com@kernel.org> wrote:
> From: Rodrigo Alencar <rodrigo.alencar@analog.com>
>
> Add a KUnit test suite covering __iio_chan_prefix_emit(), the helper
> that builds IIO sysfs attribute name prefixes from an iio_chan_spec.
> The suite groups cases by the enum iio_shared_by mode it exercises:
>
> - IIO_SHARED_BY_ALL: produces an empty prefix.
> - IIO_SHARED_BY_DIR: emits direction only ("in" / "out").
> - IIO_SHARED_BY_TYPE: emits "<dir>_<type>" and the differential
> "<dir>_<type>-<type>" variant.
> - IIO_SEPARATE: covers the full matrix of indexed, differential,
> modified, output and extend_name combinations, plus the two
> documented error paths (differential without indexed, differential
> with modifier).
>
> A final case exercises the seq_buf overflow path by passing an
> undersized buffer and expects -EOVERFLOW.
>
> Because __iio_chan_prefix_emit() is static, the test translation unit
> is pulled into industrialio-core.c.
Isn't there some magic route cases like this that makes it non static
only when self tests are enabled?
Claude tells me to look at include/kunit/visibility.h
Very nice. A couple of really small additions requested inline.
I might well have missed where you exercised the corners requested though!
+ I'll need an Ack from Lars for that maintainers entry. I'll guess that
Lars won't give one as not very active at the moment in this area.
Jonathan
>
> Also, an entry is created under MAINTAINERS dedicated to tests for IIO
> core helpers.
>
> Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
> ---
> MAINTAINERS | 8 +
> drivers/iio/industrialio-core.c | 4 +
> drivers/iio/test/Kconfig | 14 ++
> drivers/iio/test/iio-test-channel-prefix.c | 246 +++++++++++++++++++++++++++++
> 4 files changed, 272 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 2b1ec46c5919..57ffc0dcfdb6 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -12634,6 +12634,14 @@ F: include/dt-bindings/iio/
> F: include/linux/iio/
> F: tools/iio/
>
> +IIO CORE KUNIT TESTS
> +M: Lars-Peter Clausen <lars@metafoo.de>
I'd need an Ack from Lars for this entry. If we don't get one are you
fine looking after this without Lars listed?
> +M: Rodrigo Alencar <rodrigo.alencar@analog.com>
> +L: linux-iio@vger.kernel.org
> +S: Maintained
> +F: drivers/iio/test/iio-test-channel-prefix.c
> +F: drivers/iio/test/iio-test-format.c
> +
> IIO UNIT CONVERTER
> M: Peter Rosin <peda@lysator.liu.se>
> L: linux-iio@vger.kernel.org
> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> index ecc69adf61de..78a3c27d17a1 100644
> --- a/drivers/iio/industrialio-core.c
> +++ b/drivers/iio/industrialio-core.c
> @@ -2232,6 +2232,10 @@ EXPORT_SYMBOL_GPL(iio_device_get_current_mode);
> subsys_initcall(iio_init);
> module_exit(iio_exit);
>
> +#if IS_ENABLED(CONFIG_IIO_CHANNEL_PREFIX_KUNIT_TEST)
> +#include "test/iio-test-channel-prefix.c"
> +#endif
> +
> MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
> MODULE_DESCRIPTION("Industrial I/O core");
> MODULE_LICENSE("GPL");
> diff --git a/drivers/iio/test/iio-test-channel-prefix.c b/drivers/iio/test/iio-test-channel-prefix.c
> new file mode 100644
> index 000000000000..e6f2739331f2
> --- /dev/null
> +++ b/drivers/iio/test/iio-test-channel-prefix.c
> +
> +static void iio_test_prefix_shared_by_all(struct kunit *test)
> +{
> + const struct iio_chan_spec chan = {
> + .type = IIO_VOLTAGE,
I'd throw some stuff that won't be used in here. Maybe even have
two different ones where we ignore things and then end up with the
same answer? Same for the other ones where fields are ignored.
Obviously it is a bit of a dead chicken test (wave it around
and if still dead, all good :) so only really illustrates that
typically there is more stuff there that gets ignored.
> + };
> + char *buf = iio_test_prefix_alloc(test);
> + ssize_t ret;
> +
> + ret = __iio_chan_prefix_emit(NULL, &chan, IIO_SHARED_BY_ALL,
> + buf, PREFIX_BUF_SIZE);
> + EXPECT_PREFIX(test, buf, ret, "");
> +}
> +static void iio_test_prefix_separate_modified(struct kunit *test)
> +{
> + const struct iio_chan_spec chan = {
> + .type = IIO_ACCEL,
> + .modified = 1,
> + .channel2 = IIO_MOD_X,
> + };
> + char *buf = iio_test_prefix_alloc(test);
> + ssize_t ret;
> +
> + ret = __iio_chan_prefix_emit(NULL, &chan, IIO_SEPARATE,
> + buf, PREFIX_BUF_SIZE);
> + EXPECT_PREFIX(test, buf, ret, "in_accel_x");
I think it is worth one indexed + modified test
> +}
> +
> +static void iio_test_prefix_separate_extend_name(struct kunit *test)
> +{
> + const struct iio_chan_spec chan = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 2,
> + .extend_name = "supply",
> + };
> + char *buf = iio_test_prefix_alloc(test);
> + ssize_t ret;
> +
> + ret = __iio_chan_prefix_emit(NULL, &chan, IIO_SEPARATE,
> + buf, PREFIX_BUF_SIZE);
> + EXPECT_PREFIX(test, buf, ret, "in_voltage2_supply");
> +}
> +
> +static void iio_test_prefix_output_separate(struct kunit *test)
> +{
> + const struct iio_chan_spec chan = {
> + .type = IIO_VOLTAGE,
> + .output = 1,
> + .indexed = 1,
> + .channel = 0,
> + };
> + char *buf = iio_test_prefix_alloc(test);
> + ssize_t ret;
> +
> + ret = __iio_chan_prefix_emit(NULL, &chan, IIO_SEPARATE,
> + buf, PREFIX_BUF_SIZE);
> + EXPECT_PREFIX(test, buf, ret, "out_voltage0");
> +}
^ permalink raw reply
* Re: [PATCH v7 03/17] iio: ABI: add parent entry for iio channels
From: Jonathan Cameron @ 2026-07-12 0:51 UTC (permalink / raw)
To: Rodrigo Alencar via B4 Relay
Cc: rodrigo.alencar, linux-iio, devicetree, linux-kernel, linux-doc,
linux-hardening, Lars-Peter Clausen, Michael Hennerich,
David Lechner, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel, Jonathan Corbet, Shuah Khan,
Kees Cook, Gustavo A. R. Silva
In-Reply-To: <20260707-ad9910-iio-driver-v7-3-a4ec30f63700@analog.com>
On Tue, 07 Jul 2026 15:04:24 +0100
Rodrigo Alencar via B4 Relay <devnull+rodrigo.alencar.analog.com@kernel.org> wrote:
> From: Rodrigo Alencar <rodrigo.alencar@analog.com>
>
> Add documentation for a read-only sysfs attribute that allows to expose
> parent-child relationships between IIO channels.
>
> Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
> +
> + For example, an IIO device may expose a primary output channel as
> + "out_voltage0" together with a set of profile-based or mode-based
> + subchannels used to configure or compose the output signal. These
> + subchannels may carry common attributes such as frequency, phase, etc.
> + They do not represent physical outputs, but rather logical/virtual
> + channels grouping configurations that affect the primary output.
> + Each subchannel exposes a *_parent attribute reading "out_voltage0",
> + allowing userspace to group controls under the output channel they
> + belong to rather than treating them as unrelated top-level channels.
> +
Nice. Thanks for adding that.
Jonathan
> What: /sys/bus/iio/devices/iio:deviceX/in_phaseY_raw
> KernelVersion: 4.18
> Contact: linux-iio@vger.kernel.org
>
^ permalink raw reply
* [PATCH v2] docs/ja_JP: translate submitting-patches.rst (sign-off)
From: Akiyoshi Kurita @ 2026-07-11 21:55 UTC (permalink / raw)
To: linux-doc; +Cc: linux-kernel, corbet, akiyks, weibu
Translate the "Include PATCH in the subject" and "Sign your work -
the Developer's Certificate of Origin" sections into Japanese.
Keep the DCO text as a literal block to match commit 999161066dc5
("docs: submitting-patches: Fix section structure around DCO").
Signed-off-by: Akiyoshi Kurita <weibu@redadmin.org>
---
Changes in v2:
- Added the Japanese translation of the "Include PATCH in the subject" section.
- Updated the DCO translation to match the current English text and structure.
- Kept the DCO statement in a literal block following commit 999161066dc5.
.../ja_JP/process/submitting-patches.rst | 66 +++++++++++++++++++
1 file changed, 66 insertions(+)
diff --git a/Documentation/translations/ja_JP/process/submitting-patches.rst b/Documentation/translations/ja_JP/process/submitting-patches.rst
index d31d469909e4..7f97334ff479 100644
--- a/Documentation/translations/ja_JP/process/submitting-patches.rst
+++ b/Documentation/translations/ja_JP/process/submitting-patches.rst
@@ -402,3 +402,69 @@ ping したりする前に、少なくとも 1 週間は待ってください。
パッチまたはパッチシリーズの修正版を投稿する場合は、"RESEND" を
追加しないでください。"RESEND" は、前回の投稿から一切変更していない
パッチまたはパッチシリーズを再送する場合にのみ使います。
+
+
+件名に PATCH を含める
+---------------------
+
+Linus と linux-kernel メーリングリストには大量のメールが届くため、
+件名の先頭に ``[PATCH]`` を付けることが一般的な慣例となっています。
+これにより、Linus や他のカーネル開発者は、パッチとその他の議論を
+容易に区別できます。
+
+``git send-email`` は、この指定を自動的に行います。
+
+
+作業への署名 - Developer's Certificate of Origin
+--------------------------------------------------
+
+誰が何を行ったのかを追跡しやすくするため、特にパッチが複数階層の
+メンテナーを経由して最終的にカーネルへ取り込まれる場合に備えて、
+メールでやり取りされるパッチには sign-off の手続きが導入されています。
+
+sign-off は、パッチの説明の末尾に追加する単純な一行です。これは、
+そのパッチを自分で作成したか、オープンソースのパッチとして提出する
+権利を持っていることを証明します。以下を証明できる場合、規則は単純です::
+
+ Developer's Certificate of Origin 1.1
+
+ このプロジェクトへ貢献することにより、私は以下を証明します:
+
+ (a) この貢献の全部または一部を私が作成し、ファイルに示された
+ オープンソースライセンスの下で提出する権利を有していること。
+ または、
+
+ (b) 私の知る限り、この貢献は適切なオープンソースライセンスの
+ 対象となる以前の成果物に基づいており、そのライセンスに従って、
+ 私が全部または一部を変更した成果物を、ファイルに示された
+ 同じオープンソースライセンスの下で提出する権利を有していること。
+ ただし、別のライセンスで提出することを許可されている場合を除く。
+ または、
+
+ (c) この貢献は、(a)、(b)、または (c) を証明した別の人物から
+ 私へ直接提供されたものであり、私が変更を加えていないこと。
+
+ (d) このプロジェクトおよび貢献が公開されること、ならびに私が
+ 提出したすべての個人情報と sign-off を含む貢献の記録が
+ 無期限に保存され、このプロジェクトまたは関連する
+ オープンソースライセンスに従って再配布される可能性があることを
+ 理解し、同意すること。
+
+上記を証明できる場合は、次のような行を追加します::
+
+ Signed-off-by: Random J Developer <random@developer.example.org>
+
+既知の身元を使用してください。匿名での貢献は認められません。
+``git commit -s`` を使用すると、この行を自動的に追加できます。
+
+revert にも ``Signed-off-by:`` を含める必要があります。
+``git revert -s`` を使用すると、自動的に追加できます。
+
+末尾に追加のタグを付ける人もいます。現時点では無視されますが、
+社内手続きを示したり、sign-off に関する特記事項を記録したりするために
+使用できます。
+
+作者の SoB に続く追加の SoB(``Signed-off-by:``)は、パッチの開発には
+関与せず、その取り扱いや転送を行った人によるものです。SoB の連鎖は、
+パッチがメンテナーを経て最終的に Linus へ届いた実際の経路を反映する
+必要があります。最初の SoB は、単独の主要作者であることを示します。
--
2.52.0
^ permalink raw reply related
* Re: [patch 13/18] entry: Make trace_syscall_enter() return type bool
From: Thomas Gleixner @ 2026-07-11 20:33 UTC (permalink / raw)
To: Michal Suchánek
Cc: LKML, Peter Zijlstra, Michael Ellerman, Shrikanth Hegde,
linuxppc-dev, Kees Cook, Huacai Chen, loongarch, Paul Walmsley,
Palmer Dabbelt, linux-riscv, Sven Schnelle, linux-s390, x86,
Mark Rutland, Jinjie Ruan, Andy Lutomirski, Oleg Nesterov,
Richard Henderson, Russell King, Catalin Marinas, Guo Ren,
Geert Uytterhoeven, Thomas Bogendoerfer, Helge Deller,
Yoshinori Sato, Richard Weinberger, Chris Zankel,
linux-arm-kernel, linux-alpha, linux-csky, linux-m68k, linux-mips,
linux-parisc, linux-sh, linux-um, Arnd Bergmann, Vineet Gupta,
Will Deacon, Brian Cain, Michal Simek, Dinh Nguyen,
David S. Miller, Andreas Larsson, linux-snps-arc, linux-hexagon,
linux-openrisc, sparclinux, linux-arch, Jonathan Corbet,
linux-doc
In-Reply-To: <alDQ7isUKJFl8Va4@kunlun.suse.cz>
Michal!
On Fri, Jul 10 2026 at 13:01, Michal Suchánek wrote:
> On Wed, Jul 08, 2026 at 10:34:38PM +0200, Thomas Gleixner wrote:
>> does not make #2 magically go away. It's still the same problem whether
>> you like it or not.
>
> However, reading the syscall number from pt_regs only after
> syscall_enter_from_user_mode exits does.
That does not solve anything at all.
TBH, your communication style is annoying as hell. You fail to provide
any useful arguments and explanations despite me giving you a proper
analysis. And I'm absolutely tired of this.
So let me try again for _ONE_ last time to explain you why your ppc/s390
world view is broken and let's look at the current code (irrelevant
portions omitted).
static __always_inline long syscall_trace_enter(struct pt_regs *regs, unsigned long work)
{
if (work & SYSCALL_WORK_SYSCALL_USER_DISPATCH) {
#1 if (syscall_user_dispatch(regs))
return -1L;
}
if (work & (SYSCALL_WORK_SYSCALL_TRACE | SYSCALL_WORK_SYSCALL_EMU)) {
#2 ret = arch_ptrace_report_syscall_entry(regs);
if (ret || (work & SYSCALL_WORK_SYSCALL_EMU))
return -1L;
}
/* Do seccomp after ptrace, to catch any tracer changes. */
if (work & SYSCALL_WORK_SECCOMP) {
#3 ret = __secure_computing();
if (ret == -1L)
return ret;
}
/* Either of the above might have changed the syscall number */
#4 syscall = syscall_get_nr(current, regs);
if (unlikely(work & SYSCALL_WORK_SYSCALL_TRACEPOINT))
#5 syscall = trace_syscall_enter(regs, syscall);
return syscall;
}
#1) The user dispatch mechanism does not modify the syscall return
value, but it can rollback the syscall and tell the call site to
skip the invocation.
The mechanism used in upstream today is to return -1L as the syscall
number which makes the architecture specific entry code skip the
syscall and refrain from touching the return value.
#2) ptrace
ptrace can poke whatever it wants into the syscall number storage
via ptrace_set_syscall_info_entry() -> syscall_set_nr()
It does not set the return code.
It does not abort the syscall when the poked syscall number is -1L.
It only aborts when a fatal signal is pending.
#3) seccomp
seccomp reads the syscall number, which might have been modified by
ptrace and acts upon it.
It can rewrite the syscall number even if it is -1 to begin with.
It can rewrite the return code if it decides to refuse the syscall
to be executed.
If it refuses the syscall to be executed it returns -1L.
#4) Rereading the syscall number after ptrace/seccomp
That's required to give the eventually modified number to the
tracer.
Obviously the tracer could do that on it's own, but with the current
implementation it expects the eventually modified syscall number
Changing that to make the tracer do it, is possible but does not
change any of the actual expectations. That's just cosmetic wankery.
#5) tracing
tracing can have a probe or bpf attached, which in turn can
- rewrite the syscall number
- set the return code in case that it sets the syscall number to
-1L
It can even set it in case it sets it to some other value, but
the architecture code has to be resilent against that no matter
what.
- if it does not set the return code when it sets the syscall
number to -1L then it has a historical expectation that the
syscall returns -ENOSYS
That's how it is and you can argue in circles and it's not going
away unless you have a great argument why you can break existing
user space probes/bpf scripts.
So now please provide in coherent sentences the argument why this solves
anything:
> However, reading the syscall number from pt_regs only after
> syscall_enter_from_user_mode exits does.
If you can, which I doubt, then please send a patch [series] against:
git://git.kernel.org/pub/scm/linux/kernel/git/tglx/devel.git entry/rework
with proper change logs explaining the superiour solution.
If not, please spare us the next set of incoherent "I wan't a pony"
mails.
Thanks,
tglx
^ 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