* [PATCH v2 01/13] s390/chsc: Use kzalloc() for CUBs
2026-09-10 11:00 [PATCH v2 00/13] s390/cio: replace page allocator calls with k[mz]alloc() Mike Rapoport (Microsoft)
@ 2026-09-10 11:00 ` Mike Rapoport (Microsoft)
2026-09-10 11:11 ` sashiko-bot
2026-09-10 11:00 ` [PATCH v2 02/13] s390/chsc: Use kzalloc() for the SEI work area Mike Rapoport (Microsoft)
` (11 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Mike Rapoport (Microsoft) @ 2026-09-10 11:00 UTC (permalink / raw)
To: Heiko Carstens, Vasily Gorbik, Alexander Gordeev
Cc: Christian Borntraeger, Mike Rapoport, Peter Oberparleiter,
Sven Schnelle, Vineeth Vijayan, Vlastimil Babka, linux-s390,
linux-kernel, linux-mm
cub_alloc() allocates the channel measurement unit blocks and their
extended counterparts.
This memory can be allocated with kmalloc() as there's nothing special
about it to go directly to the page allocator.
kmalloc() provides a better API that does not require ugly casts and
kfree() does not need to know the size of the freed object.
Performance difference between kmalloc() and __get_free_pages() is not
measurable as both allocators take an object/page from a per-CPU list for
fast path allocations.
For the slow path the performance is anyway determined by the amount of
reclaim involved rather than by what allocator is used.
Replace use of get_zeroed_page() with kzalloc() and free_page() with
kfree().
Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com
Assisted-by: copilot:claude-opus
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
drivers/s390/cio/chsc.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 9689f722c863c..60a4a7e6086ce 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -931,12 +931,12 @@ static int cub_alloc(struct channel_subsystem *css)
int i;
for (i = 0; i < CSS_NUM_CUB_PAGES; i++) {
- css->cub[i] = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ css->cub[i] = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!css->cub[i])
return -ENOMEM;
}
for (i = 0; i < CSS_NUM_ECUB_PAGES; i++) {
- css->ecub[i] = (void *)get_zeroed_page(GFP_KERNEL);
+ css->ecub[i] = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!css->ecub[i])
return -ENOMEM;
}
@@ -949,11 +949,11 @@ static void cub_free(struct channel_subsystem *css)
int i;
for (i = 0; i < CSS_NUM_CUB_PAGES; i++) {
- free_page((unsigned long)css->cub[i]);
+ kfree(css->cub[i]);
css->cub[i] = NULL;
}
for (i = 0; i < CSS_NUM_ECUB_PAGES; i++) {
- free_page((unsigned long)css->ecub[i]);
+ kfree(css->ecub[i]);
css->ecub[i] = NULL;
}
}
--
2.53.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [PATCH v2 01/13] s390/chsc: Use kzalloc() for CUBs
2026-09-10 11:00 ` [PATCH v2 01/13] s390/chsc: Use kzalloc() for CUBs Mike Rapoport (Microsoft)
@ 2026-09-10 11:11 ` sashiko-bot
0 siblings, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-09-10 11:11 UTC (permalink / raw)
To: Mike Rapoport (Microsoft)
Cc: Vasily Gorbik, Alexander Gordeev, Heiko Carstens,
Christian Borntraeger, linux-s390
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [High] Replacing get_zeroed_page() with kzalloc() for channel measurement unit blocks (CUB/ECUB) removes the strict 4K page-alignment guarantee required by the s390 hardware.
Pre-existing issues:
- [High] Hardware DMA Use-After-Free during error rollback if the SECM disable command fails.
--
commit a4009c99c45aacf3c6ede46b7f75f80186fc50f9
Author: Mike Rapoport (Microsoft) <rppt@kernel.org>
s390/chsc: Use kzalloc() for CUBs
This commit replaces the use of get_zeroed_page() and free_page() with
kzalloc() and kfree() for allocating channel measurement unit blocks (CUBs
and ECUBs). The author notes that kmalloc() provides a better API without
requiring casts and that performance differences are negligible for these
allocations.
Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com
> diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
> index 9689f722c863c..60a4a7e6086ce 100644
> --- a/drivers/s390/cio/chsc.c
> +++ b/drivers/s390/cio/chsc.c
> @@ -931,12 +931,12 @@ static int cub_alloc(struct channel_subsystem *css)
> int i;
>
> for (i = 0; i < CSS_NUM_CUB_PAGES; i++) {
> - css->cub[i] = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
> + css->cub[i] = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
[Severity: High]
Does replacing get_zeroed_page() with kzalloc() break the strict 4K
page-alignment required for the CUB blocks?
The s390 hardware SECM command expects the physical addresses of the CUB
measurement blocks to be strictly 4KB page-aligned. When KASAN or SLUB
debugging is enabled, kzalloc(PAGE_SIZE) offsets the returned pointer with
redzones, which destroys page alignment.
When this unaligned virtual address is passed to virt_to_dma32() in
__chsc_do_secm(), it yields an unaligned physical address. Could this cause
the hardware to reject the command or write 4KB of measurement data to the
unaligned boundary, potentially corrupting SLUB metadata or adjacent objects?
> if (!css->cub[i])
> return -ENOMEM;
> }
> for (i = 0; i < CSS_NUM_ECUB_PAGES; i++) {
> - css->ecub[i] = (void *)get_zeroed_page(GFP_KERNEL);
> + css->ecub[i] = kzalloc(PAGE_SIZE, GFP_KERNEL);
[Severity: High]
Similarly, does kzalloc() remove the strict page alignment guarantee for the
ECUB blocks?
Like the CUB blocks, if SLUB debugging offsets the pointer, the physical
address sent to the hardware will be unaligned.
> if (!css->ecub[i])
> return -ENOMEM;
> }
> @@ -949,11 +949,11 @@ static void cub_free(struct channel_subsystem *css)
> int i;
>
> for (i = 0; i < CSS_NUM_CUB_PAGES; i++) {
> - free_page((unsigned long)css->cub[i]);
> + kfree(css->cub[i]);
[Severity: High]
This isn't a bug introduced by this patch, but can this lead to a DMA
use-after-free if the SECM disable command fails?
Looking at the error rollback path in chsc_secm(), if enabling SECM succeeds
but chsc_add_cmg_attr() fails, it attempts to rollback with
__chsc_do_secm(css, 0):
drivers/s390/cio/chsc.c:chsc_secm() {
...
if (css->cm_enabled) {
ret = chsc_add_cmg_attr(css);
if (ret) {
__chsc_do_secm(css, 0);
css->cm_enabled = 0;
}
}
...
out:
if (!css->cm_enabled)
cub_free(css);
...
}
The return value of __chsc_do_secm(css, 0) is ignored. If the hardware disable
command fails (e.g., due to a timeout or busy state), the hardware remains
active and will continue writing measurement data to these DMA buffers.
However, the code unconditionally sets css->cm_enabled = 0, which causes the
out label to call cub_free(css). Could this free actively-used DMA buffers
back to the allocator and cause silent memory corruption for any subsequent
user of this memory?
> css->cub[i] = NULL;
> }
> for (i = 0; i < CSS_NUM_ECUB_PAGES; i++) {
> - free_page((unsigned long)css->ecub[i]);
> + kfree(css->ecub[i]);
> css->ecub[i] = NULL;
> }
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260910-s390-cio-ready-v2-0-e931086fdde1@kernel.org?part=1
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v2 02/13] s390/chsc: Use kzalloc() for the SEI work area
2026-09-10 11:00 [PATCH v2 00/13] s390/cio: replace page allocator calls with k[mz]alloc() Mike Rapoport (Microsoft)
2026-09-10 11:00 ` [PATCH v2 01/13] s390/chsc: Use kzalloc() for CUBs Mike Rapoport (Microsoft)
@ 2026-09-10 11:00 ` Mike Rapoport (Microsoft)
2026-09-10 11:12 ` sashiko-bot
2026-09-10 11:00 ` [PATCH v2 03/13] s390/chsc_sch: Use kzalloc() for CHSC requests Mike Rapoport (Microsoft)
` (10 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Mike Rapoport (Microsoft) @ 2026-09-10 11:00 UTC (permalink / raw)
To: Heiko Carstens, Vasily Gorbik, Alexander Gordeev
Cc: Christian Borntraeger, Mike Rapoport, Peter Oberparleiter,
Sven Schnelle, Vineeth Vijayan, Vlastimil Babka, linux-s390,
linux-kernel, linux-mm
chsc_init() allocates the work area for store event information data.
This memory can be allocated with kmalloc() as there's nothing special
about it to go directly to the page allocator.
kmalloc() provides a better API that does not require ugly casts and
kfree() does not need to know the size of the freed object.
Performance difference between kmalloc() and __get_free_pages() is not
measurable as both allocators take an object/page from a per-CPU list for
fast path allocations.
For the slow path the performance is anyway determined by the amount of
reclaim involved rather than by what allocator is used.
Replace use of get_zeroed_page() with kzalloc() and free_page() with
kfree().
Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com
Assisted-by: copilot:claude-opus
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
drivers/s390/cio/chsc.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 60a4a7e6086ce..c3186c0a372b4 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -1142,7 +1142,7 @@ int __init chsc_init(void)
{
int ret;
- sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ sei_page = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
chsc_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!sei_page || !chsc_page) {
ret = -ENOMEM;
@@ -1154,7 +1154,7 @@ int __init chsc_init(void)
return ret;
out_err:
free_page((unsigned long)chsc_page);
- free_page((unsigned long)sei_page);
+ kfree(sei_page);
return ret;
}
@@ -1162,7 +1162,7 @@ void __init chsc_init_cleanup(void)
{
crw_unregister_handler(CRW_RSC_CSS);
free_page((unsigned long)chsc_page);
- free_page((unsigned long)sei_page);
+ kfree(sei_page);
}
int __chsc_enable_facility(struct chsc_sda_area *sda_area, int operation_code)
--
2.53.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* [PATCH v2 03/13] s390/chsc_sch: Use kzalloc() for CHSC requests
2026-09-10 11:00 [PATCH v2 00/13] s390/cio: replace page allocator calls with k[mz]alloc() Mike Rapoport (Microsoft)
2026-09-10 11:00 ` [PATCH v2 01/13] s390/chsc: Use kzalloc() for CUBs Mike Rapoport (Microsoft)
2026-09-10 11:00 ` [PATCH v2 02/13] s390/chsc: Use kzalloc() for the SEI work area Mike Rapoport (Microsoft)
@ 2026-09-10 11:00 ` Mike Rapoport (Microsoft)
2026-09-10 11:10 ` sashiko-bot
2026-09-10 11:00 ` [PATCH v2 04/13] s390/chsc_sch: Use __free(kfree) for synchronous " Mike Rapoport (Microsoft)
` (9 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Mike Rapoport (Microsoft) @ 2026-09-10 11:00 UTC (permalink / raw)
To: Heiko Carstens, Vasily Gorbik, Alexander Gordeev
Cc: Christian Borntraeger, Mike Rapoport, Peter Oberparleiter,
Sven Schnelle, Vineeth Vijayan, Vlastimil Babka, linux-s390,
linux-kernel, linux-mm
The CHSC ioctl handlers allocate request and response areas for
CHSC commands issued on behalf of userspace.
This memory can be allocated with kmalloc() as there's nothing special
about it to go directly to the page allocator.
kmalloc() provides a better API that does not require ugly casts and
kfree() does not need to know the size of the freed object.
Performance difference between kmalloc() and __get_free_pages() is not
measurable as both allocators take an object/page from a per-CPU list for
fast path allocations.
For the slow path the performance is anyway determined by the amount of
reclaim involved rather than by what allocator is used.
Replace use of get_zeroed_page() with kzalloc() and free_page() with
kfree().
Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com
Assisted-by: copilot:claude-opus
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
drivers/s390/cio/chsc_sch.c | 44 ++++++++++++++++++++++----------------------
1 file changed, 22 insertions(+), 22 deletions(-)
diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c
index b6cb8bb8bcc4b..c8d6871d1841e 100644
--- a/drivers/s390/cio/chsc_sch.c
+++ b/drivers/s390/cio/chsc_sch.c
@@ -292,7 +292,7 @@ static int chsc_ioctl_start(void __user *user_area)
if (!css_general_characteristics.dynio)
/* It makes no sense to try. */
return -EOPNOTSUPP;
- chsc_area = (void *)get_zeroed_page(GFP_DMA | GFP_KERNEL);
+ chsc_area = kzalloc(PAGE_SIZE, GFP_DMA | GFP_KERNEL);
if (!chsc_area)
return -ENOMEM;
request = kzalloc_obj(*request);
@@ -321,7 +321,7 @@ static int chsc_ioctl_start(void __user *user_area)
snprintf(dbf, sizeof(dbf), "ret:%d", ret);
CHSC_LOG(0, dbf);
kfree(request);
- free_page((unsigned long)chsc_area);
+ kfree(chsc_area);
return ret;
}
@@ -340,7 +340,7 @@ static int chsc_ioctl_on_close_set(void __user *user_area)
ret = -ENOMEM;
goto out_unlock;
}
- on_close_chsc_area = (void *)get_zeroed_page(GFP_DMA | GFP_KERNEL);
+ on_close_chsc_area = kzalloc(PAGE_SIZE, GFP_DMA | GFP_KERNEL);
if (!on_close_chsc_area) {
ret = -ENOMEM;
goto out_free_request;
@@ -353,7 +353,7 @@ static int chsc_ioctl_on_close_set(void __user *user_area)
goto out_unlock;
out_free_chsc:
- free_page((unsigned long)on_close_chsc_area);
+ kfree(on_close_chsc_area);
on_close_chsc_area = NULL;
out_free_request:
kfree(on_close_request);
@@ -375,7 +375,7 @@ static int chsc_ioctl_on_close_remove(void)
ret = -ENOENT;
goto out_unlock;
}
- free_page((unsigned long)on_close_chsc_area);
+ kfree(on_close_chsc_area);
on_close_chsc_area = NULL;
kfree(on_close_request);
on_close_request = NULL;
@@ -392,7 +392,7 @@ static int chsc_ioctl_start_sync(void __user *user_area)
struct chsc_sync_area *chsc_area;
int ret, ccode;
- chsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ chsc_area = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!chsc_area)
return -ENOMEM;
if (copy_from_user(chsc_area, user_area, PAGE_SIZE)) {
@@ -414,7 +414,7 @@ static int chsc_ioctl_start_sync(void __user *user_area)
else
ret = 0;
out_free:
- free_page((unsigned long)chsc_area);
+ kfree(chsc_area);
return ret;
}
@@ -438,7 +438,7 @@ static int chsc_ioctl_info_channel_path(void __user *user_cd)
u8 data[PAGE_SIZE - 20];
} __attribute__ ((packed)) *scpcd_area;
- scpcd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ scpcd_area = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!scpcd_area)
return -ENOMEM;
cd = kzalloc_obj(*cd);
@@ -476,7 +476,7 @@ static int chsc_ioctl_info_channel_path(void __user *user_cd)
ret = 0;
out_free:
kfree(cd);
- free_page((unsigned long)scpcd_area);
+ kfree(scpcd_area);
return ret;
}
@@ -500,7 +500,7 @@ static int chsc_ioctl_info_cu(void __user *user_cd)
u8 data[PAGE_SIZE - 20];
} __attribute__ ((packed)) *scucd_area;
- scucd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ scucd_area = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!scucd_area)
return -ENOMEM;
cd = kzalloc_obj(*cd);
@@ -538,7 +538,7 @@ static int chsc_ioctl_info_cu(void __user *user_cd)
ret = 0;
out_free:
kfree(cd);
- free_page((unsigned long)scucd_area);
+ kfree(scucd_area);
return ret;
}
@@ -563,7 +563,7 @@ static int chsc_ioctl_info_sch_cu(void __user *user_cud)
u8 data[PAGE_SIZE - 20];
} __attribute__ ((packed)) *sscud_area;
- sscud_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ sscud_area = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!sscud_area)
return -ENOMEM;
cud = kzalloc_obj(*cud);
@@ -602,7 +602,7 @@ static int chsc_ioctl_info_sch_cu(void __user *user_cud)
ret = 0;
out_free:
kfree(cud);
- free_page((unsigned long)sscud_area);
+ kfree(sscud_area);
return ret;
}
@@ -625,7 +625,7 @@ static int chsc_ioctl_conf_info(void __user *user_ci)
u8 data[PAGE_SIZE - 20];
} __attribute__ ((packed)) *sci_area;
- sci_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ sci_area = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!sci_area)
return -ENOMEM;
ci = kzalloc_obj(*ci);
@@ -662,7 +662,7 @@ static int chsc_ioctl_conf_info(void __user *user_ci)
ret = 0;
out_free:
kfree(ci);
- free_page((unsigned long)sci_area);
+ kfree(sci_area);
return ret;
}
@@ -696,7 +696,7 @@ static int chsc_ioctl_conf_comp_list(void __user *user_ccl)
u32 res;
} __attribute__ ((packed)) *cssids_parm;
- sccl_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ sccl_area = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!sccl_area)
return -ENOMEM;
ccl = kzalloc_obj(*ccl);
@@ -745,7 +745,7 @@ static int chsc_ioctl_conf_comp_list(void __user *user_ccl)
ret = 0;
out_free:
kfree(ccl);
- free_page((unsigned long)sccl_area);
+ kfree(sccl_area);
return ret;
}
@@ -756,7 +756,7 @@ static int chsc_ioctl_chpd(void __user *user_chpd)
int ret;
chpd = kzalloc_obj(*chpd);
- scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ scpd_area = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!scpd_area || !chpd) {
ret = -ENOMEM;
goto out_free;
@@ -775,7 +775,7 @@ static int chsc_ioctl_chpd(void __user *user_chpd)
ret = -EFAULT;
out_free:
kfree(chpd);
- free_page((unsigned long)scpd_area);
+ kfree(scpd_area);
return ret;
}
@@ -796,7 +796,7 @@ static int chsc_ioctl_dcal(void __user *user_dcal)
u8 data[PAGE_SIZE - 36];
} __attribute__ ((packed)) *sdcal_area;
- sdcal_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ sdcal_area = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!sdcal_area)
return -ENOMEM;
dcal = kzalloc_obj(*dcal);
@@ -834,7 +834,7 @@ static int chsc_ioctl_dcal(void __user *user_dcal)
ret = 0;
out_free:
kfree(dcal);
- free_page((unsigned long)sdcal_area);
+ kfree(sdcal_area);
return ret;
}
@@ -904,7 +904,7 @@ static int chsc_release(struct inode *inode, struct file *filp)
}
snprintf(dbf, sizeof(dbf), "relret:%d", ret);
CHSC_LOG(0, dbf);
- free_page((unsigned long)on_close_chsc_area);
+ kfree(on_close_chsc_area);
on_close_chsc_area = NULL;
kfree(on_close_request);
on_close_request = NULL;
--
2.53.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [PATCH v2 03/13] s390/chsc_sch: Use kzalloc() for CHSC requests
2026-09-10 11:00 ` [PATCH v2 03/13] s390/chsc_sch: Use kzalloc() for CHSC requests Mike Rapoport (Microsoft)
@ 2026-09-10 11:10 ` sashiko-bot
0 siblings, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-09-10 11:10 UTC (permalink / raw)
To: Mike Rapoport (Microsoft)
Cc: Alexander Gordeev, linux-s390, Christian Borntraeger,
Heiko Carstens, Vasily Gorbik
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Hardware Alignment Violation: Replacing get_zeroed_page() with kzalloc(PAGE_SIZE) drops the strict 4K page-alignment guarantee required by the CHSC hardware instruction.
--
commit 605eecc8759317b0b4a005d290e0db9d7f1fd0a3
Author: Mike Rapoport (Microsoft) <rppt@kernel.org>
s390/chsc_sch: Use kzalloc() for CHSC requests
This commit replaces get_zeroed_page() and free_page() with kzalloc() and
kfree() for CHSC request and response areas. The goal is to simplify the
code and use a more standard API since the memory is assumed not to require
page allocator specifics.
Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com
> diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c
> --- a/drivers/s390/cio/chsc_sch.c
> +++ b/drivers/s390/cio/chsc_sch.c
> @@ -292,7 +292,7 @@ static int chsc_ioctl_start(void __user *user_area)
> if (!css_general_characteristics.dynio)
> /* It makes no sense to try. */
> return -EOPNOTSUPP;
> - chsc_area = (void *)get_zeroed_page(GFP_DMA | GFP_KERNEL);
> + chsc_area = kzalloc(PAGE_SIZE, GFP_DMA | GFP_KERNEL);
[Severity: High]
Does this kzalloc() break the strict 4KB page alignment required by the
CHSC hardware instruction?
When slab debugging (like redzoning) is enabled, the allocated memory block
can be shifted by the redzone size, breaking page alignment.
Passing this unaligned pointer to the CHSC instruction unconditionally
triggers a hardware specification exception (program check) which breaks
channel subsystem I/O functionality.
[ ... ]
> @@ -340,7 +340,7 @@ static int chsc_ioctl_on_close_set(void __user *user_area)
> ret = -ENOMEM;
> goto out_unlock;
> }
> - on_close_chsc_area = (void *)get_zeroed_page(GFP_DMA | GFP_KERNEL);
> + on_close_chsc_area = kzalloc(PAGE_SIZE, GFP_DMA | GFP_KERNEL);
[Severity: High]
Will this also lose the hardware-required page alignment for the on-close
CHSC command area?
[ ... ]
> @@ -392,7 +392,7 @@ static int chsc_ioctl_start_sync(void __user *user_area)
> struct chsc_sync_area *chsc_area;
> int ret, ccode;
>
> - chsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
> + chsc_area = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
[Severity: High]
Is there a risk of a hardware exception here as well due to the same
alignment issue in chsc_ioctl_start_sync()?
[ ... ]
> @@ -438,7 +438,7 @@ static int chsc_ioctl_info_channel_path(void __user *user_cd)
> u8 data[PAGE_SIZE - 20];
> } __attribute__ ((packed)) *scpcd_area;
>
> - scpcd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
> + scpcd_area = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
[Severity: High]
Could this silent drop of the 4K page-alignment guarantee cause I/O failure
for the command block allocation in chsc_ioctl_info_channel_path()?
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260910-s390-cio-ready-v2-0-e931086fdde1@kernel.org?part=3
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v2 04/13] s390/chsc_sch: Use __free(kfree) for synchronous CHSC requests
2026-09-10 11:00 [PATCH v2 00/13] s390/cio: replace page allocator calls with k[mz]alloc() Mike Rapoport (Microsoft)
` (2 preceding siblings ...)
2026-09-10 11:00 ` [PATCH v2 03/13] s390/chsc_sch: Use kzalloc() for CHSC requests Mike Rapoport (Microsoft)
@ 2026-09-10 11:00 ` Mike Rapoport (Microsoft)
2026-09-10 11:05 ` sashiko-bot
2026-09-10 11:00 ` [PATCH v2 05/13] s390/cio: Use kzalloc() for CHSC work areas Mike Rapoport (Microsoft)
` (8 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Mike Rapoport (Microsoft) @ 2026-09-10 11:00 UTC (permalink / raw)
To: Heiko Carstens, Vasily Gorbik, Alexander Gordeev
Cc: Christian Borntraeger, Mike Rapoport, Peter Oberparleiter,
Sven Schnelle, Vineeth Vijayan, Vlastimil Babka, linux-s390,
linux-kernel, linux-mm
Use __free(kfree) for local buffers in the synchronous CHSC ioctl
handlers and replace their cleanup labels with early returns.
Keep the asynchronous and on-close handlers unchanged.
Assisted-by: copilot:claude-opus
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
drivers/s390/cio/chsc_sch.c | 272 +++++++++++++++-----------------------------
1 file changed, 92 insertions(+), 180 deletions(-)
diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c
index c8d6871d1841e..225aaac7dd309 100644
--- a/drivers/s390/cio/chsc_sch.c
+++ b/drivers/s390/cio/chsc_sch.c
@@ -15,6 +15,7 @@
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/kernel_stat.h>
+#include <linux/cleanup.h>
#include <asm/cio.h>
#include <asm/chsc.h>
@@ -389,39 +390,29 @@ static int chsc_ioctl_on_close_remove(void)
static int chsc_ioctl_start_sync(void __user *user_area)
{
- struct chsc_sync_area *chsc_area;
- int ret, ccode;
+ struct chsc_sync_area *chsc_area __free(kfree) = NULL;
+ int ccode;
chsc_area = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!chsc_area)
return -ENOMEM;
- if (copy_from_user(chsc_area, user_area, PAGE_SIZE)) {
- ret = -EFAULT;
- goto out_free;
- }
- if (chsc_area->header.code & 0x4000) {
- ret = -EINVAL;
- goto out_free;
- }
+ if (copy_from_user(chsc_area, user_area, PAGE_SIZE))
+ return -EFAULT;
+ if (chsc_area->header.code & 0x4000)
+ return -EINVAL;
chsc_log_command(chsc_area);
ccode = chsc(chsc_area);
- if (ccode != 0) {
- ret = -EIO;
- goto out_free;
- }
+ if (ccode != 0)
+ return -EIO;
if (copy_to_user(user_area, chsc_area, PAGE_SIZE))
- ret = -EFAULT;
- else
- ret = 0;
-out_free:
- kfree(chsc_area);
- return ret;
+ return -EFAULT;
+ return 0;
}
static int chsc_ioctl_info_channel_path(void __user *user_cd)
{
- struct chsc_chp_cd *cd;
- int ret, ccode;
+ struct chsc_chp_cd *cd __free(kfree) = NULL;
+ int ccode;
struct {
struct chsc_header request;
u32 : 2;
@@ -436,20 +427,16 @@ static int chsc_ioctl_info_channel_path(void __user *user_cd)
u32 : 32;
struct chsc_header response;
u8 data[PAGE_SIZE - 20];
- } __attribute__ ((packed)) *scpcd_area;
+ } __attribute__ ((packed)) *scpcd_area __free(kfree) = NULL;
scpcd_area = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!scpcd_area)
return -ENOMEM;
cd = kzalloc_obj(*cd);
- if (!cd) {
- ret = -ENOMEM;
- goto out_free;
- }
- if (copy_from_user(cd, user_cd, sizeof(*cd))) {
- ret = -EFAULT;
- goto out_free;
- }
+ if (!cd)
+ return -ENOMEM;
+ if (copy_from_user(cd, user_cd, sizeof(*cd)))
+ return -EFAULT;
scpcd_area->request.length = 0x0010;
scpcd_area->request.code = 0x0028;
scpcd_area->m = cd->m;
@@ -459,31 +446,23 @@ static int chsc_ioctl_info_channel_path(void __user *user_cd)
scpcd_area->last_chpid = cd->chpid.id;
ccode = chsc(scpcd_area);
- if (ccode != 0) {
- ret = -EIO;
- goto out_free;
- }
+ if (ccode != 0)
+ return -EIO;
if (scpcd_area->response.code != 0x0001) {
- ret = -EIO;
CHSC_MSG(0, "scpcd: response code=%x\n",
scpcd_area->response.code);
- goto out_free;
+ return -EIO;
}
memcpy(&cd->cpcb, &scpcd_area->response, scpcd_area->response.length);
if (copy_to_user(user_cd, cd, sizeof(*cd)))
- ret = -EFAULT;
- else
- ret = 0;
-out_free:
- kfree(cd);
- kfree(scpcd_area);
- return ret;
+ return -EFAULT;
+ return 0;
}
static int chsc_ioctl_info_cu(void __user *user_cd)
{
- struct chsc_cu_cd *cd;
- int ret, ccode;
+ struct chsc_cu_cd *cd __free(kfree) = NULL;
+ int ccode;
struct {
struct chsc_header request;
u32 : 2;
@@ -498,20 +477,16 @@ static int chsc_ioctl_info_cu(void __user *user_cd)
u32 : 32;
struct chsc_header response;
u8 data[PAGE_SIZE - 20];
- } __attribute__ ((packed)) *scucd_area;
+ } __attribute__ ((packed)) *scucd_area __free(kfree) = NULL;
scucd_area = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!scucd_area)
return -ENOMEM;
cd = kzalloc_obj(*cd);
- if (!cd) {
- ret = -ENOMEM;
- goto out_free;
- }
- if (copy_from_user(cd, user_cd, sizeof(*cd))) {
- ret = -EFAULT;
- goto out_free;
- }
+ if (!cd)
+ return -ENOMEM;
+ if (copy_from_user(cd, user_cd, sizeof(*cd)))
+ return -EFAULT;
scucd_area->request.length = 0x0010;
scucd_area->request.code = 0x0026;
scucd_area->m = cd->m;
@@ -521,31 +496,23 @@ static int chsc_ioctl_info_cu(void __user *user_cd)
scucd_area->last_cun = cd->cun;
ccode = chsc(scucd_area);
- if (ccode != 0) {
- ret = -EIO;
- goto out_free;
- }
+ if (ccode != 0)
+ return -EIO;
if (scucd_area->response.code != 0x0001) {
- ret = -EIO;
CHSC_MSG(0, "scucd: response code=%x\n",
scucd_area->response.code);
- goto out_free;
+ return -EIO;
}
memcpy(&cd->cucb, &scucd_area->response, scucd_area->response.length);
if (copy_to_user(user_cd, cd, sizeof(*cd)))
- ret = -EFAULT;
- else
- ret = 0;
-out_free:
- kfree(cd);
- kfree(scucd_area);
- return ret;
+ return -EFAULT;
+ return 0;
}
static int chsc_ioctl_info_sch_cu(void __user *user_cud)
{
- struct chsc_sch_cud *cud;
- int ret, ccode;
+ struct chsc_sch_cud *cud __free(kfree) = NULL;
+ int ccode;
struct {
struct chsc_header request;
u32 : 2;
@@ -561,20 +528,16 @@ static int chsc_ioctl_info_sch_cu(void __user *user_cud)
u32 : 32;
struct chsc_header response;
u8 data[PAGE_SIZE - 20];
- } __attribute__ ((packed)) *sscud_area;
+ } __attribute__ ((packed)) *sscud_area __free(kfree) = NULL;
sscud_area = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!sscud_area)
return -ENOMEM;
cud = kzalloc_obj(*cud);
- if (!cud) {
- ret = -ENOMEM;
- goto out_free;
- }
- if (copy_from_user(cud, user_cud, sizeof(*cud))) {
- ret = -EFAULT;
- goto out_free;
- }
+ if (!cud)
+ return -ENOMEM;
+ if (copy_from_user(cud, user_cud, sizeof(*cud)))
+ return -EFAULT;
sscud_area->request.length = 0x0010;
sscud_area->request.code = 0x0006;
sscud_area->m = cud->schid.m;
@@ -585,31 +548,23 @@ static int chsc_ioctl_info_sch_cu(void __user *user_cud)
sscud_area->last_sch = cud->schid.sch_no;
ccode = chsc(sscud_area);
- if (ccode != 0) {
- ret = -EIO;
- goto out_free;
- }
+ if (ccode != 0)
+ return -EIO;
if (sscud_area->response.code != 0x0001) {
- ret = -EIO;
CHSC_MSG(0, "sscud: response code=%x\n",
sscud_area->response.code);
- goto out_free;
+ return -EIO;
}
memcpy(&cud->scub, &sscud_area->response, sscud_area->response.length);
if (copy_to_user(user_cud, cud, sizeof(*cud)))
- ret = -EFAULT;
- else
- ret = 0;
-out_free:
- kfree(cud);
- kfree(sscud_area);
- return ret;
+ return -EFAULT;
+ return 0;
}
static int chsc_ioctl_conf_info(void __user *user_ci)
{
- struct chsc_conf_info *ci;
- int ret, ccode;
+ struct chsc_conf_info *ci __free(kfree) = NULL;
+ int ccode;
struct {
struct chsc_header request;
u32 : 2;
@@ -623,20 +578,16 @@ static int chsc_ioctl_conf_info(void __user *user_ci)
u64 : 64;
struct chsc_header response;
u8 data[PAGE_SIZE - 20];
- } __attribute__ ((packed)) *sci_area;
+ } __attribute__ ((packed)) *sci_area __free(kfree) = NULL;
sci_area = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!sci_area)
return -ENOMEM;
ci = kzalloc_obj(*ci);
- if (!ci) {
- ret = -ENOMEM;
- goto out_free;
- }
- if (copy_from_user(ci, user_ci, sizeof(*ci))) {
- ret = -EFAULT;
- goto out_free;
- }
+ if (!ci)
+ return -ENOMEM;
+ if (copy_from_user(ci, user_ci, sizeof(*ci)))
+ return -EFAULT;
sci_area->request.length = 0x0010;
sci_area->request.code = 0x0012;
sci_area->m = ci->id.m;
@@ -645,31 +596,23 @@ static int chsc_ioctl_conf_info(void __user *user_ci)
sci_area->ssid = ci->id.ssid;
ccode = chsc(sci_area);
- if (ccode != 0) {
- ret = -EIO;
- goto out_free;
- }
+ if (ccode != 0)
+ return -EIO;
if (sci_area->response.code != 0x0001) {
- ret = -EIO;
CHSC_MSG(0, "sci: response code=%x\n",
sci_area->response.code);
- goto out_free;
+ return -EIO;
}
memcpy(&ci->scid, &sci_area->response, sci_area->response.length);
if (copy_to_user(user_ci, ci, sizeof(*ci)))
- ret = -EFAULT;
- else
- ret = 0;
-out_free:
- kfree(ci);
- kfree(sci_area);
- return ret;
+ return -EFAULT;
+ return 0;
}
static int chsc_ioctl_conf_comp_list(void __user *user_ccl)
{
- struct chsc_comp_list *ccl;
- int ret, ccode;
+ struct chsc_comp_list *ccl __free(kfree) = NULL;
+ int ccode;
struct {
struct chsc_header request;
u32 ctype : 8;
@@ -681,7 +624,7 @@ static int chsc_ioctl_conf_comp_list(void __user *user_ccl)
u64 : 64;
struct chsc_header response;
u8 data[PAGE_SIZE - 36];
- } __attribute__ ((packed)) *sccl_area;
+ } __attribute__ ((packed)) *sccl_area __free(kfree) = NULL;
struct {
u32 m : 1;
u32 : 31;
@@ -700,14 +643,10 @@ static int chsc_ioctl_conf_comp_list(void __user *user_ccl)
if (!sccl_area)
return -ENOMEM;
ccl = kzalloc_obj(*ccl);
- if (!ccl) {
- ret = -ENOMEM;
- goto out_free;
- }
- if (copy_from_user(ccl, user_ccl, sizeof(*ccl))) {
- ret = -EFAULT;
- goto out_free;
- }
+ if (!ccl)
+ return -ENOMEM;
+ if (copy_from_user(ccl, user_ccl, sizeof(*ccl)))
+ return -EFAULT;
sccl_area->request.length = 0x0020;
sccl_area->request.code = 0x0030;
sccl_area->fmt = ccl->req.fmt;
@@ -728,61 +667,46 @@ static int chsc_ioctl_conf_comp_list(void __user *user_ccl)
break;
}
ccode = chsc(sccl_area);
- if (ccode != 0) {
- ret = -EIO;
- goto out_free;
- }
+ if (ccode != 0)
+ return -EIO;
if (sccl_area->response.code != 0x0001) {
- ret = -EIO;
CHSC_MSG(0, "sccl: response code=%x\n",
sccl_area->response.code);
- goto out_free;
+ return -EIO;
}
memcpy(&ccl->sccl, &sccl_area->response, sccl_area->response.length);
if (copy_to_user(user_ccl, ccl, sizeof(*ccl)))
- ret = -EFAULT;
- else
- ret = 0;
-out_free:
- kfree(ccl);
- kfree(sccl_area);
- return ret;
+ return -EFAULT;
+ return 0;
}
static int chsc_ioctl_chpd(void __user *user_chpd)
{
- struct chsc_scpd *scpd_area;
- struct chsc_cpd_info *chpd;
+ struct chsc_scpd *scpd_area __free(kfree) = NULL;
+ struct chsc_cpd_info *chpd __free(kfree) = NULL;
int ret;
chpd = kzalloc_obj(*chpd);
scpd_area = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
- if (!scpd_area || !chpd) {
- ret = -ENOMEM;
- goto out_free;
- }
- if (copy_from_user(chpd, user_chpd, sizeof(*chpd))) {
- ret = -EFAULT;
- goto out_free;
- }
+ if (!scpd_area || !chpd)
+ return -ENOMEM;
+ if (copy_from_user(chpd, user_chpd, sizeof(*chpd)))
+ return -EFAULT;
ret = chsc_determine_channel_path_desc(chpd->chpid, chpd->fmt,
chpd->rfmt, chpd->c, chpd->m,
scpd_area);
if (ret)
- goto out_free;
+ return ret;
memcpy(&chpd->chpdb, &scpd_area->response, scpd_area->response.length);
if (copy_to_user(user_chpd, chpd, sizeof(*chpd)))
- ret = -EFAULT;
-out_free:
- kfree(chpd);
- kfree(scpd_area);
- return ret;
+ return -EFAULT;
+ return 0;
}
static int chsc_ioctl_dcal(void __user *user_dcal)
{
- struct chsc_dcal *dcal;
- int ret, ccode;
+ struct chsc_dcal *dcal __free(kfree) = NULL;
+ int ccode;
struct {
struct chsc_header request;
u32 atype : 8;
@@ -794,20 +718,16 @@ static int chsc_ioctl_dcal(void __user *user_dcal)
u32 res1[2];
struct chsc_header response;
u8 data[PAGE_SIZE - 36];
- } __attribute__ ((packed)) *sdcal_area;
+ } __attribute__ ((packed)) *sdcal_area __free(kfree) = NULL;
sdcal_area = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!sdcal_area)
return -ENOMEM;
dcal = kzalloc_obj(*dcal);
- if (!dcal) {
- ret = -ENOMEM;
- goto out_free;
- }
- if (copy_from_user(dcal, user_dcal, sizeof(*dcal))) {
- ret = -EFAULT;
- goto out_free;
- }
+ if (!dcal)
+ return -ENOMEM;
+ if (copy_from_user(dcal, user_dcal, sizeof(*dcal)))
+ return -EFAULT;
sdcal_area->request.length = 0x0020;
sdcal_area->request.code = 0x0034;
sdcal_area->atype = dcal->req.atype;
@@ -816,26 +736,18 @@ static int chsc_ioctl_dcal(void __user *user_dcal)
sizeof(sdcal_area->list_parm));
ccode = chsc(sdcal_area);
- if (ccode != 0) {
- ret = -EIO;
- goto out_free;
- }
+ if (ccode != 0)
+ return -EIO;
if (sdcal_area->response.code != 0x0001) {
- ret = -EIO;
CHSC_MSG(0, "sdcal: response code=%x\n",
sdcal_area->response.code);
- goto out_free;
+ return -EIO;
}
memcpy(&dcal->sdcal, &sdcal_area->response,
sdcal_area->response.length);
if (copy_to_user(user_dcal, dcal, sizeof(*dcal)))
- ret = -EFAULT;
- else
- ret = 0;
-out_free:
- kfree(dcal);
- kfree(sdcal_area);
- return ret;
+ return -EFAULT;
+ return 0;
}
static long chsc_ioctl(struct file *filp, unsigned int cmd,
--
2.53.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* [PATCH v2 05/13] s390/cio: Use kzalloc() for CHSC work areas
2026-09-10 11:00 [PATCH v2 00/13] s390/cio: replace page allocator calls with k[mz]alloc() Mike Rapoport (Microsoft)
` (3 preceding siblings ...)
2026-09-10 11:00 ` [PATCH v2 04/13] s390/chsc_sch: Use __free(kfree) for synchronous " Mike Rapoport (Microsoft)
@ 2026-09-10 11:00 ` Mike Rapoport (Microsoft)
2026-09-10 11:12 ` sashiko-bot
2026-09-10 11:00 ` [PATCH v2 06/13] s390/cmf: Use kmalloc() for the CMB area Mike Rapoport (Microsoft)
` (7 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Mike Rapoport (Microsoft) @ 2026-09-10 11:00 UTC (permalink / raw)
To: Heiko Carstens, Vasily Gorbik, Alexander Gordeev
Cc: Christian Borntraeger, Mike Rapoport, Peter Oberparleiter,
Sven Schnelle, Vineeth Vijayan, Vlastimil Babka, linux-s390,
linux-kernel, linux-mm
chsc_init() allocates the work area for CHSC commands and
qdio_allocate() allocates one for CHSC calls during qdio_establish().
This memory can be allocated with kmalloc() as there's nothing special
about it to go directly to the page allocator.
kmalloc() provides a better API that does not require ugly casts and
kfree() does not need to know the size of the freed object.
Performance difference between kmalloc() and __get_free_pages() is not
measurable as both allocators take an object/page from a per-CPU list for
fast path allocations.
For the slow path the performance is anyway determined by the amount of
reclaim involved rather than by what allocator is used.
While on it, change qdio_irq.chsc_page to void * to get rid of the casts.
Replace use of get_zeroed_page() with kzalloc() and free_page() with
kfree().
Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com
Assisted-by: copilot:claude-opus
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
drivers/s390/cio/chsc.c | 6 +++---
drivers/s390/cio/qdio.h | 2 +-
drivers/s390/cio/qdio_main.c | 6 +++---
drivers/s390/cio/qdio_setup.c | 2 +-
drivers/s390/cio/qdio_thinint.c | 2 +-
5 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index c3186c0a372b4..d98f629e89295 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -1143,7 +1143,7 @@ int __init chsc_init(void)
int ret;
sei_page = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
- chsc_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ chsc_page = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!sei_page || !chsc_page) {
ret = -ENOMEM;
goto out_err;
@@ -1153,7 +1153,7 @@ int __init chsc_init(void)
goto out_err;
return ret;
out_err:
- free_page((unsigned long)chsc_page);
+ kfree(chsc_page);
kfree(sei_page);
return ret;
}
@@ -1161,7 +1161,7 @@ int __init chsc_init(void)
void __init chsc_init_cleanup(void)
{
crw_unregister_handler(CRW_RSC_CSS);
- free_page((unsigned long)chsc_page);
+ kfree(chsc_page);
kfree(sei_page);
}
diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h
index 4bd4c00c9c0ca..dff5f53a8795d 100644
--- a/drivers/s390/cio/qdio.h
+++ b/drivers/s390/cio/qdio.h
@@ -244,7 +244,7 @@ struct qdio_irq {
int perf_stat_enabled;
struct qdr *qdr;
- unsigned long chsc_page;
+ void *chsc_page;
struct qdio_q *input_qs[QDIO_MAX_QUEUES_PER_IRQ];
struct qdio_q *output_qs[QDIO_MAX_QUEUES_PER_IRQ];
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c
index c1e09fa34e774..d137bf8c70664 100644
--- a/drivers/s390/cio/qdio_main.c
+++ b/drivers/s390/cio/qdio_main.c
@@ -934,7 +934,7 @@ int qdio_free(struct ccw_device *cdev)
qdio_free_queues(irq_ptr);
free_page((unsigned long) irq_ptr->qdr);
- free_page(irq_ptr->chsc_page);
+ kfree(irq_ptr->chsc_page);
kfree(irq_ptr->ccw);
free_page((unsigned long) irq_ptr);
return 0;
@@ -986,7 +986,7 @@ int qdio_allocate(struct ccw_device *cdev, unsigned int no_input_qs,
* qdio_establish. In case of low memory and swap on a zfcp disk
* we may not be able to allocate memory otherwise.
*/
- irq_ptr->chsc_page = get_zeroed_page(GFP_KERNEL);
+ irq_ptr->chsc_page = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!irq_ptr->chsc_page)
goto err_chsc;
@@ -1006,7 +1006,7 @@ int qdio_allocate(struct ccw_device *cdev, unsigned int no_input_qs,
err_queues:
free_page((unsigned long) irq_ptr->qdr);
err_qdr:
- free_page(irq_ptr->chsc_page);
+ kfree(irq_ptr->chsc_page);
err_chsc:
err_dbf:
kfree(irq_ptr->ccw);
diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c
index ea09aadaae4ec..bd80703c174c7 100644
--- a/drivers/s390/cio/qdio_setup.c
+++ b/drivers/s390/cio/qdio_setup.c
@@ -253,7 +253,7 @@ int qdio_setup_get_ssqd(struct qdio_irq *irq_ptr,
if (!ssqd)
return -ENOMEM;
} else {
- ssqd = (struct chsc_ssqd_area *)irq_ptr->chsc_page;
+ ssqd = irq_ptr->chsc_page;
}
rc = chsc_ssqd(*schid, ssqd);
diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c
index e167aa75c3dff..a2ff51887537f 100644
--- a/drivers/s390/cio/qdio_thinint.c
+++ b/drivers/s390/cio/qdio_thinint.c
@@ -136,7 +136,7 @@ static struct airq_struct tiqdio_airq = {
static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset)
{
- struct chsc_scssc_area *scssc = (void *)irq_ptr->chsc_page;
+ struct chsc_scssc_area *scssc = irq_ptr->chsc_page;
dma64_t summary_indicator_addr, subchannel_indicator_addr;
int rc;
--
2.53.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [PATCH v2 05/13] s390/cio: Use kzalloc() for CHSC work areas
2026-09-10 11:00 ` [PATCH v2 05/13] s390/cio: Use kzalloc() for CHSC work areas Mike Rapoport (Microsoft)
@ 2026-09-10 11:12 ` sashiko-bot
0 siblings, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-09-10 11:12 UTC (permalink / raw)
To: Mike Rapoport (Microsoft)
Cc: linux-s390, Vasily Gorbik, Heiko Carstens, Alexander Gordeev,
Christian Borntraeger
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Hardware Alignment Violation: Replacing `get_zeroed_page()` with `kzalloc(PAGE_SIZE)` breaks the strict 4K hardware alignment requirement for S/390 CHSC command blocks.
--
commit 30a8ee9cd7633040e19703afbcfd0201f58a6178
Author: Mike Rapoport (Microsoft) <rppt@kernel.org>
s390/cio: Use kzalloc() for CHSC work areas
This commit replaces the use of get_zeroed_page() with kzalloc() for allocating
CHSC work areas. The change is intended to provide a cleaner API and remove
the need for type casts.
Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com
> diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
> index c3186c0a372b4..d98f629e89295 100644
> --- a/drivers/s390/cio/chsc.c
> +++ b/drivers/s390/cio/chsc.c
> @@ -1143,7 +1143,7 @@ int __init chsc_init(void)
> int ret;
>
> sei_page = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
> - chsc_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
> + chsc_page = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
[Severity: High]
Does this break the strict 4K hardware alignment requirement for S/390 CHSC
command blocks?
The S/390 chsc instruction requires its command block operand to be exactly
4K-aligned. While get_zeroed_page() guarantees page alignment via the buddy
allocator, kzalloc(PAGE_SIZE) does not provide this strict guarantee. If
CONFIG_SLUB_DEBUG (such as SLAB_RED_ZONE) or CONFIG_KASAN are enabled,
metadata is added to the slab object, shifting the returned pointer off the
page boundary. This unaligned address will cause the CPU to raise a
specification exception (PGM 06) and trigger a kernel panic when the
hardware instruction executes.
This isn't a bug introduced by this specific patch, but the same alignment
requirement also seems to apply to the cub array pointers in cub_alloc()
(from s390/chsc: Use kzalloc() for CUBs) and chsc_area in chsc_ioctl_start()
(from s390/chsc_sch: Use kzalloc() for CHSC requests) introduced earlier
in this patch series.
[ ... ]
> diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c
> index c1e09fa34e774..d137bf8c70664 100644
> --- a/drivers/s390/cio/qdio_main.c
> +++ b/drivers/s390/cio/qdio_main.c
> @@ -986,7 +986,7 @@ int qdio_allocate(struct ccw_device *cdev, unsigned int no_input_qs,
> * qdio_establish. In case of low memory and swap on a zfcp disk
> * we may not be able to allocate memory otherwise.
> */
> - irq_ptr->chsc_page = get_zeroed_page(GFP_KERNEL);
> + irq_ptr->chsc_page = kzalloc(PAGE_SIZE, GFP_KERNEL);
[Severity: High]
Does this allocation in qdio_allocate() also violate the 4K hardware alignment
requirement for the QDIO-specific CHSC command blocks?
Like the change in chsc_init(), allocating this with kzalloc() instead of
get_zeroed_page() risks returning an unaligned address when SLUB debugging
or KASAN are enabled, deterministically crashing the kernel on I/O setup.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260910-s390-cio-ready-v2-0-e931086fdde1@kernel.org?part=5
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v2 06/13] s390/cmf: Use kmalloc() for the CMB area
2026-09-10 11:00 [PATCH v2 00/13] s390/cio: replace page allocator calls with k[mz]alloc() Mike Rapoport (Microsoft)
` (4 preceding siblings ...)
2026-09-10 11:00 ` [PATCH v2 05/13] s390/cio: Use kzalloc() for CHSC work areas Mike Rapoport (Microsoft)
@ 2026-09-10 11:00 ` Mike Rapoport (Microsoft)
2026-09-10 11:15 ` sashiko-bot
2026-09-10 11:00 ` [PATCH v2 07/13] s390/idals: Use kmalloc() for IDAL data buffers Mike Rapoport (Microsoft)
` (6 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Mike Rapoport (Microsoft) @ 2026-09-10 11:00 UTC (permalink / raw)
To: Heiko Carstens, Vasily Gorbik, Alexander Gordeev
Cc: Christian Borntraeger, Mike Rapoport, Peter Oberparleiter,
Sven Schnelle, Vineeth Vijayan, Vlastimil Babka, linux-s390,
linux-kernel, linux-mm
alloc_cmb() allocates the channel measurement block area shared by
devices using the basic channel measurement format.
This memory can be allocated with kmalloc() as there's nothing special
about it to go directly to the page allocator.
kmalloc() provides a better API that does not require ugly casts and
kfree() does not need to know the size of the freed object.
Performance difference between kmalloc() and __get_free_pages() is not
measurable as both allocators take an object/page from a per-CPU list for
fast path allocations.
For the slow path the performance is anyway determined by the amount of
reclaim involved rather than by what allocator is used.
The measurement block origin must be 32-byte aligned. Each CMB is
32 bytes, so kmalloc() provides the required alignment without rounding
the allocation to a power-of-two number of pages. Reject an empty area
before allocating it.
Replace use of __get_free_pages() with kmalloc() and free_pages() with
kfree().
Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com
Assisted-by: copilot:claude-opus
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
drivers/s390/cio/cmf.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c
index 92ab3d546fe47..8fd41a7dfc20b 100644
--- a/drivers/s390/cio/cmf.c
+++ b/drivers/s390/cio/cmf.c
@@ -482,6 +482,9 @@ static int alloc_cmb(struct ccw_device *cdev)
ssize_t size;
struct cmb_data *cmb_data;
+ if (cmb_area.num_channels <= 0)
+ return -ENOMEM;
+
/* Allocate private cmb_data. */
cmb_data = kzalloc_obj(struct cmb_data);
if (!cmb_data)
@@ -501,12 +504,12 @@ static int alloc_cmb(struct ccw_device *cdev)
WARN_ON(!list_empty(&cmb_area.list));
spin_unlock(&cmb_area.lock);
- mem = (void *)__get_free_pages(GFP_KERNEL, get_order(size));
+ mem = kmalloc(size, GFP_KERNEL);
spin_lock(&cmb_area.lock);
if (cmb_area.mem) {
/* ok, another thread was faster */
- free_pages((unsigned long)mem, get_order(size));
+ kfree(mem);
} else if (!mem) {
/* no luck */
ret = -ENOMEM;
@@ -547,10 +550,8 @@ static void free_cmb(struct ccw_device *cdev)
list_del_init(&priv->cmb_list);
if (list_empty(&cmb_area.list)) {
- ssize_t size;
- size = sizeof(struct cmb) * cmb_area.num_channels;
cmf_activate(NULL, CMF_OFF);
- free_pages((unsigned long)cmb_area.mem, get_order(size));
+ kfree(cmb_area.mem);
cmb_area.mem = NULL;
}
spin_unlock_irq(cdev->ccwlock);
--
2.53.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [PATCH v2 06/13] s390/cmf: Use kmalloc() for the CMB area
2026-09-10 11:00 ` [PATCH v2 06/13] s390/cmf: Use kmalloc() for the CMB area Mike Rapoport (Microsoft)
@ 2026-09-10 11:15 ` sashiko-bot
0 siblings, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-09-10 11:15 UTC (permalink / raw)
To: Mike Rapoport (Microsoft)
Cc: Vasily Gorbik, linux-s390, Christian Borntraeger, Heiko Carstens,
Alexander Gordeev
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] kmalloc() does not guarantee the 32-byte alignment required by the measurement block hardware for non-power-of-two sizes.
--
commit d40fffe39ac5da27ae9d3ccc129516e13b780620
Author: Mike Rapoport (Microsoft) <rppt@kernel.org>
s390/cmf: Use kmalloc() for the CMB area
This commit replaces the use of __get_free_pages() with kmalloc() for
allocating the channel measurement block (CMB) area. It suggests that
kmalloc() naturally provides the required 32-byte alignment without
rounding up to a power-of-two number of pages.
Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com
> diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c
> index 92ab3d546fe47..8fd41a7dfc20b 100644
> --- a/drivers/s390/cio/cmf.c
> +++ b/drivers/s390/cio/cmf.c
> @@ -501,12 +504,12 @@ static int alloc_cmb(struct ccw_device *cdev)
> WARN_ON(!list_empty(&cmb_area.list));
>
> spin_unlock(&cmb_area.lock);
> - mem = (void *)__get_free_pages(GFP_KERNEL, get_order(size));
> + mem = kmalloc(size, GFP_KERNEL);
[Severity: High]
Does kmalloc() guarantee the strict 32-byte alignment required by the schm
instruction when slab debugging or KASAN is enabled?
If the maxchannels module parameter is set to a value resulting in a
non-power-of-two multiple of 32 (for example, 3 channels which is 96 bytes),
the allocation uses the kmalloc-96 slab cache. While 96-byte pacing might
naturally align to 32 bytes in a default configuration, enabling
CONFIG_SLUB_DEBUG or CONFIG_KASAN adds left redzones to the objects.
This shifts the returned memory pointer, breaking the 32-byte alignment.
When cmf_activate() uses this pointer for the schm instruction, it will
cause a hardware fault and a kernel panic (Specification Exception) because
the measurement block origin is not on a 32-byte boundary.
The previous __get_free_pages() implementation avoided this by unconditionally
providing page-aligned memory.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260910-s390-cio-ready-v2-0-e931086fdde1@kernel.org?part=6
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v2 07/13] s390/idals: Use kmalloc() for IDAL data buffers
2026-09-10 11:00 [PATCH v2 00/13] s390/cio: replace page allocator calls with k[mz]alloc() Mike Rapoport (Microsoft)
` (5 preceding siblings ...)
2026-09-10 11:00 ` [PATCH v2 06/13] s390/cmf: Use kmalloc() for the CMB area Mike Rapoport (Microsoft)
@ 2026-09-10 11:00 ` Mike Rapoport (Microsoft)
2026-09-10 11:14 ` sashiko-bot
2026-09-10 11:00 ` [PATCH v2 08/13] s390/qdio_main: Use kzalloc() for the IRQ structure Mike Rapoport (Microsoft)
` (5 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Mike Rapoport (Microsoft) @ 2026-09-10 11:00 UTC (permalink / raw)
To: Heiko Carstens, Vasily Gorbik, Alexander Gordeev
Cc: Christian Borntraeger, Mike Rapoport, Peter Oberparleiter,
Sven Schnelle, Vineeth Vijayan, Vlastimil Babka, linux-s390,
linux-kernel, linux-mm
idal_buffer_alloc() allocates the data chunks of an IDAL buffer that is
used for channel I/O.
These buffers can be allocated with kmalloc() as there's nothing special
about them to go directly to the page allocator.
kmalloc() provides a better API that does not require ugly casts and
kfree() does not need to know the size of the freed object.
Performance difference between kmalloc() and __get_free_pages() is not
measurable as both allocators take an object/page from a per-CPU list for
fast path allocations.
For the slow path the performance is anyway determined by the amount of
reclaim involved rather than by what allocator is used.
Replace use of __get_free_pages() with kmalloc() and free_pages() with
kfree().
While on it, tell kmemleak to ignore the data chunks as only their DMA
addresses are retained in the IDAL buffer.
Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com
Assisted-by: copilot:claude-opus
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
arch/s390/include/asm/idals.h | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/arch/s390/include/asm/idals.h b/arch/s390/include/asm/idals.h
index 06e1ec2afd5af..213f430a8d33a 100644
--- a/arch/s390/include/asm/idals.h
+++ b/arch/s390/include/asm/idals.h
@@ -18,6 +18,7 @@
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
+#include <linux/kmemleak.h>
#include <asm/dma-types.h>
#include <asm/cio.h>
@@ -147,9 +148,11 @@ static inline struct idal_buffer *idal_buffer_alloc(size_t size, int page_order)
ib->data[i] = dma64_add(ib->data[i - 1], IDA_BLOCK_SIZE);
continue;
}
- vaddr = (void *)__get_free_pages(GFP_KERNEL, page_order);
+ vaddr = kmalloc(PAGE_SIZE << page_order, GFP_KERNEL);
if (!vaddr)
goto error;
+ /* Only DMA addresses are retained in ib->data. */
+ kmemleak_ignore(vaddr);
ib->data[i] = virt_to_dma64(vaddr);
}
return ib;
@@ -157,7 +160,7 @@ static inline struct idal_buffer *idal_buffer_alloc(size_t size, int page_order)
while (i >= nr_chunks) {
i -= nr_chunks;
vaddr = dma64_to_virt(ib->data[i]);
- free_pages((unsigned long)vaddr, ib->page_order);
+ kfree(vaddr);
}
kfree(ib);
return ERR_PTR(-ENOMEM);
@@ -175,7 +178,7 @@ static inline void idal_buffer_free(struct idal_buffer *ib)
nr_chunks = (PAGE_SIZE << ib->page_order) >> IDA_SIZE_SHIFT;
for (i = 0; i < nr_ptrs; i += nr_chunks) {
vaddr = dma64_to_virt(ib->data[i]);
- free_pages((unsigned long)vaddr, ib->page_order);
+ kfree(vaddr);
}
kfree(ib);
}
--
2.53.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [PATCH v2 07/13] s390/idals: Use kmalloc() for IDAL data buffers
2026-09-10 11:00 ` [PATCH v2 07/13] s390/idals: Use kmalloc() for IDAL data buffers Mike Rapoport (Microsoft)
@ 2026-09-10 11:14 ` sashiko-bot
0 siblings, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-09-10 11:14 UTC (permalink / raw)
To: Mike Rapoport (Microsoft)
Cc: linux-s390, Christian Borntraeger, Alexander Gordeev,
Heiko Carstens, Vasily Gorbik
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [Critical] Hardware Alignment Violation / I/O Error due to kmalloc
Pre-existing issues:
- [High] KASAN out-of-bounds read in idal_buffer_from_user() when count is 0
--
commit 009d471661da8ef804c875150de919b995098cb8
Author: Mike Rapoport (Microsoft) <rppt@kernel.org>
s390/idals: Use kmalloc() for IDAL data buffers
idal_buffer_alloc() allocates the data chunks of an IDAL buffer that is
used for channel I/O. These buffers can be allocated with kmalloc() as
there's nothing special about them to go directly to the page allocator.
kmalloc() provides a better API that does not require ugly casts.
Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com
> diff --git a/arch/s390/include/asm/idals.h b/arch/s390/include/asm/idals.h
> index 06e1ec2afd5af..213f430a8d33a 100644
> --- a/arch/s390/include/asm/idals.h
> +++ b/arch/s390/include/asm/idals.h
[ ... ]
> @@ -147,9 +148,11 @@ static inline struct idal_buffer *idal_buffer_alloc(size_t size, int page_order)
> ib->data[i] = dma64_add(ib->data[i - 1], IDA_BLOCK_SIZE);
> continue;
> }
> - vaddr = (void *)__get_free_pages(GFP_KERNEL, page_order);
> + vaddr = kmalloc(PAGE_SIZE << page_order, GFP_KERNEL);
[Severity: Critical]
Does replacing __get_free_pages() with kmalloc() break the page-alignment
guarantee required for IDAWs?
The s390 architecture mandates that while the first IDAW (Indirect Data
Address Word) can specify any byte offset, all subsequent IDAWs must be
strictly aligned to a 2K or 4K boundary. Furthermore, hardware transfers
data on the first IDAW only up to the next physical 2K/4K boundary.
When CONFIG_SLUB_DEBUG is enabled, kmalloc redzoning shifts the payload away
from a page boundary. This unaligned address is stored in ib->data[0], and
dma64_add() propagates it to subsequent IDAWs:
arch/s390/include/asm/idals.h:idal_buffer_alloc() {
...
if (i & (nr_chunks - 1)) {
ib->data[i] = dma64_add(ib->data[i - 1], IDA_BLOCK_SIZE);
continue;
}
...
}
Will this unaligned base violate the hardware alignment requirement and cause
a channel-program check, as well as silently corrupt data since hardware
splits the transfer at the physical 4K boundary?
> if (!vaddr)
> goto error;
> + /* Only DMA addresses are retained in ib->data. */
> + kmemleak_ignore(vaddr);
> ib->data[i] = virt_to_dma64(vaddr);
> }
> return ib;
[Severity: High]
This isn't a bug introduced by this patch, but is it possible to trigger a
KASAN out-of-bounds read in idal_buffer_from_user() when count is 0?
In drivers/s390/char/fs3270.c, fs3270_write() takes a count parameter from
the user and passes it directly to the allocator without checking if it is 0:
drivers/s390/char/fs3270.c:fs3270_write() {
...
ib = idal_buffer_alloc(count, 0);
if (IS_ERR(ib))
return -ENOMEM;
...
if (idal_buffer_from_user(ib, data, count) == 0) {
...
}
If count is 0, idal_buffer_alloc() calculates nr_ptrs = 0 and allocates
exactly sizeof(struct idal_buffer) with 0 elements for the data flex-array.
Then idal_buffer_from_user() accesses the first element of this array
unconditionally:
arch/s390/include/asm/idals.h:idal_buffer_from_user() {
...
for (i = 0; count > IDA_BLOCK_SIZE; i++) {
...
}
vaddr = dma64_to_virt(ib->data[i]);
return copy_from_user(vaddr, from, count);
}
When count is 0, the loop is skipped and it evaluates ib->data[0]. Since the
array was allocated with 0 elements, does this read 8 bytes past the allocated
16-byte boundary of the struct?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260910-s390-cio-ready-v2-0-e931086fdde1@kernel.org?part=7
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v2 08/13] s390/qdio_main: Use kzalloc() for the IRQ structure
2026-09-10 11:00 [PATCH v2 00/13] s390/cio: replace page allocator calls with k[mz]alloc() Mike Rapoport (Microsoft)
` (6 preceding siblings ...)
2026-09-10 11:00 ` [PATCH v2 07/13] s390/idals: Use kmalloc() for IDAL data buffers Mike Rapoport (Microsoft)
@ 2026-09-10 11:00 ` Mike Rapoport (Microsoft)
2026-09-10 11:20 ` sashiko-bot
2026-09-10 11:00 ` [PATCH v2 09/13] s390/qdio_main: Use kzalloc() for the QDR Mike Rapoport (Microsoft)
` (4 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Mike Rapoport (Microsoft) @ 2026-09-10 11:00 UTC (permalink / raw)
To: Heiko Carstens, Vasily Gorbik, Alexander Gordeev
Cc: Christian Borntraeger, Mike Rapoport, Peter Oberparleiter,
Sven Schnelle, Vineeth Vijayan, Vlastimil Babka, linux-s390,
linux-kernel, linux-mm
qdio_allocate() allocates the QDIO irq structure.
This memory can be allocated with kmalloc() as there's nothing special
about it to go directly to the page allocator.
kmalloc() provides a better API that does not require ugly casts and
kfree() does not need to know the size of the freed object.
Performance difference between kmalloc() and __get_free_pages() is not
measurable as both allocators take an object/page from a per-CPU list for
fast path allocations.
For the slow path the performance is anyway determined by the amount of
reclaim involved rather than by what allocator is used.
Replace use of get_zeroed_page() with kzalloc() and free_page() with
kfree().
While on it, drop the kmemleak exemption for the CCW. The IRQ structure
is now tracked, so kmemleak can find its pointer to the CCW.
Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com
Assisted-by: copilot:claude-opus
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
drivers/s390/cio/qdio_main.c | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c
index d137bf8c70664..28eb8602ca97e 100644
--- a/drivers/s390/cio/qdio_main.c
+++ b/drivers/s390/cio/qdio_main.c
@@ -12,11 +12,11 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
-#include <linux/kmemleak.h>
#include <linux/delay.h>
#include <linux/gfp.h>
#include <linux/io.h>
#include <linux/atomic.h>
+#include <linux/slab.h>
#include <asm/debug.h>
#include <asm/qdio.h>
#include <asm/asm.h>
@@ -936,7 +936,7 @@ int qdio_free(struct ccw_device *cdev)
free_page((unsigned long) irq_ptr->qdr);
kfree(irq_ptr->chsc_page);
kfree(irq_ptr->ccw);
- free_page((unsigned long) irq_ptr);
+ kfree(irq_ptr);
return 0;
}
EXPORT_SYMBOL_GPL(qdio_free);
@@ -961,7 +961,7 @@ int qdio_allocate(struct ccw_device *cdev, unsigned int no_input_qs,
no_output_qs > QDIO_MAX_QUEUES_PER_IRQ)
return -EINVAL;
- irq_ptr = (void *) get_zeroed_page(GFP_KERNEL);
+ irq_ptr = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!irq_ptr)
return -ENOMEM;
@@ -969,9 +969,6 @@ int qdio_allocate(struct ccw_device *cdev, unsigned int no_input_qs,
if (!irq_ptr->ccw)
goto err_ccw;
- /* kmemleak doesn't scan the page-allocated irq_ptr: */
- kmemleak_not_leak(irq_ptr->ccw);
-
irq_ptr->cdev = cdev;
mutex_init(&irq_ptr->setup_mutex);
if (qdio_allocate_dbf(irq_ptr))
@@ -1011,7 +1008,7 @@ int qdio_allocate(struct ccw_device *cdev, unsigned int no_input_qs,
err_dbf:
kfree(irq_ptr->ccw);
err_ccw:
- free_page((unsigned long) irq_ptr);
+ kfree(irq_ptr);
return rc;
}
EXPORT_SYMBOL_GPL(qdio_allocate);
--
2.53.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [PATCH v2 08/13] s390/qdio_main: Use kzalloc() for the IRQ structure
2026-09-10 11:00 ` [PATCH v2 08/13] s390/qdio_main: Use kzalloc() for the IRQ structure Mike Rapoport (Microsoft)
@ 2026-09-10 11:20 ` sashiko-bot
0 siblings, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-09-10 11:20 UTC (permalink / raw)
To: Mike Rapoport (Microsoft)
Cc: Vasily Gorbik, Alexander Gordeev, Christian Borntraeger,
Heiko Carstens, linux-s390
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Replacing get_zeroed_page() with kzalloc() removes the guaranteed page alignment, breaking the strict 256-byte hardware alignment requirement for the embedded Queue Information Block (QIB) when SLUB debugging or KASAN is enabled.
--
commit a99e5dce5d241e395d31c60a1569a9ca578e1287
Author: Mike Rapoport (Microsoft) <rppt@kernel.org>
s390/qdio_main: Use kzalloc() for the IRQ structure
This commit replaces the page allocator with kzalloc() for the QDIO irq
structure. It also cleans up the kmemleak tracking since the IRQ structure
is now tracked by the slab allocator.
Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com
> diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c
> index d137bf8c70664..28eb8602ca97e 100644
> --- a/drivers/s390/cio/qdio_main.c
> +++ b/drivers/s390/cio/qdio_main.c
[ ... ]
> @@ -961,7 +961,7 @@ int qdio_allocate(struct ccw_device *cdev, unsigned int no_input_qs,
> no_output_qs > QDIO_MAX_QUEUES_PER_IRQ)
> return -EINVAL;
>
> - irq_ptr = (void *) get_zeroed_page(GFP_KERNEL);
> + irq_ptr = kzalloc(PAGE_SIZE, GFP_KERNEL);
[Severity: High]
Does replacing get_zeroed_page() with kzalloc() remove the guaranteed page
alignment for the Queue Information Block (QIB)?
The struct qdio_irq embeds struct qib as its first member. The IBM channel
subsystem requires the QIB physical address to be strictly 256-byte aligned.
When the kernel is configured with CONFIG_SLUB_DEBUG and redzoning is
active, or when KASAN is enabled, SLUB adds a red_left_pad before the
object in the slab. This means the pointer returned by kzalloc() is offset
from the page boundary.
If this unaligned virtual address is passed to virt_to_dma64(&irq_ptr->qib)
and written to the QDR, will the channel subsystem truncate the lower bits
and read invalid memory, or generate a hardware check?
> if (!irq_ptr)
> return -ENOMEM;
>
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260910-s390-cio-ready-v2-0-e931086fdde1@kernel.org?part=8
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v2 09/13] s390/qdio_main: Use kzalloc() for the QDR
2026-09-10 11:00 [PATCH v2 00/13] s390/cio: replace page allocator calls with k[mz]alloc() Mike Rapoport (Microsoft)
` (7 preceding siblings ...)
2026-09-10 11:00 ` [PATCH v2 08/13] s390/qdio_main: Use kzalloc() for the IRQ structure Mike Rapoport (Microsoft)
@ 2026-09-10 11:00 ` Mike Rapoport (Microsoft)
2026-09-10 11:11 ` sashiko-bot
2026-09-10 11:00 ` [PATCH v2 10/13] s390/qdio_setup: Use kzalloc() for QDIO buffers Mike Rapoport (Microsoft)
` (3 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Mike Rapoport (Microsoft) @ 2026-09-10 11:00 UTC (permalink / raw)
To: Heiko Carstens, Vasily Gorbik, Alexander Gordeev
Cc: Christian Borntraeger, Mike Rapoport, Peter Oberparleiter,
Sven Schnelle, Vineeth Vijayan, Vlastimil Babka, linux-s390,
linux-kernel, linux-mm
qdio_allocate() allocates the queue description record (QDR).
This memory can be allocated with kmalloc() as there's nothing special
about it to go directly to the page allocator.
kmalloc() provides a better API that does not require ugly casts and
kfree() does not need to know the size of the freed object.
Performance difference between kmalloc() and __get_free_pages() is not
measurable as both allocators take an object/page from a per-CPU list for
fast path allocations.
For the slow path the performance is anyway determined by the amount of
reclaim involved rather than by what allocator is used.
Replace use of get_zeroed_page() with kzalloc() and free_page() with
kfree().
Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com
Assisted-by: copilot:claude-opus
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
drivers/s390/cio/qdio_main.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c
index 28eb8602ca97e..889f63f94a4a7 100644
--- a/drivers/s390/cio/qdio_main.c
+++ b/drivers/s390/cio/qdio_main.c
@@ -933,7 +933,7 @@ int qdio_free(struct ccw_device *cdev)
mutex_unlock(&irq_ptr->setup_mutex);
qdio_free_queues(irq_ptr);
- free_page((unsigned long) irq_ptr->qdr);
+ kfree(irq_ptr->qdr);
kfree(irq_ptr->chsc_page);
kfree(irq_ptr->ccw);
kfree(irq_ptr);
@@ -988,7 +988,7 @@ int qdio_allocate(struct ccw_device *cdev, unsigned int no_input_qs,
goto err_chsc;
/* qdr is used in ccw1.cda which is u32 */
- irq_ptr->qdr = (struct qdr *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ irq_ptr->qdr = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!irq_ptr->qdr)
goto err_qdr;
@@ -1001,7 +1001,7 @@ int qdio_allocate(struct ccw_device *cdev, unsigned int no_input_qs,
return 0;
err_queues:
- free_page((unsigned long) irq_ptr->qdr);
+ kfree(irq_ptr->qdr);
err_qdr:
kfree(irq_ptr->chsc_page);
err_chsc:
--
2.53.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* [PATCH v2 10/13] s390/qdio_setup: Use kzalloc() for QDIO buffers
2026-09-10 11:00 [PATCH v2 00/13] s390/cio: replace page allocator calls with k[mz]alloc() Mike Rapoport (Microsoft)
` (8 preceding siblings ...)
2026-09-10 11:00 ` [PATCH v2 09/13] s390/qdio_main: Use kzalloc() for the QDR Mike Rapoport (Microsoft)
@ 2026-09-10 11:00 ` Mike Rapoport (Microsoft)
2026-09-10 11:15 ` sashiko-bot
2026-09-10 11:00 ` [PATCH v2 11/13] s390/qdio_setup: Use kzalloc() for the storage list Mike Rapoport (Microsoft)
` (2 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Mike Rapoport (Microsoft) @ 2026-09-10 11:00 UTC (permalink / raw)
To: Heiko Carstens, Vasily Gorbik, Alexander Gordeev
Cc: Christian Borntraeger, Mike Rapoport, Peter Oberparleiter,
Sven Schnelle, Vineeth Vijayan, Vlastimil Babka, linux-s390,
linux-kernel, linux-mm
qdio_alloc_buffers() allocates the QDIO buffers.
This memory can be allocated with kmalloc() as there's nothing special
about it to go directly to the page allocator.
kmalloc() provides a better API that does not require ugly casts and
kfree() does not need to know the size of the freed object.
Performance difference between kmalloc() and __get_free_pages() is not
measurable as both allocators take an object/page from a per-CPU list for
fast path allocations.
For the slow path the performance is anyway determined by the amount of
reclaim involved rather than by what allocator is used.
Replace use of get_zeroed_page() with kzalloc() and free_page() with
kfree().
Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com
Assisted-by: copilot:claude-opus
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
drivers/s390/cio/qdio_setup.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c
index bd80703c174c7..b6f2ff202fe64 100644
--- a/drivers/s390/cio/qdio_setup.c
+++ b/drivers/s390/cio/qdio_setup.c
@@ -35,7 +35,7 @@ void qdio_free_buffers(struct qdio_buffer **buf, unsigned int count)
int pos;
for (pos = 0; pos < count; pos += QBUFF_PER_PAGE)
- free_page((unsigned long) buf[pos]);
+ kfree(buf[pos]);
}
EXPORT_SYMBOL_GPL(qdio_free_buffers);
@@ -49,7 +49,7 @@ int qdio_alloc_buffers(struct qdio_buffer **buf, unsigned int count)
int pos;
for (pos = 0; pos < count; pos += QBUFF_PER_PAGE) {
- buf[pos] = (void *) get_zeroed_page(GFP_KERNEL);
+ buf[pos] = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!buf[pos]) {
qdio_free_buffers(buf, count);
return -ENOMEM;
--
2.53.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* [PATCH v2 11/13] s390/qdio_setup: Use kzalloc() for the storage list
2026-09-10 11:00 [PATCH v2 00/13] s390/cio: replace page allocator calls with k[mz]alloc() Mike Rapoport (Microsoft)
` (9 preceding siblings ...)
2026-09-10 11:00 ` [PATCH v2 10/13] s390/qdio_setup: Use kzalloc() for QDIO buffers Mike Rapoport (Microsoft)
@ 2026-09-10 11:00 ` Mike Rapoport (Microsoft)
2026-09-10 11:16 ` sashiko-bot
2026-09-10 11:00 ` [PATCH v2 12/13] s390/qdio_setup: Use kzalloc() for the SSQD request Mike Rapoport (Microsoft)
2026-09-10 11:00 ` [PATCH v2 13/13] s390/scm: Use kmalloc() for SCM information Mike Rapoport (Microsoft)
12 siblings, 1 reply; 27+ messages in thread
From: Mike Rapoport (Microsoft) @ 2026-09-10 11:00 UTC (permalink / raw)
To: Heiko Carstens, Vasily Gorbik, Alexander Gordeev
Cc: Christian Borntraeger, Mike Rapoport, Peter Oberparleiter,
Sven Schnelle, Vineeth Vijayan, Vlastimil Babka, linux-s390,
linux-kernel, linux-mm
__qdio_allocate_qs() allocates the storage list information block of a
queue.
This memory can be allocated with kmalloc() as there's nothing special
about it to go directly to the page allocator.
kmalloc() provides a better API that does not require ugly casts and
kfree() does not need to know the size of the freed object.
Performance difference between kmalloc() and __get_free_pages() is not
measurable as both allocators take an object/page from a per-CPU list for
fast path allocations.
For the slow path the performance is anyway determined by the amount of
reclaim involved rather than by what allocator is used.
Replace use of __get_free_page() with kzalloc() and free_page() with
kfree().
Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com
Assisted-by: copilot:claude-opus
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
drivers/s390/cio/qdio_setup.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c
index b6f2ff202fe64..7ca8f61a2387b 100644
--- a/drivers/s390/cio/qdio_setup.c
+++ b/drivers/s390/cio/qdio_setup.c
@@ -83,7 +83,7 @@ static void __qdio_free_queues(struct qdio_q **queues, unsigned int count)
for (i = 0; i < count; i++) {
q = queues[i];
- free_page((unsigned long)q->sl_page);
+ kfree(q->sl_page);
kmem_cache_free(qdio_q_cache, q);
}
}
@@ -109,7 +109,7 @@ static int __qdio_allocate_qs(struct qdio_q **irq_ptr_qs, int nr_queues)
return -ENOMEM;
}
- q->sl_page = (void *)__get_free_page(GFP_KERNEL);
+ q->sl_page = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!q->sl_page) {
kmem_cache_free(qdio_q_cache, q);
__qdio_free_queues(irq_ptr_qs, i);
--
2.53.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* [PATCH v2 12/13] s390/qdio_setup: Use kzalloc() for the SSQD request
2026-09-10 11:00 [PATCH v2 00/13] s390/cio: replace page allocator calls with k[mz]alloc() Mike Rapoport (Microsoft)
` (10 preceding siblings ...)
2026-09-10 11:00 ` [PATCH v2 11/13] s390/qdio_setup: Use kzalloc() for the storage list Mike Rapoport (Microsoft)
@ 2026-09-10 11:00 ` Mike Rapoport (Microsoft)
2026-09-10 11:19 ` sashiko-bot
2026-09-10 11:00 ` [PATCH v2 13/13] s390/scm: Use kmalloc() for SCM information Mike Rapoport (Microsoft)
12 siblings, 1 reply; 27+ messages in thread
From: Mike Rapoport (Microsoft) @ 2026-09-10 11:00 UTC (permalink / raw)
To: Heiko Carstens, Vasily Gorbik, Alexander Gordeev
Cc: Christian Borntraeger, Mike Rapoport, Peter Oberparleiter,
Sven Schnelle, Vineeth Vijayan, Vlastimil Babka, linux-s390,
linux-kernel, linux-mm
qdio_setup_get_ssqd() allocates the request block for the Store
Subchannel QDIO Data (SSQD) CHSC command.
This memory can be allocated with kmalloc() as there's nothing special
about it to go directly to the page allocator.
kmalloc() provides a better API that does not require ugly casts and
kfree() does not need to know the size of the freed object.
Performance difference between kmalloc() and __get_free_pages() is not
measurable as both allocators take an object/page from a per-CPU list for
fast path allocations.
For the slow path the performance is anyway determined by the amount of
reclaim involved rather than by what allocator is used.
Replace use of __get_free_page() with kzalloc() and free_page() with
kfree().
Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com
Assisted-by: copilot:claude-opus
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
drivers/s390/cio/qdio_setup.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c
index 7ca8f61a2387b..c0215a7d29a81 100644
--- a/drivers/s390/cio/qdio_setup.c
+++ b/drivers/s390/cio/qdio_setup.c
@@ -249,7 +249,7 @@ int qdio_setup_get_ssqd(struct qdio_irq *irq_ptr,
DBF_EVENT("getssqd:%4x", schid->sch_no);
if (!irq_ptr) {
- ssqd = (struct chsc_ssqd_area *)__get_free_page(GFP_KERNEL);
+ ssqd = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!ssqd)
return -ENOMEM;
} else {
@@ -270,7 +270,7 @@ int qdio_setup_get_ssqd(struct qdio_irq *irq_ptr,
out:
if (!irq_ptr)
- free_page((unsigned long)ssqd);
+ kfree(ssqd);
return rc;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [PATCH v2 12/13] s390/qdio_setup: Use kzalloc() for the SSQD request
2026-09-10 11:00 ` [PATCH v2 12/13] s390/qdio_setup: Use kzalloc() for the SSQD request Mike Rapoport (Microsoft)
@ 2026-09-10 11:19 ` sashiko-bot
0 siblings, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-09-10 11:19 UTC (permalink / raw)
To: Mike Rapoport (Microsoft)
Cc: Vasily Gorbik, Heiko Carstens, linux-s390, Alexander Gordeev,
Christian Borntraeger
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Hardware Alignment Violation in CHSC command block allocation
--
commit 43772ff0bb381f4318bb2f363719b4922b09d117
Author: Mike Rapoport (Microsoft) <rppt@kernel.org>
s390/qdio_setup: Use kzalloc() for the SSQD request
This commit modifies qdio_setup_get_ssqd() to use kzalloc() instead of
__get_free_page() for allocating the Store Subchannel QDIO Data (SSQD)
CHSC command block. It also replaces the corresponding free_page() call
with kfree().
Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com
> diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c
> --- a/drivers/s390/cio/qdio_setup.c
> +++ b/drivers/s390/cio/qdio_setup.c
> @@ -249,7 +249,7 @@ int qdio_setup_get_ssqd(struct qdio_irq *irq_ptr,
>
> DBF_EVENT("getssqd:%4x", schid->sch_no);
> if (!irq_ptr) {
> - ssqd = (struct chsc_ssqd_area *)__get_free_page(GFP_KERNEL);
> + ssqd = kzalloc(PAGE_SIZE, GFP_KERNEL);
[Severity: High]
In drivers/s390/cio/qdio_setup.c:qdio_setup_get_ssqd(), does this allocation
maintain the strict page alignment (4K boundary) required by the S/390 CHSC
instruction?
While kzalloc() with PAGE_SIZE might naturally align to 4K on standard
configurations, this guarantee is lost when CONFIG_SLUB_DEBUG (e.g., object
poisoning or redzoning) or KASAN is enabled. Under these configurations, SLUB
adds metadata or padding before the object, shifting the returned pointer off
the page boundary.
Passing an unaligned address to the CHSC hardware instruction triggers a
hardware specification exception (Program Check 0x06) and a kernel crash
deterministically during QDIO device setup.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260910-s390-cio-ready-v2-0-e931086fdde1@kernel.org?part=12
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v2 13/13] s390/scm: Use kmalloc() for SCM information
2026-09-10 11:00 [PATCH v2 00/13] s390/cio: replace page allocator calls with k[mz]alloc() Mike Rapoport (Microsoft)
` (11 preceding siblings ...)
2026-09-10 11:00 ` [PATCH v2 12/13] s390/qdio_setup: Use kzalloc() for the SSQD request Mike Rapoport (Microsoft)
@ 2026-09-10 11:00 ` Mike Rapoport (Microsoft)
2026-09-10 11:22 ` sashiko-bot
12 siblings, 1 reply; 27+ messages in thread
From: Mike Rapoport (Microsoft) @ 2026-09-10 11:00 UTC (permalink / raw)
To: Heiko Carstens, Vasily Gorbik, Alexander Gordeev
Cc: Christian Borntraeger, Mike Rapoport, Peter Oberparleiter,
Sven Schnelle, Vineeth Vijayan, Vlastimil Babka, linux-s390,
linux-kernel, linux-mm
scm_update_information() allocates the response buffer for the CHSC
Store SCM Information command.
This buffer can be allocated with kmalloc() as there's nothing special
about it to go directly to the page allocator.
kmalloc() provides a better API that does not require ugly casts and
kfree() does not need to know the size of the freed object.
Performance difference between kmalloc() and __get_free_pages() is not
measurable as both allocators take an object/page from a per-CPU list for
fast path allocations.
For the slow path the performance is anyway determined by the amount of
reclaim involved rather than by what allocator is used.
Replace use of __get_free_page() with kmalloc() and free_page() with
kfree().
While on it, use __free(kfree) for the local response buffer.
Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com
Assisted-by: copilot:claude-opus
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
drivers/s390/cio/scm.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/drivers/s390/cio/scm.c b/drivers/s390/cio/scm.c
index 171212a6d2d9c..b79fe023ab8d1 100644
--- a/drivers/s390/cio/scm.c
+++ b/drivers/s390/cio/scm.c
@@ -13,6 +13,7 @@
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/err.h>
+#include <linux/cleanup.h>
#include <asm/eadm.h>
#include "chsc.h"
@@ -224,12 +225,12 @@ static int scm_add(struct chsc_scm_info *scm_info, size_t num)
int scm_update_information(void)
{
- struct chsc_scm_info *scm_info;
+ struct chsc_scm_info *scm_info __free(kfree) = NULL;
u64 token = 0;
size_t num;
int ret;
- scm_info = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
+ scm_info = kmalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!scm_info)
return -ENOMEM;
@@ -250,8 +251,6 @@ int scm_update_information(void)
token = scm_info->restok;
} while (token);
- free_page((unsigned long)scm_info);
-
return ret;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [PATCH v2 13/13] s390/scm: Use kmalloc() for SCM information
2026-09-10 11:00 ` [PATCH v2 13/13] s390/scm: Use kmalloc() for SCM information Mike Rapoport (Microsoft)
@ 2026-09-10 11:22 ` sashiko-bot
0 siblings, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-09-10 11:22 UTC (permalink / raw)
To: Mike Rapoport (Microsoft)
Cc: linux-s390, Alexander Gordeev, Heiko Carstens,
Christian Borntraeger, Vasily Gorbik
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Replacing __get_free_page() with kmalloc() breaks the strict 4K-byte hardware alignment requirement for the CHSC command block, leading to a specification exception (kernel panic).
--
commit 9a0607c813ec0f02b4147f95093917e38b21c342
Author: Mike Rapoport (Microsoft) <rppt@kernel.org>
s390/scm: Use kmalloc() for SCM information
This commit replaces the page allocator with kmalloc for allocating the
response buffer in scm_update_information(), and introduces scope-based
cleanup using __free(kfree).
Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com
> diff --git a/drivers/s390/cio/scm.c b/drivers/s390/cio/scm.c
> index 171212a6d2d9c..b79fe023ab8d1 100644
> --- a/drivers/s390/cio/scm.c
> +++ b/drivers/s390/cio/scm.c
[ ... ]
> @@ -224,12 +225,12 @@ static int scm_add(struct chsc_scm_info *scm_info, size_t num)
>
> int scm_update_information(void)
> {
> - struct chsc_scm_info *scm_info;
> + struct chsc_scm_info *scm_info __free(kfree) = NULL;
> u64 token = 0;
> size_t num;
> int ret;
>
> - scm_info = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
> + scm_info = kmalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
[Severity: High]
Does switching to kmalloc here introduce a regression by breaking the
hardware alignment requirements for the CHSC command block?
While __get_free_page() guarantees strict page alignment, kmalloc() does not
guarantee 4KB alignment. When debugging options like SLUB_DEBUG or KASAN are
enabled, they add metadata that shifts the returned object away from the page
boundary.
This buffer is eventually passed to the hardware chsc instruction in
chsc_scm_info():
drivers/s390/cio/chsc.c:chsc_scm_info() {
...
ccode = chsc(scm_area);
...
}
If the address is unaligned, could the CPU throw a specification exception
resulting in a kernel panic?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260910-s390-cio-ready-v2-0-e931086fdde1@kernel.org?part=13
^ permalink raw reply [flat|nested] 27+ messages in thread