Linux Documentation
 help / color / mirror / Atom feed
* Re: [PATCH net-next v3 07/13] net: ethernet: oa_tc6: Add read_mms/write_mms register access functions
From: Andrew Lunn @ 2026-06-04 22:19 UTC (permalink / raw)
  To: ciprian.regus
  Cc: Parthiban Veerasooran, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Jonathan Corbet,
	Shuah Khan, Heiner Kallweit, Russell King, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, netdev, linux-kernel,
	linux-doc, devicetree
In-Reply-To: <20260604-adin1140-driver-v3-7-5debdb3173c4@analog.com>

> +/**
> + * oa_tc6_read_register_mms - function for reading a MAC-PHY register in a
> + * memory map other than 0.

Is MMS 0 somehow special? Does it not work if passed to this function?

I'm not suggesting all the current calls are replaced with this, just
that the documentation might be inaccurate?

> + * @tc6: oa_tc6 struct.
> + * @mms: Memory map selector for the register.
> + * @address: register address of the MAC-PHY to be read.
> + * @value: value read from the @address register address of the MAC-PHY.
> + *
> + * Return: 0 on success otherwise failed.

Isn't the "otherwise" actually an error code?

	Andrew

^ permalink raw reply

* Re: [PATCH net-next v3 06/13] net: ethernet: oa_tc6: Add the OA_TC6_ prefix to standard registers
From: Andrew Lunn @ 2026-06-04 22:14 UTC (permalink / raw)
  To: ciprian.regus
  Cc: Parthiban Veerasooran, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Jonathan Corbet,
	Shuah Khan, Heiner Kallweit, Russell King, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, netdev, linux-kernel,
	linux-doc, devicetree
In-Reply-To: <20260604-adin1140-driver-v3-6-5debdb3173c4@analog.com>

On Thu, Jun 04, 2026 at 07:32:51PM +0300, Ciprian Regus via B4 Relay wrote:
> From: Ciprian Regus <ciprian.regus@analog.com>
> 
> The OA TC6 standard registers are currently exported in a header file.
> Add the OA_TC6_ prefix to the register address and subfield mask macros
> to avoid future naming conflicts.
> 
> Signed-off-by: Ciprian Regus <ciprian.regus@analog.com>

Reviewed-by: Andrew Lunn <andrew@lunn.ch>

    Andrew

^ permalink raw reply

* Re: [PATCH net-next v3 05/13] net: ethernet: oa_tc6: Export standard defined registers
From: Andrew Lunn @ 2026-06-04 22:13 UTC (permalink / raw)
  To: ciprian.regus
  Cc: Parthiban Veerasooran, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Jonathan Corbet,
	Shuah Khan, Heiner Kallweit, Russell King, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, netdev, linux-kernel,
	linux-doc, devicetree
In-Reply-To: <20260604-adin1140-driver-v3-5-5debdb3173c4@analog.com>

On Thu, Jun 04, 2026 at 07:32:50PM +0300, Ciprian Regus via B4 Relay wrote:
> From: Ciprian Regus <ciprian.regus@analog.com>
> 
> Move defines for standard Open Alliance TC6 register addresses and
> subfields in the oa_tc6's header and add entries for the PHYID and
> CONFIG2. As such, other ethernet drivers that rely on oa_tc6 can use
> them directly.
> 
> Signed-off-by: Ciprian Regus <ciprian.regus@analog.com>

Reviewed-by: Andrew Lunn <andrew@lunn.ch>

    Andrew

^ permalink raw reply

* Re: [RFC PATCH v2 2/6] kcov: add build system support for dataflow instrumentation
From: Nathan Chancellor @ 2026-06-04 21:48 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Yunseong Kim, Ingo Molnar, Juri Lelli, Vincent Guittot,
	Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
	Valentin Schneider, K Prateek Nayak, Dmitry Vyukov,
	Andrey Konovalov, Andrew Morton, Nick Desaulniers, Bill Wendling,
	Justin Stitt, Nicolas Schier, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, Jonathan Corbet, Shuah Khan,
	linux-kernel, kasan-dev, llvm, linux-kbuild, rust-for-linux,
	workflows, linux-doc, Yunseong Kim
In-Reply-To: <20260604084519.GA3126523@noisy.programming.kicks-ass.net>

On Thu, Jun 04, 2026 at 10:45:19AM +0200, Peter Zijlstra wrote:
> On Wed, Jun 03, 2026 at 07:43:29PM +0200, Yunseong Kim wrote:
> > Add CFLAGS_KCOV_DATAFLOW and RUSTFLAGS_KCOV_DATAFLOW exports to
> > scripts/Makefile.kcov, containing:
> >   -fsanitize-coverage=dataflow-args,dataflow-ret -g
> >   (with optional -fno-inline via CONFIG_KCOV_DATAFLOW_NO_INLINE)
> > 
> > scripts/Makefile.lib applies these flags when a module's Makefile sets:
> >   KCOV_DATAFLOW_file.o := y   (per-file)
> >   KCOV_DATAFLOW := y          (per-directory)
> > 
> > Also supports CONFIG_KCOV_DATAFLOW_INSTRUMENT_ALL for global enablement.
> > The flags are only applied to kernel objects (same guard as basic KCOV).
> > 
> > Signed-off-by: Yunseong Kim <yunseong.kim@est.tech>
> > ---
> >  scripts/Makefile.kcov | 6 ++++++
> >  scripts/Makefile.lib  | 7 +++++++
> >  2 files changed, 13 insertions(+)
> > 
> > diff --git a/scripts/Makefile.kcov b/scripts/Makefile.kcov
> > index 78305a84ba9d..101173fe194b 100644
> > --- a/scripts/Makefile.kcov
> > +++ b/scripts/Makefile.kcov
> > @@ -2,10 +2,16 @@
> >  kcov-flags-y					+= -fsanitize-coverage=trace-pc
> >  kcov-flags-$(CONFIG_KCOV_ENABLE_COMPARISONS)	+= -fsanitize-coverage=trace-cmp
> >  
> > +# KCOV dataflow: trace function args and return values
> > +kcov-dataflow-flags-y := -fsanitize-coverage=dataflow-args,dataflow-ret -g
> > +kcov-dataflow-flags-$(CONFIG_KCOV_DATAFLOW_NO_INLINE) += -fno-inline
> 
> https://clang.llvm.org/docs/ClangCommandLineReference.html
> 
> Has no mention of -fno-inline, furthermore, what are the exact
> semantics? Does it inhibit __always_inline?
> 

Based on clang/test/CodeGen/always-inline.c [1], I believe the semantics
are the same as GCC's '-fno-inline' [2], which avoids inlining except
for always_inline functions.

[1]: https://github.com/llvm/llvm-project/blob/1d13b74cf086629d5cdae5f44ef4a62cebcaf3ff/clang/test/CodeGen/always-inline.c
[2]: https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-fno-inline

-- 
Cheers,
Nathan

^ permalink raw reply

* Re: [PATCH v3] killswitch: add per-function short-circuit mitigation primitive
From: Justin Suess @ 2026-06-04 21:18 UTC (permalink / raw)
  To: Sasha Levin
  Cc: Song Liu, linux-kernel, linux-doc, linux-kselftest, bpf,
	live-patching, Greg Kroah-Hartman, Andrew Morton, Jonathan Corbet,
	Mathieu Desnoyers, Joshua Peisach, Florian Weimer, Breno Leitao,
	Anthony Iliopoulos, Michal Hocko, Jiri Olsa
In-Reply-To: <agsVDqdALBoHEHlv@laps>

On Mon, May 18, 2026 at 09:33:02AM -0400, Sasha Levin wrote:
> On Sun, May 17, 2026 at 11:37:36PM -0700, Song Liu wrote:
> > On Sun, May 17, 2026 at 6:49 AM Sasha Levin <sashal@kernel.org> wrote:
> > > * fail_function (CONFIG_FUNCTION_ERROR_INJECTION) is disabled in
> > >   most production kernels. Even where enabled, it only works on
> > >   functions pre-annotated with ALLOW_ERROR_INJECTION() in source -
> > >   no help for a freshly-disclosed CVE. The debugfs UI is blocked by
> > >   lockdown=integrity and the override is probabilistic.
> > > 
> > > * BPF override (bpf_override_return) honors the same
> > >   ALLOW_ERROR_INJECTION() whitelist, and BPF itself is off in many
> > >   production kernels. Even where on, the operator interface is
> > >   "load a verified BPF program," not a one-line write.
> > 
> > If it is OK for killswitch to attach to any kernel functions, do we still
> > need ALLOW_ERROR_INJECTION() for fail_function and BPF
> > override? Shall we instead also allow fail_function and BPF override
> > to attach to any kernel functions?
> 
> I don't think so. ALLOW_ERROR_INJECTION is not a security mechanism, it's an
> integrity/safety mechanism for both bpf and fault injection.
> 
> It protects against a "developer or CI script doing legitimate fault injection
> accidentally panics the box" scenario, not an "attacker gets in" one.
>
At that point why not just make this entire killswitch mechanism an expanded
version of the bpf_override_return helper that doesn't care about ALLOW_ERROR_INJECTION?

Then killswitch mitigations are just BPF programs.

This could be paired with a userspace tool for building and
loading the killswitch programs conveniently.

You can make the helper function only succeed if (CONFIG_KILLSWITCH=y
CONFIG_BPF_KPROBE_OVERRIDE=y etc.) and taint the kernel on the first call.

BPF has the crash_kexec kfunc already that can take down the kernel.
Thus it's not crazy in my opinion to add a helper with a similar intentional
intentional footgun in another kfunc/helper.

We can automatically benefit from BPF signing mechanisms to prevent
unauthorized loading of programs. If killswitch is enabled, users can
restrict unauthorized use of it by restricting the loading of all BPF
programs to those signed w/ the key.

Thanks,
Justin
> -- 
> Thanks,
> Sasha

^ permalink raw reply

* Re: [PATCH v7 00/42] guest_memfd: In-place conversion support
From: Ackerley Tng @ 2026-06-04 21:14 UTC (permalink / raw)
  To: Sean Christopherson
  Cc: Ackerley Tng via B4 Relay, aik, andrew.jones, binbin.wu, brauner,
	chao.p.peng, david, ira.weiny, jmattson, jthoughton, michael.roth,
	oupton, pankaj.gupta, qperret, rick.p.edgecombe, rientjes,
	shivankg, steven.price, tabba, willy, wyihan, yan.y.zhao,
	forkloop, pratyush, suzuki.poulose, aneesh.kumar, liam,
	Paolo Bonzini, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
	Dave Hansen, x86, H. Peter Anvin, Steven Rostedt,
	Masami Hiramatsu, Mathieu Desnoyers, Jonathan Corbet, Shuah Khan,
	Shuah Khan, Vishal Annapurve, Andrew Morton, Chris Li,
	Kairui Song, Kemeng Shi, Nhat Pham, Baoquan He, Barry Song,
	Axel Rasmussen, Yuanchu Xie, Wei Xu, Youngjun Park, Qi Zheng,
	Shakeel Butt, Kiryl Shutsemau, Jason Gunthorpe, Vlastimil Babka,
	kvm, linux-kernel, linux-trace-kernel, linux-doc, linux-kselftest,
	linux-mm, linux-coco
In-Reply-To: <aiHeDZEPkAcWcSkn@google.com>

Sean Christopherson <seanjc@google.com> writes:

> On Wed, Jun 03, 2026, Ackerley Tng wrote:
>> Ackerley Tng via B4 Relay <devnull+ackerleytng.google.com@kernel.org>
>> writes:
>>
>> > This is v7 of guest_memfd in-place conversion support.
>> >
>>
>> Here's the outstanding items after going over everyone's comments
>> including Sashiko's:
>>
>> + KVM: TDX: Make source page optional for KVM_TDX_INIT_MEM_REGION
>>     + Need to move page clearing into __kvm_gmem_get_pfn to resolve
>>       leak where populate can put initialized kernel memory into TDX
>>       guest
>>     + See suggested fix at [1]
>
> That fix works for me.  The initial guest image will typically be a tiny subset
> of guest memory, so unnecessarily zeroing a few pages isn't a performance concern.
>

In regular usage moving the zeroing in [1] doesn't change anything,
since the same zeroing would have first happened when the host faults
the pages to put the initial image. When populating, there's no more
zeroing since it was zeroed.

[1] covers the case where the host doesn't write anything to the pages
and directly tries to populate the pages to the guest.

>> + KVM: guest_memfd: Only prepare folios for private pages,
>>     + s/non-CoCo/CoCo in commit message "INIT_SHARED is about to be
>>       supported for non-CoCo VMs in a later patch in this series
>>     + Use Suggested-by: Michael Roth <michael.roth@amd.com>
>> + KVM: selftests: Test that shared/private status is consistent across
>>   processes
>>     + Improve test reliability using pthread_mutex
>>     + I have a fixup patch offline.
>> 	
>> I would like feedback on these:
>> 	
>> + KVM: selftests: Test conversion with elevated page refcount
>>     + Askar pointed out that soon vmsplice may not pin pages. Should I
>>       pin pages through CONFIG_GUP_TEST like in [2]? I prefer not to
>>       take a dependency on CONFIG_GUP_TEST.
>
> I'm not exactly excited about taking a dependency on CONFIG_GUP_TEST either, but
> it probably is the least awful choice.  E.g. KVM also pins pages is certain flows,
> but we're _also_ actively working to remove the need to pin.
>
> Hmm, maybe IORING_REGISTER_PBUF_RING?  AFAICT, it's almost literally a "pin user
> memory" syscall.
>

Hmm that takes a dependency on io_uring, which isn't always compiled
in. Between CONFIG_IO_URING and CONFIG_GUP_TEST, I'd rather
CONFIG_GUP_TEST.

>> + KVM: selftests: Add script to exercise private_mem_conversions_test
>>     + Would like to know what people think of a wrapper script before
>>       I address Sashiko's comments.
>
> NAK to a wrapper script.  This sounds like a perfect fit for Vipin's selftest
> runner (which I'm like 4 months overdue for reviewing, testing, and merging).
> If the runner _can't_ do what you want, then I'd rather improve the runner.
>
> [*] https://lore.kernel.org/all/20260331194202.1722082-1-vipinsh@google.com
>

Good to know we have this!

Thanks, I'll work on a v8 to clean up the above.

>>
>> [1] https://lore.kernel.org/all/CAEvNRgEVC=fFuKVgZYvWyZD7t_zvUZihFG8hrACjvtkD5cwugw@mail.gmail.com/
>> [2] https://lore.kernel.org/all/baa8838f623102931e755cf34c86314b305af49c.1747264138.git.ackerleytng@google.com/
>>
>> >
>> > [...snip...]
>> >

^ permalink raw reply

* Re: [PATCH v7 00/42] guest_memfd: In-place conversion support
From: Sean Christopherson @ 2026-06-04 20:20 UTC (permalink / raw)
  To: Ackerley Tng
  Cc: Ackerley Tng via B4 Relay, aik, andrew.jones, binbin.wu, brauner,
	chao.p.peng, david, ira.weiny, jmattson, jthoughton, michael.roth,
	oupton, pankaj.gupta, qperret, rick.p.edgecombe, rientjes,
	shivankg, steven.price, tabba, willy, wyihan, yan.y.zhao,
	forkloop, pratyush, suzuki.poulose, aneesh.kumar, liam,
	Paolo Bonzini, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
	Dave Hansen, x86, H. Peter Anvin, Steven Rostedt,
	Masami Hiramatsu, Mathieu Desnoyers, Jonathan Corbet, Shuah Khan,
	Shuah Khan, Vishal Annapurve, Andrew Morton, Chris Li,
	Kairui Song, Kemeng Shi, Nhat Pham, Baoquan He, Barry Song,
	Axel Rasmussen, Yuanchu Xie, Wei Xu, Youngjun Park, Qi Zheng,
	Shakeel Butt, Kiryl Shutsemau, Jason Gunthorpe, Vlastimil Babka,
	kvm, linux-kernel, linux-trace-kernel, linux-doc, linux-kselftest,
	linux-mm, linux-coco
In-Reply-To: <CAEvNRgGpaggjd3=ooyzv7iEbmA-x1mWJHgjLSjPi8=5CPrk-yQ@mail.gmail.com>

On Wed, Jun 03, 2026, Ackerley Tng wrote:
> Ackerley Tng via B4 Relay <devnull+ackerleytng.google.com@kernel.org>
> writes:
> 
> > This is v7 of guest_memfd in-place conversion support.
> >
> 
> Here's the outstanding items after going over everyone's comments
> including Sashiko's:
> 
> + KVM: TDX: Make source page optional for KVM_TDX_INIT_MEM_REGION
>     + Need to move page clearing into __kvm_gmem_get_pfn to resolve
>       leak where populate can put initialized kernel memory into TDX
>       guest
>     + See suggested fix at [1]

That fix works for me.  The initial guest image will typically be a tiny subset
of guest memory, so unnecessarily zeroing a few pages isn't a performance concern.

> + KVM: guest_memfd: Only prepare folios for private pages,
>     + s/non-CoCo/CoCo in commit message "INIT_SHARED is about to be
>       supported for non-CoCo VMs in a later patch in this series
>     + Use Suggested-by: Michael Roth <michael.roth@amd.com>
> + KVM: selftests: Test that shared/private status is consistent across
>   processes
>     + Improve test reliability using pthread_mutex
>     + I have a fixup patch offline.
> 	
> I would like feedback on these:
> 	
> + KVM: selftests: Test conversion with elevated page refcount
>     + Askar pointed out that soon vmsplice may not pin pages. Should I
>       pin pages through CONFIG_GUP_TEST like in [2]? I prefer not to
>       take a dependency on CONFIG_GUP_TEST.

I'm not exactly excited about taking a dependency on CONFIG_GUP_TEST either, but
it probably is the least awful choice.  E.g. KVM also pins pages is certain flows,
but we're _also_ actively working to remove the need to pin.

Hmm, maybe IORING_REGISTER_PBUF_RING?  AFAICT, it's almost literally a "pin user
memory" syscall.

> + KVM: selftests: Add script to exercise private_mem_conversions_test
>     + Would like to know what people think of a wrapper script before
>       I address Sashiko's comments.

NAK to a wrapper script.  This sounds like a perfect fit for Vipin's selftest
runner (which I'm like 4 months overdue for reviewing, testing, and merging).
If the runner _can't_ do what you want, then I'd rather improve the runner.

[*] https://lore.kernel.org/all/20260331194202.1722082-1-vipinsh@google.com

> 
> [1] https://lore.kernel.org/all/CAEvNRgEVC=fFuKVgZYvWyZD7t_zvUZihFG8hrACjvtkD5cwugw@mail.gmail.com/
> [2] https://lore.kernel.org/all/baa8838f623102931e755cf34c86314b305af49c.1747264138.git.ackerleytng@google.com/
> 
> >
> > [...snip...]
> >

^ permalink raw reply

* Re: [PATCH v7 20/42] KVM: SEV: Make 'uaddr' parameter optional for KVM_SEV_SNP_LAUNCH_UPDATE
From: Michael Roth @ 2026-06-04 20:11 UTC (permalink / raw)
  To: Suzuki K Poulose
  Cc: ackerleytng, aik, andrew.jones, binbin.wu, brauner, chao.p.peng,
	david, ira.weiny, jmattson, jthoughton, oupton, pankaj.gupta,
	qperret, rick.p.edgecombe, rientjes, shivankg, steven.price,
	tabba, willy, wyihan, yan.y.zhao, forkloop, pratyush,
	aneesh.kumar, liam, Paolo Bonzini, Sean Christopherson,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
	H. Peter Anvin, Steven Rostedt, Masami Hiramatsu,
	Mathieu Desnoyers, Jonathan Corbet, Shuah Khan, Shuah Khan,
	Vishal Annapurve, Andrew Morton, Chris Li, Kairui Song,
	Kemeng Shi, Nhat Pham, Baoquan He, Barry Song, Axel Rasmussen,
	Yuanchu Xie, Wei Xu, Youngjun Park, Qi Zheng, Shakeel Butt,
	Kiryl Shutsemau, Jason Gunthorpe, Vlastimil Babka, kvm,
	linux-kernel, linux-trace-kernel, linux-doc, linux-kselftest,
	linux-mm, linux-coco
In-Reply-To: <9d15479e-e36b-4865-804c-7d93eb339e4e@arm.com>

On Thu, Jun 04, 2026 at 04:29:19PM +0100, Suzuki K Poulose wrote:
> On 23/05/2026 01:18, Ackerley Tng via B4 Relay wrote:
> > From: Michael Roth <michael.roth@amd.com>
> > 
> > For vm_memory_attributes=1, in-place conversion/population is not
> > supported, so the initial contents necessarily must need to come
> > from a separate src address, which is enforced by the current
> > implementation. However, for vm_memory_attributes=0, it is possible for
> > guest memory to be initialized directly from userspace by mmap()'ing the
> > guest_memfd and writing to it while the corresponding GPA ranges are in
> > a 'shared' state before converting them to the 'private' state expected
> > by KVM_SEV_SNP_LAUNCH_UPDATE.
> > 
> > Update the handling/documentation for KVM_SEV_SNP_LAUNCH_UPDATE to allow
> > for 'uaddr' to be set to NULL when vm_memory_attributes=0, which
> > SNP_LAUNCH_UPDATE will then use to determine when it should/shouldn't
> > copy in data from a separate memory location. Continue to enforce
> > non-NULL for the original vm_memory_attributes=1 case.
> > 
> > Signed-off-by: Michael Roth <michael.roth@amd.com>
> > [Added src_page check in error handling path when the firmware command fails]
> > [Dropped ifdef CONFIG_KVM_VM_MEMORY_ATTRIBUTES]
> > Signed-off-by: Ackerley Tng <ackerleytng@google.com>
> 
> 
> 
> 
> > ---
> >   Documentation/virt/kvm/x86/amd-memory-encryption.rst | 15 +++++++++++----
> >   arch/x86/kvm/svm/sev.c                               | 18 +++++++++++++-----
> >   virt/kvm/kvm_main.c                                  |  1 +
> >   3 files changed, 25 insertions(+), 9 deletions(-)
> > 
> > diff --git a/Documentation/virt/kvm/x86/amd-memory-encryption.rst b/Documentation/virt/kvm/x86/amd-memory-encryption.rst
> > index b2395dd4769de..43085f65b2d85 100644
> > --- a/Documentation/virt/kvm/x86/amd-memory-encryption.rst
> > +++ b/Documentation/virt/kvm/x86/amd-memory-encryption.rst
> > @@ -503,7 +503,8 @@ secrets.
> >   It is required that the GPA ranges initialized by this command have had the
> >   KVM_MEMORY_ATTRIBUTE_PRIVATE attribute set in advance. See the documentation
> > -for KVM_SET_MEMORY_ATTRIBUTES for more details on this aspect.
> > +for KVM_SET_MEMORY_ATTRIBUTES/KVM_SET_MEMORY_ATTRIBUTES2 for more details on
> > +this aspect.
> >   Upon success, this command is not guaranteed to have processed the entire
> >   range requested. Instead, the ``gfn_start``, ``uaddr``, and ``len`` fields of
> > @@ -511,9 +512,15 @@ range requested. Instead, the ``gfn_start``, ``uaddr``, and ``len`` fields of
> >   remaining range that has yet to be processed. The caller should continue
> >   calling this command until those fields indicate the entire range has been
> >   processed, e.g. ``len`` is 0, ``gfn_start`` is equal to the last GFN in the
> > -range plus 1, and ``uaddr`` is the last byte of the userspace-provided source
> > -buffer address plus 1. In the case where ``type`` is KVM_SEV_SNP_PAGE_TYPE_ZERO,
> > -``uaddr`` will be ignored completely.
> > +range plus 1, and ``uaddr`` (if specified) is the last byte of the
> > +userspace-provided source buffer address plus 1.
> > +
> > +In the case where ``type`` is KVM_SEV_SNP_PAGE_TYPE_ZERO, ``uaddr`` will be
> > +ignored completely. Otherwise, ``uaddr`` is required if
> > +kvm.vm_memory_attributes=1 and optional if kvm.vm_memory_attributes=0, since
> > +in the latter case guest memory can be initialized directly from userspace
> > +prior to converting it to private and passing the GPA range on to this
> > +interface.
> 
> Just to confirm, so the sev_gmem_prepare doesn't destroy the contents in the
> process of making it "private" ? i.e., the contents of a SNP shared
> page are preserved while transitioning to "SNP Private" (via RMP
> update).

sev_gmem_prepare() does sort of destroy contents since it finalizes the
shared->private conversion which puts the page in an unusable state
until the guest 'accepts' it as private memory and re-initializes the
contents.

But that's run-time, when the guest is doing conversions. The
documentation here is relating to initialization time when we are
setting up the initial pre-encrypted/pre-measured guest memory image,
via SNP_LAUNCH_UPDATE. That path calls into kvm_gmem_populate(), and it
is then sev_gmem_post_populate() callback that actually finalizes the
shared->private conversion. The sev_gmem_prepare() hook doesn't get used
in this flow (kvm_gmem_populate() calls __kvm_gmem_get_pfn() which skips
preparation).

-Mike

> 
> Suzuki
> 
> 
> 
> >   Parameters (in): struct  kvm_sev_snp_launch_update
> > diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
> > index 1a361f08c7a3d..e1dbc827c2807 100644
> > --- a/arch/x86/kvm/svm/sev.c
> > +++ b/arch/x86/kvm/svm/sev.c
> > @@ -2343,7 +2343,15 @@ static int sev_gmem_post_populate(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn,
> >   	int level;
> >   	int ret;
> > -	if (WARN_ON_ONCE(sev_populate_args->type != KVM_SEV_SNP_PAGE_TYPE_ZERO && !src_page))
> > +	/*
> > +	 * For vm_memory_attributes=1, in-place conversion/population is not
> > +	 * supported, so the initial contents necessarily need to come from a
> > +	 * separate src address. For vm_memory_attributes=0, this isn't
> > +	 * necessarily the case, since the pages may have been populated
> > +	 * directly from userspace before calling KVM_SEV_SNP_LAUNCH_UPDATE.
> > +	 */
> > +	if (vm_memory_attributes &&
> > +	    sev_populate_args->type != KVM_SEV_SNP_PAGE_TYPE_ZERO && !src_page)
> >   		return -EINVAL;
> >   	ret = snp_lookup_rmpentry((u64)pfn, &assigned, &level);
> > @@ -2390,7 +2398,7 @@ static int sev_gmem_post_populate(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn,
> >   	 */
> >   	if (ret && !snp_page_reclaim(kvm, pfn) &&
> >   	    sev_populate_args->type == KVM_SEV_SNP_PAGE_TYPE_CPUID &&
> > -	    sev_populate_args->fw_error == SEV_RET_INVALID_PARAM) {
> > +	    sev_populate_args->fw_error == SEV_RET_INVALID_PARAM && src_page) {
> >   		void *src_vaddr = kmap_local_page(src_page);
> >   		void *dst_vaddr = kmap_local_pfn(pfn);
> > @@ -2423,8 +2431,8 @@ static int snp_launch_update(struct kvm *kvm, struct kvm_sev_cmd *argp)
> >   	if (copy_from_user(&params, u64_to_user_ptr(argp->data), sizeof(params)))
> >   		return -EFAULT;
> > -	pr_debug("%s: GFN start 0x%llx length 0x%llx type %d flags %d\n", __func__,
> > -		 params.gfn_start, params.len, params.type, params.flags);
> > +	pr_debug("%s: GFN start 0x%llx length 0x%llx type %d flags %d src %llx\n", __func__,
> > +		 params.gfn_start, params.len, params.type, params.flags, params.uaddr);
> >   	if (!params.len || !PAGE_ALIGNED(params.len) || params.flags ||
> >   	    (params.type != KVM_SEV_SNP_PAGE_TYPE_NORMAL &&
> > @@ -2481,7 +2489,7 @@ static int snp_launch_update(struct kvm *kvm, struct kvm_sev_cmd *argp)
> >   	params.gfn_start += count;
> >   	params.len -= count * PAGE_SIZE;
> > -	if (params.type != KVM_SEV_SNP_PAGE_TYPE_ZERO)
> > +	if (src && params.type != KVM_SEV_SNP_PAGE_TYPE_ZERO)
> >   		params.uaddr += count * PAGE_SIZE;
> >   	if (copy_to_user(u64_to_user_ptr(argp->data), &params, sizeof(params)))
> > diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
> > index ba195bb239aaa..3bf212fd99193 100644
> > --- a/virt/kvm/kvm_main.c
> > +++ b/virt/kvm/kvm_main.c
> > @@ -105,6 +105,7 @@ module_param(allow_unsafe_mappings, bool, 0444);
> >   #ifdef CONFIG_KVM_VM_MEMORY_ATTRIBUTES
> >   bool vm_memory_attributes = true;
> >   module_param(vm_memory_attributes, bool, 0444);
> > +EXPORT_SYMBOL_FOR_KVM_INTERNAL(vm_memory_attributes);
> >   #endif
> >   DEFINE_STATIC_CALL_RET0(__kvm_get_memory_attributes, kvm_get_memory_attributes_t);
> >   EXPORT_SYMBOL_FOR_KVM_INTERNAL(STATIC_CALL_KEY(__kvm_get_memory_attributes));
> > 
> 

^ permalink raw reply

* Re: [PATCH v16 09/14] iio: test: iio-test-format: add test case for decimal format
From: Andy Shevchenko @ 2026-06-04 19:45 UTC (permalink / raw)
  To: rodrigo.alencar
  Cc: linux-kernel, linux-iio, devicetree, linux-doc, linux,
	Jonathan Cameron, David Lechner, Andy Shevchenko,
	Lars-Peter Clausen, Michael Hennerich, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet, Andrew Morton,
	Petr Mladek, Steven Rostedt, Rasmus Villemoes, Sergey Senozhatsky,
	Shuah Khan
In-Reply-To: <20260604-adf41513-iio-driver-v16-9-1a7d09143bc2@analog.com>

On Thu, Jun 04, 2026 at 10:59:03AM +0100, Rodrigo Alencar via B4 Relay wrote:

> Add iio_test_iio_format_value_decimal_64() kunit test case for decimal
> value formatting, exploring different scales types. Also, the same
> iio_val_s64_decompose() helper used to populate local array is used in
> iio_test_iio_format_value_integer_64().

I missed that. Always fond of test cases!
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply

* Re: [PATCH v16 05/14] lib: kstrtox: add kstrtoudec64() and kstrtodec64()
From: Andy Shevchenko @ 2026-06-04 19:40 UTC (permalink / raw)
  To: rodrigo.alencar
  Cc: linux-kernel, linux-iio, devicetree, linux-doc, linux,
	Jonathan Cameron, David Lechner, Andy Shevchenko,
	Lars-Peter Clausen, Michael Hennerich, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet, Andrew Morton,
	Petr Mladek, Steven Rostedt, Rasmus Villemoes, Sergey Senozhatsky,
	Shuah Khan
In-Reply-To: <20260604-adf41513-iio-driver-v16-5-1a7d09143bc2@analog.com>

On Thu, Jun 04, 2026 at 10:58:59AM +0100, Rodrigo Alencar via B4 Relay wrote:

> Add helpers that parses decimal numbers into 64-bit number, i.e., decimal
> point numbers with pre-defined scale are parsed into a 64-bit value (fixed
> precision). After the decimal point, digits beyond the specified scale
> are ignored.

Okay, this seems universal enough, follows the semantics of kstrto*() family
and gives a good results. Let's make it sail!

Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply

* Re: [PATCH v16 03/14] lib: vsprintf: use _parse_integer() instead of _parse_integer_limit()
From: Andy Shevchenko @ 2026-06-04 19:34 UTC (permalink / raw)
  To: Rodrigo Alencar
  Cc: rodrigo.alencar, linux-kernel, linux-iio, devicetree, linux-doc,
	linux, Jonathan Cameron, David Lechner, Andy Shevchenko,
	Lars-Peter Clausen, Michael Hennerich, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet, Andrew Morton,
	Petr Mladek, Steven Rostedt, Rasmus Villemoes, Sergey Senozhatsky,
	Shuah Khan
In-Reply-To: <a5nlyycxfhwpzczlmud3gjh5qxlugwkow4dyqhtgtjrzjf5q5x@mnmcic2da75r>

On Thu, Jun 04, 2026 at 11:06:55AM +0100, Rodrigo Alencar wrote:
> On 26/06/04 10:58AM, Rodrigo Alencar via B4 Relay wrote:

...

> >  #define _parse_integer0(s, base, res, ...)						\
> >  	_parse_integer_limit(s, base, res, INT_MAX)
> 
> removed the semicolon here...
> 
> > +#define _parse_integer1(s, base, res, max_chars, ...)					\
> > +	_parse_integer_limit(s, base, res, max_chars)
> 
> and here, as per sashiko's feedback:
> https://sashiko.dev/#/patchset/20260602203706.103449-1-andriy.shevchenko%40linux.intel.com?part=1
> https://sashiko.dev/#/patchset/20260602203706.103449-1-andriy.shevchenko%40linux.intel.com?part=2

Legit, thanks for updating!

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply

* Re: [PATCH v16 04/14] lib: kstrtox: add initial value to _parse_integer_limit()
From: Andy Shevchenko @ 2026-06-04 19:31 UTC (permalink / raw)
  To: rodrigo.alencar
  Cc: linux-kernel, linux-iio, devicetree, linux-doc, linux,
	Jonathan Cameron, David Lechner, Andy Shevchenko,
	Lars-Peter Clausen, Michael Hennerich, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet, Andrew Morton,
	Petr Mladek, Steven Rostedt, Rasmus Villemoes, Sergey Senozhatsky,
	Shuah Khan
In-Reply-To: <20260604-adf41513-iio-driver-v16-4-1a7d09143bc2@analog.com>

On Thu, Jun 04, 2026 at 10:58:58AM +0100, Rodrigo Alencar via B4 Relay wrote:

> Add init parameter to _parse_integer_limit() that defines an initial
> value for the accumulated result when parsing an 64-bit integer. The
> new function prototype is adjusted so that the _parse_integer() macros
> stay consistent allowing for one more argument, which defaults to 0.

Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply

* Re: [PATCH v7 20/42] KVM: SEV: Make 'uaddr' parameter optional for KVM_SEV_SNP_LAUNCH_UPDATE
From: Ackerley Tng @ 2026-06-04 19:05 UTC (permalink / raw)
  To: Suzuki K Poulose, aik, andrew.jones, binbin.wu, brauner,
	chao.p.peng, david, ira.weiny, jmattson, jthoughton, michael.roth,
	oupton, pankaj.gupta, qperret, rick.p.edgecombe, rientjes,
	shivankg, steven.price, tabba, willy, wyihan, yan.y.zhao,
	forkloop, pratyush, aneesh.kumar, liam, Paolo Bonzini,
	Sean Christopherson, Thomas Gleixner, Ingo Molnar,
	Borislav Petkov, Dave Hansen, x86, H. Peter Anvin, Steven Rostedt,
	Masami Hiramatsu, Mathieu Desnoyers, Jonathan Corbet, Shuah Khan,
	Shuah Khan, Vishal Annapurve, Andrew Morton, Chris Li,
	Kairui Song, Kemeng Shi, Nhat Pham, Baoquan He, Barry Song,
	Axel Rasmussen, Yuanchu Xie, Wei Xu, Youngjun Park, Qi Zheng,
	Shakeel Butt, Kiryl Shutsemau, Jason Gunthorpe, Vlastimil Babka
  Cc: kvm, linux-kernel, linux-trace-kernel, linux-doc, linux-kselftest,
	linux-mm, linux-coco
In-Reply-To: <9d15479e-e36b-4865-804c-7d93eb339e4e@arm.com>

Suzuki K Poulose <suzuki.poulose@arm.com> writes:

>
> [...snip...]
>
>> +In the case where ``type`` is KVM_SEV_SNP_PAGE_TYPE_ZERO, ``uaddr`` will be
>> +ignored completely. Otherwise, ``uaddr`` is required if
>> +kvm.vm_memory_attributes=1 and optional if kvm.vm_memory_attributes=0, since
>> +in the latter case guest memory can be initialized directly from userspace
>> +prior to converting it to private and passing the GPA range on to this
>> +interface.
>
> Just to confirm, so the sev_gmem_prepare doesn't destroy the contents in
> the process of making it "private" ? i.e., the contents of a SNP shared
> page are preserved while transitioning to "SNP Private" (via RMP
> update).
>
> Suzuki
>

The following is the guest_memfd perspective, I didn't look at the SNP
spec:

Do you mean specifically for KVM_SEV_SNP_PAGE_TYPE_ZERO, or for any
type?

guest_memfd has no plans to do any special zeroing based on type.

guest_memfd decoupled zeroing from preparation a while ago (Michael had
some patches), so zeroing is supposed to be once during folio ownership
by guest_memfd, tracked by the uptodate flag, and preparation is tracked
outside of guest_memfd. So far only SNP does preparation.

>
>
>>
>> [...snip...]
>>

^ permalink raw reply

* Re: [PATCH v2 0/6] alloc_tag: introduce IOCTL-based filtering for MAP
From: Abhishek Bapat @ 2026-06-04 18:24 UTC (permalink / raw)
  To: Suren Baghdasaryan
  Cc: Hao Ge, Andrew Morton, Kent Overstreet, Shuah Khan,
	Jonathan Corbet, linux-doc, linux-kernel, linux-mm, Sourav Panda
In-Reply-To: <CAJuCfpFdbq-0SMVg0i5Fg+gQONQEKTL-N7QC4cyCr1e9oVimSg@mail.gmail.com>

On Wed, Jun 3, 2026 at 12:51 PM Suren Baghdasaryan <surenb@google.com> wrote:
>
> On Mon, May 25, 2026 at 12:33 AM Hao Ge <hao.ge@linux.dev> wrote:
> >
> > Hi Andrew and Suren
> >
> >
> > On 2026/5/23 04:11, Andrew Morton wrote:
> > > On Fri, 22 May 2026 17:45:32 +0000 Abhishek Bapat <abhishekbapat@google.com> wrote:
> > >
> > >> Currently, memory allocation profiling data is primarily exposed through
> > >> /proc/allocinfo. While useful for manual inspection, this text-based
> > >> interface poses challenges for production monitoring and large-scale
> > >> analysis:
> > >>
> > >> 1. Userspace must parse large amounts of text to extract specific
> > >> fields.
> > >> 2. To find specific tags, userspace must read the entire dataset,
> > >> requiring many context switches and high data copying.
> > >> 3. The kernel currently aggregates per-CPU counters for every allocation
> > >> size, even those the user intends to filter out immediately.
> > >>
> > >> This series introduces a new IOCTL-based binary interface for allocinfo
> > >> that supports kernel-side filtering. By allowing the user to specify a
> > >> filter mask, we significantly reduce the work performed in-kernel and
> > >> the amount of data transferred to userspace.
> > >>
> > >> Performance measurements were conducted on an Intel Xeon Platinum 8481C
> > >> (224 CPUs) with caches dropped before each run.
> > >>
> > >> The IOCTL mechanism shows a ~20x performance improvement for
> > >> filtered queries. The kernel avoids the expensive per-CPU counter
> > >> aggregation (alloc_tag_read) for any tags that fail the initial string
> > >> or location filters.
> > >>
> > >> Scenario 1: Specific File Filtering (arch/x86/events/rapl.c)
> > >> 1. Traditional (cat /proc/allocinfo | grep): 22ms (sys)
> > >> 2. IOCTL Interface: 1ms (sys)
> > >>
> > >> Scenario 2: Compound Filtering (Filename + Size)
> > >> 1. Traditional: (cat ... | grep | awk): 21ms (sys)
> > >> 2. IOCTL Interface: 1ms (sys)
> > >>
> > >> Scenario 3: Size-Based Filtering (min_size = 1MB)
> > >> 1. Traditional: (cat ... | awk): 21ms (sys)
> > >> 2. IOCTL Interface: 14ms (sys)
> > > Yup, textual interfaces aren't fast.
> > >
> > > And ioctl-baed interfaces aren't popular.  One would prefer to see an
> > > interface which uses read()/lseek(), pread(), etc.  It would be
> > > appropriate for this [0/N] to have a discussion of why that approach
> > > was not chosen.
> > >
> > >>   .../userspace-api/ioctl/ioctl-number.rst      |   2 +
> > >>   MAINTAINERS                                   |   2 +
> > >>   include/linux/codetag.h                       |   1 +
> > >>   include/uapi/linux/alloc_tag.h                |  87 +++
> > >>   lib/alloc_tag.c                               | 303 ++++++++++-
> > >>   lib/codetag.c                                 |  11 +
> > >>   tools/testing/selftests/alloc_tag/Makefile    |   9 +
> > >>   .../alloc_tag/allocinfo_ioctl_test.c          | 505 ++++++++++++++++++
> > >>   8 files changed, 918 insertions(+), 2 deletions(-)
> > >>   create mode 100644 include/uapi/linux/alloc_tag.h
> > >>   create mode 100644 tools/testing/selftests/alloc_tag/Makefile
> > >>   create mode 100644 tools/testing/selftests/alloc_tag/allocinfo_ioctl_test.c
> > > At some point this should grow user-facing documentation, please.
> > >
> > > And the right time for that is now, because such documentation is
> > > useful for code review - it makes that review both easier and more
> > > useful.
> > >
> > > Sashiko had a few things to say:
> > >
> > >       https://sashiko.dev/#/patchset/cover.1779471082.git.abhishekbapat@google.com
> >
> > I notice that Sashiko has reported a pre-existing issue, as described below:
> >
> >
> >  >  static void *allocinfo_start(struct seq_file *m, loff_t *pos)
> > This is a pre-existing issue, but can resuming a sequential read on
> > /proc/allocinfo cause a use-after-free if a kernel module is unloaded
> > between read() system calls?
> > The seq_file read operation updates priv->iter.ct during allocinfo_next(),
> > stops iteration, and returns to userspace. If the module containing
> > priv->iter.ct is unloaded while the lock is dropped, the module's codetag
> > memory is freed.
> > On the next read() system call, allocinfo_start() with pos > 0 reacquires
> > the lock but returns priv without validating if priv->iter.ct still belongs
> > to a valid module. Does allocinfo_show() then dereference this dangling
> > pointer?
> > [ ... ]
> >
> > This issue is unrelated to the current patch series and can be resolved
> >
> > by reverting commit 9f44df50fee4.
> >
> > Therefore, I have submitted a separate patch addressing this issue,
> >
> > which is available at the link below:
> >
> > https://lore.kernel.org/all/20260525072117.112779-1-hao.ge@linux.dev/
>
> Thanks Hao! I commented on your patch, please take a look. I think
> there is a better fix.
>
> >
> > Thanks
> >
> > Best Regards
> >
> > Hao
> >

All, just wanted to acknowledge that I've gone through the comments
and will be sending out a v3 patchset addressing them. Thanks for the
reviews!

^ permalink raw reply

* Re: [PATCH mm-unstable v18 06/14] mm/khugepaged: generalize collapse_huge_page for mTHP collapse
From: Lorenzo Stoakes @ 2026-06-04 18:12 UTC (permalink / raw)
  To: Nico Pache
  Cc: David Hildenbrand (Arm), Lance Yang, linux-doc, linux-kernel,
	linux-mm, linux-trace-kernel, aarcange, akpm, anshuman.khandual,
	apopple, baohua, baolin.wang, byungchul, catalin.marinas, cl,
	corbet, dave.hansen, dev.jain, gourry, hannes, hughd, jack,
	jackmanb, jannh, jglisse, joshua.hahnjy, kas, liam,
	mathieu.desnoyers, matthew.brost, mhiramat, mhocko, peterx,
	pfalcato, rakie.kim, raquini, rdunlap, richard.weiyang, rientjes,
	rostedt, rppt, ryan.roberts, shivankg, sunnanyong, surenb,
	thomas.hellstrom, tiwai, usamaarif642, vbabka, vishal.moola,
	wangkefeng.wang, will, willy, yang, ying.huang, ziy, zokeefe,
	usama.arif
In-Reply-To: <CAA1CXcBSPVG4CJFCBDvbuodcJ_7eXoDQTpK0ZN0HEhkDPi-DEw@mail.gmail.com>

On Thu, Jun 04, 2026 at 11:04:35AM -0600, Nico Pache wrote:
> On Mon, Jun 1, 2026 at 9:00 AM Nico Pache <npache@redhat.com> wrote:
> >
> > On Mon, Jun 1, 2026 at 5:14 AM David Hildenbrand (Arm) <david@kernel.org> wrote:
> > >
> > > On 6/1/26 12:47, Lance Yang wrote:
> > > >
> > > >
> > > > On 2026/6/1 18:23, David Hildenbrand (Arm) wrote:
> > > >> On 6/1/26 11:08, Lance Yang wrote:
> > > >>>
> > > >>>
> > > >>>
> > > >>> One small thing, I think we should probably keep the smp_wmb(), and just
> > > >>> move it before the earlier pmd_populate().
> > > >>>
> > > >>> IIUC, the ordering we want is still:
> > > >>>
> > > >>>    clear old PTEs
> > > >>>    smp_wmb()
> > > >>>    pmd_populate()
> > > >>>
> > > >>> so another CPU cannot walk through the re-installed PMD and still observe
> > > >>> the old PTEs, right?
> > > >>
> > > >> There is a smp_wmb() in __folio_mark_uptodate(), that should be sufficient?
> > > >
> > > > Ah, cool! __folio_mark_uptodate() already does the job :P
> > > >
> > > > So yeah, no extra smp_wmb() needed here!
> > >
> > > Yeah. BTW, I think we'd need a spin_lock_nested(), so @Nico, treat my code as a
> > > draft.
> >
> > Okay, I read the above and did some investigating.
> >
> > I will try to implement and verify the changes you suggested :)
>
> I've implemented something slightly different actually and I *think* its better!
>
> } else {
>        /* this is map_anon_folio_pte_nopf with no mmu update */
>         __map_anon_folio_pte_nopf(folio, pte, vma, start_addr,
>                       /*uffd_wp=*/ false);
>        smp_wmb();
>         pmd_populate(mm, pmd, pmd_pgtable(_pmd));
>         /*
>          * Some architectures (e.g. MIPS) walk the live page table in
>          * their implementation. update_mmu_cache_range() must be called
>          * with a valid page table hierarchy and the PTE lock held.
>          * Acquire it nested inside pmd_ptl when they are distinct locks.
>          */
>         if (pte_ptl != pmd_ptl)
>             spin_lock_nested(pte_ptl, SINGLE_DEPTH_NESTING);
>         update_mmu_cache_range(NULL, vma, start_addr, pte, nr_pages);
>         if (pte_ptl != pmd_ptl)
>             spin_unlock(pte_ptl);
>     }
> spin_unlock(pmd_ptl);
>
> The logic here is that when the PMD becomes visible, PTEs are already
> populated (no possibility of spurious faults on local CPU)
>
> the SMP_WMB makes sure of the above
>
> And the pmd is installed with the pte and pmd lock both held through
> the mmu_cache update.
>
> This follows the conventions used in pmd_install() and clears the
> potential for local CPU faults hitting cleared PTE entries.
>
> I think both approaches are correct but this prevents any possibility
> of my first point. although mmap_write_lock prevents this too.
>
> Let me know what you think. I can revert to your implementation but
> this is what I tested.

Yeah let's go with the original implementation please :)

Thanks!

>
> Cheers,
> -- Nico
>
> >
> > Or an even crazier idea... what if we ensure MIPS checks for PMD_none
> > before walking a PTE table?
> >
> > -- Nico
> >
> > >
> > > --
> > > Cheers,
> > >
> > > David
> > >
>

Cheers, Lorenzo

^ permalink raw reply

* Re: [PATCH v3 1/4] mm/zswap: Make shrink_worker writeback cursor per-memcg
From: Nhat Pham @ 2026-06-04 17:23 UTC (permalink / raw)
  To: Hao Jia
  Cc: Yosry Ahmed, akpm, tj, hannes, shakeel.butt, mhocko, mkoutny,
	chengming.zhou, muchun.song, roman.gushchin, cgroups, linux-mm,
	linux-kernel, linux-doc, Hao Jia
In-Reply-To: <a60eedb6-f3fd-4092-b726-04a17a695ace@gmail.com>

On Thu, Jun 4, 2026 at 6:06 AM Hao Jia <jiahao.kernel@gmail.com> wrote:
>
>
>
> On 2026/6/4 13:34, Yosry Ahmed wrote:
> >>>> For instance, suppose a parent memcg has two children, memcg1 and memcg2,
> >>>> each with 200MB of zswap (100MB inactive). Triggering proactive writeback on
> >>>> the parent memcg will exhaust memcg1's inactive zswap pages. After that,
> >>>> even though memcg2 still has plenty of inactive zswap pages, it will
> >>>> continue to write back memcg1's active zswap pages. Writing back active
> >>>> zswap pages causes the user-space agent to prematurely abort the writeback
> >>>> because it detects that certain memcg metrics have exceeded predefined
> >>>> thresholds.
> >>>
> >>> This will only happen if the reclaim size is smaller than the batch
> >>> size, right? Otherwise the kernel should reclaim more or less equally
> >>> from both memcgs?
> >>>
> >>
> >> I gave it some thought. Not using a cursor could lead to unfairness
> >> issues with certain writeback sizes:
> >>
> >>    - If the writeback size is an odd multiple of WB_BATCH (e.g.,
> >> triggering a writeback of 3 * WB_BATCH), with 2 child cgroups, the
> >> writeback ratio might end up being 2:1.
> >>    - If a memcg has 5 child cgroups and a writeback of 2 * WB_BATCH is
> >> triggered, it might repeatedly write back from only the first 2 child
> >> cgroups.
> >>
> >> Although setting a smaller WB_BATCH might mitigate this unfairness, it
> >> could hurt writeback efficiency. Let's just use per-memcg cursors to
> >> completely fix these corner cases.
> >
> > Exactly, the batch size should be small enough that any unfairness is
> > not a problem. I would honestly just do batching without a per-memcg
> > cursor, unless we have numbers to prove that the efficiency is
> > affected when we use a small batch size. Let's only introduce
> > complexity when needed please.

I'm impartial towards the complexity of per-memcg cursor. I don't
think it's that big of a deal, but only if it's warranted.

Hao, if you're convinced that doing small batch is not efficient,
could you run some experiments to show the improvement bigger batchign
and fairness? Maybe implement a small batch, no-memcg cursor first.
Then implement a patch on top of it to add per-memcg cursor, and show
how much performance win we can get from that patch on top of the
patch series?

FWIW, zswap writeback right now is not that batch-efficient :) There
is no IO batching, or batched lock operations (we drop the lock
whenever we attempt to writeback a page), etc. Might be a good avenue
to optimize.

^ permalink raw reply

* Re: [PATCH v6 10/11] x86/virt/tdx: Enable Dynamic PAMT
From: Kiryl Shutsemau @ 2026-06-04 17:14 UTC (permalink / raw)
  To: Rick Edgecombe
  Cc: bp, dave.hansen, hpa, kvm, linux-coco, linux-doc, linux-kernel,
	mingo, nik.borisov, pbonzini, seanjc, tglx, vannapurve, x86,
	chao.gao, yan.y.zhao, kai.huang, Kirill A. Shutemov
In-Reply-To: <20260526023515.288829-11-rick.p.edgecombe@intel.com>

On Mon, May 25, 2026 at 07:35:14PM -0700, Rick Edgecombe wrote:
> @@ -152,7 +156,12 @@ const struct tdx_sys_info *tdx_get_sysinfo(void);
>  
>  static inline bool tdx_supports_dynamic_pamt(const struct tdx_sys_info *sysinfo)
>  {
> -	return false; /* To be enabled when kernel is ready */
> +	/*
> +	 * The TDX Module's internal Dynamic PAMT tree structure can't
> +	 * handle physical addresses with more than 48 bits.
> +	 */
> +	return sysinfo->features.tdx_features0 & TDX_FEATURES0_DYNAMIC_PAMT &&
> +	       boot_cpu_data.x86_phys_bits <= 48;

Should we warn for >48?

-- 
  Kiryl Shutsemau / Kirill A. Shutemov

^ permalink raw reply

* Re: [PATCH v6 08/11] x86/tdx: Add APIs to support Dynamic PAMT ops from KVM's fault path
From: Kiryl Shutsemau @ 2026-06-04 17:11 UTC (permalink / raw)
  To: Rick Edgecombe
  Cc: bp, dave.hansen, hpa, kvm, linux-coco, linux-doc, linux-kernel,
	mingo, nik.borisov, pbonzini, seanjc, tglx, vannapurve, x86,
	chao.gao, yan.y.zhao, kai.huang
In-Reply-To: <20260526023515.288829-9-rick.p.edgecombe@intel.com>

On Mon, May 25, 2026 at 07:35:12PM -0700, Rick Edgecombe wrote:
> When handling an EPT violation, KVM holds a spinlock while manipulating
> the EPT. Before entering the spinlock it doesn't know how many EPT page
> tables will need to be installed or whether a huge page will be used. For
> this reason it allocates a worst case number of page tables that it might
> need as part of servicing the EPT violation.
> 
> Under Dynamic PAMT these pre-allocated pages will potentially need to have
> Dynamic PAMT backing pages installed for them. KVM already has helpers to
> manage topping up page caches before taking the MMU lock, but they cannot be
> passed from KVM to arch/x86 code.
> 
> The problem of how and when to install the DPAMT backing pages for the
> pages given to the TDX module during the fault path has had a lot of
> design attempts.
>  - Extracting KVM's MMU caches requires too much inlined code added to
>    headers.
>  - A few varieties of installing Dynamic PAMT backing when allocating the
>    S-EPT page tables. [0][1]
>  - Using mempool_t to transfer the pages between KVM and arch/x86 doesn't
>    work because it is the component is designed more around maintaining a
>    pool of pages, rather than topping up a continually drained cache.
> 
> So don't do these as they all had various problems. Instead just create a
> small simple data structure to use for handing a pre-allocated list of
> pages between KVM and arch/x86 code. Model this on KVM's existing MMU
> memory caches.
> 
> Add a tdx_pamt_cache arg to tdx_pamt_get() so it can draw pages from a
> cache when needed. Not all DPAMT page installations will happen under
> spinlock, for example control pages. So have tdx_pamt_get() maintain the
> existing behavior of allocating from the page allocator when NULL is
> passed for the struct tdx_pamt_cache arg. This prevents excess allocations
> for cases where it can be avoided.
> 
> Export the new helpers for KVM.
> 
> Assisted-by: GitHub Copilot:claude-opus-4-6 Claude:claude-opus-4-7
> Co-developed-by: Sean Christopherson <seanjc@google.com>
> Signed-off-by: Sean Christopherson <seanjc@google.com>
> Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
> Link: https://lore.kernel.org/kvm/de05853257e9cc66998101943f78a4b7e6e3d741.camel@intel.com/ [0]
> Link: https://lore.kernel.org/kvm/aYprxnSHKHUtk7pt@google.com/ [1]

Reviewed-by: Kiryl Shutsemau (Meta) <kas@kernel.org>

-- 
  Kiryl Shutsemau / Kirill A. Shutemov

^ permalink raw reply

* Re: [PATCH mm-unstable v18 06/14] mm/khugepaged: generalize collapse_huge_page for mTHP collapse
From: Nico Pache @ 2026-06-04 17:04 UTC (permalink / raw)
  To: David Hildenbrand (Arm)
  Cc: Lance Yang, linux-doc, linux-kernel, linux-mm, linux-trace-kernel,
	aarcange, akpm, anshuman.khandual, apopple, baohua, baolin.wang,
	byungchul, catalin.marinas, cl, corbet, dave.hansen, dev.jain,
	gourry, hannes, hughd, jack, jackmanb, jannh, jglisse,
	joshua.hahnjy, kas, liam, ljs, mathieu.desnoyers, matthew.brost,
	mhiramat, mhocko, peterx, pfalcato, rakie.kim, raquini, rdunlap,
	richard.weiyang, rientjes, rostedt, rppt, ryan.roberts, shivankg,
	sunnanyong, surenb, thomas.hellstrom, tiwai, usamaarif642, vbabka,
	vishal.moola, wangkefeng.wang, will, willy, yang, ying.huang, ziy,
	zokeefe, usama.arif
In-Reply-To: <CAA1CXcAeEGOsqp-ywAQ7GMYQzXEeco-rUxUkk2hEF69HybC4=w@mail.gmail.com>

On Mon, Jun 1, 2026 at 9:00 AM Nico Pache <npache@redhat.com> wrote:
>
> On Mon, Jun 1, 2026 at 5:14 AM David Hildenbrand (Arm) <david@kernel.org> wrote:
> >
> > On 6/1/26 12:47, Lance Yang wrote:
> > >
> > >
> > > On 2026/6/1 18:23, David Hildenbrand (Arm) wrote:
> > >> On 6/1/26 11:08, Lance Yang wrote:
> > >>>
> > >>>
> > >>>
> > >>> One small thing, I think we should probably keep the smp_wmb(), and just
> > >>> move it before the earlier pmd_populate().
> > >>>
> > >>> IIUC, the ordering we want is still:
> > >>>
> > >>>    clear old PTEs
> > >>>    smp_wmb()
> > >>>    pmd_populate()
> > >>>
> > >>> so another CPU cannot walk through the re-installed PMD and still observe
> > >>> the old PTEs, right?
> > >>
> > >> There is a smp_wmb() in __folio_mark_uptodate(), that should be sufficient?
> > >
> > > Ah, cool! __folio_mark_uptodate() already does the job :P
> > >
> > > So yeah, no extra smp_wmb() needed here!
> >
> > Yeah. BTW, I think we'd need a spin_lock_nested(), so @Nico, treat my code as a
> > draft.
>
> Okay, I read the above and did some investigating.
>
> I will try to implement and verify the changes you suggested :)

I've implemented something slightly different actually and I *think* its better!

} else {
       /* this is map_anon_folio_pte_nopf with no mmu update */
        __map_anon_folio_pte_nopf(folio, pte, vma, start_addr,
                      /*uffd_wp=*/ false);
       smp_wmb();
        pmd_populate(mm, pmd, pmd_pgtable(_pmd));
        /*
         * Some architectures (e.g. MIPS) walk the live page table in
         * their implementation. update_mmu_cache_range() must be called
         * with a valid page table hierarchy and the PTE lock held.
         * Acquire it nested inside pmd_ptl when they are distinct locks.
         */
        if (pte_ptl != pmd_ptl)
            spin_lock_nested(pte_ptl, SINGLE_DEPTH_NESTING);
        update_mmu_cache_range(NULL, vma, start_addr, pte, nr_pages);
        if (pte_ptl != pmd_ptl)
            spin_unlock(pte_ptl);
    }
