Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2] iio: adc: mt6359: fix unchecked return value in mt6358_read_imp
From: Salah Triki @ 2026-04-27 16:57 UTC (permalink / raw)
  To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
	Matthias Brugger, AngeloGioacchino Del Regno
  Cc: linux-iio, linux-kernel, linux-arm-kernel, linux-mediatek,
	Salah Triki

In mt6358_read_imp(), the return value of regmap_read() is currently
ignored. This is problematic because if the bus read fails the variable
val_v remains uninitialized.

The function subsequently assigns this uninitialized stack value to
*vbat, leading to incorrect measurement results being reported to
the IIO subsystem without any error indication.

Update the function to check the return value of regmap_read(). Ensure
that mt6358_stop_imp_conv() is still called to clean up the hardware
state before returning the error code.

Fixes: 3587914bf61 ("iio: adc: Add support for MediaTek MT6357/8/9 Auxiliary ADC")
Signed-off-by: Salah Triki <salah.triki@gmail.com>
---
Changes in v2:
- Added Fixes tag.
- Re-examined the entire driver for unchecked regmap operations.
  While several regmap_write() and regmap_set_bits() calls also ignore
  return values, I focused on this specific regmap_read() in 
  mt6358_read_imp() because it leads to an uninitialized variable usage
  (val_v). This makes this fix critical for reporting correct data 
  to userspace.

 drivers/iio/adc/mt6359-auxadc.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/iio/adc/mt6359-auxadc.c b/drivers/iio/adc/mt6359-auxadc.c
index 6b9ed9b1fde2..f927bff4a26a 100644
--- a/drivers/iio/adc/mt6359-auxadc.c
+++ b/drivers/iio/adc/mt6359-auxadc.c
@@ -497,10 +497,13 @@ static int mt6358_read_imp(struct mt6359_auxadc *adc_dev,
 		return ret;
 
 	/* Read the params before stopping */
-	regmap_read(regmap, reg_adc0 + (cinfo->imp_adc_num << 1), &val_v);
+	ret = regmap_read(regmap, reg_adc0 + (cinfo->imp_adc_num << 1), &val_v);
 
 	mt6358_stop_imp_conv(adc_dev);
 
+	if (ret)
+		return ret;
+
 	if (vbat)
 		*vbat = val_v;
 	if (ibat)
-- 
2.43.0



^ permalink raw reply related

* Re: [PATCH v8 1/4] coresight: cti: Convert trigger usage fields to dynamic bitmaps and arrays
From: Leo Yan @ 2026-04-27 16:59 UTC (permalink / raw)
  To: Yingchao Deng
  Cc: Suzuki K Poulose, Mike Leach, James Clark, Alexander Shishkin,
	coresight, linux-arm-kernel, linux-kernel, linux-arm-msm,
	quic_yingdeng, Jinlong Mao, Tingwei Zhang, Jie Gan
In-Reply-To: <20260426-extended-cti-v8-1-23b900a4902f@oss.qualcomm.com>

On Sun, Apr 26, 2026 at 05:44:38PM +0800, Yingchao Deng wrote:

[...]

> @@ -316,23 +316,33 @@ static int cti_plat_process_filter_sigs(struct cti_drvdata *drvdata,
>  {
>  	struct cti_trig_grp *tg = NULL;
>  	int err = 0, nr_filter_sigs;
> +	int nr_trigs = drvdata->config.nr_trig_max;
>  
>  	nr_filter_sigs = cti_plat_count_sig_elements(fwnode,
>  						     CTI_DT_FILTER_OUT_SIGS);
>  	if (nr_filter_sigs == 0)
>  		return 0;
>  
> -	if (nr_filter_sigs > drvdata->config.nr_trig_max)
> +	if (nr_filter_sigs > nr_trigs)
>  		return -EINVAL;
>  
>  	tg = kzalloc_obj(*tg);
>  	if (!tg)
>  		return -ENOMEM;
>  
> +	tg->used_mask = bitmap_zalloc(nr_trigs, GFP_KERNEL);

Here would be:

  tg->used_mask = bitmap_zalloc(nr_filter_sigs, GFP_KERNEL);

> +	if (!tg->used_mask) {
> +		kfree(tg);
> +		return -ENOMEM;
> +	}
> +

It is likely this will have merge conflict with the new patch [1].

You might need to rebase this patch on the top of [1]. We need to
give [1] priority as it is a fix.

[1] https://lore.kernel.org/linux-arm-kernel/20260426-nr_sigs-v1-1-3b9df99dab97@oss.qualcomm.com/

Otherwise, LGTM:

Reviewed-by: Leo Yan <leo.yan@arm.com>


^ permalink raw reply

* Patch "arm64/mm: Enable batched TLB flush in unmap_hotplug_range()" has been added to the 6.12-stable tree
From: gregkh @ 2026-04-27 16:54 UTC (permalink / raw)
  To: anshuman.khandual, catalin.marinas, david, gregkh,
	linux-arm-kernel, ryan.roberts, will
  Cc: stable-commits


This is a note to let you know that I've just added the patch titled

    arm64/mm: Enable batched TLB flush in unmap_hotplug_range()

to the 6.12-stable tree which can be found at:
    http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary

The filename of the patch is:
     arm64-mm-enable-batched-tlb-flush-in-unmap_hotplug_range.patch
and it can be found in the queue-6.12 subdirectory.

If you, or anyone else, feels it should not be added to the stable tree,
please let <stable@vger.kernel.org> know about it.


From 48478b9f791376b4b89018d7afdfd06865498f65 Mon Sep 17 00:00:00 2001
From: Anshuman Khandual <anshuman.khandual@arm.com>
Date: Mon, 9 Mar 2026 02:57:24 +0000
Subject: arm64/mm: Enable batched TLB flush in unmap_hotplug_range()

From: Anshuman Khandual <anshuman.khandual@arm.com>

commit 48478b9f791376b4b89018d7afdfd06865498f65 upstream.

During a memory hot remove operation, both linear and vmemmap mappings for
the memory range being removed, get unmapped via unmap_hotplug_range() but
mapped pages get freed only for vmemmap mapping. This is just a sequential
operation where each table entry gets cleared, followed by a leaf specific
TLB flush, and then followed by memory free operation when applicable.

This approach was simple and uniform both for vmemmap and linear mappings.
But linear mapping might contain CONT marked block memory where it becomes
necessary to first clear out all entire in the range before a TLB flush.
This is as per the architecture requirement. Hence batch all TLB flushes
during the table tear down walk and finally do it in unmap_hotplug_range().

Prior to this fix, it was hypothetically possible for a speculative access
to a higher address in the contiguous block to fill the TLB with shattered
entries for the entire contiguous range after a lower address had already
been cleared and invalidated. Due to the table entries being shattered, the
subsequent TLB invalidation for the higher address would not then clear the
TLB entries for the lower address, meaning stale TLB entries could persist.

Besides it also helps in improving the performance via TLBI range operation
along with reduced synchronization instructions. The time spent executing
unmap_hotplug_range() improved 97% measured over a 2GB memory hot removal
in KVM guest.

This scheme is not applicable during vmemmap mapping tear down where memory
needs to be freed and hence a TLB flush is required after clearing out page
table entry.

Cc: Will Deacon <will@kernel.org>
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
Closes: https://lore.kernel.org/all/aWZYXhrT6D2M-7-N@willie-the-truck/
Fixes: bbd6ec605c0f ("arm64/mm: Enable memory hot remove")
Cc: stable@vger.kernel.org
Reviewed-by: David Hildenbrand (Arm) <david@kernel.org>
Reviewed-by: Ryan Roberts <ryan.roberts@arm.com>
Signed-off-by: Ryan Roberts <ryan.roberts@arm.com>
Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
 arch/arm64/mm/mmu.c |   36 ++++++++++++++++++++----------------
 1 file changed, 20 insertions(+), 16 deletions(-)

--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -869,10 +869,14 @@ static void unmap_hotplug_pte_range(pmd_
 
 		WARN_ON(!pte_present(pte));
 		__pte_clear(&init_mm, addr, ptep);
-		flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
-		if (free_mapped)
+		if (free_mapped) {
+			/* CONT blocks are not supported in the vmemmap */
+			WARN_ON(pte_cont(pte));
+			flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
 			free_hotplug_page_range(pte_page(pte),
 						PAGE_SIZE, altmap);
+		}
+		/* unmap_hotplug_range() flushes TLB for !free_mapped */
 	} while (addr += PAGE_SIZE, addr < end);
 }
 
@@ -893,15 +897,14 @@ static void unmap_hotplug_pmd_range(pud_
 		WARN_ON(!pmd_present(pmd));
 		if (pmd_sect(pmd)) {
 			pmd_clear(pmdp);
-
-			/*
-			 * One TLBI should be sufficient here as the PMD_SIZE
-			 * range is mapped with a single block entry.
-			 */
-			flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
-			if (free_mapped)
+			if (free_mapped) {
+				/* CONT blocks are not supported in the vmemmap */
+				WARN_ON(pmd_cont(pmd));
+				flush_tlb_kernel_range(addr, addr + PMD_SIZE);
 				free_hotplug_page_range(pmd_page(pmd),
 							PMD_SIZE, altmap);
+			}
+			/* unmap_hotplug_range() flushes TLB for !free_mapped */
 			continue;
 		}
 		WARN_ON(!pmd_table(pmd));
@@ -926,15 +929,12 @@ static void unmap_hotplug_pud_range(p4d_
 		WARN_ON(!pud_present(pud));
 		if (pud_sect(pud)) {
 			pud_clear(pudp);
-
-			/*
-			 * One TLBI should be sufficient here as the PUD_SIZE
-			 * range is mapped with a single block entry.
-			 */
-			flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
-			if (free_mapped)
+			if (free_mapped) {
+				flush_tlb_kernel_range(addr, addr + PUD_SIZE);
 				free_hotplug_page_range(pud_page(pud),
 							PUD_SIZE, altmap);
+			}
+			/* unmap_hotplug_range() flushes TLB for !free_mapped */
 			continue;
 		}
 		WARN_ON(!pud_table(pud));
@@ -964,6 +964,7 @@ static void unmap_hotplug_p4d_range(pgd_
 static void unmap_hotplug_range(unsigned long addr, unsigned long end,
 				bool free_mapped, struct vmem_altmap *altmap)
 {
+	unsigned long start = addr;
 	unsigned long next;
 	pgd_t *pgdp, pgd;
 
@@ -985,6 +986,9 @@ static void unmap_hotplug_range(unsigned
 		WARN_ON(!pgd_present(pgd));
 		unmap_hotplug_p4d_range(pgdp, addr, next, free_mapped, altmap);
 	} while (addr = next, addr < end);
+
+	if (!free_mapped)
+		flush_tlb_kernel_range(start, end);
 }
 
 static void free_empty_pte_table(pmd_t *pmdp, unsigned long addr,


Patches currently in stable-queue which might be from anshuman.khandual@arm.com are

queue-6.12/arm64-mm-enable-batched-tlb-flush-in-unmap_hotplug_range.patch


^ permalink raw reply

* Patch "arm64/mm: Enable batched TLB flush in unmap_hotplug_range()" has been added to the 6.18-stable tree
From: gregkh @ 2026-04-27 16:54 UTC (permalink / raw)
  To: anshuman.khandual, catalin.marinas, david, gregkh,
	linux-arm-kernel, ryan.roberts, will
  Cc: stable-commits


This is a note to let you know that I've just added the patch titled

    arm64/mm: Enable batched TLB flush in unmap_hotplug_range()

to the 6.18-stable tree which can be found at:
    http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary

The filename of the patch is:
     arm64-mm-enable-batched-tlb-flush-in-unmap_hotplug_range.patch
and it can be found in the queue-6.18 subdirectory.

If you, or anyone else, feels it should not be added to the stable tree,
please let <stable@vger.kernel.org> know about it.


From 48478b9f791376b4b89018d7afdfd06865498f65 Mon Sep 17 00:00:00 2001
From: Anshuman Khandual <anshuman.khandual@arm.com>
Date: Mon, 9 Mar 2026 02:57:24 +0000
Subject: arm64/mm: Enable batched TLB flush in unmap_hotplug_range()

From: Anshuman Khandual <anshuman.khandual@arm.com>

commit 48478b9f791376b4b89018d7afdfd06865498f65 upstream.

During a memory hot remove operation, both linear and vmemmap mappings for
the memory range being removed, get unmapped via unmap_hotplug_range() but
mapped pages get freed only for vmemmap mapping. This is just a sequential
operation where each table entry gets cleared, followed by a leaf specific
TLB flush, and then followed by memory free operation when applicable.

This approach was simple and uniform both for vmemmap and linear mappings.
But linear mapping might contain CONT marked block memory where it becomes
necessary to first clear out all entire in the range before a TLB flush.
This is as per the architecture requirement. Hence batch all TLB flushes
during the table tear down walk and finally do it in unmap_hotplug_range().

Prior to this fix, it was hypothetically possible for a speculative access
to a higher address in the contiguous block to fill the TLB with shattered
entries for the entire contiguous range after a lower address had already
been cleared and invalidated. Due to the table entries being shattered, the
subsequent TLB invalidation for the higher address would not then clear the
TLB entries for the lower address, meaning stale TLB entries could persist.

Besides it also helps in improving the performance via TLBI range operation
along with reduced synchronization instructions. The time spent executing
unmap_hotplug_range() improved 97% measured over a 2GB memory hot removal
in KVM guest.

This scheme is not applicable during vmemmap mapping tear down where memory
needs to be freed and hence a TLB flush is required after clearing out page
table entry.

Cc: Will Deacon <will@kernel.org>
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
Closes: https://lore.kernel.org/all/aWZYXhrT6D2M-7-N@willie-the-truck/
Fixes: bbd6ec605c0f ("arm64/mm: Enable memory hot remove")
Cc: stable@vger.kernel.org
Reviewed-by: David Hildenbrand (Arm) <david@kernel.org>
Reviewed-by: Ryan Roberts <ryan.roberts@arm.com>
Signed-off-by: Ryan Roberts <ryan.roberts@arm.com>
Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
 arch/arm64/mm/mmu.c |   36 ++++++++++++++++++++----------------
 1 file changed, 20 insertions(+), 16 deletions(-)

--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -1465,10 +1465,14 @@ static void unmap_hotplug_pte_range(pmd_
 
 		WARN_ON(!pte_present(pte));
 		__pte_clear(&init_mm, addr, ptep);
-		flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
-		if (free_mapped)
+		if (free_mapped) {
+			/* CONT blocks are not supported in the vmemmap */
+			WARN_ON(pte_cont(pte));
+			flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
 			free_hotplug_page_range(pte_page(pte),
 						PAGE_SIZE, altmap);
+		}
+		/* unmap_hotplug_range() flushes TLB for !free_mapped */
 	} while (addr += PAGE_SIZE, addr < end);
 }
 
@@ -1489,15 +1493,14 @@ static void unmap_hotplug_pmd_range(pud_
 		WARN_ON(!pmd_present(pmd));
 		if (pmd_sect(pmd)) {
 			pmd_clear(pmdp);
-
-			/*
-			 * One TLBI should be sufficient here as the PMD_SIZE
-			 * range is mapped with a single block entry.
-			 */
-			flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
-			if (free_mapped)
+			if (free_mapped) {
+				/* CONT blocks are not supported in the vmemmap */
+				WARN_ON(pmd_cont(pmd));
+				flush_tlb_kernel_range(addr, addr + PMD_SIZE);
 				free_hotplug_page_range(pmd_page(pmd),
 							PMD_SIZE, altmap);
+			}
+			/* unmap_hotplug_range() flushes TLB for !free_mapped */
 			continue;
 		}
 		WARN_ON(!pmd_table(pmd));
@@ -1522,15 +1525,12 @@ static void unmap_hotplug_pud_range(p4d_
 		WARN_ON(!pud_present(pud));
 		if (pud_sect(pud)) {
 			pud_clear(pudp);
-
-			/*
-			 * One TLBI should be sufficient here as the PUD_SIZE
-			 * range is mapped with a single block entry.
-			 */
-			flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
-			if (free_mapped)
+			if (free_mapped) {
+				flush_tlb_kernel_range(addr, addr + PUD_SIZE);
 				free_hotplug_page_range(pud_page(pud),
 							PUD_SIZE, altmap);
+			}
+			/* unmap_hotplug_range() flushes TLB for !free_mapped */
 			continue;
 		}
 		WARN_ON(!pud_table(pud));
@@ -1560,6 +1560,7 @@ static void unmap_hotplug_p4d_range(pgd_
 static void unmap_hotplug_range(unsigned long addr, unsigned long end,
 				bool free_mapped, struct vmem_altmap *altmap)
 {
+	unsigned long start = addr;
 	unsigned long next;
 	pgd_t *pgdp, pgd;
 
@@ -1581,6 +1582,9 @@ static void unmap_hotplug_range(unsigned
 		WARN_ON(!pgd_present(pgd));
 		unmap_hotplug_p4d_range(pgdp, addr, next, free_mapped, altmap);
 	} while (addr = next, addr < end);
+
+	if (!free_mapped)
+		flush_tlb_kernel_range(start, end);
 }
 
 static void free_empty_pte_table(pmd_t *pmdp, unsigned long addr,


Patches currently in stable-queue which might be from anshuman.khandual@arm.com are

queue-6.18/arm64-mm-enable-batched-tlb-flush-in-unmap_hotplug_range.patch


^ permalink raw reply

* Patch "arm64/mm: Enable batched TLB flush in unmap_hotplug_range()" has been added to the 7.0-stable tree
From: gregkh @ 2026-04-27 16:55 UTC (permalink / raw)
  To: anshuman.khandual, catalin.marinas, david, gregkh,
	linux-arm-kernel, ryan.roberts, will
  Cc: stable-commits


This is a note to let you know that I've just added the patch titled

    arm64/mm: Enable batched TLB flush in unmap_hotplug_range()

to the 7.0-stable tree which can be found at:
    http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary

The filename of the patch is:
     arm64-mm-enable-batched-tlb-flush-in-unmap_hotplug_range.patch
and it can be found in the queue-7.0 subdirectory.

If you, or anyone else, feels it should not be added to the stable tree,
please let <stable@vger.kernel.org> know about it.


From 48478b9f791376b4b89018d7afdfd06865498f65 Mon Sep 17 00:00:00 2001
From: Anshuman Khandual <anshuman.khandual@arm.com>
Date: Mon, 9 Mar 2026 02:57:24 +0000
Subject: arm64/mm: Enable batched TLB flush in unmap_hotplug_range()

From: Anshuman Khandual <anshuman.khandual@arm.com>

commit 48478b9f791376b4b89018d7afdfd06865498f65 upstream.

During a memory hot remove operation, both linear and vmemmap mappings for
the memory range being removed, get unmapped via unmap_hotplug_range() but
mapped pages get freed only for vmemmap mapping. This is just a sequential
operation where each table entry gets cleared, followed by a leaf specific
TLB flush, and then followed by memory free operation when applicable.

This approach was simple and uniform both for vmemmap and linear mappings.
But linear mapping might contain CONT marked block memory where it becomes
necessary to first clear out all entire in the range before a TLB flush.
This is as per the architecture requirement. Hence batch all TLB flushes
during the table tear down walk and finally do it in unmap_hotplug_range().

Prior to this fix, it was hypothetically possible for a speculative access
to a higher address in the contiguous block to fill the TLB with shattered
entries for the entire contiguous range after a lower address had already
been cleared and invalidated. Due to the table entries being shattered, the
subsequent TLB invalidation for the higher address would not then clear the
TLB entries for the lower address, meaning stale TLB entries could persist.

Besides it also helps in improving the performance via TLBI range operation
along with reduced synchronization instructions. The time spent executing
unmap_hotplug_range() improved 97% measured over a 2GB memory hot removal
in KVM guest.

This scheme is not applicable during vmemmap mapping tear down where memory
needs to be freed and hence a TLB flush is required after clearing out page
table entry.

Cc: Will Deacon <will@kernel.org>
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
Closes: https://lore.kernel.org/all/aWZYXhrT6D2M-7-N@willie-the-truck/
Fixes: bbd6ec605c0f ("arm64/mm: Enable memory hot remove")
Cc: stable@vger.kernel.org
Reviewed-by: David Hildenbrand (Arm) <david@kernel.org>
Reviewed-by: Ryan Roberts <ryan.roberts@arm.com>
Signed-off-by: Ryan Roberts <ryan.roberts@arm.com>
Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
 arch/arm64/mm/mmu.c |   36 ++++++++++++++++++++----------------
 1 file changed, 20 insertions(+), 16 deletions(-)

--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -1462,10 +1462,14 @@ static void unmap_hotplug_pte_range(pmd_
 
 		WARN_ON(!pte_present(pte));
 		__pte_clear(&init_mm, addr, ptep);
-		flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
-		if (free_mapped)
+		if (free_mapped) {
+			/* CONT blocks are not supported in the vmemmap */
+			WARN_ON(pte_cont(pte));
+			flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
 			free_hotplug_page_range(pte_page(pte),
 						PAGE_SIZE, altmap);
+		}
+		/* unmap_hotplug_range() flushes TLB for !free_mapped */
 	} while (addr += PAGE_SIZE, addr < end);
 }
 
@@ -1486,15 +1490,14 @@ static void unmap_hotplug_pmd_range(pud_
 		WARN_ON(!pmd_present(pmd));
 		if (pmd_sect(pmd)) {
 			pmd_clear(pmdp);
-
-			/*
-			 * One TLBI should be sufficient here as the PMD_SIZE
-			 * range is mapped with a single block entry.
-			 */
-			flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
-			if (free_mapped)
+			if (free_mapped) {
+				/* CONT blocks are not supported in the vmemmap */
+				WARN_ON(pmd_cont(pmd));
+				flush_tlb_kernel_range(addr, addr + PMD_SIZE);
 				free_hotplug_page_range(pmd_page(pmd),
 							PMD_SIZE, altmap);
+			}
+			/* unmap_hotplug_range() flushes TLB for !free_mapped */
 			continue;
 		}
 		WARN_ON(!pmd_table(pmd));
@@ -1519,15 +1522,12 @@ static void unmap_hotplug_pud_range(p4d_
 		WARN_ON(!pud_present(pud));
 		if (pud_sect(pud)) {
 			pud_clear(pudp);
-
-			/*
-			 * One TLBI should be sufficient here as the PUD_SIZE
-			 * range is mapped with a single block entry.
-			 */
-			flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
-			if (free_mapped)
+			if (free_mapped) {
+				flush_tlb_kernel_range(addr, addr + PUD_SIZE);
 				free_hotplug_page_range(pud_page(pud),
 							PUD_SIZE, altmap);
+			}
+			/* unmap_hotplug_range() flushes TLB for !free_mapped */
 			continue;
 		}
 		WARN_ON(!pud_table(pud));
@@ -1557,6 +1557,7 @@ static void unmap_hotplug_p4d_range(pgd_
 static void unmap_hotplug_range(unsigned long addr, unsigned long end,
 				bool free_mapped, struct vmem_altmap *altmap)
 {
+	unsigned long start = addr;
 	unsigned long next;
 	pgd_t *pgdp, pgd;
 
@@ -1578,6 +1579,9 @@ static void unmap_hotplug_range(unsigned
 		WARN_ON(!pgd_present(pgd));
 		unmap_hotplug_p4d_range(pgdp, addr, next, free_mapped, altmap);
 	} while (addr = next, addr < end);
+
+	if (!free_mapped)
+		flush_tlb_kernel_range(start, end);
 }
 
 static void free_empty_pte_table(pmd_t *pmdp, unsigned long addr,


Patches currently in stable-queue which might be from anshuman.khandual@arm.com are

queue-7.0/arm64-mm-enable-batched-tlb-flush-in-unmap_hotplug_range.patch


^ permalink raw reply

* [PATCH] staging: media: imx: remove unnecessary braces from single statement if block
From: Shyam Sunder Reddy Padira @ 2026-04-27 17:06 UTC (permalink / raw)
  To: slongerbeam, p.zabel, mchehab, gregkh, s.hauer
  Cc: Frank.Li, kernel, festevam, imx, linux-media, linux-staging,
	linux-arm-kernel, linux-kernel, Shyam Sunder Reddy Padira

Remove braces from a single statement if block to
follow kernel coding style and improve readability.

No functional change.

Signed-off-by: Shyam Sunder Reddy Padira <shyamsunderreddypadira@gmail.com>
---
 drivers/staging/media/imx/imx-media-of.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/staging/media/imx/imx-media-of.c b/drivers/staging/media/imx/imx-media-of.c
index bb28daa4d713..7413551052ae 100644
--- a/drivers/staging/media/imx/imx-media-of.c
+++ b/drivers/staging/media/imx/imx-media-of.c
@@ -57,9 +57,8 @@ int imx_media_add_of_subdevs(struct imx_media_dev *imxmd,
 		of_node_put(csi_np);
 		if (ret) {
 			/* unavailable or already added is not an error */
-			if (ret == -ENODEV || ret == -EEXIST) {
+			if (ret == -ENODEV || ret == -EEXIST)
 				continue;
-			}
 
 			/* other error, can't continue */
 			return ret;
-- 
2.43.0



^ permalink raw reply related

* Re: [PATCH] iio: adc: mt6359: fix unchecked return value in mt6358_read_imp
From: Jonathan Cameron @ 2026-04-27 17:15 UTC (permalink / raw)
  To: Salah Triki
  Cc: Andy Shevchenko, David Lechner, Nuno Sá, Andy Shevchenko,
	Matthias Brugger, AngeloGioacchino Del Regno, linux-iio,
	linux-kernel, linux-arm-kernel, linux-mediatek
In-Reply-To: <ae9JFucpXBPwwGzr@pc>

On Mon, 27 Apr 2026 12:31:34 +0100
Salah Triki <salah.triki@gmail.com> wrote:

> On Mon, Apr 27, 2026 at 12:14:40PM +0300, Andy Shevchenko wrote:
> > On Mon, Apr 27, 2026 at 09:54:57AM +0100, Salah Triki wrote:  
> > > In mt6358_read_imp(), the return value of regmap_read() is currently
> > > ignored. This is problematic because if the bus read fails the variable
> > > val_v remains uninitialized.
> > > 
> > > The function subsequently assigns this uninitialized stack value to
> > > *vbat, leading to incorrect measurement results being reported to
> > > the IIO subsystem without any error indication.
> > > 
> > > Update the function to check the return value of regmap_read(). Ensure
> > > that mt6358_stop_imp_conv() is still called to clean up the hardware
> > > state before returning the error code.  
> > 
> > Sounds like this deserves a Fixes tag, but the problem is that the whole driver
> > is written like this. Why does having this fixed make it special?
> > 
> > -- 
> > With Best Regards,
> > Andy Shevchenko
> > 
> >   
> 
> You are right, Andy. I noticed that several other functions in this driver
> share the same issue. I will send a V2 that addresses all unchecked
> regmap_read() calls in this file and I will include the Fixes tag. Thanks
> for the feedback.
What's the bus in this case?

Whilst maybe we should be checking them, it is not uncommon to forgo those
checks in internal busses.  In general they should be more reliable than
something going over PCB tracks like spi or i2c.

I'd be tempted to fix the uninitialized variable by setting it at declaration
rather than worry too much about checking all the regmap return values.

> 
> Best Regards
> Salah Triki



^ permalink raw reply

* [RFC PATCH v1 0/9] uaccess: Convert small fixed size copy_{to/from}_user() to scoped user access
From: Christophe Leroy (CS GROUP) @ 2026-04-27 17:13 UTC (permalink / raw)
  To: Yury Norov, Andrew Morton, Linus Torvalds, David Laight,
	Thomas Gleixner
  Cc: Christophe Leroy (CS GROUP), linux-alpha, linux-kernel,
	linux-snps-arc, linux-arm-kernel, linux-mips, linuxppc-dev, kvm,
	linux-riscv, linux-s390, sparclinux, linux-um, dmaengine,
	linux-efi, linux-fsi, amd-gfx, dri-devel, intel-gfx, linux-wpan,
	netdev, linux-wireless, linux-spi, linux-media, linux-staging,
	linux-serial, linux-usb, xen-devel, linux-fsdevel, ocfs2-devel,
	bpf, kasan-dev, linux-mm, linux-x25, rust-for-linux, linux-sound,
	sound-open-firmware, linux-csky, linux-hexagon, loongarch,
	linux-m68k, linux-openrisc, linux-parisc, linux-sh, linux-arch

A lot of copy_from_user() and copy_to_user() perform copies of small
fixed size pieces of data between kernel and userspace, and don't
care about partial copies.

copy_from_user() and copy_to_user() are big functions optimised for
copying large amount of data, with cache management, etc ... This is
often overkill for small copies that could just be inlined instead.

What makes things a bit more tricky is that those copy functions
are designed to handle partial copies in case of page fault. But among
the 6000 callers of those functions, only 2% really care about the
quantity of no-copied data that those functions return. All other ones
fails as soon as the returned value is not 0, returning -EACCESS.

So first step in this series is to introduce variants called
copy_from_user_partial() and copy_to_user_partial() which will be
called by the 2% users that care about the partial copy, then the
original copy_from_user() and copy_to_user() are changed to return
-EFAULT when the copy fails.

Then the second step is to implement copy of small fixed-size data
with scoped user access instead of calling the arch specific heavy
user copy functions.

Patch 5, can be split in different patches for each archicture or
subsystem, but let's get a first feedback and agree on the principle.

Christophe Leroy (CS GROUP) (9):
  uaccess: Split check_zeroed_user() out of usercopy.c
  uaccess: Convert INLINE_COPY_{TO/FROM}_USER to kconfig and reduce
    ifdefery
  x86/umip: Be stricter in fixup_umip_exception()
  uaccess: Introduce copy_{to/from}_user_partial()
  uaccess: Switch to copy_{to/from}_user_partial() when relevant
  uaccess: Change copy_{to/from}_user to return -EFAULT
  x86: Add unsafe_copy_from_user()
  arm64: Add unsafe_copy_from_user()
  uaccess: Convert small fixed size copy_{to/from}_user() to scoped user
    access

 arch/alpha/Kconfig                            |   1 +
 arch/alpha/kernel/osf_sys.c                   |   4 +-
 arch/alpha/kernel/termios.c                   |   2 +-
 arch/arc/include/asm/uaccess.h                |   3 -
 arch/arc/kernel/disasm.c                      |   2 +-
 arch/arm/include/asm/uaccess.h                |   2 -
 arch/arm64/include/asm/gcs.h                  |   2 +-
 arch/arm64/include/asm/uaccess.h              |  30 +++--
 arch/arm64/kernel/signal32.c                  |   2 +-
 arch/csky/Kconfig                             |   1 +
 arch/hexagon/include/asm/uaccess.h            |   3 -
 arch/loongarch/include/asm/uaccess.h          |   3 -
 arch/m68k/include/asm/uaccess.h               |   3 -
 arch/microblaze/include/asm/uaccess.h         |   2 -
 arch/mips/include/asm/uaccess.h               |   3 -
 arch/mips/kernel/rtlx.c                       |   8 +-
 arch/mips/kernel/vpe.c                        |   2 +-
 arch/nios2/include/asm/uaccess.h              |   2 -
 arch/openrisc/include/asm/uaccess.h           |   2 -
 arch/parisc/include/asm/uaccess.h             |   3 -
 arch/powerpc/Kconfig                          |   1 +
 arch/powerpc/kvm/book3s_64_mmu_hv.c           |   4 +-
 arch/powerpc/kvm/book3s_64_mmu_radix.c        |   4 +-
 arch/powerpc/kvm/book3s_hv.c                  |   2 +-
 arch/riscv/Kconfig                            |   1 +
 arch/riscv/kernel/signal.c                    |   2 +-
 arch/s390/include/asm/idals.h                 |   8 +-
 arch/s390/include/asm/uaccess.h               |   3 -
 arch/sh/include/asm/uaccess.h                 |   2 -
 arch/sparc/include/asm/uaccess_32.h           |   3 -
 arch/sparc/include/asm/uaccess_64.h           |   2 -
 arch/sparc/kernel/termios.c                   |   2 +-
 arch/um/include/asm/uaccess.h                 |   3 -
 arch/um/kernel/process.c                      |   2 +-
 arch/x86/Kconfig                              |   1 +
 arch/x86/include/asm/uaccess.h                |  29 ++++-
 arch/x86/kernel/umip.c                        |   2 +-
 arch/x86/lib/insn-eval.c                      |   2 +-
 arch/x86/um/signal.c                          |   2 +-
 arch/xtensa/include/asm/uaccess.h             |   2 -
 drivers/android/binder_alloc.c                |   2 +-
 drivers/comedi/comedi_fops.c                  |   4 +-
 drivers/dma/idxd/cdev.c                       |   2 +-
 drivers/firmware/efi/test/efi_test.c          |   2 +-
 drivers/fsi/fsi-scom.c                        |   2 +-
 .../amd/display/amdgpu_dm/amdgpu_dm_debugfs.c |   2 +-
 drivers/gpu/drm/i915/gt/intel_sseu.c          |   4 +-
 drivers/gpu/drm/i915/i915_gem.c               |   4 +-
 drivers/hwtracing/intel_th/msu.c              |   2 +-
 drivers/misc/ibmvmc.c                         |   2 +-
 drivers/misc/vmw_vmci/vmci_host.c             |   2 +-
 drivers/most/most_cdev.c                      |   2 +-
 drivers/net/ieee802154/ca8210.c               |   4 +-
 drivers/net/wireless/ath/wil6210/debugfs.c    |   2 +-
 .../intel/iwlwifi/pcie/gen1_2/trans.c         |   2 +-
 drivers/net/wireless/ti/wlcore/debugfs.c      |   2 +-
 drivers/ps3/ps3-lpm.c                         |   2 +-
 drivers/s390/crypto/zcrypt_api.h              |   4 +-
 drivers/spi/spidev.c                          |   2 +-
 .../staging/media/atomisp/pci/atomisp_cmd.c   |   8 +-
 drivers/tty/tty_ioctl.c                       |  14 +--
 drivers/tty/vt/vc_screen.c                    |   4 +-
 drivers/usb/gadget/function/f_hid.c           |   4 +-
 drivers/usb/gadget/function/f_printer.c       |   2 +-
 drivers/vfio/vfio_iommu_type1.c               |   4 +-
 drivers/xen/xenbus/xenbus_dev_frontend.c      |   2 +-
 fs/namespace.c                                |   2 +-
 fs/ocfs2/dlmfs/dlmfs.c                        |   2 +-
 fs/proc/base.c                                |   4 +-
 include/asm-generic/uaccess.h                 |   2 -
 include/linux/bpfptr.h                        |   2 +-
 include/linux/sockptr.h                       |   4 +-
 include/linux/uaccess.h                       | 107 ++++++++++++++----
 ipc/msg.c                                     |   8 +-
 ipc/sem.c                                     |   8 +-
 ipc/shm.c                                     |  18 +--
 kernel/regset.c                               |   2 +-
 kernel/sys.c                                  |   4 +-
 lib/Kconfig                                   |   3 +
 lib/Makefile                                  |   4 +-
 lib/kfifo.c                                   |   8 +-
 lib/{usercopy.c => usercheck.c}               |  22 ----
 lib/usercopy.c                                |  66 -----------
 mm/kasan/kasan_test_c.c                       |   4 +-
 mm/memory.c                                   |   2 +-
 net/x25/af_x25.c                              |   2 +-
 rust/helpers/uaccess.c                        |   6 +-
 sound/pci/emu10k1/emufx.c                     |   4 +-
 sound/pci/rme9652/hdsp.c                      |   6 +-
 sound/soc/intel/avs/probes.c                  |   6 +-
 sound/soc/sof/compress.c                      |  12 +-
 sound/soc/sof/sof-client-probes.c             |   6 +-
 92 files changed, 269 insertions(+), 288 deletions(-)
 copy lib/{usercopy.c => usercheck.c} (73%)

-- 
2.49.0



^ permalink raw reply

* [RFC PATCH v1 1/9] uaccess: Split check_zeroed_user() out of usercopy.c
From: Christophe Leroy (CS GROUP) @ 2026-04-27 17:13 UTC (permalink / raw)
  To: Yury Norov, Andrew Morton, Linus Torvalds, David Laight,
	Thomas Gleixner
  Cc: Christophe Leroy (CS GROUP), linux-alpha, linux-kernel,
	linux-snps-arc, linux-arm-kernel, linux-mips, linuxppc-dev, kvm,
	linux-riscv, linux-s390, sparclinux, linux-um, dmaengine,
	linux-efi, linux-fsi, amd-gfx, dri-devel, intel-gfx, linux-wpan,
	netdev, linux-wireless, linux-spi, linux-media, linux-staging,
	linux-serial, linux-usb, xen-devel, linux-fsdevel, ocfs2-devel,
	bpf, kasan-dev, linux-mm, linux-x25, rust-for-linux, linux-sound,
	sound-open-firmware, linux-csky, linux-hexagon, loongarch,
	linux-m68k, linux-openrisc, linux-parisc, linux-sh, linux-arch
In-Reply-To: <cover.1777306795.git.chleroy@kernel.org>

Until commit f5a1a536fa14 ("lib: introduce copy_struct_from_user()
helper"), lib/usercopy.c was containing only the out-line version
of user copy fonctions.

That commit added function check_zeroed_user() into the same file.
Move that function into a new file named usercheck.c, so that next
patch can change usercopy.c build to a conditional build.

Signed-off-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
---
 lib/Makefile                    |  1 +
 lib/{usercopy.c => usercheck.c} | 22 ------------
 lib/usercopy.c                  | 62 ---------------------------------
 3 files changed, 1 insertion(+), 84 deletions(-)
 copy lib/{usercopy.c => usercheck.c} (73%)

diff --git a/lib/Makefile b/lib/Makefile
index f33a24bf1c19..7c0334d7675b 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -59,6 +59,7 @@ obj-y += bcd.o sort.o parser.o debug_locks.o random32.o \
 	 percpu-refcount.o rhashtable.o base64.o \
 	 once.o refcount.o rcuref.o usercopy.o errseq.o bucket_locks.o \
 	 generic-radix-tree.o bitmap-str.o
+obj-y += usercheck.o
 obj-y += string_helpers.o
 obj-y += hexdump.o
 obj-$(CONFIG_TEST_HEXDUMP) += test_hexdump.o
diff --git a/lib/usercopy.c b/lib/usercheck.c
similarity index 73%
copy from lib/usercopy.c
copy to lib/usercheck.c
index b00a3a957de6..15b0d9a18435 100644
--- a/lib/usercopy.c
+++ b/lib/usercheck.c
@@ -2,32 +2,10 @@
 #include <linux/compiler.h>
 #include <linux/errno.h>
 #include <linux/export.h>
-#include <linux/fault-inject-usercopy.h>
-#include <linux/instrumented.h>
 #include <linux/kernel.h>
-#include <linux/nospec.h>
-#include <linux/string.h>
 #include <linux/uaccess.h>
 #include <linux/wordpart.h>
 
-/* out-of-line parts */
-
-#if !defined(INLINE_COPY_FROM_USER)
-unsigned long _copy_from_user(void *to, const void __user *from, unsigned long n)
-{
-	return _inline_copy_from_user(to, from, n);
-}
-EXPORT_SYMBOL(_copy_from_user);
-#endif
-
-#if !defined(INLINE_COPY_TO_USER)
-unsigned long _copy_to_user(void __user *to, const void *from, unsigned long n)
-{
-	return _inline_copy_to_user(to, from, n);
-}
-EXPORT_SYMBOL(_copy_to_user);
-#endif
-
 /**
  * check_zeroed_user: check if a userspace buffer only contains zero bytes
  * @from: Source address, in userspace.
diff --git a/lib/usercopy.c b/lib/usercopy.c
index b00a3a957de6..7a93f56d81dd 100644
--- a/lib/usercopy.c
+++ b/lib/usercopy.c
@@ -1,14 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
-#include <linux/compiler.h>
-#include <linux/errno.h>
 #include <linux/export.h>
-#include <linux/fault-inject-usercopy.h>
-#include <linux/instrumented.h>
-#include <linux/kernel.h>
-#include <linux/nospec.h>
-#include <linux/string.h>
 #include <linux/uaccess.h>
-#include <linux/wordpart.h>
 
 /* out-of-line parts */
 
@@ -27,57 +19,3 @@ unsigned long _copy_to_user(void __user *to, const void *from, unsigned long n)
 }
 EXPORT_SYMBOL(_copy_to_user);
 #endif
-
-/**
- * check_zeroed_user: check if a userspace buffer only contains zero bytes
- * @from: Source address, in userspace.
- * @size: Size of buffer.
- *
- * This is effectively shorthand for "memchr_inv(from, 0, size) == NULL" for
- * userspace addresses (and is more efficient because we don't care where the
- * first non-zero byte is).
- *
- * Returns:
- *  * 0: There were non-zero bytes present in the buffer.
- *  * 1: The buffer was full of zero bytes.
- *  * -EFAULT: access to userspace failed.
- */
-int check_zeroed_user(const void __user *from, size_t size)
-{
-	unsigned long val;
-	uintptr_t align = (uintptr_t) from % sizeof(unsigned long);
-
-	if (unlikely(size == 0))
-		return 1;
-
-	from -= align;
-	size += align;
-
-	if (!user_read_access_begin(from, size))
-		return -EFAULT;
-
-	unsafe_get_user(val, (unsigned long __user *) from, err_fault);
-	if (align)
-		val &= ~aligned_byte_mask(align);
-
-	while (size > sizeof(unsigned long)) {
-		if (unlikely(val))
-			goto done;
-
-		from += sizeof(unsigned long);
-		size -= sizeof(unsigned long);
-
-		unsafe_get_user(val, (unsigned long __user *) from, err_fault);
-	}
-
-	if (size < sizeof(unsigned long))
-		val &= aligned_byte_mask(size);
-
-done:
-	user_read_access_end();
-	return (val == 0);
-err_fault:
-	user_read_access_end();
-	return -EFAULT;
-}
-EXPORT_SYMBOL(check_zeroed_user);
-- 
2.49.0



^ permalink raw reply related

* [RFC PATCH v1 2/9] uaccess: Convert INLINE_COPY_{TO/FROM}_USER to kconfig and reduce ifdefery
From: Christophe Leroy (CS GROUP) @ 2026-04-27 17:13 UTC (permalink / raw)
  To: Yury Norov, Andrew Morton, Linus Torvalds, David Laight,
	Thomas Gleixner
  Cc: Christophe Leroy (CS GROUP), linux-alpha, linux-kernel,
	linux-snps-arc, linux-arm-kernel, linux-mips, linuxppc-dev, kvm,
	linux-riscv, linux-s390, sparclinux, linux-um, dmaengine,
	linux-efi, linux-fsi, amd-gfx, dri-devel, intel-gfx, linux-wpan,
	netdev, linux-wireless, linux-spi, linux-media, linux-staging,
	linux-serial, linux-usb, xen-devel, linux-fsdevel, ocfs2-devel,
	bpf, kasan-dev, linux-mm, linux-x25, rust-for-linux, linux-sound,
	sound-open-firmware, linux-csky, linux-hexagon, loongarch,
	linux-m68k, linux-openrisc, linux-parisc, linux-sh, linux-arch
In-Reply-To: <cover.1777306795.git.chleroy@kernel.org>

Among the 21 architectures supported by the kernel, 16 define both
INLINE_COPY_TO_USER and INLINE_COPY_FROM_USER while the 5 other ones
don't define any of the two.

To simplify and reduce risk of mistakes, convert them to a single
kconfig item named CONFIG_ARCH_WANTS_NOINLINE_COPY which will be
selected by the 5 architectures that don't want inlined copy.

To minimise complication in a later patch, also remove
ifdefery and replace it with IS_ENABLED().

Signed-off-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
---
 arch/alpha/Kconfig                    |  1 +
 arch/arc/include/asm/uaccess.h        |  3 ---
 arch/arm/include/asm/uaccess.h        |  2 --
 arch/arm64/include/asm/uaccess.h      |  3 ---
 arch/csky/Kconfig                     |  1 +
 arch/hexagon/include/asm/uaccess.h    |  3 ---
 arch/loongarch/include/asm/uaccess.h  |  3 ---
 arch/m68k/include/asm/uaccess.h       |  3 ---
 arch/microblaze/include/asm/uaccess.h |  2 --
 arch/mips/include/asm/uaccess.h       |  3 ---
 arch/nios2/include/asm/uaccess.h      |  2 --
 arch/openrisc/include/asm/uaccess.h   |  2 --
 arch/parisc/include/asm/uaccess.h     |  3 ---
 arch/powerpc/Kconfig                  |  1 +
 arch/riscv/Kconfig                    |  1 +
 arch/s390/include/asm/uaccess.h       |  3 ---
 arch/sh/include/asm/uaccess.h         |  2 --
 arch/sparc/include/asm/uaccess_32.h   |  3 ---
 arch/sparc/include/asm/uaccess_64.h   |  2 --
 arch/um/include/asm/uaccess.h         |  3 ---
 arch/x86/Kconfig                      |  1 +
 arch/xtensa/include/asm/uaccess.h     |  2 --
 include/asm-generic/uaccess.h         |  2 --
 include/linux/uaccess.h               | 32 ++++++++++++---------------
 lib/Kconfig                           |  3 +++
 lib/Makefile                          |  3 ++-
 lib/usercopy.c                        |  4 ----
 rust/helpers/uaccess.c                |  2 +-
 28 files changed, 25 insertions(+), 70 deletions(-)

diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig
index 7b7dafe7d9df..65e533cead6b 100644
--- a/arch/alpha/Kconfig
+++ b/arch/alpha/Kconfig
@@ -11,6 +11,7 @@ config ALPHA
 	select ARCH_NO_PREEMPT
 	select ARCH_NO_SG_CHAIN
 	select ARCH_USE_CMPXCHG_LOCKREF
+	select ARCH_WANTS_NOINLINE_COPY_USER
 	select FORCE_PCI
 	select PCI_DOMAINS if PCI
 	select PCI_SYSCALL if PCI
diff --git a/arch/arc/include/asm/uaccess.h b/arch/arc/include/asm/uaccess.h
index 1e8809ea000a..e8b161b37a03 100644
--- a/arch/arc/include/asm/uaccess.h
+++ b/arch/arc/include/asm/uaccess.h
@@ -628,9 +628,6 @@ static inline unsigned long __clear_user(void __user *to, unsigned long n)
 	return res;
 }
 
-#define INLINE_COPY_TO_USER
-#define INLINE_COPY_FROM_USER
-
 #define __clear_user			__clear_user
 
 #include <asm-generic/uaccess.h>
diff --git a/arch/arm/include/asm/uaccess.h b/arch/arm/include/asm/uaccess.h
index d6ae80b5df36..7280c162bb71 100644
--- a/arch/arm/include/asm/uaccess.h
+++ b/arch/arm/include/asm/uaccess.h
@@ -616,8 +616,6 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n)
 }
 #define __clear_user(addr, n)		(memset((void __force *)addr, 0, n), 0)
 #endif
-#define INLINE_COPY_TO_USER
-#define INLINE_COPY_FROM_USER
 
 static inline unsigned long __must_check clear_user(void __user *to, unsigned long n)
 {
diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h
index b0c83a08dda9..1e20ec91b56f 100644
--- a/arch/arm64/include/asm/uaccess.h
+++ b/arch/arm64/include/asm/uaccess.h
@@ -456,9 +456,6 @@ do {									\
 	unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u8, label);	\
 } while (0)
 
-#define INLINE_COPY_TO_USER
-#define INLINE_COPY_FROM_USER
-
 extern unsigned long __must_check __arch_clear_user(void __user *to, unsigned long n);
 static inline unsigned long __must_check __clear_user(void __user *to, unsigned long n)
 {
diff --git a/arch/csky/Kconfig b/arch/csky/Kconfig
index 4331313a42ff..d010d7eb47bf 100644
--- a/arch/csky/Kconfig
+++ b/arch/csky/Kconfig
@@ -40,6 +40,7 @@ config CSKY
 	select ARCH_NEED_CMPXCHG_1_EMU
 	select ARCH_WANT_FRAME_POINTERS if !CPU_CK610 && $(cc-option,-mbacktrace)
 	select ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT
+	select ARCH_WANTS_NOINLINE_COPY_USER
 	select COMMON_CLK
 	select CLKSRC_MMIO
 	select CSKY_MPINTC if CPU_CK860
diff --git a/arch/hexagon/include/asm/uaccess.h b/arch/hexagon/include/asm/uaccess.h
index bff77efc0d9a..4bf863217636 100644
--- a/arch/hexagon/include/asm/uaccess.h
+++ b/arch/hexagon/include/asm/uaccess.h
@@ -26,9 +26,6 @@ unsigned long raw_copy_from_user(void *to, const void __user *from,
 				     unsigned long n);
 unsigned long raw_copy_to_user(void __user *to, const void *from,
 				   unsigned long n);
-#define INLINE_COPY_FROM_USER
-#define INLINE_COPY_TO_USER
-
 __kernel_size_t __clear_user_hexagon(void __user *dest, unsigned long count);
 #define __clear_user(a, s) __clear_user_hexagon((a), (s))
 
diff --git a/arch/loongarch/include/asm/uaccess.h b/arch/loongarch/include/asm/uaccess.h
index 438269313e78..72a04ac88549 100644
--- a/arch/loongarch/include/asm/uaccess.h
+++ b/arch/loongarch/include/asm/uaccess.h
@@ -292,9 +292,6 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n)
 	return __copy_user((__force void *)to, from, n);
 }
 
-#define INLINE_COPY_FROM_USER
-#define INLINE_COPY_TO_USER
-
 /*
  * __clear_user: - Zero a block of memory in user space, with less checking.
  * @addr: Destination address, in user space.
diff --git a/arch/m68k/include/asm/uaccess.h b/arch/m68k/include/asm/uaccess.h
index 64914872a5c9..20e249a6ad07 100644
--- a/arch/m68k/include/asm/uaccess.h
+++ b/arch/m68k/include/asm/uaccess.h
@@ -377,9 +377,6 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n)
 		return __constant_copy_to_user(to, from, n);
 	return __generic_copy_to_user(to, from, n);
 }
-#define INLINE_COPY_FROM_USER
-#define INLINE_COPY_TO_USER
-
 #define __get_kernel_nofault(dst, src, type, err_label)			\
 do {									\
 	type *__gk_dst = (type *)(dst);					\
diff --git a/arch/microblaze/include/asm/uaccess.h b/arch/microblaze/include/asm/uaccess.h
index 3aab2f17e046..3355f541e12a 100644
--- a/arch/microblaze/include/asm/uaccess.h
+++ b/arch/microblaze/include/asm/uaccess.h
@@ -250,8 +250,6 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n)
 {
 	return __copy_tofrom_user(to, (__force const void __user *)from, n);
 }
-#define INLINE_COPY_FROM_USER
-#define INLINE_COPY_TO_USER
 
 /*
  * Copy a null terminated string from userspace.
diff --git a/arch/mips/include/asm/uaccess.h b/arch/mips/include/asm/uaccess.h
index c0cede273c7c..8714caefbac8 100644
--- a/arch/mips/include/asm/uaccess.h
+++ b/arch/mips/include/asm/uaccess.h
@@ -433,9 +433,6 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n)
 	return __cu_len_r;
 }
 
-#define INLINE_COPY_FROM_USER
-#define INLINE_COPY_TO_USER
-
 extern __kernel_size_t __bzero(void __user *addr, __kernel_size_t size);
 
 /*
diff --git a/arch/nios2/include/asm/uaccess.h b/arch/nios2/include/asm/uaccess.h
index 6ccc9a232c23..46d7312a1c96 100644
--- a/arch/nios2/include/asm/uaccess.h
+++ b/arch/nios2/include/asm/uaccess.h
@@ -57,8 +57,6 @@ extern unsigned long
 raw_copy_from_user(void *to, const void __user *from, unsigned long n);
 extern unsigned long
 raw_copy_to_user(void __user *to, const void *from, unsigned long n);
-#define INLINE_COPY_FROM_USER
-#define INLINE_COPY_TO_USER
 
 extern long strncpy_from_user(char *__to, const char __user *__from,
 			      long __len);
diff --git a/arch/openrisc/include/asm/uaccess.h b/arch/openrisc/include/asm/uaccess.h
index d6500a374e18..c84effde867a 100644
--- a/arch/openrisc/include/asm/uaccess.h
+++ b/arch/openrisc/include/asm/uaccess.h
@@ -218,8 +218,6 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long size)
 {
 	return __copy_tofrom_user((__force void *)to, from, size);
 }
-#define INLINE_COPY_FROM_USER
-#define INLINE_COPY_TO_USER
 
 extern unsigned long __clear_user(void __user *addr, unsigned long size);
 
diff --git a/arch/parisc/include/asm/uaccess.h b/arch/parisc/include/asm/uaccess.h
index 6c531d2c847e..1dd6a1dd653f 100644
--- a/arch/parisc/include/asm/uaccess.h
+++ b/arch/parisc/include/asm/uaccess.h
@@ -197,7 +197,4 @@ unsigned long __must_check raw_copy_to_user(void __user *dst, const void *src,
 					    unsigned long len);
 unsigned long __must_check raw_copy_from_user(void *dst, const void __user *src,
 					    unsigned long len);
-#define INLINE_COPY_TO_USER
-#define INLINE_COPY_FROM_USER
-
 #endif /* __PARISC_UACCESS_H */
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index e93df95b79e7..6816f402fe3d 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -187,6 +187,7 @@ config PPC
 	select ARCH_WANT_LD_ORPHAN_WARN
 	select ARCH_WANT_OPTIMIZE_DAX_VMEMMAP	if PPC_RADIX_MMU
 	select ARCH_WANTS_MODULES_DATA_IN_VMALLOC	if PPC_BOOK3S_32 || PPC_8xx
+	select ARCH_WANTS_NOINLINE_COPY_USER
 	select ARCH_WEAK_RELEASE_ACQUIRE
 	select AUDIT_ARCH_COMPAT_GENERIC
 	select BINFMT_ELF
diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index d235396c4514..492b920c1a51 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -88,6 +88,7 @@ config RISCV
 	select ARCH_WANT_OPTIMIZE_DAX_VMEMMAP
 	select ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP
 	select ARCH_WANTS_NO_INSTR
+	select ARCH_WANTS_NOINLINE_COPY_USER if MMU
 	select ARCH_WANTS_THP_SWAP if HAVE_ARCH_TRANSPARENT_HUGEPAGE
 	select ARCH_WEAK_RELEASE_ACQUIRE if ARCH_USE_QUEUED_SPINLOCKS
 	select BINFMT_FLAT_NO_DATA_START_OFFSET if !MMU
diff --git a/arch/s390/include/asm/uaccess.h b/arch/s390/include/asm/uaccess.h
index dff035372601..2e0472c20da0 100644
--- a/arch/s390/include/asm/uaccess.h
+++ b/arch/s390/include/asm/uaccess.h
@@ -30,9 +30,6 @@ void debug_user_asce(int exit);
 #define uaccess_kmsan_or_inline __always_inline
 #endif
 
-#define INLINE_COPY_FROM_USER
-#define INLINE_COPY_TO_USER
-
 static uaccess_kmsan_or_inline __must_check unsigned long
 raw_copy_from_user(void *to, const void __user *from, unsigned long size)
 {
diff --git a/arch/sh/include/asm/uaccess.h b/arch/sh/include/asm/uaccess.h
index a79609eb14be..0cd75308e6d3 100644
--- a/arch/sh/include/asm/uaccess.h
+++ b/arch/sh/include/asm/uaccess.h
@@ -95,8 +95,6 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n)
 {
 	return __copy_user((__force void *)to, from, n);
 }
-#define INLINE_COPY_FROM_USER
-#define INLINE_COPY_TO_USER
 
 /*
  * Clear the area and return remaining number of bytes
diff --git a/arch/sparc/include/asm/uaccess_32.h b/arch/sparc/include/asm/uaccess_32.h
index 43284b6ec46a..e01f43c6421c 100644
--- a/arch/sparc/include/asm/uaccess_32.h
+++ b/arch/sparc/include/asm/uaccess_32.h
@@ -190,9 +190,6 @@ static inline unsigned long raw_copy_from_user(void *to, const void __user *from
 	return __copy_user((__force void __user *) to, from, n);
 }
 
-#define INLINE_COPY_FROM_USER
-#define INLINE_COPY_TO_USER
-
 static inline unsigned long __clear_user(void __user *addr, unsigned long size)
 {
 	unsigned long ret;
diff --git a/arch/sparc/include/asm/uaccess_64.h b/arch/sparc/include/asm/uaccess_64.h
index b825a5dd0210..62ee0b074fec 100644
--- a/arch/sparc/include/asm/uaccess_64.h
+++ b/arch/sparc/include/asm/uaccess_64.h
@@ -231,8 +231,6 @@ unsigned long __must_check raw_copy_from_user(void *to,
 unsigned long __must_check raw_copy_to_user(void __user *to,
 					   const void *from,
 					   unsigned long size);
-#define INLINE_COPY_FROM_USER
-#define INLINE_COPY_TO_USER
 
 unsigned long __must_check raw_copy_in_user(void __user *to,
 					   const void __user *from,
diff --git a/arch/um/include/asm/uaccess.h b/arch/um/include/asm/uaccess.h
index 0df9ea4abda8..1e14260c7f0f 100644
--- a/arch/um/include/asm/uaccess.h
+++ b/arch/um/include/asm/uaccess.h
@@ -27,9 +27,6 @@ static inline int __access_ok(const void __user *ptr, unsigned long size);
 #define __access_ok __access_ok
 #define __clear_user __clear_user
 
-#define INLINE_COPY_FROM_USER
-#define INLINE_COPY_TO_USER
-
 #include <asm-generic/uaccess.h>
 
 static inline int __access_ok(const void __user *ptr, unsigned long size)
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index f3f7cb01d69d..c1e58d8c6864 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -143,6 +143,7 @@ config X86
 	select ARCH_WANTS_CLOCKSOURCE_READ_INLINE	if X86_64
 	select ARCH_WANTS_DYNAMIC_TASK_STRUCT
 	select ARCH_WANTS_NO_INSTR
+	select ARCH_WANTS_NOINLINE_COPY_USER
 	select ARCH_WANT_GENERAL_HUGETLB
 	select ARCH_WANT_HUGE_PMD_SHARE		if X86_64
 	select ARCH_WANT_LD_ORPHAN_WARN
diff --git a/arch/xtensa/include/asm/uaccess.h b/arch/xtensa/include/asm/uaccess.h
index 56aec6d504fe..f9e1623a7be9 100644
--- a/arch/xtensa/include/asm/uaccess.h
+++ b/arch/xtensa/include/asm/uaccess.h
@@ -237,8 +237,6 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n)
 	prefetch(from);
 	return __xtensa_copy_user((__force void *)to, from, n);
 }
-#define INLINE_COPY_FROM_USER
-#define INLINE_COPY_TO_USER
 
 /*
  * We need to return the number of bytes not cleared.  Our memset()
diff --git a/include/asm-generic/uaccess.h b/include/asm-generic/uaccess.h
index b276f783494c..fb33a71fd24e 100644
--- a/include/asm-generic/uaccess.h
+++ b/include/asm-generic/uaccess.h
@@ -91,8 +91,6 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n)
 	memcpy((void __force *)to, from, n);
 	return 0;
 }
-#define INLINE_COPY_FROM_USER
-#define INLINE_COPY_TO_USER
 #endif /* CONFIG_UACCESS_MEMCPY */
 
 /*
diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h
index 56328601218c..bd1201c81d94 100644
--- a/include/linux/uaccess.h
+++ b/include/linux/uaccess.h
@@ -83,8 +83,8 @@
  * with that.  They should not be used directly; they are used to implement
  * the 6 functions (copy_{to,from}_user(), __copy_{to,from}_user_inatomic())
  * that are used instead.  Out of those, __... ones are inlined.  Plain
- * copy_{to,from}_user() might or might not be inlined.  If you want them
- * inlined, have asm/uaccess.h define INLINE_COPY_{TO,FROM}_USER.
+ * copy_{to,from}_user() might or might not be inlined.  If you don't want them
+ * inlined, select CONFIG_ARCH_WANTS_NOINLINE_COPY_USER.
  *
  * NOTE: only copy_from_user() zero-pads the destination in case of short copy.
  * Neither __copy_from_user() nor __copy_from_user_inatomic() zero anything
@@ -157,8 +157,8 @@ __copy_to_user(void __user *to, const void *from, unsigned long n)
 }
 
 /*
- * Architectures that #define INLINE_COPY_TO_USER use this function
- * directly in the normal copy_to/from_user(), the other ones go
+ * Architectures that don't select CONFIG_ARCH_WANTS_NOINLINE_COPY_USER use
+ * this function directly in the normal copy_to/from_user(), the other ones go
  * through an extern _copy_to/from_user(), which expands the same code
  * here.
  */
@@ -190,10 +190,9 @@ _inline_copy_from_user(void *to, const void __user *from, unsigned long n)
 	memset(to + (n - res), 0, res);
 	return res;
 }
-#ifndef INLINE_COPY_FROM_USER
+
 extern __must_check unsigned long
 _copy_from_user(void *, const void __user *, unsigned long);
-#endif
 
 static inline __must_check unsigned long
 _inline_copy_to_user(void __user *to, const void *from, unsigned long n)
@@ -207,21 +206,19 @@ _inline_copy_to_user(void __user *to, const void *from, unsigned long n)
 	}
 	return n;
 }
-#ifndef INLINE_COPY_TO_USER
+
 extern __must_check unsigned long
 _copy_to_user(void __user *, const void *, unsigned long);
-#endif
 
 static __always_inline unsigned long __must_check
 copy_from_user(void *to, const void __user *from, unsigned long n)
 {
 	if (!check_copy_size(to, n, false))
 		return n;
-#ifdef INLINE_COPY_FROM_USER
-	return _inline_copy_from_user(to, from, n);
-#else
-	return _copy_from_user(to, from, n);
-#endif
+	if (IS_ENABLED(ARCH_WANTS_NOINLINE_COPY_USER))
+		return _copy_from_user(to, from, n);
+	else
+		return _inline_copy_from_user(to, from, n);
 }
 
 static __always_inline unsigned long __must_check
@@ -230,11 +227,10 @@ copy_to_user(void __user *to, const void *from, unsigned long n)
 	if (!check_copy_size(from, n, true))
 		return n;
 
-#ifdef INLINE_COPY_TO_USER
-	return _inline_copy_to_user(to, from, n);
-#else
-	return _copy_to_user(to, from, n);
-#endif
+	if (IS_ENABLED(ARCH_WANTS_NOINLINE_COPY_USER))
+		return _copy_to_user(to, from, n);
+	else
+		return _inline_copy_to_user(to, from, n);
 }
 
 #ifndef copy_mc_to_kernel
diff --git a/lib/Kconfig b/lib/Kconfig
index 00a9509636c1..a2e07d4dd2bf 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -68,6 +68,9 @@ config ARCH_HAS_STRNCPY_FROM_USER
 config ARCH_HAS_STRNLEN_USER
 	bool
 
+config ARCH_WANTS_NOINLINE_COPY_USER
+	bool
+
 config GENERIC_STRNCPY_FROM_USER
 	def_bool !ARCH_HAS_STRNCPY_FROM_USER
 
diff --git a/lib/Makefile b/lib/Makefile
index 7c0334d7675b..f4d577910671 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -57,9 +57,10 @@ obj-y += bcd.o sort.o parser.o debug_locks.o random32.o \
 	 list_sort.o uuid.o iov_iter.o clz_ctz.o \
 	 bsearch.o find_bit.o llist.o lwq.o memweight.o kfifo.o \
 	 percpu-refcount.o rhashtable.o base64.o \
-	 once.o refcount.o rcuref.o usercopy.o errseq.o bucket_locks.o \
+	 once.o refcount.o rcuref.o errseq.o bucket_locks.o \
 	 generic-radix-tree.o bitmap-str.o
 obj-y += usercheck.o
+obj-$(CONFIG_ARCH_WANTS_NOINLINE_COPY_USER) += usercopy.o
 obj-y += string_helpers.o
 obj-y += hexdump.o
 obj-$(CONFIG_TEST_HEXDUMP) += test_hexdump.o
diff --git a/lib/usercopy.c b/lib/usercopy.c
index 7a93f56d81dd..d2deb4b0a3c5 100644
--- a/lib/usercopy.c
+++ b/lib/usercopy.c
@@ -4,18 +4,14 @@
 
 /* out-of-line parts */
 
-#if !defined(INLINE_COPY_FROM_USER)
 unsigned long _copy_from_user(void *to, const void __user *from, unsigned long n)
 {
 	return _inline_copy_from_user(to, from, n);
 }
 EXPORT_SYMBOL(_copy_from_user);
-#endif
 
-#if !defined(INLINE_COPY_TO_USER)
 unsigned long _copy_to_user(void __user *to, const void *from, unsigned long n)
 {
 	return _inline_copy_to_user(to, from, n);
 }
 EXPORT_SYMBOL(_copy_to_user);
-#endif
diff --git a/rust/helpers/uaccess.c b/rust/helpers/uaccess.c
index d9625b9ee046..01de4fbbcc84 100644
--- a/rust/helpers/uaccess.c
+++ b/rust/helpers/uaccess.c
@@ -14,7 +14,7 @@ rust_helper_copy_to_user(void __user *to, const void *from, unsigned long n)
 	return copy_to_user(to, from, n);
 }
 
-#ifdef INLINE_COPY_FROM_USER
+#ifndef CONFIG_ARCH_WANTS_NOINLINE_COPY_USER
 __rust_helper
 unsigned long rust_helper__copy_from_user(void *to, const void __user *from, unsigned long n)
 {
-- 
2.49.0



^ permalink raw reply related

* [RFC PATCH v1 3/9] x86/umip: Be stricter in fixup_umip_exception()
From: Christophe Leroy (CS GROUP) @ 2026-04-27 17:13 UTC (permalink / raw)
  To: Yury Norov, Andrew Morton, Linus Torvalds, David Laight,
	Thomas Gleixner
  Cc: Christophe Leroy (CS GROUP), linux-alpha, linux-kernel,
	linux-snps-arc, linux-arm-kernel, linux-mips, linuxppc-dev, kvm,
	linux-riscv, linux-s390, sparclinux, linux-um, dmaengine,
	linux-efi, linux-fsi, amd-gfx, dri-devel, intel-gfx, linux-wpan,
	netdev, linux-wireless, linux-spi, linux-media, linux-staging,
	linux-serial, linux-usb, xen-devel, linux-fsdevel, ocfs2-devel,
	bpf, kasan-dev, linux-mm, linux-x25, rust-for-linux, linux-sound,
	sound-open-firmware, linux-csky, linux-hexagon, loongarch,
	linux-m68k, linux-openrisc, linux-parisc, linux-sh, linux-arch
In-Reply-To: <cover.1777306795.git.chleroy@kernel.org>

fixup_umip_exception() calls copy_to_user() and checks whether
the returned value is strictly positive.

A subsequent patch will change the return of copy_to_user() to
return -EFAULT in case of error.

Change the test to checking that the result is not 0.

At the time being copy_to_user() return an unsigned value so
'strictly positive' is the same as 'not 0'.

Signed-off-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
---
 arch/x86/kernel/umip.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/x86/kernel/umip.c b/arch/x86/kernel/umip.c
index 3ce99cbcf187..dfff28ea1dea 100644
--- a/arch/x86/kernel/umip.c
+++ b/arch/x86/kernel/umip.c
@@ -409,7 +409,7 @@ bool fixup_umip_exception(struct pt_regs *regs)
 			return false;
 
 		nr_copied = copy_to_user(uaddr, dummy_data, dummy_data_size);
-		if (nr_copied  > 0) {
+		if (nr_copied) {
 			/*
 			 * If copy fails, send a signal and tell caller that
 			 * fault was fixed up.
-- 
2.49.0



^ permalink raw reply related

* [RFC PATCH v1 4/9] uaccess: Introduce copy_{to/from}_user_partial()
From: Christophe Leroy (CS GROUP) @ 2026-04-27 17:13 UTC (permalink / raw)
  To: Yury Norov, Andrew Morton, Linus Torvalds, David Laight,
	Thomas Gleixner
  Cc: Christophe Leroy (CS GROUP), linux-alpha, linux-kernel,
	linux-snps-arc, linux-arm-kernel, linux-mips, linuxppc-dev, kvm,
	linux-riscv, linux-s390, sparclinux, linux-um, dmaengine,
	linux-efi, linux-fsi, amd-gfx, dri-devel, intel-gfx, linux-wpan,
	netdev, linux-wireless, linux-spi, linux-media, linux-staging,
	linux-serial, linux-usb, xen-devel, linux-fsdevel, ocfs2-devel,
	bpf, kasan-dev, linux-mm, linux-x25, rust-for-linux, linux-sound,
	sound-open-firmware, linux-csky, linux-hexagon, loongarch,
	linux-m68k, linux-openrisc, linux-parisc, linux-sh, linux-arch
In-Reply-To: <cover.1777306795.git.chleroy@kernel.org>

Today there are approximately 3000 calls for copy_to_user() and
3000 calls to copy_from_user().

The majority of callers of copy_{to/from}_user() don't care about the
return value, they only check whether it is 0 or not, and when it is
not 0 they handle it as a -EACCES.

In order to allow better optimisation of copy_{to/from}_user() when
the size of the copy is known at build time, create new fonctions
named copy_{to/from}_user_partial() to be used by the few callers
that are interested in partial copies and need to now how many
bytes remain at the end of the copy.

For the time being it is just the same as copy_{to/from}_user().

Signed-off-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
---
 include/linux/uaccess.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h
index bd1201c81d94..2d37173782b3 100644
--- a/include/linux/uaccess.h
+++ b/include/linux/uaccess.h
@@ -221,6 +221,8 @@ copy_from_user(void *to, const void __user *from, unsigned long n)
 		return _inline_copy_from_user(to, from, n);
 }
 
+#define copy_from_user_partial copy_from_user
+
 static __always_inline unsigned long __must_check
 copy_to_user(void __user *to, const void *from, unsigned long n)
 {
@@ -233,6 +235,8 @@ copy_to_user(void __user *to, const void *from, unsigned long n)
 		return _inline_copy_to_user(to, from, n);
 }
 
+#define copy_to_user_partial copy_to_user
+
 #ifndef copy_mc_to_kernel
 /*
  * Without arch opt-in this generic copy_mc_to_kernel() will not handle
-- 
2.49.0



^ permalink raw reply related

* Re: [PATCH v5 6/6] arm64: dts: rockchip: Add cooling fan to ROCK 4D
From: Nicolas Frattaroli @ 2026-04-27 17:17 UTC (permalink / raw)
  To: Uwe Kleine-König, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Heiko Stuebner, Lee Jones, William Breathitt Gray,
	Damon Ding
  Cc: kernel, Jonas Karlman, Alexey Charkov, linux-rockchip, linux-pwm,
	devicetree, linux-arm-kernel, linux-kernel, linux-iio
In-Reply-To: <35afc21f-74c9-4f52-bdf1-18a34fb58578@rock-chips.com>

Hello,

On Sunday, 26 April 2026 09:23:01 Central European Summer Time Damon Ding wrote:
> Hi Nicolas,
> 
> On 4/20/2026 9:52 PM, Nicolas Frattaroli wrote:
> > The ROCK 4D has a header to connect a small cooling fan. This fan is
> > driven by one of the SoC's PWM outputs driving a transistor, that in
> > turn controls the fan's power.
> > 
> > With the introduction of PWM support, add a description of this cooling
> > fan, as well as the additional trips and cooling-maps for it.
> > 
> > Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
> > ---
> >   arch/arm64/boot/dts/rockchip/rk3576-rock-4d.dts | 50 +++++++++++++++++++++++++
> >   1 file changed, 50 insertions(+)
> > 
> > diff --git a/arch/arm64/boot/dts/rockchip/rk3576-rock-4d.dts b/arch/arm64/boot/dts/rockchip/rk3576-rock-4d.dts
> > index 899a84b1fbf9..2d5ede010ad0 100644
> > --- a/arch/arm64/boot/dts/rockchip/rk3576-rock-4d.dts
> > +++ b/arch/arm64/boot/dts/rockchip/rk3576-rock-4d.dts
> > @@ -45,6 +45,14 @@ rfkill {
> >   		shutdown-gpios = <&gpio2 RK_PD1 GPIO_ACTIVE_HIGH>;
> >   	};
> >   
> > +	fan: pwm-fan {
> > +		compatible = "pwm-fan";
> > +		cooling-levels = <0 180 205 230 255>;
> > +		fan-supply = <&vcc_5v0_sys>;
> > +		pwms = <&pwm2_8ch_5 0 60000 0>;
> > +		#cooling-cells = <2>;
> > +	};
> > +
> >   	leds: leds {
> >   		compatible = "gpio-leds";
> >   		pinctrl-names = "default";
> > @@ -711,6 +719,36 @@ rgmii_phy0: ethernet-phy@1 {
> >   	};
> >   };
> >   
> > +&package_thermal {
> > +	polling-delay = <100>;
> > +
> > +	trips {
> > +		package_fan0: package-fan0 {
> > +			temperature = <50000>;
> > +			hysteresis = <2000>;
> > +			type = "active";
> > +		};
> > +
> > +		package_fan1: package-fan1 {
> > +			temperature = <60000>;
> > +			hysteresis = <2000>;
> > +			type = "active";
> > +		};
> > +	};
> > +
> > +	cooling-maps {
> > +		map1 {
> > +			trip = <&package_fan0>;
> > +			cooling-device = <&fan THERMAL_NO_LIMIT 1>;
> > +		};
> > +
> > +		map2 {
> > +			trip = <&package_fan1>;
> > +			cooling-device = <&fan 2 THERMAL_NO_LIMIT>;
> > +		};
> > +	};
> > +};
> > +
> >   &pcie0 {
> >   	pinctrl-names = "default";
> >   	pinctrl-0 = <&pcie_reset>;
> > @@ -720,6 +758,13 @@ &pcie0 {
> >   };
> >   
> >   &pinctrl {
> > +	fan {
> > +		fan_pwm: fan-pwm {
> > +			rockchip,pins =
> > +				<4 RK_PC5 14 &pcfg_pull_down_drv_level_5>;
> > +		};
> > +	};
> > +
> >   	hym8563 {
> >   		hym8563_int: hym8563-int {
> >   			rockchip,pins = <0 RK_PA0 RK_FUNC_GPIO &pcfg_pull_up>;
> > @@ -770,6 +815,11 @@ wifi_en_h: wifi-en-h {
> >   	};
> >   };
> >   
> > +&pwm2_8ch_5 {
> > +	pinctrl-0 = <&fan_pwm>;
> 
> May I ask why the pinctrl does not directly use &pwm2m1_ch5?
> 
> Is it because the default pin configuration cannot meet the requirements 
> of the fan?

Yes, I'm adding a strong pull down. The PWM pin controls a transistor,
and if it floats when not in use the fan can start spinning when no
PWM signal is present. If I recall correctly (it's been a while since
I wrote this DT patch) it could be left spinning when the board was
shut down with a `poweroff` command.

> 
> > +	status = "okay";
> > +};
> > +
> >   &sai6 {
> >   	status = "okay";
> >   };
> > 
> 
> Best regards,
> Damon
> 
> 

Kind regards,
Nicolas Frattaroli




^ permalink raw reply

* [RFC PATCH v1 5/9] uaccess: Switch to copy_{to/from}_user_partial() when relevant
From: Christophe Leroy (CS GROUP) @ 2026-04-27 17:13 UTC (permalink / raw)
  To: Yury Norov, Andrew Morton, Linus Torvalds, David Laight,
	Thomas Gleixner
  Cc: Christophe Leroy (CS GROUP), linux-alpha, linux-kernel,
	linux-snps-arc, linux-arm-kernel, linux-mips, linuxppc-dev, kvm,
	linux-riscv, linux-s390, sparclinux, linux-um, dmaengine,
	linux-efi, linux-fsi, amd-gfx, dri-devel, intel-gfx, linux-wpan,
	netdev, linux-wireless, linux-spi, linux-media, linux-staging,
	linux-serial, linux-usb, xen-devel, linux-fsdevel, ocfs2-devel,
	bpf, kasan-dev, linux-mm, linux-x25, rust-for-linux, linux-sound,
	sound-open-firmware, linux-csky, linux-hexagon, loongarch,
	linux-m68k, linux-openrisc, linux-parisc, linux-sh, linux-arch
In-Reply-To: <cover.1777306795.git.chleroy@kernel.org>

In a subsequent patch, copy_{to/from}_user() will be modified to
return -EFAULT when copy fails.

Among the 6000 calls to copy_{to/from}_user(), around 2% rely on
copy_{to/from}_user() doing partial copies and returning amount of not
copied bytes. Change those users to copy_{to/from}_user_partial().

This change was done based on whether callers assign the returned value
to a variable or just check whether the return value is 0 or not.

Several of them only use it for debug to print the amount of bytes not
copied. Those could maybe be changed to stop reporting that amount and
not be converted to partial copy.

Some not trivial handling might have been unecessarily converted. This
is not a problem and they can be converted back later for better
performance.

The callers where located with following commands then reviewed one by
one:

	sed -i s/"return copy_to_user("/"return copy_to_user_partial("/g `git grep -l "return copy_to_user("`
	sed -i s/" = copy_to_user("/" = copy_to_user_partial("/g `git grep -l " = copy_to_user("`
	sed -i s/" += copy_to_user("/" += copy_to_user_partial("/g `git grep -l " += copy_to_user("`
	sed -i s/" -= copy_to_user("/" -= copy_to_user_partial("/g `git grep -l " -= copy_to_user("`

Then the same was done with copy_from_user().

During the review, patterns like the following were rejected and kept
as is:

-	return copy_to_user(osf_stat, &tmp, sizeof(tmp)) ? -EFAULT : 0;
+	return copy_to_user_partial(osf_stat, &tmp, sizeof(tmp)) ? -EFAULT : 0;

Signed-off-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
---
 arch/alpha/kernel/osf_sys.c                    |  4 ++--
 arch/alpha/kernel/termios.c                    |  2 +-
 arch/arc/kernel/disasm.c                       |  2 +-
 arch/arm64/include/asm/gcs.h                   |  2 +-
 arch/arm64/kernel/signal32.c                   |  2 +-
 arch/mips/kernel/rtlx.c                        |  8 ++++----
 arch/mips/kernel/vpe.c                         |  2 +-
 arch/powerpc/kvm/book3s_64_mmu_hv.c            |  4 ++--
 arch/powerpc/kvm/book3s_64_mmu_radix.c         |  4 ++--
 arch/powerpc/kvm/book3s_hv.c                   |  2 +-
 arch/riscv/kernel/signal.c                     |  2 +-
 arch/s390/include/asm/idals.h                  |  8 ++++----
 arch/sparc/kernel/termios.c                    |  2 +-
 arch/um/kernel/process.c                       |  2 +-
 arch/x86/lib/insn-eval.c                       |  2 +-
 arch/x86/um/signal.c                           |  2 +-
 drivers/android/binder_alloc.c                 |  2 +-
 drivers/comedi/comedi_fops.c                   |  4 ++--
 drivers/dma/idxd/cdev.c                        |  2 +-
 drivers/firmware/efi/test/efi_test.c           |  2 +-
 drivers/fsi/fsi-scom.c                         |  2 +-
 .../amd/display/amdgpu_dm/amdgpu_dm_debugfs.c  |  2 +-
 drivers/gpu/drm/i915/gt/intel_sseu.c           |  4 ++--
 drivers/gpu/drm/i915/i915_gem.c                |  4 ++--
 drivers/hwtracing/intel_th/msu.c               |  2 +-
 drivers/misc/ibmvmc.c                          |  2 +-
 drivers/misc/vmw_vmci/vmci_host.c              |  2 +-
 drivers/most/most_cdev.c                       |  2 +-
 drivers/net/ieee802154/ca8210.c                |  4 ++--
 drivers/net/wireless/ath/wil6210/debugfs.c     |  2 +-
 .../wireless/intel/iwlwifi/pcie/gen1_2/trans.c |  2 +-
 drivers/net/wireless/ti/wlcore/debugfs.c       |  2 +-
 drivers/ps3/ps3-lpm.c                          |  2 +-
 drivers/s390/crypto/zcrypt_api.h               |  4 ++--
 drivers/spi/spidev.c                           |  2 +-
 .../staging/media/atomisp/pci/atomisp_cmd.c    |  8 ++++----
 drivers/tty/tty_ioctl.c                        | 14 +++++++-------
 drivers/tty/vt/vc_screen.c                     |  4 ++--
 drivers/usb/gadget/function/f_hid.c            |  4 ++--
 drivers/usb/gadget/function/f_printer.c        |  2 +-
 drivers/vfio/vfio_iommu_type1.c                |  4 ++--
 drivers/xen/xenbus/xenbus_dev_frontend.c       |  2 +-
 fs/namespace.c                                 |  2 +-
 fs/ocfs2/dlmfs/dlmfs.c                         |  2 +-
 fs/proc/base.c                                 |  4 ++--
 include/linux/bpfptr.h                         |  2 +-
 include/linux/sockptr.h                        |  4 ++--
 ipc/msg.c                                      |  8 ++++----
 ipc/sem.c                                      |  8 ++++----
 ipc/shm.c                                      | 18 +++++++++---------
 kernel/regset.c                                |  2 +-
 kernel/sys.c                                   |  4 ++--
 lib/kfifo.c                                    |  8 ++++----
 mm/kasan/kasan_test_c.c                        |  4 ++--
 mm/memory.c                                    |  2 +-
 net/x25/af_x25.c                               |  2 +-
 rust/helpers/uaccess.c                         |  4 ++--
 sound/pci/emu10k1/emufx.c                      |  4 ++--
 sound/pci/rme9652/hdsp.c                       |  6 +++---
 sound/soc/intel/avs/probes.c                   |  6 +++---
 sound/soc/sof/compress.c                       | 12 ++++++------
 sound/soc/sof/sof-client-probes.c              |  6 +++---
 62 files changed, 122 insertions(+), 122 deletions(-)

diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c
index 7b6543d2cca3..c8ea39fdbb9f 100644
--- a/arch/alpha/kernel/osf_sys.c
+++ b/arch/alpha/kernel/osf_sys.c
@@ -944,7 +944,7 @@ get_tv32(struct timespec64 *o, struct timeval32 __user *i)
 static inline long
 put_tv32(struct timeval32 __user *o, struct timespec64 *i)
 {
-	return copy_to_user(o, &(struct timeval32){
+	return copy_to_user_partial(o, &(struct timeval32){
 				.tv_sec = i->tv_sec,
 				.tv_usec = i->tv_nsec / NSEC_PER_USEC},
 			    sizeof(struct timeval32));
@@ -953,7 +953,7 @@ put_tv32(struct timeval32 __user *o, struct timespec64 *i)
 static inline long
 put_tv_to_tv32(struct timeval32 __user *o, struct __kernel_old_timeval *i)
 {
-	return copy_to_user(o, &(struct timeval32){
+	return copy_to_user_partial(o, &(struct timeval32){
 				.tv_sec = i->tv_sec,
 				.tv_usec = i->tv_usec},
 			    sizeof(struct timeval32));
diff --git a/arch/alpha/kernel/termios.c b/arch/alpha/kernel/termios.c
index a4c29a22edf7..a3693c29a0fd 100644
--- a/arch/alpha/kernel/termios.c
+++ b/arch/alpha/kernel/termios.c
@@ -52,5 +52,5 @@ int kernel_termios_to_user_termio(struct termio __user *termio,
 	v.c_cc[_VEOL2]  = termios->c_cc[VEOL2];
 	v.c_cc[_VSWTC]  = termios->c_cc[VSWTC];
 
-	return copy_to_user(termio, &v, sizeof(struct termio));
+	return copy_to_user_partial(termio, &v, sizeof(struct termio));
 }
diff --git a/arch/arc/kernel/disasm.c b/arch/arc/kernel/disasm.c
index ccc7e8c39eb3..a3ef9d079e7f 100644
--- a/arch/arc/kernel/disasm.c
+++ b/arch/arc/kernel/disasm.c
@@ -34,7 +34,7 @@ void __kprobes disasm_instr(unsigned long addr, struct disasm_state *state,
 	/* This fetches the upper part of the 32 bit instruction
 	 * in both the cases of Little Endian or Big Endian configurations. */
 	if (userspace) {
-		bytes_not_copied = copy_from_user(ins_buf,
+		bytes_not_copied = copy_from_user_partial(ins_buf,
 						(const void __user *) addr, 8);
 		if (bytes_not_copied > 6)
 			goto fault;
diff --git a/arch/arm64/include/asm/gcs.h b/arch/arm64/include/asm/gcs.h
index 8fa0707069e8..7ee23a8130b0 100644
--- a/arch/arm64/include/asm/gcs.h
+++ b/arch/arm64/include/asm/gcs.h
@@ -139,7 +139,7 @@ static inline u64 get_user_gcs(unsigned long __user *addr, int *err)
 
 	/* Ensure previous GCS operation are visible before we read the page */
 	gcsb_dsync();
-	ret = copy_from_user(&load, addr, sizeof(load));
+	ret = copy_from_user_partial(&load, addr, sizeof(load));
 	if (ret != 0)
 		*err = ret;
 	return load;
diff --git a/arch/arm64/kernel/signal32.c b/arch/arm64/kernel/signal32.c
index bb3b526ff43f..7016d2a3bb76 100644
--- a/arch/arm64/kernel/signal32.c
+++ b/arch/arm64/kernel/signal32.c
@@ -53,7 +53,7 @@ static inline int put_sigset_t(compat_sigset_t __user *uset, sigset_t *set)
 	cset.sig[0] = set->sig[0] & 0xffffffffull;
 	cset.sig[1] = set->sig[0] >> 32;
 
-	return copy_to_user(uset, &cset, sizeof(*uset));
+	return copy_to_user_partial(uset, &cset, sizeof(*uset));
 }
 
 static inline int get_sigset_t(sigset_t *set,
diff --git a/arch/mips/kernel/rtlx.c b/arch/mips/kernel/rtlx.c
index 18c509c59f33..bc468064194d 100644
--- a/arch/mips/kernel/rtlx.c
+++ b/arch/mips/kernel/rtlx.c
@@ -262,13 +262,13 @@ ssize_t rtlx_read(int index, void __user *buff, size_t count)
 	/* then how much from the read pointer onwards */
 	fl = min(count, (size_t)lx->buffer_size - lx->lx_read);
 
-	failed = copy_to_user(buff, lx->lx_buffer + lx->lx_read, fl);
+	failed = copy_to_user_partial(buff, lx->lx_buffer + lx->lx_read, fl);
 	if (failed)
 		goto out;
 
 	/* and if there is anything left at the beginning of the buffer */
 	if (count - fl)
-		failed = copy_to_user(buff + fl, lx->lx_buffer, count - fl);
+		failed = copy_to_user_partial(buff + fl, lx->lx_buffer, count - fl);
 
 out:
 	count -= failed;
@@ -304,13 +304,13 @@ ssize_t rtlx_write(int index, const void __user *buffer, size_t count)
 	/* first bit from write pointer to the end of the buffer, or count */
 	fl = min(count, (size_t) rt->buffer_size - rt->rt_write);
 
-	failed = copy_from_user(rt->rt_buffer + rt->rt_write, buffer, fl);
+	failed = copy_from_user_partial(rt->rt_buffer + rt->rt_write, buffer, fl);
 	if (failed)
 		goto out;
 
 	/* if there's any left copy to the beginning of the buffer */
 	if (count - fl)
-		failed = copy_from_user(rt->rt_buffer, buffer + fl, count - fl);
+		failed = copy_from_user_partial(rt->rt_buffer, buffer + fl, count - fl);
 
 out:
 	count -= failed;
diff --git a/arch/mips/kernel/vpe.c b/arch/mips/kernel/vpe.c
index b05ee21a1d67..5a8d72d6c80c 100644
--- a/arch/mips/kernel/vpe.c
+++ b/arch/mips/kernel/vpe.c
@@ -854,7 +854,7 @@ static ssize_t vpe_write(struct file *file, const char __user *buffer,
 		return -ENOMEM;
 	}
 
-	count -= copy_from_user(v->pbuffer + v->len, buffer, count);
+	count -= copy_from_user_partial(v->pbuffer + v->len, buffer, count);
 	if (!count)
 		return -EFAULT;
 
diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c b/arch/powerpc/kvm/book3s_64_mmu_hv.c
index 2ccb3d138f46..1c43c7b8e801 100644
--- a/arch/powerpc/kvm/book3s_64_mmu_hv.c
+++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c
@@ -2028,7 +2028,7 @@ static ssize_t debugfs_htab_read(struct file *file, char __user *buf,
 		n = p->chars_left;
 		if (n > len)
 			n = len;
-		r = copy_to_user(buf, p->buf + p->buf_index, n);
+		r = copy_to_user_partial(buf, p->buf + p->buf_index, n);
 		n -= r;
 		p->chars_left -= n;
 		p->buf_index += n;
@@ -2068,7 +2068,7 @@ static ssize_t debugfs_htab_read(struct file *file, char __user *buf,
 		p->chars_left = n;
 		if (n > len)
 			n = len;
-		r = copy_to_user(buf, p->buf, n);
+		r = copy_to_user_partial(buf, p->buf, n);
 		n -= r;
 		p->chars_left -= n;
 		p->buf_index = n;
diff --git a/arch/powerpc/kvm/book3s_64_mmu_radix.c b/arch/powerpc/kvm/book3s_64_mmu_radix.c
index 933fc7cb9afc..0a27e018d27b 100644
--- a/arch/powerpc/kvm/book3s_64_mmu_radix.c
+++ b/arch/powerpc/kvm/book3s_64_mmu_radix.c
@@ -1307,7 +1307,7 @@ static ssize_t debugfs_radix_read(struct file *file, char __user *buf,
 		n = p->chars_left;
 		if (n > len)
 			n = len;
-		r = copy_to_user(buf, p->buf + p->buf_index, n);
+		r = copy_to_user_partial(buf, p->buf + p->buf_index, n);
 		n -= r;
 		p->chars_left -= n;
 		p->buf_index += n;
@@ -1407,7 +1407,7 @@ static ssize_t debugfs_radix_read(struct file *file, char __user *buf,
 		p->chars_left = n;
 		if (n > len)
 			n = len;
-		r = copy_to_user(buf, p->buf, n);
+		r = copy_to_user_partial(buf, p->buf, n);
 		n -= r;
 		p->chars_left -= n;
 		p->buf_index = n;
diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c
index 61dbeea317f3..4c7a8f687c99 100644
--- a/arch/powerpc/kvm/book3s_hv.c
+++ b/arch/powerpc/kvm/book3s_hv.c
@@ -2916,7 +2916,7 @@ static ssize_t debugfs_timings_read(struct file *file, char __user *buf,
 		return 0;
 	if (len > p->buflen - pos)
 		len = p->buflen - pos;
-	n = copy_to_user(buf, p->buf + pos, len);
+	n = copy_to_user_partial(buf, p->buf + pos, len);
 	if (n) {
 		if (n == len)
 			return -EFAULT;
diff --git a/arch/riscv/kernel/signal.c b/arch/riscv/kernel/signal.c
index 59784dc117e4..4630dbad7428 100644
--- a/arch/riscv/kernel/signal.c
+++ b/arch/riscv/kernel/signal.c
@@ -139,7 +139,7 @@ static long __restore_v_state(struct pt_regs *regs, void __user *sc_vec)
 	 * Copy the whole vector content from user space datap. Use
 	 * copy_from_user to prevent information leak.
 	 */
-	return copy_from_user(current->thread.vstate.datap, datap, riscv_v_vsize);
+	return copy_from_user_partial(current->thread.vstate.datap, datap, riscv_v_vsize);
 }
 
 static long save_cfiss_state(struct pt_regs *regs, void __user *sc_cfi)
diff --git a/arch/s390/include/asm/idals.h b/arch/s390/include/asm/idals.h
index 06e1ec2afd5a..d86f4eb1ce42 100644
--- a/arch/s390/include/asm/idals.h
+++ b/arch/s390/include/asm/idals.h
@@ -301,14 +301,14 @@ static inline size_t idal_buffer_to_user(struct idal_buffer *ib, void __user *to
 	BUG_ON(count > ib->size);
 	for (i = 0; count > IDA_BLOCK_SIZE; i++) {
 		vaddr = dma64_to_virt(ib->data[i]);
-		left = copy_to_user(to, vaddr, IDA_BLOCK_SIZE);
+		left = copy_to_user_partial(to, vaddr, IDA_BLOCK_SIZE);
 		if (left)
 			return left + count - IDA_BLOCK_SIZE;
 		to = (void __user *)to + IDA_BLOCK_SIZE;
 		count -= IDA_BLOCK_SIZE;
 	}
 	vaddr = dma64_to_virt(ib->data[i]);
-	return copy_to_user(to, vaddr, count);
+	return copy_to_user_partial(to, vaddr, count);
 }
 
 /*
@@ -323,14 +323,14 @@ static inline size_t idal_buffer_from_user(struct idal_buffer *ib, const void __
 	BUG_ON(count > ib->size);
 	for (i = 0; count > IDA_BLOCK_SIZE; i++) {
 		vaddr = dma64_to_virt(ib->data[i]);
-		left = copy_from_user(vaddr, from, IDA_BLOCK_SIZE);
+		left = copy_from_user_partial(vaddr, from, IDA_BLOCK_SIZE);
 		if (left)
 			return left + count - IDA_BLOCK_SIZE;
 		from = (void __user *)from + IDA_BLOCK_SIZE;
 		count -= IDA_BLOCK_SIZE;
 	}
 	vaddr = dma64_to_virt(ib->data[i]);
-	return copy_from_user(vaddr, from, count);
+	return copy_from_user_partial(vaddr, from, count);
 }
 
 #endif
diff --git a/arch/sparc/kernel/termios.c b/arch/sparc/kernel/termios.c
index ee64965c27cd..db9c07b7d5ee 100644
--- a/arch/sparc/kernel/termios.c
+++ b/arch/sparc/kernel/termios.c
@@ -27,7 +27,7 @@ int kernel_termios_to_user_termio(struct termio __user *termio,
 		v.c_cc[_VMIN] = termios->c_cc[VMIN];
 		v.c_cc[_VTIME] = termios->c_cc[VTIME];
 	}
-	return copy_to_user(termio, &v, sizeof(struct termio));
+	return copy_to_user_partial(termio, &v, sizeof(struct termio));
 }
 
 int user_termios_to_kernel_termios(struct ktermios *k,
diff --git a/arch/um/kernel/process.c b/arch/um/kernel/process.c
index 63b38a3f73f7..d41625dfa00b 100644
--- a/arch/um/kernel/process.c
+++ b/arch/um/kernel/process.c
@@ -252,7 +252,7 @@ EXPORT_SYMBOL(uml_strdup);
 
 int copy_from_user_proc(void *to, void __user *from, int size)
 {
-	return copy_from_user(to, from, size);
+	return copy_from_user_partial(to, from, size);
 }
 
 int singlestepping(void)
diff --git a/arch/x86/lib/insn-eval.c b/arch/x86/lib/insn-eval.c
index e03eeec55cfe..e7cb03ab26f1 100644
--- a/arch/x86/lib/insn-eval.c
+++ b/arch/x86/lib/insn-eval.c
@@ -1512,7 +1512,7 @@ int insn_fetch_from_user(struct pt_regs *regs, unsigned char buf[MAX_INSN_SIZE])
 	if (insn_get_effective_ip(regs, &ip))
 		return -EINVAL;
 
-	not_copied = copy_from_user(buf, (void __user *)ip, MAX_INSN_SIZE);
+	not_copied = copy_from_user_partial(buf, (void __user *)ip, MAX_INSN_SIZE);
 
 	return MAX_INSN_SIZE - not_copied;
 }
diff --git a/arch/x86/um/signal.c b/arch/x86/um/signal.c
index 2934e170b0fe..e0fab7c1625b 100644
--- a/arch/x86/um/signal.c
+++ b/arch/x86/um/signal.c
@@ -40,7 +40,7 @@ static int copy_sc_from_user(struct pt_regs *regs,
 	/* Always make any pending restarted system calls return -EINTR */
 	current->restart_block.fn = do_no_restart_syscall;
 
-	err = copy_from_user(&sc, from, sizeof(sc));
+	err = copy_from_user_partial(&sc, from, sizeof(sc));
 	if (err)
 		return err;
 
diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c
index e4488ad86a65..8ba9c57b489c 100644
--- a/drivers/android/binder_alloc.c
+++ b/drivers/android/binder_alloc.c
@@ -1346,7 +1346,7 @@ binder_alloc_copy_user_to_buffer(struct binder_alloc *alloc,
 					     buffer_offset, &pgoff);
 		size = min_t(size_t, bytes, PAGE_SIZE - pgoff);
 		kptr = kmap_local_page(page) + pgoff;
-		ret = copy_from_user(kptr, from, size);
+		ret = copy_from_user_partial(kptr, from, size);
 		kunmap_local(kptr);
 		if (ret)
 			return bytes - size + ret;
diff --git a/drivers/comedi/comedi_fops.c b/drivers/comedi/comedi_fops.c
index c09bbe04be6c..272fdc54fb81 100644
--- a/drivers/comedi/comedi_fops.c
+++ b/drivers/comedi/comedi_fops.c
@@ -2659,7 +2659,7 @@ static unsigned int comedi_buf_copy_to_user(struct comedi_subdevice *s,
 		unsigned int copy_amount = min(n, PAGE_SIZE - offset);
 		unsigned int uncopied;
 
-		uncopied = copy_to_user(dest, buf_page_list[page].virt_addr +
+		uncopied = copy_to_user_partial(dest, buf_page_list[page].virt_addr +
 					offset, copy_amount);
 		copy_amount -= uncopied;
 		n -= copy_amount;
@@ -2687,7 +2687,7 @@ static unsigned int comedi_buf_copy_from_user(struct comedi_subdevice *s,
 		unsigned int copy_amount = min(n, PAGE_SIZE - offset);
 		unsigned int uncopied;
 
-		uncopied = copy_from_user(buf_page_list[page].virt_addr +
+		uncopied = copy_from_user_partial(buf_page_list[page].virt_addr +
 					  offset, src, copy_amount);
 		copy_amount -= uncopied;
 		n -= copy_amount;
diff --git a/drivers/dma/idxd/cdev.c b/drivers/dma/idxd/cdev.c
index 0366c7cf3502..ac79bab6d6c3 100644
--- a/drivers/dma/idxd/cdev.c
+++ b/drivers/dma/idxd/cdev.c
@@ -751,7 +751,7 @@ int idxd_copy_cr(struct idxd_wq *wq, ioasid_t pasid, unsigned long addr,
 	 * to addr in the mm.
 	 */
 	kthread_use_mm(mm);
-	left = copy_to_user((void __user *)addr + status_size, cr + status_size,
+	left = copy_to_user_partial((void __user *)addr + status_size, cr + status_size,
 			    len - status_size);
 	/*
 	 * Copy status only after the rest of completion record is copied
diff --git a/drivers/firmware/efi/test/efi_test.c b/drivers/firmware/efi/test/efi_test.c
index d54d6a671326..43b280ceb955 100644
--- a/drivers/firmware/efi/test/efi_test.c
+++ b/drivers/firmware/efi/test/efi_test.c
@@ -133,7 +133,7 @@ copy_ucs2_to_user_len(efi_char16_t __user *dst, efi_char16_t *src, size_t len)
 	if (!src)
 		return 0;
 
-	return copy_to_user(dst, src, len);
+	return copy_to_user_partial(dst, src, len);
 }
 
 static long efi_runtime_get_variable(unsigned long arg)
diff --git a/drivers/fsi/fsi-scom.c b/drivers/fsi/fsi-scom.c
index bb4d3700c934..370ec75b20e6 100644
--- a/drivers/fsi/fsi-scom.c
+++ b/drivers/fsi/fsi-scom.c
@@ -332,7 +332,7 @@ static ssize_t scom_read(struct file *filep, char __user *buf, size_t len,
 		return rc;
 	}
 
-	rc = copy_to_user(buf, &val, len);
+	rc = copy_to_user_partial(buf, &val, len);
 	if (rc)
 		dev_dbg(dev, "copy to user failed:%d\n", rc);
 
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c
index 2409ac72b166..712605ec7ecc 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c
@@ -1346,7 +1346,7 @@ static ssize_t dp_sdp_message_debugfs_write(struct file *f, const char __user *b
 
 	acrtc_state = to_dm_crtc_state(connector->base.state->crtc->state);
 
-	r = copy_from_user(data, buf, write_size);
+	r = copy_from_user_partial(data, buf, write_size);
 
 	write_size -= r;
 
diff --git a/drivers/gpu/drm/i915/gt/intel_sseu.c b/drivers/gpu/drm/i915/gt/intel_sseu.c
index 656a499b2706..3f5b450a914a 100644
--- a/drivers/gpu/drm/i915/gt/intel_sseu.c
+++ b/drivers/gpu/drm/i915/gt/intel_sseu.c
@@ -114,7 +114,7 @@ int intel_sseu_copy_eumask_to_user(void __user *to,
 		}
 	}
 
-	return copy_to_user(to, eu_mask, len);
+	return copy_to_user_partial(to, eu_mask, len);
 }
 
 /**
@@ -146,7 +146,7 @@ int intel_sseu_copy_ssmask_to_user(void __user *to,
 		}
 	}
 
-	return copy_to_user(to, ss_mask, len);
+	return copy_to_user_partial(to, ss_mask, len);
 }
 
 static void gen11_compute_sseu_info(struct sseu_dev_info *sseu,
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index a432daf8038a..c1c2e762498f 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -291,7 +291,7 @@ gtt_user_read(struct io_mapping *mapping,
 	io_mapping_unmap_atomic(vaddr);
 	if (unwritten) {
 		vaddr = io_mapping_map_wc(mapping, base, PAGE_SIZE);
-		unwritten = copy_to_user(user_data,
+		unwritten = copy_to_user_partial(user_data,
 					 (void __force *)vaddr + offset,
 					 length);
 		io_mapping_unmap(vaddr);
@@ -525,7 +525,7 @@ ggtt_write(struct io_mapping *mapping,
 	io_mapping_unmap_atomic(vaddr);
 	if (unwritten) {
 		vaddr = io_mapping_map_wc(mapping, base, PAGE_SIZE);
-		unwritten = copy_from_user((void __force *)vaddr + offset,
+		unwritten = copy_from_user_partial((void __force *)vaddr + offset,
 					   user_data, length);
 		io_mapping_unmap(vaddr);
 	}
diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c
index a82cf74f39ad..9b97b71b44f1 100644
--- a/drivers/hwtracing/intel_th/msu.c
+++ b/drivers/hwtracing/intel_th/msu.c
@@ -1457,7 +1457,7 @@ static unsigned long msc_win_to_user(void *data, void *src, size_t len)
 	struct msc_win_to_user_struct *u = data;
 	unsigned long ret;
 
-	ret = copy_to_user(u->buf + u->offset, src, len);
+	ret = copy_to_user_partial(u->buf + u->offset, src, len);
 	u->offset += len - ret;
 
 	return ret;
diff --git a/drivers/misc/ibmvmc.c b/drivers/misc/ibmvmc.c
index beb18c34f20d..e1d99354dd29 100644
--- a/drivers/misc/ibmvmc.c
+++ b/drivers/misc/ibmvmc.c
@@ -1112,7 +1112,7 @@ static ssize_t ibmvmc_write(struct file *file, const char *buffer,
 	while (c > 0) {
 		bytes = min_t(size_t, c, vmc_buffer->size);
 
-		bytes -= copy_from_user(buf, p, bytes);
+		bytes -= copy_from_user_partial(buf, p, bytes);
 		if (!bytes) {
 			ret = -EFAULT;
 			goto out;
diff --git a/drivers/misc/vmw_vmci/vmci_host.c b/drivers/misc/vmw_vmci/vmci_host.c
index b71ca1bf0a20..bd502edbc173 100644
--- a/drivers/misc/vmw_vmci/vmci_host.c
+++ b/drivers/misc/vmw_vmci/vmci_host.c
@@ -213,7 +213,7 @@ static int drv_cp_harray_to_user(void __user *user_buf_uva,
 
 	*user_buf_size = array_size * sizeof(*handles);
 	if (*user_buf_size)
-		*retval = copy_to_user(user_buf_uva,
+		*retval = copy_to_user_partial(user_buf_uva,
 				       vmci_handle_arr_get_handles
 				       (handle_array), *user_buf_size);
 
diff --git a/drivers/most/most_cdev.c b/drivers/most/most_cdev.c
index 5df508d8d60a..969c865ccbef 100644
--- a/drivers/most/most_cdev.c
+++ b/drivers/most/most_cdev.c
@@ -265,7 +265,7 @@ comp_read(struct file *filp, char __user *buf, size_t count, loff_t *offset)
 			count,
 			mbo->processed_length - c->mbo_offs);
 
-	not_copied = copy_to_user(buf,
+	not_copied = copy_to_user_partial(buf,
 				  mbo->virt_address + c->mbo_offs,
 				  to_copy);
 
diff --git a/drivers/net/ieee802154/ca8210.c b/drivers/net/ieee802154/ca8210.c
index ed4178155a5d..d474a008c73e 100644
--- a/drivers/net/ieee802154/ca8210.c
+++ b/drivers/net/ieee802154/ca8210.c
@@ -2460,7 +2460,7 @@ static ssize_t ca8210_test_int_user_write(
 		return -EBADE;
 	}
 
-	ret = copy_from_user(command, in_buf, len);
+	ret = copy_from_user_partial(command, in_buf, len);
 	if (ret) {
 		dev_err(
 			&priv->spi->dev,
@@ -2548,7 +2548,7 @@ static ssize_t ca8210_test_int_user_read(
 	cmdlen = fifo_buffer[1];
 	bytes_not_copied = cmdlen + 2;
 
-	bytes_not_copied = copy_to_user(buf, fifo_buffer, bytes_not_copied);
+	bytes_not_copied = copy_to_user_partial(buf, fifo_buffer, bytes_not_copied);
 	if (bytes_not_copied > 0) {
 		dev_err(
 			&priv->spi->dev,
diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c
index b8cb736a7185..f2130248fb7f 100644
--- a/drivers/net/wireless/ath/wil6210/debugfs.c
+++ b/drivers/net/wireless/ath/wil6210/debugfs.c
@@ -659,7 +659,7 @@ static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf,
 	wil_memcpy_fromio_32(buf, (const void __iomem *)
 			     wil_blob->blob.data + aligned_pos, aligned_count);
 
-	ret = copy_to_user(user_buf, buf + unaligned_bytes, count);
+	ret = copy_to_user_partial(user_buf, buf + unaligned_bytes, count);
 
 	wil_mem_access_unlock(wil);
 	wil_pm_runtime_put(wil);
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c
index a05f60f9224b..66ddaa0d8e36 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c
@@ -3060,7 +3060,7 @@ static bool iwl_write_to_user_buf(char __user *user_buf, ssize_t count,
 	if (*size > buf_size_left)
 		*size = buf_size_left;
 
-	*size -= copy_to_user(user_buf, buf, *size);
+	*size -= copy_to_user_partial(user_buf, buf, *size);
 	*bytes_copied += *size;
 
 	if (buf_size_left == *size)
diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c
index bbfd2725215b..d359baea5100 100644
--- a/drivers/net/wireless/ti/wlcore/debugfs.c
+++ b/drivers/net/wireless/ti/wlcore/debugfs.c
@@ -1088,7 +1088,7 @@ static ssize_t dev_mem_read(struct file *file,
 	mutex_unlock(&wl->mutex);
 
 	if (ret == 0) {
-		ret = copy_to_user(user_buf, buf, bytes);
+		ret = copy_to_user_partial(user_buf, buf, bytes);
 		if (ret < bytes) {
 			bytes -= ret;
 			*ppos += bytes;
diff --git a/drivers/ps3/ps3-lpm.c b/drivers/ps3/ps3-lpm.c
index f8d8f607134a..5a2b150cda49 100644
--- a/drivers/ps3/ps3-lpm.c
+++ b/drivers/ps3/ps3-lpm.c
@@ -999,7 +999,7 @@ int ps3_lpm_copy_tb_to_user(unsigned long offset, void __user *buf,
 			return result == LV1_WRONG_STATE ? -EBUSY : -EINVAL;
 		}
 
-		result = copy_to_user(buf, lpm_priv->tb_cache, tmp);
+		result = copy_to_user_partial(buf, lpm_priv->tb_cache, tmp);
 
 		if (result) {
 			dev_dbg(sbd_core(), "%s:%u: 0x%llx bytes at 0x%p\n",
diff --git a/drivers/s390/crypto/zcrypt_api.h b/drivers/s390/crypto/zcrypt_api.h
index 6ef8850a42df..61a5de90c354 100644
--- a/drivers/s390/crypto/zcrypt_api.h
+++ b/drivers/s390/crypto/zcrypt_api.h
@@ -185,7 +185,7 @@ static inline unsigned long z_copy_from_user(bool userspace,
 					     unsigned long n)
 {
 	if (likely(userspace))
-		return copy_from_user(to, from, n);
+		return copy_from_user_partial(to, from, n);
 	memcpy(to, (void __force *)from, n);
 	return 0;
 }
@@ -196,7 +196,7 @@ static inline unsigned long z_copy_to_user(bool userspace,
 					   unsigned long n)
 {
 	if (likely(userspace))
-		return copy_to_user(to, from, n);
+		return copy_to_user_partial(to, from, n);
 	memcpy((void __force *)to, from, n);
 	return 0;
 }
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index 638221178384..5b42fabcf4c4 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -157,7 +157,7 @@ spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
 	if (status > 0) {
 		unsigned long	missing;
 
-		missing = copy_to_user(buf, spidev->rx_buffer, status);
+		missing = copy_to_user_partial(buf, spidev->rx_buffer, status);
 		if (missing == status)
 			status = -EFAULT;
 		else
diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c
index fec369575d88..10a7aff375a9 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c
@@ -1491,7 +1491,7 @@ int atomisp_gdc_cac_table(struct atomisp_sub_device *asd, int flag,
 		}
 
 		for (i = 0; i < IA_CSS_MORPH_TABLE_NUM_PLANES; i++) {
-			ret = copy_from_user(tab->coordinates_x[i],
+			ret = copy_from_user_partial(tab->coordinates_x[i],
 					     config->coordinates_x[i],
 					     config->height * config->width *
 					     sizeof(*config->coordinates_x[i]));
@@ -1502,7 +1502,7 @@ int atomisp_gdc_cac_table(struct atomisp_sub_device *asd, int flag,
 				atomisp_css_morph_table_free(tab);
 				return -EFAULT;
 			}
-			ret = copy_from_user(tab->coordinates_y[i],
+			ret = copy_from_user_partial(tab->coordinates_y[i],
 					     config->coordinates_y[i],
 					     config->height * config->width *
 					     sizeof(*config->coordinates_y[i]));
@@ -1709,7 +1709,7 @@ int atomisp_3a_stat(struct atomisp_sub_device *asd, int flag,
 	config->exp_id = s3a_buf->s3a_data->exp_id;
 	config->isp_config_id = s3a_buf->s3a_data->isp_config_id;
 
-	ret = copy_to_user(config->data, asd->params.s3a_user_stat->data,
+	ret = copy_to_user_partial(config->data, asd->params.s3a_user_stat->data,
 			   asd->params.s3a_output_bytes);
 	if (ret) {
 		dev_err(isp->dev, "copy to user failed: copied %lu bytes\n",
@@ -2031,7 +2031,7 @@ static unsigned int long copy_from_compatible(void *to, const void *from,
 	unsigned long n, bool from_user)
 {
 	if (from_user)
-		return copy_from_user(to, (void __user *)from, n);
+		return copy_from_user_partial(to, (void __user *)from, n);
 	else
 		memcpy(to, from, n);
 	return 0;
diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c
index 90c70d8d14e3..cdc274c0ff81 100644
--- a/drivers/tty/tty_ioctl.c
+++ b/drivers/tty/tty_ioctl.c
@@ -388,29 +388,29 @@ __weak int kernel_termios_to_user_termio(struct termio __user *termio,
 	v.c_lflag = termios->c_lflag;
 	v.c_line = termios->c_line;
 	memcpy(v.c_cc, termios->c_cc, NCC);
-	return copy_to_user(termio, &v, sizeof(struct termio));
+	return copy_to_user_partial(termio, &v, sizeof(struct termio));
 }
 
 #ifdef TCGETS2
 __weak int user_termios_to_kernel_termios(struct ktermios *k,
 						 struct termios2 __user *u)
 {
-	return copy_from_user(k, u, sizeof(struct termios2));
+	return copy_from_user_partial(k, u, sizeof(struct termios2));
 }
 __weak int kernel_termios_to_user_termios(struct termios2 __user *u,
 						 struct ktermios *k)
 {
-	return copy_to_user(u, k, sizeof(struct termios2));
+	return copy_to_user_partial(u, k, sizeof(struct termios2));
 }
 __weak int user_termios_to_kernel_termios_1(struct ktermios *k,
 						   struct termios __user *u)
 {
-	return copy_from_user(k, u, sizeof(struct termios));
+	return copy_from_user_partial(k, u, sizeof(struct termios));
 }
 __weak int kernel_termios_to_user_termios_1(struct termios __user *u,
 						   struct ktermios *k)
 {
-	return copy_to_user(u, k, sizeof(struct termios));
+	return copy_to_user_partial(u, k, sizeof(struct termios));
 }
 
 #else
@@ -418,12 +418,12 @@ __weak int kernel_termios_to_user_termios_1(struct termios __user *u,
 __weak int user_termios_to_kernel_termios(struct ktermios *k,
 						 struct termios __user *u)
 {
-	return copy_from_user(k, u, sizeof(struct termios));
+	return copy_from_user_partial(k, u, sizeof(struct termios));
 }
 __weak int kernel_termios_to_user_termios(struct termios __user *u,
 						 struct ktermios *k)
 {
-	return copy_to_user(u, k, sizeof(struct termios));
+	return copy_to_user_partial(u, k, sizeof(struct termios));
 }
 #endif /* TCGETS2 */
 
diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c
index 4d2d46c95fef..e54c708149c3 100644
--- a/drivers/tty/vt/vc_screen.c
+++ b/drivers/tty/vt/vc_screen.c
@@ -450,7 +450,7 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
 		 */
 
 		console_unlock();
-		ret = copy_to_user(buf, con_buf + skip, this_round);
+		ret = copy_to_user_partial(buf, con_buf + skip, this_round);
 		console_lock();
 
 		if (ret) {
@@ -630,7 +630,7 @@ vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
 		 * in the write data from userspace safely.
 		 */
 		console_unlock();
-		ret = copy_from_user(con_buf, buf, this_round);
+		ret = copy_from_user_partial(con_buf, buf, this_round);
 		console_lock();
 
 		if (ret) {
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index c5a12a6760ea..f22dd3697a46 100644
--- a/drivers/usb/gadget/function/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -349,7 +349,7 @@ static ssize_t f_hidg_intout_read(struct file *file, char __user *buffer,
 	spin_unlock_irqrestore(&hidg->read_spinlock, flags);
 
 	/* copy to user outside spinlock */
-	count -= copy_to_user(buffer, req->buf + list->pos, count);
+	count -= copy_to_user_partial(buffer, req->buf + list->pos, count);
 	list->pos += count;
 
 	/*
@@ -410,7 +410,7 @@ static ssize_t f_hidg_ssreport_read(struct file *file, char __user *buffer,
 	spin_unlock_irqrestore(&hidg->read_spinlock, flags);
 
 	if (tmp_buf != NULL) {
-		count -= copy_to_user(buffer, tmp_buf, count);
+		count -= copy_to_user_partial(buffer, tmp_buf, count);
 		kfree(tmp_buf);
 	} else {
 		count = -ENOMEM;
diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c
index e4f7828ae75d..4fbed987b639 100644
--- a/drivers/usb/gadget/function/f_printer.c
+++ b/drivers/usb/gadget/function/f_printer.c
@@ -525,7 +525,7 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
 		else
 			size = len;
 
-		size -= copy_to_user(buf, current_rx_buf, size);
+		size -= copy_to_user_partial(buf, current_rx_buf, size);
 		bytes_copied += size;
 		len -= size;
 		buf += size;
diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
index c8151ba54de3..ad74a891aa80 100644
--- a/drivers/vfio/vfio_iommu_type1.c
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -3173,7 +3173,7 @@ static int vfio_iommu_type1_dma_rw_chunk(struct vfio_iommu *iommu,
 	vaddr = dma->vaddr + offset;
 
 	if (write) {
-		*copied = copy_to_user((void __user *)vaddr, data,
+		*copied = copy_to_user_partial((void __user *)vaddr, data,
 					 count) ? 0 : count;
 		if (*copied && iommu->dirty_page_tracking) {
 			unsigned long pgshift = __ffs(iommu->pgsize_bitmap);
@@ -3186,7 +3186,7 @@ static int vfio_iommu_type1_dma_rw_chunk(struct vfio_iommu *iommu,
 				   (offset >> pgshift) + 1);
 		}
 	} else
-		*copied = copy_from_user(data, (void __user *)vaddr,
+		*copied = copy_from_user_partial(data, (void __user *)vaddr,
 					   count) ? 0 : count;
 	if (kthread)
 		kthread_unuse_mm(mm);
diff --git a/drivers/xen/xenbus/xenbus_dev_frontend.c b/drivers/xen/xenbus/xenbus_dev_frontend.c
index 61db6932a9d2..b1db90dac1d1 100644
--- a/drivers/xen/xenbus/xenbus_dev_frontend.c
+++ b/drivers/xen/xenbus/xenbus_dev_frontend.c
@@ -150,7 +150,7 @@ static ssize_t xenbus_file_read(struct file *filp,
 	while (i < len) {
 		size_t sz = min_t(size_t, len - i, rb->len - rb->cons);
 
-		ret = copy_to_user(ubuf + i, &rb->msg[rb->cons], sz);
+		ret = copy_to_user_partial(ubuf + i, &rb->msg[rb->cons], sz);
 
 		i += sz - ret;
 		rb->cons += sz - ret;
diff --git a/fs/namespace.c b/fs/namespace.c
index fe919abd2f01..27afb73fef20 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -4033,7 +4033,7 @@ static void *copy_mount_options(const void __user * data)
 	if (!copy)
 		return ERR_PTR(-ENOMEM);
 
-	left = copy_from_user(copy, data, PAGE_SIZE);
+	left = copy_from_user_partial(copy, data, PAGE_SIZE);
 
 	/*
 	 * Not all architectures have an exact copy_from_user(). Resort to
diff --git a/fs/ocfs2/dlmfs/dlmfs.c b/fs/ocfs2/dlmfs/dlmfs.c
index 5821e33df78f..97c0b391b98e 100644
--- a/fs/ocfs2/dlmfs/dlmfs.c
+++ b/fs/ocfs2/dlmfs/dlmfs.c
@@ -255,7 +255,7 @@ static ssize_t dlmfs_file_write(struct file *filp,
 	if (!count)
 		return 0;
 
-	bytes_left = copy_from_user(lvb_buf, buf, count);
+	bytes_left = copy_from_user_partial(lvb_buf, buf, count);
 	count -= bytes_left;
 	if (count)
 		user_dlm_write_lvb(inode, lvb_buf, count);
diff --git a/fs/proc/base.c b/fs/proc/base.c
index d9acfa89c894..49577662ae70 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -278,7 +278,7 @@ static ssize_t get_mm_proctitle(struct mm_struct *mm, char __user *buf,
 			len -= pos;
 			if (len > count)
 				len = count;
-			len -= copy_to_user(buf, page+pos, len);
+			len -= copy_to_user_partial(buf, page+pos, len);
 			if (!len)
 				len = -EFAULT;
 			ret = len;
@@ -359,7 +359,7 @@ static ssize_t get_mm_cmdline(struct mm_struct *mm, char __user *buf,
 		got = access_remote_vm(mm, pos, page, size, FOLL_ANON);
 		if (got <= 0)
 			break;
-		got -= copy_to_user(buf, page, got);
+		got -= copy_to_user_partial(buf, page, got);
 		if (unlikely(!got)) {
 			if (!len)
 				len = -EFAULT;
diff --git a/include/linux/bpfptr.h b/include/linux/bpfptr.h
index f6e0795db484..e4444d0f0cfe 100644
--- a/include/linux/bpfptr.h
+++ b/include/linux/bpfptr.h
@@ -50,7 +50,7 @@ static inline int copy_from_bpfptr_offset(void *dst, bpfptr_t src,
 					  size_t offset, size_t size)
 {
 	if (!bpfptr_is_kernel(src))
-		return copy_from_user(dst, src.user + offset, size);
+		return copy_from_user_partial(dst, src.user + offset, size);
 	return copy_from_kernel_nofault(dst, src.kernel + offset, size);
 }
 
diff --git a/include/linux/sockptr.h b/include/linux/sockptr.h
index 3e6c8e9d67ae..52ddddfe728d 100644
--- a/include/linux/sockptr.h
+++ b/include/linux/sockptr.h
@@ -45,7 +45,7 @@ static inline int copy_from_sockptr_offset(void *dst, sockptr_t src,
 		size_t offset, size_t size)
 {
 	if (!sockptr_is_kernel(src))
-		return copy_from_user(dst, src.user + offset, size);
+		return copy_from_user_partial(dst, src.user + offset, size);
 	memcpy(dst, src.kernel + offset, size);
 	return 0;
 }
@@ -111,7 +111,7 @@ static inline int copy_to_sockptr_offset(sockptr_t dst, size_t offset,
 		const void *src, size_t size)
 {
 	if (!sockptr_is_kernel(dst))
-		return copy_to_user(dst.user + offset, src, size);
+		return copy_to_user_partial(dst.user + offset, src, size);
 	memcpy(dst.kernel + offset, src, size);
 	return 0;
 }
diff --git a/ipc/msg.c b/ipc/msg.c
index 62996b97f0ac..39848238219d 100644
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -322,7 +322,7 @@ copy_msqid_to_user(void __user *buf, struct msqid64_ds *in, int version)
 {
 	switch (version) {
 	case IPC_64:
-		return copy_to_user(buf, in, sizeof(*in));
+		return copy_to_user_partial(buf, in, sizeof(*in));
 	case IPC_OLD:
 	{
 		struct msqid_ds out;
@@ -355,7 +355,7 @@ copy_msqid_to_user(void __user *buf, struct msqid64_ds *in, int version)
 		out.msg_lspid		= in->msg_lspid;
 		out.msg_lrpid		= in->msg_lrpid;
 
-		return copy_to_user(buf, &out, sizeof(out));
+		return copy_to_user_partial(buf, &out, sizeof(out));
 	}
 	default:
 		return -EINVAL;
@@ -712,7 +712,7 @@ static int copy_compat_msqid_to_user(void __user *buf, struct msqid64_ds *in,
 		v.msg_qbytes = in->msg_qbytes;
 		v.msg_lspid = in->msg_lspid;
 		v.msg_lrpid = in->msg_lrpid;
-		return copy_to_user(buf, &v, sizeof(v));
+		return copy_to_user_partial(buf, &v, sizeof(v));
 	} else {
 		struct compat_msqid_ds v;
 		memset(&v, 0, sizeof(v));
@@ -725,7 +725,7 @@ static int copy_compat_msqid_to_user(void __user *buf, struct msqid64_ds *in,
 		v.msg_qbytes = in->msg_qbytes;
 		v.msg_lspid = in->msg_lspid;
 		v.msg_lrpid = in->msg_lrpid;
-		return copy_to_user(buf, &v, sizeof(v));
+		return copy_to_user_partial(buf, &v, sizeof(v));
 	}
 }
 
diff --git a/ipc/sem.c b/ipc/sem.c
index 6cdf862b1f5c..3b56086ba07d 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -1196,7 +1196,7 @@ static unsigned long copy_semid_to_user(void __user *buf, struct semid64_ds *in,
 {
 	switch (version) {
 	case IPC_64:
-		return copy_to_user(buf, in, sizeof(*in));
+		return copy_to_user_partial(buf, in, sizeof(*in));
 	case IPC_OLD:
 	    {
 		struct semid_ds out;
@@ -1209,7 +1209,7 @@ static unsigned long copy_semid_to_user(void __user *buf, struct semid64_ds *in,
 		out.sem_ctime	= in->sem_ctime;
 		out.sem_nsems	= in->sem_nsems;
 
-		return copy_to_user(buf, &out, sizeof(out));
+		return copy_to_user_partial(buf, &out, sizeof(out));
 	    }
 	default:
 		return -EINVAL;
@@ -1759,7 +1759,7 @@ static int copy_compat_semid_to_user(void __user *buf, struct semid64_ds *in,
 		v.sem_ctime	 = lower_32_bits(in->sem_ctime);
 		v.sem_ctime_high = upper_32_bits(in->sem_ctime);
 		v.sem_nsems = in->sem_nsems;
-		return copy_to_user(buf, &v, sizeof(v));
+		return copy_to_user_partial(buf, &v, sizeof(v));
 	} else {
 		struct compat_semid_ds v;
 		memset(&v, 0, sizeof(v));
@@ -1767,7 +1767,7 @@ static int copy_compat_semid_to_user(void __user *buf, struct semid64_ds *in,
 		v.sem_otime = in->sem_otime;
 		v.sem_ctime = in->sem_ctime;
 		v.sem_nsems = in->sem_nsems;
-		return copy_to_user(buf, &v, sizeof(v));
+		return copy_to_user_partial(buf, &v, sizeof(v));
 	}
 }
 
diff --git a/ipc/shm.c b/ipc/shm.c
index a95dae447707..1eb53c3df3b9 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -853,7 +853,7 @@ static inline unsigned long copy_shmid_to_user(void __user *buf, struct shmid64_
 {
 	switch (version) {
 	case IPC_64:
-		return copy_to_user(buf, in, sizeof(*in));
+		return copy_to_user_partial(buf, in, sizeof(*in));
 	case IPC_OLD:
 	    {
 		struct shmid_ds out;
@@ -868,7 +868,7 @@ static inline unsigned long copy_shmid_to_user(void __user *buf, struct shmid64_
 		out.shm_lpid	= in->shm_lpid;
 		out.shm_nattch	= in->shm_nattch;
 
-		return copy_to_user(buf, &out, sizeof(out));
+		return copy_to_user_partial(buf, &out, sizeof(out));
 	    }
 	default:
 		return -EINVAL;
@@ -905,7 +905,7 @@ static inline unsigned long copy_shminfo_to_user(void __user *buf, struct shminf
 {
 	switch (version) {
 	case IPC_64:
-		return copy_to_user(buf, in, sizeof(*in));
+		return copy_to_user_partial(buf, in, sizeof(*in));
 	case IPC_OLD:
 	    {
 		struct shminfo out;
@@ -920,7 +920,7 @@ static inline unsigned long copy_shminfo_to_user(void __user *buf, struct shminf
 		out.shmseg	= in->shmseg;
 		out.shmall	= in->shmall;
 
-		return copy_to_user(buf, &out, sizeof(out));
+		return copy_to_user_partial(buf, &out, sizeof(out));
 	    }
 	default:
 		return -EINVAL;
@@ -1359,7 +1359,7 @@ static int copy_compat_shminfo_to_user(void __user *buf, struct shminfo64 *in,
 		info.shmmni = in->shmmni;
 		info.shmseg = in->shmseg;
 		info.shmall = in->shmall;
-		return copy_to_user(buf, &info, sizeof(info));
+		return copy_to_user_partial(buf, &info, sizeof(info));
 	} else {
 		struct shminfo info;
 		memset(&info, 0, sizeof(info));
@@ -1368,7 +1368,7 @@ static int copy_compat_shminfo_to_user(void __user *buf, struct shminfo64 *in,
 		info.shmmni = in->shmmni;
 		info.shmseg = in->shmseg;
 		info.shmall = in->shmall;
-		return copy_to_user(buf, &info, sizeof(info));
+		return copy_to_user_partial(buf, &info, sizeof(info));
 	}
 }
 
@@ -1384,7 +1384,7 @@ static int put_compat_shm_info(struct shm_info *ip,
 	info.shm_swp = ip->shm_swp;
 	info.swap_attempts = ip->swap_attempts;
 	info.swap_successes = ip->swap_successes;
-	return copy_to_user(uip, &info, sizeof(info));
+	return copy_to_user_partial(uip, &info, sizeof(info));
 }
 
 static int copy_compat_shmid_to_user(void __user *buf, struct shmid64_ds *in,
@@ -1404,7 +1404,7 @@ static int copy_compat_shmid_to_user(void __user *buf, struct shmid64_ds *in,
 		v.shm_nattch = in->shm_nattch;
 		v.shm_cpid = in->shm_cpid;
 		v.shm_lpid = in->shm_lpid;
-		return copy_to_user(buf, &v, sizeof(v));
+		return copy_to_user_partial(buf, &v, sizeof(v));
 	} else {
 		struct compat_shmid_ds v;
 		memset(&v, 0, sizeof(v));
@@ -1417,7 +1417,7 @@ static int copy_compat_shmid_to_user(void __user *buf, struct shmid64_ds *in,
 		v.shm_nattch = in->shm_nattch;
 		v.shm_cpid = in->shm_cpid;
 		v.shm_lpid = in->shm_lpid;
-		return copy_to_user(buf, &v, sizeof(v));
+		return copy_to_user_partial(buf, &v, sizeof(v));
 	}
 }
 
diff --git a/kernel/regset.c b/kernel/regset.c
index b2871fa68b2a..29c6d19c3465 100644
--- a/kernel/regset.c
+++ b/kernel/regset.c
@@ -70,7 +70,7 @@ int copy_regset_to_user(struct task_struct *target,
 
 	ret = regset_get_alloc(target, regset, size, &buf);
 	if (ret > 0)
-		ret = copy_to_user(data, buf, ret) ? -EFAULT : 0;
+		ret = copy_to_user_partial(data, buf, ret) ? -EFAULT : 0;
 	kvfree(buf);
 	return ret;
 }
diff --git a/kernel/sys.c b/kernel/sys.c
index 62e842055cc9..8e1ce8c26884 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -1343,7 +1343,7 @@ static int override_release(char __user *release, size_t len)
 		v = LINUX_VERSION_PATCHLEVEL + 60;
 		copy = clamp_t(size_t, len, 1, sizeof(buf));
 		copy = scnprintf(buf, copy, "2.6.%u%s", v, rest);
-		ret = copy_to_user(release, buf, copy + 1);
+		ret = copy_to_user_partial(release, buf, copy + 1);
 	}
 	return ret;
 }
@@ -1567,7 +1567,7 @@ SYSCALL_DEFINE2(getrlimit, unsigned int, resource, struct rlimit __user *, rlim)
 
 	ret = do_prlimit(current, resource, NULL, &value);
 	if (!ret)
-		ret = copy_to_user(rlim, &value, sizeof(*rlim)) ? -EFAULT : 0;
+		ret = copy_to_user_partial(rlim, &value, sizeof(*rlim)) ? -EFAULT : 0;
 
 	return ret;
 }
diff --git a/lib/kfifo.c b/lib/kfifo.c
index 2633f9cc336c..00c19a321aae 100644
--- a/lib/kfifo.c
+++ b/lib/kfifo.c
@@ -203,11 +203,11 @@ static unsigned long kfifo_copy_from_user(struct __kfifo *fifo,
 	}
 	l = min(len, size - off);
 
-	ret = copy_from_user(fifo->data + off, from, l);
+	ret = copy_from_user_partial(fifo->data + off, from, l);
 	if (unlikely(ret))
 		ret = DIV_ROUND_UP(ret + len - l, esize);
 	else {
-		ret = copy_from_user(fifo->data, from + l, len - l);
+		ret = copy_from_user_partial(fifo->data, from + l, len - l);
 		if (unlikely(ret))
 			ret = DIV_ROUND_UP(ret, esize);
 	}
@@ -263,11 +263,11 @@ static unsigned long kfifo_copy_to_user(struct __kfifo *fifo, void __user *to,
 	}
 	l = min(len, size - off);
 
-	ret = copy_to_user(to, fifo->data + off, l);
+	ret = copy_to_user_partial(to, fifo->data + off, l);
 	if (unlikely(ret))
 		ret = DIV_ROUND_UP(ret + len - l, esize);
 	else {
-		ret = copy_to_user(to + l, fifo->data, len - l);
+		ret = copy_to_user_partial(to + l, fifo->data, len - l);
 		if (unlikely(ret))
 			ret = DIV_ROUND_UP(ret, esize);
 	}
diff --git a/mm/kasan/kasan_test_c.c b/mm/kasan/kasan_test_c.c
index 32d06cbf6a31..a4d19fc1068a 100644
--- a/mm/kasan/kasan_test_c.c
+++ b/mm/kasan/kasan_test_c.c
@@ -2169,9 +2169,9 @@ static void copy_user_test_oob(struct kunit *test)
 	usermem = (char __user *)useraddr;
 
 	KUNIT_EXPECT_KASAN_FAIL(test,
-		unused = copy_from_user(kmem, usermem, size + 1));
+		unused = copy_from_user_partial(kmem, usermem, size + 1));
 	KUNIT_EXPECT_KASAN_FAIL_READ(test,
-		unused = copy_to_user(usermem, kmem, size + 1));
+		unused = copy_to_user_partial(usermem, kmem, size + 1));
 	KUNIT_EXPECT_KASAN_FAIL(test,
 		unused = __copy_from_user(kmem, usermem, size + 1));
 	KUNIT_EXPECT_KASAN_FAIL_READ(test,
diff --git a/mm/memory.c b/mm/memory.c
index ea6568571131..5a2f7543a2da 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -7529,7 +7529,7 @@ long copy_folio_from_user(struct folio *dst_folio,
 		kaddr = kmap_local_page(subpage);
 		if (!allow_pagefault)
 			pagefault_disable();
-		rc = copy_from_user(kaddr, usr_src + i * PAGE_SIZE, PAGE_SIZE);
+		rc = copy_from_user_partial(kaddr, usr_src + i * PAGE_SIZE, PAGE_SIZE);
 		if (!allow_pagefault)
 			pagefault_enable();
 		kunmap_local(kaddr);
diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c
index af8762b24039..7327c98b206a 100644
--- a/net/x25/af_x25.c
+++ b/net/x25/af_x25.c
@@ -471,7 +471,7 @@ static int x25_getsockopt(struct socket *sock, int level, int optname,
 		goto out;
 
 	val = test_bit(X25_Q_BIT_FLAG, &x25_sk(sk)->flags);
-	rc = copy_to_user(optval, &val, len) ? -EFAULT : 0;
+	rc = copy_to_user_partial(optval, &val, len) ? -EFAULT : 0;
 out:
 	return rc;
 }
diff --git a/rust/helpers/uaccess.c b/rust/helpers/uaccess.c
index 01de4fbbcc84..710e07cd60ae 100644
--- a/rust/helpers/uaccess.c
+++ b/rust/helpers/uaccess.c
@@ -5,13 +5,13 @@
 __rust_helper unsigned long
 rust_helper_copy_from_user(void *to, const void __user *from, unsigned long n)
 {
-	return copy_from_user(to, from, n);
+	return copy_from_user_partial(to, from, n);
 }
 
 __rust_helper unsigned long
 rust_helper_copy_to_user(void __user *to, const void *from, unsigned long n)
 {
-	return copy_to_user(to, from, n);
+	return copy_to_user_partial(to, from, n);
 }
 
 #ifndef CONFIG_ARCH_WANTS_NOINLINE_COPY_USER
diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c
index 08e0556bf161..3941bf9666a9 100644
--- a/sound/pci/emu10k1/emufx.c
+++ b/sound/pci/emu10k1/emufx.c
@@ -739,10 +739,10 @@ static int copy_gctl_to_user(struct snd_emu10k1 *emu,
 
 	_dst = (struct snd_emu10k1_fx8010_control_gpr __user *)dst;
 	if (emu->support_tlv)
-		return copy_to_user(&_dst[idx], src, sizeof(*src));
+		return copy_to_user_partial(&_dst[idx], src, sizeof(*src));
 	
 	octl = (struct snd_emu10k1_fx8010_control_old_gpr __user *)dst;
-	return copy_to_user(&octl[idx], src, sizeof(*octl));
+	return copy_to_user_partial(&octl[idx], src, sizeof(*octl));
 }
 
 static int copy_ctl_elem_id(const struct emu10k1_ctl_elem_id *list, int i,
diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c
index 31cc2d91c8d2..d5842d8a8509 100644
--- a/sound/pci/rme9652/hdsp.c
+++ b/sound/pci/rme9652/hdsp.c
@@ -4541,7 +4541,7 @@ static int snd_hdsp_capture_release(struct snd_pcm_substream *substream)
 static inline int copy_u32_le(void __user *dest, void __iomem *src)
 {
 	u32 val = readl(src);
-	return copy_to_user(dest, &val, 4);
+	return copy_to_user_partial(dest, &val, 4);
 }
 
 static inline int copy_u64_le(void __user *dest, void __iomem *src_low, void __iomem *src_high)
@@ -4551,7 +4551,7 @@ static inline int copy_u64_le(void __user *dest, void __iomem *src_low, void __i
 	rms_low = readl(src_low);
 	rms_high = readl(src_high);
 	rms = ((u64)rms_high << 32) | rms_low;
-	return copy_to_user(dest, &rms, 8);
+	return copy_to_user_partial(dest, &rms, 8);
 }
 
 static inline int copy_u48_le(void __user *dest, void __iomem *src_low, void __iomem *src_high)
@@ -4561,7 +4561,7 @@ static inline int copy_u48_le(void __user *dest, void __iomem *src_low, void __i
 	rms_low = readl(src_low) & 0xffffff00;
 	rms_high = readl(src_high) & 0xffffff00;
 	rms = ((u64)rms_high << 32) | rms_low;
-	return copy_to_user(dest, &rms, 8);
+	return copy_to_user_partial(dest, &rms, 8);
 }
 
 static int hdsp_9652_get_peak(struct hdsp *hdsp, struct hdsp_peak_rms __user *peak_rms)
diff --git a/sound/soc/intel/avs/probes.c b/sound/soc/intel/avs/probes.c
index 099119ad28b3..bc2871d3e18c 100644
--- a/sound/soc/intel/avs/probes.c
+++ b/sound/soc/intel/avs/probes.c
@@ -244,10 +244,10 @@ static int avs_probe_compr_copy(struct snd_soc_component *comp, struct snd_compr
 	n = rtd->buffer_size - offset;
 
 	if (count < n) {
-		ret = copy_to_user(buf, ptr, count);
+		ret = copy_to_user_partial(buf, ptr, count);
 	} else {
-		ret = copy_to_user(buf, ptr, n);
-		ret += copy_to_user(buf + n, rtd->dma_area, count - n);
+		ret = copy_to_user_partial(buf, ptr, n);
+		ret += copy_to_user_partial(buf + n, rtd->dma_area, count - n);
 	}
 
 	if (ret)
diff --git a/sound/soc/sof/compress.c b/sound/soc/sof/compress.c
index 93f2376585db..d54be8a188ec 100644
--- a/sound/soc/sof/compress.c
+++ b/sound/soc/sof/compress.c
@@ -324,10 +324,10 @@ static int sof_compr_copy_playback(struct snd_compr_runtime *rtd,
 	n = rtd->buffer_size - offset;
 
 	if (count < n) {
-		ret = copy_from_user(ptr, buf, count);
+		ret = copy_from_user_partial(ptr, buf, count);
 	} else {
-		ret = copy_from_user(ptr, buf, n);
-		ret += copy_from_user(rtd->dma_area, buf + n, count - n);
+		ret = copy_from_user_partial(ptr, buf, n);
+		ret += copy_from_user_partial(rtd->dma_area, buf + n, count - n);
 	}
 
 	return count - ret;
@@ -345,10 +345,10 @@ static int sof_compr_copy_capture(struct snd_compr_runtime *rtd,
 	n = rtd->buffer_size - offset;
 
 	if (count < n) {
-		ret = copy_to_user(buf, ptr, count);
+		ret = copy_to_user_partial(buf, ptr, count);
 	} else {
-		ret = copy_to_user(buf, ptr, n);
-		ret += copy_to_user(buf + n, rtd->dma_area, count - n);
+		ret = copy_to_user_partial(buf, ptr, n);
+		ret += copy_to_user_partial(buf + n, rtd->dma_area, count - n);
 	}
 
 	return count - ret;
diff --git a/sound/soc/sof/sof-client-probes.c b/sound/soc/sof/sof-client-probes.c
index 124f55508159..4c5f4f016ff8 100644
--- a/sound/soc/sof/sof-client-probes.c
+++ b/sound/soc/sof/sof-client-probes.c
@@ -184,10 +184,10 @@ static int sof_probes_compr_copy(struct snd_soc_component *component,
 	n = rtd->buffer_size - offset;
 
 	if (count < n) {
-		ret = copy_to_user(buf, ptr, count);
+		ret = copy_to_user_partial(buf, ptr, count);
 	} else {
-		ret = copy_to_user(buf, ptr, n);
-		ret += copy_to_user(buf + n, rtd->dma_area, count - n);
+		ret = copy_to_user_partial(buf, ptr, n);
+		ret += copy_to_user_partial(buf + n, rtd->dma_area, count - n);
 	}
 
 	if (ret)
-- 
2.49.0



^ permalink raw reply related

* [RFC PATCH v1 6/9] uaccess: Change copy_{to/from}_user to return -EFAULT
From: Christophe Leroy (CS GROUP) @ 2026-04-27 17:13 UTC (permalink / raw)
  To: Yury Norov, Andrew Morton, Linus Torvalds, David Laight,
	Thomas Gleixner
  Cc: Christophe Leroy (CS GROUP), linux-alpha, linux-kernel,
	linux-snps-arc, linux-arm-kernel, linux-mips, linuxppc-dev, kvm,
	linux-riscv, linux-s390, sparclinux, linux-um, dmaengine,
	linux-efi, linux-fsi, amd-gfx, dri-devel, intel-gfx, linux-wpan,
	netdev, linux-wireless, linux-spi, linux-media, linux-staging,
	linux-serial, linux-usb, xen-devel, linux-fsdevel, ocfs2-devel,
	bpf, kasan-dev, linux-mm, linux-x25, rust-for-linux, linux-sound,
	sound-open-firmware, linux-csky, linux-hexagon, loongarch,
	linux-m68k, linux-openrisc, linux-parisc, linux-sh, linux-arch
In-Reply-To: <cover.1777306795.git.chleroy@kernel.org>

Now that copy_{to/from}_user_partial() are used by callers which expect
partial copy with number of not copied bytes as return value, change
copy_{to/from}_user() to return an int, and return -EFAULT when the
copy is not complete.

Signed-off-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
---
 include/linux/uaccess.h | 28 ++++++++++++++++++++++++----
 1 file changed, 24 insertions(+), 4 deletions(-)

diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h
index 2d37173782b3..33b7d0f5f808 100644
--- a/include/linux/uaccess.h
+++ b/include/linux/uaccess.h
@@ -211,7 +211,7 @@ extern __must_check unsigned long
 _copy_to_user(void __user *, const void *, unsigned long);
 
 static __always_inline unsigned long __must_check
-copy_from_user(void *to, const void __user *from, unsigned long n)
+copy_from_user_common(void *to, const void __user *from, unsigned long n, bool partial)
 {
 	if (!check_copy_size(to, n, false))
 		return n;
@@ -221,10 +221,20 @@ copy_from_user(void *to, const void __user *from, unsigned long n)
 		return _inline_copy_from_user(to, from, n);
 }
 
-#define copy_from_user_partial copy_from_user
+static __always_inline unsigned long __must_check
+copy_from_user_partial(void *to, const void __user *from, unsigned long n)
+{
+	return copy_from_user_common(to, from, n, true);
+}
+
+static __always_inline int __must_check
+copy_from_user(void *to, const void __user *from, unsigned long n)
+{
+	return copy_from_user_common(to, from, n, false) ? -EFAULT : 0;
+}
 
 static __always_inline unsigned long __must_check
-copy_to_user(void __user *to, const void *from, unsigned long n)
+copy_to_user_common(void __user *to, const void *from, unsigned long n, bool partial)
 {
 	if (!check_copy_size(from, n, true))
 		return n;
@@ -235,7 +245,17 @@ copy_to_user(void __user *to, const void *from, unsigned long n)
 		return _inline_copy_to_user(to, from, n);
 }
 
-#define copy_to_user_partial copy_to_user
+static __always_inline unsigned long __must_check
+copy_to_user_partial(void __user *to, const void *from, unsigned long n)
+{
+	return copy_to_user_common(to, from, n, true);
+}
+
+static __always_inline int __must_check
+copy_to_user(void __user *to, const void *from, unsigned long n)
+{
+	return copy_to_user_common(to, from, n, false) ? -EFAULT : 0;
+}
 
 #ifndef copy_mc_to_kernel
 /*
-- 
2.49.0



^ permalink raw reply related

* [RFC PATCH v1 7/9] x86: Add unsafe_copy_from_user()
From: Christophe Leroy (CS GROUP) @ 2026-04-27 17:13 UTC (permalink / raw)
  To: Yury Norov, Andrew Morton, Linus Torvalds, David Laight,
	Thomas Gleixner
  Cc: Christophe Leroy (CS GROUP), linux-alpha, linux-kernel,
	linux-snps-arc, linux-arm-kernel, linux-mips, linuxppc-dev, kvm,
	linux-riscv, linux-s390, sparclinux, linux-um, dmaengine,
	linux-efi, linux-fsi, amd-gfx, dri-devel, intel-gfx, linux-wpan,
	netdev, linux-wireless, linux-spi, linux-media, linux-staging,
	linux-serial, linux-usb, xen-devel, linux-fsdevel, ocfs2-devel,
	bpf, kasan-dev, linux-mm, linux-x25, rust-for-linux, linux-sound,
	sound-open-firmware, linux-csky, linux-hexagon, loongarch,
	linux-m68k, linux-openrisc, linux-parisc, linux-sh, linux-arch
In-Reply-To: <cover.1777306795.git.chleroy@kernel.org>

At the time being, x86 and arm64 are missing unsafe_copy_from_user().

Add it.

Signed-off-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
---
 arch/x86/include/asm/uaccess.h | 29 ++++++++++++++++++++++++-----
 1 file changed, 24 insertions(+), 5 deletions(-)

diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h
index 3a0dd3c2b233..10c458ffa399 100644
--- a/arch/x86/include/asm/uaccess.h
+++ b/arch/x86/include/asm/uaccess.h
@@ -598,7 +598,7 @@ _label:									\
  * We want the unsafe accessors to always be inlined and use
  * the error labels - thus the macro games.
  */
-#define unsafe_copy_loop(dst, src, len, type, label)				\
+#define unsafe_put_loop(dst, src, len, type, label)				\
 	while (len >= sizeof(type)) {						\
 		unsafe_put_user(*(type *)(src),(type __user *)(dst),label);	\
 		dst += sizeof(type);						\
@@ -611,10 +611,29 @@ do {									\
 	char __user *__ucu_dst = (_dst);				\
 	const char *__ucu_src = (_src);					\
 	size_t __ucu_len = (_len);					\
-	unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u64, label);	\
-	unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u32, label);	\
-	unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u16, label);	\
-	unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u8, label);	\
+	unsafe_put_loop(__ucu_dst, __ucu_src, __ucu_len, u64, label);	\
+	unsafe_put_loop(__ucu_dst, __ucu_src, __ucu_len, u32, label);	\
+	unsafe_put_loop(__ucu_dst, __ucu_src, __ucu_len, u16, label);	\
+	unsafe_put_loop(__ucu_dst, __ucu_src, __ucu_len, u8, label);	\
+} while (0)
+
+#define unsafe_get_loop(dst, src, len, type, label)				\
+	while (len >= sizeof(type)) {						\
+		unsafe_get_user(*(type __user *)(src),(type *)(dst),label);	\
+		dst += sizeof(type);						\
+		src += sizeof(type);						\
+		len -= sizeof(type);						\
+	}
+
+#define unsafe_copy_from_user(_dst,_src,_len,label)			\
+do {									\
+	char *__ucu_dst = (_dst);					\
+	const char __user *__ucu_src = (_src);				\
+	size_t __ucu_len = (_len);					\
+	unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u64, label);	\
+	unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u32, label);	\
+	unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u16, label);	\
+	unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u8, label);	\
 } while (0)
 
 #ifdef CONFIG_CC_HAS_ASM_GOTO_OUTPUT
-- 
2.49.0



^ permalink raw reply related

* [RFC PATCH v1 8/9] arm64: Add unsafe_copy_from_user()
From: Christophe Leroy (CS GROUP) @ 2026-04-27 17:13 UTC (permalink / raw)
  To: Yury Norov, Andrew Morton, Linus Torvalds, David Laight,
	Thomas Gleixner
  Cc: Christophe Leroy (CS GROUP), linux-alpha, linux-kernel,
	linux-snps-arc, linux-arm-kernel, linux-mips, linuxppc-dev, kvm,
	linux-riscv, linux-s390, sparclinux, linux-um, dmaengine,
	linux-efi, linux-fsi, amd-gfx, dri-devel, intel-gfx, linux-wpan,
	netdev, linux-wireless, linux-spi, linux-media, linux-staging,
	linux-serial, linux-usb, xen-devel, linux-fsdevel, ocfs2-devel,
	bpf, kasan-dev, linux-mm, linux-x25, rust-for-linux, linux-sound,
	sound-open-firmware, linux-csky, linux-hexagon, loongarch,
	linux-m68k, linux-openrisc, linux-parisc, linux-sh, linux-arch
In-Reply-To: <cover.1777306795.git.chleroy@kernel.org>

At the time being, x86 and arm64 are missing unsafe_copy_from_user().

Add it.

Signed-off-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
---
 arch/arm64/include/asm/uaccess.h | 29 ++++++++++++++++++++++++-----
 1 file changed, 24 insertions(+), 5 deletions(-)

diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h
index 1e20ec91b56f..adfdb52cd82b 100644
--- a/arch/arm64/include/asm/uaccess.h
+++ b/arch/arm64/include/asm/uaccess.h
@@ -437,7 +437,7 @@ static inline void user_access_restore(unsigned long enabled) { }
  * We want the unsafe accessors to always be inlined and use
  * the error labels - thus the macro games.
  */
-#define unsafe_copy_loop(dst, src, len, type, label)				\
+#define unsafe_put_loop(dst, src, len, type, label)				\
 	while (len >= sizeof(type)) {						\
 		unsafe_put_user(*(type *)(src),(type __user *)(dst),label);	\
 		dst += sizeof(type);						\
@@ -450,10 +450,29 @@ do {									\
 	char __user *__ucu_dst = (_dst);				\
 	const char *__ucu_src = (_src);					\
 	size_t __ucu_len = (_len);					\
-	unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u64, label);	\
-	unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u32, label);	\
-	unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u16, label);	\
-	unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u8, label);	\
+	unsafe_put_loop(__ucu_dst, __ucu_src, __ucu_len, u64, label);	\
+	unsafe_put_loop(__ucu_dst, __ucu_src, __ucu_len, u32, label);	\
+	unsafe_put_loop(__ucu_dst, __ucu_src, __ucu_len, u16, label);	\
+	unsafe_put_loop(__ucu_dst, __ucu_src, __ucu_len, u8, label);	\
+} while (0)
+
+#define unsafe_get_loop(dst, src, len, type, label)				\
+	while (len >= sizeof(type)) {						\
+		unsafe_get_user(*(type __user *)(src),(type *)(dst),label);	\
+		dst += sizeof(type);						\
+		src += sizeof(type);						\
+		len -= sizeof(type);						\
+	}
+
+#define unsafe_copy_from_user(_dst,_src,_len,label)			\
+do {									\
+	char *__ucu_dst = (_dst);					\
+	const char __user *__ucu_src = (_src);				\
+	size_t __ucu_len = (_len);					\
+	unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u64, label);	\
+	unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u32, label);	\
+	unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u16, label);	\
+	unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u8, label);	\
 } while (0)
 
 extern unsigned long __must_check __arch_clear_user(void __user *to, unsigned long n);
-- 
2.49.0



^ permalink raw reply related

* Re: [PATCH v2] iio: adc: mt6359: fix unchecked return value in mt6358_read_imp
From: Jonathan Cameron @ 2026-04-27 17:18 UTC (permalink / raw)
  To: Salah Triki
  Cc: David Lechner, Nuno Sá, Andy Shevchenko, Matthias Brugger,
	AngeloGioacchino Del Regno, linux-iio, linux-kernel,
	linux-arm-kernel, linux-mediatek
In-Reply-To: <20260427165759.5756-1-salah.triki@gmail.com>

On Mon, 27 Apr 2026 17:57:59 +0100
Salah Triki <salah.triki@gmail.com> wrote:

> In mt6358_read_imp(), the return value of regmap_read() is currently
> ignored. This is problematic because if the bus read fails the variable
> val_v remains uninitialized.
> 
> The function subsequently assigns this uninitialized stack value to
> *vbat, leading to incorrect measurement results being reported to
> the IIO subsystem without any error indication.
> 
> Update the function to check the return value of regmap_read(). Ensure
> that mt6358_stop_imp_conv() is still called to clean up the hardware
> state before returning the error code.
> 
> Fixes: 3587914bf61 ("iio: adc: Add support for MediaTek MT6357/8/9 Auxiliary ADC")
> Signed-off-by: Salah Triki <salah.triki@gmail.com>
> ---
> Changes in v2:
> - Added Fixes tag.
> - Re-examined the entire driver for unchecked regmap operations.
>   While several regmap_write() and regmap_set_bits() calls also ignore
>   return values, I focused on this specific regmap_read() in 
>   mt6358_read_imp() because it leads to an uninitialized variable usage
>   (val_v). This makes this fix critical for reporting correct data 
>   to userspace.

I'd be more concerned about what it might otherwise report.. Will expose
something random that was on the stack previously.

Ok on this one having a fixes tag given the uninitialized, though as I mentioned
I'd have been tempted to take the approach of just initialising it.

Even with the code as you have it here a static analyzer may not be able
to see far enough to tell it is initialized and so throw false positive warnings.

Still, let's see what Andy thinks before you change anything.

J
> 
>  drivers/iio/adc/mt6359-auxadc.c | 5 ++++-
>  1 file changed, 4 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/iio/adc/mt6359-auxadc.c b/drivers/iio/adc/mt6359-auxadc.c
> index 6b9ed9b1fde2..f927bff4a26a 100644
> --- a/drivers/iio/adc/mt6359-auxadc.c
> +++ b/drivers/iio/adc/mt6359-auxadc.c
> @@ -497,10 +497,13 @@ static int mt6358_read_imp(struct mt6359_auxadc *adc_dev,
>  		return ret;
>  
>  	/* Read the params before stopping */
> -	regmap_read(regmap, reg_adc0 + (cinfo->imp_adc_num << 1), &val_v);
> +	ret = regmap_read(regmap, reg_adc0 + (cinfo->imp_adc_num << 1), &val_v);
>  
>  	mt6358_stop_imp_conv(adc_dev);
>  
> +	if (ret)
> +		return ret;
> +
>  	if (vbat)
>  		*vbat = val_v;
>  	if (ibat)



^ permalink raw reply

* [RFC PATCH v1 9/9] uaccess: Convert small fixed size copy_{to/from}_user() to scoped user access
From: Christophe Leroy (CS GROUP) @ 2026-04-27 17:13 UTC (permalink / raw)
  To: Yury Norov, Andrew Morton, Linus Torvalds, David Laight,
	Thomas Gleixner
  Cc: Christophe Leroy (CS GROUP), linux-alpha, linux-kernel,
	linux-snps-arc, linux-arm-kernel, linux-mips, linuxppc-dev, kvm,
	linux-riscv, linux-s390, sparclinux, linux-um, dmaengine,
	linux-efi, linux-fsi, amd-gfx, dri-devel, intel-gfx, linux-wpan,
	netdev, linux-wireless, linux-spi, linux-media, linux-staging,
	linux-serial, linux-usb, xen-devel, linux-fsdevel, ocfs2-devel,
	bpf, kasan-dev, linux-mm, linux-x25, rust-for-linux, linux-sound,
	sound-open-firmware, linux-csky, linux-hexagon, loongarch,
	linux-m68k, linux-openrisc, linux-parisc, linux-sh, linux-arch
In-Reply-To: <cover.1777306795.git.chleroy@kernel.org>

copy_{to/from}_user() is a heavy function optimised for copy of large
blocs of memory between user and kernel space.

When the number of bytes to be copied is known at build time and small,
using scoped user access removes the burden of that optimisation.

Signed-off-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
---
 include/linux/uaccess.h | 47 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 47 insertions(+)

diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h
index 33b7d0f5f808..3ac544527af2 100644
--- a/include/linux/uaccess.h
+++ b/include/linux/uaccess.h
@@ -50,6 +50,8 @@
  #define mask_user_address(src) (src)
 #endif
 
+#define SMALL_COPY_USER		64
+
 /*
  * Architectures should provide two primitives (raw_copy_{to,from}_user())
  * and get rid of their private instances of copy_{to,from}_user() and
@@ -191,6 +193,9 @@ _inline_copy_from_user(void *to, const void __user *from, unsigned long n)
 	return res;
 }
 
+static __always_inline __must_check unsigned long
+_small_copy_from_user(void *to, const void __user *from, unsigned long n);
+
 extern __must_check unsigned long
 _copy_from_user(void *, const void __user *, unsigned long);
 
@@ -207,6 +212,9 @@ _inline_copy_to_user(void __user *to, const void *from, unsigned long n)
 	return n;
 }
 
+static __always_inline __must_check unsigned long
+_small_copy_to_user(void __user *to, const void *from, unsigned long n);
+
 extern __must_check unsigned long
 _copy_to_user(void __user *, const void *, unsigned long);
 
@@ -215,6 +223,8 @@ copy_from_user_common(void *to, const void __user *from, unsigned long n, bool p
 {
 	if (!check_copy_size(to, n, false))
 		return n;
+	if (!partial && __builtin_constant_p(n) && n <= SMALL_COPY_USER)
+		return _small_copy_from_user(to, from, n);
 	if (IS_ENABLED(ARCH_WANTS_NOINLINE_COPY_USER))
 		return _copy_from_user(to, from, n);
 	else
@@ -239,6 +249,8 @@ copy_to_user_common(void __user *to, const void *from, unsigned long n, bool par
 	if (!check_copy_size(from, n, true))
 		return n;
 
+	if (!partial && __builtin_constant_p(n) && n <= SMALL_COPY_USER)
+		return _small_copy_to_user(to, from, n);
 	if (IS_ENABLED(ARCH_WANTS_NOINLINE_COPY_USER))
 		return _copy_to_user(to, from, n);
 	else
@@ -838,6 +850,41 @@ for (bool done = false; !done; done = true)					\
 #define scoped_user_rw_access(uptr, elbl)				\
 	scoped_user_rw_access_size(uptr, sizeof(*(uptr)), elbl)
 
+static __always_inline __must_check unsigned long
+_small_copy_from_user(void *to, const void __user *from, unsigned long n)
+{
+	might_fault();
+	instrument_copy_from_user_before(to, from, n);
+	scoped_user_read_access_size(from, n, failed) {
+		/*
+		 * Ensure that bad access_ok() speculation will not lead
+		 * to nasty side effects *after* the copy is finished:
+		 */
+		if (!can_do_masked_user_access())
+			barrier_nospec();
+		unsafe_copy_from_user(to, from, n, failed);
+	}
+	instrument_copy_from_user_after(to, from, n, 0);
+	return 0;
+failed:
+	instrument_copy_from_user_after(to, from, n, n);
+	return n;
+}
+
+static __always_inline __must_check unsigned long
+_small_copy_to_user(void __user *to, const void *from, unsigned long n)
+{
+	might_fault();
+	if (should_fail_usercopy())
+		return n;
+	instrument_copy_to_user(to, from, n);
+	scoped_user_write_access_size(to, n, failed)
+		unsafe_copy_to_user(to, from, n, failed);
+	return 0;
+failed:
+	return n;
+}
+
 /**
  * get_user_inline - Read user data inlined
  * @val:	The variable to store the value read from user memory
-- 
2.49.0



^ permalink raw reply related

* [PATCH v21 00/13] Implement PSCI reboot mode driver for PSCI resets
From: Shivendra Pratap @ 2026-04-27 17:34 UTC (permalink / raw)
  To: Sebastian Reichel, Mark Rutland, Lorenzo Pieralisi,
	Rafael J. Wysocki, Daniel Lezcano, Christian Loehle, Ulf Hansson,
	Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Bjorn Andersson, Konrad Dybcio, Arnd Bergmann, Souvik Chakravarty,
	Andy Yan, Matthias Brugger, John Stultz, Moritz Fischer,
	Bartosz Golaszewski, Sudeep Holla, Ulf Hansson
  Cc: linux-pm, linux-kernel, linux-arm-msm, linux-arm-kernel,
	devicetree, Shivendra Pratap, Florian Fainelli,
	Krzysztof Kozlowski, Dmitry Baryshkov, Mukesh Ojha, Andre Draszik,
	Greg Kroah-Hartman, Kathiravan Thirumoorthy, Srinivas Kandagatla,
	Sebastian Reichel, Bartosz Golaszewski, Song Xue

Userspace should be able to initiate device reboots using the various
PSCI SYSTEM_RESET and SYSTEM_RESET2 types defined by PSCI spec. This
patch series introduces psci-reboot-mode driver that will induce
command-based resets to psci driver for executing the device reset.

The PSCI system reset calls takes two arguments: reset_type and cookie.
It defines predefined reset types, such as warm and cold reset, and
vendor-specific reset types which are SoC vendor specific. To support
these requirements, the reboot-mode framework is enhanced in two key
ways:
1. 64-bit magic support: Extend reboot-mode to handle two 32-bit
arguments (reset_type and cookie) by encoding them into a single 64-bit
magic value.
2. Predefined modes: Add support for predefined reboot modes in the
framework.

With these enhancements, the patch series enables:
 - Arch Warm reset and system reset cold as predefined reboot modes.
 - Vendor-specific resets, configurable via the SoC-specific device tree.

Together, these changes allow userspace to trigger all above PSCI resets
from userspace.

Note on introducing PSCI-MFD:
In v19/20, psci-reboot-mode was implemented as a faux-device. Review
discussion suggested this may not be the best model for firmware-backed
consumers, and that representing PSCI users as regular platform devices
would be better. One suggestion was to add a PSCI-MFD driver, allowing
multiple consumers tied to a single PSCI node "arm,psci-1.0" be probed
as MFD cells.

Following this, the series adds a PSCI-MFD driver and introduces
cpuidle-psci-domain and psci-reboot-mode as child cells. To meet the
psci-reboot-mode requirement, the MFD core is extended to support
fwnode.
Reference discussions on this:
https://lore.kernel.org/all/20260306-faux-dev-with-fwnode-v1-1-792a86a62530@oss.qualcomm.com/

---
- Carrying Reviewed-by form Sebastian and Bart on Patch1.
- Carrying Reviewed-by form Sebastian on Patch2.
- Dropping Reviewed-by form Sebastian on Patch3 due to additional changes.

The patch is tested on rb3Gen2, lemans-ride, lemans-evk, monaco-ride,
qcs615-ride.

Signed-off-by: Shivendra Pratap <shivendra.pratap@oss.qualcomm.com>

Changes in v21:
By Krzysztof
 power: reset: reboot-mode: Remove devres based allocations
  - remove fixes tag.
By Lorenzo
  firmware: psci: Introduce command-based resets
  - psci_set_reset_cmd() only takes reset_type/cookie as input params.
    - updated this as a encoded u64 reset_command - from Pavan Kondeti.
  - Update commit text for reason for explicit panic_handling.
  - Clean split of reset flow in psci_sys_reset via handle_reboot_mode and handle_cmd_reset.
  - Add psci_has_system_reset2_support() helper and PSCI reset-type defines.
By Lorenzo/Bart
 power: reset: Add psci-reboot-mode driver
  By Bart
   - Drop faux-device based probe.
   - Convert driver to platform-driver model (probed via PSCI MFD).
  By Lorenzo:
   - Use PSCI-specific predefined reset mode naming/magic.
   - Register arch-warm predefined mode only when SYSTEM_RESET2 is supported.
   - psci_reboot_mode_write to directly pass reset_type and cokie.
   - Add MAINTAINERS entry for drivers/power/reset/psci-reboot-mode.c.
By Bart
 mfd: psci-mfd: Introduce psci mfd driver for cpuidle-psci-domain cell
  - Introduce psci-mfd driver.
 mfd: Add psci-reboot-mode cell via fwnode
 - Register psci-reboot-mode from psci-mfd with reboot-mode child node
   fwnode.
 - Update reset Kconfig dependency to tie PSCI reboot-mode to MFD PSCI path.
For alignment:
 - mfd: core: Add firmware-node support to MFD cells
   - Add firmware-node support to MFD cells.
By Pavan
 power: reset: reboot-mode: Add support for predefined reboot modes
  - Move redundant logic of adding modes to list to a common function.
By Arnd
 - Remove refrences for Linux reboot-modes from code and commit text.
By Konrad
 - Updated all dt changes to add reboot-mode for supported board files.
- Link to v20: https://lore.kernel.org/r/20260304-arm-psci-system_reset2-vendor-reboots-v20-0-cf7d346b8372@oss.qualcomm.com

Changes in v20:
By Bart:
 power: reset: Add psci-reboot-mode driver
 - Check for psci compatible in init arm,psci-1.0
 - Node pointer should not be assigned in device probe.
   To Align on above:
   - Remove probe call for faux device.
   - Set node using device_set_node after faux_device_create.
   - Register the reboot mode using explicit call to psci_reboot_mode_register_device.
 - Updated in-code documentation.
For Alignment to use of device_property_xx:
 power: reset: reboot-mode: Add support for 64 bit magic
  - Use device_property_count_u32 instead of device_property_read_u32.
  - Check count of properties before reading.
  - u64 magic changed to u32 magic[2].
  - nvmem-reboot rebased on recent changes.
  - Update documentation and commit text.
power: reset: reboot-mode: Remove devres based allocations
 By Dmitry/Bart:
  - pr_err to pr_debug in case of invalid reboot-mode prop.
 By Bart:
  - Use device_property_read_u32 instead of of_property_read_u32.
  - Avoid repeated code for free list. Now calling unregister from
    error path of regiister_reboot_mode.
 - Fix magic assignment.
- Link to v19: https://lore.kernel.org/r/20251228-arm-psci-system_reset2-vendor-reboots-v19-0-ebb956053098@oss.qualcomm.com

Changes in v19:
- Add missing To/Cc entries (include devicetree list) – Thanks to
  Krzysztof for pointing this out.
- Fix compilation error in reboot-mode.c for ARCH=powerpc by explicitly
  including <linux/slab.h>.
- Link to v18: https://lore.kernel.org/r/20251223-arm-psci-system_reset2-vendor-reboots-v18-0-32fa9e76efc3@oss.qualcomm.com

Changes in v18:
 power: reset: reboot-mode: Remove devres based allocations
 - Update commit text for alignment. – Bart
 - Read magic before assigning kzalloc(info) in reboot_mode_register. - Mukesh
 - Update error handling path. - Mukesh
By Bjorn
 - Expose sysfs for reboot-mode bisected to different series-
   Link: https://lore.kernel.org/all/20251222-next-15nov_expose_sysfs-v21-0-244614135fd8@oss.qualcomm.com/
By Bjorn/Lorenzo/Mukesh
  power: reset: reboot-mode: Add support for 64 bit magic
  - Use FIELD_GET/FIELD_PREP for u64 magic wherever required.
  - Update commit text and add documentation for structure of 64 bit magic.
By Lorenzo
 - Remove direct reboot-mode registration by psci driver.
 - Add support for predefined reboot modes in reboot-mode framework.
 - Add psci-reboot-mode driver and implement a psci-resets to accommodate
   all psci-resets including warm, cold and customizable vendor-resets.
By Bjorn
 - Update DT patches for qcm6490, lemans, monaco and tolos.
  - Update commit text to include more details – By Bjorn
For Alignment
 - dt-bindings: arm: Document reboot mode magic
   - Update reboot mode documentation to clarify that argument1 should provide
     full value of reset_type along with the 31st bit wherever required.
 - DT patches for qcm6490, lemans, monaco and tolos.
   - Provide full value of reset_type including 31st bit.(eg:0x80000001).
- Link to v17: https://lore.kernel.org/r/20251109-arm-psci-system_reset2-vendor-reboots-v17-0-46e085bca4cc@oss.qualcomm.com

Previous versions:
- Link to v16: https://lore.kernel.org/r/20251015-arm-psci-system_reset2-vendor-reboots-v16-0-b98aedaa23ee@oss.qualcomm.com
- Link to v15: https://lore.kernel.org/r/20250922-arm-psci-system_reset2-vendor-reboots-v15-0-7ce3a08878f1@oss.qualcomm.com
- Link to v14: https://lore.kernel.org/r/20250815-arm-psci-system_reset2-vendor-reboots-v14-0-37d29f59ac9a@oss.qualcomm.com
- Link to v13: https://lore.kernel.org/r/20250727-arm-psci-system_reset2-vendor-reboots-v13-0-6b8d23315898@oss.qualcomm.com
- Link to v12: https://lore.kernel.org/r/20250721-arm-psci-system_reset2-vendor-reboots-v12-0-87bac3ec422e@oss.qualcomm.com
- Link to v11: https://lore.kernel.org/r/20250717-arm-psci-system_reset2-vendor-reboots-v11-0-df3e2b2183c3@oss.qualcomm.com
- Link to v10: https://lore.kernel.org/all/569f154d-c714-1714-b898-83a42a38771c@oss.qualcomm.com/
- Link to v9: https://lore.kernel.org/all/20250303-arm-psci-system_reset2-vendor-reboots-v9-0-b2cf4a20feda@oss.qualcomm.com/
- Link to v8: https://lore.kernel.org/r/20241107-arm-psci-system_reset2-vendor-reboots-v8-0-e8715fa65cb5@quicinc.com
- Link to v7: https://lore.kernel.org/r/20241028-arm-psci-system_reset2-vendor-reboots-v7-0-a4c40b0ebc54@quicinc.com
- Link to v6: https://lore.kernel.org/r/20241018-arm-psci-system_reset2-vendor-reboots-v6-0-50cbe88b0a24@quicinc.com
- Link to v5: https://lore.kernel.org/r/20240617-arm-psci-system_reset2-vendor-reboots-v5-0-086950f650c8@quicinc.com
- Link to v4: https://lore.kernel.org/r/20240611-arm-psci-system_reset2-vendor-reboots-v4-0-98f55aa74ae8@quicinc.com
- Link to v3: https://lore.kernel.org/r/20240515-arm-psci-system_reset2-vendor-reboots-v3-0-16dd4f9c0ab4@quicinc.com
- Link to v2: https://lore.kernel.org/r/20240414-arm-psci-system_reset2-vendor-reboots-v2-0-da9a055a648f@quicinc.com
- Link to v1: https://lore.kernel.org/r/20231117-arm-psci-system_reset2-vendor-reboots-v1-0-03c4612153e2@quicinc.com
- Link to RFC: https://lore.kernel.org/r/20231030-arm-psci-system_reset2-vendor-reboots-v1-0-dcdd63352ad1@quicinc.com

To: Lorenzo Pieralisi <lpieralisi@kernel.org>
To: Arnd Bergmann <arnd@arndb.de>
To: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
To: Bjorn Andersson <andersson@kernel.org>
To: Sebastian Reichel <sre@kernel.org>
To: Rob Herring <robh@kernel.org>
To: Sudeep Holla <sudeep.holla@arm.com>
To: Souvik Chakravarty <Souvik.Chakravarty@arm.com>
To: Krzysztof Kozlowski <krzk+dt@kernel.org>
To: Andy Yan <andy.yan@rock-chips.com>
To: Matthias Brugger <matthias.bgg@gmail.com>
To: Mark Rutland <mark.rutland@arm.com>
To: Conor Dooley <conor+dt@kernel.org>
To: Konrad Dybcio <konradybcio@kernel.org>
To: John Stultz <john.stultz@linaro.org>
To: Moritz Fischer <moritz.fischer@ettus.com>
To: Rafael J. Wysocki <rafael@kernel.org>
To: Daniel Lezcano <daniel.lezcano@kernel.org>
To: Christian Loehle <christian.loehle@arm.com>
To: Ulf Hansson <ulf.hansson@linaro.org>
To: Lee Jones <lee@kernel.org>
To: Ulf Hansson <ulfh@kernel.org>
Cc: Florian Fainelli <florian.fainelli@broadcom.com>
Cc: Krzysztof Kozlowski <krzk@kernel.org>
Cc: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Cc: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Cc: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>
Cc: Andre Draszik <andre.draszik@linaro.org>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Kathiravan Thirumoorthy <kathiravan.thirumoorthy@oss.qualcomm.com>
Cc: linux-pm@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-arm-msm@vger.kernel.org
Cc: devicetree@vger.kernel.org

---
Shivendra Pratap (13):
      power: reset: reboot-mode: Remove devres based allocations
      power: reset: reboot-mode: Add support for 64 bit magic
      power: reset: reboot-mode: Add support for predefined reboot modes
      firmware: psci: Introduce command-based with magic
      mfd: psci-mfd: Add PSCI MFD driver for cpuidle-psci-domain cell
      dt-bindings: arm: Document reboot mode magic
      power: reset: Add psci-reboot-mode driver
      mfd: core: Add firmware-node support to MFD cells
      mfd: psci-mfd: Add psci-reboot-mode child cell
      arm64: dts: qcom: Add psci reboot-modes for kodiak boards
      arm64: dts: qcom: Add psci reboot-modes for lemans boards
      arm64: dts: qcom: Add psci reboot-modes for monaco boards
      arm64: dts: qcom: Add psci reboot-modes for talos boards

 Documentation/devicetree/bindings/arm/psci.yaml  |  42 +++++++++
 MAINTAINERS                                      |   2 +
 arch/arm64/boot/dts/qcom/kodiak.dtsi             |   2 +-
 arch/arm64/boot/dts/qcom/lemans-evk.dts          |   7 ++
 arch/arm64/boot/dts/qcom/lemans-ride-common.dtsi |   7 ++
 arch/arm64/boot/dts/qcom/lemans.dtsi             |   2 +-
 arch/arm64/boot/dts/qcom/monaco-evk.dts          |   7 ++
 arch/arm64/boot/dts/qcom/monaco.dtsi             |   2 +-
 arch/arm64/boot/dts/qcom/qcm6490-idp.dts         |   7 ++
 arch/arm64/boot/dts/qcom/qcs615-ride.dts         |   7 ++
 arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts     |   7 ++
 arch/arm64/boot/dts/qcom/qcs8300-ride.dts        |   7 ++
 arch/arm64/boot/dts/qcom/talos.dtsi              |   2 +-
 drivers/cpuidle/Kconfig.arm                      |   1 +
 drivers/cpuidle/cpuidle-psci-domain.c            |   9 +-
 drivers/firmware/psci/psci.c                     |  75 +++++++++++++++-
 drivers/mfd/Kconfig                              |  10 +++
 drivers/mfd/Makefile                             |   2 +
 drivers/mfd/mfd-core.c                           |  10 +++
 drivers/mfd/psci-mfd.c                           |  62 +++++++++++++
 drivers/power/reset/Kconfig                      |  10 +++
 drivers/power/reset/Makefile                     |   1 +
 drivers/power/reset/nvmem-reboot-mode.c          |   9 +-
 drivers/power/reset/psci-reboot-mode.c           | 109 +++++++++++++++++++++++
 drivers/power/reset/qcom-pon.c                   |   7 +-
 drivers/power/reset/reboot-mode.c                |  91 ++++++++++++-------
 drivers/power/reset/syscon-reboot-mode.c         |   7 +-
 include/linux/mfd/core.h                         |   6 ++
 include/linux/psci.h                             |  19 ++++
 include/linux/reboot-mode.h                      |  23 ++++-
 30 files changed, 496 insertions(+), 56 deletions(-)
---
base-commit: 39704f00f747aba3144289870b5fd8ac230a9aaf
change-id: 20250709-arm-psci-system_reset2-vendor-reboots-46c80044afcf

Best regards,
-- 
Shivendra Pratap <shivendra.pratap@oss.qualcomm.com>



^ permalink raw reply

* [PATCH v21 01/13] power: reset: reboot-mode: Remove devres based allocations
From: Shivendra Pratap @ 2026-04-27 17:34 UTC (permalink / raw)
  To: Sebastian Reichel, Mark Rutland, Lorenzo Pieralisi,
	Rafael J. Wysocki, Daniel Lezcano, Christian Loehle, Ulf Hansson,
	Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Bjorn Andersson, Konrad Dybcio, Arnd Bergmann, Souvik Chakravarty,
	Andy Yan, Matthias Brugger, John Stultz, Moritz Fischer,
	Bartosz Golaszewski, Sudeep Holla, Ulf Hansson
  Cc: linux-pm, linux-kernel, linux-arm-msm, linux-arm-kernel,
	devicetree, Shivendra Pratap, Florian Fainelli,
	Krzysztof Kozlowski, Dmitry Baryshkov, Mukesh Ojha, Andre Draszik,
	Greg Kroah-Hartman, Kathiravan Thirumoorthy, Srinivas Kandagatla,
	Sebastian Reichel, Bartosz Golaszewski
In-Reply-To: <20260427-arm-psci-system_reset2-vendor-reboots-v21-0-dcf937775e73@oss.qualcomm.com>

Devres APIs are intended for use in drivers, where the managed lifetime
of resources is tied directly to the driver attach/detach cycle. In
shared subsystem code, there is no guarantee that the subsystem
functions will only be called after a driver has been attached, nor that
they will not be referenced after the managed resources have been
released during driver detach.

To ensure correct lifetime handling, avoid using devres-based
allocations in the reboot-mode and explicitly handle allocation and
cleanup of resources.

Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: Shivendra Pratap <shivendra.pratap@oss.qualcomm.com>
---
 drivers/power/reset/reboot-mode.c | 32 ++++++++++++++++++++------------
 1 file changed, 20 insertions(+), 12 deletions(-)

diff --git a/drivers/power/reset/reboot-mode.c b/drivers/power/reset/reboot-mode.c
index d20e44db053256d87ad8034ddab233e49d36072d..7a74f72a8313b73c3e0930a426ebcf7ef6eed99b 100644
--- a/drivers/power/reset/reboot-mode.c
+++ b/drivers/power/reset/reboot-mode.c
@@ -3,6 +3,8 @@
  * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
  */
 
+#define pr_fmt(fmt)	"reboot-mode: " fmt
+
 #include <linux/device.h>
 #include <linux/err.h>
 #include <linux/init.h>
@@ -10,6 +12,7 @@
 #include <linux/list.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/property.h>
 #include <linux/reboot.h>
 #include <linux/reboot-mode.h>
 #include <linux/slab.h>
@@ -168,10 +171,11 @@ static int reboot_mode_create_device(struct reboot_mode_driver *reboot)
  */
 int reboot_mode_register(struct reboot_mode_driver *reboot)
 {
-	struct mode_info *info;
+	struct mode_info *info = NULL;
 	struct property *prop;
 	struct device_node *np = reboot->dev->of_node;
 	size_t len = strlen(PREFIX);
+	u32 magic;
 	int ret;
 
 	INIT_LIST_HEAD(&reboot->head);
@@ -180,19 +184,18 @@ int reboot_mode_register(struct reboot_mode_driver *reboot)
 		if (strncmp(prop->name, PREFIX, len))
 			continue;
 
-		info = devm_kzalloc(reboot->dev, sizeof(*info), GFP_KERNEL);
+		if (device_property_read_u32(reboot->dev, prop->name, &magic)) {
+			pr_debug("reboot mode %s without magic number\n", prop->name);
+			continue;
+		}
+
+		info = kzalloc(sizeof(*info), GFP_KERNEL);
 		if (!info) {
 			ret = -ENOMEM;
 			goto error;
 		}
 
-		if (of_property_read_u32(np, prop->name, &info->magic)) {
-			dev_err(reboot->dev, "reboot mode %s without magic number\n",
-				info->mode);
-			devm_kfree(reboot->dev, info);
-			continue;
-		}
-
+		info->magic = magic;
 		info->mode = kstrdup_const(prop->name + len, GFP_KERNEL);
 		if (!info->mode) {
 			ret =  -ENOMEM;
@@ -200,12 +203,12 @@ int reboot_mode_register(struct reboot_mode_driver *reboot)
 		} else if (info->mode[0] == '\0') {
 			kfree_const(info->mode);
 			ret = -EINVAL;
-			dev_err(reboot->dev, "invalid mode name(%s): too short!\n",
-				prop->name);
+			pr_err("invalid mode name(%s): too short!\n", prop->name);
 			goto error;
 		}
 
 		list_add_tail(&info->list, &reboot->head);
+		info = NULL;
 	}
 
 	reboot->reboot_notifier.notifier_call = reboot_mode_notify;
@@ -218,6 +221,7 @@ int reboot_mode_register(struct reboot_mode_driver *reboot)
 	return 0;
 
 error:
+	kfree(info);
 	reboot_mode_unregister(reboot);
 	return ret;
 }
@@ -261,12 +265,16 @@ static inline void reboot_mode_unregister_device(struct reboot_mode_driver *rebo
 int reboot_mode_unregister(struct reboot_mode_driver *reboot)
 {
 	struct mode_info *info;
+	struct mode_info *next;
 
 	unregister_reboot_notifier(&reboot->reboot_notifier);
 	reboot_mode_unregister_device(reboot);
 
-	list_for_each_entry(info, &reboot->head, list)
+	list_for_each_entry_safe(info, next, &reboot->head, list) {
+		list_del(&info->list);
 		kfree_const(info->mode);
+		kfree(info);
+	}
 
 	return 0;
 }

-- 
2.34.1



^ permalink raw reply related

* [PATCH v21 02/13] power: reset: reboot-mode: Add support for 64 bit magic
From: Shivendra Pratap @ 2026-04-27 17:34 UTC (permalink / raw)
  To: Sebastian Reichel, Mark Rutland, Lorenzo Pieralisi,
	Rafael J. Wysocki, Daniel Lezcano, Christian Loehle, Ulf Hansson,
	Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Bjorn Andersson, Konrad Dybcio, Arnd Bergmann, Souvik Chakravarty,
	Andy Yan, Matthias Brugger, John Stultz, Moritz Fischer,
	Bartosz Golaszewski, Sudeep Holla, Ulf Hansson
  Cc: linux-pm, linux-kernel, linux-arm-msm, linux-arm-kernel,
	devicetree, Shivendra Pratap, Florian Fainelli,
	Krzysztof Kozlowski, Dmitry Baryshkov, Mukesh Ojha, Andre Draszik,
	Greg Kroah-Hartman, Kathiravan Thirumoorthy, Srinivas Kandagatla,
	Sebastian Reichel
In-Reply-To: <20260427-arm-psci-system_reset2-vendor-reboots-v21-0-dcf937775e73@oss.qualcomm.com>

Current reboot-mode supports a single 32-bit argument for any
supported mode. Some reboot-mode based drivers may require
passing two independent 32-bit arguments during a reboot
sequence, for uses-cases, where a mode requires an additional
argument. Such drivers may not be able to use the reboot-mode
driver. For example, ARM PSCI vendor-specific resets, need two
arguments for its operation – reset_type and cookie, to complete
the reset operation. If a driver wants to implement this
firmware-based reset, it cannot use reboot-mode framework.

Introduce 64-bit magic values in reboot-mode driver to
accommodate up-to two 32-bit arguments in below format.
|    Higher 32 bit  |   Lower 32 bit    |
|	 arg2	    | 	    arg1	|

Update current reboot-mode drivers for 64-bit magic.

Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Signed-off-by: Shivendra Pratap <shivendra.pratap@oss.qualcomm.com>
---
 drivers/power/reset/nvmem-reboot-mode.c  |  8 ++++----
 drivers/power/reset/qcom-pon.c           |  6 +++---
 drivers/power/reset/reboot-mode.c        | 19 ++++++++++++-------
 drivers/power/reset/syscon-reboot-mode.c |  6 +++---
 include/linux/reboot-mode.h              | 15 ++++++++++++++-
 5 files changed, 36 insertions(+), 18 deletions(-)

diff --git a/drivers/power/reset/nvmem-reboot-mode.c b/drivers/power/reset/nvmem-reboot-mode.c
index d260715fccf67f9f072bb56c5defbf885750650e..bd05d660490c686b43134f82f1eadd7665403d20 100644
--- a/drivers/power/reset/nvmem-reboot-mode.c
+++ b/drivers/power/reset/nvmem-reboot-mode.c
@@ -17,10 +17,10 @@ struct nvmem_reboot_mode {
 	struct nvmem_cell *cell;
 };
 
-static int nvmem_reboot_mode_write(struct reboot_mode_driver *reboot,
-				    unsigned int magic)
+static int nvmem_reboot_mode_write(struct reboot_mode_driver *reboot, u64 magic)
 {
 	struct nvmem_reboot_mode *nvmem_rbm;
+	u32 magic_arg1 = REBOOT_MODE_ARG1(magic);
 	size_t buf_len;
 	void *buf;
 	int ret;
@@ -32,10 +32,10 @@ static int nvmem_reboot_mode_write(struct reboot_mode_driver *reboot,
 		return PTR_ERR(buf);
 	kfree(buf);
 
-	if (buf_len > sizeof(magic))
+	if (buf_len > sizeof(magic_arg1))
 		return -EINVAL;
 
-	ret = nvmem_cell_write(nvmem_rbm->cell, &magic, buf_len);
+	ret = nvmem_cell_write(nvmem_rbm->cell, &magic_arg1, buf_len);
 	if (ret < 0)
 		dev_err(reboot->dev, "update reboot mode bits failed\n");
 
diff --git a/drivers/power/reset/qcom-pon.c b/drivers/power/reset/qcom-pon.c
index 7e108982a582e8243c5c806bd4a793646b87189f..57b36e6186f80aff947fd7f5aae5ce280c65dc6b 100644
--- a/drivers/power/reset/qcom-pon.c
+++ b/drivers/power/reset/qcom-pon.c
@@ -27,17 +27,17 @@ struct qcom_pon {
 	long reason_shift;
 };
 
-static int qcom_pon_reboot_mode_write(struct reboot_mode_driver *reboot,
-				    unsigned int magic)
+static int qcom_pon_reboot_mode_write(struct reboot_mode_driver *reboot, u64 magic)
 {
 	struct qcom_pon *pon = container_of
 			(reboot, struct qcom_pon, reboot_mode);
+	u32 magic_arg1 = REBOOT_MODE_ARG1(magic);
 	int ret;
 
 	ret = regmap_update_bits(pon->regmap,
 				 pon->baseaddr + PON_SOFT_RB_SPARE,
 				 GENMASK(7, pon->reason_shift),
-				 magic << pon->reason_shift);
+				 magic_arg1 << pon->reason_shift);
 	if (ret < 0)
 		dev_err(pon->dev, "update reboot mode bits failed\n");
 
diff --git a/drivers/power/reset/reboot-mode.c b/drivers/power/reset/reboot-mode.c
index 7a74f72a8313b73c3e0930a426ebcf7ef6eed99b..f1372dc700e48043320812c3d3619ab1539e1854 100644
--- a/drivers/power/reset/reboot-mode.c
+++ b/drivers/power/reset/reboot-mode.c
@@ -22,7 +22,7 @@
 
 struct mode_info {
 	const char *mode;
-	u32 magic;
+	u64 magic;
 	struct list_head list;
 };
 
@@ -74,8 +74,7 @@ static const struct class reboot_mode_class = {
 	.dev_groups = reboot_mode_groups,
 };
 
-static unsigned int get_reboot_mode_magic(struct reboot_mode_driver *reboot,
-					  const char *cmd)
+static u64 get_reboot_mode_magic(struct reboot_mode_driver *reboot, const char *cmd)
 {
 	const char *normal = "normal";
 	struct mode_info *info;
@@ -107,7 +106,7 @@ static int reboot_mode_notify(struct notifier_block *this,
 			      unsigned long mode, void *cmd)
 {
 	struct reboot_mode_driver *reboot;
-	unsigned int magic;
+	u64 magic;
 
 	reboot = container_of(this, struct reboot_mode_driver, reboot_notifier);
 	magic = get_reboot_mode_magic(reboot, cmd);
@@ -175,16 +174,22 @@ int reboot_mode_register(struct reboot_mode_driver *reboot)
 	struct property *prop;
 	struct device_node *np = reboot->dev->of_node;
 	size_t len = strlen(PREFIX);
-	u32 magic;
+	size_t count;
+	u32 magic[2];
 	int ret;
 
 	INIT_LIST_HEAD(&reboot->head);
 
 	for_each_property_of_node(np, prop) {
+		memset(magic, 0, sizeof(magic));
+
 		if (strncmp(prop->name, PREFIX, len))
 			continue;
 
-		if (device_property_read_u32(reboot->dev, prop->name, &magic)) {
+		count = device_property_count_u32(reboot->dev, prop->name);
+
+		if (!count || count > ARRAY_SIZE(magic) ||
+		    device_property_read_u32_array(reboot->dev, prop->name, magic, count)) {
 			pr_debug("reboot mode %s without magic number\n", prop->name);
 			continue;
 		}
@@ -195,7 +200,7 @@ int reboot_mode_register(struct reboot_mode_driver *reboot)
 			goto error;
 		}
 
-		info->magic = magic;
+		info->magic = REBOOT_MODE_MAGIC(magic[0], magic[1]);
 		info->mode = kstrdup_const(prop->name + len, GFP_KERNEL);
 		if (!info->mode) {
 			ret =  -ENOMEM;
diff --git a/drivers/power/reset/syscon-reboot-mode.c b/drivers/power/reset/syscon-reboot-mode.c
index e0772c9f70f7a19cd8ec8a0b7fdbbaa7ba44afd0..9f4b18c5e46f6a8bf197773ceceb80b250f57541 100644
--- a/drivers/power/reset/syscon-reboot-mode.c
+++ b/drivers/power/reset/syscon-reboot-mode.c
@@ -20,16 +20,16 @@ struct syscon_reboot_mode {
 	u32 mask;
 };
 
-static int syscon_reboot_mode_write(struct reboot_mode_driver *reboot,
-				    unsigned int magic)
+static int syscon_reboot_mode_write(struct reboot_mode_driver *reboot, u64 magic)
 {
 	struct syscon_reboot_mode *syscon_rbm;
+	u32 magic_arg1 = REBOOT_MODE_ARG1(magic);
 	int ret;
 
 	syscon_rbm = container_of(reboot, struct syscon_reboot_mode, reboot);
 
 	ret = regmap_update_bits(syscon_rbm->map, syscon_rbm->offset,
-				 syscon_rbm->mask, magic);
+				 syscon_rbm->mask, magic_arg1);
 	if (ret < 0)
 		dev_err(reboot->dev, "update reboot mode bits failed\n");
 
diff --git a/include/linux/reboot-mode.h b/include/linux/reboot-mode.h
index 4a2abb38d1d612ec0fdf05eb18c98b210f631b7f..2ce189fdfff4b396d7cc6f175b30016781ae4fe9 100644
--- a/include/linux/reboot-mode.h
+++ b/include/linux/reboot-mode.h
@@ -2,10 +2,23 @@
 #ifndef __REBOOT_MODE_H__
 #define __REBOOT_MODE_H__
 
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/types.h>
+
+/* Construct 64-bit reboot magic: arg2 in upper 32 bits, arg1 in lower 32 */
+#define REBOOT_MODE_MAGIC(arg1, arg2) \
+	(FIELD_PREP(GENMASK_ULL(31, 0), (arg1)) | \
+	 FIELD_PREP(GENMASK_ULL(63, 32), (arg2)))
+/* Get 32 bit arg1 from 64 bit magic */
+#define REBOOT_MODE_ARG1(magic) FIELD_GET(GENMASK_ULL(31, 0), magic)
+/* Get 32 bit arg2 from 64 bit magic */
+#define REBOOT_MODE_ARG2(magic) FIELD_GET(GENMASK_ULL(63, 32), magic)
+
 struct reboot_mode_driver {
 	struct device *dev;
 	struct list_head head;
-	int (*write)(struct reboot_mode_driver *reboot, unsigned int magic);
+	int (*write)(struct reboot_mode_driver *reboot, u64 magic);
 	struct notifier_block reboot_notifier;
 };
 

-- 
2.34.1



^ permalink raw reply related

* [PATCH v21 03/13] power: reset: reboot-mode: Add support for predefined reboot modes
From: Shivendra Pratap @ 2026-04-27 17:34 UTC (permalink / raw)
  To: Sebastian Reichel, Mark Rutland, Lorenzo Pieralisi,
	Rafael J. Wysocki, Daniel Lezcano, Christian Loehle, Ulf Hansson,
	Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Bjorn Andersson, Konrad Dybcio, Arnd Bergmann, Souvik Chakravarty,
	Andy Yan, Matthias Brugger, John Stultz, Moritz Fischer,
	Bartosz Golaszewski, Sudeep Holla, Ulf Hansson
  Cc: linux-pm, linux-kernel, linux-arm-msm, linux-arm-kernel,
	devicetree, Shivendra Pratap, Florian Fainelli,
	Krzysztof Kozlowski, Dmitry Baryshkov, Mukesh Ojha, Andre Draszik,
	Greg Kroah-Hartman, Kathiravan Thirumoorthy, Srinivas Kandagatla
In-Reply-To: <20260427-arm-psci-system_reset2-vendor-reboots-v21-0-dcf937775e73@oss.qualcomm.com>

reboot-mode based drivers can define a reboot-mode by adding it under
the reboot-mode node in device tree. This limits such drivers, to define
any predefined reboot-modes statically within the driver and creates a
dependency on device-tree.

Introduce a list for predefined modes in the reboot-mode framework and
process the predefined reboot-modes along with the device-tree defined
reboot-modes. Modify existing reboot-mode based drivers to initialize
the predefined list-head as empty.

This patch enables a reboot mode driver to define reboot-modes through a
predefined static list, in addition to the device-tree based reboot-modes.

Signed-off-by: Shivendra Pratap <shivendra.pratap@oss.qualcomm.com>
---
 drivers/power/reset/nvmem-reboot-mode.c  |  1 +
 drivers/power/reset/qcom-pon.c           |  1 +
 drivers/power/reset/reboot-mode.c        | 66 ++++++++++++++++++++------------
 drivers/power/reset/syscon-reboot-mode.c |  1 +
 include/linux/reboot-mode.h              |  8 ++++
 5 files changed, 52 insertions(+), 25 deletions(-)

diff --git a/drivers/power/reset/nvmem-reboot-mode.c b/drivers/power/reset/nvmem-reboot-mode.c
index bd05d660490c686b43134f82f1eadd7665403d20..83a8d80fd7d1ccb1b736aee5f2d675246a63b8f8 100644
--- a/drivers/power/reset/nvmem-reboot-mode.c
+++ b/drivers/power/reset/nvmem-reboot-mode.c
@@ -53,6 +53,7 @@ static int nvmem_reboot_mode_probe(struct platform_device *pdev)
 
 	nvmem_rbm->reboot.dev = &pdev->dev;
 	nvmem_rbm->reboot.write = nvmem_reboot_mode_write;
+	INIT_LIST_HEAD(&nvmem_rbm->reboot.predefined_modes);
 
 	nvmem_rbm->cell = devm_nvmem_cell_get(&pdev->dev, "reboot-mode");
 	if (IS_ERR(nvmem_rbm->cell)) {
diff --git a/drivers/power/reset/qcom-pon.c b/drivers/power/reset/qcom-pon.c
index 57b36e6186f80aff947fd7f5aae5ce280c65dc6b..9d0e3fc621a6173438c6da4cce38394199451881 100644
--- a/drivers/power/reset/qcom-pon.c
+++ b/drivers/power/reset/qcom-pon.c
@@ -73,6 +73,7 @@ static int qcom_pon_probe(struct platform_device *pdev)
 		pon->reboot_mode.dev = &pdev->dev;
 		pon->reason_shift = reason_shift;
 		pon->reboot_mode.write = qcom_pon_reboot_mode_write;
+		INIT_LIST_HEAD(&pon->reboot_mode.predefined_modes);
 		error = devm_reboot_mode_register(&pdev->dev, &pon->reboot_mode);
 		if (error) {
 			dev_err(&pdev->dev, "can't register reboot mode\n");
diff --git a/drivers/power/reset/reboot-mode.c b/drivers/power/reset/reboot-mode.c
index f1372dc700e48043320812c3d3619ab1539e1854..a84f7c67a4746d4d5c932d4df018aae10bd5518c 100644
--- a/drivers/power/reset/reboot-mode.c
+++ b/drivers/power/reset/reboot-mode.c
@@ -20,12 +20,6 @@
 
 #define PREFIX "mode-"
 
-struct mode_info {
-	const char *mode;
-	u64 magic;
-	struct list_head list;
-};
-
 struct reboot_mode_sysfs_data {
 	struct device *reboot_mode_device;
 	struct list_head head;
@@ -116,6 +110,33 @@ static int reboot_mode_notify(struct notifier_block *this,
 	return NOTIFY_DONE;
 }
 
+static int reboot_mode_add_mode(struct reboot_mode_driver *reboot,
+				const char *mode, u64 magic,
+				const char *name)
+{
+	struct mode_info *info;
+
+	if (!mode || mode[0] == '\0') {
+		pr_err("invalid mode name(%s): too short!\n", name);
+		return -EINVAL;
+	}
+
+	info = kzalloc_obj(*info, GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	info->mode = kstrdup_const(mode, GFP_KERNEL);
+	if (!info->mode) {
+		kfree(info);
+		return -ENOMEM;
+	}
+
+	info->magic = magic;
+	list_add_tail(&info->list, &reboot->head);
+
+	return 0;
+}
+
 static int reboot_mode_create_device(struct reboot_mode_driver *reboot)
 {
 	struct reboot_mode_sysfs_data *priv;
@@ -180,9 +201,11 @@ int reboot_mode_register(struct reboot_mode_driver *reboot)
 
 	INIT_LIST_HEAD(&reboot->head);
 
+	if (!np)
+		goto predefined_modes;
+
 	for_each_property_of_node(np, prop) {
 		memset(magic, 0, sizeof(magic));
-
 		if (strncmp(prop->name, PREFIX, len))
 			continue;
 
@@ -194,26 +217,20 @@ int reboot_mode_register(struct reboot_mode_driver *reboot)
 			continue;
 		}
 
-		info = kzalloc(sizeof(*info), GFP_KERNEL);
-		if (!info) {
-			ret = -ENOMEM;
+		ret = reboot_mode_add_mode(reboot, prop->name + len,
+					   REBOOT_MODE_MAGIC(magic[0], magic[1]),
+					   prop->name);
+		if (ret)
 			goto error;
-		}
+	}
 
-		info->magic = REBOOT_MODE_MAGIC(magic[0], magic[1]);
-		info->mode = kstrdup_const(prop->name + len, GFP_KERNEL);
-		if (!info->mode) {
-			ret =  -ENOMEM;
+predefined_modes:
+	list_for_each_entry(info, &reboot->predefined_modes, list) {
+		ret = reboot_mode_add_mode(reboot, info->mode,
+					   info->magic,
+					   info->mode);
+		if (ret)
 			goto error;
-		} else if (info->mode[0] == '\0') {
-			kfree_const(info->mode);
-			ret = -EINVAL;
-			pr_err("invalid mode name(%s): too short!\n", prop->name);
-			goto error;
-		}
-
-		list_add_tail(&info->list, &reboot->head);
-		info = NULL;
 	}
 
 	reboot->reboot_notifier.notifier_call = reboot_mode_notify;
@@ -226,7 +243,6 @@ int reboot_mode_register(struct reboot_mode_driver *reboot)
 	return 0;
 
 error:
-	kfree(info);
 	reboot_mode_unregister(reboot);
 	return ret;
 }
diff --git a/drivers/power/reset/syscon-reboot-mode.c b/drivers/power/reset/syscon-reboot-mode.c
index 9f4b18c5e46f6a8bf197773ceceb80b250f57541..0218b71541a0cefe1534e306f956ae51ea9ee870 100644
--- a/drivers/power/reset/syscon-reboot-mode.c
+++ b/drivers/power/reset/syscon-reboot-mode.c
@@ -48,6 +48,7 @@ static int syscon_reboot_mode_probe(struct platform_device *pdev)
 	syscon_rbm->reboot.dev = &pdev->dev;
 	syscon_rbm->reboot.write = syscon_reboot_mode_write;
 	syscon_rbm->mask = 0xffffffff;
+	INIT_LIST_HEAD(&syscon_rbm->reboot.predefined_modes);
 
 	syscon_rbm->map = syscon_node_to_regmap(pdev->dev.parent->of_node);
 	if (IS_ERR(syscon_rbm->map))
diff --git a/include/linux/reboot-mode.h b/include/linux/reboot-mode.h
index 2ce189fdfff4b396d7cc6f175b30016781ae4fe9..f4518528fcc1fac49209de91cec7e050088ef8ec 100644
--- a/include/linux/reboot-mode.h
+++ b/include/linux/reboot-mode.h
@@ -15,9 +15,17 @@
 /* Get 32 bit arg2 from 64 bit magic */
 #define REBOOT_MODE_ARG2(magic) FIELD_GET(GENMASK_ULL(63, 32), magic)
 
+struct mode_info {
+	const char *mode;
+	u64 magic;
+	struct list_head list;
+};
+
 struct reboot_mode_driver {
 	struct device *dev;
 	struct list_head head;
+	/* List of predefined reboot-modes, a reboot-mode-driver may populate. */
+	struct list_head predefined_modes;
 	int (*write)(struct reboot_mode_driver *reboot, u64 magic);
 	struct notifier_block reboot_notifier;
 };

-- 
2.34.1



^ permalink raw reply related

* Re: [PATCH v5 4/6] counter: Add rockchip-pwm-capture driver
From: Nicolas Frattaroli @ 2026-04-27 17:35 UTC (permalink / raw)
  To: Uwe Kleine-König, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Heiko Stuebner, Lee Jones, William Breathitt Gray,
	Damon Ding
  Cc: kernel, Jonas Karlman, Alexey Charkov, linux-rockchip, linux-pwm,
	devicetree, linux-arm-kernel, linux-kernel, linux-iio
In-Reply-To: <91eed9a4-4dc3-4846-baf3-e9cef53be79b@rock-chips.com>

Hi Damon,

On Sunday, 26 April 2026 12:55:20 Central European Summer Time Damon Ding wrote:
> Hi Nicolas,
> 
> On 4/20/2026 9:52 PM, Nicolas Frattaroli wrote:
> > Among many other things, Rockchip's new PWMv4 IP in the RK3576 supports
> > PWM capture functionality.
> > 
> > Add a basic driver for this that works to expose HPC/LPC counts and
> > state change events to userspace through the counter framework. It's
> > quite basic, but works well enough to demonstrate the device function
> > exclusion stuff that mfpwm does, in order to eventually support all the
> > functions of this device in drivers within their appropriate subsystems,
> > without them interfering with each other.
> > 
> > Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
> > ---
> >   MAINTAINERS                            |   1 +
> >   drivers/counter/Kconfig                |  11 ++
> >   drivers/counter/Makefile               |   1 +
> >   drivers/counter/rockchip-pwm-capture.c | 307 +++++++++++++++++++++++++++++++++
> >   4 files changed, 320 insertions(+)
> > 
> 
> For functional validation, I connected PWM0/PWM1 (continuous output)
> to PWM2 (capture input) pairwise.
> 
> I enabled the counter via:
> /sys/bus/counter/devices/counter0/count0/enable
> 
> Then I verified the functionality by reading the count values from:
> /sys/bus/counter/devices/counter0/count0/count
> /sys/bus/counter/devices/counter0/count1/count
> 
> Tested-by: Damon Ding <damon.ding@rock-chips.com>

Thanks for testing! To make sure a bug on the pwm output driver won't
be cancelled out by an equivalent bug in the counter driver, I did
my counter driver testing by using an RK3588 as the source of the
PWM signal, with the two boards sharing a common ground. Maybe I
should get a proper function generator. :)

> BTW: Is there any user-space test tool similar to libpwm for the
> counter subsystem?

I've tried looking for this as well, and couldn't find anything. If
it exists then adding a mention of it to generic-counter.rst would
be in order I think.

> 
> ......
> > diff --git a/drivers/counter/rockchip-pwm-capture.c b/drivers/counter/rockchip-pwm-capture.c
> > new file mode 100644
> > index 000000000000..09a92f2bc409
> > --- /dev/null
> > +++ b/drivers/counter/rockchip-pwm-capture.c
> > @@ -0,0 +1,307 @@
> > +// SPDX-License-Identifier: GPL-2.0-or-later
> > +/*
> > + * Copyright (c) 2025 Collabora Ltd.
> > + *
> > + * A counter driver for the Pulse-Width-Modulation (PWM) hardware found on
> > + * Rockchip SoCs such as the RK3576, internally referred to as "PWM v4". It
> > + * allows for measuring the high cycles and low cycles of a PWM signal through
> > + * the generic counter framework, while guaranteeing exclusive use over the
> > + * MFPWM device while the counter is enabled.
> > + *
> > + * Authors:
> > + *     Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
> > + */
> > +
> > +#include <linux/cleanup.h>
> > +#include <linux/counter.h>
> > +#include <linux/devm-helpers.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/mfd/rockchip-mfpwm.h>
> > +#include <linux/mod_devicetable.h>
> > +#include <linux/of.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/spinlock.h>
> > +
> > +#define RKPWMC_INT_MASK			(PWMV4_INT_LPC | PWMV4_INT_HPC)
> > +
> > +struct rockchip_pwm_capture {
> > +	struct rockchip_mfpwm_func *pwmf;
> > +	struct counter_device *counter;
> > +};
> > +
> > +static struct counter_signal rkpwmc_signals[] = {
> > +	{
> > +		.id = 0,
> > +		.name = "PWM Clock"
> > +	},
> > +};
> > +
> > +static const enum counter_synapse_action rkpwmc_hpc_lpc_actions[] = {
> > +	COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
> > +	COUNTER_SYNAPSE_ACTION_NONE,
> > +};
> 
> For the capture function, it uses the PWM's reference clock (dclk) as 
> the time base to measure how many reference cycles the high and low 
> levels of the input waveform last respectively.
> 
> I find it a bit strange to set COUNTER_SYNAPSE_ACTION_BOTH_EDGES for 
> counting. If we treat the input waveform as a sequence of square waves 
> sampled by dclk cycles, it feels like we should count on a single edge 
> (rising edge only) rather than both edges.
> 

Yeah, that's a good point. I think I've been struggling to wrap my head
around what the signal is and what the synapse should trigger on because
the signal isn't something exposed in this case.

Your explanation makes sense, and perhaps I should rename
rkpwmc_hpc_lpc_actions as well. Currently it makes it seem like HPC
and LPC are the signal, when actually the waveform is the signal, HPC
and LPC are the counts, and the synapse is a sample every dclk spotting
a transition if I understand this correctly.

> > +
> > +static struct counter_synapse rkpwmc_pwm_synapses[] = {
> > +	{
> > +		.actions_list = rkpwmc_hpc_lpc_actions,
> > +		.num_actions = ARRAY_SIZE(rkpwmc_hpc_lpc_actions),
> > +		.signal = &rkpwmc_signals[0]
> > +	},
> > +};
> > +
> 
> Best regards,
> Damon
> 

Kind regards,
Nicolas Frattaroli




^ permalink raw reply

* [PATCH v21 04/13] firmware: psci: Introduce command-based with magic
From: Shivendra Pratap @ 2026-04-27 17:34 UTC (permalink / raw)
  To: Sebastian Reichel, Mark Rutland, Lorenzo Pieralisi,
	Rafael J. Wysocki, Daniel Lezcano, Christian Loehle, Ulf Hansson,
	Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Bjorn Andersson, Konrad Dybcio, Arnd Bergmann, Souvik Chakravarty,
	Andy Yan, Matthias Brugger, John Stultz, Moritz Fischer,
	Bartosz Golaszewski, Sudeep Holla, Ulf Hansson
  Cc: linux-pm, linux-kernel, linux-arm-msm, linux-arm-kernel,
	devicetree, Shivendra Pratap, Florian Fainelli,
	Krzysztof Kozlowski, Dmitry Baryshkov, Mukesh Ojha, Andre Draszik,
	Greg Kroah-Hartman, Kathiravan Thirumoorthy, Srinivas Kandagatla
In-Reply-To: <20260427-arm-psci-system_reset2-vendor-reboots-v21-0-dcf937775e73@oss.qualcomm.com>

PSCI currently supports only two resets - SYSTEM_RESET and SYSTEM_RESET2
ARCH WARM reset. The reset patch is selected based on the Linux
reboot_mode variable. The PSCI specification now includes SYSTEM_RESET2
for vendor-specific resets but there's no mechanism to issue these
through psci_sys_reset().

Add a command-based reset mechanism that allows external drivers to set
the psci reset command via a exported psci_set_reset_cmd() function.

Define predefined reset_types - PSCI_RESET_TYPE_SYSTEM_RESET to map to
SYSTEM_RESET, and PSCI_RESET_TYPE_SYSTEM_RESET2_ARCH_WARM to map to
SYSTEM_RESET2 arch warm reset. Interpret zero cmd_reset_type, for
predefined reset-command selection via cmd_cookie. For non-zero
cmd_reset_type, check for valid vendor_reset_type and set the psci
reset_command and cookie accordingly.

Disable PSCI command-based reset by default and treat invalid reset
commands as no‑op. psci_sys_reset() follows its original flow based on
reboot_mode until a reset command is explicitly set by
psci_set_reset_cmd().  In the device reset flow, psci_set_reset_cmd() is
called in reboot_notifier phase and the device reset happens in
psci_sys_reset() which is called later in the restart_notifier phase. If
a kernel panic occurs in between these two phases, the reboot should
take its original flow based on the value of reboot_mode. Disable the
command-based reset in such case.

Signed-off-by: Shivendra Pratap <shivendra.pratap@oss.qualcomm.com>
---
 drivers/firmware/psci/psci.c | 75 ++++++++++++++++++++++++++++++++++++++++++--
 include/linux/psci.h         | 19 +++++++++++
 2 files changed, 92 insertions(+), 2 deletions(-)

diff --git a/drivers/firmware/psci/psci.c b/drivers/firmware/psci/psci.c
index 38ca190d4a22d6e7e0f06420e8478a2b0ec2fe6f..cb37c39e2b4b1d99f0080f6a5cd6c92a070beda8 100644
--- a/drivers/firmware/psci/psci.c
+++ b/drivers/firmware/psci/psci.c
@@ -51,6 +51,16 @@ static int resident_cpu = -1;
 struct psci_operations psci_ops;
 static enum arm_smccc_conduit psci_conduit = SMCCC_CONDUIT_NONE;
 
+/*
+ * Encoded reset command:
+ * bits[63:32] = cookie
+ * bits[31:0]  = reset_type
+ */
+static u64 reset_cmd;
+
+#define PSCI_RESET_TYPE(reset_cmd)	((u32)(reset_cmd))
+#define PSCI_RESET_COOKIE(reset_cmd)	((u32)((reset_cmd) >> 32))
+
 bool psci_tos_resident_on(int cpu)
 {
 	return cpu == resident_cpu;
@@ -80,6 +90,35 @@ static u32 psci_cpu_suspend_feature;
 static bool psci_system_reset2_supported;
 static bool psci_system_off2_hibernate_supported;
 
+static u32 psci_fn_from_cookie(u32 cookie)
+{
+	switch (cookie) {
+	case PSCI_RESET_TYPE_SYSTEM_RESET2_ARCH_WARM:
+		if (psci_system_reset2_supported)
+			return PSCI_FN_NATIVE(1_1, SYSTEM_RESET2);
+		return 0;
+	case PSCI_RESET_TYPE_SYSTEM_RESET:
+		return PSCI_0_2_FN_SYSTEM_RESET;
+	default:
+		return 0;
+	}
+}
+
+/** psci_set_reset_cmd() - Configure reset request for psci_sys_reset()
+ * @psci_reset_cmd: reset command encoded as cookie[63:32] | reset_type[31:0]
+ *
+ * Save reset command.
+ */
+void psci_set_reset_cmd(u64 psci_reset_cmd)
+{
+	reset_cmd = psci_reset_cmd;
+}
+
+bool psci_has_system_reset2_support(void)
+{
+	return psci_system_reset2_supported;
+}
+
 static inline bool psci_has_ext_power_state(void)
 {
 	return psci_cpu_suspend_feature &
@@ -306,8 +345,24 @@ static int get_set_conduit_method(const struct device_node *np)
 	return 0;
 }
 
-static int psci_sys_reset(struct notifier_block *nb, unsigned long action,
-			  void *data)
+static void psci_handle_reset_cmd(void)
+{
+	u32 psci_sys_reset_fn;
+
+	if ((reset_cmd & BIT_ULL(31)) && psci_system_reset2_supported) {
+		/* PSCI SYSTEM_RESET2 Vendor-specific reset */
+		invoke_psci_fn(PSCI_FN_NATIVE(1_1, SYSTEM_RESET2),
+			       PSCI_RESET_TYPE(reset_cmd),
+			       PSCI_RESET_COOKIE(reset_cmd), 0);
+	} else {
+		/* cookie part of the reset_cmd decides ARCH WARM RESET vs SYSTEM_RESET */
+		psci_sys_reset_fn = psci_fn_from_cookie(PSCI_RESET_COOKIE(reset_cmd));
+		if (!PSCI_RESET_TYPE(reset_cmd) && psci_sys_reset_fn)
+			invoke_psci_fn(psci_sys_reset_fn, 0, 0, 0);
+	}
+}
+
+static void psci_handle_reboot_mode(void)
 {
 	if ((reboot_mode == REBOOT_WARM || reboot_mode == REBOOT_SOFT) &&
 	    psci_system_reset2_supported) {
@@ -320,6 +375,22 @@ static int psci_sys_reset(struct notifier_block *nb, unsigned long action,
 	} else {
 		invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0);
 	}
+}
+
+static int psci_sys_reset(struct notifier_block *nb, unsigned long action,
+			  void *data)
+{
+	/*
+	 * Command-based resets are configured at the reboot_notifier phase.
+	 * If a kernel panic occurs between the reboot_notifier and this final
+	 * reset, ignore the command-based reset and let reboot_mode drive the
+	 * reset flow.
+	 * If reset_cmd is zero, there is no command to handle.
+	 */
+	if (reset_cmd && !panic_in_progress())
+		psci_handle_reset_cmd();
+	else
+		psci_handle_reboot_mode();
 
 	return NOTIFY_DONE;
 }
diff --git a/include/linux/psci.h b/include/linux/psci.h
index 4ca0060a3fc42ba1ca751c7862fb4ad8dda35a4c..c2458291a3faf5ac89b1528dae2c9b805a2dd075 100644
--- a/include/linux/psci.h
+++ b/include/linux/psci.h
@@ -21,6 +21,21 @@ bool psci_power_state_is_valid(u32 state);
 int psci_set_osi_mode(bool enable);
 bool psci_has_osi_support(void);
 
+/**
+ * enum psci_reset_type	-	PSCI_RESET_TYPE for SYSTEM_RESET.
+ * @PSCI_RESET_TYPE_SYSTEM_RESET: Standard SYSTEM_RESET command.
+ * @PSCI_RESET_TYPE_SYSTEM_RESET2_ARCH_WARM: SYSTEM_RESET2 architectural warm reset.
+ *
+ * These enum values map PSCI_RESET_TYPE_SYSTEM_* constants to reset strings
+ * issued from user space. When user space requests a reset, the cookie
+ * carries one of these values, and the PSCI reset path translates it into
+ * the appropriate PSCI system reset call.
+ */
+enum psci_reset_type {
+	PSCI_RESET_TYPE_SYSTEM_RESET = 1,
+	PSCI_RESET_TYPE_SYSTEM_RESET2_ARCH_WARM,
+};
+
 struct psci_operations {
 	u32 (*get_version)(void);
 	int (*cpu_suspend)(u32 state, unsigned long entry_point);
@@ -45,8 +60,12 @@ struct psci_0_1_function_ids get_psci_0_1_function_ids(void);
 
 #if defined(CONFIG_ARM_PSCI_FW)
 int __init psci_dt_init(void);
+void psci_set_reset_cmd(u64 psci_reset_cmd);
+bool psci_has_system_reset2_support(void);
 #else
 static inline int psci_dt_init(void) { return 0; }
+static inline void psci_set_reset_cmd(u64 psci_reset_cmd) { }
+static inline bool psci_has_system_reset2_support(void) { return false; }
 #endif
 
 #if defined(CONFIG_ARM_PSCI_FW) && defined(CONFIG_ACPI)

-- 
2.34.1



^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox