[PATCH v2 1/4] x86/process: Shorten the default LAM tag width

From: Maciej Wieczor-Retman

Date: Tue Feb 24 2026 - 08:22:21 EST


From: Maciej Wieczor-Retman <maciej.wieczor-retman@xxxxxxxxx>

With the announcement of ChkTag, it's worth preparing a stable x86
linear address masking (lam) user interface. One important aspect of lam
is the tag width, and aligning it with other industry solutions can
provide a more popular, generalized interface that other technologies
could utilize.

ChkTag will use 4-bit tags and since that's the direction other memory
tagging implementations seem to be taking too (for example Arm's MTE)
it's reasonable to converge lam in linux to the same specification. Even
though x86's LAM supports 6-bit tags it is beneficial to default lam to
4 bits as ChkTag will likely be the main user of the interface and such
connection should simplify things in the future.

Set the default tag width to 4 bits and make it variable. While static
keys were considered to implement it, the LAM tag width isn't used in
any performance intensive code paths, and doesn't justify anything more
than a simple global variable.

Signed-off-by: Maciej Wieczor-Retman <maciej.wieczor-retman@xxxxxxxxx>
---
arch/x86/include/asm/mmu_context.h | 3 +++
arch/x86/kernel/process_64.c | 15 +++++++++------
2 files changed, 12 insertions(+), 6 deletions(-)

diff --git a/arch/x86/include/asm/mmu_context.h b/arch/x86/include/asm/mmu_context.h
index 1acafb1c6a93..f8b2e8b855a1 100644
--- a/arch/x86/include/asm/mmu_context.h
+++ b/arch/x86/include/asm/mmu_context.h
@@ -86,6 +86,9 @@ static inline void switch_ldt(struct mm_struct *prev, struct mm_struct *next)
#endif

#ifdef CONFIG_ADDRESS_MASKING
+
+extern unsigned long lam_available_bits;
+
static inline unsigned long mm_lam_cr3_mask(struct mm_struct *mm)
{
/*
diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
index 08e72f429870..4a217f13ab90 100644
--- a/arch/x86/kernel/process_64.c
+++ b/arch/x86/kernel/process_64.c
@@ -797,7 +797,10 @@ static long prctl_map_vdso(const struct vdso_image *image, unsigned long addr)

#ifdef CONFIG_ADDRESS_MASKING

-#define LAM_U57_BITS 6
+#define LAM_MAX_BITS 6
+#define LAM_DEFAULT_BITS 4
+
+unsigned long lam_available_bits = LAM_DEFAULT_BITS;

static void enable_lam_func(void *__mm)
{
@@ -811,10 +814,10 @@ static void enable_lam_func(void *__mm)
}
}

-static void mm_enable_lam(struct mm_struct *mm)
+static void mm_enable_lam(struct mm_struct *mm, unsigned long nr_bits)
{
mm->context.lam_cr3_mask = X86_CR3_LAM_U57;
- mm->context.untag_mask = ~GENMASK(62, 57);
+ mm->context.untag_mask = ~GENMASK(57 + lam_available_bits - 1, 57);

/*
* Even though the process must still be single-threaded at this
@@ -850,12 +853,12 @@ static int prctl_enable_tagged_addr(struct mm_struct *mm, unsigned long nr_bits)
return -EBUSY;
}

- if (!nr_bits || nr_bits > LAM_U57_BITS) {
+ if (!nr_bits || nr_bits > lam_available_bits) {
mmap_write_unlock(mm);
return -EINVAL;
}

- mm_enable_lam(mm);
+ mm_enable_lam(mm, nr_bits);

mmap_write_unlock(mm);

@@ -965,7 +968,7 @@ long do_arch_prctl_64(struct task_struct *task, int option, unsigned long arg2)
if (!cpu_feature_enabled(X86_FEATURE_LAM))
return put_user(0, (unsigned long __user *)arg2);
else
- return put_user(LAM_U57_BITS, (unsigned long __user *)arg2);
+ return put_user(lam_available_bits, (unsigned long __user *)arg2);
#endif
case ARCH_SHSTK_ENABLE:
case ARCH_SHSTK_DISABLE:
--
2.53.0