* Re: [PATCH 6/8] arm64: dts: qcom: kaanapali: Add GPU cooling
From: Dmitry Baryshkov @ 2026-05-17 18:26 UTC (permalink / raw)
To: Gaurav Kohli
Cc: Akhil P Oommen, Will Deacon, Robin Murphy, Joerg Roedel,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
Konrad Dybcio, Rob Clark, Dmitry Baryshkov, Abhinav Kumar,
Jessica Zhang, Marijn Suijten, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Sean Paul,
linux-arm-kernel, iommu, devicetree, linux-kernel, linux-arm-msm,
freedreno, dri-devel
In-Reply-To: <e6572f5b-3686-46ca-956d-c06a7363cef2@oss.qualcomm.com>
On Thu, May 14, 2026 at 12:17:54PM +0530, Gaurav Kohli wrote:
>
>
> On 5/13/2026 11:23 PM, Dmitry Baryshkov wrote:
> > On Tue, May 12, 2026 at 03:53:20AM +0530, Akhil P Oommen wrote:
> > > From: Gaurav Kohli <gaurav.kohli@oss.qualcomm.com>
> > >
> > > Unlike the CPU, the GPU does not throttle its speed automatically when it
> > > reaches high temperatures.
> > >
> > > Set up GPU cooling by throttling the GPU speed
> > > when reaching 105°C.
> > >
> > > Signed-off-by: Gaurav Kohli <gaurav.kohli@oss.qualcomm.com>
> > > Signed-off-by: Akhil P Oommen <akhilpo@oss.qualcomm.com>
> > > ---
> > > arch/arm64/boot/dts/qcom/kaanapali.dtsi | 165 ++++++++++++++++++++++++++------
> > > 1 file changed, 135 insertions(+), 30 deletions(-)
> > >
> > > +
> > > thermal-sensors = <&tsens5 0>;
> > > trips {
> > > - gpuss-0-hot {
> > > - temperature = <120000>;
> > > + gpuss_0_alert0: gpuss-0-alert0 {
> > > + temperature = <105000>;
> > > hysteresis = <5000>;
> > > - type = "hot";
> > > + type = "passive";
> > > };
> >
> > Why don't we keep both passive and hot trip points?
> >
>
> Need guidance here, we are keeping passive at low temp so still hot trip is
> needed for such cases.
I think we are saying the same. Keep both passive and hot trip points.
>
> > > gpuss-0-critical {
> > >
> >
>
--
With best wishes
Dmitry
^ permalink raw reply
* [PATCH 08/12] crypto: atmel-ecc - switch to module_i2c_driver
From: Lothar Rubusch @ 2026-05-17 18:06 UTC (permalink / raw)
To: thorsten.blum, herbert, davem, nicolas.ferre, alexandre.belloni,
claudiu.beznea
Cc: linux-crypto, linux-arm-kernel, linux-kernel, l.rubusch
In-Reply-To: <20260517180639.9657-1-l.rubusch@gmail.com>
Replace custom module init/exit functions with module_i2c_driver() for
simplified driver registration.
Initialization of the shared I2C client management structure is handled by
the core driver and no longer performed in the ECC module init path.
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
drivers/crypto/atmel-ecc.c | 16 +---------------
1 file changed, 1 insertion(+), 15 deletions(-)
diff --git a/drivers/crypto/atmel-ecc.c b/drivers/crypto/atmel-ecc.c
index e5dd77008b97..dcfc09d24497 100644
--- a/drivers/crypto/atmel-ecc.c
+++ b/drivers/crypto/atmel-ecc.c
@@ -392,21 +392,7 @@ static struct i2c_driver atmel_ecc_driver = {
.id_table = atmel_ecc_id,
};
-static int __init atmel_ecc_init(void)
-{
- spin_lock_init(&atmel_i2c_mgmt.i2c_list_lock);
- INIT_LIST_HEAD(&atmel_i2c_mgmt.i2c_client_list);
- return i2c_add_driver(&atmel_ecc_driver);
-}
-
-static void __exit atmel_ecc_exit(void)
-{
- atmel_i2c_flush_queue();
- i2c_del_driver(&atmel_ecc_driver);
-}
-
-module_init(atmel_ecc_init);
-module_exit(atmel_ecc_exit);
+module_i2c_driver(atmel_ecc_driver);
MODULE_AUTHOR("Tudor Ambarus");
MODULE_DESCRIPTION("Microchip / Atmel ECC (I2C) driver");
--
2.53.0
^ permalink raw reply related
* Re: [RFC V2 01/14] mm: Abstract printing of pxd_val()
From: Mike Rapoport @ 2026-05-17 18:57 UTC (permalink / raw)
To: Anshuman Khandual
Cc: linux-arm-kernel, Catalin Marinas, Will Deacon, Ryan Roberts,
Mark Rutland, Lorenzo Stoakes, Andrew Morton, David Hildenbrand,
Linu Cherian, Usama Arif, linux-kernel, linux-mm
In-Reply-To: <20260513044547.4128549-2-anshuman.khandual@arm.com>
On Wed, May 13, 2026 at 10:15:34AM +0530, Anshuman Khandual wrote:
> Ahead of adding support for D128 pgtables, refactor places that print
> PTE values to use the new __PRIpte format specifier and __PRIpte_args()
> macro to prepare the argument(s). When using D128 pgtables in future,
> we can simply redefine __PRIpte and __PTIpte_args().
>
> Besides there is also an assumption about pxd_val() being always capped
> at 'unsigned long long' size but that will not work for D128 pgtables.
> Just increase its size to u128 if the compiler supports via a separate
> data type pxdval_t which also defaults to existing 'unsigned long long'.
>
> Cc: Andrew Morton <akpm@linux-foundation.org>
> Cc: David Hildenbrand <david@kernel.org>
> Cc: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
> Cc: Mike Rapoport <rppt@kernel.org>
> Cc: linux-mm@kvack.org
> Cc: linux-kernel@vger.kernel.org
> Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
> ---
> Changes in RFC V2:
>
> - Moved pxdval_t definition inside generic page table header per Mike
> - Restored print format in __print_bad_page_map_pgtable() per Usama
> - Renamed __PRIpte as __PRIpxx per David
>
> include/linux/pgtable.h | 11 +++++++++++
> mm/memory.c | 23 +++++++++++++----------
> 2 files changed, 24 insertions(+), 10 deletions(-)
--
Sincerely yours,
Mike.
^ permalink raw reply
* Re: [RFC V2 02/14] mm: Add read-write accessors for vm_page_prot
From: Mike Rapoport @ 2026-05-17 18:59 UTC (permalink / raw)
To: Anshuman Khandual
Cc: linux-arm-kernel, Catalin Marinas, Will Deacon, Ryan Roberts,
Mark Rutland, Lorenzo Stoakes, Andrew Morton, David Hildenbrand,
Linu Cherian, Usama Arif, linux-kernel, linux-mm
In-Reply-To: <20260513044547.4128549-3-anshuman.khandual@arm.com>
On Wed, May 13, 2026 at 10:15:35AM +0530, Anshuman Khandual wrote:
> Currently vma->vm_page_prot is safely read from and written to, without any
> locks with READ_ONCE() and WRITE_ONCE(). But with introduction of D128 page
> tables on arm64 platform, vm_page_prot grows to 128 bits which can't safely
> be handled with READ_ONCE() and WRITE_ONCE().
>
> Add read and write accessors for vm_page_prot like pgprot_[read|write]()
> which any platform can override when required, although still defaulting as
> READ_ONCE() and WRITE_ONCE(), thus preserving the functionality for others.
>
> Cc: Andrew Morton <akpm@linux-foundation.org>
> Cc: David Hildenbrand <david@kernel.org>
> Cc: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
> Cc: Mike Rapoport <rppt@kernel.org>
> Cc: linux-mm@kvack.org
> Cc: linux-kernel@vger.kernel.org
> Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
> ---
> Changes in RFC V2:
>
> - Dropped _once from pgprot_[read|write]() callbacks per Mike
>
> include/linux/pgtable.h | 14 ++++++++++++++
> mm/huge_memory.c | 4 ++--
> mm/memory.c | 2 +-
> mm/migrate.c | 2 +-
> mm/mmap.c | 2 +-
> 5 files changed, 19 insertions(+), 5 deletions(-)
--
Sincerely yours,
Mike.
^ permalink raw reply
* [PATCH 0/4] firmware: arm_scmi: Fix protocol parsing and validation
From: Sudeep Holla @ 2026-05-17 19:02 UTC (permalink / raw)
To: Cristian Marussi, arm-scmi, linux-arm-kernel, Sudeep Holla
Hi,
This series fixes a few SCMI protocol parsing and validation issues found
while checking the driver message layouts against the SCMI specification.
The first patch fixes a clear response width mismatch in SENSOR_CONFIG_GET,
where the driver requested a 4-byte response but read it as a 64-bit value.
The next two patches harden notification parsing for variable-sized payloads.
BASE_ERROR_EVENT and SENSOR_UPDATE both carry counted trailing arrays, so the
received payload size must be validated before copying or parsing those
entries.
The final patch fixes Powercap protocol v2 helpers to validate the domain id
before accessing cached per-domain state.
No functional change is intended for well-formed SCMI responses.
Cc: Cristian Marussi <cristian.marussi@arm.com>
Cc: arm-scmi@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
Regards,
Sudeep
---
Sudeep Holla (4):
firmware: arm_scmi: Read sensor config as 32-bit value
firmware: arm_scmi: Validate BASE_ERROR_EVENT payload size
firmware: arm_scmi: Validate SENSOR_UPDATE payload size
firmware: arm_scmi: Validate Powercap domains before state access
drivers/firmware/arm_scmi/base.c | 15 +++++++++++++--
drivers/firmware/arm_scmi/powercap.c | 25 ++++++++++++++++++-------
drivers/firmware/arm_scmi/sensors.c | 12 ++++++++++--
3 files changed, 41 insertions(+), 11 deletions(-)
---
base-commit: d0deb8265c8551343c5c155aaded6b56639e3820
change-id: 20260517-scmi_fixes-211563fc2ff2
--
Regards,
Sudeep
^ permalink raw reply
* [PATCH 3/4] firmware: arm_scmi: Validate SENSOR_UPDATE payload size
From: Sudeep Holla @ 2026-05-17 19:02 UTC (permalink / raw)
To: Cristian Marussi, arm-scmi, linux-arm-kernel, Sudeep Holla
In-Reply-To: <20260517-scmi_fixes-v1-0-d86daec4defd@kernel.org>
SENSOR_UPDATE carries one or more sensor readings after the fixed
notification header. The parser derives the expected reading count
from the sensor description, but it did not verify that the received
payload contains those entries before parsing them.
Reject truncated update notifications before reading the variable
array.
Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
---
drivers/firmware/arm_scmi/sensors.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c
index 836c294a9f42..b14bb1146356 100644
--- a/drivers/firmware/arm_scmi/sensors.c
+++ b/drivers/firmware/arm_scmi/sensors.c
@@ -1072,12 +1072,15 @@ scmi_sensor_fill_custom_report(const struct scmi_protocol_handle *ph,
case SCMI_EVENT_SENSOR_UPDATE:
{
int i;
+ size_t expected_sz;
struct scmi_sensor_info *s;
const struct scmi_sensor_update_notify_payld *p = payld;
struct scmi_sensor_update_report *r = report;
struct sensors_info *sinfo = ph->get_priv(ph);
- /* payld_sz is variable for this event */
+ if (payld_sz < sizeof(*p))
+ break;
+
r->sensor_id = le32_to_cpu(p->sensor_id);
if (r->sensor_id >= sinfo->num_sensors)
break;
@@ -1091,6 +1094,11 @@ scmi_sensor_fill_custom_report(const struct scmi_protocol_handle *ph,
* readings defined for this sensor or 1 for scalar sensors.
*/
r->readings_count = s->num_axis ?: 1;
+ expected_sz = sizeof(*p) + r->readings_count *
+ sizeof(p->readings[0]);
+ if (payld_sz < expected_sz)
+ break;
+
for (i = 0; i < r->readings_count; i++)
scmi_parse_sensor_readings(&r->readings[i],
&p->readings[i]);
--
2.43.0
^ permalink raw reply related
* [PATCH 4/4] firmware: arm_scmi: Validate Powercap domains before state access
From: Sudeep Holla @ 2026-05-17 19:02 UTC (permalink / raw)
To: Cristian Marussi, arm-scmi, linux-arm-kernel, Sudeep Holla
In-Reply-To: <20260517-scmi_fixes-v1-0-d86daec4defd@kernel.org>
Powercap protocol v2 keeps local enable and last-cap state per
domain. Some public operations indexed that state before checking that
the supplied domain id was valid, and cap_enable_get() updated it even
when cap_get() failed.
Validate the domain before touching the per-domain state and only
refresh cached enable state after a successful cap_get().
Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
---
drivers/firmware/arm_scmi/powercap.c | 25 ++++++++++++++++++-------
1 file changed, 18 insertions(+), 7 deletions(-)
diff --git a/drivers/firmware/arm_scmi/powercap.c b/drivers/firmware/arm_scmi/powercap.c
index ab9733f4458b..eb5c35cad026 100644
--- a/drivers/firmware/arm_scmi/powercap.c
+++ b/drivers/firmware/arm_scmi/powercap.c
@@ -453,10 +453,14 @@ static int scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
return -EINVAL;
/* Just log the last set request if acting on a disabled domain */
- if (PROTOCOL_REV_MAJOR(ph->version) >= 0x2 &&
- !pi->states[domain_id].enabled) {
- pi->states[domain_id].last_pcap = power_cap;
- return 0;
+ if (PROTOCOL_REV_MAJOR(ph->version) >= 0x2) {
+ if (!scmi_powercap_dom_info_get(ph, domain_id))
+ return -EINVAL;
+
+ if (!pi->states[domain_id].enabled) {
+ pi->states[domain_id].last_pcap = power_cap;
+ return 0;
+ }
}
return __scmi_powercap_cap_set(ph, pi, domain_id,
@@ -637,6 +641,9 @@ static int scmi_powercap_cap_enable_set(const struct scmi_protocol_handle *ph,
if (PROTOCOL_REV_MAJOR(ph->version) < 0x2)
return -EINVAL;
+ if (!scmi_powercap_dom_info_get(ph, domain_id))
+ return -EINVAL;
+
if (enable == pi->states[domain_id].enabled)
return 0;
@@ -678,16 +685,20 @@ static int scmi_powercap_cap_enable_get(const struct scmi_protocol_handle *ph,
if (PROTOCOL_REV_MAJOR(ph->version) < 0x2)
return 0;
+ if (!scmi_powercap_dom_info_get(ph, domain_id))
+ return -EINVAL;
+
/*
* Report always real platform state; platform could have ignored
* a previous disable request. Default true on any error.
*/
ret = scmi_powercap_cap_get(ph, domain_id, &power_cap);
- if (!ret)
+ if (!ret) {
*enable = !!power_cap;
- /* Update internal state with current real platform state */
- pi->states[domain_id].enabled = *enable;
+ /* Update internal state with current real platform state */
+ pi->states[domain_id].enabled = *enable;
+ }
return 0;
}
--
2.43.0
^ permalink raw reply related
* [PATCH 2/4] firmware: arm_scmi: Validate BASE_ERROR_EVENT payload size
From: Sudeep Holla @ 2026-05-17 19:02 UTC (permalink / raw)
To: Cristian Marussi, arm-scmi, linux-arm-kernel, Sudeep Holla
In-Reply-To: <20260517-scmi_fixes-v1-0-d86daec4defd@kernel.org>
BASE_ERROR_EVENT carries a variable number of message reports,
with the count encoded in error_status. The notification parser used
that count without checking whether the received payload contained all
reported entries.
Reject truncated payloads before copying the report array.
Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
---
drivers/firmware/arm_scmi/base.c | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c
index 4df2620e3c5d..d08a4f6a9ced 100644
--- a/drivers/firmware/arm_scmi/base.c
+++ b/drivers/firmware/arm_scmi/base.c
@@ -325,6 +325,8 @@ static void *scmi_base_fill_custom_report(const struct scmi_protocol_handle *ph,
void *report, u32 *src_id)
{
int i;
+ u32 error_status;
+ size_t expected_sz;
const struct scmi_base_error_notify_payld *p = payld;
struct scmi_base_error_report *r = report;
@@ -338,10 +340,19 @@ static void *scmi_base_fill_custom_report(const struct scmi_protocol_handle *ph,
if (evt_id != SCMI_EVENT_BASE_ERROR_EVENT || sizeof(*p) < payld_sz)
return NULL;
+ expected_sz = sizeof(p->agent_id) + sizeof(p->error_status);
+ if (payld_sz < expected_sz)
+ return NULL;
+
r->timestamp = timestamp;
r->agent_id = le32_to_cpu(p->agent_id);
- r->fatal = IS_FATAL_ERROR(le32_to_cpu(p->error_status));
- r->cmd_count = ERROR_CMD_COUNT(le32_to_cpu(p->error_status));
+ error_status = le32_to_cpu(p->error_status);
+ r->fatal = IS_FATAL_ERROR(error_status);
+ r->cmd_count = ERROR_CMD_COUNT(error_status);
+ expected_sz += r->cmd_count * sizeof(p->msg_reports[0]);
+ if (payld_sz < expected_sz)
+ return NULL;
+
for (i = 0; i < r->cmd_count; i++)
r->reports[i] = le64_to_cpu(p->msg_reports[i]);
*src_id = 0;
--
2.43.0
^ permalink raw reply related
* [PATCH 1/4] firmware: arm_scmi: Read sensor config as 32-bit value
From: Sudeep Holla @ 2026-05-17 19:02 UTC (permalink / raw)
To: Cristian Marussi, arm-scmi, linux-arm-kernel, Sudeep Holla
In-Reply-To: <20260517-scmi_fixes-v1-0-d86daec4defd@kernel.org>
The SENSOR_CONFIG_GET response contains a 32-bit sensor_config field,
and the xfer is initialized with a 4-byte RX buffer. Reading it with
get_unaligned_le64() can consume bytes past the returned payload.
Use get_unaligned_le32() to match the protocol layout and the allocated
response size.
Fixes: 7b83c5f41088 ("firmware: arm_scmi: Add SCMI v3.0 sensor configuration support")
Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
---
drivers/firmware/arm_scmi/sensors.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c
index 882d55f987d2..836c294a9f42 100644
--- a/drivers/firmware/arm_scmi/sensors.c
+++ b/drivers/firmware/arm_scmi/sensors.c
@@ -793,7 +793,7 @@ static int scmi_sensor_config_get(const struct scmi_protocol_handle *ph,
if (!ret) {
struct scmi_sensor_info *s = si->sensors + sensor_id;
- *sensor_config = get_unaligned_le64(t->rx.buf);
+ *sensor_config = get_unaligned_le32(t->rx.buf);
s->sensor_config = *sensor_config;
}
--
2.43.0
^ permalink raw reply related
* Re: [PATCH 0/8] iio: timestamp declaration cleanup
From: David Lechner @ 2026-05-17 19:22 UTC (permalink / raw)
To: Jyoti Bhayana, Jonathan Cameron, Nuno Sá, Andy Shevchenko,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Maxime Coquelin,
Alexandre Torgue, Benson Leung, Guenter Roeck
Cc: linux-iio, linux-kernel, linux-arm-kernel, linux-stm32,
chrome-platform
In-Reply-To: <20260517-iio-timestamp-cleanup-v1-0-61fb908c11c7@baylibre.com>
On 5/17/26 1:17 PM, David Lechner wrote:
> While looking around the code, I noticed that there are a lot of places
> were we are manually filling all of the fields of an IIO timestamp.
>
> This is error-prone (as seen in the first patch) and more verbose than
> it needs to be.
>
> I went with the approach of using the existing IIO_CHAN_SOFT_TIMESTAMP()
> macro for doing a struct assignment. This does require a cast, which
> makes it a bit more verbose, but we were already doing that in to
> drivers, so I went with it anyway.
>
> If we want to consider alternatives, we could make a iio helper function
> or macro like the first and second patches did.
>
I should have looked harder for existing alternatives. Just found one
more that avoids the cast via a local variable (in ad4170-4.c):
/* Add timestamp channel */
struct iio_chan_spec ts_chan = IIO_CHAN_SOFT_TIMESTAMP(chan_num);
st->chans[chan_num] = ts_chan;
And similar code is found in ad7192.c.
^ permalink raw reply
* [PATCH v2] arm64: dts: renesas: r8a78000: Describe all reserved memory
From: Marek Vasut @ 2026-05-17 20:07 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Marek Vasut, Conor Dooley, Geert Uytterhoeven,
Krzysztof Kozlowski, Magnus Damm, Rob Herring, devicetree,
linux-kernel, linux-renesas-soc
Fully describe all available DRAM in the DT, and describe regions which
are not accessible because they are used by firmware in reserved-memory
node.
Replace first memory bank memory@60600000 with memory@40000000 and a
518 MiB long reserved-memory no-map subnode. This memory region is used
by other cores in the system.
Reserve 32 kiB of memory at 0x8c100000 for parameters shared by IPL,
SCP, TFA BL31 and TEE.
Reserve 512 kiB of memory at 0x8c200000 for TFA BL31. The upcoming
upstream TFA 2.15 BL31 uses memory from 0x8c200000..0x8c242fff, the
round up to 512 kiB is slight future proofing.
Reserve 32 MiB of memory at 0x8c400000 for OPTEE-OS, which is the
entire OPTEE-OS TZ protected DRAM area.
Neither the TFA BL31 nor OPTEE-OS do modify the DT passed to Linux in
any way with any new reserved-memory {} node to reserve memory areas
used by the TFA BL31 or OPTEE-OS to prevent the next stage from using
those areas, which lets Linux use all of the available DRAM as it is
described in the DT that was passed in by U-Boot, including the areas
that are newly utilized by TFA BL31 or OPTEE-OS.
In case of high DRAM utilization, unless the memory used by TFA BL31
or OPTEE-OS is properly reserved, Linux may use and corrupt the memory
used by TFA BL31 or OPTEE-OS, which would lead to the system becoming
unresponsive.
Fixes: ad142a4ef710 ("arm64: dts: renesas: r8a78000: Add initial Ironhide board support")
Signed-off-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
---
Cc: Conor Dooley <conor+dt@kernel.org>
Cc: Geert Uytterhoeven <geert+renesas@glider.be>
Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
Cc: Magnus Damm <magnus.damm@gmail.com>
Cc: Rob Herring <robh@kernel.org>
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-renesas-soc@vger.kernel.org
---
V2: Run checkpatch, fix indent
---
.../boot/dts/renesas/r8a78000-ironhide.dts | 35 +++++++++++++++++--
1 file changed, 32 insertions(+), 3 deletions(-)
diff --git a/arch/arm64/boot/dts/renesas/r8a78000-ironhide.dts b/arch/arm64/boot/dts/renesas/r8a78000-ironhide.dts
index a721734fbd5d0..ed027a6c356ed 100644
--- a/arch/arm64/boot/dts/renesas/r8a78000-ironhide.dts
+++ b/arch/arm64/boot/dts/renesas/r8a78000-ironhide.dts
@@ -20,10 +20,9 @@ chosen {
stdout-path = "serial0:1843200n8";
};
- memory@60600000 {
+ memory@40000000 {
device_type = "memory";
- /* first 518MiB is reserved for other purposes. */
- reg = <0x0 0x60600000 0x0 0x5fa00000>;
+ reg = <0x0 0x40000000 0x0 0x80000000>;
};
memory@1080000000 {
@@ -65,6 +64,36 @@ memory@1e00000000 {
device_type = "memory";
reg = <0x1e 0x00000000 0x1 0x00000000>;
};
+
+ reserved-memory {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+
+ /* First 518 MiB is reserved for other purposes. */
+ firmware@40000000 {
+ reg = <0x0 0x40000000 0x0 0x20600000>;
+ no-map;
+ };
+
+ /* Parameters set by IPL. */
+ parameters@8c100000 {
+ reg = <0x0 0x8c100000 0x0 0x00008000>;
+ no-map;
+ };
+
+ /* TFA BL31. */
+ tfa-bl31@8c200000 {
+ reg = <0x0 0x8c200000 0x0 0x00080000>;
+ no-map;
+ };
+
+ /* TEE TZ DRAM. */
+ tee@8c400000 {
+ reg = <0x0 0x8c400000 0x0 0x02000000>;
+ no-map;
+ };
+ };
};
&extal_clk {
--
2.53.0
^ permalink raw reply related
* Re: [PATCH v1] irqchip/atmel-aic5: Free SMR cache on init failure
From: 최유호 @ 2026-05-17 20:34 UTC (permalink / raw)
To: Nicolas Ferre
Cc: Thomas Gleixner, Alexandre Belloni, Claudiu Beznea, linux-kernel,
linux-arm-kernel
In-Reply-To: <067c55c0-d56c-496e-bf75-5c3491524256@microchip.com>
Fair point. I submitted this from a general code hygiene perspective.
Thanks for taking the time to review it.
Best regards,
Yuho Choi
On Fri, 15 May 2026 at 05:58, Nicolas Ferre <nicolas.ferre@microchip.com> wrote:
>
> On 14/05/2026 at 01:31, Yuho Choi wrote:
> > sama5d2_aic5_of_init() allocates the SMR cache before calling
> > aic5_of_init(). If that fails, the cache is left allocated even though
> > no IRQ domain was installed and the cache will not be used.
> >
> > Free the cache on the failure path and clear the global pointer.
>
> No aic5 = no viable system to run on: I'm not sure it's worth
> considering the case.
>
> > Fixes: a50ac562ef48 ("irqchip/atmel-aic5: Handle suspend to RAM")
> > Signed-off-by: Yuho Choi <dbgh9129@gmail.com>
> > ---
> > drivers/irqchip/irq-atmel-aic5.c | 11 ++++++++++-
> > 1 file changed, 10 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/irqchip/irq-atmel-aic5.c b/drivers/irqchip/irq-atmel-aic5.c
> > index 1f14b401f71d..58650eb16880 100644
> > --- a/drivers/irqchip/irq-atmel-aic5.c
> > +++ b/drivers/irqchip/irq-atmel-aic5.c
> > @@ -358,13 +358,22 @@ static int __init sama5d2_aic5_of_init(struct device_node *node,
> > struct device_node *parent)
> > {
> > #ifdef CONFIG_PM
> > + int rc = 0;
> > smr_cache = kcalloc(DIV_ROUND_UP(NR_SAMA5D2_IRQS, 32) * 32,
> > sizeof(*smr_cache), GFP_KERNEL);
> > if (!smr_cache)
> > return -ENOMEM;
> > -#endif
> >
> > + rc = aic5_of_init(node, parent, NR_SAMA5D2_IRQS);
> > + if (rc) {
> > + kfree(smr_cache);
> > + smr_cache = NULL;
> > + }
> > +
> > + return rc;
> > +#else
> > return aic5_of_init(node, parent, NR_SAMA5D2_IRQS);
> > +#endif
> > }
> > IRQCHIP_DECLARE(sama5d2_aic5, "atmel,sama5d2-aic", sama5d2_aic5_of_init);
> >
> > --
> > 2.43.0
> >
>
^ permalink raw reply
* Re: [PATCH v2 3/3] arm64: dts: allwinner: A133: add support for Baijie Helper A133 board
From: Alexander Sverdlin @ 2026-05-17 20:38 UTC (permalink / raw)
To: Andre Przywara, linux-sunxi
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Chen-Yu Tsai,
Jernej Skrabec, Samuel Holland, devicetree, linux-arm-kernel,
linux-kernel
In-Reply-To: <2306dd3c-6362-40ee-8d9f-77f89be3a502@arm.com>
Hi Andre,
thanks for the quick feedback!
On Mon, 2026-05-11 at 13:44 +0200, Andre Przywara wrote:
> > --- /dev/null
> > +++ b/arch/arm64/boot/dts/allwinner/sun50i-a133-baije-core.dtsi
> > @@ -0,0 +1,162 @@
> > +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> > +/*
> > + * Copyright (c) 2025 Arm Ltd.
>
> Please put your own copyright here, even if that has been largely copied
> from an existing file.
>
> > + */
> > +
> > +/dts-v1/;
> > +
> > +#include "sun50i-a100.dtsi"
> > +#include "sun50i-a100-cpu-opp.dtsi"
> > +
> > +/{
> > + compatible = "baijie,helper-a133-core",
> > + "allwinner,sun50i-a100";
> > +
> > + aliases {
> > + serial1 = &uart1; /* BT module */
>
> Do we really need an alias for the BT UART? And is the BT module
> supported already? Then please add a child node to the UART node.
That's the only thing I can do currently regarding BT: stabilize the
serial enumeration, because UART1 cannot be used for anything else
except BT module, because this is soldered inside "core" module.
We can avoid different tty enumeration, should the support for
BT be implemented in the future...
> Isn't the WiFi/BT module on the SoM? Then please mention and enable MMC1
> here. Provide the child node for the WiFi chip, even if there is no
> upstream support in the kernel for it yet.
So both the above BT and the WiFi is AW869A/AIC8800 combo chip, which
has neither upstream driver, nor [upstream] DT bindings. Even github
driver for AIC8800 doesn't seem to use DT, therefore it looks quite
pointless to me at this point to specify anything in the DT for the
chip which doesn't have the bindings idea even theoretically.
Nothing in the current DT shall block any future work on the AW869A
support though and the above "aliases" entry shall even guarantee
unchanged serial enumeration shall such support arise.
> > +®_aldo1 {
>
> What is aldo1 used for, actually? I don't see this referenced anywhere.
> I guess the kernel turns that off after booting?
> If you have access to the schematic, please check that. If that's for
> some peripheral not yet supported, please note the user anyway, ideally
> by an explaining regulator-name, or by a comment. Also if it's used for
> any of the required SoC VDD pins. See the Liontron .dts for comparison.
>
> > + regulator-always-on;
^^^^^^^^^^^^^^^^^^^
I suppose it's not being switcdhed of because of the above.
It's used for both PLL supply for the whole SoC + as analog voltage reference
for LRADC (the buttons you've noticed on the board are connected to
this ADC via a resistor ladder).
>
> > +®_aldo2 {
> > + regulator-always-on;
>
> For always-on regulators we definitely need an explanation. Does the
> board stop booting if you remove this line?
> Maybe it's for DRAM? Can you say what voltage it is, either from the
> reset default, or set by the bootloader?
Thanks for the hint! I'll put proper voltages into all regulators +
comment all the always-on regulators.
>
> > diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a133-baijie-helper.dts b/arch/arm64/boot/dts/allwinner/sun50i-a133-baijie-helper.dts
> > new file mode 100644
> > index 000000000000..ccbca5d0a40c
> > --- /dev/null
> > +++ b/arch/arm64/boot/dts/allwinner/sun50i-a133-baijie-helper.dts
> And you should provide a top level 5V regulator here, to be the root of
> the regulator tree. Look at reg_vcc5v in the Liontron .dts.
It doesn't look to me as if Liontron had reg_vcc5v as its 5V "root" regulator.
It seems to be only used for reg_usb1_vbus, while HelperBoard A133 doesn't
have USB power control. The second issue with Helper/Core split is that
all PMIC story is inside Core board which has 5V input rail, while HelperBoard
around it has indeed 12V->5V DCDC regulator (similar to Liontron), but
putting it in the DT would introduce wierd dependency of the core to the
HelperBoard which carries it. Do you think it would make sense?
> So from the pictures I found online it looks like there is an USB-C port
> labelled "OTG", so can you please add an &usbotg reference here and
> describe that port.
Nice catch! I've missed the fact usbphy 0 has to be in peripheral mode,
not host mode. Will rework!
> > +&usbphy {
>
> Are the two USB ports always powered?
>
> And anyway, I see a *dual* USB-A socket on the pictures online, in
> addition to the USB-OTG port. So where does the third USB come from? The
> A133 only supports one host USB port plus the one OTG port. So is there
> an USB hub chip on the board?
There are two hubs, one on each usbphy. OTG side hub is even bus-powered,
two USB-A ports are always powered from the board's 12V->5V DCDC, no USB
load switches.
>
--
Alexander Sverdlin.
^ permalink raw reply
* Re: [PATCH/RFC 05/14] firmware: arm_scmi: Add scmi_get_base_info()
From: Cristian Marussi @ 2026-05-17 20:42 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: Cristian Marussi, Sudeep Holla, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Magnus Damm, Saravana Kannan, Michael Turquette,
Stephen Boyd, Philipp Zabel, Ulf Hansson, Rafael J . Wysocki,
Kevin Hilman, Florian Fainelli, Wolfram Sang, Marek Vasut,
Kuninori Morimoto, arm-scmi, linux-arm-kernel, linux-renesas-soc,
linux-clk, devicetree, linux-pm, linux-kernel
In-Reply-To: <CAMuHMdWK3Noh5ZQVWT9C0EKyXZePMaJ97C0A_syK+xCb_=abeQ@mail.gmail.com>
On Fri, May 08, 2026 at 12:26:19PM +0200, Geert Uytterhoeven wrote:
> Hi Cristian,
>
Hi,
> On Mon, 27 Apr 2026 at 01:03, Cristian Marussi <cristian.marussi@arm.com> wrote:
> > On Fri, Apr 24, 2026 at 02:08:55PM +0200, Geert Uytterhoeven wrote:
> > > On Wed, 22 Apr 2026 at 20:45, Cristian Marussi <cristian.marussi@arm.com> wrote:
> > > > Also because this should be one of the selling point of the SCMI stack
> > > > in a virtualized environment: you can ship the same kernel drivers with
> > > > the same DT and you know that ID=<N> will always identify the specific
> > > > resource that is needed by your driver without worrying about the fact
> > > > that in reality in the backstage the effectively managed physical resource
> > > > could be different across different platforms, because that does not matter
> > >
> > > This sounds strange to me, do I understand it correctly?
> > > So the ID should (1) be tied to the use-case, and not to the underlying
> > > hardware, and (2) be the same for different platforms?
> > >
> > > For (1): Then we must not put these IDs in DT at all, as DT is supposed
> > > to describe the hardware (and firmware IDs in DT were IMHO already
> > > a stretch before).
> > > For (2): How can there be a contiguous list of IDs, as not all platforms
> > > may have the same underlying hardware?
> >
> > I would NOT say that an SCMI FW must behave like this regarding IDs, but it
> > is a possible SCMI deployed setup that can be useful in virtualized setups
> >
> > I mean, the DT describes the hardware of course BUT when you refer to
> > some of this hardware DT bits from some other subsystem by referencing a
> > phandle, even in the non-SCMI world, you are in fact selecting a specific
> > resource that fit you use case, right ? Can we say this ?
> > I mean you needed that specific clock or regulator that you described
> > previously so as to be able to enable some other piece of HW...
> >
> > Now, the SCMI provides an abstraction on top of this, since you really
> > discover domain IDs of a specific class (clocks/regulators etc) you are
> > in fact describing an HW abstraction that you then refer with the usual
> > phandle...also because there is NOT so much SCMI hardware to describe,
> > given that the HW is handled transparently (opaquely really :P) by the
> > driver on the FW side...
> >
> > ...you basically obtain such domain ID, usable as phandles through dynamic
> > SCMI enumeration so that you can use it all over your DT to make use of such
> > resources...
> >
> > ...on top of this, consider that the SCMI server CAN provide to its agents
> > a per-agent-view of the world, IOW it can (and should) expose to a specific
> > agent ONLY the resources needed by that agent, i.e. it can expose the set
> > of resources 1-N to two distinct agents and that does NOT mean that the
> > underlying physical resource mapped by ID=3 in both agents has to be
> > effectively the same piece of hardware: it could be the case, and this
> > would be useful to exposed and managed properly a shared resource, or
> > it could also be that the same ID=3 could refer to completely distinct
> > pieces of the same class of hardware...(same protocol same class of
> > resource...)
>
> Exposing only the clocks/reset/power domains the agent can use,
> in a contiguous list of IDS, means that the number space changes,
> depending on which resources are exposed.
>
Yes, potentially, it depedns on how the HW/FW stack was designed I
suppose...
> Suppose you have a system where you want to assign a specific hardware
> block in the SoC to the realtime CPU core instead of the application
> CPU core running Linux.
Ok, so this is definitely a considerable change.
> That means all resources used by that block must no longer be exposed
> to the Linux agent, and the corresponding IDs must be removed from
> the ID space exposed to Linux. As the ID space must be sequential
> and contiguous, the IDs must be renumbered, impacting resources that
> are exposed to Linux. As these IDs are used in the SoC .dtsi, they
> must be changed there, too, However, these IDs have become part of
> the stable DT ABI, and thus cannot be changed.
Well, you have to ship a final DTB blob that is crafted to describe the
actual HW at the end, right ?
I mean, in your example, it seems to me that you have changed considerably
the HW surface by moving a clock (and its related resources) away from the
reach of Linux as a whole, so should not be expected to have an updated
DTB shipped ?
And I am NOT saying to do this by changing the base SoC dtsi, but via build
time overrides and/or runtime overlays so as to derive from that same SoC
base dtsi a properly reviewed final DTB that describes how the HW has actually
changed, becasue beside the renumbering that you mention there will be
also a bunch of HW pieces that were relying on that clock that now will
have to be removed from the DTB if no more usable (or remapped to use a
different, maybe non SCMI resource)
I maybe too naive and not used to very complex DTBs, but why all of the
above cannot be done along the lines of how is done as an example in
JUNO [1], where some initial base dtsi was overriden by virtue of includes
and overrides to properly describe the board at hand (r0/r1/r2), even going
to the extreme, funny enough, to move from the old SCPI to SCMI.
[1]: https://elixir.bootlin.com/linux/v7.0.8/source/arch/arm64/boot/dts/arm/juno-scmi.dts
>
> This patch series fixes that issue, too, by describing the actual
> hardware in DT, and doing the mapping to exposed SCMI features in the
> kernel, based on which firmware version is running on the SCP.
>
Which seems to me to go in the opposite direction to what SCMI expects:
but I understand that we have to deal and cope with the existing already
shipped FW, so my concern is not really around enabling this in particular,
is much more the fact that we open the door and normalize this kind of
design even with future, still to be developed, SCMI FWs.
...otherwise...have the spec changed/clarified to allow for such
non-contiguos IDs...
...anyway let's hear Sudeep opinion on this general issue of contiguos
remapped IDs and being able to peek into SCMI version from outside the
SCMI world...
Thanks,
Cristian
^ permalink raw reply
* Re: [PATCH/RFC 05/14] firmware: arm_scmi: Add scmi_get_base_info()
From: Cristian Marussi @ 2026-05-17 20:58 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: Cristian Marussi, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Sudeep Holla, Magnus Damm, Saravana Kannan, Michael Turquette,
Stephen Boyd, Philipp Zabel, Ulf Hansson, Rafael J . Wysocki,
Kevin Hilman, Florian Fainelli, Wolfram Sang, Marek Vasut,
Kuninori Morimoto, arm-scmi, linux-arm-kernel, linux-renesas-soc,
linux-clk, devicetree, linux-pm, linux-kernel
In-Reply-To: <CAMuHMdX8b9C3D8fV5th6GCgs6JAq7HZbF_SrOKz_gDy2H8ksow@mail.gmail.com>
On Fri, May 15, 2026 at 05:39:45PM +0200, Geert Uytterhoeven wrote:
> Hi Christian,
>
> On Wed, 22 Apr 2026 at 20:45, Cristian Marussi <cristian.marussi@arm.com> wrote:
> > On Tue, Apr 21, 2026 at 08:11:38PM +0200, Geert Uytterhoeven wrote:
> > > Currently non-SCMI drivers cannot find out what the specific versions of
> > > each SCMI provider implementation on the running system are.
> >
> > Thanks for your patches....this is not a proper full review of the series,
> > BUT this patch catched my eye..
> >
> > Indeed, yes, it is deliberate that the SCMI version information is NOT
> > exposed out of the SCMI world, since being the SCMI an attempt to
> > standardize a common FW interface (as in [1] of course), you should not
> > know what runs inside the black-box, it should be irrelevant...
> >
> > ...indeed the versioning is used inside the SCMI stack to deal properly
> > with different protocol versions implemented by the server OR to apply
> > proper quirks when needed, but all the rest should be standard....
>
> [...]
>
> > I watched a bit of the LPC discussions around this (from Marek I think)
> > but sincerely most of those problems had one (not necessarily simple)
> > solution: fix your firmwares AND/OR apply quirks in the meantime...
>
> So let's forward to the future, where the firmware is fixed, is fully
> compliant with the SCMI spec, and all IDs are stable, so no quirks are
> needed.
>
> Where do we specify the SCMI IDs to use? Unless when using the
> remapping driver proposed in this patch series, they must end up in the
> DTB. Existing upstream users put them either in the SoC-specific .dtsi,
> or in board-specific .dts.
>
> The SCMI server is supposed to expose to an agent (e.g. Linux) a
> sequential and contiguous list of IDs that represent only resources that
> the agent is allowed to use.
> - We cannot put the SCMI IDs in the SoC-specific .dtsi, as that
> describes all hardware in the SoC, which is typically much more than
> Linux can or even wants to use when running on a specific board.
> - You would think we could put the SCMI IDs in the board-specific
> .dts. However, that would limit actual use cases later, which do
> not necessarily depend on the board solely.
> - E.g. when moving control of the CAN-FD controller from Linux to
> the Realtime OS, the CAN-FD node must be disabled in the DTB (by
> overriding status to "reserved", or by just deleting the CAN-FD
> node, both of which can be done by the boot loader). However,
> with SCMI, the IDs corresponding to CAN-FD resources must be
> removed from the ID space, causing a full renumbering. Who is
> supposed to update the IDs in the DTB?
As per my previous email, after such a breaking change I would expect a
new DTB describing the new HW to be needed anyway.
> - E.g. when partitioning a single Linux system in multiple VMs,
> and distributing hardware across these VMs, all VMs need
> different DTBs, each describing a subset of the hardware. With
> SCMI, each VM needs different SCMI ID spaces, causing not a
> simple partitioning of the devices in the DTB, but also a
> renumbering of all IDs.
Ok now I am lost..why do you need a distinct IDs space for each VM ?
In a virtualized env, I would expect to leverage the SCMI stack to
realize the exact opposite: same set of IDs advertised to each VM (and
so same DTB potentially) by the server which in turn can decide to
assign the same device (and handle the sharing) to some ID or assign
different devices to the same or different IDs on each VM: i.e. you
have a set of virtual_IDs that is what the server exposes to each VM
SCMI agent, and then a bunch of real physical IDs, without any contraint
on their numbering, that the server uses in the backstage to refer to
the real resources and that it properly remaps to each per-VM set of
exposed virtual_IDs during the build and/or boot board configuration
phase....i.e. when the FW adapts and reconfigures to the specific
board that is finally running on.
Thanks,
Cristian
^ permalink raw reply
* Re: [PATCH v2 11/11] iio: dac: add mcf54415 DAC
From: Angelo Dureghello @ 2026-05-17 21:04 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Angelo Dureghello, Greg Ungerer, Geert Uytterhoeven, Steven King,
Arnd Bergmann, Maxime Coquelin, Alexandre Torgue,
Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Greg Ungerer, linux-m68k, linux-kernel, linux-stm32,
linux-arm-kernel, linux-iio
In-Reply-To: <agTe7vZ94DnmD4Ed@ashevche-desk.local>
H Andy,
On Wed, May 13, 2026 at 11:28:30PM +0300, Andy Shevchenko wrote:
> On Wed, May 13, 2026 at 11:14:35AM +0200, Angelo Dureghello wrote:
> >
> > Add basic version of mcf54415 DAC driver. DAC is embedded in the cpu and
> > DAC configuration registers are mapped in the internal IO address space.
> >
> > The DAC accepts a 12-bit digital signal and creates a monotonic 12-bit
> > analog output varying from DAC_VREFL to DAC_VREFH. The DAC module
> > consists of a conversion unit, an output amplifier, and the associated
> > digital control blocks. Default register values for DAC_VREFL and DAC_VREFH
> > are respectively 0 and 0xfff, left untouched in this initial version.
> >
> > This initial version of the driver is minimalistic, "output raw" only, to
> > be extended in the future. DMA and external sync are disabled, default mode
> > is high speed, default format is right-justified 12bit on 16bit word.
>
> ...
>
> > +#include <linux/array_size.h>
> > +#include <linux/bitfield.h>
> > +#include <linux/bits.h>
> > +#include <linux/clk.h>
> > +#include <linux/compiler_types.h>
> > +#include <linux/delay.h>
>
> + err.h
>
> > +#include <linux/io.h>
> > +#include <linux/module.h>
> > +#include <linux/mutex.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/regmap.h>
>
> ...
>
> > +struct mcf54415_dac {
> > + struct clk *clk;
> > + struct regmap *map;
>
> I believe that regmap pointer is used more often, can you check with
> bloat-o-meter that swapping these two (by the order) gives any benefit in
> object size?
>
> > +};
>
> ...
>
> > + .max_register = 0x1F,
>
> No definition? What datasheet says about this? Perhaps define the MAX as per
> last defined register in the datasheet?
>
> > +};
>
> ...
>
> > +static void mcf54415_dac_init(struct mcf54415_dac *info)
> > +{
> > + /* Keeping defaults and enable DAC (bit 0 set to 0) */
> > + regmap_write(info->map, MCF54415_DAC_CR, MCF54415_DAC_CR_FILT |
> > + FIELD_PREP(MCF54415_DAC_CR_WMLVL, 1));
>
> Seems the whole driver ignores IO errors, why?
>
> > + /* DAC is ready after 12us, from RM table 40-3 */
> > + fsleep(12);
> > +}
>
> ...
>
> > +static void mcf54415_dac_exit(void *data)
> > +{
> > + struct mcf54415_dac *info = data;
> > +
> > + regmap_update_bits(info->map, MCF54415_DAC_CR, MCF54415_DAC_CR_PDN,
> > + MCF54415_DAC_CR_PDN);
>
> regmap_set_bits()
>
> > +}
>
> ...
>
> > +static int mcf54415_write_raw(struct iio_dev *indio_dev,
> > + struct iio_chan_spec const *chan,
> > + int val, int val2, long mask)
> > +{
> > + struct mcf54415_dac *info = iio_priv(indio_dev);
> > +
> > + switch (mask) {
> > + case IIO_CHAN_INFO_RAW:
> > + if (val < 0 || val > 4095)
>
> Do we have a definition for the resolution? I'm fine with the plain numbers,
> but it's better to add a short comment to say that this is "based on the
> resolution of XXX register per datasheet".
>
> > + return -EINVAL;
> > + regmap_write(info->map, MCF54415_DAC_DATA, val);
> > + return 0;
> > + default:
> > + return -EINVAL;
> > + }
> > +}
>
> ...
>
> > +static int mcf54415_dac_probe(struct platform_device *pdev)
> > +{
> > + struct device *dev = &pdev->dev;
> > + struct iio_dev *indio_dev;
> > + struct mcf54415_dac *info;
> > + void __iomem *regs;
> > + int ret;
> > +
> > + indio_dev = devm_iio_device_alloc(dev, sizeof(*info));
> > + if (!indio_dev)
> > + return -ENOMEM;
> > +
> > + info = iio_priv(indio_dev);
> > +
> > + regs = devm_platform_ioremap_resource(pdev, 0);
> > + if (IS_ERR(regs))
> > + return dev_err_probe(dev, PTR_ERR(regs),
> > + "failed to get io regs\n");
>
> One line.
>
> > +
> > + info->map = devm_regmap_init_mmio(dev, regs,
> > + &mcf54415_dac_regmap_config);
> > + if (IS_ERR(info->map))
> > + return PTR_ERR(info->map);
> > +
> > + info->clk = devm_clk_get_enabled(dev, "dac");
> > + if (IS_ERR(info->clk))
> > + return dev_err_probe(dev, PTR_ERR(info->clk),
> > + "failed getting clock\n");
>
> Also can be a single line, but this one a bit longer than above, gives
> 88 characters.
>
> > + platform_set_drvdata(pdev, indio_dev);
> > +
> > + indio_dev->name = "mcf54415";
> > + indio_dev->info = &mcf54415_dac_iio_info;
> > + indio_dev->modes = INDIO_DIRECT_MODE;
> > + indio_dev->channels = mcf54415_dac_iio_channels;
> > + indio_dev->num_channels = ARRAY_SIZE(mcf54415_dac_iio_channels);
> > +
> > + mcf54415_dac_init(info);
> > +
> > + ret = devm_add_action_or_reset(dev, mcf54415_dac_exit, info);
> > + if (ret)
> > + return ret;
> > +
> > + return devm_iio_device_register(dev, indio_dev);
> > +}
Thanks, will look into all the above, together with sashiko, and issue
a v3 in short.
>
> --
> With Best Regards,
> Andy Shevchenko
>
>
Regards,
angelo
^ permalink raw reply
* [PATCH 1/8] mm: Add ptep_try_install() for lockless empty-slot installs
From: Tejun Heo @ 2026-05-17 21:12 UTC (permalink / raw)
To: David Vernet, Andrea Righi, Changwoo Min, Alexei Starovoitov,
Andrii Nakryiko, Daniel Borkmann, Martin KaFai Lau,
Kumar Kartikeya Dwivedi
Cc: Catalin Marinas, Will Deacon, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, Andrew Morton, David Hildenbrand,
Mike Rapoport, Emil Tsalapatis, sched-ext, bpf, x86,
linux-arm-kernel, linux-mm, linux-kernel, Tejun Heo
In-Reply-To: <20260517211232.1670594-1-tj@kernel.org>
Add ptep_try_install(ptep, new_pte): atomically set *ptep to new_pte
iff it is currently pte_none(). Returns true on success, false if the
slot was already populated or the arch has no implementation.
The intended caller is the upcoming bpf_arena kernel-side fault
recovery path. The install runs from a page fault and may have to
contend with locks already held by the faulting kernel caller, so
keeping it lock-free via cmpxchg is the safe choice.
The generic version in <linux/pgtable.h> returns false. x86 and arm64
override with try_cmpxchg-based implementations on the underlying
pteval. Other architectures get the false stub - the callers there
already fall through to oops.
Suggested-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Suggested-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Tejun Heo <tj@kernel.org>
---
arch/arm64/include/asm/pgtable.h | 8 ++++++++
arch/x86/include/asm/pgtable.h | 8 ++++++++
include/linux/pgtable.h | 16 ++++++++++++++++
3 files changed, 32 insertions(+)
diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
index 9029b81ccbe8..eb1dd59aaea3 100644
--- a/arch/arm64/include/asm/pgtable.h
+++ b/arch/arm64/include/asm/pgtable.h
@@ -1830,6 +1830,14 @@ static inline pte_t ptep_get_and_clear(struct mm_struct *mm,
return __ptep_get_and_clear(mm, addr, ptep);
}
+#define __HAVE_ARCH_PTEP_TRY_INSTALL
+static inline bool ptep_try_install(pte_t *ptep, pte_t new_pte)
+{
+ pteval_t old = 0;
+
+ return try_cmpxchg(&pte_val(*ptep), &old, pte_val(new_pte));
+}
+
#define test_and_clear_young_ptes test_and_clear_young_ptes
static inline bool test_and_clear_young_ptes(struct vm_area_struct *vma,
unsigned long addr, pte_t *ptep, unsigned int nr)
diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h
index 13e3e9a054cb..afff54a77ffd 100644
--- a/arch/x86/include/asm/pgtable.h
+++ b/arch/x86/include/asm/pgtable.h
@@ -1284,6 +1284,14 @@ static inline void ptep_set_wrprotect(struct mm_struct *mm,
} while (!try_cmpxchg((long *)&ptep->pte, (long *)&old_pte, *(long *)&new_pte));
}
+#define __HAVE_ARCH_PTEP_TRY_INSTALL
+static inline bool ptep_try_install(pte_t *ptep, pte_t new_pte)
+{
+ pte_t old_pte = __pte(0);
+
+ return try_cmpxchg((long *)&ptep->pte, (long *)&old_pte, *(long *)&new_pte);
+}
+
#define flush_tlb_fix_spurious_fault(vma, address, ptep) do { } while (0)
#define __HAVE_ARCH_PMDP_SET_ACCESS_FLAGS
diff --git a/include/linux/pgtable.h b/include/linux/pgtable.h
index cdd68ed3ae1a..60a4afdc9131 100644
--- a/include/linux/pgtable.h
+++ b/include/linux/pgtable.h
@@ -1036,6 +1036,22 @@ static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addres
}
#endif
+#ifndef __HAVE_ARCH_PTEP_TRY_INSTALL
+/**
+ * ptep_try_install - atomically install an empty PTE
+ * @ptep: page table entry
+ * @new_pte: value to install
+ *
+ * Atomically set *@ptep to @new_pte iff *@ptep is pte_none(). Return
+ * true on success. Architectures opt in by providing a cmpxchg-based
+ * override. The generic stub returns false.
+ */
+static inline bool ptep_try_install(pte_t *ptep, pte_t new_pte)
+{
+ return false;
+}
+#endif
+
#ifndef wrprotect_ptes
/**
* wrprotect_ptes - Write-protect PTEs that map consecutive pages of the same
--
2.54.0
^ permalink raw reply related
* [PATCH 3/8] bpf: Add sleepable variant of bpf_arena_alloc_pages for kernel callers
From: Tejun Heo @ 2026-05-17 21:12 UTC (permalink / raw)
To: David Vernet, Andrea Righi, Changwoo Min, Alexei Starovoitov,
Andrii Nakryiko, Daniel Borkmann, Martin KaFai Lau,
Kumar Kartikeya Dwivedi
Cc: Catalin Marinas, Will Deacon, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, Andrew Morton, David Hildenbrand,
Mike Rapoport, Emil Tsalapatis, sched-ext, bpf, x86,
linux-arm-kernel, linux-mm, linux-kernel, Tejun Heo
In-Reply-To: <20260517211232.1670594-1-tj@kernel.org>
The existing kernel-side export of bpf_arena_alloc_pages is _non_sleepable
only - it's used by the verifier to inline the kfunc when the call site is
non-sleepable. There is no sleepable equivalent for kernel callers; the
kfunc bpf_arena_alloc_pages itself is BPF-only.
sched_ext needs sleepable kernel-side allocs for its arena pool init/grow
paths. Add bpf_arena_alloc_pages_sleepable() mirroring the _non_sleepable
wrapper but passing sleepable=true to arena_alloc_pages().
Signed-off-by: Tejun Heo <tj@kernel.org>
---
include/linux/bpf.h | 8 ++++++++
kernel/bpf/arena.c | 13 +++++++++++++
2 files changed, 21 insertions(+)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 831996c411cf..64968ca6db51 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -679,6 +679,8 @@ int bpf_dynptr_from_file_sleepable(struct file *file, u32 flags,
void *bpf_arena_alloc_pages_non_sleepable(void *p__map, void *addr__ign, u32 page_cnt, int node_id,
u64 flags);
void bpf_arena_free_pages_non_sleepable(void *p__map, void *ptr__ign, u32 page_cnt);
+void *bpf_arena_alloc_pages_sleepable(void *p__map, void *addr__ign, u32 page_cnt, int node_id,
+ u64 flags);
#else
static inline void *bpf_arena_alloc_pages_non_sleepable(void *p__map, void *addr__ign, u32 page_cnt,
int node_id, u64 flags)
@@ -689,6 +691,12 @@ static inline void *bpf_arena_alloc_pages_non_sleepable(void *p__map, void *addr
static inline void bpf_arena_free_pages_non_sleepable(void *p__map, void *ptr__ign, u32 page_cnt)
{
}
+
+static inline void *bpf_arena_alloc_pages_sleepable(void *p__map, void *addr__ign, u32 page_cnt,
+ int node_id, u64 flags)
+{
+ return NULL;
+}
#endif
extern const struct bpf_map_ops bpf_map_offload_ops;
diff --git a/kernel/bpf/arena.c b/kernel/bpf/arena.c
index bc696bef7104..8eb7b95f4999 100644
--- a/kernel/bpf/arena.c
+++ b/kernel/bpf/arena.c
@@ -937,6 +937,19 @@ void *bpf_arena_alloc_pages_non_sleepable(void *p__map, void *addr__ign, u32 pag
return (void *)arena_alloc_pages(arena, (long)addr__ign, page_cnt, node_id, false);
}
+
+void *bpf_arena_alloc_pages_sleepable(void *p__map, void *addr__ign, u32 page_cnt,
+ int node_id, u64 flags)
+{
+ struct bpf_map *map = p__map;
+ struct bpf_arena *arena = container_of(map, struct bpf_arena, map);
+
+ if (map->map_type != BPF_MAP_TYPE_ARENA || flags || !page_cnt)
+ return NULL;
+
+ return (void *)arena_alloc_pages(arena, (long)addr__ign, page_cnt, node_id, true);
+}
+
__bpf_kfunc void bpf_arena_free_pages(void *p__map, void *ptr__ign, u32 page_cnt)
{
struct bpf_map *map = p__map;
--
2.54.0
^ permalink raw reply related
* [PATCH 6/8] sched_ext: Require an arena for cid-form schedulers
From: Tejun Heo @ 2026-05-17 21:12 UTC (permalink / raw)
To: David Vernet, Andrea Righi, Changwoo Min, Alexei Starovoitov,
Andrii Nakryiko, Daniel Borkmann, Martin KaFai Lau,
Kumar Kartikeya Dwivedi
Cc: Catalin Marinas, Will Deacon, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, Andrew Morton, David Hildenbrand,
Mike Rapoport, Emil Tsalapatis, sched-ext, bpf, x86,
linux-arm-kernel, linux-mm, linux-kernel, Tejun Heo
In-Reply-To: <20260517211232.1670594-1-tj@kernel.org>
Upcoming patches will let the kernel place arena-resident scratch shared
with the BPF program (e.g. per-CPU set_cmask cmask) so the BPF side can
dereference it directly via __arena pointers, replacing the current
cmask_copy_from_kernel() probe-read loop. That requires each cid-form
scheduler to expose its arena to the kernel. Kernel- side accesses are
recovered by the per-arena scratch-page mechanism.
bpf_scx_reg_cid() walks the struct_ops member progs via
bpf_struct_ops_for_each_prog() and reads each prog's arena via
bpf_prog_arena(). The verifier enforces one arena per program, so each
member prog contributes at most one arena. All non-NULL contributions must
match and at least one member prog must use an arena. The map ref is held on
scx_sched and dropped on sched destroy. cpu-form schedulers (bpf_scx_reg)
are unchanged - no arena requirement.
Signed-off-by: Tejun Heo <tj@kernel.org>
---
kernel/sched/ext.c | 56 ++++++++++++++++++++++++++++++++++++-
kernel/sched/ext_internal.h | 8 ++++++
2 files changed, 63 insertions(+), 1 deletion(-)
diff --git a/kernel/sched/ext.c b/kernel/sched/ext.c
index 64f8a096f133..94aab7037329 100644
--- a/kernel/sched/ext.c
+++ b/kernel/sched/ext.c
@@ -5009,6 +5009,8 @@ static void scx_sched_free_rcu_work(struct work_struct *work)
rhashtable_free_and_destroy(&sch->dsq_hash, NULL, NULL);
free_exit_info(sch->exit_info);
+ if (sch->arena_map)
+ bpf_map_put(sch->arena_map);
kfree(sch);
}
@@ -6732,6 +6734,7 @@ struct scx_enable_cmd {
struct sched_ext_ops_cid *ops_cid;
};
bool is_cid_type;
+ struct bpf_map *arena_map; /* arena ref to transfer to sch */
int ret;
};
@@ -6898,6 +6901,15 @@ static struct scx_sched *scx_alloc_and_add_sched(struct scx_enable_cmd *cmd,
return ERR_PTR(ret);
}
#endif /* CONFIG_EXT_SUB_SCHED */
+
+ /*
+ * Consume the arena_map ref bpf_scx_reg_cid() took. Defer to here so
+ * earlier failure paths leave cmd->arena_map set and bpf_scx_reg_cid
+ * drops the ref. After this point, sch owns the ref and any cleanup
+ * runs through scx_sched_free_rcu_work() which puts it.
+ */
+ sch->arena_map = cmd->arena_map;
+ cmd->arena_map = NULL;
return sch;
#ifdef CONFIG_EXT_SUB_SCHED
@@ -7884,11 +7896,53 @@ static int bpf_scx_reg(void *kdata, struct bpf_link *link)
return scx_enable(&cmd, link);
}
+struct scx_arena_scan {
+ struct bpf_map *arena;
+ int err;
+};
+
+/*
+ * The verifier enforces one arena per BPF program, so each struct_ops
+ * member prog contributes at most one arena via bpf_prog_arena().
+ * Require all non-NULL contributions to match.
+ */
+static int scx_arena_scan_prog(struct bpf_prog *prog, void *data)
+{
+ struct scx_arena_scan *s = data;
+ struct bpf_map *arena = bpf_prog_arena(prog);
+
+ if (!arena)
+ return 0;
+ if (s->arena && s->arena != arena) {
+ s->err = -EINVAL;
+ return 1;
+ }
+ s->arena = arena;
+ return 0;
+}
+
static int bpf_scx_reg_cid(void *kdata, struct bpf_link *link)
{
struct scx_enable_cmd cmd = { .ops_cid = kdata, .is_cid_type = true };
+ struct scx_arena_scan scan = {};
+ int ret;
- return scx_enable(&cmd, link);
+ bpf_struct_ops_for_each_prog(kdata, scx_arena_scan_prog, &scan);
+ if (scan.err) {
+ pr_err("sched_ext: cid-form scheduler uses multiple arena maps\n");
+ return scan.err;
+ }
+ if (!scan.arena) {
+ pr_err("sched_ext: cid-form scheduler must use a BPF arena map\n");
+ return -EINVAL;
+ }
+
+ bpf_map_inc(scan.arena);
+ cmd.arena_map = scan.arena;
+ ret = scx_enable(&cmd, link);
+ if (cmd.arena_map) /* not consumed by scx_alloc_and_add_sched() */
+ bpf_map_put(cmd.arena_map);
+ return ret;
}
static void bpf_scx_unreg(void *kdata, struct bpf_link *link)
diff --git a/kernel/sched/ext_internal.h b/kernel/sched/ext_internal.h
index 7258aea94b9f..d40cfd29ddaa 100644
--- a/kernel/sched/ext_internal.h
+++ b/kernel/sched/ext_internal.h
@@ -1111,6 +1111,14 @@ struct scx_sched {
struct sched_ext_ops_cid ops_cid;
};
bool is_cid_type; /* true if registered via bpf_sched_ext_ops_cid */
+
+ /*
+ * Arena map auto-discovered from member progs at struct_ops attach.
+ * cid-form schedulers must use exactly one arena across all member
+ * progs. NULL on cpu-form.
+ */
+ struct bpf_map *arena_map;
+
DECLARE_BITMAP(has_op, SCX_OPI_END);
/*
--
2.54.0
^ permalink raw reply related
* [PATCHSET v2 sched_ext/for-7.2] bpf/arena: Direct kernel-side access
From: Tejun Heo @ 2026-05-17 21:12 UTC (permalink / raw)
To: David Vernet, Andrea Righi, Changwoo Min, Alexei Starovoitov,
Andrii Nakryiko, Daniel Borkmann, Martin KaFai Lau,
Kumar Kartikeya Dwivedi
Cc: Catalin Marinas, Will Deacon, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, Andrew Morton, David Hildenbrand,
Mike Rapoport, Emil Tsalapatis, sched-ext, bpf, x86,
linux-arm-kernel, linux-mm, linux-kernel, Tejun Heo
Hello,
This makes BPF arena memory directly dereferenceable from kernel code
(struct_ops callbacks, kfuncs). Each arena gets a per-arena scratch page
that an arch fault hook installs into empty PTEs on kernel-side faults,
after KFENCE. The faulting instruction retries and the violation is reported
through the program's BPF stream.
v2 changes since RFC v1 (20260427105109.2554518-1-tj@kernel.org):
Switched from pre-filling the arena recovery range with a fallback page to
leaving PTEs sparse and installing the scratch page from an arch fault hook
on demand. Pre-fill erased the fault and lost error attribution; on-demand
keeps the fault visible and reports the violation through the program's
bpf_stream. Plus misc fixes.
Motivation
----------
sched_ext's ops_cid.set_cmask() hands the BPF scheduler a struct scx_cmask
*. The kernel translates a kernel cpumask to a cmask, but it had no way to
write into the arena, so the cmask lived in kernel memory and was passed as
a trusted pointer. BPF cmask helpers all operate on arena cmasks though, so
the BPF side had to word-by-word probe-read the kernel cmask into an arena
cmask via cmask_copy_from_kernel() before any helper could touch it. It
works, but is clumsy.
The shape isn't unique to set_cmask. Sub-scheduler support is on the way and
more sched_ext callbacks will want to pass structured data to BPF. Anywhere
a kfunc or struct_ops callback wants to hand a struct to a BPF program,
arena residence is the natural answer.
Approach
--------
Each arena gets a per-arena scratch page. Arenas stay sparsely mapped as
today - PTEs are populated only for allocated pages. A new arch fault hook
(bpf_arena_handle_page_fault) is wired into x86 page_fault_oops() and arm64
__do_kernel_fault(), after KFENCE. When a kernel-side access faults inside
an arena's kern_vm range, the helper walks the stack to find the BPF program
responsible, range- checks the fault address against prog->aux->arena, and
atomically installs the scratch page into the empty PTE via the new
ptep_try_install() wrapper. The kernel instruction retries and reads/writes
the scratch page. Free paths and map destruction treat scratch as non-owned.
Real allocation refuses to overwrite scratch (apply_range_set_cb returns
-EBUSY); a scratched address stays dead until map destroy, since its
presence means the BPF program has already malfunctioned.
The mechanism is default behavior - no UAPI flag.
What this preserves
-------------------
All the debugging properties of today's sparse-PTE design are
preserved:
* BPF programs still fault on unmapped arena accesses. The fault semantics
(instruction retry with rdst = 0) and the violation report through
bpf_streams are unchanged for prog-side accesses.
* The first kernel-side touch of an unmapped address is reported via
bpf_streams the same way as a prog-side fault, with the stack walk
attributing it to the originating prog.
* User-side fault on a never-scratched address still lazy-allocates a real
page (or returns SIGSEGV under BPF_F_SEGV_ON_FAULT). User- side fault on a
scratched address SIGSEGVs.
What changes for the kernel-side caller is just that an unmapped deref no
longer oopses - it retries through the scratch page and emits a violation
report. The same shape today's BPF instruction faults have.
Patches 1-2 (atomic PTE install + arena scratch-page recovery)
--------------------------------------------------------------
mm: Add ptep_try_install() for lockless empty-slot installs
bpf: Recover arena kernel faults with scratch page
Patches 3-5 (helpers used by struct_ops registration)
-----------------------------------------------------
bpf: Add sleepable variant of bpf_arena_alloc_pages for kernel callers
bpf: Add bpf_struct_ops_for_each_prog()
bpf/arena: Add bpf_arena_map_kern_vm_start() and bpf_prog_arena()
Patches 6-8 (sched_ext: arena auto-discovery, allocator, set_cmask)
-------------------------------------------------------------------
sched_ext: Require an arena for cid-form schedulers
sched_ext: Sub-allocator over kernel-claimed BPF arena pages
sched_ext: Convert ops.set_cmask() to arena-resident cmask
Patch 6 reads each member prog's prog->aux->arena via bpf_prog_arena() and
requires the cid-form struct_ops to reference exactly one arena. Patch 7
builds a gen_pool sub-allocator inside that arena. Patch 8 converts
set_cmask() to write into arena memory; BPF dereferences via __arena like
any other arena struct, no probe-reads.
Base
----
sched_ext/for-7.2 (c9017d335aab) with cmask-prep-v2.1 applied:
https://lore.kernel.org/r/20260517183614.1191534-1-tj@kernel.org
Git tree: git://git.kernel.org/pub/scm/linux/kernel/git/tj/sched_ext.git arena-direct-v2
Documentation/bpf/kfuncs.rst | 14 +++
arch/arm64/include/asm/pgtable.h | 8 ++
arch/arm64/mm/fault.c | 10 +-
arch/x86/include/asm/pgtable.h | 8 ++
arch/x86/mm/fault.c | 12 +-
include/linux/bpf.h | 14 +++
include/linux/bpf_defs.h | 11 ++
include/linux/pgtable.h | 16 +++
kernel/bpf/arena.c | 209 ++++++++++++++++++++++++++++------
kernel/bpf/bpf_struct_ops.c | 36 ++++++
kernel/bpf/core.c | 5 +
kernel/sched/build_policy.c | 4 +
kernel/sched/ext.c | 135 +++++++++++++++++++++-
kernel/sched/ext_arena.c | 127 +++++++++++++++++++++
kernel/sched/ext_arena.h | 18 +++
kernel/sched/ext_cid.c | 20 +---
kernel/sched/ext_internal.h | 23 +++-
tools/sched_ext/include/scx/cid.bpf.h | 52 ---------
tools/sched_ext/scx_qmap.bpf.c | 5 +-
19 files changed, 604 insertions(+), 123 deletions(-)
Thanks.
--
tejun
^ permalink raw reply
* [PATCH 2/8] bpf: Recover arena kernel faults with scratch page
From: Tejun Heo @ 2026-05-17 21:12 UTC (permalink / raw)
To: David Vernet, Andrea Righi, Changwoo Min, Alexei Starovoitov,
Andrii Nakryiko, Daniel Borkmann, Martin KaFai Lau,
Kumar Kartikeya Dwivedi
Cc: Catalin Marinas, Will Deacon, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, Andrew Morton, David Hildenbrand,
Mike Rapoport, Emil Tsalapatis, sched-ext, bpf, x86,
linux-arm-kernel, linux-mm, linux-kernel, Tejun Heo
In-Reply-To: <20260517211232.1670594-1-tj@kernel.org>
From: Kumar Kartikeya Dwivedi <memxor@gmail.com>
BPF arena usage is becoming more prevalent, but kernel <-> BPF communication
over arena memory is awkward today. Data has to be staged through a trusted
kernel pointer with extra code and copying on the BPF side. While reads
through arena pointers can use a fault-safe helper, writes don't have a good
solution; the in-line alternative would need instruction emulation or asm
fixup labels.
Enable direct kernel-side reads and writes within GUARD_SZ / 2 of any
handed-in arena pointer, without bounds checking. A per-arena scratch page
is installed by the arch fault path into empty arena kernel PTEs - x86 from
page_fault_oops() for not-present faults, arm64 from __do_kernel_fault() for
translation faults, both after the existing exception-table and KFENCE
handling. The faulting instruction retries and the access is also reported
through the program's BPF stream, preserving error reporting.
bpf_prog_find_from_stack() resolves the current BPF program (and its arena)
from the kernel stack - no new bpf_run_ctx state is added. Recovery covers
the 4 GiB arena plus the upper half-guard (GUARD_SZ / 2); the lower
half-guard is excluded because well-behaved kfuncs only access forward from
arena pointers. The kfunc-author contract - access at most GUARD_SZ / 2 past
a handed-in pointer - is documented in Documentation/bpf/kfuncs.rst.
The install is lock-free via ptep_try_install(): on race-loss the winning
installer's PTE is already valid, so the access retry succeeds. No
flush_tlb_kernel_range() afterwards - stale "not mapped" entries just cause
one extra re-fault, cheaper than a global IPI on every install.
Scratch exists only to keep the kernel from oopsing on an in-line arena
access. Its presence at a PTE means the BPF program has already
malfunctioned, and the violation is reported through the program's BPF
stream. The only requirement for behavior on a scratched PTE is that the
kernel doesn't crash; in particular, any user-side access through such a PTE
may segfault. The shared scratch page is freed once during map destruction.
BPF instruction faults continue to use the existing JIT exception-table
path; this patch changes only the kernel-text fault path. No UAPI flag is
added; the new behavior is the default.
Suggested-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
---
Documentation/bpf/kfuncs.rst | 14 +++
arch/arm64/mm/fault.c | 10 ++-
arch/x86/mm/fault.c | 12 ++-
include/linux/bpf.h | 1 +
include/linux/bpf_defs.h | 11 +++
kernel/bpf/arena.c | 170 +++++++++++++++++++++++++++--------
kernel/bpf/core.c | 5 ++
7 files changed, 181 insertions(+), 42 deletions(-)
create mode 100644 include/linux/bpf_defs.h
diff --git a/Documentation/bpf/kfuncs.rst b/Documentation/bpf/kfuncs.rst
index 75e6c078e0e7..6d497e720998 100644
--- a/Documentation/bpf/kfuncs.rst
+++ b/Documentation/bpf/kfuncs.rst
@@ -462,6 +462,20 @@ In order to accommodate such requirements, the verifier will enforce strict
PTR_TO_BTF_ID type matching if two types have the exact same name, with one
being suffixed with ``___init``.
+2.8 Accessing arena memory through kfunc arguments
+--------------------------------------------------
+
+A read or write at any address inside an arena does not oops the kernel.
+Unallocated arena pages are lazily backed by a scratch page and the
+access is reported through the program's BPF stream as an error. Only
+the BPF program's correctness is affected; the kernel itself remains
+intact.
+
+The arena is followed by a ``GUARD_SZ / 2`` (32 KiB) guard region that
+is also covered by this recovery. A kfunc handed an arena pointer may
+therefore access up to ``GUARD_SZ / 2`` past it without bounds-checking
+against the arena. Larger accesses must verify the range explicitly.
+
.. _BPF_kfunc_lifecycle_expectations:
3. kfunc lifecycle expectations
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index 920a8b244d59..0d58d667fcd8 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -9,6 +9,7 @@
#include <linux/acpi.h>
#include <linux/bitfield.h>
+#include <linux/bpf_defs.h>
#include <linux/extable.h>
#include <linux/kfence.h>
#include <linux/signal.h>
@@ -416,9 +417,12 @@ static void __do_kernel_fault(unsigned long addr, unsigned long esr,
} else if (addr < PAGE_SIZE) {
msg = "NULL pointer dereference";
} else {
- if (esr_fsc_is_translation_fault(esr) &&
- kfence_handle_page_fault(addr, esr & ESR_ELx_WNR, regs))
- return;
+ if (esr_fsc_is_translation_fault(esr)) {
+ if (kfence_handle_page_fault(addr, esr & ESR_ELx_WNR, regs))
+ return;
+ if (bpf_arena_handle_page_fault(addr, esr & ESR_ELx_WNR, regs->pc))
+ return;
+ }
msg = "paging request";
}
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index f0e77e084482..b0f103ddbd23 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -8,6 +8,7 @@
#include <linux/sched/task_stack.h> /* task_stack_*(), ... */
#include <linux/kdebug.h> /* oops_begin/end, ... */
#include <linux/memblock.h> /* max_low_pfn */
+#include <linux/bpf_defs.h> /* bpf_arena_handle_page_fault */
#include <linux/kfence.h> /* kfence_handle_page_fault */
#include <linux/kprobes.h> /* NOKPROBE_SYMBOL, ... */
#include <linux/mmiotrace.h> /* kmmio_handler, ... */
@@ -688,10 +689,13 @@ page_fault_oops(struct pt_regs *regs, unsigned long error_code,
if (IS_ENABLED(CONFIG_EFI))
efi_crash_gracefully_on_page_fault(address);
- /* Only not-present faults should be handled by KFENCE. */
- if (!(error_code & X86_PF_PROT) &&
- kfence_handle_page_fault(address, error_code & X86_PF_WRITE, regs))
- return;
+ /* Only not-present faults should be handled by KFENCE or BPF arena. */
+ if (!(error_code & X86_PF_PROT)) {
+ if (kfence_handle_page_fault(address, error_code & X86_PF_WRITE, regs))
+ return;
+ if (bpf_arena_handle_page_fault(address, error_code & X86_PF_WRITE, regs->ip))
+ return;
+ }
oops:
/*
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 0136a108d083..831996c411cf 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -6,6 +6,7 @@
#include <uapi/linux/bpf.h>
#include <uapi/linux/filter.h>
+#include <linux/bpf_defs.h>
#include <crypto/sha2.h>
#include <linux/workqueue.h>
diff --git a/include/linux/bpf_defs.h b/include/linux/bpf_defs.h
new file mode 100644
index 000000000000..d98e033b8c0b
--- /dev/null
+++ b/include/linux/bpf_defs.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Subset of bpf.h declarations, split out so files that need only these
+ * declarations can avoid bpf.h's full include cost.
+ */
+#ifndef _LINUX_BPF_DEFS_H
+#define _LINUX_BPF_DEFS_H
+
+bool bpf_arena_handle_page_fault(unsigned long addr, bool is_write, unsigned long fault_ip);
+
+#endif /* _LINUX_BPF_DEFS_H */
diff --git a/kernel/bpf/arena.c b/kernel/bpf/arena.c
index 08d008cc471e..bc696bef7104 100644
--- a/kernel/bpf/arena.c
+++ b/kernel/bpf/arena.c
@@ -53,6 +53,7 @@ struct bpf_arena {
u64 user_vm_start;
u64 user_vm_end;
struct vm_struct *kern_vm;
+ struct page *scratch_page;
struct range_tree rt;
/* protects rt */
rqspinlock_t spinlock;
@@ -118,6 +119,11 @@ struct apply_range_data {
int i;
};
+struct clear_range_data {
+ struct llist_head *free_pages;
+ struct page *scratch_page;
+};
+
static int apply_range_set_cb(pte_t *pte, unsigned long addr, void *data)
{
struct apply_range_data *d = data;
@@ -144,8 +150,9 @@ static void flush_vmap_cache(unsigned long start, unsigned long size)
flush_cache_vmap(start, start + size);
}
-static int apply_range_clear_cb(pte_t *pte, unsigned long addr, void *free_pages)
+static int apply_range_clear_cb(pte_t *pte, unsigned long addr, void *data)
{
+ struct clear_range_data *d = data;
pte_t old_pte;
struct page *page;
@@ -160,17 +167,45 @@ static int apply_range_clear_cb(pte_t *pte, unsigned long addr, void *free_pages
pte_clear(&init_mm, addr, pte);
+ if (unlikely(!d))
+ return 0;
+
+ /*
+ * Skip the per-arena scratch page. A kernel fault on an unallocated uaddr
+ * scratches its PTE; a later bpf_arena_free_pages() over that range walks
+ * here. Without the skip, scratch_page would be freed.
+ */
+ if (page == d->scratch_page)
+ return 0;
+
/* Add page to the list so it is freed later */
- if (free_pages)
- __llist_add(&page->pcp_llist, free_pages);
+ __llist_add(&page->pcp_llist, d->free_pages);
+ return 0;
+}
+
+static int apply_range_set_scratch_cb(pte_t *pte, unsigned long addr, void *data)
+{
+ struct page *scratch_page = data;
+ if (!pte_none(ptep_get(pte)))
+ return 0;
+ /*
+ * Best-effort install. ptep_try_install() returns false only if another
+ * installer (real allocation or concurrent fault) won the cmpxchg; their
+ * PTE is already valid, so the access retry succeeds.
+ *
+ * No flush_tlb_kernel_range() needed: stale "not mapped" entries just
+ * cause one extra re-fault through this same path.
+ */
+ ptep_try_install(pte, mk_pte(scratch_page, PAGE_KERNEL));
return 0;
}
static int populate_pgtable_except_pte(struct bpf_arena *arena)
{
+ /* Populate intermediates for the recovery range (4 GiB + upper half-guard). */
return apply_to_page_range(&init_mm, bpf_arena_get_kern_vm_start(arena),
- KERN_VM_SZ - GUARD_SZ, apply_range_set_cb, NULL);
+ SZ_4G + GUARD_SZ / 2, apply_range_set_cb, NULL);
}
static struct bpf_map *arena_map_alloc(union bpf_attr *attr)
@@ -221,22 +256,29 @@ static struct bpf_map *arena_map_alloc(union bpf_attr *attr)
init_irq_work(&arena->free_irq, arena_free_irq);
INIT_WORK(&arena->free_work, arena_free_worker);
bpf_map_init_from_attr(&arena->map, attr);
+
+ err = bpf_map_alloc_pages(&arena->map, NUMA_NO_NODE, 1, &arena->scratch_page);
+ if (err)
+ goto err_free_arena;
+
range_tree_init(&arena->rt);
err = range_tree_set(&arena->rt, 0, attr->max_entries);
- if (err) {
- bpf_map_area_free(arena);
- goto err;
- }
+ if (err)
+ goto err_free_scratch;
mutex_init(&arena->lock);
raw_res_spin_lock_init(&arena->spinlock);
err = populate_pgtable_except_pte(arena);
- if (err) {
- range_tree_destroy(&arena->rt);
- bpf_map_area_free(arena);
- goto err;
- }
+ if (err)
+ goto err_destroy_rt;
return &arena->map;
+
+err_destroy_rt:
+ range_tree_destroy(&arena->rt);
+err_free_scratch:
+ __free_page(arena->scratch_page);
+err_free_arena:
+ bpf_map_area_free(arena);
err:
free_vm_area(kern_vm);
return ERR_PTR(err);
@@ -244,6 +286,7 @@ static struct bpf_map *arena_map_alloc(union bpf_attr *attr)
static int existing_page_cb(pte_t *ptep, unsigned long addr, void *data)
{
+ struct bpf_arena *arena = data;
struct page *page;
pte_t pte;
@@ -251,6 +294,12 @@ static int existing_page_cb(pte_t *ptep, unsigned long addr, void *data)
if (!pte_present(pte)) /* sanity check */
return 0;
page = pte_page(pte);
+ /*
+ * Skip the scratch page. The walk is page-table-driven, not range-tree-driven,
+ * so it can visit scratch PTEs at uaddrs the BPF program never allocated.
+ */
+ if (page == arena->scratch_page)
+ return 0;
/*
* We do not update pte here:
* 1. Nobody should be accessing bpf_arena's range outside of a kernel bug
@@ -286,9 +335,10 @@ static void arena_map_free(struct bpf_map *map)
* free those pages.
*/
apply_to_existing_page_range(&init_mm, bpf_arena_get_kern_vm_start(arena),
- KERN_VM_SZ - GUARD_SZ, existing_page_cb, NULL);
+ SZ_4G + GUARD_SZ / 2, existing_page_cb, arena);
free_vm_area(arena->kern_vm);
range_tree_destroy(&arena->rt);
+ __free_page(arena->scratch_page);
bpf_map_area_free(arena);
}
@@ -374,33 +424,37 @@ static vm_fault_t arena_vm_fault(struct vm_fault *vmf)
return VM_FAULT_RETRY;
page = vmalloc_to_page((void *)kaddr);
- if (page)
+ if (page) {
+ if (page == arena->scratch_page)
+ /* BPF triggered scratch here; don't lazy-alloc over it */
+ goto out_sigsegv;
/* already have a page vmap-ed */
goto out;
+ }
bpf_map_memcg_enter(&arena->map, &old_memcg, &new_memcg);
if (arena->map.map_flags & BPF_F_SEGV_ON_FAULT)
/* User space requested to segfault when page is not allocated by bpf prog */
- goto out_unlock_sigsegv;
+ goto out_sigsegv_memcg;
ret = range_tree_clear(&arena->rt, vmf->pgoff, 1);
if (ret)
- goto out_unlock_sigsegv;
+ goto out_sigsegv_memcg;
struct apply_range_data data = { .pages = &page, .i = 0 };
/* Account into memcg of the process that created bpf_arena */
ret = bpf_map_alloc_pages(map, NUMA_NO_NODE, 1, &page);
if (ret) {
range_tree_set(&arena->rt, vmf->pgoff, 1);
- goto out_unlock_sigsegv;
+ goto out_sigsegv_memcg;
}
ret = apply_to_page_range(&init_mm, kaddr, PAGE_SIZE, apply_range_set_cb, &data);
if (ret) {
range_tree_set(&arena->rt, vmf->pgoff, 1);
free_pages_nolock(page, 0);
- goto out_unlock_sigsegv;
+ goto out_sigsegv_memcg;
}
flush_vmap_cache(kaddr, PAGE_SIZE);
bpf_map_memcg_exit(old_memcg, new_memcg);
@@ -409,8 +463,9 @@ static vm_fault_t arena_vm_fault(struct vm_fault *vmf)
raw_res_spin_unlock_irqrestore(&arena->spinlock, flags);
vmf->page = page;
return 0;
-out_unlock_sigsegv:
+out_sigsegv_memcg:
bpf_map_memcg_exit(old_memcg, new_memcg);
+out_sigsegv:
raw_res_spin_unlock_irqrestore(&arena->spinlock, flags);
return VM_FAULT_SIGSEGV;
}
@@ -668,6 +723,7 @@ static void arena_free_pages(struct bpf_arena *arena, long uaddr, long page_cnt,
struct llist_head free_pages;
struct llist_node *pos, *t;
struct arena_free_span *s;
+ struct clear_range_data cdata;
unsigned long flags;
int ret = 0;
@@ -696,9 +752,11 @@ static void arena_free_pages(struct bpf_arena *arena, long uaddr, long page_cnt,
range_tree_set(&arena->rt, pgoff, page_cnt);
init_llist_head(&free_pages);
+ cdata.free_pages = &free_pages;
+ cdata.scratch_page = arena->scratch_page;
/* clear ptes and collect struct pages */
apply_to_existing_page_range(&init_mm, kaddr, page_cnt << PAGE_SHIFT,
- apply_range_clear_cb, &free_pages);
+ apply_range_clear_cb, &cdata);
/* drop the lock to do the tlb flush and zap pages */
raw_res_spin_unlock_irqrestore(&arena->spinlock, flags);
@@ -788,6 +846,7 @@ static void arena_free_worker(struct work_struct *work)
struct arena_free_span *s;
u64 arena_vm_start, user_vm_start;
struct llist_head free_pages;
+ struct clear_range_data cdata;
struct page *page;
unsigned long full_uaddr;
long kaddr, page_cnt, pgoff;
@@ -801,6 +860,8 @@ static void arena_free_worker(struct work_struct *work)
bpf_map_memcg_enter(&arena->map, &old_memcg, &new_memcg);
init_llist_head(&free_pages);
+ cdata.free_pages = &free_pages;
+ cdata.scratch_page = arena->scratch_page;
arena_vm_start = bpf_arena_get_kern_vm_start(arena);
user_vm_start = bpf_arena_get_user_vm_start(arena);
@@ -813,7 +874,7 @@ static void arena_free_worker(struct work_struct *work)
/* clear ptes and collect pages in free_pages llist */
apply_to_existing_page_range(&init_mm, kaddr, page_cnt << PAGE_SHIFT,
- apply_range_clear_cb, &free_pages);
+ apply_range_clear_cb, &cdata);
range_tree_set(&arena->rt, pgoff, page_cnt);
}
@@ -928,23 +989,12 @@ static int __init kfunc_init(void)
}
late_initcall(kfunc_init);
-void bpf_prog_report_arena_violation(bool write, unsigned long addr, unsigned long fault_ip)
+static void __bpf_prog_report_arena_violation(struct bpf_prog *prog, bool write,
+ unsigned long addr, unsigned long fault_ip)
{
struct bpf_stream_stage ss;
- struct bpf_prog *prog;
u64 user_vm_start;
- /*
- * The RCU read lock is held to safely traverse the latch tree, but we
- * don't need its protection when accessing the prog, since it will not
- * disappear while we are handling the fault.
- */
- rcu_read_lock();
- prog = bpf_prog_ksym_find(fault_ip);
- rcu_read_unlock();
- if (!prog)
- return;
-
/* Use main prog for stream access */
prog = prog->aux->main_prog_aux->prog;
@@ -957,3 +1007,53 @@ void bpf_prog_report_arena_violation(bool write, unsigned long addr, unsigned lo
bpf_stream_dump_stack(ss);
}));
}
+
+bool bpf_arena_handle_page_fault(unsigned long addr, bool is_write, unsigned long fault_ip)
+{
+ struct bpf_arena *arena;
+ struct bpf_prog *prog;
+ unsigned long kbase;
+ unsigned long page_addr = addr & PAGE_MASK;
+
+ prog = bpf_prog_find_from_stack();
+ if (!prog)
+ return false;
+
+ arena = prog->aux->arena;
+ /* a prog not using arena may be on stack, so arena can be NULL */
+ if (!arena)
+ return false;
+
+ kbase = bpf_arena_get_kern_vm_start(arena);
+
+ /*
+ * Recovery covers the 4 GiB mappable band plus the upper half-guard.
+ * Lower guard is unreachable from kfuncs; an address there indicates
+ * a different bug class - leave it to the regular kernel oops path.
+ */
+ if (page_addr < kbase || page_addr >= kbase + SZ_4G + GUARD_SZ / 2)
+ return false;
+
+ apply_to_page_range(&init_mm, page_addr, PAGE_SIZE,
+ apply_range_set_scratch_cb, arena->scratch_page);
+ flush_vmap_cache(page_addr, PAGE_SIZE);
+ __bpf_prog_report_arena_violation(prog, is_write, page_addr - kbase, fault_ip);
+ return true;
+}
+
+void bpf_prog_report_arena_violation(bool write, unsigned long addr, unsigned long fault_ip)
+{
+ struct bpf_prog *prog;
+
+ /*
+ * The RCU read lock is held to safely traverse the latch tree, but we
+ * don't need its protection when accessing the prog, since it will not
+ * disappear while we are handling the fault.
+ */
+ rcu_read_lock();
+ prog = bpf_prog_ksym_find(fault_ip);
+ rcu_read_unlock();
+ if (!prog)
+ return;
+ __bpf_prog_report_arena_violation(prog, write, addr, fault_ip);
+}
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index 066b86e7233c..fa368d8920d9 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -3290,6 +3290,11 @@ __weak u64 bpf_arena_get_kern_vm_start(struct bpf_arena *arena)
{
return 0;
}
+__weak bool bpf_arena_handle_page_fault(unsigned long addr, bool is_write,
+ unsigned long fault_ip)
+{
+ return false;
+}
#ifdef CONFIG_BPF_SYSCALL
static int __init bpf_global_ma_init(void)
--
2.54.0
^ permalink raw reply related
* [PATCH 4/8] bpf: Add bpf_struct_ops_for_each_prog()
From: Tejun Heo @ 2026-05-17 21:12 UTC (permalink / raw)
To: David Vernet, Andrea Righi, Changwoo Min, Alexei Starovoitov,
Andrii Nakryiko, Daniel Borkmann, Martin KaFai Lau,
Kumar Kartikeya Dwivedi
Cc: Catalin Marinas, Will Deacon, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, Andrew Morton, David Hildenbrand,
Mike Rapoport, Emil Tsalapatis, sched-ext, bpf, x86,
linux-arm-kernel, linux-mm, linux-kernel, Tejun Heo
In-Reply-To: <20260517211232.1670594-1-tj@kernel.org>
Add a helper that walks the member progs of the struct_ops map
containing a given @kdata vmtable. struct_ops ->reg() callbacks (and
similar) sometimes need to inspect the loaded BPF programs, e.g. to
discover maps they reference via prog->aux->used_maps.
The implementation mirrors bpf_struct_ops_id(): container_of @kdata
to recover the bpf_struct_ops_map, then iterate st_map->links[i]->prog
for i in [0, funcs_cnt). Same access pattern, no new locking - by the
time ->reg() fires st_map is fully populated and stable.
A sched_ext follow-up walks the member progs of a cid-form scheduler's
struct_ops map, reads prog->aux->arena directly, and requires all member
progs to reference exactly one arena, without requiring the BPF program
to call a registration kfunc.
Signed-off-by: Tejun Heo <tj@kernel.org>
---
include/linux/bpf.h | 3 +++
kernel/bpf/bpf_struct_ops.c | 36 ++++++++++++++++++++++++++++++++++++
2 files changed, 39 insertions(+)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 64968ca6db51..5b99d786e98c 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -2129,6 +2129,9 @@ int bpf_prog_assoc_struct_ops(struct bpf_prog *prog, struct bpf_map *map);
void bpf_prog_disassoc_struct_ops(struct bpf_prog *prog);
void *bpf_prog_get_assoc_struct_ops(const struct bpf_prog_aux *aux);
u32 bpf_struct_ops_id(const void *kdata);
+int bpf_struct_ops_for_each_prog(const void *kdata,
+ int (*cb)(struct bpf_prog *prog, void *data),
+ void *data);
#ifdef CONFIG_NET
/* Define it here to avoid the use of forward declaration */
diff --git a/kernel/bpf/bpf_struct_ops.c b/kernel/bpf/bpf_struct_ops.c
index 05b366b821c3..16aec18ed31b 100644
--- a/kernel/bpf/bpf_struct_ops.c
+++ b/kernel/bpf/bpf_struct_ops.c
@@ -1203,6 +1203,42 @@ u32 bpf_struct_ops_id(const void *kdata)
}
EXPORT_SYMBOL_GPL(bpf_struct_ops_id);
+/**
+ * bpf_struct_ops_for_each_prog - Invoke @cb for each member prog
+ * @kdata: kernel-side struct_ops vmtable (the @kdata arg to ->reg/->update/->unreg)
+ * @cb: callback invoked once per member prog; non-zero return stops iteration
+ * @data: opaque argument passed to @cb
+ *
+ * Walks the struct_ops member progs registered on the map containing @kdata.
+ * Intended for use from struct_ops ->reg() callbacks (and similar) that need to
+ * inspect the loaded BPF programs (for example to discover maps they reference
+ * via @prog->aux->used_maps).
+ *
+ * Return 0 if iteration completed, otherwise the first non-zero @cb return.
+ */
+int bpf_struct_ops_for_each_prog(const void *kdata,
+ int (*cb)(struct bpf_prog *prog, void *data),
+ void *data)
+{
+ struct bpf_struct_ops_value *kvalue;
+ struct bpf_struct_ops_map *st_map;
+ u32 i;
+ int ret;
+
+ kvalue = container_of(kdata, struct bpf_struct_ops_value, data);
+ st_map = container_of(kvalue, struct bpf_struct_ops_map, kvalue);
+
+ for (i = 0; i < st_map->funcs_cnt; i++) {
+ if (!st_map->links[i])
+ continue;
+ ret = cb(st_map->links[i]->prog, data);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(bpf_struct_ops_for_each_prog);
+
static bool bpf_struct_ops_valid_to_reg(struct bpf_map *map)
{
struct bpf_struct_ops_map *st_map = (struct bpf_struct_ops_map *)map;
--
2.54.0
^ permalink raw reply related
* [PATCH 5/8] bpf/arena: Add bpf_arena_map_kern_vm_start() and bpf_prog_arena()
From: Tejun Heo @ 2026-05-17 21:12 UTC (permalink / raw)
To: David Vernet, Andrea Righi, Changwoo Min, Alexei Starovoitov,
Andrii Nakryiko, Daniel Borkmann, Martin KaFai Lau,
Kumar Kartikeya Dwivedi
Cc: Catalin Marinas, Will Deacon, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, Andrew Morton, David Hildenbrand,
Mike Rapoport, Emil Tsalapatis, sched-ext, bpf, x86,
linux-arm-kernel, linux-mm, linux-kernel, Tejun Heo
In-Reply-To: <20260517211232.1670594-1-tj@kernel.org>
struct bpf_arena is opaque to callers outside arena.c. Add two helpers
for struct_ops subsystems that need to reach into an arena:
bpf_arena_map_kern_vm_start(struct bpf_map *map)
returns @map's kern_vm_start. A sched_ext follow-up needs this
to translate kern_va <-> uaddr.
bpf_prog_arena(struct bpf_prog *prog)
returns the bpf_map of the arena referenced by @prog (NULL if
@prog references no arena). The verifier enforces at most one
arena per program. Used by struct_ops callers that auto-discover
an arena from a member prog and need to take a map reference.
Suggested-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
---
include/linux/bpf.h | 2 ++
kernel/bpf/arena.c | 26 ++++++++++++++++++++++++++
2 files changed, 28 insertions(+)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 5b99d786e98c..e1ba57c10aaa 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -618,6 +618,8 @@ void bpf_rb_root_free(const struct btf_field *field, void *rb_root,
struct bpf_spin_lock *spin_lock);
u64 bpf_arena_get_kern_vm_start(struct bpf_arena *arena);
u64 bpf_arena_get_user_vm_start(struct bpf_arena *arena);
+u64 bpf_arena_map_kern_vm_start(struct bpf_map *map);
+struct bpf_map *bpf_prog_arena(struct bpf_prog *prog);
int bpf_obj_name_cpy(char *dst, const char *src, unsigned int size);
struct bpf_offload_dev;
diff --git a/kernel/bpf/arena.c b/kernel/bpf/arena.c
index 8eb7b95f4999..5a82d39e1916 100644
--- a/kernel/bpf/arena.c
+++ b/kernel/bpf/arena.c
@@ -84,6 +84,32 @@ u64 bpf_arena_get_user_vm_start(struct bpf_arena *arena)
return arena ? arena->user_vm_start : 0;
}
+/**
+ * bpf_arena_map_kern_vm_start - kern_vm_start lookup by struct bpf_map *
+ * @map: a BPF_MAP_TYPE_ARENA map
+ *
+ * Return @map's kern_vm_start.
+ */
+u64 bpf_arena_map_kern_vm_start(struct bpf_map *map)
+{
+ return bpf_arena_get_kern_vm_start(container_of(map, struct bpf_arena, map));
+}
+
+/**
+ * bpf_prog_arena - return the bpf_map of the arena referenced by @prog
+ * @prog: a loaded BPF program
+ *
+ * The verifier enforces at most one arena per program and stores it in
+ * prog->aux->arena. Return that arena's underlying bpf_map, or NULL if
+ * @prog does not reference an arena.
+ */
+struct bpf_map *bpf_prog_arena(struct bpf_prog *prog)
+{
+ struct bpf_arena *arena = prog->aux->arena;
+
+ return arena ? &arena->map : NULL;
+}
+
static long arena_map_peek_elem(struct bpf_map *map, void *value)
{
return -EOPNOTSUPP;
--
2.54.0
^ permalink raw reply related
* [PATCH 7/8] sched_ext: Sub-allocator over kernel-claimed BPF arena pages
From: Tejun Heo @ 2026-05-17 21:12 UTC (permalink / raw)
To: David Vernet, Andrea Righi, Changwoo Min, Alexei Starovoitov,
Andrii Nakryiko, Daniel Borkmann, Martin KaFai Lau,
Kumar Kartikeya Dwivedi
Cc: Catalin Marinas, Will Deacon, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, Andrew Morton, David Hildenbrand,
Mike Rapoport, Emil Tsalapatis, sched-ext, bpf, x86,
linux-arm-kernel, linux-mm, linux-kernel, Tejun Heo
In-Reply-To: <20260517211232.1670594-1-tj@kernel.org>
Build a per-scheduler sub-allocator on top of pages claimed from the BPF
arena registered in the previous patch. Subsequent kernel-managed
arena-resident structures (e.g. per-CPU set_cmask cmask) carve their storage
from this pool.
scx_arena_pool_init() creates a gen_pool. scx_arena_alloc() returns the
kernel VA. On exhaustion, the pool grows by claiming more pages via
bpf_arena_alloc_pages_sleepable(). Chunks are added at the kernel-side
mapping address; callers translate to the BPF-arena form themselves if
needed.
Allocations sleep (GFP_KERNEL) - they may grow the pool through vzalloc and
arena page allocation. All current consumers run from the enable path (after
ops.init() and the kernel-side arena auto-discovery, before validate_ops()),
where sleeping is fine.
scx_arena_pool_destroy() walks each chunk, returns outstanding ranges to the
gen_pool with gen_pool_free() and then calls gen_pool_destroy(). The
underlying arena pages are released when the arena map itself is torn down,
so the pool destroy doesn't free them explicitly.
Signed-off-by: Tejun Heo <tj@kernel.org>
---
kernel/sched/build_policy.c | 4 ++
kernel/sched/ext.c | 11 ++++
kernel/sched/ext_arena.c | 127 ++++++++++++++++++++++++++++++++++++
kernel/sched/ext_arena.h | 18 +++++
kernel/sched/ext_internal.h | 5 ++
5 files changed, 165 insertions(+)
create mode 100644 kernel/sched/ext_arena.c
create mode 100644 kernel/sched/ext_arena.h
diff --git a/kernel/sched/build_policy.c b/kernel/sched/build_policy.c
index 5e76c9177d54..067979a7b69e 100644
--- a/kernel/sched/build_policy.c
+++ b/kernel/sched/build_policy.c
@@ -59,12 +59,16 @@
#ifdef CONFIG_SCHED_CLASS_EXT
# include <linux/btf_ids.h>
+# include <linux/find.h>
+# include <linux/genalloc.h>
# include "ext_types.h"
# include "ext_internal.h"
# include "ext_cid.h"
+# include "ext_arena.h"
# include "ext_idle.h"
# include "ext.c"
# include "ext_cid.c"
+# include "ext_arena.c"
# include "ext_idle.c"
#endif
diff --git a/kernel/sched/ext.c b/kernel/sched/ext.c
index 94aab7037329..3025fbe198d3 100644
--- a/kernel/sched/ext.c
+++ b/kernel/sched/ext.c
@@ -5009,6 +5009,7 @@ static void scx_sched_free_rcu_work(struct work_struct *work)
rhashtable_free_and_destroy(&sch->dsq_hash, NULL, NULL);
free_exit_info(sch->exit_info);
+ scx_arena_pool_destroy(sch);
if (sch->arena_map)
bpf_map_put(sch->arena_map);
kfree(sch);
@@ -7140,6 +7141,12 @@ static void scx_root_enable_workfn(struct kthread_work *work)
sch->exit_info->flags |= SCX_EFLAG_INITIALIZED;
}
+ ret = scx_arena_pool_init(sch);
+ if (ret) {
+ cpus_read_unlock();
+ goto err_disable;
+ }
+
for (i = SCX_OPI_CPU_HOTPLUG_BEGIN; i < SCX_OPI_CPU_HOTPLUG_END; i++)
if (((void (**)(void))ops)[i])
set_bit(i, sch->has_op);
@@ -7458,6 +7465,10 @@ static void scx_sub_enable_workfn(struct kthread_work *work)
sch->exit_info->flags |= SCX_EFLAG_INITIALIZED;
}
+ ret = scx_arena_pool_init(sch);
+ if (ret)
+ goto err_disable;
+
if (validate_ops(sch, ops))
goto err_disable;
diff --git a/kernel/sched/ext_arena.c b/kernel/sched/ext_arena.c
new file mode 100644
index 000000000000..53174033765d
--- /dev/null
+++ b/kernel/sched/ext_arena.c
@@ -0,0 +1,127 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * BPF extensible scheduler class: Documentation/scheduler/sched-ext.rst
+ *
+ * scx_arena_pool: kernel-side sub-allocator over BPF-arena pages.
+ *
+ * Each chunk added to @sch->arena_pool comes from one
+ * bpf_arena_alloc_pages_sleepable() call and is registered at the
+ * kernel-side mapping address. Callers translate to the BPF-arena form
+ * themselves if needed.
+ *
+ * Allocations grow the pool on demand. Underlying arena pages are released
+ * when the arena map itself is torn down.
+ *
+ * Copyright (c) 2026 Meta Platforms, Inc. and affiliates.
+ * Copyright (c) 2026 Tejun Heo <tj@kernel.org>
+ */
+
+enum scx_arena_consts {
+ SCX_ARENA_MIN_ORDER = 3, /* 8-byte minimum sub-allocation */
+ SCX_ARENA_GROW_PAGES = 4, /* per growth */
+};
+
+s32 scx_arena_pool_init(struct scx_sched *sch)
+{
+ if (!sch->arena_map)
+ return 0;
+
+ sch->arena_pool = gen_pool_create(SCX_ARENA_MIN_ORDER, NUMA_NO_NODE);
+ if (!sch->arena_pool)
+ return -ENOMEM;
+ return 0;
+}
+
+static void scx_arena_clear_chunk(struct gen_pool *pool, struct gen_pool_chunk *chunk,
+ void *data)
+{
+ int order = pool->min_alloc_order;
+ size_t chunk_sz = chunk->end_addr - chunk->start_addr + 1;
+ unsigned long end_bit = chunk_sz >> order;
+ unsigned long b, e;
+
+ for_each_set_bitrange(b, e, chunk->bits, end_bit)
+ gen_pool_free(pool, chunk->start_addr + (b << order),
+ (e - b) << order);
+}
+
+/*
+ * Tear down the pool. Outstanding gen_pool allocations are freed via
+ * scx_arena_clear_chunk() so gen_pool_destroy() doesn't BUG. The underlying
+ * arena pages are released when the arena map itself is torn down.
+ */
+void scx_arena_pool_destroy(struct scx_sched *sch)
+{
+ if (!sch->arena_pool)
+ return;
+ gen_pool_for_each_chunk(sch->arena_pool, scx_arena_clear_chunk, NULL);
+ gen_pool_destroy(sch->arena_pool);
+ sch->arena_pool = NULL;
+}
+
+/*
+ * Grow the pool by @page_cnt pages. bpf_arena_alloc_pages_sleepable() and
+ * gen_pool_add() (which calls vzalloc(GFP_KERNEL)) require a sleepable
+ * context.
+ */
+static int scx_arena_grow(struct scx_sched *sch, u32 page_cnt)
+{
+ u64 kern_vm_start;
+ u32 uaddr32;
+ void *p;
+ int ret;
+
+ if (!sch->arena_map || !sch->arena_pool)
+ return -EINVAL;
+
+ p = bpf_arena_alloc_pages_sleepable(sch->arena_map, NULL,
+ page_cnt, NUMA_NO_NODE, 0);
+ if (!p)
+ return -ENOMEM;
+
+ uaddr32 = (u32)(unsigned long)p;
+ kern_vm_start = bpf_arena_map_kern_vm_start(sch->arena_map);
+
+ ret = gen_pool_add(sch->arena_pool, kern_vm_start + uaddr32,
+ page_cnt * PAGE_SIZE, NUMA_NO_NODE);
+ if (ret) {
+ bpf_arena_free_pages_non_sleepable(sch->arena_map, p, page_cnt);
+ return ret;
+ }
+ return 0;
+}
+
+/*
+ * Allocate @size bytes from the arena pool. Returns kernel VA on success, NULL
+ * on failure. May grow the pool via scx_arena_grow() which sleeps. Caller must
+ * be in a GFP_KERNEL context.
+ */
+void *scx_arena_alloc(struct scx_sched *sch, size_t size)
+{
+ unsigned long kern_va;
+ u32 page_cnt;
+
+ might_sleep();
+
+ if (!sch->arena_pool)
+ return NULL;
+
+ kern_va = gen_pool_alloc(sch->arena_pool, size);
+ if (!kern_va) {
+ page_cnt = max_t(u32, SCX_ARENA_GROW_PAGES,
+ (size + PAGE_SIZE - 1) >> PAGE_SHIFT);
+ if (scx_arena_grow(sch, page_cnt))
+ return NULL;
+ kern_va = gen_pool_alloc(sch->arena_pool, size);
+ if (!kern_va)
+ return NULL;
+ }
+
+ return (void *)kern_va;
+}
+
+void scx_arena_free(struct scx_sched *sch, void *kern_va, size_t size)
+{
+ if (sch->arena_pool && kern_va)
+ gen_pool_free(sch->arena_pool, (unsigned long)kern_va, size);
+}
diff --git a/kernel/sched/ext_arena.h b/kernel/sched/ext_arena.h
new file mode 100644
index 000000000000..4f3610160102
--- /dev/null
+++ b/kernel/sched/ext_arena.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * BPF extensible scheduler class: Documentation/scheduler/sched-ext.rst
+ *
+ * Copyright (c) 2025 Meta Platforms, Inc. and affiliates.
+ * Copyright (c) 2025 Tejun Heo <tj@kernel.org>
+ */
+#ifndef _KERNEL_SCHED_EXT_ARENA_H
+#define _KERNEL_SCHED_EXT_ARENA_H
+
+struct scx_sched;
+
+s32 scx_arena_pool_init(struct scx_sched *sch);
+void scx_arena_pool_destroy(struct scx_sched *sch);
+void *scx_arena_alloc(struct scx_sched *sch, size_t size);
+void scx_arena_free(struct scx_sched *sch, void *kern_va, size_t size);
+
+#endif /* _KERNEL_SCHED_EXT_ARENA_H */
diff --git a/kernel/sched/ext_internal.h b/kernel/sched/ext_internal.h
index d40cfd29ddaa..ff7e882bd67a 100644
--- a/kernel/sched/ext_internal.h
+++ b/kernel/sched/ext_internal.h
@@ -1116,8 +1116,13 @@ struct scx_sched {
* Arena map auto-discovered from member progs at struct_ops attach.
* cid-form schedulers must use exactly one arena across all member
* progs. NULL on cpu-form.
+ *
+ * @arena_pool sub-allocates @arena_map. Each gen_pool chunk is added
+ * at the kernel-side mapping address. Grows on demand and pages are
+ * not released until sched destroy.
*/
struct bpf_map *arena_map;
+ struct gen_pool *arena_pool;
DECLARE_BITMAP(has_op, SCX_OPI_END);
--
2.54.0
^ permalink raw reply related
* [PATCH 8/8] sched_ext: Convert ops.set_cmask() to arena-resident cmask
From: Tejun Heo @ 2026-05-17 21:12 UTC (permalink / raw)
To: David Vernet, Andrea Righi, Changwoo Min, Alexei Starovoitov,
Andrii Nakryiko, Daniel Borkmann, Martin KaFai Lau,
Kumar Kartikeya Dwivedi
Cc: Catalin Marinas, Will Deacon, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, Andrew Morton, David Hildenbrand,
Mike Rapoport, Emil Tsalapatis, sched-ext, bpf, x86,
linux-arm-kernel, linux-mm, linux-kernel, Tejun Heo
In-Reply-To: <20260517211232.1670594-1-tj@kernel.org>
ops_cid.set_cmask() expects a cmask. The kernel couldn't write into the
arena, so it translated cpumask -> cmask in kernel memory and passed the
result as a trusted pointer. The BPF cmask helpers all operate on arena
cmasks though, so the BPF side had to word-by-word probe-read the kernel
cmask into an arena cmask via cmask_copy_from_kernel() before any helper
could touch it. It works, but is clumsy.
With direct kernel-side arena access now in place, build the cmask in the
arena. The kernel writes to it through the kern_va side of the dual mapping;
BPF directly dereferences it via an __arena pointer like any other arena
struct.
Signed-off-by: Tejun Heo <tj@kernel.org>
---
kernel/sched/ext.c | 68 +++++++++++++++++++++++++--
kernel/sched/ext_cid.c | 20 +-------
kernel/sched/ext_internal.h | 10 +++-
tools/sched_ext/include/scx/cid.bpf.h | 52 --------------------
tools/sched_ext/scx_qmap.bpf.c | 5 +-
5 files changed, 75 insertions(+), 80 deletions(-)
diff --git a/kernel/sched/ext.c b/kernel/sched/ext.c
index 3025fbe198d3..1369dc7e4b4e 100644
--- a/kernel/sched/ext.c
+++ b/kernel/sched/ext.c
@@ -621,11 +621,16 @@ static inline void scx_call_op_set_cpumask(struct scx_sched *sch, struct rq *rq,
update_locked_rq(rq);
if (scx_is_cid_type()) {
- struct scx_cmask *cmask = this_cpu_ptr(scx_set_cmask_scratch);
-
- lockdep_assert_irqs_disabled();
- scx_cpumask_to_cmask(cpumask, cmask);
- sch->ops_cid.set_cmask(task, cmask);
+ struct scx_cmask *kern_va = *this_cpu_ptr(sch->set_cmask_scratch);
+ unsigned long uaddr = (unsigned long)kern_va -
+ bpf_arena_map_kern_vm_start(sch->arena_map);
+ /*
+ * Build the per-CPU arena cmask and hand BPF the uaddr. Caller
+ * holds the rq lock with IRQs disabled, which makes us the sole
+ * user of the scratch area.
+ */
+ scx_cpumask_to_cmask(cpumask, kern_va);
+ sch->ops_cid.set_cmask(task, (struct scx_cmask *)uaddr);
} else {
sch->ops.set_cpumask(task, cpumask);
}
@@ -4957,6 +4962,48 @@ static const struct attribute_group scx_global_attr_group = {
static void free_pnode(struct scx_sched_pnode *pnode);
static void free_exit_info(struct scx_exit_info *ei);
+static s32 scx_set_cmask_scratch_alloc(struct scx_sched *sch)
+{
+ size_t size = struct_size_t(struct scx_cmask, bits,
+ SCX_CMASK_NR_WORDS(num_possible_cpus()));
+ int cpu;
+
+ if (!sch->is_cid_type || !sch->arena_pool)
+ return 0;
+
+ sch->set_cmask_scratch = alloc_percpu(struct scx_cmask *);
+ if (!sch->set_cmask_scratch)
+ return -ENOMEM;
+
+ for_each_possible_cpu(cpu) {
+ struct scx_cmask **slot = per_cpu_ptr(sch->set_cmask_scratch, cpu);
+
+ *slot = scx_arena_alloc(sch, size);
+ if (!*slot)
+ return -ENOMEM;
+ scx_cmask_init(*slot, 0, num_possible_cpus());
+ }
+ return 0;
+}
+
+static void scx_set_cmask_scratch_free(struct scx_sched *sch)
+{
+ size_t size = struct_size_t(struct scx_cmask, bits,
+ SCX_CMASK_NR_WORDS(num_possible_cpus()));
+ int cpu;
+
+ if (!sch->set_cmask_scratch)
+ return;
+
+ for_each_possible_cpu(cpu) {
+ struct scx_cmask **slot = per_cpu_ptr(sch->set_cmask_scratch, cpu);
+
+ scx_arena_free(sch, *slot, size);
+ }
+ free_percpu(sch->set_cmask_scratch);
+ sch->set_cmask_scratch = NULL;
+}
+
static void scx_sched_free_rcu_work(struct work_struct *work)
{
struct rcu_work *rcu_work = to_rcu_work(work);
@@ -5009,6 +5056,7 @@ static void scx_sched_free_rcu_work(struct work_struct *work)
rhashtable_free_and_destroy(&sch->dsq_hash, NULL, NULL);
free_exit_info(sch->exit_info);
+ scx_set_cmask_scratch_free(sch);
scx_arena_pool_destroy(sch);
if (sch->arena_map)
bpf_map_put(sch->arena_map);
@@ -7147,6 +7195,12 @@ static void scx_root_enable_workfn(struct kthread_work *work)
goto err_disable;
}
+ ret = scx_set_cmask_scratch_alloc(sch);
+ if (ret) {
+ cpus_read_unlock();
+ goto err_disable;
+ }
+
for (i = SCX_OPI_CPU_HOTPLUG_BEGIN; i < SCX_OPI_CPU_HOTPLUG_END; i++)
if (((void (**)(void))ops)[i])
set_bit(i, sch->has_op);
@@ -7469,6 +7523,10 @@ static void scx_sub_enable_workfn(struct kthread_work *work)
if (ret)
goto err_disable;
+ ret = scx_set_cmask_scratch_alloc(sch);
+ if (ret)
+ goto err_disable;
+
if (validate_ops(sch, ops))
goto err_disable;
diff --git a/kernel/sched/ext_cid.c b/kernel/sched/ext_cid.c
index 5cd14143f88f..245a39e2e5eb 100644
--- a/kernel/sched/ext_cid.c
+++ b/kernel/sched/ext_cid.c
@@ -7,14 +7,6 @@
*/
#include <linux/cacheinfo.h>
-/*
- * Per-cpu scratch cmask used by scx_call_op_set_cpumask() to synthesize a
- * cmask from a cpumask. Allocated alongside the cid arrays on first enable
- * and never freed. Sized to the full cid space. Caller holds rq lock so
- * this_cpu_ptr is safe.
- */
-struct scx_cmask __percpu *scx_set_cmask_scratch;
-
/*
* cid tables.
*
@@ -54,8 +46,6 @@ static s32 scx_cid_arrays_alloc(void)
u32 npossible = num_possible_cpus();
s16 *cid_to_cpu, *cpu_to_cid;
struct scx_cid_topo *cid_topo;
- struct scx_cmask __percpu *set_cmask_scratch;
- s32 cpu;
if (scx_cid_to_cpu_tbl)
return 0;
@@ -63,25 +53,17 @@ static s32 scx_cid_arrays_alloc(void)
cid_to_cpu = kzalloc_objs(*scx_cid_to_cpu_tbl, npossible, GFP_KERNEL);
cpu_to_cid = kzalloc_objs(*scx_cpu_to_cid_tbl, nr_cpu_ids, GFP_KERNEL);
cid_topo = kmalloc_objs(*scx_cid_topo, npossible, GFP_KERNEL);
- set_cmask_scratch = __alloc_percpu(struct_size(set_cmask_scratch, bits,
- SCX_CMASK_NR_WORDS(npossible)),
- sizeof(u64));
- if (!cid_to_cpu || !cpu_to_cid || !cid_topo || !set_cmask_scratch) {
+ if (!cid_to_cpu || !cpu_to_cid || !cid_topo) {
kfree(cid_to_cpu);
kfree(cpu_to_cid);
kfree(cid_topo);
- free_percpu(set_cmask_scratch);
return -ENOMEM;
}
WRITE_ONCE(scx_cid_to_cpu_tbl, cid_to_cpu);
WRITE_ONCE(scx_cpu_to_cid_tbl, cpu_to_cid);
WRITE_ONCE(scx_cid_topo, cid_topo);
- for_each_possible_cpu(cpu)
- scx_cmask_init(per_cpu_ptr(set_cmask_scratch, cpu),
- 0, npossible);
- WRITE_ONCE(scx_set_cmask_scratch, set_cmask_scratch);
return 0;
}
diff --git a/kernel/sched/ext_internal.h b/kernel/sched/ext_internal.h
index ff7e882bd67a..9bb65367f510 100644
--- a/kernel/sched/ext_internal.h
+++ b/kernel/sched/ext_internal.h
@@ -1124,6 +1124,14 @@ struct scx_sched {
struct bpf_map *arena_map;
struct gen_pool *arena_pool;
+ /*
+ * Per-CPU arena cmask used by scx_call_op_set_cpumask() to hand a cmask
+ * to ops_cid.set_cmask(). The kernel writes through the stored kern_va;
+ * the BPF-arena uaddr handed to BPF is recovered by subtracting the
+ * arena's kern_vm_start.
+ */
+ struct scx_cmask * __percpu *set_cmask_scratch;
+
DECLARE_BITMAP(has_op, SCX_OPI_END);
/*
@@ -1480,8 +1488,6 @@ enum scx_ops_state {
extern struct scx_sched __rcu *scx_root;
DECLARE_PER_CPU(struct rq *, scx_locked_rq_state);
-extern struct scx_cmask __percpu *scx_set_cmask_scratch;
-
/*
* True when the currently loaded scheduler hierarchy is cid-form. All scheds
* in a hierarchy share one form, so this single key tells callsites which
diff --git a/tools/sched_ext/include/scx/cid.bpf.h b/tools/sched_ext/include/scx/cid.bpf.h
index 257d8bdca966..875003f04bdc 100644
--- a/tools/sched_ext/include/scx/cid.bpf.h
+++ b/tools/sched_ext/include/scx/cid.bpf.h
@@ -669,56 +669,4 @@ static __always_inline void cmask_from_cpumask(struct scx_cmask __arena *m,
}
}
-/**
- * cmask_copy_from_kernel - probe-read a kernel cmask into an arena cmask
- * @dst: arena cmask to fill; must have @dst->base == 0 and be sized for @src.
- * @src: kernel-memory cmask (e.g. ops.set_cmask() arg); @src->base must be 0.
- *
- * Word-for-word copy; @src and @dst must share base 0 alignment. Triggers
- * scx_bpf_error() on probe failure or precondition violation.
- */
-static __always_inline void cmask_copy_from_kernel(struct scx_cmask __arena *dst,
- const struct scx_cmask *src)
-{
- u32 base = 0, nr_cids = 0, nr_words, wi;
-
- if (dst->base != 0) {
- scx_bpf_error("cmask_copy_from_kernel requires dst->base == 0");
- return;
- }
-
- if (bpf_probe_read_kernel(&base, sizeof(base), &src->base)) {
- scx_bpf_error("probe-read cmask->base failed");
- return;
- }
- if (base != 0) {
- scx_bpf_error("cmask_copy_from_kernel requires src->base == 0");
- return;
- }
-
- if (bpf_probe_read_kernel(&nr_cids, sizeof(nr_cids), &src->nr_cids)) {
- scx_bpf_error("probe-read cmask->nr_cids failed");
- return;
- }
-
- if (nr_cids > dst->nr_cids) {
- scx_bpf_error("src cmask nr_cids=%u exceeds dst nr_cids=%u",
- nr_cids, dst->nr_cids);
- return;
- }
-
- nr_words = CMASK_NR_WORDS(nr_cids);
- cmask_zero(dst);
- bpf_for(wi, 0, CMASK_MAX_WORDS) {
- u64 word = 0;
- if (wi >= nr_words)
- break;
- if (bpf_probe_read_kernel(&word, sizeof(u64), &src->bits[wi])) {
- scx_bpf_error("probe-read cmask->bits[%u] failed", wi);
- return;
- }
- dst->bits[wi] = word;
- }
-}
-
#endif /* __SCX_CID_BPF_H */
diff --git a/tools/sched_ext/scx_qmap.bpf.c b/tools/sched_ext/scx_qmap.bpf.c
index 7e77f22674ea..8a2d6a8ebd8e 100644
--- a/tools/sched_ext/scx_qmap.bpf.c
+++ b/tools/sched_ext/scx_qmap.bpf.c
@@ -919,14 +919,15 @@ void BPF_STRUCT_OPS(qmap_update_idle, s32 cid, bool idle)
}
void BPF_STRUCT_OPS(qmap_set_cmask, struct task_struct *p,
- const struct scx_cmask *cmask)
+ const struct scx_cmask *cmask_in)
{
+ struct scx_cmask __arena *cmask = (struct scx_cmask __arena *)(long)cmask_in;
task_ctx_t *taskc;
taskc = lookup_task_ctx(p);
if (!taskc)
return;
- cmask_copy_from_kernel(&taskc->cpus_allowed, cmask);
+ cmask_copy(&taskc->cpus_allowed, cmask);
}
struct monitor_timer {
--
2.54.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