spin_unlock(pmd_ptl);

The logic here is that when the PMD becomes visible, PTEs are already
populated (no possibility of spurious faults on local CPU)

the SMP_WMB makes sure of the above

And the pmd is installed with the pte and pmd lock both held through
the mmu_cache update.

This follows the conventions used in pmd_install() and clears the
potential for local CPU faults hitting cleared PTE entries.

I think both approaches are correct but this prevents any possibility
of my first point. although mmap_write_lock prevents this too.

Let me know what you think. I can revert to your implementation but
this is what I tested.

Cheers,
-- Nico

>
> Or an even crazier idea... what if we ensure MIPS checks for PMD_none
> before walking a PTE table?
>
> -- Nico
>
> >
> > --
> > Cheers,
> >
> > David
> >


^ permalink raw reply

* Re: [PATCH v6 06/11] x86/virt/tdx: Optimize tdx_pamt_get/put()
From: Kiryl Shutsemau @ 2026-06-04 16:59 UTC (permalink / raw)
  To: Edgecombe, Rick P
  Cc: Gao, Chao, kvm@vger.kernel.org, linux-coco@lists.linux.dev,
	Huang, Kai, Hansen, Dave, Zhao, Yan Y, seanjc@google.com,
	mingo@redhat.com, linux-kernel@vger.kernel.org,
	pbonzini@redhat.com, nik.borisov@suse.com,
	linux-doc@vger.kernel.org, hpa@zytor.com, tglx@kernel.org,
	Annapurve, Vishal, bp@alien8.de, kirill.shutemov@linux.intel.com,
	x86@kernel.org
In-Reply-To: <fe08f03a22acfe758cd97f7c2880deeafbc5fe58.camel@intel.com>

On Tue, May 26, 2026 at 04:42:24PM +0000, Edgecombe, Rick P wrote:
> On Tue, 2026-05-26 at 16:57 +0800, Chao Gao wrote:
> > > -	scoped_guard(spinlock, &pamt_lock) {
> > 
> > This converts the scoped_guard() added by the previous patch to
> > explicit lock/unlock and goto. It would reduce code churn if the
> > previous patch used that form directly.
> 
> Yea, it's a good point. I actually debated doing it, but decided not to because
> the scoped version is cleaner for the non-optimized version. But for
> reviewability, never doing the scoped version is probably better.

I don't see a reason why we can't keep the scoped_guard() on get side.

On put side, we cannot get atomic_get_and_lock() semantics without
dropping the scoped_guard().

Maybe we should keep it for get?

-- 
  Kiryl Shutsemau / Kirill A. Shutemov

^ permalink raw reply

* Re: [PATCH net-next v3 01/13] dt-bindings: net: Add ADIN1140
From: Conor Dooley @ 2026-06-04 16:39 UTC (permalink / raw)
  To: ciprian.regus
  Cc: Parthiban Veerasooran, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Jonathan Corbet,
	Shuah Khan, Andrew Lunn, Heiner Kallweit, Russell King,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, netdev,
	linux-kernel, linux-doc, devicetree
In-Reply-To: <20260604-adin1140-driver-v3-1-5debdb3173c4@analog.com>

[-- Attachment #1: Type: text/plain, Size: 1128 bytes --]

On Thu, Jun 04, 2026 at 07:32:46PM +0300, Ciprian Regus via B4 Relay wrote:
> From: Ciprian Regus <ciprian.regus@analog.com>
> 
> The ADIN1140 is a single port 10BASE-T1S Ethernet controller that
> includes both the MAC and a PHY in the same package.
> 
> Signed-off-by: Ciprian Regus <ciprian.regus@analog.com>
> 
> ---
> v3 changelog:
>  - set adi,ad3306 as a fallback compatible.
> v2 changelog:
>  - Reorder the compatible entries in the dt schema (ad3306, adin1140).
>  - Removed "dt-bindings" from the commit title and message.
>  - Updated the DT example to use IRQ_TYPE_LEVEL_LOW instead of
>    IRQ_TYPE_EDGE_FALLING for the interrupt trigger condition.
>  - "implements" -> "tries to implement" in the description.
>  - Removed the MAINTAINERS entry, as it will be added in a later patch
>    in the series.
>  - Reordered as the first patch of the series
> ---
>  .../devicetree/bindings/net/adi,adin1140.yaml      | 71 ++++++++++++++++++++++

File should probably be named after the fallback compatible, but
otherwise this looks okay.
Reviewed-by: Conor Dooley <conor.dooley@microchip.com>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply

* [PATCH net-next v3 12/13] net: phy: Add support for the ADIN1140 PHY
From: Ciprian Regus via B4 Relay @ 2026-06-04 16:32 UTC (permalink / raw)
  To: Parthiban Veerasooran, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Jonathan Corbet,
	Shuah Khan, Andrew Lunn, Heiner Kallweit, Russell King,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: netdev, linux-kernel, linux-doc, devicetree, Ciprian Regus
In-Reply-To: <20260604-adin1140-driver-v3-0-5debdb3173c4@analog.com>

From: Ciprian Regus <ciprian.regus@analog.com>

Add a driver for the ADIN1140's internal 10BASE-T1S PHY. The device
doesn't implement autonegotiation, so the link is always reported as
being up.

The device implements both C22 and C45 MDIO access methods, but can only
be discovered over C22, since the C45 MMD devices lack the MDIO_DEVID1 and
MDIO_DEVID2 registers. The indirect C45 over C22 feature is not
supported.

Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Ciprian Regus <ciprian.regus@analog.com>
---
v3 changelog:
 - no change
v2 changelog:
 - No longer setting PHY_MAC_INTERRUPT in order to avoid state polling.
 - Replace the driver specific .read/write_mmd() functions with the ones
   exported from genphy.
 - Renamed the file to adin1140-phy.c in order to avoid module name
   conflicts with the adin1140 ethernet driver.
---
 MAINTAINERS                    |  7 ++++
 drivers/net/phy/Kconfig        |  6 ++++
 drivers/net/phy/Makefile       |  1 +
 drivers/net/phy/adin1140-phy.c | 72 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 86 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index ca6c7425b45f..eda74f3154dc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1857,6 +1857,13 @@ S:	Supported
 W:	https://ez.analog.com/linux-software-drivers
 F:	drivers/dma/dma-axi-dmac.c
 
+ANALOG DEVICES INC ETHERNET PHY DRIVERS
+M:	Ciprian Regus <ciprian.regus@analog.com>
+L:	netdev@vger.kernel.org
+S:	Maintained
+W:	https://ez.analog.com/linux-software-drivers
+F:	drivers/net/phy/adin1140-phy.c
+
 ANALOG DEVICES INC IIO DRIVERS
 M:	Lars-Peter Clausen <lars@metafoo.de>
 M:	Michael Hennerich <Michael.Hennerich@analog.com>
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 8b51bdc2e945..bd21a5dad366 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -124,6 +124,12 @@ config ADIN1100_PHY
 	  Currently supports the:
 	  - ADIN1100 - Robust,Industrial, Low Power 10BASE-T1L Ethernet PHY
 
+config ADIN1140_PHY
+	tristate "Analog Devices ADIN1140 10BASE-T1S PHY"
+	help
+	  Adds support for the Analog Devices, Inc. ADIN1140's internal
+	  10BASE-T1S PHY.
+
 config AMCC_QT2025_PHY
 	tristate "AMCC QT2025 PHY"
 	depends on RUST_PHYLIB_ABSTRACTIONS
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 05e4878af27a..73152845b0b2 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -29,6 +29,7 @@ obj-y				+= $(sfp-obj-y) $(sfp-obj-m)
 
 obj-$(CONFIG_ADIN_PHY)		+= adin.o
 obj-$(CONFIG_ADIN1100_PHY)	+= adin1100.o
+obj-$(CONFIG_ADIN1140_PHY)	+= adin1140-phy.o
 obj-$(CONFIG_AIR_EN8811H_PHY)   += air_en8811h.o
 obj-$(CONFIG_AMD_PHY)		+= amd.o
 obj-$(CONFIG_AMCC_QT2025_PHY)	+= qt2025.o
diff --git a/drivers/net/phy/adin1140-phy.c b/drivers/net/phy/adin1140-phy.c
new file mode 100644
index 000000000000..d35da4ad680d
--- /dev/null
+++ b/drivers/net/phy/adin1140-phy.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Driver for Analog Devices, Inc. ADIN1140 10BASE-T1S PHY
+ *
+ * Copyright 2026 Analog Devices Inc.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/phy.h>
+
+#define ADIN1140_PHY_ID		0x0283be00
+
+#define ADIN1140_PCS_CTRL		0x08f3
+#define ADIN1140_PCS_CTRL_LOOPBACK	BIT(14)
+
+static int adin1140_config_aneg(struct phy_device *phydev)
+{
+	/* phylib tries to clear BIT(12) in MDIO_CTRL1, since AN is disabled.
+	 * However, on the ADIN1140, that field is non-standard, being used
+	 * to control the reset status of the PHY (thus it needs to remain set).
+	 */
+	return 0;
+}
+
+static int adin1140_loopback(struct phy_device *phydev, bool enable, int speed)
+{
+	if (enable && speed)
+		return -EOPNOTSUPP;
+
+	return phy_modify_mmd(phydev, MDIO_MMD_PCS, ADIN1140_PCS_CTRL,
+			      ADIN1140_PCS_CTRL_LOOPBACK,
+			      enable ? ADIN1140_PCS_CTRL_LOOPBACK : 0);
+}
+
+static int adin1140_read_status(struct phy_device *phydev)
+{
+	phydev->link = 1;
+	phydev->duplex = DUPLEX_HALF;
+	phydev->speed = SPEED_10;
+	phydev->autoneg = AUTONEG_DISABLE;
+
+	return 0;
+}
+
+static struct phy_driver adin1140_driver[] = {
+	{
+		PHY_ID_MATCH_EXACT(ADIN1140_PHY_ID),
+		.name = "ADIN1140_PHY",
+		.features = PHY_BASIC_T1S_P2MP_FEATURES,
+		.read_status = adin1140_read_status,
+		.config_aneg = adin1140_config_aneg,
+		.set_loopback = adin1140_loopback,
+		.read_mmd = genphy_read_mmd_c45,
+		.write_mmd = genphy_write_mmd_c45,
+		.get_plca_cfg = genphy_c45_plca_get_cfg,
+		.set_plca_cfg = genphy_c45_plca_set_cfg,
+		.get_plca_status = genphy_c45_plca_get_status,
+	},
+};
+module_phy_driver(adin1140_driver);
+
+static const struct mdio_device_id __maybe_unused adin1140_tbl[] = {
+	{ PHY_ID_MATCH_EXACT(ADIN1140_PHY_ID) },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(mdio, adin1140_tbl);
+
+MODULE_DESCRIPTION("Analog Devices, Inc. ADIN1140 10BASE-T1S PHY");
+MODULE_AUTHOR("Ciprian Regus <ciprian.regus@analog.com>");
+MODULE_LICENSE("GPL");

-- 
2.43.0



^ permalink raw reply related

* [PATCH net-next v3 13/13] net: ethernet: adi: Add a driver for the ADIN1140 MACPHY
From: Ciprian Regus via B4 Relay @ 2026-06-04 16:32 UTC (permalink / raw)
  To: Parthiban Veerasooran, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Jonathan Corbet,
	Shuah Khan, Andrew Lunn, Heiner Kallweit, Russell King,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: netdev, linux-kernel, linux-doc, devicetree, Ciprian Regus
In-Reply-To: <20260604-adin1140-driver-v3-0-5debdb3173c4@analog.com>

From: Ciprian Regus <ciprian.regus@analog.com>

Add a driver for ADIN1140. The device is a 10BASE-T1S MAC-PHY
(integrated in the same package) that connects to a CPU over an SPI bus,
and implements the Open Alliance TC6 protocol for control and frame
transfers. As such, this driver relies on oa_tc6 for the communication
with the device. The device has an alternative name (AD3306), so the
driver can be probed using one of the two compatible strings.

For control transactions, ADIN1140 only implements the protected mode.
The driver has a custom implementation for the mii_bus access methods as a
workaround for hardware issues:

1. The OA TC6 standard defines the direct and indirect access modes for
   MDIO transactions. The ADIN1140 incorrectly advertises indirect mode
   only (supported capabilities register - 0x2, bit 9), while actually
   implementing just the direct mode. We cannot rely on the CAP register
   to choose an access method (which oa_tc6 does by default, even though
   it only implements the direct mode), so the driver has to use its
   own.
2. The ADIN1140 cannot access the C22 register space of the internal
   PHY, while the PHY is busy receiving frames. If that happens, the
   CONFIG0 and CONFIG2 registers of the MAC will get corrupted and the
   data transfer will stop. Those two registers configure settings for
   the transfer protocol between the MAC and host, so the value for some
   of their subfields shouldn't be changed while the netdev is up.
   Since we know the PHY is internal, the MAC driver can implement a
   custom mii_bus, which can intercept C22 accesses. Most of the
   registers mapped in the 0x0 - 0x3 range (the only ones the PHY offers)
   are read only, and their value can be read from somewhere else (e.g
   the PHYID 1 & 2 have the same value as 0x1 in the MAC memory map).
   For the fields that are R/W (loopback and AN/reset) in the control
   register, the PHY driver already implements the set_loopback() and
   config_aneg() functions. The C22 write function of the driver is a
   no-op and is used to protect against the ioctl MDIO access path.
   C45 accesses do not cause this issue, so we can properly implement
   them.

Signed-off-by: Ciprian Regus <ciprian.regus@analog.com>

---
v3 changelog:
 - Clear the unused destination address filter slots in
   adin1140_rx_mode_work(). This is required in case we remove
   multicast or unicast addresses from a netdev.
 - The device only allows destination MAC masks for the first 2 slots.
   Fix adin1140_mac_filter_set() to take this into account.
 - use oa_tc6_{write,read}_register_mms, since the OA_TC6_MMS_REG()
   macro was removed. Update the register address defines accordingly.
 - use the devres API for mdio and netdev alloc/register instead of
   manually managing those.
 - use dev_err_probe() in several places to simplify logging during
   probe.
 - use scoped_guard() instead of spin_lock/unlock(). Had to break some
   sequences in their own function to fit the 80 character limit.
 - fix the comment describing the reason for skb padding in the TX path.
v2 changelog:
 - Exported statistics that match the ethtool_stats entries as such and
   kept the other ones custom, using ethtool strings.
 - Used phy_do_ioctl_running() for ndo_eth_ioctl.
 - Adapted the mii_bus and PHY handling to the newly added
   OA_TC6_BROKEN_PHY flag for oa_tc6.
 - Used the oa_tc6_mdiobus_read_c45/oa_tc6_mdiobus_write_c45 functions
   for the C45 read/write mii_bus operations.
 - Removed OA TC6 register definitions (e.g CONFIG2) from the adin1140
   driver and instead used the ones exported from oa_tc6.h
 - Used OA_TC6_MMS_REG to define MMS registers instead of
   ADIN1140_MMS_REG.
 - Returned default values for the MII_PHYSID1/MII_PHYSID2.
 - Set the mii_bus->phy_mask, since the the same PHY will be registered
   32 times otherwise.
 - Updated the MAINTAINERS entry to include the dt-bindings.
---
 MAINTAINERS                         |   8 +
 drivers/net/ethernet/adi/Kconfig    |  12 +
 drivers/net/ethernet/adi/Makefile   |   1 +
 drivers/net/ethernet/adi/adin1140.c | 815 ++++++++++++++++++++++++++++++++++++
 4 files changed, 836 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index eda74f3154dc..3d6da16c4312 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1857,6 +1857,14 @@ S:	Supported
 W:	https://ez.analog.com/linux-software-drivers
 F:	drivers/dma/dma-axi-dmac.c
 
+ANALOG DEVICES INC ETHERNET DRIVERS
+M:	Ciprian Regus <ciprian.regus@analog.com>
+L:	netdev@vger.kernel.org
+S:	Maintained
+W:	https://ez.analog.com/linux-software-drivers
+F:	Documentation/devicetree/bindings/net/adi,adin1140.yaml
+F:	drivers/net/ethernet/adi/adin1140.c
+
 ANALOG DEVICES INC ETHERNET PHY DRIVERS
 M:	Ciprian Regus <ciprian.regus@analog.com>
 L:	netdev@vger.kernel.org
diff --git a/drivers/net/ethernet/adi/Kconfig b/drivers/net/ethernet/adi/Kconfig
index 760a9a60bc15..bdb8ff7d15da 100644
--- a/drivers/net/ethernet/adi/Kconfig
+++ b/drivers/net/ethernet/adi/Kconfig
@@ -26,4 +26,16 @@ config ADIN1110
 	  Say yes here to build support for Analog Devices ADIN1110
 	  Low Power 10BASE-T1L Ethernet MAC-PHY.
 
+config ADIN1140
+	tristate "Analog Devices ADIN1140 MAC-PHY"
+	depends on SPI
+	select ADIN1140_PHY
+	select OA_TC6
+	help
+	  Say yes here to build support for Analog Devices, Inc. ADIN1140
+	  10BASE-T1S Ethernet MAC-PHY.
+
+	  To compile this driver as a module, choose M here. The module will be
+	  called adin1140.
+
 endif # NET_VENDOR_ADI
diff --git a/drivers/net/ethernet/adi/Makefile b/drivers/net/ethernet/adi/Makefile
index d0383d94303c..0390ca8ccc49 100644
--- a/drivers/net/ethernet/adi/Makefile
+++ b/drivers/net/ethernet/adi/Makefile
@@ -4,3 +4,4 @@
 #
 
 obj-$(CONFIG_ADIN1110) += adin1110.o
+obj-$(CONFIG_ADIN1140) += adin1140.o
diff --git a/drivers/net/ethernet/adi/adin1140.c b/drivers/net/ethernet/adi/adin1140.c
new file mode 100644
index 000000000000..358e9a11c993
--- /dev/null
+++ b/drivers/net/ethernet/adi/adin1140.c
@@ -0,0 +1,815 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Driver for Analog Devices, Inc. ADIN1140 10BASE-T1S MAC-PHY
+ *
+ * Copyright 2026 Analog Devices Inc.
+ */
+
+#include <linux/cleanup.h>
+#include <linux/etherdevice.h>
+#include <linux/kernel.h>
+#include <linux/mdio.h>
+#include <linux/module.h>
+#include <linux/oa_tc6.h>
+#include <linux/phy.h>
+
+#define ADIN1140_CONFIG2_FWD_UNK2HOST	BIT(2)
+
+#define ADIN1140_MAC_P1_LOOP_ADDR_REG	0xC4
+
+#define ADIN1140_MAC_ADDR_FILT_UPR_REG		0x50
+#define ADIN1140_MAC_ADDR_FILT_APPLY2PORT1	BIT(30)
+#define ADIN1140_MAC_ADDR_FILT_TO_HOST		BIT(16)
+
+#define ADIN1140_MAC_ADDR_FILT_LWR_REG		0x51
+
+#define ADIN1140_MAC_ADDR_MASK_UPR_REG	0x70
+#define ADIN1140_MAC_ADDR_MASK_LWR_REG	0x71
+
+#define ADIN1140_MAC_FILT_MC_SLOT	0U
+#define ADIN1140_MAC_FILT_BC_SLOT	1U
+#define ADIN1140_MAC_FILT_UC_SLOT	2U
+#define ADIN1140_MAC_FILT_MAX_SLOT	16U
+#define ADIN1140_MAC_FILT_MASK_LIMIT	2U
+
+#define ADIN1140_RX_FRAME_CNT		0xA1
+#define ADIN1140_RX_BC_FRAME_CNT	0xA2
+#define ADIN1140_RX_MC_FRAME_CNT	0xA3
+#define ADIN1140_RX_UC_FRAME_CNT	0xA4
+#define ADIN1140_RX_CRC_ERR_CNT		0xA5
+#define ADIN1140_RX_ALIGN_ERR_CNT	0xA6
+#define ADIN1140_RX_PREAMBLE_ERR_CNT	0xA7
+#define ADIN1140_RX_SHORT_ERR_CNT	0xA8
+#define ADIN1140_RX_LONG_ERR_CNT	0xA9
+#define ADIN1140_RX_PHY_ERR_CNT		0xAA
+#define ADIN1140_RX_DRP_FULL_CNT	0xAB
+#define ADIN1140_RX_DRP_FILTER_CNT	0xAD
+#define ADIN1140_RX_IFG_ERR_CNT		0xAE
+#define ADIN1140_TX_FRAME_CNT		0xB1
+#define ADIN1140_TX_BC_FRAME_CNT	0xB2
+#define ADIN1140_TX_MC_FRAME_CNT	0xB3
+#define ADIN1140_TX_UC_FRAME_CNT	0xB4
+#define ADIN1140_TX_SINGLE_COL_CNT	0xB5
+#define ADIN1140_TX_MULTI_COL_CNT	0xB6
+#define ADIN1140_TX_DEFERRED_CNT	0xB7
+#define ADIN1140_TX_LATE_COL_CNT	0xB8
+#define ADIN1140_TX_EXCESS_COL_CNT	0xB9
+#define ADIN1140_TX_UNDERRUN_CNT	0xBA
+
+/* ADIN1140_MAC_FILT_MAX_SLOT - 3 (multicast, broadcast and unicast
+ * reserved slots)
+ */
+#define ADIN1140_MAC_FILT_AVAIL	13U
+
+#define ADIN1140_PHY_CTRL_DEFAULT	0x1000
+#define ADIN1140_PHY_STATUS_DEFAULT	0x082D
+#define ADIN1140_PHY_ID1		0x0283
+#define ADIN1140_PHY_ID2		0xBE00
+
+#define ADIN1140_STATS_CHECK_DELAY	(3 * HZ)
+
+enum adin1140_statistics_entry {
+	rx_frames,
+	rx_bc_frames,
+	rx_mc_frames,
+	rx_uc_frames,
+	rx_crc_errors,
+	rx_align_errors,
+	rx_preamble_errors,
+	rx_short_frame_errors,
+	rx_long_frame_errors,
+	rx_phy_errors,
+	rx_fifo_full_dropped,
+	rx_addr_filter_dropped,
+	rx_ifg_errors,
+	tx_frames,
+	tx_bc_frames,
+	tx_mc_frames,
+	tx_uc_frames,
+	tx_single_collision,
+	tx_multi_collision,
+	tx_deferred,
+	tx_late_collision,
+	tx_excess_collision,
+	tx_underrun,
+	ADIN1140_STATS_CNT,
+};
+
+struct adin1140_statistics_reg {
+	const char *name;
+	enum adin1140_statistics_entry idx;
+};
+
+struct adin1140_priv {
+	struct net_device *netdev;
+	struct oa_tc6 *tc6;
+	struct mii_bus *mdiobus;
+	struct phy_device *phydev;
+	struct work_struct rx_mode_work;
+	struct delayed_work stats_work;
+
+	/* Protects stats[] from concurrent updates in adin1140_stats_work
+	 * and reads in the get_stats functions
+	 */
+	spinlock_t stat_lock;
+	u64 stats[ADIN1140_STATS_CNT];
+};
+
+static const u32 adin1140_stat_regs[] = {
+	[rx_frames] = ADIN1140_RX_FRAME_CNT,
+	[rx_bc_frames] = ADIN1140_RX_BC_FRAME_CNT,
+	[rx_mc_frames] = ADIN1140_RX_MC_FRAME_CNT,
+	[rx_uc_frames] = ADIN1140_RX_UC_FRAME_CNT,
+	[rx_crc_errors] = ADIN1140_RX_CRC_ERR_CNT,
+	[rx_align_errors] = ADIN1140_RX_ALIGN_ERR_CNT,
+	[rx_preamble_errors] = ADIN1140_RX_PREAMBLE_ERR_CNT,
+	[rx_short_frame_errors]	= ADIN1140_RX_SHORT_ERR_CNT,
+	[rx_long_frame_errors] = ADIN1140_RX_LONG_ERR_CNT,
+	[rx_phy_errors] = ADIN1140_RX_PHY_ERR_CNT,
+	[rx_fifo_full_dropped] = ADIN1140_RX_DRP_FULL_CNT,
+	[rx_addr_filter_dropped] = ADIN1140_RX_DRP_FILTER_CNT,
+	[rx_ifg_errors] = ADIN1140_RX_IFG_ERR_CNT,
+	[tx_frames] = ADIN1140_TX_FRAME_CNT,
+	[tx_bc_frames] = ADIN1140_TX_BC_FRAME_CNT,
+	[tx_mc_frames] = ADIN1140_TX_MC_FRAME_CNT,
+	[tx_uc_frames] = ADIN1140_TX_UC_FRAME_CNT,
+	[tx_single_collision] = ADIN1140_TX_SINGLE_COL_CNT,
+	[tx_multi_collision] = ADIN1140_TX_MULTI_COL_CNT,
+	[tx_deferred] = ADIN1140_TX_DEFERRED_CNT,
+	[tx_late_collision] = ADIN1140_TX_LATE_COL_CNT,
+	[tx_excess_collision] = ADIN1140_TX_EXCESS_COL_CNT,
+	[tx_underrun] = ADIN1140_TX_UNDERRUN_CNT,
+};
+
+static const struct adin1140_statistics_reg adin1140_stats[] = {
+	{.name = "rx_unicast_frames", .idx = rx_uc_frames},
+	{.name = "rx_preamble_errors", .idx = rx_preamble_errors},
+	{.name = "rx_ifg_errors", .idx = rx_ifg_errors},
+	{.name = "rx_addr_filter_dropped", .idx = rx_addr_filter_dropped},
+	{.name = "tx_unicast_frames", .idx = tx_uc_frames},
+};
+
+static int adin1140_mac_filter_set(struct adin1140_priv *priv,
+				   const u8 *addr, const u8 *mask,
+				   u8 slot)
+{
+	u32 reg_address;
+	u32 val;
+	int ret;
+
+	if (slot >= ADIN1140_MAC_FILT_MAX_SLOT)
+		return -ENOSPC;
+
+	reg_address = ADIN1140_MAC_ADDR_FILT_UPR_REG + 2 * slot;
+
+	ret = oa_tc6_write_register_mms(priv->tc6, OA_TC6_VEND_MMS1,
+					reg_address,
+					get_unaligned_be16(&addr[0]) |
+					ADIN1140_MAC_ADDR_FILT_APPLY2PORT1 |
+					ADIN1140_MAC_ADDR_FILT_TO_HOST);
+	if (ret)
+		return ret;
+
+	reg_address = ADIN1140_MAC_ADDR_FILT_LWR_REG + 2 * slot;
+	ret = oa_tc6_write_register_mms(priv->tc6, OA_TC6_VEND_MMS1,
+					reg_address,
+					get_unaligned_be32(&addr[2]));
+	if (ret)
+		return ret;
+
+	/* Only the first 2 destination MAC filter slots support masking.
+	 * For the other entries, the destination address in the received
+	 * frame must match exactly.
+	 */
+	if (slot >= ADIN1140_MAC_FILT_MASK_LIMIT)
+		return 0;
+
+	val = get_unaligned_be16(&mask[0]);
+	reg_address = ADIN1140_MAC_ADDR_MASK_UPR_REG + (2 * slot);
+
+	ret = oa_tc6_write_register_mms(priv->tc6, OA_TC6_VEND_MMS1,
+					reg_address, val);
+	if (ret)
+		return ret;
+
+	val = get_unaligned_be32(&mask[2]);
+	reg_address = ADIN1140_MAC_ADDR_MASK_LWR_REG + (2 * slot);
+
+	return oa_tc6_write_register_mms(priv->tc6, OA_TC6_VEND_MMS1,
+					 reg_address, val);
+}
+
+static int adin1140_mac_filter_clear(struct adin1140_priv *priv, u8 slot)
+{
+	u8 mask[ETH_ALEN];
+	u8 addr[ETH_ALEN];
+
+	memset(mask, 0xFF, ETH_ALEN);
+	memset(addr, 0x0, ETH_ALEN);
+
+	return adin1140_mac_filter_set(priv, addr, mask, slot);
+}
+
+static int adin1140_filter_unicast(struct adin1140_priv *priv)
+{
+	/* Only the first 2 filter slots support masking, so no unicast
+	 * address will ever need a mask. The first slots are used for the
+	 * all multicast and broadcast filter.
+	 */
+	return adin1140_mac_filter_set(priv, priv->netdev->dev_addr, NULL,
+				       ADIN1140_MAC_FILT_UC_SLOT);
+}
+
+static int adin1140_filter_all_multicast(struct adin1140_priv *priv, bool en)
+{
+	u8 multicast_addr[ETH_ALEN] = {1, 0, 0, 0, 0, 0};
+
+	if (en)
+		return adin1140_mac_filter_set(priv, multicast_addr,
+					       multicast_addr,
+					       ADIN1140_MAC_FILT_MC_SLOT);
+
+	return adin1140_mac_filter_clear(priv, ADIN1140_MAC_FILT_MC_SLOT);
+}
+
+static int adin1140_filter_broadcast(struct adin1140_priv *priv, bool enabled)
+{
+	u8 mask[ETH_ALEN];
+
+	if (enabled) {
+		memset(mask, 0xFF, ETH_ALEN);
+		return adin1140_mac_filter_set(priv, mask, mask,
+					       ADIN1140_MAC_FILT_BC_SLOT);
+	}
+
+	return adin1140_mac_filter_clear(priv, ADIN1140_MAC_FILT_BC_SLOT);
+}
+
+static int adin1140_default_filter_config(struct adin1140_priv *priv)
+{
+	int ret;
+
+	ret = adin1140_filter_broadcast(priv, true);
+	if (ret)
+		return ret;
+
+	return adin1140_filter_unicast(priv);
+}
+
+static int adin1140_promiscuous_mode(struct adin1140_priv *priv, bool enabled)
+{
+	int ret;
+	u32 val;
+
+	ret = oa_tc6_read_register(priv->tc6, OA_TC6_REG_CONFIG2, &val);
+	if (ret)
+		return ret;
+
+	if (enabled)
+		val |= ADIN1140_CONFIG2_FWD_UNK2HOST;
+	else
+		val &= ~ADIN1140_CONFIG2_FWD_UNK2HOST;
+
+	return oa_tc6_write_register(priv->tc6, OA_TC6_REG_CONFIG2, val);
+}
+
+static void adin1140_rx_mode_work(struct work_struct *work)
+{
+	struct adin1140_priv *priv = container_of(work, struct adin1140_priv,
+						  rx_mode_work);
+	struct netdev_hw_addr *ha;
+	bool all_multi, promisc;
+	u8 mask[ETH_ALEN];
+	u8 start, end;
+	u32 mac_addrs;
+	u8 slot, i;
+	int ret;
+
+	/* The ADIN1140 has 16 dest MAC address filter slots:
+	 * 0 - reserved for all multicast filter.
+	 * 1 - reserved for broadcast filter.
+	 * 2 - reserved for the device's own unicast MAC.
+	 * 3 -> 15 - available for other unicast/multicast filters.
+	 */
+
+	mac_addrs = netdev_uc_count(priv->netdev) +
+		    netdev_mc_count(priv->netdev);
+
+	if (priv->netdev->flags & IFF_PROMISC) {
+		promisc = true;
+		all_multi = false;
+	} else if (priv->netdev->flags & IFF_ALLMULTI) {
+		promisc = false;
+		all_multi = true;
+	} else if (mac_addrs <= ADIN1140_MAC_FILT_AVAIL) {
+		promisc = false;
+		all_multi = false;
+
+		slot = ADIN1140_MAC_FILT_UC_SLOT + 1;
+		memset(mask, 0xFF, ETH_ALEN);
+
+		netdev_for_each_uc_addr(ha, priv->netdev) {
+			ret = adin1140_mac_filter_set(priv, ha->addr, mask,
+						      slot);
+			if (ret)
+				return;
+
+			slot++;
+		}
+
+		netdev_for_each_mc_addr(ha, priv->netdev) {
+			ret = adin1140_mac_filter_set(priv, ha->addr, mask,
+						      slot);
+			if (ret)
+				return;
+
+			slot++;
+		}
+
+		for (i = slot; i < ADIN1140_MAC_FILT_MAX_SLOT; i++) {
+			ret = adin1140_mac_filter_clear(priv, i);
+			if (ret)
+				return;
+		}
+
+	} else {
+		/* The filter table is full. Enable promisc mode. */
+		promisc = true;
+		all_multi = false;
+
+		start = ADIN1140_MAC_FILT_UC_SLOT + 1;
+		end = ADIN1140_MAC_FILT_MAX_SLOT;
+		for (i = start; i < end; i++) {
+			ret = adin1140_mac_filter_clear(priv, i);
+			if (ret)
+				return;
+		}
+	}
+
+	ret = adin1140_promiscuous_mode(priv, promisc);
+	if (ret)
+		return;
+
+	adin1140_filter_all_multicast(priv, all_multi);
+}
+
+static void adin1140_rx_mode(struct net_device *netdev)
+{
+	struct adin1140_priv *priv = netdev_priv(netdev);
+
+	schedule_work(&priv->rx_mode_work);
+}
+
+static void adin1140_stats_work(struct work_struct *work)
+{
+	struct delayed_work *dwork = to_delayed_work(work);
+	u64 stat_buff[ADIN1140_STATS_CNT] = {};
+	struct adin1140_priv *priv;
+	u32 reg_val;
+	int ret;
+	u32 i;
+
+	priv = container_of(dwork, struct adin1140_priv, stats_work);
+
+	for (i = 0; i < ARRAY_SIZE(adin1140_stat_regs); i++) {
+		ret = oa_tc6_read_register_mms(priv->tc6, OA_TC6_VEND_MMS1,
+					       adin1140_stat_regs[i],
+					       &reg_val);
+		if (ret)
+			break;
+
+		stat_buff[i] = reg_val;
+	}
+
+	scoped_guard(spinlock, &priv->stat_lock)
+		memcpy(&priv->stats, stat_buff, sizeof(priv->stats));
+
+	schedule_delayed_work(dwork, ADIN1140_STATS_CHECK_DELAY);
+}
+
+static int adin1140_configure(struct adin1140_priv *priv)
+{
+	int ret;
+
+	ret = oa_tc6_zero_align_receive_frame_enable(priv->tc6);
+	if (ret)
+		return ret;
+
+	/* Disable MAC loopback */
+	ret = oa_tc6_write_register_mms(priv->tc6, OA_TC6_VEND_MMS1,
+					ADIN1140_MAC_P1_LOOP_ADDR_REG, 0x0);
+	if (ret)
+		return ret;
+
+	return adin1140_default_filter_config(priv);
+}
+
+static int adin1140_open(struct net_device *netdev)
+{
+	struct adin1140_priv *priv = netdev_priv(netdev);
+
+	schedule_delayed_work(&priv->stats_work, ADIN1140_STATS_CHECK_DELAY);
+
+	phy_start(netdev->phydev);
+	netif_start_queue(netdev);
+
+	return 0;
+}
+
+static int adin1140_close(struct net_device *netdev)
+{
+	struct adin1140_priv *priv = netdev_priv(netdev);
+
+	cancel_delayed_work_sync(&priv->stats_work);
+
+	netif_stop_queue(netdev);
+	phy_stop(netdev->phydev);
+
+	return 0;
+}
+
+static netdev_tx_t adin1140_start_xmit(struct sk_buff *skb,
+				       struct net_device *netdev)
+{
+	struct adin1140_priv *priv = netdev_priv(netdev);
+
+	/* The MAC doesn't automatically pad the frame to a 60 byte minimum
+	 * size in case the host sends a shorter skb, so we have to do it in
+	 * the driver. The FCS will be added by the MAC.
+	 */
+	if (skb_put_padto(skb, ETH_ZLEN))
+		return NETDEV_TX_OK;
+
+	return oa_tc6_start_xmit(priv->tc6, skb);
+}
+
+static int adin1140_set_mac_address(struct net_device *netdev, void *addr)
+{
+	struct adin1140_priv *priv = netdev_priv(netdev);
+	struct sockaddr *address = addr;
+	u8 mask[ETH_ALEN];
+	int ret;
+
+	ret = eth_prepare_mac_addr_change(netdev, addr);
+	if (ret < 0)
+		return ret;
+
+	if (ether_addr_equal(address->sa_data, netdev->dev_addr))
+		return 0;
+
+	memset(mask, 0xFF, ETH_ALEN);
+	ret = adin1140_mac_filter_set(priv, address->sa_data, mask,
+				      ADIN1140_MAC_FILT_UC_SLOT);
+	if (ret)
+		return ret;
+
+	eth_commit_mac_addr_change(netdev, addr);
+
+	return 0;
+}
+
+static void __adin1140_ndo_get_stats64(struct adin1140_priv *priv,
+				       struct rtnl_link_stats64 *storage)
+{
+	storage->rx_errors = priv->stats[rx_crc_errors] +
+			     priv->stats[rx_align_errors] +
+			     priv->stats[rx_preamble_errors] +
+			     priv->stats[rx_short_frame_errors] +
+			     priv->stats[rx_long_frame_errors] +
+			     priv->stats[rx_phy_errors] +
+			     priv->stats[rx_ifg_errors];
+
+	storage->tx_errors = priv->stats[tx_excess_collision] +
+			     priv->stats[tx_underrun];
+
+	storage->rx_dropped = priv->stats[rx_fifo_full_dropped] +
+			      priv->stats[rx_addr_filter_dropped];
+
+	storage->multicast = priv->stats[rx_mc_frames];
+
+	storage->collisions = priv->stats[tx_single_collision] +
+			      priv->stats[tx_multi_collision];
+
+	storage->rx_length_errors = priv->stats[rx_short_frame_errors] +
+				    priv->stats[rx_long_frame_errors];
+	storage->rx_over_errors = priv->stats[rx_fifo_full_dropped];
+	storage->rx_crc_errors = priv->stats[rx_crc_errors];
+	storage->rx_frame_errors = priv->stats[rx_align_errors];
+	storage->rx_missed_errors = priv->stats[rx_fifo_full_dropped];
+
+	storage->tx_aborted_errors = priv->stats[tx_excess_collision];
+	storage->tx_fifo_errors = priv->stats[tx_underrun];
+	storage->tx_window_errors = priv->stats[tx_late_collision];
+}
+
+static void adin1140_ndo_get_stats64(struct net_device *dev,
+				     struct rtnl_link_stats64 *storage)
+{
+	struct adin1140_priv *priv = netdev_priv(dev);
+
+	storage->rx_packets = priv->netdev->stats.rx_packets;
+	storage->tx_packets = priv->netdev->stats.tx_packets;
+
+	storage->rx_bytes = priv->netdev->stats.rx_bytes;
+	storage->tx_bytes = priv->netdev->stats.tx_bytes;
+
+	scoped_guard(spinlock, &priv->stat_lock)
+		__adin1140_ndo_get_stats64(priv, storage);
+}
+
+static void adin1140_get_drvinfo(struct net_device *netdev,
+				 struct ethtool_drvinfo *info)
+{
+	strscpy(info->driver, "ADIN1140", sizeof(info->driver));
+	strscpy(info->bus_info, dev_name(netdev->dev.parent),
+		sizeof(info->bus_info));
+}
+
+static void adin1140_get_ethtool_stats(struct net_device *netdev,
+				       struct ethtool_stats *stats, u64 *data)
+{
+	struct adin1140_priv *priv = netdev_priv(netdev);
+	u32 i;
+
+	scoped_guard(spinlock, &priv->stat_lock) {
+		for (i = 0; i < ARRAY_SIZE(adin1140_stats); i++)
+			data[i] = priv->stats[adin1140_stats[i].idx];
+	}
+}
+
+static void adin1140_get_ethtool_strings(struct net_device *netdev, u32 sset,
+					 u8 *p)
+{
+	u32 i;
+
+	switch (sset) {
+	case ETH_SS_STATS:
+		for (i = 0; i < ARRAY_SIZE(adin1140_stats); i++)
+			ethtool_puts(&p, adin1140_stats[i].name);
+
+		break;
+	}
+}
+
+static int adin1140_get_sset_count(struct net_device *netdev, int sset)
+{
+	switch (sset) {
+	case ETH_SS_STATS:
+		return ARRAY_SIZE(adin1140_stats);
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+static void __adin1140_eth_mac_stats(struct adin1140_priv *priv,
+				     struct ethtool_eth_mac_stats *mac_stats)
+{
+	mac_stats->FramesReceivedOK = priv->stats[rx_frames];
+	mac_stats->BroadcastFramesReceivedOK = priv->stats[rx_bc_frames];
+	mac_stats->MulticastFramesReceivedOK = priv->stats[rx_mc_frames];
+	mac_stats->FrameCheckSequenceErrors = priv->stats[rx_crc_errors];
+	mac_stats->AlignmentErrors = priv->stats[rx_align_errors];
+	mac_stats->FrameTooLongErrors = priv->stats[rx_long_frame_errors];
+	mac_stats->FramesLostDueToIntMACRcvError =
+					priv->stats[rx_fifo_full_dropped];
+	mac_stats->FramesTransmittedOK = priv->stats[tx_frames];
+	mac_stats->BroadcastFramesXmittedOK = priv->stats[tx_bc_frames];
+	mac_stats->MulticastFramesXmittedOK = priv->stats[tx_mc_frames];
+	mac_stats->SingleCollisionFrames = priv->stats[tx_single_collision];
+	mac_stats->MultipleCollisionFrames = priv->stats[tx_multi_collision];
+	mac_stats->FramesWithDeferredXmissions = priv->stats[tx_deferred];
+	mac_stats->LateCollisions = priv->stats[tx_late_collision];
+	mac_stats->FramesAbortedDueToXSColls =
+					priv->stats[tx_excess_collision];
+	mac_stats->FramesLostDueToIntMACXmitError = priv->stats[tx_underrun];
+}
+
+static void adin1140_get_eth_mac_stats(struct net_device *netdev,
+				       struct ethtool_eth_mac_stats *mac_stats)
+{
+	struct adin1140_priv *priv = netdev_priv(netdev);
+
+	scoped_guard(spinlock, &priv->stat_lock)
+		__adin1140_eth_mac_stats(priv, mac_stats);
+}
+
+static int adin1140_mdiobus_read(struct mii_bus *bus, int addr, int regnum)
+{
+	/* The ADIN1140's standard PHY C22 register map (OA TC6 0xFF00 -
+	 * 0xFF1F), of which only 0xFF00 - 0xFF03 are implemented) cannot be
+	 * accessed while frames are being received by the PHY. In case this
+	 * happens the CONFIG0 and CONFIG2 register values will get corrupted,
+	 * getting a random value. Both reads and writes cause the same
+	 * behavior. This is a workaround that avoids MDIO accesses all
+	 * together. Since this is a 10BASE-T1S PHY, only the loopback and
+	 * reset (AN) bits in the control register (0x0) can be written.
+	 * These functionalities have custom implementations in the PHY
+	 * driver. C45 accesses do not cause this issue.
+	 */
+
+	switch (regnum) {
+	case MII_BMCR:
+		return ADIN1140_PHY_CTRL_DEFAULT;
+	case MII_BMSR:
+		return ADIN1140_PHY_STATUS_DEFAULT;
+	case MII_PHYSID1:
+		return ADIN1140_PHY_ID1;
+	case MII_PHYSID2:
+		return ADIN1140_PHY_ID2;
+	default:
+		return 0xFFFF;
+	}
+}
+
+static int adin1140_mdiobus_write(struct mii_bus *bus, int addr, int regnum,
+				  u16 val)
+{
+	return -EIO;
+}
+
+static int adin1140_mdio_register(struct adin1140_priv *priv,
+				  struct spi_device *spidev)
+{
+	priv->mdiobus = devm_mdiobus_alloc(&spidev->dev);
+	if (!priv->mdiobus)
+		return dev_err_probe(&spidev->dev, -ENOMEM,
+				     "MDIO bus alloc failed\n");
+
+	priv->mdiobus->name = "adin1140-mdiobus";
+	priv->mdiobus->priv = priv->tc6;
+	priv->mdiobus->parent = &spidev->dev;
+	priv->mdiobus->phy_mask = GENMASK(31, 1);
+	priv->mdiobus->read = adin1140_mdiobus_read;
+	priv->mdiobus->write = adin1140_mdiobus_write;
+	priv->mdiobus->read_c45 = oa_tc6_mdiobus_read_c45;
+	priv->mdiobus->write_c45 = oa_tc6_mdiobus_write_c45;
+
+	snprintf(priv->mdiobus->id, MII_BUS_ID_SIZE, "adin1140-%s.%u",
+		 dev_name(&spidev->dev), spi_get_chipselect(spidev, 0));
+
+	return devm_mdiobus_register(&spidev->dev, priv->mdiobus);
+}
+
+static void adin1140_handle_link_change(struct net_device *netdev)
+{
+	phy_print_status(netdev->phydev);
+}
+
+static void adin1140_phy_remove(void *data)
+{
+	phy_disconnect(data);
+}
+
+static int adin1140_phy_init(struct adin1140_priv *priv,
+			     struct spi_device *spidev)
+{
+	int ret;
+
+	ret = adin1140_mdio_register(priv, spidev);
+	if (ret)
+		return ret;
+
+	priv->phydev = phy_find_first(priv->mdiobus);
+	if (!priv->phydev)
+		return dev_err_probe(&spidev->dev, -ENODEV, "No PHY found\n");
+
+	priv->phydev->is_internal = true;
+	ret = phy_connect_direct(priv->netdev, priv->phydev,
+				 &adin1140_handle_link_change,
+				 PHY_INTERFACE_MODE_INTERNAL);
+	if (ret)
+		return dev_err_probe(&spidev->dev, ret,
+				     "Can't attach PHY to %s\n",
+				     priv->mdiobus->id);
+
+	ret = devm_add_action_or_reset(&spidev->dev, adin1140_phy_remove,
+				       priv->phydev);
+	if (ret)
+		return ret;
+
+	phy_attached_info(priv->phydev);
+
+	return 0;
+}
+
+static const struct ethtool_ops adin1140_ethtool_ops = {
+	.get_drvinfo = adin1140_get_drvinfo,
+	.get_link = ethtool_op_get_link,
+	.get_ethtool_stats = adin1140_get_ethtool_stats,
+	.get_sset_count = adin1140_get_sset_count,
+	.get_strings = adin1140_get_ethtool_strings,
+	.get_link_ksettings = phy_ethtool_get_link_ksettings,
+	.set_link_ksettings = phy_ethtool_set_link_ksettings,
+	.get_eth_mac_stats = adin1140_get_eth_mac_stats,
+};
+
+static const struct net_device_ops adin1140_netdev_ops = {
+	.ndo_open = adin1140_open,
+	.ndo_stop = adin1140_close,
+	.ndo_start_xmit	= adin1140_start_xmit,
+	.ndo_set_mac_address = adin1140_set_mac_address,
+	.ndo_validate_addr = eth_validate_addr,
+	.ndo_set_rx_mode = adin1140_rx_mode,
+	.ndo_eth_ioctl = phy_do_ioctl_running,
+	.ndo_get_stats64 = adin1140_ndo_get_stats64,
+};
+
+static void adin1140_oa_tc6_remove(void *data)
+{
+	oa_tc6_exit(data);
+}
+
+static void adin1140_cancel_rx_mode_work(void *data)
+{
+	cancel_work_sync(data);
+}
+
+static int adin1140_probe(struct spi_device *spi)
+{
+	struct oa_tc6_quirks tc6_quirks = {};
+	struct net_device *netdev;
+	struct adin1140_priv *priv;
+	int ret;
+
+	netdev = devm_alloc_etherdev(&spi->dev, sizeof(struct adin1140_priv));
+	if (!netdev)
+		return -ENOMEM;
+
+	priv = netdev_priv(netdev);
+	priv->netdev = netdev;
+	spi_set_drvdata(spi, priv);
+	spin_lock_init(&priv->stat_lock);
+
+	tc6_quirks.quirk_flags = OA_TC6_BROKEN_PHY;
+
+	priv->tc6 = oa_tc6_init(spi, netdev, &tc6_quirks);
+	if (!priv->tc6)
+		return -ENODEV;
+
+	ret = devm_add_action_or_reset(&spi->dev, adin1140_oa_tc6_remove,
+				       priv->tc6);
+	if (ret)
+		return ret;
+
+	ret = adin1140_phy_init(priv, spi);
+	if (ret)
+		return ret;
+
+	if (device_get_ethdev_address(&spi->dev, netdev))
+		eth_hw_addr_random(netdev);
+
+	ret = adin1140_configure(priv);
+	if (ret)
+		return ret;
+
+	INIT_WORK(&priv->rx_mode_work, adin1140_rx_mode_work);
+	INIT_DELAYED_WORK(&priv->stats_work, adin1140_stats_work);
+
+	ret = devm_add_action_or_reset(&spi->dev, adin1140_cancel_rx_mode_work,
+				       &priv->rx_mode_work);
+	if (ret)
+		return ret;
+
+	netdev->if_port = IF_PORT_10BASET;
+	netdev->irq = spi->irq;
+	netdev->netdev_ops = &adin1140_netdev_ops;
+	netdev->ethtool_ops = &adin1140_ethtool_ops;
+	netdev->netns_immutable = true;
+	netdev->priv_flags |= IFF_LIVE_ADDR_CHANGE |
+			      IFF_UNICAST_FLT;
+
+	ret = devm_register_netdev(&spi->dev, netdev);
+	if (ret)
+		return dev_err_probe(&spi->dev, ret,
+				     "Failed to register netdev");
+
+	return 0;
+}
+
+static const struct spi_device_id adin1140_spi_id[] = {
+	{ .name = "ad3306" },
+	{ .name = "adin1140" },
+	{},
+};
+MODULE_DEVICE_TABLE(spi, adin1140_spi_id);
+
+static const struct of_device_id adin1140_match_table[] = {
+	{ .compatible = "adi,ad3306" },
+	{ .compatible = "adi,adin1140" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, adin1140_match_table);
+
+static struct spi_driver adin1140_driver = {
+	.driver = {
+		.name = "adin1140",
+		.of_match_table = adin1140_match_table,
+	 },
+	.probe = adin1140_probe,
+	.id_table = adin1140_spi_id,
+};
+module_spi_driver(adin1140_driver);
+
+MODULE_DESCRIPTION("Analog Devices, Inc. ADIN1140 10BASE-T1S MAC-PHY");
+MODULE_AUTHOR("Ciprian Regus <ciprian.regus@analog.com>");
+MODULE_LICENSE("GPL");

-- 
2.43.0



^ permalink raw reply related

* [PATCH net-next v3 09/13] net: ethernet: oa_tc6: Add new register address defines
From: Ciprian Regus via B4 Relay @ 2026-06-04 16:32 UTC (permalink / raw)
  To: Parthiban Veerasooran, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Jonathan Corbet,
	Shuah Khan, Andrew Lunn, Heiner Kallweit, Russell King,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: netdev, linux-kernel, linux-doc, devicetree, Ciprian Regus
In-Reply-To: <20260604-adin1140-driver-v3-0-5debdb3173c4@analog.com>

From: Ciprian Regus <ciprian.regus@analog.com>

Add macro defines for the CONFIG2 register and the MMS1 memory map.

Signed-off-by: Ciprian Regus <ciprian.regus@analog.com>
---
v3 changelog:
 - New patch
---
 include/linux/oa_tc6.h | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/include/linux/oa_tc6.h b/include/linux/oa_tc6.h
index 9fa4397303d1..e6eca352b2c0 100644
--- a/include/linux/oa_tc6.h
+++ b/include/linux/oa_tc6.h
@@ -25,6 +25,9 @@
 #define OA_TC6_CONFIG0_ZARFE_ENABLE		BIT(12)
 #define OA_TC6_CONFIG0_PROTE			BIT(5)
 
+/* Configuration Register #2 */
+#define OA_TC6_REG_CONFIG2			0x0006
+
 /* Status Register #0 */
 #define OA_TC6_REG_STATUS0			0x0008
 #define OA_TC6_STATUS0_RESETC			BIT(6)	/* Reset Complete */
@@ -49,6 +52,9 @@
 #define OA_TC6_PHY_STD_REG_ADDR_BASE		0xFF00
 #define OA_TC6_PHY_STD_REG_ADDR_MASK		0x1F
 
+/* Vendor specific memory map. */
+#define OA_TC6_VEND_MMS1			1
+
 /* PHY – Clause 45 registers memory map selector (MMS) as per table 6 in the
  * OPEN Alliance specification.
  */

-- 
2.43.0



^ permalink raw reply related

* [PATCH net-next v3 11/13] net: phy: microchip-t1s: use generic C45 MMD access helpers
From: Ciprian Regus via B4 Relay @ 2026-06-04 16:32 UTC (permalink / raw)
  To: Parthiban Veerasooran, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Jonathan Corbet,
	Shuah Khan, Andrew Lunn, Heiner Kallweit, Russell King,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: netdev, linux-kernel, linux-doc, devicetree, Ciprian Regus
In-Reply-To: <20260604-adin1140-driver-v3-0-5debdb3173c4@analog.com>

From: Ciprian Regus <ciprian.regus@analog.com>

Replace the driver specific lan865x_phy_read_mmd() and
lan865x_phy_write_mmd() with the shared genphy_read_mmd_c45() and
genphy_write_mmd_c45() helpers.

No functional change.

Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Ciprian Regus <ciprian.regus@analog.com>
---
v3 changelog:
 - no change
v2 changelog:
 - New patch
---
 drivers/net/phy/microchip_t1s.c | 32 ++------------------------------
 1 file changed, 2 insertions(+), 30 deletions(-)

diff --git a/drivers/net/phy/microchip_t1s.c b/drivers/net/phy/microchip_t1s.c
index e601d56b2507..73c23d311d72 100644
--- a/drivers/net/phy/microchip_t1s.c
+++ b/drivers/net/phy/microchip_t1s.c
@@ -506,34 +506,6 @@ static int lan86xx_read_status(struct phy_device *phydev)
 	return 0;
 }
 
-/* OPEN Alliance 10BASE-T1x compliance MAC-PHYs will have both C22 and
- * C45 registers space. If the PHY is discovered via C22 bus protocol it assumes
- * it uses C22 protocol and always uses C22 registers indirect access to access
- * C45 registers. This is because, we don't have a clean separation between
- * C22/C45 register space and C22/C45 MDIO bus protocols. Resulting, PHY C45
- * registers direct access can't be used which can save multiple SPI bus access.
- * To support this feature, set .read_mmd/.write_mmd in the PHY driver to call
- * .read_c45/.write_c45 in the OPEN Alliance framework
- * drivers/net/ethernet/oa_tc6.c
- */
-static int lan865x_phy_read_mmd(struct phy_device *phydev, int devnum,
-				u16 regnum)
-{
-	struct mii_bus *bus = phydev->mdio.bus;
-	int addr = phydev->mdio.addr;
-
-	return __mdiobus_c45_read(bus, addr, devnum, regnum);
-}
-
-static int lan865x_phy_write_mmd(struct phy_device *phydev, int devnum,
-				 u16 regnum, u16 val)
-{
-	struct mii_bus *bus = phydev->mdio.bus;
-	int addr = phydev->mdio.addr;
-
-	return __mdiobus_c45_write(bus, addr, devnum, regnum, val);
-}
-
 static struct phy_driver microchip_t1s_driver[] = {
 	{
 		PHY_ID_MATCH_EXACT(PHY_ID_LAN867X_REVB1),
@@ -584,8 +556,8 @@ static struct phy_driver microchip_t1s_driver[] = {
 		.features           = PHY_BASIC_T1S_P2MP_FEATURES,
 		.config_init        = lan865x_revb_config_init,
 		.read_status        = lan86xx_read_status,
-		.read_mmd           = lan865x_phy_read_mmd,
-		.write_mmd          = lan865x_phy_write_mmd,
+		.read_mmd           = genphy_read_mmd_c45,
+		.write_mmd          = genphy_write_mmd_c45,
 		.get_plca_cfg	    = genphy_c45_plca_get_cfg,
 		.set_plca_cfg	    = lan86xx_plca_set_cfg,
 		.get_plca_status    = genphy_c45_plca_get_status,

-- 
2.43.0



^ permalink raw reply related


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