* [RFC PATCH v4 0/1] mm/damon: add node_eligible_mem_bp and node_ineligible_mem_bp goal metrics
From: Ravi Jonnalagadda @ 2026-03-20 19:04 UTC (permalink / raw)
To: sj, damon, linux-mm, linux-kernel, linux-doc
Cc: akpm, corbet, bijan311, ajayjoshi, honggyu.kim, yunjeong.mun,
ravis.opensrc
This patch introduces two new DAMON quota goal metrics for controlling
memory distribution in heterogeneous memory systems (e.g., DRAM and CXL
memory tiering) using physical address (PA) mode monitoring.
v3: https://lore.kernel.org/linux-mm/20260223123232.12851-1-ravis.opensrc@gmail.com/
Changes since v3:
=================
- The first two patches from v3 (goal_tuner initialization fix and
esz=0 quota bypass fix) are now in damon/next. This submission
contains only the core metrics patch, rebased on top of those fixes.
- Simplified implementation: removed per-node eligible_bytes array, now
iterates scheme-eligible regions directly for each goal evaluation.
- Handle regions crossing node boundaries: uses damon_get_folio() to
determine actual NUMA node placement of each folio rather than
assuming uniform node placement within a region.
- Pass scheme pointer directly to metric calculation functions, avoiding
container_of() derivation from quota pointer.
- Fixed 80-column wrapping issues.
Background and Motivation
=========================
In heterogeneous memory systems, controlling memory distribution across
NUMA nodes is essential for performance optimization. This patch enables
system-wide page distribution with target-state goals like "maintain 30%
of scheme-eligible memory on CXL" using PA-mode DAMON schemes.
What These Metrics Measure
==========================
node_eligible_mem_bp:
scheme_eligible_bytes_on_node / total_scheme_eligible_bytes * 10000
node_ineligible_mem_bp:
(total - scheme_eligible_bytes_on_node) / total * 10000
The metrics are complementary: eligible_bp + ineligible_bp = 10000 bp.
Two-Scheme Setup for Hot Page Distribution
==========================================
For maintaining 30% of hot memory on CXL (node 1):
PUSH scheme: migrate_hot from node 0 -> node 1
goal: node_ineligible_mem_bp, nid=0, target=3000
"Push hot pages out until 30% of hot memory is NOT on DRAM"
PULL scheme: migrate_hot from node 1 -> node 0
goal: node_eligible_mem_bp, nid=0, target=7000
"Pull hot pages back until 70% of hot memory IS on DRAM"
The complementary goals create a feedback loop that converges to the
target distribution.
Dependencies
============
This patch is based on SJ's damon/next branch which includes the
TEMPORAL goal tuner required for these metrics.
Testing Results
===============
Functionally tested on a two-node heterogeneous memory system with DRAM
(node 0) and CXL memory (node 1). Used PUSH+PULL scheme configuration
with migrate_hot action to maintain a target hot memory ratio between
the two tiers.
With the TEMPORAL goal tuner, the system converges quickly to the target
distribution. The tuner drives esz to maximum when under goal and to
zero once the goal is met, forming a simple on/off feedback loop that
stabilizes at the desired ratio.
With the CONSIST tuner, the scheme still converges but more slowly, as
it migrates and then throttles itself based on quota feedback. The time
to reach the goal varies depending on workload intensity.
Ravi Jonnalagadda (1):
mm/damon: add node_eligible_mem_bp and node_ineligible_mem_bp goal
metrics
include/linux/damon.h | 6 ++
mm/damon/core.c | 158 ++++++++++++++++++++++++++++++++++++---
mm/damon/sysfs-schemes.c | 12 +++
3 files changed, 164 insertions(+), 12 deletions(-)
--
2.43.0
^ permalink raw reply
* Re: [PATCH v3 05/16] fs: afs: correctly drop reference count on mapping failure
From: Lorenzo Stoakes (Oracle) @ 2026-03-20 19:01 UTC (permalink / raw)
To: Vlastimil Babka (SUSE)
Cc: Andrew Morton, Jonathan Corbet, Clemens Ladisch, Arnd Bergmann,
Greg Kroah-Hartman, K . Y . Srinivasan, Haiyang Zhang, Wei Liu,
Dexuan Cui, Long Li, Alexander Shishkin, Maxime Coquelin,
Alexandre Torgue, Miquel Raynal, Richard Weinberger,
Vignesh Raghavendra, Bodo Stroesser, Martin K . Petersen,
David Howells, Marc Dionne, Alexander Viro, Christian Brauner,
Jan Kara, David Hildenbrand, Liam R . Howlett, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Jann Horn, Pedro Falcato,
linux-kernel, linux-doc, linux-hyperv, linux-mtd, linux-staging,
linux-scsi, target-devel, linux-afs, linux-fsdevel, linux-mm,
Ryan Roberts
In-Reply-To: <608ba54c-f19e-4e27-8142-0870f91d6514@kernel.org>
On Fri, Mar 20, 2026 at 07:57:29PM +0100, Vlastimil Babka (SUSE) wrote:
> On 3/19/26 19:23, Lorenzo Stoakes (Oracle) wrote:
> > Commit 9d5403b1036c ("fs: convert most other generic_file_*mmap() users to
> > .mmap_prepare()") updated AFS to use the mmap_prepare callback in favour
> > of the deprecated mmap callback.
> >
> > However, it did not account for the fact that mmap_prepare is called
> > pre-merge, and may then be merged, nor that mmap_prepare can fail to map
> > due to an out of memory error.
>
> So that means a file can become pinned forever? OOM is probably only a
> problem with fault injection in practice, but the merge case can happen. And
> 9d5403b1036c is pre-6.18 LTS. Are we going to need Fixes: and Cc: stable then?
That'd require backporting all of the .mapped functionality and half of this
series, I don't think that's really practical.
I guess I can do a manual backport of a partial revert.
Thanks, Lorenzo
^ permalink raw reply
* Re: [PATCH v3 05/16] fs: afs: correctly drop reference count on mapping failure
From: Vlastimil Babka (SUSE) @ 2026-03-20 18:57 UTC (permalink / raw)
To: Lorenzo Stoakes (Oracle), Andrew Morton
Cc: Jonathan Corbet, Clemens Ladisch, Arnd Bergmann,
Greg Kroah-Hartman, K . Y . Srinivasan, Haiyang Zhang, Wei Liu,
Dexuan Cui, Long Li, Alexander Shishkin, Maxime Coquelin,
Alexandre Torgue, Miquel Raynal, Richard Weinberger,
Vignesh Raghavendra, Bodo Stroesser, Martin K . Petersen,
David Howells, Marc Dionne, Alexander Viro, Christian Brauner,
Jan Kara, David Hildenbrand, Liam R . Howlett, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Jann Horn, Pedro Falcato,
linux-kernel, linux-doc, linux-hyperv, linux-mtd, linux-staging,
linux-scsi, target-devel, linux-afs, linux-fsdevel, linux-mm,
Ryan Roberts
In-Reply-To: <018cd0d8b2dae44de6d3952527e754e52ef02da8.1773944114.git.ljs@kernel.org>
On 3/19/26 19:23, Lorenzo Stoakes (Oracle) wrote:
> Commit 9d5403b1036c ("fs: convert most other generic_file_*mmap() users to
> .mmap_prepare()") updated AFS to use the mmap_prepare callback in favour
> of the deprecated mmap callback.
>
> However, it did not account for the fact that mmap_prepare is called
> pre-merge, and may then be merged, nor that mmap_prepare can fail to map
> due to an out of memory error.
So that means a file can become pinned forever? OOM is probably only a
problem with fault injection in practice, but the merge case can happen. And
9d5403b1036c is pre-6.18 LTS. Are we going to need Fixes: and Cc: stable then?
> Both of those are cases in which we should not be incrementing a reference
> count.
>
> With the newly added vm_ops->mapped callback available, we can simply
> defer this operation to that callback which is only invoked once the
> mapping is successfully in place (but not yet visible to userspace as the
> mmap and VMA write locks are held).
>
> Therefore add afs_mapped() to implement this callback for AFS, and remove
> the code doing so in afs_mmap_prepare().
>
> Also update afs_vm_open(), afs_vm_close() and afs_vm_map_pages() to be
> consistent in how the vnode is accessed.
>
> Signed-off-by: Lorenzo Stoakes (Oracle) <ljs@kernel.org>
> ---
> fs/afs/file.c | 36 ++++++++++++++++++++++++++----------
> 1 file changed, 26 insertions(+), 10 deletions(-)
>
> diff --git a/fs/afs/file.c b/fs/afs/file.c
> index f609366fd2ac..85696ac984cc 100644
> --- a/fs/afs/file.c
> +++ b/fs/afs/file.c
> @@ -28,6 +28,8 @@ static ssize_t afs_file_splice_read(struct file *in, loff_t *ppos,
> static void afs_vm_open(struct vm_area_struct *area);
> static void afs_vm_close(struct vm_area_struct *area);
> static vm_fault_t afs_vm_map_pages(struct vm_fault *vmf, pgoff_t start_pgoff, pgoff_t end_pgoff);
> +static int afs_mapped(unsigned long start, unsigned long end, pgoff_t pgoff,
> + const struct file *file, void **vm_private_data);
>
> const struct file_operations afs_file_operations = {
> .open = afs_open,
> @@ -61,6 +63,7 @@ const struct address_space_operations afs_file_aops = {
> };
>
> static const struct vm_operations_struct afs_vm_ops = {
> + .mapped = afs_mapped,
> .open = afs_vm_open,
> .close = afs_vm_close,
> .fault = filemap_fault,
> @@ -494,32 +497,45 @@ static void afs_drop_open_mmap(struct afs_vnode *vnode)
> */
> static int afs_file_mmap_prepare(struct vm_area_desc *desc)
> {
> - struct afs_vnode *vnode = AFS_FS_I(file_inode(desc->file));
> int ret;
>
> - afs_add_open_mmap(vnode);
> -
> ret = generic_file_mmap_prepare(desc);
> - if (ret == 0)
> - desc->vm_ops = &afs_vm_ops;
> - else
> - afs_drop_open_mmap(vnode);
> + if (ret)
> + return ret;
> +
> + desc->vm_ops = &afs_vm_ops;
> return ret;
> }
>
> +static int afs_mapped(unsigned long start, unsigned long end, pgoff_t pgoff,
> + const struct file *file, void **vm_private_data)
> +{
> + struct afs_vnode *vnode = AFS_FS_I(file_inode(file));
> +
> + afs_add_open_mmap(vnode);
> + return 0;
> +}
> +
> static void afs_vm_open(struct vm_area_struct *vma)
> {
> - afs_add_open_mmap(AFS_FS_I(file_inode(vma->vm_file)));
> + struct file *file = vma->vm_file;
> + struct afs_vnode *vnode = AFS_FS_I(file_inode(file));
> +
> + afs_add_open_mmap(vnode);
> }
>
> static void afs_vm_close(struct vm_area_struct *vma)
> {
> - afs_drop_open_mmap(AFS_FS_I(file_inode(vma->vm_file)));
> + struct file *file = vma->vm_file;
> + struct afs_vnode *vnode = AFS_FS_I(file_inode(file));
> +
> + afs_drop_open_mmap(vnode);
> }
>
> static vm_fault_t afs_vm_map_pages(struct vm_fault *vmf, pgoff_t start_pgoff, pgoff_t end_pgoff)
> {
> - struct afs_vnode *vnode = AFS_FS_I(file_inode(vmf->vma->vm_file));
> + struct file *file = vmf->vma->vm_file;
> + struct afs_vnode *vnode = AFS_FS_I(file_inode(file));
>
> if (afs_check_validity(vnode))
> return filemap_map_pages(vmf, start_pgoff, end_pgoff);
^ permalink raw reply
* Re: [PATCH v3 04/16] mm: add vm_ops->mapped hook
From: Vlastimil Babka (SUSE) @ 2026-03-20 18:26 UTC (permalink / raw)
To: Lorenzo Stoakes (Oracle), Andrew Morton
Cc: Jonathan Corbet, Clemens Ladisch, Arnd Bergmann,
Greg Kroah-Hartman, K . Y . Srinivasan, Haiyang Zhang, Wei Liu,
Dexuan Cui, Long Li, Alexander Shishkin, Maxime Coquelin,
Alexandre Torgue, Miquel Raynal, Richard Weinberger,
Vignesh Raghavendra, Bodo Stroesser, Martin K . Petersen,
David Howells, Marc Dionne, Alexander Viro, Christian Brauner,
Jan Kara, David Hildenbrand, Liam R . Howlett, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Jann Horn, Pedro Falcato,
linux-kernel, linux-doc, linux-hyperv, linux-mtd, linux-staging,
linux-scsi, target-devel, linux-afs, linux-fsdevel, linux-mm,
Ryan Roberts
In-Reply-To: <a97366fa6f22a0ca1340cfd2b0d4df87c80ac80a.1773944114.git.ljs@kernel.org>
On 3/19/26 19:23, Lorenzo Stoakes (Oracle) wrote:
> Previously, when a driver needed to do something like establish a
> reference count, it could do so in the mmap hook in the knowledge that the
> mapping would succeed.
>
> With the introduction of f_op->mmap_prepare this is no longer the case, as
> it is invoked prior to actually establishing the mapping.
>
> mmap_prepare is not appropriate for this kind of thing as it is called
> before any merge might take place, and after which an error might occur
> meaning resources could be leaked.
>
> To take this into account, introduce a new vm_ops->mapped callback which
> is invoked when the VMA is first mapped (though notably - not when it is
> merged - which is correct and mirrors existing mmap/open/close behaviour).
>
> We do better that vm_ops->open() here, as this callback can return an
> error, at which point the VMA will be unmapped.
>
> Note that vm_ops->mapped() is invoked after any mmap action is complete
> (such as I/O remapping).
>
> We intentionally do not expose the VMA at this point, exposing only the
> fields that could be used, and an output parameter in case the operation
> needs to update the vma->vm_private_data field.
>
> In order to deal with stacked filesystems which invoke inner filesystem's
> mmap() invocations, add __compat_vma_mapped() and invoke it on vfs_mmap()
> (via compat_vma_mmap()) to ensure that the mapped callback is handled when
> an mmap() caller invokes a nested filesystem's mmap_prepare() callback.
>
> We can now also remove call_action_complete() and invoke
> mmap_action_complete() directly, as we separate out the rmap lock logic.
>
> The rmap lock logic, which was added in order to keep hugetlb working (!)
> to allow for the rmap lock to be held longer, needs to be propagated to the
> error paths on mmap complete and mapped hook error paths.
>
> This is because do_munmap() might otherwise deadlock with the rmap being
> held, so instead we unlock at the point of unmap.
Hmm but that was also true prior to this series? So is this a bugfix? Should
it be a stable hotfix done outside of the series before the refactoring?
> This is fine as any reliance on the rmap being held is irrelevant on error.
>
> While we're here, refactor mmap_action_finish() to avoid a big if (err)
> branch.
>
> We also abstract unmapping of a VMA on mmap action completion into its own
> helper function, unmap_vma_locked().
>
> Update the mmap_prepare documentation to describe the mapped hook and make
> it clear what its intended use is.
>
> Additionally, update VMA userland test headers to reflect the change.
>
> Signed-off-by: Lorenzo Stoakes (Oracle) <ljs@kernel.org>
> ---
> Documentation/filesystems/mmap_prepare.rst | 15 +++
> include/linux/fs.h | 9 +-
> include/linux/mm.h | 20 +++-
> mm/internal.h | 8 ++
> mm/util.c | 129 ++++++++++++++-------
> mm/vma.c | 35 +++---
> tools/testing/vma/include/dup.h | 27 ++++-
> tools/testing/vma/include/stubs.h | 3 +-
> 8 files changed, 186 insertions(+), 60 deletions(-)
>
> diff --git a/Documentation/filesystems/mmap_prepare.rst b/Documentation/filesystems/mmap_prepare.rst
> index 65a1f094e469..20db474915da 100644
> --- a/Documentation/filesystems/mmap_prepare.rst
> +++ b/Documentation/filesystems/mmap_prepare.rst
> @@ -25,6 +25,21 @@ That is - no resources should be allocated nor state updated to reflect that a
> mapping has been established, as the mapping may either be merged, or fail to be
> mapped after the callback is complete.
>
> +Mapped callback
> +---------------
> +
> +If resources need to be allocated per-mapping, or state such as a reference
> +count needs to be manipulated, this should be done using the ``vm_ops->mapped``
> +hook, which itself should be set by the >mmap_prepare hook.
> +
> +This callback is only invoked if a new mapping has been established and was not
> +merged with any other, and is invoked at a point where no error may occur before
> +the mapping is established.
> +
> +You may return an error to the callback itself, which will cause the mapping to
> +become unmapped and an error returned to the mmap() caller. This is useful if
> +resources need to be allocated, and that allocation might fail.
> +
> How To Use
> ==========
>
> diff --git a/include/linux/fs.h b/include/linux/fs.h
> index a2628a12bd2b..c390f5c667e3 100644
> --- a/include/linux/fs.h
> +++ b/include/linux/fs.h
> @@ -2059,13 +2059,20 @@ static inline bool can_mmap_file(struct file *file)
> }
>
> int compat_vma_mmap(struct file *file, struct vm_area_struct *vma);
> +int __vma_check_mmap_hook(struct vm_area_struct *vma);
>
> static inline int vfs_mmap(struct file *file, struct vm_area_struct *vma)
> {
> + int err;
> +
> if (file->f_op->mmap_prepare)
> return compat_vma_mmap(file, vma);
>
> - return file->f_op->mmap(file, vma);
> + err = file->f_op->mmap(file, vma);
> + if (err)
> + return err;
> +
> + return __vma_check_mmap_hook(vma);
> }
>
> static inline int vfs_mmap_prepare(struct file *file, struct vm_area_desc *desc)
> diff --git a/include/linux/mm.h b/include/linux/mm.h
> index da94edb287cd..68dee1101313 100644
> --- a/include/linux/mm.h
> +++ b/include/linux/mm.h
> @@ -777,6 +777,23 @@ struct vm_operations_struct {
> * Context: User context. May sleep. Caller holds mmap_lock.
> */
> void (*close)(struct vm_area_struct *vma);
> + /**
> + * @mapped: Called when the VMA is first mapped in the MM. Not called if
> + * the new VMA is merged with an adjacent VMA.
> + *
> + * The @vm_private_data field is an output field allowing the user to
> + * modify vma->vm_private_data as necessary.
> + *
> + * ONLY valid if set from f_op->mmap_prepare. Will result in an error if
> + * set from f_op->mmap.
> + *
> + * Returns %0 on success, or an error otherwise. On error, the VMA will
> + * be unmapped.
> + *
> + * Context: User context. May sleep. Caller holds mmap_lock.
> + */
> + int (*mapped)(unsigned long start, unsigned long end, pgoff_t pgoff,
> + const struct file *file, void **vm_private_data);
> /* Called any time before splitting to check if it's allowed */
> int (*may_split)(struct vm_area_struct *vma, unsigned long addr);
> int (*mremap)(struct vm_area_struct *vma);
> @@ -4327,7 +4344,8 @@ static inline void mmap_action_ioremap_full(struct vm_area_desc *desc,
>
> int mmap_action_prepare(struct vm_area_desc *desc);
> int mmap_action_complete(struct vm_area_struct *vma,
> - struct mmap_action *action);
> + struct mmap_action *action,
> + bool rmap_lock_held);
>
> /* Look up the first VMA which exactly match the interval vm_start ... vm_end */
> static inline struct vm_area_struct *find_exact_vma(struct mm_struct *mm,
> diff --git a/mm/internal.h b/mm/internal.h
> index 0256ca44115a..e0f554178143 100644
> --- a/mm/internal.h
> +++ b/mm/internal.h
> @@ -202,6 +202,14 @@ static inline void vma_close(struct vm_area_struct *vma)
> /* unmap_vmas is in mm/memory.c */
> void unmap_vmas(struct mmu_gather *tlb, struct unmap_desc *unmap);
>
> +static inline void unmap_vma_locked(struct vm_area_struct *vma)
> +{
> + const size_t len = vma_pages(vma) << PAGE_SHIFT;
> +
> + mmap_assert_write_locked(vma->vm_mm);
> + do_munmap(vma->vm_mm, vma->vm_start, len, NULL);
> +}
> +
> #ifdef CONFIG_MMU
>
> static inline void get_anon_vma(struct anon_vma *anon_vma)
> diff --git a/mm/util.c b/mm/util.c
> index 73c97a748d8e..fc1bd8a8f3ea 100644
> --- a/mm/util.c
> +++ b/mm/util.c
> @@ -1163,6 +1163,54 @@ void flush_dcache_folio(struct folio *folio)
> EXPORT_SYMBOL(flush_dcache_folio);
> #endif
>
> +static int __compat_vma_mmap(struct file *file, struct vm_area_struct *vma)
> +{
> + struct vm_area_desc desc = {
> + .mm = vma->vm_mm,
> + .file = file,
> + .start = vma->vm_start,
> + .end = vma->vm_end,
> +
> + .pgoff = vma->vm_pgoff,
> + .vm_file = vma->vm_file,
> + .vma_flags = vma->flags,
> + .page_prot = vma->vm_page_prot,
> +
> + .action.type = MMAP_NOTHING, /* Default */
> + };
> + int err;
> +
> + err = vfs_mmap_prepare(file, &desc);
> + if (err)
> + return err;
> +
> + err = mmap_action_prepare(&desc);
> + if (err)
> + return err;
> +
> + set_vma_from_desc(vma, &desc);
> + return mmap_action_complete(vma, &desc.action, /*rmap_lock_held=*/false);
Patch 1 removed this function and this one reinstates it with some
modifications. Could patch 1 only remove the export and otherwise both do
only the necessary modifications?
> +}
> +
> +static int __compat_vma_mapped(struct file *file, struct vm_area_struct *vma)
> +{
> + const struct vm_operations_struct *vm_ops = vma->vm_ops;
> + void *vm_private_data = vma->vm_private_data;
> + int err;
> +
> + if (!vm_ops || !vm_ops->mapped)
> + return 0;
> +
> + err = vm_ops->mapped(vma->vm_start, vma->vm_end, vma->vm_pgoff, file,
> + &vm_private_data);
> + if (err)
> + unmap_vma_locked(vma);
> + else if (vm_private_data != vma->vm_private_data)
> + vma->vm_private_data = vm_private_data;
> +
> + return err;
> +}
> +
> /**
> * compat_vma_mmap() - Apply the file's .mmap_prepare() hook to an
> * existing VMA and execute any requested actions.
> @@ -1191,34 +1239,26 @@ EXPORT_SYMBOL(flush_dcache_folio);
> */
> int compat_vma_mmap(struct file *file, struct vm_area_struct *vma)
> {
> - struct vm_area_desc desc = {
> - .mm = vma->vm_mm,
> - .file = file,
> - .start = vma->vm_start,
> - .end = vma->vm_end,
> -
> - .pgoff = vma->vm_pgoff,
> - .vm_file = vma->vm_file,
> - .vma_flags = vma->flags,
> - .page_prot = vma->vm_page_prot,
> -
> - .action.type = MMAP_NOTHING, /* Default */
> - };
> int err;
>
> - err = vfs_mmap_prepare(file, &desc);
> + err = __compat_vma_mmap(file, vma);
> if (err)
> return err;
>
> - err = mmap_action_prepare(&desc);
> - if (err)
> - return err;
> -
> - set_vma_from_desc(vma, &desc);
> - return mmap_action_complete(vma, &desc.action);
> + return __compat_vma_mapped(file, vma);
> }
> EXPORT_SYMBOL(compat_vma_mmap);
>
> +int __vma_check_mmap_hook(struct vm_area_struct *vma)
> +{
> + /* vm_ops->mapped is not valid if mmap() is specified. */
> + if (vma->vm_ops && WARN_ON_ONCE(vma->vm_ops->mapped))
> + return -EINVAL;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(__vma_check_mmap_hook);
> +
> static void set_ps_flags(struct page_snapshot *ps, const struct folio *folio,
> const struct page *page)
> {
> @@ -1308,32 +1348,31 @@ void snapshot_page(struct page_snapshot *ps, const struct page *page)
> }
>
> static int mmap_action_finish(struct vm_area_struct *vma,
> - struct mmap_action *action, int err)
> + struct mmap_action *action, int err,
> + bool rmap_lock_held)
> {
> + if (rmap_lock_held)
> + i_mmap_unlock_write(vma->vm_file->f_mapping);
Should this be moved below the if (!err) ?
Otherwise I think we unlock prematurely, and can even try to unlock twice -
here and in call_mapped_hook(). And we want to unlock only if we're about to
munmap, right?
> +
> + if (!err) {
> + if (action->success_hook)
> + return action->success_hook(vma);
> + return 0;
> + }
> +
> /*
> * If an error occurs, unmap the VMA altogether and return an error. We
> * only clear the newly allocated VMA, since this function is only
> * invoked if we do NOT merge, so we only clean up the VMA we created.
> */
> - if (err) {
> - const size_t len = vma_pages(vma) << PAGE_SHIFT;
> -
> - do_munmap(current->mm, vma->vm_start, len, NULL);
> -
> - if (action->error_hook) {
> - /* We may want to filter the error. */
> - err = action->error_hook(err);
> -
> - /* The caller should not clear the error. */
> - VM_WARN_ON_ONCE(!err);
> - }
> - return err;
> + unmap_vma_locked(vma);
> + if (action->error_hook) {
> + /* We may want to filter the error. */
> + err = action->error_hook(err);
> + /* The caller should not clear the error. */
> + VM_WARN_ON_ONCE(!err);
> }
> -
> - if (action->success_hook)
> - return action->success_hook(vma);
> -
> - return 0;
> + return err;
> }
>
> #ifdef CONFIG_MMU
> @@ -1364,13 +1403,15 @@ EXPORT_SYMBOL(mmap_action_prepare);
> * mmap_action_complete - Execute VMA descriptor action.
> * @vma: The VMA to perform the action upon.
> * @action: The action to perform.
> + * @rmap_lock_held: Is the file rmap lock held?
> *
> * Similar to mmap_action_prepare().
> *
> * Return: 0 on success, or error, at which point the VMA will be unmapped.
> */
> int mmap_action_complete(struct vm_area_struct *vma,
> - struct mmap_action *action)
> + struct mmap_action *action,
> + bool rmap_lock_held)
>
> {
> int err = 0;
> @@ -1388,7 +1429,8 @@ int mmap_action_complete(struct vm_area_struct *vma,
> break;
> }
>
> - return mmap_action_finish(vma, action, err);
> + return mmap_action_finish(vma, action, err,
> + rmap_lock_held);
> }
> EXPORT_SYMBOL(mmap_action_complete);
> #else
> @@ -1408,7 +1450,8 @@ int mmap_action_prepare(struct vm_area_desc *desc)
> EXPORT_SYMBOL(mmap_action_prepare);
>
> int mmap_action_complete(struct vm_area_struct *vma,
> - struct mmap_action *action)
> + struct mmap_action *action,
> + bool rmap_lock_held)
> {
> int err = 0;
>
> @@ -1423,7 +1466,7 @@ int mmap_action_complete(struct vm_area_struct *vma,
> break;
> }
>
> - return mmap_action_finish(vma, action, err);
> + return mmap_action_finish(vma, action, err, rmap_lock_held);
> }
> EXPORT_SYMBOL(mmap_action_complete);
> #endif
> diff --git a/mm/vma.c b/mm/vma.c
> index 2a86c7575000..a27d1278ea6d 100644
> --- a/mm/vma.c
> +++ b/mm/vma.c
> @@ -2731,21 +2731,28 @@ static bool can_set_ksm_flags_early(struct mmap_state *map)
> return false;
> }
>
> -static int call_action_complete(struct mmap_state *map,
> - struct mmap_action *action,
> - struct vm_area_struct *vma)
> +static int call_mapped_hook(struct mmap_state *map,
> + struct vm_area_struct *vma)
> {
> - int ret;
> -
> - ret = mmap_action_complete(vma, action);
> + const struct vm_operations_struct *vm_ops = vma->vm_ops;
> + void *vm_private_data = vma->vm_private_data;
> + int err;
>
> - /* If we held the file rmap we need to release it. */
> - if (map->hold_file_rmap_lock) {
> - struct file *file = vma->vm_file;
> + if (!vm_ops || !vm_ops->mapped)
> + return 0;
> + err = vm_ops->mapped(vma->vm_start, vma->vm_end, vma->vm_pgoff,
> + vma->vm_file, &vm_private_data);
> + if (err) {
> + if (map->hold_file_rmap_lock)
> + i_mmap_unlock_write(vma->vm_file->f_mapping);
>
> - i_mmap_unlock_write(file->f_mapping);
> + unmap_vma_locked(vma);
> + return err;
> }
> - return ret;
> + /* Update private data if changed. */
> + if (vm_private_data != vma->vm_private_data)
> + vma->vm_private_data = vm_private_data;
> + return 0;
> }
>
> static unsigned long __mmap_region(struct file *file, unsigned long addr,
> @@ -2799,8 +2806,10 @@ static unsigned long __mmap_region(struct file *file, unsigned long addr,
> __mmap_complete(&map, vma);
>
> if (have_mmap_prepare && allocated_new) {
> - error = call_action_complete(&map, &desc.action, vma);
> -
> + error = mmap_action_complete(vma, &desc.action,
> + map.hold_file_rmap_lock);
> + if (!error)
> + error = call_mapped_hook(&map, vma);
And if neither of those above end up doing i_mmap_unlock_write(), we should
do it here? I think currently the misplaced unlock in mmap_action_finish()
masks the lack of it here, otherwise bots would already notice. Loss of
locking coverage (due to premature unlock) or the risk of double unlock is
probably harder to trigger. Or maybe a later patch in the series happens to
fix the issues, so it's just a bisection hazard here. Or I'm completely wrong.
> if (error)
> return error;
> }
> diff --git a/tools/testing/vma/include/dup.h b/tools/testing/vma/include/dup.h
> index 8ae525ed1738..aa34966cbc62 100644
> --- a/tools/testing/vma/include/dup.h
> +++ b/tools/testing/vma/include/dup.h
> @@ -643,6 +643,23 @@ struct vm_operations_struct {
> * Context: User context. May sleep. Caller holds mmap_lock.
> */
> void (*close)(struct vm_area_struct *vma);
> + /**
> + * @mapped: Called when the VMA is first mapped in the MM. Not called if
> + * the new VMA is merged with an adjacent VMA.
> + *
> + * The @vm_private_data field is an output field allowing the user to
> + * modify vma->vm_private_data as necessary.
> + *
> + * ONLY valid if set from f_op->mmap_prepare. Will result in an error if
> + * set from f_op->mmap.
> + *
> + * Returns %0 on success, or an error otherwise. On error, the VMA will
> + * be unmapped.
> + *
> + * Context: User context. May sleep. Caller holds mmap_lock.
> + */
> + int (*mapped)(unsigned long start, unsigned long end, pgoff_t pgoff,
> + const struct file *file, void **vm_private_data);
> /* Called any time before splitting to check if it's allowed */
> int (*may_split)(struct vm_area_struct *vma, unsigned long addr);
> int (*mremap)(struct vm_area_struct *vma);
> @@ -1281,7 +1298,7 @@ static inline int __compat_vma_mmap(const struct file_operations *f_op,
> return err;
>
> set_vma_from_desc(vma, &desc);
> - return mmap_action_complete(vma, &desc.action);
> + return mmap_action_complete(vma, &desc.action, /*rmap_lock_held=*/false);
> }
>
> static inline int compat_vma_mmap(struct file *file,
> @@ -1500,3 +1517,11 @@ static inline pgprot_t vma_get_page_prot(vma_flags_t vma_flags)
>
> return vm_get_page_prot(vm_flags);
> }
> +
> +static inline void unmap_vma_locked(struct vm_area_struct *vma)
> +{
> + const size_t len = vma_pages(vma) << PAGE_SHIFT;
> +
> + mmap_assert_write_locked(vma->vm_mm);
> + do_munmap(vma->vm_mm, vma->vm_start, len, NULL);
> +}
> diff --git a/tools/testing/vma/include/stubs.h b/tools/testing/vma/include/stubs.h
> index a30b8bc84955..d1c3d4ddb5e9 100644
> --- a/tools/testing/vma/include/stubs.h
> +++ b/tools/testing/vma/include/stubs.h
> @@ -87,7 +87,8 @@ static inline int mmap_action_prepare(struct vm_area_desc *desc)
> }
>
> static inline int mmap_action_complete(struct vm_area_struct *vma,
> - struct mmap_action *action)
> + struct mmap_action *action,
> + bool rmap_lock_held)
> {
> return 0;
> }
^ permalink raw reply
* Re: [PATCH v7 07/10] x86/vmscape: Use static_call() for predictor flush
From: Pawan Gupta @ 2026-03-20 18:23 UTC (permalink / raw)
To: Borislav Petkov
Cc: Peter Zijlstra, x86, Nikolay Borisov, H. Peter Anvin,
Josh Poimboeuf, David Kaplan, Sean Christopherson, Dave Hansen,
Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, KP Singh,
Jiri Olsa, David S. Miller, David Laight, Andy Lutomirski,
Thomas Gleixner, Ingo Molnar, David Ahern, Martin KaFai Lau,
Eduard Zingerman, Song Liu, Yonghong Song, John Fastabend,
Stanislav Fomichev, Hao Luo, Paolo Bonzini, Jonathan Corbet,
linux-kernel, kvm, Asit Mallick, Tao Zhang, bpf, netdev,
linux-doc
In-Reply-To: <20260320113134.GAab0wFqe-hewZc175@fat_crate.local>
On Fri, Mar 20, 2026 at 12:31:34PM +0100, Borislav Petkov wrote:
> On Fri, Mar 20, 2026 at 10:03:40AM +0100, Peter Zijlstra wrote:
> > On Thu, Mar 19, 2026 at 11:22:06PM -0700, Pawan Gupta wrote:
> >
> > > This plus extending it to support EXPORT_STATIC_CALL_FOR_KVM() is probably
> > > a better solution. Please let me know which one you prefer.
> >
> > The EXPORT twiddling will do I suppose. I'll try and not forget looking
> > at doing the RO static_call thing some time.
>
> Dunno, but exporting a static_call sounds really really wrong to me. No matter
> where. As in: we're exporting the underlying inner workings of it and that
> should be a big fat no-no.
I am curious, what problems do you anticipate? There are nearly 50
instances of static key being exported. For example:
$ git grep -A1 -n DEFINE_STATIC_KEY | grep -B 1 EXPORT_SYMBOL
arch/arm64/kernel/mte.c:34:DEFINE_STATIC_KEY_FALSE(mte_async_or_asymm_mode);
arch/arm64/kernel/mte.c-35-EXPORT_SYMBOL_GPL(mte_async_or_asymm_mode);
--
arch/arm64/kernel/rsi.c:22:DEFINE_STATIC_KEY_FALSE_RO(rsi_present);
arch/arm64/kernel/rsi.c-23-EXPORT_SYMBOL(rsi_present);
--
arch/powerpc/kernel/firmware.c:25:DEFINE_STATIC_KEY_FALSE(kvm_guest);
arch/powerpc/kernel/firmware.c-26-EXPORT_SYMBOL_GPL(kvm_guest);
...
Since EXPORT_STATIC_CALL_FOR_KVM() exports only to a module that needs it,
it limits the scope of the problem.
> So definitely +1 on exporting the helper instead.
The helper approach can be easily replaced with the static_call export
later. I can go with the helper for now.
^ permalink raw reply
* [PATCH v5] hwmon: add driver for ARCTIC Fan Controller
From: Aureo Serrano de Souza @ 2026-03-20 18:00 UTC (permalink / raw)
To: linux-hwmon
Cc: linux, linux, corbet, skhan, linux-doc, linux-kernel,
Aureo Serrano de Souza
Add hwmon driver for the ARCTIC Fan Controller, a USB HID device
(VID 0x3904, PID 0xF001) with 10 fan channels. Exposes fan speed in
RPM (read-only) and PWM duty cycle (0-255, read/write) via sysfs.
The device pushes IN reports at ~1 Hz containing RPM readings. PWM is
set via OUT reports; the device applies the new duty cycle and sends
back a 2-byte ACK (Report ID 0x02). The driver waits up to 1 s for
the ACK using a completion. Measured device latency: max ~563 ms over
500 iterations. PWM control is manual-only: the device never changes
duty cycle autonomously.
raw_event() may run in hardirq context, so fan_rpm[] is protected by
a spinlock with irq-save. pwm_duty[] and the report buffer are
serialized by the hwmon core, which holds its lock for the duration of
the read/write callbacks.
Signed-off-by: Aureo Serrano de Souza <aureo.serrano@arctic.de>
---
Thanks to Guenter Roeck and Thomas Weißschuh for the reviews.
Changes since v4:
- arctic_fan_write(): switch to wait_for_completion_timeout() (non-
interruptible); eliminates the signal-interrupted write case of the
late-ACK race that write_pending could not fully prevent
- arctic_fan_write(): guard pwm_duty[channel] commit with
ack_status == 0 check; a device error ACK (status 0x01) no longer
silently poisons the cached duty used in future OUT reports
- arctic_fan_probe()/remove(): replace devm_add_action_or_reset() +
no-op remove() with explicit hid_device_io_stop/hid_hw_close/
hid_hw_stop in remove(); devm_add_action_or_reset() was called after
hdev->driver = NULL, causing a NULL deref in hid_hw_close() on unbind
- add reset_resume callback: device resets PWM to hardware defaults on
power loss during suspend; driver now clears cached pwm_duty[] on
reset-resume so stale pre-suspend values are not re-sent as if valid
- Documentation/hwmon/arctic_fan_controller.rst: document suspend/
resume behaviour and the updated pwm[1-10] read semantics
Changes since v3:
- buf[]: upgrade from __aligned(8) to ____cacheline_aligned so the
DMA buffer occupies its own cache line, preventing false sharing with
adjacent fan_rpm[]/pwm_duty[] fields on non-coherent architectures
- arctic_fan_write(): add write_pending flag (protected by
in_report_lock) so raw_event() delivers ACKs only while a write is
in flight
- arctic_fan_write(): commit pwm_duty[channel] only after the device
ACKs the command; a failed or timed-out write no longer leaves a
stale value in the cached duty state
- arctic_fan_probe(): start IO (hid_device_io_start) before registering
with hwmon; previously a sysfs write arriving between hwmon
registration and io_start could send an OUT report whose ACK would be
discarded by the HID core, causing a spurious timeout
- Documentation/hwmon/arctic_fan_controller.rst: document that cached
PWM values start at 0 (hardware state unknown at probe) and that each
OUT report carries all 10 channel values
Changes since v2:
- buf[]: add __aligned(8) for DMA safety
- ARCTIC_ACK_TIMEOUT_MS: restore 1000 ms; note observed max ~563 ms
- arctic_fan_parse_report(): replace hwmon_lock/hwmon_unlock with
spin_lock_irqsave; hwmon_lock() may sleep and is unsafe when
raw_event() runs in hardirq/softirq context
- arctic_fan_raw_event(): use spin_lock_irqsave for ACK path
- arctic_fan_write(): use spin_lock_irqsave for completion reinit
- arctic_fan_write(): clamp val to [0, 255] before u8 cast
- remove priv->hwmon_dev (no longer needed)
Changes since v1:
- Use hid_dbg() instead of module_param debug flag
- Move hid_device_id table adjacent to hid_driver struct
- Use get_unaligned_le16() for RPM parsing
- Remove impossible bounds/NULL checks; remove retry loop
- Add hid_is_usb() guard
- Do not update pwm_duty from IN reports (device is manual-only)
- Add completion/ACK mechanism for OUT report acknowledgement
- Add Documentation/hwmon/arctic_fan_controller.rst and MAINTAINERS
diff --git a/Documentation/hwmon/arctic_fan_controller.rst b/Documentation/hwmon/arctic_fan_controller.rst
new file mode 100644
index 0000000000..b5be88ae46
--- /dev/null
+++ b/Documentation/hwmon/arctic_fan_controller.rst
@@ -0,0 +1,56 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+Kernel driver arctic_fan_controller
+=====================================
+
+Supported devices:
+
+* ARCTIC Fan Controller (USB HID, VID 0x3904, PID 0xF001)
+
+Author: Aureo Serrano de Souza <aureo.serrano@arctic.de>
+
+Description
+-----------
+
+This driver provides hwmon support for the ARCTIC Fan Controller, a USB
+Custom HID device with 10 fan channels. The device sends IN reports about
+once per second containing current RPM values (bytes 11-30, 10 x uint16 LE).
+Fan speed control is manual-only: the device does not change PWM
+autonomously; it only applies a new duty cycle when it receives an OUT
+report from the host.
+
+After the device applies an OUT report, it sends back a 2-byte ACK IN
+report (Report ID 0x02, byte 1 = 0x00 on success) confirming the command
+was applied.
+
+Usage notes
+-----------
+
+Since it is a USB device, hotplug is supported. The device is autodetected.
+
+The device does not support GET_REPORT, so the driver cannot read back the
+current hardware PWM state at probe time. The cached PWM values (readable
+via pwm[1-10]) start at 0 and reflect only values that have been
+successfully written. Because each OUT report carries all 10 channel values,
+writing a single channel also sends the cached values for all other channels.
+Users should set all channels to the desired values before relying on the
+cached state.
+
+On system suspend, the device may lose power and reset its PWM channels to
+hardware defaults. The driver clears its cached duty values on resume so
+that reads reflect the unknown hardware state rather than stale pre-suspend
+values. Userspace is responsible for re-applying the desired duty cycles
+after resume.
+
+Sysfs entries
+-------------
+
+================ ==============================================================
+fan[1-10]_input Fan speed in RPM (read-only). Updated from IN reports at ~1 Hz.
+pwm[1-10] PWM duty cycle (0-255). Write: sends an OUT report setting the
+ duty cycle (scaled from 0-255 to 0-100% for the device);
+ the cached value is updated only after the device ACKs the
+ command with a success status. Read: returns the last
+ successfully written value; initialized to 0 at driver load
+ and after resume (hardware state unknown).
+================ ==============================================================
diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
index b2ca8513cf..c34713040e 100644
--- a/Documentation/hwmon/index.rst
+++ b/Documentation/hwmon/index.rst
@@ -42,6 +42,7 @@ Hardware Monitoring Kernel Drivers
aht10
amc6821
aquacomputer_d5next
+ arctic_fan_controller
asb100
asc7621
aspeed-g6-pwm-tach
diff --git a/MAINTAINERS b/MAINTAINERS
index 96ea84948d..ec3112bd41 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2053,6 +2053,13 @@ S: Maintained
F: drivers/net/arcnet/
F: include/uapi/linux/if_arcnet.h
+ARCTIC FAN CONTROLLER DRIVER
+M: Aureo Serrano de Souza <aureo.serrano@arctic.de>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/arctic_fan_controller.rst
+F: drivers/hwmon/arctic_fan_controller.c
+
ARM AND ARM64 SoC SUB-ARCHITECTURES (COMMON PARTS)
M: Arnd Bergmann <arnd@arndb.de>
M: Krzysztof Kozlowski <krzk@kernel.org>
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 328867242c..6c90a8dd40 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -388,6 +388,18 @@ config SENSORS_APPLESMC
Say Y here if you have an applicable laptop and want to experience
the awesome power of applesmc.
+config SENSORS_ARCTIC_FAN_CONTROLLER
+ tristate "ARCTIC Fan Controller"
+ depends on USB_HID
+ help
+ If you say yes here you get support for the ARCTIC Fan Controller,
+ a USB HID device (VID 0x3904, PID 0xF001) with 10 fan channels.
+ The driver exposes fan speed (RPM) and PWM control via the hwmon
+ sysfs interface.
+
+ This driver can also be built as a module. If so, the module
+ will be called arctic_fan_controller.
+
config SENSORS_ARM_SCMI
tristate "ARM SCMI Sensors"
depends on ARM_SCMI_PROTOCOL
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 5833c807c6..ef831c3375 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -49,6 +49,7 @@ obj-$(CONFIG_SENSORS_ADT7475) += adt7475.o
obj-$(CONFIG_SENSORS_AHT10) += aht10.o
obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o
obj-$(CONFIG_SENSORS_AQUACOMPUTER_D5NEXT) += aquacomputer_d5next.o
+obj-$(CONFIG_SENSORS_ARCTIC_FAN_CONTROLLER) += arctic_fan_controller.o
obj-$(CONFIG_SENSORS_ARM_SCMI) += scmi-hwmon.o
obj-$(CONFIG_SENSORS_ARM_SCPI) += scpi-hwmon.o
obj-$(CONFIG_SENSORS_AS370) += as370-hwmon.o
diff --git a/drivers/hwmon/arctic_fan_controller.c b/drivers/hwmon/arctic_fan_controller.c
new file mode 100644
index 0000000000..f50a9a6689
--- /dev/null
+++ b/drivers/hwmon/arctic_fan_controller.c
@@ -0,0 +1,338 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Linux hwmon driver for ARCTIC Fan Controller
+ *
+ * USB Custom HID device with 10 fan channels.
+ * Exposes fan RPM (input) and PWM (0-255) via hwmon. Device pushes IN reports
+ * at ~1 Hz; no GET_REPORT. OUT reports set PWM duty (bytes 1-10, 0-100%).
+ * PWM is manual-only: the device does not change duty autonomously, only
+ * when it receives an OUT report from the host.
+ */
+
+#include <linux/cache.h>
+#include <linux/completion.h>
+#include <linux/err.h>
+#include <linux/hid.h>
+#include <linux/hwmon.h>
+#include <linux/jiffies.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/unaligned.h>
+
+#define ARCTIC_VID 0x3904
+#define ARCTIC_PID 0xF001
+#define ARCTIC_NUM_FANS 10
+#define ARCTIC_OUTPUT_REPORT_ID 0x01
+#define ARCTIC_REPORT_LEN 32
+#define ARCTIC_RPM_OFFSET 11 /* bytes 11-30: 10 x uint16 LE */
+/* ACK report: device sends Report ID 0x02, 2 bytes (ID + status) after applying OUT report */
+#define ARCTIC_ACK_REPORT_ID 0x02
+#define ARCTIC_ACK_REPORT_LEN 2
+/*
+ * Time to wait for ACK report after send.
+ * Measured over 500 iterations: max ~563 ms. Keep 1 s as margin.
+ */
+#define ARCTIC_ACK_TIMEOUT_MS 1000
+
+struct arctic_fan_data {
+ struct hid_device *hdev;
+ spinlock_t in_report_lock; /* protects fan_rpm[], ack_status, write_pending */
+ struct completion in_report_received; /* ACK (ID 0x02) received in raw_event */
+ int ack_status; /* 0 = OK, negative errno on device error */
+ bool write_pending; /* true while an OUT report ACK is in flight */
+ u32 fan_rpm[ARCTIC_NUM_FANS];
+ u8 pwm_duty[ARCTIC_NUM_FANS]; /* 0-255 matching sysfs range; converted to 0-100 on send */
+ /*
+ * OUT report buffer. Cache-line aligned so it occupies its own cache
+ * line, preventing DMA cache-coherency issues with adjacent fields
+ * (fan_rpm[], pwm_duty[]) on non-coherent architectures.
+ * Embedded in the devm_kzalloc'd struct so it is heap-allocated and
+ * passes usb_hcd_map_urb_for_dma(). Serialized by the hwmon core.
+ */
+ u8 buf[ARCTIC_REPORT_LEN] ____cacheline_aligned;
+};
+
+/*
+ * Parse RPM values from the periodic status report (10 x uint16 LE at rpm_off).
+ * pwm_duty is not updated from the report: the device is manual-only, so the
+ * host cache is the authoritative source for PWM.
+ * Called from raw_event which may run in IRQ context; must not sleep.
+ */
+static void arctic_fan_parse_report(struct arctic_fan_data *priv, u8 *buf,
+ int len, int rpm_off)
+{
+ unsigned long flags;
+ int i;
+
+ if (len < rpm_off + 20)
+ return;
+
+ spin_lock_irqsave(&priv->in_report_lock, flags);
+ for (i = 0; i < ARCTIC_NUM_FANS; i++)
+ priv->fan_rpm[i] = get_unaligned_le16(&buf[rpm_off + i * 2]);
+ spin_unlock_irqrestore(&priv->in_report_lock, flags);
+}
+
+/*
+ * raw_event: IN reports.
+ *
+ * Status report: Report ID 0x01, 32 bytes:
+ * byte 0 = report ID, bytes 1-10 = PWM 0-100%, bytes 11-30 = 10 x RPM uint16 LE.
+ * Device pushes these at ~1 Hz; no GET_REPORT.
+ *
+ * ACK report: Report ID 0x02, 2 bytes:
+ * byte 0 = 0x02, byte 1 = status (0x00 = OK, 0x01 = ERROR).
+ * Sent once after accepting and applying an OUT report (ID 0x01).
+ */
+static int arctic_fan_raw_event(struct hid_device *hdev,
+ struct hid_report *report, u8 *data, int size)
+{
+ struct arctic_fan_data *priv = hid_get_drvdata(hdev);
+ unsigned long flags;
+
+ hid_dbg(hdev, "arctic_fan: raw_event id=%u size=%d\n", report->id, size);
+
+ if (report->id == ARCTIC_ACK_REPORT_ID && size == ARCTIC_ACK_REPORT_LEN) {
+ spin_lock_irqsave(&priv->in_report_lock, flags);
+ /*
+ * Only deliver if a write is in flight. This prevents a
+ * late-arriving ACK from a timed-out write from erroneously
+ * satisfying a subsequent write's completion wait.
+ */
+ if (priv->write_pending) {
+ priv->ack_status = data[1] == 0x00 ? 0 : -EIO;
+ complete(&priv->in_report_received);
+ }
+ spin_unlock_irqrestore(&priv->in_report_lock, flags);
+ return 0;
+ }
+
+ if (report->id != ARCTIC_OUTPUT_REPORT_ID || size != ARCTIC_REPORT_LEN) {
+ hid_dbg(hdev, "arctic_fan: raw_event id=%u size=%d ignored\n",
+ report->id, size);
+ return 0;
+ }
+
+ arctic_fan_parse_report(priv, data, size, ARCTIC_RPM_OFFSET);
+ return 0;
+}
+
+static umode_t arctic_fan_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ if (type == hwmon_fan && attr == hwmon_fan_input)
+ return 0444;
+ if (type == hwmon_pwm && attr == hwmon_pwm_input)
+ return 0644;
+ return 0;
+}
+
+static int arctic_fan_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct arctic_fan_data *priv = dev_get_drvdata(dev);
+ unsigned long flags;
+
+ if (type == hwmon_fan && attr == hwmon_fan_input) {
+ spin_lock_irqsave(&priv->in_report_lock, flags);
+ *val = priv->fan_rpm[channel];
+ spin_unlock_irqrestore(&priv->in_report_lock, flags);
+ return 0;
+ }
+ if (type == hwmon_pwm && attr == hwmon_pwm_input) {
+ /* pwm_duty is modified only in write(), which the hwmon core serializes */
+ *val = priv->pwm_duty[channel];
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int arctic_fan_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ struct arctic_fan_data *priv = dev_get_drvdata(dev);
+ u8 new_duty = (u8)clamp_val(val, 0, 255);
+ unsigned long flags;
+ unsigned long t;
+ int i, ret;
+
+ /*
+ * The hwmon core holds its lock for the duration of this callback,
+ * serializing concurrent writes. priv->buf is heap-allocated (embedded
+ * in the devm_kzalloc'd struct), satisfying usb_hcd_map_urb_for_dma().
+ *
+ * Use new_duty for the target channel and the cached value for all
+ * others. pwm_duty[channel] is committed only after a positive device
+ * ACK so a failed or timed-out write does not corrupt the cached state.
+ */
+ priv->buf[0] = ARCTIC_OUTPUT_REPORT_ID;
+ for (i = 0; i < ARCTIC_NUM_FANS; i++) {
+ u8 d = i == channel ? new_duty : priv->pwm_duty[i];
+
+ priv->buf[1 + i] = DIV_ROUND_CLOSEST((unsigned int)d * 100, 255);
+ }
+
+ /*
+ * Set write_pending before sending the report and clear it in every
+ * exit path. raw_event() calls complete() only when write_pending is
+ * true, so a stale ACK from a timed-out write cannot satisfy a
+ * subsequent write's completion wait. The wait is non-interruptible
+ * to prevent the same race in the signal-interrupted case.
+ * Serialized by the hwmon core: only one arctic_fan_write() at a time.
+ * Use irqsave to match the IRQ context in which raw_event may run.
+ */
+ spin_lock_irqsave(&priv->in_report_lock, flags);
+ priv->ack_status = -ETIMEDOUT;
+ priv->write_pending = true;
+ reinit_completion(&priv->in_report_received);
+ spin_unlock_irqrestore(&priv->in_report_lock, flags);
+
+ ret = hid_hw_output_report(priv->hdev, priv->buf, ARCTIC_REPORT_LEN);
+ if (ret < 0) {
+ spin_lock_irqsave(&priv->in_report_lock, flags);
+ priv->write_pending = false;
+ spin_unlock_irqrestore(&priv->in_report_lock, flags);
+ return ret;
+ }
+
+ t = wait_for_completion_timeout(&priv->in_report_received,
+ msecs_to_jiffies(ARCTIC_ACK_TIMEOUT_MS));
+ spin_lock_irqsave(&priv->in_report_lock, flags);
+ priv->write_pending = false;
+ spin_unlock_irqrestore(&priv->in_report_lock, flags);
+
+ if (!t)
+ return -ETIMEDOUT;
+
+ /* Commit the new duty only after hardware has positively acknowledged it */
+ if (priv->ack_status == 0)
+ priv->pwm_duty[channel] = new_duty;
+ return priv->ack_status; /* 0=OK, -EIO=device error */
+}
+
+static const struct hwmon_ops arctic_fan_ops = {
+ .is_visible = arctic_fan_is_visible,
+ .read = arctic_fan_read,
+ .write = arctic_fan_write,
+};
+
+static const struct hwmon_channel_info *arctic_fan_info[] = {
+ HWMON_CHANNEL_INFO(fan,
+ HWMON_F_INPUT, HWMON_F_INPUT, HWMON_F_INPUT,
+ HWMON_F_INPUT, HWMON_F_INPUT, HWMON_F_INPUT,
+ HWMON_F_INPUT, HWMON_F_INPUT, HWMON_F_INPUT,
+ HWMON_F_INPUT),
+ HWMON_CHANNEL_INFO(pwm,
+ HWMON_PWM_INPUT, HWMON_PWM_INPUT, HWMON_PWM_INPUT,
+ HWMON_PWM_INPUT, HWMON_PWM_INPUT, HWMON_PWM_INPUT,
+ HWMON_PWM_INPUT, HWMON_PWM_INPUT, HWMON_PWM_INPUT,
+ HWMON_PWM_INPUT),
+ NULL
+};
+
+static const struct hwmon_chip_info arctic_fan_chip_info = {
+ .ops = &arctic_fan_ops,
+ .info = arctic_fan_info,
+};
+
+static int arctic_fan_reset_resume(struct hid_device *hdev)
+{
+ struct arctic_fan_data *priv = hid_get_drvdata(hdev);
+
+ /*
+ * The device resets its PWM channels to hardware defaults on power
+ * loss during suspend. Clear the cached duty values so they reflect
+ * the unknown hardware state, consistent with probe-time behaviour
+ * (the device has no GET_REPORT support).
+ */
+ memset(priv->pwm_duty, 0, sizeof(priv->pwm_duty));
+ return 0;
+}
+
+static int arctic_fan_probe(struct hid_device *hdev,
+ const struct hid_device_id *id)
+{
+ struct arctic_fan_data *priv;
+ struct device *hwmon_dev;
+ int ret;
+
+ if (!hid_is_usb(hdev))
+ return -ENODEV;
+
+ ret = hid_parse(hdev);
+ if (ret)
+ return ret;
+
+ priv = devm_kzalloc(&hdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->hdev = hdev;
+ spin_lock_init(&priv->in_report_lock);
+ init_completion(&priv->in_report_received);
+ hid_set_drvdata(hdev, priv);
+
+ ret = hid_hw_start(hdev, HID_CONNECT_DRIVER);
+ if (ret)
+ return ret;
+
+ ret = hid_hw_open(hdev);
+ if (ret)
+ goto out_stop;
+
+ /*
+ * Start IO before registering with hwmon. If IO were started after
+ * hwmon registration, a sysfs write arriving in that narrow window
+ * would send an OUT report but the ACK could not be delivered (the HID
+ * core discards events until io_started), causing a spurious timeout.
+ */
+ hid_device_io_start(hdev);
+
+ hwmon_dev = devm_hwmon_device_register_with_info(&hdev->dev, "arctic_fan",
+ priv, &arctic_fan_chip_info,
+ NULL);
+ if (IS_ERR(hwmon_dev)) {
+ ret = PTR_ERR(hwmon_dev);
+ goto out_close;
+ }
+
+ return 0;
+
+out_close:
+ hid_device_io_stop(hdev);
+ hid_hw_close(hdev);
+out_stop:
+ hid_hw_stop(hdev);
+ return ret;
+}
+
+static void arctic_fan_remove(struct hid_device *hdev)
+{
+ hid_device_io_stop(hdev);
+ hid_hw_close(hdev);
+ hid_hw_stop(hdev);
+}
+
+static const struct hid_device_id arctic_fan_id_table[] = {
+ { HID_USB_DEVICE(ARCTIC_VID, ARCTIC_PID) },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, arctic_fan_id_table);
+
+static struct hid_driver arctic_fan_driver = {
+ .name = "arctic_fan",
+ .id_table = arctic_fan_id_table,
+ .probe = arctic_fan_probe,
+ .remove = arctic_fan_remove,
+ .raw_event = arctic_fan_raw_event,
+ .reset_resume = arctic_fan_reset_resume,
+};
+
+module_hid_driver(arctic_fan_driver);
+
+MODULE_AUTHOR("Aureo Serrano de Souza <aureo.serrano@arctic.de>");
+MODULE_DESCRIPTION("HID hwmon driver for ARCTIC Fan Controller");
+MODULE_LICENSE("GPL");
^ permalink raw reply related
* Re: [PATCH v2 0/2] module: expose imported namespaces via sysfs
From: Sami Tolvanen @ 2026-03-20 17:45 UTC (permalink / raw)
To: Luis Chamberlain, Petr Pavlu, Daniel Gomez, Nicholas Sielicki
Cc: Aaron Tomlin, Matthias Maennich, Peter Zijlstra, Jonathan Corbet,
Shuah Khan, Randy Dunlap, linux-modules, linux-doc, linux-kernel
In-Reply-To: <20260307090010.20828-1-linux@opensource.nslick.com>
On Sat, 07 Mar 2026 03:00:08 -0600, Nicholas Sielicki wrote:
> Add /sys/module/*/import_ns to expose the symbol namespaces imported
> by a loaded module.
>
> Changes since v1:
> - Simplified commit message to drop unnecessary/incorrect background
> - Use .setup/.free callbacks in module_attribute to ensure
> imported_namespaces is NULL-initialized before error paths and
> NULL'd after kfree (Sami)
> - Updated KernelVersion to 7.1 in docs for next merge window
>
> [...]
Applied to modules-next, thanks!
[1/2] module: expose imported namespaces via sysfs
commit: 3fe1dcbc2d20c5dbc581c0bb458e05365bfffcf7
[2/2] docs: symbol-namespaces: mention sysfs attribute
commit: f15dbe8a94b6e3768b10e10bf8ab95b28682db80
Best regards,
Sami
^ permalink raw reply
* Re: [PATCH v3 3/3] ima: Add support for staging measurements for deletion
From: Roberto Sassu @ 2026-03-20 17:26 UTC (permalink / raw)
To: steven chen, Mimi Zohar, corbet, skhan, dmitry.kasatkin,
eric.snowberg, paul, jmorris, serge
Cc: linux-doc, linux-kernel, linux-integrity, linux-security-module,
gregorylumen, nramas, Roberto Sassu
In-Reply-To: <8f66014c-d7c8-4a33-be7b-cfd945af4a3a@linux.microsoft.com>
On Fri, 2026-03-20 at 10:24 -0700, steven chen wrote:
> On 3/20/2026 10:10 AM, Roberto Sassu wrote:
> > On Fri, 2026-03-20 at 09:58 -0700, steven chen wrote:
> > > On 3/20/2026 5:41 AM, Mimi Zohar wrote:
> > > > On Thu, 2026-03-19 at 14:31 -0700, steven chen wrote:
> > > >
> > > > > > - Support for deleting N measurement records (and pre-pending the remaining
> > > > > > measurement records)
> > > > > Is there any problem to bring work of "stage" step together to the
> > > > > deletion step?
> > > > >
> > > > > "Trim N" method does everything that "staged" method can do, right?
> > > > > what's the "stage" method can do but "trim N" method can't do?
> > > > >
> > > > > in user space, if in "staged" state, no other user space agent can
> > > > > access the IMA measure list, right?
> > > > >
> > > > > Could you explain the benefit of bringing the "stage" step?
> > > > The performance improvement is because "staging" the IMA measurement list takes
> > > > the lock in order to move the measurement list pointer and then releases it.
> > > > New measurements can then be appended to a new measurement list. Deleting
> > > > records is done without taking the lock to walk the staged measurement list.
> > > >
> > > > Without staging the measurement list, walking the measurement list to trim N
> > > > records requires taking and holding the lock. The performance is dependent on
> > > > the size of the measurement list.
> > > >
> > > > Your question isn't really about "staging" the measurement list records, but
> > > > requiring a userspace signal to delete them. To answer that question, deleting
> > > > N records (third patch) could imply staging all the measurement records and
> > > > immediately deleting N records without an explicit userspace signal.
> > > >
> > > > I expect the requested "documentation" patch will provide the motivation for the
> > > > delayed deletion of the measurement list.
> > > >
> > > > Mimi
> > > "Staging" is great on reducing kernel IMA measurement list locking time.
> > >
> > > How about just do "stage N" entries and then delete the staged list in
> > > one shot?
> > > It means merge two APIs into one API
> > > int ima_queue_stage(void)
> > > int ima_queue_delete_staged(unsigned long req_value)
> > >
> > > The kernel lock time will be the same. And user space lock time will be
> > > reduced.
> > It is not the same. The walk on the staged list is done without holding
> > ima_extend_list_mutex.
> >
> > Roberto
>
> Is it possible to merge two APIs work into one API?
> int ima_queue_stage(void)
> int ima_queue_delete_staged(unsigned long req_value)
It will be done transparently for the user. IMA will call both
functions for the same securityfs write.
Roberto
^ permalink raw reply
* Re: [PATCH v3 3/3] ima: Add support for staging measurements for deletion
From: steven chen @ 2026-03-20 17:40 UTC (permalink / raw)
To: Roberto Sassu, Mimi Zohar, corbet, skhan, dmitry.kasatkin,
eric.snowberg, paul, jmorris, serge
Cc: linux-doc, linux-kernel, linux-integrity, linux-security-module,
gregorylumen, nramas, Roberto Sassu
In-Reply-To: <a523c0cf05e10838bf08e4d2e9a05df402f4c9b0.camel@huaweicloud.com>
On 3/20/2026 10:26 AM, Roberto Sassu wrote:
> On Fri, 2026-03-20 at 10:24 -0700, steven chen wrote:
>> On 3/20/2026 10:10 AM, Roberto Sassu wrote:
>>> On Fri, 2026-03-20 at 09:58 -0700, steven chen wrote:
>>>> On 3/20/2026 5:41 AM, Mimi Zohar wrote:
>>>>> On Thu, 2026-03-19 at 14:31 -0700, steven chen wrote:
>>>>>
>>>>>>> - Support for deleting N measurement records (and pre-pending the remaining
>>>>>>> measurement records)
>>>>>> Is there any problem to bring work of "stage" step together to the
>>>>>> deletion step?
>>>>>>
>>>>>> "Trim N" method does everything that "staged" method can do, right?
>>>>>> what's the "stage" method can do but "trim N" method can't do?
>>>>>>
>>>>>> in user space, if in "staged" state, no other user space agent can
>>>>>> access the IMA measure list, right?
>>>>>>
>>>>>> Could you explain the benefit of bringing the "stage" step?
>>>>> The performance improvement is because "staging" the IMA measurement list takes
>>>>> the lock in order to move the measurement list pointer and then releases it.
>>>>> New measurements can then be appended to a new measurement list. Deleting
>>>>> records is done without taking the lock to walk the staged measurement list.
>>>>>
>>>>> Without staging the measurement list, walking the measurement list to trim N
>>>>> records requires taking and holding the lock. The performance is dependent on
>>>>> the size of the measurement list.
>>>>>
>>>>> Your question isn't really about "staging" the measurement list records, but
>>>>> requiring a userspace signal to delete them. To answer that question, deleting
>>>>> N records (third patch) could imply staging all the measurement records and
>>>>> immediately deleting N records without an explicit userspace signal.
>>>>>
>>>>> I expect the requested "documentation" patch will provide the motivation for the
>>>>> delayed deletion of the measurement list.
>>>>>
>>>>> Mimi
>>>> "Staging" is great on reducing kernel IMA measurement list locking time.
>>>>
>>>> How about just do "stage N" entries and then delete the staged list in
>>>> one shot?
>>>> It means merge two APIs into one API
>>>> int ima_queue_stage(void)
>>>> int ima_queue_delete_staged(unsigned long req_value)
>>>>
>>>> The kernel lock time will be the same. And user space lock time will be
>>>> reduced.
>>> It is not the same. The walk on the staged list is done without holding
>>> ima_extend_list_mutex.
>>>
>>> Roberto
>> Is it possible to merge two APIs work into one API?
>> int ima_queue_stage(void)
>> int ima_queue_delete_staged(unsigned long req_value)
> It will be done transparently for the user. IMA will call both
> functions for the same securityfs write.
>
> Roberto
If merge two APIs into one API, it will reduce user space measurement
list lock time.
Thanks,
Steven
^ permalink raw reply
* Re: [PATCH v2 08/10] gpu: nova-core: convert falcon registers to kernel register macro
From: Joel Fernandes @ 2026-03-20 17:38 UTC (permalink / raw)
To: Alexandre Courbot, Danilo Krummrich, 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
Cc: John Hubbard, Alistair Popple, Timur Tabi, Zhi Wang,
Eliot Courtney, dri-devel, linux-kernel, linux-riscv, linux-doc,
rust-for-linux
In-Reply-To: <20260320-b4-nova-register-v2-8-88fcf103e8d4@nvidia.com>
Hi Alex,
On 3/20/2026 8:19 AM, Alexandre Courbot wrote:
> /// Reset the controller, select the falcon core, and wait for memory scrubbing to complete.
> @@ -616,9 +462,10 @@ pub(crate) fn reset(&self, bar: &Bar0) -> Result {
> self.hal.select_core(self, bar)?;
> self.hal.reset_wait_mem_scrubbing(bar)?;
>
> - regs::NV_PFALCON_FALCON_RM::default()
> - .set_value(bar.read(regs::NV_PMC_BOOT_0).into())
> - .write(bar, &E::ID);
> + bar.write(
> + WithBase::of::<E>(),
> + regs::NV_PFALCON_FALCON_RM::from(bar.read(regs::NV_PMC_BOOT_0).into_raw()),
> + );
>
Overall, I think the series is good improvement but I still feel this part is a
step back in readability, and we should come up with something better. I don't
think there's any chance anyone can memorize this syntax.
What about using a macro to hide the boilerplate?
> Ok(())
> }
> @@ -636,25 +483,27 @@ fn pio_wr_imem_slice(&self, bar: &Bar0, load_offsets: FalconPioImemLoadTarget<'_
> return Err(EINVAL);
> }
>
> - regs::NV_PFALCON_FALCON_IMEMC::default()
> - .set_secure(load_offsets.secure)
> - .set_aincw(true)
> - .set_offs(load_offsets.dst_start)
> - .write(bar, &E::ID, Self::PIO_PORT);
> + bar.write(
> + WithBase::of::<E>().at(Self::PIO_PORT),
> + regs::NV_PFALCON_FALCON_IMEMC::zeroed()
> + .with_secure(load_offsets.secure)
> + .with_aincw(true)
> + .with_offs(load_offsets.dst_start),
> + );
And bare minimum, probably the inner:
> + regs::NV_PFALCON_FALCON_IMEMC::zeroed()
> + .with_secure(load_offsets.secure)
> + .with_aincw(true)
> + .with_offs(load_offsets.dst_start),
Should be assigned to a separate variable for readability. I think otherwise it
is quite unbeatable.
thanks,
--
Joel Fernandes
^ permalink raw reply
* Re: [PATCH v2 06/10] gpu: nova-core: convert PDISP registers to kernel register macro
From: Joel Fernandes @ 2026-03-20 17:33 UTC (permalink / raw)
To: Alexandre Courbot, Danilo Krummrich, 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
Cc: John Hubbard, Alistair Popple, Timur Tabi, Zhi Wang,
Eliot Courtney, dri-devel, linux-kernel, linux-riscv, linux-doc,
rust-for-linux
In-Reply-To: <20260320-b4-nova-register-v2-6-88fcf103e8d4@nvidia.com>
On 3/20/2026 8:19 AM, Alexandre Courbot wrote:
> Convert all PDISP registers to use the kernel's register macro and
> update the code accordingly.
>
> Reviewed-by: Eliot Courtney <ecourtney@nvidia.com>
> Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
> ---
> drivers/gpu/nova-core/fb.rs | 6 +++++-
> drivers/gpu/nova-core/regs.rs | 12 ++++++++----
> 2 files changed, 13 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/gpu/nova-core/fb.rs b/drivers/gpu/nova-core/fb.rs
> index 6536d0035cb1..62fc90fa6a84 100644
> --- a/drivers/gpu/nova-core/fb.rs
> +++ b/drivers/gpu/nova-core/fb.rs
> @@ -8,6 +8,7 @@
> use kernel::{
> device,
> fmt,
> + io::Io,
> prelude::*,
> ptr::{
> Alignable,
> @@ -189,7 +190,10 @@ pub(crate) fn new(chipset: Chipset, bar: &Bar0, gsp_fw: &GspFirmware) -> Result<
> let base = fb.end - NV_PRAMIN_SIZE;
>
> if hal.supports_display(bar) {
> - match regs::NV_PDISP_VGA_WORKSPACE_BASE::read(bar).vga_workspace_addr() {
> + match bar
> + .read(regs::NV_PDISP_VGA_WORKSPACE_BASE)
> + .vga_workspace_addr()
> + {
> Some(addr) => {
> if addr < base {
> const VBIOS_WORKSPACE_SIZE: u64 = usize_as_u64(SZ_128K);
> diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
> index 61a8dba22d88..b051d5568cd8 100644
> --- a/drivers/gpu/nova-core/regs.rs
> +++ b/drivers/gpu/nova-core/regs.rs
> @@ -250,10 +250,14 @@ pub(crate) fn usable_fb_size(self) -> u64 {
>
> // PDISP
>
> -register!(NV_PDISP_VGA_WORKSPACE_BASE @ 0x00625f04 {
> - 3:3 status_valid as bool, "Set if the `addr` field is valid";
> - 31:8 addr as u32, "VGA workspace base address divided by 0x10000";
> -});
> +io::register! {
> + pub(crate) NV_PDISP_VGA_WORKSPACE_BASE(u32) @ 0x00625f04 {
> + /// VGA workspace base address divided by 0x10000.
> + 31:8 addr;
> + /// Set if the `addr` field is valid.
> + 3:3 status_valid => bool;
> + }
> +}
Shouldn't this re-ordering of bit ranges be a separate patch? Also, what did we
conclude on the ordering issue? I remember this was discussed, but I am not sure
what the conclusion was.
Other than that,
Reviewed-by: Joel Fernandes <joelagnelf@nvidia.com>
--
Joel Fernandes
^ permalink raw reply
* Re: [PATCH v3 03/16] mm: document vm_operations_struct->open the same as close()
From: Vlastimil Babka (SUSE) @ 2026-03-20 17:30 UTC (permalink / raw)
To: Lorenzo Stoakes (Oracle), Andrew Morton
Cc: Jonathan Corbet, Clemens Ladisch, Arnd Bergmann,
Greg Kroah-Hartman, K . Y . Srinivasan, Haiyang Zhang, Wei Liu,
Dexuan Cui, Long Li, Alexander Shishkin, Maxime Coquelin,
Alexandre Torgue, Miquel Raynal, Richard Weinberger,
Vignesh Raghavendra, Bodo Stroesser, Martin K . Petersen,
David Howells, Marc Dionne, Alexander Viro, Christian Brauner,
Jan Kara, David Hildenbrand, Liam R . Howlett, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Jann Horn, Pedro Falcato,
linux-kernel, linux-doc, linux-hyperv, linux-mtd, linux-staging,
linux-scsi, target-devel, linux-afs, linux-fsdevel, linux-mm,
Ryan Roberts
In-Reply-To: <808919eaae0b682ec631301b3c06d85c62ba428d.1773944114.git.ljs@kernel.org>
On 3/19/26 19:23, Lorenzo Stoakes (Oracle) wrote:
> Describe when the operation is invoked and the context in which it is
> invoked, matching the description already added for vm_op->close().
>
> While we're here, update all outdated references to an 'area' field for
> VMAs to the more consistent 'vma'.
>
> Signed-off-by: Lorenzo Stoakes (Oracle) <ljs@kernel.org>
Acked-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>
^ permalink raw reply
* Re: [PATCH v3 3/3] ima: Add support for staging measurements for deletion
From: Roberto Sassu @ 2026-03-20 17:10 UTC (permalink / raw)
To: steven chen, Mimi Zohar, corbet, skhan, dmitry.kasatkin,
eric.snowberg, paul, jmorris, serge
Cc: linux-doc, linux-kernel, linux-integrity, linux-security-module,
gregorylumen, nramas, Roberto Sassu
In-Reply-To: <c9258708-2db2-4c08-998f-e67a681781da@linux.microsoft.com>
On Fri, 2026-03-20 at 09:58 -0700, steven chen wrote:
> On 3/20/2026 5:41 AM, Mimi Zohar wrote:
> > On Thu, 2026-03-19 at 14:31 -0700, steven chen wrote:
> >
> > > > - Support for deleting N measurement records (and pre-pending the remaining
> > > > measurement records)
> > > Is there any problem to bring work of "stage" step together to the
> > > deletion step?
> > >
> > > "Trim N" method does everything that "staged" method can do, right?
> > > what's the "stage" method can do but "trim N" method can't do?
> > >
> > > in user space, if in "staged" state, no other user space agent can
> > > access the IMA measure list, right?
> > >
> > > Could you explain the benefit of bringing the "stage" step?
> > The performance improvement is because "staging" the IMA measurement list takes
> > the lock in order to move the measurement list pointer and then releases it.
> > New measurements can then be appended to a new measurement list. Deleting
> > records is done without taking the lock to walk the staged measurement list.
> >
> > Without staging the measurement list, walking the measurement list to trim N
> > records requires taking and holding the lock. The performance is dependent on
> > the size of the measurement list.
> >
> > Your question isn't really about "staging" the measurement list records, but
> > requiring a userspace signal to delete them. To answer that question, deleting
> > N records (third patch) could imply staging all the measurement records and
> > immediately deleting N records without an explicit userspace signal.
> >
> > I expect the requested "documentation" patch will provide the motivation for the
> > delayed deletion of the measurement list.
> >
> > Mimi
>
> "Staging" is great on reducing kernel IMA measurement list locking time.
>
> How about just do "stage N" entries and then delete the staged list in
> one shot?
> It means merge two APIs into one API
> int ima_queue_stage(void)
> int ima_queue_delete_staged(unsigned long req_value)
>
> The kernel lock time will be the same. And user space lock time will be
> reduced.
It is not the same. The walk on the staged list is done without holding
ima_extend_list_mutex.
Roberto
^ permalink raw reply
* Re: [PATCH v3 3/3] ima: Add support for staging measurements for deletion
From: steven chen @ 2026-03-20 17:24 UTC (permalink / raw)
To: Roberto Sassu, Mimi Zohar, corbet, skhan, dmitry.kasatkin,
eric.snowberg, paul, jmorris, serge
Cc: linux-doc, linux-kernel, linux-integrity, linux-security-module,
gregorylumen, nramas, Roberto Sassu, steven chen
In-Reply-To: <332fc1447c03893988620189a40501cccaa8b4c5.camel@huaweicloud.com>
On 3/20/2026 10:10 AM, Roberto Sassu wrote:
> On Fri, 2026-03-20 at 09:58 -0700, steven chen wrote:
>> On 3/20/2026 5:41 AM, Mimi Zohar wrote:
>>> On Thu, 2026-03-19 at 14:31 -0700, steven chen wrote:
>>>
>>>>> - Support for deleting N measurement records (and pre-pending the remaining
>>>>> measurement records)
>>>> Is there any problem to bring work of "stage" step together to the
>>>> deletion step?
>>>>
>>>> "Trim N" method does everything that "staged" method can do, right?
>>>> what's the "stage" method can do but "trim N" method can't do?
>>>>
>>>> in user space, if in "staged" state, no other user space agent can
>>>> access the IMA measure list, right?
>>>>
>>>> Could you explain the benefit of bringing the "stage" step?
>>> The performance improvement is because "staging" the IMA measurement list takes
>>> the lock in order to move the measurement list pointer and then releases it.
>>> New measurements can then be appended to a new measurement list. Deleting
>>> records is done without taking the lock to walk the staged measurement list.
>>>
>>> Without staging the measurement list, walking the measurement list to trim N
>>> records requires taking and holding the lock. The performance is dependent on
>>> the size of the measurement list.
>>>
>>> Your question isn't really about "staging" the measurement list records, but
>>> requiring a userspace signal to delete them. To answer that question, deleting
>>> N records (third patch) could imply staging all the measurement records and
>>> immediately deleting N records without an explicit userspace signal.
>>>
>>> I expect the requested "documentation" patch will provide the motivation for the
>>> delayed deletion of the measurement list.
>>>
>>> Mimi
>> "Staging" is great on reducing kernel IMA measurement list locking time.
>>
>> How about just do "stage N" entries and then delete the staged list in
>> one shot?
>> It means merge two APIs into one API
>> int ima_queue_stage(void)
>> int ima_queue_delete_staged(unsigned long req_value)
>>
>> The kernel lock time will be the same. And user space lock time will be
>> reduced.
> It is not the same. The walk on the staged list is done without holding
> ima_extend_list_mutex.
>
> Roberto
Is it possible to merge two APIs work into one API?
int ima_queue_stage(void)
int ima_queue_delete_staged(unsigned long req_value)
Thank,
Steven
^ permalink raw reply
* Re: [PATCH v3 02/16] mm: add documentation for the mmap_prepare file operation callback
From: Vlastimil Babka (SUSE) @ 2026-03-20 17:23 UTC (permalink / raw)
To: Lorenzo Stoakes (Oracle), Andrew Morton
Cc: Jonathan Corbet, Clemens Ladisch, Arnd Bergmann,
Greg Kroah-Hartman, K . Y . Srinivasan, Haiyang Zhang, Wei Liu,
Dexuan Cui, Long Li, Alexander Shishkin, Maxime Coquelin,
Alexandre Torgue, Miquel Raynal, Richard Weinberger,
Vignesh Raghavendra, Bodo Stroesser, Martin K . Petersen,
David Howells, Marc Dionne, Alexander Viro, Christian Brauner,
Jan Kara, David Hildenbrand, Liam R . Howlett, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Jann Horn, Pedro Falcato,
linux-kernel, linux-doc, linux-hyperv, linux-mtd, linux-staging,
linux-scsi, target-devel, linux-afs, linux-fsdevel, linux-mm,
Ryan Roberts
In-Reply-To: <172ef809d9976b067bba4cd9d2b78410c6c6d03d.1773944114.git.ljs@kernel.org>
On 3/19/26 19:23, Lorenzo Stoakes (Oracle) wrote:
> This documentation makes it easier for a driver/file system implementer to
> correctly use this callback.
>
> It covers the fundamentals, whilst intentionally leaving the less lovely
> possible actions one might take undocumented (for instance - the
> success_hook, error_hook fields in mmap_action).
>
> The document also covers the new VMA flags implementation which is the
> only one which will work correctly with mmap_prepare.
>
> Signed-off-by: Lorenzo Stoakes (Oracle) <ljs@kernel.org>
Acked-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>
^ permalink raw reply
* Re: [PATCH RFC v2 1/9] dt-bindings: iio: frequency: add ad9910
From: Conor Dooley @ 2026-03-20 17:14 UTC (permalink / raw)
To: Rodrigo Alencar
Cc: rodrigo.alencar, linux-iio, devicetree, linux-kernel, linux-doc,
Lars-Peter Clausen, Michael Hennerich, Jonathan Cameron,
David Lechner, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel, Jonathan Corbet, Shuah Khan
In-Reply-To: <zi7ifl45h5fu76rlbdubkeq7wa7gtve5wsdruo574gzj5qbfu6@fl6rh3soaj74>
[-- Attachment #1: Type: text/plain, Size: 1320 bytes --]
On Fri, Mar 20, 2026 at 11:21:37AM +0000, Rodrigo Alencar wrote:
> On 26/03/19 05:25PM, Conor Dooley wrote:
> > On Wed, Mar 18, 2026 at 05:56:01PM +0000, Rodrigo Alencar via B4 Relay wrote:
> > > From: Rodrigo Alencar <rodrigo.alencar@analog.com>
> > >
> > > DT-bindings for AD9910, a 1 GSPS DDS with 14-bit DAC. It includes
> > > configurations for clocks, DAC current, reset and basic GPIO control.
> > >
> > > Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
>
> ...
>
> > > +
> > > + clock-names:
> > > + oneOf:
> > > + - items:
> > > + - const: ref_clk
> >
> > s/_clk//, not like it can be anything else!
> >
> > > + - items:
> > > + - const: ref_clk
> > > + - const: sync_in
> > > +
> > > + '#clock-cells':
> > > + const: 1
> > > +
> > > + clock-output-names:
> > > + minItems: 1
> > > + maxItems: 3
> > > + items:
> > > + enum: [ sync_clk, pdclk, sync_out ]
> >
> > I'd say same here, but then you've got some issues with differentiation,
> > so idk.
>
> so I've got the names as they are referred in the device pins in the datasheet
Coming back to this one, ye I think it just is less confusing to keep
the _clk ultimately. This looks good then, I think, modulo the RFC-state
of the series.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH v3 01/16] mm: various small mmap_prepare cleanups
From: Vlastimil Babka (SUSE) @ 2026-03-20 17:10 UTC (permalink / raw)
To: Lorenzo Stoakes (Oracle), Andrew Morton
Cc: Jonathan Corbet, Clemens Ladisch, Arnd Bergmann,
Greg Kroah-Hartman, K . Y . Srinivasan, Haiyang Zhang, Wei Liu,
Dexuan Cui, Long Li, Alexander Shishkin, Maxime Coquelin,
Alexandre Torgue, Miquel Raynal, Richard Weinberger,
Vignesh Raghavendra, Bodo Stroesser, Martin K . Petersen,
David Howells, Marc Dionne, Alexander Viro, Christian Brauner,
Jan Kara, David Hildenbrand, Liam R . Howlett, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Jann Horn, Pedro Falcato,
linux-kernel, linux-doc, linux-hyperv, linux-stm32,
linux-arm-kernel, linux-mtd, linux-staging, linux-scsi,
target-devel, linux-afs, linux-fsdevel, linux-mm, Ryan Roberts
In-Reply-To: <498a579bfbcbb8b0e4a9c39243b4454347f03a46.1773944114.git.ljs@kernel.org>
On 3/19/26 19:23, Lorenzo Stoakes (Oracle) wrote:
> Rather than passing arbitrary fields, pass a vm_area_desc pointer to mmap
> prepare functions to mmap prepare, and an action and vma pointer to mmap
> complete in order to put all the action-specific logic in the function
> actually doing the work.
>
> Additionally, allow mmap prepare functions to return an error so we can
> error out as soon as possible if there is something logically incorrect in
> the input.
>
> Update remap_pfn_range_prepare() to properly check the input range for the
> CoW case.
>
> Also remove io_remap_pfn_range_complete(), as we can simply set up the
> fields correctly in io_remap_pfn_range_prepare() and use
> remap_pfn_range_complete() for this.
>
> While we're here, make remap_pfn_range_prepare_vma() a little neater, and
> pass mmap_action directly to call_action_complete().
>
> Then, update compat_vma_mmap() to perform its logic directly, as
> __compat_vma_map() is not used by anything so we don't need to export it.
>
> Also update compat_vma_mmap() to use vfs_mmap_prepare() rather than
> calling the mmap_prepare op directly.
>
> Finally, update the VMA userland tests to reflect the changes.
>
> Signed-off-by: Lorenzo Stoakes (Oracle) <ljs@kernel.org>
Acked-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>
^ permalink raw reply
* Re: [PATCH v3 3/3] ima: Add support for staging measurements for deletion
From: steven chen @ 2026-03-20 16:58 UTC (permalink / raw)
To: Mimi Zohar, Roberto Sassu, corbet, skhan, dmitry.kasatkin,
eric.snowberg, paul, jmorris, serge
Cc: linux-doc, linux-kernel, linux-integrity, linux-security-module,
gregorylumen, nramas, Roberto Sassu, steven chen
In-Reply-To: <ffe1d4645a66a690892163be8e16c4b5d24a690d.camel@linux.ibm.com>
On 3/20/2026 5:41 AM, Mimi Zohar wrote:
> On Thu, 2026-03-19 at 14:31 -0700, steven chen wrote:
>
>>> - Support for deleting N measurement records (and pre-pending the remaining
>>> measurement records)
>> Is there any problem to bring work of "stage" step together to the
>> deletion step?
>>
>> "Trim N" method does everything that "staged" method can do, right?
>> what's the "stage" method can do but "trim N" method can't do?
>>
>> in user space, if in "staged" state, no other user space agent can
>> access the IMA measure list, right?
>>
>> Could you explain the benefit of bringing the "stage" step?
> The performance improvement is because "staging" the IMA measurement list takes
> the lock in order to move the measurement list pointer and then releases it.
> New measurements can then be appended to a new measurement list. Deleting
> records is done without taking the lock to walk the staged measurement list.
>
> Without staging the measurement list, walking the measurement list to trim N
> records requires taking and holding the lock. The performance is dependent on
> the size of the measurement list.
>
> Your question isn't really about "staging" the measurement list records, but
> requiring a userspace signal to delete them. To answer that question, deleting
> N records (third patch) could imply staging all the measurement records and
> immediately deleting N records without an explicit userspace signal.
>
> I expect the requested "documentation" patch will provide the motivation for the
> delayed deletion of the measurement list.
>
> Mimi
"Staging" is great on reducing kernel IMA measurement list locking time.
How about just do "stage N" entries and then delete the staged list in
one shot?
It means merge two APIs into one API
int ima_queue_stage(void)
int ima_queue_delete_staged(unsigned long req_value)
The kernel lock time will be the same. And user space lock time will be
reduced.
Thanks,
Steven
>
>
>
>
>
^ permalink raw reply
* [PATCH v2 20/20] drm/bridge_connector: Convert to atomic_create_state
From: Maxime Ripard @ 2026-03-20 16:27 UTC (permalink / raw)
To: Maarten Lankhorst, Thomas Zimmermann, David Airlie, Simona Vetter,
Jonathan Corbet, Shuah Khan, Dmitry Baryshkov, Jyri Sarha,
Tomi Valkeinen, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Simon Ser,
Harry Wentland, Melissa Wen, Sebastian Wick, Alex Hung,
Jani Nikula, Rodrigo Vivi, Joonas Lahtinen, Tvrtko Ursulin,
Chen-Yu Tsai, Samuel Holland, Dave Stevenson, Maíra Canal,
Raspberry Pi Kernel Maintenance
Cc: dri-devel, linux-doc, linux-kernel, Daniel Stone, intel-gfx,
intel-xe, linux-arm-kernel, linux-sunxi, Maxime Ripard,
Laurent Pinchart
In-Reply-To: <20260320-drm-mode-config-init-v2-0-c63f1134e76c@kernel.org>
The connector created by drm_bridge_connector only initializes a
pristine state in reset, which is equivalent to that atomic_create_state
would expect. Let's convert to it.
Reviewed-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Signed-off-by: Maxime Ripard <mripard@kernel.org>
---
drivers/gpu/drm/display/drm_bridge_connector.c | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index 929f06b290626d2091418a6377a5230e3c264b60..b640e7d5a065d165a901bd73fc836afaab967dac 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -263,26 +263,33 @@ static void drm_bridge_connector_debugfs_init(struct drm_connector *connector,
if (bridge->funcs->debugfs_init)
bridge->funcs->debugfs_init(bridge, root);
}
}
-static void drm_bridge_connector_reset(struct drm_connector *connector)
+static struct drm_connector_state *
+drm_bridge_connector_create_state(struct drm_connector *connector)
{
struct drm_bridge_connector *bridge_connector =
to_drm_bridge_connector(connector);
+ struct drm_connector_state *conn_state;
+
+ conn_state = drm_atomic_helper_connector_create_state(connector);
+ if (IS_ERR(conn_state))
+ return conn_state;
- drm_atomic_helper_connector_reset(connector);
if (bridge_connector->bridge_hdmi)
__drm_atomic_helper_connector_hdmi_state_init(connector,
- connector->state);
+ conn_state);
+
+ return conn_state;
}
static const struct drm_connector_funcs drm_bridge_connector_funcs = {
- .reset = drm_bridge_connector_reset,
.detect = drm_bridge_connector_detect,
.force = drm_bridge_connector_force,
.fill_modes = drm_helper_probe_single_connector_modes,
+ .atomic_create_state = drm_bridge_connector_create_state,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
.debugfs_init = drm_bridge_connector_debugfs_init,
.oob_hotplug_event = drm_bridge_connector_oob_hotplug_event,
};
--
2.53.0
^ permalink raw reply related
* [PATCH v2 19/20] drm/tidss: Convert to atomic_create_state
From: Maxime Ripard @ 2026-03-20 16:27 UTC (permalink / raw)
To: Maarten Lankhorst, Thomas Zimmermann, David Airlie, Simona Vetter,
Jonathan Corbet, Shuah Khan, Dmitry Baryshkov, Jyri Sarha,
Tomi Valkeinen, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Simon Ser,
Harry Wentland, Melissa Wen, Sebastian Wick, Alex Hung,
Jani Nikula, Rodrigo Vivi, Joonas Lahtinen, Tvrtko Ursulin,
Chen-Yu Tsai, Samuel Holland, Dave Stevenson, Maíra Canal,
Raspberry Pi Kernel Maintenance
Cc: dri-devel, linux-doc, linux-kernel, Daniel Stone, intel-gfx,
intel-xe, linux-arm-kernel, linux-sunxi, Maxime Ripard,
Laurent Pinchart
In-Reply-To: <20260320-drm-mode-config-init-v2-0-c63f1134e76c@kernel.org>
Our driver uses reset to create the various object states, but only
calls the helper that allocate a new state. They are thus strictly
equivalent to the new atomic_create_state helpers, so let's switch to
these.
Reviewed-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Signed-off-by: Maxime Ripard <mripard@kernel.org>
---
drivers/gpu/drm/tidss/tidss_crtc.c | 17 +++++++----------
drivers/gpu/drm/tidss/tidss_plane.c | 2 +-
2 files changed, 8 insertions(+), 11 deletions(-)
diff --git a/drivers/gpu/drm/tidss/tidss_crtc.c b/drivers/gpu/drm/tidss/tidss_crtc.c
index a31c21c5f855ac8a94089dd3908e2510193b7d67..66e3d161c60bc14b2982cff4cdd43030d4086798 100644
--- a/drivers/gpu/drm/tidss/tidss_crtc.c
+++ b/drivers/gpu/drm/tidss/tidss_crtc.c
@@ -355,24 +355,21 @@ static void tidss_crtc_destroy_state(struct drm_crtc *crtc,
__drm_atomic_helper_crtc_destroy_state(&tstate->base);
kfree(tstate);
}
-static void tidss_crtc_reset(struct drm_crtc *crtc)
+static struct drm_crtc_state *tidss_crtc_create_state(struct drm_crtc *crtc)
{
struct tidss_crtc_state *tstate;
- if (crtc->state)
- tidss_crtc_destroy_state(crtc, crtc->state);
-
tstate = kzalloc_obj(*tstate);
- if (!tstate) {
- crtc->state = NULL;
- return;
- }
+ if (!tstate)
+ return ERR_PTR(-ENOMEM);
- __drm_atomic_helper_crtc_reset(crtc, &tstate->base);
+ __drm_atomic_helper_crtc_create_state(crtc, &tstate->base);
+
+ return &tstate->base;
}
static struct drm_crtc_state *tidss_crtc_duplicate_state(struct drm_crtc *crtc)
{
struct tidss_crtc_state *state, *current_state;
@@ -403,14 +400,14 @@ static void tidss_crtc_destroy(struct drm_crtc *crtc)
drm_crtc_cleanup(crtc);
kfree(tcrtc);
}
static const struct drm_crtc_funcs tidss_crtc_funcs = {
- .reset = tidss_crtc_reset,
.destroy = tidss_crtc_destroy,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
+ .atomic_create_state = tidss_crtc_create_state,
.atomic_duplicate_state = tidss_crtc_duplicate_state,
.atomic_destroy_state = tidss_crtc_destroy_state,
.enable_vblank = tidss_crtc_enable_vblank,
.disable_vblank = tidss_crtc_disable_vblank,
};
diff --git a/drivers/gpu/drm/tidss/tidss_plane.c b/drivers/gpu/drm/tidss/tidss_plane.c
index aaa02c851c595aa3781ec2e6741af1999092aa40..518498d4576528a0ec59fd03cf27a87b1b3f1e6e 100644
--- a/drivers/gpu/drm/tidss/tidss_plane.c
+++ b/drivers/gpu/drm/tidss/tidss_plane.c
@@ -176,12 +176,12 @@ static const struct drm_plane_helper_funcs tidss_primary_plane_helper_funcs = {
};
static const struct drm_plane_funcs tidss_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
- .reset = drm_atomic_helper_plane_reset,
.destroy = drm_plane_destroy,
+ .atomic_create_state = drm_atomic_helper_plane_create_state,
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
};
struct tidss_plane *tidss_plane_create(struct tidss_device *tidss,
--
2.53.0
^ permalink raw reply related
* [PATCH v2 18/20] drm/tidss: Drop call to drm_mode_config_reset at probe time
From: Maxime Ripard @ 2026-03-20 16:27 UTC (permalink / raw)
To: Maarten Lankhorst, Thomas Zimmermann, David Airlie, Simona Vetter,
Jonathan Corbet, Shuah Khan, Dmitry Baryshkov, Jyri Sarha,
Tomi Valkeinen, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Simon Ser,
Harry Wentland, Melissa Wen, Sebastian Wick, Alex Hung,
Jani Nikula, Rodrigo Vivi, Joonas Lahtinen, Tvrtko Ursulin,
Chen-Yu Tsai, Samuel Holland, Dave Stevenson, Maíra Canal,
Raspberry Pi Kernel Maintenance
Cc: dri-devel, linux-doc, linux-kernel, Daniel Stone, intel-gfx,
intel-xe, linux-arm-kernel, linux-sunxi, Maxime Ripard
In-Reply-To: <20260320-drm-mode-config-init-v2-0-c63f1134e76c@kernel.org>
Now that we have drm_mode_config_create_state() to create the initial
state called as part of drm_dev_register(), we don't need to call
drm_mode_config_reset() during the driver probe.
Signed-off-by: Maxime Ripard <mripard@kernel.org>
---
drivers/gpu/drm/tidss/tidss_drv.c | 2 --
drivers/gpu/drm/tidss/tidss_kms.c | 2 --
2 files changed, 4 deletions(-)
diff --git a/drivers/gpu/drm/tidss/tidss_drv.c b/drivers/gpu/drm/tidss/tidss_drv.c
index 1c8cc18bc53c3ea3c50368b9f55ab02a0a02fc77..ddd9f428c2a96557ee02040320cbe5f6f0fd6052 100644
--- a/drivers/gpu/drm/tidss/tidss_drv.c
+++ b/drivers/gpu/drm/tidss/tidss_drv.c
@@ -169,12 +169,10 @@ static int tidss_probe(struct platform_device *pdev)
goto err_runtime_suspend;
}
drm_kms_helper_poll_init(ddev);
- drm_mode_config_reset(ddev);
-
ret = drm_dev_register(ddev, 0);
if (ret) {
dev_err(dev, "failed to register DRM device\n");
goto err_irq_uninstall;
}
diff --git a/drivers/gpu/drm/tidss/tidss_kms.c b/drivers/gpu/drm/tidss/tidss_kms.c
index 8bb93194e5ac686050c47f986b8cb6063eae22d3..b4779c09a1bfa5be7010bcd7d4f1379362996d2e 100644
--- a/drivers/gpu/drm/tidss/tidss_kms.c
+++ b/drivers/gpu/drm/tidss/tidss_kms.c
@@ -285,11 +285,9 @@ int tidss_modeset_init(struct tidss_device *tidss)
ret = drm_vblank_init(ddev, tidss->num_crtcs);
if (ret)
return ret;
- drm_mode_config_reset(ddev);
-
dev_dbg(tidss->dev, "%s done\n", __func__);
return 0;
}
--
2.53.0
^ permalink raw reply related
* [PATCH v2 17/20] drm/drv: Drop drm_mode_config_reset() from our skeleton
From: Maxime Ripard @ 2026-03-20 16:27 UTC (permalink / raw)
To: Maarten Lankhorst, Thomas Zimmermann, David Airlie, Simona Vetter,
Jonathan Corbet, Shuah Khan, Dmitry Baryshkov, Jyri Sarha,
Tomi Valkeinen, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Simon Ser,
Harry Wentland, Melissa Wen, Sebastian Wick, Alex Hung,
Jani Nikula, Rodrigo Vivi, Joonas Lahtinen, Tvrtko Ursulin,
Chen-Yu Tsai, Samuel Holland, Dave Stevenson, Maíra Canal,
Raspberry Pi Kernel Maintenance
Cc: dri-devel, linux-doc, linux-kernel, Daniel Stone, intel-gfx,
intel-xe, linux-arm-kernel, linux-sunxi, Maxime Ripard
In-Reply-To: <20260320-drm-mode-config-init-v2-0-c63f1134e76c@kernel.org>
Now that we have drm_mode_config_create_state() to create the initial
state called as part of drm_dev_register(), we can stop recommending
calling drm_mode_config_reset() to perform that initialization in our
skeleton.
Signed-off-by: Maxime Ripard <mripard@kernel.org>
---
drivers/gpu/drm/drm_drv.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 820106d56ab399a39cac56d98662b5ddbcae8ded..83752725f7c63364880225ec1083f19bd00ddcef 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -339,12 +339,10 @@ void drm_minor_release(struct drm_minor *minor)
*
* // Further setup, display pipeline etc
*
* platform_set_drvdata(pdev, drm);
*
- * drm_mode_config_reset(drm);
- *
* ret = drm_dev_register(drm);
* if (ret)
* return ret;
*
* drm_fbdev_{...}_setup(drm, 32);
--
2.53.0
^ permalink raw reply related
* [PATCH v2 16/20] drm/atomic: Drop private obj state allocation
From: Maxime Ripard @ 2026-03-20 16:27 UTC (permalink / raw)
To: Maarten Lankhorst, Thomas Zimmermann, David Airlie, Simona Vetter,
Jonathan Corbet, Shuah Khan, Dmitry Baryshkov, Jyri Sarha,
Tomi Valkeinen, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Simon Ser,
Harry Wentland, Melissa Wen, Sebastian Wick, Alex Hung,
Jani Nikula, Rodrigo Vivi, Joonas Lahtinen, Tvrtko Ursulin,
Chen-Yu Tsai, Samuel Holland, Dave Stevenson, Maíra Canal,
Raspberry Pi Kernel Maintenance
Cc: dri-devel, linux-doc, linux-kernel, Daniel Stone, intel-gfx,
intel-xe, linux-arm-kernel, linux-sunxi, Maxime Ripard,
Laurent Pinchart
In-Reply-To: <20260320-drm-mode-config-init-v2-0-c63f1134e76c@kernel.org>
Now that drm_dev_register() calls drm_mode_config_create_state() for
every modeset driver, the private obj states will be initialized at
driver registration automatically if they haven't already.
Thus, the explicit initial allocation we have in
drm_atomic_private_obj_init() is now redundant, and we can remove it.
Reviewed-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Signed-off-by: Maxime Ripard <mripard@kernel.org>
---
drivers/gpu/drm/drm_atomic.c | 7 -------
1 file changed, 7 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 5aa3f9f0620bd155dd53023e6c76e3883ffaadef..3bd52602fe307a1b82a3b68d82b458e1da8a4f6d 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -995,25 +995,18 @@ static void drm_atomic_plane_print_state(struct drm_printer *p,
*/
int drm_atomic_private_obj_init(struct drm_device *dev,
struct drm_private_obj *obj,
const struct drm_private_state_funcs *funcs)
{
- struct drm_private_state *state;
memset(obj, 0, sizeof(*obj));
drm_modeset_lock_init(&obj->lock);
obj->dev = dev;
obj->funcs = funcs;
list_add_tail(&obj->head, &dev->mode_config.privobj_list);
- state = obj->funcs->atomic_create_state(obj);
- if (IS_ERR(state))
- return PTR_ERR(state);
-
- obj->state = state;
-
return 0;
}
EXPORT_SYMBOL(drm_atomic_private_obj_init);
/**
--
2.53.0
^ permalink raw reply related
* [PATCH v2 15/20] drm/drv: Call drm_mode_config_create_state() by default
From: Maxime Ripard @ 2026-03-20 16:27 UTC (permalink / raw)
To: Maarten Lankhorst, Thomas Zimmermann, David Airlie, Simona Vetter,
Jonathan Corbet, Shuah Khan, Dmitry Baryshkov, Jyri Sarha,
Tomi Valkeinen, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Simon Ser,
Harry Wentland, Melissa Wen, Sebastian Wick, Alex Hung,
Jani Nikula, Rodrigo Vivi, Joonas Lahtinen, Tvrtko Ursulin,
Chen-Yu Tsai, Samuel Holland, Dave Stevenson, Maíra Canal,
Raspberry Pi Kernel Maintenance
Cc: dri-devel, linux-doc, linux-kernel, Daniel Stone, intel-gfx,
intel-xe, linux-arm-kernel, linux-sunxi, Maxime Ripard
In-Reply-To: <20260320-drm-mode-config-init-v2-0-c63f1134e76c@kernel.org>
Almost all drivers, and our documented skeleton, call
drm_mode_config_reset() prior to calling drm_dev_register() to
initialize its DRM object states.
Now that we have drm_mode_config_create_state() to create that initial
state if it doesn't exist, we can call it directly in
drm_dev_register(). That way, we know that the initial atomic state will
always be allocated without any boilerplate.
Signed-off-by: Maxime Ripard <mripard@kernel.org>
---
drivers/gpu/drm/drm_drv.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 2915118436ce8a6640cfb0c59936031990727ed1..820106d56ab399a39cac56d98662b5ddbcae8ded 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -1097,10 +1097,14 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags)
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
ret = drm_modeset_register_all(dev);
if (ret)
goto err_unload;
+
+ ret = drm_mode_config_create_state(dev);
+ if (ret)
+ goto err_unload;
}
drm_panic_register(dev);
drm_client_sysrq_register(dev);
DRM_INFO("Initialized %s %d.%d.%d for %s on minor %d\n",
--
2.53.0
^ permalink raw reply related
* [PATCH v2 14/20] drm/mode-config: Create drm_mode_config_create_state()
From: Maxime Ripard @ 2026-03-20 16:27 UTC (permalink / raw)
To: Maarten Lankhorst, Thomas Zimmermann, David Airlie, Simona Vetter,
Jonathan Corbet, Shuah Khan, Dmitry Baryshkov, Jyri Sarha,
Tomi Valkeinen, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Simon Ser,
Harry Wentland, Melissa Wen, Sebastian Wick, Alex Hung,
Jani Nikula, Rodrigo Vivi, Joonas Lahtinen, Tvrtko Ursulin,
Chen-Yu Tsai, Samuel Holland, Dave Stevenson, Maíra Canal,
Raspberry Pi Kernel Maintenance
Cc: dri-devel, linux-doc, linux-kernel, Daniel Stone, intel-gfx,
intel-xe, linux-arm-kernel, linux-sunxi, Maxime Ripard
In-Reply-To: <20260320-drm-mode-config-init-v2-0-c63f1134e76c@kernel.org>
drm_mode_config_reset() can be used to create the initial state, but
also to return to the initial state, when doing a suspend/resume cycle
for example.
It also affects both the software and the hardware, and drivers can
choose to reset the hardware as well. Most will just create an empty
state and the synchronisation between hardware and software states will
effectively be done when the first commit is done.
That dual role can be harmful, since some objects do need to be
initialized but also need to be preserved across a suspend/resume cycle.
drm_private_obj are such objects for example.
Thus, let's create another helper for drivers to call to initialize
their state when the driver is loaded, so we can make
drm_mode_config_reset() only about handling suspend/resume and similar.
Signed-off-by: Maxime Ripard <mripard@kernel.org>
---
drivers/gpu/drm/drm_atomic.c | 12 +++++-
drivers/gpu/drm/drm_mode_config.c | 87 +++++++++++++++++++++++++++++++++++++++
include/drm/drm_mode_config.h | 1 +
3 files changed, 98 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 253a00f450b0fb1b7eb6ea42b3235fbf9ee78376..5aa3f9f0620bd155dd53023e6c76e3883ffaadef 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -59,12 +59,20 @@
* when preparing the update and kept alive as long as they are active
* in the device.
*
* Their respective lifetimes are:
*
- * - at reset time, the object reset implementation will allocate a new
- * default state and will store it in the object state pointer.
+ * - at driver initialization time, the driver will allocate an initial,
+ * pristine, state and will store it using
+ * drm_mode_config_create_state(). Historically, this was one of
+ * drm_mode_config_reset() job, so one might still encounter it in a
+ * driver.
+ *
+ * - at reset time, for example during suspend/resume,
+ * drm_mode_config_reset() will reset the software and hardware state
+ * to a known default and will store it in the object's state pointer.
+ * Not all objects are affected by drm_mode_config_reset() though.
*
* - whenever a new update is needed:
*
* + A new &struct drm_atomic_state is allocated using
* drm_atomic_state_alloc().
diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c
index 124cef8f0c6eb15c9fde4cf7cee2936cbf2b902f..61c3ba5d3901ea2bb05bbd1db0644187820a44dd 100644
--- a/drivers/gpu/drm/drm_mode_config.c
+++ b/drivers/gpu/drm/drm_mode_config.c
@@ -21,10 +21,11 @@
*/
#include <linux/export.h>
#include <linux/uaccess.h>
+#include <drm/drm_atomic.h>
#include <drm/drm_drv.h>
#include <drm/drm_encoder.h>
#include <drm/drm_file.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_managed.h>
@@ -281,10 +282,96 @@ void drm_mode_config_reset(struct drm_device *dev)
}
drm_connector_list_iter_end(&conn_iter);
}
EXPORT_SYMBOL(drm_mode_config_reset);
+/**
+ * drm_mode_config_create_state - Allocates the initial state
+ * @dev: drm device
+ *
+ * This functions creates the initial state for all the objects. Drivers
+ * can use this in e.g. probe to initialize their software state.
+ *
+ * It has two main differences with drm_mode_config_reset(): the reset()
+ * hooks aren't called and thus the hardware will be left untouched, but
+ * also the @drm_private_obj structures will be initialized as opposed
+ * to drm_mode_config_reset() that skips them.
+ *
+ * Returns: 0 on success, negative error value on failure.
+ */
+int drm_mode_config_create_state(struct drm_device *dev)
+{
+ struct drm_crtc *crtc;
+ struct drm_colorop *colorop;
+ struct drm_plane *plane;
+ struct drm_connector *connector;
+ struct drm_connector_list_iter conn_iter;
+ struct drm_private_obj *privobj;
+ int ret;
+
+ drm_for_each_privobj(privobj, dev) {
+ struct drm_private_state *privobj_state;
+
+ if (privobj->state)
+ continue;
+
+ if (!privobj->funcs->atomic_create_state)
+ continue;
+
+ privobj_state = privobj->funcs->atomic_create_state(privobj);
+ if (IS_ERR(privobj_state))
+ return PTR_ERR(privobj_state);
+
+ privobj->state = privobj_state;
+ }
+
+ drm_for_each_colorop(colorop, dev) {
+ struct drm_colorop_state *colorop_state;
+
+ if (colorop->state)
+ continue;
+
+ colorop_state = drm_atomic_helper_colorop_create_state(colorop);
+ if (IS_ERR(colorop_state))
+ return PTR_ERR(colorop_state);
+
+ colorop->state = colorop_state;
+ }
+
+ drm_for_each_plane(plane, dev) {
+ if (plane->state)
+ continue;
+
+ ret = drm_mode_config_plane_create_state(plane);
+ if (ret)
+ return ret;
+ }
+
+ drm_for_each_crtc(crtc, dev) {
+ if (crtc->state)
+ continue;
+
+ ret = drm_mode_config_crtc_create_state(crtc);
+ if (ret)
+ return ret;
+ }
+
+ drm_connector_list_iter_begin(dev, &conn_iter);
+ drm_for_each_connector_iter(connector, &conn_iter) {
+ if (connector->state)
+ continue;
+
+ ret = drm_mode_config_connector_create_state(connector);
+ if (ret)
+ return ret;
+ }
+ drm_connector_list_iter_end(&conn_iter);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_mode_config_create_state);
+
/*
* Global properties
*/
static const struct drm_prop_enum_list drm_plane_type_enum_list[] = {
{ DRM_PLANE_TYPE_OVERLAY, "Overlay" },
diff --git a/include/drm/drm_mode_config.h b/include/drm/drm_mode_config.h
index 687c0ee163d25dfa7cf563e77209dd33e349800b..22c7e767a2e98f99fff9311eb590f62bd4c9c8a9 100644
--- a/include/drm/drm_mode_config.h
+++ b/include/drm/drm_mode_config.h
@@ -1005,9 +1005,10 @@ int __must_check drmm_mode_config_init(struct drm_device *dev);
static inline int drm_mode_config_init(struct drm_device *dev)
{
return drmm_mode_config_init(dev);
}
+int drm_mode_config_create_state(struct drm_device *dev);
void drm_mode_config_reset(struct drm_device *dev);
void drm_mode_config_cleanup(struct drm_device *dev);
#endif
--
2.53.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox