Linux Documentation
 help / color / mirror / Atom feed
* [PATCH 3/4] dmaengine: dmatest: fix preposition error in documentation
From: Wang Zihan @ 2026-05-02  5:59 UTC (permalink / raw)
  To: vkoul; +Cc: dmaengine, linux-doc, Wang Zihan

Change "built-in in" to "built into".

Signed-off-by: Wang Zihan <jiyu03@qq.com>
---
 Documentation/driver-api/dmaengine/dmatest.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Documentation/driver-api/dmaengine/dmatest.rst b/Documentation/driver-api/dmaengine/dmatest.rst
index e2a63cefd..dafb84afd 100644
--- a/Documentation/driver-api/dmaengine/dmatest.rst
+++ b/Documentation/driver-api/dmaengine/dmatest.rst
@@ -108,7 +108,7 @@ Example::
     % cat /sys/module/dmatest/parameters/wait
     % modprobe -r dmatest
 
-Part 3 - When built-in in the kernel
+Part 3 - When built into the kernel
 ====================================
 
 The module parameters that is supplied to the kernel command line will be used
-- 
2.54.0


^ permalink raw reply related

* [PATCH 2/4] net: switchdev: fix duplicate word in documentation
From: Wang Zihan @ 2026-05-02  5:59 UTC (permalink / raw)
  To: kuba; +Cc: netdev, linux-doc, Wang Zihan

Remove duplicate "in" word.

Signed-off-by: Wang Zihan <jiyu03@qq.com>
---
 Documentation/networking/switchdev.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Documentation/networking/switchdev.rst b/Documentation/networking/switchdev.rst
index 2966b7122..948bce44c 100644
--- a/Documentation/networking/switchdev.rst
+++ b/Documentation/networking/switchdev.rst
@@ -162,7 +162,7 @@ The switchdev driver can know a particular port's position in the topology by
 monitoring NETDEV_CHANGEUPPER notifications.  For example, a port moved into a
 bond will see its upper master change.  If that bond is moved into a bridge,
 the bond's upper master will change.  And so on.  The driver will track such
-movements to know what position a port is in in the overall topology by
+movements to know what position a port is in the overall topology by
 registering for netdevice events and acting on NETDEV_CHANGEUPPER.
 
 L2 Forwarding Offload
-- 
2.54.0


^ permalink raw reply related

* [PATCH 1/4] iio: adxl313: fix typos in documentation
From: Wang Zihan @ 2026-05-02  5:58 UTC (permalink / raw)
  To: jic23; +Cc: linux-iio, linux-doc, Wang Zihan

Add missing space in "ADXL313is" and correct "a single types"
to "a single type".

Signed-off-by: Wang Zihan <jiyu03@qq.com>
---
 Documentation/iio/adxl313.rst | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Documentation/iio/adxl313.rst b/Documentation/iio/adxl313.rst
index 966e72c01..3662153a6 100644
--- a/Documentation/iio/adxl313.rst
+++ b/Documentation/iio/adxl313.rst
@@ -11,7 +11,7 @@ This driver supports Analog Device's ADXL313 on SPI/I2C bus.
 
 * `ADXL313 <https://www.analog.com/ADXL313>`_
 
-The ADXL313is a low noise density, low power, 3-axis accelerometer with
+The ADXL313 is a low noise density, low power, 3-axis accelerometer with
 selectable measurement ranges. The ADXL313 supports the ±0.5 g, ±1 g, ±2 g and
 ±4 g ranges.
 
@@ -112,7 +112,7 @@ apply the following formula:
 Where _offset and _scale are device attributes. If no _offset attribute is
 present, simply assume its value is 0.
 
-The ADXL313 driver offers data for a single types of channels, the table below
+The ADXL313 driver offers data for a single type of channels, the table below
 shows the measurement units for the processed value, which are defined by the
 IIO framework:
 
-- 
2.54.0


^ permalink raw reply related

* Re: [PATCH 2/3] Documentation: security-bugs: explain what is and is not a security bug
From: Demi Marie Obenour @ 2026-05-02  5:51 UTC (permalink / raw)
  To: Willy Tarreau
  Cc: Greg KH, leon, security, Jonathan Corbet, skhan, workflows,
	linux-doc, linux-kernel, Qubes Developer Mailing List
In-Reply-To: <afWNHZoN64fldbUK@1wt.eu>


[-- Attachment #1.1.1: Type: text/plain, Size: 3308 bytes --]

On 5/2/26 01:35, Willy Tarreau wrote:
> Hi Demi Marie,
> 
> On Sat, May 02, 2026 at 01:20:10AM -0400, Demi Marie Obenour wrote:
>>>>> Ah, but USB does cover "some" modification of devices, so this is going
>>>>> to be something that is good to document over time, if for no other
>>>>> reason to keep these scanning tools in check from hallucinating crazy
>>>>> situations that are obviously not a valid thing we care about.
>>>>
>>>> OK but does this mean you still want to get these reports in the end ?
>>>
>>> I want a patch if a user cares about that threat-model (as Android does
>>> but no one else) as it's up to the user groups that want to change the
>>> default kernel's behavior like this to actually submit patches to do so.
>> FYI, I don't think this is limited to Android.  Chrome OS definitely
>> cares about malicious USB devices, and the whole purpose of USBGuard is
>> to prevent a USB device from being able to compromise the system unless
>> authorized.  I believe Qubes OS also cares, as it supports USB device
>> assignment to virtual machines.  CCing qubes-devel for confirmation.
>>
>> What should that patch look like?  Could there be a way for these user
>> groups to be informed of vulnerabilities in the USB subsystem, so that
>> they can take responsibility for fixing them before they become public?
> 
> I've posted a proposal elsewhere in the same thread:
> 
>    https://lore.kernel.org/lkml/afSxSX8RK0Z4kkOI@1wt.eu/

I saw that, but it's still not quite clear what is meant here.
My understanding is that those concerned about malicious USB devices
are generally concerned about _arbitrary_ malicious USB devices.
The one thing a USB device shouldn't be able to spoof is the port
it is plugged into, and userspace tools like USBGuard can use that
information.  But to do that, they have to trust that the device
can't harm the system if it isn't assigned to any drivers.

>> It does make sense for those who care about the security of a subsystem
>> to be responsible for vulnerabilities in that system, but right now
>> I'm not sure how one would offer to take up that responsibility.
> 
> I think that at least some subsystems will want to add their own
> restrictions based on the bug reports they keep receiving, and I hope
> it can help distros figure where there's a gap between is promised to
> users and what the kernel promises, that needs to be filled by userland
> verification tools for example.
> 
> Willy

I think there might be another category, which is were there is a
third party who is much more interested in the security of a subsystem
than its primary maintainers are.  I suspect that Google is said
third party in multiple such cases, especially various USB drivers.
In particular, exploiting the kernel via USB is a common attack
technique used in the wild by tools like Cellebrite.

In these cases, I think it makes sense to funnel vulnerability
reports to the people who actually seriously care about fixing them.
For instance, problems in USB might be funneled to the Chrome OS and
Android security teams.  They will get fixed much more quickly, and
upstream maintainers won't be flooded with reports that don't have
attached patches.
-- 
Sincerely,
Demi Marie Obenour (she/her/hers)

[-- Attachment #1.1.2: OpenPGP public key --]
[-- Type: application/pgp-keys, Size: 7253 bytes --]

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply

* Re: [PATCH 2/3] Documentation: security-bugs: explain what is and is not a security bug
From: Willy Tarreau @ 2026-05-02  5:35 UTC (permalink / raw)
  To: Demi Marie Obenour
  Cc: Greg KH, leon, security, Jonathan Corbet, skhan, workflows,
	linux-doc, linux-kernel, Qubes Developer Mailing List
In-Reply-To: <1d3f8659-8c69-47f6-bb38-4c1d06cf8307@gmail.com>

Hi Demi Marie,

On Sat, May 02, 2026 at 01:20:10AM -0400, Demi Marie Obenour wrote:
> >>> Ah, but USB does cover "some" modification of devices, so this is going
> >>> to be something that is good to document over time, if for no other
> >>> reason to keep these scanning tools in check from hallucinating crazy
> >>> situations that are obviously not a valid thing we care about.
> >>
> >> OK but does this mean you still want to get these reports in the end ?
> > 
> > I want a patch if a user cares about that threat-model (as Android does
> > but no one else) as it's up to the user groups that want to change the
> > default kernel's behavior like this to actually submit patches to do so.
> FYI, I don't think this is limited to Android.  Chrome OS definitely
> cares about malicious USB devices, and the whole purpose of USBGuard is
> to prevent a USB device from being able to compromise the system unless
> authorized.  I believe Qubes OS also cares, as it supports USB device
> assignment to virtual machines.  CCing qubes-devel for confirmation.
> 
> What should that patch look like?  Could there be a way for these user
> groups to be informed of vulnerabilities in the USB subsystem, so that
> they can take responsibility for fixing them before they become public?

I've posted a proposal elsewhere in the same thread:

   https://lore.kernel.org/lkml/afSxSX8RK0Z4kkOI@1wt.eu/

> It does make sense for those who care about the security of a subsystem
> to be responsible for vulnerabilities in that system, but right now
> I'm not sure how one would offer to take up that responsibility.

I think that at least some subsystems will want to add their own
restrictions based on the bug reports they keep receiving, and I hope
it can help distros figure where there's a gap between is promised to
users and what the kernel promises, that needs to be filled by userland
verification tools for example.

Willy

^ permalink raw reply

* Re: [PATCH v3] docs/ja_JP: translate more of submitting-patches.rst
From: Akira Yokosawa @ 2026-05-02  5:25 UTC (permalink / raw)
  To: Akiyoshi Kurita; +Cc: linux-kernel, corbet, linux-doc
In-Reply-To: <20260501191114.939418-1-weibu@redadmin.org>

Hi,

On 2 May 2026 04:11:14 +0900, Akiyoshi Kurita wrote:
> Translate the "Separate your changes", "Style-check your changes",
> and "Select the recipients for your patch" sections in
> Documentation/translations/ja_JP/process/submitting-patches.rst.
> 
> Keep the wording close to the English text and wrap lines to match
> the style used in the surrounding Japanese translation.
> 
> Signed-off-by: Akiyoshi Kurita <weibu@redadmin.org>
> ---
> v3:
> - Use a file-local cross-reference to the translated "変更を分割する" section
> - Keep the TODO for the untranslated "The canonical patch format" section
> - Drop the obsolete TODO for "Separate your changes"
> - Rewrap the latter part more consistently
> 
>  .../ja_JP/process/submitting-patches.rst      | 123 +++++++++++++++++-
>  1 file changed, 118 insertions(+), 5 deletions(-)

This does not apply on docs-next.

> 
> diff --git a/Documentation/translations/ja_JP/process/submitting-patches.rst b/Documentation/translations/ja_JP/process/submitting-patches.rst
> index 91bd79a0e9dc..8f85d2cfde71 100644

At docs-next, .../ja_JP/process/submitting-patches.rst has index 9d63220abd15.

Your patch should start with:

index 9d63220abd15..xxxxxxxxxxxx 100644

Please rebase and resend.

Thanks, Akira

[...]


^ permalink raw reply

* Re: [PATCH 2/3] Documentation: security-bugs: explain what is and is not a security bug
From: Demi Marie Obenour @ 2026-05-02  5:20 UTC (permalink / raw)
  To: Greg KH, Willy Tarreau
  Cc: leon, security, Jonathan Corbet, skhan, workflows, linux-doc,
	linux-kernel, Qubes Developer Mailing List
In-Reply-To: <2026042804-overbook-ripeness-73dd@gregkh>


[-- Attachment #1.1.1: Type: text/plain, Size: 5780 bytes --]

On 4/28/26 17:13, Greg KH wrote:
> On Mon, Apr 27, 2026 at 06:14:15PM +0200, Willy Tarreau wrote:
>> On Mon, Apr 27, 2026 at 09:35:04AM -0600, Greg KH wrote:
>>> On Mon, Apr 27, 2026 at 05:27:46PM +0200, Willy Tarreau wrote:
>>>> On Mon, Apr 27, 2026 at 07:48:23AM -0600, Greg KH wrote:
>>>>> On Sun, Apr 26, 2026 at 06:39:13PM +0200, Willy Tarreau wrote:
>>>>>> +In the Linux kernel's threat model, an issue is **not** a security bug, and
>>>>>> +should not be reported to the security list, when triggering it requires the
>>>>>> +reporter to first undermine the system they are attacking.  This includes, but
>>>>>> +is not limited to, behavior that only manifests after the administrator has
>>>>>> +explicitly enabled it (loading a module, setting a sysctl, writing to a debugfs
>>>>>> +knob, or otherwise using an interface documented as privileged or unsafe); bugs
>>>>>> +reachable only through root or CAP_SYS_ADMIN or CAP_NET_ADMIN on a machine the
>>>>>> +actor already fully controls, with no further privilege boundary being crossed;
>>>>>> +prediction of random numbers that only works in a totally silent environment
>>>>>> +(such as IP ID, TCP ports or sequence numbers that can only be guessed in a
>>>>>> +lab), issues that appear only in debug, lockdep, KASAN, fault-injection,
>>>>>> +CONFIG_NOMMU, or other developer-oriented kernel builds that are not intended
>>>>>> +for production use; problems seen only under development simulators, emulators,
>>>>>> +or fuzzing harnesses that present hardware or input states which cannot occur
>>>>>> +on real systems; bugs that require modified or emulated hardware; missing
>>>>>> +hardening or defence-in-depth suggestions with no demonstrable exploit path
>>>>>> +(including local ASLR bypass); mounting file systems that would be fixed or
>>>>>> +rejected by fsck; and bugs in out-of-tree modules or vendor forks, which should
>>>>>> +be reported to the relevant vendor.  Functional and performance regressions,
>>>>>> +and disagreements with documented kernel policy (for example, "root can load
>>>>>> +modules"), are likewise ordinary bugs or feature requests rather than security
>>>>>> +issues, and should be reported via the usual channels.
>>>>>
>>>>> This is a great list to start with, but perhaps we should put it in list
>>>>> form so that it's easier to read?
>>>>
>>>> In fact that's what I tried first and it was super long with many short
>>>> lines, making it possibly worse. But maybe aggregating several short
>>>> entries on a line by similarities could work, I can give it a try.
>>>>
>>>>> Also, I can see this turning into a separate document eventually as
>>>>> different subsystems should have a chance to weigh in on what they
>>>>> consider the threat model to be
>>>>
>>>> My fear if we redirect to other files is that it won't be read again.
>>>> However, we could possibly suggest to always look for the subsystem's
>>>> specific rules in this subsytem's doc, leaving enough freedom to
>>>> maintainers to reject more things.
>>>
>>> AI tools are good at following links, so I wouldn't worry about that.
>>
>> Yes but let's not forget the minority of humble humans still sending
>> honest reports ;-)
>>
>>> We can point at other files, as this list is going to get long over
>>> time, which is a good thing.
>>
>> Sure. I'm just unsure where this could be enumerated, as it's likely
>> that there would be just one or two lines max per subsystem for the
>> majority of them. Or we could have a totally separate file, "threat
>> model", that goes into great lengths detailing all this with sections
>> per category or subsystem when they start to grow maybe, and refer only
>> to that one from security-bugs ?
> 
> I think a separate file is good, I know I need to write up what the USB
> model is, and it's different from PCI, and different from other
> subsystems.  All should probably be documented eventually.
> 
>>>>> (like what the IB subsystem does which I
>>>>> don't think you listed above, or the USB subsystem.)
>>>>
>>>> Indeed I didn't list IB (I'm never sure about it, I seem to remember
>>>> we simply trust any peer, is that right?), nor did I make specific
>>>> mentions for USB which is implicitly covered by "hardware emulation
>>>> or modification".
>>>
>>> Ah, but USB does cover "some" modification of devices, so this is going
>>> to be something that is good to document over time, if for no other
>>> reason to keep these scanning tools in check from hallucinating crazy
>>> situations that are obviously not a valid thing we care about.
>>
>> OK but does this mean you still want to get these reports in the end ?
> 
> I want a patch if a user cares about that threat-model (as Android does
> but no one else) as it's up to the user groups that want to change the
> default kernel's behavior like this to actually submit patches to do so.
FYI, I don't think this is limited to Android.  Chrome OS definitely
cares about malicious USB devices, and the whole purpose of USBGuard is
to prevent a USB device from being able to compromise the system unless
authorized.  I believe Qubes OS also cares, as it supports USB device
assignment to virtual machines.  CCing qubes-devel for confirmation.

What should that patch look like?  Could there be a way for these user
groups to be informed of vulnerabilities in the USB subsystem, so that
they can take responsibility for fixing them before they become public?

It does make sense for those who care about the security of a subsystem
to be responsible for vulnerabilities in that system, but right now
I'm not sure how one would offer to take up that responsibility.
-- 
Sincerely,
Demi Marie Obenour (she/her/hers)

[-- Attachment #1.1.2: OpenPGP public key --]
[-- Type: application/pgp-keys, Size: 7253 bytes --]

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply

* Re: [PATCH 0/5] mm: Support selecting doing direct COW for anonymous pmd entry
From: Luka Bai @ 2026-05-02  5:06 UTC (permalink / raw)
  To: David Hildenbrand (Arm)
  Cc: linux-mm, Jonathan Corbet, Shuah Khan, Andrew Morton,
	Lorenzo Stoakes, Zi Yan, Baolin Wang, Liam R. Howlett, Nico Pache,
	Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Vlastimil Babka,
	Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Jann Horn,
	Arnd Bergmann, Kairui Song, linux-kernel, linux-arch, linux-doc,
	Luka Bai
In-Reply-To: <785b6164-aa71-4fc4-a4f3-f4977b7db30e@kernel.org>

在 Fri, May 01, 2026 at 08:30:39PM +0200,David Hildenbrand (Arm) 写道:

Hi David,

Thanks for replying again :). I've read your advices, and I agreed with your opinion,
THP COW is premature now for the upstream, we can reconsider other approaches. :)

> >>
> >> Note that there was a recent related discussion for executable, which was rejected:
> >>
> >> https://lore.kernel.org/r/20251226100337.4171191-1-zhangqilong3@huawei.com
> >>
> > 
> > Yes, I know this history, and I know that it will cost some memory or latency,
> > That’s why I was wondering maybe I can add a switch to it to make it
> > configurable :).
> 
> Switches for something like that is just not a good fit.
> 
> For example, for a short-lived child (e.g., fork+exec) it usually makes no sense
> to cow a larger chunk of address space, when you know that it will exit
> immediately either way and free up the memory.
> 

Yeah, for most workloads, fork will be used with a exec call soon, which makes the
COW for THP not so useful for these workloads.

> >>>
> >>> In addition to the problem above, this logic can also generate some
> >>> deficiency for THP itself. Currently THP is just a "best-effort" choice
> >>> with no "certainty". THP is easily splitted into multiple small pages
> >>> on common calling path like reclaiming, COW. A transparent splitting
> >>> can cause throughput fluctuation for some workloads. For these workloads,
> >>> we may want to give THP some "certainty" just like hugetlbfs,
> >>
> >> There are no such guarantees, though. And We wouldn't want to commit to any such
> >> guarantees today. For example, simple page migration can split the folio.
> >> Allocation failures will fallback to small pages etc.
> >>
> >> If you need guarantees, use hugetlb for now.
> >>
> > 
> > The reason why I want to use THP over hugetlb is that I need reclamation for my
> > workload :). There are many processes in my workload that need 2M
> > aligned folios for better performance, and we want to reclaim them back automatically
> > when the process doesn’t need the folios. 
> 
> Can you share some details how exactly that is supposed to work?
> 

Sorry for that, it's basically just a bunch of processes in the environment with 2M sized
folio as their backend, but we want the OS to reclaim them when it's possible. And we want to
balance performance and memory saving, and don't split so often since it may cause fraction
and may influence the future 2M folio allocation. :)

> > But hugetlbfs cannot do passive reclamation
> > from what I know (except doing active madvise by the processes themselves). And using
> 
> Right, you can only return hugetlb folios by doing MADV_DONTNEED or munmap().
> 
> > THP can easily split the hugepages. So that’s why I would like to add certainty for THP,
> 
> Repeat after me: there are no guarantees. There is no certainty :)
> 
> > and use THP for these processes as backend, because THP is very well integrated with
> > the swap system and other filesystems. And from what I checked,
> > it seems the most common case for splitting a THP is COW and swapping so I am trying
> > to handle these two scenarios (But coincidentally, PMD swapping is committed in
> > https://lore.kernel.org/all/D3F08F85-76E0-4C5A-ABA1-537C68E038B8@nvidia.com/
> > a few days before, which is a great implementation :) ).
> 
> Right, but that really only changes how we map large folios, not how we allocate
> them. There are no guarantees.
> 
> > 
> >>> The effect
> >>> we want is: after some customized setup, if only the system has usable
> >>> folio, and the virtual memory alignment permits (or we setup to), we can
> >>> make sure we always use THP for it, the system will never split it except
> >>> the user wants to do so.
> >>>
> >>> This patchset is about both two things above, firstly we add pmd level
> >>> THP COW support by revising the code in do_huge_pmd_wp_page, we added
> >>> switch for it because different workloads may need different resources,
> >>
> >> The switch is bad, and we won't accept any toggle like that. A system-wide
> >> setting does not make sense for such behavior.
> >>
> > 
> > Oh, the reason why I added a switch globally is also because the scenario I mentioned
> > above, I want those processes to always use PMD sized folios as backend to make sure
> > performance. 
> 
> "Always" is wishful thinking in many scenarios I'm afraid.

Yeah, I agree, so we also think that maybe we can do some other things to increase the
possibility of allocating pmd sized folios. :) 

> 
> > COW is truly not that common like swap out/swap in, it just can happen
> > sometimes, which I guess the reason may be about image duplication. Setting the system
> > globally is more convenient for my situation :). I can go without this global switch
> > if it's more reasonable.
> > 
> >> A per-VMA flag? Maybe, but I expect pushback as well, as it is way too specific.
> >> So we'd have to find some concept that abstracts these semantics. But I expect
> >> pushback as well.
> >>
> >> We messed up enough with toggles in THP space, unfortunately.
> >>
> >> Also, anything that only works for PMD-sized THPs is a warning sign in 2026 :)
> >>
> > 
> > And for PMD-sized THPs, actually, I’m also considering adding more support for
> > COW to mTHP if the upstream consider it useful. And also for pud sized THPs
> > also. But I guess I have to firstly handle stage 1: PMD level COW now :).
> > And also, since PMD sized folio is commonly used in my workload, I'm also wondering
> > digging into pmd sized KSM in the future, in which I think pmd sized COW may
> > be more useful then :).
> 
> I'm afraid I have to stop you right there: there has to be a pretty convincing
> story to add any of that. In particular KSM with large folios (/me shivering).
> 
> But I already don't buy the COW story. Just configure khugepaged in a better way
> or use MADV_COLLAPSE and you don't really need to modify the kernel at all in
> 99.99% of the case. (khugepaged needs a lot of tuning work, it's currently not
> the smartest implementation)
> 
> Maybe, we might give khugepaged better direction of what to try scanning next
> (e.g., where we just COW'ed a THP). Not sure, there are plenty of things to explore.
> 

I see, actually all the things here is about saving memory and keeping the 2M tlb
benifit at the same time, including my KSM thought and THP COW. Giving khugepaged
a better direction is also what we are considering in the next step :).

> > 
> >> You don't really raise any concrete use cases or performance numbers for these
> >> use cases. Some details about applications that use fork() and rely on such
> >> behavior would be helpful.
> >>
> > 
> > Sorry for that, the concrete workload itself hasn't been finished yet. 
> 
> Okay, what I thought after seeing no workloads an no performance numbers :)
> 
> If you don't know the workload, how can you claim that the additional latency
> and/or memory consumption is not a problem?
> 

Oh, our point here is that we don't want these 2M folios to be split, since that
may cause some fraction and increases the difficulty a little for allocating a
2M folio later as the time goes on. It may cause some additional latency and memory
consumption, that's true, but the workload we are trying to do wants to make sure
the 2M folios in the system first. And though the workload hasn't been finished, we
analyzed what it will do, and we think for the most time, COWed 2M folio will be
written in a range much bigger than 4K. So it will will cause more page faults if
the fault size is 4K :). As we see, the time consuming of only one 2M sized page
fault should be able to beat the multiple 4K sized page fault, since the copy
can be merged together and there are not so many times of handling other than
copy. We can see a performance improvement in the end to end copying test which
includes many possible COW on 2M pages, though. The performance improvement is
about 2 times. The improvement is not big as we imagine it should be. We'll dig
into more details then. :)

> Also: there is no guarantee that you will actually succeed in allocating a PMD
> THP during a COW fault.
> 

Yes, so we are actually also considering some solutions to increase the success rate
of allocating PMD sized THP like reserving, the idea comes from TAO of Yu Zhao in
https://lore.kernel.org/all/20240229183436.4110845-1-yuzhao@google.com/. But we may
do the reserving using another approach like migratetype though since that may make
it easier to dynamically change the reserving size. We also want to discuss it with
the upstream about this if anyone has time :).

> 
> > Now it just
> > can happen sometimes in my multi-2M-sized-processes workload test. But the user
> > of our 2M sized folio schema is actually not necessarily myself but also can be the
> > userspace developers. I cannot guarantee that fork will not be used in the
> > performance test of their workload since that is a normal posix call. Maybe a little
> > overthinking? :)
> 
> If your application cares about performance, you either shouldn't be using
> fork(), or you should be using it very, very wisely (e.g., interaction with
> multi-threading, MADV_DONTFORK, avoid touching memory in parent until child
> completed).
> 

Agreed, we'll try that, thank you. :)

> > I just think swap and COW are two main scenarios that may transparently split pmd sized
> > folios, so maybe we can solve it and make THP both reclaimable and stable.
> 
> There is page migration, MADV_DONTNEED, munmap/mremap/madvise/mprotect in sub-2M
> blocks, memory failure handling and probably a lot more. THP allocation might
> fail. THP swapout+swapin might fail to allocate THPs.
> 

Yes, in my former consideration I just thought munmap/mremap/madvise/mprotect are actively
called, so it should be easier to restrict them, like directly fail them when the user calls
them in sub-2M blocks. Page migration is truly another scenorio we need to handle. But
it's not that likely to happen if we configure it so since we have our
CONFIG_ARCH_ENABLE_THP_MIGRATION setup, things like numa balancing, live migration, compaction
can all be disabled though. Maybe there is something I missed here? :)

> Tackling COW handling when you don't even know that it's a real problem seems
> premature.
> 

Yeah, I guess THP COW can be replaced by other approaches. We just think maybe we can
firstly add the THP COW, then the COW that may happen in fork, pmd swap in, and maybe other
places can all benifit from it. But maybe it's still not the only way to solve the
problem we meet right now.

> > Maybe
> > that can make THP more widely used in real deployed environment since the resource
> > can become more controllable for the users :). That's why I was thinking maybe
> > implementing it with setup switches is a reasonable solution?
> 
> No magical toggles.
> 
> > 
> >> Note that an application that does fork() could use MADV_COLLAPSE after fork()
> >> to make sure that it immediately gets THPs back.
> >>
> >> There is also the option to just use MADV_DONTFORK to not even share ranges with
> >> a child process in the first place, avoiding page copies entirely.
> >>
> > 
> > MADV_DONTFORK and MADV_COLLAPSE are nice and great options :), but the former one seems
> > to be a little wasteful :). 
> 
> It's actually the right thing to do (tm) if you care about fork() performance
> and know that your child will not actually need certain memory areas.
> 
> For example, in QEMU we use it to exclude all guest memory from fork(), heavily
> improving fork() performance. [there are not a lot of fork() use cases left in
> QEMU today, fortunately]
> 

Yeah, these two are nice in many situations, we'll consider using MADV_DONTFORK and
MADV_COLLAPSE and other tools for our workload, thanks!

> -- 
> Cheers,
> 
> David

Best regards,
Luka

^ permalink raw reply

* [PATCH] char: dtlk: remove driver for ISA speech synthesizer card
From: Ethan Nelson-Moore @ 2026-05-02  4:33 UTC (permalink / raw)
  To: linux-kernel, linux-doc
  Cc: James R. Van Zandt, Ethan Nelson-Moore, Jonathan Corbet,
	Shuah Khan, Madhavan Srinivasan, Michael Ellerman,
	Nicholas Piggin, Christophe Leroy (CS GROUP), Arnd Bergmann,
	Greg Kroah-Hartman, Bagas Sanjaya, Haren Myneni, Jakub Kicinski,
	Andrew Lunn, Eric Biggers

The dtlk driver supports the RC Systems DoubleTalk PC ISA speech
synthesizer card. It has severe coding style issues and has only
received tree-wide fixes and drive-by cleanups in the entire Git
history (since Linux 2.6.12-rc2). The same hardware is supported by
drivers/accessibility/speakup for screen reader use, but that
implementation does not share any code with this driver. Given all of
these factors, it is likely the driver is entirely unused. Remove it to
reduce future maintenance workload.

Note: The removed maintainer is already listed in CREDITS.
Signed-off-by: Ethan Nelson-Moore <enelsonmoore@gmail.com>
---
 .../userspace-api/ioctl/ioctl-number.rst      |   1 -
 MAINTAINERS                                   |   7 -
 arch/powerpc/configs/ppc6xx_defconfig         |   1 -
 drivers/char/Kconfig                          |  11 -
 drivers/char/Makefile                         |   1 -
 drivers/char/dtlk.c                           | 663 ------------------
 include/linux/dtlk.h                          |  86 ---
 7 files changed, 770 deletions(-)
 delete mode 100644 drivers/char/dtlk.c
 delete mode 100644 include/linux/dtlk.h

diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst
index 331223761fff..23ca772fbdf0 100644
--- a/Documentation/userspace-api/ioctl/ioctl-number.rst
+++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
@@ -342,7 +342,6 @@ Code  Seq#    Include File                                             Comments
 0xA2  all    uapi/linux/acrn.h                                         ACRN hypervisor
 0xA3  80-8F                                                            Port ACL  in development:
                                                                        <mailto:tlewis@mindspring.com>
-0xA3  90-9F  linux/dtlk.h
 0xA4  00-1F  uapi/linux/tee.h                                          Generic TEE subsystem
 0xA4  00-1F  uapi/asm/sgx.h                                            <mailto:linux-sgx@vger.kernel.org>
 0xA5  01-05  linux/surface_aggregator/cdev.h                           Microsoft Surface Platform System Aggregator
diff --git a/MAINTAINERS b/MAINTAINERS
index 882214b0e7db..9b61010c80d3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7721,13 +7721,6 @@ T:	git git://linuxtv.org/media.git
 F:	Documentation/devicetree/bindings/media/i2c/dongwoon,dw9807-vcm.yaml
 F:	drivers/media/i2c/dw9807-vcm.c
 
-DOUBLETALK DRIVER
-M:	"James R. Van Zandt" <jrv@vanzandt.mv.com>
-L:	blinux-list@redhat.com
-S:	Maintained
-F:	drivers/char/dtlk.c
-F:	include/linux/dtlk.h
-
 DPAA2 DATAPATH I/O (DPIO) DRIVER
 M:	Roy Pledge <Roy.Pledge@nxp.com>
 L:	linux-kernel@vger.kernel.org
diff --git a/arch/powerpc/configs/ppc6xx_defconfig b/arch/powerpc/configs/ppc6xx_defconfig
index ccabc6e17168..0763da873eb0 100644
--- a/arch/powerpc/configs/ppc6xx_defconfig
+++ b/arch/powerpc/configs/ppc6xx_defconfig
@@ -576,7 +576,6 @@ CONFIG_PPDEV=m
 CONFIG_HW_RANDOM=y
 CONFIG_HW_RANDOM_VIRTIO=m
 CONFIG_NVRAM=y
-CONFIG_DTLK=m
 CONFIG_IPWIRELESS=m
 CONFIG_I2C_CHARDEV=m
 CONFIG_I2C_HYDRA=m
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 2a3a37b2cf3c..3063b337907b 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -199,17 +199,6 @@ config NWFLASH
 
 source "drivers/char/hw_random/Kconfig"
 
-config DTLK
-	tristate "Double Talk PC internal speech card support"
-	depends on ISA
-	help
-	  This driver is for the DoubleTalk PC, a speech synthesizer
-	  manufactured by RC Systems (<https://www.rcsys.com/>).  It is also
-	  called the `internal DoubleTalk'.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called dtlk.
-
 config XILINX_HWICAP
 	tristate "Xilinx HWICAP Support"
 	depends on MICROBLAZE
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 47bdc882797a..c9060054ddbc 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -16,7 +16,6 @@ obj-$(CONFIG_PRINTER)		+= lp.o
 
 obj-$(CONFIG_APM_EMULATION)	+= apm-emulation.o
 
-obj-$(CONFIG_DTLK)		+= dtlk.o
 obj-$(CONFIG_APPLICOM)		+= applicom.o
 obj-$(CONFIG_SONYPI)		+= sonypi.o
 obj-$(CONFIG_HPET)		+= hpet.o
diff --git a/drivers/char/dtlk.c b/drivers/char/dtlk.c
deleted file mode 100644
index 16618079298a..000000000000
--- a/drivers/char/dtlk.c
+++ /dev/null
@@ -1,663 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*                                              -*- linux-c -*-
- * dtlk.c - DoubleTalk PC driver for Linux
- *
- * Original author: Chris Pallotta <chris@allmedia.com>
- * Current maintainer: Jim Van Zandt <jrv@vanzandt.mv.com>
- * 
- * 2000-03-18 Jim Van Zandt: Fix polling.
- *  Eliminate dtlk_timer_active flag and separate dtlk_stop_timer
- *  function.  Don't restart timer in dtlk_timer_tick.  Restart timer
- *  in dtlk_poll after every poll.  dtlk_poll returns mask (duh).
- *  Eliminate unused function dtlk_write_byte.  Misc. code cleanups.
- */
-
-/* This driver is for the DoubleTalk PC, a speech synthesizer
-   manufactured by RC Systems (http://www.rcsys.com/).  It was written
-   based on documentation in their User's Manual file and Developer's
-   Tools disk.
-
-   The DoubleTalk PC contains four voice synthesizers: text-to-speech
-   (TTS), linear predictive coding (LPC), PCM/ADPCM, and CVSD.  It
-   also has a tone generator.  Output data for LPC are written to the
-   LPC port, and output data for the other modes are written to the
-   TTS port.
-
-   Two kinds of data can be read from the DoubleTalk: status
-   information (in response to the "\001?" interrogation command) is
-   read from the TTS port, and index markers (which mark the progress
-   of the speech) are read from the LPC port.  Not all models of the
-   DoubleTalk PC implement index markers.  Both the TTS and LPC ports
-   can also display status flags.
-
-   The DoubleTalk PC generates no interrupts.
-
-   These characteristics are mapped into the Unix stream I/O model as
-   follows:
-
-   "write" sends bytes to the TTS port.  It is the responsibility of
-   the user program to switch modes among TTS, PCM/ADPCM, and CVSD.
-   This driver was written for use with the text-to-speech
-   synthesizer.  If LPC output is needed some day, other minor device
-   numbers can be used to select among output modes.
-
-   "read" gets index markers from the LPC port.  If the device does
-   not implement index markers, the read will fail with error EINVAL.
-
-   Status information is available using the DTLK_INTERROGATE ioctl.
-
- */
-
-#include <linux/module.h>
-
-#define KERNEL
-#include <linux/types.h>
-#include <linux/fs.h>
-#include <linux/mm.h>
-#include <linux/errno.h>	/* for -EBUSY */
-#include <linux/ioport.h>	/* for request_region */
-#include <linux/delay.h>	/* for loops_per_jiffy */
-#include <linux/sched.h>
-#include <linux/mutex.h>
-#include <asm/io.h>		/* for inb_p, outb_p, inb, outb, etc. */
-#include <linux/uaccess.h>	/* for get_user, etc. */
-#include <linux/wait.h>		/* for wait_queue */
-#include <linux/init.h>		/* for __init, module_{init,exit} */
-#include <linux/poll.h>		/* for EPOLLIN, etc. */
-#include <linux/dtlk.h>		/* local header file for DoubleTalk values */
-
-#ifdef TRACING
-#define TRACE_TEXT(str) printk(str);
-#define TRACE_RET printk(")")
-#else				/* !TRACING */
-#define TRACE_TEXT(str) ((void) 0)
-#define TRACE_RET ((void) 0)
-#endif				/* TRACING */
-
-static DEFINE_MUTEX(dtlk_mutex);
-static void dtlk_timer_tick(struct timer_list *unused);
-
-static int dtlk_major;
-static int dtlk_port_lpc;
-static int dtlk_port_tts;
-static int dtlk_busy;
-static int dtlk_has_indexing;
-static unsigned int dtlk_portlist[] =
-{0x25e, 0x29e, 0x2de, 0x31e, 0x35e, 0x39e, 0};
-static wait_queue_head_t dtlk_process_list;
-static DEFINE_TIMER(dtlk_timer, dtlk_timer_tick);
-
-/* prototypes for file_operations struct */
-static ssize_t dtlk_read(struct file *, char __user *,
-			 size_t nbytes, loff_t * ppos);
-static ssize_t dtlk_write(struct file *, const char __user *,
-			  size_t nbytes, loff_t * ppos);
-static __poll_t dtlk_poll(struct file *, poll_table *);
-static int dtlk_open(struct inode *, struct file *);
-static int dtlk_release(struct inode *, struct file *);
-static long dtlk_ioctl(struct file *file,
-		       unsigned int cmd, unsigned long arg);
-
-static const struct file_operations dtlk_fops =
-{
-	.owner		= THIS_MODULE,
-	.read		= dtlk_read,
-	.write		= dtlk_write,
-	.poll		= dtlk_poll,
-	.unlocked_ioctl	= dtlk_ioctl,
-	.open		= dtlk_open,
-	.release	= dtlk_release,
-};
-
-/* local prototypes */
-static int dtlk_dev_probe(void);
-static struct dtlk_settings *dtlk_interrogate(void);
-static int dtlk_readable(void);
-static char dtlk_read_lpc(void);
-static char dtlk_read_tts(void);
-static int dtlk_writeable(void);
-static char dtlk_write_bytes(const char *buf, int n);
-static char dtlk_write_tts(char);
-/*
-   static void dtlk_handle_error(char, char, unsigned int);
- */
-
-static ssize_t dtlk_read(struct file *file, char __user *buf,
-			 size_t count, loff_t * ppos)
-{
-	unsigned int minor = iminor(file_inode(file));
-	char ch;
-	int i = 0, retries;
-
-	TRACE_TEXT("(dtlk_read");
-	/*  printk("DoubleTalk PC - dtlk_read()\n"); */
-
-	if (minor != DTLK_MINOR || !dtlk_has_indexing)
-		return -EINVAL;
-
-	for (retries = 0; retries < loops_per_jiffy; retries++) {
-		while (i < count && dtlk_readable()) {
-			ch = dtlk_read_lpc();
-			/*        printk("dtlk_read() reads 0x%02x\n", ch); */
-			if (put_user(ch, buf++))
-				return -EFAULT;
-			i++;
-		}
-		if (i)
-			return i;
-		if (file->f_flags & O_NONBLOCK)
-			break;
-		msleep_interruptible(100);
-	}
-	if (retries == loops_per_jiffy)
-		printk(KERN_ERR "dtlk_read times out\n");
-	TRACE_RET;
-	return -EAGAIN;
-}
-
-static ssize_t dtlk_write(struct file *file, const char __user *buf,
-			  size_t count, loff_t * ppos)
-{
-	int i = 0, retries = 0, ch;
-
-	TRACE_TEXT("(dtlk_write");
-#ifdef TRACING
-	printk(" \"");
-	{
-		int i, ch;
-		for (i = 0; i < count; i++) {
-			if (get_user(ch, buf + i))
-				return -EFAULT;
-			if (' ' <= ch && ch <= '~')
-				printk("%c", ch);
-			else
-				printk("\\%03o", ch);
-		}
-		printk("\"");
-	}
-#endif
-
-	if (iminor(file_inode(file)) != DTLK_MINOR)
-		return -EINVAL;
-
-	while (1) {
-		while (i < count && !get_user(ch, buf) &&
-		       (ch == DTLK_CLEAR || dtlk_writeable())) {
-			dtlk_write_tts(ch);
-			buf++;
-			i++;
-			if (i % 5 == 0)
-				/* We yield our time until scheduled
-				   again.  This reduces the transfer
-				   rate to 500 bytes/sec, but that's
-				   still enough to keep up with the
-				   speech synthesizer. */
-				msleep_interruptible(1);
-			else {
-				/* the RDY bit goes zero 2-3 usec
-				   after writing, and goes 1 again
-				   180-190 usec later.  Here, we wait
-				   up to 250 usec for the RDY bit to
-				   go nonzero. */
-				for (retries = 0;
-				     retries < loops_per_jiffy / (4000/HZ);
-				     retries++)
-					if (inb_p(dtlk_port_tts) &
-					    TTS_WRITABLE)
-						break;
-			}
-			retries = 0;
-		}
-		if (i == count)
-			return i;
-		if (file->f_flags & O_NONBLOCK)
-			break;
-
-		msleep_interruptible(1);
-
-		if (++retries > 10 * HZ) { /* wait no more than 10 sec
-					      from last write */
-			printk("dtlk: write timeout.  "
-			       "inb_p(dtlk_port_tts) = 0x%02x\n",
-			       inb_p(dtlk_port_tts));
-			TRACE_RET;
-			return -EBUSY;
-		}
-	}
-	TRACE_RET;
-	return -EAGAIN;
-}
-
-static __poll_t dtlk_poll(struct file *file, poll_table * wait)
-{
-	__poll_t mask = 0;
-	unsigned long expires;
-
-	TRACE_TEXT(" dtlk_poll");
-	/*
-	   static long int j;
-	   printk(".");
-	   printk("<%ld>", jiffies-j);
-	   j=jiffies;
-	 */
-	poll_wait(file, &dtlk_process_list, wait);
-
-	if (dtlk_has_indexing && dtlk_readable()) {
-	        timer_delete(&dtlk_timer);
-		mask = EPOLLIN | EPOLLRDNORM;
-	}
-	if (dtlk_writeable()) {
-	        timer_delete(&dtlk_timer);
-		mask |= EPOLLOUT | EPOLLWRNORM;
-	}
-	/* there are no exception conditions */
-
-	/* There won't be any interrupts, so we set a timer instead. */
-	expires = jiffies + 3*HZ / 100;
-	mod_timer(&dtlk_timer, expires);
-
-	return mask;
-}
-
-static void dtlk_timer_tick(struct timer_list *unused)
-{
-	TRACE_TEXT(" dtlk_timer_tick");
-	wake_up_interruptible(&dtlk_process_list);
-}
-
-static long dtlk_ioctl(struct file *file,
-		       unsigned int cmd,
-		       unsigned long arg)
-{
-	char __user *argp = (char __user *)arg;
-	struct dtlk_settings *sp;
-	char portval;
-	TRACE_TEXT(" dtlk_ioctl");
-
-	switch (cmd) {
-
-	case DTLK_INTERROGATE:
-		mutex_lock(&dtlk_mutex);
-		sp = dtlk_interrogate();
-		mutex_unlock(&dtlk_mutex);
-		if (copy_to_user(argp, sp, sizeof(struct dtlk_settings)))
-			return -EINVAL;
-		return 0;
-
-	case DTLK_STATUS:
-		portval = inb_p(dtlk_port_tts);
-		return put_user(portval, argp);
-
-	default:
-		return -EINVAL;
-	}
-}
-
-/* Note that nobody ever sets dtlk_busy... */
-static int dtlk_open(struct inode *inode, struct file *file)
-{
-	TRACE_TEXT("(dtlk_open");
-
-	switch (iminor(inode)) {
-	case DTLK_MINOR:
-		if (dtlk_busy)
-			return -EBUSY;
-		return stream_open(inode, file);
-
-	default:
-		return -ENXIO;
-	}
-}
-
-static int dtlk_release(struct inode *inode, struct file *file)
-{
-	TRACE_TEXT("(dtlk_release");
-
-	switch (iminor(inode)) {
-	case DTLK_MINOR:
-		break;
-
-	default:
-		break;
-	}
-	TRACE_RET;
-	
-	timer_delete_sync(&dtlk_timer);
-
-	return 0;
-}
-
-static int __init dtlk_init(void)
-{
-	int err;
-
-	dtlk_port_lpc = 0;
-	dtlk_port_tts = 0;
-	dtlk_busy = 0;
-	dtlk_major = register_chrdev(0, "dtlk", &dtlk_fops);
-	if (dtlk_major < 0) {
-		printk(KERN_ERR "DoubleTalk PC - cannot register device\n");
-		return dtlk_major;
-	}
-	err = dtlk_dev_probe();
-	if (err) {
-		unregister_chrdev(dtlk_major, "dtlk");
-		return err;
-	}
-	printk(", MAJOR %d\n", dtlk_major);
-
-	init_waitqueue_head(&dtlk_process_list);
-
-	return 0;
-}
-
-static void __exit dtlk_cleanup (void)
-{
-	dtlk_write_bytes("goodbye", 8);
-	msleep_interruptible(500);		/* nap 0.50 sec but
-						   could be awakened
-						   earlier by
-						   signals... */
-
-	dtlk_write_tts(DTLK_CLEAR);
-	unregister_chrdev(dtlk_major, "dtlk");
-	release_region(dtlk_port_lpc, DTLK_IO_EXTENT);
-}
-
-module_init(dtlk_init);
-module_exit(dtlk_cleanup);
-
-/* ------------------------------------------------------------------------ */
-
-static int dtlk_readable(void)
-{
-#ifdef TRACING
-	printk(" dtlk_readable=%u@%u", inb_p(dtlk_port_lpc) != 0x7f, jiffies);
-#endif
-	return inb_p(dtlk_port_lpc) != 0x7f;
-}
-
-static int dtlk_writeable(void)
-{
-	/* TRACE_TEXT(" dtlk_writeable"); */
-#ifdef TRACINGMORE
-	printk(" dtlk_writeable=%u", (inb_p(dtlk_port_tts) & TTS_WRITABLE)!=0);
-#endif
-	return inb_p(dtlk_port_tts) & TTS_WRITABLE;
-}
-
-static int __init dtlk_dev_probe(void)
-{
-	unsigned int testval = 0;
-	int i = 0;
-	struct dtlk_settings *sp;
-
-	if (dtlk_port_lpc | dtlk_port_tts)
-		return -EBUSY;
-
-	for (i = 0; dtlk_portlist[i]; i++) {
-#if 0
-		printk("DoubleTalk PC - Port %03x = %04x\n",
-		       dtlk_portlist[i], (testval = inw_p(dtlk_portlist[i])));
-#endif
-
-		if (!request_region(dtlk_portlist[i], DTLK_IO_EXTENT, 
-			       "dtlk"))
-			continue;
-		testval = inw_p(dtlk_portlist[i]);
-		if ((testval &= 0xfbff) == 0x107f) {
-			dtlk_port_lpc = dtlk_portlist[i];
-			dtlk_port_tts = dtlk_port_lpc + 1;
-
-			sp = dtlk_interrogate();
-			printk("DoubleTalk PC at %03x-%03x, "
-			       "ROM version %s, serial number %u",
-			       dtlk_portlist[i], dtlk_portlist[i] +
-			       DTLK_IO_EXTENT - 1,
-			       sp->rom_version, sp->serial_number);
-
-                        /* put LPC port into known state, so
-			   dtlk_readable() gives valid result */
-			outb_p(0xff, dtlk_port_lpc); 
-
-                        /* INIT string and index marker */
-			dtlk_write_bytes("\036\1@\0\0012I\r", 8);
-			/* posting an index takes 18 msec.  Here, we
-			   wait up to 100 msec to see whether it
-			   appears. */
-			msleep_interruptible(100);
-			dtlk_has_indexing = dtlk_readable();
-#ifdef TRACING
-			printk(", indexing %d\n", dtlk_has_indexing);
-#endif
-#ifdef INSCOPE
-			{
-/* This macro records ten samples read from the LPC port, for later display */
-#define LOOK					\
-for (i = 0; i < 10; i++)			\
-  {						\
-    buffer[b++] = inb_p(dtlk_port_lpc);		\
-    __delay(loops_per_jiffy/(1000000/HZ));             \
-  }
-				char buffer[1000];
-				int b = 0, i, j;
-
-				LOOK
-				outb_p(0xff, dtlk_port_lpc);
-				buffer[b++] = 0;
-				LOOK
-				dtlk_write_bytes("\0012I\r", 4);
-				buffer[b++] = 0;
-				__delay(50 * loops_per_jiffy / (1000/HZ));
-				outb_p(0xff, dtlk_port_lpc);
-				buffer[b++] = 0;
-				LOOK
-
-				printk("\n");
-				for (j = 0; j < b; j++)
-					printk(" %02x", buffer[j]);
-				printk("\n");
-			}
-#endif				/* INSCOPE */
-
-#ifdef OUTSCOPE
-			{
-/* This macro records ten samples read from the TTS port, for later display */
-#define LOOK					\
-for (i = 0; i < 10; i++)			\
-  {						\
-    buffer[b++] = inb_p(dtlk_port_tts);		\
-    __delay(loops_per_jiffy/(1000000/HZ));  /* 1 us */ \
-  }
-				char buffer[1000];
-				int b = 0, i, j;
-
-				mdelay(10);	/* 10 ms */
-				LOOK
-				outb_p(0x03, dtlk_port_tts);
-				buffer[b++] = 0;
-				LOOK
-				LOOK
-
-				printk("\n");
-				for (j = 0; j < b; j++)
-					printk(" %02x", buffer[j]);
-				printk("\n");
-			}
-#endif				/* OUTSCOPE */
-
-			dtlk_write_bytes("Double Talk found", 18);
-
-			return 0;
-		}
-		release_region(dtlk_portlist[i], DTLK_IO_EXTENT);
-	}
-
-	printk(KERN_INFO "DoubleTalk PC - not found\n");
-	return -ENODEV;
-}
-
-/*
-   static void dtlk_handle_error(char op, char rc, unsigned int minor)
-   {
-   printk(KERN_INFO"\nDoubleTalk PC - MINOR: %d, OPCODE: %d, ERROR: %d\n", 
-   minor, op, rc);
-   return;
-   }
- */
-
-/* interrogate the DoubleTalk PC and return its settings */
-static struct dtlk_settings *dtlk_interrogate(void)
-{
-	unsigned char *t;
-	static char buf[sizeof(struct dtlk_settings) + 1];
-	int total, i;
-	static struct dtlk_settings status;
-	TRACE_TEXT("(dtlk_interrogate");
-	dtlk_write_bytes("\030\001?", 3);
-	for (total = 0, i = 0; i < 50; i++) {
-		buf[total] = dtlk_read_tts();
-		if (total > 2 && buf[total] == 0x7f)
-			break;
-		if (total < sizeof(struct dtlk_settings))
-			total++;
-	}
-	/*
-	   if (i==50) printk("interrogate() read overrun\n");
-	   for (i=0; i<sizeof(buf); i++)
-	   printk(" %02x", buf[i]);
-	   printk("\n");
-	 */
-	t = buf;
-	status.serial_number = t[0] + t[1] * 256; /* serial number is
-						     little endian */
-	t += 2;
-
-	i = 0;
-	while (*t != '\r') {
-		status.rom_version[i] = *t;
-		if (i < sizeof(status.rom_version) - 1)
-			i++;
-		t++;
-	}
-	status.rom_version[i] = 0;
-	t++;
-
-	status.mode = *t++;
-	status.punc_level = *t++;
-	status.formant_freq = *t++;
-	status.pitch = *t++;
-	status.speed = *t++;
-	status.volume = *t++;
-	status.tone = *t++;
-	status.expression = *t++;
-	status.ext_dict_loaded = *t++;
-	status.ext_dict_status = *t++;
-	status.free_ram = *t++;
-	status.articulation = *t++;
-	status.reverb = *t++;
-	status.eob = *t++;
-	status.has_indexing = dtlk_has_indexing;
-	TRACE_RET;
-	return &status;
-}
-
-static char dtlk_read_tts(void)
-{
-	int portval, retries = 0;
-	char ch;
-	TRACE_TEXT("(dtlk_read_tts");
-
-	/* verify DT is ready, read char, wait for ACK */
-	do {
-		portval = inb_p(dtlk_port_tts);
-	} while ((portval & TTS_READABLE) == 0 &&
-		 retries++ < DTLK_MAX_RETRIES);
-	if (retries > DTLK_MAX_RETRIES)
-		printk(KERN_ERR "dtlk_read_tts() timeout\n");
-
-	ch = inb_p(dtlk_port_tts);	/* input from TTS port */
-	ch &= 0x7f;
-	outb_p(ch, dtlk_port_tts);
-
-	retries = 0;
-	do {
-		portval = inb_p(dtlk_port_tts);
-	} while ((portval & TTS_READABLE) != 0 &&
-		 retries++ < DTLK_MAX_RETRIES);
-	if (retries > DTLK_MAX_RETRIES)
-		printk(KERN_ERR "dtlk_read_tts() timeout\n");
-
-	TRACE_RET;
-	return ch;
-}
-
-static char dtlk_read_lpc(void)
-{
-	int retries = 0;
-	char ch;
-	TRACE_TEXT("(dtlk_read_lpc");
-
-	/* no need to test -- this is only called when the port is readable */
-
-	ch = inb_p(dtlk_port_lpc);	/* input from LPC port */
-
-	outb_p(0xff, dtlk_port_lpc);
-
-	/* acknowledging a read takes 3-4
-	   usec.  Here, we wait up to 20 usec
-	   for the acknowledgement */
-	retries = (loops_per_jiffy * 20) / (1000000/HZ);
-	while (inb_p(dtlk_port_lpc) != 0x7f && --retries > 0);
-	if (retries == 0)
-		printk(KERN_ERR "dtlk_read_lpc() timeout\n");
-
-	TRACE_RET;
-	return ch;
-}
-
-/* write n bytes to tts port */
-static char dtlk_write_bytes(const char *buf, int n)
-{
-	char val = 0;
-	/*  printk("dtlk_write_bytes(\"%-*s\", %d)\n", n, buf, n); */
-	TRACE_TEXT("(dtlk_write_bytes");
-	while (n-- > 0)
-		val = dtlk_write_tts(*buf++);
-	TRACE_RET;
-	return val;
-}
-
-static char dtlk_write_tts(char ch)
-{
-	int retries = 0;
-#ifdef TRACINGMORE
-	printk("  dtlk_write_tts(");
-	if (' ' <= ch && ch <= '~')
-		printk("'%c'", ch);
-	else
-		printk("0x%02x", ch);
-#endif
-	if (ch != DTLK_CLEAR)	/* no flow control for CLEAR command */
-		while ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0 &&
-		       retries++ < DTLK_MAX_RETRIES)	/* DT ready? */
-			;
-	if (retries > DTLK_MAX_RETRIES)
-		printk(KERN_ERR "dtlk_write_tts() timeout\n");
-
-	outb_p(ch, dtlk_port_tts);	/* output to TTS port */
-	/* the RDY bit goes zero 2-3 usec after writing, and goes
-	   1 again 180-190 usec later.  Here, we wait up to 10
-	   usec for the RDY bit to go zero. */
-	for (retries = 0; retries < loops_per_jiffy / (100000/HZ); retries++)
-		if ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0)
-			break;
-
-#ifdef TRACINGMORE
-	printk(")\n");
-#endif
-	return 0;
-}
-
-MODULE_DESCRIPTION("RC Systems DoubleTalk PC speech card driver");
-MODULE_LICENSE("GPL");
diff --git a/include/linux/dtlk.h b/include/linux/dtlk.h
deleted file mode 100644
index 27b95e70bde3..000000000000
--- a/include/linux/dtlk.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#define DTLK_MINOR	0
-#define DTLK_IO_EXTENT	0x02
-
-	/* ioctl's use magic number of 0xa3 */
-#define DTLK_INTERROGATE 0xa390	/* get settings from the DoubleTalk */
-#define DTLK_STATUS 0xa391	/* get status from the DoubleTalk */
-
-
-#define DTLK_CLEAR 0x18		/* stops speech */
-
-#define DTLK_MAX_RETRIES (loops_per_jiffy/(10000/HZ))
-
-	/* TTS Port Status Flags */
-#define TTS_READABLE     0x80	/* mask for bit which is nonzero if a
-				   byte can be read from the TTS port */
-#define TTS_SPEAKING     0x40	/* mask for SYNC bit, which is nonzero
-				   while DoubleTalk is producing
-				   output with TTS, PCM or CVSD
-				   synthesizers or tone generators
-				   (that is, all but LPC) */
-#define TTS_SPEAKING2    0x20	/* mask for SYNC2 bit,
-				   which falls to zero up to 0.4 sec
-				   before speech stops */
-#define TTS_WRITABLE     0x10	/* mask for RDY bit, which when set to
-             			   1, indicates the TTS port is ready
-             			   to accept a byte of data.  The RDY
-             			   bit goes zero 2-3 usec after
-             			   writing, and goes 1 again 180-190
-             			   usec later. */
-#define TTS_ALMOST_FULL  0x08	/* mask for AF bit: When set to 1,
-				   indicates that less than 300 free
-				   bytes are available in the TTS
-				   input buffer. AF is always 0 in the
-				   PCM, TGN and CVSD modes. */
-#define TTS_ALMOST_EMPTY 0x04	/* mask for AE bit: When set to 1,
-				   indicates that less than 300 bytes
-				   of data remain in DoubleTalk's
-				   input (TTS or PCM) buffer. AE is
-				   always 1 in the TGN and CVSD
-				   modes. */
-
-	/* LPC speak commands */
-#define LPC_5220_NORMAL 0x60	/* 5220 format decoding table, normal rate */
-#define LPC_5220_FAST 0x64	/* 5220 format decoding table, fast rate */
-#define LPC_D6_NORMAL 0x20	/* D6 format decoding table, normal rate */
-#define LPC_D6_FAST 0x24	/* D6 format decoding table, fast rate */
-
-	/* LPC Port Status Flags (valid only after one of the LPC
-           speak commands) */
-#define LPC_SPEAKING     0x80	/* mask for TS bit: When set to 1,
-				   indicates the LPC synthesizer is
-				   producing speech.*/
-#define LPC_BUFFER_LOW   0x40	/* mask for BL bit: When set to 1,
-				   indicates that the hardware LPC
-				   data buffer has less than 30 bytes
-				   remaining. (Total internal buffer
-				   size = 4096 bytes.) */
-#define LPC_BUFFER_EMPTY 0x20	/* mask for BE bit: When set to 1,
-				   indicates that the LPC data buffer
-				   ran out of data (error condition if
-				   TS is also 1).  */
-
-				/* data returned by Interrogate command */
-struct dtlk_settings
-{
-  unsigned short serial_number;	/* 0-7Fh:0-7Fh */
-  unsigned char rom_version[24]; /* null terminated string */
-  unsigned char mode;		/* 0=Character; 1=Phoneme; 2=Text */
-  unsigned char punc_level;	/* nB; 0-7 */
-  unsigned char formant_freq;	/* nF; 0-9 */
-  unsigned char pitch;		/* nP; 0-99 */
-  unsigned char speed;		/* nS; 0-9 */
-  unsigned char volume;		/* nV; 0-9 */
-  unsigned char tone;		/* nX; 0-2 */
-  unsigned char expression;	/* nE; 0-9 */
-  unsigned char ext_dict_loaded; /* 1=exception dictionary loaded */
-  unsigned char ext_dict_status; /* 1=exception dictionary enabled */
-  unsigned char free_ram;	/* # pages (truncated) remaining for
-                                   text buffer */
-  unsigned char articulation;	/* nA; 0-9 */
-  unsigned char reverb;		/* nR; 0-9 */
-  unsigned char eob;		/* 7Fh value indicating end of
-                                   parameter block */
-  unsigned char has_indexing;	/* nonzero if indexing is implemented */
-};
-- 
2.43.0


^ permalink raw reply related

* Re: [PATCH v1] docs: Remove icn= ISDN parameter
From: Jakub Kicinski @ 2026-05-02  2:05 UTC (permalink / raw)
  To: Jonathan Corbet
  Cc: Costa Shulyupin, Shuah Khan, Andrew Morton, Borislav Petkov (AMD),
	Randy Dunlap, Dave Hansen, Dapeng Mi, Kees Cook, Marco Elver,
	Li RongQing, Eric Biggers, Paul E. McKenney, linux-doc,
	linux-kernel
In-Reply-To: <20260501182634.1110715-1-costa.shul@redhat.com>

On Fri,  1 May 2026 21:26:30 +0300 Costa Shulyupin wrote:
> The ICN ISDN driver was removed in commit 02bbd9802da7
> ("staging: i4l: delete the whole thing"), but the icn= kernel
> parameter documentation was left behind.
> 
> Assisted-by: Claude:claude-opus-4-6
> Signed-off-by: Costa Shulyupin <costa.shul@redhat.com>

Acked-by: Jakub Kicinski <kuba@kernel.org>

Jon, since linux-doc got CCed I suppose it's most expedient for you to
take this? LMK if you prefer us to handle it via netdev.

^ permalink raw reply

* [PATCH 2/2] Docs/admin-guide/mm/damon/stat: document kdamond_pid parameter
From: SeongJae Park @ 2026-05-02  2:05 UTC (permalink / raw)
  To: Andrew Morton
  Cc: SeongJae Park, Liam R. Howlett, David Hildenbrand,
	Jonathan Corbet, Lorenzo Stoakes, Michal Hocko, Mike Rapoport,
	Shuah Khan, Suren Baghdasaryan, Vlastimil Babka, damon, linux-doc,
	linux-kernel, linux-mm
In-Reply-To: <20260502020505.80822-1-sj@kernel.org>

Update DAMON_STAT usage document for newly added kdamond_pid parameter.

Signed-off-by: SeongJae Park <sj@kernel.org>
---
 Documentation/admin-guide/mm/damon/stat.rst | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/Documentation/admin-guide/mm/damon/stat.rst b/Documentation/admin-guide/mm/damon/stat.rst
index c4b14daeb2dd6..46c5dd96aa2ed 100644
--- a/Documentation/admin-guide/mm/damon/stat.rst
+++ b/Documentation/admin-guide/mm/damon/stat.rst
@@ -89,3 +89,10 @@ percentiles of the idle time values via this read-only parameter.  Reading the
 parameter returns 101 idle time values in milliseconds, separated by comma.
 Each value represents 0-th, 1st, 2nd, 3rd, ..., 99th and 100th percentile idle
 times.
+
+kdamond_pid
+-----------
+
+PID of the DAMON thread.
+
+If DAMON_STAT is enabled, this becomes the PID of the worker thread.  Else, -1.
-- 
2.47.3

^ permalink raw reply related

* [PATCH 0/2] mm/damon/stat: add kdamond_pid parameter
From: SeongJae Park @ 2026-05-02  2:05 UTC (permalink / raw)
  To: Andrew Morton
  Cc: SeongJae Park, Liam R. Howlett, David Hildenbrand,
	Jonathan Corbet, Lorenzo Stoakes, Michal Hocko, Mike Rapoport,
	Shuah Khan, Suren Baghdasaryan, Vlastimil Babka, damon, linux-doc,
	linux-kernel, linux-mm

DAMON_STAT doesn't provide the pid of its kdamond, unlike DAMON_RECLAIM
and DAMON_LRU_SORT.  This makes user-space management of DAMON_STAT
unnecessarily complicated.  Provide the information via a new parameter,
namely kdamond_pid, and document it.

Changes from RFC v2.1
- v2.1: https://lore.kernel.org/20260430142013.80993-1-sj@kernel.org
- Add parameter description.
- Rebase to latest mm-new.
Changes from RFC v2
- v2: https://lore.kernel.org/20260425203309.108879-1-sj@kernel.org
- Rebase to latest mm-new.
Changes from RFC v1.2
- rfc v1.2: https://lore.kernel.org/20260416002149.87090-1-sj@kernel.org
- Detect and use fresh kdamond pid.
Changes from RFC v1.1
- rfc v1.1: https://lore.kernel.org/20260414235912.98174-1-sj@kernel.org
- Close the parentheses of error handling block.
Changes from RFC
- rfc: https://lore.kernel.org/20260414053742.90296-1-sj@kernel.org
- Fix damon_kdamond_pid() failure handling.

SeongJae Park (2):
  mm/damon/stat: add a parameter for reading kdamond pid
  Docs/admin-guide/mm/damon/stat: document kdamond_pid parameter

 Documentation/admin-guide/mm/damon/stat.rst |  7 ++++
 mm/damon/stat.c                             | 39 +++++++++++++++++++++
 2 files changed, 46 insertions(+)


base-commit: c9d6cc2ef4bef0a86002460ee8c39a1c76b3f1f7
-- 
2.47.3

^ permalink raw reply

* Re: [PATCH 00/11] mm/damon: introduce DAMOS failed region quota charge ratio
From: SeongJae Park @ 2026-05-02  1:56 UTC (permalink / raw)
  To: David Hildenbrand (Arm)
  Cc: SeongJae Park, Andrew Morton, Liam R. Howlett, Brendan Higgins,
	David Gow, Jonathan Corbet, Lorenzo Stoakes, Michal Hocko,
	Mike Rapoport, Shuah Khan, Shuah Khan, Suren Baghdasaryan,
	Vlastimil Babka, damon, kunit-dev, linux-doc, linux-kernel,
	linux-kselftest, linux-mm
In-Reply-To: <761b47b6-c2f1-4fef-bfab-48ee1d0bbe47@kernel.org>

On Fri, 1 May 2026 08:49:43 +0200 "David Hildenbrand (Arm)" <david@kernel.org> wrote:
[...]
> For Damon Andrew should for now just trust your ACKs. If it has your ACK, it's
> good to go.
> 
> In the future, I expect you would pick up the patches yourself, which is where
> you as the component maintainer would look for any blockers.

FWIW I'm picking DAMON patches to damon/next tree on my own, for my testing and
for a case that Andrew might miss those.

> 
> So for Damon patches I don't think we need the AI review notices from Andrew.

All make sense to me :)


Thanks,
SJ

[...]

^ permalink raw reply

* Re: [PATCH net-next 1/2] net: cs89x0: remove ISA bus probing
From: patchwork-bot+netdevbpf @ 2026-05-02  0:10 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: davem, edumazet, kuba, pabeni, corbet, andrew+netdev, arnd, akpm,
	horms, skhan, mengyuanlou, netdev, linux-doc, linux-kernel
In-Reply-To: <20260429145624.2948432-1-arnd@kernel.org>

Hello:

This series was applied to netdev/net-next.git (main)
by Jakub Kicinski <kuba@kernel.org>:

On Wed, 29 Apr 2026 16:55:45 +0200 you wrote:
> From: Arnd Bergmann <arnd@arndb.de>
> 
> The cs89x0 driver is really two in one, and they are mutually exclusive:
> 
>  - the ISA driver was used on 486-era PCs. It likely has no remaining
>    users, like the other ethernet drivers that got removed in
>    linux-7.1. The DMA support in here is the last device driver use of
>    the deprecated isa_bus_to_virt() interface, all other users are either
>    x86 specific or or got converted to the normal dma-mapping interface.
>    The driver was maintained by Andrew Morton at the time, based on
>    the linux-2.2 vendor driver from Cirrus Logic.
> 
> [...]

Here is the summary with links:
  - [net-next,1/2] net: cs89x0: remove ISA bus probing
    https://git.kernel.org/netdev/net-next/c/93cda0c120ac
  - [net-next,2/2] ne2k: fold drivers/net/Space.c into ne.c
    https://git.kernel.org/netdev/net-next/c/4fe18ddd17d8

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* [PATCH v2 9/9] Documentation: kdump: Add arm64 and ppc64le to encrypted dump target support list
From: Coiby Xu @ 2026-05-01 23:43 UTC (permalink / raw)
  To: kexec
  Cc: Andrew Morton, Sourabh Jain, Baoquan He, Dave Young,
	Mike Rapoport, Pasha Tatashin, Pratyush Yadav, Jonathan Corbet,
	Shuah Khan, Coiby Xu, Rob Herring (Arm), open list:DOCUMENTATION,
	open list
In-Reply-To: <20260501234342.2518281-1-coiby.xu@gmail.com>

The encrypted dump target support is now extended to arm64 and ppc64le.

Fixes: e3a84be1ec2f ("arm64,ppc64le/kdump: pass dm-crypt keys to kdump kernel")
Reported-by: Sourabh Jain <sourabhjain@linux.ibm.com>
Signed-off-by: Coiby Xu <coiby.xu@gmail.com>
---
 Documentation/admin-guide/kdump/kdump.rst | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Documentation/admin-guide/kdump/kdump.rst b/Documentation/admin-guide/kdump/kdump.rst
index 73f2e9500c60..8708ef394212 100644
--- a/Documentation/admin-guide/kdump/kdump.rst
+++ b/Documentation/admin-guide/kdump/kdump.rst
@@ -572,8 +572,8 @@ Write the dump file to encrypted disk volume
 ============================================
 
 CONFIG_CRASH_DM_CRYPT can be enabled to support saving the dump file to an
-encrypted disk volume (only x86_64 supported for now). User space can interact
-with /sys/kernel/config/crash_dm_crypt_keys for setup,
+encrypted disk volume (only x86_64, arm64, ppc64le supported for now). User
+space can interact with /sys/kernel/config/crash_dm_crypt_keys for setup,
 
 1. Tell the first kernel what logon keys are needed to unlock the disk volumes,
     # Add key #1
-- 
2.54.0


^ permalink raw reply related

* [PATCH v2 8/9] crash_dump: Disallow configfs/crash_dm_crypt_key/reuse if CONFIG_CRASH_HOTPLUG enabled
From: Coiby Xu @ 2026-05-01 23:43 UTC (permalink / raw)
  To: kexec
  Cc: Andrew Morton, Sourabh Jain, Baoquan He, Dave Young,
	Mike Rapoport, Pasha Tatashin, Pratyush Yadav, Jonathan Corbet,
	Shuah Khan, Coiby Xu, open list:DOCUMENTATION, open list
In-Reply-To: <20260501234342.2518281-1-coiby.xu@gmail.com>

If CONFIG_CRASH_HOTPLUG is enabled, dm-crypt keys saved to reserved
memory will be took care of automatically. Thus it doesn't make sense
to use configfs/crash_dm_crypt_key/reuse. Reserving
image->dm_crypt_keys_addr is also unnecessary. Currently x86_64 and
ppc64le have implemented CONFIG_CRASH_HOTPLUG feature.

Also update the doc accordingly. Note two doc issues are fixed as well.

Fixes: 9ebfa8dcaea7 ("crash_dump: reuse saved dm crypt keys for CPU/memory hot-plugging")
Signed-off-by: Coiby Xu <coiby.xu@gmail.com>
---
 Documentation/admin-guide/kdump/kdump.rst |  9 ++++++---
 kernel/crash_dump_dm_crypt.c              | 14 +++++++++++---
 2 files changed, 17 insertions(+), 6 deletions(-)

diff --git a/Documentation/admin-guide/kdump/kdump.rst b/Documentation/admin-guide/kdump/kdump.rst
index 7587caadbae1..73f2e9500c60 100644
--- a/Documentation/admin-guide/kdump/kdump.rst
+++ b/Documentation/admin-guide/kdump/kdump.rst
@@ -577,9 +577,10 @@ with /sys/kernel/config/crash_dm_crypt_keys for setup,
 
 1. Tell the first kernel what logon keys are needed to unlock the disk volumes,
     # Add key #1
-    mkdir /sys/kernel/config/crash_dm_crypt_keys/7d26b7b4-e342-4d2d-b660-7426b0996720
+    VOL1_UUID=7d26b7b4-e342-4d2d-b660-7426b0996720
+    mkdir /sys/kernel/config/crash_dm_crypt_keys/$VOL1_UUID
     # Add key #1's description
-    echo cryptsetup:7d26b7b4-e342-4d2d-b660-7426b0996720 > /sys/kernel/config/crash_dm_crypt_keys/description
+    echo cryptsetup:$VOL1_UUID > /sys/kernel/config/crash_dm_crypt_keys/$VOL1_UUID/description
 
     # how many keys do we have now?
     cat /sys/kernel/config/crash_dm_crypt_keys/count
@@ -593,7 +594,9 @@ with /sys/kernel/config/crash_dm_crypt_keys for setup,
 
     # To support CPU/memory hot-plugging, reuse keys already saved to reserved
     # memory
-    echo true > /sys/kernel/config/crash_dm_crypt_key/reuse
+    # Note if CONFIG_CRASH_HOTPLUG is enabled, this API is totally unnecessary
+    # thus will be disabled.
+    echo true > /sys/kernel/config/crash_dm_crypt_keys/reuse
 
 2. Load the dump-capture kernel
 
diff --git a/kernel/crash_dump_dm_crypt.c b/kernel/crash_dump_dm_crypt.c
index 36e51807d94f..7a7cae17f578 100644
--- a/kernel/crash_dump_dm_crypt.c
+++ b/kernel/crash_dump_dm_crypt.c
@@ -304,6 +304,11 @@ static ssize_t config_keys_reuse_store(struct config_item *item,
 	bool val;
 	int r;
 
+	if (IS_ENABLED(CONFIG_CRASH_HOTPLUG)) {
+		pr_info("CONFIG_CRASH_HOTPLUG already enabled");
+		return -EINVAL;
+	}
+
 	if (!kexec_crash_image || !kexec_crash_image->dm_crypt_keys_addr) {
 		pr_info("dm-crypt keys haven't be saved to crash-reserved memory\n");
 		return -EINVAL;
@@ -486,15 +491,18 @@ int crash_load_dm_crypt_keys(struct kimage *image)
 void kexec_file_post_load_cleanup_dm_crypt(struct kimage *image)
 {
 	/*
-	 * For CPU/memory hot-plugging, the kdump image will be reloaded. Prevent
-	 * keys_header from being cleaned up during unloading when
-	 * is_dm_key_reused=true
+	 * For CPU/memory hot-plugging without CONFIG_CRASH_HOTPLUG, the whole kdump
+	 * image will be reloaded. Prevent keys_header from being cleaned up during
+	 * unloading when is_dm_key_reused=true
 	 */
 	if (!is_dm_key_reused) {
 		kfree_sensitive(keys_header);
 		keys_header = NULL;
 	}
 
+	if (IS_ENABLED(CONFIG_CRASH_HOTPLUG))
+		image->dm_crypt_keys_addr = 0;
+
 	if (mutex_is_locked(&config_keys_subsys.su_mutex))
 		mutex_unlock(&config_keys_subsys.su_mutex);
 }
-- 
2.54.0


^ permalink raw reply related

* Re: [PATCH RFC v5 00/53] guest_memfd: In-place conversion support
From: Ackerley Tng @ 2026-05-01 22:21 UTC (permalink / raw)
  To: Michael Roth
  Cc: 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, suzuki.poulose,
	aneesh.kumar, 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, Jacob Xu, Darwin Guo
In-Reply-To: <CAEvNRgHRpvsEjtr1A_Qz3d4oMEaffTxESavrZ73Jtt6OobCwhA@mail.gmail.com>

Ackerley Tng <ackerleytng@google.com> writes:

>
> [...snip...]
>
>
> TLDR:
>
> + PRESERVE == guarantee that the process of setting memory attributes
>   doesn't change memory contents.
>     + implementation == do nothing in most cases, except -EOPNOTSUPP for
>       to-shared on TDX, since unmapping is a required part of setting
>       memory attributes to private, and a TDX side effect of unmapping
>       is zeroing memory,

-EOPNOTSUPP will only be for TDX, not SNP.

> + ZERO == guarantee that the process of setting memory attributes zeroes
>   memory contents.
>     + implementation == memset(zero) in most cases. For TDX, a future
>       optimization exists, where memset() can be skipped for pages that
>       were mapped in Secure EPTs before conversion
> + UNSPECIFIED == no guarantees
>     + implementation == guest_memfd does nothing explicitly about memory
>       contents. The implementation is pretty much the same as PRESERVE
>       except guest_memfd won't take into account vendor-specific side
>       effects of the process of conversion. Except for the test vehicle
>       KVM_X86_SW_PROTECTED_VMS, where memory is scrambled.
>

Found another use case internally for pre-finalize, SNP, to-shared,
PRESERVE, which works with the above smaller scope.

During SNP_LAUNCH_UPDATE, when inserting a CPUID page, the firmware will
check that the CPUID values would not lead to an insecure guest
state. SNP_LAUNCH_UPDATE will fail with an error and the page remains
shared in the RMP table.

Here's the proposed flow in the userspace VMM:

1. Load CPUID in shared guest_memfd memory
2. SET_MEMORY_ATTRIBUTES(PRIVATE, PRESERVE)
3. SNP_LAUNCH_UPDATE => get error since CPUID was insecure
4. SET_MEMORY_ATTRIBUTES(SHARED, PRESERVE)
5. Read shared guest_memfd memory, error if VMM disagrees
6. SET_MEMORY_ATTRIBUTES(PRIVATE, PRESERVE)
7. SNP_LAUNCH_UPDATE => successful, since CPUID is now corrected

Does that seem ok?

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

^ permalink raw reply

* Re: [PATCH v2] arm64: errata: Reformat table for IDs
From: Randy Dunlap @ 2026-05-01 21:52 UTC (permalink / raw)
  To: Robin Murphy, will, catalin.marinas; +Cc: linux-arm-kernel, linux-doc
In-Reply-To: <0d4c8f3968e5c5c0a6f3dc295c3e9f696b9006f4.1777657487.git.robin.murphy@arm.com>



On 5/1/26 10:52 AM, Robin Murphy wrote:
> We have some inconsistency where multiple errata for the same component
> share the same Kconfig workaround; some are one ID per line, some are
> smooshed together, and some are entirely separate entries. Standardise
> on the single entry, one ID per line format so that things render nice
> and consistently in the HTML docs, and it's simple and clear to add new
> IDs to existing workarounds without churning the table too much.
> 
> Acked-by: Catalin Marinas <catalin.marinas@arm.com>
> Signed-off-by: Robin Murphy <robin.murphy@arm.com>

LGTM. Thanks.
Tested-by: Randy Dunlap <rdunlap@infradead.org>

> ---
> 
> v2: Rebase for 7.0-rc1 
> 
> One last tilt at this windmill - at least I did remember! :)
> 
>  Documentation/arch/arm64/silicon-errata.rst | 47 +++++++++++----------
>  1 file changed, 25 insertions(+), 22 deletions(-)
-- 
~Randy

^ permalink raw reply

* [PATCH v1 7/7] gpu: nova-core: document INTR_CTRL interrupt tree
From: Joel Fernandes @ 2026-05-01 20:58 UTC (permalink / raw)
  To: linux-kernel
  Cc: Danilo Krummrich, Alexandre Courbot, John Hubbard, Alice Ryhl,
	David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Trevor Gross, Jonathan Corbet, Shuah Khan, nova-gpu, dri-devel,
	rust-for-linux, linux-doc, Joel Fernandes
In-Reply-To: <20260501205825.73614-1-joelagnelf@nvidia.com>

Add documentation describing the interrupt controller architecture for
modern NVIDIA GPUs.

Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
---
 Documentation/gpu/nova/core/intr-ctrl.rst | 305 ++++++++++++++++++++++
 Documentation/gpu/nova/index.rst          |   1 +
 2 files changed, 306 insertions(+)
 create mode 100644 Documentation/gpu/nova/core/intr-ctrl.rst

diff --git a/Documentation/gpu/nova/core/intr-ctrl.rst b/Documentation/gpu/nova/core/intr-ctrl.rst
new file mode 100644
index 000000000000..10091c258f9c
--- /dev/null
+++ b/Documentation/gpu/nova/core/intr-ctrl.rst
@@ -0,0 +1,305 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==============================================
+INTR_CTRL: The GPU's Interrupt Controller
+==============================================
+
+This document describes the interrupt controller which sits between
+the GPU's internal engines (including GSP) and the host's MSI delivery path.
+It is the first hardware block the host driver consults whenever an interrupt is
+delivered, and it is responsible for telling software which engine interrupted.
+It is also known as the "INTR_CTRL" block. The main evolution of interrupt
+controller architecture is to support virtualization (multiple views of the
+interrupt tree for PFs and VFs).
+
+Per-function trees
+==================
+
+Each PCIe function has its own private interrupt tree:
+
+* The Physical Function (PF) sees a tree at a fixed BAR0 offset.
+* Each Virtual Function (VF) sees its own tree at the same BAR0 offset
+  *within its own BAR0 view*, and it cannot observe the PF's tree.
+* The GSP firmware also has its own logical tree in INTR_CTRL used for
+  receiving interrupts to GSP from the engines, but we don't need to
+  bother with those in nova-core (that's GSP's business).
+
+.. note::
+  The PF can also see the VF's tree at an aliased offset in BAR0, which
+  is useful if the guest driver needs host help in configuring interrupts,
+  but we currently do not use that in nova-core.
+
+Two-level interrupt tree
+========================
+
+INTR_CTRL multiplexes up to 256 internal interrupt vectors onto the single
+MSI line allocated to the PCIe function via a two-level tree of MMIO
+registers, where each TOP bit covers exactly two adjacent leaves (known
+as a "subtree"). As an example, on GA102, the tree looks like this::
+
+    TOP register (32-bit)
+     |   bit N == 1 => subtree N has at least one pending leaf
+     |
+     +-- bit 0 --> LEAF[0] (32-bit)  vectors    0..  31  (nonstall base)
+     |             LEAF[1] (32-bit)  vectors   32..  63
+     |
+     +-- bit 1 --> LEAF[2] (32-bit)  vectors   64..  95
+     |             LEAF[3] (32-bit)  vectors   96.. 127
+     |
+     +-- bit 2 --> LEAF[4] (32-bit)  vectors  128.. 159  (CPU doorbell @ 129)
+     |             LEAF[5] (32-bit)  vectors  160.. 191
+     |
+     +-- bit 3 --> LEAF[6] (32-bit)  vectors  192.. 223  (engine stall base,
+     |             LEAF[7] (32-bit)  vectors  224.. 255   GSP stall vector)
+     |
+     +-- bits 4..7 (Hopper+ only) --> LEAF[8..15]
+
+
+The second level (LEAF registers) is where individual engines deposit
+their interrupt events. The first level (TOP register) is a summary: bit
+``N`` of TOP is set if and only if at least one bit is set in the two
+leaves owned by subtree ``N``. Software can therefore start from TOP,
+identify which subtrees have work, and then descend into just those leaves
+- it never needs to read all 16 leaf registers blindly.
+
+The advantage of this architecture is that it allows the host to mask
+entire subtrees of interrupts at once, rather than having to mask each
+leaf individually. Similar reasoning for determining which interrupt
+source fired, the host can walk the tree without going through all 16
+leaves.
+
+Each TOP bit, called a **subtree**, is wired in hardware to exactly two
+adjacent leaves (``leaves 2*N`` and ``2*N + 1``), so nova-core derives
+``num_subtrees = num_leaves / 2`` rather than tracking both numbers
+independently.
+
+End-to-end engine interrupt routing to MSI
+===============================================
+
+The engine interrupt routing is done by the engine's INTR_CTRL(i)
+register. This register is written once by GSP at boot and decides
+which tree/leaf to activate in the INTR_CTRL. This model assists in
+virtualization, as it is possible for the GSP to route engines to the
+correct tree/leaf corresponding to the VF. GSP then provides the
+information to the host via the INTR_GET_KERNEL_TABLE RPC so that
+the host knows which leaf bits correspond to an engine's interrupt.
+
+It roughly looks like the following::
+              +--------------- Engine (CE, GR, NVDEC, ...) ---------------+
+              |                                                           |
+              |   internal work completes                                 |
+              |          |                                                |
+              |          v                                                |
+              |   +-----------------------------------------+             |
+              |   | INTR_CTRL(i): programmable register     |             |
+              |   | (written once by GSP-RM at boot,        |             |
+              |   |  one such reg per engine)               |             |
+              |   |                                         |             |
+              |   |   VECTOR  = 200   (-> which leaf bit)   |             |
+              |   |   GFID    = 0     (-> which function's  |             |
+              |   |                       tree: 0=PF, N=VF) |             |
+              |   |   CPU     = 1     (-> copy to CPU tree?)|             |
+              |   |   GSP     = 0     (-> copy to GSP tree?)|             |
+              |   +--------------------+--------------------+             |
+              |                        |                                  |
+              |     engine builds      |                                  |
+              |     interrupt ctrl     |                                  |
+              |     command message    |                                  |
+              |  (all2ctrl_intr_cmd)   |                                  |
+              +------------------------|----------------------------------+
+                                       |
+                                       v
+                   +-----------------------------------------+
+                   | Central INTR_CTRL block                 |
+                   |                                         |
+                   | reads message; for the tree picked      |
+                   | by GFID, sets:                          |
+                   |   LEAF[ 200 / 32 ]  = LEAF[6]           |
+                   |   bit  ( 200 % 32 ) = bit 8             |
+                   | TOP subtree 3 = pending                 |
+                   +--------------------+--------------------+
+                                        |
+                                        v
+                                MSI to host (PF)
+
+Vector encoding
+---------------
+
+A vector number ``v`` (0..255) maps to a unique ``(leaf, bit)`` pair::
+
+    leaf_index = v / 32
+    bit_in_leaf = v % 32
+
+For example, vector 129 (the CPU doorbell self-test vector we use in
+the INTR_CTRL self-test, see below) lives in ``LEAF[4]`` at bit 1,
+which is reachable through subtree 2 in the TOP register.
+
+Architecture differences
+------------------------
+
+The number of *active* leaves depends on the GPU architecture:
+
+==================  =================  ==========  ================
+Architecture        Active leaves      Subtrees    ``subtree_mask``
+==================  =================  ==========  ================
+Turing / Ampere     8                  4           ``0x0f``
+Ada Lovelace        8                  4           ``0x0f``
+Hopper / Blackwell  16                 8           ``0xff``
+==================  =================  ==========  ================
+
+Pre-Hopper chipsets only have leaves 0-7 wired up; the upper half of the
+TOP register is unused and reads back as zero. Hopper widened the tree to
+16 leaves to support more engines and more virtual functions.
+
+Stall vs nonstall vector ranges
+===============================
+
+A common point of confusion: **stall and nonstall are NOT separate
+interrupt trees**. They are two different *vector ranges* within the same
+INTR_CTRL tree, and the source engine picks which range its interrupt
+lands in.
+
+* **Nonstall** vectors live in the low leaves (``LEAF[0..1]``, vectors
+  0..63). The engine fires the interrupt and continues immediately,
+  whether or not the host has acknowledged it. Used for "fire and
+  forget" notifications - examples: vblank, semaphore wakeups, performance
+  counter overflow).
+
+* **Stall** vectors live in the high leaves.
+  On Turing and Ampere:
+  ``LEAF[6..7]`` (vectors 192..255, subtree 3).
+  On Hopper:
+  ``LEAF[6..11]`` (subtrees 3..5).
+  The engine *blocks* (stalls) until the host writes a W1C (Write 1 to Clear)
+  ack to the leaf bit. Example: MMU fault.
+
+ISR operation flow
+==================
+
+When an MSI fires, the ISR walks the tree in a fixed sequence::
+
+    1. UNARM    write subtree_mask -> TOP_EN_CLEAR  (stop MSI delivery)
+    2. READ     pending = TOP                       (which subtrees fired?)
+    3. ACK      for each pending leaf:
+                   mask = LEAF[i]                   (read pending vectors)
+                   LEAF[i] = mask                   (W1C the latches)
+                   dispatch handlers for set bits
+    4. REARM    write subtree_mask -> TOP_EN_SET    (resume MSI delivery)
+
+A few important properties:
+
+* **All pending leaf bits must be acked**, even bits that nova-core does
+  not currently dispatch. Leaving a bit set keeps its subtree pending in
+  TOP, which means the next REARM immediately fires another MSI - an
+  interrupt storm. The handler therefore acks the full leaf mask, not
+  just the bits it recognizes.
+
+* **REARM happens only after every pending leaf has been acked.**
+  Otherwise a still-set leaf bit would re-fire MSI on the next REARM
+  even though the ISR is mid-processing.
+
+Edge-trigger and rearm semantics
+================================
+
+Each LEAF bit is a sticky latch with edge-triggered SET behaviour:
+
+* The latch SETS on the rising edge of the source signal (an engine
+  message arriving on the interrupt control command interface, or a falcon
+  output wire transitioning low->high).
+* The latch CLEARS only when the host writes a 1 to that bit (W1C).
+* A still-asserted source does **not** re-set the latch. There is no
+  way to make a level-asserted signal "re-fire" except to drop and
+  re-raise it.
+
+There are two distinct rescue mechanisms, at two different layers,
+for two different problems. They are easy to confuse, so first some
+vocabulary as the rescues are entirely about how these pieces of
+hardware are wired together:
+
+* ``LEAF[i]``: each bit is a *sticky latch*: bit ``b`` SETs on the
+  rising edge of an ``all2ctrl_intr_cmd`` message and CLEARs only
+  when the host writes 1 to that bit (W1C ack).
+
+* ``TOP[N]``: bit ``N`` of the read-only ``TOP`` register. Purely
+  combinational: it reads 1 if and only if at least one bit is
+  latched in either of the two leaves owned by subtree ``N``,
+  i.e. ``LEAF[2N]`` or ``LEAF[2N+1]``. Software cannot write ``TOP``;
+  the hardware tracks the leaves automatically.
+
+* ``TOP_EN[N]``: a single host-controlled "armed?" bit per subtree,
+  internal to INTR_CTRL. The host *sets* it by writing 1 to
+  ``TOP_EN_SET`` and *clears* it by writing 1 to ``TOP_EN_CLEAR``.
+  Reading either register returns the current ``TOP_EN`` bitmask.
+  ``TOP_EN`` is not a latch; it just remembers what the host last set
+  or cleared.
+
+* The **MSI-edge AND-gate**: one per subtree, internal to ``INTR_CTRL``.
+  It ANDs ``TOP[N]`` with ``TOP_EN[N]`` and drives the output
+  through an edge detector. An MSI for subtree ``N`` is delivered
+  on every *rising edge* of this AND output; level changes that
+  drop the output to 0 (for any reason) deliver no MSI.
+
+::
+
+       LEAF[2N], LEAF[2N+1]         (sticky latches; W1C to clear)
+              |
+              v
+       (OR of all 64 latched bits in subtree N)
+              |
+              v
+        TOP[N] ----+
+                   |
+                   AND ---(rising edge detector)---> MSI for subtree N
+                   |
+       TOP_EN[N] --+
+              ^
+              |   host pokes:
+              |     write 1 to TOP_EN_SET[N]   -> TOP_EN[N] becomes 1
+              |     write 1 to TOP_EN_CLEAR[N] -> TOP_EN[N] becomes 0
+
+With that in hand:
+
+1. **REARM** (writing the subtree mask to ``TOP_EN_SET``) rescues a
+   timing race: between the ISR's last leaf ack and the moment
+   ``TOP_EN`` is brought back high, *new* engine events can arrive
+   and latch fresh leaf bits. The ISR did its best to drain
+   everything visible at the time of its W1C, but the W1C only
+   clears the bits the ISR snapshotted; anything the engine fires
+   afterwards sets new bits in ``LEAF[i]`` that the ISR never saw.
+
+2. **INTR_RETRIGGER** (a per-engine register, not part of INTR_CTRL)
+   rescues a still-asserted level source *inside an engine*. Most
+   engines drive their internal "interrupt pending" signal as a
+   level and convert it to an ``all2ctrl_intr_cmd`` message via an
+   edge converter that fires only on the rising edge of that level.
+   So one rising edge of the engine's level produces one message,
+   which sets one leaf bit. After the host's W1C clears that leaf
+   bit, a level that has stayed high produces no new edge, so the
+   engine's edge converter never sends another message to INTR_CTRL,
+   the leaf stays clear, and ``TOP[N]`` is 0. REARM's AND-gate trick
+   is useless here. Writing 1 to the engine's
+   ``INTR_RETRIGGER`` register drops the engine's level for one
+   clock cycle; the level then returns to 1 (the engine still has
+   work pending in its source register), the edge converter sees a
+   fresh 0->1 transition, sends a new message, the leaf re-latches,
+   ``TOP[N]`` goes back to 1, and an MSI follows on REARM (or
+   immediately, if ``TOP_EN[N]`` was already 1). ``INTR_RETRIGGER``
+   bridges the asymmetry between level-asserted internal engine
+   logic and edge-driven ``INTR_CTRL`` leaf messages.
+
+CPU doorbell self-test
+======================
+
+INTR_CTRL exposes a software-trigger register, ``NV_VF_INTR_LEAF_TRIGGER``.
+Writing a vector number ``v`` to this register synthesizes a hardware
+interrupt event on vector ``v``: the matching leaf bit latches, TOP
+updates, and (assuming the subtree is armed and the leaf vector is
+enabled) an MSI is delivered to the host.
+
+nova-core uses vector 129 (``LEAF[4]`` bit 1) as a self-test "doorbell":
+during early initialization, the driver registers a temporary ISR for vector
+129, writes 129 to ``LEAF_TRIGGER``, and verifies that its ISR fires.
+This validates the entire MSI -> INTR_CTRL -> ISR path *without* needing
+the GSP firmware to be running, which makes it useful for debugging early
+PCI / MSI issues, VFIO passthrough setups, and testing when GSP is not yet
+available.
diff --git a/Documentation/gpu/nova/index.rst b/Documentation/gpu/nova/index.rst
index e39cb3163581..1ea111988e35 100644
--- a/Documentation/gpu/nova/index.rst
+++ b/Documentation/gpu/nova/index.rst
@@ -32,3 +32,4 @@ vGPU manager VFIO driver and the nova-drm driver.
    core/devinit
    core/fwsec
    core/falcon
+   core/intr-ctrl
-- 
2.34.1


^ permalink raw reply related

* [PATCH v1 5/7] gpu: nova-core: add INTR_CTRL interrupt controller API
From: Joel Fernandes @ 2026-05-01 20:58 UTC (permalink / raw)
  To: linux-kernel
  Cc: Danilo Krummrich, Alexandre Courbot, John Hubbard, Alice Ryhl,
	David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Trevor Gross, Jonathan Corbet, Shuah Khan, nova-gpu, dri-devel,
	rust-for-linux, linux-doc, Joel Fernandes
In-Reply-To: <20260501205825.73614-1-joelagnelf@nvidia.com>

Create the irq/ module with a type-state INTR_CTRL interrupt
controller API. The IntrCtrl struct provides factory methods for Top
and Leaf objects that use a sealed State trait with Idle/Pending types
to enforce correct usage at compile time.

The type-state pattern ensures ack() is only callable after
read_pending() has cached the hardware state, preventing mismatched
masks at compile time.

The later CPU doorbell self-test will make use of it.

Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
---
 drivers/gpu/nova-core/irq.rs           |   2 +
 drivers/gpu/nova-core/irq/intr_ctrl.rs | 281 +++++++++++++++++++++++++
 drivers/gpu/nova-core/nova_core.rs     |   1 +
 3 files changed, 284 insertions(+)
 create mode 100644 drivers/gpu/nova-core/irq/intr_ctrl.rs

diff --git a/drivers/gpu/nova-core/irq.rs b/drivers/gpu/nova-core/irq.rs
index 3a2a40519f11..01ae638bf494 100644
--- a/drivers/gpu/nova-core/irq.rs
+++ b/drivers/gpu/nova-core/irq.rs
@@ -10,6 +10,8 @@
     prelude::*,
 };
 
+mod intr_ctrl;
+
 pub(crate) fn alloc_vector(pdev: &pci::Device<Bound>) -> Result<pci::IrqVector<'_>> {
     let msi_types = IrqTypes::default().with(IrqType::Msi).with(IrqType::MsiX);
 
diff --git a/drivers/gpu/nova-core/irq/intr_ctrl.rs b/drivers/gpu/nova-core/irq/intr_ctrl.rs
new file mode 100644
index 000000000000..dde77cc1f42f
--- /dev/null
+++ b/drivers/gpu/nova-core/irq/intr_ctrl.rs
@@ -0,0 +1,281 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! GPU interrupt controller support (INTR_CTRL).
+//!
+//! Each PCIe function (PF and each VF, also known as a GFID) has its own
+//! interrupt tree. In this module, we only interact with the PF tree.
+//! The VF interacts with its own tree (which appears as a PF tree to it).
+//!
+//! See `Documentation/gpu/nova/core/intr-ctrl.rst` for detailed documentation
+//! of the INTR_CTRL architecture.
+
+use kernel::{
+    io::{
+        register::Array,
+        Io, //
+    },
+    num::Bounded,
+};
+
+use crate::{driver::Bar0, gpu::Chipset, regs};
+
+/// Type alias for a leaf interrupt index, bounded to valid values 0-15.
+pub(super) type LeafIndex = Bounded<usize, 4>;
+
+// Type-state for `Top` and `Leaf`.
+//
+// `Top` follows Idle -> Unarmed -> Pending -> consumed (rearmed).
+// `Leaf` uses Idle -> Pending to catch.
+//
+// This catches issues at compile time where we perform an operation
+// on an object in the wrong state (example, rearming `Top` without reading
+// pending bits first).
+/// Sealed trait representing the interrupt controller state.
+pub(super) trait State: private::Sealed {}
+
+/// Idle state: TOP_EN may or may not be armed; no snapshot held.
+pub(super) struct Idle;
+impl State for Idle {}
+
+/// Unarmed state: TOP_EN was just cleared by this Top handle, snapshot not yet read.
+pub(super) struct Unarmed;
+impl State for Unarmed {}
+
+/// Pending state: interrupt mask has been read from hardware.
+pub(super) struct Pending {
+    mask: u32,
+}
+impl State for Pending {}
+
+mod private {
+    pub(in crate::irq) trait Sealed {}
+    impl Sealed for super::Idle {}
+    impl Sealed for super::Unarmed {}
+    impl Sealed for super::Pending {}
+}
+
+/// Interrupt controller for a single PCIe function's interrupt tree.
+#[derive(Clone)]
+pub(super) struct IntrCtrl {
+    subtree_mask: u8,
+}
+
+impl IntrCtrl {
+    /// Create an `IntrCtrl` configured for the given chipset's interrupt tree width.
+    pub(super) fn new(chipset: Chipset) -> Self {
+        // Each TOP bit covers 2 leaves; subtree_mask has one bit per subtree.
+        //   Pre-Hopper:  8 leaves / 2 = 4 subtrees -> 0x0f (bits [3:0])
+        //   Hopper+:    16 leaves / 2 = 8 subtrees -> 0xff (bits [7:0])
+        Self {
+            subtree_mask: if chipset.arch().is_pre_hopper() {
+                0xf
+            } else {
+                0xff
+            },
+        }
+    }
+
+    /// Return a [`Top`] handle in the [`Idle`] state for this controller.
+    pub(super) fn top(&self) -> Top<Idle> {
+        Top {
+            subtree_mask: self.subtree_mask,
+            state: Idle,
+        }
+    }
+
+    /// Return a [`Leaf`] handle in the [`Idle`] state for the given leaf index.
+    pub(super) fn leaf(&self, index: LeafIndex) -> Leaf<Idle> {
+        Leaf::from_index(index)
+    }
+
+    /// Trigger a CPU doorbell interrupt for the given MSI vector number.
+    pub(super) fn trigger(&self, bar: &Bar0, vector: u32) {
+        bar.write(regs::NV_VF_INTR_LEAF_TRIGGER, vector.into());
+    }
+
+    /// Drain any pending interrupts on this controller.
+    ///
+    /// Walks all enabled subtrees, reads each leaf's pending mask, and acks
+    /// any pending bits. Useful for clearing stale interrupt state, e.g.,
+    /// state leftover when GSP booted.
+    pub(super) fn drain(&self, bar: &Bar0) {
+        let top = self.top().unarm(bar).read_pending(bar);
+
+        for subtree in top.iter_subtrees() {
+            for leaf in subtree.iter_pending_leaves(self, bar) {
+                leaf.ack(bar);
+            }
+        }
+
+        top.rearm(bar);
+    }
+}
+
+/// Top-level interrupt controller view.
+pub(super) struct Top<S: State = Idle> {
+    subtree_mask: u8,
+    state: S,
+}
+
+impl Top<Idle> {
+    /// Arm the controller (write TOP_EN_SET). Use for one-shot initial
+    /// setup before any interrupts are expected. The ISR's normal
+    /// re-arm path goes through `unarm()` -> `read_pending()` ->
+    /// `Top<Pending>::rearm()` instead.
+    pub(super) fn arm(self, bar: &Bar0) {
+        bar.write(
+            regs::NV_VF_INTR_TOP_EN_SET,
+            u32::from(self.subtree_mask).into(),
+        );
+    }
+
+    /// Unarm the controller (write TOP_EN_CLEAR). MSI is edge-triggered,
+    /// so this stops the GPU from firing redundant MSI writes over PCIe
+    /// while the host drains the tree. Consumes self and transitions to
+    /// `Top<Unarmed>`, which can then `read_pending()`.
+    pub(super) fn unarm(self, bar: &Bar0) -> Top<Unarmed> {
+        bar.write(
+            regs::NV_VF_INTR_TOP_EN_CLEAR,
+            u32::from(self.subtree_mask).into(),
+        );
+        Top {
+            subtree_mask: self.subtree_mask,
+            state: Unarmed,
+        }
+    }
+}
+
+impl Top<Unarmed> {
+    /// Read the TOP register's pending bitmask. Consumes self and
+    /// returns a `Top<Pending>` carrying the snapshot.
+    pub(super) fn read_pending(self, bar: &Bar0) -> Top<Pending> {
+        let mask = bar.read(regs::NV_VF_INTR_TOP).into_raw();
+        Top {
+            subtree_mask: self.subtree_mask,
+            state: Pending { mask },
+        }
+    }
+}
+
+/// One subtree in the INTR_TOP pending mask (covers two adjacent leaf indices).
+#[derive(Clone, Copy)]
+pub(super) struct Subtree {
+    index: usize,
+}
+
+impl Subtree {
+    /// Yields the two [`Leaf`] slots covered by this subtree's TOP bit.
+    fn iter_leaves<'a>(
+        self,
+        ctrl: &'a IntrCtrl,
+    ) -> impl Iterator<Item = Leaf<Idle>> + 'a {
+        // Each subtree covers two adjacent leaf indices for all architectures.
+        (0..2usize).filter_map(move |offset| {
+            // self.index is 0-31 and offset is 0-1, so idx is at most 63.
+            let idx = self.index * 2 + offset;
+            LeafIndex::try_new(idx).map(|idx| ctrl.leaf(idx))
+        })
+    }
+
+    /// Like [`Self::iter_leaves`], but keeps only leaves with a non-zero pending mask.
+    pub(super) fn iter_pending_leaves<'a>(
+        self,
+        ctrl: &'a IntrCtrl,
+        bar: &'a Bar0,
+    ) -> impl Iterator<Item = Leaf<Pending>> + 'a {
+        self.iter_leaves(ctrl).filter_map(move |idle| {
+            let pending = idle.read_pending(bar);
+            (pending.mask() != 0).then_some(pending)
+        })
+    }
+}
+
+impl Top<Pending> {
+    /// Return the raw TOP pending bitmask snapshot.
+    pub(super) fn mask(&self) -> u32 {
+        self.state.mask
+    }
+
+    /// Iterate over all subtrees with a pending TOP bit set in the snapshot.
+    pub(super) fn iter_subtrees(&self) -> impl Iterator<Item = Subtree> + '_ {
+        (0..32usize)
+            .filter(move |&bit| self.state.mask & (1u32 << bit) != 0)
+            .map(|index| Subtree { index })
+    }
+
+    /// Re-arm the controller (write TOP_EN_SET). Consumes self so the
+    /// pending snapshot cannot be consulted or re-iterated afterwards.
+    pub(super) fn rearm(self, bar: &Bar0) {
+        bar.write(
+            regs::NV_VF_INTR_TOP_EN_SET,
+            u32::from(self.subtree_mask).into(),
+        );
+    }
+}
+
+/// Leaf interrupt controller view for one interrupt leaf.
+pub(super) struct Leaf<S: State = Idle> {
+    index: LeafIndex,
+    state: S,
+}
+
+impl<Left: State, Right: State> PartialEq<Leaf<Right>> for Leaf<Left> {
+    fn eq(&self, other: &Leaf<Right>) -> bool {
+        self.index == other.index
+    }
+}
+
+impl<S: State> Eq for Leaf<S> {}
+
+// All `try_at().unwrap()` calls below are safe: `LeafIndex` is `Bounded<usize, 4>`,
+// guaranteeing values 0-15, and all INTR_CTRL leaf register arrays have 16 elements.
+impl Leaf<Idle> {
+    /// Construct a [`Leaf`] handle for the given leaf index.
+    pub(super) fn from_index(index: LeafIndex) -> Self {
+        Leaf { index, state: Idle }
+    }
+
+    /// Enable the bits in `mask` in this leaf's EN_SET register.
+    pub(super) fn allow(&self, bar: &Bar0, mask: u32) {
+        bar.write(
+            regs::NV_VF_INTR_LEAF_EN_SET::try_at(self.index.get()).unwrap(),
+            mask.into(),
+        );
+    }
+
+    /// Disable the bits in `mask` in this leaf's EN_CLEAR register.
+    pub(super) fn block(&self, bar: &Bar0, mask: u32) {
+        bar.write(
+            regs::NV_VF_INTR_LEAF_EN_CLEAR::try_at(self.index.get()).unwrap(),
+            mask.into(),
+        );
+    }
+
+    /// Read this leaf's pending interrupt mask and transition to [`Pending`].
+    pub(super) fn read_pending(self, bar: &Bar0) -> Leaf<Pending> {
+        let mask = bar
+            .read(regs::NV_VF_INTR_LEAF::try_at(self.index.get()).unwrap())
+            .into_raw();
+        Leaf {
+            index: self.index,
+            state: Pending { mask },
+        }
+    }
+}
+
+impl Leaf<Pending> {
+    /// Return the raw pending interrupt bitmask read from hardware.
+    pub(super) fn mask(&self) -> u32 {
+        self.state.mask
+    }
+
+    /// Acknowledge all pending bits by writing the mask back to the leaf register.
+    pub(super) fn ack(&self, bar: &Bar0) {
+        if self.state.mask != 0 {
+            bar.write(
+                regs::NV_VF_INTR_LEAF::try_at(self.index.get()).unwrap(),
+                self.state.mask.into(),
+            );
+        }
+    }
+}
diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs
index 837aa2d36a0e..6d0e4b2f53c7 100644
--- a/drivers/gpu/nova-core/nova_core.rs
+++ b/drivers/gpu/nova-core/nova_core.rs
@@ -19,6 +19,7 @@
 mod firmware;
 mod gpu;
 mod gsp;
+#[expect(dead_code)]
 mod irq;
 #[macro_use]
 mod num;
-- 
2.34.1


^ permalink raw reply related

* [PATCH v1 6/7] gpu: nova-core: add CPU doorbell IRQ self-test
From: Joel Fernandes @ 2026-05-01 20:58 UTC (permalink / raw)
  To: linux-kernel
  Cc: Danilo Krummrich, Alexandre Courbot, John Hubbard, Alice Ryhl,
	David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Trevor Gross, Jonathan Corbet, Shuah Khan, nova-gpu, dri-devel,
	rust-for-linux, linux-doc, Joel Fernandes
In-Reply-To: <20260501205825.73614-1-joelagnelf@nvidia.com>

Add a CPU doorbell interrupt self-test that runs during probe, after GSP
boot. The test validates the full MSI interrupt path from GPU through
PCIe to the CPU interrupt handler.

Tested with qemu + GPU passthrough on GA102, with dmesg as follows:
  NovaCore 0000:00:06.0: CPU doorbell self-test: PASS (irq_count=1)

Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
---
 drivers/gpu/nova-core/Kconfig              |  13 ++
 drivers/gpu/nova-core/gpu.rs               |   8 +
 drivers/gpu/nova-core/irq.rs               |   2 +
 drivers/gpu/nova-core/irq/doorbell_test.rs | 203 +++++++++++++++++++++
 drivers/gpu/nova-core/nova_core.rs         |   2 +-
 5 files changed, 227 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/nova-core/irq/doorbell_test.rs

diff --git a/drivers/gpu/nova-core/Kconfig b/drivers/gpu/nova-core/Kconfig
index d8456f8eaa05..e2c8a090c7ff 100644
--- a/drivers/gpu/nova-core/Kconfig
+++ b/drivers/gpu/nova-core/Kconfig
@@ -15,3 +15,16 @@ config NOVA_CORE
 	  This driver is work in progress and may not be functional.
 
 	  If M is selected, the module will be called nova_core.
+
+config NOVA_CORE_IRQ_SELFTEST
+	bool "Nova IRQ self-test during probe"
+	depends on NOVA_CORE
+	help
+	  Enable the CPU doorbell IRQ self-test that runs during nova-core
+	  probe. The test triggers vector 129 (CPU doorbell) and verifies
+	  the interrupt is received through the INTR_CTRL interrupt tree.
+
+	  This validates the full MSI interrupt path from GPU through PCIe
+	  to the CPU interrupt handler.
+
+	  If unsure, say N.
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index 3b45bce6738b..f6e02007ef8f 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -305,6 +305,14 @@ pub(crate) fn new<'a>(
             // Allocate a PCI interrupt vector.
             _: {
                 let _irq_vector = irq::alloc_vector(pdev)?;
+
+                #[cfg(CONFIG_NOVA_CORE_IRQ_SELFTEST)]
+                irq::doorbell_test::run_selftest(
+                    pdev,
+                    &devres_bar,
+                    spec.chipset,
+                    _irq_vector,
+                )?;
             },
 
             bar: devres_bar,
diff --git a/drivers/gpu/nova-core/irq.rs b/drivers/gpu/nova-core/irq.rs
index 01ae638bf494..f4ed4593e795 100644
--- a/drivers/gpu/nova-core/irq.rs
+++ b/drivers/gpu/nova-core/irq.rs
@@ -10,6 +10,8 @@
     prelude::*,
 };
 
+#[cfg(CONFIG_NOVA_CORE_IRQ_SELFTEST)]
+pub(crate) mod doorbell_test;
 mod intr_ctrl;
 
 pub(crate) fn alloc_vector(pdev: &pci::Device<Bound>) -> Result<pci::IrqVector<'_>> {
diff --git a/drivers/gpu/nova-core/irq/doorbell_test.rs b/drivers/gpu/nova-core/irq/doorbell_test.rs
new file mode 100644
index 000000000000..fb4e039ac032
--- /dev/null
+++ b/drivers/gpu/nova-core/irq/doorbell_test.rs
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use kernel::{
+    device::{Bound, Device},
+    devres::Devres,
+    irq, pci,
+    prelude::*,
+    sync::{
+        atomic::{
+            Atomic,
+            Relaxed, //
+        },
+        Arc, Completion,
+    },
+    time,
+};
+
+use super::intr_ctrl::{
+    IntrCtrl,
+    Leaf,
+    LeafIndex, //
+};
+use crate::{
+    driver::Bar0,
+    gpu::Chipset, //
+};
+
+// The following are constant across all architectures.
+
+/// CPU doorbell vector.
+const DOORBELL_VECTOR: u32 = 129;
+
+/// Leaf index for the doorbell vector: 129 / 32 = 4.
+const DOORBELL_LEAF: usize = 4;
+
+/// Bit within the leaf: 129 % 32 = 1.
+const DOORBELL_BIT: u32 = 1 << 1;
+
+/// IRQ handler for the CPU doorbell self-test.
+///
+/// Performs a minimal interrupt-tree drain cycle:
+/// unarm -> read TOP -> iterate leaves -> ack -> rearm.
+/// Signals completion and increments the interrupt counter on each handled interrupt.
+/// Records the leaf index and pending mask observed by the handler for verification.
+#[pin_data]
+struct DoorbellTestHandler {
+    bar: Arc<Devres<Bar0>>,
+    intr_ctrl: IntrCtrl,
+    #[pin]
+    completion: Completion,
+    /// Used to confirm the number of interrupts handled.
+    irq_count: Atomic<u32>,
+    /// Used to confirm the mask observed on the doorbell leaf (leaf 4).
+    doorbell_leaf_mask: Atomic<u32>,
+}
+
+impl irq::Handler for DoorbellTestHandler {
+    fn handle(&self, dev: &Device<Bound>) -> irq::IrqReturn {
+        let Ok(bar) = self.bar.access(dev) else {
+            return irq::IrqReturn::None;
+        };
+
+        let top = self.intr_ctrl.top().unarm(bar).read_pending(bar);
+
+        if top.mask() == 0 {
+            top.rearm(bar);
+            return irq::IrqReturn::None;
+        }
+
+        // Record the doorbell leaf mask for later verification.
+        let doorbell_leaf = Leaf::from_index(LeafIndex::new::<DOORBELL_LEAF>());
+
+        for subtree in top.iter_subtrees() {
+            for leaf in subtree.iter_pending_leaves(&self.intr_ctrl, bar) {
+                if leaf == doorbell_leaf {
+                    self.doorbell_leaf_mask.store(leaf.mask(), Relaxed);
+                }
+                leaf.ack(bar);
+            }
+        }
+
+        top.rearm(bar);
+
+        // Increment the interrupt counter and signal the completion.
+        self.irq_count.fetch_add(1, Relaxed);
+        self.completion.complete_all();
+
+        irq::IrqReturn::Handled
+    }
+}
+
+/// Run the CPU doorbell IRQ self-test.
+///
+/// Registers an IRQ handler, triggers CPU doorbell vector, and verifies the
+/// interrupt is received through the interrupt tree. This validates the full MSI path:
+/// GPU -> PCIe -> CPU -> handler.
+pub(crate) fn run_selftest(
+    pdev: &pci::Device<Bound>,
+    bar_devres: &Arc<Devres<Bar0>>,
+    chipset: Chipset,
+    irq_vector: pci::IrqVector<'_>,
+) -> Result {
+    let bar = bar_devres.access(pdev.as_ref())?;
+    let intr_ctrl = IntrCtrl::new(chipset);
+
+    // Clear stale pending bits before enabling the doorbell.
+    intr_ctrl.drain(bar);
+
+    let handler_init = try_pin_init!(DoorbellTestHandler {
+        bar: bar_devres.clone(),
+        intr_ctrl,
+        completion <- Completion::new(),
+        irq_count: Atomic::new(0),
+        doorbell_leaf_mask: Atomic::new(0),
+    }? Error);
+
+    let reg = Arc::pin_init(
+        pdev.request_irq(
+            irq_vector,
+            irq::Flags::TRIGGER_NONE,
+            c"nova-core",
+            handler_init,
+        ),
+        GFP_KERNEL,
+    )?;
+
+    let handler = reg.handler();
+
+    // Allow doorbell leaf.
+    let doorbell_leaf_idx = LeafIndex::new::<DOORBELL_LEAF>();
+    handler
+        .intr_ctrl
+        .leaf(doorbell_leaf_idx)
+        .allow(bar, DOORBELL_BIT);
+
+    // The doorbell bit must be clear before triggering, otherwise the test
+    // cannot prove that the IRQ came from the trigger below.
+    let pre_mask = handler
+        .intr_ctrl
+        .leaf(doorbell_leaf_idx)
+        .read_pending(bar)
+        .mask();
+    if pre_mask & DOORBELL_BIT != 0 {
+        handler
+            .intr_ctrl
+            .leaf(doorbell_leaf_idx)
+            .block(bar, DOORBELL_BIT);
+        let _ = handler.intr_ctrl.top().unarm(bar);
+        dev_warn!(
+            pdev.as_ref(),
+            "CPU doorbell self-test: FAIL (doorbell bit already pending, leaf[{}] mask={:#x})\n",
+            DOORBELL_LEAF,
+            pre_mask,
+        );
+        return Err(EIO);
+    }
+
+    // Arm the INTR_CTRL top level to enable MSI generation.
+    handler.intr_ctrl.top().arm(bar);
+
+    // Trigger the CPU doorbell interrupt.
+    handler.intr_ctrl.trigger(bar, DOORBELL_VECTOR);
+
+    // Wait up to 1 second for the interrupt handler to fire.
+    let completed = handler
+        .completion
+        .wait_for_completion_timeout(time::msecs_to_jiffies(1000));
+
+    let count = handler.irq_count.load(Relaxed);
+    let leaf_mask = handler.doorbell_leaf_mask.load(Relaxed);
+
+    // Block the doorbell leaf after the test.
+    handler
+        .intr_ctrl
+        .leaf(doorbell_leaf_idx)
+        .block(bar, DOORBELL_BIT);
+    let _ = handler.intr_ctrl.top().unarm(bar);
+
+    // Verify that the doorbell IRQ fired.
+    let doorbell_bit_seen = leaf_mask & DOORBELL_BIT != 0;
+    let pass = completed && count == 1 && doorbell_bit_seen;
+
+    if pass {
+        dev_info!(
+            pdev.as_ref(),
+            "CPU doorbell self-test: PASS (irq_count={}, leaf[{}] mask={:#x})\n",
+            count,
+            DOORBELL_LEAF,
+            leaf_mask,
+        );
+    } else {
+        dev_warn!(
+            pdev.as_ref(),
+            "CPU doorbell self-test: FAIL (completed={}, irq_count={}, leaf[{}] mask={:#x})\n",
+            completed,
+            count,
+            DOORBELL_LEAF,
+            leaf_mask,
+        );
+    }
+
+    Ok(())
+}
diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs
index 6d0e4b2f53c7..5fce7068db03 100644
--- a/drivers/gpu/nova-core/nova_core.rs
+++ b/drivers/gpu/nova-core/nova_core.rs
@@ -19,7 +19,7 @@
 mod firmware;
 mod gpu;
 mod gsp;
-#[expect(dead_code)]
+#[cfg_attr(not(CONFIG_NOVA_CORE_IRQ_SELFTEST), expect(dead_code))]
 mod irq;
 #[macro_use]
 mod num;
-- 
2.34.1


^ permalink raw reply related

* [PATCH v1 4/7] gpu: nova-core: add Architecture::is_pre_hopper() helper
From: Joel Fernandes @ 2026-05-01 20:58 UTC (permalink / raw)
  To: linux-kernel
  Cc: Danilo Krummrich, Alexandre Courbot, John Hubbard, Alice Ryhl,
	David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Trevor Gross, Jonathan Corbet, Shuah Khan, nova-gpu, dri-devel,
	rust-for-linux, linux-doc, Joel Fernandes
In-Reply-To: <20260501205825.73614-1-joelagnelf@nvidia.com>

Add a helper method to the Architecture enum that returns true for Turing,
Ampere, and Ada -- the GPU generations that predate Hopper.

The interrupt controller uses this to determine the number of active
interrupt leaves at construction time.

Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
---
 drivers/gpu/nova-core/gpu.rs | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index 3ac9cb106bfd..3b45bce6738b 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -163,6 +163,13 @@ pub(crate) enum Architecture with TryFrom<Bounded<u32, 6>> {
     }
 }
 
+impl Architecture {
+    /// Returns `true` for GPU architectures that predate Hopper.
+    pub(crate) fn is_pre_hopper(self) -> bool {
+        matches!(self, Self::Turing | Self::Ampere | Self::Ada)
+    }
+}
+
 #[derive(Clone, Copy)]
 pub(crate) struct Revision {
     major: Bounded<u8, 4>,
-- 
2.34.1


^ permalink raw reply related

* [PATCH v1 3/7] gpu: nova-core: add interrupt controller register definitions
From: Joel Fernandes @ 2026-05-01 20:58 UTC (permalink / raw)
  To: linux-kernel
  Cc: Danilo Krummrich, Alexandre Courbot, John Hubbard, Alice Ryhl,
	David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Trevor Gross, Jonathan Corbet, Shuah Khan, nova-gpu, dri-devel,
	rust-for-linux, linux-doc, Joel Fernandes
In-Reply-To: <20260501205825.73614-1-joelagnelf@nvidia.com>

Define the interrupt controller register layout in regs.rs.

The runtime leaf count is architecture-dependent. Leaf arrays have 16
entries covering Hopper/Blackwell maximum. Pre-Hopper chipsets
(Turing/Ampere/Ada) only use indices 0-7.

Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
---
 drivers/gpu/nova-core/regs.rs | 47 +++++++++++++++++++++++++++++++++++
 1 file changed, 47 insertions(+)

diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index 6faeed73901d..51dff318acf1 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -284,6 +284,53 @@ pub(crate) fn vga_workspace_addr(self) -> Option<u64> {
     }
 }
 
+// INTR_CTRL block.
+// Definition of the PF interrupt tree of the GPU interrupt controller. The PF
+// can also view VF interrupt trees, but that is not supported right now.
+
+register! {
+    /// Per-leaf pending interrupt bitmap. Bit N is set when vector
+    /// `(leaf * 32) + N` is pending. Reading returns the current pending bitmap;
+    /// writing acknowledges set bits (write-1-to-clear). 16 leaves cover the
+    /// Hopper/Blackwell+ maximum of 512 vectors; earlier architectures
+    /// (Turing/Ampere/Ada) only use indices 0..7.
+    pub(crate) NV_VF_INTR_LEAF(u32)[16] @ 0x00b81000 {}
+
+    /// Per-leaf interrupt enable set ("allow"). Writing a 1 to bit N enables
+    /// vector `(leaf * 32) + N` (write-1-to-set; writing 0 has no effect).
+    /// Used to unmask interrupts from a specific source.
+    pub(crate) NV_VF_INTR_LEAF_EN_SET(u32)[16] @ 0x00b81200 {}
+
+    /// Per-leaf interrupt enable clear ("block"). Writing a 1 to bit N disables
+    /// vector `(leaf * 32) + N` (write-1-to-clear; writing 0 has no effect).
+    /// Used to mask interrupts from a specific source.
+    pub(crate) NV_VF_INTR_LEAF_EN_CLEAR(u32)[16] @ 0x00b81400 {}
+
+    /// Top-level pending bitmap. Bit N is set if any enabled vector in subtree
+    /// N is pending. Each subtree covers two consecutive leaves
+    /// (subtree N = leaves 2N and 2N+1). The top bit clears automatically once
+    /// every pending vector in the subtree has been acknowledged via the
+    /// corresponding LEAF register.
+    pub(crate) NV_VF_INTR_TOP(u32) @ 0x00b81600 {}
+
+    /// Top-level enable set ("rearm"). Writing a 1 to bit N enables MSI
+    /// delivery for subtree N (write-1-to-set). The ISR writes the active
+    /// subtree mask here after servicing all pending leaves to resume MSI
+    /// generation.
+    pub(crate) NV_VF_INTR_TOP_EN_SET(u32) @ 0x00b81608 {}
+
+    /// Top-level enable clear ("unarm"). Writing a 1 to bit N disables MSI
+    /// delivery for subtree N (write-1-to-clear). The ISR writes the active
+    /// subtree mask here on entry to mask further MSI writes while servicing
+    /// the pending leaves.
+    pub(crate) NV_VF_INTR_TOP_EN_CLEAR(u32) @ 0x00b81610 {}
+
+    /// Synthetic interrupt trigger. Writing a vector number sets that vector's
+    /// LEAF bit as if the corresponding hardware source had asserted, allowing
+    /// software to inject interrupts. Used by the CPU doorbell self-test.
+    pub(crate) NV_VF_INTR_LEAF_TRIGGER(u32) @ 0x00b81640 {}
+}
+
 // PFALCON
 
 register! {
-- 
2.34.1


^ permalink raw reply related

* [PATCH v1 2/7] gpu: nova-core: allocate PCI MSI vector during probe
From: Joel Fernandes @ 2026-05-01 20:58 UTC (permalink / raw)
  To: linux-kernel
  Cc: Danilo Krummrich, Alexandre Courbot, John Hubbard, Alice Ryhl,
	David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Trevor Gross, Jonathan Corbet, Shuah Khan, nova-gpu, dri-devel,
	rust-for-linux, linux-doc, Joel Fernandes
In-Reply-To: <20260501205825.73614-1-joelagnelf@nvidia.com>

Allocate a single PCI MSI interrupt vector in the probe path.

Try MSI/MSI-X first. If that fails (possible in broken VFIO setups),
fall back to INTx with a dev_warn so the issue is visible in dmesg.
The allocation is devres-managed and automatically freed on unbind.

Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
---
 drivers/gpu/nova-core/gpu.rs       |  7 +++++++
 drivers/gpu/nova-core/irq.rs       | 25 +++++++++++++++++++++++++
 drivers/gpu/nova-core/nova_core.rs |  1 +
 3 files changed, 33 insertions(+)
 create mode 100644 drivers/gpu/nova-core/irq.rs

diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index 659f6a24ee13..3ac9cb106bfd 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -11,6 +11,8 @@
     sync::Arc, //
 };
 
+use crate::irq;
+
 use crate::{
     bounded_enum,
     driver::Bar0,
@@ -293,6 +295,11 @@ pub(crate) fn new<'a>(
 
             _: { gsp.boot(pdev, bar, spec.chipset, gsp_falcon, sec2_falcon)? },
 
+            // Allocate a PCI interrupt vector.
+            _: {
+                let _irq_vector = irq::alloc_vector(pdev)?;
+            },
+
             bar: devres_bar,
         })
     }
diff --git a/drivers/gpu/nova-core/irq.rs b/drivers/gpu/nova-core/irq.rs
new file mode 100644
index 000000000000..3a2a40519f11
--- /dev/null
+++ b/drivers/gpu/nova-core/irq.rs
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use kernel::{
+    device::Bound,
+    pci::{
+        self,
+        IrqType,
+        IrqTypes, //
+    },
+    prelude::*,
+};
+
+pub(crate) fn alloc_vector(pdev: &pci::Device<Bound>) -> Result<pci::IrqVector<'_>> {
+    let msi_types = IrqTypes::default().with(IrqType::Msi).with(IrqType::MsiX);
+
+    let irq_vectors = match pdev.alloc_irq_vectors(1, 1, msi_types) {
+        Ok(vecs) => vecs,
+        Err(_) => {
+            dev_warn!(pdev.as_ref(), "MSI not available, falling back to INTx\n");
+            pdev.alloc_irq_vectors(1, 1, IrqTypes::default().with(IrqType::Intx))?
+        }
+    };
+
+    Ok(*irq_vectors.start())
+}
diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs
index 3a609f6937e4..837aa2d36a0e 100644
--- a/drivers/gpu/nova-core/nova_core.rs
+++ b/drivers/gpu/nova-core/nova_core.rs
@@ -19,6 +19,7 @@
 mod firmware;
 mod gpu;
 mod gsp;
+mod irq;
 #[macro_use]
 mod num;
 mod regs;
-- 
2.34.1


^ permalink raw reply related

* [PATCH v1 0/7] gpu: nova-core: add INTR_CTRL interrupt controller and CPU doorbell self-test
From: Joel Fernandes @ 2026-05-01 20:58 UTC (permalink / raw)
  To: linux-kernel
  Cc: Danilo Krummrich, Alexandre Courbot, John Hubbard, Alice Ryhl,
	David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Trevor Gross, Jonathan Corbet, Shuah Khan, nova-gpu, dri-devel,
	rust-for-linux, linux-doc, Joel Fernandes

This series adds interrupt controller support to nova-core, and validates the
full interrupt path with a CPU-doorbell self-test. It is based on today's
drm-rust-next tree.

The GPU interrupt controller block (INTR_CTRL)
----------------------------------------------
INTR_CTRL is the GPU's two-level (top/leaf) interrupt controller. It
multiplexes all GSP and engine interrupts onto a single PCI MSI line. We need
it to receive asynchronous messages from GSP. GSP will the host with a SWGEN0
interrupt (routed via INTR_CTRL) to deliver async RPC messages like error
reports (XIDs) and other event.  INTR_CTRL also routes notifications from GPU
engines to PCIe MSI such as submitted work completion, MMU faults, etc.
Detailed documentation of the architecture with diagrams are provided in a
separate patch (patch 7/7).

What this series proves
-----------------------
The CPU doorbell self-test (patch 6/7) exercises the full interrupt path
end-to-end. It uses the LEAF_TRIGGER hardware register in INTR_CTRL to trigger
a interrupt, then waits for the IRQ handler to fire. The path under test is:

  LEAF_TRIGGER write
       v
  INTR_CTRL TOP/LEAF goes pending
       v
  GPU fires PCI MSI write
       v
  Host VFIO -> guest IOMMU/IRQ
       v
  Guest Linux IRQ -> nova-core handler
       v
  Handler

The full stack has been end-to-end tested on Ampere GA102 with GPU passthrough.

What comes next
---------------
This is the first stage. Once this lands, the next step is to read (via RPC)
the interrupt vector table that GSP programs at boot. That table tells us which
INTR_CTRL leaf and bit each engine is wired to, so the driver can route
incoming interrupts to per-engine handlers. Future patches will also add GSP interrupt
support, and a per-engine ISR dispatch loops.

The git tree with all patches can be found at:
git://git.kernel.org/pub/scm/linux/kernel/git/jfern/linux.git (tag: nova-intr-ctrl-v1-20260501b)

Joel Fernandes (7):
  rust: sync: completion: add wait_for_completion_timeout()
  gpu: nova-core: allocate PCI MSI vector during probe
  gpu: nova-core: add interrupt controller register definitions
  gpu: nova-core: add Architecture::is_pre_hopper() helper
  gpu: nova-core: add INTR_CTRL interrupt controller API
  gpu: nova-core: add CPU doorbell IRQ self-test
  gpu: nova-core: document INTR_CTRL interrupt tree

 Documentation/gpu/nova/core/intr-ctrl.rst  | 305 +++++++++++++++++++++
 Documentation/gpu/nova/index.rst           |   1 +
 drivers/gpu/nova-core/Kconfig              |  13 +
 drivers/gpu/nova-core/gpu.rs               |  22 ++
 drivers/gpu/nova-core/irq.rs               |  29 ++
 drivers/gpu/nova-core/irq/doorbell_test.rs | 203 ++++++++++++++
 drivers/gpu/nova-core/irq/intr_ctrl.rs     | 281 +++++++++++++++++++
 drivers/gpu/nova-core/nova_core.rs         |   2 +
 drivers/gpu/nova-core/regs.rs              |  13 +
 rust/kernel/sync/completion.rs             |  18 +-
 10 files changed, 886 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/gpu/nova/core/intr-ctrl.rst
 create mode 100644 drivers/gpu/nova-core/irq.rs
 create mode 100644 drivers/gpu/nova-core/irq/doorbell_test.rs
 create mode 100644 drivers/gpu/nova-core/irq/intr_ctrl.rs


base-commit: 610e892bdb57043c7769982c2bff0260b6007b75
-- 
2.34.1


^ permalink raw reply


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