* Re: [PATCH v16 01/18] seccomp: Convert __secure_computing() to return boolean
From: Ada Couprie Diaz @ 2026-06-30 16:37 UTC (permalink / raw)
To: Jinjie Ruan
Cc: Ada Couprie Diaz, oleg, richard.henderson, mattst88, linmag7,
linux, catalin.marinas, will, kees, guoren, chenhuacai, kernel,
geert, tsbogend, James.Bottomley, deller, maddy, mpe, npiggin,
chleroy, pjw, palmer, aou, alex, hca, gor, agordeev, borntraeger,
svens, ysato, dalias, glaubitz, richard, anton.ivanov, johannes,
luto, tglx, mingo, bp, dave.hansen, hpa, chris, jcmvbkbc, peterz,
wad, thuth, mark.rutland, kevin.brodsky, linusw, yeoreum.yun,
song, james.morse, anshuman.khandual, broonie, liqiang01, pengcan,
ryan.roberts, yangtiezhu, sshegde, mchauras, austin.kim, jchrist,
arnd, thomas.weissschuh, sohil.mehta, andrew.cooper3, jgross, kas,
x86, linux-alpha, linux-kernel, linux-arm-kernel, linux-mm,
linux-csky, loongarch, linux-m68k, linux-mips, linux-parisc,
linuxppc-dev, linux-riscv, linux-s390, linux-sh, linux-um
In-Reply-To: <20260629130616.642022-2-ruanjinjie@huawei.com>
Hi Jinjie,
On 29/06/2026 14:05, Jinjie Ruan wrote:
> The return value of __secure_computing() currently uses 0 to indicate
> that a system call should be allowed, and -1 to indicate that it should
> be blocked/killed. This 0/-1 pattern is non-intuitive for a security
> check function and makes the control flow at the call sites less readable.
>
> Furthermore, any potential future changes to these return values would
> require a high-risk, error-prone audit of all its users across different
> architectures.
>
> Sanitize this logic by converting the return type of __secure_computing()
> to a proper boolean, where 'true' explicitly means 'allow' and 'false'
> means 'fail/deny'.
>
> Update all the two dozen or so call sites across the tree to align with
> this new boolean semantic. No functional changes are intended, as the
> callers still return -1 to the lower-level assembly entry code upon
> seccomp denial.
Would it be relevant to mention that this fixes the unsound return value of
`syscall_trace_enter()` in generic entry, which motivated the patch
initially[0] ?
> Suggested-by: Thomas Gleixner <tglx@kernel.org>
> Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
> ---
> arch/alpha/kernel/ptrace.c | 2 +-
> arch/arm/kernel/ptrace.c | 2 +-
> arch/arm64/kernel/ptrace.c | 2 +-
> arch/csky/kernel/ptrace.c | 2 +-
> arch/m68k/kernel/ptrace.c | 2 +-
> arch/mips/kernel/ptrace.c | 2 +-
> arch/parisc/kernel/ptrace.c | 2 +-
> arch/sh/kernel/ptrace_32.c | 2 +-
> arch/um/kernel/skas/syscall.c | 2 +-
> arch/x86/entry/vsyscall/vsyscall_64.c | 2 +-
> arch/xtensa/kernel/ptrace.c | 3 +--
> include/linux/entry-common.h | 7 +++---
> include/linux/seccomp.h | 10 ++++----
> kernel/seccomp.c | 34 +++++++++++++--------------
> 14 files changed, 36 insertions(+), 38 deletions(-)
This is missing an update to the Kconfig documentation, a possible
suggestion :
diff --git a/arch/Kconfig b/arch/Kconfig
index fa7507ac8e13..9e3d40088afb 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -637,7 +637,7 @@ config HAVE_ARCH_SECCOMP_FILTER
- syscall_set_return_value()
- SIGSYS siginfo_t support
- secure_computing is called from a ptrace_event()-safe context
- - secure_computing return value is checked and a return value of -1
+ - secure_computing return value is checked and if false
results in the system call being skipped immediately.
- seccomp syscall wired up
- if !HAVE_SPARSE_SYSCALL_NR, have SECCOMP_ARCH_NATIVE,
> [...]
> diff --git a/include/linux/entry-common.h b/include/linux/entry-common.h
> index 416a3352261f..3f66320e46d3 100644
> --- a/include/linux/entry-common.h
> +++ b/include/linux/entry-common.h
> @@ -100,9 +100,8 @@ static __always_inline long syscall_trace_enter(struct pt_regs *regs, unsigned l
>
> /* Do seccomp after ptrace, to catch any tracer changes. */
> if (work & SYSCALL_WORK_SECCOMP) {
> - ret = __secure_computing();
> - if (ret == -1L)
> - return ret;
> + if (!__secure_computing())
> + return -1L;
> }
>
> /* Either of the above might have changed the syscall number */
> @@ -113,7 +112,7 @@ static __always_inline long syscall_trace_enter(struct pt_regs *regs, unsigned l
>
> syscall_enter_audit(regs, syscall);
>
> - return ret ? : syscall;
> + return syscall;
> }
> [...]
Otherwise this feels like a more appropriate change with regards to
"safeguarding against new `secure_computing()` return value" !
With the updated Kconfig :
Reviewed-by: Ada Couprie Diaz <ada.coupriediaz@arm.com>
Thanks,
Ada
[0]:
https://lore.kernel.org/r/20260511092103.1974980-2-ruanjinjie@huawei.com
^ permalink raw reply related
* Re: [PATCH v2 1/3] selftests/mm: handle EINVAL when configuring gigantic hugepages
From: Sayali Patil @ 2026-06-30 20:20 UTC (permalink / raw)
To: David Hildenbrand (Arm), Andrew Morton, Shuah Khan, linux-mm,
linux-kernel, linux-kselftest, Ritesh Harjani
Cc: Zi Yan, Michal Hocko, Oscar Salvador, Lorenzo Stoakes, Dev Jain,
Liam.Howlett, linuxppc-dev, Miaohe Lin, Venkat Rao Bagalkote
In-Reply-To: <ccba342f-1ed0-43fe-ac59-9b8bbfb168d4@kernel.org>
On 30/06/26 16:15, David Hildenbrand (Arm) wrote:
> On 6/30/26 11:32, Sayali Patil wrote:
>> Some MM selftests attempt to configure the amount of
>> HugeTLB pages of different sizes by writing to nr_hugepages.
>>
>> PowerPC hash MMU pSeries systems advertise gigantic hugepage sizes
>> but do not support runtime allocation of such pages, writes
>> to the corresponding nr_hugepages file fail with -EINVAL.
>> This causes the test to bail out even though the failure is due
>> to a platform limitation rather than the
>> functionality being tested.
>>
>> Treat -EINVAL from the sysfs write as a skipped configuration request
>> and continue running the test instead of failing.
>>
>> Before patch:
>> -------------------------
>> running ./hugetlb-madvise
>> -------------------------
>> TAP version 13
>> 1..1
>> [INFO] detected hugetlb page size: 16777216 KiB
>> [INFO] detected hugetlb page size: 16384 KiB
>> ok 1 MADV_DONTNEED and MADV_REMOVE on hugetlb
>> Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0
>> Bail out! /sys/kernel/mm/hugepages/hugepages-16777216kB/nr_hugepages
>> write(0) failed: Invalid argument
>> Totals: pass:0 fail:0 xfail:0 xpass:0 skip:0 error:0
>> [FAIL]
>>
>> After patch:
>> -------------------------
>> running ./hugetlb-madvise
>> -------------------------
>> TAP version 13
>> 1..1
>> [INFO] detected hugetlb page size: 16777216 KiB
>> [INFO] detected hugetlb page size: 16384 KiB
>> ok 1 MADV_DONTNEED and MADV_REMOVE on hugetlb
>> Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0
>> /sys/kernel/mm/hugepages/hugepages-16777216kB/nr_hugepages
>> write(0) failed: Invalid argument
>> [PASS]
>>
>> Fixes: 27477b28b74f ("selftests/mm: hugepage_settings: add APIs to get and set nr_hugepages")
>> Signed-off-by: Sayali Patil <sayalip@linux.ibm.com>
>> ---
>> .../testing/selftests/mm/hugepage_settings.c | 32 ++++++++++++++++++-
>> .../testing/selftests/mm/hugepage_settings.h | 1 +
>> 2 files changed, 32 insertions(+), 1 deletion(-)
>>
>> diff --git a/tools/testing/selftests/mm/hugepage_settings.c b/tools/testing/selftests/mm/hugepage_settings.c
>> index 2eab2110ac6a..ce38ae3da01a 100644
>> --- a/tools/testing/selftests/mm/hugepage_settings.c
>> +++ b/tools/testing/selftests/mm/hugepage_settings.c
>> @@ -422,6 +422,36 @@ static void hugetlb_sysfs_path(char *buf, size_t buflen,
>> size / 1024, attr);
>> }
>>
>> +void hugetlb_write_num(const char *path, unsigned long num)
>> +{
>> + int fd, saved_errno;
>> + ssize_t numwritten;
>> + char buf[21];
>> +
>> + sprintf(buf, "%lu", num);
>> +
>> + fd = open(path, O_WRONLY);
>> + if (fd == -1)
>> + ksft_exit_fail_msg("%s open failed: %s\n", path, strerror(errno));
>> +
>> + numwritten = write(fd, buf, strlen(buf));
>> + saved_errno = errno;
>> + close(fd);
>> + errno = saved_errno;
>> +
>> + /* Treat EINVAL as a skipped configuration (e.g., unsupported gigantic pages) */
>> + if (numwritten < 0 && errno == EINVAL) {
>> + ksft_print_msg("%s write(%s) failed: %s\n", path, buf, strerror(errno));
>
> Should we even print anything here? Rather confusing. It's just like we cannot
> allocate anything (no memory).
>
> In general, you are copy-pasting a lot of write_num()+write_file() content,
> which is really suboptimal.
>
> All you want is an option for write_num -> write_file to skip on -EINVAL, correct?
>
> There are not that many write_num / write_file users ...
>
Hi David,
Yes, all I need is to ignore the expected -EINVAL when attempting to
configure gigantic hugepages via nr_hugepages.
I looked at extending write_num()/write_file() for this as in v1
(https://lore.kernel.org/all/8bfa921e30eb94072685103f6496784aa23bb166.1782365671.git.sayalip@linux.ibm.com/),
but these helpers are shared by several other selftests.
For example, write_file() is used by split_huge_page_test setup and by
khugepaged tests for drop_caches, and is also used for various THP and
khugepaged settings where -EINVAL would indicate a genuine setup
failure. This concern was also raised during the v1 review.
Because the expected -EINVAL is specific to gigantic hugepage runtime
allocation, I kept the handling local to the hugetlb setup path rather
than changing the semantics of the common helpers.
I also agree that printing a message is not particularly useful in this
case, and we can simply return without emitting any output.
Please let me know if you would prefer a different approach.
Thanks,
Sayali
^ permalink raw reply
* Re: [PATCH V2] powerpc/syscall: Fix seccomp errno handling with GENERIC_ENTRY
From: Shrikanth Hegde @ 2026-06-30 20:11 UTC (permalink / raw)
To: Mukesh Kumar Chaurasiya (IBM), maddy, mpe, npiggin, chleroy,
mkchauras, ryan.roberts, ruanjinjie, linuxppc-dev, linux-kernel
Cc: Michal Suchánek
In-Reply-To: <20260629182946.419552-1-mkchauras@gmail.com>
Hi Mukesh.
On 6/29/26 11:59 PM, Mukesh Kumar Chaurasiya (IBM) wrote:
> After enabling GENERIC_ENTRY on PowerPC, seccomp filters using
> SCMP_ACT_ERRNO without an explicit errnoRet value return ENOSYS
> (Function not implemented) instead of the expected EPERM (Operation
> not permitted).
>
> The issue occurs in system_call_exception() when syscall_enter_from_user_mode()
> returns -1 to indicate the syscall should be skipped (e.g., blocked by seccomp).
> The current code treats this -1 as a syscall number and compares it against
> NR_syscalls. Since -1 is greater than NR_syscalls,
> the code incorrectly returns -ENOSYS, overwriting the errno that seccomp
> already set via syscall_set_return_value().
>
> The generic entry code in syscall_trace_enter() calls __secure_computing(),
> which sets the appropriate errno in regs->gpr[3] and returns -1 to signal
> that the syscall should be skipped. However, the PowerPC syscall handler
> was not checking for this -1 return value before validating the syscall
> number.
>
> Fix this by explicitly checking if syscall_enter_from_user_mode() returns
> -1 and returning the value already set in regs->gpr[3] (the errno from
> seccomp) before performing the syscall number validation.
>
> Also Move the syscall_enter_from_user_mode() call and the seccomp/ptrace
> skip check to after the NR_syscalls bounds check.
>
> When syscall -1 was passed, the r0 == -1L check would trigger before
> the NR_syscalls check, causing syscall_get_error() to return 0 instead
> of -ENOSYS. This resulted in a silent success (ret=0, errno=0) instead
> of the expected ENOSYS error.
>
> By moving syscall_enter_from_user_mode() after the bounds check, an
> initial syscall number of -1 is correctly rejected with -ENOSYS first.
> The seccomp/ptrace skip path still works correctly for valid syscall
> numbers that get overridden to -1 by seccomp or ptrace.
>
> This aligns PowerPC's behavior with other architectures using GENERIC_ENTRY
> and restores correct seccomp errno handling.
>
> Fixes: bee25f97ad24 ("powerpc: Enable GENERIC_ENTRY feature")
> Reported-by: Michal Suchánek <msuchanek@suse.de>
> Closes: https://lore.kernel.org/all/ajpp-_XnbF3UTM_E@kunlun.suse.cz/
> Signed-off-by: Mukesh Kumar Chaurasiya (IBM) <mkchauras@gmail.com>
> ---
>
> v1 -> v2:
> - Fix issues in the previous fix (Michal)
> v1: https://lore.kernel.org/all/20260624171520.772408-1-mkchauras@gmail.com
>
> arch/powerpc/kernel/syscall.c | 7 ++++++-
> 1 file changed, 6 insertions(+), 1 deletion(-)
>
> diff --git a/arch/powerpc/kernel/syscall.c b/arch/powerpc/kernel/syscall.c
> index a9da2af6efa8..36d73933a311 100644
> --- a/arch/powerpc/kernel/syscall.c
> +++ b/arch/powerpc/kernel/syscall.c
> @@ -20,7 +20,6 @@ notrace long system_call_exception(struct pt_regs *regs, unsigned long r0)
> syscall_fn f;
>
> add_random_kstack_offset();
> - r0 = syscall_enter_from_user_mode(regs, r0);
>
> if (unlikely(r0 >= NR_syscalls)) {
> if (unlikely(trap_is_unsupported_scv(regs))) {
> @@ -31,6 +30,12 @@ notrace long system_call_exception(struct pt_regs *regs, unsigned long r0)
> return -ENOSYS;
> }
>
> + r0 = syscall_enter_from_user_mode(regs, r0);
> +
I see many arch first do syscall_enter_from_user_mode and then check for return value.
take x86 for example,
__visible noinstr bool do_syscall_64(struct pt_regs *regs, int nr)
{
nr = syscall_enter_from_user_mode(regs, nr);
if (!do_syscall_x64(regs, nr) && !do_syscall_x32(regs, nr) && nr != -1) {
/* Invalid system call, but still a system call. */
regs->ax = __x64_sys_ni_syscall(regs);
}
}
So seccomp fails silently there if initial nr was -1?
> + /* Seccomp or ptrace may have set return value, skip syscall */
> + if (unlikely(r0 == -1L))
> + return syscall_get_error(current, regs);
> +
> /* May be faster to do array_index_nospec? */
> barrier_nospec();
>
Code per se, looks okay to me.
^ permalink raw reply
* Re: [PATCH v3] powerpc/audit: Convert powerpc to AUDIT_ARCH_COMPAT_GENERIC
From: Paul Moore @ 2026-06-30 19:11 UTC (permalink / raw)
To: Madhavan Srinivasan, Venkat Rao Bagalkote
Cc: Harsh Prateek Bora, Christophe Leroy (CS GROUP), Michael Ellerman,
Nicholas Piggin, Eric Paris, Christophe Leroy, linux-kernel,
linuxppc-dev, audit, Thomas Weissschuh, Cédric Le Goater,
ritesh.list
In-Reply-To: <09e72f42-e180-4e2b-b379-92b755792a2c@linux.ibm.com>
On Wed, May 13, 2026 at 1:42 AM Madhavan Srinivasan <maddy@linux.ibm.com> wrote:
> On 5/13/26 10:05 AM, Harsh Prateek Bora wrote:
> > On 11/03/26 12:49 am, Paul Moore wrote:
> >> On Tue, Mar 10, 2026 at 11:08 AM Christophe Leroy (CS GROUP)
> >> <chleroy@kernel.org> wrote:
> >>>
> >>> From: Christophe Leroy <christophe.leroy@csgroup.eu>
> >>>
> >>> Commit e65e1fc2d24b ("[PATCH] syscall class hookup for all normal
> >>> targets") added generic support for AUDIT but that didn't include
> >>> support for bi-arch like powerpc.
> >>>
> >>> Commit 4b58841149dc ("audit: Add generic compat syscall support")
> >>> added generic support for bi-arch.
> >>>
> >>> Convert powerpc to that bi-arch generic audit support.
> >>>
> >>> With this change generated text is similar.
> >>>
> >>> Thomas has confirmed that the previously failing filter_exclude/test
> >>> is now successful both without and with this patch, see [1]
> >>>
> >>> [1]
> >>> https://lore.kernel.org/all/20260306115350-ef265661-6d6b-4043-9bd0-8e6b437d0d67@linutronix.de/
> >>>
> >>> Link: https://github.com/linuxppc/issues/issues/412
> >>> Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
> >>> Reviewed-by: Cédric Le Goater <clg@kaod.org>
> >>> ---
> >>> Venkat, a test result with
> >>> https://github.com/linux-audit/audit-testsuite would be appreciated.
> >>
> >> Yes, I'd like to see confirmation that the audit test suite runs clean
> >> on ppc systems with this patch applied, and unfortunately without a
> >> ppc system I have no way to test this myself.
>
> My bad, this is a miss from my end.
> Venkat is already on this and will update the results here.
Do we have an update on this? Maybe I missed it, but I don't recall
seeing any test results.
--
paul-moore.com
^ permalink raw reply
* Re: [PATCH v6 00/20] dma-mapping: Use DMA_ATTR_CC_SHARED through direct, pool and swiotlb paths
From: Jason Gunthorpe @ 2026-06-30 17:42 UTC (permalink / raw)
To: Aneesh Kumar K.V
Cc: Alexey Kardashevskiy, Catalin Marinas, iommu, linux-arm-kernel,
linux-kernel, linux-coco, Robin Murphy, Marek Szyprowski,
Will Deacon, Marc Zyngier, Steven Price, Suzuki K Poulose,
Jiri Pirko, Mostafa Saleh, Petr Tesarik, Dan Williams, Xu Yilun,
linuxppc-dev, linux-s390, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Alexander Gordeev,
Gerald Schaefer, Heiko Carstens, Vasily Gorbik,
Christian Borntraeger, Sven Schnelle, x86
In-Reply-To: <yq5ao6gtoncp.fsf@kernel.org>
On Mon, Jun 29, 2026 at 12:16:30PM +0530, Aneesh Kumar K.V wrote:
> >> Thinking about this more, I guess we should mark the swiotlb as
> >> cc_shared only with CC_ATTR_GUEST_MEM_ENCRYPT instead of
> >> CC_ATTR_MEM_ENCRYPT as we have below.
> >
> > The name cc_shared should be used for GUEST scenarios only.
> >
> > I guess there is some merit in keeping swiotlb using "decrypted" to
> > mean it usinig pgprot_decrypted and set_memory_decyped() which AMD
> > gives meaning to on both host and guest.
>
> Are you suggesting to change the struct io_tlb_mem::cc_shared back to
> struct io_tlb_mem::unencrypted?.
Yes
> > IDK what AMD should do on the host by default. I guess it should setup
> > a swiotlb pool of low dma addrs "unencrypted", but not "cc_shared"?
> >
>
> If by low DMA address you mean using an address with the C-bit
> cleared.
Yes
> The current code already does this and uses the swiotlb pool correctly
> on SME.
Well, through the force_dma_unencrypted() hack...
> The challenge arises when we want to force SWIOTLB
> bouncing even for devices that can handle encrypted DMA addresses (more
> on that below). For such a config force_dma_uencrypted(dev) will return
> false and swiotlb will be marked cc_shared/decrypted = true; This trip
> the new check we added.
Yes, because cc_shared (guest) and unencrypted (host) are very
different things and we've mixed them:
> if (unlikely(mem->cc_shared != force_dma_unencrypted(dev)))
I'm aruging force_dma_unencrypted should mean cc_shared and be
guest_only, but the SME hack breaks this.
> We can also do
>
> if (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)) {
> /* swiotlb pool is incorrect for this device */
> if (unlikely(mem->cc_shared != force_dma_unencrypted(dev)))
> return (phys_addr_t)DMA_MAPPING_ERROR;
>
> /* Force attrs to match the kind of memory in the pool */
> if (mem->cc_shared)
> *attrs |= DMA_ATTR_CC_SHARED;
> else
> *attrs &= ~DMA_ATTR_CC_SHARED;
> } else {
> /*
> * Host memory encryption where device requires an
> * unencrypted dma_addr_t due to dma mask limit
> */
> if (force_dma_unencrypted(dev))
> *attrs |= DMA_ATTR_CC_SHARED;
> else
> *attrs &= ~DMA_ATTR_CC_SHARED;
> }
If we do this I would like to split the force_dma_.. functions into
guest and host, ie force_dma_cc_shared() and force_host_decrypted()
To make it clear there are two very different things here.
> Here I see value in having DMA_ATTR_UNENCRYPTED. The question is do we
> need to split this into two flags and introduce the resulting code
> duplication.
The external flag name should be DMA_ATTR_CC_SHARED and only used on
CC guest. Internally that turns into using set_memory_decrypted()
which works on guest and host for AMD. I don't know how to make the
host only case clearer and still keep the code efficient..
> > The dma api has to detect, after the driver sets the dma limit, that
> > none of system memory is usable when:
> > - The direct path is being used
> > - phys to dma for 0 is outside the dma limit
> >
> > Then it should assume the arch has setup a swiotlb pool for it to use
> > to fix the high memory problem.
> >
> > Similar hackery would be needed in the dma alloc path to know that
> > decrypted can be used to fix the high memory problem like for GUEST.
> >
> > I guess some 'dev_cannot_reach_memory(dev)' sort of test in a
> > few key places? Setup with a static branch to be a nop on everything
> > but AMD, compiled out on every other arch.
> >
>
> If we are not able to reach the memory because of the memory encryption
> bit, then isn't dev_cannot_reach_memory(dev) the same as
> force_dma_unencrypted(dev)? If so, that is how it is already done.
Sort of yes, but it is properly named to its purpose and not confused
with what should be a guest-only function.
> x86/dma: Disable forced SWIOTLB bouncing for SME IOMMU passthrough
Maybe as a crutch to get this series merged..
Jason
^ permalink raw reply
* Re: [PATCH V2] powerpc/syscall: Fix seccomp errno handling with GENERIC_ENTRY
From: Michal Suchánek @ 2026-06-30 17:19 UTC (permalink / raw)
To: Mukesh Kumar Chaurasiya (IBM)
Cc: maddy, mpe, npiggin, chleroy, mkchauras, sshegde, ryan.roberts,
ruanjinjie, linuxppc-dev, linux-kernel
In-Reply-To: <20260629182946.419552-1-mkchauras@gmail.com>
On Mon, Jun 29, 2026 at 11:59:46PM +0530, Mukesh Kumar Chaurasiya (IBM) wrote:
> After enabling GENERIC_ENTRY on PowerPC, seccomp filters using
> SCMP_ACT_ERRNO without an explicit errnoRet value return ENOSYS
> (Function not implemented) instead of the expected EPERM (Operation
> not permitted).
>
> The issue occurs in system_call_exception() when syscall_enter_from_user_mode()
> returns -1 to indicate the syscall should be skipped (e.g., blocked by seccomp).
> The current code treats this -1 as a syscall number and compares it against
> NR_syscalls. Since -1 is greater than NR_syscalls,
> the code incorrectly returns -ENOSYS, overwriting the errno that seccomp
> already set via syscall_set_return_value().
>
> The generic entry code in syscall_trace_enter() calls __secure_computing(),
> which sets the appropriate errno in regs->gpr[3] and returns -1 to signal
> that the syscall should be skipped. However, the PowerPC syscall handler
> was not checking for this -1 return value before validating the syscall
> number.
>
> Fix this by explicitly checking if syscall_enter_from_user_mode() returns
> -1 and returning the value already set in regs->gpr[3] (the errno from
> seccomp) before performing the syscall number validation.
>
> Also Move the syscall_enter_from_user_mode() call and the seccomp/ptrace
> skip check to after the NR_syscalls bounds check.
>
> When syscall -1 was passed, the r0 == -1L check would trigger before
> the NR_syscalls check, causing syscall_get_error() to return 0 instead
> of -ENOSYS. This resulted in a silent success (ret=0, errno=0) instead
> of the expected ENOSYS error.
>
> By moving syscall_enter_from_user_mode() after the bounds check, an
> initial syscall number of -1 is correctly rejected with -ENOSYS first.
> The seccomp/ptrace skip path still works correctly for valid syscall
> numbers that get overridden to -1 by seccomp or ptrace.
>
> This aligns PowerPC's behavior with other architectures using GENERIC_ENTRY
> and restores correct seccomp errno handling.
>
> Fixes: bee25f97ad24 ("powerpc: Enable GENERIC_ENTRY feature")
> Reported-by: Michal Suchánek <msuchanek@suse.de>
Tested-by: Michal Suchánek <msuchanek@suse.de>
Reviewed-by: Michal Suchánek <msuchanek@suse.de>
Thanks
Michal
> Closes: https://lore.kernel.org/all/ajpp-_XnbF3UTM_E@kunlun.suse.cz/
> Signed-off-by: Mukesh Kumar Chaurasiya (IBM) <mkchauras@gmail.com>
> ---
>
> v1 -> v2:
> - Fix issues in the previous fix (Michal)
> v1: https://lore.kernel.org/all/20260624171520.772408-1-mkchauras@gmail.com
>
> arch/powerpc/kernel/syscall.c | 7 ++++++-
> 1 file changed, 6 insertions(+), 1 deletion(-)
>
> diff --git a/arch/powerpc/kernel/syscall.c b/arch/powerpc/kernel/syscall.c
> index a9da2af6efa8..36d73933a311 100644
> --- a/arch/powerpc/kernel/syscall.c
> +++ b/arch/powerpc/kernel/syscall.c
> @@ -20,7 +20,6 @@ notrace long system_call_exception(struct pt_regs *regs, unsigned long r0)
> syscall_fn f;
>
> add_random_kstack_offset();
> - r0 = syscall_enter_from_user_mode(regs, r0);
>
> if (unlikely(r0 >= NR_syscalls)) {
> if (unlikely(trap_is_unsupported_scv(regs))) {
> @@ -31,6 +30,12 @@ notrace long system_call_exception(struct pt_regs *regs, unsigned long r0)
> return -ENOSYS;
> }
>
> + r0 = syscall_enter_from_user_mode(regs, r0);
> +
> + /* Seccomp or ptrace may have set return value, skip syscall */
> + if (unlikely(r0 == -1L))
> + return syscall_get_error(current, regs);
> +
> /* May be faster to do array_index_nospec? */
> barrier_nospec();
>
> --
> 2.54.0
>
^ permalink raw reply
* Re: [PATCH v6 00/20] dma-mapping: Use DMA_ATTR_CC_SHARED through direct, pool and swiotlb paths
From: Jason Gunthorpe @ 2026-06-30 16:18 UTC (permalink / raw)
To: Alexey Kardashevskiy
Cc: Aneesh Kumar K.V, Catalin Marinas, iommu, linux-arm-kernel,
linux-kernel, linux-coco, Robin Murphy, Marek Szyprowski,
Will Deacon, Marc Zyngier, Steven Price, Suzuki K Poulose,
Jiri Pirko, Mostafa Saleh, Petr Tesarik, Dan Williams, Xu Yilun,
linuxppc-dev, linux-s390, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Alexander Gordeev,
Gerald Schaefer, Heiko Carstens, Vasily Gorbik,
Christian Borntraeger, Sven Schnelle, x86
In-Reply-To: <9f20ce61-1edd-411e-a7c3-be541fb89cb4@amd.com>
On Mon, Jun 22, 2026 at 10:58:23AM +1000, Alexey Kardashevskiy wrote:
> > I think it was a big mistake for the AMD SME stuff to overload the
> > decrypted/encrypted CC stuff which should mean shared/private in a
> > guest context to also mean things about physical memory encryption in
> > the host. It is really confusing.
>
> It is a bit in the PTE which says "encrypted", what do you mean by overloaded?...
Encrypted meaning I'm using DRAM encryption on the host and Encrypted
meaning this page is private and inaccessible to the hypervisor are
very different things with very different requirements and is
confusing they have been overloaded in Linux :(
> > The SME side is just a bad arch choice, the real world doesn't work
> > well if you set high address bits in your dma_addr_t. I think AMD
> > needs to use those restricted swiotlb pool where it allocates this
> > very special "SME Disabled" memory that will have a low
> > dma_addr_t.
>
> The generic __init iommu_subsys_init(void) calls
> iommu_set_default_translated() if CC_ATTR_MEM_ENCRYPT (==force the
> use of IOMMU) and eliminates the bouncing by default, pretty
> much.
Sure, I know, it is a gross solution to a self inflict error.
> We (AMD) do not really want to force Cbit in DMA handles and
> it is not happening unless "iommu=pt".
Lots of real HW won't work will because of this, so yeah you pretty
much have to. But also there is HW that is fine, like you can use a
mlx5 device and it will handle the C bit just fine.
It is pretty hacky to globally force the iommu mode because some
devices end up not working.
> > Then alloc and bouncing will get memory with a suitable
> > dma_addr_t. This has nothing to do with force_dma_unencrypted() which
> > is only a CC guest concept and nothing else in the OS should ever
> > touch decrypted memory.
>
> True.
>
> Although, with "iommu=pt" enabled, dma handles from swiotlb should
> not have Cbit so these swiotlb pages have to be unencrypted.
That is how it should ideall work, in this case the purpose of the
swiotlb pool is to provide low dma address memory because the device
cannot reach the normal linux dram addresses.
> As you mentioned in another mail in the thread, DMAing to
> unencrypted memory with mem_encrypt=on make no sense security
> wise.
Yes, pretty much.
> May be enforce either mem_encrypt=on or iommu=pt is allowed at
> the same time but not both? I am worried though that some weirdo
> still has a use case for it.
Arguably it should be done per device. The problem is the iommu layer
doesn't know what the dma mask is until the driver binds so it can't
detect a device that is unable to reach any dram and switch away from
identity automatically. That would be much cleaner.
> > > I am looking for a way to set up my "sev-guest" device such as when
> >
> > Whats a "sev-guest" device?
>
> It is a platform device, presented in SNP VMs as /dev/sev-guest and
> the guest userspace calls ioctls on it when it needs VM attestation
> report/certificates/etc.
>
> The sev-guest driver makes calls to the HV (GHCB protocol) to:
> 1) get report/certificates/measurements from the HV <- this is done
> via shared memory as the HV writes to it;
> 2) asks the HV to get the digests from the PSP <- this is done via
> encrypted memory (buuuut it is software encrypted and as far as the
> hw is concerned - it is shared - no Cbit, no RMP - these buffers
> contain plaintext headers of the PSP requests and cyphertext of the
> request/response body).
Ok, but here you have overloaded the word encrypted again :( Decrypted
memory containing ciphertext I think you mean
> > > dma_alloc_attrs(snp_dev->dev,...) happens, it allocates a page from
> > > the shared swiotlb pool (with no actual bouncing) and there is no
> > > obvious way to trick the DMA layer into doing that.
> >
> > Why do you need this?
>
> /dev/sev-guest uses only shared memory (from the HW standpoint), and
> it is normally lot less than 1MB. If hugepages are used, then today
> it allocates 4K pages (they come encrypted and likely backed with a
> 2M page), the driver converts them to shared to make that GHCB
> call. The conversion smashes backing 2M page to 4K pages (+RMP
> +IOPDE as there is possible ongoing DMA), which is a problem (I have
> mentioned it as "TMPM" before - a hw/fw helper to do the smashing).
Okay, but this has nothing to do with sev-guest at all, and should not
be solved uniquely for it.
The DMA API in general has a problem spraying allocations all over
system memory and fragmenting the RMP/GPT/etc and yes it needs a
solution, but it should be entirely in the DMA API and have no
special involvment with sev-guest. sev-guest should just make coherent
allocations and use them in the normal way.
> The idea here is that if swiotlb is already shared, the sev-guest
> could use that memory pool.
dma_alloc_coherent using the swiotlb pool instead of allocating and
converting in general is a reasonable proposal, IMHO. Again, nothing
to do with sev-guest.
Jason
^ permalink raw reply
* Re: [PATCH v6 03/20] dma-direct: use DMA_ATTR_CC_SHARED in alloc/free paths
From: Jason Gunthorpe @ 2026-06-30 16:02 UTC (permalink / raw)
To: Alexey Kardashevskiy
Cc: Aneesh Kumar K.V (Arm), iommu, linux-arm-kernel, linux-kernel,
linux-coco, Robin Murphy, Marek Szyprowski, Will Deacon,
Marc Zyngier, Steven Price, Suzuki K Poulose, Catalin Marinas,
Jiri Pirko, Mostafa Saleh, Petr Tesarik, Dan Williams, Xu Yilun,
linuxppc-dev, linux-s390, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Alexander Gordeev,
Gerald Schaefer, Heiko Carstens, Vasily Gorbik,
Christian Borntraeger, Sven Schnelle, x86, Jiri Pirko,
Michael Kelley, Cheloha, Scott
In-Reply-To: <25155bd6-4348-4aa8-ba70-0a882fc84db9@amd.com>
On Thu, Jun 18, 2026 at 12:39:21PM +1000, Alexey Kardashevskiy wrote:
>
>
> On 18/6/26 01:41, Jason Gunthorpe wrote:
> > On Wed, Jun 17, 2026 at 10:50:39AM +1000, Alexey Kardashevskiy wrote:
> > > > @@ -193,16 +193,31 @@ void *dma_direct_alloc(struct device *dev, size_t size,
> > > > dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs)
> > > > {
> > > > bool remap = false, set_uncached = false;
> > > > - bool mark_mem_decrypt = true;
> > > > + bool mark_mem_decrypt = false;
> > > > struct page *page;
> > > > void *ret;
> > > > + /*
> > > > + * DMA_ATTR_CC_SHARED is not a caller-visible dma_alloc_*()
> > > > + * attribute. The direct allocator uses it internally after it has
> > > > + * decided that the backing pages must be shared/decrypted, so the
> > > > + * rest of the allocation path can consistently select DMA addresses,
> > > > + * choose compatible pools and restore encryption on free.
> > >
> > > Why this limit?
> > >
> > > Context: I am looking for a memory pool for a few shared pages (to
> > > do some guest<->host communication), SWIOTLB seems like the right
> > > fit but swiotlb_alloc() is not exported and
> > > dma_direct_alloc(DMA_ATTR_CC_SHARED) is not allowed. Thanks,
> >
> > Then setup your struct device so that the DMA API knows the
> > guest<->host channel requires unecrypted and it will work correctly.
> >
> > I think this is a reasonable API to use for that, and I was just
> > advocating that hyperv should be using it too.
> >
> > But it all relies on a properly setup struct device.
>
> Sounds good but how do I do that in practice?
I think we haven't got there yet, I understood Dan's plan was to add a
bit in the struct device that signals if the device must be
unencrypted or can support all memory.
Currently the dma api assumes all devices must have unencrypted by
default so it should be fine already, shouldn't it?
> not externally available so I'll have to trick the DMA layer into
> using SWIOTLB (which is still all shared, right?) as I specifically
> want to skip page conversions. Setting low DMA mask won't guarantee
> that the DMA layer won't allocate a page outside of SWIOTLB and
> convert it. Manually do
Why so particular? Any address that satisifies the constraints should
be good enough?
Jason
^ permalink raw reply
* Re: [PATCH v17 00/10] arm64/riscv: Add support for crashkernel CMA reservation
From: Mike Rapoport @ 2026-06-30 15:49 UTC (permalink / raw)
To: corbet, skhan, catalin.marinas, will, chenhuacai, kernel, maddy,
mpe, npiggin, chleroy, pjw, palmer, aou, alex, tglx, mingo, bp,
dave.hansen, hpa, robh, saravanak, akpm, baoquan.he,
pasha.tatashin, pratyush, ruirui.yang, rdunlap, peterz, feng.tang,
dapeng1.mi, elver, enelsonmoore, kuba, ebiggers, lirongqing,
leitao, kees, coxu, cfsworks, jbohac, osandov, ryan.roberts,
tangyouling, sourabhjain, ritesh.list, gaohan, david, wangruikang,
muchun.song, vishal.moola, junhui.liu, guoren, namcao,
rick.p.edgecombe, djbw, liaoyuanhong, fuqiang.wang,
vishal.l.verma, chenjiahao16, x86, linux-doc, linux-kernel,
linux-arm-kernel, loongarch, linuxppc-dev, linux-riscv,
devicetree, kexec, Jinjie Ruan
In-Reply-To: <20260629094746.191843-1-ruanjinjie@huawei.com>
On Mon, 29 Jun 2026 17:47:36 +0800, Jinjie Ruan wrote:
> arm64/riscv: Add support for crashkernel CMA reservation
>
> The crash memory allocation, and the exclude of crashk_res, crashk_low_res
> and crashk_cma memory are almost identical across different architectures,
> This patch set handle them in crash core in a general way, which eliminate
> a lot of duplication code.
>
> [...]
Applied to crashkernel-cma branch of liveupdate/linux.git tree, thanks!
[01/10] riscv: kexec_file: Fix crashk_low_res not exclude bug
commit: 5fc6e7d45373571d03cd04fd4c6069c0a97fa75a
[02/10] powerpc/crash: sort crash memory ranges before preparing elfcorehdr
commit: 202b5de3ec34317d6a8944239fe227ef8f6853ec
[03/10] crash: Add crash_prepare_headers() to exclude crash kernel memory
commit: 5beabef0cffaa1ea6e27e85dbd526b7a28e0e7c7
[04/10] arm64: kexec_file: Use crash_prepare_headers() helper to simplify code
commit: 201b561cbc6c594aa1ef718a1204dd45720d96dd
[05/10] x86/crash: Use crash_prepare_headers() helper to simplify code
commit: a8f09f2a7485956c3653696f6cb94faadd243645
[06/10] riscv: kexec_file: Use crash_prepare_headers() helper to simplify code
commit: 7b078a0aa2753dcf5cae88e5349a25ac39998a9b
[07/10] LoongArch: kexec_file: Use crash_prepare_headers() helper to simplify code
commit: 51ed0950adf05c99e393818887a2616dd4ab3e34
[08/10] powerpc/kexec_file: Use crash_exclude_core_ranges() helper
commit: b0e06c5a30742bc6bc8523fe9c71c1a043a7661d
[09/10] arm64: kexec_file: Add support for crashkernel CMA reservation
commit: c12c63d05dfe7088aa8b1b872fe261e25537c823
[10/10] riscv: kexec_file: Add support for crashkernel CMA reservation
commit: a037fe3c58ed489ca8ad8531bd7e16b90a84badc
tree: https://git.kernel.org/pub/scm/linux/kernel/git/liveupdate/linux
branch: crashkernel-cma
--
Sincerely yours,
Mike.
^ permalink raw reply
* Re: [PATCH V2] ASoC: fsl_audmix: rework runtime PM handling in probe
From: Mark Brown @ 2026-06-30 11:26 UTC (permalink / raw)
To: shengjiu.wang, Xiubo.Lee, festevam, nicoleotsuka, lgirdwood,
perex, tiwai, linux-sound, linuxppc-dev, linux-kernel,
shengjiu.wang
In-Reply-To: <20260618023818.31618-1-shengjiu.wang@oss.nxp.com>
On Thu, 18 Jun 2026 10:38:18 +0800, shengjiu.wang@oss.nxp.com wrote:
> ASoC: fsl_audmix: rework runtime PM handling in probe
Applied to
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-7.3
Thanks!
[1/1] ASoC: fsl_audmix: rework runtime PM handling in probe
https://git.kernel.org/broonie/sound/c/3359ba93d01a
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
^ permalink raw reply
* [PATCH] powerpc/dt_cpu_ftrs: Avoid separate strlen() in scan_callback()
From: Thorsten Blum @ 2026-06-30 15:46 UTC (permalink / raw)
To: Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
Christophe Leroy (CS GROUP)
Cc: Thorsten Blum, linuxppc-dev, linux-kernel
Use the return value of strscpy() when copying the display name instead
of checking the source string length with strlen() first.
Keep dt_cpu_name static but move it into dt_cpu_ftrs_scan_callback(),
where it is assigned.
Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
---
arch/powerpc/kernel/dt_cpu_ftrs.c | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/arch/powerpc/kernel/dt_cpu_ftrs.c b/arch/powerpc/kernel/dt_cpu_ftrs.c
index 3af6c06af02f..8d8e94e4d2bc 100644
--- a/arch/powerpc/kernel/dt_cpu_ftrs.c
+++ b/arch/powerpc/kernel/dt_cpu_ftrs.c
@@ -90,8 +90,6 @@ static void __restore_cpu_cpufeatures(void)
init_pmu_registers();
}
-static char dt_cpu_name[64];
-
static struct cpu_spec __initdata base_cpu_spec = {
.cpu_name = NULL,
.cpu_features = CPU_FTRS_DT_CPU_BASE,
@@ -1069,6 +1067,7 @@ static int __init count_cpufeatures_subnodes(unsigned long node,
static int __init dt_cpu_ftrs_scan_callback(unsigned long node, const char
*uname, int depth, void *data)
{
+ static char dt_cpu_name[64];
const __be32 *prop;
int count, i;
u32 isa;
@@ -1106,10 +1105,8 @@ static int __init dt_cpu_ftrs_scan_callback(unsigned long node, const char
}
prop = of_get_flat_dt_prop(node, "display-name", NULL);
- if (prop && strlen((char *)prop) != 0) {
- strscpy(dt_cpu_name, (char *)prop, sizeof(dt_cpu_name));
+ if (prop && strscpy(dt_cpu_name, (char *)prop) != 0)
cur_cpu_spec->cpu_name = dt_cpu_name;
- }
cpufeatures_setup_finished();
^ permalink raw reply related
* Re: [PATCH v16 18/18] arm64: vdso: Expose sigreturn address on vdso to the kernel
From: Thomas Weißschuh @ 2026-06-30 15:32 UTC (permalink / raw)
To: Jinjie Ruan
Cc: oleg, richard.henderson, mattst88, linmag7, linux,
catalin.marinas, will, kees, guoren, chenhuacai, kernel, geert,
tsbogend, James.Bottomley, deller, maddy, mpe, npiggin, chleroy,
pjw, palmer, aou, alex, hca, gor, agordeev, borntraeger, svens,
ysato, dalias, glaubitz, richard, anton.ivanov, johannes, luto,
tglx, mingo, bp, dave.hansen, hpa, chris, jcmvbkbc, peterz, wad,
thuth, mark.rutland, ada.coupriediaz, kevin.brodsky, linusw,
yeoreum.yun, song, james.morse, anshuman.khandual, broonie,
liqiang01, pengcan, ryan.roberts, yangtiezhu, sshegde, mchauras,
austin.kim, jchrist, arnd, sohil.mehta, andrew.cooper3, jgross,
kas, x86, linux-alpha, linux-kernel, linux-arm-kernel, linux-mm,
linux-csky, loongarch, linux-m68k, linux-mips, linux-parisc,
linuxppc-dev, linux-riscv, linux-s390, linux-sh, linux-um
In-Reply-To: <20260629130616.642022-19-ruanjinjie@huawei.com>
On Mon, Jun 29, 2026 at 09:06:16PM +0800, Jinjie Ruan wrote:
> Syscall User Dispatch (SUD) requires the signal trampoline code to not be
> intercepted. This is necessary to support returning with a locked selector
> while avoiding infinite recursion back into the signal handler.
>
> Implement arch_syscall_is_vdso_sigreturn() for arm64 to support this
> exclusion mechanism. For native 64-bit tasks, it checks whether the current
> PC matches the 'svc #0' instruction inside the vDSO sigreturn trampoline.
>
> SYM_CODE_START(__kernel_rt_sigreturn)
> mov x8, #__NR_rt_sigreturn
> svc #0
> SYM_CODE_END(__kernel_rt_sigreturn)
>
> For COMPAT tasks, it verifies if the instruction falls within
> the architecture's 'sigpage' range, allowing the kernel to safely bypass
> dispatching syscalls originating from these areas back to userspace.
>
> Suggested-by: Kevin Brodsky <kevin.brodsky@arm.com>
> Suggested-by: kemal <kmal@cock.li>
> Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
> ---
> arch/arm64/include/asm/elf.h | 1 +
> arch/arm64/kernel/vdso.c | 16 ++++++++++++++++
> 2 files changed, 17 insertions(+)
>
> diff --git a/arch/arm64/include/asm/elf.h b/arch/arm64/include/asm/elf.h
> index d2779d604c7b..f43d4dea916a 100644
> --- a/arch/arm64/include/asm/elf.h
> +++ b/arch/arm64/include/asm/elf.h
> @@ -185,6 +185,7 @@ do { \
> struct linux_binprm;
> extern int arch_setup_additional_pages(struct linux_binprm *bprm,
> int uses_interp);
> +extern bool arch_syscall_is_vdso_sigreturn(struct pt_regs *regs);
A header for that?
> /* 1GB of VA */
> #ifdef CONFIG_COMPAT
> diff --git a/arch/arm64/kernel/vdso.c b/arch/arm64/kernel/vdso.c
> index 592dd8668de4..5a0314a3c26e 100644
> --- a/arch/arm64/kernel/vdso.c
> +++ b/arch/arm64/kernel/vdso.c
> @@ -343,3 +343,19 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
>
> return ret;
> }
> +
> +bool arch_syscall_is_vdso_sigreturn(struct pt_regs *regs)
> +{
> + unsigned long sigtramp;
> +
> +#ifdef CONFIG_COMPAT
> + if (is_compat_task()) {
> + unsigned long sigpage = (unsigned long)current->mm->context.sigpage;
> +
> + return regs->pc >= sigpage && regs->pc < (sigpage + PAGE_SIZE);
> + }
> +#endif
> + sigtramp = (unsigned long)VDSO_SYMBOL(current->mm->context.vdso, sigtramp);
> +
> + return regs->pc == (sigtramp + 8);
Instead of hardcoding 'sigtramp + 8' you could add a new label to the 'svc #0'
instruction and use that with VDSO_SYMBOL().
Thomas
^ permalink raw reply
* Re: [PATCH v2 10/19] pmdomain: imx: use platform_device_set_of_node()
From: Frank Li @ 2026-06-30 15:27 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Lee Jones, Mark Brown, Thierry Reding, Sebastian Hesselbarth,
Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Srinivas Kandagatla, Greg Kroah-Hartman, Vinod Koul,
Rafael J. Wysocki, Danilo Krummrich, Rob Herring, Saravana Kannan,
Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
Christophe Leroy (CS GROUP), Andi Shyti, Andy Shevchenko,
Joerg Roedel, Will Deacon, Robin Murphy, Doug Berger,
Florian Fainelli, Broadcom internal kernel review list,
Ulf Hansson, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Matthew Brost, Thomas Hellström, Rodrigo Vivi,
David Airlie, Simona Vetter, Peter Chen, Paul Cercueil, Bin Liu,
Philipp Zabel, Maximilian Luz, Hans de Goede, Ilpo Järvinen,
Krzysztof Kozlowski, Benjamin Herrenschmidt, brgl, linux-kernel,
netdev, linux-arm-msm, linux-sound, driver-core, devicetree,
linuxppc-dev, linux-i2c, iommu, linux-pm, imx, linux-arm-kernel,
intel-xe, dri-devel, linux-usb, linux-mips, platform-driver-x86
In-Reply-To: <20260629-pdev-fwnode-ref-v2-10-8abe2513f96e@oss.qualcomm.com>
On Mon, Jun 29, 2026 at 11:12:33AM +0200, Bartosz Golaszewski wrote:
>
> Ahead of reworking the reference counting logic for platform devices,
> encapsulate the assignment of the OF node for dynamically allocated
> platform devices with the provided helper.
>
> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> drivers/pmdomain/imx/gpc.c | 3 +--
> 1 file changed, 1 insertion(+), 2 deletions(-)
>
> diff --git a/drivers/pmdomain/imx/gpc.c b/drivers/pmdomain/imx/gpc.c
> index 42e50c9b4fb9ffb96a20a462d4eb5168942a893c..abca5f449a226fbae4213926e1395c413160c950 100644
> --- a/drivers/pmdomain/imx/gpc.c
> +++ b/drivers/pmdomain/imx/gpc.c
> @@ -487,8 +487,7 @@ static int imx_gpc_probe(struct platform_device *pdev)
> domain->ipg_rate_mhz = ipg_rate_mhz;
>
> pd_pdev->dev.parent = &pdev->dev;
> - pd_pdev->dev.of_node = of_node_get(np);
> - pd_pdev->dev.fwnode = of_fwnode_handle(np);
> + platform_device_set_of_node(pd_pdev, np);
>
> ret = platform_device_add(pd_pdev);
> if (ret) {
>
> --
> 2.47.3
>
>
^ permalink raw reply
* Re: [PATCH 01/13] mm: introduce vma_flags_can_grow() and vma_can_grow()
From: Zi Yan @ 2026-06-30 15:09 UTC (permalink / raw)
To: Lorenzo Stoakes
Cc: Andrew Morton, Thomas Bogendoerfer, Madhavan Srinivasan,
Michael Ellerman, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Lucas Stach,
Inki Dae, Seung-Woo Kim, Kyungmin Park, Krzysztof Kozlowski,
Peter Griffin, Jani Nikula, Joonas Lahtinen, Rodrigo Vivi,
Tvrtko Ursulin, Rob Clark, Dmitry Baryshkov, Lyude Paul,
Danilo Krummrich, Tomi Valkeinen, Sandy Huang, Heiko Stübner,
Andy Yan, Thierry Reding, Mikko Perttunen, Jonathan Hunter,
Gerd Hoffmann, Dmitry Osipenko, Zack Rusin, Matthew Brost,
Thomas Hellstrom, Oleksandr Andrushchenko, Helge Deller,
Benjamin LaHaise, Alexander Viro, Christian Brauner, Muchun Song,
Oscar Salvador, David Hildenbrand, Baolin Wang, Liam R . Howlett,
Nico Pache, Ryan Roberts, Dev Jain, Barry Song, Lance Yang,
Hugh Dickins, Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan,
Michal Hocko, Jann Horn, Pedro Falcato, Kees Cook,
Jaroslav Kysela, Takashi Iwai, linux-mips, linux-kernel,
linuxppc-dev, dri-devel, etnaviv, linux-arm-kernel,
linux-samsung-soc, intel-gfx, linux-arm-msm, freedreno, nouveau,
linux-rockchip, linux-tegra, virtualization, intel-xe, xen-devel,
linux-fbdev, linux-aio, linux-fsdevel, linux-mm, linux-sound
In-Reply-To: <akNucoP3eaDN2_Vz@lucifer>
On Tue Jun 30, 2026 at 3:38 AM EDT, Lorenzo Stoakes wrote:
> On Mon, Jun 29, 2026 at 04:26:18PM -0400, Zi Yan wrote:
>> On Mon Jun 29, 2026 at 3:25 PM EDT, Lorenzo Stoakes wrote:
>> > These test whether the VMA has stack sematics, i.e. is able to grow upwards
>> > or downwards depending on the architecture.
>> >
>> > In order to account for arches which do not support upward-growing stacks,
>> > introduce VMA_GROWSUP whose definition depends on the architecture
>> > supporting it, and use vma_flags_test_single_mask() in vma_flags_can_grow()
>> > to account for this.
>> >
>> > Update the VMA userland tests to reflect the changes
>> >
>> > No functional change intended.
>> >
>> > Signed-off-by: Lorenzo Stoakes <ljs@kernel.org>
>> > ---
>> > include/linux/mm.h | 21 ++++++++++++++++++---
>> > tools/testing/vma/include/dup.h | 4 ++++
>> > 2 files changed, 22 insertions(+), 3 deletions(-)
>> >
>> > diff --git a/include/linux/mm.h b/include/linux/mm.h
>> > index 868b2334bff3..cf7df1569052 100644
>> > --- a/include/linux/mm.h
>> > +++ b/include/linux/mm.h
>> > @@ -472,6 +472,7 @@ enum {
>> > #define VM_SAO INIT_VM_FLAG(SAO)
>> > #elif defined(CONFIG_PARISC)
>> > #define VM_GROWSUP INIT_VM_FLAG(GROWSUP)
>> > +#define VMA_GROWSUP mk_vma_flags(VMA_GROWSUP_BIT)
>> > #elif defined(CONFIG_SPARC64)
>> > #define VM_SPARC_ADI INIT_VM_FLAG(SPARC_ADI)
>> > #define VM_ARCH_CLEAR INIT_VM_FLAG(ARCH_CLEAR)
>> > @@ -483,6 +484,7 @@ enum {
>> > #endif
>> > #ifndef VM_GROWSUP
>> > #define VM_GROWSUP VM_NONE
>> > +#define VMA_GROWSUP EMPTY_VMA_FLAGS
>> > #endif
>> > #ifdef CONFIG_ARM64_MTE
>> > #define VM_MTE INIT_VM_FLAG(MTE)
>> > @@ -1563,11 +1565,24 @@ static inline bool vma_is_initial_stack(const struct vm_area_struct *vma)
>> > vma->vm_end >= vma->vm_mm->start_stack;
>> > }
>> >
>> > -static inline bool vma_is_temporary_stack(const struct vm_area_struct *vma)
>> > +static inline bool vma_flags_can_grow(const vma_flags_t *flags)
>> > {
>> > - int maybe_stack = vma->vm_flags & (VM_GROWSDOWN | VM_GROWSUP);
>> > + if (vma_flags_test_single_mask(flags, VMA_GROWSUP))
>> > + return true;
>> > + if (vma_flags_test(flags, VMA_GROWSDOWN_BIT))
>> > + return true;
>> > +
>> > + return false;
>> > +}
>> >
>> > - if (!maybe_stack)
>> > +static inline bool vma_can_grow(const struct vm_area_struct *vma)
>> > +{
>> > + return vma_flags_can_grow(&vma->flags);
>>
>> Would it save vma_flags_can_grow() if we do below?
>>
>> return vma_test(vma, VMA_GROWSDOWN_BIT) || vma_test_single_mask(vma, VMA_GROWSUP);
>>
>> I find these two functions when I am reading mm.h.
>
> Yeah but we require vma_flags_can_grow() for code in mmap.c, the majority of
> checks of this have only vma_flags_t to work with not a VMA :)
>
Got it. I think I need to finish this series. :)
>>
>> > +}
>> > +
>> > +static inline bool vma_is_temporary_stack(const struct vm_area_struct *vma)
>> > +{
>> > + if (!vma_can_grow(vma))
>> > return false;
>> >
>> > if ((vma->vm_flags & VM_STACK_INCOMPLETE_SETUP) ==
>> > diff --git a/tools/testing/vma/include/dup.h b/tools/testing/vma/include/dup.h
>> > index 5d7d0afd7765..6f5bcd7fbcd8 100644
>> > --- a/tools/testing/vma/include/dup.h
>> > +++ b/tools/testing/vma/include/dup.h
>> > @@ -245,8 +245,10 @@ enum {
>> > #define VM_STACK INIT_VM_FLAG(STACK)
>> > #ifdef CONFIG_STACK_GROWS_UP
>> > #define VM_STACK_EARLY INIT_VM_FLAG(STACK_EARLY)
>> > +#define VMA_STACK_EARLY mk_vma_flags(VMA_STACK_EARLY_BIT)
>> > #else
>> > #define VM_STACK_EARLY VM_NONE
>> > +#define VMA_STACK_EARLY EMPTY_VMA_FLAGS
>> > #endif
>> > #ifdef CONFIG_ARCH_HAS_PKEYS
>> > #define VM_PKEY_SHIFT ((__force int)VMA_HIGH_ARCH_0_BIT)
>> > @@ -315,6 +317,8 @@ enum {
>> >
>> > /* Bits set in the VMA until the stack is in its final location */
>> > #define VM_STACK_INCOMPLETE_SETUP (VM_RAND_READ | VM_SEQ_READ | VM_STACK_EARLY)
>> > +#define VMA_STACK_INCOMPLETE_SETUP append_vma_flags( \
>> > + VMA_STACK_EARLY, VMA_RAND_READ_BIT, VMA_SEQ_READ_BIT)
>> >
>> > #define TASK_EXEC_BIT ((current->personality & READ_IMPLIES_EXEC) ? \
>> > VM_EXEC_BIT : VM_READ_BIT)
>>
>> Why are VMA_STACK_EARLY and VMA_STACK_INCOMPLETE_SETUP added here but
>> not in mm.h?
>
> Yeah urgh oops my bad. It doesn't really break anything but I'll fix it if a
> respin is needed...
Sure.
--
Best Regards,
Yan, Zi
^ permalink raw reply
* [PATCH v3 6/8] powerpc64/kasan: Remove unreachable invalid range check in kasan_init_phys_region()
From: Sang-Heon Jeon @ 2026-06-30 15:04 UTC (permalink / raw)
To: rppt, Madhavan Srinivasan, Michael Ellerman
Cc: linux-mm, Sang-Heon Jeon, Christophe Leroy (CS GROUP),
linux-kernel, linuxppc-dev, Nicholas Piggin
In-Reply-To: <20260630150413.1718632-1-ekffu200098@gmail.com>
kasan_init() maps each memblock region with for_each_mem_range() and passes
it to kasan_init_phys_region(), which does nothing when start >= end.
for_each_mem_range() never returns an invalid range, so start < end always.
Therefore the start >= end check is unreachable, so remove it.
No functional change.
Signed-off-by: Sang-Heon Jeon <ekffu200098@gmail.com>
---
arch/powerpc/mm/kasan/init_book3e_64.c | 3 ---
arch/powerpc/mm/kasan/init_book3s_64.c | 3 ---
2 files changed, 6 deletions(-)
diff --git a/arch/powerpc/mm/kasan/init_book3e_64.c b/arch/powerpc/mm/kasan/init_book3e_64.c
index 0d3a73d6d4b0..0ed372fb8b09 100644
--- a/arch/powerpc/mm/kasan/init_book3e_64.c
+++ b/arch/powerpc/mm/kasan/init_book3e_64.c
@@ -68,9 +68,6 @@ static void __init kasan_init_phys_region(void *start, void *end)
unsigned long k_start, k_end, k_cur;
void *va;
- if (start >= end)
- return;
-
k_start = ALIGN_DOWN((unsigned long)kasan_mem_to_shadow(start), PAGE_SIZE);
k_end = ALIGN((unsigned long)kasan_mem_to_shadow(end), PAGE_SIZE);
diff --git a/arch/powerpc/mm/kasan/init_book3s_64.c b/arch/powerpc/mm/kasan/init_book3s_64.c
index dcafa641804c..ccfbfb894637 100644
--- a/arch/powerpc/mm/kasan/init_book3s_64.c
+++ b/arch/powerpc/mm/kasan/init_book3s_64.c
@@ -24,9 +24,6 @@ static void __init kasan_init_phys_region(void *start, void *end)
unsigned long k_start, k_end, k_cur;
void *va;
- if (start >= end)
- return;
-
k_start = ALIGN_DOWN((unsigned long)kasan_mem_to_shadow(start), PAGE_SIZE);
k_end = ALIGN((unsigned long)kasan_mem_to_shadow(end), PAGE_SIZE);
--
2.43.0
^ permalink raw reply related
* [PATCH v3 0/8] treewide: remove unnecessary invalid range checks in memblock iteration loops
From: Sang-Heon Jeon @ 2026-06-30 15:04 UTC (permalink / raw)
To: rppt, Albert Ou, Andrew Morton, Andrey Ryabinin, Catalin Marinas,
Huacai Chen, Madhavan Srinivasan, Michael Ellerman, Muchun Song,
Oscar Salvador, Palmer Dabbelt, Paul Walmsley, Russell King,
Will Deacon
Cc: linux-mm, Sang-Heon Jeon, Alexander Potapenko, Alexandre Ghiti,
Andrey Konovalov, Christophe Leroy (CS GROUP), David Hildenbrand,
Dmitry Vyukov, kasan-dev, linux-arm-kernel, linux-kernel,
linuxppc-dev, linux-riscv, loongarch, Nicholas Piggin,
Vincenzo Frascino, WANG Xuerui
The memblock API guarantees that for_each_mem_range() and
for_each_mem_pfn_range() never return an invalid range, meaning start is
always less than end.
Several memblock callers still have unnecessary invalid range checks in
their loop bodies, so remove them.
Patches 1-6 cover for_each_mem_range() callers. memblock never stores a
zero-size region, so the range it returns always has start < end. Some
callers apply __va() or __phys_to_virt() before comparing, but these keep
start < end too, so the check is unreachable.
Patches 7-8 cover for_each_mem_pfn_range() callers. __next_mem_pfn_range()
skips any region that contains no whole page, so it only ever returns
start_pfn < end_pfn and the check is unnecessary.
For reference, commit 36ca7f4be809 ("arm64: mm: Remove bogus stop
condition from map_mem() loop") did a similar cleanup in arm64 map_mem().
All these checks are in different trees, so I split the change into one
patch per arch/subsystem. The patches are independent and can be applied
separately.
---
Changes from v1 [1]
- Add review-by, tested-by tags from v1
- Add missing simliar patches(5,6) to patch series
- Change base to rppt/for-next
Changes from v2 [2]
- Remove unused local variable and braces
[1] https://lore.kernel.org/all/20260621145919.1453-1-ekffu200098@gmail.com/
[2] https://lore.kernel.org/all/20260629163736.1606688-1-ekffu200098@gmail.com/
---
Sang-Heon Jeon (8):
arm64: mm: remove unreachable invalid range check in
kasan_init_shadow()
LoongArch: remove unreachable invalid range check in kasan_init()
riscv: remove unreachable invalid range check in
create_linear_mapping_page_table()
riscv: remove unreachable invalid range check in kasan_init()
ARM: remove unreachable invalid range check in kasan_init()
powerpc64/kasan: Remove unreachable invalid range check in
kasan_init_phys_region()
mm: remove unnecessary empty range check in
early_calculate_totalpages()
mm/hugetlb: remove unnecessary empty range check in
hugetlb_bootmem_set_nodes()
arch/arm/mm/kasan_init.c | 6 ------
arch/arm64/mm/kasan_init.c | 3 ---
arch/loongarch/mm/kasan_init.c | 3 ---
arch/powerpc/mm/kasan/init_book3e_64.c | 3 ---
arch/powerpc/mm/kasan/init_book3s_64.c | 3 ---
arch/riscv/mm/init.c | 2 --
arch/riscv/mm/kasan_init.c | 3 ---
mm/hugetlb.c | 7 ++-----
mm/mm_init.c | 3 +--
9 files changed, 3 insertions(+), 30 deletions(-)
--
2.43.0
^ permalink raw reply
* [PATCH v1 18/18] ibmveth: Fix MQ RX poll and shutdown hangs after queue resize
From: Mingming Cao @ 2026-06-30 14:53 UTC (permalink / raw)
To: netdev
Cc: horms, bjking1, haren, ricklind, mmc, kuba, edumazet, pabeni,
linuxppc-dev, maddy, mpe, Dave Marquardt
In-Reply-To: <cover.1782758799.git.mmc@linux.ibm.com>
After aggressive ethtool -L cycling, PHYP can leave a VALID RX descriptor
with a correlator that no longer matches the per-queue buffer pools. Poll
treated this as fatal: ibmveth_rxq_get_buffer() WARNed and returned NULL
without advancing the ring, then restart_poll retried the same slot forever.
Advance past bad correlators instead of spinning: validate correlators
without WARN_ON, skip invalid slots in poll (count as invalid_buffers),
and advance the RX ring when remove_buffer_from_pool cannot map the
correlator. Rate-limit the bad correlator message.
Complete NAPI when the interface is down or napi_disable is pending so
ibmveth_cleanup_rx_interrupts() can finish. Do not restart_poll in that
window. Close keeps hypervisor IRQ disable before napi_disable (via
cleanup_rx_interrupts()).
Signed-off-by: Mingming Cao <mmc@linux.ibm.com>
Reviewed-by: Dave Marquardt <davemarq@linux.ibm.com>
---
drivers/net/ethernet/ibm/ibmveth.c | 76 ++++++++++++++++++++++--------
1 file changed, 57 insertions(+), 19 deletions(-)
diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c
index 50a332ab83fd..d7bf01271161 100644
--- a/drivers/net/ethernet/ibm/ibmveth.c
+++ b/drivers/net/ethernet/ibm/ibmveth.c
@@ -158,6 +158,25 @@ static inline int ibmveth_rxq_frame_length(struct ibmveth_adapter *adapter,
return be32_to_cpu(rxq->queue_addr[rxq->index].length);
}
+static inline bool
+ibmveth_rxq_correlator_valid(struct ibmveth_adapter *adapter, int queue_index,
+ u64 correlator)
+{
+ unsigned int pool = correlator >> 32;
+ unsigned int index = correlator & 0xffffffffUL;
+
+ return pool < IBMVETH_NUM_BUFF_POOLS &&
+ index < adapter->rx_buff_pool[queue_index][pool].size;
+}
+
+static inline void ibmveth_rxq_advance(struct ibmveth_rx_q *rxq)
+{
+ if (++rxq->index == rxq->num_slots) {
+ rxq->index = 0;
+ rxq->toggle = !rxq->toggle;
+ }
+}
+
static inline int ibmveth_rxq_csum_good(struct ibmveth_adapter *adapter,
int queue_index)
{
@@ -1284,17 +1303,12 @@ static int ibmveth_remove_buffer_from_pool(struct ibmveth_adapter *adapter,
unsigned int free_index;
struct sk_buff *skb;
- if (WARN_ON(pool >= IBMVETH_NUM_BUFF_POOLS) ||
- WARN_ON(index >= adapter->rx_buff_pool[queue_index][pool].size)) {
- schedule_work(&adapter->work);
+ if (!ibmveth_rxq_correlator_valid(adapter, queue_index, correlator))
return -EINVAL;
- }
skb = adapter->rx_buff_pool[queue_index][pool].skbuff[index];
- if (WARN_ON(!skb)) {
- schedule_work(&adapter->work);
+ if (!skb)
return -EFAULT;
- }
/* if we are going to reuse the buffer then keep the pointers around
* but mark index as available. replenish will see the skb pointer and
@@ -1335,11 +1349,8 @@ static inline struct sk_buff *ibmveth_rxq_get_buffer(struct ibmveth_adapter *ada
unsigned int pool = correlator >> 32;
unsigned int index = correlator & 0xffffffffUL;
- if (WARN_ON(pool >= IBMVETH_NUM_BUFF_POOLS) ||
- WARN_ON(index >= adapter->rx_buff_pool[queue_index][pool].size)) {
- schedule_work(&adapter->work);
+ if (!ibmveth_rxq_correlator_valid(adapter, queue_index, correlator))
return NULL;
- }
return adapter->rx_buff_pool[queue_index][pool].skbuff[index];
}
@@ -1365,14 +1376,15 @@ static int ibmveth_rxq_harvest_buffer(struct ibmveth_adapter *adapter,
cor = rxq->queue_addr[rxq->index].correlator;
rc = ibmveth_remove_buffer_from_pool(adapter, cor, queue_index, reuse);
- if (unlikely(rc))
+ if (unlikely(rc)) {
+ if (rc == -EINVAL || rc == -EFAULT)
+ goto advance;
return rc;
-
- if (++rxq->index == rxq->num_slots) {
- rxq->index = 0;
- rxq->toggle = !rxq->toggle;
}
+advance:
+ ibmveth_rxq_advance(rxq);
+
return 0;
}
@@ -2931,11 +2943,19 @@ static int ibmveth_poll(struct napi_struct *napi, int budget)
if (WARN_ON(queue_index < 0 || queue_index >= adapter->num_rx_queues))
return 0;
+ if (!netif_running(netdev) || napi_disable_pending(napi)) {
+ napi_complete_done(napi, 0);
+ return 0;
+ }
+
if (adapter->rx_qstats)
adapter->rx_qstats[queue_index].polls++;
restart_poll:
while (frames_processed < budget) {
+ if (!netif_running(netdev) || napi_disable_pending(napi))
+ break;
+
if (!ibmveth_rxq_pending_buffer(adapter, queue_index))
break;
@@ -2959,8 +2979,21 @@ static int ibmveth_poll(struct napi_struct *napi, int budget)
__sum16 iph_check = 0;
skb = ibmveth_rxq_get_buffer(adapter, queue_index);
- if (unlikely(!skb))
- break;
+ if (unlikely(!skb)) {
+ if (net_ratelimit())
+ netdev_err(netdev,
+ "bad correlator on queue %d, skipping slot\n",
+ queue_index);
+ if (adapter->rx_qstats)
+ adapter->rx_qstats[queue_index].invalid_buffers++;
+ else
+ adapter->rx_invalid_buffer++;
+ rc = ibmveth_rxq_harvest_buffer(adapter, queue_index,
+ true);
+ if (unlikely(rc))
+ break;
+ continue;
+ }
/* if the large packet bit is set in the rx queue
* descriptor, the mss will be written by PHYP eight
@@ -3034,8 +3067,11 @@ static int ibmveth_poll(struct napi_struct *napi, int budget)
ibmveth_replenish_task(adapter, queue_index);
- if (frames_processed == budget)
+ if (frames_processed == budget) {
+ if (!netif_running(netdev) || napi_disable_pending(napi))
+ napi_complete_done(napi, frames_processed);
goto out;
+ }
if (!napi_complete_done(napi, frames_processed))
goto out;
@@ -3053,6 +3089,8 @@ static int ibmveth_poll(struct napi_struct *napi, int budget)
}
if (ibmveth_rxq_pending_buffer(adapter, queue_index) &&
+ netif_running(netdev) &&
+ !napi_disable_pending(napi) &&
napi_schedule(napi)) {
lpar_rc = ibmveth_disable_irq(adapter, queue_index);
WARN_ON(lpar_rc != H_SUCCESS);
--
2.39.3 (Apple Git-146)
^ permalink raw reply related
* [PATCH v1 16/18] ibmveth: Implement incremental MQ RX queue resize
From: Mingming Cao @ 2026-06-30 14:53 UTC (permalink / raw)
To: netdev
Cc: horms, bjking1, haren, ricklind, mmc, kuba, edumazet, pabeni,
linuxppc-dev, maddy, mpe, Dave Marquardt
In-Reply-To: <cover.1782758799.git.mmc@linux.ibm.com>
Add ibmveth_resize_rx_queues_incremental() to grow or shrink
adapter->num_rx_queues while the netdev stays up.
Scale-up, per new queue index:
alloc RX resources and per-queue pools
register subordinate queue with PHYP
request_irq(), then ibmveth_enable_irq(), then napi_enable
update num_rx_queues, replenish new queues
netif_set_real_num_rx_queues()
Scale-down disables NAPI on excess queues, drains pending buffers,
disables PHYP IRQ delivery and waits for in-flight handlers with
synchronize_irq() before lowering num_rx_queues, then tears down
IRQ/PHYP/memory.
Reject out-of-range new_count. On scale-down netif failure, re-enable
NAPI on queues not yet torn down. Refresh VIO CMO entitlement after a
successful resize when FW_FEATURE_CMO is enabled.
Scale-up rollback mirrors scale-down: drain posted buffers and wait for
in-flight handlers before deregistering with PHYP.
In replenish_task(), skip queues with queue_index >= num_rx_queues and
require pool->free_map before replenishing so in-flight handlers avoid
queues being torn down without clearing probe-time pool->active on free.
Queue 0 is never removed here. Scale-up failure unwinds only queues
added in this call. ethtool -L wiring is next.
Signed-off-by: Mingming Cao <mmc@linux.ibm.com>
Reviewed-by: Dave Marquardt <davemarq@linux.ibm.com>
---
drivers/net/ethernet/ibm/ibmveth.c | 183 ++++++++++++++++++++++++++++-
1 file changed, 178 insertions(+), 5 deletions(-)
diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c
index cd0acd1715da..ac4d89a66a8d 100644
--- a/drivers/net/ethernet/ibm/ibmveth.c
+++ b/drivers/net/ethernet/ibm/ibmveth.c
@@ -945,18 +945,22 @@ static void ibmveth_replenish_task(struct ibmveth_adapter *adapter,
unsigned long flags;
int i;
- if (queue_index >= adapter->num_rx_queues)
- return;
-
adapter->replenish_task_cycles++;
+ if (queue_index >= adapter->num_rx_queues) {
+ netdev_dbg(adapter->netdev,
+ "Skipping replenish for freed queue %d (num_queues=%d)\n",
+ queue_index, adapter->num_rx_queues);
+ return;
+ }
+
spin_lock_irqsave(&rxq->replenish_lock, flags);
for (i = (IBMVETH_NUM_BUFF_POOLS - 1); i >= 0; i--) {
struct ibmveth_buff_pool *pool =
&adapter->rx_buff_pool[queue_index][i];
- if (pool->active &&
+ if (pool->active && pool->free_map &&
(atomic_read(&pool->available) < pool->threshold))
ibmveth_replenish_buffer_pool(adapter, pool,
queue_index);
@@ -1682,7 +1686,7 @@ ibmveth_register_single_rx_queue(struct ibmveth_adapter *adapter,
* the IRQ mapping for subordinate queues. Queue 0 is freed only through
* ibmveth_free_all_queues() (H_FREE_LOGICAL_LAN).
*/
-static void __maybe_unused
+static void
ibmveth_deregister_single_rx_queue(struct ibmveth_adapter *adapter,
int queue_idx)
{
@@ -1714,6 +1718,175 @@ ibmveth_deregister_single_rx_queue(struct ibmveth_adapter *adapter,
netdev_dbg(adapter->netdev, "Deregistered queue %d\n", queue_idx);
}
+/**
+ * ibmveth_resize_rx_queues_incremental - Resize RX queue count incrementally
+ * @adapter: ibmveth adapter structure
+ * @new_count: Target number of RX queues
+ * @rxq_entries: Number of entries per RX queue
+ *
+ * Adds or removes RX queues without tearing down the entire adapter.
+ * Active queues continue receiving during scale-up; scale-down drains
+ * excess queues before deregistering them with the hypervisor.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int
+ibmveth_resize_rx_queues_incremental(struct ibmveth_adapter *adapter,
+ int new_count, int rxq_entries)
+{
+ struct net_device *netdev = adapter->netdev;
+ u64 mac_address = ether_addr_to_u64(netdev->dev_addr);
+ int old_count = adapter->num_rx_queues;
+ int failed_queue;
+ int rc, i;
+
+ if (old_count == new_count) {
+ netdev_dbg(netdev, "RX queue count unchanged (%d), nothing to do\n",
+ old_count);
+ return 0;
+ }
+
+ if (new_count < 1 || new_count > IBMVETH_MAX_RX_QUEUES) {
+ netdev_err(netdev, "Invalid RX queue count %d (must be 1-%d)\n",
+ new_count, IBMVETH_MAX_RX_QUEUES);
+ return -EINVAL;
+ }
+
+ netdev_info(netdev, "Incrementally resizing RX queues: %d to %d\n",
+ old_count, new_count);
+
+ if (new_count > old_count) {
+ netdev_dbg(netdev, "Scale-up: adding queues %d-%d\n",
+ old_count, new_count - 1);
+
+ for (i = old_count; i < new_count; i++) {
+ rc = ibmveth_alloc_single_rx_queue(adapter, i, rxq_entries);
+ if (rc) {
+ netdev_err(netdev, "Failed to allocate queue %d: %d\n",
+ i, rc);
+ goto cleanup_new_queues;
+ }
+
+ rc = ibmveth_register_single_rx_queue(adapter, i,
+ mac_address);
+ if (rc) {
+ netdev_err(netdev, "Failed to register queue %d: %d\n",
+ i, rc);
+ ibmveth_free_single_rx_queue(adapter, i);
+ goto cleanup_new_queues;
+ }
+
+ rc = ibmveth_setup_single_rx_interrupt(adapter, i);
+ if (rc) {
+ netdev_err(netdev,
+ "Failed to setup IRQ for queue %d: %d\n",
+ i, rc);
+ ibmveth_deregister_single_rx_queue(adapter, i);
+ ibmveth_free_single_rx_queue(adapter, i);
+ goto cleanup_new_queues;
+ }
+
+ rc = ibmveth_enable_irq(adapter, i);
+ if (rc) {
+ netdev_err(netdev,
+ "Failed to enable IRQ for queue %d: %d\n",
+ i, rc);
+ ibmveth_cleanup_single_rx_interrupt(adapter, i);
+ ibmveth_deregister_single_rx_queue(adapter, i);
+ ibmveth_free_single_rx_queue(adapter, i);
+ goto cleanup_new_queues;
+ }
+
+ napi_enable(&adapter->napi[i]);
+ }
+
+ adapter->num_rx_queues = new_count;
+
+ for (i = old_count; i < new_count; i++)
+ ibmveth_replenish_task(adapter, i);
+
+ rc = netif_set_real_num_rx_queues(netdev, new_count);
+ if (rc) {
+ netdev_err(netdev, "Failed to set real RX queues to %d: %d\n",
+ new_count, rc);
+ goto cleanup_new_queues;
+ }
+ } else {
+ netdev_dbg(netdev, "Scale-down: removing queues %d-%d\n",
+ new_count, old_count - 1);
+
+ for (i = new_count; i < old_count; i++)
+ napi_disable(&adapter->napi[i]);
+
+ for (i = new_count; i < old_count; i++)
+ ibmveth_drain_rx_queue(adapter, i);
+
+ synchronize_net();
+
+ rc = netif_set_real_num_rx_queues(netdev, new_count);
+ if (rc) {
+ netdev_err(netdev, "Failed to set real RX queues to %d: %d\n",
+ new_count, rc);
+ for (i = new_count; i < old_count; i++)
+ napi_enable(&adapter->napi[i]);
+ return rc;
+ }
+
+ /* Disable hypervisor interrupts and wait for handlers to complete
+ * before updating num_rx_queues.
+ */
+ for (i = new_count; i < old_count; i++) {
+ ibmveth_disable_irq(adapter, i);
+ synchronize_irq(adapter->queue_irq[i]);
+ }
+
+ adapter->num_rx_queues = new_count;
+
+ for (i = new_count; i < old_count; i++) {
+ ibmveth_cleanup_single_rx_interrupt(adapter, i);
+ ibmveth_deregister_single_rx_queue(adapter, i);
+ ibmveth_free_single_rx_queue(adapter, i);
+ }
+ }
+
+ netdev_info(netdev, "Successfully resized to %d RX queues (incremental)\n",
+ adapter->num_rx_queues);
+
+ if (firmware_has_feature(FW_FEATURE_CMO))
+ vio_cmo_set_dev_desired(adapter->vdev,
+ ibmveth_get_desired_dma(adapter->vdev));
+
+ return 0;
+
+cleanup_new_queues:
+ failed_queue = i;
+ netdev_err(netdev,
+ "Scale-up failed at queue %d, cleaning up queues %d-%d\n",
+ failed_queue, old_count, failed_queue - 1);
+ for (i = old_count; i < failed_queue; i++)
+ napi_disable(&adapter->napi[i]);
+
+ for (i = old_count; i < failed_queue; i++)
+ ibmveth_drain_rx_queue(adapter, i);
+
+ synchronize_net();
+
+ for (i = old_count; i < failed_queue; i++) {
+ ibmveth_disable_irq(adapter, i);
+ synchronize_irq(adapter->queue_irq[i]);
+ }
+
+ for (i = old_count; i < failed_queue; i++) {
+ ibmveth_cleanup_single_rx_interrupt(adapter, i);
+ ibmveth_deregister_single_rx_queue(adapter, i);
+ ibmveth_free_single_rx_queue(adapter, i);
+ }
+ adapter->num_rx_queues = old_count;
+ netdev_warn(netdev, "Keeping %d queues after scale-up failure\n",
+ old_count);
+ return rc;
+}
+
/**
* ibmveth_free_all_queues - Free all RX queues at once
* @adapter: ibmveth adapter structure
--
2.39.3 (Apple Git-146)
^ permalink raw reply related
* [PATCH v1 17/18] ibmveth: Wire ethtool set_channels to MQ RX queue resize
From: Mingming Cao @ 2026-06-30 14:53 UTC (permalink / raw)
To: netdev
Cc: horms, bjking1, haren, ricklind, mmc, kuba, edumazet, pabeni,
linuxppc-dev, maddy, mpe, Dave Marquardt
In-Reply-To: <cover.1782758799.git.mmc@linux.ibm.com>
Expose incremental RX resize through ethtool channel control.
get_channels() reports rx_count from adapter->num_rx_queues and max_rx
as IBMVETH_MAX_RX_QUEUES when MQ firmware is enabled, else 1.
set_channels() validates rx_count is within 1..IBMVETH_MAX_RX_QUEUES.
When rx_count changes and the interface is up, call
ibmveth_resize_rx_queues_incremental(). When the interface is down,
store the requested rx_count in adapter->num_rx_queues so the next open
registers that many queues. Non-MQ firmware returns -EOPNOTSUPP for
rx > 1.
TX queue changes keep existing stop/wake behavior when tx_count changes.
Signed-off-by: Mingming Cao <mmc@linux.ibm.com>
Reviewed-by: Dave Marquardt <davemarq@linux.ibm.com>
---
drivers/net/ethernet/ibm/ibmveth.c | 58 +++++++++++++++++++++++++++---
1 file changed, 54 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c
index ac4d89a66a8d..50a332ab83fd 100644
--- a/drivers/net/ethernet/ibm/ibmveth.c
+++ b/drivers/net/ethernet/ibm/ibmveth.c
@@ -2534,19 +2534,69 @@ static int ibmveth_set_channels(struct net_device *netdev,
struct ethtool_channels *channels)
{
struct ibmveth_adapter *adapter = netdev_priv(netdev);
- unsigned int old = netdev->real_num_tx_queues,
- goal = channels->tx_count;
+ unsigned int old_rx = adapter->num_rx_queues;
+ unsigned int goal_rx = channels->rx_count;
+ unsigned int old = netdev->real_num_tx_queues;
+ unsigned int goal = channels->tx_count;
+ int rxq_entries = adapter->rx_queue[0].num_slots;
int rc, i;
/* If ndo_open has not been called yet then don't allocate, just set
* desired netdev_queue's and return
*/
- if (!(netdev->flags & IFF_UP))
+ if (!(netdev->flags & IFF_UP)) {
+ if (goal_rx > 1 && !adapter->multi_queue) {
+ netdev_err(netdev,
+ "Cannot resize to %u RX queues: multi-queue mode not supported by firmware\n",
+ goal_rx);
+ return -EOPNOTSUPP;
+ }
+
+ if (goal_rx < 1 || goal_rx > IBMVETH_MAX_RX_QUEUES) {
+ netdev_err(netdev,
+ "Invalid RX queue count %u (must be 1-%d)\n",
+ goal_rx, IBMVETH_MAX_RX_QUEUES);
+ return -EINVAL;
+ }
+
+ /* Stash desired RX count; open() publishes it via
+ * netif_set_real_num_rx_queues() after queue registration.
+ */
+ if (goal_rx != adapter->num_rx_queues)
+ adapter->num_rx_queues = goal_rx;
+
return netif_set_real_num_tx_queues(netdev, goal);
+ }
+
+ if (goal_rx > 1 && !adapter->multi_queue) {
+ netdev_err(netdev,
+ "Cannot resize to %u RX queues: multi-queue mode not supported by firmware\n",
+ goal_rx);
+ return -EOPNOTSUPP;
+ }
+
+ if (goal_rx < 1 || goal_rx > IBMVETH_MAX_RX_QUEUES) {
+ netdev_err(netdev,
+ "Invalid RX queue count %u (must be 1-%d)\n",
+ goal_rx, IBMVETH_MAX_RX_QUEUES);
+ return -EINVAL;
+ }
+
+ if (goal_rx != old_rx) {
+ rc = ibmveth_resize_rx_queues_incremental(adapter, goal_rx,
+ rxq_entries);
+ if (rc) {
+ netdev_err(netdev, "Failed to resize RX queues: %d\n", rc);
+ return rc;
+ }
+ }
/* We have IBMVETH_MAX_QUEUES netdev_queue's allocated
* but we may need to alloc/free the ltb's.
*/
+ if (goal == old)
+ return 0;
+
netif_tx_stop_all_queues(netdev);
/* Allocate any queue that we need */
@@ -2580,7 +2630,7 @@ static int ibmveth_set_channels(struct net_device *netdev,
netif_tx_wake_all_queues(netdev);
- return rc;
+ return 0;
}
static const struct ethtool_ops netdev_ethtool_ops = {
--
2.39.3 (Apple Git-146)
^ permalink raw reply related
* [PATCH v1 15/18] ibmveth: Add helpers for incremental MQ RX queue resize
From: Mingming Cao @ 2026-06-30 14:53 UTC (permalink / raw)
To: netdev
Cc: horms, bjking1, haren, ricklind, mmc, kuba, edumazet, pabeni,
linuxppc-dev, maddy, mpe, Dave Marquardt
In-Reply-To: <cover.1782758799.git.mmc@linux.ibm.com>
Patches 15-17 add runtime RX queue resize via ethtool -L: single-queue
helpers here, ibmveth_resize_rx_queues_incremental() next, then ethtool
set_channels wiring.
Design: rx queue count must be changeable without a full close/open.
Close tears down the whole logical LAN (H_FREE_LOGICAL_LAN), dropping
every queue and disrupting traffic on queues that should stay up.
Incremental resize is viable because MQ PHYP registers subordinate
queues independently (H_REG_LOGICAL_LAN_QUEUE and per-queue free) while
queue 0 keeps the adapter handle; earlier per-queue bring-up helpers
already split pools, IRQs, and PHYP registration by queue index. Resize
then grows or shrinks by touching only the indices that change, leaving
surviving queues registered with buffers and IRQs intact.
This patch adds the single-queue Linux-side lifecycle helpers the resize
path calls for each new or removed index:
ibmveth_drain_rx_queue()
ibmveth_alloc_single_rx_queue()
ibmveth_free_single_rx_queue()
ibmveth_setup_single_rx_interrupt()
ibmveth_cleanup_single_rx_interrupt()
Scale-up copies pool geometry from queue 0 and uses
ibmveth_alloc_queue_buffer_pools() so only active pools are allocated
for the new queue index.
No user-visible behavior yet: helpers are added but not called until
the next patch implements ibmveth_resize_rx_queues_incremental().
Signed-off-by: Mingming Cao <mmc@linux.ibm.com>
Reviewed-by: Dave Marquardt <davemarq@linux.ibm.com>
---
drivers/net/ethernet/ibm/ibmveth.c | 223 +++++++++++++++++++++++++++++
1 file changed, 223 insertions(+)
diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c
index ecc472ee8f71..cd0acd1715da 100644
--- a/drivers/net/ethernet/ibm/ibmveth.c
+++ b/drivers/net/ethernet/ibm/ibmveth.c
@@ -589,6 +589,54 @@ ibmveth_cleanup_rx_interrupts(struct ibmveth_adapter *adapter)
adapter->queue_irq[0] = 0;
}
+/**
+ * ibmveth_setup_single_rx_interrupt - Setup interrupt for a single RX queue
+ * @adapter: ibmveth adapter structure
+ * @queue_idx: Queue index to setup
+ *
+ * Registers the IRQ handler for one queue. Used during incremental
+ * scale-up when adding new RX queues; the caller enables NAPI via
+ * napi_enable() after ibmveth_enable_irq().
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int
+ibmveth_setup_single_rx_interrupt(struct ibmveth_adapter *adapter,
+ int queue_idx)
+{
+ struct net_device *netdev = adapter->netdev;
+ int rc;
+
+ rc = request_irq(adapter->queue_irq[queue_idx], ibmveth_interrupt,
+ 0, netdev->name, &adapter->napi[queue_idx]);
+ if (rc) {
+ netdev_err(netdev, "request_irq() failed for queue %d: %d\n",
+ queue_idx, rc);
+ return rc;
+ }
+
+ netdev_dbg(netdev, "Setup IRQ %d for queue %d\n",
+ adapter->queue_irq[queue_idx], queue_idx);
+ return 0;
+}
+
+/**
+ * ibmveth_cleanup_single_rx_interrupt - Cleanup interrupt for a single RX queue
+ * @adapter: ibmveth adapter structure
+ * @queue_idx: Queue index to cleanup
+ *
+ * Frees the IRQ handler for one queue. Used during incremental scale-down.
+ */
+static void
+ibmveth_cleanup_single_rx_interrupt(struct ibmveth_adapter *adapter,
+ int queue_idx)
+{
+ if (adapter->queue_irq[queue_idx]) {
+ free_irq(adapter->queue_irq[queue_idx], &adapter->napi[queue_idx]);
+ netdev_dbg(adapter->netdev, "Freed IRQ for queue %d\n", queue_idx);
+ }
+}
+
/* setup the initial settings for a buffer pool */
static void ibmveth_init_buffer_pool(struct ibmveth_buff_pool *pool,
u32 pool_index, u32 pool_size,
@@ -1080,6 +1128,138 @@ static void ibmveth_free_buffer_pools(struct ibmveth_adapter *adapter)
adapter->num_rx_queues);
}
+/**
+ * ibmveth_alloc_single_rx_queue - Allocate resources for a single RX queue
+ * @adapter: ibmveth adapter structure
+ * @queue_idx: Queue index to allocate
+ * @rxq_entries: Number of RX queue entries
+ *
+ * Allocates buffer list, RX queue, and per-queue buffer pools for one queue.
+ * Used during incremental scale-up without affecting existing queues.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int
+ibmveth_alloc_single_rx_queue(struct ibmveth_adapter *adapter, int queue_idx,
+ int rxq_entries)
+{
+ struct device *dev = &adapter->vdev->dev;
+ struct net_device *netdev = adapter->netdev;
+ int i, rc = -ENOMEM;
+
+ adapter->buffer_list_addr[queue_idx] = (void *)get_zeroed_page(GFP_KERNEL);
+ if (!adapter->buffer_list_addr[queue_idx]) {
+ netdev_err(netdev, "unable to allocate buffer list for queue %d\n",
+ queue_idx);
+ return -ENOMEM;
+ }
+
+ adapter->rx_queue[queue_idx].queue_len =
+ sizeof(struct ibmveth_rx_q_entry) * rxq_entries;
+ adapter->rx_queue[queue_idx].queue_addr =
+ dma_alloc_coherent(dev, adapter->rx_queue[queue_idx].queue_len,
+ &adapter->rx_queue[queue_idx].queue_dma,
+ GFP_KERNEL);
+ if (!adapter->rx_queue[queue_idx].queue_addr) {
+ netdev_err(netdev, "unable to allocate RX queue for queue %d\n",
+ queue_idx);
+ goto out_free_buflist;
+ }
+
+ adapter->buffer_list_dma[queue_idx] =
+ dma_map_single(dev, adapter->buffer_list_addr[queue_idx],
+ 4096, DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(dev, adapter->buffer_list_dma[queue_idx])) {
+ netdev_err(netdev, "unable to map buffer list for queue %d\n",
+ queue_idx);
+ goto out_free_rxq;
+ }
+
+ for (i = 0; i < IBMVETH_NUM_BUFF_POOLS; i++) {
+ adapter->rx_buff_pool[queue_idx][i].size =
+ adapter->rx_buff_pool[0][i].size;
+ adapter->rx_buff_pool[queue_idx][i].buff_size =
+ adapter->rx_buff_pool[0][i].buff_size;
+ adapter->rx_buff_pool[queue_idx][i].threshold =
+ adapter->rx_buff_pool[0][i].threshold;
+ adapter->rx_buff_pool[queue_idx][i].active =
+ adapter->rx_buff_pool[0][i].active;
+ }
+
+ rc = ibmveth_alloc_queue_buffer_pools(adapter, queue_idx);
+ if (rc) {
+ netdev_err(netdev,
+ "Failed to allocate buffer pools for queue %d\n",
+ queue_idx);
+ goto out_unmap_buflist;
+ }
+
+ adapter->rx_queue[queue_idx].index = 0;
+ adapter->rx_queue[queue_idx].num_slots = rxq_entries;
+ adapter->rx_queue[queue_idx].toggle = 1;
+ spin_lock_init(&adapter->rx_queue[queue_idx].replenish_lock);
+
+ netdev_dbg(netdev,
+ "Allocated queue %d: buffer_list @ %p (DMA: 0x%llx), rx_queue @ %p (DMA: 0x%llx), %d entries\n",
+ queue_idx, adapter->buffer_list_addr[queue_idx],
+ (unsigned long long)adapter->buffer_list_dma[queue_idx],
+ adapter->rx_queue[queue_idx].queue_addr,
+ (unsigned long long)adapter->rx_queue[queue_idx].queue_dma,
+ rxq_entries);
+
+ return 0;
+
+out_unmap_buflist:
+ dma_unmap_single(dev, adapter->buffer_list_dma[queue_idx],
+ 4096, DMA_BIDIRECTIONAL);
+ adapter->buffer_list_dma[queue_idx] = 0;
+out_free_rxq:
+ dma_free_coherent(dev, adapter->rx_queue[queue_idx].queue_len,
+ adapter->rx_queue[queue_idx].queue_addr,
+ adapter->rx_queue[queue_idx].queue_dma);
+ adapter->rx_queue[queue_idx].queue_addr = NULL;
+out_free_buflist:
+ free_page((unsigned long)adapter->buffer_list_addr[queue_idx]);
+ adapter->buffer_list_addr[queue_idx] = NULL;
+ return rc;
+}
+
+/**
+ * ibmveth_free_single_rx_queue - Free resources for a single RX queue
+ * @adapter: ibmveth adapter structure
+ * @queue_idx: Queue index to free
+ *
+ * Frees buffer list, RX queue, and per-queue buffer pools for one queue.
+ * Used during incremental scale-down without affecting remaining queues.
+ */
+static void
+ibmveth_free_single_rx_queue(struct ibmveth_adapter *adapter, int queue_idx)
+{
+ struct device *dev = &adapter->vdev->dev;
+
+ ibmveth_free_queue_buffer_pools(adapter, queue_idx);
+
+ if (adapter->buffer_list_dma[queue_idx]) {
+ dma_unmap_single(dev, adapter->buffer_list_dma[queue_idx],
+ 4096, DMA_BIDIRECTIONAL);
+ adapter->buffer_list_dma[queue_idx] = 0;
+ }
+
+ if (adapter->rx_queue[queue_idx].queue_addr) {
+ dma_free_coherent(dev, adapter->rx_queue[queue_idx].queue_len,
+ adapter->rx_queue[queue_idx].queue_addr,
+ adapter->rx_queue[queue_idx].queue_dma);
+ adapter->rx_queue[queue_idx].queue_addr = NULL;
+ }
+
+ if (adapter->buffer_list_addr[queue_idx]) {
+ free_page((unsigned long)adapter->buffer_list_addr[queue_idx]);
+ adapter->buffer_list_addr[queue_idx] = NULL;
+ }
+
+ netdev_dbg(adapter->netdev, "Freed queue %d resources\n", queue_idx);
+}
+
/**
* ibmveth_remove_buffer_from_pool - remove a buffer from a pool
* @adapter: adapter instance
@@ -1192,6 +1372,49 @@ static int ibmveth_rxq_harvest_buffer(struct ibmveth_adapter *adapter,
return 0;
}
+/**
+ * ibmveth_drain_rx_queue - Drain pending buffers from an RX queue
+ * @adapter: ibmveth adapter structure
+ * @queue_index: Queue index to drain
+ *
+ * Recycles all pending buffers back to the per-queue buffer pools.
+ * Must be called with NAPI disabled for this queue.
+ *
+ * Return: Number of buffers drained
+ */
+static int
+ibmveth_drain_rx_queue(struct ibmveth_adapter *adapter, int queue_index)
+{
+ struct net_device *netdev = adapter->netdev;
+ int drained = 0;
+ int limit = adapter->rx_queue[queue_index].num_slots;
+ int rc;
+
+ netdev_dbg(netdev, "Draining RX queue %d (limit: %d slots)\n",
+ queue_index, limit);
+
+ while (drained < limit &&
+ ibmveth_rxq_pending_buffer(adapter, queue_index)) {
+ rc = ibmveth_rxq_harvest_buffer(adapter, queue_index, true);
+ if (rc) {
+ netdev_err(netdev,
+ "Failed to harvest buffer from queue %d during drain: %d\n",
+ queue_index, rc);
+ break;
+ }
+ drained++;
+ }
+
+ if (drained > 0)
+ netdev_dbg(netdev, "Drained %d buffer(s) from RX queue %d\n",
+ drained, queue_index);
+ else
+ netdev_dbg(netdev, "No buffers to drain from RX queue %d\n",
+ queue_index);
+
+ return drained;
+}
+
static void ibmveth_free_tx_ltb(struct ibmveth_adapter *adapter, int idx)
{
dma_unmap_single(&adapter->vdev->dev, adapter->tx_ltb_dma[idx],
--
2.39.3 (Apple Git-146)
^ permalink raw reply related
* [PATCH] powerpc: fsl_rio: Use snprintf() in fsl_rio_setup()
From: Thorsten Blum @ 2026-06-30 14:49 UTC (permalink / raw)
To: Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
Christophe Leroy (CS GROUP), Kees Cook
Cc: Thorsten Blum, linuxppc-dev, linux-kernel
While the current code works correctly, replace the unbounded sprintf()
with the safer snprintf() in fsl_rio_setup() to follow secure coding
best practices.
Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
---
arch/powerpc/sysdev/fsl_rio.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/powerpc/sysdev/fsl_rio.c b/arch/powerpc/sysdev/fsl_rio.c
index eb55dabb4748..077c03cd93e4 100644
--- a/arch/powerpc/sysdev/fsl_rio.c
+++ b/arch/powerpc/sysdev/fsl_rio.c
@@ -612,7 +612,7 @@ static int fsl_rio_setup(struct platform_device *dev)
kfree(port);
continue;
}
- sprintf(port->name, "RIO mport %d", i);
+ snprintf(port->name, sizeof(port->name), "RIO mport %d", i);
priv->dev = &dev->dev;
port->dev.parent = &dev->dev;
^ permalink raw reply related
* [PATCH v1 14/18] ibmveth: Expose per-queue buffer pool details via sysfs
From: Mingming Cao @ 2026-06-30 14:53 UTC (permalink / raw)
To: netdev
Cc: horms, bjking1, haren, ricklind, mmc, kuba, edumazet, pabeni,
linuxppc-dev, maddy, mpe, Dave Marquardt
In-Reply-To: <cover.1782758799.git.mmc@linux.ibm.com>
Add a read-only buffer_pools sysfs attribute under the VIO device that
lists size, buff_size, active, and available for every RX queue and
pool: runtime per-queue buffer pressure during MQ operation. ethtool -S
pool%d_* (previous patch) reports queue-0 static probe geometry only;
sysfs is the right place for dynamic per-queue pool state at scale.
Signed-off-by: Mingming Cao <mmc@linux.ibm.com>
Reviewed-by: Dave Marquardt <davemarq@linux.ibm.com>
---
drivers/net/ethernet/ibm/ibmveth.c | 56 ++++++++++++++++++++++++++++++
1 file changed, 56 insertions(+)
diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c
index 4e3f49b6346f..ecc472ee8f71 100644
--- a/drivers/net/ethernet/ibm/ibmveth.c
+++ b/drivers/net/ethernet/ibm/ibmveth.c
@@ -2896,6 +2896,52 @@ static const struct net_device_ops ibmveth_netdev_ops = {
#endif
};
+static const struct attribute_group ibmveth_attr_group;
+
+static ssize_t buffer_pools_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct net_device *netdev = dev_get_drvdata(dev);
+ struct ibmveth_adapter *adapter = netdev_priv(netdev);
+ int len = 0;
+ int i, j;
+
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "Queue Pool Size BuffSize Active Available\n");
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "----- ---- ---- -------- ------ ---------\n");
+
+ for (i = 0; i < adapter->num_rx_queues; i++) {
+ for (j = 0; j < IBMVETH_NUM_BUFF_POOLS; j++) {
+ struct ibmveth_buff_pool *pool =
+ &adapter->rx_buff_pool[i][j];
+
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "%5d %4d %4u %8u %6d %9d\n",
+ i, j, pool->size, pool->buff_size,
+ pool->active,
+ atomic_read(&pool->available));
+
+ if (len >= PAGE_SIZE - 100)
+ goto out;
+ }
+ }
+
+out:
+ return len;
+}
+static DEVICE_ATTR_RO(buffer_pools);
+
+static struct attribute *ibmveth_attrs[] = {
+ &dev_attr_buffer_pools.attr,
+ NULL,
+};
+
+static const struct attribute_group ibmveth_attr_group = {
+ .attrs = ibmveth_attrs,
+};
+
static int ibmveth_probe(struct vio_dev *dev, const struct vio_device_id *id)
{
int rc, i, mac_len;
@@ -3056,6 +3102,14 @@ static int ibmveth_probe(struct vio_dev *dev, const struct vio_device_id *id)
netdev_dbg(netdev, "registered\n");
+ rc = sysfs_create_group(&dev->dev.kobj, &ibmveth_attr_group);
+ if (rc) {
+ netdev_err(netdev, "failed to create sysfs attributes rc=%d\n", rc);
+ unregister_netdev(netdev);
+ free_netdev(netdev);
+ return rc;
+ }
+
return 0;
}
@@ -3067,6 +3121,8 @@ static void ibmveth_remove(struct vio_dev *dev)
cancel_work_sync(&adapter->work);
+ sysfs_remove_group(&dev->dev.kobj, &ibmveth_attr_group);
+
for (i = 0; i < IBMVETH_NUM_BUFF_POOLS; i++)
kobject_put(&adapter->rx_buff_pool[0][i].kobj);
--
2.39.3 (Apple Git-146)
^ permalink raw reply related
* [PATCH v1 13/18] ibmveth: Add per-queue TX statistics reporting
From: Mingming Cao @ 2026-06-30 14:53 UTC (permalink / raw)
To: netdev
Cc: horms, bjking1, haren, ricklind, mmc, kuba, edumazet, pabeni,
linuxppc-dev, maddy, mpe, Dave Marquardt
In-Reply-To: <cover.1782758799.git.mmc@linux.ibm.com>
Track transmit counters per TX queue to avoid cache line contention in
the xmit hot path and expose per-queue visibility via ethtool -S and
ndo_get_stats64() aggregation.
Global tx_large_packets and tx_send_failed continue to be aggregated on
the ethtool read path for backward compatibility with existing tools.
Signed-off-by: Mingming Cao <mmc@linux.ibm.com>
Reviewed-by: Dave Marquardt <davemarq@linux.ibm.com>
---
drivers/net/ethernet/ibm/ibmveth.c | 129 +++++++++++++++++++++++++----
drivers/net/ethernet/ibm/ibmveth.h | 13 +++
2 files changed, 124 insertions(+), 18 deletions(-)
diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c
index 1c08082ffbd6..4e3f49b6346f 100644
--- a/drivers/net/ethernet/ibm/ibmveth.c
+++ b/drivers/net/ethernet/ibm/ibmveth.c
@@ -252,6 +252,33 @@ static void ibmveth_free_rx_qstats(struct ibmveth_adapter *adapter)
adapter->rx_qstats = NULL;
}
+/**
+ * ibmveth_alloc_tx_qstats - Allocate per-queue TX statistics
+ * @adapter: ibmveth adapter structure
+ *
+ * Return: 0 on success, -ENOMEM on failure
+ */
+static int ibmveth_alloc_tx_qstats(struct ibmveth_adapter *adapter)
+{
+ adapter->tx_qstats = kcalloc(IBMVETH_MAX_QUEUES,
+ sizeof(struct ibmveth_tx_queue_stats),
+ GFP_KERNEL);
+ if (!adapter->tx_qstats)
+ return -ENOMEM;
+
+ return 0;
+}
+
+/**
+ * ibmveth_free_tx_qstats - Free per-queue TX statistics
+ * @adapter: ibmveth adapter structure
+ */
+static void ibmveth_free_tx_qstats(struct ibmveth_adapter *adapter)
+{
+ kfree(adapter->tx_qstats);
+ adapter->tx_qstats = NULL;
+}
+
/**
* ibmveth_alloc_rx_queues - Allocate per-queue RX resources
* @adapter: ibmveth adapter structure
@@ -1628,6 +1655,10 @@ static int ibmveth_open(struct net_device *netdev)
if (rc)
goto out_cleanup_rx_interrupts;
+ rc = ibmveth_alloc_tx_qstats(adapter);
+ if (rc)
+ goto out_free_tx_resources;
+
netif_tx_start_all_queues(netdev);
netdev_dbg(netdev, "open complete\n");
@@ -1668,6 +1699,7 @@ static int ibmveth_close(struct net_device *netdev)
}
}
+ ibmveth_free_tx_qstats(adapter);
ibmveth_free_tx_resources(adapter);
ibmveth_cleanup_rx_interrupts(adapter);
ibmveth_update_rx_no_buffer(adapter);
@@ -1960,6 +1992,32 @@ static void ibmveth_aggregate_rx_qstats(struct ibmveth_adapter *adapter)
adapter->rx_large_packets = total_large;
}
+/**
+ * ibmveth_aggregate_tx_qstats - Sum per-queue TX stats into globals
+ * @adapter: ibmveth adapter
+ *
+ * Cold path only (ethtool). Keeps legacy global counters meaningful for
+ * tools that read the adapter-level fields in ibmveth_stats[].
+ */
+static void ibmveth_aggregate_tx_qstats(struct ibmveth_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+ u64 total_large = 0;
+ u64 total_send_failed = 0;
+ int i;
+
+ if (!adapter->tx_qstats)
+ return;
+
+ for (i = 0; i < netdev->real_num_tx_queues; i++) {
+ total_large += adapter->tx_qstats[i].large_packets;
+ total_send_failed += adapter->tx_qstats[i].send_failures;
+ }
+
+ adapter->tx_large_packets = total_large;
+ adapter->tx_send_failed = total_send_failed;
+}
+
static void ibmveth_get_strings(struct net_device *dev, u32 stringset, u8 *data)
{
struct ibmveth_adapter *adapter = netdev_priv(dev);
@@ -1984,6 +2042,15 @@ static void ibmveth_get_strings(struct net_device *dev, u32 stringset, u8 *data)
ethtool_sprintf(&p, "rx%d_no_buffer_drops", i);
}
+ for (i = 0; i < dev->real_num_tx_queues; i++) {
+ ethtool_sprintf(&p, "tx%d_packets", i);
+ ethtool_sprintf(&p, "tx%d_bytes", i);
+ ethtool_sprintf(&p, "tx%d_large_packets", i);
+ ethtool_sprintf(&p, "tx%d_dropped_packets", i);
+ ethtool_sprintf(&p, "tx%d_send_failures", i);
+ ethtool_sprintf(&p, "tx%d_checksum_offload", i);
+ }
+
for (i = 0; i < IBMVETH_NUM_BUFF_POOLS; i++) {
ethtool_sprintf(&p, "pool%d_size", i);
ethtool_sprintf(&p, "pool%d_active", i);
@@ -1999,6 +2066,7 @@ static int ibmveth_get_sset_count(struct net_device *dev, int sset)
case ETH_SS_STATS:
return ARRAY_SIZE(ibmveth_stats) +
adapter->num_rx_queues * IBMVETH_NUM_RX_QSTATS +
+ dev->real_num_tx_queues * IBMVETH_NUM_TX_QSTATS +
IBMVETH_NUM_BUFF_POOLS * 3;
default:
return -EOPNOTSUPP;
@@ -2012,6 +2080,7 @@ static void ibmveth_get_ethtool_stats(struct net_device *dev,
int i, j;
ibmveth_aggregate_rx_qstats(adapter);
+ ibmveth_aggregate_tx_qstats(adapter);
for (i = 0; i < ARRAY_SIZE(ibmveth_stats); i++)
data[i] = IBMVETH_GET_STAT(adapter, ibmveth_stats[i].offset);
@@ -2030,6 +2099,19 @@ static void ibmveth_get_ethtool_stats(struct net_device *dev,
}
}
+ for (j = 0; j < dev->real_num_tx_queues; j++) {
+ if (adapter->tx_qstats) {
+ data[i++] = adapter->tx_qstats[j].packets;
+ data[i++] = adapter->tx_qstats[j].bytes;
+ data[i++] = adapter->tx_qstats[j].large_packets;
+ data[i++] = adapter->tx_qstats[j].dropped_packets;
+ data[i++] = adapter->tx_qstats[j].send_failures;
+ data[i++] = adapter->tx_qstats[j].checksum_offload;
+ } else {
+ i += IBMVETH_NUM_TX_QSTATS;
+ }
+ }
+
for (j = 0; j < IBMVETH_NUM_BUFF_POOLS; j++) {
data[i++] = adapter->rx_buff_pool[0][j].size;
data[i++] = adapter->rx_buff_pool[0][j].active;
@@ -2152,8 +2234,10 @@ static int ibmveth_send(struct ibmveth_adapter *adapter,
}
static int ibmveth_is_packet_unsupported(struct sk_buff *skb,
- struct net_device *netdev)
+ struct ibmveth_adapter *adapter,
+ int queue_num)
{
+ struct net_device *netdev = adapter->netdev;
struct ethhdr *ether_header;
int ret = 0;
@@ -2161,7 +2245,8 @@ static int ibmveth_is_packet_unsupported(struct sk_buff *skb,
if (ether_addr_equal(ether_header->h_dest, netdev->dev_addr)) {
netdev_dbg(netdev, "veth doesn't support loopback packets, dropping packet.\n");
- netdev->stats.tx_dropped++;
+ if (adapter->tx_qstats)
+ adapter->tx_qstats[queue_num].dropped_packets++;
ret = -EOPNOTSUPP;
}
@@ -2177,7 +2262,7 @@ static netdev_tx_t ibmveth_start_xmit(struct sk_buff *skb,
int i, queue_num = skb_get_queue_mapping(skb);
unsigned long mss = 0;
- if (ibmveth_is_packet_unsupported(skb, netdev))
+ if (ibmveth_is_packet_unsupported(skb, adapter, queue_num))
goto out;
/* veth can't checksum offload UDP */
if (skb->ip_summed == CHECKSUM_PARTIAL &&
@@ -2188,7 +2273,7 @@ static netdev_tx_t ibmveth_start_xmit(struct sk_buff *skb,
skb_checksum_help(skb)) {
netdev_err(netdev, "tx: failed to checksum packet\n");
- netdev->stats.tx_dropped++;
+ adapter->tx_qstats[queue_num].dropped_packets++;
goto out;
}
@@ -2200,6 +2285,8 @@ static netdev_tx_t ibmveth_start_xmit(struct sk_buff *skb,
desc_flags |= (IBMVETH_BUF_NO_CSUM | IBMVETH_BUF_CSUM_GOOD);
+ adapter->tx_qstats[queue_num].checksum_offload++;
+
/* Need to zero out the checksum */
buf[0] = 0;
buf[1] = 0;
@@ -2211,7 +2298,7 @@ static netdev_tx_t ibmveth_start_xmit(struct sk_buff *skb,
if (skb->ip_summed == CHECKSUM_PARTIAL && skb_is_gso(skb)) {
if (adapter->fw_large_send_support) {
mss = (unsigned long)skb_shinfo(skb)->gso_size;
- adapter->tx_large_packets++;
+ adapter->tx_qstats[queue_num].large_packets++;
} else if (!skb_is_gso_v6(skb)) {
/* Put -1 in the IP checksum to tell phyp it
* is a largesend packet. Put the mss in
@@ -2220,7 +2307,7 @@ static netdev_tx_t ibmveth_start_xmit(struct sk_buff *skb,
ip_hdr(skb)->check = 0xffff;
tcp_hdr(skb)->check =
cpu_to_be16(skb_shinfo(skb)->gso_size);
- adapter->tx_large_packets++;
+ adapter->tx_qstats[queue_num].large_packets++;
}
}
@@ -2228,7 +2315,7 @@ static netdev_tx_t ibmveth_start_xmit(struct sk_buff *skb,
if (unlikely(skb->len > adapter->tx_ltb_size)) {
netdev_err(adapter->netdev, "tx: packet size (%u) exceeds ltb (%u)\n",
skb->len, adapter->tx_ltb_size);
- netdev->stats.tx_dropped++;
+ adapter->tx_qstats[queue_num].dropped_packets++;
goto out;
}
memcpy(adapter->tx_ltb_ptr[queue_num], skb->data, skb_headlen(skb));
@@ -2245,7 +2332,7 @@ static netdev_tx_t ibmveth_start_xmit(struct sk_buff *skb,
if (unlikely(total_bytes != skb->len)) {
netdev_err(adapter->netdev, "tx: incorrect packet len copied into ltb (%u != %u)\n",
skb->len, total_bytes);
- netdev->stats.tx_dropped++;
+ adapter->tx_qstats[queue_num].dropped_packets++;
goto out;
}
desc.fields.flags_len = desc_flags | skb->len;
@@ -2254,11 +2341,11 @@ static netdev_tx_t ibmveth_start_xmit(struct sk_buff *skb,
dma_wmb();
if (ibmveth_send(adapter, desc.desc, mss)) {
- adapter->tx_send_failed++;
- netdev->stats.tx_dropped++;
+ adapter->tx_qstats[queue_num].send_failures++;
+ adapter->tx_qstats[queue_num].dropped_packets++;
} else {
- netdev->stats.tx_packets++;
- netdev->stats.tx_bytes += skb->len;
+ adapter->tx_qstats[queue_num].packets++;
+ adapter->tx_qstats[queue_num].bytes += skb->len;
}
out:
@@ -2759,12 +2846,13 @@ static netdev_features_t ibmveth_features_check(struct sk_buff *skb,
}
/**
- * ibmveth_get_stats64 - Return aggregated per-queue RX statistics
+ * ibmveth_get_stats64 - Return aggregated per-queue statistics
* @dev: network device
* @stats: rtnl link statistics storage
*
- * Sums per-queue rx_qstats into rx_packets/rx_bytes for multi-queue mode.
- * TX counters continue to come from netdev->stats (updated in start_xmit).
+ * Sums per-queue rx_qstats and tx_qstats into the rtnl counters.
+ * Callers use ndo_get_stats64(); avoid updating netdev->stats on the
+ * xmit/poll paths to keep per-queue counters off the hot cache line.
*/
static void ibmveth_get_stats64(struct net_device *dev,
struct rtnl_link_stats64 *stats)
@@ -2779,9 +2867,14 @@ static void ibmveth_get_stats64(struct net_device *dev,
}
}
- stats->tx_packets = dev->stats.tx_packets;
- stats->tx_bytes = dev->stats.tx_bytes;
- stats->tx_dropped = dev->stats.tx_dropped;
+ if (adapter->tx_qstats) {
+ for (i = 0; i < dev->real_num_tx_queues; i++) {
+ stats->tx_packets += adapter->tx_qstats[i].packets;
+ stats->tx_bytes += adapter->tx_qstats[i].bytes;
+ stats->tx_dropped += adapter->tx_qstats[i].dropped_packets;
+ }
+ }
+
stats->tx_errors = dev->stats.tx_errors;
}
diff --git a/drivers/net/ethernet/ibm/ibmveth.h b/drivers/net/ethernet/ibm/ibmveth.h
index f7b20fd01acb..390c660af979 100644
--- a/drivers/net/ethernet/ibm/ibmveth.h
+++ b/drivers/net/ethernet/ibm/ibmveth.h
@@ -316,9 +316,21 @@ struct ibmveth_rx_queue_stats {
u64 no_buffer_drops;
};
+struct ibmveth_tx_queue_stats {
+ u64 packets;
+ u64 bytes;
+ u64 large_packets;
+ u64 dropped_packets;
+ u64 send_failures;
+ u64 checksum_offload;
+};
+
#define IBMVETH_NUM_RX_QSTATS \
(sizeof(struct ibmveth_rx_queue_stats) / sizeof(u64))
+#define IBMVETH_NUM_TX_QSTATS \
+ (sizeof(struct ibmveth_tx_queue_stats) / sizeof(u64))
+
struct ibmveth_buff_pool {
u32 size;
u32 index;
@@ -386,6 +398,7 @@ struct ibmveth_adapter {
/* Multi-queue statistics */
struct ibmveth_hcall_stats hcall_stats;
struct ibmveth_rx_queue_stats *rx_qstats;
+ struct ibmveth_tx_queue_stats *tx_qstats;
/* Ethtool settings */
u8 duplex;
--
2.39.3 (Apple Git-146)
^ permalink raw reply related
* [PATCH v1 11/18] ibmveth: Enable multi-queue RX receive path
From: Mingming Cao @ 2026-06-30 14:53 UTC (permalink / raw)
To: netdev
Cc: horms, bjking1, haren, ricklind, mmc, kuba, edumazet, pabeni,
linuxppc-dev, maddy, mpe, Dave Marquardt
In-Reply-To: <cover.1782758799.git.mmc@linux.ibm.com>
This is the first patch that sets multi_queue from H_ILLAN_ATTRIBUTES
and switches registration, buffer posting, and receive to the MQ
hcall path. It also raises num_rx_queues and enables per-queue NAPI.
This is where MQ actually receives packets. If firmware sets
IBMVETH_ILLAN_RX_MULTI_QUEUE_SUPPORT in H_ILLAN_ATTRIBUTES, probe sets
multi_queue and num_rx_queues to min(num_online_cpus(),
IBMVETH_DEFAULT_QUEUES), matching the existing TX default (cap 8).
Up to IBMVETH_MAX_RX_QUEUES (16) remains available via ethtool -L.
Otherwise we stay at one queue like today.
Raise IBMVETH_MAX_RX_QUEUES to 16 here so adapter arrays and NAPI state
can hold every queue before num_rx_queues is increased.
Register a NAPI struct per possible queue at probe, use
alloc_etherdev_mqs(), and call netif_set_real_num_rx_queues() after PHYP
registration on open.
With MQ enabled, open runs initial replenish on every active queue before
starting TX; legacy still kicks replenish via queue-0 interrupt/NAPI only.
PHYP can deliver to any registered queue immediately, so unprimed queues
see no-buffer drops until their NAPI path runs.
Datapath: derive queue_index from the NAPI instance, thread it through
harvest/replenish/pool access, and enable/disable IRQ per queue on NAPI
completion. Add per-queue replenish_lock around buffer posting (same-queue
NAPI vs netpoll/resize). poll_controller() and get_desired_dma() walk all
queues.
Update KUnit tests for the queue_index argument added to
ibmveth_remove_buffer_from_pool() and ibmveth_rxq_get_buffer().
Legacy firmware without the MQ bit is unchanged.
Signed-off-by: Mingming Cao <mmc@linux.ibm.com>
Reviewed-by: Dave Marquardt <davemarq@linux.ibm.com>
---
drivers/net/ethernet/ibm/ibmveth.c | 257 ++++++++++++++++++-----------
drivers/net/ethernet/ibm/ibmveth.h | 10 +-
2 files changed, 171 insertions(+), 96 deletions(-)
diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c
index b3b3886c3eed..863e5c68b42c 100644
--- a/drivers/net/ethernet/ibm/ibmveth.c
+++ b/drivers/net/ethernet/ibm/ibmveth.c
@@ -30,6 +30,7 @@
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/slab.h>
+#include <linux/spinlock.h>
#include <asm/hvcall.h>
#include <linux/atomic.h>
#include <asm/vio.h>
@@ -101,45 +102,58 @@ static struct ibmveth_stat ibmveth_stats[] = {
};
/* simple methods of getting data from the current rxq entry */
-static inline u32 ibmveth_rxq_flags(struct ibmveth_adapter *adapter)
+static inline u32 ibmveth_rxq_flags(struct ibmveth_adapter *adapter,
+ int queue_index)
{
- return be32_to_cpu(adapter->rx_queue[0].queue_addr[adapter->rx_queue[0].index].flags_off);
+ struct ibmveth_rx_q *rxq = &adapter->rx_queue[queue_index];
+
+ return be32_to_cpu(rxq->queue_addr[rxq->index].flags_off);
}
-static inline int ibmveth_rxq_toggle(struct ibmveth_adapter *adapter)
+static inline int ibmveth_rxq_toggle(struct ibmveth_adapter *adapter,
+ int queue_index)
{
- return (ibmveth_rxq_flags(adapter) & IBMVETH_RXQ_TOGGLE) >>
- IBMVETH_RXQ_TOGGLE_SHIFT;
+ return (ibmveth_rxq_flags(adapter, queue_index) & IBMVETH_RXQ_TOGGLE) >>
+ IBMVETH_RXQ_TOGGLE_SHIFT;
}
-static inline int ibmveth_rxq_pending_buffer(struct ibmveth_adapter *adapter)
+static inline int ibmveth_rxq_pending_buffer(struct ibmveth_adapter *adapter,
+ int queue_index)
{
- return ibmveth_rxq_toggle(adapter) == adapter->rx_queue[0].toggle;
+ return ibmveth_rxq_toggle(adapter, queue_index) ==
+ adapter->rx_queue[queue_index].toggle;
}
-static inline int ibmveth_rxq_buffer_valid(struct ibmveth_adapter *adapter)
+static inline int ibmveth_rxq_buffer_valid(struct ibmveth_adapter *adapter,
+ int queue_index)
{
- return ibmveth_rxq_flags(adapter) & IBMVETH_RXQ_VALID;
+ return ibmveth_rxq_flags(adapter, queue_index) & IBMVETH_RXQ_VALID;
}
-static inline int ibmveth_rxq_frame_offset(struct ibmveth_adapter *adapter)
+static inline int ibmveth_rxq_frame_offset(struct ibmveth_adapter *adapter,
+ int queue_index)
{
- return ibmveth_rxq_flags(adapter) & IBMVETH_RXQ_OFF_MASK;
+ return ibmveth_rxq_flags(adapter, queue_index) & IBMVETH_RXQ_OFF_MASK;
}
-static inline int ibmveth_rxq_large_packet(struct ibmveth_adapter *adapter)
+static inline int ibmveth_rxq_large_packet(struct ibmveth_adapter *adapter,
+ int queue_index)
{
- return ibmveth_rxq_flags(adapter) & IBMVETH_RXQ_LRG_PKT;
+ return ibmveth_rxq_flags(adapter, queue_index) & IBMVETH_RXQ_LRG_PKT;
}
-static inline int ibmveth_rxq_frame_length(struct ibmveth_adapter *adapter)
+static inline int ibmveth_rxq_frame_length(struct ibmveth_adapter *adapter,
+ int queue_index)
{
- return be32_to_cpu(adapter->rx_queue[0].queue_addr[adapter->rx_queue[0].index].length);
+ struct ibmveth_rx_q *rxq = &adapter->rx_queue[queue_index];
+
+ return be32_to_cpu(rxq->queue_addr[rxq->index].length);
}
-static inline int ibmveth_rxq_csum_good(struct ibmveth_adapter *adapter)
+static inline int ibmveth_rxq_csum_good(struct ibmveth_adapter *adapter,
+ int queue_index)
{
- return ibmveth_rxq_flags(adapter) & IBMVETH_RXQ_CSUM_GOOD;
+ return ibmveth_rxq_flags(adapter, queue_index) & IBMVETH_RXQ_CSUM_GOOD;
}
static unsigned int ibmveth_real_max_tx_queues(void)
@@ -274,6 +288,7 @@ ibmveth_alloc_rx_queues(struct ibmveth_adapter *adapter, int rxq_entries)
adapter->rx_queue[i].index = 0;
adapter->rx_queue[i].num_slots = rxq_entries;
adapter->rx_queue[i].toggle = 1;
+ spin_lock_init(&adapter->rx_queue[i].replenish_lock);
netdev_dbg(netdev, "queue %d: buffer_list @ 0x%p (DMA: 0x%llx), rx_queue @ 0x%p (DMA: 0x%llx), %llu entries\n",
i, adapter->buffer_list_addr[i],
@@ -826,15 +841,23 @@ static void ibmveth_replenish_buffer_pool(struct ibmveth_adapter *adapter,
*/
static void ibmveth_update_rx_no_buffer(struct ibmveth_adapter *adapter)
{
- __be64 *p = adapter->buffer_list_addr[0] + 4096 - 8;
+ int i;
+
+ for (i = 0; i < adapter->num_rx_queues; i++) {
+ __be64 *p = adapter->buffer_list_addr[i] + 4096 - 8;
+ u64 drops = be64_to_cpup(p);
- adapter->rx_no_buffer = be64_to_cpup(p);
+ if (i == 0)
+ adapter->rx_no_buffer = drops;
+ }
}
/* replenish routine */
static void ibmveth_replenish_task(struct ibmveth_adapter *adapter,
int queue_index)
{
+ struct ibmveth_rx_q *rxq = &adapter->rx_queue[queue_index];
+ unsigned long flags;
int i;
if (queue_index >= adapter->num_rx_queues)
@@ -842,6 +865,8 @@ static void ibmveth_replenish_task(struct ibmveth_adapter *adapter,
adapter->replenish_task_cycles++;
+ spin_lock_irqsave(&rxq->replenish_lock, flags);
+
for (i = (IBMVETH_NUM_BUFF_POOLS - 1); i >= 0; i--) {
struct ibmveth_buff_pool *pool =
&adapter->rx_buff_pool[queue_index][i];
@@ -853,6 +878,8 @@ static void ibmveth_replenish_task(struct ibmveth_adapter *adapter,
}
ibmveth_update_rx_no_buffer(adapter);
+
+ spin_unlock_irqrestore(&rxq->replenish_lock, flags);
}
/* empty and free ana buffer pool - also used to do cleanup in error paths */
@@ -1028,7 +1055,8 @@ static void ibmveth_free_buffer_pools(struct ibmveth_adapter *adapter)
* * %-EFAULT - pool and index map to null skb
*/
static int ibmveth_remove_buffer_from_pool(struct ibmveth_adapter *adapter,
- u64 correlator, bool reuse)
+ u64 correlator, int queue_index,
+ bool reuse)
{
unsigned int pool = correlator >> 32;
unsigned int index = correlator & 0xffffffffUL;
@@ -1036,12 +1064,12 @@ static int ibmveth_remove_buffer_from_pool(struct ibmveth_adapter *adapter,
struct sk_buff *skb;
if (WARN_ON(pool >= IBMVETH_NUM_BUFF_POOLS) ||
- WARN_ON(index >= adapter->rx_buff_pool[0][pool].size)) {
+ WARN_ON(index >= adapter->rx_buff_pool[queue_index][pool].size)) {
schedule_work(&adapter->work);
return -EINVAL;
}
- skb = adapter->rx_buff_pool[0][pool].skbuff[index];
+ skb = adapter->rx_buff_pool[queue_index][pool].skbuff[index];
if (WARN_ON(!skb)) {
schedule_work(&adapter->work);
return -EFAULT;
@@ -1055,42 +1083,44 @@ static int ibmveth_remove_buffer_from_pool(struct ibmveth_adapter *adapter,
/* remove the skb pointer to mark free. actual freeing is done
* by upper level networking after gro_receive
*/
- adapter->rx_buff_pool[0][pool].skbuff[index] = NULL;
+ adapter->rx_buff_pool[queue_index][pool].skbuff[index] = NULL;
dma_unmap_single(&adapter->vdev->dev,
- adapter->rx_buff_pool[0][pool].dma_addr[index],
- adapter->rx_buff_pool[0][pool].buff_size,
+ adapter->rx_buff_pool[queue_index][pool].dma_addr[index],
+ adapter->rx_buff_pool[queue_index][pool].buff_size,
DMA_FROM_DEVICE);
}
- free_index = adapter->rx_buff_pool[0][pool].producer_index;
- adapter->rx_buff_pool[0][pool].producer_index++;
- if (adapter->rx_buff_pool[0][pool].producer_index >=
- adapter->rx_buff_pool[0][pool].size)
- adapter->rx_buff_pool[0][pool].producer_index = 0;
- adapter->rx_buff_pool[0][pool].free_map[free_index] = index;
+ free_index = adapter->rx_buff_pool[queue_index][pool].producer_index;
+ adapter->rx_buff_pool[queue_index][pool].producer_index++;
+ if (adapter->rx_buff_pool[queue_index][pool].producer_index >=
+ adapter->rx_buff_pool[queue_index][pool].size)
+ adapter->rx_buff_pool[queue_index][pool].producer_index = 0;
+ adapter->rx_buff_pool[queue_index][pool].free_map[free_index] = index;
mb();
- atomic_dec(&adapter->rx_buff_pool[0][pool].available);
+ atomic_dec(&adapter->rx_buff_pool[queue_index][pool].available);
return 0;
}
/* get the current buffer on the rx queue */
-static inline struct sk_buff *ibmveth_rxq_get_buffer(struct ibmveth_adapter *adapter)
+static inline struct sk_buff *ibmveth_rxq_get_buffer(struct ibmveth_adapter *adapter,
+ int queue_index)
{
- u64 correlator = adapter->rx_queue[0].queue_addr[adapter->rx_queue[0].index].correlator;
+ struct ibmveth_rx_q *rxq = &adapter->rx_queue[queue_index];
+ u64 correlator = rxq->queue_addr[rxq->index].correlator;
unsigned int pool = correlator >> 32;
unsigned int index = correlator & 0xffffffffUL;
if (WARN_ON(pool >= IBMVETH_NUM_BUFF_POOLS) ||
- WARN_ON(index >= adapter->rx_buff_pool[0][pool].size)) {
+ WARN_ON(index >= adapter->rx_buff_pool[queue_index][pool].size)) {
schedule_work(&adapter->work);
return NULL;
}
- return adapter->rx_buff_pool[0][pool].skbuff[index];
+ return adapter->rx_buff_pool[queue_index][pool].skbuff[index];
}
/**
@@ -1106,19 +1136,20 @@ static inline struct sk_buff *ibmveth_rxq_get_buffer(struct ibmveth_adapter *ada
* * other - non-zero return from ibmveth_remove_buffer_from_pool
*/
static int ibmveth_rxq_harvest_buffer(struct ibmveth_adapter *adapter,
- bool reuse)
+ int queue_index, bool reuse)
{
+ struct ibmveth_rx_q *rxq = &adapter->rx_queue[queue_index];
u64 cor;
int rc;
- cor = adapter->rx_queue[0].queue_addr[adapter->rx_queue[0].index].correlator;
- rc = ibmveth_remove_buffer_from_pool(adapter, cor, reuse);
+ cor = rxq->queue_addr[rxq->index].correlator;
+ rc = ibmveth_remove_buffer_from_pool(adapter, cor, queue_index, reuse);
if (unlikely(rc))
return rc;
- if (++adapter->rx_queue[0].index == adapter->rx_queue[0].num_slots) {
- adapter->rx_queue[0].index = 0;
- adapter->rx_queue[0].toggle = !adapter->rx_queue[0].toggle;
+ if (++rxq->index == rxq->num_slots) {
+ rxq->index = 0;
+ rxq->toggle = !rxq->toggle;
}
return 0;
@@ -2268,34 +2299,40 @@ static void ibmveth_rx_csum_helper(struct sk_buff *skb,
static int ibmveth_poll(struct napi_struct *napi, int budget)
{
- struct ibmveth_adapter *adapter =
- container_of(napi, struct ibmveth_adapter, napi[0]);
- struct net_device *netdev = adapter->netdev;
+ struct net_device *netdev = napi->dev;
+ struct ibmveth_adapter *adapter = netdev_priv(netdev);
int frames_processed = 0;
unsigned long lpar_rc;
+ int queue_index, rc;
u16 mss = 0;
+ queue_index = napi - adapter->napi;
+
+ if (WARN_ON(queue_index < 0 || queue_index >= adapter->num_rx_queues))
+ return 0;
+
restart_poll:
while (frames_processed < budget) {
- if (!ibmveth_rxq_pending_buffer(adapter))
+ if (!ibmveth_rxq_pending_buffer(adapter, queue_index))
break;
smp_rmb();
- if (!ibmveth_rxq_buffer_valid(adapter)) {
+ if (!ibmveth_rxq_buffer_valid(adapter, queue_index)) {
wmb(); /* suggested by larson1 */
adapter->rx_invalid_buffer++;
netdev_dbg(netdev, "recycling invalid buffer\n");
- if (unlikely(ibmveth_rxq_harvest_buffer(adapter, true)))
+ rc = ibmveth_rxq_harvest_buffer(adapter, queue_index, true);
+ if (unlikely(rc))
break;
} else {
struct sk_buff *skb, *new_skb;
- int length = ibmveth_rxq_frame_length(adapter);
- int offset = ibmveth_rxq_frame_offset(adapter);
- int csum_good = ibmveth_rxq_csum_good(adapter);
- int lrg_pkt = ibmveth_rxq_large_packet(adapter);
+ int length = ibmveth_rxq_frame_length(adapter, queue_index);
+ int offset = ibmveth_rxq_frame_offset(adapter, queue_index);
+ int csum_good = ibmveth_rxq_csum_good(adapter, queue_index);
+ int lrg_pkt = ibmveth_rxq_large_packet(adapter, queue_index);
__sum16 iph_check = 0;
- skb = ibmveth_rxq_get_buffer(adapter);
+ skb = ibmveth_rxq_get_buffer(adapter, queue_index);
if (unlikely(!skb))
break;
@@ -2320,12 +2357,14 @@ static int ibmveth_poll(struct napi_struct *napi, int budget)
length);
if (rx_flush)
ibmveth_flush_buffer(skb->data,
- length + offset);
- if (unlikely(ibmveth_rxq_harvest_buffer(adapter, true)))
+ length + offset);
+ rc = ibmveth_rxq_harvest_buffer(adapter, queue_index, true);
+ if (unlikely(rc))
break;
skb = new_skb;
} else {
- if (unlikely(ibmveth_rxq_harvest_buffer(adapter, false)))
+ rc = ibmveth_rxq_harvest_buffer(adapter, queue_index, false);
+ if (unlikely(rc))
break;
skb_reserve(skb, offset);
}
@@ -2361,7 +2400,7 @@ static int ibmveth_poll(struct napi_struct *napi, int budget)
}
}
- ibmveth_replenish_task(adapter, 0);
+ ibmveth_replenish_task(adapter, queue_index);
if (frames_processed == budget)
goto out;
@@ -2372,15 +2411,19 @@ static int ibmveth_poll(struct napi_struct *napi, int budget)
/* We think we are done - reenable interrupts,
* then check once more to make sure we are done.
*/
- lpar_rc = h_vio_signal(adapter->vdev->unit_address, VIO_IRQ_ENABLE);
- if (WARN_ON(lpar_rc != H_SUCCESS)) {
+ lpar_rc = ibmveth_enable_irq(adapter, queue_index);
+ if (lpar_rc != H_SUCCESS) {
+ netdev_err(netdev,
+ "Failed to enable IRQ for queue %d (rc=0x%lx), scheduling reset\n",
+ queue_index, lpar_rc);
schedule_work(&adapter->work);
goto out;
}
- if (ibmveth_rxq_pending_buffer(adapter) && napi_schedule(napi)) {
- lpar_rc = h_vio_signal(adapter->vdev->unit_address,
- VIO_IRQ_DISABLE);
+ if (ibmveth_rxq_pending_buffer(adapter, queue_index) &&
+ napi_schedule(napi)) {
+ lpar_rc = ibmveth_disable_irq(adapter, queue_index);
+ WARN_ON(lpar_rc != H_SUCCESS);
goto restart_poll;
}
@@ -2511,9 +2554,13 @@ static int ibmveth_change_mtu(struct net_device *dev, int new_mtu)
static void ibmveth_poll_controller(struct net_device *dev)
{
struct ibmveth_adapter *adapter = netdev_priv(dev);
+ int i;
- ibmveth_replenish_task(adapter, 0);
- ibmveth_interrupt(dev->irq, &adapter->napi[0]);
+ for (i = 0; i < adapter->num_rx_queues; i++)
+ ibmveth_replenish_task(adapter, i);
+
+ for (i = 0; i < adapter->num_rx_queues; i++)
+ ibmveth_interrupt(adapter->queue_irq[i], &adapter->napi[i]);
}
#endif
@@ -2531,8 +2578,7 @@ static unsigned long ibmveth_get_desired_dma(struct vio_dev *vdev)
struct ibmveth_adapter *adapter;
struct iommu_table *tbl;
unsigned long ret;
- int i;
- int rxqentries = 1;
+ int i, q;
tbl = get_iommu_table_base(&vdev->dev);
@@ -2547,18 +2593,22 @@ static unsigned long ibmveth_get_desired_dma(struct vio_dev *vdev)
/* add size of mapped tx buffers */
ret += IOMMU_PAGE_ALIGN(IBMVETH_MAX_TX_BUF_SIZE, tbl);
- for (i = 0; i < IBMVETH_NUM_BUFF_POOLS; i++) {
- /* add the size of the active receive buffers */
- if (adapter->rx_buff_pool[0][i].active)
- ret +=
- adapter->rx_buff_pool[0][i].size *
- IOMMU_PAGE_ALIGN(adapter->rx_buff_pool[0][i].
- buff_size, tbl);
- rxqentries += adapter->rx_buff_pool[0][i].size;
- }
- /* add the size of the receive queue entries */
- ret += IOMMU_PAGE_ALIGN(
- rxqentries * sizeof(struct ibmveth_rx_q_entry), tbl);
+ for (q = 0; q < adapter->num_rx_queues; q++) {
+ int rxqentries = 1;
+
+ for (i = 0; i < IBMVETH_NUM_BUFF_POOLS; i++) {
+ /* add the size of the active receive buffers */
+ if (adapter->rx_buff_pool[q][i].active)
+ ret += adapter->rx_buff_pool[q][i].size *
+ IOMMU_PAGE_ALIGN(adapter->rx_buff_pool[q][i].buff_size,
+ tbl);
+ rxqentries += adapter->rx_buff_pool[q][i].size;
+ }
+
+ /* add the size of the receive queue entries */
+ ret += IOMMU_PAGE_ALIGN(rxqentries *
+ sizeof(struct ibmveth_rx_q_entry), tbl);
+ }
return ret;
}
@@ -2660,7 +2710,8 @@ static int ibmveth_probe(struct vio_dev *dev, const struct vio_device_id *id)
return -EINVAL;
}
- netdev = alloc_etherdev_mqs(sizeof(struct ibmveth_adapter), IBMVETH_MAX_QUEUES, 1);
+ netdev = alloc_etherdev_mqs(sizeof(struct ibmveth_adapter),
+ IBMVETH_MAX_QUEUES, IBMVETH_MAX_RX_QUEUES);
if (!netdev)
return -ENOMEM;
@@ -2673,7 +2724,8 @@ static int ibmveth_probe(struct vio_dev *dev, const struct vio_device_id *id)
adapter->mcastFilterSize = be32_to_cpu(*mcastFilterSize_p);
ibmveth_init_link_settings(netdev);
- netif_napi_add_weight(netdev, &adapter->napi[0], ibmveth_poll, 16);
+ for (i = 0; i < IBMVETH_MAX_RX_QUEUES; i++)
+ netif_napi_add_weight(netdev, &adapter->napi[i], ibmveth_poll, 16);
netdev->irq = dev->irq;
netdev->netdev_ops = &ibmveth_netdev_ops;
@@ -2705,16 +2757,27 @@ static int ibmveth_probe(struct vio_dev *dev, const struct vio_device_id *id)
netdev->features |= NETIF_F_FRAGLIST;
}
- /* Initialize queue count - always 1 for now */
- adapter->multi_queue = 0;
- adapter->num_rx_queues = 1;
+ if (ret == H_SUCCESS &&
+ (ret_attr & IBMVETH_ILLAN_RX_MULTI_QUEUE_SUPPORT)) {
+ adapter->multi_queue = 1;
+ adapter->num_rx_queues = min(num_online_cpus(), IBMVETH_DEFAULT_QUEUES);
+ netdev_dbg(netdev, "RX multi queue mode enabled: %d queues\n",
+ adapter->num_rx_queues);
+ } else {
+ adapter->multi_queue = 0;
+ adapter->num_rx_queues = 1;
+ }
if (ret == H_SUCCESS &&
(ret_attr & IBMVETH_ILLAN_RX_MULTI_BUFF_SUPPORT)) {
- adapter->rx_buffers_per_hcall = IBMVETH_MAX_RX_PER_HCALL;
+ if (adapter->multi_queue)
+ adapter->rx_buffers_per_hcall = IBMVETH_MAX_RX_QUEUE;
+ else
+ adapter->rx_buffers_per_hcall = IBMVETH_MAX_RX_REGULAR;
+
netdev_dbg(netdev,
"RX Multi-buffer hcall supported by FW, batch set to %u\n",
- adapter->rx_buffers_per_hcall);
+ adapter->rx_buffers_per_hcall);
} else {
adapter->rx_buffers_per_hcall = 1;
netdev_dbg(netdev,
@@ -3057,17 +3120,23 @@ static void ibmveth_remove_buffer_from_pool_test(struct kunit *test)
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pool->skbuff);
correlator = ((u64)IBMVETH_NUM_BUFF_POOLS << 32) | 0;
- KUNIT_EXPECT_EQ(test, -EINVAL, ibmveth_remove_buffer_from_pool(adapter, correlator, false));
- KUNIT_EXPECT_EQ(test, -EINVAL, ibmveth_remove_buffer_from_pool(adapter, correlator, true));
+ KUNIT_EXPECT_EQ(test, -EINVAL,
+ ibmveth_remove_buffer_from_pool(adapter, correlator, 0, false));
+ KUNIT_EXPECT_EQ(test, -EINVAL,
+ ibmveth_remove_buffer_from_pool(adapter, correlator, 0, true));
correlator = ((u64)0 << 32) | adapter->rx_buff_pool[0][0].size;
- KUNIT_EXPECT_EQ(test, -EINVAL, ibmveth_remove_buffer_from_pool(adapter, correlator, false));
- KUNIT_EXPECT_EQ(test, -EINVAL, ibmveth_remove_buffer_from_pool(adapter, correlator, true));
+ KUNIT_EXPECT_EQ(test, -EINVAL,
+ ibmveth_remove_buffer_from_pool(adapter, correlator, 0, false));
+ KUNIT_EXPECT_EQ(test, -EINVAL,
+ ibmveth_remove_buffer_from_pool(adapter, correlator, 0, true));
correlator = (u64)0 | 0;
pool->skbuff[0] = NULL;
- KUNIT_EXPECT_EQ(test, -EFAULT, ibmveth_remove_buffer_from_pool(adapter, correlator, false));
- KUNIT_EXPECT_EQ(test, -EFAULT, ibmveth_remove_buffer_from_pool(adapter, correlator, true));
+ KUNIT_EXPECT_EQ(test, -EFAULT,
+ ibmveth_remove_buffer_from_pool(adapter, correlator, 0, false));
+ KUNIT_EXPECT_EQ(test, -EFAULT,
+ ibmveth_remove_buffer_from_pool(adapter, correlator, 0, true));
flush_work(&adapter->work);
}
@@ -3111,15 +3180,15 @@ static void ibmveth_rxq_get_buffer_test(struct kunit *test)
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pool->skbuff);
adapter->rx_queue[0].queue_addr[0].correlator = (u64)IBMVETH_NUM_BUFF_POOLS << 32 | 0;
- KUNIT_EXPECT_PTR_EQ(test, NULL, ibmveth_rxq_get_buffer(adapter));
+ KUNIT_EXPECT_PTR_EQ(test, NULL, ibmveth_rxq_get_buffer(adapter, 0));
adapter->rx_queue[0].queue_addr[0].correlator =
(u64)0 << 32 | adapter->rx_buff_pool[0][0].size;
- KUNIT_EXPECT_PTR_EQ(test, NULL, ibmveth_rxq_get_buffer(adapter));
+ KUNIT_EXPECT_PTR_EQ(test, NULL, ibmveth_rxq_get_buffer(adapter, 0));
pool->skbuff[0] = skb;
adapter->rx_queue[0].queue_addr[0].correlator = (u64)0 << 32 | 0;
- KUNIT_EXPECT_PTR_EQ(test, skb, ibmveth_rxq_get_buffer(adapter));
+ KUNIT_EXPECT_PTR_EQ(test, skb, ibmveth_rxq_get_buffer(adapter, 0));
flush_work(&adapter->work);
}
diff --git a/drivers/net/ethernet/ibm/ibmveth.h b/drivers/net/ethernet/ibm/ibmveth.h
index d2ceeccd5fbd..f7b20fd01acb 100644
--- a/drivers/net/ethernet/ibm/ibmveth.h
+++ b/drivers/net/ethernet/ibm/ibmveth.h
@@ -14,6 +14,8 @@
#ifndef _IBMVETH_H
#define _IBMVETH_H
+#include <linux/spinlock_types.h>
+
/* constants for H_MULTICAST_CTRL */
#define IbmVethMcastReceptionModifyBit 0x80000UL
#define IbmVethMcastReceptionEnableBit 0x20000UL
@@ -28,6 +30,7 @@
#define IbmVethMcastRemoveFilter 0x2UL
#define IbmVethMcastClearFilterTable 0x3UL
+#define IBMVETH_ILLAN_RX_MULTI_QUEUE_SUPPORT 0x0000000000080000UL
#define IBMVETH_ILLAN_RX_MULTI_BUFF_SUPPORT 0x0000000000040000UL
#define IBMVETH_ILLAN_LRG_SR_ENABLED 0x0000000000010000UL
#define IBMVETH_ILLAN_LRG_SND_SUPPORT 0x0000000000008000UL
@@ -279,9 +282,11 @@ static inline long h_illan_attributes(unsigned long unit_address,
#define IBMVETH_MAX_TX_BUF_SIZE (1024 * 64)
#define IBMVETH_MAX_QUEUES 16U
#define IBMVETH_DEFAULT_QUEUES 8U
-#define IBMVETH_MAX_RX_QUEUES 1U
+#define IBMVETH_MAX_RX_QUEUES 16U
#define IBMVETH_DEFAULT_RX_QUEUES 1U
-#define IBMVETH_MAX_RX_PER_HCALL 8U
+#define IBMVETH_MAX_RX_REGULAR 8U
+#define IBMVETH_MAX_RX_QUEUE 12U
+#define IBMVETH_MAX_RX_PER_HCALL 12U
static int pool_size[] = { 512, 1024 * 2, 1024 * 16, 1024 * 32, 1024 * 64 };
static int pool_count[] = { 256, 512, 256, 256, 256 };
@@ -336,6 +341,7 @@ struct ibmveth_rx_q {
dma_addr_t queue_dma;
u32 queue_len;
struct ibmveth_rx_q_entry *queue_addr;
+ spinlock_t replenish_lock; /* serializes per-queue buffer replenish */
};
struct ibmveth_adapter {
--
2.39.3 (Apple Git-146)
^ permalink raw reply related
* [PATCH v1 12/18] ibmveth: Add per-queue RX statistics collection and reporting
From: Mingming Cao @ 2026-06-30 14:53 UTC (permalink / raw)
To: netdev
Cc: horms, bjking1, haren, ricklind, mmc, kuba, edumazet, pabeni,
linuxppc-dev, maddy, mpe, Dave Marquardt
In-Reply-To: <cover.1782758799.git.mmc@linux.ibm.com>
Count per-queue RX stats in poll, replenish, and the IRQ handler:
packets, bytes, polls, large_packets, invalid_buffers, no_buffer_drops,
and interrupts. Stop updating netdev->stats.rx_* in poll; totals are
summed from rx_qstats[] in get_stats64(). Per-queue TX stats follow in
the next patch.
Expose the counters via:
- ethtool -S: per-queue rxN_* strings and aggregated invalid/large
packet globals via ibmveth_aggregate_rx_qstats(). pool%d_* reports
queue-0 pool geometry (size, active, available) only: static probe
config used as the template for every queue. Live per-queue pool
usage is exported through sysfs in the next patch.
- get_stats64: sum rx_qstats[] so ip -s and /proc/net/dev report total RX
- ethtool hcall_stats counters and count send_lan on successful TX hcalls
Fix get_channels() reporting: max_rx is IBMVETH_MAX_RX_QUEUES only when
MQ firmware is enabled, rx_count tracks adapter->num_rx_queues.
Signed-off-by: Mingming Cao <mmc@linux.ibm.com>
Reviewed-by: Dave Marquardt <davemarq@linux.ibm.com>
---
drivers/net/ethernet/ibm/ibmveth.c | 152 ++++++++++++++++++++++++++---
1 file changed, 141 insertions(+), 11 deletions(-)
diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c
index 863e5c68b42c..1c08082ffbd6 100644
--- a/drivers/net/ethernet/ibm/ibmveth.c
+++ b/drivers/net/ethernet/ibm/ibmveth.c
@@ -98,7 +98,15 @@ static struct ibmveth_stat ibmveth_stats[] = {
{ "fw_enabled_ipv6_csum", IBMVETH_STAT_OFF(fw_ipv6_csum_support) },
{ "tx_large_packets", IBMVETH_STAT_OFF(tx_large_packets) },
{ "rx_large_packets", IBMVETH_STAT_OFF(rx_large_packets) },
- { "fw_enabled_large_send", IBMVETH_STAT_OFF(fw_large_send_support) }
+ { "fw_enabled_large_send", IBMVETH_STAT_OFF(fw_large_send_support) },
+ { "hcall_reg_lan_queue", IBMVETH_STAT_OFF(hcall_stats.reg_lan_queue) },
+ { "hcall_reg_lan", IBMVETH_STAT_OFF(hcall_stats.reg_lan) },
+ { "hcall_add_bufs_queue", IBMVETH_STAT_OFF(hcall_stats.add_bufs_queue) },
+ { "hcall_add_bufs", IBMVETH_STAT_OFF(hcall_stats.add_bufs) },
+ { "hcall_add_buf", IBMVETH_STAT_OFF(hcall_stats.add_buf) },
+ { "hcall_free_lan_queue", IBMVETH_STAT_OFF(hcall_stats.free_lan_queue) },
+ { "hcall_free_lan", IBMVETH_STAT_OFF(hcall_stats.free_lan) },
+ { "hcall_send_lan", IBMVETH_STAT_OFF(hcall_stats.send_lan) },
};
/* simple methods of getting data from the current rxq entry */
@@ -847,6 +855,8 @@ static void ibmveth_update_rx_no_buffer(struct ibmveth_adapter *adapter)
__be64 *p = adapter->buffer_list_addr[i] + 4096 - 8;
u64 drops = be64_to_cpup(p);
+ if (adapter->rx_qstats)
+ adapter->rx_qstats[i].no_buffer_drops = drops;
if (i == 0)
adapter->rx_no_buffer = drops;
}
@@ -1925,22 +1935,71 @@ static int ibmveth_set_features(struct net_device *dev,
return rc1 ? rc1 : rc2;
}
+/**
+ * ibmveth_aggregate_rx_qstats - Sum per-queue RX stats into globals
+ * @adapter: ibmveth adapter
+ *
+ * Cold path only (ethtool). Keeps legacy global counters meaningful for
+ * tools that read the adapter-level fields in ibmveth_stats[].
+ */
+static void ibmveth_aggregate_rx_qstats(struct ibmveth_adapter *adapter)
+{
+ u64 total_invalid = 0;
+ u64 total_large = 0;
+ int i;
+
+ if (!adapter->rx_qstats)
+ return;
+
+ for (i = 0; i < adapter->num_rx_queues; i++) {
+ total_invalid += adapter->rx_qstats[i].invalid_buffers;
+ total_large += adapter->rx_qstats[i].large_packets;
+ }
+
+ adapter->rx_invalid_buffer = total_invalid;
+ adapter->rx_large_packets = total_large;
+}
+
static void ibmveth_get_strings(struct net_device *dev, u32 stringset, u8 *data)
{
+ struct ibmveth_adapter *adapter = netdev_priv(dev);
+ u8 *p = data;
int i;
if (stringset != ETH_SS_STATS)
return;
- for (i = 0; i < ARRAY_SIZE(ibmveth_stats); i++, data += ETH_GSTRING_LEN)
- memcpy(data, ibmveth_stats[i].name, ETH_GSTRING_LEN);
+ for (i = 0; i < ARRAY_SIZE(ibmveth_stats); i++) {
+ memcpy(p, ibmveth_stats[i].name, ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ }
+
+ for (i = 0; i < adapter->num_rx_queues; i++) {
+ ethtool_sprintf(&p, "rx%d_packets", i);
+ ethtool_sprintf(&p, "rx%d_bytes", i);
+ ethtool_sprintf(&p, "rx%d_interrupts", i);
+ ethtool_sprintf(&p, "rx%d_polls", i);
+ ethtool_sprintf(&p, "rx%d_large_packets", i);
+ ethtool_sprintf(&p, "rx%d_invalid_buffers", i);
+ ethtool_sprintf(&p, "rx%d_no_buffer_drops", i);
+ }
+
+ for (i = 0; i < IBMVETH_NUM_BUFF_POOLS; i++) {
+ ethtool_sprintf(&p, "pool%d_size", i);
+ ethtool_sprintf(&p, "pool%d_active", i);
+ ethtool_sprintf(&p, "pool%d_available", i);
+ }
}
static int ibmveth_get_sset_count(struct net_device *dev, int sset)
{
+ struct ibmveth_adapter *adapter = netdev_priv(dev);
+
switch (sset) {
case ETH_SS_STATS:
- return ARRAY_SIZE(ibmveth_stats);
+ return ARRAY_SIZE(ibmveth_stats) +
+ adapter->num_rx_queues * IBMVETH_NUM_RX_QSTATS +
+ IBMVETH_NUM_BUFF_POOLS * 3;
default:
return -EOPNOTSUPP;
}
@@ -1949,21 +2008,48 @@ static int ibmveth_get_sset_count(struct net_device *dev, int sset)
static void ibmveth_get_ethtool_stats(struct net_device *dev,
struct ethtool_stats *stats, u64 *data)
{
- int i;
struct ibmveth_adapter *adapter = netdev_priv(dev);
+ int i, j;
+
+ ibmveth_aggregate_rx_qstats(adapter);
for (i = 0; i < ARRAY_SIZE(ibmveth_stats); i++)
data[i] = IBMVETH_GET_STAT(adapter, ibmveth_stats[i].offset);
+
+ for (j = 0; j < adapter->num_rx_queues; j++) {
+ if (adapter->rx_qstats) {
+ data[i++] = adapter->rx_qstats[j].packets;
+ data[i++] = adapter->rx_qstats[j].bytes;
+ data[i++] = adapter->rx_qstats[j].interrupts;
+ data[i++] = adapter->rx_qstats[j].polls;
+ data[i++] = adapter->rx_qstats[j].large_packets;
+ data[i++] = adapter->rx_qstats[j].invalid_buffers;
+ data[i++] = adapter->rx_qstats[j].no_buffer_drops;
+ } else {
+ i += IBMVETH_NUM_RX_QSTATS;
+ }
+ }
+
+ for (j = 0; j < IBMVETH_NUM_BUFF_POOLS; j++) {
+ data[i++] = adapter->rx_buff_pool[0][j].size;
+ data[i++] = adapter->rx_buff_pool[0][j].active;
+ data[i++] = atomic_read(&adapter->rx_buff_pool[0][j].available);
+ }
}
static void ibmveth_get_channels(struct net_device *netdev,
struct ethtool_channels *channels)
{
+ struct ibmveth_adapter *adapter = netdev_priv(netdev);
+
channels->max_tx = ibmveth_real_max_tx_queues();
channels->tx_count = netdev->real_num_tx_queues;
- channels->max_rx = netdev->real_num_rx_queues;
- channels->rx_count = netdev->real_num_rx_queues;
+ if (adapter->multi_queue)
+ channels->max_rx = IBMVETH_MAX_RX_QUEUES;
+ else
+ channels->max_rx = 1;
+ channels->rx_count = adapter->num_rx_queues;
}
static int ibmveth_set_channels(struct net_device *netdev,
@@ -2061,6 +2147,7 @@ static int ibmveth_send(struct ibmveth_adapter *adapter,
return 1;
}
+ adapter->hcall_stats.send_lan++;
return 0;
}
@@ -2311,6 +2398,9 @@ static int ibmveth_poll(struct napi_struct *napi, int budget)
if (WARN_ON(queue_index < 0 || queue_index >= adapter->num_rx_queues))
return 0;
+ if (adapter->rx_qstats)
+ adapter->rx_qstats[queue_index].polls++;
+
restart_poll:
while (frames_processed < budget) {
if (!ibmveth_rxq_pending_buffer(adapter, queue_index))
@@ -2319,7 +2409,10 @@ static int ibmveth_poll(struct napi_struct *napi, int budget)
smp_rmb();
if (!ibmveth_rxq_buffer_valid(adapter, queue_index)) {
wmb(); /* suggested by larson1 */
- adapter->rx_invalid_buffer++;
+ if (adapter->rx_qstats)
+ adapter->rx_qstats[queue_index].invalid_buffers++;
+ else
+ adapter->rx_invalid_buffer++;
netdev_dbg(netdev, "recycling invalid buffer\n");
rc = ibmveth_rxq_harvest_buffer(adapter, queue_index, true);
if (unlikely(rc))
@@ -2384,7 +2477,10 @@ static int ibmveth_poll(struct napi_struct *napi, int budget)
if ((length > netdev->mtu + ETH_HLEN) ||
lrg_pkt || iph_check == 0xffff) {
ibmveth_rx_mss_helper(skb, mss, lrg_pkt);
- adapter->rx_large_packets++;
+ if (adapter->rx_qstats)
+ adapter->rx_qstats[queue_index].large_packets++;
+ else
+ adapter->rx_large_packets++;
}
if (csum_good) {
@@ -2394,8 +2490,11 @@ static int ibmveth_poll(struct napi_struct *napi, int budget)
napi_gro_receive(napi, skb); /* send it up */
- netdev->stats.rx_packets++;
- netdev->stats.rx_bytes += length;
+ if (adapter->rx_qstats) {
+ adapter->rx_qstats[queue_index].packets++;
+ adapter->rx_qstats[queue_index].bytes += length;
+ }
+
frames_processed++;
}
}
@@ -2444,6 +2543,9 @@ static irqreturn_t ibmveth_interrupt(int irq, void *dev_instance)
if (WARN_ON(qindex < 0 || qindex >= adapter->num_rx_queues))
return IRQ_NONE;
+ if (adapter->rx_qstats)
+ adapter->rx_qstats[qindex].interrupts++;
+
if (napi_schedule_prep(napi)) {
lpar_rc = ibmveth_disable_irq(adapter, qindex);
WARN_ON(lpar_rc != H_SUCCESS);
@@ -2656,6 +2758,33 @@ static netdev_features_t ibmveth_features_check(struct sk_buff *skb,
return vlan_features_check(skb, features);
}
+/**
+ * ibmveth_get_stats64 - Return aggregated per-queue RX statistics
+ * @dev: network device
+ * @stats: rtnl link statistics storage
+ *
+ * Sums per-queue rx_qstats into rx_packets/rx_bytes for multi-queue mode.
+ * TX counters continue to come from netdev->stats (updated in start_xmit).
+ */
+static void ibmveth_get_stats64(struct net_device *dev,
+ struct rtnl_link_stats64 *stats)
+{
+ struct ibmveth_adapter *adapter = netdev_priv(dev);
+ int i;
+
+ if (adapter->rx_qstats) {
+ for (i = 0; i < adapter->num_rx_queues; i++) {
+ stats->rx_packets += adapter->rx_qstats[i].packets;
+ stats->rx_bytes += adapter->rx_qstats[i].bytes;
+ }
+ }
+
+ stats->tx_packets = dev->stats.tx_packets;
+ stats->tx_bytes = dev->stats.tx_bytes;
+ stats->tx_dropped = dev->stats.tx_dropped;
+ stats->tx_errors = dev->stats.tx_errors;
+}
+
static const struct net_device_ops ibmveth_netdev_ops = {
.ndo_open = ibmveth_open,
.ndo_stop = ibmveth_close,
@@ -2668,6 +2797,7 @@ static const struct net_device_ops ibmveth_netdev_ops = {
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = ibmveth_set_mac_addr,
.ndo_features_check = ibmveth_features_check,
+ .ndo_get_stats64 = ibmveth_get_stats64,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = ibmveth_poll_controller,
#endif
--
2.39.3 (Apple Git-146)
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox