* [PATCH v2 4/4] clk: test: convert constants to use HZ_PER_MHZ
From: Brian Masney @ 2026-03-25 23:52 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd, Maxime Ripard, Jonathan Corbet,
Shuah Khan
Cc: linux-clk, linux-kernel, linux-doc, Brian Masney
In-Reply-To: <20260325-clk-docs-v2-0-bcf660e1ceb5@redhat.com>
Convert the DUMMY_CLOCK_* constants over to use HZ_PER_MHZ.
Signed-off-by: Brian Masney <bmasney@redhat.com>
---
drivers/clk/clk_test.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/drivers/clk/clk_test.c b/drivers/clk/clk_test.c
index a268d7b5d4cb28ec1f029f828c31107f8e130556..372dd289a7ba148a0725ea0643342ccda7196216 100644
--- a/drivers/clk/clk_test.c
+++ b/drivers/clk/clk_test.c
@@ -7,6 +7,7 @@
#include <linux/clk/clk-conf.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/units.h>
/* Needed for clk_hw_get_clk() */
#include "clk.h"
@@ -21,9 +22,9 @@
static const struct clk_ops empty_clk_ops = { };
-#define DUMMY_CLOCK_INIT_RATE (42 * 1000 * 1000)
-#define DUMMY_CLOCK_RATE_1 (142 * 1000 * 1000)
-#define DUMMY_CLOCK_RATE_2 (242 * 1000 * 1000)
+#define DUMMY_CLOCK_INIT_RATE (42 * HZ_PER_MHZ)
+#define DUMMY_CLOCK_RATE_1 (142 * HZ_PER_MHZ)
+#define DUMMY_CLOCK_RATE_2 (242 * HZ_PER_MHZ)
struct clk_dummy_context {
struct clk_hw hw;
--
2.53.0
^ permalink raw reply related
* Re: [PATCH v2] docs: rework footer with semantic markup and responsive layout
From: Rito Rhymes @ 2026-03-26 0:28 UTC (permalink / raw)
To: Jonathan Corbet, Rito Rhymes, Mauro Carvalho Chehab, linux-doc
Cc: Shuah Khan, linux-kernel
In-Reply-To: <87bjgbecgw.fsf@trenco.lwn.net>
> We are not going to fix Sphinx accessibility piecemeal in this
> way. If that is really the objective, I think the right thing
> is to work with the Sphinx project directly and come up with a
> proper plan for all of their major elements. Tweaking the
> footer - the contents of which will be read by the screen
> reader and understood just fine - doesn't really address that
> problem.
I see where you're coming from.
In that case, I can reroll this as a CSS-only override for the mobile
rendering issue and drop the markup change.
I agree that the semantic footer question is better handled upstream in
Sphinx rather than piecemeal here.
If that direction works for you, I'll send a reroll accordingly.
Rito
^ permalink raw reply
* [PATCH v3] docs: restore and reflow footer on narrow screens
From: Rito Rhymes @ 2026-03-26 0:58 UTC (permalink / raw)
To: Jonathan Corbet; +Cc: Shuah Khan, linux-doc, linux-kernel, Rito Rhymes
In-Reply-To: <20260321131857.14166-1-rito@ritovision.com>
Alabaster hides the documentation footer below its 65em breakpoint.
Add a local override in custom.css so the footer remains visible on
narrow screens. Adjust its layout to better fit mobile screens without
the sidebar.
Signed-off-by: Rito Rhymes <rito@ritovision.com>
Assisted-by: Codex:GPT-5.4
---
v3: switch to a CSS-only override; no footer replacement
Documentation/sphinx-static/custom.css | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/Documentation/sphinx-static/custom.css b/Documentation/sphinx-static/custom.css
index db24f4344..397780b39 100644
--- a/Documentation/sphinx-static/custom.css
+++ b/Documentation/sphinx-static/custom.css
@@ -169,3 +169,12 @@ a.manpage {
font-weight: bold;
font-family: "Courier New", Courier, monospace;
}
+
+@media screen and (max-width: 65em) {
+ div.footer {
+ display: block;
+ width: auto;
+ margin: 30px 0 0px 0;
+ text-align: center;
+ }
+}
--
2.51.0
^ permalink raw reply related
* Re: [PATCH v3] docs: wrap generated tables to contain small-screen overflow
From: Rito Rhymes @ 2026-03-26 2:38 UTC (permalink / raw)
To: Jonathan Corbet, Rito Rhymes, Mauro Carvalho Chehab, linux-doc
Cc: Shuah Khan, linux-kernel, rdunlap
In-Reply-To: <87mrzvg2qx.fsf@trenco.lwn.net>
> So this CSS perhaps makes sense, but..
> But why do you need to inject another <div>, creating a whole
> new extension to do so, rather than just applying the CSS
> directly to the <table> elements? I just gave that a try, and
> it would appear to work just fine.
I did try applying it directly to the <table> elements.
That works on some pages, but in my testing it introduces
regressions on others that do not happen with the wrapper
approach.
For example, on `/bpf/standardization/instruction-set.html` at
400px viewport width, applying CSS scrollability directly to
the table creates an extra outer border with an awkward gap and,
more importantly, causes cell content to wrap much more
aggressively as the viewport narrows. Instead of letting the
table remain a readable horizontally scrollable unit, the text
gets squeezed into very narrow stacked lines that are harder
to read.
Screenshots:
Double border with gap issue highlighted -
https://github.com/user-attachments/assets/0ddacc4e-edce-4c14-b129-0940e8014d3f
Text wrapping issue highlighted -
https://github.com/user-attachments/assets/c172e126-7a4c-4b3c-a01c-fd4c8f08bda6
On `/arch/arc/features.html`, the wrapping issue is not present
and it scrolls fine, but I still see the double-border issue
there. In that case there is no gap, so it mainly looks like a
thicker border.
The reason for the wrapper is that it gives one consistent
overflow treatment across the existing table cases without
changing the table element's layout behavior directly. In my
testing, applying the scrollability to the <table> itself
works for some pages but regresses others, while the wrapper
approach avoids needing targeted CSS rules for different
rendered table cases.
If helpful, you're welcome to add any screenshots as a
comment on this GitHub issue:
https://github.com/ritovision/linux-kernel-docs/issues/1
Rito
^ permalink raw reply
* Re: [PATCH v10 0/8] arm64/riscv: Add support for crashkernel CMA reservation
From: Andrew Morton @ 2026-03-26 4:00 UTC (permalink / raw)
To: Jinjie Ruan
Cc: corbet, skhan, catalin.marinas, will, chenhuacai, kernel, maddy,
mpe, npiggin, chleroy, pjw, palmer, aou, alex, tglx, mingo, bp,
dave.hansen, hpa, robh, saravanak, bhe, vgoyal, dyoung, rdunlap,
peterz, pawan.kumar.gupta, feng.tang, dapeng1.mi, kees, elver,
paulmck, lirongqing, rppt, ardb, leitao, osandov, cfsworks,
tangyouling, sourabhjain, ritesh.list, eajames, songshuaishuai,
kevin.brodsky, samuel.holland, vishal.moola, junhui.liu, coxu,
liaoyuanhong, jbohac, fuqiang.wang, guoren, chenjiahao16,
hbathini, james.morse, takahiro.akashi, lizhengyu3, x86,
linux-doc, linux-kernel, linux-arm-kernel, loongarch,
linuxppc-dev, linux-riscv, devicetree, kexec
In-Reply-To: <20260325025904.2811960-1-ruanjinjie@huawei.com>
On Wed, 25 Mar 2026 10:58:56 +0800 Jinjie Ruan <ruanjinjie@huawei.com> wrote:
> The crash memory allocation, and the exclude of crashk_res, crashk_low_res
> and crashk_cma memory are almost identical across different architectures,
> This patch set handle them in crash core in a general way, which eliminate
> a lot of duplication code.
>
> And add support for crashkernel CMA reservation for arm64 and riscv.
So who is patchmonkey for this.
> .../admin-guide/kernel-parameters.txt | 16 +--
> arch/arm64/kernel/machine_kexec_file.c | 39 ++-----
> arch/arm64/mm/init.c | 5 +-
> arch/loongarch/kernel/machine_kexec_file.c | 39 ++-----
> arch/powerpc/include/asm/kexec_ranges.h | 1 -
> arch/powerpc/kexec/crash.c | 7 +-
> arch/powerpc/kexec/ranges.c | 101 +----------------
> arch/riscv/kernel/machine_kexec_file.c | 38 ++-----
> arch/riscv/mm/init.c | 5 +-
> arch/x86/kernel/crash.c | 89 ++-------------
> drivers/of/fdt.c | 9 +-
> drivers/of/kexec.c | 9 ++
> include/linux/crash_core.h | 9 ++
> kernel/crash_core.c | 105 +++++++++++++++++-
Me, I guess, with as many arch acks as I can gather, please.
I'm seriously trying to slow things down now, but I guess I can make an
exception for non-MM material.
AI review asks a few questions:
https://sashiko.dev/#/patchset/20260325025904.2811960-1-ruanjinjie@huawei.com
Can you please check these? And I'm interested in learning how many of
these are valid. Thanks.
^ permalink raw reply
* Re: [PATCH v2 3/4] docs: clk: include some identifiers to keep documentation up to date
From: Randy Dunlap @ 2026-03-26 5:04 UTC (permalink / raw)
To: Brian Masney, Michael Turquette, Stephen Boyd, Maxime Ripard,
Jonathan Corbet, Shuah Khan
Cc: linux-clk, linux-kernel, linux-doc
In-Reply-To: <20260325-clk-docs-v2-3-bcf660e1ceb5@redhat.com>
On 3/25/26 4:52 PM, Brian Masney wrote:
> The clk documentation currently has a separate list of some members of
> struct clk_core and struct clk_ops. Now that all of these structures
> have proper kernel docs, let's go ahead and just include them here via
> the identifiers statement in kerneldoc.
>
> While changes are being made here, let's also include the newly-added
> enum clk_core_flags.
>
> Signed-off-by: Brian Masney <bmasney@redhat.com>
> ---
> Documentation/driver-api/clk.rst | 58 +++++++---------------------------------
> 1 file changed, 9 insertions(+), 49 deletions(-)
Acked-by: Randy Dunlap <rdunlap@infradead.org>
Tested-by: Randy Dunlap <rdunlap@infradead.org>
Thanks.
--
~Randy
^ permalink raw reply
* Re: [PATCH v2 2/4] clk: add kernel docs for struct clk_core
From: Randy Dunlap @ 2026-03-26 5:04 UTC (permalink / raw)
To: Brian Masney, Michael Turquette, Stephen Boyd, Maxime Ripard,
Jonathan Corbet, Shuah Khan
Cc: linux-clk, linux-kernel, linux-doc
In-Reply-To: <20260325-clk-docs-v2-2-bcf660e1ceb5@redhat.com>
On 3/25/26 4:52 PM, Brian Masney wrote:
> Document all of the members of struct clk_core.
>
> Signed-off-by: Brian Masney <bmasney@redhat.com>
> ---
> drivers/clk/clk.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 51 insertions(+)
Acked-by: Randy Dunlap <rdunlap@infradead.org>
Thanks.
--
~Randy
^ permalink raw reply
* Re: [PATCH v2 1/4] clk: move core flags into a new enum for kernel docs
From: Randy Dunlap @ 2026-03-26 5:04 UTC (permalink / raw)
To: Brian Masney, Michael Turquette, Stephen Boyd, Maxime Ripard,
Jonathan Corbet, Shuah Khan
Cc: linux-clk, linux-kernel, linux-doc
In-Reply-To: <20260325-clk-docs-v2-1-bcf660e1ceb5@redhat.com>
On 3/25/26 4:52 PM, Brian Masney wrote:
> Let's move all of the existing clk flags into a new enum so that all of
> the flags can be easily referenced in the kernel documentation. Note
> that I went with name clk_core_flags for the enum since the name
> clk_flags is already in use in clk.c for the debugfs interface.
>
> Note: The comment about "Please update clk_flags..." is included as a
> separate comment so it doesn't show up in the generated documents.
>
> Signed-off-by: Brian Masney <bmasney@redhat.com>
> ---
> include/linux/clk-provider.h | 55 +++++++++++++++++++++++++++-----------------
> 1 file changed, 34 insertions(+), 21 deletions(-)
LGTM. Thanks.
Acked-by: Randy Dunlap <rdunlap@infradead.org>
--
~Randy
^ permalink raw reply
* [PATCH v3 0/9] Reintrodce Hornet LSM
From: Blaise Boscaccy @ 2026-03-26 6:06 UTC (permalink / raw)
To: Blaise Boscaccy, Jonathan Corbet, Paul Moore, James Morris,
Serge E. Hallyn, Mickaël Salaün, Günther Noack,
Dr. David Alan Gilbert, Andrew Morton, James.Bottomley, dhowells,
Fan Wu, Ryan Foster, Randy Dunlap, linux-security-module,
linux-doc, linux-kernel, bpf
This patch series introduces the next iteration of the Hornet LSM.
Hornet’s goal is to provide a secure and extensible in-kernel
signature verification mechanism for eBPF programs.
Hornet addresses concerns from users who require strict audit trails and
verification guarantees for eBPF programs, especially in
security-sensitive environments. Many production systems need assurance
that only authorized, unmodified eBPF programs are loaded into the
kernel. Hornet provides this assurance through cryptographic signature
verification.
The currently accepted loader-plus-map signature verification scheme,
mandated by Alexei and KP, is simple to implement and generally
acceptable if users and administrators are satisfied with it. However,
verifying both the loader and the maps offers additional benefits
beyond verifying the loader alone:
1. Security and Audit Integrity
A key advantage is that the LSM hook for authorizing BPF program loads
can operate after signature verification. This ensures:
* Access control decisions are based on verified signature status.
* Accurate system state measurement and logging.
* Log entries claiming a verified signature are truthful, avoiding
misleading records where only the loader was verified while the actual
BPF program verification occurs later without logging.
2. TOCTOU Attack Prevention
The current map hash implementation may be vulnerable to a TOCTOU
attack because it allows unfrozen maps to cache a previously
calculated hash. The accepted “trusted loader” scheme cannot detect
this and may permit loading altered maps.
3. Supply Chain Integrity
Verify that eBPF programs and their associated map data have not been
modified since they were built and signed, in the kernel proper, may
aid in protecting against supply chain attacks.
This approach addresses concerns from users who require strict audit
trails and verification guarantees, especially in security-sensitive
environments. Map hashes for extended verification are passed via the
existing PKCS#7 UAPI and verified by the crypto subsystem. Hornet then
calculates the program’s verification state. Hornet itself does not
enforce a policy on whether unsigned or partially signed programs
should be rejected. It delegates that decision to downstream LSMs
hook, making it a composable building block in a larger security
architecture.
Changes in V3:
- Updated for signed attribute patch series changes
- Added some new result enum values
- Minor documentation clarification
- Misc style fixes
- Added missing signed-off-by tags
Link to V2: https://lore.kernel.org/linux-security-module/20260227233930.2418522-1-bboscaccy@linux.microsoft.com/
Changes in V2:
- Addressed possible TocTou races in hash verification
- Improved documentation and tooling
- Added Alexie's nack
Link to RFC: https://lore.kernel.org/linux-security-module/20251211021257.1208712-1-bboscaccy@linux.microsoft.com/
Blaise Boscaccy (5):
lsm: security: Add additional enum values for bpf integrity checks
security: Hornet LSM
hornet: Introduce gen_sig
hornet: Add a light skeleton data extractor scripts
selftests/hornet: Add a selftest for the Hornet LSM
James Bottomley (3):
crypto: pkcs7: add flag for validated trust on a signed info block
crypto: pkcs7: add ability to extract signed attributes by OID
crypto: pkcs7: add tests for pkcs7_get_authattr
Paul Moore (1):
lsm: framework for BPF integrity verification
Documentation/admin-guide/LSM/Hornet.rst | 321 +++++++++++++++
Documentation/admin-guide/LSM/index.rst | 1 +
MAINTAINERS | 9 +
certs/system_keyring.c | 1 +
crypto/asymmetric_keys/Makefile | 4 +-
crypto/asymmetric_keys/pkcs7_aa.asn1 | 18 +
crypto/asymmetric_keys/pkcs7_key_type.c | 44 ++-
crypto/asymmetric_keys/pkcs7_parser.c | 81 ++++
crypto/asymmetric_keys/pkcs7_parser.h | 1 +
crypto/asymmetric_keys/pkcs7_trust.c | 1 +
include/crypto/pkcs7.h | 4 +
include/linux/lsm_hook_defs.h | 5 +
include/linux/oid_registry.h | 3 +
include/linux/security.h | 28 ++
include/uapi/linux/lsm.h | 1 +
scripts/Makefile | 1 +
scripts/hornet/Makefile | 5 +
scripts/hornet/extract-insn.sh | 27 ++
scripts/hornet/extract-map.sh | 27 ++
scripts/hornet/extract-skel.sh | 27 ++
scripts/hornet/gen_sig.c | 392 +++++++++++++++++++
scripts/hornet/write-sig.sh | 27 ++
security/Kconfig | 3 +-
security/Makefile | 1 +
security/hornet/Kconfig | 11 +
security/hornet/Makefile | 7 +
security/hornet/hornet.asn1 | 13 +
security/hornet/hornet_lsm.c | 333 ++++++++++++++++
security/security.c | 75 +++-
tools/testing/selftests/Makefile | 1 +
tools/testing/selftests/hornet/Makefile | 63 +++
tools/testing/selftests/hornet/loader.c | 21 +
tools/testing/selftests/hornet/trivial.bpf.c | 33 ++
33 files changed, 1583 insertions(+), 6 deletions(-)
create mode 100644 Documentation/admin-guide/LSM/Hornet.rst
create mode 100644 crypto/asymmetric_keys/pkcs7_aa.asn1
create mode 100644 scripts/hornet/Makefile
create mode 100755 scripts/hornet/extract-insn.sh
create mode 100755 scripts/hornet/extract-map.sh
create mode 100755 scripts/hornet/extract-skel.sh
create mode 100644 scripts/hornet/gen_sig.c
create mode 100755 scripts/hornet/write-sig.sh
create mode 100644 security/hornet/Kconfig
create mode 100644 security/hornet/Makefile
create mode 100644 security/hornet/hornet.asn1
create mode 100644 security/hornet/hornet_lsm.c
create mode 100644 tools/testing/selftests/hornet/Makefile
create mode 100644 tools/testing/selftests/hornet/loader.c
create mode 100644 tools/testing/selftests/hornet/trivial.bpf.c
--
2.53.0
^ permalink raw reply
* [PATCH v3 1/9] crypto: pkcs7: add flag for validated trust on a signed info block
From: Blaise Boscaccy @ 2026-03-26 6:06 UTC (permalink / raw)
To: Blaise Boscaccy, Jonathan Corbet, Paul Moore, James Morris,
Serge E. Hallyn, Mickaël Salaün, Günther Noack,
Dr. David Alan Gilbert, Andrew Morton, James.Bottomley, dhowells,
Fan Wu, Ryan Foster, Randy Dunlap, linux-security-module,
linux-doc, linux-kernel, bpf
In-Reply-To: <20260326060655.2550595-1-bboscaccy@linux.microsoft.com>
From: James Bottomley <James.Bottomley@HansenPartnership.com>
Allow consumers of struct pkcs7_message to tell if any of the sinfo
fields has passed a trust validation. Note that this does not happen
in parsing, pkcs7_validate_trust() must be explicitly called or called
via validate_pkcs7_trust(). Since the way to get this trusted pkcs7
object is via verify_pkcs7_message_sig, export that so modules can use
it.
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
---
certs/system_keyring.c | 1 +
crypto/asymmetric_keys/pkcs7_parser.h | 1 +
crypto/asymmetric_keys/pkcs7_trust.c | 1 +
3 files changed, 3 insertions(+)
diff --git a/certs/system_keyring.c b/certs/system_keyring.c
index e0761436ec7f4..9bda49295bd02 100644
--- a/certs/system_keyring.c
+++ b/certs/system_keyring.c
@@ -380,6 +380,7 @@ int verify_pkcs7_message_sig(const void *data, size_t len,
pr_devel("<==%s() = %d\n", __func__, ret);
return ret;
}
+EXPORT_SYMBOL(verify_pkcs7_message_sig);
/**
* verify_pkcs7_signature - Verify a PKCS#7-based signature on system data.
diff --git a/crypto/asymmetric_keys/pkcs7_parser.h b/crypto/asymmetric_keys/pkcs7_parser.h
index 6ef9f335bb17f..203062a33def6 100644
--- a/crypto/asymmetric_keys/pkcs7_parser.h
+++ b/crypto/asymmetric_keys/pkcs7_parser.h
@@ -20,6 +20,7 @@ struct pkcs7_signed_info {
unsigned index;
bool unsupported_crypto; /* T if not usable due to missing crypto */
bool blacklisted;
+ bool verified; /* T if this signer has validated trust */
/* Message digest - the digest of the Content Data (or NULL) */
const void *msgdigest;
diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c
index 9a87c34ed1733..78ebfb6373b61 100644
--- a/crypto/asymmetric_keys/pkcs7_trust.c
+++ b/crypto/asymmetric_keys/pkcs7_trust.c
@@ -127,6 +127,7 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
for (p = sinfo->signer; p != x509; p = p->signer)
p->verified = true;
}
+ sinfo->verified = true;
kleave(" = 0");
return 0;
}
--
2.53.0
^ permalink raw reply related
* [PATCH v3 2/9] crypto: pkcs7: add ability to extract signed attributes by OID
From: Blaise Boscaccy @ 2026-03-26 6:06 UTC (permalink / raw)
To: Blaise Boscaccy, Jonathan Corbet, Paul Moore, James Morris,
Serge E. Hallyn, Mickaël Salaün, Günther Noack,
Dr. David Alan Gilbert, Andrew Morton, James.Bottomley, dhowells,
Fan Wu, Ryan Foster, Randy Dunlap, linux-security-module,
linux-doc, linux-kernel, bpf
In-Reply-To: <20260326060655.2550595-1-bboscaccy@linux.microsoft.com>
From: James Bottomley <James.Bottomley@HansenPartnership.com>
Signers may add any information they like in signed attributes and
sometimes this information turns out to be relevant to specific
signing cases, so add an api pkcs7_get_authattr() to extract the value
of an authenticated attribute by specific OID. The current
implementation is designed for the single signer use case and simply
terminates the search when it finds the relevant OID.
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
---
crypto/asymmetric_keys/Makefile | 4 +-
crypto/asymmetric_keys/pkcs7_aa.asn1 | 18 ++++++
crypto/asymmetric_keys/pkcs7_parser.c | 81 +++++++++++++++++++++++++++
include/crypto/pkcs7.h | 4 ++
4 files changed, 106 insertions(+), 1 deletion(-)
create mode 100644 crypto/asymmetric_keys/pkcs7_aa.asn1
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index bc65d3b98dcbf..f99b7169ae7cd 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -53,12 +53,14 @@ clean-files += pkcs8.asn1.c pkcs8.asn1.h
obj-$(CONFIG_PKCS7_MESSAGE_PARSER) += pkcs7_message.o
pkcs7_message-y := \
pkcs7.asn1.o \
+ pkcs7_aa.asn1.o \
pkcs7_parser.o \
pkcs7_trust.o \
pkcs7_verify.o
-$(obj)/pkcs7_parser.o: $(obj)/pkcs7.asn1.h
+$(obj)/pkcs7_parser.o: $(obj)/pkcs7.asn1.h $(obj)/pkcs7_aa.asn1.h
$(obj)/pkcs7.asn1.o: $(obj)/pkcs7.asn1.c $(obj)/pkcs7.asn1.h
+$(obj)/pkcs7_aa.asn1.o: $(obj)/pkcs7_aa.asn1.c $(obj)/pkcs7_aa.asn1.h
#
# PKCS#7 parser testing key
diff --git a/crypto/asymmetric_keys/pkcs7_aa.asn1 b/crypto/asymmetric_keys/pkcs7_aa.asn1
new file mode 100644
index 0000000000000..7a8857bdf56e1
--- /dev/null
+++ b/crypto/asymmetric_keys/pkcs7_aa.asn1
@@ -0,0 +1,18 @@
+-- SPDX-License-Identifier: BSD-3-Clause
+--
+-- Copyright (C) 2009 IETF Trust and the persons identified as authors
+-- of the code
+--
+-- https://www.rfc-editor.org/rfc/rfc5652#section-3
+
+AA ::= CHOICE {
+ aaSet [0] IMPLICIT AASet,
+ aaSequence [2] EXPLICIT SEQUENCE OF AuthenticatedAttribute
+}
+
+AASet ::= SET OF AuthenticatedAttribute
+
+AuthenticatedAttribute ::= SEQUENCE {
+ type OBJECT IDENTIFIER ({ pkcs7_aa_note_OID }),
+ values SET OF ANY ({ pkcs7_aa_note_attr })
+}
diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys/pkcs7_parser.c
index 6e3ffdac83ace..d467866f7d930 100644
--- a/crypto/asymmetric_keys/pkcs7_parser.c
+++ b/crypto/asymmetric_keys/pkcs7_parser.c
@@ -15,6 +15,7 @@
#include <crypto/public_key.h>
#include "pkcs7_parser.h"
#include "pkcs7.asn1.h"
+#include "pkcs7_aa.asn1.h"
MODULE_DESCRIPTION("PKCS#7 parser");
MODULE_AUTHOR("Red Hat, Inc.");
@@ -211,6 +212,86 @@ int pkcs7_get_content_data(const struct pkcs7_message *pkcs7,
}
EXPORT_SYMBOL_GPL(pkcs7_get_content_data);
+struct pkcs7_aa_context {
+ bool found;
+ enum OID oid_to_find;
+ const void *data;
+ size_t len;
+};
+
+int pkcs7_aa_note_OID(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pkcs7_aa_context *ctx = context;
+ enum OID oid = look_up_OID(value, vlen);
+
+ ctx->found = (oid == ctx->oid_to_find);
+
+ return 0;
+}
+
+int pkcs7_aa_note_attr(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pkcs7_aa_context *ctx = context;
+
+ if (ctx->found) {
+ ctx->data = value;
+ ctx->len = vlen;
+ }
+
+ return 0;
+}
+
+/**
+ * pkcs7_get_authattr - get authenticated attribute by OID
+ * @pkcs7: The preparsed PKCS#7 message
+ * @oid: the enum value of the OID to find
+ * @_data: Place to return a pointer to the attribute value
+ * @_len: length of the attribute value
+ *
+ * Searches the authenticated attributes until one is found with a
+ * matching OID. Note that because the attributes are per signer
+ * there could be multiple signers with different values, but this
+ * routine will simply return the first one in parse order.
+ *
+ * Returns -ENODATA if the attribute can't be found
+ */
+int pkcs7_get_authattr(const struct pkcs7_message *pkcs7,
+ enum OID oid,
+ const void **_data, size_t *_len)
+{
+ struct pkcs7_signed_info *sinfo = pkcs7->signed_infos;
+ struct pkcs7_aa_context ctx;
+
+ ctx.data = NULL;
+ ctx.oid_to_find = oid;
+
+ for (; sinfo; sinfo = sinfo->next) {
+ int ret;
+
+ /* only extract OIDs from validated signers */
+ if (!sinfo->verified)
+ continue;
+
+ ret = asn1_ber_decoder(&pkcs7_aa_decoder, &ctx,
+ sinfo->authattrs, sinfo->authattrs_len);
+ if (ret < 0 || ctx.data != NULL)
+ break;
+ }
+
+ if (!ctx.data)
+ return -ENODATA;
+
+ *_data = ctx.data;
+ *_len = ctx.len;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pkcs7_get_authattr);
+
/*
* Note an OID when we find one for later processing when we know how
* to interpret it.
diff --git a/include/crypto/pkcs7.h b/include/crypto/pkcs7.h
index 38ec7f5f90411..bd83202cd805c 100644
--- a/include/crypto/pkcs7.h
+++ b/include/crypto/pkcs7.h
@@ -25,6 +25,10 @@ extern void pkcs7_free_message(struct pkcs7_message *pkcs7);
extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7,
const void **_data, size_t *_datalen,
size_t *_headerlen);
+extern int pkcs7_get_authattr(const struct pkcs7_message *pkcs7,
+ enum OID oid,
+ const void **_data, size_t *_len);
+
/*
* pkcs7_trust.c
--
2.53.0
^ permalink raw reply related
* [PATCH v3 3/9] crypto: pkcs7: add tests for pkcs7_get_authattr
From: Blaise Boscaccy @ 2026-03-26 6:06 UTC (permalink / raw)
To: Blaise Boscaccy, Jonathan Corbet, Paul Moore, James Morris,
Serge E. Hallyn, Mickaël Salaün, Günther Noack,
Dr. David Alan Gilbert, Andrew Morton, James.Bottomley, dhowells,
Fan Wu, Ryan Foster, Randy Dunlap, linux-security-module,
linux-doc, linux-kernel, bpf
In-Reply-To: <20260326060655.2550595-1-bboscaccy@linux.microsoft.com>
From: James Bottomley <James.Bottomley@HansenPartnership.com>
Add example code to the test module pkcs7_key_type.c that verifies a
message and then pulls out a known authenticated attribute.
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
Acked-by: David Howells <dhowells@redhat.com>
---
crypto/asymmetric_keys/pkcs7_key_type.c | 44 ++++++++++++++++++++++++-
1 file changed, 43 insertions(+), 1 deletion(-)
diff --git a/crypto/asymmetric_keys/pkcs7_key_type.c b/crypto/asymmetric_keys/pkcs7_key_type.c
index b930d3bbf1af5..e0b1ce0202f6d 100644
--- a/crypto/asymmetric_keys/pkcs7_key_type.c
+++ b/crypto/asymmetric_keys/pkcs7_key_type.c
@@ -12,6 +12,7 @@
#include <linux/verification.h>
#include <linux/key-type.h>
#include <keys/user-type.h>
+#include <crypto/pkcs7.h>
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("PKCS#7 testing key type");
@@ -51,16 +52,57 @@ static int pkcs7_view_content(void *ctx, const void *data, size_t len,
static int pkcs7_preparse(struct key_preparsed_payload *prep)
{
enum key_being_used_for usage = pkcs7_usage;
+ int ret;
+ struct pkcs7_message *pkcs7;
+ const void *data;
+ size_t len;
if (usage >= NR__KEY_BEING_USED_FOR) {
pr_err("Invalid usage type %d\n", usage);
return -EINVAL;
}
- return verify_pkcs7_signature(NULL, 0,
+ ret = verify_pkcs7_signature(NULL, 0,
prep->data, prep->datalen,
VERIFY_USE_SECONDARY_KEYRING, usage,
pkcs7_view_content, prep);
+ if (ret)
+ return ret;
+
+ pkcs7 = pkcs7_parse_message(prep->data, prep->datalen);
+ if (IS_ERR(pkcs7)) {
+ pr_err("pkcs7 parse error\n");
+ return PTR_ERR(pkcs7);
+ }
+
+ /*
+ * the parsed message has no trusted signer, so nothing should
+ * be returned here
+ */
+ ret = pkcs7_get_authattr(pkcs7, OID_messageDigest, &data, &len);
+ if (ret == 0) {
+ pr_err("OID returned when no trust in signer\n");
+ goto out;
+ }
+ /* add trust and check again */
+ ret = verify_pkcs7_message_sig(NULL, 0, pkcs7,
+ VERIFY_USE_SECONDARY_KEYRING, usage,
+ NULL, NULL);
+ if (ret) {
+ pr_err("verify_pkcs7_message_sig failed!!\n");
+ goto out;
+ }
+ /* now we should find the OID */
+ ret = pkcs7_get_authattr(pkcs7, OID_messageDigest, &data, &len);
+ if (ret) {
+ pr_err("Failed to get message digest\n");
+ goto out;
+ }
+ pr_info("Correctly Got message hash, size=%zu\n", len);
+
+ out:
+ pkcs7_free_message(pkcs7);
+ return 0;
}
/*
--
2.53.0
^ permalink raw reply related
* [PATCH v3 4/9] lsm: framework for BPF integrity verification
From: Blaise Boscaccy @ 2026-03-26 6:06 UTC (permalink / raw)
To: Blaise Boscaccy, Jonathan Corbet, Paul Moore, James Morris,
Serge E. Hallyn, Mickaël Salaün, Günther Noack,
Dr. David Alan Gilbert, Andrew Morton, James.Bottomley, dhowells,
Fan Wu, Ryan Foster, Randy Dunlap, linux-security-module,
linux-doc, linux-kernel, bpf
In-Reply-To: <20260326060655.2550595-1-bboscaccy@linux.microsoft.com>
From: Paul Moore <paul@paul-moore.com>
Add a new LSM hook and two new LSM hook callbacks to support LSMs that
perform integrity verification, e.g. digital signature verification,
of BPF programs.
While the BPF subsystem does implement a signature verification scheme,
it does not satisfy a number of existing requirements, adding support
for BPF program integrity verification to the LSM framework allows
administrators to select additional integrity verification mechanisms
to meet these needs while also providing a mechanism for future
expansion. Additional on why this is necessary can be found at the
lore archive link below:
https://lore.kernel.org/linux-security-module/CAHC9VhTQ_DR=ANzoDBjcCtrimV7XcCZVUsANPt=TjcvM4d-vjg@mail.gmail.com/
The LSM-based BPF integrity verification mechanism works within the
existing security_bpf_prog_load() hook called by the BPF subsystem.
It adds an additional dedicated integrity callback and a new LSM
hook/callback to be called from within LSMs implementing integrity
verification.
The first new callback, bpf_prog_load_integrity(), located within the
security_bpf_prog_load() hook, is necessary to ensure that the integrity
verification callbacks are executed before any of the existing LSMs
are executed via the bpf_prog_load() callback. Reusing the existing
bpf_prog_load() callback for integrity verification could result in LSMs
not having access to the integrity verification results when asked to
authorize the BPF program load in the bpf_prog_load() callback.
The new LSM hook, security_bpf_prog_load_post_integrity(), is intended
to be called from within LSMs performing BPF program integrity
verification. It is used to report the verdict of the integrity
verification to other LSMs enforcing access control policy on BPF
program loads. LSMs enforcing such access controls should register a
bpf_prog_load_post_integrity() callback to receive integrity verdicts.
More information on these new callbacks and hook can be found in the
code comments in this patch.
Signed-off-by: Paul Moore <paul@paul-moore.com>
Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
---
include/linux/lsm_hook_defs.h | 5 +++
include/linux/security.h | 25 ++++++++++++
security/security.c | 75 +++++++++++++++++++++++++++++++++--
3 files changed, 102 insertions(+), 3 deletions(-)
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index 8c42b4bde09c0..4971d3c36d5b4 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -434,6 +434,11 @@ LSM_HOOK(int, 0, bpf_prog, struct bpf_prog *prog)
LSM_HOOK(int, 0, bpf_map_create, struct bpf_map *map, union bpf_attr *attr,
struct bpf_token *token, bool kernel)
LSM_HOOK(void, LSM_RET_VOID, bpf_map_free, struct bpf_map *map)
+LSM_HOOK(int, 0, bpf_prog_load_post_integrity, struct bpf_prog *prog,
+ union bpf_attr *attr, struct bpf_token *token, bool kernel,
+ const struct lsm_id *lsmid, enum lsm_integrity_verdict verdict)
+LSM_HOOK(int, 0, bpf_prog_load_integrity, struct bpf_prog *prog,
+ union bpf_attr *attr, struct bpf_token *token, bool kernel)
LSM_HOOK(int, 0, bpf_prog_load, struct bpf_prog *prog, union bpf_attr *attr,
struct bpf_token *token, bool kernel)
LSM_HOOK(void, LSM_RET_VOID, bpf_prog_free, struct bpf_prog *prog)
diff --git a/include/linux/security.h b/include/linux/security.h
index 83a646d72f6f8..298a43b7744a4 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -67,6 +67,7 @@ enum fs_value_type;
struct watch;
struct watch_notification;
struct lsm_ctx;
+struct lsm_id;
/* Default (no) options for the capable function */
#define CAP_OPT_NONE 0x0
@@ -100,6 +101,14 @@ enum lsm_integrity_type {
LSM_INT_FSVERITY_BUILTINSIG_VALID,
};
+enum lsm_integrity_verdict {
+ LSM_INT_VERDICT_NONE = 0,
+ LSM_INT_VERDICT_OK,
+ LSM_INT_VERDICT_UNSIGNED,
+ LSM_INT_VERDICT_PARTIALSIG,
+ LSM_INT_VERDICT_BADSIG,
+};
+
/*
* These are reasons that can be passed to the security_locked_down()
* LSM hook. Lockdown reasons that protect kernel integrity (ie, the
@@ -2269,6 +2278,12 @@ extern int security_bpf_prog(struct bpf_prog *prog);
extern int security_bpf_map_create(struct bpf_map *map, union bpf_attr *attr,
struct bpf_token *token, bool kernel);
extern void security_bpf_map_free(struct bpf_map *map);
+extern int security_bpf_prog_load_post_integrity(struct bpf_prog *prog,
+ union bpf_attr *attr,
+ struct bpf_token *token,
+ bool kernel,
+ const struct lsm_id *lsmid,
+ enum lsm_integrity_verdict verdict);
extern int security_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
struct bpf_token *token, bool kernel);
extern void security_bpf_prog_free(struct bpf_prog *prog);
@@ -2303,6 +2318,16 @@ static inline int security_bpf_map_create(struct bpf_map *map, union bpf_attr *a
static inline void security_bpf_map_free(struct bpf_map *map)
{ }
+static inline int security_bpf_prog_load_post_integrity(struct bpf_prog *prog,
+ union bpf_attr *attr,
+ struct bpf_token *token,
+ bool kernel,
+ const struct lsm_id *lsmid,
+ enum lsm_integrity_verdict verdict)
+{
+ return 0;
+}
+
static inline int security_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
struct bpf_token *token, bool kernel)
{
diff --git a/security/security.c b/security/security.c
index 67af9228c4e94..2d8279bd4aae2 100644
--- a/security/security.c
+++ b/security/security.c
@@ -5232,6 +5232,50 @@ int security_bpf_map_create(struct bpf_map *map, union bpf_attr *attr,
return rc;
}
+/**
+ * security_bpf_prog_load_post_integrity() - Check if the BPF prog is allowed
+ * @prog: BPF program object
+ * @attr: BPF syscall attributes used to create BPF program
+ * @token: BPF token used to grant user access to BPF subsystem
+ * @kernel: whether or not call originated from kernel
+ * @lsmid: LSM ID of the LSM providing @verdict
+ * @verdict: result of the integrity verification
+ *
+ * See the comment block for the security_bpf_prog_load() LSM hook.
+ *
+ * This LSM hook is intended to be called from within the
+ * bpf_prog_load_integrity() callback that is part of the
+ * security_bpf_prog_load() hook; kernel subsystems outside the scope of the
+ * LSM framework should not call this hook directly.
+ *
+ * If the LSM calling into this hook receives a non-zero error code, it should
+ * return the same error code back to its caller. If this hook returns a zero,
+ * it does not necessarily mean that all of the enabled LSMs have authorized
+ * the BPF program load, as there may be other LSMs implementing BPF integrity
+ * checks which have yet to execute. However, if a zero is returned, the LSM
+ * calling into this hook should continue and return zero back to its caller.
+ *
+ * LSMs which implement the bpf_prog_load_post_integrity() callback and
+ * determine that a particular BPF program load is not authorized may choose to
+ * either return an error code for immediate rejection, or store their decision
+ * in their own LSM state attached to @prog, later returning an error code in
+ * the bpf_prog_load() callback. An immediate error code return is in keeping
+ * with the "fail fast" practice, but waiting until the bpf_prog_load()
+ * callback allows the LSM to consider multiple different integrity verdicts.
+ *
+ * Return: Returns 0 on success, error on failure.
+ */
+int security_bpf_prog_load_post_integrity(struct bpf_prog *prog,
+ union bpf_attr *attr,
+ struct bpf_token *token,
+ bool kernel,
+ const struct lsm_id *lsmid,
+ enum lsm_integrity_verdict verdict)
+{
+ return call_int_hook(bpf_prog_load_post_integrity, prog, attr, token,
+ kernel, lsmid, verdict);
+}
+
/**
* security_bpf_prog_load() - Check if loading of BPF program is allowed
* @prog: BPF program object
@@ -5240,8 +5284,24 @@ int security_bpf_map_create(struct bpf_map *map, union bpf_attr *attr,
* @kernel: whether or not call originated from kernel
*
* Perform an access control check when the kernel loads a BPF program and
- * allocates associated BPF program object. This hook is also responsible for
- * allocating any required LSM state for the BPF program.
+ * allocates the associated BPF program object. This hook is also responsible
+ * for allocating any required LSM state for the BPF program.
+ *
+ * This hook calls two LSM callbacks: bpf_prog_load_integrity() and
+ * bpf_prog_load(). The bpf_prog_load_integrity() callback is for those LSMs
+ * that wish to implement integrity verifications of BPF programs, e.g.
+ * signature verification, while the bpf_prog_load() callback is for general
+ * authorization of the BPF program load. Performing both verification and
+ * authorization in a single callback, with arbitrary LSM ordering, would be
+ * a challenge.
+ *
+ * LSMs which implement the bpf_prog_load_integrity() callback should call into
+ * the security_bpf_prog_load_post_integrity() hook with their integrity
+ * verdict. LSMs which implement BPF program integrity policy can register a
+ * callback for the security_bpf_prog_load_post_integrity() hook and
+ * either update their own internal state based on the verdict, or immediately
+ * reject the BPF program load with an error code. See the comment block for
+ * security_bpf_prog_load_post_integrity() for more information.
*
* Return: Returns 0 on success, error on failure.
*/
@@ -5254,9 +5314,18 @@ int security_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
if (unlikely(rc))
return rc;
+ rc = call_int_hook(bpf_prog_load_integrity, prog, attr, token, kernel);
+ if (unlikely(rc))
+ goto err;
+
rc = call_int_hook(bpf_prog_load, prog, attr, token, kernel);
if (unlikely(rc))
- security_bpf_prog_free(prog);
+ goto err;
+
+ return rc;
+
+err:
+ security_bpf_prog_free(prog);
return rc;
}
--
2.53.0
^ permalink raw reply related
* [PATCH v3 5/9] lsm: security: Add additional enum values for bpf integrity checks
From: Blaise Boscaccy @ 2026-03-26 6:06 UTC (permalink / raw)
To: Blaise Boscaccy, Jonathan Corbet, Paul Moore, James Morris,
Serge E. Hallyn, Mickaël Salaün, Günther Noack,
Dr. David Alan Gilbert, Andrew Morton, James.Bottomley, dhowells,
Fan Wu, Ryan Foster, Randy Dunlap, linux-security-module,
linux-doc, linux-kernel, bpf
In-Reply-To: <20260326060655.2550595-1-bboscaccy@linux.microsoft.com>
First add a generic LSM_INT_VERDICT_FAULT value to indicate a system
failure during checking. Second, add a LSM_INT_VERDICT_UNKNOWNKEY to
signal that the payload was signed with a key other than one that
exists in the secondary keyring. And finally add an
LSM_INT_VERDICT_UNEXPECTED enum value to indicate that a unexpected
hash value was encountered at some stage of verification.
Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
---
include/linux/security.h | 3 +++
1 file changed, 3 insertions(+)
diff --git a/include/linux/security.h b/include/linux/security.h
index 298a43b7744a4..84c82c41b48c4 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -106,6 +106,9 @@ enum lsm_integrity_verdict {
LSM_INT_VERDICT_OK,
LSM_INT_VERDICT_UNSIGNED,
LSM_INT_VERDICT_PARTIALSIG,
+ LSM_INT_VERDICT_UNKNOWNKEY,
+ LSM_INT_VERDICT_UNEXPECTED,
+ LSM_INT_VERDICT_FAULT,
LSM_INT_VERDICT_BADSIG,
};
--
2.53.0
^ permalink raw reply related
* [PATCH v3 6/9] security: Hornet LSM
From: Blaise Boscaccy @ 2026-03-26 6:06 UTC (permalink / raw)
To: Blaise Boscaccy, Jonathan Corbet, Paul Moore, James Morris,
Serge E. Hallyn, Mickaël Salaün, Günther Noack,
Dr. David Alan Gilbert, Andrew Morton, James.Bottomley, dhowells,
Fan Wu, Ryan Foster, Randy Dunlap, linux-security-module,
linux-doc, linux-kernel, bpf
In-Reply-To: <20260326060655.2550595-1-bboscaccy@linux.microsoft.com>
This adds the Hornet Linux Security Module which provides enhanced
signature verification and data validation for eBPF programs. This
allows users to continue to maintain an invariant that all code
running inside of the kernel has actually been signed and verified, by
the kernel.
This effort builds upon the currently excepted upstream solution. It
further hardens it by providing deterministic, in-kernel checking of
map hashes to solidify auditing along with preventing TOCTOU attacks
against lskel map hashes.
Target map hashes are passed in via PKCS#7 signed attributes. Hornet
determines the extent which the eBFP program is signed and defers to
other LSMs for policy decisions.
Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
Nacked-by: Alexei Starovoitov <alexei.starovoitov@gmail.com>
---
Documentation/admin-guide/LSM/Hornet.rst | 321 ++++++++++++++++++++++
Documentation/admin-guide/LSM/index.rst | 1 +
MAINTAINERS | 9 +
include/linux/oid_registry.h | 3 +
include/uapi/linux/lsm.h | 1 +
security/Kconfig | 3 +-
security/Makefile | 1 +
security/hornet/Kconfig | 11 +
security/hornet/Makefile | 7 +
security/hornet/hornet.asn1 | 13 +
security/hornet/hornet_lsm.c | 333 +++++++++++++++++++++++
11 files changed, 702 insertions(+), 1 deletion(-)
create mode 100644 Documentation/admin-guide/LSM/Hornet.rst
create mode 100644 security/hornet/Kconfig
create mode 100644 security/hornet/Makefile
create mode 100644 security/hornet/hornet.asn1
create mode 100644 security/hornet/hornet_lsm.c
diff --git a/Documentation/admin-guide/LSM/Hornet.rst b/Documentation/admin-guide/LSM/Hornet.rst
new file mode 100644
index 0000000000000..af5e9cd9d83a8
--- /dev/null
+++ b/Documentation/admin-guide/LSM/Hornet.rst
@@ -0,0 +1,321 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+======
+Hornet
+======
+
+Hornet is a Linux Security Module that provides extensible signature
+verification for eBPF programs. This is selectable at build-time with
+``CONFIG_SECURITY_HORNET``.
+
+Overview
+========
+
+Hornet addresses concerns from users who require strict audit trails and
+verification guarantees for eBPF programs, especially in
+security-sensitive environments. Many production systems need assurance
+that only authorized, unmodified eBPF programs are loaded into the
+kernel. Hornet provides this assurance through cryptographic signature
+verification.
+
+When an eBPF program is loaded via the ``bpf()`` syscall, Hornet
+verifies a PKCS#7 signature attached to the program instructions. The
+signature is checked against the kernel's secondary keyring using the
+existing kernel cryptographic infrastructure. In addition to signing the
+program bytecode, Hornet supports signing SHA-256 hashes of associated
+BPF maps, enabling integrity verification of map contents at load time
+and at runtime.
+
+After verification, Hornet classifies the program into one of the
+following integrity states and passes the result to a downstream LSM hook
+(``bpf_prog_load_post_integrity``), allowing other security modules to
+make policy decisions based on the verification outcome:
+
+``LSM_INT_VERDICT_OK``
+ The program signature and all map hashes verified successfully.
+
+``LSM_INT_VERDICT_UNSIGNED``
+ No signature was provided with the program.
+
+``LSM_INT_VERDICT_PARTIALSIG``
+ The program signature verified, but the signature did not contain
+ hornet map hash data.
+
+``LSM_INT_VERDICT_UNKNOWNKEY``
+ The signing certificate is not trusted in the secondary keyring,
+
+``LSM_INT_VERDICT_FAULT``
+ A system error occured during verification.
+
+``LSM_INT_VERDICT_UNEXPECTED``
+ An unexpected map hash value was encountered.
+
+``LSM_INT_VERDICT_BADSIG``
+ The signature or a map hash failed verification.
+
+Hornet itself does not enforce a policy on whether unsigned or partially
+signed programs should be rejected. It delegates that decision to
+downstream LSMs via the ``bpf_prog_load_post_integrity`` hook, making it
+a composable building block in a larger security architecture.
+
+Use Cases
+=========
+
+- **Locked-down production environments**: Ensure only eBPF programs
+ signed by a trusted authority can be loaded, preventing unauthorized
+ or tampered programs from running in the kernel.
+
+- **Audit and compliance**: Provide cryptographic evidence that loaded
+ eBPF programs match their expected build artifacts, supporting
+ compliance requirements in regulated industries.
+
+- **Supply chain integrity**: Verify that eBPF programs and their
+ associated map data have not been modified since they were built and
+ signed, protecting against supply chain attacks.
+
+Threat Model
+============
+
+Hornet protects against the following threats:
+
+- **Unauthorized eBPF program loading**: Programs that have not been
+ signed by a trusted key will be reported as unsigned or badly signed.
+
+- **Tampering with program instructions**: Any modification to the eBPF
+ bytecode after signing will cause signature verification to fail.
+
+- **Tampering with map data**: When map hashes are included in the
+ signature, Hornet verifies that frozen BPF maps match their expected
+ SHA-256 hashes at load time. Maps are also re-verified before program
+ execution via ``BPF_PROG_RUN``.
+
+Hornet does **not** protect against:
+
+- Compromise of the signing key itself.
+- Attacks that occur after a program has been loaded and verified.
+- Programs loaded by the kernel itself (kernel-internal loads bypass
+ the ``BPF_PROG_RUN`` map check).
+
+Known Limitations
+=================
+
+- Hornet requires programs to use :doc:`light skeletons
+ </bpf/libbpf/libbpf_naming_convention>` (lskels) for the signing
+ workflow, as the tooling operates on lskel-generated headers.
+
+- A maximum of 64 maps per program can be tracked for hash
+ verification.
+
+- Map hash verification requires the maps to be frozen before loading.
+ Maps that are not frozen at load time will cause verification to fail
+ when their hashes are included in the signature.
+
+- Hornet relies on the kernel's secondary keyring
+ (``VERIFY_USE_SECONDARY_KEYRING``) for certificate trust. Keys must
+ be provisioned into this keyring before programs can be verified.
+
+- The only hashing algorithm available is SHA256 due to it be hardcoded
+ in the bpf subsystem.
+
+Configuration
+=============
+
+Build Configuration
+-------------------
+
+Enable Hornet by setting the following kernel configuration option::
+
+ CONFIG_SECURITY_HORNET=y
+
+This option is found under :menuselection:`Security options --> Hornet
+support` and depends on ``CONFIG_SECURITY``.
+
+When enabled, Hornet is included in the default LSM initialization order
+and will appear in ``/sys/kernel/security/lsm``.
+
+Architecture
+============
+
+Signature Verification Flow
+---------------------------
+
+The following describes what happens when a userspace program calls
+``bpf(BPF_PROG_LOAD, ...)`` with a signature attached:
+
+1. The ``bpf_prog_load_integrity`` LSM hook is invoked.
+
+2. Hornet reads the signature from the userspace buffer specified by
+ ``attr->signature`` (with length ``attr->signature_size``).
+
+3. The PKCS#7 signature is verified against the program instructions
+ using ``verify_pkcs7_signature()`` with the kernel's secondary
+ keyring.
+
+4. The PKCS#7 message is parsed and its trust chain is validated via
+ ``validate_pkcs7_trust()``.
+
+5. Hornet extracts the authenticated attribute identified by
+ ``OID_hornet_data`` (OID ``2.25.316487325684022475439036912669789383960``)
+ from the PKCS#7 message. This attribute contains an ASN.1-encoded set
+ of map index/hash pairs.
+
+6. For each map hash entry, Hornet retrieves the corresponding BPF map
+ via its file descriptor, confirms it is frozen, computes its SHA-256
+ hash, and compares it against the signed hash.
+
+7. The resulting integrity verdict is passed to the
+ ``bpf_prog_load_post_integrity`` hook so that downstream LSMs can
+ enforce policy.
+
+Runtime Map Verification
+------------------------
+
+When ``bpf(BPF_PROG_RUN, ...)`` is called from userspace, Hornet
+re-verifies the hashes of all maps associated with the program. This
+ensures that map contents have not been modified between program load
+and execution. If any map hash no longer matches, the ``BPF_PROG_RUN``
+command is denied.
+
+Userspace Interface
+-------------------
+
+Signatures are passed to the kernel through fields in ``union bpf_attr``
+when using the ``BPF_PROG_LOAD`` command:
+
+``signature``
+ A pointer to a userspace buffer containing the PKCS#7 signature.
+
+``signature_size``
+ The size of the signature buffer in bytes.
+
+ASN.1 Schema
+------------
+
+Map hashes are encoded as a signed attribute in the PKCS#7 message using
+the following ASN.1 schema::
+
+ HornetData ::= SET OF Map
+
+ Map ::= SEQUENCE {
+ index INTEGER,
+ sha OCTET STRING
+ }
+
+Each ``Map`` entry contains the index of the map in the program's
+``fd_array`` and its expected SHA-256 hash. A zero-length ``sha`` field
+indicates that the map at that index should be skipped during
+verification.
+
+Tooling
+=======
+
+Helper scripts and a signature generation tool are provided in
+``scripts/hornet/`` to support the development of signed eBPF light
+skeletons.
+
+gen_sig
+-------
+
+``gen_sig`` is a C program (using OpenSSL) that creates a PKCS#7
+signature over eBPF program instructions and optionally includes
+SHA-256 hashes of BPF maps as signed attributes.
+
+Usage::
+
+ gen_sig --data <instructions.bin> \
+ --cert <signer.crt> \
+ --key <signer.key> \
+ [--pass <passphrase>] \
+ --out <signature.p7b> \
+ [--add <mapfile.bin>:<index> ...]
+
+``--data``
+ Path to the binary file containing eBPF program instructions to sign.
+
+``--cert``
+ Path to the signing certificate (PEM or DER format).
+
+``--key``
+ Path to the private key (PEM or DER format).
+
+``--pass``
+ Optional passphrase for the private key.
+
+``--out``
+ Path to write the output PKCS#7 signature.
+
+``--add``
+ Attach a map hash as a signed attribute. The argument is a path to a
+ binary map file followed by a colon and the map's index in the
+ ``fd_array``. This option may be specified multiple times.
+
+extract-skel.sh
+---------------
+
+Extracts a named field from an autogenerated eBPF lskel header file.
+Used internally by other helper scripts.
+
+extract-insn.sh
+---------------
+
+Extracts the eBPF program instructions (``opts_insn``) from an lskel
+header into a binary file suitable for signing with ``gen_sig``.
+
+extract-map.sh
+--------------
+
+Extracts the map data (``opts_data``) from an lskel header into a
+binary file suitable for hashing with ``gen_sig``.
+
+write-sig.sh
+------------
+
+Replaces the signature data in an lskel header with a new signature
+from a binary file. This is used to embed a freshly generated signature
+back into the header after signing.
+
+Signing Workflow
+================
+
+A typical workflow for building and signing an eBPF light skeleton is:
+
+1. **Compile the eBPF program**::
+
+ clang -O2 -target bpf -c program.bpf.c -o program.bpf.o
+
+2. **Generate the light skeleton header** using ``bpftool``::
+
+ bpftool gen skeleton -S program.bpf.o > loader.h
+
+3. **Extract instructions and map data** from the generated header::
+
+ scripts/hornet/extract-insn.sh loader.h > insn.bin
+ scripts/hornet/extract-map.sh loader.h > map.bin
+
+4. **Generate the signature** with ``gen_sig``::
+
+ scripts/hornet/gen_sig \
+ --key signing_key.pem \
+ --cert signing_key.x509 \
+ --data insn.bin \
+ --add map.bin:0 \
+ --out sig.bin
+
+5. **Embed the signature** back into the header::
+
+ scripts/hornet/write-sig.sh loader.h sig.bin > signed_loader.h
+
+6. **Build the loader program** using the signed header::
+
+ cc -o loader loader.c -lbpf
+
+The resulting loader program will pass the embedded signature to the
+kernel when loading the eBPF program, enabling Hornet to verify it.
+
+Testing
+=======
+
+Self-tests are provided in ``tools/testing/selftests/hornet/``. The test
+suite builds a minimal eBPF program (``trivial.bpf.c``), signs it using
+the workflow described above, and verifies that the signed program loads
+successfully.
diff --git a/Documentation/admin-guide/LSM/index.rst b/Documentation/admin-guide/LSM/index.rst
index b44ef68f6e4da..57f6e9fbe5fd1 100644
--- a/Documentation/admin-guide/LSM/index.rst
+++ b/Documentation/admin-guide/LSM/index.rst
@@ -49,3 +49,4 @@ subdirectories.
SafeSetID
ipe
landlock
+ Hornet
diff --git a/MAINTAINERS b/MAINTAINERS
index 55af015174a54..6e91234a9ba4e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11682,6 +11682,15 @@ S: Maintained
F: Documentation/devicetree/bindings/iio/pressure/honeywell,mprls0025pa.yaml
F: drivers/iio/pressure/mprls0025pa*
+HORNET SECURITY MODULE
+M: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
+L: linux-security-module@vger.kernel.org
+S: Supported
+T: git https://github.com/blaiseboscaccy/hornet.git
+F: Documentation/admin-guide/LSM/Hornet.rst
+F: scripts/hornet/
+F: security/hornet/
+
HP BIOSCFG DRIVER
M: Jorge Lopez <jorge.lopez2@hp.com>
L: platform-driver-x86@vger.kernel.org
diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h
index ebce402854de4..bf852715aaea4 100644
--- a/include/linux/oid_registry.h
+++ b/include/linux/oid_registry.h
@@ -150,6 +150,9 @@ enum OID {
OID_id_ml_dsa_65, /* 2.16.840.1.101.3.4.3.18 */
OID_id_ml_dsa_87, /* 2.16.840.1.101.3.4.3.19 */
+ /* Hornet LSM */
+ OID_hornet_data, /* 2.25.316487325684022475439036912669789383960 */
+
OID__NR
};
diff --git a/include/uapi/linux/lsm.h b/include/uapi/linux/lsm.h
index 938593dfd5daf..2ff9bcdd551e2 100644
--- a/include/uapi/linux/lsm.h
+++ b/include/uapi/linux/lsm.h
@@ -65,6 +65,7 @@ struct lsm_ctx {
#define LSM_ID_IMA 111
#define LSM_ID_EVM 112
#define LSM_ID_IPE 113
+#define LSM_ID_HORNET 114
/*
* LSM_ATTR_XXX definitions identify different LSM attributes
diff --git a/security/Kconfig b/security/Kconfig
index 6a4393fce9a17..283c4a1032094 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -230,6 +230,7 @@ source "security/safesetid/Kconfig"
source "security/lockdown/Kconfig"
source "security/landlock/Kconfig"
source "security/ipe/Kconfig"
+source "security/hornet/Kconfig"
source "security/integrity/Kconfig"
@@ -274,7 +275,7 @@ config LSM
default "landlock,lockdown,yama,loadpin,safesetid,apparmor,selinux,smack,tomoyo,ipe,bpf" if DEFAULT_SECURITY_APPARMOR
default "landlock,lockdown,yama,loadpin,safesetid,tomoyo,ipe,bpf" if DEFAULT_SECURITY_TOMOYO
default "landlock,lockdown,yama,loadpin,safesetid,ipe,bpf" if DEFAULT_SECURITY_DAC
- default "landlock,lockdown,yama,loadpin,safesetid,selinux,smack,tomoyo,apparmor,ipe,bpf"
+ default "landlock,lockdown,yama,loadpin,safesetid,selinux,smack,tomoyo,apparmor,ipe,hornet,bpf"
help
A comma-separated list of LSMs, in initialization order.
Any LSMs left off this list, except for those with order
diff --git a/security/Makefile b/security/Makefile
index 4601230ba442a..b68cb56e419bc 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_CGROUPS) += device_cgroup.o
obj-$(CONFIG_BPF_LSM) += bpf/
obj-$(CONFIG_SECURITY_LANDLOCK) += landlock/
obj-$(CONFIG_SECURITY_IPE) += ipe/
+obj-$(CONFIG_SECURITY_HORNET) += hornet/
# Object integrity file lists
obj-$(CONFIG_INTEGRITY) += integrity/
diff --git a/security/hornet/Kconfig b/security/hornet/Kconfig
new file mode 100644
index 0000000000000..19406aa237ac6
--- /dev/null
+++ b/security/hornet/Kconfig
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config SECURITY_HORNET
+ bool "Hornet support"
+ depends on SECURITY
+ default n
+ help
+ This selects Hornet.
+ Further information can be found in
+ Documentation/admin-guide/LSM/Hornet.rst.
+
+ If you are unsure how to answer this question, answer N.
diff --git a/security/hornet/Makefile b/security/hornet/Makefile
new file mode 100644
index 0000000000000..26b6f954f762e
--- /dev/null
+++ b/security/hornet/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_SECURITY_HORNET) := hornet.o
+
+hornet-y := hornet.asn1.o \
+ hornet_lsm.o \
+
+$(obj)/hornet.asn1.o: $(obj)/hornet.asn1.c $(obj)/hornet.asn1.h
diff --git a/security/hornet/hornet.asn1 b/security/hornet/hornet.asn1
new file mode 100644
index 0000000000000..c8d47b16b65d7
--- /dev/null
+++ b/security/hornet/hornet.asn1
@@ -0,0 +1,13 @@
+-- SPDX-License-Identifier: BSD-3-Clause
+--
+-- Copyright (C) 2009 IETF Trust and the persons identified as authors
+-- of the code
+--
+-- https://www.rfc-editor.org/rfc/rfc5652#section-3
+
+HornetData ::= SET OF Map
+
+Map ::= SEQUENCE {
+ index INTEGER ({ hornet_map_index }),
+ sha OCTET STRING ({ hornet_map_hash })
+} ({ hornet_next_map })
diff --git a/security/hornet/hornet_lsm.c b/security/hornet/hornet_lsm.c
new file mode 100644
index 0000000000000..21355bb7b5583
--- /dev/null
+++ b/security/hornet/hornet_lsm.c
@@ -0,0 +1,333 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Hornet Linux Security Module
+ *
+ * Author: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
+ *
+ * Copyright (C) 2026 Microsoft Corporation
+ */
+
+#include <linux/lsm_hooks.h>
+#include <uapi/linux/lsm.h>
+#include <linux/bpf.h>
+#include <linux/verification.h>
+#include <crypto/public_key.h>
+#include <linux/module_signature.h>
+#include <crypto/pkcs7.h>
+#include <linux/sort.h>
+#include <linux/asn1_decoder.h>
+#include <linux/oid_registry.h>
+#include "hornet.asn1.h"
+
+#define MAX_USED_MAPS 64
+
+struct hornet_maps {
+ bpfptr_t fd_array;
+};
+
+/* The only hashing algorithm available is SHA256 due to it be hardcoded
+ in the bpf subsystem. */
+
+struct hornet_parse_context {
+ int indexes[MAX_USED_MAPS];
+ bool skips[MAX_USED_MAPS];
+ unsigned char hashes[SHA256_DIGEST_SIZE * MAX_USED_MAPS];
+ int hash_count;
+};
+
+struct hornet_prog_security_struct {
+ bool checked[MAX_USED_MAPS];
+ unsigned char hashes[SHA256_DIGEST_SIZE * MAX_USED_MAPS];
+};
+
+struct hornet_map_security_struct {
+ bool checked;
+ int index;
+};
+
+struct lsm_blob_sizes hornet_blob_sizes __ro_after_init = {
+ .lbs_bpf_map = sizeof(struct hornet_map_security_struct),
+ .lbs_bpf_prog = sizeof(struct hornet_prog_security_struct),
+};
+
+static inline struct hornet_prog_security_struct *
+hornet_bpf_prog_security(struct bpf_prog *prog)
+{
+ return prog->aux->security + hornet_blob_sizes.lbs_bpf_prog;
+}
+
+static inline struct hornet_map_security_struct *
+hornet_bpf_map_security(struct bpf_map *map)
+{
+ return map->security + hornet_blob_sizes.lbs_bpf_map;
+}
+
+static int hornet_verify_hashes(struct hornet_maps *maps,
+ struct hornet_parse_context *ctx,
+ struct bpf_prog *prog)
+{
+ int map_fd;
+ u32 i;
+ struct bpf_map *map;
+ int err = 0;
+ unsigned char hash[SHA256_DIGEST_SIZE];
+ struct hornet_prog_security_struct *security = hornet_bpf_prog_security(prog);
+ struct hornet_map_security_struct *map_security;
+
+ for (i = 0; i < ctx->hash_count; i++) {
+ if (ctx->skips[i])
+ continue;
+
+ err = copy_from_bpfptr_offset(&map_fd, maps->fd_array,
+ ctx->indexes[i] * sizeof(map_fd),
+ sizeof(map_fd));
+ if (err < 0)
+ return LSM_INT_VERDICT_FAULT;
+
+ CLASS(fd, f)(map_fd);
+ if (fd_empty(f))
+ return LSM_INT_VERDICT_FAULT;
+ if (unlikely(fd_file(f)->f_op != &bpf_map_fops))
+ return LSM_INT_VERDICT_FAULT;
+
+ map = fd_file(f)->private_data;
+ if (!map->frozen)
+ return LSM_INT_VERDICT_FAULT;
+
+ map->ops->map_get_hash(map, SHA256_DIGEST_SIZE, hash);
+
+ err = memcmp(hash, &ctx->hashes[i * SHA256_DIGEST_SIZE],
+ SHA256_DIGEST_SIZE);
+ if (err)
+ return LSM_INT_VERDICT_UNEXPECTED;
+
+ security->checked[i] = true;
+ memcpy(&security->hashes[i * SHA256_DIGEST_SIZE], hash, SHA256_DIGEST_SIZE);
+ map_security = hornet_bpf_map_security(map);
+ map_security->checked = true;
+ map_security->index = i;
+ }
+ return LSM_INT_VERDICT_OK;
+}
+
+int hornet_next_map(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct hornet_parse_context *ctx = (struct hornet_parse_context *)context;
+
+ if (++ctx->hash_count >= MAX_USED_MAPS)
+ return -EINVAL;
+ return 0;
+}
+
+int hornet_map_index(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct hornet_parse_context *ctx = (struct hornet_parse_context *)context;
+
+ if (vlen > 1)
+ return -EINVAL;
+
+ ctx->indexes[ctx->hash_count] = *(u8 *)value;
+ return 0;
+}
+
+int hornet_map_hash(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+
+{
+ struct hornet_parse_context *ctx = (struct hornet_parse_context *)context;
+
+ if (vlen != SHA256_DIGEST_SIZE && vlen != 0)
+ return -EINVAL;
+
+ if (vlen) {
+ ctx->skips[ctx->hash_count] = false;
+ memcpy(&ctx->hashes[ctx->hash_count * SHA256_DIGEST_SIZE], value, vlen);
+ } else
+ ctx->skips[ctx->hash_count] = true;
+
+ return 0;
+}
+
+static int hornet_check_program(struct bpf_prog *prog, union bpf_attr *attr,
+ struct bpf_token *token, bool is_kernel,
+ enum lsm_integrity_verdict *verdict)
+{
+ struct hornet_maps maps = {0};
+ bpfptr_t usig = make_bpfptr(attr->signature, is_kernel);
+ struct pkcs7_message *msg;
+ struct hornet_parse_context *ctx;
+ void *sig;
+ int err;
+ const void *authattrs;
+ size_t authattrs_len;
+
+ if (!attr->signature) {
+ *verdict = LSM_INT_VERDICT_UNSIGNED;
+ return 0;
+ }
+
+ ctx = kzalloc(sizeof(struct hornet_parse_context), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ maps.fd_array = make_bpfptr(attr->fd_array, is_kernel);
+ sig = kzalloc(attr->signature_size, GFP_KERNEL);
+ if (!sig) {
+ err = -ENOMEM;
+ goto out;
+ }
+ err = copy_from_bpfptr(sig, usig, attr->signature_size);
+ if (err != 0)
+ goto cleanup_sig;
+
+ msg = pkcs7_parse_message(sig, attr->signature_size);
+ if (IS_ERR(msg)) {
+ err = LSM_INT_VERDICT_BADSIG;
+ goto cleanup_sig;
+ }
+
+ if (verify_pkcs7_message_sig(prog->insnsi, prog->len * sizeof(struct bpf_insn), msg,
+ VERIFY_USE_SECONDARY_KEYRING,
+ VERIFYING_BPF_SIGNATURE,
+ NULL, NULL)) {
+ err = LSM_INT_VERDICT_UNKNOWNKEY;
+ goto cleanup_msg;
+ }
+
+ if (pkcs7_get_authattr(msg, OID_hornet_data,
+ &authattrs, &authattrs_len) == -ENODATA) {
+ err = LSM_INT_VERDICT_PARTIALSIG;
+ goto cleanup_msg;
+ }
+
+ err = asn1_ber_decoder(&hornet_decoder, ctx, authattrs, authattrs_len);
+ if (err < 0 || authattrs == NULL) {
+ err = LSM_INT_VERDICT_BADSIG;
+ goto cleanup_msg;
+ }
+
+ err = hornet_verify_hashes(&maps, ctx, prog);
+
+cleanup_msg:
+ pkcs7_free_message(msg);
+cleanup_sig:
+ kfree(sig);
+out:
+ kfree(ctx);
+ return err;
+}
+
+static const struct lsm_id hornet_lsmid = {
+ .name = "hornet",
+ .id = LSM_ID_HORNET,
+};
+
+static int hornet_bpf_prog_load_integrity(struct bpf_prog *prog, union bpf_attr *attr,
+ struct bpf_token *token, bool is_kernel)
+{
+ enum lsm_integrity_verdict verdict;
+ int result = hornet_check_program(prog, attr, token, is_kernel, &verdict);
+
+ if (result < 0)
+ return result;
+
+ return security_bpf_prog_load_post_integrity(prog, attr, token, is_kernel,
+ &hornet_lsmid, verdict);
+}
+
+static int hornet_verify_map(struct bpf_prog *prog, int index)
+{
+ unsigned char hash[SHA256_DIGEST_SIZE];
+ int i;
+ struct bpf_map *map;
+ struct hornet_prog_security_struct *security = hornet_bpf_prog_security(prog);
+ struct hornet_map_security_struct *map_security;
+
+ if (!security->checked[index])
+ return 0;
+
+ for (i = 0; i < prog->aux->used_map_cnt; i++) {
+ map = prog->aux->used_maps[i];
+ map_security = hornet_bpf_map_security(map);
+ if (map_security->index != index)
+ continue;
+
+ if (!map->frozen)
+ return -EPERM;
+
+ map->ops->map_get_hash(map, SHA256_DIGEST_SIZE, hash);
+ if (memcmp(hash, &security->hashes[index * SHA256_DIGEST_SIZE],
+ SHA256_DIGEST_SIZE) != 0)
+ return -EPERM;
+ else
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int hornet_check_prog_maps(u32 ufd)
+{
+ CLASS(fd, f)(ufd);
+ struct bpf_prog *prog;
+ int i, result = 0;
+
+ if (fd_empty(f))
+ return -EBADF;
+ if (fd_file(f)->f_op != &bpf_prog_fops)
+ return -EINVAL;
+
+ prog = fd_file(f)->private_data;
+
+ mutex_lock(&prog->aux->used_maps_mutex);
+ if (!prog->aux->used_map_cnt)
+ goto out;
+
+ for (i = 0; i < prog->aux->used_map_cnt; i++) {
+ result = hornet_verify_map(prog, i);
+ if (result)
+ goto out;
+ }
+out:
+ mutex_unlock(&prog->aux->used_maps_mutex);
+
+ return result;
+}
+
+static int hornet_bpf(int cmd, union bpf_attr *attr, unsigned int size, bool kernel)
+{
+ /* in horent_bpf(), anything that had originated from kernel space we assume
+ has already been checked, in some form or another, so we don't bother
+ checking the intergity of any maps. In hornet_bpf_prog_load_integrity(),
+ hornet doesn't make any opinion on that and delegates that to the downstream
+ policy enforcement. */
+
+ if (cmd != BPF_PROG_RUN)
+ return 0;
+ if (kernel)
+ return 0;
+
+ return hornet_check_prog_maps(attr->test.prog_fd);
+}
+
+static struct security_hook_list hornet_hooks[] __ro_after_init = {
+ LSM_HOOK_INIT(bpf_prog_load_integrity, hornet_bpf_prog_load_integrity),
+ LSM_HOOK_INIT(bpf, hornet_bpf),
+};
+
+static int __init hornet_init(void)
+{
+ pr_info("Hornet: eBPF signature verification enabled\n");
+ security_add_hooks(hornet_hooks, ARRAY_SIZE(hornet_hooks), &hornet_lsmid);
+ return 0;
+}
+
+DEFINE_LSM(hornet) = {
+ .id = &hornet_lsmid,
+ .blobs = &hornet_blob_sizes,
+ .init = hornet_init,
+};
--
2.53.0
^ permalink raw reply related
* [PATCH v3 7/9] hornet: Introduce gen_sig
From: Blaise Boscaccy @ 2026-03-26 6:06 UTC (permalink / raw)
To: Blaise Boscaccy, Jonathan Corbet, Paul Moore, James Morris,
Serge E. Hallyn, Mickaël Salaün, Günther Noack,
Dr. David Alan Gilbert, Andrew Morton, James.Bottomley, dhowells,
Fan Wu, Ryan Foster, Randy Dunlap, linux-security-module,
linux-doc, linux-kernel, bpf
In-Reply-To: <20260326060655.2550595-1-bboscaccy@linux.microsoft.com>
This introduces the gen_sig tool. It creates a pkcs#7 signature of a
data payload. Additionally it appends a signed attribute containing a
set of hashes.
Typical usage is to provide a payload containing the light skeleton
ebpf syscall program binary and it's associated maps, which can be
extracted from the auto-generated skeleton header.
Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
---
scripts/Makefile | 1 +
scripts/hornet/Makefile | 5 +
scripts/hornet/gen_sig.c | 392 ++++++++++++++++++++++++++++++++++++
scripts/hornet/write-sig.sh | 27 +++
4 files changed, 425 insertions(+)
create mode 100644 scripts/hornet/Makefile
create mode 100644 scripts/hornet/gen_sig.c
create mode 100755 scripts/hornet/write-sig.sh
diff --git a/scripts/Makefile b/scripts/Makefile
index 0941e5ce7b575..dea8ab91bbe4e 100644
--- a/scripts/Makefile
+++ b/scripts/Makefile
@@ -63,6 +63,7 @@ subdir-$(CONFIG_GENKSYMS) += genksyms
subdir-$(CONFIG_GENDWARFKSYMS) += gendwarfksyms
subdir-$(CONFIG_SECURITY_SELINUX) += selinux
subdir-$(CONFIG_SECURITY_IPE) += ipe
+subdir-$(CONFIG_SECURITY_HORNET) += hornet
# Let clean descend into subdirs
subdir- += basic dtc gdb kconfig mod
diff --git a/scripts/hornet/Makefile b/scripts/hornet/Makefile
new file mode 100644
index 0000000000000..3ee41e5e9a9ff
--- /dev/null
+++ b/scripts/hornet/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+hostprogs-always-y := gen_sig
+
+HOSTCFLAGS_gen_sig.o = $(shell $(HOSTPKG_CONFIG) --cflags libcrypto 2> /dev/null)
+HOSTLDLIBS_gen_sig = $(shell $(HOSTPKG_CONFIG) --libs libcrypto 2> /dev/null || echo -lcrypto)
diff --git a/scripts/hornet/gen_sig.c b/scripts/hornet/gen_sig.c
new file mode 100644
index 0000000000000..f966516ebc99b
--- /dev/null
+++ b/scripts/hornet/gen_sig.c
@@ -0,0 +1,392 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+ *
+ * Generate a signature for an eBPF program along with appending
+ * map hashes as signed attributes
+ *
+ * Copyright © 2025 Microsoft Corporation.
+ *
+ * Authors: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the licence, or (at your option) any later version.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <err.h>
+#include <getopt.h>
+
+#include <openssl/cms.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/pkcs7.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#include <openssl/objects.h>
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/opensslv.h>
+#include <openssl/bio.h>
+#include <openssl/stack.h>
+
+#if OPENSSL_VERSION_MAJOR >= 3
+# define USE_PKCS11_PROVIDER
+# include <openssl/provider.h>
+# include <openssl/store.h>
+#else
+# if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_DEPRECATED_3_0)
+# define USE_PKCS11_ENGINE
+# include <openssl/engine.h>
+# endif
+#endif
+#include "../ssl-common.h"
+
+#define SHA256_LEN 32
+#define BUF_SIZE (1 << 15) // 32 KiB
+#define MAX_HASHES 64
+
+struct hash_spec {
+ char *file;
+ int index;
+};
+
+typedef struct {
+ ASN1_INTEGER *index;
+ ASN1_OCTET_STRING *hash;
+
+} HORNET_MAP;
+
+DECLARE_ASN1_FUNCTIONS(HORNET_MAP)
+ASN1_SEQUENCE(HORNET_MAP) = {
+ ASN1_SIMPLE(HORNET_MAP, index, ASN1_INTEGER),
+ ASN1_SIMPLE(HORNET_MAP, hash, ASN1_OCTET_STRING)
+} ASN1_SEQUENCE_END(HORNET_MAP);
+
+IMPLEMENT_ASN1_FUNCTIONS(HORNET_MAP)
+
+DEFINE_STACK_OF(HORNET_MAP)
+
+typedef struct {
+ STACK_OF(HORNET_MAP) * maps;
+} MAP_SET;
+
+DECLARE_ASN1_FUNCTIONS(MAP_SET)
+ASN1_SEQUENCE(MAP_SET) = {
+ ASN1_SET_OF(MAP_SET, maps, HORNET_MAP)
+} ASN1_SEQUENCE_END(MAP_SET);
+
+IMPLEMENT_ASN1_FUNCTIONS(MAP_SET)
+
+#define DIE(...) do { fprintf(stderr, __VA_ARGS__); fputc('\n', stderr); \
+ exit(EXIT_FAILURE); } while (0)
+
+static BIO *bio_open_wr(const char *path)
+{
+ BIO *b = BIO_new_file(path, "wb");
+
+ if (!b) {
+ perror(path);
+ ERR_print_errors_fp(stderr);
+ exit(EXIT_FAILURE);
+ }
+ return b;
+}
+
+static void usage(const char *prog)
+{
+ fprintf(stderr,
+ "Usage:\n"
+ " %s --data content.bin --cert signer.crt --key signer.key [-pass pass]\n"
+ " --out newsig.p7b \n"
+ " --add FILE:index [--add FILE:index ...]\n",
+ prog);
+}
+
+static const char *key_pass;
+
+static int pem_pw_cb(char *buf, int len, int w, void *v)
+{
+ int pwlen;
+
+ if (!key_pass)
+ return -1;
+
+ pwlen = strlen(key_pass);
+ if (pwlen >= len)
+ return -1;
+
+ strcpy(buf, key_pass);
+
+ key_pass = NULL;
+
+ return pwlen;
+}
+
+static EVP_PKEY *read_private_key(const char *private_key_name)
+{
+ EVP_PKEY *private_key;
+ BIO *b;
+
+ b = BIO_new_file(private_key_name, "rb");
+ ERR(!b, "%s", private_key_name);
+ private_key = PEM_read_bio_PrivateKey(b, NULL, pem_pw_cb,
+ NULL);
+ ERR(!private_key, "%s", private_key_name);
+ BIO_free(b);
+
+ return private_key;
+}
+
+static X509 *read_x509(const char *x509_name)
+{
+ unsigned char buf[2];
+ X509 *x509;
+ BIO *b;
+ int n;
+
+ b = BIO_new_file(x509_name, "rb");
+ ERR(!b, "%s", x509_name);
+
+ /* Look at the first two bytes of the file to determine the encoding */
+ n = BIO_read(b, buf, 2);
+ if (n != 2) {
+ if (BIO_should_retry(b)) {
+ fprintf(stderr, "%s: Read wanted retry\n", x509_name);
+ exit(1);
+ }
+ if (n >= 0) {
+ fprintf(stderr, "%s: Short read\n", x509_name);
+ exit(1);
+ }
+ ERR(1, "%s", x509_name);
+ }
+
+ ERR(BIO_reset(b) != 0, "%s", x509_name);
+
+ if (buf[0] == 0x30 && buf[1] >= 0x81 && buf[1] <= 0x84)
+ /* Assume raw DER encoded X.509 */
+ x509 = d2i_X509_bio(b, NULL);
+ else
+ /* Assume PEM encoded X.509 */
+ x509 = PEM_read_bio_X509(b, NULL, NULL, NULL);
+
+ BIO_free(b);
+ ERR(!x509, "%s", x509_name);
+
+ return x509;
+}
+
+static int sha256(const char *path, unsigned char out[SHA256_LEN], unsigned int *out_len)
+{
+ FILE *f;
+ int rc;
+ EVP_MD_CTX *ctx;
+ unsigned char buf[BUF_SIZE];
+ size_t n;
+ unsigned int mdlen = 0;
+
+ if (!path || !out)
+ return -1;
+
+ f = fopen(path, "rb");
+ if (!f) {
+ perror("fopen");
+ return -2;
+ }
+
+ ERR_load_crypto_strings();
+
+ rc = -3;
+ ctx = EVP_MD_CTX_new();
+ if (!ctx) {
+ rc = -4;
+ goto done;
+ }
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ if (EVP_DigestInit_ex2(ctx, EVP_sha256(), NULL) != 1) {
+ rc = -5;
+ goto done;
+ }
+#else
+ if (EVP_DigestInit_ex(ctx, EVP_sha256(), NULL) != 1) {
+ rc = -5;
+ goto done;
+ }
+#endif
+ while ((n = fread(buf, 1, sizeof(buf), f)) > 0) {
+ if (EVP_DigestUpdate(ctx, buf, n) != 1) {
+ rc = -6;
+ goto done;
+ }
+ }
+ if (ferror(f)) {
+ rc = -7;
+ goto done;
+ }
+
+ if (EVP_DigestFinal_ex(ctx, out, &mdlen) != 1) {
+ rc = -8;
+ goto done;
+ }
+ if (mdlen != SHA256_LEN) {
+ rc = -9;
+ goto done;
+ }
+
+ if (out_len)
+ *out_len = mdlen;
+ rc = 0;
+
+done:
+ EVP_MD_CTX_free(ctx);
+ fclose(f);
+ ERR_free_strings();
+ return rc;
+}
+
+static void add_hash(MAP_SET *set, unsigned char *buffer, int buffer_len, int index)
+{
+ HORNET_MAP *map = NULL;
+
+ map = HORNET_MAP_new();
+ ASN1_INTEGER_set(map->index, index);
+ ASN1_OCTET_STRING_set(map->hash, buffer, buffer_len);
+ sk_HORNET_MAP_push(set->maps, map);
+}
+
+int main(int argc, char **argv)
+{
+ const char *cert_path = NULL;
+ const char *key_path = NULL;
+ const char *data_path = NULL;
+ const char *out_path = NULL;
+
+ X509 *signer;
+ EVP_PKEY *pkey;
+ BIO *data_in;
+ CMS_ContentInfo *cms_out;
+ struct hash_spec hashes[MAX_HASHES];
+ int hash_count = 0;
+ int flags;
+ CMS_SignerInfo *si;
+ MAP_SET *set;
+ unsigned char hash_buffer[SHA256_LEN];
+ unsigned int hash_len;
+ ASN1_OBJECT *oid;
+ unsigned char *der = NULL;
+ int der_len;
+ int err;
+ BIO *b_out;
+ int i;
+ char opt;
+
+ const char *short_opts = "C:K:P:O:A:Sh";
+
+ static const struct option long_opts[] = {
+ {"cert", required_argument, 0, 'C'},
+ {"key", required_argument, 0, 'K'},
+ {"pass", required_argument, 0, 'P'},
+ {"out", required_argument, 0, 'O'},
+ {"data", required_argument, 0, 'D'},
+ {"add", required_argument, 0, 'A'},
+ {"help", no_argument, 0, 'h'},
+ {0, 0, 0, 0}
+ };
+
+ while ((opt = getopt_long_only(argc, argv, short_opts, long_opts, NULL)) != -1) {
+ switch (opt) {
+ case 'C':
+ cert_path = optarg;
+ break;
+ case 'K':
+ key_path = optarg;
+ break;
+ case 'P':
+ key_pass = optarg;
+ break;
+ case 'O':
+ out_path = optarg;
+ break;
+ case 'D':
+ data_path = optarg;
+ break;
+ case 'A':
+ if (strchr(optarg, ':')) {
+ hashes[hash_count].file = strsep(&optarg, ":");
+ hashes[hash_count].index = atoi(optarg);
+ hash_count++;
+ } else {
+ usage(argv[0]);
+ return EXIT_FAILURE;
+ }
+ }
+ }
+
+ if (!cert_path || !key_path || !out_path || !data_path) {
+ usage(argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ OpenSSL_add_all_algorithms();
+ ERR_load_crypto_strings();
+
+ signer = read_x509(cert_path);
+ ERR(!signer, "Load cert failed");
+
+ pkey = read_private_key(key_path);
+ ERR(!pkey, "Load key failed");
+
+ data_in = BIO_new_file(data_path, "rb");
+ ERR(!data_in, "Load data failed");
+
+ cms_out = CMS_sign(NULL, NULL, NULL, NULL,
+ CMS_NOCERTS | CMS_PARTIAL | CMS_BINARY | CMS_DETACHED);
+ ERR(!cms_out, "create cms failed");
+
+ flags = CMS_NOCERTS | CMS_PARTIAL | CMS_BINARY | CMS_NOSMIMECAP | CMS_DETACHED;
+
+ si = CMS_add1_signer(cms_out, signer, pkey, EVP_sha256(), flags);
+ ERR(!si, "add signer failed");
+
+ set = MAP_SET_new();
+ set->maps = sk_HORNET_MAP_new_null();
+
+ for (i = 0; i < hash_count; i++) {
+ sha256(hashes[i].file, hash_buffer, &hash_len);
+ add_hash(set, hash_buffer, hash_len, hashes[i].index);
+ }
+
+ oid = OBJ_txt2obj("2.25.316487325684022475439036912669789383960", 1);
+ if (!oid) {
+ ERR_print_errors_fp(stderr);
+ DIE("create oid failed");
+ }
+
+ der_len = ASN1_item_i2d((ASN1_VALUE *)set, &der, ASN1_ITEM_rptr(MAP_SET));
+ CMS_signed_add1_attr_by_OBJ(si, oid, V_ASN1_SEQUENCE, der, der_len);
+
+ err = CMS_final(cms_out, data_in, NULL, CMS_NOCERTS | CMS_BINARY);
+ ERR(!err, "cms final failed");
+
+ OPENSSL_free(der);
+ MAP_SET_free(set);
+
+ b_out = bio_open_wr(out_path);
+ ERR(!b_out, "opening output path failed");
+
+ i2d_CMS_bio_stream(b_out, cms_out, NULL, 0);
+
+ BIO_free(data_in);
+ BIO_free(b_out);
+ EVP_cleanup();
+ ERR_free_strings();
+ return 0;
+}
diff --git a/scripts/hornet/write-sig.sh b/scripts/hornet/write-sig.sh
new file mode 100755
index 0000000000000..7eaabe3bab9aa
--- /dev/null
+++ b/scripts/hornet/write-sig.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2025 Microsoft Corporation
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of version 2 of the GNU General Public
+# License as published by the Free Software Foundation.
+
+function usage() {
+ echo "Sample for rewriting an autogenerated eBPF lskel headers"
+ echo "with a new signature"
+ echo ""
+ echo "USAGE: header_file sig"
+ exit
+}
+
+ARGC=$#
+
+EXPECTED_ARGS=2
+
+if [ $ARGC -ne $EXPECTED_ARGS ] ; then
+ usage
+else
+ SIG=$(xxd -p $2 | tr -d '\n' | sed 's/\(..\)/\\\\x\1/g')
+ sed '/const char opts_sig/,/;/c\\tstatic const char opts_sig[] __attribute__((__aligned__(8))) = "\\\n'"$(printf '%s\n' "$SIG")"'\";' $1
+fi
--
2.53.0
^ permalink raw reply related
* [PATCH v3 8/9] hornet: Add a light skeleton data extractor scripts
From: Blaise Boscaccy @ 2026-03-26 6:06 UTC (permalink / raw)
To: Blaise Boscaccy, Jonathan Corbet, Paul Moore, James Morris,
Serge E. Hallyn, Mickaël Salaün, Günther Noack,
Dr. David Alan Gilbert, Andrew Morton, James.Bottomley, dhowells,
Fan Wu, Ryan Foster, Randy Dunlap, linux-security-module,
linux-doc, linux-kernel, bpf
In-Reply-To: <20260326060655.2550595-1-bboscaccy@linux.microsoft.com>
These script eases light skeleton development against Hornet by
generating a data payloads which can be used for signing a light
skeleton binary using gen_sig.
Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
---
| 27 +++++++++++++++++++++++++++
| 27 +++++++++++++++++++++++++++
| 27 +++++++++++++++++++++++++++
3 files changed, 81 insertions(+)
create mode 100755 scripts/hornet/extract-insn.sh
create mode 100755 scripts/hornet/extract-map.sh
create mode 100755 scripts/hornet/extract-skel.sh
--git a/scripts/hornet/extract-insn.sh b/scripts/hornet/extract-insn.sh
new file mode 100755
index 0000000000000..52338f057ff6b
--- /dev/null
+++ b/scripts/hornet/extract-insn.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2025 Microsoft Corporation
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of version 2 of the GNU General Public
+# License as published by the Free Software Foundation.
+
+function usage() {
+ echo "Sample script for extracting instructions"
+ echo "autogenerated eBPF lskel headers"
+ echo ""
+ echo "USAGE: header_file"
+ exit
+}
+
+ARGC=$#
+
+EXPECTED_ARGS=1
+
+if [ $ARGC -ne $EXPECTED_ARGS ] ; then
+ usage
+else
+ printf $(gcc -E $1 | grep "opts_insn" | \
+ awk -F"=" '{print $2}' | sed 's/;\+$//' | sed 's/\"//g')
+fi
--git a/scripts/hornet/extract-map.sh b/scripts/hornet/extract-map.sh
new file mode 100755
index 0000000000000..c309f505c6238
--- /dev/null
+++ b/scripts/hornet/extract-map.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2025 Microsoft Corporation
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of version 2 of the GNU General Public
+# License as published by the Free Software Foundation.
+
+function usage() {
+ echo "Sample script for extracting instructions"
+ echo "autogenerated eBPF lskel headers"
+ echo ""
+ echo "USAGE: header_file"
+ exit
+}
+
+ARGC=$#
+
+EXPECTED_ARGS=1
+
+if [ $ARGC -ne $EXPECTED_ARGS ] ; then
+ usage
+else
+ printf $(gcc -E $1 | grep "opts_data" | \
+ awk -F"=" '{print $2}' | sed 's/;\+$//' | sed 's/\"//g')
+fi
--git a/scripts/hornet/extract-skel.sh b/scripts/hornet/extract-skel.sh
new file mode 100755
index 0000000000000..6550a86b89917
--- /dev/null
+++ b/scripts/hornet/extract-skel.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2025 Microsoft Corporation
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of version 2 of the GNU General Public
+# License as published by the Free Software Foundation.
+
+function usage() {
+ echo "Sample script for extracting instructions and map data out of"
+ echo "autogenerated eBPF lskel headers"
+ echo ""
+ echo "USAGE: header_file field"
+ exit
+}
+
+ARGC=$#
+
+EXPECTED_ARGS=2
+
+if [ $ARGC -ne $EXPECTED_ARGS ] ; then
+ usage
+else
+ printf $(gcc -E $1 | grep "static const char opts_$2" | \
+ awk -F"=" '{print $2}' | sed 's/;\+$//' | sed 's/\"//g')
+fi
--
2.53.0
^ permalink raw reply related
* [PATCH v3 9/9] selftests/hornet: Add a selftest for the Hornet LSM
From: Blaise Boscaccy @ 2026-03-26 6:06 UTC (permalink / raw)
To: Blaise Boscaccy, Jonathan Corbet, Paul Moore, James Morris,
Serge E. Hallyn, Mickaël Salaün, Günther Noack,
Dr. David Alan Gilbert, Andrew Morton, James.Bottomley, dhowells,
Fan Wu, Ryan Foster, Randy Dunlap, linux-security-module,
linux-doc, linux-kernel, bpf
In-Reply-To: <20260326060655.2550595-1-bboscaccy@linux.microsoft.com>
This selftest contains a testcase that utilizes light skeleton eBPF
loaders and exercises hornet's map validation.
Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
---
tools/testing/selftests/Makefile | 1 +
tools/testing/selftests/hornet/Makefile | 63 ++++++++++++++++++++
tools/testing/selftests/hornet/loader.c | 21 +++++++
tools/testing/selftests/hornet/trivial.bpf.c | 33 ++++++++++
4 files changed, 118 insertions(+)
create mode 100644 tools/testing/selftests/hornet/Makefile
create mode 100644 tools/testing/selftests/hornet/loader.c
create mode 100644 tools/testing/selftests/hornet/trivial.bpf.c
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index 450f13ba4cca9..4e2d1cd88c825 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -44,6 +44,7 @@ TARGETS += ftrace
TARGETS += futex
TARGETS += gpio
TARGETS += hid
+TARGETS += hornet
TARGETS += intel_pstate
TARGETS += iommu
TARGETS += ipc
diff --git a/tools/testing/selftests/hornet/Makefile b/tools/testing/selftests/hornet/Makefile
new file mode 100644
index 0000000000000..432bce59f54e7
--- /dev/null
+++ b/tools/testing/selftests/hornet/Makefile
@@ -0,0 +1,63 @@
+# SPDX-License-Identifier: GPL-2.0
+include ../../../build/Build.include
+include ../../../scripts/Makefile.arch
+include ../../../scripts/Makefile.include
+
+CLANG ?= clang
+CFLAGS := -g -O2 -Wall
+BPFTOOL ?= $(TOOLSDIR)/bpf/bpftool/bpftool
+SCRIPTSDIR := $(abspath ../../../../scripts/hornet)
+TOOLSDIR := $(abspath ../../..)
+LIBDIR := $(TOOLSDIR)/lib
+BPFDIR := $(LIBDIR)/bpf
+TOOLSINCDIR := $(TOOLSDIR)/include
+APIDIR := $(TOOLSINCDIR)/uapi
+CERTDIR := $(abspath ../../../../certs)
+PKG_CONFIG ?= $(CROSS_COMPILE)pkg-config
+
+TEST_GEN_PROGS := loader
+TEST_GEN_FILES := vmlinux.h loader.h trivial.bpf.o map.bin sig.bin insn.bin signed_loader.h
+$(TEST_GEN_PROGS): LDLIBS += -lbpf
+$(TEST_GEN_PROGS): $(TEST_GEN_FILES)
+
+include ../lib.mk
+
+BPF_CFLAGS := -target bpf \
+ -D__TARGET_ARCH_$(ARCH) \
+ -I/usr/include/$(shell uname -m)-linux-gnu \
+ $(KHDR_INCLUDES)
+
+vmlinux.h:
+ $(BPFTOOL) btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
+
+trivial.bpf.o: trivial.bpf.c vmlinux.h
+ $(CLANG) $(CFLAGS) $(BPF_CFLAGS) -c $< -o $@
+
+loader.h: trivial.bpf.o
+ $(BPFTOOL) gen skeleton -S -k $(CERTDIR)/signing_key.pem -i $(CERTDIR)/signing_key.x509 \
+ -L $< name trivial > $@
+
+insn.bin: loader.h
+ $(SCRIPTSDIR)/extract-insn.sh $< > $@
+
+map.bin: loader.h
+ $(SCRIPTSDIR)/extract-map.sh $< > $@
+
+$(OUTPUT)/gen_sig: ../../../../scripts/hornet/gen_sig.c
+ $(call msg,GEN_SIG,,$@)
+ $(Q)$(CC) $(shell $(PKG_CONFIG) --cflags libcrypto 2> /dev/null) \
+ $< -o $@ \
+ $(shell $(PKG_CONFIG) --libs libcrypto 2> /dev/null || echo -lcrypto)
+
+sig.bin: insn.bin map.bin $(OUTPUT)/gen_sig
+ $(OUTPUT)/gen_sig --key $(CERTDIR)/signing_key.pem --cert $(CERTDIR)/signing_key.x509 \
+ --data insn.bin --add map.bin:0 --out sig.bin
+
+signed_loader.h: sig.bin
+ $(SCRIPTSDIR)/write-sig.sh loader.h sig.bin > $@
+
+loader: loader.c signed_loader.h
+ $(CC) $(CFLAGS) -I$(LIBDIR) -I$(APIDIR) $< -o $@ -lbpf
+
+
+EXTRA_CLEAN = $(OUTPUT)/gen_sig
diff --git a/tools/testing/selftests/hornet/loader.c b/tools/testing/selftests/hornet/loader.c
new file mode 100644
index 0000000000000..f27580c7262b3
--- /dev/null
+++ b/tools/testing/selftests/hornet/loader.c
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <sys/resource.h>
+#include <bpf/libbpf.h>
+#include <errno.h>
+#include "signed_loader.h"
+
+int main(int argc, char **argv)
+{
+ struct trivial *skel;
+
+ skel = trivial__open_and_load();
+ if (!skel)
+ return -1;
+
+ trivial__destroy(skel);
+ return 0;
+}
diff --git a/tools/testing/selftests/hornet/trivial.bpf.c b/tools/testing/selftests/hornet/trivial.bpf.c
new file mode 100644
index 0000000000000..d38c5b53ff932
--- /dev/null
+++ b/tools/testing/selftests/hornet/trivial.bpf.c
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+
+#include "vmlinux.h"
+
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_core_read.h>
+
+char LICENSE[] SEC("license") = "Dual BSD/GPL";
+
+int monitored_pid = 0;
+
+SEC("tracepoint/syscalls/sys_enter_unlinkat")
+int handle_enter_unlink(struct trace_event_raw_sys_enter *ctx)
+{
+ char filename[128] = { 0 };
+ struct task_struct *task;
+ unsigned long start_time = 0;
+ int pid = bpf_get_current_pid_tgid() >> 32;
+ char *pathname_ptr = (char *) BPF_CORE_READ(ctx, args[1]);
+
+ bpf_probe_read_str(filename, sizeof(filename), pathname_ptr);
+ task = (struct task_struct *)bpf_get_current_task();
+ start_time = BPF_CORE_READ(task, start_time);
+
+ bpf_printk("BPF triggered unlinkat by PID: %d, start_time %ld. pathname = %s",
+ pid, start_time, filename);
+
+ if (monitored_pid == pid)
+ bpf_printk("target pid found");
+
+ return 0;
+}
--
2.53.0
^ permalink raw reply related
* [PATCH] checkpatch: allow correctly handle full files on stdin
From: Dmitry Torokhov @ 2026-03-26 6:20 UTC (permalink / raw)
To: Dwaipayan Ray, Lukas Bulwahn, Joe Perches, Andy Whitcroft
Cc: Jonathan Corbet, Shuah Khan, workflows, linux-doc, linux-kernel
checkpatch does not handle full files well when they are passed on
stdin, because it does not know how to treat the text, and whether it is
a C file, or a DTS file, or something else, and so it assumes that when
it works with stdin it should be a unified diff. For full files it
expects to have a file name as an argument and read the contents from
disk. Unfortunately this does not well when trying to use checkpatch as
an online linter and feed it contents of an editor buffer that have not
made it to the disk yet.
To solve the above introduce a new optional argument --stdin-filename=FILE
that allows tell checkpatch the kind of file it is dealing with and
apply appropriate set of checks and rules to it.
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
Documentation/dev-tools/checkpatch.rst | 4 ++++
scripts/checkpatch.pl | 11 +++++++++++
2 files changed, 15 insertions(+)
diff --git a/Documentation/dev-tools/checkpatch.rst b/Documentation/dev-tools/checkpatch.rst
index dccede68698c..b521e3ca6ebf 100644
--- a/Documentation/dev-tools/checkpatch.rst
+++ b/Documentation/dev-tools/checkpatch.rst
@@ -68,6 +68,10 @@ Available options:
Show the diffed file position instead of the input file position.
+ - --stdin-filename
+
+ When using stdin, identify the file as FILE.
+
- -g, --git
Treat FILE as a single commit or a git revision range.
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index e56374662ff7..e26951ceb36b 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -57,6 +57,7 @@ my %ignore_type = ();
my @ignore = ();
my $help = 0;
my $configuration_file = ".checkpatch.conf";
+my $stdin_filename;
my $max_line_length = 100;
my $ignore_perl_version = 0;
my $minimum_perl_version = 5.10.0;
@@ -94,6 +95,7 @@ Options:
--emacs emacs compile window format
--terse one line per report
--showfile emit diffed file position, not input file position
+ --stdin-filename=FILE when using stdin, identify the file as FILE
-g, --git treat FILE as a single commit or git revision range
single git commit with:
<rev>
@@ -323,6 +325,7 @@ GetOptions(
'showfile!' => \$showfile,
'f|file!' => \$file,
'g|git!' => \$git,
+ 'stdin-filename=s' => \$stdin_filename,
'subjective!' => \$check,
'strict!' => \$check,
'ignore=s' => \@ignore,
@@ -2652,6 +2655,10 @@ sub is_userspace {
sub process {
my $filename = shift;
+ if ($filename eq '-' && defined($stdin_filename)) {
+ $filename = $stdin_filename;
+ }
+
my $linenr=0;
my $prevline="";
my $prevrawline="";
@@ -2891,6 +2898,10 @@ sub process {
$realfile =~ s@^([^/]*)/@@ if (!$file);
$in_commit_log = 0;
+ if ($realfile eq "-" && defined($stdin_filename)) {
+ $realfile = $stdin_filename;
+ }
+
$p1_prefix = $1;
if (!$file && $tree && $p1_prefix ne '' &&
-e "$root/$p1_prefix") {
--
2.53.0.1018.g2bb0e51243-goog
--
Dmitry
^ permalink raw reply related
* Re: [PATCH v4 00/10] gpu: nova-core: convert registers to use the kernel register macro
From: Alexandre Courbot @ 2026-03-26 6:27 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Miguel Ojeda,
Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Trevor Gross
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Zhi Wang, Eliot Courtney, dri-devel, linux-kernel, linux-riscv,
linux-doc, rust-for-linux, Alexandre Courbot
In-Reply-To: <20260325-b4-nova-register-v4-0-bdf172f0f6ca@nvidia.com>
On Wed Mar 25, 2026 at 11:46 AM JST, Alexandre Courbot wrote:
> nova-core carried its own helper macro to declare register types. Its
> purpose was to be temporary since the beginning, and to serve as a
> testbed to develop an equivalent that could be used kernel-wide.
>
> That equivalent has now been merged, so it is time to retire the
> nova-core local version.
>
> The kernel register macro has evolved into something significantly
> different from the one in nova-core, so it cannot be used as a drop-in
> replacement. All declarations and sites using registers need to be
> updated. No semantic change should happen as a result.
>
> All the patches in this series could also be squashed into a single one
> without altering their reviewability significantly.
>
> I hope to be able to merge this quickly so we can rebase in-flight
> series to use the updated register syntax.
>
> This series is based on drm-rust-next as of 2026-03-25 and has been
> tested on Turing.
>
> Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
Pushed to drm-rust-next, thanks!
^ permalink raw reply
* [PATCH net-next V9 00/14] devlink and mlx5: Support cross-function rate scheduling
From: Tariq Toukan @ 2026-03-26 6:59 UTC (permalink / raw)
To: Eric Dumazet, Jakub Kicinski, Paolo Abeni, Andrew Lunn,
David S. Miller
Cc: Donald Hunter, Simon Horman, Jiri Pirko, Jonathan Corbet,
Shuah Khan, Saeed Mahameed, Leon Romanovsky, Tariq Toukan,
Mark Bloch, Shuah Khan, Chuck Lever, Matthieu Baerts (NGI0),
Carolina Jubran, Cosmin Ratiu, Dragos Tatulea, Jacob Keller,
Shahar Shitrit, Daniel Zahka, Parav Pandit, Adithya Jayachandran,
Kees Cook, Shay Drori, Daniel Jurgens, Moshe Shemesh,
Willem de Bruijn, David Wei, Petr Machata, Stanislav Fomichev,
Daniel Borkmann, Joe Damato, Nikolay Aleksandrov, Vadim Fedorenko,
Michael S. Tsirkin, Antonio Quartulli, Allison Henderson,
Bui Quang Minh, Nimrod Oren, netdev, linux-kernel, linux-doc,
linux-rdma, linux-kselftest, Gal Pressman, Jiri Pirko
Hi,
This series by Cosmin adds support for cross-function rate scheduling in
devlink and mlx5.
See detailed explanation by Cosmin below [0].
Regards,
Tariq
[0]
devlink objects support rate management for TX scheduling, which
involves maintaining a tree of rate nodes that corresponds to TX
schedulers in hardware. 'man devlink-rate' has the full details.
The tree of rate nodes is maintained per devlink object, protected by
the devlink lock.
There exists hardware capable of instantiating TX scheduling trees
spanning multiple functions of the same physical device (and thus
devlink objects) and therefore the current API and locking scheme is
insufficient.
This patch series changes the devlink rate implementation and API to
allow supporting such hardware and managing TX scheduling trees across
multiple functions of a physical device.
Modeling this requires having devlink rate nodes with parents in other
devlink objects. A naive approach that relies on the current
one-lock-per-devlink model is impossible, as it would require in some
cases acquiring multiple devlink locks in the correct order.
The solution proposed in this patch series makes use of the recently
introduced shared devlink instance [1] to manage rate hierarchy changes
across multiple functions.
V1 of this patch series was sent a long time ago [2], using a different
approach of storing rates in a shared rate domain with special locking
rules. This new approach uses standard devlink instances and nesting.
The first part of the series adds support to devlink rates for
maintaining the rate tree across multiple functions.
The second part changes the mlx5 implementation to make use of this (and
cleans up remnants of the previous approach, involving rate domains).
The neat part about using the shared devlink object is that it works for
SFs as well, which are already nested in their parent PF instances. So
with this series, complex scheduling trees spanning multiple SFs across
multiple PFs of the same NIC can now be supported.
Patches:
devlink rate changes for cross-device TX scheduling:
devlink: Add helpers to lock nested-in instances
devlink: Migrate from info->user_ptr to info->ctx
devlink: Decouple rate storage from associated devlink object
devlink: Add parent dev to devlink API
devlink: Allow parent dev for rate-set and rate-new
devlink: Allow rate node parents from other devlinks
mlx5 support for cross-device TX scheduling:
net/mlx5: qos: Use mlx5_lag_query_bond_speed to query LAG speed
net/mlx5: qos: Expose a function to clear a vport's parent
net/mlx5: qos: Model the root node in the scheduling hierarchy
net/mlx5: qos: Remove qos domains and use shd lock
net/mlx5: qos: Support cross-device tx scheduling
selftests: drv-net: Add test for cross-esw rate scheduling
net/mlx5: Document devlink rates
[1] https://lore.kernel.org/all/20260312100407.551173-1-jiri@resnulli.us/T/#u
[2] https://lore.kernel.org/netdev/20250213180134.323929-1-tariqt@nvidia.com/
V9:
- Link to V8:
https://lore.kernel.org/all/20260324122848.36731-1-tariqt@nvidia.com/
- Updated netlink_gen.c policy related to index, missed during a rebase.
- Fixed off-by-one error in devlink_get_parent_from_attrs_lock.
- Fixed crash in qos.c when setting a parent to NULL.
Cosmin Ratiu (14):
devlink: Update nested instance locking comment
devlink: Add helpers to lock nested-in instances
devlink: Migrate from info->user_ptr to info->ctx
devlink: Decouple rate storage from associated devlink object
devlink: Add parent dev to devlink API
devlink: Allow parent dev for rate-set and rate-new
devlink: Allow rate node parents from other devlinks
net/mlx5: qos: Use mlx5_lag_query_bond_speed to query LAG speed
net/mlx5: qos: Expose a function to clear a vport's parent
net/mlx5: qos: Model the root node in the scheduling hierarchy
net/mlx5: qos: Remove qos domains and use shd lock
net/mlx5: qos: Support cross-device tx scheduling
selftests: drv-net: Add test for cross-esw rate scheduling
net/mlx5: Document devlink rates
Documentation/netlink/specs/devlink.yaml | 24 +-
.../networking/devlink/devlink-port.rst | 2 +
Documentation/networking/devlink/index.rst | 4 +-
Documentation/networking/devlink/mlx5.rst | 33 ++
.../net/ethernet/mellanox/mlx5/core/devlink.c | 1 +
.../mellanox/mlx5/core/esw/devlink_port.c | 2 +-
.../net/ethernet/mellanox/mlx5/core/esw/qos.c | 532 +++++++-----------
.../net/ethernet/mellanox/mlx5/core/esw/qos.h | 3 -
.../net/ethernet/mellanox/mlx5/core/eswitch.c | 8 -
.../net/ethernet/mellanox/mlx5/core/eswitch.h | 15 +-
include/net/devlink.h | 5 +
include/uapi/linux/devlink.h | 2 +
net/devlink/core.c | 42 ++
net/devlink/dev.c | 16 +-
net/devlink/devl_internal.h | 19 +
net/devlink/dpipe.c | 14 +-
net/devlink/health.c | 12 +-
net/devlink/linecard.c | 4 +-
net/devlink/netlink.c | 82 ++-
net/devlink/netlink_gen.c | 24 +-
net/devlink/netlink_gen.h | 8 +
net/devlink/param.c | 4 +-
net/devlink/port.c | 18 +-
net/devlink/rate.c | 290 ++++++++--
net/devlink/region.c | 6 +-
net/devlink/resource.c | 6 +-
net/devlink/sb.c | 22 +-
net/devlink/trap.c | 12 +-
.../testing/selftests/drivers/net/hw/Makefile | 1 +
.../drivers/net/hw/devlink_rate_cross_esw.py | 300 ++++++++++
30 files changed, 1032 insertions(+), 479 deletions(-)
create mode 100755 tools/testing/selftests/drivers/net/hw/devlink_rate_cross_esw.py
base-commit: b50a48b65cb0c955728894b24ba5ecc3cd4b8c7d
--
2.44.0
^ permalink raw reply
* [PATCH net-next V9 01/14] devlink: Update nested instance locking comment
From: Tariq Toukan @ 2026-03-26 6:59 UTC (permalink / raw)
To: Eric Dumazet, Jakub Kicinski, Paolo Abeni, Andrew Lunn,
David S. Miller
Cc: Donald Hunter, Simon Horman, Jiri Pirko, Jonathan Corbet,
Shuah Khan, Saeed Mahameed, Leon Romanovsky, Tariq Toukan,
Mark Bloch, Shuah Khan, Chuck Lever, Matthieu Baerts (NGI0),
Carolina Jubran, Cosmin Ratiu, Dragos Tatulea, Jacob Keller,
Shahar Shitrit, Daniel Zahka, Parav Pandit, Adithya Jayachandran,
Kees Cook, Shay Drori, Daniel Jurgens, Moshe Shemesh,
Willem de Bruijn, David Wei, Petr Machata, Stanislav Fomichev,
Daniel Borkmann, Joe Damato, Nikolay Aleksandrov, Vadim Fedorenko,
Michael S. Tsirkin, Antonio Quartulli, Allison Henderson,
Bui Quang Minh, Nimrod Oren, netdev, linux-kernel, linux-doc,
linux-rdma, linux-kselftest, Gal Pressman, Jiri Pirko
In-Reply-To: <20260326065949.44058-1-tariqt@nvidia.com>
From: Cosmin Ratiu <cratiu@nvidia.com>
In commit [1] a comment about nested instance locking was updated. But
there's another place where this is mentioned, so update that as well.
[1] commit 0061b5199d7c ("devlink: Reverse locking order for nested
instances")
Signed-off-by: Cosmin Ratiu <cratiu@nvidia.com>
Reviewed-by: Carolina Jubran <cjubran@nvidia.com>
Reviewed-by: Jiri Pirko <jiri@nvidia.com>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
---
Documentation/networking/devlink/index.rst | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Documentation/networking/devlink/index.rst b/Documentation/networking/devlink/index.rst
index f7ba7dcf477d..2087c2de5104 100644
--- a/Documentation/networking/devlink/index.rst
+++ b/Documentation/networking/devlink/index.rst
@@ -31,8 +31,8 @@ sure to respect following rules:
- Lock ordering should be maintained. If driver needs to take instance
lock of both nested and parent instances at the same time, devlink
- instance lock of the parent instance should be taken first, only then
- instance lock of the nested instance could be taken.
+ instance lock of the nested instance should be taken first, only then
+ instance lock of the parent instance could be taken.
- Driver should use object-specific helpers to setup the
nested relationship:
--
2.44.0
^ permalink raw reply related
* [PATCH net-next V9 02/14] devlink: Add helpers to lock nested-in instances
From: Tariq Toukan @ 2026-03-26 6:59 UTC (permalink / raw)
To: Eric Dumazet, Jakub Kicinski, Paolo Abeni, Andrew Lunn,
David S. Miller
Cc: Donald Hunter, Simon Horman, Jiri Pirko, Jonathan Corbet,
Shuah Khan, Saeed Mahameed, Leon Romanovsky, Tariq Toukan,
Mark Bloch, Shuah Khan, Chuck Lever, Matthieu Baerts (NGI0),
Carolina Jubran, Cosmin Ratiu, Dragos Tatulea, Jacob Keller,
Shahar Shitrit, Daniel Zahka, Parav Pandit, Adithya Jayachandran,
Kees Cook, Shay Drori, Daniel Jurgens, Moshe Shemesh,
Willem de Bruijn, David Wei, Petr Machata, Stanislav Fomichev,
Daniel Borkmann, Joe Damato, Nikolay Aleksandrov, Vadim Fedorenko,
Michael S. Tsirkin, Antonio Quartulli, Allison Henderson,
Bui Quang Minh, Nimrod Oren, netdev, linux-kernel, linux-doc,
linux-rdma, linux-kselftest, Gal Pressman, Jiri Pirko
In-Reply-To: <20260326065949.44058-1-tariqt@nvidia.com>
From: Cosmin Ratiu <cratiu@nvidia.com>
Upcoming code will need to obtain a reference to locked nested-in
devlink instances. Add helpers to lock, obtain an already locked
reference and unlock/unref the nested-in instance.
Signed-off-by: Cosmin Ratiu <cratiu@nvidia.com>
Reviewed-by: Carolina Jubran <cjubran@nvidia.com>
Reviewed-by: Jiri Pirko <jiri@nvidia.com>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
---
net/devlink/core.c | 42 +++++++++++++++++++++++++++++++++++++
net/devlink/devl_internal.h | 3 +++
2 files changed, 45 insertions(+)
diff --git a/net/devlink/core.c b/net/devlink/core.c
index eeb6a71f5f56..db11248df712 100644
--- a/net/devlink/core.c
+++ b/net/devlink/core.c
@@ -67,6 +67,48 @@ static void __devlink_rel_put(struct devlink_rel *rel)
devlink_rel_free(rel);
}
+struct devlink *devlink_nested_in_get_lock(struct devlink_rel *rel)
+{
+ struct devlink *devlink;
+
+ if (!rel)
+ return NULL;
+ devlink = devlinks_xa_get(rel->nested_in.devlink_index);
+ if (!devlink)
+ return NULL;
+ devl_lock(devlink);
+ if (devl_is_registered(devlink))
+ return devlink;
+ devl_unlock(devlink);
+ devlink_put(devlink);
+ return NULL;
+}
+
+/* Returns the nested in devlink object and validates its lock is held. */
+struct devlink *devlink_nested_in_get_locked(struct devlink_rel *rel)
+{
+ struct devlink *devlink;
+ unsigned long index;
+
+ if (!rel)
+ return NULL;
+ index = rel->nested_in.devlink_index;
+ devlink = xa_find(&devlinks, &index, index, DEVLINK_REGISTERED);
+ if (devlink)
+ devl_assert_locked(devlink);
+ return devlink;
+}
+
+void devlink_nested_in_put_unlock(struct devlink_rel *rel)
+{
+ struct devlink *devlink = devlink_nested_in_get_locked(rel);
+
+ if (devlink) {
+ devl_unlock(devlink);
+ devlink_put(devlink);
+ }
+}
+
static void devlink_rel_nested_in_notify_work(struct work_struct *work)
{
struct devlink_rel *rel = container_of(work, struct devlink_rel,
diff --git a/net/devlink/devl_internal.h b/net/devlink/devl_internal.h
index 7dfb7cdd2d23..3b4364677b18 100644
--- a/net/devlink/devl_internal.h
+++ b/net/devlink/devl_internal.h
@@ -136,6 +136,9 @@ typedef void devlink_rel_notify_cb_t(struct devlink *devlink, u32 obj_index);
typedef void devlink_rel_cleanup_cb_t(struct devlink *devlink, u32 obj_index,
u32 rel_index);
+struct devlink *devlink_nested_in_get_lock(struct devlink_rel *rel);
+struct devlink *devlink_nested_in_get_locked(struct devlink_rel *rel);
+void devlink_nested_in_put_unlock(struct devlink_rel *rel);
void devlink_rel_nested_in_clear(u32 rel_index);
int devlink_rel_nested_in_add(u32 *rel_index, u32 devlink_index,
u32 obj_index, devlink_rel_notify_cb_t *notify_cb,
--
2.44.0
^ permalink raw reply related
* [PATCH net-next V9 03/14] devlink: Migrate from info->user_ptr to info->ctx
From: Tariq Toukan @ 2026-03-26 6:59 UTC (permalink / raw)
To: Eric Dumazet, Jakub Kicinski, Paolo Abeni, Andrew Lunn,
David S. Miller
Cc: Donald Hunter, Simon Horman, Jiri Pirko, Jonathan Corbet,
Shuah Khan, Saeed Mahameed, Leon Romanovsky, Tariq Toukan,
Mark Bloch, Shuah Khan, Chuck Lever, Matthieu Baerts (NGI0),
Carolina Jubran, Cosmin Ratiu, Dragos Tatulea, Jacob Keller,
Shahar Shitrit, Daniel Zahka, Parav Pandit, Adithya Jayachandran,
Kees Cook, Shay Drori, Daniel Jurgens, Moshe Shemesh,
Willem de Bruijn, David Wei, Petr Machata, Stanislav Fomichev,
Daniel Borkmann, Joe Damato, Nikolay Aleksandrov, Vadim Fedorenko,
Michael S. Tsirkin, Antonio Quartulli, Allison Henderson,
Bui Quang Minh, Nimrod Oren, netdev, linux-kernel, linux-doc,
linux-rdma, linux-kselftest, Gal Pressman, Jiri Pirko
In-Reply-To: <20260326065949.44058-1-tariqt@nvidia.com>
From: Cosmin Ratiu <cratiu@nvidia.com>
Replace deprecated info->user_ptr[0]/[1] with a typed
devlink_nl_ctx struct stored in info->ctx. The struct aliases
the same union memory, so the migration is safe.
There are no functionality changes here.
Signed-off-by: Cosmin Ratiu <cratiu@nvidia.com>
Reviewed-by: Jiri Pirko <jiri@nvidia.com>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
---
net/devlink/dev.c | 16 ++++++++--------
net/devlink/devl_internal.h | 13 +++++++++++++
net/devlink/dpipe.c | 14 +++++++-------
net/devlink/health.c | 12 ++++++------
net/devlink/linecard.c | 4 ++--
net/devlink/netlink.c | 8 ++++----
net/devlink/param.c | 4 ++--
net/devlink/port.c | 18 +++++++++---------
net/devlink/rate.c | 8 ++++----
net/devlink/region.c | 6 +++---
net/devlink/resource.c | 6 +++---
net/devlink/sb.c | 22 +++++++++++-----------
net/devlink/trap.c | 12 ++++++------
13 files changed, 78 insertions(+), 65 deletions(-)
diff --git a/net/devlink/dev.c b/net/devlink/dev.c
index 57b2b8f03543..bcf001554e84 100644
--- a/net/devlink/dev.c
+++ b/net/devlink/dev.c
@@ -222,7 +222,7 @@ static void devlink_notify(struct devlink *devlink, enum devlink_command cmd)
int devlink_nl_get_doit(struct sk_buff *skb, struct genl_info *info)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct sk_buff *msg;
int err;
@@ -519,7 +519,7 @@ devlink_nl_reload_actions_performed_snd(struct devlink *devlink, u32 actions_per
int devlink_nl_reload_doit(struct sk_buff *skb, struct genl_info *info)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
enum devlink_reload_action action;
enum devlink_reload_limit limit;
struct net *dest_net = NULL;
@@ -683,7 +683,7 @@ static int devlink_nl_eswitch_fill(struct sk_buff *msg, struct devlink *devlink,
int devlink_nl_eswitch_get_doit(struct sk_buff *skb, struct genl_info *info)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct sk_buff *msg;
int err;
@@ -704,7 +704,7 @@ int devlink_nl_eswitch_get_doit(struct sk_buff *skb, struct genl_info *info)
int devlink_nl_eswitch_set_doit(struct sk_buff *skb, struct genl_info *info)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
const struct devlink_ops *ops = devlink->ops;
enum devlink_eswitch_encap_mode encap_mode;
u8 inline_mode;
@@ -906,7 +906,7 @@ devlink_nl_info_fill(struct sk_buff *msg, struct devlink *devlink,
int devlink_nl_info_get_doit(struct sk_buff *skb, struct genl_info *info)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct sk_buff *msg;
int err;
@@ -1134,7 +1134,7 @@ int devlink_nl_flash_update_doit(struct sk_buff *skb, struct genl_info *info)
{
struct nlattr *nla_overwrite_mask, *nla_file_name;
struct devlink_flash_update_params params = {};
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
const char *file_name;
u32 supported_params;
int ret;
@@ -1302,7 +1302,7 @@ devlink_nl_selftests_fill(struct sk_buff *msg, struct devlink *devlink,
int devlink_nl_selftests_get_doit(struct sk_buff *skb, struct genl_info *info)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct sk_buff *msg;
int err;
@@ -1372,7 +1372,7 @@ static const struct nla_policy devlink_selftest_nl_policy[DEVLINK_ATTR_SELFTEST_
int devlink_nl_selftests_run_doit(struct sk_buff *skb, struct genl_info *info)
{
struct nlattr *tb[DEVLINK_ATTR_SELFTEST_ID_MAX + 1];
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct nlattr *attrs, *selftests;
struct sk_buff *msg;
void *hdr;
diff --git a/net/devlink/devl_internal.h b/net/devlink/devl_internal.h
index 3b4364677b18..1af445f044e5 100644
--- a/net/devlink/devl_internal.h
+++ b/net/devlink/devl_internal.h
@@ -150,6 +150,19 @@ int devlink_rel_devlink_handle_put(struct sk_buff *msg, struct devlink *devlink,
bool *msg_updated);
/* Netlink */
+struct devlink_nl_ctx {
+ struct devlink *devlink;
+ struct devlink_port *devlink_port;
+};
+
+static inline struct devlink_nl_ctx *
+devlink_nl_ctx(struct genl_info *info)
+{
+ BUILD_BUG_ON(sizeof(struct devlink_nl_ctx) >
+ sizeof_field(struct genl_info, ctx));
+ return (struct devlink_nl_ctx *)info->ctx;
+}
+
enum devlink_multicast_groups {
DEVLINK_MCGRP_CONFIG,
};
diff --git a/net/devlink/dpipe.c b/net/devlink/dpipe.c
index c8d4a4374ae1..08c7b66fc3e8 100644
--- a/net/devlink/dpipe.c
+++ b/net/devlink/dpipe.c
@@ -213,7 +213,7 @@ static int devlink_dpipe_tables_fill(struct genl_info *info,
struct list_head *dpipe_tables,
const char *table_name)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct devlink_dpipe_table *table;
struct nlattr *tables_attr;
struct sk_buff *skb = NULL;
@@ -290,7 +290,7 @@ static int devlink_dpipe_tables_fill(struct genl_info *info,
int devlink_nl_dpipe_table_get_doit(struct sk_buff *skb, struct genl_info *info)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
const char *table_name = NULL;
if (info->attrs[DEVLINK_ATTR_DPIPE_TABLE_NAME])
@@ -478,7 +478,7 @@ int devlink_dpipe_entry_ctx_prepare(struct devlink_dpipe_dump_ctx *dump_ctx)
if (!dump_ctx->hdr)
goto nla_put_failure;
- devlink = dump_ctx->info->user_ptr[0];
+ devlink = devlink_nl_ctx(dump_ctx->info)->devlink;
if (devlink_nl_put_handle(dump_ctx->skb, devlink))
goto nla_put_failure;
dump_ctx->nest = nla_nest_start_noflag(dump_ctx->skb,
@@ -563,7 +563,7 @@ static int devlink_dpipe_entries_fill(struct genl_info *info,
int devlink_nl_dpipe_entries_get_doit(struct sk_buff *skb,
struct genl_info *info)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct devlink_dpipe_table *table;
const char *table_name;
@@ -650,7 +650,7 @@ static int devlink_dpipe_headers_fill(struct genl_info *info,
struct devlink_dpipe_headers *
dpipe_headers)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct nlattr *headers_attr;
struct sk_buff *skb = NULL;
struct nlmsghdr *nlh;
@@ -713,7 +713,7 @@ static int devlink_dpipe_headers_fill(struct genl_info *info,
int devlink_nl_dpipe_headers_get_doit(struct sk_buff *skb,
struct genl_info *info)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
if (!devlink->dpipe_headers)
return -EOPNOTSUPP;
@@ -747,7 +747,7 @@ static int devlink_dpipe_table_counters_set(struct devlink *devlink,
int devlink_nl_dpipe_table_counters_set_doit(struct sk_buff *skb,
struct genl_info *info)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
const char *table_name;
bool counters_enable;
diff --git a/net/devlink/health.c b/net/devlink/health.c
index 449c7611c640..7ff0d707734a 100644
--- a/net/devlink/health.c
+++ b/net/devlink/health.c
@@ -358,7 +358,7 @@ devlink_health_reporter_get_from_info(struct devlink *devlink,
int devlink_nl_health_reporter_get_doit(struct sk_buff *skb,
struct genl_info *info)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct devlink_health_reporter *reporter;
struct sk_buff *msg;
int err;
@@ -456,7 +456,7 @@ int devlink_nl_health_reporter_get_dumpit(struct sk_buff *skb,
int devlink_nl_health_reporter_set_doit(struct sk_buff *skb,
struct genl_info *info)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct devlink_health_reporter *reporter;
reporter = devlink_health_reporter_get_from_info(devlink, info);
@@ -715,7 +715,7 @@ EXPORT_SYMBOL_GPL(devlink_health_reporter_state_update);
int devlink_nl_health_reporter_recover_doit(struct sk_buff *skb,
struct genl_info *info)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct devlink_health_reporter *reporter;
reporter = devlink_health_reporter_get_from_info(devlink, info);
@@ -1157,7 +1157,7 @@ static int devlink_fmsg_dumpit(struct devlink_fmsg *fmsg, struct sk_buff *skb,
int devlink_nl_health_reporter_diagnose_doit(struct sk_buff *skb,
struct genl_info *info)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct devlink_health_reporter *reporter;
struct devlink_fmsg *fmsg;
int err;
@@ -1252,7 +1252,7 @@ int devlink_nl_health_reporter_dump_get_dumpit(struct sk_buff *skb,
int devlink_nl_health_reporter_dump_clear_doit(struct sk_buff *skb,
struct genl_info *info)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct devlink_health_reporter *reporter;
reporter = devlink_health_reporter_get_from_info(devlink, info);
@@ -1269,7 +1269,7 @@ int devlink_nl_health_reporter_dump_clear_doit(struct sk_buff *skb,
int devlink_nl_health_reporter_test_doit(struct sk_buff *skb,
struct genl_info *info)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct devlink_health_reporter *reporter;
reporter = devlink_health_reporter_get_from_info(devlink, info);
diff --git a/net/devlink/linecard.c b/net/devlink/linecard.c
index 8315d35cb91d..fd18f2759770 100644
--- a/net/devlink/linecard.c
+++ b/net/devlink/linecard.c
@@ -171,7 +171,7 @@ void devlink_linecards_notify_unregister(struct devlink *devlink)
int devlink_nl_linecard_get_doit(struct sk_buff *skb, struct genl_info *info)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct devlink_linecard *linecard;
struct sk_buff *msg;
int err;
@@ -371,7 +371,7 @@ static int devlink_linecard_type_unset(struct devlink_linecard *linecard,
int devlink_nl_linecard_set_doit(struct sk_buff *skb, struct genl_info *info)
{
struct netlink_ext_ack *extack = info->extack;
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct devlink_linecard *linecard;
int err;
diff --git a/net/devlink/netlink.c b/net/devlink/netlink.c
index 32ddbe244cb7..5624cf71592f 100644
--- a/net/devlink/netlink.c
+++ b/net/devlink/netlink.c
@@ -252,18 +252,18 @@ static int __devlink_nl_pre_doit(struct sk_buff *skb, struct genl_info *info,
if (IS_ERR(devlink))
return PTR_ERR(devlink);
- info->user_ptr[0] = devlink;
+ devlink_nl_ctx(info)->devlink = devlink;
if (flags & DEVLINK_NL_FLAG_NEED_PORT) {
devlink_port = devlink_port_get_from_info(devlink, info);
if (IS_ERR(devlink_port)) {
err = PTR_ERR(devlink_port);
goto unlock;
}
- info->user_ptr[1] = devlink_port;
+ devlink_nl_ctx(info)->devlink_port = devlink_port;
} else if (flags & DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT) {
devlink_port = devlink_port_get_from_info(devlink, info);
if (!IS_ERR(devlink_port))
- info->user_ptr[1] = devlink_port;
+ devlink_nl_ctx(info)->devlink_port = devlink_port;
}
return 0;
@@ -304,7 +304,7 @@ static void __devlink_nl_post_doit(struct sk_buff *skb, struct genl_info *info,
bool dev_lock = flags & DEVLINK_NL_FLAG_NEED_DEV_LOCK;
struct devlink *devlink;
- devlink = info->user_ptr[0];
+ devlink = devlink_nl_ctx(info)->devlink;
devl_dev_unlock(devlink, dev_lock);
devlink_put(devlink);
}
diff --git a/net/devlink/param.c b/net/devlink/param.c
index cf95268da5b0..201e0619683b 100644
--- a/net/devlink/param.c
+++ b/net/devlink/param.c
@@ -566,7 +566,7 @@ devlink_param_get_from_info(struct xarray *params, struct genl_info *info)
int devlink_nl_param_get_doit(struct sk_buff *skb,
struct genl_info *info)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct devlink_param_item *param_item;
struct sk_buff *msg;
int err;
@@ -667,7 +667,7 @@ static int __devlink_nl_cmd_param_set_doit(struct devlink *devlink,
int devlink_nl_param_set_doit(struct sk_buff *skb, struct genl_info *info)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
return __devlink_nl_cmd_param_set_doit(devlink, 0, &devlink->params,
info, DEVLINK_CMD_PARAM_NEW);
diff --git a/net/devlink/port.c b/net/devlink/port.c
index 7fcd1d3ed44c..93b4a45892e0 100644
--- a/net/devlink/port.c
+++ b/net/devlink/port.c
@@ -594,7 +594,7 @@ void devlink_ports_notify_unregister(struct devlink *devlink)
int devlink_nl_port_get_doit(struct sk_buff *skb, struct genl_info *info)
{
- struct devlink_port *devlink_port = info->user_ptr[1];
+ struct devlink_port *devlink_port = devlink_nl_ctx(info)->devlink_port;
struct sk_buff *msg;
int err;
@@ -830,7 +830,7 @@ static int devlink_port_function_set(struct devlink_port *port,
int devlink_nl_port_set_doit(struct sk_buff *skb, struct genl_info *info)
{
- struct devlink_port *devlink_port = info->user_ptr[1];
+ struct devlink_port *devlink_port = devlink_nl_ctx(info)->devlink_port;
int err;
if (info->attrs[DEVLINK_ATTR_PORT_TYPE]) {
@@ -856,8 +856,8 @@ int devlink_nl_port_set_doit(struct sk_buff *skb, struct genl_info *info)
int devlink_nl_port_split_doit(struct sk_buff *skb, struct genl_info *info)
{
- struct devlink_port *devlink_port = info->user_ptr[1];
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink_port *devlink_port = devlink_nl_ctx(info)->devlink_port;
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
u32 count;
if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_PORT_SPLIT_COUNT))
@@ -887,8 +887,8 @@ int devlink_nl_port_split_doit(struct sk_buff *skb, struct genl_info *info)
int devlink_nl_port_unsplit_doit(struct sk_buff *skb, struct genl_info *info)
{
- struct devlink_port *devlink_port = info->user_ptr[1];
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink_port *devlink_port = devlink_nl_ctx(info)->devlink_port;
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
if (!devlink_port->ops->port_unsplit)
return -EOPNOTSUPP;
@@ -899,7 +899,7 @@ int devlink_nl_port_new_doit(struct sk_buff *skb, struct genl_info *info)
{
struct netlink_ext_ack *extack = info->extack;
struct devlink_port_new_attrs new_attrs = {};
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct devlink_port *devlink_port;
struct sk_buff *msg;
int err;
@@ -961,9 +961,9 @@ int devlink_nl_port_new_doit(struct sk_buff *skb, struct genl_info *info)
int devlink_nl_port_del_doit(struct sk_buff *skb, struct genl_info *info)
{
- struct devlink_port *devlink_port = info->user_ptr[1];
+ struct devlink_port *devlink_port = devlink_nl_ctx(info)->devlink_port;
struct netlink_ext_ack *extack = info->extack;
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
if (!devlink_port->ops->port_del)
return -EOPNOTSUPP;
diff --git a/net/devlink/rate.c b/net/devlink/rate.c
index 41be2d6c2954..478142910919 100644
--- a/net/devlink/rate.c
+++ b/net/devlink/rate.c
@@ -239,7 +239,7 @@ int devlink_nl_rate_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
int devlink_nl_rate_get_doit(struct sk_buff *skb, struct genl_info *info)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct devlink_rate *devlink_rate;
struct sk_buff *msg;
int err;
@@ -585,7 +585,7 @@ static bool devlink_rate_set_ops_supported(const struct devlink_ops *ops,
int devlink_nl_rate_set_doit(struct sk_buff *skb, struct genl_info *info)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct devlink_rate *devlink_rate;
const struct devlink_ops *ops;
int err;
@@ -607,7 +607,7 @@ int devlink_nl_rate_set_doit(struct sk_buff *skb, struct genl_info *info)
int devlink_nl_rate_new_doit(struct sk_buff *skb, struct genl_info *info)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct devlink_rate *rate_node;
const struct devlink_ops *ops;
int err;
@@ -663,7 +663,7 @@ int devlink_nl_rate_new_doit(struct sk_buff *skb, struct genl_info *info)
int devlink_nl_rate_del_doit(struct sk_buff *skb, struct genl_info *info)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct devlink_rate *rate_node;
int err;
diff --git a/net/devlink/region.c b/net/devlink/region.c
index 5588e3d560b9..537779bbff07 100644
--- a/net/devlink/region.c
+++ b/net/devlink/region.c
@@ -469,7 +469,7 @@ static void devlink_region_snapshot_del(struct devlink_region *region,
int devlink_nl_region_get_doit(struct sk_buff *skb, struct genl_info *info)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct devlink_port *port = NULL;
struct devlink_region *region;
const char *region_name;
@@ -588,7 +588,7 @@ int devlink_nl_region_get_dumpit(struct sk_buff *skb,
int devlink_nl_region_del_doit(struct sk_buff *skb, struct genl_info *info)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct devlink_snapshot *snapshot;
struct devlink_port *port = NULL;
struct devlink_region *region;
@@ -633,7 +633,7 @@ int devlink_nl_region_del_doit(struct sk_buff *skb, struct genl_info *info)
int devlink_nl_region_new_doit(struct sk_buff *skb, struct genl_info *info)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct devlink_snapshot *snapshot;
struct devlink_port *port = NULL;
struct nlattr *snapshot_id_attr;
diff --git a/net/devlink/resource.c b/net/devlink/resource.c
index 351835a710b1..763355d22517 100644
--- a/net/devlink/resource.c
+++ b/net/devlink/resource.c
@@ -107,7 +107,7 @@ devlink_resource_validate_size(struct devlink_resource *resource, u64 size,
int devlink_nl_resource_set_doit(struct sk_buff *skb, struct genl_info *info)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct devlink_resource *resource;
u64 resource_id;
u64 size;
@@ -216,7 +216,7 @@ static int devlink_resource_put(struct devlink *devlink, struct sk_buff *skb,
static int devlink_resource_fill(struct genl_info *info,
enum devlink_command cmd, int flags)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct devlink_resource *resource;
struct nlattr *resources_attr;
struct sk_buff *skb = NULL;
@@ -284,7 +284,7 @@ static int devlink_resource_fill(struct genl_info *info,
int devlink_nl_resource_dump_doit(struct sk_buff *skb, struct genl_info *info)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
if (list_empty(&devlink->resource_list))
return -EOPNOTSUPP;
diff --git a/net/devlink/sb.c b/net/devlink/sb.c
index 49fcbfe08f15..129bd016e302 100644
--- a/net/devlink/sb.c
+++ b/net/devlink/sb.c
@@ -204,7 +204,7 @@ static int devlink_nl_sb_fill(struct sk_buff *msg, struct devlink *devlink,
int devlink_nl_sb_get_doit(struct sk_buff *skb, struct genl_info *info)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct devlink_sb *devlink_sb;
struct sk_buff *msg;
int err;
@@ -306,7 +306,7 @@ static int devlink_nl_sb_pool_fill(struct sk_buff *msg, struct devlink *devlink,
int devlink_nl_sb_pool_get_doit(struct sk_buff *skb, struct genl_info *info)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct devlink_sb *devlink_sb;
struct sk_buff *msg;
u16 pool_index;
@@ -415,7 +415,7 @@ static int devlink_sb_pool_set(struct devlink *devlink, unsigned int sb_index,
int devlink_nl_sb_pool_set_doit(struct sk_buff *skb, struct genl_info *info)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
enum devlink_sb_threshold_type threshold_type;
struct devlink_sb *devlink_sb;
u16 pool_index;
@@ -506,7 +506,7 @@ static int devlink_nl_sb_port_pool_fill(struct sk_buff *msg,
int devlink_nl_sb_port_pool_get_doit(struct sk_buff *skb,
struct genl_info *info)
{
- struct devlink_port *devlink_port = info->user_ptr[1];
+ struct devlink_port *devlink_port = devlink_nl_ctx(info)->devlink_port;
struct devlink *devlink = devlink_port->devlink;
struct devlink_sb *devlink_sb;
struct sk_buff *msg;
@@ -624,8 +624,8 @@ static int devlink_sb_port_pool_set(struct devlink_port *devlink_port,
int devlink_nl_sb_port_pool_set_doit(struct sk_buff *skb,
struct genl_info *info)
{
- struct devlink_port *devlink_port = info->user_ptr[1];
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink_port *devlink_port = devlink_nl_ctx(info)->devlink_port;
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct devlink_sb *devlink_sb;
u16 pool_index;
u32 threshold;
@@ -716,7 +716,7 @@ devlink_nl_sb_tc_pool_bind_fill(struct sk_buff *msg, struct devlink *devlink,
int devlink_nl_sb_tc_pool_bind_get_doit(struct sk_buff *skb,
struct genl_info *info)
{
- struct devlink_port *devlink_port = info->user_ptr[1];
+ struct devlink_port *devlink_port = devlink_nl_ctx(info)->devlink_port;
struct devlink *devlink = devlink_port->devlink;
struct devlink_sb *devlink_sb;
struct sk_buff *msg;
@@ -864,8 +864,8 @@ static int devlink_sb_tc_pool_bind_set(struct devlink_port *devlink_port,
int devlink_nl_sb_tc_pool_bind_set_doit(struct sk_buff *skb,
struct genl_info *info)
{
- struct devlink_port *devlink_port = info->user_ptr[1];
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink_port *devlink_port = devlink_nl_ctx(info)->devlink_port;
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
enum devlink_sb_pool_type pool_type;
struct devlink_sb *devlink_sb;
u16 tc_index;
@@ -902,7 +902,7 @@ int devlink_nl_sb_tc_pool_bind_set_doit(struct sk_buff *skb,
int devlink_nl_sb_occ_snapshot_doit(struct sk_buff *skb, struct genl_info *info)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
const struct devlink_ops *ops = devlink->ops;
struct devlink_sb *devlink_sb;
@@ -918,7 +918,7 @@ int devlink_nl_sb_occ_snapshot_doit(struct sk_buff *skb, struct genl_info *info)
int devlink_nl_sb_occ_max_clear_doit(struct sk_buff *skb,
struct genl_info *info)
{
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
const struct devlink_ops *ops = devlink->ops;
struct devlink_sb *devlink_sb;
diff --git a/net/devlink/trap.c b/net/devlink/trap.c
index 8edb31654a68..793ffc66dc11 100644
--- a/net/devlink/trap.c
+++ b/net/devlink/trap.c
@@ -302,7 +302,7 @@ static int devlink_nl_trap_fill(struct sk_buff *msg, struct devlink *devlink,
int devlink_nl_trap_get_doit(struct sk_buff *skb, struct genl_info *info)
{
struct netlink_ext_ack *extack = info->extack;
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct devlink_trap_item *trap_item;
struct sk_buff *msg;
int err;
@@ -412,7 +412,7 @@ static int devlink_trap_action_set(struct devlink *devlink,
int devlink_nl_trap_set_doit(struct sk_buff *skb, struct genl_info *info)
{
struct netlink_ext_ack *extack = info->extack;
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct devlink_trap_item *trap_item;
if (list_empty(&devlink->trap_list))
@@ -511,7 +511,7 @@ devlink_nl_trap_group_fill(struct sk_buff *msg, struct devlink *devlink,
int devlink_nl_trap_group_get_doit(struct sk_buff *skb, struct genl_info *info)
{
struct netlink_ext_ack *extack = info->extack;
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct devlink_trap_group_item *group_item;
struct sk_buff *msg;
int err;
@@ -682,7 +682,7 @@ static int devlink_trap_group_set(struct devlink *devlink,
int devlink_nl_trap_group_set_doit(struct sk_buff *skb, struct genl_info *info)
{
struct netlink_ext_ack *extack = info->extack;
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct devlink_trap_group_item *group_item;
bool modified = false;
int err;
@@ -804,7 +804,7 @@ int devlink_nl_trap_policer_get_doit(struct sk_buff *skb,
{
struct devlink_trap_policer_item *policer_item;
struct netlink_ext_ack *extack = info->extack;
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
struct sk_buff *msg;
int err;
@@ -924,7 +924,7 @@ int devlink_nl_trap_policer_set_doit(struct sk_buff *skb,
{
struct devlink_trap_policer_item *policer_item;
struct netlink_ext_ack *extack = info->extack;
- struct devlink *devlink = info->user_ptr[0];
+ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
if (list_empty(&devlink->trap_policer_list))
return -EOPNOTSUPP;
--
2.44.0
^ permalink raw reply related
* [PATCH net-next V9 04/14] devlink: Decouple rate storage from associated devlink object
From: Tariq Toukan @ 2026-03-26 6:59 UTC (permalink / raw)
To: Eric Dumazet, Jakub Kicinski, Paolo Abeni, Andrew Lunn,
David S. Miller
Cc: Donald Hunter, Simon Horman, Jiri Pirko, Jonathan Corbet,
Shuah Khan, Saeed Mahameed, Leon Romanovsky, Tariq Toukan,
Mark Bloch, Shuah Khan, Chuck Lever, Matthieu Baerts (NGI0),
Carolina Jubran, Cosmin Ratiu, Dragos Tatulea, Jacob Keller,
Shahar Shitrit, Daniel Zahka, Parav Pandit, Adithya Jayachandran,
Kees Cook, Shay Drori, Daniel Jurgens, Moshe Shemesh,
Willem de Bruijn, David Wei, Petr Machata, Stanislav Fomichev,
Daniel Borkmann, Joe Damato, Nikolay Aleksandrov, Vadim Fedorenko,
Michael S. Tsirkin, Antonio Quartulli, Allison Henderson,
Bui Quang Minh, Nimrod Oren, netdev, linux-kernel, linux-doc,
linux-rdma, linux-kselftest, Gal Pressman, Jiri Pirko
In-Reply-To: <20260326065949.44058-1-tariqt@nvidia.com>
From: Cosmin Ratiu <cratiu@nvidia.com>
Devlink rate leafs and nodes were stored in their respective devlink
objects pointed to by devlink_rate->devlink.
This patch removes that association by introducing the concept of
'rate node devlink', which is where all rates that could link to each
other are stored. For now this is the same as devlink_rate->devlink.
After this patch, the devlink rates stored in this devlink instance
could potentially be from multiple other devlink instances. So all rate
node manipulation code was updated to:
- correctly compare the actual devlink object during iteration.
- maybe acquire additional locks (noop for now).
Signed-off-by: Cosmin Ratiu <cratiu@nvidia.com>
Reviewed-by: Carolina Jubran <cjubran@nvidia.com>
Reviewed-by: Jiri Pirko <jiri@nvidia.com>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
---
net/devlink/rate.c | 196 +++++++++++++++++++++++++++++++++------------
1 file changed, 144 insertions(+), 52 deletions(-)
diff --git a/net/devlink/rate.c b/net/devlink/rate.c
index 478142910919..9ebbc72130c6 100644
--- a/net/devlink/rate.c
+++ b/net/devlink/rate.c
@@ -30,13 +30,31 @@ devlink_rate_leaf_get_from_info(struct devlink *devlink, struct genl_info *info)
return devlink_rate ?: ERR_PTR(-ENODEV);
}
+static struct devlink *devl_rate_lock(struct devlink *devlink)
+{
+ return devlink;
+}
+
+static struct devlink *
+devl_get_rate_node_instance_locked(struct devlink *devlink)
+{
+ return devlink;
+}
+
+static void devl_rate_unlock(struct devlink *devlink)
+{
+}
+
static struct devlink_rate *
devlink_rate_node_get_by_name(struct devlink *devlink, const char *node_name)
{
struct devlink_rate *devlink_rate;
+ struct devlink *rate_devlink;
- list_for_each_entry(devlink_rate, &devlink->rate_list, list) {
- if (devlink_rate_is_node(devlink_rate) &&
+ rate_devlink = devl_get_rate_node_instance_locked(devlink);
+ list_for_each_entry(devlink_rate, &rate_devlink->rate_list, list) {
+ if (devlink_rate->devlink == devlink &&
+ devlink_rate_is_node(devlink_rate) &&
!strcmp(node_name, devlink_rate->name))
return devlink_rate;
}
@@ -190,17 +208,25 @@ static void devlink_rate_notify(struct devlink_rate *devlink_rate,
void devlink_rates_notify_register(struct devlink *devlink)
{
struct devlink_rate *rate_node;
+ struct devlink *rate_devlink;
- list_for_each_entry(rate_node, &devlink->rate_list, list)
- devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_NEW);
+ rate_devlink = devl_rate_lock(devlink);
+ list_for_each_entry(rate_node, &rate_devlink->rate_list, list)
+ if (rate_node->devlink == devlink)
+ devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_NEW);
+ devl_rate_unlock(devlink);
}
void devlink_rates_notify_unregister(struct devlink *devlink)
{
struct devlink_rate *rate_node;
+ struct devlink *rate_devlink;
- list_for_each_entry_reverse(rate_node, &devlink->rate_list, list)
- devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_DEL);
+ rate_devlink = devl_rate_lock(devlink);
+ list_for_each_entry_reverse(rate_node, &rate_devlink->rate_list, list)
+ if (rate_node->devlink == devlink)
+ devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_DEL);
+ devl_rate_unlock(devlink);
}
static int
@@ -209,17 +235,20 @@ devlink_nl_rate_get_dump_one(struct sk_buff *msg, struct devlink *devlink,
{
struct devlink_nl_dump_state *state = devlink_dump_state(cb);
struct devlink_rate *devlink_rate;
+ struct devlink *rate_devlink;
int idx = 0;
int err = 0;
- list_for_each_entry(devlink_rate, &devlink->rate_list, list) {
+ rate_devlink = devl_rate_lock(devlink);
+ list_for_each_entry(devlink_rate, &rate_devlink->rate_list, list) {
enum devlink_command cmd = DEVLINK_CMD_RATE_NEW;
u32 id = NETLINK_CB(cb->skb).portid;
- if (idx < state->idx) {
+ if (idx < state->idx || devlink_rate->devlink != devlink) {
idx++;
continue;
}
+
err = devlink_nl_rate_fill(msg, devlink_rate, cmd, id,
cb->nlh->nlmsg_seq, flags, NULL);
if (err) {
@@ -228,6 +257,7 @@ devlink_nl_rate_get_dump_one(struct sk_buff *msg, struct devlink *devlink,
}
idx++;
}
+ devl_rate_unlock(devlink);
return err;
}
@@ -244,23 +274,33 @@ int devlink_nl_rate_get_doit(struct sk_buff *skb, struct genl_info *info)
struct sk_buff *msg;
int err;
+ devl_rate_lock(devlink);
devlink_rate = devlink_rate_get_from_info(devlink, info);
- if (IS_ERR(devlink_rate))
- return PTR_ERR(devlink_rate);
+ if (IS_ERR(devlink_rate)) {
+ err = PTR_ERR(devlink_rate);
+ goto unlock;
+ }
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
- if (!msg)
- return -ENOMEM;
+ if (!msg) {
+ err = -ENOMEM;
+ goto unlock;
+ }
err = devlink_nl_rate_fill(msg, devlink_rate, DEVLINK_CMD_RATE_NEW,
info->snd_portid, info->snd_seq, 0,
info->extack);
- if (err) {
- nlmsg_free(msg);
- return err;
- }
+ if (err)
+ goto err_fill;
+ devl_rate_unlock(devlink);
return genlmsg_reply(msg, info);
+
+err_fill:
+ nlmsg_free(msg);
+unlock:
+ devl_rate_unlock(devlink);
+ return err;
}
static bool
@@ -590,24 +630,32 @@ int devlink_nl_rate_set_doit(struct sk_buff *skb, struct genl_info *info)
const struct devlink_ops *ops;
int err;
+ devl_rate_lock(devlink);
devlink_rate = devlink_rate_get_from_info(devlink, info);
- if (IS_ERR(devlink_rate))
- return PTR_ERR(devlink_rate);
+ if (IS_ERR(devlink_rate)) {
+ err = PTR_ERR(devlink_rate);
+ goto unlock;
+ }
ops = devlink->ops;
- if (!ops || !devlink_rate_set_ops_supported(ops, info, devlink_rate->type))
- return -EOPNOTSUPP;
+ if (!ops ||
+ !devlink_rate_set_ops_supported(ops, info, devlink_rate->type)) {
+ err = -EOPNOTSUPP;
+ goto unlock;
+ }
err = devlink_nl_rate_set(devlink_rate, ops, info);
if (!err)
devlink_rate_notify(devlink_rate, DEVLINK_CMD_RATE_NEW);
+unlock:
+ devl_rate_unlock(devlink);
return err;
}
int devlink_nl_rate_new_doit(struct sk_buff *skb, struct genl_info *info)
{
- struct devlink *devlink = devlink_nl_ctx(info)->devlink;
+ struct devlink *rate_devlink, *devlink = devlink_nl_ctx(info)->devlink;
struct devlink_rate *rate_node;
const struct devlink_ops *ops;
int err;
@@ -621,15 +669,21 @@ int devlink_nl_rate_new_doit(struct sk_buff *skb, struct genl_info *info)
if (!devlink_rate_set_ops_supported(ops, info, DEVLINK_RATE_TYPE_NODE))
return -EOPNOTSUPP;
+ rate_devlink = devl_rate_lock(devlink);
rate_node = devlink_rate_node_get_from_attrs(devlink, info->attrs);
- if (!IS_ERR(rate_node))
- return -EEXIST;
- else if (rate_node == ERR_PTR(-EINVAL))
- return -EINVAL;
+ if (!IS_ERR(rate_node)) {
+ err = -EEXIST;
+ goto unlock;
+ } else if (rate_node == ERR_PTR(-EINVAL)) {
+ err = -EINVAL;
+ goto unlock;
+ }
rate_node = kzalloc_obj(*rate_node);
- if (!rate_node)
- return -ENOMEM;
+ if (!rate_node) {
+ err = -ENOMEM;
+ goto unlock;
+ }
rate_node->devlink = devlink;
rate_node->type = DEVLINK_RATE_TYPE_NODE;
@@ -648,8 +702,9 @@ int devlink_nl_rate_new_doit(struct sk_buff *skb, struct genl_info *info)
goto err_rate_set;
refcount_set(&rate_node->refcnt, 1);
- list_add(&rate_node->list, &devlink->rate_list);
+ list_add(&rate_node->list, &rate_devlink->rate_list);
devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_NEW);
+ devl_rate_unlock(devlink);
return 0;
err_rate_set:
@@ -658,6 +713,8 @@ int devlink_nl_rate_new_doit(struct sk_buff *skb, struct genl_info *info)
kfree(rate_node->name);
err_strdup:
kfree(rate_node);
+unlock:
+ devl_rate_unlock(devlink);
return err;
}
@@ -667,13 +724,17 @@ int devlink_nl_rate_del_doit(struct sk_buff *skb, struct genl_info *info)
struct devlink_rate *rate_node;
int err;
+ devl_rate_lock(devlink);
rate_node = devlink_rate_node_get_from_info(devlink, info);
- if (IS_ERR(rate_node))
- return PTR_ERR(rate_node);
+ if (IS_ERR(rate_node)) {
+ err = PTR_ERR(rate_node);
+ goto unlock;
+ }
if (refcount_read(&rate_node->refcnt) > 1) {
NL_SET_ERR_MSG(info->extack, "Node has children. Cannot delete node.");
- return -EBUSY;
+ err = -EBUSY;
+ goto unlock;
}
devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_DEL);
@@ -684,6 +745,8 @@ int devlink_nl_rate_del_doit(struct sk_buff *skb, struct genl_info *info)
list_del(&rate_node->list);
kfree(rate_node->name);
kfree(rate_node);
+unlock:
+ devl_rate_unlock(devlink);
return err;
}
@@ -692,14 +755,20 @@ int devlink_rates_check(struct devlink *devlink,
struct netlink_ext_ack *extack)
{
struct devlink_rate *devlink_rate;
+ struct devlink *rate_devlink;
+ int err = 0;
- list_for_each_entry(devlink_rate, &devlink->rate_list, list)
- if (!rate_filter || rate_filter(devlink_rate)) {
+ rate_devlink = devl_rate_lock(devlink);
+ list_for_each_entry(devlink_rate, &rate_devlink->rate_list, list)
+ if (devlink_rate->devlink == devlink &&
+ (!rate_filter || rate_filter(devlink_rate))) {
if (extack)
NL_SET_ERR_MSG(extack, "Rate node(s) exists.");
- return -EBUSY;
+ err = -EBUSY;
+ break;
}
- return 0;
+ devl_rate_unlock(devlink);
+ return err;
}
/**
@@ -716,14 +785,20 @@ devl_rate_node_create(struct devlink *devlink, void *priv, char *node_name,
struct devlink_rate *parent)
{
struct devlink_rate *rate_node;
+ struct devlink *rate_devlink;
+ rate_devlink = devl_rate_lock(devlink);
rate_node = devlink_rate_node_get_by_name(devlink, node_name);
- if (!IS_ERR(rate_node))
- return ERR_PTR(-EEXIST);
+ if (!IS_ERR(rate_node)) {
+ rate_node = ERR_PTR(-EEXIST);
+ goto unlock;
+ }
rate_node = kzalloc_obj(*rate_node);
- if (!rate_node)
- return ERR_PTR(-ENOMEM);
+ if (!rate_node) {
+ rate_node = ERR_PTR(-ENOMEM);
+ goto unlock;
+ }
if (parent) {
rate_node->parent = parent;
@@ -737,12 +812,15 @@ devl_rate_node_create(struct devlink *devlink, void *priv, char *node_name,
rate_node->name = kstrdup(node_name, GFP_KERNEL);
if (!rate_node->name) {
kfree(rate_node);
- return ERR_PTR(-ENOMEM);
+ rate_node = ERR_PTR(-ENOMEM);
+ goto unlock;
}
refcount_set(&rate_node->refcnt, 1);
- list_add(&rate_node->list, &devlink->rate_list);
+ list_add(&rate_node->list, &rate_devlink->rate_list);
devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_NEW);
+unlock:
+ devl_rate_unlock(devlink);
return rate_node;
}
EXPORT_SYMBOL_GPL(devl_rate_node_create);
@@ -758,10 +836,10 @@ EXPORT_SYMBOL_GPL(devl_rate_node_create);
int devl_rate_leaf_create(struct devlink_port *devlink_port, void *priv,
struct devlink_rate *parent)
{
- struct devlink *devlink = devlink_port->devlink;
+ struct devlink *rate_devlink, *devlink = devlink_port->devlink;
struct devlink_rate *devlink_rate;
- devl_assert_locked(devlink_port->devlink);
+ devl_assert_locked(devlink);
if (WARN_ON(devlink_port->devlink_rate))
return -EBUSY;
@@ -770,6 +848,7 @@ int devl_rate_leaf_create(struct devlink_port *devlink_port, void *priv,
if (!devlink_rate)
return -ENOMEM;
+ rate_devlink = devl_rate_lock(devlink);
if (parent) {
devlink_rate->parent = parent;
refcount_inc(&devlink_rate->parent->refcnt);
@@ -779,9 +858,10 @@ int devl_rate_leaf_create(struct devlink_port *devlink_port, void *priv,
devlink_rate->devlink = devlink;
devlink_rate->devlink_port = devlink_port;
devlink_rate->priv = priv;
- list_add_tail(&devlink_rate->list, &devlink->rate_list);
+ list_add_tail(&devlink_rate->list, &rate_devlink->rate_list);
devlink_port->devlink_rate = devlink_rate;
devlink_rate_notify(devlink_rate, DEVLINK_CMD_RATE_NEW);
+ devl_rate_unlock(devlink);
return 0;
}
@@ -797,16 +877,19 @@ EXPORT_SYMBOL_GPL(devl_rate_leaf_create);
void devl_rate_leaf_destroy(struct devlink_port *devlink_port)
{
struct devlink_rate *devlink_rate = devlink_port->devlink_rate;
+ struct devlink *devlink = devlink_port->devlink;
- devl_assert_locked(devlink_port->devlink);
+ devl_assert_locked(devlink);
if (!devlink_rate)
return;
+ devl_rate_lock(devlink);
devlink_rate_notify(devlink_rate, DEVLINK_CMD_RATE_DEL);
if (devlink_rate->parent)
refcount_dec(&devlink_rate->parent->refcnt);
list_del(&devlink_rate->list);
devlink_port->devlink_rate = NULL;
+ devl_rate_unlock(devlink);
kfree(devlink_rate);
}
EXPORT_SYMBOL_GPL(devl_rate_leaf_destroy);
@@ -815,20 +898,25 @@ EXPORT_SYMBOL_GPL(devl_rate_leaf_destroy);
* devl_rate_nodes_destroy - destroy all devlink rate nodes on device
* @devlink: devlink instance
*
- * Unset parent for all rate objects and destroy all rate nodes
- * on specified device.
+ * Unset parent for all rate objects involving this device and destroy all rate
+ * nodes on it.
*/
void devl_rate_nodes_destroy(struct devlink *devlink)
{
- const struct devlink_ops *ops = devlink->ops;
struct devlink_rate *devlink_rate, *tmp;
+ const struct devlink_ops *ops;
+ struct devlink *rate_devlink;
devl_assert_locked(devlink);
+ rate_devlink = devl_rate_lock(devlink);
- list_for_each_entry(devlink_rate, &devlink->rate_list, list) {
- if (!devlink_rate->parent)
+ list_for_each_entry(devlink_rate, &rate_devlink->rate_list, list) {
+ if (!devlink_rate->parent ||
+ (devlink_rate->devlink != devlink &&
+ devlink_rate->parent->devlink != devlink))
continue;
+ ops = devlink_rate->devlink->ops;
if (devlink_rate_is_leaf(devlink_rate))
ops->rate_leaf_parent_set(devlink_rate, NULL, devlink_rate->priv,
NULL, NULL);
@@ -839,13 +927,17 @@ void devl_rate_nodes_destroy(struct devlink *devlink)
refcount_dec(&devlink_rate->parent->refcnt);
devlink_rate->parent = NULL;
}
- list_for_each_entry_safe(devlink_rate, tmp, &devlink->rate_list, list) {
- if (devlink_rate_is_node(devlink_rate)) {
+ ops = devlink->ops;
+ list_for_each_entry_safe(devlink_rate, tmp, &rate_devlink->rate_list,
+ list) {
+ if (devlink_rate->devlink == devlink &&
+ devlink_rate_is_node(devlink_rate)) {
ops->rate_node_del(devlink_rate, devlink_rate->priv, NULL);
list_del(&devlink_rate->list);
kfree(devlink_rate->name);
kfree(devlink_rate);
}
}
+ devl_rate_unlock(devlink);
}
EXPORT_SYMBOL_GPL(devl_rate_nodes_destroy);
--
2.44.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox