* Re: [PATCH v6 05/43] KVM: guest_memfd: Wire up kvm_get_memory_attributes() to per-gmem attributes
From: Sean Christopherson @ 2026-05-21 13:31 UTC (permalink / raw)
To: Fuad Tabba
Cc: Ackerley Tng, aik, andrew.jones, binbin.wu, brauner, chao.p.peng,
david, ira.weiny, jmattson, jthoughton, michael.roth, oupton,
pankaj.gupta, qperret, rick.p.edgecombe, rientjes, shivankg,
steven.price, willy, wyihan, yan.y.zhao, forkloop, pratyush,
suzuki.poulose, aneesh.kumar, liam, Paolo Bonzini,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Shuah Khan, Shuah Khan,
Vishal Annapurve, Andrew Morton, Chris Li, Kairui Song,
Kemeng Shi, Nhat Pham, Baoquan He, Barry Song, Axel Rasmussen,
Yuanchu Xie, Wei Xu, Youngjun Park, Qi Zheng, Shakeel Butt,
Kiryl Shutsemau, Jason Gunthorpe, Vlastimil Babka, kvm,
linux-kernel, linux-trace-kernel, linux-doc, linux-kselftest,
linux-mm, linux-coco
In-Reply-To: <CA+EHjTzLCD-dU-euZKgzwyEr2ecPqFDNutcaHm2fCDGA+MHVXA@mail.gmail.com>
On Thu, May 21, 2026, Fuad Tabba wrote:
> On Wed, 20 May 2026 at 22:44, Ackerley Tng <ackerleytng@google.com> wrote:
> >
> > Fuad Tabba <tabba@google.com> writes:
> >
> > >
> > > [...snip...]
> > >
> > >> +unsigned long kvm_gmem_get_memory_attributes(struct kvm *kvm, gfn_t gfn)
> > >> +{
> > >> + struct kvm_memory_slot *slot = gfn_to_memslot(kvm, gfn);
> > >> + struct inode *inode;
> > >> +
> > >> + /*
> > >> + * If this gfn has no associated memslot, there's no chance of the gfn
> > >> + * being backed by private memory, since guest_memfd must be used for
> > >> + * private memory, and guest_memfd must be associated with some memslot.
> > >> + */
> > >> + if (!slot)
> > >> + return 0;
> > >> +
> > >> + CLASS(gmem_get_file, file)(slot);
> > >> + if (!file)
> > >> + return 0;
> > >> +
> > >> + inode = file_inode(file);
> > >> +
> > >> + /*
> > >> + * Rely on the maple tree's internal RCU lock to ensure a
> > >> + * stable result. This result can become stale as soon as the
> > >> + * lock is dropped, so the caller _must_ still protect
> > >> + * consumption of private vs. shared by checking
> > >> + * mmu_invalidate_retry_gfn() under mmu_lock to serialize
> > >> + * against ongoing attribute updates.
> > >> + */
> > >> + return kvm_gmem_get_attributes(inode, kvm_gmem_get_index(slot, gfn));
> > >> +}
> > >
> > > Doesn't this imply that all consumers of kvm_mem_is_private() should
> > > validate the result using mmu_lock and the invalidation sequence?
> >
> > Let me know how I can improve the comment.
>
> Given Sean's context, the comment is good I think. I would quibble
> with the the "_must_ still protect" phrasing being a bit too strict.
>
> Maybe just soften it slightly to acknowledge the exception? Something like:
>
> * lock is dropped, so callers that require a strict result _must_ protect
> * consumption of private vs. shared by checking mmu_invalidate_retry_gfn()
> * under mmu_lock to serialize against ongoing attribute updates. Callers
> * doing lockless reads must be able to tolerate a stale result.
>
> That aligns the comment with how KVM is actually using it today. That
> said, this is nitpicking. Feel free to use or ignore.
Hmm, I wonder if we can figure out a way to consolidate some documentation,
because this is _exactly_ the same pattern that x86's host_pfn_mapping_level()
deals with (see its big comment below).
There's also the stale comment in kvm_invalidate_memslot(), which, stating the
obvious, speaks to the memslot+SRCU side of things.
Maybe it makes sense to to find a central location for one giant comment about
how how MMU notifier events and memslot+SRCU protections work? And then refer
to that in paths where some asset needs to be tied into MMU notifiers and/or
memslots+SRCU?
[*] https://lore.kernel.org/all/agcbWe8s9lmPuJwG@google.com
/*
* Lookup the mapping level for @gfn in the current mm.
*
* WARNING! Use of host_pfn_mapping_level() requires the caller and the end
* consumer to be tied into KVM's handlers for MMU notifier events!
*
* There are several ways to safely use this helper:
*
* - Check mmu_invalidate_retry_gfn() after grabbing the mapping level, before
* consuming it. In this case, mmu_lock doesn't need to be held during the
* lookup, but it does need to be held while checking the MMU notifier.
*
* - Hold mmu_lock AND ensure there is no in-progress MMU notifier invalidation
* event for the hva. This can be done by explicit checking the MMU notifier
* or by ensuring that KVM already has a valid mapping that covers the hva.
*
* - Do not use the result to install new mappings, e.g. use the host mapping
* level only to decide whether or not to zap an entry. In this case, it's
* not required to hold mmu_lock (though it's highly likely the caller will
* want to hold mmu_lock anyways, e.g. to modify SPTEs).
*
* Note! The lookup can still race with modifications to host page tables, but
* the above "rules" ensure KVM will not _consume_ the result of the walk if a
* race with the primary MMU occurs.
*/
^ permalink raw reply
* Re: [PATCH v6 16/43] KVM: guest_memfd: Use actual size for invalidation in kvm_gmem_release()
From: Fuad Tabba @ 2026-05-21 13:29 UTC (permalink / raw)
To: Sean Christopherson
Cc: ackerleytng, aik, andrew.jones, binbin.wu, brauner, chao.p.peng,
david, ira.weiny, jmattson, jthoughton, michael.roth, oupton,
pankaj.gupta, qperret, rick.p.edgecombe, rientjes, shivankg,
steven.price, willy, wyihan, yan.y.zhao, forkloop, pratyush,
suzuki.poulose, aneesh.kumar, liam, Paolo Bonzini,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Shuah Khan, Shuah Khan,
Vishal Annapurve, Andrew Morton, Chris Li, Kairui Song,
Kemeng Shi, Nhat Pham, Baoquan He, Barry Song, Axel Rasmussen,
Yuanchu Xie, Wei Xu, Youngjun Park, Qi Zheng, Shakeel Butt,
Kiryl Shutsemau, Jason Gunthorpe, Vlastimil Babka, kvm,
linux-kernel, linux-trace-kernel, linux-doc, linux-kselftest,
linux-mm, linux-coco
In-Reply-To: <ag8BmtzxTlcuA_zy@google.com>
On Thu, 21 May 2026 at 13:59, Sean Christopherson <seanjc@google.com> wrote:
>
> On Thu, May 21, 2026, Fuad Tabba wrote:
> > Hi Ackerley,
> >
> > On Thu, 7 May 2026 at 21:22, Ackerley Tng via B4 Relay
> > <devnull+ackerleytng.google.com@kernel.org> wrote:
> > >
> > > From: Ackerley Tng <ackerleytng@google.com>
> > >
> > > __kvm_gmem_invalidate_begin() and __kvm_gmem_invalidate_end() actually do
> > > not specially handle -1ul. -1ul is used as a huge number, which legal
> > > indices do not exceed, and hence the invalidation works as expected.
> > >
> > > Since a later patch is going to make use of the exact range, calculate the
> > > size of the guest_memfd inode and use it as the end range for invalidating
> > > SPTEs.
> > >
> > > Signed-off-by: Ackerley Tng <ackerleytng@google.com>
> >
> > Want to look at what Sashiko has to say? Seems to be a real issue:
> >
> > https://sashiko.dev/#/patchset/20260507-gmem-inplace-conversion-v6-0-91ab5a8b19a4%40google.com?part=16
> >
> > If I understand correctly, the fix should simple: use
> > check_add_overflow() to validate the offset and size parameters in
> > kvm_gmem_bind()
> >
> > int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot,
> > unsigned int fd, loff_t offset)
> > {
> > loff_t size = slot->npages << PAGE_SHIFT;
> > + loff_t end;
> > unsigned long start, end_index;
> > struct gmem_file *f;
> > ...
> > - if (offset < 0 || !PAGE_ALIGNED(offset) ||
> > - offset + size > i_size_read(inode))
> > + if (offset < 0 || !PAGE_ALIGNED(offset) ||
> > + check_add_overflow(offset, size, &end) ||
>
> Eww, TIL I'm not a fan of check_add_overflow(). Burying an out-param in an
> if-statement is nasty.
>
> > + end > i_size_read(inode))
>
> This is all rather silly. @offset and and @slot->npages are fundamentally
> unsigned values. I don't see any reason to convert them to signed values, only
> to convert them *back* to unsigned values (when stored in start/end, because xarrays
> operate on "unsigned long" indices).
>
> i_size_read() obviously has to return a positive value, so can't we just do this?
lgtm,
/fuad
>
> diff --git virt/kvm/guest_memfd.c virt/kvm/guest_memfd.c
> index a35a55571a2d..9c6dbb54e800 100644
> --- virt/kvm/guest_memfd.c
> +++ virt/kvm/guest_memfd.c
> @@ -640,9 +640,9 @@ int kvm_gmem_create(struct kvm *kvm, struct kvm_create_guest_memfd *args)
> }
>
> int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot,
> - unsigned int fd, loff_t offset)
> + unsigned int fd, u64 offset)
> {
> - loff_t size = slot->npages << PAGE_SHIFT;
> + u64 size = slot->npages << PAGE_SHIFT;
> unsigned long start, end;
> struct gmem_file *f;
> struct inode *inode;
> @@ -664,8 +664,7 @@ int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot,
>
> inode = file_inode(file);
>
> - if (offset < 0 || !PAGE_ALIGNED(offset) ||
> - offset + size > i_size_read(inode))
> + if (!PAGE_ALIGNED(offset) || offset + size > i_size_read(inode))
> goto err;
>
> filemap_invalidate_lock(inode->i_mapping);
> diff --git virt/kvm/kvm_mm.h virt/kvm/kvm_mm.h
> index 9fcc5d5b7f8d..3cb5ef86d0d9 100644
> --- virt/kvm/kvm_mm.h
> +++ virt/kvm/kvm_mm.h
> @@ -72,7 +72,7 @@ int kvm_gmem_init(struct module *module);
> void kvm_gmem_exit(void);
> int kvm_gmem_create(struct kvm *kvm, struct kvm_create_guest_memfd *args);
> int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot,
> - unsigned int fd, loff_t offset);
> + unsigned int fd, u64 offset);
> void kvm_gmem_unbind(struct kvm_memory_slot *slot);
> #else
> static inline int kvm_gmem_init(struct module *module)
> @@ -80,9 +80,8 @@ static inline int kvm_gmem_init(struct module *module)
> return 0;
> }
> static inline void kvm_gmem_exit(void) {};
> -static inline int kvm_gmem_bind(struct kvm *kvm,
> - struct kvm_memory_slot *slot,
> - unsigned int fd, loff_t offset)
> +static inline int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot,
> + unsigned int fd, u64 offset)
> {
> WARN_ON_ONCE(1);
> return -EIO;
>
^ permalink raw reply
* Re: [PATCH v6 21/43] KVM: SEV: Make 'uaddr' parameter optional for KVM_SEV_SNP_LAUNCH_UPDATE
From: Sean Christopherson @ 2026-05-21 13:21 UTC (permalink / raw)
To: Fuad Tabba
Cc: ackerleytng, aik, andrew.jones, binbin.wu, brauner, chao.p.peng,
david, ira.weiny, jmattson, jthoughton, michael.roth, oupton,
pankaj.gupta, qperret, rick.p.edgecombe, rientjes, shivankg,
steven.price, willy, wyihan, yan.y.zhao, forkloop, pratyush,
suzuki.poulose, aneesh.kumar, liam, Paolo Bonzini,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Shuah Khan, Shuah Khan,
Vishal Annapurve, Andrew Morton, Chris Li, Kairui Song,
Kemeng Shi, Nhat Pham, Baoquan He, Barry Song, Axel Rasmussen,
Yuanchu Xie, Wei Xu, Youngjun Park, Qi Zheng, Shakeel Butt,
Kiryl Shutsemau, Jason Gunthorpe, Vlastimil Babka, kvm,
linux-kernel, linux-trace-kernel, linux-doc, linux-kselftest,
linux-mm, linux-coco
In-Reply-To: <CA+EHjTwrygfMrZZSw4y7-ry8fidW2x0C7iuF2Q=dnPNHUmNtUg@mail.gmail.com>
On Thu, May 21, 2026, Fuad Tabba wrote:
> Hi,
>
> On Thu, 7 May 2026 at 21:22, Ackerley Tng via B4 Relay
> <devnull+ackerleytng.google.com@kernel.org> wrote:
> >
> > From: Michael Roth <michael.roth@amd.com>
> >
> > For vm_memory_attributes=1, in-place conversion/population is not
> > supported, so the initial contents necessarily must need to come
> > from a separate src address, which is enforced by the current
> > implementation. However, for vm_memory_attributes=0, it is possible for
> > guest memory to be initialized directly from userspace by mmap()'ing the
> > guest_memfd and writing to it while the corresponding GPA ranges are in
> > a 'shared' state before converting them to the 'private' state expected
> > by KVM_SEV_SNP_LAUNCH_UPDATE.
> >
> > Update the handling/documentation for KVM_SEV_SNP_LAUNCH_UPDATE to allow
> > for 'uaddr' to be set to NULL when vm_memory_attributes=0, which
> > SNP_LAUNCH_UPDATE will then use to determine when it should/shouldn't
> > copy in data from a separate memory location. Continue to enforce
> > non-NULL for the original vm_memory_attributes=1 case.
> >
> > Signed-off-by: Michael Roth <michael.roth@amd.com>
> > [Added src_page check in error handling path when the firmware command fails]
> > [Dropped ifdef CONFIG_KVM_VM_MEMORY_ATTRIBUTES]
> > Signed-off-by: Ackerley Tng <ackerleytng@google.com>
>
> I'm not very familiar with the SEV-SNP populate flows, but it looks
> like Sashiko is on to something:
> https://sashiko.dev/#/patchset/20260507-gmem-inplace-conversion-v6-0-91ab5a8b19a4%40google.com?part=21
>
> - a potential read-only page overwrite, because src_page is acquired
> via get_user_pages_fast() without the FOLL_WRITE flag, but is then
> overwritten via memcpy
Oof, yeah, that's bad. Adding FOLL_WRITE to kvm_gmem_populate() feels wrong, and
could break uABI, but doing gup() in SNP code would reintroduce the AB-BA issue
with filemap_invalidate_lock().
Aha! Not if we use get_user_page_fast_only(). Ugh, but then we'd have to plumb
the userspace address into the post-populated callback.
Hrm. Given that no one has yelled about overwriting their CPUID page, and given
that the CPUID page is likely dynamically created and thus is unlikely to be a
read-only mapping (e.g. versus the initial image), maybe this?
diff --git arch/x86/kvm/svm/sev.c arch/x86/kvm/svm/sev.c
index 37d4cfa5d980..c73c028d72c1 100644
--- arch/x86/kvm/svm/sev.c
+++ arch/x86/kvm/svm/sev.c
@@ -2456,6 +2456,7 @@ static int snp_launch_update(struct kvm *kvm, struct kvm_sev_cmd *argp)
sev_populate_args.type = params.type;
count = kvm_gmem_populate(kvm, params.gfn_start, src, npages,
+ params.type == KVM_SEV_SNP_PAGE_TYPE_CPUID,
sev_gmem_post_populate, &sev_populate_args);
if (count < 0) {
argp->error = sev_populate_args.fw_error;
diff --git arch/x86/kvm/vmx/tdx.c arch/x86/kvm/vmx/tdx.c
index f97bcf580e6d..33f35be4455b 100644
--- arch/x86/kvm/vmx/tdx.c
+++ arch/x86/kvm/vmx/tdx.c
@@ -3188,7 +3188,7 @@ static int tdx_vcpu_init_mem_region(struct kvm_vcpu *vcpu, struct kvm_tdx_cmd *c
};
gmem_ret = kvm_gmem_populate(kvm, gpa_to_gfn(region.gpa),
u64_to_user_ptr(region.source_addr),
- 1, tdx_gmem_post_populate, &arg);
+ 1, false, tdx_gmem_post_populate, &arg);
if (gmem_ret < 0) {
ret = gmem_ret;
break;
diff --git include/linux/kvm_host.h include/linux/kvm_host.h
index 61a3430957f2..b83cda2870ba 100644
--- include/linux/kvm_host.h
+++ include/linux/kvm_host.h
@@ -2596,7 +2596,8 @@ int kvm_arch_gmem_prepare(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn, int max_ord
typedef int (*kvm_gmem_populate_cb)(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn,
struct page *page, void *opaque);
-long kvm_gmem_populate(struct kvm *kvm, gfn_t gfn, void __user *src, long npages,
+long kvm_gmem_populate(struct kvm *kvm, gfn_t start_gfn, void __user *src,
+ long npages, bool writable,
kvm_gmem_populate_cb post_populate, void *opaque);
#endif
diff --git virt/kvm/guest_memfd.c virt/kvm/guest_memfd.c
index a35a55571a2d..6553d4e032ce 100644
--- virt/kvm/guest_memfd.c
+++ virt/kvm/guest_memfd.c
@@ -858,7 +858,8 @@ static long __kvm_gmem_populate(struct kvm *kvm, struct kvm_memory_slot *slot,
return ret;
}
-long kvm_gmem_populate(struct kvm *kvm, gfn_t start_gfn, void __user *src, long npages,
+long kvm_gmem_populate(struct kvm *kvm, gfn_t start_gfn, void __user *src,
+ long npages, bool writable,
kvm_gmem_populate_cb post_populate, void *opaque)
{
struct kvm_memory_slot *slot;
@@ -892,8 +893,9 @@ long kvm_gmem_populate(struct kvm *kvm, gfn_t start_gfn, void __user *src, long
if (src) {
unsigned long uaddr = (unsigned long)src + i * PAGE_SIZE;
+ unsigned int flags = writable ? FOLL_WRITE : 0;
- ret = get_user_pages_fast(uaddr, 1, 0, &src_page);
+ ret = get_user_pages_fast(uaddr, 1, flags, &src_page);
if (ret < 0)
break;
if (ret != 1) {
> - an ordering violation with the kunmap_local() calls
Yeesh, that's a new one for me. Thankfully this is 64-bit only, so it's not an
issue.
> These predate this patch series and are just being touched by the
> 'src_page' addition, but if Sashiko's right, these should probably be
> fixed sooner rather than later.
Yeah, ditto with the offset wrapping case.
^ permalink raw reply related
* Re: [PATCH net-next 3/3] net/mlx5: Apply devlink default eswitch mode during init
From: Mark Bloch @ 2026-05-21 13:16 UTC (permalink / raw)
To: Tariq Toukan, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Andrew Lunn, David S. Miller, Thomas Weißschuh,
Thomas Gleixner, Arnd Bergmann
Cc: Jonathan Corbet, Shuah Khan, Jiri Pirko, Simon Horman,
Saeed Mahameed, Leon Romanovsky, Borislav Petkov (AMD),
Andrew Morton, Randy Dunlap, Thomas Gleixner, Petr Mladek,
Peter Zijlstra (Intel), Tejun Heo, Vlastimil Babka, Feng Tang,
Christian Brauner, Dave Hansen, Dapeng Mi, Kees Cook, Marco Elver,
Li RongQing, Eric Biggers, Paul E. McKenney, linux-doc,
linux-kernel, netdev, linux-rdma, Gal Pressman, Dragos Tatulea,
Jiri Pirko, Shay Drori, Moshe Shemesh
In-Reply-To: <20260521072434.362624-4-tariqt@nvidia.com>
On 21/05/2026 10:24, Tariq Toukan wrote:
> From: Mark Bloch <mbloch@nvidia.com>
>
> Apply devlink default eswitch mode for mlx5 devices after successful
> device initialization while holding the devlink instance lock.
>
> At this point the devlink instance is registered and the mlx5 devlink
> operations are available, so the default eswitch mode can be applied to
> the matching PCI devlink handle.
>
> Signed-off-by: Mark Bloch <mbloch@nvidia.com>
> Reviewed-by: Shay Drori <shayd@nvidia.com>
> Reviewed-by: Moshe Shemesh <moshe@nvidia.com>
> Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
> ---
> drivers/net/ethernet/mellanox/mlx5/core/main.c | 17 +++++++++++++++++
> 1 file changed, 17 insertions(+)
>
> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c
> index 0c6e4efe38c8..4528097f3d84 100644
> --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c
> +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c
> @@ -1391,6 +1391,21 @@ static void mlx5_unload(struct mlx5_core_dev *dev)
> mlx5_free_bfreg(dev, &dev->priv.bfreg);
> }
>
> +static void mlx5_devl_apply_default_esw_mode(struct mlx5_core_dev *dev)
> +{
> + struct devlink *devlink = priv_to_devlink(dev);
> + int err;
> +
> + if (!MLX5_ESWITCH_MANAGER(dev))
> + return;
> +
> + devl_assert_locked(devlink);
> + err = devl_apply_default_esw_mode(devlink);
> + if (err)
> + mlx5_core_warn(dev, "Couldn't apply default eswitch mode, err %d\n",
> + err);
> +}
> +
> int mlx5_init_one_devl_locked(struct mlx5_core_dev *dev)
> {
> bool light_probe = mlx5_dev_is_lightweight(dev);
> @@ -1437,6 +1452,7 @@ int mlx5_init_one_devl_locked(struct mlx5_core_dev *dev)
> mlx5_core_err(dev, "mlx5_hwmon_dev_register failed with error code %d\n", err);
>
> mutex_unlock(&dev->intf_state_mutex);
> + mlx5_devl_apply_default_esw_mode(dev);
> return 0;
>
> err_register:
> @@ -1538,6 +1554,7 @@ int mlx5_load_one_devl_locked(struct mlx5_core_dev *dev, bool recovery)
> goto err_attach;
>
> mutex_unlock(&dev->intf_state_mutex);
> + mlx5_devl_apply_default_esw_mode(dev);
> return 0;
>
> err_attach:
NIPA flagged this patch with a build_allmodconfig_warn failure:
https://netdev-ctrl.bots.linux.dev/logs/build/1098506/14585935/build_allmodconfig_warn/
I do not see how this mlx5 patch is related to the reported issue,
but I looked into it anyway.
After the kernel has been built once, the issue can be reproduced by rerunning sparse
only on version.o, which filters out the unrelated noise. I had an older sparse installed,
so I used a local copy:
rm -f arch/x86/boot/version.o
make V=1 C=1 CHECK=/labhome/mbloch/bin/sparse arch/x86/boot/version.o
This gives the same error reported by NIPA:
...
...
make -f ./scripts/Makefile.vmlinux
make -f ./scripts/Makefile.build obj=arch/x86/boot arch/x86/boot/bzImage
make -f ./scripts/Makefile.build obj=arch/x86/boot/compressed arch/x86/boot/compressed/vmlinux
# CC arch/x86/boot/version.o
gcc -Wp,-MMD,arch/x86/boot/.version.o.d -nostdinc -I./arch/x86/include -I./arch/x86/include/generated -I./include -I./include -I./arch/x86/include/uapi -I./arch/x86/include/generated/uapi -I./include/uapi -I./include/generated/uapi -include ./include/linux/compiler-version.h -include ./include/linux/kconfig.h -include ./include/linux/compiler_types.h -D__KERNEL__ -std=gnu11 -fms-extensions -m16 -g -Os -DDISABLE_BRANCH_PROFILING -D__DISABLE_EXPORTS -Wall -Wstrict-prototypes -march=i386 -mregparm=3 -fno-strict-aliasing -fomit-frame-pointer -fno-pic -mno-mmx -mno-sse -fcf-protection=none -ffreestanding -fno-stack-protector -Wno-address-of-packed-member -mpreferred-stack-boundary=2 -D_SETUP -fno-asynchronous-unwind-tables -Wimplicit-fallthrough=5 -DKBUILD_MODFILE='"arch/x86/boot/version"' -DKBUILD_BASENAME='"version"' -DKBUILD_MODNAME='"version"' -D__KBUILD_MODNAME=version -c -o arch/x86/boot/version.o arch/x86/boot/version.c
# CHECK arch/x86/boot/version.c
/labhome/mbloch/bin/sparse -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise -Wno-return-void -Wno-unknown-attribute -D__x86_64__ --arch=x86 -mlittle-endian -m64 -Wp,-MMD,arch/x86/boot/.version.o.d -nostdinc -I./arch/x86/include -I./arch/x86/include/generated -I./include -I./include -I./arch/x86/include/uapi -I./arch/x86/include/generated/uapi -I./include/uapi -I./include/generated/uapi -include ./include/linux/compiler-version.h -include ./include/linux/kconfig.h -include ./include/linux/compiler_types.h -D__KERNEL__ -std=gnu11 -fms-extensions -m16 -g -Os -DDISABLE_BRANCH_PROFILING -D__DISABLE_EXPORTS -Wall -Wstrict-prototypes -march=i386 -mregparm=3 -fno-strict-aliasing -fomit-frame-pointer -fno-pic -mno-mmx -mno-sse -fcf-protection=none -ffreestanding -fno-stack-protector -Wno-address-of-packed-member -mpreferred-stack-boundary=2 -D_SETUP -fno-asynchronous-unwind-tables -Wimplicit-fallthrough=5 -DKBUILD_MODFILE='"arch/x86/boot/version"' -DKBUILD_BASENAME='"version"' -DKBUILD_MODNAME='"version"' -D__KBUILD_MODNAME=version arch/x86/boot/version.c
arch/x86/boot/version.c: note: in included file (through arch/x86/include/uapi/asm/bitsperlong.h, include/uapi/asm-generic/int-ll64.h, include/asm-generic/int-ll64.h, include/uapi/asm-generic/types.h, ...):
./include/asm-generic/bitsperlong.h:23:2: error: Inconsistent word size. Check asm/bitsperlong.h
./include/asm-generic/bitsperlong.h:27:33: error: static assertion failed: "Inconsistent word size. Check asm/bitsperlong.h"
# cmd_gen_symversions_c arch/x86/boot/version.o
if nm arch/x86/boot/version.o 2>/dev/null | grep -q ' __export_symbol_'; then gcc -E -D__GENKSYMS__ -Wp,-MMD,arch/x86/boot/.version.o.d -nostdinc -I./arch/x86/include -I./arch/x86/include/generated -I./include -I./include -I./arch/x86/include/uapi -I./arch/x86/include/generated/uapi -I./include/uapi -I./include/generated/uapi -include ./include/linux/compiler-version.h -include ./include/linux/kconfig.h -include ./include/linux/compiler_types.h -D__KERNEL__ -std=gnu11 -fms-extensions -m16 -g -Os -DDISABLE_BRANCH_PROFILING -D__DISABLE_EXPORTS -Wall -Wstrict-prototypes -march=i386 -mregparm=3 -fno-strict-aliasing -fomit-frame-pointer -fno-pic -mno-mmx -mno-sse -fcf-protection=none -ffreestanding -fno-stack-protector -Wno-address-of-packed-member -mpreferred-stack-boundary=2 -D_SETUP -fno-asynchronous-unwind-tables -Wimplicit-fallthrough=5 -DKBUILD_MODFILE='"arch/x86/boot/version"' -DKBUILD_BASENAME='"version"' -DKBUILD_MODNAME='"version"' -D__KBUILD_MODNAME=version arch/x86/boot/version.c | ./scripts/genksyms/genksyms >> arch/x86/boot/.version.o.cmd; fi
# LD arch/x86/boot/setup.elf
ld -m elf_x86_64 -z noexecstack -m elf_i386 -z noexecstack -T arch/x86/boot/setup.ld arch/x86/boot/a20.o arch/x86/boot/bioscall.o arch/x86/boot/cmdline.o arch/x86/boot/copy.o arch/x86/boot/cpu.o arch/x86/boot/cpuflags.o arch/x86/boot/cpucheck.o arch/x86/boot/early_serial_console.o arch/x86/boot/edd.o arch/x86/boot/header.o arch/x86/boot/main.o arch/x86/boot/memory.o arch/x86/boot/pm.o arch/x86/boot/pmjump.o arch/x86/boot/printf.o arch/x86/boot/regs.o arch/x86/boot/string.o arch/x86/boot/tty.o arch/x86/boot/video.o arch/x86/boot/video-mode.o arch/x86/boot/version.o arch/x86/boot/video-vga.o arch/x86/boot/video-vesa.o arch/x86/boot/video-bios.o -o arch/x86/boot/setup.elf
# OBJCOPY arch/x86/boot/setup.bin
objcopy -O binary arch/x86/boot/setup.elf arch/x86/boot/setup.bin
# BUILD arch/x86/boot/bzImage
(dd if=arch/x86/boot/setup.bin bs=4k conv=sync status=none; cat arch/x86/boot/vmlinux.bin) >arch/x86/boot/bzImage
mkdir -p ./arch/x86_64/boot
ln -fsn ../../x86/boot/bzImage ./arch/x86_64/boot/bzImage
To me this looks like sparse is getting a conflicting set of flags.
The command line contains both "-D__x86_64__ -m64" and "-m16 -march=i386 -D_SETUP".
I confirmed that the following patch "fixes" the issue, but I do not know whether
this is the right fix. This area is outside my comfort zone, so it would be
helpful if someone more familiar with the x86 build/sparse flow could take a
look:
diff --git a/arch/x86/boot/Makefile b/arch/x86/boot/Makefile
index 3f9fb3698d66..80923864f6f9 100644
--- a/arch/x86/boot/Makefile
+++ b/arch/x86/boot/Makefile
@@ -71,6 +71,10 @@ $(obj)/vmlinux.bin: $(obj)/compressed/vmlinux FORCE
SETUP_OBJS = $(addprefix $(obj)/,$(setup-y))
+realmode-checkflags-$(CONFIG_X86_64) := -m32 -U__x86_64__ -D__i386__
+REALMODE_CHECKFLAGS := $(filter-out -m64 -D__x86_64__,$(CHECKFLAGS)) $(realmode-checkflags-y)
+$(SETUP_OBJS): CHECKFLAGS := $(REALMODE_CHECKFLAGS)
+
sed-zoffset := -e 's/^\([0-9a-fA-F]*\) [a-zA-Z] \(startup_32\|efi.._stub_entry\|efi\(32\)\?_pe_entry\|input_data\|kernel_info\|_end\|_ehead\|_text\|_e\?data\|_e\?sbat\|z_.*\)$$/\#define ZO_\2 0x\1/p'
quiet_cmd_zoffset = ZOFFSET $@
diff --git a/arch/x86/realmode/rm/Makefile b/arch/x86/realmode/rm/Makefile
index a0fb39abc5c8..341b0ff20c3d 100644
--- a/arch/x86/realmode/rm/Makefile
+++ b/arch/x86/realmode/rm/Makefile
@@ -29,6 +29,10 @@ targets += $(realmode-y)
REALMODE_OBJS = $(addprefix $(obj)/,$(realmode-y))
+realmode-checkflags-$(CONFIG_X86_64) := -m32 -U__x86_64__ -D__i386__
+REALMODE_CHECKFLAGS := $(filter-out -m64 -D__x86_64__,$(CHECKFLAGS)) $(realmode-checkflags-y)
+$(REALMODE_OBJS): CHECKFLAGS := $(REALMODE_CHECKFLAGS)
+
sed-pasyms := -n -r -e 's/^([0-9a-fA-F]+) [ABCDGRSTVW] (.+)$$/pa_\2 = \2;/p'
quiet_cmd_pasyms = PASYMS $@
^ permalink raw reply related
* [PATCH v6] stm: class: Add MIPI OST protocol support
From: Yingchao Deng @ 2026-05-21 13:14 UTC (permalink / raw)
To: Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers,
Jonathan Corbet, Shuah Khan, Alexander Shishkin, Alexandre Torgue
Cc: linux-kernel, linux-trace-kernel, linux-doc, linux-arm-kernel,
quic_yingdeng, tingwei.zhang, jinlong.mao, jie.gan,
yuanfang.zhang, Yingchao Deng
Add MIPI OST (Open System Trace) protocol support for stm to format the
traces. The OST Protocol abstracts the underlying layers from the sending
and receiving applications, thus removing dependencies on the connection
media and platform implementation.
OST over STP packet consists of Header/Payload/End. Header is designed to
include the information required by all OST packets. Information that is
not shared by all packets is left to the higher layer protocols. Thus, the
OST Protocol Header can be regarded as the first part of a complete OST
Packet Header, while a higher layer header can be regarded as an extension
designed for a specific purpose.
+--------+--------+--------+--------+
| start |version |entity |protocol|
+--------+--------+--------+--------+
| stm version | magic |
+-----------------------------------+
| cpu |
+-----------------------------------+
| timestamp |
| |
+-----------------------------------+
| tgid |
| |
+-----------------------------------+
| payload |
+-----------------------------------+
| ... | end |
+-----------------------------------+
In header, there will be STARTSIMPLE/VERSION/ENTITY/PROTOCOL.
STARTSIMPLE is used to signal the beginning of a simplified OST protocol.
The Version field is a one byte, unsigned number identifying the version
of the OST Protocol. The Entity ID field is a one byte unsigned number
that identifies the source.
Entity ID values (0~239) are defined and controlled by the TS owner, and
shall be unique for the whole TS. The configfs entity attribute allows the
user to configure which Entity ID is associated with each policy node.
The Protocol ID field is a one byte unsigned number identifying the higher
layer protocol of the OST Packet, i.e. identifying the format of the data
after the OST Protocol Header. OST Control Protocol ID value represents
the common control protocol, the remaining Protocol ID values may be used
by any higher layer protocols capable of being transported by the OST
Protocol.
Co-developed-by: Tingwei Zhang <tingwei.zhang@oss.qualcomm.com>
Signed-off-by: Tingwei Zhang <tingwei.zhang@oss.qualcomm.com>
Co-developed-by: Yuanfang Zhang <yuanfang.zhang@oss.qualcomm.com>
Signed-off-by: Yuanfang Zhang <yuanfang.zhang@oss.qualcomm.com>
Co-developed-by: Jinlong Mao <jinlong.mao@oss.qualcomm.com>
Signed-off-by: Jinlong Mao <jinlong.mao@oss.qualcomm.com>
Signed-off-by: Yingchao Deng <yingchao.deng@oss.qualcomm.com>
---
Changes in v6:
1. Rebase on top of linux-next-20260518.
2. Fix Kconfig: 'default CONFIG_STM' -> 'default STM'.
3. Fix documentation grammar issues.
4. Add p_ost entry to Documentation/trace/index.rst.
5. Add missing priv_sz field to stm_protocol_driver registration.
6. Use kzalloc_obj() instead of kzalloc() in ost_output_open().
7. Add mutex protection in entity configfs store handler.
8. Keep the configfs entity attribute: entity ID values (0~239) are
defined and controlled by the TS owner and are deployment-specific.
stm_source_type only carries a small number of in-kernel source
classifications and cannot represent the full range of OST entity
assignments needed in practice. The configfs attribute allows each
policy node to declare its entity.
OST_ENTITY_TYPE_NONE is an enum sentinel (not entity ID 0) that causes
ost_write() to return -EINVAL when no entity is configured, preventing
emission of packets with an unintended entity field.
OST_ENTITY_DIAG (0xEE) is a TS-owner-defined value used by Qualcomm's
diagnostic framework as the standard entity identifier for diagnostic
trace sources.
Link to v5: https://lore.kernel.org/all/20260129-p_ost-v5-1-2b14fff39428@oss.qualcomm.com/
Changes in v5:
1. Add Co-developed-by tag.
2. Use yearless copyright for new file.
- Link to v4: https://lore.kernel.org/all/20251024-p_ost-v4-1-3652a06fd055@oss.qualcomm.com/
Changes in v4:
1. Delete unused variable 'i'.
2. Fix build error: call to undeclared function 'task_tgid_nr'.
Link to v3 - https://lore.kernel.org/all/20251022071834.1658684-1-yingchao.deng@oss.qualcomm.com/
Changes in v3:
1. Add more details about OST.
2. Delete 'entity_available' node, and 'entity' node will show available
and currently selected (shown in square brackets) entity.
3. Removed the usage of config_item->ci_group->cg_subsys->su_mutex.
Link to v2 - https://lore.kernel.org/all/20230419141328.37472-1-quic_jinlmao@quicinc.com/
---
.../ABI/testing/configfs-stp-policy-p_ost | 9 +
Documentation/trace/index.rst | 1 +
Documentation/trace/p_ost.rst | 39 ++++
drivers/hwtracing/stm/Kconfig | 14 ++
drivers/hwtracing/stm/Makefile | 2 +
drivers/hwtracing/stm/p_ost.c | 241 +++++++++++++++++++++
6 files changed, 306 insertions(+)
diff --git a/Documentation/ABI/testing/configfs-stp-policy-p_ost b/Documentation/ABI/testing/configfs-stp-policy-p_ost
new file mode 100644
index 000000000000..8fb160b50c40
--- /dev/null
+++ b/Documentation/ABI/testing/configfs-stp-policy-p_ost
@@ -0,0 +1,9 @@
+What: /config/stp-policy/<device>:p_ost.<policy>/<node>/entity
+Date: May 2026
+KernelVersion: 7.1
+Description:
+ Set the entity ID which identifies the trace source in the
+ OST packet header. Entity ID values (0~239) are defined by
+ the TS owner. Currently supported values are ftrace, console
+ and diag. RW.
+
diff --git a/Documentation/trace/index.rst b/Documentation/trace/index.rst
index 5d9bf4694d5d..9cd1e0b5af6d 100644
--- a/Documentation/trace/index.rst
+++ b/Documentation/trace/index.rst
@@ -72,6 +72,7 @@ interactions and system performance.
intel_th
stm
sys-t
+ p_ost
coresight/index
rv/index
hisi-ptt
diff --git a/Documentation/trace/p_ost.rst b/Documentation/trace/p_ost.rst
new file mode 100644
index 000000000000..2b92e2229653
--- /dev/null
+++ b/Documentation/trace/p_ost.rst
@@ -0,0 +1,39 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===================
+MIPI OST over STP
+===================
+
+The OST (Open System Trace) driver is used with STM class devices to
+generate standardized trace stream. Trace sources can be identified
+by different entity IDs.
+
+CONFIG_STM_PROTO_OST is for p_ost driver enablement. Once this config
+is enabled, you can select the p_ost protocol by command below:
+
+# mkdir /sys/kernel/config/stp-policy/stm0:p_ost.policy
+
+The policy name format is extended like this:
+
+ <device_name>:<protocol_name>.<policy_name>
+
+With a coresight-stm device, it will look like "stm0:p_ost.policy".
+
+With the MIPI OST protocol driver, the attributes for each protocol node are:
+
+# mkdir /sys/kernel/config/stp-policy/stm0:p_ost.policy/default
+# ls /sys/kernel/config/stp-policy/stm0:p_ost.policy/default
+channels entity masters
+
+The entity here is the set of entities that p_ost supports. Currently
+p_ost supports ftrace, console and diag entities.
+
+Set entity:
+# echo 'ftrace' > /sys/kernel/config/stp-policy/stm0:p_ost.policy/default/entity
+
+Get available and currently selected (shown in square brackets) entity:
+# cat /sys/kernel/config/stp-policy/stm0:p_ost.policy/default/entity
+[ftrace] console diag
+
+See Documentation/ABI/testing/configfs-stp-policy-p_ost for more details.
+
diff --git a/drivers/hwtracing/stm/Kconfig b/drivers/hwtracing/stm/Kconfig
index cd7f0b0f3fbe..4c83da5d95a0 100644
--- a/drivers/hwtracing/stm/Kconfig
+++ b/drivers/hwtracing/stm/Kconfig
@@ -40,6 +40,20 @@ config STM_PROTO_SYS_T
If you don't know what this is, say N.
+config STM_PROTO_OST
+ tristate "MIPI OST STM framing protocol driver"
+ default STM
+ help
+ This is an implementation of MIPI OST protocol to be used
+ over the STP transport. In addition to the data payload, it
+ also carries additional metadata for entity, better
+ means of trace source identification, etc.
+
+ The receiving side must be able to decode this protocol in
+ addition to the MIPI STP, in order to extract the data.
+
+ If you don't know what this is, say N.
+
config STM_DUMMY
tristate "Dummy STM driver"
help
diff --git a/drivers/hwtracing/stm/Makefile b/drivers/hwtracing/stm/Makefile
index 1692fcd29277..d9c8615849b9 100644
--- a/drivers/hwtracing/stm/Makefile
+++ b/drivers/hwtracing/stm/Makefile
@@ -5,9 +5,11 @@ stm_core-y := core.o policy.o
obj-$(CONFIG_STM_PROTO_BASIC) += stm_p_basic.o
obj-$(CONFIG_STM_PROTO_SYS_T) += stm_p_sys-t.o
+obj-$(CONFIG_STM_PROTO_OST) += stm_p_ost.o
stm_p_basic-y := p_basic.o
stm_p_sys-t-y := p_sys-t.o
+stm_p_ost-y := p_ost.o
obj-$(CONFIG_STM_DUMMY) += dummy_stm.o
diff --git a/drivers/hwtracing/stm/p_ost.c b/drivers/hwtracing/stm/p_ost.c
new file mode 100644
index 000000000000..d2174872b761
--- /dev/null
+++ b/drivers/hwtracing/stm/p_ost.c
@@ -0,0 +1,241 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ * MIPI OST framing protocol for STM devices.
+ */
+
+#include <linux/pid.h>
+#include <linux/sched/clock.h>
+#include <linux/slab.h>
+#include <linux/stm.h>
+#include "stm.h"
+
+/*
+ * OST Base Protocol Header
+ *
+ * Position Bits Field Name
+ * 0 8 STARTSIMPLE
+ * 1 8 Version
+ * 2 8 Entity ID
+ * 3 8 Protocol ID
+ */
+#define OST_FIELD_STARTSIMPLE 0
+#define OST_FIELD_VERSION 8
+#define OST_FIELD_ENTITY 16
+#define OST_FIELD_PROTOCOL 24
+
+#define OST_TOKEN_STARTSIMPLE 0x10
+#define OST_VERSION_MIPI1 0x10
+
+/* entity id to identify the source */
+#define OST_ENTITY_FTRACE 0x01
+#define OST_ENTITY_CONSOLE 0x02
+#define OST_ENTITY_DIAG 0xEE
+
+#define OST_CONTROL_PROTOCOL 0x0
+
+#define DATA_HEADER ((OST_TOKEN_STARTSIMPLE << OST_FIELD_STARTSIMPLE) | \
+ (OST_VERSION_MIPI1 << OST_FIELD_VERSION) | \
+ (OST_CONTROL_PROTOCOL << OST_FIELD_PROTOCOL))
+
+#define STM_MAKE_VERSION(ma, mi) (((ma) << 8) | (mi))
+#define STM_HEADER_MAGIC (0x5953)
+
+enum ost_entity_type {
+ OST_ENTITY_TYPE_NONE,
+ OST_ENTITY_TYPE_FTRACE,
+ OST_ENTITY_TYPE_CONSOLE,
+ OST_ENTITY_TYPE_DIAG,
+};
+
+static const char * const str_ost_entity_type[] = {
+ [OST_ENTITY_TYPE_NONE] = "none",
+ [OST_ENTITY_TYPE_FTRACE] = "ftrace",
+ [OST_ENTITY_TYPE_CONSOLE] = "console",
+ [OST_ENTITY_TYPE_DIAG] = "diag",
+};
+
+static const u32 ost_entity_value[] = {
+ [OST_ENTITY_TYPE_NONE] = 0,
+ [OST_ENTITY_TYPE_FTRACE] = OST_ENTITY_FTRACE,
+ [OST_ENTITY_TYPE_CONSOLE] = OST_ENTITY_CONSOLE,
+ [OST_ENTITY_TYPE_DIAG] = OST_ENTITY_DIAG,
+};
+
+struct ost_policy_node {
+ enum ost_entity_type entity_type;
+};
+
+struct ost_output {
+ struct ost_policy_node node;
+};
+
+/* Set default entity type as none */
+static void ost_policy_node_init(void *priv)
+{
+ struct ost_policy_node *pn = priv;
+
+ pn->entity_type = OST_ENTITY_TYPE_NONE;
+}
+
+static int ost_output_open(void *priv, struct stm_output *output)
+{
+ struct ost_policy_node *pn = priv;
+ struct ost_output *opriv;
+
+ opriv = kzalloc_obj(*opriv, GFP_ATOMIC);
+ if (!opriv)
+ return -ENOMEM;
+
+ memcpy(&opriv->node, pn, sizeof(opriv->node));
+ output->pdrv_private = opriv;
+ return 0;
+}
+
+static void ost_output_close(struct stm_output *output)
+{
+ kfree(output->pdrv_private);
+}
+
+static ssize_t ost_t_policy_entity_show(struct config_item *item,
+ char *page)
+{
+ struct ost_policy_node *pn = to_pdrv_policy_node(item);
+ ssize_t sz = 0;
+ int i;
+
+ for (i = 1; i < ARRAY_SIZE(str_ost_entity_type); i++) {
+ if (i == pn->entity_type)
+ sz += sysfs_emit_at(page, sz, "[%s] ", str_ost_entity_type[i]);
+ else
+ sz += sysfs_emit_at(page, sz, "%s ", str_ost_entity_type[i]);
+ }
+
+ sz += sysfs_emit_at(page, sz, "\n");
+ return sz;
+}
+
+static int entity_index(const char *str)
+{
+ int i;
+
+ for (i = 1; i < ARRAY_SIZE(str_ost_entity_type); i++) {
+ if (sysfs_streq(str, str_ost_entity_type[i]))
+ return i;
+ }
+
+ return 0;
+}
+
+static ssize_t
+ost_t_policy_entity_store(struct config_item *item, const char *page,
+ size_t count)
+{
+ struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex;
+ struct ost_policy_node *pn = to_pdrv_policy_node(item);
+ int i;
+
+ i = entity_index(page);
+ if (i) {
+ mutex_lock(mutexp);
+ pn->entity_type = i;
+ mutex_unlock(mutexp);
+ } else {
+ return -EINVAL;
+ }
+
+ return count;
+}
+CONFIGFS_ATTR(ost_t_policy_, entity);
+
+static struct configfs_attribute *ost_t_policy_attrs[] = {
+ &ost_t_policy_attr_entity,
+ NULL,
+};
+
+static ssize_t
+notrace ost_write(struct stm_data *data, struct stm_output *output,
+ unsigned int chan, const char *buf, size_t count,
+ struct stm_source_data *source)
+{
+ struct ost_output *op = output->pdrv_private;
+ unsigned int c = output->channel + chan;
+ unsigned int m = output->master;
+ const unsigned char nil = 0;
+ u32 header = DATA_HEADER;
+ struct trc_hdr {
+ u16 version;
+ u16 magic;
+ u32 cpu;
+ u64 timestamp;
+ u64 tgid;
+ } hdr;
+ ssize_t sz;
+
+ /*
+ * Identify the source by entity type.
+ * If entity type is not set, return error value.
+ */
+ if (op->node.entity_type)
+ header |= (ost_entity_value[op->node.entity_type] << OST_FIELD_ENTITY);
+ else
+ return -EINVAL;
+
+ /*
+ * STP framing rules for OST frames:
+ * * the first packet of the OST frame is marked;
+ * * the last packet is a FLAG with timestamped tag.
+ */
+ /* Message layout: HEADER / DATA / TAIL */
+ /* HEADER */
+ sz = data->packet(data, m, c, STP_PACKET_DATA, STP_PACKET_MARKED,
+ 4, (u8 *)&header);
+ if (sz <= 0)
+ return sz;
+
+ /* DATA */
+ hdr.version = STM_MAKE_VERSION(0, 3);
+ hdr.magic = STM_HEADER_MAGIC;
+ hdr.cpu = raw_smp_processor_id();
+ hdr.timestamp = sched_clock();
+ hdr.tgid = task_tgid_nr(current);
+ sz = stm_data_write(data, m, c, false, &hdr, sizeof(hdr));
+ if (sz <= 0)
+ return sz;
+
+ sz = stm_data_write(data, m, c, false, buf, count);
+
+ /* TAIL */
+ if (sz > 0)
+ data->packet(data, m, c, STP_PACKET_FLAG,
+ STP_PACKET_TIMESTAMPED, 0, &nil);
+
+ return sz;
+}
+
+static const struct stm_protocol_driver ost_pdrv = {
+ .owner = THIS_MODULE,
+ .name = "p_ost",
+ .priv_sz = sizeof(struct ost_policy_node),
+ .write = ost_write,
+ .policy_attr = ost_t_policy_attrs,
+ .output_open = ost_output_open,
+ .output_close = ost_output_close,
+ .policy_node_init = ost_policy_node_init,
+};
+
+static int ost_stm_init(void)
+{
+ return stm_register_protocol(&ost_pdrv);
+}
+module_init(ost_stm_init);
+
+static void ost_stm_exit(void)
+{
+ stm_unregister_protocol(&ost_pdrv);
+}
+module_exit(ost_stm_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("MIPI Open System Trace STM framing protocol driver");
---
base-commit: 80dd246accce631c328ea43294e53b2b2dd2aa32
change-id: 20260521-stm_p_ost-3489f42a9e8c
Best regards,
--
Yingchao Deng <yingchao.deng@oss.qualcomm.com>
^ permalink raw reply related
* [PATCH v6] stm: class: Add MIPI OST protocol support
From: Yingchao Deng @ 2026-05-21 13:08 UTC (permalink / raw)
To: Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers,
Jonathan Corbet, Shuah Khan, Alexander Shishkin, Alexandre Torgue
Cc: linux-kernel, linux-trace-kernel, linux-doc, linux-arm-kernel,
Tingwei Zhang, Yuanfang Zhang, Jinlong Mao, Yingchao Deng
Add MIPI OST (Open System Trace) protocol support for stm to format the
traces. The OST Protocol abstracts the underlying layers from the sending
and receiving applications, thus removing dependencies on the connection
media and platform implementation.
OST over STP packet consists of Header/Payload/End. Header is designed to
include the information required by all OST packets. Information that is
not shared by all packets is left to the higher layer protocols. Thus, the
OST Protocol Header can be regarded as the first part of a complete OST
Packet Header, while a higher layer header can be regarded as an extension
designed for a specific purpose.
+--------+--------+--------+--------+
| start |version |entity |protocol|
+--------+--------+--------+--------+
| stm version | magic |
+-----------------------------------+
| cpu |
+-----------------------------------+
| timestamp |
| |
+-----------------------------------+
| tgid |
| |
+-----------------------------------+
| payload |
+-----------------------------------+
| ... | end |
+-----------------------------------+
In header, there will be STARTSIMPLE/VERSION/ENTITY/PROTOCOL.
STARTSIMPLE is used to signal the beginning of a simplified OST protocol.
The Version field is a one byte, unsigned number identifying the version
of the OST Protocol. The Entity ID field is a one byte unsigned number
that identifies the source.
Entity ID values (0~239) are defined and controlled by the TS owner, and
shall be unique for the whole TS. The configfs entity attribute allows the
user to configure which Entity ID is associated with each policy node.
The Protocol ID field is a one byte unsigned number identifying the higher
layer protocol of the OST Packet, i.e. identifying the format of the data
after the OST Protocol Header. OST Control Protocol ID value represents
the common control protocol, the remaining Protocol ID values may be used
by any higher layer protocols capable of being transported by the OST
Protocol.
Co-developed-by: Tingwei Zhang <tingwei.zhang@oss.qualcomm.com>
Signed-off-by: Tingwei Zhang <tingwei.zhang@oss.qualcomm.com>
Co-developed-by: Yuanfang Zhang <yuanfang.zhang@oss.qualcomm.com>
Signed-off-by: Yuanfang Zhang <yuanfang.zhang@oss.qualcomm.com>
Co-developed-by: Jinlong Mao <jinlong.mao@oss.qualcomm.com>
Signed-off-by: Jinlong Mao <jinlong.mao@oss.qualcomm.com>
Signed-off-by: Yingchao Deng <yingchao.deng@oss.qualcomm.com>
---
Changes in v6:
1. Rebase on top of linux-next-20260518.
2. Fix Kconfig: 'default CONFIG_STM' -> 'default STM'.
3. Fix documentation grammar issues.
4. Add p_ost entry to Documentation/trace/index.rst.
5. Add missing priv_sz field to stm_protocol_driver registration.
6. Use kzalloc_obj() instead of kzalloc() in ost_output_open().
7. Add mutex protection in entity configfs store handler.
8. Keep the configfs entity attribute: entity ID values (0~239) are
defined and controlled by the TS owner and are deployment-specific.
stm_source_type only carries a small number of in-kernel source
classifications and cannot represent the full range of OST entity
assignments needed in practice. The configfs attribute allows each
policy node to declare its entity.
OST_ENTITY_TYPE_NONE is an enum sentinel (not entity ID 0) that causes
ost_write() to return -EINVAL when no entity is configured, preventing
emission of packets with an unintended entity field.
OST_ENTITY_DIAG (0xEE) is a TS-owner-defined value used by Qualcomm's
diagnostic framework as the standard entity identifier for diagnostic
trace sources.
Link to v5: https://lore.kernel.org/all/20260129-p_ost-v5-1-2b14fff39428@oss.qualcomm.com/
Changes in v5:
1. Add Co-developed-by tag.
2. Use yearless copyright for new file.
- Link to v4: https://lore.kernel.org/all/20251024-p_ost-v4-1-3652a06fd055@oss.qualcomm.com/
Changes in v4:
1. Delete unused variable 'i'.
2. Fix build error: call to undeclared function 'task_tgid_nr'.
Link to v3 - https://lore.kernel.org/all/20251022071834.1658684-1-yingchao.deng@oss.qualcomm.com/
Changes in v3:
1. Add more details about OST.
2. Delete 'entity_available' node, and 'entity' node will show available
and currently selected (shown in square brackets) entity.
3. Removed the usage of config_item->ci_group->cg_subsys->su_mutex.
Link to v2 - https://lore.kernel.org/all/20230419141328.37472-1-quic_jinlmao@quicinc.com/
---
.../ABI/testing/configfs-stp-policy-p_ost | 9 +
Documentation/trace/index.rst | 1 +
Documentation/trace/p_ost.rst | 39 ++++
drivers/hwtracing/stm/Kconfig | 14 ++
drivers/hwtracing/stm/Makefile | 2 +
drivers/hwtracing/stm/p_ost.c | 241 +++++++++++++++++++++
6 files changed, 306 insertions(+)
diff --git a/Documentation/ABI/testing/configfs-stp-policy-p_ost b/Documentation/ABI/testing/configfs-stp-policy-p_ost
new file mode 100644
index 000000000000..8fb160b50c40
--- /dev/null
+++ b/Documentation/ABI/testing/configfs-stp-policy-p_ost
@@ -0,0 +1,9 @@
+What: /config/stp-policy/<device>:p_ost.<policy>/<node>/entity
+Date: May 2026
+KernelVersion: 7.1
+Description:
+ Set the entity ID which identifies the trace source in the
+ OST packet header. Entity ID values (0~239) are defined by
+ the TS owner. Currently supported values are ftrace, console
+ and diag. RW.
+
diff --git a/Documentation/trace/index.rst b/Documentation/trace/index.rst
index 5d9bf4694d5d..9cd1e0b5af6d 100644
--- a/Documentation/trace/index.rst
+++ b/Documentation/trace/index.rst
@@ -72,6 +72,7 @@ interactions and system performance.
intel_th
stm
sys-t
+ p_ost
coresight/index
rv/index
hisi-ptt
diff --git a/Documentation/trace/p_ost.rst b/Documentation/trace/p_ost.rst
new file mode 100644
index 000000000000..2b92e2229653
--- /dev/null
+++ b/Documentation/trace/p_ost.rst
@@ -0,0 +1,39 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===================
+MIPI OST over STP
+===================
+
+The OST (Open System Trace) driver is used with STM class devices to
+generate standardized trace stream. Trace sources can be identified
+by different entity IDs.
+
+CONFIG_STM_PROTO_OST is for p_ost driver enablement. Once this config
+is enabled, you can select the p_ost protocol by command below:
+
+# mkdir /sys/kernel/config/stp-policy/stm0:p_ost.policy
+
+The policy name format is extended like this:
+
+ <device_name>:<protocol_name>.<policy_name>
+
+With a coresight-stm device, it will look like "stm0:p_ost.policy".
+
+With the MIPI OST protocol driver, the attributes for each protocol node are:
+
+# mkdir /sys/kernel/config/stp-policy/stm0:p_ost.policy/default
+# ls /sys/kernel/config/stp-policy/stm0:p_ost.policy/default
+channels entity masters
+
+The entity here is the set of entities that p_ost supports. Currently
+p_ost supports ftrace, console and diag entities.
+
+Set entity:
+# echo 'ftrace' > /sys/kernel/config/stp-policy/stm0:p_ost.policy/default/entity
+
+Get available and currently selected (shown in square brackets) entity:
+# cat /sys/kernel/config/stp-policy/stm0:p_ost.policy/default/entity
+[ftrace] console diag
+
+See Documentation/ABI/testing/configfs-stp-policy-p_ost for more details.
+
diff --git a/drivers/hwtracing/stm/Kconfig b/drivers/hwtracing/stm/Kconfig
index cd7f0b0f3fbe..4c83da5d95a0 100644
--- a/drivers/hwtracing/stm/Kconfig
+++ b/drivers/hwtracing/stm/Kconfig
@@ -40,6 +40,20 @@ config STM_PROTO_SYS_T
If you don't know what this is, say N.
+config STM_PROTO_OST
+ tristate "MIPI OST STM framing protocol driver"
+ default STM
+ help
+ This is an implementation of MIPI OST protocol to be used
+ over the STP transport. In addition to the data payload, it
+ also carries additional metadata for entity, better
+ means of trace source identification, etc.
+
+ The receiving side must be able to decode this protocol in
+ addition to the MIPI STP, in order to extract the data.
+
+ If you don't know what this is, say N.
+
config STM_DUMMY
tristate "Dummy STM driver"
help
diff --git a/drivers/hwtracing/stm/Makefile b/drivers/hwtracing/stm/Makefile
index 1692fcd29277..d9c8615849b9 100644
--- a/drivers/hwtracing/stm/Makefile
+++ b/drivers/hwtracing/stm/Makefile
@@ -5,9 +5,11 @@ stm_core-y := core.o policy.o
obj-$(CONFIG_STM_PROTO_BASIC) += stm_p_basic.o
obj-$(CONFIG_STM_PROTO_SYS_T) += stm_p_sys-t.o
+obj-$(CONFIG_STM_PROTO_OST) += stm_p_ost.o
stm_p_basic-y := p_basic.o
stm_p_sys-t-y := p_sys-t.o
+stm_p_ost-y := p_ost.o
obj-$(CONFIG_STM_DUMMY) += dummy_stm.o
diff --git a/drivers/hwtracing/stm/p_ost.c b/drivers/hwtracing/stm/p_ost.c
new file mode 100644
index 000000000000..d2174872b761
--- /dev/null
+++ b/drivers/hwtracing/stm/p_ost.c
@@ -0,0 +1,241 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ * MIPI OST framing protocol for STM devices.
+ */
+
+#include <linux/pid.h>
+#include <linux/sched/clock.h>
+#include <linux/slab.h>
+#include <linux/stm.h>
+#include "stm.h"
+
+/*
+ * OST Base Protocol Header
+ *
+ * Position Bits Field Name
+ * 0 8 STARTSIMPLE
+ * 1 8 Version
+ * 2 8 Entity ID
+ * 3 8 Protocol ID
+ */
+#define OST_FIELD_STARTSIMPLE 0
+#define OST_FIELD_VERSION 8
+#define OST_FIELD_ENTITY 16
+#define OST_FIELD_PROTOCOL 24
+
+#define OST_TOKEN_STARTSIMPLE 0x10
+#define OST_VERSION_MIPI1 0x10
+
+/* entity id to identify the source */
+#define OST_ENTITY_FTRACE 0x01
+#define OST_ENTITY_CONSOLE 0x02
+#define OST_ENTITY_DIAG 0xEE
+
+#define OST_CONTROL_PROTOCOL 0x0
+
+#define DATA_HEADER ((OST_TOKEN_STARTSIMPLE << OST_FIELD_STARTSIMPLE) | \
+ (OST_VERSION_MIPI1 << OST_FIELD_VERSION) | \
+ (OST_CONTROL_PROTOCOL << OST_FIELD_PROTOCOL))
+
+#define STM_MAKE_VERSION(ma, mi) (((ma) << 8) | (mi))
+#define STM_HEADER_MAGIC (0x5953)
+
+enum ost_entity_type {
+ OST_ENTITY_TYPE_NONE,
+ OST_ENTITY_TYPE_FTRACE,
+ OST_ENTITY_TYPE_CONSOLE,
+ OST_ENTITY_TYPE_DIAG,
+};
+
+static const char * const str_ost_entity_type[] = {
+ [OST_ENTITY_TYPE_NONE] = "none",
+ [OST_ENTITY_TYPE_FTRACE] = "ftrace",
+ [OST_ENTITY_TYPE_CONSOLE] = "console",
+ [OST_ENTITY_TYPE_DIAG] = "diag",
+};
+
+static const u32 ost_entity_value[] = {
+ [OST_ENTITY_TYPE_NONE] = 0,
+ [OST_ENTITY_TYPE_FTRACE] = OST_ENTITY_FTRACE,
+ [OST_ENTITY_TYPE_CONSOLE] = OST_ENTITY_CONSOLE,
+ [OST_ENTITY_TYPE_DIAG] = OST_ENTITY_DIAG,
+};
+
+struct ost_policy_node {
+ enum ost_entity_type entity_type;
+};
+
+struct ost_output {
+ struct ost_policy_node node;
+};
+
+/* Set default entity type as none */
+static void ost_policy_node_init(void *priv)
+{
+ struct ost_policy_node *pn = priv;
+
+ pn->entity_type = OST_ENTITY_TYPE_NONE;
+}
+
+static int ost_output_open(void *priv, struct stm_output *output)
+{
+ struct ost_policy_node *pn = priv;
+ struct ost_output *opriv;
+
+ opriv = kzalloc_obj(*opriv, GFP_ATOMIC);
+ if (!opriv)
+ return -ENOMEM;
+
+ memcpy(&opriv->node, pn, sizeof(opriv->node));
+ output->pdrv_private = opriv;
+ return 0;
+}
+
+static void ost_output_close(struct stm_output *output)
+{
+ kfree(output->pdrv_private);
+}
+
+static ssize_t ost_t_policy_entity_show(struct config_item *item,
+ char *page)
+{
+ struct ost_policy_node *pn = to_pdrv_policy_node(item);
+ ssize_t sz = 0;
+ int i;
+
+ for (i = 1; i < ARRAY_SIZE(str_ost_entity_type); i++) {
+ if (i == pn->entity_type)
+ sz += sysfs_emit_at(page, sz, "[%s] ", str_ost_entity_type[i]);
+ else
+ sz += sysfs_emit_at(page, sz, "%s ", str_ost_entity_type[i]);
+ }
+
+ sz += sysfs_emit_at(page, sz, "\n");
+ return sz;
+}
+
+static int entity_index(const char *str)
+{
+ int i;
+
+ for (i = 1; i < ARRAY_SIZE(str_ost_entity_type); i++) {
+ if (sysfs_streq(str, str_ost_entity_type[i]))
+ return i;
+ }
+
+ return 0;
+}
+
+static ssize_t
+ost_t_policy_entity_store(struct config_item *item, const char *page,
+ size_t count)
+{
+ struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex;
+ struct ost_policy_node *pn = to_pdrv_policy_node(item);
+ int i;
+
+ i = entity_index(page);
+ if (i) {
+ mutex_lock(mutexp);
+ pn->entity_type = i;
+ mutex_unlock(mutexp);
+ } else {
+ return -EINVAL;
+ }
+
+ return count;
+}
+CONFIGFS_ATTR(ost_t_policy_, entity);
+
+static struct configfs_attribute *ost_t_policy_attrs[] = {
+ &ost_t_policy_attr_entity,
+ NULL,
+};
+
+static ssize_t
+notrace ost_write(struct stm_data *data, struct stm_output *output,
+ unsigned int chan, const char *buf, size_t count,
+ struct stm_source_data *source)
+{
+ struct ost_output *op = output->pdrv_private;
+ unsigned int c = output->channel + chan;
+ unsigned int m = output->master;
+ const unsigned char nil = 0;
+ u32 header = DATA_HEADER;
+ struct trc_hdr {
+ u16 version;
+ u16 magic;
+ u32 cpu;
+ u64 timestamp;
+ u64 tgid;
+ } hdr;
+ ssize_t sz;
+
+ /*
+ * Identify the source by entity type.
+ * If entity type is not set, return error value.
+ */
+ if (op->node.entity_type)
+ header |= (ost_entity_value[op->node.entity_type] << OST_FIELD_ENTITY);
+ else
+ return -EINVAL;
+
+ /*
+ * STP framing rules for OST frames:
+ * * the first packet of the OST frame is marked;
+ * * the last packet is a FLAG with timestamped tag.
+ */
+ /* Message layout: HEADER / DATA / TAIL */
+ /* HEADER */
+ sz = data->packet(data, m, c, STP_PACKET_DATA, STP_PACKET_MARKED,
+ 4, (u8 *)&header);
+ if (sz <= 0)
+ return sz;
+
+ /* DATA */
+ hdr.version = STM_MAKE_VERSION(0, 3);
+ hdr.magic = STM_HEADER_MAGIC;
+ hdr.cpu = raw_smp_processor_id();
+ hdr.timestamp = sched_clock();
+ hdr.tgid = task_tgid_nr(current);
+ sz = stm_data_write(data, m, c, false, &hdr, sizeof(hdr));
+ if (sz <= 0)
+ return sz;
+
+ sz = stm_data_write(data, m, c, false, buf, count);
+
+ /* TAIL */
+ if (sz > 0)
+ data->packet(data, m, c, STP_PACKET_FLAG,
+ STP_PACKET_TIMESTAMPED, 0, &nil);
+
+ return sz;
+}
+
+static const struct stm_protocol_driver ost_pdrv = {
+ .owner = THIS_MODULE,
+ .name = "p_ost",
+ .priv_sz = sizeof(struct ost_policy_node),
+ .write = ost_write,
+ .policy_attr = ost_t_policy_attrs,
+ .output_open = ost_output_open,
+ .output_close = ost_output_close,
+ .policy_node_init = ost_policy_node_init,
+};
+
+static int ost_stm_init(void)
+{
+ return stm_register_protocol(&ost_pdrv);
+}
+module_init(ost_stm_init);
+
+static void ost_stm_exit(void)
+{
+ stm_unregister_protocol(&ost_pdrv);
+}
+module_exit(ost_stm_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("MIPI Open System Trace STM framing protocol driver");
---
base-commit: 80dd246accce631c328ea43294e53b2b2dd2aa32
change-id: 20260521-stm_p_ost-3489f42a9e8c
Best regards,
--
Yingchao Deng <yingchao.deng@oss.qualcomm.com>
^ permalink raw reply related
* Re: [PATCH v6 16/43] KVM: guest_memfd: Use actual size for invalidation in kvm_gmem_release()
From: Sean Christopherson @ 2026-05-21 12:59 UTC (permalink / raw)
To: Fuad Tabba
Cc: ackerleytng, aik, andrew.jones, binbin.wu, brauner, chao.p.peng,
david, ira.weiny, jmattson, jthoughton, michael.roth, oupton,
pankaj.gupta, qperret, rick.p.edgecombe, rientjes, shivankg,
steven.price, willy, wyihan, yan.y.zhao, forkloop, pratyush,
suzuki.poulose, aneesh.kumar, liam, Paolo Bonzini,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Shuah Khan, Shuah Khan,
Vishal Annapurve, Andrew Morton, Chris Li, Kairui Song,
Kemeng Shi, Nhat Pham, Baoquan He, Barry Song, Axel Rasmussen,
Yuanchu Xie, Wei Xu, Youngjun Park, Qi Zheng, Shakeel Butt,
Kiryl Shutsemau, Jason Gunthorpe, Vlastimil Babka, kvm,
linux-kernel, linux-trace-kernel, linux-doc, linux-kselftest,
linux-mm, linux-coco
In-Reply-To: <CA+EHjTxcadguOfOo7RpJVtAzcY5JAFZTbrAT_wcN6akMi8gCUg@mail.gmail.com>
On Thu, May 21, 2026, Fuad Tabba wrote:
> Hi Ackerley,
>
> On Thu, 7 May 2026 at 21:22, Ackerley Tng via B4 Relay
> <devnull+ackerleytng.google.com@kernel.org> wrote:
> >
> > From: Ackerley Tng <ackerleytng@google.com>
> >
> > __kvm_gmem_invalidate_begin() and __kvm_gmem_invalidate_end() actually do
> > not specially handle -1ul. -1ul is used as a huge number, which legal
> > indices do not exceed, and hence the invalidation works as expected.
> >
> > Since a later patch is going to make use of the exact range, calculate the
> > size of the guest_memfd inode and use it as the end range for invalidating
> > SPTEs.
> >
> > Signed-off-by: Ackerley Tng <ackerleytng@google.com>
>
> Want to look at what Sashiko has to say? Seems to be a real issue:
>
> https://sashiko.dev/#/patchset/20260507-gmem-inplace-conversion-v6-0-91ab5a8b19a4%40google.com?part=16
>
> If I understand correctly, the fix should simple: use
> check_add_overflow() to validate the offset and size parameters in
> kvm_gmem_bind()
>
> int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot,
> unsigned int fd, loff_t offset)
> {
> loff_t size = slot->npages << PAGE_SHIFT;
> + loff_t end;
> unsigned long start, end_index;
> struct gmem_file *f;
> ...
> - if (offset < 0 || !PAGE_ALIGNED(offset) ||
> - offset + size > i_size_read(inode))
> + if (offset < 0 || !PAGE_ALIGNED(offset) ||
> + check_add_overflow(offset, size, &end) ||
Eww, TIL I'm not a fan of check_add_overflow(). Burying an out-param in an
if-statement is nasty.
> + end > i_size_read(inode))
This is all rather silly. @offset and and @slot->npages are fundamentally
unsigned values. I don't see any reason to convert them to signed values, only
to convert them *back* to unsigned values (when stored in start/end, because xarrays
operate on "unsigned long" indices).
i_size_read() obviously has to return a positive value, so can't we just do this?
diff --git virt/kvm/guest_memfd.c virt/kvm/guest_memfd.c
index a35a55571a2d..9c6dbb54e800 100644
--- virt/kvm/guest_memfd.c
+++ virt/kvm/guest_memfd.c
@@ -640,9 +640,9 @@ int kvm_gmem_create(struct kvm *kvm, struct kvm_create_guest_memfd *args)
}
int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot,
- unsigned int fd, loff_t offset)
+ unsigned int fd, u64 offset)
{
- loff_t size = slot->npages << PAGE_SHIFT;
+ u64 size = slot->npages << PAGE_SHIFT;
unsigned long start, end;
struct gmem_file *f;
struct inode *inode;
@@ -664,8 +664,7 @@ int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot,
inode = file_inode(file);
- if (offset < 0 || !PAGE_ALIGNED(offset) ||
- offset + size > i_size_read(inode))
+ if (!PAGE_ALIGNED(offset) || offset + size > i_size_read(inode))
goto err;
filemap_invalidate_lock(inode->i_mapping);
diff --git virt/kvm/kvm_mm.h virt/kvm/kvm_mm.h
index 9fcc5d5b7f8d..3cb5ef86d0d9 100644
--- virt/kvm/kvm_mm.h
+++ virt/kvm/kvm_mm.h
@@ -72,7 +72,7 @@ int kvm_gmem_init(struct module *module);
void kvm_gmem_exit(void);
int kvm_gmem_create(struct kvm *kvm, struct kvm_create_guest_memfd *args);
int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot,
- unsigned int fd, loff_t offset);
+ unsigned int fd, u64 offset);
void kvm_gmem_unbind(struct kvm_memory_slot *slot);
#else
static inline int kvm_gmem_init(struct module *module)
@@ -80,9 +80,8 @@ static inline int kvm_gmem_init(struct module *module)
return 0;
}
static inline void kvm_gmem_exit(void) {};
-static inline int kvm_gmem_bind(struct kvm *kvm,
- struct kvm_memory_slot *slot,
- unsigned int fd, loff_t offset)
+static inline int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot,
+ unsigned int fd, u64 offset)
{
WARN_ON_ONCE(1);
return -EIO;
^ permalink raw reply related
* Re: [PATCH v2 00/25] Introduce meminspect
From: Mukesh Ojha @ 2026-05-21 12:20 UTC (permalink / raw)
To: Bjorn Andersson
Cc: Jonathan Corbet, Shuah Khan, Eugen Hristev, Arnd Bergmann,
Dennis Zhou, Tejun Heo, Christoph Lameter, Andrew Morton,
Thomas Gleixner, Peter Zijlstra, Anna-Maria Behnsen,
Frederic Weisbecker, Ingo Molnar, Juri Lelli, Vincent Guittot,
Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
Valentin Schneider, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Kees Cook, Brendan Jackman,
Johannes Weiner, Zi Yan, Chris Li, Kairui Song, Kemeng Shi,
Nhat Pham, Baoquan He, Barry Song, Youngjun Park, Petr Mladek,
John Ogness, Sergey Senozhatsky, Mathieu Poirier, Konrad Dybcio,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
workflows, linux-doc, linux-kernel, linux-arch, linux-mm,
linux-arm-msm, linux-remoteproc, devicetree
In-Reply-To: <abtlUQqMOxj5PwGB@baldur>
On Wed, Mar 18, 2026 at 09:55:29PM -0500, Bjorn Andersson wrote:
> On Mon, Mar 16, 2026 at 11:46:47PM +0530, Mukesh Ojha wrote:
> > On Sun, Mar 15, 2026 at 09:24:39PM -0500, Bjorn Andersson wrote:
> > > On Wed, Mar 11, 2026 at 01:45:44AM +0530, Mukesh Ojha wrote:
> [..]
> > > >, to get all the regions as
> > > > separate files. The tool from the host computer will list the regions
> > > > in the order they were downloaded.
> > > >
> > > > Once you have all the files simply use `cat` to put them all together,
> > > > in the order of the indexes. For my kernel config and setup, here is my
> > > > cat command : (you can use a script or something, I haven't done that so
> > > > far):
> > >
> > > So these need to be sorted in numerical order, by that number at the end
> > > of the file name?
> > >
> > > Do you manually punch these in? How do we make this user friendly?
> >
> > Yes, manually.. but I think we can do better. We could make
> > this more user‑friendly by using the section header and string table in
> > the md_KELF binary both of which existed in the earlier implementation.
> > Then, we can write an upstream‑friendly script that reads this KELF
> > metadata file, checks whether a binary with the registered name is
> > present, and stitches everything together to form a complete ELF that
> > the crash tool can consume. Let me know if you have any suggestion..
> >
>
> Can we somehow identify that these regions belong to the minidump and
> teach QDL to build the ELF for us?
Raised PR for the QDL https://github.com/linux-msm/qdl/pull/243 which
does not use numerating order and we can completely drop.
With that PR, QDL will be generating minidump.elf .
And I checked the latest crash tool with no extra patching and just the
--minimal option (as we do not have everything in the minidump) and just
dmesg.
$ ./crash --minimal minidump/vmlinux ./minidump/minidump.elf
crash 9.0.2++
Copyright (C) 2002-2026 Red Hat, Inc.
Copyright (C) 2004, 2005, 2006, 2010 IBM Corporation
Copyright (C) 1999-2006 Hewlett-Packard Co
Copyright (C) 2005, 2006, 2011, 2012 Fujitsu Limited
Copyright (C) 2006, 2007 VA Linux Systems Japan K.K.
Copyright (C) 2005, 2011, 2020-2024 NEC Corporation
Copyright (C) 1999, 2002, 2007 Silicon Graphics, Inc.
Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc.
Copyright (C) 2015, 2021 VMware, Inc.
This program is free software, covered by the GNU General Public License,
and you are welcome to change it and/or distribute copies of it under
certain conditions. Enter "help copying" to see the conditions.
This program has absolutely no warranty. Enter "help warranty" for details.
GNU gdb (GDB) 16.2
Copyright (C) 2024 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "--host=x86_64-pc-linux-gnu --target=aarch64-elf-linux".
Type "show configuration" for configuration details.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
NOTE: minimal mode commands: log, dis, rd, sym, eval, set, extend and exit
crash> log
[ 0.000000] Booting Linux on physical CPU 0x0000000000 [0x512f0030]
[ 0.000000] Linux version 7.1.0-rc2-next-20260504-00228-g2595b97d6061 (@f134cd6ce783) (aarch64-linux-gnu-gcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0, GNU ld (GNU Binutils for Ubuntu) 2.42) #11 SMP PREEMPT Fri May 15 09:34:41 UTC 2026
[ 0.000000] KASLR disabled on command line
[ 0.000000] random: crng init done
[ 0.000000] Machine model: Qualcomm Technologies, Inc. XXXX
[ 0.000000] earlycon: qcom_geni0 at MMIO 0x0000000000a9c000 (options '')
[ 0.000000] printk: legacy bootconsole [qcom_geni0] enabled
[ 0.000000] efi: UEFI not found.
...
..
[ 48.488296] sysrq: Trigger a crash
[ 48.492004] Kernel panic - not syncing: sysrq triggered crash
[ 48.497944] CPU: 3 UID: 0 PID: 363 Comm: sh Tainted: G W 7.1.0-rc2-next-20260504-00228-g2595b97d6061 #11 PREEMPT
[ 48.510055] Tainted: [W]=WARN
[ 48.513140] Hardware name: Qualcomm Technologies, Inc. XXXX
[ 48.519699] Call trace:
[ 48.522276] show_stack+0x18/0x24 (C)
[ 48.526089] dump_stack_lvl+0x34/0x8c
[ 48.529903] dump_stack+0x18/0x24
[ 48.533366] vpanic+0x47c/0x4dc
[ 48.536646] do_panic_on_target_cpu+0x0/0x1c
[ 48.541078] sysrq_reset_seq_param_set+0x0/0x94
[ 48.545788] __handle_sysrq+0xd4/0x1b8
[ 48.549679] write_sysrq_trigger+0xc0/0xd0
[ 48.553930] proc_reg_write+0x9c/0xf0
[ 48.557737] vfs_write+0xd4/0x358
[ 48.561187] ksys_write+0x6c/0x104
[ 48.564724] __arm64_sys_write+0x1c/0x28
[ 48.568809] invoke_syscall+0x54/0x10c
[ 48.572704] el0_svc_common.constprop.0+0xc0/0xe0
[ 48.577578] do_el0_svc+0x1c/0x28
[ 48.581025] el0_svc+0x38/0x138
[ 48.584317] el0t_64_sync_handler+0xa0/0xe4
[ 48.588660] el0t_64_sync+0x198/0x19c
[ 48.592474] SMP: stopping secondary CPUs
[ 48.796761] Kernel Offset: disabled
[ 48.806510] Memory Limit: none
crash>
--
-Mukesh Ojha
^ permalink raw reply
* [PATCH v7 3/8] docs/zh_CN: Add authorization.rst translation
From: Kefan Bai @ 2026-05-21 9:55 UTC (permalink / raw)
To: linux-usb, si.yanteng
Cc: gregkh, seakeel, alexs, dzm91, corbet, skhan, linux-doc, doubled
In-Reply-To: <cover.1779355170.git.baikefan@leap-io-kernel.com>
Translate .../usb/authorization.rst into Chinese
Update the translation through commit f176638af476
("USB: Remove Wireless USB and UWB documentation")
Reviewed-by: Yanteng Si <siyanteng@cqsoftware.com.cn>
Signed-off-by: Kefan Bai <baikefan@leap-io-kernel.com>
---
.../translations/zh_CN/usb/authorization.rst | 139 ++++++++++++++++++
.../translations/zh_CN/usb/index.rst | 2 +-
2 files changed, 140 insertions(+), 1 deletion(-)
create mode 100644 Documentation/translations/zh_CN/usb/authorization.rst
diff --git a/Documentation/translations/zh_CN/usb/authorization.rst b/Documentation/translations/zh_CN/usb/authorization.rst
new file mode 100644
index 000000000000..2aa311f6b967
--- /dev/null
+++ b/Documentation/translations/zh_CN/usb/authorization.rst
@@ -0,0 +1,139 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/usb/authorization.rst
+
+:翻译:
+
+ 白钶凡 Kefan Bai <baikefan@leap-io-kernel.com>
+
+:校译:
+
+
+=============================
+授权或禁止 USB 设备连接到系统
+=============================
+
+版权 (C) 2007 Inaky Perez-Gonzalez
+<inaky@linux.intel.com> 英特尔公司
+
+此功能允许你控制 USB 设备是否可以在系统中使用。
+借助它,你可以完全通过用户空间实现对 USB 设备的锁定。
+
+目前,当插入一个 USB 设备时,系统会对其进行配置,
+其接口会立即向用户开放。
+有了这项改动,只有在 root 授权设备完成配置后,
+设备才可被使用。
+
+
+用法
+====
+
+授权设备接入::
+
+ $ echo 1 > /sys/bus/usb/devices/DEVICE/authorized
+
+取消对设备的授权::
+
+ $ echo 0 > /sys/bus/usb/devices/DEVICE/authorized
+
+将新连接到 ``hostX`` 的设备默认设为未授权(即锁定)::
+
+ $ echo 0 > /sys/bus/usb/devices/usbX/authorized_default
+
+解除锁定::
+
+ $ echo 1 > /sys/bus/usb/devices/usbX/authorized_default
+
+默认情况下,所有 USB 设备都是授权的。
+向 ``authorized_default`` 属性写入 ``2`` 会使内核
+默认只授权连接到内部 USB 端口的设备。
+
+系统锁定示例(比较粗糙)
+------------------------
+
+假设你想实现一个锁定功能,只允许类型为 XYZ 的设备接入
+(例如某台带有外露 USB 端口的自助服务终端)::
+
+ 启动系统
+ rc.local ->
+
+ for host in /sys/bus/usb/devices/usb*
+ do
+ echo 0 > $host/authorized_default
+ done
+
+给 udev 挂一个脚本,用于处理新插入的 USB 设备::
+
+ if device_is_my_type $DEV
+ then
+ echo 1 > $device_path/authorized
+ done
+
+
+``device_is_my_type()`` 才是锁定方案真正见功夫的
+地方。仅仅检查 class、type 和 protocol 是否匹配
+某个值,是你能做出的最糟糕的安全验证之一;
+对想绕过它的人来说,这反而是最容易利用的方案。
+如果你需要真正安全的办法,那就该使用加密、
+证书认证之类的机制。把 USB 存储设备当作
+“钥匙”的一个简单例子可以是::
+
+ function device_is_my_type()
+ {
+ echo 1 > authorized # 暂时授权它
+ # FIXME: 确保没人能挂载它
+ mount DEVICENODE /mntpoint
+ sum=$(md5sum /mntpoint/.signature)
+ if [ $sum = $(cat /etc/lockdown/keysum) ]
+ then
+ echo "We are good, connected"
+ umount /mntpoint
+ # 再做一些额外处理,让其他人也能使用它
+ else
+ echo 0 > authorized
+ fi
+ }
+
+
+当然,这个例子很粗糙;真正要做的话,
+你会想用基于 PKI 的证书校验,这样就不必依赖
+共享密钥之类的东西。不过你应该已经明白意思了。
+任何拿到设备仿真工具包的人都能伪造描述符和设备信息。
+别信这个。
+
+接口授权
+--------
+
+也有类似的方法用于允许或拒绝特定 USB 接口。
+这使得你可以只阻止某个 USB 设备中的部分接口。
+
+授权接口::
+
+ $ echo 1 > /sys/bus/usb/devices/INTERFACE/authorized
+
+取消接口授权::
+
+ $ echo 0 > /sys/bus/usb/devices/INTERFACE/authorized
+
+也可以更改特定 USB 总线上新接口的默认授权值。
+
+默认允许接口::
+
+ $ echo 1 > /sys/bus/usb/devices/usbX/interface_authorized_default
+
+默认拒绝接口::
+
+ $ echo 0 > /sys/bus/usb/devices/usbX/interface_authorized_default
+
+默认情况下,
+``interface_authorized_default`` 位为 ``1``,
+因此所有接口默认都处于已授权状态。
+
+注意:
+ 如果把一个先前未授权的接口改为已授权,
+ 则必须通过将 ``INTERFACE`` 写入 ``/sys/bus/usb/drivers_probe``
+ 来手动触发驱动探测。
+
+对于需要多个接口的驱动程序,应先授权所有必需接口,
+然后再触发驱动探测。这样做可以避免副作用。
diff --git a/Documentation/translations/zh_CN/usb/index.rst b/Documentation/translations/zh_CN/usb/index.rst
index 686e5b0a9384..3480966fee19 100644
--- a/Documentation/translations/zh_CN/usb/index.rst
+++ b/Documentation/translations/zh_CN/usb/index.rst
@@ -18,10 +18,10 @@ USB 支持
:maxdepth: 1
acm
+ authorization
Todolist:
-* authorization
* chipidea
* dwc3
* ehci
--
2.54.0
^ permalink raw reply related
* Re: [PATCH v6 24/43] KVM: selftests: Rename guest_memfd{,_offset} to gmem_{fd,offset}
From: Fuad Tabba @ 2026-05-21 12:13 UTC (permalink / raw)
To: ackerleytng
Cc: aik, andrew.jones, binbin.wu, brauner, chao.p.peng, david,
ira.weiny, jmattson, jthoughton, michael.roth, oupton,
pankaj.gupta, qperret, rick.p.edgecombe, rientjes, shivankg,
steven.price, willy, wyihan, yan.y.zhao, forkloop, pratyush,
suzuki.poulose, aneesh.kumar, liam, Paolo Bonzini,
Sean Christopherson, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, x86, H. Peter Anvin, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Jonathan Corbet, Shuah Khan,
Shuah Khan, Vishal Annapurve, Andrew Morton, Chris Li,
Kairui Song, Kemeng Shi, Nhat Pham, Baoquan He, Barry Song,
Axel Rasmussen, Yuanchu Xie, Wei Xu, Youngjun Park, Qi Zheng,
Shakeel Butt, Kiryl Shutsemau, Jason Gunthorpe, Vlastimil Babka,
kvm, linux-kernel, linux-trace-kernel, linux-doc, linux-kselftest,
linux-mm, linux-coco
In-Reply-To: <20260507-gmem-inplace-conversion-v6-24-91ab5a8b19a4@google.com>
On Thu, 7 May 2026 at 21:23, Ackerley Tng via B4 Relay
<devnull+ackerleytng.google.com@kernel.org> wrote:
>
> From: Sean Christopherson <seanjc@google.com>
>
> Rename local variables and function parameters for the guest memory file
> descriptor and its offset to use a "gmem_" prefix instead of
> "guest_memfd_".
>
> No functional change intended.
>
> Signed-off-by: Sean Christopherson <seanjc@google.com>
> Signed-off-by: Ackerley Tng <ackerleytng@google.com>
Reviewed-by: Fuad Tabba <tabba@google.com>
Cheers,
/fuad
> ---
> tools/testing/selftests/kvm/include/kvm_util.h | 6 +++---
> tools/testing/selftests/kvm/lib/kvm_util.c | 26 +++++++++++++-------------
> 2 files changed, 16 insertions(+), 16 deletions(-)
>
> diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h
> index 2ecaaa0e99654..f19383376ee8e 100644
> --- a/tools/testing/selftests/kvm/include/kvm_util.h
> +++ b/tools/testing/selftests/kvm/include/kvm_util.h
> @@ -690,17 +690,17 @@ int __vm_set_user_memory_region(struct kvm_vm *vm, u32 slot, u32 flags,
> gpa_t gpa, u64 size, void *hva);
> void vm_set_user_memory_region2(struct kvm_vm *vm, u32 slot, u32 flags,
> gpa_t gpa, u64 size, void *hva,
> - u32 guest_memfd, u64 guest_memfd_offset);
> + u32 gmem_fd, u64 gmem_offset);
> int __vm_set_user_memory_region2(struct kvm_vm *vm, u32 slot, u32 flags,
> gpa_t gpa, u64 size, void *hva,
> - u32 guest_memfd, u64 guest_memfd_offset);
> + u32 gmem_fd, u64 gmem_offset);
>
> void vm_userspace_mem_region_add(struct kvm_vm *vm,
> enum vm_mem_backing_src_type src_type,
> gpa_t gpa, u32 slot, u64 npages, u32 flags);
> void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type,
> gpa_t gpa, u32 slot, u64 npages, u32 flags,
> - int guest_memfd_fd, u64 guest_memfd_offset);
> + int gmem_fd, u64 gmem_offset);
>
> #ifndef vm_arch_has_protected_memory
> static inline bool vm_arch_has_protected_memory(struct kvm_vm *vm)
> diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
> index df73b23a4c66a..11da9b7546d03 100644
> --- a/tools/testing/selftests/kvm/lib/kvm_util.c
> +++ b/tools/testing/selftests/kvm/lib/kvm_util.c
> @@ -947,7 +947,7 @@ void vm_set_user_memory_region(struct kvm_vm *vm, u32 slot, u32 flags,
>
> int __vm_set_user_memory_region2(struct kvm_vm *vm, u32 slot, u32 flags,
> gpa_t gpa, u64 size, void *hva,
> - u32 guest_memfd, u64 guest_memfd_offset)
> + u32 gmem_fd, u64 gmem_offset)
> {
> struct kvm_userspace_memory_region2 region = {
> .slot = slot,
> @@ -955,8 +955,8 @@ int __vm_set_user_memory_region2(struct kvm_vm *vm, u32 slot, u32 flags,
> .guest_phys_addr = gpa,
> .memory_size = size,
> .userspace_addr = (uintptr_t)hva,
> - .guest_memfd = guest_memfd,
> - .guest_memfd_offset = guest_memfd_offset,
> + .guest_memfd = gmem_fd,
> + .guest_memfd_offset = gmem_offset,
> };
>
> TEST_REQUIRE_SET_USER_MEMORY_REGION2();
> @@ -966,10 +966,10 @@ int __vm_set_user_memory_region2(struct kvm_vm *vm, u32 slot, u32 flags,
>
> void vm_set_user_memory_region2(struct kvm_vm *vm, u32 slot, u32 flags,
> gpa_t gpa, u64 size, void *hva,
> - u32 guest_memfd, u64 guest_memfd_offset)
> + u32 gmem_fd, u64 gmem_offset)
> {
> int ret = __vm_set_user_memory_region2(vm, slot, flags, gpa, size, hva,
> - guest_memfd, guest_memfd_offset);
> + gmem_fd, gmem_offset);
>
> TEST_ASSERT(!ret, "KVM_SET_USER_MEMORY_REGION2 failed, errno = %d (%s)",
> errno, strerror(errno));
> @@ -979,7 +979,7 @@ void vm_set_user_memory_region2(struct kvm_vm *vm, u32 slot, u32 flags,
> /* FIXME: This thing needs to be ripped apart and rewritten. */
> void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type,
> gpa_t gpa, u32 slot, u64 npages, u32 flags,
> - int guest_memfd, u64 guest_memfd_offset)
> + int gmem_fd, u64 gmem_offset)
> {
> int ret;
> struct userspace_mem_region *region;
> @@ -1055,12 +1055,12 @@ void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type,
> region->mmap_size += alignment;
>
> if (flags & KVM_MEM_GUEST_MEMFD) {
> - if (guest_memfd < 0) {
> - u32 guest_memfd_flags = 0;
> + if (gmem_fd < 0) {
> + u32 gmem_flags = 0;
>
> - TEST_ASSERT(!guest_memfd_offset,
> + TEST_ASSERT(!gmem_offset,
> "Offset must be zero when creating new guest_memfd");
> - guest_memfd = vm_create_guest_memfd(vm, mem_size, guest_memfd_flags);
> + gmem_fd = vm_create_guest_memfd(vm, mem_size, gmem_flags);
> } else {
> /*
> * Install a unique fd for each memslot so that the fd
> @@ -1068,11 +1068,11 @@ void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type,
> * needing to track if the fd is owned by the framework
> * or by the caller.
> */
> - guest_memfd = kvm_dup(guest_memfd);
> + gmem_fd = kvm_dup(gmem_fd);
> }
>
> - region->region.guest_memfd = guest_memfd;
> - region->region.guest_memfd_offset = guest_memfd_offset;
> + region->region.guest_memfd = gmem_fd;
> + region->region.guest_memfd_offset = gmem_offset;
> } else {
> region->region.guest_memfd = -1;
> }
>
> --
> 2.54.0.563.g4f69b47b94-goog
>
>
^ permalink raw reply
* Re: [PATCH v6 23/43] KVM: selftests: Create gmem fd before "regular" fd when adding memslot
From: Fuad Tabba @ 2026-05-21 12:11 UTC (permalink / raw)
To: ackerleytng
Cc: aik, andrew.jones, binbin.wu, brauner, chao.p.peng, david,
ira.weiny, jmattson, jthoughton, michael.roth, oupton,
pankaj.gupta, qperret, rick.p.edgecombe, rientjes, shivankg,
steven.price, willy, wyihan, yan.y.zhao, forkloop, pratyush,
suzuki.poulose, aneesh.kumar, liam, Paolo Bonzini,
Sean Christopherson, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, x86, H. Peter Anvin, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Jonathan Corbet, Shuah Khan,
Shuah Khan, Vishal Annapurve, Andrew Morton, Chris Li,
Kairui Song, Kemeng Shi, Nhat Pham, Baoquan He, Barry Song,
Axel Rasmussen, Yuanchu Xie, Wei Xu, Youngjun Park, Qi Zheng,
Shakeel Butt, Kiryl Shutsemau, Jason Gunthorpe, Vlastimil Babka,
kvm, linux-kernel, linux-trace-kernel, linux-doc, linux-kselftest,
linux-mm, linux-coco
In-Reply-To: <20260507-gmem-inplace-conversion-v6-23-91ab5a8b19a4@google.com>
On Thu, 7 May 2026 at 21:23, Ackerley Tng via B4 Relay
<devnull+ackerleytng.google.com@kernel.org> wrote:
>
> From: Sean Christopherson <seanjc@google.com>
>
> When adding a memslot associated a guest_memfd instance, create/dup the
> guest_memfd before creating the "normal" backing file. This will allow
> dup'ing the gmem fd as the normal fd when guest_memfd supports mmap(),
> i.e. to make guest_memfd the _only_ backing source for the memslot.
>
> Signed-off-by: Sean Christopherson <seanjc@google.com>
> Signed-off-by: Ackerley Tng <ackerleytng@google.com>
Reviewed-by: Fuad Tabba <tabba@google.com>
Cheers,
/fuad
> ---
> tools/testing/selftests/kvm/lib/kvm_util.c | 45 +++++++++++++++---------------
> 1 file changed, 23 insertions(+), 22 deletions(-)
>
> diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
> index 2a76eca7029d3..df73b23a4c66a 100644
> --- a/tools/testing/selftests/kvm/lib/kvm_util.c
> +++ b/tools/testing/selftests/kvm/lib/kvm_util.c
> @@ -1054,6 +1054,29 @@ void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type,
> if (alignment > 1)
> region->mmap_size += alignment;
>
> + if (flags & KVM_MEM_GUEST_MEMFD) {
> + if (guest_memfd < 0) {
> + u32 guest_memfd_flags = 0;
> +
> + TEST_ASSERT(!guest_memfd_offset,
> + "Offset must be zero when creating new guest_memfd");
> + guest_memfd = vm_create_guest_memfd(vm, mem_size, guest_memfd_flags);
> + } else {
> + /*
> + * Install a unique fd for each memslot so that the fd
> + * can be closed when the region is deleted without
> + * needing to track if the fd is owned by the framework
> + * or by the caller.
> + */
> + guest_memfd = kvm_dup(guest_memfd);
> + }
> +
> + region->region.guest_memfd = guest_memfd;
> + region->region.guest_memfd_offset = guest_memfd_offset;
> + } else {
> + region->region.guest_memfd = -1;
> + }
> +
> region->fd = -1;
> if (backing_src_is_shared(src_type))
> region->fd = kvm_memfd_alloc(region->mmap_size,
> @@ -1083,28 +1106,6 @@ void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type,
>
> region->backing_src_type = src_type;
>
> - if (flags & KVM_MEM_GUEST_MEMFD) {
> - if (guest_memfd < 0) {
> - u32 guest_memfd_flags = 0;
> - TEST_ASSERT(!guest_memfd_offset,
> - "Offset must be zero when creating new guest_memfd");
> - guest_memfd = vm_create_guest_memfd(vm, mem_size, guest_memfd_flags);
> - } else {
> - /*
> - * Install a unique fd for each memslot so that the fd
> - * can be closed when the region is deleted without
> - * needing to track if the fd is owned by the framework
> - * or by the caller.
> - */
> - guest_memfd = kvm_dup(guest_memfd);
> - }
> -
> - region->region.guest_memfd = guest_memfd;
> - region->region.guest_memfd_offset = guest_memfd_offset;
> - } else {
> - region->region.guest_memfd = -1;
> - }
> -
> region->unused_phy_pages = sparsebit_alloc();
> if (vm_arch_has_protected_memory(vm))
> region->protected_phy_pages = sparsebit_alloc();
>
> --
> 2.54.0.563.g4f69b47b94-goog
>
>
^ permalink raw reply
* Re: [PATCH v2 4/4] cpufreq: Use policy->min/max init as QoS request
From: Pierre Gondois @ 2026-05-21 11:58 UTC (permalink / raw)
To: Jie Zhan, linux-kernel
Cc: Lifeng Zheng, Ionela Voinescu, Sumit Gupta, Zhongqiu Han,
Rafael J. Wysocki, Viresh Kumar, Jonathan Corbet, Shuah Khan,
Huang Rui, Mario Limonciello, Perry Yuan, K Prateek Nayak,
Srinivas Pandruvada, Len Brown, Saravana Kannan, linux-pm,
linux-doc
In-Reply-To: <112c6947-d496-46a9-8561-bbc23793e615@hisilicon.com>
Hello Jie,
On 5/20/26 10:38, Jie Zhan wrote:
> On 5/11/2026 9:55 PM, Pierre Gondois wrote:
>> Consider policy->min/max being set in the driver .init()
>> callback as a QoS request. Impacted driver are:
>> - gx-suspmod.c (min)
>> - cppc-cpufreq.c (min)
>> - longrun.c (min/max)
>>
>> Update the documentation accordingly.
>>
>> Signed-off-by: Pierre Gondois<pierre.gondois@arm.com>
>> ---
>> Documentation/cpu-freq/cpu-drivers.rst | 10 ++++++++--
>> drivers/cpufreq/cpufreq.c | 12 ++++++++++--
>> 2 files changed, 18 insertions(+), 4 deletions(-)
>>
>> diff --git a/Documentation/cpu-freq/cpu-drivers.rst b/Documentation/cpu-freq/cpu-drivers.rst
>> index c5635ac3de547..ab4f3c0f3a89b 100644
>> --- a/Documentation/cpu-freq/cpu-drivers.rst
>> +++ b/Documentation/cpu-freq/cpu-drivers.rst
>> @@ -114,8 +114,14 @@ Then, the driver must fill in the following values:
>> |policy->cur | The current operating frequency of |
>> | | this CPU (if appropriate) |
>> +-----------------------------------+--------------------------------------+
>> -|policy->min, | |
>> -|policy->max, | |
>> +|policy->min | If set by the driver in ->init(), |
>> +| | used as initial minimum frequency |
>> +| | QoS request. |
>> ++-----------------------------------+--------------------------------------+
>> +|policy->max | If set by the driver in ->init(), |
>> +| | used as initial maximum frequency |
>> +| | QoS request. |
>> ++-----------------------------------+--------------------------------------+
> Hi Pierre,
>
> Trivial bit: add the general meaning alongside its driver usage at the init
> stage, and mention it defaults to cpuinfo_min/max_freq if not set?
>
> I mean something like:
> The minimum/maximum scaling frequency. If set by the driver in ->init(),
> used as initial minimum/maximum frequency QoS request; otherwise, follow
> policy->cpuinfo.min/max_freq.
Just one NIT, policy->min/max should follow the min/max allowed freq.
the cpufreq driver can set. E.g. a thermal constraint can impact it (cf.
dtpm_cpu.c).
Would this fit ?
The minimum/maximum scaling frequency. If set by the driver in ->init(),
used as initial minimum/maximum frequency QoS request; otherwise, follow
the min/max allowed freq. the cpufreq driver can set.
> Thanks,
> Jie
>> |policy->policy and, if necessary, | |
>> |policy->governor | must contain the "default policy" for|
>> | | this CPU. A few moments later, |
> [ ... ]
^ permalink raw reply
* Re: [PATCH v2 4/4] cpufreq: Use policy->min/max init as QoS request
From: Pierre Gondois @ 2026-05-21 11:58 UTC (permalink / raw)
To: zhenglifeng (A), linux-kernel
Cc: Jie Zhan, Ionela Voinescu, Sumit Gupta, Zhongqiu Han,
Rafael J. Wysocki, Viresh Kumar, Jonathan Corbet, Shuah Khan,
Huang Rui, Mario Limonciello, Perry Yuan, K Prateek Nayak,
Srinivas Pandruvada, Len Brown, Saravana Kannan, linux-pm,
linux-doc
In-Reply-To: <05a5a8a3-7153-460d-86f8-d2be04062d6b@huawei.com>
Hello Lifeng,
On 5/21/26 13:26, zhenglifeng (A) wrote:
> On 5/11/2026 9:55 PM, Pierre Gondois wrote:
>> Consider policy->min/max being set in the driver .init()
>> callback as a QoS request. Impacted driver are:
>> - gx-suspmod.c (min)
>> - cppc-cpufreq.c (min)
>> - longrun.c (min/max)
>>
>> Update the documentation accordingly.
>>
>> Signed-off-by: Pierre Gondois<pierre.gondois@arm.com>
>> ---
>> Documentation/cpu-freq/cpu-drivers.rst | 10 ++++++++--
>> drivers/cpufreq/cpufreq.c | 12 ++++++++++--
>> 2 files changed, 18 insertions(+), 4 deletions(-)
>>
>> diff --git a/Documentation/cpu-freq/cpu-drivers.rst b/Documentation/cpu-freq/cpu-drivers.rst
>> index c5635ac3de547..ab4f3c0f3a89b 100644
>> --- a/Documentation/cpu-freq/cpu-drivers.rst
>> +++ b/Documentation/cpu-freq/cpu-drivers.rst
>> @@ -114,8 +114,14 @@ Then, the driver must fill in the following values:
>> |policy->cur | The current operating frequency of |
>> | | this CPU (if appropriate) |
>> +-----------------------------------+--------------------------------------+
>> -|policy->min, | |
>> -|policy->max, | |
>> +|policy->min | If set by the driver in ->init(), |
>> +| | used as initial minimum frequency |
>> +| | QoS request. |
>> ++-----------------------------------+--------------------------------------+
>> +|policy->max | If set by the driver in ->init(), |
>> +| | used as initial maximum frequency |
>> +| | QoS request. |
>> ++-----------------------------------+--------------------------------------+
>> |policy->policy and, if necessary, | |
>> |policy->governor | must contain the "default policy" for|
>> | | this CPU. A few moments later, |
>> diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
>> index 9e2d9d3fc5351..9a005367ed87b 100644
>> --- a/drivers/cpufreq/cpufreq.c
>> +++ b/drivers/cpufreq/cpufreq.c
>> @@ -1399,8 +1399,16 @@ static void cpufreq_policy_free(struct cpufreq_policy *policy)
>>
>> static int cpufreq_policy_init_qos(struct cpufreq_policy *policy)
>> {
>> + unsigned int min_freq, max_freq;
>> int ret;
>>
>> + /* Use policy->min/max set by the driver as QoS requests. */
>> + min_freq = max(FREQ_QOS_MIN_DEFAULT_VALUE, policy->min);
>> + if (policy->max)
>> + max_freq = min(FREQ_QOS_MAX_DEFAULT_VALUE, policy->max);
>> + else
>> + max_freq = FREQ_QOS_MAX_DEFAULT_VALUE;
>> +
> Can't see the point of this. Why not just use policy->max and policy->min
> to init qos?
With patch [3/4] "cpufreq: Remove driver default policy->min/max init",
some drivers don't set policy->min/max. For the max value, we would end-up
with a max QoS constraint of 0.
If we were to use cpuinfo.max_freq instead, then we this would bring us
back to:
521223d8b3ec ("cpufreq: Fix initialization of min and max
frequency QoS requests")
>> /*
>> * If the driver didn't set policy->min/max, set them as
>> * they are used to clamp frequency requests.
>> @@ -1418,12 +1426,12 @@ static int cpufreq_policy_init_qos(struct cpufreq_policy *policy)
>> }
>>
>> ret = freq_qos_add_request(&policy->constraints, &policy->min_freq_req,
>> - FREQ_QOS_MIN, FREQ_QOS_MIN_DEFAULT_VALUE);
>> + FREQ_QOS_MIN, min_freq);
>> if (ret < 0)
>> return ret;
>>
>> ret = freq_qos_add_request(&policy->constraints, &policy->max_freq_req,
>> - FREQ_QOS_MAX, FREQ_QOS_MAX_DEFAULT_VALUE);
>> + FREQ_QOS_MAX, max_freq);
>> if (ret < 0)
>> return ret;
>>
^ permalink raw reply
* Re: [PATCH v2 4/4] cpufreq: Use policy->min/max init as QoS request
From: Pierre Gondois @ 2026-05-21 11:58 UTC (permalink / raw)
To: Viresh Kumar
Cc: linux-kernel, Jie Zhan, Lifeng Zheng, Ionela Voinescu,
Sumit Gupta, Zhongqiu Han, Rafael J. Wysocki, Jonathan Corbet,
Shuah Khan, Huang Rui, Mario Limonciello, Perry Yuan,
K Prateek Nayak, Srinivas Pandruvada, Len Brown, Saravana Kannan,
linux-pm, linux-doc
In-Reply-To: <bflxwyho5epheovbjnzlsvgvoitaqjbiv7kxcwbnoiz2nlmuvv@dtunrpupeyie>
Hello Viresh,
On 5/20/26 12:03, Viresh Kumar wrote:
> On 11-05-26, 15:55, Pierre Gondois wrote:
>> @@ -1399,8 +1399,16 @@ static void cpufreq_policy_free(struct cpufreq_policy *policy)
>>
>> static int cpufreq_policy_init_qos(struct cpufreq_policy *policy)
>> {
>> + unsigned int min_freq, max_freq;
>> int ret;
>>
>> + /* Use policy->min/max set by the driver as QoS requests. */
>> + min_freq = max(FREQ_QOS_MIN_DEFAULT_VALUE, policy->min);
>> + if (policy->max)
>> + max_freq = min(FREQ_QOS_MAX_DEFAULT_VALUE, policy->max);
>> + else
>> + max_freq = FREQ_QOS_MAX_DEFAULT_VALUE;
>> +
> Why is this required to be done before setting policy->min/max ? And
> so I don't think patch 1/4 is required at all.
Sorry if I misunderstand, but if we do:
"""
/*
* If the driver didn't set policy->min/max, set them as
* they are used to clamp frequency requests.
*/
policy->min = policy->min ? policy->min : policy->cpuinfo.min_freq;
policy->max = policy->max ? policy->max : policy->cpuinfo.max_freq;
/* Use policy->min/max set by the driver as QoS requests. */
min_freq = max(FREQ_QOS_MIN_DEFAULT_VALUE, policy->min);
if (policy->max)
max_freq = min(FREQ_QOS_MAX_DEFAULT_VALUE, policy->max);
else
max_freq = FREQ_QOS_MAX_DEFAULT_VALUE;
"""
then drivers that don't set policy->min/max in their .init() callback
will end up with a QoS constraint of:
[cpuinfo.min_freq:cpuinfo.max_freq].
This would bring us to what the following patch tried to solve:
521223d8b3ec ("cpufreq: Fix initialization of min and max
frequency QoS requests")
------
About removing patch [1/4], Zhongqiu noted that policy->min/max should
be set before the CPUFREQ_CREATE_POLICY notifier [1].
I then thought it would be better to save policy->min/max values
that are meant to become QoS constraint:
- as close as possible to the cpufreq_driver->init() call
- in a separate function, to do all the QoS creation in a separate
function.
[1]
https://lore.kernel.org/all/73fac9ca-451d-49f0-b9c7-5ef6bc0119bf@oss.qualcomm.com/
>> /*
>> * If the driver didn't set policy->min/max, set them as
>> * they are used to clamp frequency requests.
>> @@ -1418,12 +1426,12 @@ static int cpufreq_policy_init_qos(struct cpufreq_policy *policy)
>> }
>>
>> ret = freq_qos_add_request(&policy->constraints, &policy->min_freq_req,
>> - FREQ_QOS_MIN, FREQ_QOS_MIN_DEFAULT_VALUE);
>> + FREQ_QOS_MIN, min_freq);
>> if (ret < 0)
>> return ret;
>>
>> ret = freq_qos_add_request(&policy->constraints, &policy->max_freq_req,
>> - FREQ_QOS_MAX, FREQ_QOS_MAX_DEFAULT_VALUE);
>> + FREQ_QOS_MAX, max_freq);
>> if (ret < 0)
>> return ret;
>>
>> --
>> 2.43.0
^ permalink raw reply
* RE: [PATCH v12 5/6] iio: adc: ad4691: add oversampling support
From: Sabau, Radu bogdan @ 2026-05-21 11:32 UTC (permalink / raw)
To: Sabau, Radu bogdan, Lars-Peter Clausen, Hennerich, Michael,
Jonathan Cameron, David Lechner, Sa, Nuno, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Liam Girdwood, Mark Brown, Linus Walleij,
Bartosz Golaszewski, Philipp Zabel, Jonathan Corbet, Shuah Khan
Cc: linux-iio@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org,
linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org
In-Reply-To: <20260519-ad4692-multichannel-sar-adc-driver-v12-5-5b335162aa51@analog.com>
> -----Original Message-----
> From: Radu Sabau via B4 Relay <devnull+radu.sabau.analog.com@kernel.org>
> Sent: Tuesday, May 19, 2026 3:20 PM
...
>
> + iio_for_each_active_channel(indio_dev, bit) {
> + ret = regmap_write(st->regmap,
> AD4691_ACC_DEPTH_IN(bit), st->osr[bit]);
Unfortunately enough, I think a v13 will come, too...
Had a look again on what Sashiko had to say, and seeing the sampling frequency
shared_by_all comment again made me have a deeper look see how the code could
be commented so he wouldn't complain about this anymore, and...
Perhaps he is a bit right after all. I found a section stating that in standard
sequencer mode (which the driver uses right now), all the channels actually use
the ACC_DEPTH_IN0 for osr, and so changing ACC_DEPTH_INn for other channels
doesn't really do much. And so I tested this selecting both voltage0 and voltage1
for sampling with osr4 for voltage0 and osr1 for voltage1 and with a 100kHz osc freq
indeed DR fell after approximately 80us which points out both channels were actually
using OSR of 4. Perhaps the OSR should be shared by all and therefore the
sampling frequency would also be shared by all, right?
The usage of internal_osc_freq and pre-computed freq values depending on osr would
stay the same since those are still correct anyway.
What's your opinion on this?
Radu
^ permalink raw reply
* Re: [PATCH v2 4/4] cpufreq: Use policy->min/max init as QoS request
From: zhenglifeng (A) @ 2026-05-21 11:26 UTC (permalink / raw)
To: Pierre Gondois, linux-kernel
Cc: Jie Zhan, Ionela Voinescu, Sumit Gupta, Zhongqiu Han,
Rafael J. Wysocki, Viresh Kumar, Jonathan Corbet, Shuah Khan,
Huang Rui, Mario Limonciello, Perry Yuan, K Prateek Nayak,
Srinivas Pandruvada, Len Brown, Saravana Kannan, linux-pm,
linux-doc
In-Reply-To: <20260511135538.522653-5-pierre.gondois@arm.com>
On 5/11/2026 9:55 PM, Pierre Gondois wrote:
> Consider policy->min/max being set in the driver .init()
> callback as a QoS request. Impacted driver are:
> - gx-suspmod.c (min)
> - cppc-cpufreq.c (min)
> - longrun.c (min/max)
>
> Update the documentation accordingly.
>
> Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
> ---
> Documentation/cpu-freq/cpu-drivers.rst | 10 ++++++++--
> drivers/cpufreq/cpufreq.c | 12 ++++++++++--
> 2 files changed, 18 insertions(+), 4 deletions(-)
>
> diff --git a/Documentation/cpu-freq/cpu-drivers.rst b/Documentation/cpu-freq/cpu-drivers.rst
> index c5635ac3de547..ab4f3c0f3a89b 100644
> --- a/Documentation/cpu-freq/cpu-drivers.rst
> +++ b/Documentation/cpu-freq/cpu-drivers.rst
> @@ -114,8 +114,14 @@ Then, the driver must fill in the following values:
> |policy->cur | The current operating frequency of |
> | | this CPU (if appropriate) |
> +-----------------------------------+--------------------------------------+
> -|policy->min, | |
> -|policy->max, | |
> +|policy->min | If set by the driver in ->init(), |
> +| | used as initial minimum frequency |
> +| | QoS request. |
> ++-----------------------------------+--------------------------------------+
> +|policy->max | If set by the driver in ->init(), |
> +| | used as initial maximum frequency |
> +| | QoS request. |
> ++-----------------------------------+--------------------------------------+
> |policy->policy and, if necessary, | |
> |policy->governor | must contain the "default policy" for|
> | | this CPU. A few moments later, |
> diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
> index 9e2d9d3fc5351..9a005367ed87b 100644
> --- a/drivers/cpufreq/cpufreq.c
> +++ b/drivers/cpufreq/cpufreq.c
> @@ -1399,8 +1399,16 @@ static void cpufreq_policy_free(struct cpufreq_policy *policy)
>
> static int cpufreq_policy_init_qos(struct cpufreq_policy *policy)
> {
> + unsigned int min_freq, max_freq;
> int ret;
>
> + /* Use policy->min/max set by the driver as QoS requests. */
> + min_freq = max(FREQ_QOS_MIN_DEFAULT_VALUE, policy->min);
> + if (policy->max)
> + max_freq = min(FREQ_QOS_MAX_DEFAULT_VALUE, policy->max);
> + else
> + max_freq = FREQ_QOS_MAX_DEFAULT_VALUE;
> +
Can't see the point of this. Why not just use policy->max and policy->min
to init qos?
> /*
> * If the driver didn't set policy->min/max, set them as
> * they are used to clamp frequency requests.
> @@ -1418,12 +1426,12 @@ static int cpufreq_policy_init_qos(struct cpufreq_policy *policy)
> }
>
> ret = freq_qos_add_request(&policy->constraints, &policy->min_freq_req,
> - FREQ_QOS_MIN, FREQ_QOS_MIN_DEFAULT_VALUE);
> + FREQ_QOS_MIN, min_freq);
> if (ret < 0)
> return ret;
>
> ret = freq_qos_add_request(&policy->constraints, &policy->max_freq_req,
> - FREQ_QOS_MAX, FREQ_QOS_MAX_DEFAULT_VALUE);
> + FREQ_QOS_MAX, max_freq);
> if (ret < 0)
> return ret;
>
^ permalink raw reply
* [PATCH v3 4/4] selftests/mm: rewrite gup_test as a standalone harness-based selftest
From: Sarthak Sharma @ 2026-05-21 11:18 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand
Cc: Jonathan Corbet, Lorenzo Stoakes, Liam R . Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Shuah Khan, Shuah Khan, Jason Gunthorpe, John Hubbard, Peter Xu,
Leon Romanovsky, Zi Yan, Baolin Wang, Nico Pache, Ryan Roberts,
Dev Jain, Barry Song, Lance Yang, Mark Brown, linux-kernel,
linux-mm, linux-kselftest, linux-doc, Sarthak Sharma
In-Reply-To: <20260521111801.173019-1-sarthak.sharma@arm.com>
Rewrite gup_test.c using kselftest_harness.h. The new test covers 12
mapping configurations: THP on, THP off and hugetlb, each across
private/shared and read/write variants. It runs seven test cases per
variant: get_user_pages, get_user_pages_fast, pin_user_pages,
pin_user_pages_fast, pin_user_pages_longterm, and DUMP_USER_PAGES_TEST
via both get and pin.
Each test case sweeps four nr_pages_per_call values: 1, 512, 123, and
all pages. This preserves the old run_gup_matrix() sweep: 12 mapping
combinations x 5 GUP/PUP operations x 4 batch sizes = 240 ioctl sweeps.
It also expands DUMP_USER_PAGES_TEST coverage from one standalone
invocation to 12 variants x 2 dump modes x 4 batch sizes = 96
additional sweeps.
Preserve the old sparse dump coverage from run_vmtests.sh with one
standalone test that exercises DUMP_USER_PAGES_TEST with page indices
0, 19 and 0x1000. This brings the total to 85 TAP-reported cases
(12 x 7 + 1) and 337 ioctl sweeps (240 + 96 + 1).
On a Radxa Orion O6 board, ./gup_test completes in 5.39s on average
over 10 runs (range: 5.33s - 5.48s).
Update run_vmtests.sh: remove run_gup_matrix() and the multiple flagged
invocations of gup_test, replacing them with a single unconditional
invocation. Benchmark functionality is handled by tools/mm/gup_bench
introduced in the previous patch.
Update Documentation/core-api/pin_user_pages.rst to reflect the new
harness-based gup_test interface rather than command-line flag
invocations.
Suggested-by: David Hildenbrand (Arm) <david@kernel.org>
Signed-off-by: Sarthak Sharma <sarthak.sharma@arm.com>
---
Documentation/core-api/pin_user_pages.rst | 12 +-
tools/testing/selftests/mm/gup_test.c | 584 ++++++++++++++--------
tools/testing/selftests/mm/run_vmtests.sh | 37 +-
3 files changed, 376 insertions(+), 257 deletions(-)
diff --git a/Documentation/core-api/pin_user_pages.rst b/Documentation/core-api/pin_user_pages.rst
index c16ca163b55e..ea722adf22cc 100644
--- a/Documentation/core-api/pin_user_pages.rst
+++ b/Documentation/core-api/pin_user_pages.rst
@@ -230,10 +230,16 @@ This file::
tools/testing/selftests/mm/gup_test.c
-has the following new calls to exercise the new pin*() wrapper functions:
+contains the following test cases to exercise pin_user_pages*():
-* PIN_FAST_BENCHMARK (./gup_test -a)
-* PIN_BASIC_TEST (./gup_test -b)
+* pin_user_pages via PIN_BASIC_TEST
+* pin_user_pages_fast via PIN_FAST_BENCHMARK
+* pin_user_pages_longterm via PIN_LONGTERM_BENCHMARK
+
+Run with::
+
+ make -C tools/testing/selftests/mm
+ ./tools/testing/selftests/mm/gup_test
You can monitor how many total dma-pinned pages have been acquired and released
since the system was booted, via two new /proc/vmstat entries: ::
diff --git a/tools/testing/selftests/mm/gup_test.c b/tools/testing/selftests/mm/gup_test.c
index 803ab829a841..3f6626fe94a2 100644
--- a/tools/testing/selftests/mm/gup_test.c
+++ b/tools/testing/selftests/mm/gup_test.c
@@ -9,268 +9,416 @@
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
-#include <pthread.h>
-#include <assert.h>
#include <mm/gup_test.h>
#include <mm/hugepage_settings.h>
#include "kselftest.h"
#include "vm_util.h"
+#include "kselftest_harness.h"
#define MB (1UL << 20)
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+#endif
+
/* Just the flags we need, copied from the kernel internals. */
#define FOLL_WRITE 0x01 /* check pte is writable */
+/* Page counts exercising single, THP-batch, partial, and full-mapping GUP. */
+static const int nr_pages_list[] = { 1, 512, 123, -1 };
+
#define GUP_TEST_FILE "/sys/kernel/debug/gup_test"
-static unsigned long cmd = GUP_FAST_BENCHMARK;
-static int gup_fd, repeats = 1;
-static unsigned long size = 128 * MB;
-/* Serialize prints */
-static pthread_mutex_t print_mutex = PTHREAD_MUTEX_INITIALIZER;
+FIXTURE(gup_test)
+{
+ int gup_fd;
+ char *addr;
+ unsigned long size;
+};
+
+FIXTURE_VARIANT(gup_test)
+{
+ bool thp;
+ bool hugetlb;
+ bool write;
+ bool shared;
+};
+
+FIXTURE_VARIANT_ADD(gup_test, private_write)
+{
+ .thp = false,
+ .hugetlb = false,
+ .write = true,
+ .shared = false,
+};
+
+FIXTURE_VARIANT_ADD(gup_test, private_readonly)
+{
+ .thp = false,
+ .hugetlb = false,
+ .write = false,
+ .shared = false,
+};
+
+FIXTURE_VARIANT_ADD(gup_test, private_write_thp)
+{
+ .thp = true,
+ .hugetlb = false,
+ .write = true,
+ .shared = false,
+};
+
+FIXTURE_VARIANT_ADD(gup_test, private_readonly_thp)
+{
+ .thp = true,
+ .hugetlb = false,
+ .write = false,
+ .shared = false,
+};
+
+FIXTURE_VARIANT_ADD(gup_test, private_write_hugetlb)
+{
+ .thp = false,
+ .hugetlb = true,
+ .write = true,
+ .shared = false,
+};
+
+FIXTURE_VARIANT_ADD(gup_test, private_readonly_hugetlb)
+{
+ .thp = false,
+ .hugetlb = true,
+ .write = false,
+ .shared = false,
+};
+
+FIXTURE_VARIANT_ADD(gup_test, shared_write)
+{
+ .thp = false,
+ .hugetlb = false,
+ .write = true,
+ .shared = true,
+};
+
+FIXTURE_VARIANT_ADD(gup_test, shared_readonly)
+{
+ .thp = false,
+ .hugetlb = false,
+ .write = false,
+ .shared = true,
+};
+
+FIXTURE_VARIANT_ADD(gup_test, shared_write_thp)
+{
+ .thp = true,
+ .hugetlb = false,
+ .write = true,
+ .shared = true,
+};
-static char *cmd_to_str(unsigned long cmd)
+FIXTURE_VARIANT_ADD(gup_test, shared_readonly_thp)
{
- switch (cmd) {
- case GUP_FAST_BENCHMARK:
- return "GUP_FAST_BENCHMARK";
- case PIN_FAST_BENCHMARK:
- return "PIN_FAST_BENCHMARK";
- case PIN_LONGTERM_BENCHMARK:
- return "PIN_LONGTERM_BENCHMARK";
- case GUP_BASIC_TEST:
- return "GUP_BASIC_TEST";
- case PIN_BASIC_TEST:
- return "PIN_BASIC_TEST";
- case DUMP_USER_PAGES_TEST:
- return "DUMP_USER_PAGES_TEST";
+ .thp = true,
+ .hugetlb = false,
+ .write = false,
+ .shared = true,
+};
+
+FIXTURE_VARIANT_ADD(gup_test, shared_write_hugetlb)
+{
+ .thp = false,
+ .hugetlb = true,
+ .write = true,
+ .shared = true,
+};
+
+FIXTURE_VARIANT_ADD(gup_test, shared_readonly_hugetlb)
+{
+ .thp = false,
+ .hugetlb = true,
+ .write = false,
+ .shared = true,
+};
+
+FIXTURE_SETUP(gup_test)
+{
+ int mmap_flags = MAP_PRIVATE;
+ int zero_fd;
+ char *p;
+
+ self->size = variant->hugetlb ? 256 * MB : 128 * MB;
+
+ /* Check for hugetlb */
+ if (variant->hugetlb) {
+ unsigned long hp_size = default_huge_page_size();
+
+ if (!hp_size)
+ SKIP(return, "HugeTLB not available\n");
+
+ self->size = (self->size + hp_size - 1) & ~(hp_size - 1);
+ if (!hugetlb_setup_default(self->size / hp_size)) {
+ hugetlb_restore_settings();
+ SKIP(return, "Not enough huge pages\n");
+ }
+
+ mmap_flags |= (MAP_HUGETLB | MAP_ANONYMOUS);
}
- return "Unknown command";
+
+ /* zero_fd has to be >= 0. Already checked in main() */
+ zero_fd = open("/dev/zero", O_RDWR);
+ ASSERT_GE(zero_fd, 0);
+
+ /* gup_fd has to be >= 0. Already checked in main() */
+ self->gup_fd = open(GUP_TEST_FILE, O_RDWR);
+ ASSERT_GE(self->gup_fd, 0);
+
+ if (variant->shared)
+ mmap_flags = (mmap_flags & ~MAP_PRIVATE) | MAP_SHARED;
+
+ self->addr = mmap(NULL, self->size, PROT_READ | PROT_WRITE,
+ mmap_flags, zero_fd, 0);
+ close(zero_fd);
+ ASSERT_NE(self->addr, MAP_FAILED);
+
+ if (variant->thp)
+ madvise(self->addr, self->size, MADV_HUGEPAGE);
+ else
+ madvise(self->addr, self->size, MADV_NOHUGEPAGE);
+
+ for (p = self->addr; (unsigned long)p < (unsigned long)self->addr
+ + self->size; p += psize())
+ p[0] = 0;
}
-void *gup_thread(void *data)
+FIXTURE_TEARDOWN(gup_test)
{
- struct gup_test gup = *(struct gup_test *)data;
- int i, status;
-
- /* Only report timing information on the *_BENCHMARK commands: */
- if ((cmd == PIN_FAST_BENCHMARK) || (cmd == GUP_FAST_BENCHMARK) ||
- (cmd == PIN_LONGTERM_BENCHMARK)) {
- for (i = 0; i < repeats; i++) {
- gup.size = size;
- status = ioctl(gup_fd, cmd, &gup);
- if (status)
- break;
-
- pthread_mutex_lock(&print_mutex);
- ksft_print_msg("%s: Time: get:%lld put:%lld us",
- cmd_to_str(cmd), gup.get_delta_usec,
- gup.put_delta_usec);
- if (gup.size != size)
- ksft_print_msg(", truncated (size: %lld)", gup.size);
- ksft_print_msg("\n");
- pthread_mutex_unlock(&print_mutex);
- }
- } else {
- gup.size = size;
- status = ioctl(gup_fd, cmd, &gup);
- if (status)
- goto return_;
-
- pthread_mutex_lock(&print_mutex);
- ksft_print_msg("%s: done\n", cmd_to_str(cmd));
- if (gup.size != size)
- ksft_print_msg("Truncated (size: %lld)\n", gup.size);
- pthread_mutex_unlock(&print_mutex);
+ munmap(self->addr, self->size);
+ close(self->gup_fd);
+
+ if (variant->hugetlb)
+ hugetlb_restore_settings();
+}
+
+TEST_F(gup_test, get_user_pages)
+{
+ /* Tests the get_user_pages path */
+ int i;
+
+ for (i = 0; i < (int)ARRAY_SIZE(nr_pages_list); i++) {
+ struct gup_test gup = { 0 };
+
+ gup.addr = (unsigned long)self->addr;
+ gup.size = self->size;
+ gup.nr_pages_per_call = nr_pages_list[i] < 0 ?
+ self->size / psize() : nr_pages_list[i];
+
+ if (variant->write)
+ gup.gup_flags |= FOLL_WRITE;
+
+ TH_LOG("nr_pages_per_call=%u", gup.nr_pages_per_call);
+ ASSERT_EQ(ioctl(self->gup_fd, GUP_BASIC_TEST, &gup), 0);
+ }
+}
+
+TEST_F(gup_test, pin_user_pages)
+{
+ /* Tests the pin_user_pages path */
+ int i;
+
+ for (i = 0; i < (int)ARRAY_SIZE(nr_pages_list); i++) {
+ struct gup_test gup = { 0 };
+
+ gup.addr = (unsigned long)self->addr;
+ gup.size = self->size;
+ gup.nr_pages_per_call = nr_pages_list[i] < 0 ?
+ self->size / psize() : nr_pages_list[i];
+
+ if (variant->write)
+ gup.gup_flags |= FOLL_WRITE;
+
+ TH_LOG("nr_pages_per_call=%u", gup.nr_pages_per_call);
+ ASSERT_EQ(ioctl(self->gup_fd, PIN_BASIC_TEST, &gup), 0);
}
+}
+
+TEST_F(gup_test, dump_user_pages_with_get)
+{
+ /* Tests DUMP_USER_PAGES_TEST using get_user_pages */
+ int i;
+
+ for (i = 0; i < (int)ARRAY_SIZE(nr_pages_list); i++) {
+ struct gup_test gup = { 0 };
+
+ gup.addr = (unsigned long)self->addr;
+ gup.size = self->size;
+ gup.nr_pages_per_call = nr_pages_list[i] < 0 ?
+ self->size / psize() : nr_pages_list[i];
-return_:
- ksft_test_result(!status, "ioctl status %d\n", status);
- return NULL;
+ if (variant->write)
+ gup.gup_flags |= FOLL_WRITE;
+
+ gup.which_pages[0] = 1;
+
+ TH_LOG("nr_pages_per_call=%u", gup.nr_pages_per_call);
+ ASSERT_EQ(ioctl(self->gup_fd, DUMP_USER_PAGES_TEST, &gup), 0);
+ }
}
-int main(int argc, char **argv)
+TEST_F(gup_test, dump_user_pages_with_pin)
{
- struct gup_test gup = { 0 };
- int filed, i, opt, nr_pages = 1, thp = -1, write = 1, nthreads = 1, ret;
- int flags = MAP_PRIVATE;
- char *file = "/dev/zero";
- bool hugetlb = false;
- pthread_t *tid;
- char *p;
+ /* Tests DUMP_USER_PAGES_TEST using pin_user_pages */
+ int i;
- while ((opt = getopt(argc, argv, "m:r:n:F:f:abcj:tTLUuwWSHpz")) != -1) {
- switch (opt) {
- case 'a':
- cmd = PIN_FAST_BENCHMARK;
- break;
- case 'b':
- cmd = PIN_BASIC_TEST;
- break;
- case 'L':
- cmd = PIN_LONGTERM_BENCHMARK;
- break;
- case 'c':
- cmd = DUMP_USER_PAGES_TEST;
- /*
- * Dump page 0 (index 1). May be overridden later, by
- * user's non-option arguments.
- *
- * .which_pages is zero-based, so that zero can mean "do
- * nothing".
- */
- gup.which_pages[0] = 1;
- break;
- case 'p':
- /* works only with DUMP_USER_PAGES_TEST */
- gup.test_flags |= GUP_TEST_FLAG_DUMP_PAGES_USE_PIN;
- break;
- case 'F':
- /* strtol, so you can pass flags in hex form */
- gup.gup_flags = strtol(optarg, 0, 0);
- break;
- case 'j':
- nthreads = atoi(optarg);
- break;
- case 'm':
- size = atoi(optarg) * MB;
- break;
- case 'r':
- repeats = atoi(optarg);
- break;
- case 'n':
- nr_pages = atoi(optarg);
- if (nr_pages < 0)
- nr_pages = size / psize();
- break;
- case 't':
- thp = 1;
- break;
- case 'T':
- thp = 0;
- break;
- case 'U':
- cmd = GUP_BASIC_TEST;
- break;
- case 'u':
- cmd = GUP_FAST_BENCHMARK;
- break;
- case 'w':
- write = 1;
- break;
- case 'W':
- write = 0;
- break;
- case 'f':
- file = optarg;
- break;
- case 'S':
- flags &= ~MAP_PRIVATE;
- flags |= MAP_SHARED;
- break;
- case 'H':
- flags |= (MAP_HUGETLB | MAP_ANONYMOUS);
- hugetlb = true;
- break;
- default:
- ksft_exit_fail_msg("Wrong argument\n");
- }
+ for (i = 0; i < (int)ARRAY_SIZE(nr_pages_list); i++) {
+ struct gup_test gup = { 0 };
+
+ gup.addr = (unsigned long)self->addr;
+ gup.size = self->size;
+ gup.nr_pages_per_call = nr_pages_list[i] < 0 ?
+ self->size / psize() : nr_pages_list[i];
+
+ if (variant->write)
+ gup.gup_flags |= FOLL_WRITE;
+
+ gup.which_pages[0] = 1;
+ gup.test_flags |= GUP_TEST_FLAG_DUMP_PAGES_USE_PIN;
+
+ TH_LOG("nr_pages_per_call=%u", gup.nr_pages_per_call);
+ ASSERT_EQ(ioctl(self->gup_fd, DUMP_USER_PAGES_TEST, &gup), 0);
}
+}
- if (optind < argc) {
- int extra_arg_count = 0;
- /*
- * For example:
- *
- * ./gup_test -c 0 1 0x1001
- *
- * ...to dump pages 0, 1, and 4097
- */
-
- while ((optind < argc) &&
- (extra_arg_count < GUP_TEST_MAX_PAGES_TO_DUMP)) {
- /*
- * Do the 1-based indexing here, so that the user can
- * use normal 0-based indexing on the command line.
- */
- long page_index = strtol(argv[optind], 0, 0) + 1;
-
- gup.which_pages[extra_arg_count] = page_index;
- extra_arg_count++;
- optind++;
- }
+TEST_F(gup_test, get_user_pages_fast)
+{
+ /* Tests the lockless get_user_pages_fast() path */
+ int i;
+
+ for (i = 0; i < (int)ARRAY_SIZE(nr_pages_list); i++) {
+ struct gup_test gup = { 0 };
+
+ gup.addr = (unsigned long)self->addr;
+ gup.size = self->size;
+ gup.nr_pages_per_call = nr_pages_list[i] < 0 ?
+ self->size / psize() : nr_pages_list[i];
+
+ if (variant->write)
+ gup.gup_flags |= FOLL_WRITE;
+
+ TH_LOG("nr_pages_per_call=%u", gup.nr_pages_per_call);
+ ASSERT_EQ(ioctl(self->gup_fd, GUP_FAST_BENCHMARK, &gup), 0);
}
+}
- ksft_print_header();
+TEST_F(gup_test, pin_user_pages_fast)
+{
+ /* Tests the lockless pin_user_pages_fast() path */
+ int i;
- if (hugetlb) {
- unsigned long hp_size = default_huge_page_size();
+ for (i = 0; i < (int)ARRAY_SIZE(nr_pages_list); i++) {
+ struct gup_test gup = { 0 };
- if (!hp_size)
- ksft_exit_skip("HugeTLB is unavailable\n");
+ gup.addr = (unsigned long)self->addr;
+ gup.size = self->size;
+ gup.nr_pages_per_call = nr_pages_list[i] < 0 ?
+ self->size / psize() : nr_pages_list[i];
+
+ if (variant->write)
+ gup.gup_flags |= FOLL_WRITE;
- size = (size + hp_size - 1) & ~(hp_size - 1);
- if (!hugetlb_setup_default(size / hp_size))
- ksft_exit_skip("Not enough huge pages\n");
+ TH_LOG("nr_pages_per_call=%u", gup.nr_pages_per_call);
+ ASSERT_EQ(ioctl(self->gup_fd, PIN_FAST_BENCHMARK, &gup), 0);
}
+}
- ksft_set_plan(nthreads);
+TEST_F(gup_test, pin_user_pages_longterm)
+{
+ /* Tests pin_user_pages() with FOLL_LONGTERM */
+ int i;
- filed = open(file, O_RDWR|O_CREAT, 0664);
- if (filed < 0)
- ksft_exit_fail_msg("Unable to open %s: %s\n", file, strerror(errno));
+ for (i = 0; i < (int)ARRAY_SIZE(nr_pages_list); i++) {
+ struct gup_test gup = { 0 };
- gup.nr_pages_per_call = nr_pages;
- if (write)
- gup.gup_flags |= FOLL_WRITE;
+ gup.addr = (unsigned long)self->addr;
+ gup.size = self->size;
+ gup.nr_pages_per_call = nr_pages_list[i] < 0 ?
+ self->size / psize() : nr_pages_list[i];
- gup_fd = open(GUP_TEST_FILE, O_RDWR);
- if (gup_fd == -1) {
- switch (errno) {
- case EACCES:
- if (getuid())
- ksft_print_msg("Please run this test as root\n");
- break;
- case ENOENT:
- if (opendir("/sys/kernel/debug") == NULL)
- ksft_print_msg("mount debugfs at /sys/kernel/debug\n");
- ksft_print_msg("check if CONFIG_GUP_TEST is enabled in kernel config\n");
- break;
- default:
- ksft_print_msg("failed to open %s: %s\n", GUP_TEST_FILE, strerror(errno));
- break;
- }
- ksft_test_result_skip("Please run this test as root\n");
- ksft_exit_pass();
+ if (variant->write)
+ gup.gup_flags |= FOLL_WRITE;
+
+ TH_LOG("nr_pages_per_call=%u", gup.nr_pages_per_call);
+ ASSERT_EQ(ioctl(self->gup_fd, PIN_LONGTERM_BENCHMARK, &gup), 0);
}
+}
- p = mmap(NULL, size, PROT_READ | PROT_WRITE, flags, filed, 0);
- if (p == MAP_FAILED)
- ksft_exit_fail_msg("mmap: %s\n", strerror(errno));
- gup.addr = (unsigned long)p;
+TEST(dump_user_pages_sparse_indices)
+{
+ /* Tests sparse multi-index which_pages[] inputs. */
+ struct gup_test gup = { 0 };
+ unsigned long size = 128 * MB;
+ int zero_fd, gup_fd;
+ char *addr, *p;
+
+ zero_fd = open("/dev/zero", O_RDWR);
+ ASSERT_GE(zero_fd, 0);
+
+ gup_fd = open(GUP_TEST_FILE, O_RDWR);
+ ASSERT_GE(gup_fd, 0);
- if (thp == 1)
- madvise(p, size, MADV_HUGEPAGE);
- else if (thp == 0)
- madvise(p, size, MADV_NOHUGEPAGE);
+ addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, zero_fd, 0);
+ close(zero_fd);
+ ASSERT_NE(addr, MAP_FAILED);
- /* Fault them in here, from user space. */
- for (; (unsigned long)p < gup.addr + size; p += psize())
+ ASSERT_EQ(madvise(addr, size, MADV_HUGEPAGE), 0);
+
+ for (p = addr; (unsigned long)p < (unsigned long)addr + size;
+ p += psize())
p[0] = 0;
- tid = malloc(sizeof(pthread_t) * nthreads);
- assert(tid);
- for (i = 0; i < nthreads; i++) {
- ret = pthread_create(&tid[i], NULL, gup_thread, &gup);
- assert(ret == 0);
- }
- for (i = 0; i < nthreads; i++) {
- ret = pthread_join(tid[i], NULL);
- assert(ret == 0);
+ gup.addr = (unsigned long)addr;
+ gup.size = size;
+ gup.gup_flags = FOLL_WRITE;
+ gup.which_pages[0] = 1;
+ gup.which_pages[1] = 20;
+ gup.which_pages[2] = 0x1001;
+
+ /*
+ * Preserve the old "./gup_test -ct -F 0x1 0 19 0x1000" sparse dump
+ * coverage after removing command-line parsing from this binary.
+ */
+ ASSERT_EQ(ioctl(gup_fd, DUMP_USER_PAGES_TEST, &gup), 0);
+
+ munmap(addr, size);
+ close(gup_fd);
+}
+
+int main(int argc, char **argv)
+{
+ int fd;
+ char *file = "/dev/zero";
+
+ fd = open(file, O_RDWR);
+ if (fd < 0) {
+ ksft_print_header();
+ ksft_exit_fail_msg("Unable to open %s: %s\n", file, strerror(errno));
}
+ close(fd);
- free(tid);
+ fd = open(GUP_TEST_FILE, O_RDWR);
+ if (fd == -1) {
+ ksft_print_header();
+ if (errno == EACCES)
+ ksft_exit_skip("Please run this test as root\n");
+ if (errno == ENOENT) {
+ if (opendir("/sys/kernel/debug") == NULL)
+ ksft_exit_skip("Mount debugfs at /sys/kernel/debug\n");
+ else
+ ksft_exit_skip("Check CONFIG_GUP_TEST in kernel config\n");
+ }
+ ksft_exit_skip("failed to open %s: %s\n", GUP_TEST_FILE, strerror(errno));
+ }
+ close(fd);
- ksft_exit_pass();
+ return test_harness_run(argc, argv);
}
diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh
index 043aa3ed2596..65a4ef0f3748 100755
--- a/tools/testing/selftests/mm/run_vmtests.sh
+++ b/tools/testing/selftests/mm/run_vmtests.sh
@@ -130,30 +130,6 @@ test_selected() {
fi
}
-run_gup_matrix() {
- # -t: thp=on, -T: thp=off, -H: hugetlb=on
- local hugetlb_mb=256
-
- for huge in -t -T "-H -m $hugetlb_mb"; do
- # -u: gup-fast, -U: gup-basic, -a: pin-fast, -b: pin-basic, -L: pin-longterm
- for test_cmd in -u -U -a -b -L; do
- # -w: write=1, -W: write=0
- for write in -w -W; do
- # -S: shared
- for share in -S " "; do
- # -n: How many pages to fetch together? 512 is special
- # because it's default thp size (or 2M on x86), 123 to
- # just test partial gup when hit a huge in whatever form
- for num in "-n 1" "-n 512" "-n 123" "-n -1"; do
- CATEGORY="gup_test" run_test ./gup_test \
- $huge $test_cmd $write $share $num
- done
- done
- done
- done
- done
-}
-
# filter 64bit architectures
ARCH64STR="arm64 mips64 parisc64 ppc64 ppc64le riscv64 s390x sparc64 x86_64"
if [ -z "$ARCH" ]; then
@@ -239,18 +215,7 @@ fi
CATEGORY="mmap" run_test ./map_fixed_noreplace
-if $RUN_ALL; then
- run_gup_matrix
-else
- # get_user_pages_fast() benchmark
- CATEGORY="gup_test" run_test ./gup_test -u -n 1
- CATEGORY="gup_test" run_test ./gup_test -u -n -1
- # pin_user_pages_fast() benchmark
- CATEGORY="gup_test" run_test ./gup_test -a -n 1
- CATEGORY="gup_test" run_test ./gup_test -a -n -1
-fi
-# Dump pages 0, 19, and 4096, using pin_user_pages:
-CATEGORY="gup_test" run_test ./gup_test -ct -F 0x1 0 19 0x1000
+CATEGORY="gup_test" run_test ./gup_test
CATEGORY="gup_test" run_test ./gup_longterm
CATEGORY="userfaultfd" run_test ./uffd-unit-tests
--
2.39.5
^ permalink raw reply related
* [PATCH v3 3/4] tools/mm: add a standalone GUP microbenchmark
From: Sarthak Sharma @ 2026-05-21 11:18 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand
Cc: Jonathan Corbet, Lorenzo Stoakes, Liam R . Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Shuah Khan, Shuah Khan, Jason Gunthorpe, John Hubbard, Peter Xu,
Leon Romanovsky, Zi Yan, Baolin Wang, Nico Pache, Ryan Roberts,
Dev Jain, Barry Song, Lance Yang, Mark Brown, linux-kernel,
linux-mm, linux-kselftest, linux-doc, Sarthak Sharma
In-Reply-To: <20260521111801.173019-1-sarthak.sharma@arm.com>
Add a command-line tool for benchmarking get_user_pages fast-path
(GUP_FAST), pin_user_pages fast-path (PIN_FAST), and pin_user_pages
longterm (PIN_LONGTERM) via the CONFIG_GUP_TEST debugfs interface.
When invoked without arguments, gup_bench runs the same matrix of
configurations as run_gup_matrix() in run_vmtests.sh: all three GUP
commands across read/write, private/shared mappings, and a range of
page counts, with THP on/off for regular mappings and hugetlb for huge
page mappings.
This tool is a mix of reused and new logic. The mapping/setup path comes
from selftests/mm/gup_test.c, while the default benchmark matrix matches
run_gup_matrix() in run_vmtests.sh. The standalone CLI and tools/mm
integration are added here and the HugeTLB setup code is shared via
tools/lib/mm.
Add gup_bench to BUILD_TARGETS and INSTALL_TARGETS in tools/mm/Makefile,
link it against the shared tools/lib/mm hugepage helpers, and ignore the
resulting binary in tools/mm/.gitignore. While here, also add the
missing thp_swap_allocator_test entry to .gitignore.
Add tools/mm/gup_bench.c to the GUP entry in MAINTAINERS.
Suggested-by: David Hildenbrand (Arm) <david@kernel.org>
Signed-off-by: Sarthak Sharma <sarthak.sharma@arm.com>
---
MAINTAINERS | 1 +
tools/mm/.gitignore | 2 +
tools/mm/Makefile | 10 +-
tools/mm/gup_bench.c | 390 +++++++++++++++++++++++++++++++++++++++++++
4 files changed, 400 insertions(+), 3 deletions(-)
create mode 100644 tools/mm/gup_bench.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 7887a3263373..0dbb90247a75 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -16839,6 +16839,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
F: mm/gup.c
F: mm/gup_test.c
F: mm/gup_test.h
+F: tools/mm/gup_bench.c
F: tools/testing/selftests/mm/gup_longterm.c
F: tools/testing/selftests/mm/gup_test.c
diff --git a/tools/mm/.gitignore b/tools/mm/.gitignore
index 922879f93fc8..154d740be02e 100644
--- a/tools/mm/.gitignore
+++ b/tools/mm/.gitignore
@@ -2,3 +2,5 @@
slabinfo
page-types
page_owner_sort
+thp_swap_allocator_test
+gup_bench
diff --git a/tools/mm/Makefile b/tools/mm/Makefile
index f5725b5c23aa..d82cc8c43ee0 100644
--- a/tools/mm/Makefile
+++ b/tools/mm/Makefile
@@ -3,13 +3,14 @@
#
include ../scripts/Makefile.include
-BUILD_TARGETS=page-types slabinfo page_owner_sort thp_swap_allocator_test
+BUILD_TARGETS=page-types slabinfo page_owner_sort thp_swap_allocator_test gup_bench
INSTALL_TARGETS = $(BUILD_TARGETS) thpmaps
LIB_DIR = ../lib/api
LIBS = $(LIB_DIR)/libapi.a
+GUP_BENCH_OBJS = gup_bench.c ../lib/mm/hugepage_settings.c ../lib/mm/file_utils.c
-CFLAGS += -Wall -Wextra -I../lib/ -pthread
+CFLAGS += -Wall -Wextra -I../lib/ -I../.. -pthread
LDFLAGS += $(LIBS) -pthread
all: $(BUILD_TARGETS)
@@ -22,8 +23,11 @@ $(LIBS):
%: %.c
$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
+gup_bench: $(GUP_BENCH_OBJS) $(LIBS)
+ $(CC) $(CFLAGS) -o $@ $(GUP_BENCH_OBJS) $(LDFLAGS)
+
clean:
- $(RM) page-types slabinfo page_owner_sort thp_swap_allocator_test
+ $(RM) page-types slabinfo page_owner_sort thp_swap_allocator_test gup_bench
make -C $(LIB_DIR) clean
sbindir ?= /usr/sbin
diff --git a/tools/mm/gup_bench.c b/tools/mm/gup_bench.c
new file mode 100644
index 000000000000..50faff4527e5
--- /dev/null
+++ b/tools/mm/gup_bench.c
@@ -0,0 +1,390 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Microbenchmark for get_user_pages (GUP) kernel interfaces.
+ *
+ * Exercises GUP_FAST_BENCHMARK, PIN_FAST_BENCHMARK, and
+ * PIN_LONGTERM_BENCHMARK via the CONFIG_GUP_TEST debugfs interface.
+ *
+ * Example use:
+ * # Run the full matrix (all commands, access modes, page counts):
+ * ./gup_bench
+ *
+ * # Single run: pin_user_pages_fast, 512 pages, write access, hugetlb:
+ * ./gup_bench -a -n 512 -w -H
+ *
+ * Requires CONFIG_GUP_TEST=y and debugfs mounted at /sys/kernel/debug.
+ * Must be run as root.
+ */
+
+#define __SANE_USERSPACE_TYPES__ // Use ll64
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdatomic.h>
+#include <stdint.h>
+#include <limits.h>
+#include <string.h>
+#include <mm/gup_test.h>
+#include <mm/hugepage_settings.h>
+
+#define MB (1UL << 20)
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+#endif
+
+/* Just the flags we need, copied from the kernel internals. */
+#define FOLL_WRITE 0x01 /* check pte is writable */
+
+#define GUP_TEST_FILE "/sys/kernel/debug/gup_test"
+
+static unsigned int psize(void)
+{
+ static unsigned int __page_size;
+
+ if (!__page_size)
+ __page_size = sysconf(_SC_PAGESIZE);
+ return __page_size;
+}
+
+static unsigned long cmd;
+static const char *bench_label;
+static int gup_fd, repeats = 1;
+static unsigned long size = 128 * MB;
+static atomic_int bench_error;
+/* Serialize prints */
+static pthread_mutex_t print_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static const unsigned long bench_cmds[] = {
+ GUP_FAST_BENCHMARK,
+ PIN_FAST_BENCHMARK,
+ PIN_LONGTERM_BENCHMARK,
+};
+static const int bench_thp_modes[] = { 1, 0 }; /* on, off */
+static const int bench_nr_pages_list[] = { 1, 512, 123, -1 };
+
+static const char *cmd_to_str(unsigned long cmd)
+{
+ switch (cmd) {
+ case GUP_FAST_BENCHMARK:
+ return "GUP_FAST_BENCHMARK";
+ case PIN_FAST_BENCHMARK:
+ return "PIN_FAST_BENCHMARK";
+ case PIN_LONGTERM_BENCHMARK:
+ return "PIN_LONGTERM_BENCHMARK";
+ }
+ return "Unknown command";
+}
+
+struct bench_run {
+ unsigned long cmd;
+ int thp; /* -1: default, 0: off, 1: on */
+ bool hugetlb;
+ bool write;
+ bool shared;
+ int nr_pages; /* -1 means all pages (size / psize()) */
+ unsigned long size;
+ char *file;
+ int nthreads;
+ unsigned int gup_flags;
+};
+
+void *gup_thread(void *data)
+{
+ struct gup_test gup = *(struct gup_test *)data;
+ int i, status;
+
+ for (i = 0; i < repeats; i++) {
+ gup.size = size;
+ status = ioctl(gup_fd, cmd, &gup);
+ if (status) {
+ bench_error = 1;
+ break;
+ }
+
+ pthread_mutex_lock(&print_mutex);
+ printf("%s time: get:%lld put:%lld us",
+ bench_label, gup.get_delta_usec,
+ gup.put_delta_usec);
+ if (gup.size != size)
+ printf(", truncated (size: %lld)", gup.size);
+ printf("\n");
+ pthread_mutex_unlock(&print_mutex);
+ }
+
+ return NULL;
+}
+
+static int run_bench(struct bench_run *run)
+{
+ struct gup_test gup = { 0 };
+ int zero_fd, i, ret, started_threads = 0;
+ int flags = MAP_PRIVATE;
+ pthread_t *tid;
+ char label[128];
+ char *p;
+
+ /* Set globals consumed by gup_thread */
+ cmd = run->cmd;
+ size = run->size;
+ bench_error = 0;
+
+ if (run->hugetlb) {
+ unsigned long hp_size = default_huge_page_size();
+
+ if (!hp_size) {
+ fprintf(stderr, "Could not determine huge page size\n");
+ return 1;
+ }
+ size = (size + hp_size - 1) & ~(hp_size - 1);
+ if (!hugetlb_setup_default(size / hp_size)) {
+ fprintf(stderr, "Not enough huge pages\n");
+ return 1;
+ }
+ flags |= (MAP_HUGETLB | MAP_ANONYMOUS);
+ }
+
+ if (run->shared) {
+ flags &= ~MAP_PRIVATE;
+ flags |= MAP_SHARED;
+ }
+
+ gup.nr_pages_per_call = run->nr_pages < 0 ? size / psize() :
+ (unsigned long)run->nr_pages;
+
+ gup.gup_flags = run->gup_flags;
+ if (run->write)
+ gup.gup_flags |= FOLL_WRITE;
+
+ snprintf(label, sizeof(label), "%s (nr_pages=%-4u %s %s %s %s)",
+ cmd_to_str(run->cmd),
+ gup.nr_pages_per_call,
+ run->write ? "write" : "read",
+ run->shared ? "shared" : "private",
+ run->hugetlb ? "hugetlb=on" : "hugetlb=off",
+ run->hugetlb ? "thp=off" :
+ (run->thp == 1 ? "thp=on" :
+ (run->thp == 0 ? "thp=off" : "thp=default")));
+ bench_label = label;
+
+ zero_fd = open(run->file, O_RDWR);
+ if (zero_fd < 0) {
+ fprintf(stderr, "Unable to open %s: %s\n", run->file, strerror(errno));
+ return 1;
+ }
+
+ p = mmap(NULL, size, PROT_READ | PROT_WRITE, flags, zero_fd, 0);
+ close(zero_fd);
+ if (p == MAP_FAILED) {
+ fprintf(stderr, "mmap: %s\n", strerror(errno));
+ return 1;
+ }
+ gup.addr = (unsigned long)p;
+
+ if (run->thp == 1)
+ madvise(p, size, MADV_HUGEPAGE);
+ else if (run->thp == 0)
+ madvise(p, size, MADV_NOHUGEPAGE);
+
+ /* Fault them in here, from user space. */
+ for (; (unsigned long)p < gup.addr + size; p += psize())
+ p[0] = 0;
+
+ tid = malloc(sizeof(pthread_t) * run->nthreads);
+ if (!tid) {
+ fprintf(stderr, "Failed to allocate %d threads: %s\n",
+ run->nthreads, strerror(errno));
+ munmap((void *)gup.addr, size);
+ return 1;
+ }
+
+ for (i = 0; i < run->nthreads; i++) {
+ ret = pthread_create(&tid[i], NULL, gup_thread, &gup);
+ if (ret) {
+ fprintf(stderr, "pthread_create failed: %s\n", strerror(ret));
+ bench_error = 1;
+ break;
+ }
+ started_threads++;
+ }
+ for (i = 0; i < started_threads; i++) {
+ ret = pthread_join(tid[i], NULL);
+ if (ret) {
+ fprintf(stderr, "pthread_join failed: %s\n", strerror(ret));
+ bench_error = 1;
+ }
+ }
+
+ free(tid);
+ munmap((void *)gup.addr, size);
+
+ return bench_error ? 1 : 0;
+}
+
+static int run_matrix(void)
+{
+ unsigned int c, t, w, s, n;
+ int ret = 0;
+
+ for (c = 0; c < ARRAY_SIZE(bench_cmds); c++) {
+ for (w = 0; w <= 1; w++) {
+ for (s = 0; s <= 1; s++) {
+ for (t = 0; t < ARRAY_SIZE(bench_thp_modes); t++) {
+ for (n = 0; n < ARRAY_SIZE(bench_nr_pages_list); n++) {
+ struct bench_run run = {
+ .cmd = bench_cmds[c],
+ .thp = bench_thp_modes[t],
+ .hugetlb = false,
+ .write = w,
+ .shared = s,
+ .nr_pages = bench_nr_pages_list[n],
+ .size = 128 * MB,
+ .file = "/dev/zero",
+ .nthreads = 1,
+ };
+ ret |= run_bench(&run);
+ }
+ }
+ /* hugetlb: 256M to match run_gup_matrix() in run_vmtests.sh */
+ for (n = 0; n < ARRAY_SIZE(bench_nr_pages_list); n++) {
+ struct bench_run run = {
+ .cmd = bench_cmds[c],
+ .thp = -1,
+ .hugetlb = true,
+ .write = w,
+ .shared = s,
+ .nr_pages = bench_nr_pages_list[n],
+ .size = 256 * MB,
+ .file = "/dev/zero",
+ .nthreads = 1,
+ };
+ ret |= run_bench(&run);
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+int main(int argc, char **argv)
+{
+ struct bench_run run = {
+ .cmd = GUP_FAST_BENCHMARK,
+ .thp = -1,
+ .hugetlb = false,
+ .write = true,
+ .shared = false,
+ .nr_pages = 1,
+ .size = 128 * MB,
+ .file = "/dev/zero",
+ .nthreads = 1,
+ };
+ int opt, result;
+
+ while ((opt = getopt(argc, argv, "m:r:n:F:f:aj:tTLuwWSH")) != -1) {
+ switch (opt) {
+
+ /* Command selection */
+ case 'u':
+ run.cmd = GUP_FAST_BENCHMARK;
+ break;
+ case 'a':
+ run.cmd = PIN_FAST_BENCHMARK;
+ break;
+ case 'L':
+ run.cmd = PIN_LONGTERM_BENCHMARK;
+ break;
+
+ /* Memory type */
+ case 'H':
+ run.hugetlb = true;
+ break;
+ case 't':
+ run.thp = 1;
+ break;
+ case 'T':
+ run.thp = 0;
+ break;
+
+ /* Access mode */
+ case 'w':
+ run.write = true;
+ break;
+ case 'W':
+ run.write = false;
+ break;
+ case 'S':
+ run.shared = true;
+ break;
+
+ /* Mapping */
+ case 'f':
+ run.file = optarg;
+ break;
+
+ /* Sizing and iteration */
+ case 'm':
+ run.size = atoi(optarg) * MB;
+ break;
+ case 'n':
+ run.nr_pages = atoi(optarg);
+ break;
+ case 'r':
+ repeats = atoi(optarg);
+ break;
+ case 'j': {
+ char *end;
+ long val;
+
+ errno = 0;
+ val = strtol(optarg, &end, 10);
+ if (errno || end == optarg || *end != '\0' || val < 1 ||
+ val > INT_MAX ||
+ (size_t)val > SIZE_MAX / sizeof(pthread_t)) {
+ fprintf(stderr, "Invalid thread count '%s'\n", optarg);
+ exit(1);
+ }
+ run.nthreads = val;
+ break;
+ }
+
+ /* Advanced */
+ case 'F':
+ /* strtol, so you can pass flags in hex form */
+ run.gup_flags = strtol(optarg, 0, 0);
+ break;
+
+ default:
+ fprintf(stderr, "Wrong argument\n");
+ exit(1);
+ }
+ }
+
+ gup_fd = open(GUP_TEST_FILE, O_RDWR);
+ if (gup_fd == -1) {
+ if (errno == EACCES) {
+ fprintf(stderr, "Please run as root\n");
+ } else if (errno == ENOENT) {
+ if (opendir("/sys/kernel/debug") == NULL)
+ fprintf(stderr, "Mount debugfs at /sys/kernel/debug\n");
+ else
+ fprintf(stderr, "Check CONFIG_GUP_TEST in kernel config\n");
+ } else {
+ fprintf(stderr, "Failed to open %s: %s\n", GUP_TEST_FILE,
+ strerror(errno));
+ }
+ exit(1);
+ }
+
+ result = (argc == 1) ? run_matrix() : run_bench(&run);
+ close(gup_fd);
+ return result;
+}
--
2.39.5
^ permalink raw reply related
* [PATCH v3 2/4] tools/lib/mm: move hugepage_settings out of selftests
From: Sarthak Sharma @ 2026-05-21 11:17 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand
Cc: Jonathan Corbet, Lorenzo Stoakes, Liam R . Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Shuah Khan, Shuah Khan, Jason Gunthorpe, John Hubbard, Peter Xu,
Leon Romanovsky, Zi Yan, Baolin Wang, Nico Pache, Ryan Roberts,
Dev Jain, Barry Song, Lance Yang, Mark Brown, linux-kernel,
linux-mm, linux-kselftest, linux-doc, Sarthak Sharma
In-Reply-To: <20260521111801.173019-1-sarthak.sharma@arm.com>
Move hugepage_settings.[ch] from tools/testing/selftests/mm/ to
tools/lib/mm/ so the THP and HugeTLB helpers can be shared more easily
between selftests and other tools.
Update the mm selftest users to include the header from tools/lib/mm/
and adjust the selftests/mm build to compile the moved implementation
from its new location.
Remove the remaining kselftest dependency from hugepage_settings.c by
replacing ksft_print_msg() calls with plain fprintf() calls. Drop the
non-fatal hugetlb informational prints from the shared helper, so it
does not emit unwanted output during selftest runs.
Signed-off-by: Sarthak Sharma <sarthak.sharma@arm.com>
---
.../selftests => lib}/mm/hugepage_settings.c | 15 +++++++++------
.../selftests => lib}/mm/hugepage_settings.h | 0
tools/testing/selftests/mm/Makefile | 6 ++++--
tools/testing/selftests/mm/compaction_test.c | 2 +-
tools/testing/selftests/mm/cow.c | 2 +-
.../testing/selftests/mm/folio_split_race_test.c | 3 ++-
tools/testing/selftests/mm/guard-regions.c | 3 ++-
tools/testing/selftests/mm/gup_longterm.c | 2 +-
tools/testing/selftests/mm/gup_test.c | 3 ++-
tools/testing/selftests/mm/hmm-tests.c | 6 +++---
tools/testing/selftests/mm/hugetlb-madvise.c | 3 ++-
tools/testing/selftests/mm/hugetlb-mmap.c | 3 ++-
tools/testing/selftests/mm/hugetlb-mremap.c | 3 ++-
tools/testing/selftests/mm/hugetlb-shm.c | 2 +-
tools/testing/selftests/mm/hugetlb-soft-offline.c | 2 +-
tools/testing/selftests/mm/hugetlb-vmemmap.c | 3 ++-
tools/testing/selftests/mm/hugetlb_dio.c | 3 ++-
.../selftests/mm/hugetlb_fault_after_madv.c | 2 +-
tools/testing/selftests/mm/hugetlb_madv_vs_map.c | 2 +-
tools/testing/selftests/mm/khugepaged.c | 2 +-
tools/testing/selftests/mm/ksm_tests.c | 2 +-
tools/testing/selftests/mm/migration.c | 6 +++---
tools/testing/selftests/mm/pagemap_ioctl.c | 2 +-
tools/testing/selftests/mm/prctl_thp_disable.c | 2 +-
tools/testing/selftests/mm/protection_keys.c | 2 +-
tools/testing/selftests/mm/soft-dirty.c | 2 +-
tools/testing/selftests/mm/split_huge_page_test.c | 2 +-
tools/testing/selftests/mm/thuge-gen.c | 3 ++-
tools/testing/selftests/mm/transhuge-stress.c | 3 ++-
tools/testing/selftests/mm/uffd-common.h | 2 +-
tools/testing/selftests/mm/uffd-wp-mremap.c | 3 ++-
tools/testing/selftests/mm/va_high_addr_switch.c | 2 +-
32 files changed, 57 insertions(+), 41 deletions(-)
rename tools/{testing/selftests => lib}/mm/hugepage_settings.c (98%)
rename tools/{testing/selftests => lib}/mm/hugepage_settings.h (100%)
diff --git a/tools/testing/selftests/mm/hugepage_settings.c b/tools/lib/mm/hugepage_settings.c
similarity index 98%
rename from tools/testing/selftests/mm/hugepage_settings.c
rename to tools/lib/mm/hugepage_settings.c
index 5e947abb7425..b08b27776fc5 100644
--- a/tools/testing/selftests/mm/hugepage_settings.c
+++ b/tools/lib/mm/hugepage_settings.c
@@ -8,13 +8,17 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#include <mm/file_utils.h>
-#include "kselftest.h"
+#include "file_utils.h"
#include "hugepage_settings.h"
#define THP_SYSFS "/sys/kernel/mm/transparent_hugepage/"
#define MAX_SETTINGS_DEPTH 4
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+#endif
+
static struct thp_settings settings_stack[MAX_SETTINGS_DEPTH];
static int settings_index;
static struct thp_settings saved_settings;
@@ -383,8 +387,6 @@ int detect_hugetlb_page_sizes(unsigned long sizes[], int max)
if (sscanf(entry->d_name, "hugepages-%zukB", &kb) != 1)
continue;
sizes[count++] = kb * 1024;
- ksft_print_msg("[INFO] detected hugetlb page size: %zu KiB\n",
- kb);
}
closedir(dir);
return count;
@@ -503,7 +505,6 @@ unsigned long hugetlb_setup(unsigned long nr, unsigned long sizes[],
return 0;
if (nr_enabled > max) {
- ksft_print_msg("detected %d huge page sizes, will only test %d\n", nr_enabled, max);
nr_enabled = max;
}
@@ -575,8 +576,10 @@ static void hugepage_restore_settings_atexit(void)
static void hugepage_restore_settings_sighandler(int sig)
{
+ (void)sig;
+
/* exit() will invoke the hugepage_restore_settings_atexit handler. */
- exit(KSFT_FAIL);
+ exit(EXIT_FAILURE);
}
void hugepage_save_settings(bool thp, bool hugetlb)
diff --git a/tools/testing/selftests/mm/hugepage_settings.h b/tools/lib/mm/hugepage_settings.h
similarity index 100%
rename from tools/testing/selftests/mm/hugepage_settings.h
rename to tools/lib/mm/hugepage_settings.h
diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile
index b5fb4b6ab31b..8a307b777630 100644
--- a/tools/testing/selftests/mm/Makefile
+++ b/tools/testing/selftests/mm/Makefile
@@ -187,8 +187,10 @@ TEST_FILES += write_hugetlb_memory.sh
include ../lib.mk
-$(TEST_GEN_PROGS): vm_util.c hugepage_settings.c $(top_srcdir)/tools/lib/mm/file_utils.c
-$(TEST_GEN_FILES): vm_util.c hugepage_settings.c $(top_srcdir)/tools/lib/mm/file_utils.c
+$(TEST_GEN_PROGS): vm_util.c $(top_srcdir)/tools/lib/mm/hugepage_settings.c \
+ $(top_srcdir)/tools/lib/mm/file_utils.c
+$(TEST_GEN_FILES): vm_util.c $(top_srcdir)/tools/lib/mm/hugepage_settings.c \
+ $(top_srcdir)/tools/lib/mm/file_utils.c
$(OUTPUT)/uffd-stress: uffd-common.c
$(OUTPUT)/uffd-unit-tests: uffd-common.c
diff --git a/tools/testing/selftests/mm/compaction_test.c b/tools/testing/selftests/mm/compaction_test.c
index de0633f9a7e5..7c58506c0aa7 100644
--- a/tools/testing/selftests/mm/compaction_test.c
+++ b/tools/testing/selftests/mm/compaction_test.c
@@ -15,9 +15,9 @@
#include <errno.h>
#include <unistd.h>
#include <string.h>
+#include <mm/hugepage_settings.h>
#include "kselftest.h"
-#include "hugepage_settings.h"
#define MAP_SIZE_MB 100
#define MAP_SIZE (MAP_SIZE_MB * 1024 * 1024)
diff --git a/tools/testing/selftests/mm/cow.c b/tools/testing/selftests/mm/cow.c
index 0c627ea89ff7..7c29d3d2d887 100644
--- a/tools/testing/selftests/mm/cow.c
+++ b/tools/testing/selftests/mm/cow.c
@@ -20,6 +20,7 @@
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <linux/memfd.h>
+#include <mm/hugepage_settings.h>
#include "local_config.h"
#ifdef LOCAL_CONFIG_HAVE_LIBURING
@@ -29,7 +30,6 @@
#include "../../../../mm/gup_test.h"
#include "kselftest.h"
#include "vm_util.h"
-#include "hugepage_settings.h"
static size_t pagesize;
static int pagemap_fd;
diff --git a/tools/testing/selftests/mm/folio_split_race_test.c b/tools/testing/selftests/mm/folio_split_race_test.c
index 6329e37fff4c..ca9b95c39c16 100644
--- a/tools/testing/selftests/mm/folio_split_race_test.c
+++ b/tools/testing/selftests/mm/folio_split_race_test.c
@@ -23,9 +23,10 @@
#include <sys/mman.h>
#include <signal.h>
#include <unistd.h>
+#include <mm/hugepage_settings.h>
+
#include "vm_util.h"
#include "kselftest.h"
-#include "hugepage_settings.h"
uint64_t page_size;
uint64_t pmd_pagesize;
diff --git a/tools/testing/selftests/mm/guard-regions.c b/tools/testing/selftests/mm/guard-regions.c
index b21df3040b1c..ccca432c8802 100644
--- a/tools/testing/selftests/mm/guard-regions.c
+++ b/tools/testing/selftests/mm/guard-regions.c
@@ -20,8 +20,9 @@
#include <sys/syscall.h>
#include <sys/uio.h>
#include <unistd.h>
+#include <mm/hugepage_settings.h>
+
#include "vm_util.h"
-#include "hugepage_settings.h"
#include "../pidfd/pidfd.h"
diff --git a/tools/testing/selftests/mm/gup_longterm.c b/tools/testing/selftests/mm/gup_longterm.c
index eb8963e9d98f..0cfc67fc5c8f 100644
--- a/tools/testing/selftests/mm/gup_longterm.c
+++ b/tools/testing/selftests/mm/gup_longterm.c
@@ -20,6 +20,7 @@
#include <sys/vfs.h>
#include <linux/magic.h>
#include <linux/memfd.h>
+#include <mm/hugepage_settings.h>
#include "local_config.h"
#ifdef LOCAL_CONFIG_HAVE_LIBURING
@@ -29,7 +30,6 @@
#include "../../../../mm/gup_test.h"
#include "kselftest.h"
#include "vm_util.h"
-#include "hugepage_settings.h"
static size_t pagesize;
static int nr_hugetlbsizes;
diff --git a/tools/testing/selftests/mm/gup_test.c b/tools/testing/selftests/mm/gup_test.c
index 3f841a96f870..803ab829a841 100644
--- a/tools/testing/selftests/mm/gup_test.c
+++ b/tools/testing/selftests/mm/gup_test.c
@@ -12,9 +12,10 @@
#include <pthread.h>
#include <assert.h>
#include <mm/gup_test.h>
+#include <mm/hugepage_settings.h>
+
#include "kselftest.h"
#include "vm_util.h"
-#include "hugepage_settings.h"
#define MB (1UL << 20)
diff --git a/tools/testing/selftests/mm/hmm-tests.c b/tools/testing/selftests/mm/hmm-tests.c
index e1c8a679a4cf..b7f85a144f03 100644
--- a/tools/testing/selftests/mm/hmm-tests.c
+++ b/tools/testing/selftests/mm/hmm-tests.c
@@ -10,9 +10,6 @@
* bugs.
*/
-#include "kselftest_harness.h"
-#include "hugepage_settings.h"
-
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
@@ -27,6 +24,9 @@
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/time.h>
+#include <mm/hugepage_settings.h>
+
+#include "kselftest_harness.h"
/*
* This is a private UAPI to the kernel test module so it isn't exported
diff --git a/tools/testing/selftests/mm/hugetlb-madvise.c b/tools/testing/selftests/mm/hugetlb-madvise.c
index 555b4b3d1430..8e18b5027904 100644
--- a/tools/testing/selftests/mm/hugetlb-madvise.c
+++ b/tools/testing/selftests/mm/hugetlb-madvise.c
@@ -12,9 +12,10 @@
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
+#include <mm/hugepage_settings.h>
+
#include "vm_util.h"
#include "kselftest.h"
-#include "hugepage_settings.h"
#define MIN_FREE_PAGES 20
#define NR_HUGE_PAGES 10 /* common number of pages to map/allocate */
diff --git a/tools/testing/selftests/mm/hugetlb-mmap.c b/tools/testing/selftests/mm/hugetlb-mmap.c
index 0f2aad1b7dbd..12184c1889ad 100644
--- a/tools/testing/selftests/mm/hugetlb-mmap.c
+++ b/tools/testing/selftests/mm/hugetlb-mmap.c
@@ -16,9 +16,10 @@
#include <sys/mman.h>
#include <fcntl.h>
#include <linux/memfd.h>
+#include <mm/hugepage_settings.h>
+
#include "vm_util.h"
#include "kselftest.h"
-#include "hugepage_settings.h"
#define LENGTH (256UL*1024*1024)
#define PROTECTION (PROT_READ | PROT_WRITE)
diff --git a/tools/testing/selftests/mm/hugetlb-mremap.c b/tools/testing/selftests/mm/hugetlb-mremap.c
index d239905790dd..0b1a1fc9e766 100644
--- a/tools/testing/selftests/mm/hugetlb-mremap.c
+++ b/tools/testing/selftests/mm/hugetlb-mremap.c
@@ -24,9 +24,10 @@
#include <sys/ioctl.h>
#include <string.h>
#include <stdbool.h>
+#include <mm/hugepage_settings.h>
+
#include "kselftest.h"
#include "vm_util.h"
-#include "hugepage_settings.h"
#define DEFAULT_LENGTH_MB 10UL
#define MB_TO_BYTES(x) (x * 1024 * 1024)
diff --git a/tools/testing/selftests/mm/hugetlb-shm.c b/tools/testing/selftests/mm/hugetlb-shm.c
index 3ff7f062b7eb..25988f97057a 100644
--- a/tools/testing/selftests/mm/hugetlb-shm.c
+++ b/tools/testing/selftests/mm/hugetlb-shm.c
@@ -27,9 +27,9 @@
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/mman.h>
+#include <mm/hugepage_settings.h>
#include "vm_util.h"
-#include "hugepage_settings.h"
#define LENGTH (256UL*1024*1024)
diff --git a/tools/testing/selftests/mm/hugetlb-soft-offline.c b/tools/testing/selftests/mm/hugetlb-soft-offline.c
index bc202e4ed2bd..20864e7d4e0c 100644
--- a/tools/testing/selftests/mm/hugetlb-soft-offline.c
+++ b/tools/testing/selftests/mm/hugetlb-soft-offline.c
@@ -21,9 +21,9 @@
#include <sys/mman.h>
#include <sys/statfs.h>
#include <sys/types.h>
+#include <mm/hugepage_settings.h>
#include "kselftest.h"
-#include "hugepage_settings.h"
#ifndef MADV_SOFT_OFFLINE
#define MADV_SOFT_OFFLINE 101
diff --git a/tools/testing/selftests/mm/hugetlb-vmemmap.c b/tools/testing/selftests/mm/hugetlb-vmemmap.c
index 507df78a158d..ffecea89ffa1 100644
--- a/tools/testing/selftests/mm/hugetlb-vmemmap.c
+++ b/tools/testing/selftests/mm/hugetlb-vmemmap.c
@@ -10,8 +10,9 @@
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
+#include <mm/hugepage_settings.h>
+
#include "vm_util.h"
-#include "hugepage_settings.h"
#define PAGE_COMPOUND_HEAD (1UL << 15)
#define PAGE_COMPOUND_TAIL (1UL << 16)
diff --git a/tools/testing/selftests/mm/hugetlb_dio.c b/tools/testing/selftests/mm/hugetlb_dio.c
index fb4600570e13..896c6e3c09da 100644
--- a/tools/testing/selftests/mm/hugetlb_dio.c
+++ b/tools/testing/selftests/mm/hugetlb_dio.c
@@ -18,9 +18,10 @@
#include <string.h>
#include <sys/mman.h>
#include <sys/syscall.h>
+#include <mm/hugepage_settings.h>
+
#include "vm_util.h"
#include "kselftest.h"
-#include "hugepage_settings.h"
#ifndef STATX_DIOALIGN
#define STATX_DIOALIGN 0x00002000U
diff --git a/tools/testing/selftests/mm/hugetlb_fault_after_madv.c b/tools/testing/selftests/mm/hugetlb_fault_after_madv.c
index 2dc158054f66..70e685f0b510 100644
--- a/tools/testing/selftests/mm/hugetlb_fault_after_madv.c
+++ b/tools/testing/selftests/mm/hugetlb_fault_after_madv.c
@@ -7,10 +7,10 @@
#include <unistd.h>
#include <setjmp.h>
#include <signal.h>
+#include <mm/hugepage_settings.h>
#include "vm_util.h"
#include "kselftest.h"
-#include "hugepage_settings.h"
#define INLOOP_ITER 100
diff --git a/tools/testing/selftests/mm/hugetlb_madv_vs_map.c b/tools/testing/selftests/mm/hugetlb_madv_vs_map.c
index f94549efcc6f..c8f6414e8a77 100644
--- a/tools/testing/selftests/mm/hugetlb_madv_vs_map.c
+++ b/tools/testing/selftests/mm/hugetlb_madv_vs_map.c
@@ -23,9 +23,9 @@
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
+#include <mm/hugepage_settings.h>
#include "vm_util.h"
-#include "hugepage_settings.h"
#define INLOOP_ITER 100
diff --git a/tools/testing/selftests/mm/khugepaged.c b/tools/testing/selftests/mm/khugepaged.c
index ae5945e2bfac..9bf1f8744431 100644
--- a/tools/testing/selftests/mm/khugepaged.c
+++ b/tools/testing/selftests/mm/khugepaged.c
@@ -19,11 +19,11 @@
#include <sys/sysmacros.h>
#include <sys/vfs.h>
#include <mm/file_utils.h>
+#include <mm/hugepage_settings.h>
#include "linux/magic.h"
#include "vm_util.h"
-#include "hugepage_settings.h"
#define BASE_ADDR ((void *)(1UL << 30))
static unsigned long hpage_pmd_size;
diff --git a/tools/testing/selftests/mm/ksm_tests.c b/tools/testing/selftests/mm/ksm_tests.c
index a050f4840cfa..4fda799a148d 100644
--- a/tools/testing/selftests/mm/ksm_tests.c
+++ b/tools/testing/selftests/mm/ksm_tests.c
@@ -11,11 +11,11 @@
#include <fcntl.h>
#include <stdint.h>
#include <err.h>
+#include <mm/hugepage_settings.h>
#include "kselftest.h"
#include <include/vdso/time64.h>
#include "vm_util.h"
-#include "hugepage_settings.h"
#define KSM_SYSFS_PATH "/sys/kernel/mm/ksm/"
#define KSM_FP(s) (KSM_SYSFS_PATH s)
diff --git a/tools/testing/selftests/mm/migration.c b/tools/testing/selftests/mm/migration.c
index 29f7492453d4..c3e60ddcefa7 100644
--- a/tools/testing/selftests/mm/migration.c
+++ b/tools/testing/selftests/mm/migration.c
@@ -4,9 +4,6 @@
* paths in the kernel.
*/
-#include "kselftest_harness.h"
-#include "hugepage_settings.h"
-
#include <strings.h>
#include <pthread.h>
#include <numa.h>
@@ -16,6 +13,9 @@
#include <sys/types.h>
#include <signal.h>
#include <time.h>
+#include <mm/hugepage_settings.h>
+
+#include "kselftest_harness.h"
#include "vm_util.h"
#define TWOMEG (2<<20)
diff --git a/tools/testing/selftests/mm/pagemap_ioctl.c b/tools/testing/selftests/mm/pagemap_ioctl.c
index 762306177ad8..924308ead037 100644
--- a/tools/testing/selftests/mm/pagemap_ioctl.c
+++ b/tools/testing/selftests/mm/pagemap_ioctl.c
@@ -20,10 +20,10 @@
#include <assert.h>
#include <sys/ipc.h>
#include <sys/shm.h>
+#include <mm/hugepage_settings.h>
#include "vm_util.h"
#include "kselftest.h"
-#include "hugepage_settings.h"
#define PAGEMAP_BITS_ALL (PAGE_IS_WPALLOWED | PAGE_IS_WRITTEN | \
PAGE_IS_FILE | PAGE_IS_PRESENT | \
diff --git a/tools/testing/selftests/mm/prctl_thp_disable.c b/tools/testing/selftests/mm/prctl_thp_disable.c
index d8d9d1de57b8..db1807adf72e 100644
--- a/tools/testing/selftests/mm/prctl_thp_disable.c
+++ b/tools/testing/selftests/mm/prctl_thp_disable.c
@@ -12,9 +12,9 @@
#include <linux/mman.h>
#include <sys/prctl.h>
#include <sys/wait.h>
+#include <mm/hugepage_settings.h>
#include "kselftest_harness.h"
-#include "hugepage_settings.h"
#include "vm_util.h"
#ifndef PR_THP_DISABLE_EXCEPT_ADVISED
diff --git a/tools/testing/selftests/mm/protection_keys.c b/tools/testing/selftests/mm/protection_keys.c
index 9a6d954ee371..5ba2033e8a09 100644
--- a/tools/testing/selftests/mm/protection_keys.c
+++ b/tools/testing/selftests/mm/protection_keys.c
@@ -45,8 +45,8 @@
#include <unistd.h>
#include <sys/ptrace.h>
#include <setjmp.h>
+#include <mm/hugepage_settings.h>
-#include "hugepage_settings.h"
#include "pkey-helpers.h"
int iteration_nr = 1;
diff --git a/tools/testing/selftests/mm/soft-dirty.c b/tools/testing/selftests/mm/soft-dirty.c
index fb1864a68e1c..852cc0a9743f 100644
--- a/tools/testing/selftests/mm/soft-dirty.c
+++ b/tools/testing/selftests/mm/soft-dirty.c
@@ -6,10 +6,10 @@
#include <stdint.h>
#include <malloc.h>
#include <sys/mman.h>
+#include <mm/hugepage_settings.h>
#include "kselftest.h"
#include "vm_util.h"
-#include "hugepage_settings.h"
#define PAGEMAP_FILE_PATH "/proc/self/pagemap"
#define TEST_ITERATIONS 10000
diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c
index 4a2ccc055c8b..b4ff8ed0b446 100644
--- a/tools/testing/selftests/mm/split_huge_page_test.c
+++ b/tools/testing/selftests/mm/split_huge_page_test.c
@@ -20,10 +20,10 @@
#include <stdbool.h>
#include <time.h>
#include <mm/file_utils.h>
+#include <mm/hugepage_settings.h>
#include "vm_util.h"
#include "kselftest.h"
-#include "hugepage_settings.h"
uint64_t pagesize;
unsigned int pageshift;
diff --git a/tools/testing/selftests/mm/thuge-gen.c b/tools/testing/selftests/mm/thuge-gen.c
index 22b9c2f1c35d..5556a1b89393 100644
--- a/tools/testing/selftests/mm/thuge-gen.c
+++ b/tools/testing/selftests/mm/thuge-gen.c
@@ -12,9 +12,10 @@
#include <unistd.h>
#include <stdarg.h>
#include <string.h>
+#include <mm/hugepage_settings.h>
+
#include "vm_util.h"
#include "kselftest.h"
-#include "hugepage_settings.h"
#if !defined(MAP_HUGETLB)
#define MAP_HUGETLB 0x40000
diff --git a/tools/testing/selftests/mm/transhuge-stress.c b/tools/testing/selftests/mm/transhuge-stress.c
index 8eb0c5630e7e..ed78b2142ac1 100644
--- a/tools/testing/selftests/mm/transhuge-stress.c
+++ b/tools/testing/selftests/mm/transhuge-stress.c
@@ -15,9 +15,10 @@
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
+#include <mm/hugepage_settings.h>
+
#include "vm_util.h"
#include "kselftest.h"
-#include "hugepage_settings.h"
int backing_fd = -1;
int mmap_flags = MAP_ANONYMOUS | MAP_NORESERVE | MAP_PRIVATE;
diff --git a/tools/testing/selftests/mm/uffd-common.h b/tools/testing/selftests/mm/uffd-common.h
index 92a21b97f745..b8e25a96381d 100644
--- a/tools/testing/selftests/mm/uffd-common.h
+++ b/tools/testing/selftests/mm/uffd-common.h
@@ -34,10 +34,10 @@
#include <stdint.h>
#include <sys/random.h>
#include <stdatomic.h>
+#include <mm/hugepage_settings.h>
#include "kselftest.h"
#include "vm_util.h"
-#include "hugepage_settings.h"
#define UFFD_FLAGS (O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY)
diff --git a/tools/testing/selftests/mm/uffd-wp-mremap.c b/tools/testing/selftests/mm/uffd-wp-mremap.c
index 90ac410c6c6f..dcd7ac35deba 100644
--- a/tools/testing/selftests/mm/uffd-wp-mremap.c
+++ b/tools/testing/selftests/mm/uffd-wp-mremap.c
@@ -7,8 +7,9 @@
#include <assert.h>
#include <linux/mman.h>
#include <sys/mman.h>
+#include <mm/hugepage_settings.h>
+
#include "kselftest.h"
-#include "hugepage_settings.h"
#include "uffd-common.h"
static int pagemap_fd;
diff --git a/tools/testing/selftests/mm/va_high_addr_switch.c b/tools/testing/selftests/mm/va_high_addr_switch.c
index e24d7ba00b44..07fcb03316cc 100644
--- a/tools/testing/selftests/mm/va_high_addr_switch.c
+++ b/tools/testing/selftests/mm/va_high_addr_switch.c
@@ -8,10 +8,10 @@
#include <stdio.h>
#include <sys/mman.h>
#include <string.h>
+#include <mm/hugepage_settings.h>
#include "vm_util.h"
#include "kselftest.h"
-#include "hugepage_settings.h"
/*
* The hint addr value is used to allocate addresses
--
2.39.5
^ permalink raw reply related
* [PATCH v3 1/4] tools/lib/mm: add shared file helpers
From: Sarthak Sharma @ 2026-05-21 11:17 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand
Cc: Jonathan Corbet, Lorenzo Stoakes, Liam R . Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Shuah Khan, Shuah Khan, Jason Gunthorpe, John Hubbard, Peter Xu,
Leon Romanovsky, Zi Yan, Baolin Wang, Nico Pache, Ryan Roberts,
Dev Jain, Barry Song, Lance Yang, Mark Brown, linux-kernel,
linux-mm, linux-kselftest, linux-doc, Sarthak Sharma
In-Reply-To: <20260521111801.173019-1-sarthak.sharma@arm.com>
Move read_file(), write_file(), read_num(), and write_num() out of
tools/testing/selftests/mm/vm_util.c into a new shared helper under
tools/lib/mm/.
These helpers are used by mm selftests today and will also be needed by
shared hugepage helpers in subsequent patches. Move them to a generic
location and drop the kselftest-specific dependency from their
implementation so they can be reused outside selftests as well.
Update the current mm selftest users to include the new header directly,
and link the new helper into the selftests/mm build.
Add tools/lib/mm/ to the MEMORY MANAGEMENT - MISC entry in MAINTAINERS.
Signed-off-by: Sarthak Sharma <sarthak.sharma@arm.com>
---
MAINTAINERS | 1 +
tools/lib/mm/file_utils.c | 83 +++++++++++++++++++
tools/lib/mm/file_utils.h | 12 +++
tools/testing/selftests/mm/Makefile | 6 +-
.../testing/selftests/mm/hugepage_settings.c | 3 +-
tools/testing/selftests/mm/khugepaged.c | 1 +
.../selftests/mm/split_huge_page_test.c | 2 +
tools/testing/selftests/mm/vm_util.c | 65 +--------------
tools/testing/selftests/mm/vm_util.h | 5 --
9 files changed, 106 insertions(+), 72 deletions(-)
create mode 100644 tools/lib/mm/file_utils.c
create mode 100644 tools/lib/mm/file_utils.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 46ed0f0e76d8..7887a3263373 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -16936,6 +16936,7 @@ F: mm/memory-tiers.c
F: mm/page_idle.c
F: mm/pgalloc-track.h
F: mm/process_vm_access.c
+F: tools/lib/mm/
F: tools/testing/selftests/mm/
MEMORY MANAGEMENT - NUMA MEMBLOCKS AND NUMA EMULATION
diff --git a/tools/lib/mm/file_utils.c b/tools/lib/mm/file_utils.c
new file mode 100644
index 000000000000..0f9322f2cf41
--- /dev/null
+++ b/tools/lib/mm/file_utils.c
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "file_utils.h"
+
+int read_file(const char *path, char *buf, size_t buflen)
+{
+ int fd;
+ ssize_t numread;
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return 0;
+
+ numread = read(fd, buf, buflen - 1);
+ if (numread < 1) {
+ close(fd);
+ return 0;
+ }
+
+ buf[numread] = '\0';
+ close(fd);
+
+ return (unsigned int)numread;
+}
+
+void write_file(const char *path, const char *buf, size_t buflen)
+{
+ int fd, saved_errno;
+ ssize_t numwritten;
+
+ if (buflen < 2) {
+ fprintf(stderr, "Incorrect buffer len: %zu\n", buflen);
+ exit(EXIT_FAILURE);
+ }
+
+ fd = open(path, O_WRONLY);
+ if (fd == -1) {
+ fprintf(stderr, "%s open failed: %s\n", path, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ numwritten = write(fd, buf, buflen - 1);
+ saved_errno = errno;
+ close(fd);
+ errno = saved_errno;
+ if (numwritten < 0) {
+ fprintf(stderr, "%s write(%.*s) failed: %s\n",
+ path, (int)(buflen - 1), buf, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ if (numwritten != (ssize_t)(buflen - 1)) {
+ fprintf(stderr,
+ "%s write(%.*s) is truncated, expected %zu bytes, got %zd bytes\n",
+ path, (int)(buflen - 1), buf, buflen - 1, numwritten);
+ exit(EXIT_FAILURE);
+ }
+}
+
+unsigned long read_num(const char *path)
+{
+ char buf[21];
+
+ if (read_file(path, buf, sizeof(buf)) < 0) {
+ perror("read_file()");
+ exit(EXIT_FAILURE);
+ }
+
+ return strtoul(buf, NULL, 10);
+}
+
+void write_num(const char *path, unsigned long num)
+{
+ char buf[21];
+
+ snprintf(buf, sizeof(buf), "%lu", num);
+ write_file(path, buf, strlen(buf) + 1);
+}
diff --git a/tools/lib/mm/file_utils.h b/tools/lib/mm/file_utils.h
new file mode 100644
index 000000000000..060a84725c9d
--- /dev/null
+++ b/tools/lib/mm/file_utils.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __MM_FILE_UTILS_H__
+#define __MM_FILE_UTILS_H__
+
+#include <stddef.h>
+
+int read_file(const char *path, char *buf, size_t buflen);
+void write_file(const char *path, const char *buf, size_t buflen);
+unsigned long read_num(const char *path);
+void write_num(const char *path, unsigned long num);
+
+#endif
diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile
index e6df968f0971..b5fb4b6ab31b 100644
--- a/tools/testing/selftests/mm/Makefile
+++ b/tools/testing/selftests/mm/Makefile
@@ -37,7 +37,7 @@ endif
# LDLIBS.
MAKEFLAGS += --no-builtin-rules
-CFLAGS = -Wall -O2 -I $(top_srcdir) $(EXTRA_CFLAGS) $(KHDR_INCLUDES) $(TOOLS_INCLUDES)
+CFLAGS = -Wall -O2 -I $(top_srcdir) -I $(top_srcdir)/tools/lib $(EXTRA_CFLAGS) $(KHDR_INCLUDES) $(TOOLS_INCLUDES)
CFLAGS += -Wunreachable-code
LDLIBS = -lrt -lpthread -lm
@@ -187,8 +187,8 @@ TEST_FILES += write_hugetlb_memory.sh
include ../lib.mk
-$(TEST_GEN_PROGS): vm_util.c hugepage_settings.c
-$(TEST_GEN_FILES): vm_util.c hugepage_settings.c
+$(TEST_GEN_PROGS): vm_util.c hugepage_settings.c $(top_srcdir)/tools/lib/mm/file_utils.c
+$(TEST_GEN_FILES): vm_util.c hugepage_settings.c $(top_srcdir)/tools/lib/mm/file_utils.c
$(OUTPUT)/uffd-stress: uffd-common.c
$(OUTPUT)/uffd-unit-tests: uffd-common.c
diff --git a/tools/testing/selftests/mm/hugepage_settings.c b/tools/testing/selftests/mm/hugepage_settings.c
index 2eab2110ac6a..5e947abb7425 100644
--- a/tools/testing/selftests/mm/hugepage_settings.c
+++ b/tools/testing/selftests/mm/hugepage_settings.c
@@ -8,8 +8,9 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <mm/file_utils.h>
-#include "vm_util.h"
+#include "kselftest.h"
#include "hugepage_settings.h"
#define THP_SYSFS "/sys/kernel/mm/transparent_hugepage/"
diff --git a/tools/testing/selftests/mm/khugepaged.c b/tools/testing/selftests/mm/khugepaged.c
index 10e8dedcb087..ae5945e2bfac 100644
--- a/tools/testing/selftests/mm/khugepaged.c
+++ b/tools/testing/selftests/mm/khugepaged.c
@@ -18,6 +18,7 @@
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/vfs.h>
+#include <mm/file_utils.h>
#include "linux/magic.h"
diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c
index 50c80ceb4988..4a2ccc055c8b 100644
--- a/tools/testing/selftests/mm/split_huge_page_test.c
+++ b/tools/testing/selftests/mm/split_huge_page_test.c
@@ -19,6 +19,8 @@
#include <malloc.h>
#include <stdbool.h>
#include <time.h>
+#include <mm/file_utils.h>
+
#include "vm_util.h"
#include "kselftest.h"
#include "hugepage_settings.h"
diff --git a/tools/testing/selftests/mm/vm_util.c b/tools/testing/selftests/mm/vm_util.c
index 311fc5b4513e..631dc2baff97 100644
--- a/tools/testing/selftests/mm/vm_util.c
+++ b/tools/testing/selftests/mm/vm_util.c
@@ -8,6 +8,8 @@
#include <linux/fs.h>
#include <sys/syscall.h>
#include <unistd.h>
+#include <mm/file_utils.h>
+
#include "kselftest.h"
#include "vm_util.h"
@@ -698,69 +700,6 @@ int unpoison_memory(unsigned long pfn)
return ret > 0 ? 0 : -errno;
}
-int read_file(const char *path, char *buf, size_t buflen)
-{
- int fd;
- ssize_t numread;
-
- fd = open(path, O_RDONLY);
- if (fd == -1)
- return 0;
-
- numread = read(fd, buf, buflen - 1);
- if (numread < 1) {
- close(fd);
- return 0;
- }
-
- buf[numread] = '\0';
- close(fd);
-
- return (unsigned int) numread;
-}
-
-void write_file(const char *path, const char *buf, size_t buflen)
-{
- int fd, saved_errno;
- ssize_t numwritten;
-
- if (buflen < 2)
- ksft_exit_fail_msg("Incorrect buffer len: %zu\n", buflen);
-
- fd = open(path, O_WRONLY);
- if (fd == -1)
- ksft_exit_fail_msg("%s open failed: %s\n", path, strerror(errno));
-
- numwritten = write(fd, buf, buflen - 1);
- saved_errno = errno;
- close(fd);
- errno = saved_errno;
- if (numwritten < 0)
- ksft_exit_fail_msg("%s write(%.*s) failed: %s\n", path, (int)(buflen - 1),
- buf, strerror(errno));
- if (numwritten != buflen - 1)
- ksft_exit_fail_msg("%s write(%.*s) is truncated, expected %zu bytes, got %zd bytes\n",
- path, (int)(buflen - 1), buf, buflen - 1, numwritten);
-}
-
-unsigned long read_num(const char *path)
-{
- char buf[21];
-
- if (read_file(path, buf, sizeof(buf)) < 0)
- ksft_exit_fail_perror("read_file()");
-
- return strtoul(buf, NULL, 10);
-}
-
-void write_num(const char *path, unsigned long num)
-{
- char buf[21];
-
- sprintf(buf, "%lu", num);
- write_file(path, buf, strlen(buf) + 1);
-}
-
static unsigned long shmall, shmmax;
void __shm_limits_restore(void)
diff --git a/tools/testing/selftests/mm/vm_util.h b/tools/testing/selftests/mm/vm_util.h
index ea8fc8fdf0eb..d0932cdd9a34 100644
--- a/tools/testing/selftests/mm/vm_util.h
+++ b/tools/testing/selftests/mm/vm_util.h
@@ -164,11 +164,6 @@ int unpoison_memory(unsigned long pfn);
#define PAGEMAP_PRESENT(ent) (((ent) & (1ull << 63)) != 0)
#define PAGEMAP_PFN(ent) ((ent) & ((1ull << 55) - 1))
-void write_file(const char *path, const char *buf, size_t buflen);
-int read_file(const char *path, char *buf, size_t buflen);
-unsigned long read_num(const char *path);
-void write_num(const char *path, unsigned long num);
-
void shm_limits_prepare(unsigned long length);
void __shm_limits_restore(void);
--
2.39.5
^ permalink raw reply related
* [PATCH v3 0/4] selftests/mm: separate GUP microbenchmarking from functional testing
From: Sarthak Sharma @ 2026-05-21 11:17 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand
Cc: Jonathan Corbet, Lorenzo Stoakes, Liam R . Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Shuah Khan, Shuah Khan, Jason Gunthorpe, John Hubbard, Peter Xu,
Leon Romanovsky, Zi Yan, Baolin Wang, Nico Pache, Ryan Roberts,
Dev Jain, Barry Song, Lance Yang, Mark Brown, linux-kernel,
linux-mm, linux-kselftest, linux-doc, Sarthak Sharma
gup_test.c currently serves two distinct purposes: microbenchmarking
(GUP_FAST_BENCHMARK, PIN_FAST_BENCHMARK, PIN_LONGTERM_BENCHMARK) and
functional correctness testing (GUP_BASIC_TEST, PIN_BASIC_TEST,
DUMP_USER_PAGES_TEST). Mixing these in a single binary means functional
tests cannot be run or reported individually and run_vmtests.sh must
invoke the binary multiple times with different flag combinations to
cover all configurations.
This patch series separates the two concerns: tools/mm/gup_bench for
benchmarking and tools/testing/selftests/mm/gup_test for functional
testing. To avoid duplicating HugeTLB and related file helpers, the
series first moves the common helper code to tools/lib/mm/ so it can be
shared by both selftests and tools/mm.
Patch 1 adds tools/lib/mm/file_utils.[ch], moving read_file(),
write_file(), read_num() and write_num() out of vm_util.c into a shared
helper without a kselftest dependency. It also adds tools/lib/mm/ to the
MEMORY MANAGEMENT - MISC entry in MAINTAINERS.
Patch 2 moves hugepage_settings.[ch] from selftests/mm to tools/lib/mm/,
updates the selftests/mm users to include it from there and removes the
remaining kselftest dependency from the implementation while dropping
non-fatal hugetlb informational prints from the shared helper.
Patch 3 adds tools/mm/gup_bench.c, a standalone microbenchmark for
GUP_FAST, PIN_FAST and PIN_LONGTERM via the CONFIG_GUP_TEST debugfs
interface. It runs the same matrix of configurations as the old
run_gup_matrix() shell function (all three commands, read/write,
private/shared, four page counts, THP on/off, hugetlb), but as a
standalone C program under tools/mm using the shared tools/lib/mm
helpers.
Patch 4 rewrites gup_test.c as a kselftest harness-based selftest. It
covers all five GUP kernel functions (get_user_pages, get_user_pages_fast,
pin_user_pages, pin_user_pages_fast, pin_user_pages with FOLL_LONGTERM)
plus DUMP_USER_PAGES_TEST, across 12 mapping configurations (THP on,
THP off and hugetlb, each across private/shared and read/write variants)
and four batch sizes (1, 512, 123, all pages). It also preserves the old
sparse dump coverage for pages 0, 19 and 0x1000. Results are reported as
standard TAP output with no command-line arguments required.
---
These patches apply on top of mm/mm-new.
Changes in v3:
- Address v2 feedback from Sashiko
- Add shared file_utils helpers under tools/lib/mm
- Move hugepage_settings out of selftests and into tools/lib/mm
- Convert gup_bench to use the shared tools/lib/mm helpers
- Guard against invalid thread counts in gup_bench
- Handle thread-array allocation failure cleanly in gup_bench
- Restore hugetlb settings on setup failure in gup_test
- Add sparse DUMP_USER_PAGES_TEST coverage for pages 0, 19 and 0x1000 in gup_test
Changes in v2:
- Address v1 feedback from Sashiko
- Add fast and longterm GUP/PUP coverage
- Sweep nr_pages_per_call over 1, 512, 123 and all pages
- Call madvise(MADV_NOHUGEPAGE) in non-THP variants
- Use 256 MB for hugetlb fixtures
- Use hugetlb_restore_settings() in FIXTURE_TEARDOWN instead of atexit()
- Add TH_LOG to report nr_pages_per_call for each iteration
- Update Documentation/core-api/pin_user_pages.rst unit testing section
Previous versions:
v2: https://lore.kernel.org/all/20260519120506.184512-1-sarthak.sharma@arm.com/
v1: https://lore.kernel.org/all/20260515084840.174652-1-sarthak.sharma@arm.com/
---
Sarthak Sharma (4):
tools/lib/mm: add shared file helpers
tools/lib/mm: move hugepage_settings out of selftests
tools/mm: add a standalone GUP microbenchmark
selftests/mm: rewrite gup_test as a standalone harness-based selftest
Documentation/core-api/pin_user_pages.rst | 12 +-
MAINTAINERS | 2 +
tools/lib/mm/file_utils.c | 83 +++
tools/lib/mm/file_utils.h | 12 +
.../selftests => lib}/mm/hugepage_settings.c | 14 +-
.../selftests => lib}/mm/hugepage_settings.h | 0
tools/mm/.gitignore | 2 +
tools/mm/Makefile | 10 +-
tools/mm/gup_bench.c | 390 ++++++++++++
tools/testing/selftests/mm/Makefile | 8 +-
tools/testing/selftests/mm/compaction_test.c | 2 +-
tools/testing/selftests/mm/cow.c | 2 +-
.../selftests/mm/folio_split_race_test.c | 3 +-
tools/testing/selftests/mm/guard-regions.c | 3 +-
tools/testing/selftests/mm/gup_longterm.c | 2 +-
tools/testing/selftests/mm/gup_test.c | 587 +++++++++++-------
tools/testing/selftests/mm/hmm-tests.c | 6 +-
tools/testing/selftests/mm/hugetlb-madvise.c | 3 +-
tools/testing/selftests/mm/hugetlb-mmap.c | 3 +-
tools/testing/selftests/mm/hugetlb-mremap.c | 3 +-
tools/testing/selftests/mm/hugetlb-shm.c | 2 +-
.../selftests/mm/hugetlb-soft-offline.c | 2 +-
tools/testing/selftests/mm/hugetlb-vmemmap.c | 3 +-
tools/testing/selftests/mm/hugetlb_dio.c | 3 +-
.../selftests/mm/hugetlb_fault_after_madv.c | 2 +-
.../selftests/mm/hugetlb_madv_vs_map.c | 2 +-
tools/testing/selftests/mm/khugepaged.c | 3 +-
tools/testing/selftests/mm/ksm_tests.c | 2 +-
tools/testing/selftests/mm/migration.c | 6 +-
tools/testing/selftests/mm/pagemap_ioctl.c | 2 +-
.../testing/selftests/mm/prctl_thp_disable.c | 2 +-
tools/testing/selftests/mm/protection_keys.c | 2 +-
tools/testing/selftests/mm/run_vmtests.sh | 37 +-
tools/testing/selftests/mm/soft-dirty.c | 2 +-
.../selftests/mm/split_huge_page_test.c | 4 +-
tools/testing/selftests/mm/thuge-gen.c | 3 +-
tools/testing/selftests/mm/transhuge-stress.c | 3 +-
tools/testing/selftests/mm/uffd-common.h | 2 +-
tools/testing/selftests/mm/uffd-wp-mremap.c | 3 +-
.../selftests/mm/va_high_addr_switch.c | 2 +-
tools/testing/selftests/mm/vm_util.c | 65 +-
tools/testing/selftests/mm/vm_util.h | 5 -
42 files changed, 935 insertions(+), 369 deletions(-)
create mode 100644 tools/lib/mm/file_utils.c
create mode 100644 tools/lib/mm/file_utils.h
rename tools/{testing/selftests => lib}/mm/hugepage_settings.c (98%)
rename tools/{testing/selftests => lib}/mm/hugepage_settings.h (100%)
create mode 100644 tools/mm/gup_bench.c
base-commit: 4c6512d033e5e9469cc76d07512c24a0ee0ac207
--
2.39.5
^ permalink raw reply
* [PATCH v7 7/8] docs/zh_CN: Add usbmon.rst translation
From: Kefan Bai @ 2026-05-21 9:55 UTC (permalink / raw)
To: linux-usb, si.yanteng
Cc: gregkh, seakeel, alexs, dzm91, corbet, skhan, linux-doc, doubled
In-Reply-To: <cover.1779355170.git.baikefan@leap-io-kernel.com>
Translate .../usb/usbmon.rst into Chinese
Update the translation through commit 788183a6e8b0
("docs: usb: fix literal block marker in usbmon verification example")
Reviewed-by: Yanteng Si <siyanteng@cqsoftware.com.cn>
Signed-off-by: Kefan Bai <baikefan@leap-io-kernel.com>
---
.../translations/zh_CN/usb/index.rst | 2 +-
.../translations/zh_CN/usb/usbmon.rst | 427 ++++++++++++++++++
2 files changed, 428 insertions(+), 1 deletion(-)
create mode 100644 Documentation/translations/zh_CN/usb/usbmon.rst
diff --git a/Documentation/translations/zh_CN/usb/index.rst b/Documentation/translations/zh_CN/usb/index.rst
index 8c6b26912320..eb5aca0c13ec 100644
--- a/Documentation/translations/zh_CN/usb/index.rst
+++ b/Documentation/translations/zh_CN/usb/index.rst
@@ -22,10 +22,10 @@ USB 支持
chipidea
dwc3
ehci
+ usbmon
Todolist:
-* usbmon
* functionfs
* functionfs-desc
* gadget_configfs
diff --git a/Documentation/translations/zh_CN/usb/usbmon.rst b/Documentation/translations/zh_CN/usb/usbmon.rst
new file mode 100644
index 000000000000..11b6d5b59dce
--- /dev/null
+++ b/Documentation/translations/zh_CN/usb/usbmon.rst
@@ -0,0 +1,427 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/usb/usbmon.rst
+
+:翻译:
+
+ 白钶凡 Kefan Bai <baikefan@leap-io-kernel.com>
+
+:校译:
+
+
+======
+usbmon
+======
+
+简介
+====
+小写形式的 ``usbmon`` 指的是内核中的一项功能,
+用于收集 USB 总线上的 I/O 跟踪信息。它类似于网络监控工具
+``tcpdump(1)`` 或 Ethereal 所使用的数据包套接字。
+类似地,人们希望使用 usbdump 或 USBMon
+(首字母大写)之类的工具来检查
+usbmon 生成的原始跟踪数据。
+
+usbmon 报告的是各个外设驱动
+向主机控制器驱动(HCD)发出的请求。
+因此,如果 HCD 本身有 bug,那么 usbmon 报告的跟踪信息
+可能无法精确对应实际的总线事务。
+这和 tcpdump 的情况是一样的。
+
+目前实现了两种 API: ``text`` 和 ``binary``。
+二进制 API 通过 ``/dev`` 命名空间中的字符设备提供,
+并且属于 ABI。文本 API 自内核 2.6.35 起已废弃,
+但为了方便仍然可用。
+
+如何使用 usbmon 收集原始文本跟踪信息
+====================================
+
+与数据包套接字不同,usbmon 提供了一种接口,
+可以输出文本格式的跟踪信息。这样做有两个目的:
+第一,在更完善的格式最终确定之前,
+它作为工具间通用的跟踪交换格式;
+第二,在不使用工具的情况下,人们也可以直接阅读这些信息。
+
+要收集原始文本跟踪信息,请按以下步骤进行操作。
+
+1. 准备
+-------
+
+挂载 debugfs(内核配置中必须启用它),并加载 usbmon 模块
+(如果它是作为模块构建的)。如果 usbmon 已经编入内核,
+那么第二步可以省略。
+
+命令示例::
+
+ # mount -t debugfs none_debugs /sys/kernel/debug
+ # modprobe usbmon
+ #
+
+确认总线套接字是否存在::
+
+ # ls /sys/kernel/debug/usb/usbmon
+ 0s 0u 1s 1t 1u 2s 2t 2u 3s 3t 3u 4s 4t 4u
+ #
+
+现在,你可以选择使用 ``0u`` 捕获所有总线上的数据包,
+并跳到第 3 步;
+也可以先按第 2 步找到目标设备所在的总线。
+这样可以过滤掉那些持续输出数据的烦人设备。
+
+2. 查找目标设备连接的是哪条总线
+-------------------------------
+
+运行 ``cat /sys/kernel/debug/usb/devices``,
+找到对应设备的 T 行。通常可以通过厂商字符串来查找。
+如果有许多类似设备,可以拔掉其中一个,
+再比较前后两次 ``/sys/kernel/debug/usb/devices``
+的输出。T 行里会包含总线编号。
+
+示例::
+
+ T: Bus=03 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=12 MxCh= 0
+ D: Ver= 1.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1
+ P: Vendor=0557 ProdID=2004 Rev= 1.00
+ S: Manufacturer=ATEN
+ S: Product=UC100KM V2.00
+
+``Bus=03`` 表示它位于 3 号总线上。或者,
+也可以查看 ``lsusb`` 的输出,并从对应行得到总线编号。
+
+示例如下::
+
+ Bus 003 Device 002: ID 0557:2004 ATEN UC100KM V2.00
+
+
+3. 启动 cat 命令
+----------------
+
+如果只监听单条总线,可执行::
+
+ # cat /sys/kernel/debug/usb/usbmon/3u > /tmp/1.mon.out
+
+否则,如果要监听所有总线,则执行::
+
+ # cat /sys/kernel/debug/usb/usbmon/0u > /tmp/1.mon.out
+
+此进程会一直读取,直到被终止。
+由于输出通常会很长,因此更推荐将输出重定向到某个位置。
+
+
+4. 在 USB 总线上执行期望的操作
+------------------------------
+
+此处需要执行一些会产生 USB 流量的动作,
+比如插入 U 盘、拷贝文件、操作摄像头等。
+
+
+5. 停止 cat
+-----------
+
+这一步通常通过键盘中断(Control-C)完成。
+
+此时输出文件(本例中为 ``/tmp/1.mon.out``)
+可以保存、通过电子邮件发送,或使用文本编辑器查看。
+如果使用最后一种方式,请确保文件不会大到编辑器无法打开。
+
+
+原始文本数据格式
+================
+
+目前支持两种格式:原始格式,也就是 ``1t`` 格式,
+以及 ``1u`` 格式。``1t`` 格式在内核 2.6.21 中已被废弃。
+``1u`` 格式增加了一些字段,例如 ISO 帧描述符、
+``interval`` 等。它生成的行会稍长一些,
+但在其他方面是 ``1t`` 格式的完整超集。
+
+如果程序需要区分上述两种格式,
+可以查看 ``address`` 字段(见下文)。
+如果其中有两个冒号,就是 ``1t`` 格式;
+否则是 ``1u`` 格式。
+
+任何文本格式的数据由一系列事件组成,
+如 URB 提交、URB 回调、提交错误等。
+每个事件对应单独的一行文本,
+由使用空白符间隔的若干字段组成。
+字段的数量与位置可能取决于事件类型,
+但以下字段对所有类型都通用:
+
+下面按从左到右的顺序列出这些共有字段:
+
+- URB Tag。用于标识 URB,通常是 URB 结构体在内核中的地址
+ (以十六进制表示),
+ 但也可能是序号或其他合理的唯一字符串。
+
+- 时间戳(微秒),十进制数字。
+ 时间戳的精度取决于可用时钟,
+ 因此可能远差于
+ 1 微秒(例如实现使用的是 jiffies)。
+
+- 事件类型。它表示的是事件的格式,而不是 URB 的类型。
+ 可用值为:``S`` 表示提交,``C`` 表示回调,``E`` 表示提交错误。
+
+- ``Address`` 字段(以前称作 ``pipe``)。
+ 它包含四个由冒号分隔的字段:
+ URB 类型及方向、总线号、设备地址和端点号。类型与方向的编码如下:
+
+ == == ==========================
+ Ci Co 控制输入和输出
+ Zi Zo 等时输入和输出
+ Ii Io 中断输入和输出
+ Bi Bo 批量输入和输出
+ == == ==========================
+
+ 总线号、设备地址和端点号使用十进制,但可能有前导零。
+
+- URB 状态字段。这个字段要么是一个字母,
+ 要么是几个由冒号分隔的数字:
+ URB 状态、``interval``、``start frame`` 和 ``error count``。
+ 与 ``address`` 字段不同,除了状态外,其余字段都是可选的。
+ ``interval`` 只会为中断和等时 URB 打印;``start frame`` 只会为
+ 等时 URB 打印;错误计数只会在等时回调事件中打印。
+
+ 状态字段是一个十进制数字,有时为负数,
+ 对应 URB 的 ``status`` 字段。
+ 对于提交事件,这个字段本身没有实际意义,
+ 但为了便于脚本解析,它仍然存在。
+ 当发生错误时,该字段包含错误码。
+
+ 在提交控制包时,这个字段包含的是 ``Setup Tag``,
+ 而不是一组数字。
+ 判断 ``Setup Tag`` 是否存在很容易,因为它从来不是数字。
+ 因此,如果脚本在这个字段里发现的是一组数字,
+ 就会继续读取数据长度(等时 URB 除外)。
+ 如果发现的是其他内容,比如一个字母,
+ 那么脚本会先读取 ``Setup`` 包,再读取数据长度或等时描述符。
+
+- ``Setup`` 包由 5 个字段组成:
+ ``bmRequestType``、``bRequest``、``wValue``、
+ ``wIndex`` 和 ``wLength``。这些字段由 USB 2.0 规范定义。
+ 如果 ``Setup Tag`` 为 ``s``,就可以安全地解码这些字段。
+ 否则,说明 Setup 包虽然存在,但并未被捕获,此时各字段中会填入占位内容。
+
+- 等时传输帧描述符的数量及其内容:
+ 如果一个等时传输事件带有一组描述符,首先打印该 URB 中描述符的总数,
+ 然后为每个描述符打印一个字段,最多打印 5 个字段。
+ 每个字段由三个用冒号分隔的十进制数字组成,
+ 分别表示状态(status)、偏移(offset)和长度(length)。
+ 对于提交(submission),报告的是初始长度;
+ 对于回调(callback),报告的是实际长度。
+
+- 数据长度:
+ 对于提交,表示请求的长度;对于回调,表示实际传输的长度。
+
+- 数据标签:
+ 即使数据长度非零,usbmon 也不一定会捕获数据。
+ 仅当标签为 ``=`` 时,才会有数据字段。
+
+- 数据字段:
+ 以大端十六进制格式显示。注意,这些并不是真正的机器字,
+ 而只是把字节流拆成若干“字”以便阅读。因此最后一个字可能只包含
+ 1 到 4 个字节。
+ 收集的数据长度是有限的,可能小于数据长度字段中报告的值。
+ 因为数据长度字段只统计实际接收到的字节,而数据字段包含整个传输缓冲区,
+ 所以,在等时输入(Zi)完成且缓冲区中接收到的数据稀疏的情况下,
+ 收集的数据长度可能大于数据长度字段的值。
+
+
+
+示例:
+
+获取端口状态的输入控制传输::
+
+ d5ea89a0 3575914555 S Ci:1:001:0 s a3 00 0000 0003 0004 4 <
+ d5ea89a0 3575914560 C Ci:1:001:0 0 4 = 01050000
+
+向地址为 5 的存储设备发送
+31 字节 Bulk 包装的 SCSI 命令 ``0x28``
+(``READ_10``)的输出批量传输::
+
+ dd65f0e8 4128379752 S Bo:1:005:2 -115 31 = 55534243 ad000000 00800000 80010a28 20000000 20000040 00000000 000000
+ dd65f0e8 4128379808 C Bo:1:005:2 0 31 >
+
+原始二进制格式与 API
+====================
+API 的整体架构与前文大体相同,只是事件以二进制格式传递。
+每个事件都通过下面的结构发送
+(这个名字是为了叙述方便而虚构的)::
+
+
+ struct usbmon_packet {
+ u64 id; /* 0: URB ID - 从提交到回调 */
+ unsigned char type; /* 8: 与文本相同;可扩展 */
+ unsigned char xfer_type; /* ISO (0)、中断、控制、批量 (3) */
+ unsigned char epnum; /* 端点号和传输方向 */
+ unsigned char devnum; /* 设备地址 */
+ u16 busnum; /* 12: 总线号 */
+ char flag_setup; /* 14: 与文本相同 */
+ char flag_data; /* 15: 与文本相同;二进制零也可 */
+ s64 ts_sec; /* 16: gettimeofday */
+ s32 ts_usec; /* 24: gettimeofday */
+ int status; /* 28: */
+ unsigned int length; /* 32: 数据长度(提交或实际) */
+ unsigned int len_cap; /* 36: 已捕获的数据长度 */
+ union { /* 40: */
+ unsigned char setup[SETUP_LEN]; /* 仅用于控制类 S 事件 */
+ struct iso_rec { /* 仅用于 ISO */
+ int error_count;
+ int numdesc;
+ } iso;
+ } s;
+ int interval; /* 48: 仅用于中断和 ISO */
+ int start_frame; /* 52: 仅用于 ISO */
+ unsigned int xfer_flags; /* 56: URB 的 transfer_flags 副本 */
+ unsigned int ndesc; /* 60: 实际 ISO 描述符数量 */
+ }; /* 64 总长度 */
+
+可以用 ``read(2)``、``ioctl(2)``,
+或者通过 ``mmap`` 访问缓冲区,
+从字符设备接收这些事件。
+不过,出于兼容性原因,``read(2)``
+只返回前 48 个字节。
+
+字符设备通常命名为 ``/dev/usbmonN``,
+其中 ``N`` 是 USB 总线号。
+编号为零的设备(``/dev/usbmon0``)比较特殊,
+表示“所有总线”。
+请注意,具体命名策略由 Linux 发行版决定。
+
+如果你手动创建 ``/dev/usbmon0``,
+请确保它归 root 所有,并且权限为 ``0600``。
+否则,非特权用户将能够窃听键盘流量。
+
+以下 ``MON_IOC_MAGIC`` 为 ``0x92`` 的 ioctl 调用可用:
+
+``MON_IOCQ_URB_LEN``,定义为 ``_IO(MON_IOC_MAGIC, 1)``
+
+该调用返回下一个事件的数据长度。
+注意大多数事件不包含数据,
+因此如果该调用返回零,并不意味着没有事件。
+
+``MON_IOCG_STATS``,定义为
+``_IOR(MON_IOC_MAGIC, 3, struct mon_bin_stats)``
+
+参数是指向以下结构的指针::
+
+ struct mon_bin_stats {
+ u32 queued;
+ u32 dropped;
+ };
+
+成员 ``queued`` 表示当前缓冲区中已经排队的事件数量,
+而不是自上次重置以来处理过的事件数量。
+
+成员 ``dropped`` 表示自上次调用
+``MON_IOCG_STATS`` 以来丢失的事件数量。
+
+``MON_IOCT_RING_SIZE``,定义为 ``_IO(MON_IOC_MAGIC, 4)``
+
+此调用设置缓冲区大小。参数为以字节为单位的缓冲区大小。
+大小可能会向下取整到下一个块(或页)。
+如果请求的大小超出该内核的 [未指定] 范围,
+则调用会失败并返回 ``-EINVAL``。
+
+``MON_IOCQ_RING_SIZE``,定义为 ``_IO(MON_IOC_MAGIC, 5)``
+
+该调用返回缓冲区当前大小(以字节为单位)。
+
+``MON_IOCX_GET``,定义为
+``_IOW(MON_IOC_MAGIC, 6, struct mon_get_arg)``
+``MON_IOCX_GETX``,定义为
+``_IOW(MON_IOC_MAGIC, 10, struct mon_get_arg)``
+
+如果内核缓冲区中没有事件,
+这些调用就会一直等待,直到有事件到达,
+然后返回第一个事件。
+参数是指向以下结构的指针::
+
+ struct mon_get_arg {
+ struct usbmon_packet *hdr;
+ void *data;
+ size_t alloc; /* 数据长度可以为零 */
+ };
+
+
+调用前,应填好 ``hdr``、``data`` 和 ``alloc``。
+调用返回后,``hdr`` 指向的区域中包含下一个事件的结构;
+如果存在数据,那么数据缓冲区中也会包含相应数据。
+该事件会从内核缓冲区中移除。
+
+``MON_IOCX_GET`` 会将 48 字节的数据复制到 ``hdr`` 区域,
+``MON_IOCX_GETX`` 会复制 64 字节。
+
+``MON_IOCX_MFETCH``,定义为
+``_IOWR(MON_IOC_MAGIC, 7, struct mon_mfetch_arg)``
+
+当应用程序通过 ``mmap(2)`` 访问缓冲区时,
+主要使用这个 ioctl。
+其参数是指向以下结构的指针::
+
+ struct mon_mfetch_arg {
+ uint32_t *offvec; /* 获取的事件偏移向量 */
+ uint32_t nfetch; /* 要获取的事件数量(输出:已获取) */
+ uint32_t nflush; /* 要刷新的事件数量 */
+ };
+
+
+该 ioctl 的操作分为三个阶段:
+
+首先,从内核缓冲区移除并丢弃最多 ``nflush`` 个事件。
+实际丢弃的事件数量会写回 ``nflush``。
+
+其次,除非伪设备以 ``O_NONBLOCK`` 打开,否则会一直等待,
+直到缓冲区中出现事件。
+
+第三,将最多 ``nfetch`` 个偏移量提取到 mmap
+缓冲区,并存入 ``offvec`` 中。
+实际提取到的事件偏移数量会存回 ``nfetch``。
+
+``MON_IOCH_MFLUSH``,定义为 ``_IO(MON_IOC_MAGIC, 8)``
+
+此调用从内核缓冲区移除若干事件。
+其参数为要移除的事件数量。
+如果缓冲区中的事件少于请求数量,
+则移除所有事件,且不报告错误。
+当没有事件时也可使用。
+
+``FIONBIO``
+
+如果有需要,将来可能会实现 ``FIONBIO`` ioctl。
+
+除了 ``ioctl(2)`` 和 ``read(2)`` 之外,
+二进制 API 的特殊文件也可以用 ``select(2)`` 和
+``poll(2)`` 轮询。
+但 ``lseek(2)`` 不起作用。
+
+* 二进制 API 的内核缓冲区内存映射访问
+
+基本思想很简单:
+
+准备时,先获取当前大小,再用 ``mmap(2)`` 映射缓冲区。
+然后执行类似下面伪代码的循环::
+
+ struct mon_mfetch_arg fetch;
+ struct usbmon_packet *hdr;
+ int nflush = 0;
+ for (;;) {
+ fetch.offvec = vec; // 有 N 个 32 位字
+ fetch.nfetch = N; // 或者少于 N
+ fetch.nflush = nflush;
+ ioctl(fd, MON_IOCX_MFETCH, &fetch); // 同时处理错误
+ nflush = fetch.nfetch; // 完成后要刷新这么多包
+ for (i = 0; i < nflush; i++) {
+ hdr = (struct ubsmon_packet *) &mmap_area[vec[i]];
+ if (hdr->type == '@') // 填充包
+ continue;
+ caddr_t data = &mmap_area[vec[i]] + 64;
+ process_packet(hdr, data);
+ }
+ }
+
+
+
+因此,主要思想是每 N 个事件只执行一次 ioctl。
+
+虽然缓冲区是环形的,但返回的头和数据不会跨越缓冲区末端,
+因此上面的伪代码无需任何合并操作。
--
2.54.0
^ permalink raw reply related
* [PATCH v7 2/8] docs/zh_CN: Add acm.rst translation
From: Kefan Bai @ 2026-05-21 9:55 UTC (permalink / raw)
To: linux-usb, si.yanteng
Cc: gregkh, seakeel, alexs, dzm91, corbet, skhan, linux-doc, doubled
In-Reply-To: <cover.1779355170.git.baikefan@leap-io-kernel.com>
Translate .../usb/acm.rst into Chinese
Update the translation through commit ecefae6db042
("docs: usb: rename files to .rst and add them to drivers-api")
Reviewed-by: Yanteng Si <siyanteng@cqsoftware.com.cn>
Signed-off-by: Kefan Bai <baikefan@leap-io-kernel.com>
---
Documentation/translations/zh_CN/usb/acm.rst | 147 ++++++++++++++++++
.../translations/zh_CN/usb/index.rst | 2 +-
2 files changed, 148 insertions(+), 1 deletion(-)
create mode 100644 Documentation/translations/zh_CN/usb/acm.rst
diff --git a/Documentation/translations/zh_CN/usb/acm.rst b/Documentation/translations/zh_CN/usb/acm.rst
new file mode 100644
index 000000000000..51d6eb8f5660
--- /dev/null
+++ b/Documentation/translations/zh_CN/usb/acm.rst
@@ -0,0 +1,147 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/usb/acm.rst
+
+:翻译:
+
+ 白钶凡 Kefan Bai <baikefan@leap-io-kernel.com>
+
+:校译:
+
+
+====================
+Linux ACM 驱动 v0.16
+====================
+
+版权所有 (c) 1999 Vojtech Pavlik <vojtech@suse.cz>
+
+由 SuSE 赞助
+
+0. 免责声明
+~~~~~~~~~~~
+本程序是自由软件;你可以在自由软件基金会发布的
+GNU 通用公共许可证第 2 版,或者(按你的选择)
+任何后续版本的条款下重新发布和/或修改它。
+
+发布本程序是希望它能发挥作用,但它不附带任何担保;
+甚至不包括对适销性或特定用途适用性的默示担保。
+详情见 GNU 通用公共许可证。
+
+你应该已经随本程序收到了 GNU 通用公共许可证的副本;
+如果没有,请致信:Free Software Foundation, Inc., 59
+Temple Place, Suite 330, Boston, MA 02111-1307 USA。
+
+如需联系作者,可发送电子邮件至 vojtech@suse.cz,
+或邮寄至:
+Vojtech Pavlik, Ucitelska 1576, Prague 8,
+182 00, Czech Republic。
+
+为方便起见,软件包中已附带 GNU 通用公共许可证
+第 2 版:见 COPYING 文件。
+
+1. 使用方法
+~~~~~~~~~~~
+``drivers/usb/class/cdc-acm.c`` 驱动可用于符合 USB
+通信设备类抽象控制模型(USB CDC ACM)规范的
+USB 调制解调器和 USB ISDN 终端适配器。
+
+许多调制解调器支持此驱动,以下是我所知道的一些型号:
+
+ - 3Com OfficeConnect 56k
+ - 3Com Voice FaxModem Pro
+ - 3Com Sportster
+ - MultiTech MultiModem 56k
+ - Zoom 2986L FaxModem
+ - Compaq 56k FaxModem
+ - ELSA Microlink 56k
+
+我知道有一款 ISDN 终端适配器可以与 ACM 驱动一起使用:
+
+ - 3Com USR ISDN Pro TA
+
+一些手机也可以通过 USB 连接。
+我知道以下机型可以正常工作:
+
+ - SonyEricsson K800i
+
+遗憾的是,许多调制解调器和大多数 ISDN TA
+都使用专有接口,因此无法与此驱动配合工作。
+购买前请先确认设备是否符合 ACM 规范。
+
+要使用这些调制解调器,需要加载以下模块::
+
+ usbcore.ko
+ uhci-hcd.ko ohci-hcd.ko or ehci-hcd.ko
+ cdc-acm.ko
+
+之后就应该可以访问这些调制解调器了。
+应当可以使用 ``minicom``、``ppp`` 和 ``mgetty``
+与它们通信。
+
+2. 验证驱动是否正常工作
+~~~~~~~~~~~~~~~~~~~~~~~
+
+第一步是检查 ``/sys/kernel/debug/usb/devices``,
+其内容应该类似如下::
+
+ T: Bus=01 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=12 MxCh= 2
+ B: Alloc= 0/900 us ( 0%), #Int= 0, #Iso= 0
+ D: Ver= 1.00 Cls=09(hub ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1
+ P: Vendor=0000 ProdID=0000 Rev= 0.00
+ S: Product=USB UHCI Root Hub
+ S: SerialNumber=6800
+ C:* #Ifs= 1 Cfg#= 1 Atr=40 MxPwr= 0mA
+ I: If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub
+ E: Ad=81(I) Atr=03(Int.) MxPS= 8 Ivl=255ms
+ T: Bus=01 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#= 2 Spd=12 MxCh= 0
+ D: Ver= 1.00 Cls=02(comm.) Sub=00 Prot=00 MxPS= 8 #Cfgs= 2
+ P: Vendor=04c1 ProdID=008f Rev= 2.07
+ S: Manufacturer=3Com Inc.
+ S: Product=3Com U.S. Robotics Pro ISDN TA
+ S: SerialNumber=UFT53A49BVT7
+ C: #Ifs= 1 Cfg#= 1 Atr=60 MxPwr= 0mA
+ I: If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=acm
+ E: Ad=85(I) Atr=02(Bulk) MxPS= 64 Ivl= 0ms
+ E: Ad=04(O) Atr=02(Bulk) MxPS= 64 Ivl= 0ms
+ E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=128ms
+ C:* #Ifs= 2 Cfg#= 2 Atr=60 MxPwr= 0mA
+ I: If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=02 Prot=01 Driver=acm
+ E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=128ms
+ I: If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=acm
+ E: Ad=85(I) Atr=02(Bulk) MxPS= 64 Ivl= 0ms
+ E: Ad=04(O) Atr=02(Bulk) MxPS= 64 Ivl= 0ms
+
+这三行的存在很关键(以及 ``Cls=`` 字段里出现的
+``comm`` 和 ``data`` 类);它说明这是一个 ACM
+设备。``Driver=acm`` 表示该设备正在使用 acm 驱动。
+如果只看到 ``Cls=ff(vend.)``,那就无能为力了:
+这说明你手上的设备使用的是厂商专有接口::
+
+ D: Ver= 1.00 Cls=02(comm.) Sub=00 Prot=00 MxPS= 8 #Cfgs= 2
+ I: If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=02 Prot=01 Driver=acm
+ I: If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=acm
+
+在系统日志中应该可以看到::
+
+ usb.c: USB new device connect, assigned device number 2
+ usb.c: kmalloc IF c7691fa0, numif 1
+ usb.c: kmalloc IF c7b5f3e0, numif 2
+ usb.c: skipped 4 class/vendor specific interface descriptors
+ usb.c: new device strings: Mfr=1, Product=2, SerialNumber=3
+ usb.c: USB device number 2 default language ID 0x409
+ Manufacturer: 3Com Inc.
+ Product: 3Com U.S. Robotics Pro ISDN TA
+ SerialNumber: UFT53A49BVT7
+ acm.c: probing config 1
+ acm.c: probing config 2
+ ttyACM0: USB ACM device
+ acm.c: acm_control_msg: rq: 0x22 val: 0x0 len: 0x0 result: 0
+ acm.c: acm_control_msg: rq: 0x20 val: 0x0 len: 0x7 result: 7
+ usb.c: acm driver claimed interface c7b5f3e0
+ usb.c: acm driver claimed interface c7b5f3f8
+ usb.c: acm driver claimed interface c7691fa0
+
+如果以上都正常,请启动 ``minicom``,
+把它配置为连接 ``ttyACM`` 设备,然后
+尝试输入 ``at``。如果返回 ``OK``,说明一切工作正常。
diff --git a/Documentation/translations/zh_CN/usb/index.rst b/Documentation/translations/zh_CN/usb/index.rst
index b4cb0ccaa39b..686e5b0a9384 100644
--- a/Documentation/translations/zh_CN/usb/index.rst
+++ b/Documentation/translations/zh_CN/usb/index.rst
@@ -17,10 +17,10 @@ USB 支持
.. toctree::
:maxdepth: 1
+ acm
Todolist:
-* acm
* authorization
* chipidea
* dwc3
--
2.54.0
^ permalink raw reply related
* [PATCH v7 0/8] Add Chinese translation for USB subsystem
From: Kefan Bai @ 2026-05-21 9:55 UTC (permalink / raw)
To: linux-usb, si.yanteng
Cc: gregkh, seakeel, alexs, dzm91, corbet, skhan, linux-doc, doubled
This patch set adds Chinese translations for the USB documentation.
This patch set adds Chinese translations for the USB documentation.
Many thanks to Alex Shi for the careful review and suggestions, and
apologies for the delay in sending this updated version.
Changes in v7:
- Applied the formatting cleanup suggested by Alex Shi across the
whole series.
- Reflowed the translated text for cleaner line alignment and
adjusted title/section adornments for consistency.
- No content changes beyond formatting cleanup.
Changes in v6:
- Rebased the series onto the latest docs-next branch in Jon's tree
(git://git.lwn.net/linux.git), as suggested by Alex Shi.
- Added the USB maintainers to the Cc list.
- Rechecked and polished the Chinese translations throughout the series.
- Link to v6: https://lore.kernel.org/linux-usb/cover.1778415392.git.baikefan@leap-io-kernel.com/
Changes in v5:
- Moved the index.rst entries for acm, authorization, chipidea, dwc3,
ehci, and usbmon into the corresponding patches so the series builds
cleanly when applied patch by patch.
- Removed extra spaces in chipidea.rst.
- Sent the series to linux-usb@vger.kernel.org for review by
Chinese-speaking developers, as suggested by Alex Shi and Yanteng Si.
- Link to v5: https://lore.kernel.org/linux-usb/cover.1765180570.git.baikefan@leap-io-kernel.com/
Changes in v4:
- Shortened overlong title underline and overline markers.
- Removed the CREDITS entry from index.rst.
- Link to v4: https://lore.kernel.org/all/cover.1764674650.git.baikefan@leap-io-kernel.com/
Changes in v3:
- Updated my sign-off to use my full legal name, as requested by
Jonathan Corbet.
- Reviewed and fixed the RST syntax issues noted by Alex Shi.
- Kept the number of translated files to eight to make submission and
review more manageable.
- Link to v3: https://lore.kernel.org/all/cover.1763984424.git.baikefan@leap-io-kernel.com/
Changes in v2:
- Updated [PATCH 01/25] docs/zh_CN: Add index.rst translation to include
the corresponding changes to
Documentation/translations/zh_CN/subsystem-apis.rst.
- Link to v2: https://lore.kernel.org/all/cover.1763897036.git.baikefan@leap-io-kernel.com/
v1:
- Link: https://lore.kernel.org/all/20251123074540.34161-1-baikefan@leap-io-kernel.com/
Kefan Bai (8):
docs/zh_CN: Add index.rst translation
docs/zh_CN: Add acm.rst translation
docs/zh_CN: Add authorization.rst translation
docs/zh_CN: Add chipidea.rst translation
docs/zh_CN: Add dwc3.rst translation
docs/zh_CN: Add ehci.rst translation
docs/zh_CN: Add usbmon.rst translation
docs/zh_CN: Add CREDITS translation
.../translations/zh_CN/subsystem-apis.rst | 2 +-
Documentation/translations/zh_CN/usb/CREDITS | 163 +++++++
Documentation/translations/zh_CN/usb/acm.rst | 147 ++++++
.../translations/zh_CN/usb/authorization.rst | 139 ++++++
.../translations/zh_CN/usb/chipidea.rst | 150 ++++++
Documentation/translations/zh_CN/usb/dwc3.rst | 63 +++
Documentation/translations/zh_CN/usb/ehci.rst | 261 +++++++++++
.../translations/zh_CN/usb/index.rst | 54 +++
.../translations/zh_CN/usb/usbmon.rst | 427 ++++++++++++++++++
9 files changed, 1405 insertions(+), 1 deletion(-)
create mode 100644 Documentation/translations/zh_CN/usb/CREDITS
create mode 100644 Documentation/translations/zh_CN/usb/acm.rst
create mode 100644 Documentation/translations/zh_CN/usb/authorization.rst
create mode 100644 Documentation/translations/zh_CN/usb/chipidea.rst
create mode 100644 Documentation/translations/zh_CN/usb/dwc3.rst
create mode 100644 Documentation/translations/zh_CN/usb/ehci.rst
create mode 100644 Documentation/translations/zh_CN/usb/index.rst
create mode 100644 Documentation/translations/zh_CN/usb/usbmon.rst
--
2.54.0
^ permalink raw reply
* Re: [PATCH v2] Fail the build on RUST=y and RUST_IS_AVAILABLE=n
From: Neal Gompa @ 2026-05-21 10:14 UTC (permalink / raw)
To: Sasha Finkelstein
Cc: Alice Ryhl, Andreas Hindborg, Benno Lossin, Björn Roy Baron,
Boqun Feng, Danilo Krummrich, Gary Guo, Jonathan Corbet,
Miguel Ojeda, Shuah Khan, Trevor Gross, linux-doc, linux-kernel,
rust-for-linux
In-Reply-To: <20260521-evolve-to-crab-v2-1-c18e0e98fc54@chaosmail.tech>
On Thu, May 21, 2026 at 4:32 AM Sasha Finkelstein <k@chaosmail.tech> wrote:
>
> The current approach of silently disabling all rust drivers if the
> toolchain is missing results in users that try to compile their own
> kernels getting a "successful" build and then being confused about where
> did their drivers go. In comparison, missing openssl results in a build
> failure, not a disappearance of everything that depends on it.
>
> This also means that allyesconfig will depend on rust, but since the
> rust experiment concluded with "rust is here to stay", i believe that
> allyesconfig should be building rust drivers too.
>
> Signed-off-by: Sasha Finkelstein <k@chaosmail.tech>
> ---
> Changes in v2:
> - No longer a RFC, let's make it happen.
> - Update the docs.
> - Link to v1: https://patch.msgid.link/20260510-evolve-to-crab-v1-1-208df84e67be@chaosmail.tech
> ---
> Documentation/rust/quick-start.rst | 6 +++---
> init/Kconfig | 1 -
> 2 files changed, 3 insertions(+), 4 deletions(-)
>
At this point, yes, we should just go ahead and do this.
Reviewed-by: Neal Gompa <neal@gompa.dev>
--
真実はいつも一つ!/ Always, there's only one truth!
^ permalink raw reply
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