Linux Documentation
 help / color / mirror / Atom feed
* [PATCH v3 2/3] riscv: hwprobe: export the availability of vector to user
       [not found] <20260725001614.2578617-1-tchiu@tenstorrent.com>
@ 2026-07-25  0:15 ` Andy Chiu
  2026-08-05 17:38   ` Jesse Taube
  2026-08-13 23:38   ` Mark Harris
  0 siblings, 2 replies; 3+ messages in thread
From: Andy Chiu @ 2026-07-25  0:15 UTC (permalink / raw)
  To: Jonathan Corbet, Shuah Khan, Paul Walmsley, Palmer Dabbelt,
	Albert Ou, Alexandre Ghiti, Samuel Holland, linux-doc,
	linux-riscv
  Cc: bergner, kito.cheng, Andy Chiu, dfustini, greentime.hu,
	Andrew Jones, Guodong Xu, Aleksa Paunovic, Pincheng Wang, Xu Lu,
	Yao Zihong, Jingwei Wang, Deepak Gupta, Clément Léger

Userland IFUNC resolvers use hwprobe to decide whether to dispatch to
vectorized routines. But RISCV_HWPROBE_KEY_IMA_EXT_0 only reports what
is present in hardware, not what the calling process may actually use:
when Vector is disabled for a process via
prctl(PR_RISCV_V_SET_CONTROL, PR_RISCV_V_VSTATE_CTRL_OFF), it is still
reported as present. A resolver that trusts this and runs a vector
instruction crashes with SIGILL.

Add RISCV_HWPROBE_KEY_EXT_ENABLED, a positional modifier key that carries
no value of its own. Within a single request, keys placed after it report
extensions that are both present and enabled for the calling process,
while keys before it keep reporting hardware presence. This masks out V
and its V-dependent sub-extensions when V is disabled for the process, and
lets userland obtain both views in one query:

      [ {IMA_EXT_0}, {EXT_ENABLED}, {IMA_EXT_0} ]
          present       modifier       enabled

The enabled view depends on per-process state, so it cannot be served from
the vDSO's process-independent cache; requests carrying the modifier are
deferred to the syscall. Unknown keys are still reported as -1, so the
feature is detectable and existing users are unaffected.

Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220795
Link: https://lore.kernel.org/all/20260107000609.63892-1-andybnac@gmail.com/
Signed-off-by: Andy Chiu <tchiu@tenstorrent.com>
---
Changelog v2:
 - rebase on top of latest for-next
 - add test_avail to sifive's vector depending extensions
---
 Documentation/arch/riscv/hwprobe.rst          | 27 ++++++++++++++
 arch/riscv/include/asm/hwprobe.h              |  2 +-
 .../asm/vendor_extensions/sifive_hwprobe.h    |  6 ++--
 .../asm/vendor_extensions/thead_hwprobe.h     |  6 ++--
 arch/riscv/include/uapi/asm/hwprobe.h         |  2 ++
 arch/riscv/kernel/sys_hwprobe.c               | 36 +++++++++++++------
 arch/riscv/kernel/vdso/hwprobe.c              | 25 +++++++++----
 .../kernel/vendor_extensions/sifive_hwprobe.c | 16 ++++++---
 .../kernel/vendor_extensions/thead_hwprobe.c  |  9 +++--
 9 files changed, 100 insertions(+), 29 deletions(-)

diff --git a/Documentation/arch/riscv/hwprobe.rst b/Documentation/arch/riscv/hwprobe.rst
index d9928641deb9..b9b3cf23c212 100644
--- a/Documentation/arch/riscv/hwprobe.rst
+++ b/Documentation/arch/riscv/hwprobe.rst
@@ -401,3 +401,30 @@ The following keys are defined:
     as defined in version 1.0 of the RISC-V Control-flow Integrity (CFI)
     extensions specification, ratified in commit 302a2d45c243
     ("Update build-pdf.yml") of riscv-cfi.
+
+ * :c:macro:`RISCV_HWPROBE_KEY_EXT_ENABLED`: A modifier key. It reports no
+  value of its own (its value is always 0) and instead changes how the
+  extension-bitmask keys that follow it in the same request are reported.
+  Keys placed before it report the extensions that are present in hardware,
+  as usual. Keys placed after it additionally require each reported extension
+  to be enabled for the calling process, i.e. usable without receiving a
+  SIGILL.
+
+  Currently this applies to Vector. When V has been disabled for the process
+  with ``prctl(PR_RISCV_V_SET_CONTROL, PR_RISCV_V_VSTATE_CTRL_OFF)``,
+  :c:macro:`RISCV_HWPROBE_IMA_V` and the V-dependent sub-extensions are
+  cleared from any :c:macro:`RISCV_HWPROBE_KEY_IMA_EXT_0` that follows the
+  modifier, and :c:macro:`RISCV_HWPROBE_VENDOR_EXT_XTHEADVECTOR` is cleared
+  from any :c:macro:`RISCV_HWPROBE_KEY_VENDOR_EXT_THEAD_0` that follows it,
+  while a preceding key still reports them as present. A single request can
+  therefore return both the hardware-present set and the process-usable
+  set::
+
+      struct riscv_hwprobe pairs[3] = {
+          { .key = RISCV_HWPROBE_KEY_IMA_EXT_0, },   /* present in hardware */
+          { .key = RISCV_HWPROBE_KEY_EXT_ENABLED, }, /* modifier            */
+          { .key = RISCV_HWPROBE_KEY_IMA_EXT_0, },   /* present and enabled */
+      };
+
+  The effect is positional: reordering the pairs changes which keys have the
+  enablement filter applied.
diff --git a/arch/riscv/include/asm/hwprobe.h b/arch/riscv/include/asm/hwprobe.h
index 8b9f5e1cf4cb..e8364a3eaeef 100644
--- a/arch/riscv/include/asm/hwprobe.h
+++ b/arch/riscv/include/asm/hwprobe.h
@@ -8,7 +8,7 @@
 
 #include <uapi/asm/hwprobe.h>
 
-#define RISCV_HWPROBE_MAX_KEY		16
+#define RISCV_HWPROBE_MAX_KEY		17
 
 static inline bool riscv_hwprobe_key_is_valid(__s64 key)
 {
diff --git a/arch/riscv/include/asm/vendor_extensions/sifive_hwprobe.h b/arch/riscv/include/asm/vendor_extensions/sifive_hwprobe.h
index 90a61abd033c..c961c78a7927 100644
--- a/arch/riscv/include/asm/vendor_extensions/sifive_hwprobe.h
+++ b/arch/riscv/include/asm/vendor_extensions/sifive_hwprobe.h
@@ -7,10 +7,12 @@
 #include <uapi/asm/hwprobe.h>
 
 #ifdef CONFIG_RISCV_ISA_VENDOR_EXT_SIFIVE
-void hwprobe_isa_vendor_ext_sifive_0(struct riscv_hwprobe *pair, const struct cpumask *cpus);
+void hwprobe_isa_vendor_ext_sifive_0(struct riscv_hwprobe *pair,
+				     const struct cpumask *cpus, bool test_avail);
 #else
 static inline void hwprobe_isa_vendor_ext_sifive_0(struct riscv_hwprobe *pair,
-						   const struct cpumask *cpus)
+						   const struct cpumask *cpus,
+						   bool test_avail)
 {
 	pair->value = 0;
 }
diff --git a/arch/riscv/include/asm/vendor_extensions/thead_hwprobe.h b/arch/riscv/include/asm/vendor_extensions/thead_hwprobe.h
index 65a9c5612466..3e1c4271383f 100644
--- a/arch/riscv/include/asm/vendor_extensions/thead_hwprobe.h
+++ b/arch/riscv/include/asm/vendor_extensions/thead_hwprobe.h
@@ -7,10 +7,12 @@
 #include <uapi/asm/hwprobe.h>
 
 #ifdef CONFIG_RISCV_ISA_VENDOR_EXT_THEAD
-void hwprobe_isa_vendor_ext_thead_0(struct riscv_hwprobe *pair, const struct cpumask *cpus);
+void hwprobe_isa_vendor_ext_thead_0(struct riscv_hwprobe *pair,
+				    const struct cpumask *cpus, bool test_avail);
 #else
 static inline void hwprobe_isa_vendor_ext_thead_0(struct riscv_hwprobe *pair,
-						  const struct cpumask *cpus)
+						  const struct cpumask *cpus,
+						  bool test_avail)
 {
 	pair->value = 0;
 }
diff --git a/arch/riscv/include/uapi/asm/hwprobe.h b/arch/riscv/include/uapi/asm/hwprobe.h
index 9139edba0aec..561483172ce5 100644
--- a/arch/riscv/include/uapi/asm/hwprobe.h
+++ b/arch/riscv/include/uapi/asm/hwprobe.h
@@ -116,6 +116,8 @@ struct riscv_hwprobe {
 #define RISCV_HWPROBE_KEY_ZICBOP_BLOCK_SIZE	15
 #define RISCV_HWPROBE_KEY_IMA_EXT_1		16
 #define		RISCV_HWPROBE_EXT_ZICFISS	(1ULL << 0)
+/* Modifier key working as a signal to the kernel to gather enablement status */
+#define RISCV_HWPROBE_KEY_EXT_ENABLED	17
 
 /* Increase RISCV_HWPROBE_MAX_KEY when adding items. */
 
diff --git a/arch/riscv/kernel/sys_hwprobe.c b/arch/riscv/kernel/sys_hwprobe.c
index d2bb70fed8b9..5949e3d9146f 100644
--- a/arch/riscv/kernel/sys_hwprobe.c
+++ b/arch/riscv/kernel/sys_hwprobe.c
@@ -79,8 +79,9 @@ static void hwprobe_arch_id(struct riscv_hwprobe *pair,
 }
 
 static void hwprobe_isa_ext0(struct riscv_hwprobe *pair,
-			     const struct cpumask *cpus)
+			     const struct cpumask *cpus, bool report_avail)
 {
+	bool report_v;
 	int cpu;
 	u64 missing = 0;
 
@@ -91,7 +92,8 @@ static void hwprobe_isa_ext0(struct riscv_hwprobe *pair,
 	if (riscv_isa_extension_available(NULL, c))
 		pair->value |= RISCV_HWPROBE_IMA_C;
 
-	if (has_vector() && riscv_isa_extension_available(NULL, v))
+	report_v = report_avail ? riscv_v_vstate_ctrl_user_allowed() : true;
+	if (has_vector() && riscv_isa_extension_available(NULL, v) && report_v)
 		pair->value |= RISCV_HWPROBE_IMA_V;
 
 	/*
@@ -146,7 +148,7 @@ static void hwprobe_isa_ext0(struct riscv_hwprobe *pair,
 		 * All the following extensions must depend on the kernel
 		 * support of V.
 		 */
-		if (has_vector()) {
+		if (has_vector() && report_v) {
 			EXT_KEY(isainfo->isa, ZVBB, pair->value, missing);
 			EXT_KEY(isainfo->isa, ZVBC, pair->value, missing);
 			EXT_KEY(isainfo->isa, ZVE32F, pair->value, missing);
@@ -215,7 +217,7 @@ static bool hwprobe_ext0_has(const struct cpumask *cpus, u64 ext)
 {
 	struct riscv_hwprobe pair;
 
-	hwprobe_isa_ext0(&pair, cpus);
+	hwprobe_isa_ext0(&pair, cpus, false);
 	return (pair.value & ext);
 }
 
@@ -293,7 +295,7 @@ static u64 hwprobe_vec_misaligned(const struct cpumask *cpus)
 #endif
 
 static void hwprobe_one_pair(struct riscv_hwprobe *pair,
-			     const struct cpumask *cpus)
+			     const struct cpumask *cpus, bool test_avail)
 {
 	pair->value = 0;
 
@@ -314,7 +316,7 @@ static void hwprobe_one_pair(struct riscv_hwprobe *pair,
 		break;
 
 	case RISCV_HWPROBE_KEY_IMA_EXT_0:
-		hwprobe_isa_ext0(pair, cpus);
+		hwprobe_isa_ext0(pair, cpus, test_avail);
 		break;
 
 	case RISCV_HWPROBE_KEY_IMA_EXT_1:
@@ -351,11 +353,11 @@ static void hwprobe_one_pair(struct riscv_hwprobe *pair,
 		break;
 
 	case RISCV_HWPROBE_KEY_VENDOR_EXT_SIFIVE_0:
-		hwprobe_isa_vendor_ext_sifive_0(pair, cpus);
+		hwprobe_isa_vendor_ext_sifive_0(pair, cpus, test_avail);
 		break;
 
 	case RISCV_HWPROBE_KEY_VENDOR_EXT_THEAD_0:
-		hwprobe_isa_vendor_ext_thead_0(pair, cpus);
+		hwprobe_isa_vendor_ext_thead_0(pair, cpus, test_avail);
 		break;
 	case RISCV_HWPROBE_KEY_VENDOR_EXT_MIPS_0:
 		hwprobe_isa_vendor_ext_mips_0(pair, cpus);
@@ -378,6 +380,7 @@ static int hwprobe_get_values(struct riscv_hwprobe __user *pairs,
 			      unsigned long __user *cpus_user,
 			      unsigned int flags)
 {
+	bool test_avail = false;
 	size_t out;
 	int ret;
 	cpumask_t cpus;
@@ -418,7 +421,10 @@ static int hwprobe_get_values(struct riscv_hwprobe __user *pairs,
 			return -EFAULT;
 
 		pair.value = 0;
-		hwprobe_one_pair(&pair, &cpus);
+		if (pair.key == RISCV_HWPROBE_KEY_EXT_ENABLED)
+			test_avail = true;
+		else
+			hwprobe_one_pair(&pair, &cpus, test_avail);
 		ret = put_user(pair.key, &pairs->key);
 		if (ret == 0)
 			ret = put_user(pair.value, &pairs->value);
@@ -437,6 +443,7 @@ static int hwprobe_get_cpus(struct riscv_hwprobe __user *pairs,
 {
 	cpumask_t cpus, one_cpu;
 	bool clear_all = false;
+	bool test_avail = false;
 	size_t i;
 	int ret;
 
@@ -476,6 +483,10 @@ static int hwprobe_get_cpus(struct riscv_hwprobe __user *pairs,
 			if (ret)
 				return -EFAULT;
 		}
+		if (pair.key == RISCV_HWPROBE_KEY_EXT_ENABLED) {
+			test_avail = true;
+			continue;
+		}
 
 		if (clear_all)
 			continue;
@@ -485,7 +496,7 @@ static int hwprobe_get_cpus(struct riscv_hwprobe __user *pairs,
 		for_each_cpu(cpu, &cpus) {
 			cpumask_set_cpu(cpu, &one_cpu);
 
-			hwprobe_one_pair(&tmp, &one_cpu);
+			hwprobe_one_pair(&tmp, &one_cpu, test_avail);
 
 			if (!riscv_hwprobe_pair_cmp(&tmp, &pair))
 				cpumask_clear_cpu(cpu, &cpus);
@@ -535,8 +546,11 @@ static int complete_hwprobe_vdso_data(void)
 	 * save a syscall in the common case.
 	 */
 	for (key = 0; key <= RISCV_HWPROBE_MAX_KEY; key++) {
+		if (key == RISCV_HWPROBE_KEY_EXT_ENABLED)
+			continue;
+
 		pair.key = key;
-		hwprobe_one_pair(&pair, cpu_online_mask);
+		hwprobe_one_pair(&pair, cpu_online_mask, false);
 
 		WARN_ON_ONCE(pair.key < 0);
 
diff --git a/arch/riscv/kernel/vdso/hwprobe.c b/arch/riscv/kernel/vdso/hwprobe.c
index 8f45500d0a6e..f40c21e44ef9 100644
--- a/arch/riscv/kernel/vdso/hwprobe.c
+++ b/arch/riscv/kernel/vdso/hwprobe.c
@@ -14,7 +14,7 @@ extern int riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count,
 
 static int riscv_vdso_get_values(struct riscv_hwprobe *pairs, size_t pair_count,
 				 size_t cpusetsize, unsigned long *cpus,
-				 unsigned int flags)
+				 unsigned int flags, bool avail_test)
 {
 	const struct vdso_arch_data *avd = &vdso_u_arch_data;
 	bool all_cpus = !cpusetsize && !cpus;
@@ -27,7 +27,8 @@ static int riscv_vdso_get_values(struct riscv_hwprobe *pairs, size_t pair_count,
 	 * homogeneous, then this function can handle requests for arbitrary
 	 * masks.
 	 */
-	if (flags != 0 || (!all_cpus && !avd->homogeneous_cpus) || unlikely(!avd->ready))
+	if (flags != 0 || (!all_cpus && !avd->homogeneous_cpus) ||
+	    unlikely(!avd->ready) || avail_test)
 		return riscv_hwprobe(pairs, pair_count, cpusetsize, cpus, flags);
 
 	/* This is something we can handle, fill out the pairs. */
@@ -48,7 +49,7 @@ static int riscv_vdso_get_values(struct riscv_hwprobe *pairs, size_t pair_count,
 
 static int riscv_vdso_get_cpus(struct riscv_hwprobe *pairs, size_t pair_count,
 			       size_t cpusetsize, unsigned long *cpus,
-			       unsigned int flags)
+			       unsigned int flags, bool avail_test)
 {
 	const struct vdso_arch_data *avd = &vdso_u_arch_data;
 	struct riscv_hwprobe *p = pairs;
@@ -68,7 +69,8 @@ static int riscv_vdso_get_cpus(struct riscv_hwprobe *pairs, size_t pair_count,
 		}
 	}
 
-	if (empty_cpus || flags != RISCV_HWPROBE_WHICH_CPUS || !avd->homogeneous_cpus)
+	if (empty_cpus || flags != RISCV_HWPROBE_WHICH_CPUS ||
+	    !avd->homogeneous_cpus || avail_test)
 		return riscv_hwprobe(pairs, pair_count, cpusetsize, cpus, flags);
 
 	while (p < end) {
@@ -105,10 +107,21 @@ int __vdso_riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count,
 			 size_t cpusetsize, unsigned long *cpus,
 			 unsigned int flags)
 {
+	struct riscv_hwprobe *p = pairs;
+	bool avail_test = false;
+	size_t i;
+
+	for (i = 0; i < pair_count; i++) {
+		if (p[i].key == RISCV_HWPROBE_KEY_EXT_ENABLED) {
+			avail_test = true;
+			break;
+		}
+	}
+
 	if (flags & RISCV_HWPROBE_WHICH_CPUS)
 		return riscv_vdso_get_cpus(pairs, pair_count, cpusetsize,
-					   cpus, flags);
+					   cpus, flags, avail_test);
 
 	return riscv_vdso_get_values(pairs, pair_count, cpusetsize,
-				     cpus, flags);
+				     cpus, flags, avail_test);
 }
diff --git a/arch/riscv/kernel/vendor_extensions/sifive_hwprobe.c b/arch/riscv/kernel/vendor_extensions/sifive_hwprobe.c
index 1f77f6309763..379ea16bd89f 100644
--- a/arch/riscv/kernel/vendor_extensions/sifive_hwprobe.c
+++ b/arch/riscv/kernel/vendor_extensions/sifive_hwprobe.c
@@ -6,17 +6,23 @@
 
 #include <linux/cpumask.h>
 #include <linux/types.h>
+#include <asm/vector.h>
 
 #include <uapi/asm/hwprobe.h>
 #include <uapi/asm/vendor/sifive.h>
 
-void hwprobe_isa_vendor_ext_sifive_0(struct riscv_hwprobe *pair, const struct cpumask *cpus)
+void hwprobe_isa_vendor_ext_sifive_0(struct riscv_hwprobe *pair, const struct cpumask *cpus,
+				     bool test_avail)
 {
+	bool report_v = test_avail ? riscv_v_vstate_ctrl_user_allowed() : true;
+
 	VENDOR_EXTENSION_SUPPORTED(pair, cpus,
 				   riscv_isa_vendor_ext_list_sifive.per_hart_isa_bitmap, {
-		VENDOR_EXT_KEY(XSFVQMACCDOD);
-		VENDOR_EXT_KEY(XSFVQMACCQOQ);
-		VENDOR_EXT_KEY(XSFVFNRCLIPXFQF);
-		VENDOR_EXT_KEY(XSFVFWMACCQQQ);
+		if (report_v) {
+			VENDOR_EXT_KEY(XSFVQMACCDOD);
+			VENDOR_EXT_KEY(XSFVQMACCQOQ);
+			VENDOR_EXT_KEY(XSFVFNRCLIPXFQF);
+			VENDOR_EXT_KEY(XSFVFWMACCQQQ);
+		}
 	});
 }
diff --git a/arch/riscv/kernel/vendor_extensions/thead_hwprobe.c b/arch/riscv/kernel/vendor_extensions/thead_hwprobe.c
index 2eba34011786..28c58a7123d5 100644
--- a/arch/riscv/kernel/vendor_extensions/thead_hwprobe.c
+++ b/arch/riscv/kernel/vendor_extensions/thead_hwprobe.c
@@ -6,14 +6,19 @@
 
 #include <linux/cpumask.h>
 #include <linux/types.h>
+#include <asm/vector.h>
 
 #include <uapi/asm/hwprobe.h>
 #include <uapi/asm/vendor/thead.h>
 
-void hwprobe_isa_vendor_ext_thead_0(struct riscv_hwprobe *pair, const struct cpumask *cpus)
+void hwprobe_isa_vendor_ext_thead_0(struct riscv_hwprobe *pair, const struct cpumask *cpus,
+				    bool test_avail)
 {
+	bool report_v = test_avail ? riscv_v_vstate_ctrl_user_allowed() : true;
+
 	VENDOR_EXTENSION_SUPPORTED(pair, cpus,
 				   riscv_isa_vendor_ext_list_thead.per_hart_isa_bitmap, {
-		VENDOR_EXT_KEY(XTHEADVECTOR);
+		if (report_v)
+			VENDOR_EXT_KEY(XTHEADVECTOR);
 	});
 }
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH v3 2/3] riscv: hwprobe: export the availability of vector to user
  2026-07-25  0:15 ` [PATCH v3 2/3] riscv: hwprobe: export the availability of vector to user Andy Chiu
@ 2026-08-05 17:38   ` Jesse Taube
  2026-08-13 23:38   ` Mark Harris
  1 sibling, 0 replies; 3+ messages in thread
From: Jesse Taube @ 2026-08-05 17:38 UTC (permalink / raw)
  To: Andy Chiu
  Cc: Jonathan Corbet, Shuah Khan, Paul Walmsley, Palmer Dabbelt,
	Albert Ou, Alexandre Ghiti, Samuel Holland, linux-doc,
	linux-riscv, bergner, kito.cheng, dfustini, greentime.hu,
	Andrew Jones, Guodong Xu, Aleksa Paunovic, Pincheng Wang, Xu Lu,
	Yao Zihong, Jingwei Wang, Deepak Gupta, Clément Léger

On Fri, Jul 24, 2026 at 8:17 PM Andy Chiu <tchiu@tenstorrent.com> wrote:
>
> Userland IFUNC resolvers use hwprobe to decide whether to dispatch to
> vectorized routines. But RISCV_HWPROBE_KEY_IMA_EXT_0 only reports what
> is present in hardware, not what the calling process may actually use:
> when Vector is disabled for a process via
> prctl(PR_RISCV_V_SET_CONTROL, PR_RISCV_V_VSTATE_CTRL_OFF), it is still
> reported as present. A resolver that trusts this and runs a vector
> instruction crashes with SIGILL.
>
> Add RISCV_HWPROBE_KEY_EXT_ENABLED, a positional modifier key that carries
> no value of its own. Within a single request, keys placed after it report
> extensions that are both present and enabled for the calling process,
> while keys before it keep reporting hardware presence. This masks out V
> and its V-dependent sub-extensions when V is disabled for the process, and
> lets userland obtain both views in one query:
>
>       [ {IMA_EXT_0}, {EXT_ENABLED}, {IMA_EXT_0} ]
>           present       modifier       enabled
>
> The enabled view depends on per-process state, so it cannot be served from
> the vDSO's process-independent cache; requests carrying the modifier are
> deferred to the syscall. Unknown keys are still reported as -1, so the
> feature is detectable and existing users are unaffected.
>
> Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220795
> Link: https://lore.kernel.org/all/20260107000609.63892-1-andybnac@gmail.com/
> Signed-off-by: Andy Chiu <tchiu@tenstorrent.com>
> ---
> Changelog v2:
>  - rebase on top of latest for-next
>  - add test_avail to sifive's vector depending extensions
> ---
>  Documentation/arch/riscv/hwprobe.rst          | 27 ++++++++++++++
>  arch/riscv/include/asm/hwprobe.h              |  2 +-
>  .../asm/vendor_extensions/sifive_hwprobe.h    |  6 ++--
>  .../asm/vendor_extensions/thead_hwprobe.h     |  6 ++--
>  arch/riscv/include/uapi/asm/hwprobe.h         |  2 ++
>  arch/riscv/kernel/sys_hwprobe.c               | 36 +++++++++++++------
>  arch/riscv/kernel/vdso/hwprobe.c              | 25 +++++++++----
>  .../kernel/vendor_extensions/sifive_hwprobe.c | 16 ++++++---
>  .../kernel/vendor_extensions/thead_hwprobe.c  |  9 +++--
>  9 files changed, 100 insertions(+), 29 deletions(-)
>
> diff --git a/Documentation/arch/riscv/hwprobe.rst b/Documentation/arch/riscv/hwprobe.rst
> index d9928641deb9..b9b3cf23c212 100644
> --- a/Documentation/arch/riscv/hwprobe.rst
> +++ b/Documentation/arch/riscv/hwprobe.rst
> @@ -401,3 +401,30 @@ The following keys are defined:
>      as defined in version 1.0 of the RISC-V Control-flow Integrity (CFI)
>      extensions specification, ratified in commit 302a2d45c243
>      ("Update build-pdf.yml") of riscv-cfi.
> +
> + * :c:macro:`RISCV_HWPROBE_KEY_EXT_ENABLED`: A modifier key. It reports no
> +  value of its own (its value is always 0) and instead changes how the
> +  extension-bitmask keys that follow it in the same request are reported.
> +  Keys placed before it report the extensions that are present in hardware,
> +  as usual. Keys placed after it additionally require each reported extension
> +  to be enabled for the calling process, i.e. usable without receiving a
> +  SIGILL.
> +
> +  Currently this applies to Vector. When V has been disabled for the process
> +  with ``prctl(PR_RISCV_V_SET_CONTROL, PR_RISCV_V_VSTATE_CTRL_OFF)``,
> +  :c:macro:`RISCV_HWPROBE_IMA_V` and the V-dependent sub-extensions are
> +  cleared from any :c:macro:`RISCV_HWPROBE_KEY_IMA_EXT_0` that follows the
> +  modifier, and :c:macro:`RISCV_HWPROBE_VENDOR_EXT_XTHEADVECTOR` is cleared
> +  from any :c:macro:`RISCV_HWPROBE_KEY_VENDOR_EXT_THEAD_0` that follows it,
> +  while a preceding key still reports them as present. A single request can
> +  therefore return both the hardware-present set and the process-usable
> +  set::
> +
> +      struct riscv_hwprobe pairs[3] = {
> +          { .key = RISCV_HWPROBE_KEY_IMA_EXT_0, },   /* present in hardware */
> +          { .key = RISCV_HWPROBE_KEY_EXT_ENABLED, }, /* modifier            */
> +          { .key = RISCV_HWPROBE_KEY_IMA_EXT_0, },   /* present and enabled */
> +      };
> +
> +  The effect is positional: reordering the pairs changes which keys have the
> +  enablement filter applied.
> diff --git a/arch/riscv/include/asm/hwprobe.h b/arch/riscv/include/asm/hwprobe.h
> index 8b9f5e1cf4cb..e8364a3eaeef 100644
> --- a/arch/riscv/include/asm/hwprobe.h
> +++ b/arch/riscv/include/asm/hwprobe.h
> @@ -8,7 +8,7 @@
>
>  #include <uapi/asm/hwprobe.h>
>
> -#define RISCV_HWPROBE_MAX_KEY          16
> +#define RISCV_HWPROBE_MAX_KEY          17
>
>  static inline bool riscv_hwprobe_key_is_valid(__s64 key)
>  {
> diff --git a/arch/riscv/include/asm/vendor_extensions/sifive_hwprobe.h b/arch/riscv/include/asm/vendor_extensions/sifive_hwprobe.h
> index 90a61abd033c..c961c78a7927 100644
> --- a/arch/riscv/include/asm/vendor_extensions/sifive_hwprobe.h
> +++ b/arch/riscv/include/asm/vendor_extensions/sifive_hwprobe.h
> @@ -7,10 +7,12 @@
>  #include <uapi/asm/hwprobe.h>
>
>  #ifdef CONFIG_RISCV_ISA_VENDOR_EXT_SIFIVE
> -void hwprobe_isa_vendor_ext_sifive_0(struct riscv_hwprobe *pair, const struct cpumask *cpus);
> +void hwprobe_isa_vendor_ext_sifive_0(struct riscv_hwprobe *pair,
> +                                    const struct cpumask *cpus, bool test_avail);
>  #else
>  static inline void hwprobe_isa_vendor_ext_sifive_0(struct riscv_hwprobe *pair,
> -                                                  const struct cpumask *cpus)
> +                                                  const struct cpumask *cpus,
> +                                                  bool test_avail)
>  {
>         pair->value = 0;
>  }
> diff --git a/arch/riscv/include/asm/vendor_extensions/thead_hwprobe.h b/arch/riscv/include/asm/vendor_extensions/thead_hwprobe.h
> index 65a9c5612466..3e1c4271383f 100644
> --- a/arch/riscv/include/asm/vendor_extensions/thead_hwprobe.h
> +++ b/arch/riscv/include/asm/vendor_extensions/thead_hwprobe.h
> @@ -7,10 +7,12 @@
>  #include <uapi/asm/hwprobe.h>
>
>  #ifdef CONFIG_RISCV_ISA_VENDOR_EXT_THEAD
> -void hwprobe_isa_vendor_ext_thead_0(struct riscv_hwprobe *pair, const struct cpumask *cpus);
> +void hwprobe_isa_vendor_ext_thead_0(struct riscv_hwprobe *pair,
> +                                   const struct cpumask *cpus, bool test_avail);
>  #else
>  static inline void hwprobe_isa_vendor_ext_thead_0(struct riscv_hwprobe *pair,
> -                                                 const struct cpumask *cpus)
> +                                                 const struct cpumask *cpus,
> +                                                 bool test_avail)
>  {
>         pair->value = 0;
>  }
> diff --git a/arch/riscv/include/uapi/asm/hwprobe.h b/arch/riscv/include/uapi/asm/hwprobe.h
> index 9139edba0aec..561483172ce5 100644
> --- a/arch/riscv/include/uapi/asm/hwprobe.h
> +++ b/arch/riscv/include/uapi/asm/hwprobe.h
> @@ -116,6 +116,8 @@ struct riscv_hwprobe {
>  #define RISCV_HWPROBE_KEY_ZICBOP_BLOCK_SIZE    15
>  #define RISCV_HWPROBE_KEY_IMA_EXT_1            16
>  #define                RISCV_HWPROBE_EXT_ZICFISS       (1ULL << 0)
> +/* Modifier key working as a signal to the kernel to gather enablement status */
> +#define RISCV_HWPROBE_KEY_EXT_ENABLED  17
>
>  /* Increase RISCV_HWPROBE_MAX_KEY when adding items. */
>
> diff --git a/arch/riscv/kernel/sys_hwprobe.c b/arch/riscv/kernel/sys_hwprobe.c
> index d2bb70fed8b9..5949e3d9146f 100644
> --- a/arch/riscv/kernel/sys_hwprobe.c
> +++ b/arch/riscv/kernel/sys_hwprobe.c
> @@ -79,8 +79,9 @@ static void hwprobe_arch_id(struct riscv_hwprobe *pair,
>  }
>
>  static void hwprobe_isa_ext0(struct riscv_hwprobe *pair,
> -                            const struct cpumask *cpus)
> +                            const struct cpumask *cpus, bool report_avail)
>  {
> +       bool report_v;
>         int cpu;
>         u64 missing = 0;
>
> @@ -91,7 +92,8 @@ static void hwprobe_isa_ext0(struct riscv_hwprobe *pair,
>         if (riscv_isa_extension_available(NULL, c))
>                 pair->value |= RISCV_HWPROBE_IMA_C;
>
> -       if (has_vector() && riscv_isa_extension_available(NULL, v))
> +       report_v = report_avail ? riscv_v_vstate_ctrl_user_allowed() : true;
> +       if (has_vector() && riscv_isa_extension_available(NULL, v) && report_v)
>                 pair->value |= RISCV_HWPROBE_IMA_V;
>
>         /*
> @@ -146,7 +148,7 @@ static void hwprobe_isa_ext0(struct riscv_hwprobe *pair,
>                  * All the following extensions must depend on the kernel
>                  * support of V.
>                  */
> -               if (has_vector()) {
> +               if (has_vector() && report_v) {
>                         EXT_KEY(isainfo->isa, ZVBB, pair->value, missing);
>                         EXT_KEY(isainfo->isa, ZVBC, pair->value, missing);
>                         EXT_KEY(isainfo->isa, ZVE32F, pair->value, missing);
> @@ -215,7 +217,7 @@ static bool hwprobe_ext0_has(const struct cpumask *cpus, u64 ext)
>  {
>         struct riscv_hwprobe pair;
>
> -       hwprobe_isa_ext0(&pair, cpus);
> +       hwprobe_isa_ext0(&pair, cpus, false);
>         return (pair.value & ext);
>  }
>
> @@ -293,7 +295,7 @@ static u64 hwprobe_vec_misaligned(const struct cpumask *cpus)
>  #endif
>
>  static void hwprobe_one_pair(struct riscv_hwprobe *pair,
> -                            const struct cpumask *cpus)
> +                            const struct cpumask *cpus, bool test_avail)
>  {
>         pair->value = 0;
>
> @@ -314,7 +316,7 @@ static void hwprobe_one_pair(struct riscv_hwprobe *pair,
>                 break;
>
>         case RISCV_HWPROBE_KEY_IMA_EXT_0:
> -               hwprobe_isa_ext0(pair, cpus);
> +               hwprobe_isa_ext0(pair, cpus, test_avail);
>                 break;
>
>         case RISCV_HWPROBE_KEY_IMA_EXT_1:
> @@ -351,11 +353,11 @@ static void hwprobe_one_pair(struct riscv_hwprobe *pair,
>                 break;
>
>         case RISCV_HWPROBE_KEY_VENDOR_EXT_SIFIVE_0:
> -               hwprobe_isa_vendor_ext_sifive_0(pair, cpus);
> +               hwprobe_isa_vendor_ext_sifive_0(pair, cpus, test_avail);
>                 break;
>
>         case RISCV_HWPROBE_KEY_VENDOR_EXT_THEAD_0:
> -               hwprobe_isa_vendor_ext_thead_0(pair, cpus);
> +               hwprobe_isa_vendor_ext_thead_0(pair, cpus, test_avail);
>                 break;
>         case RISCV_HWPROBE_KEY_VENDOR_EXT_MIPS_0:
>                 hwprobe_isa_vendor_ext_mips_0(pair, cpus);

It might be good to add a warning if `RISCV_HWPROBE_KEY_EXT_ENABLED`
is ever passed to `hwprobe_one_pair` in the future as it should never
be passed here.

> @@ -378,6 +380,7 @@ static int hwprobe_get_values(struct riscv_hwprobe __user *pairs,
>                               unsigned long __user *cpus_user,
>                               unsigned int flags)
>  {
> +       bool test_avail = false;
>         size_t out;
>         int ret;
>         cpumask_t cpus;
> @@ -418,7 +421,10 @@ static int hwprobe_get_values(struct riscv_hwprobe __user *pairs,
>                         return -EFAULT;
>
>                 pair.value = 0;
> -               hwprobe_one_pair(&pair, &cpus);
> +               if (pair.key == RISCV_HWPROBE_KEY_EXT_ENABLED)
> +                       test_avail = true;
> +               else
> +                       hwprobe_one_pair(&pair, &cpus, test_avail);
>                 ret = put_user(pair.key, &pairs->key);
>                 if (ret == 0)
>                         ret = put_user(pair.value, &pairs->value);
> @@ -437,6 +443,7 @@ static int hwprobe_get_cpus(struct riscv_hwprobe __user *pairs,
>  {
>         cpumask_t cpus, one_cpu;
>         bool clear_all = false;
> +       bool test_avail = false;
>         size_t i;
>         int ret;
>
> @@ -476,6 +483,10 @@ static int hwprobe_get_cpus(struct riscv_hwprobe __user *pairs,
>                         if (ret)
>                                 return -EFAULT;
>                 }
> +               if (pair.key == RISCV_HWPROBE_KEY_EXT_ENABLED) {
> +                       test_avail = true;
> +                       continue;
> +               }
>
>                 if (clear_all)
>                         continue;
> @@ -485,7 +496,7 @@ static int hwprobe_get_cpus(struct riscv_hwprobe __user *pairs,
>                 for_each_cpu(cpu, &cpus) {
>                         cpumask_set_cpu(cpu, &one_cpu);
>
> -                       hwprobe_one_pair(&tmp, &one_cpu);
> +                       hwprobe_one_pair(&tmp, &one_cpu, test_avail);
>
>                         if (!riscv_hwprobe_pair_cmp(&tmp, &pair))
>                                 cpumask_clear_cpu(cpu, &cpus);
> @@ -535,8 +546,11 @@ static int complete_hwprobe_vdso_data(void)
>          * save a syscall in the common case.

By the way this got removed in for-next
("Revert "riscv: hwprobe: Fix stale vDSO data for late-initialized
keys at boot"")

>          */
>         for (key = 0; key <= RISCV_HWPROBE_MAX_KEY; key++) {
> +               if (key == RISCV_HWPROBE_KEY_EXT_ENABLED)
> +                       continue;
> +
>                 pair.key = key;
> -               hwprobe_one_pair(&pair, cpu_online_mask);
> +               hwprobe_one_pair(&pair, cpu_online_mask, false);
>
>                 WARN_ON_ONCE(pair.key < 0);
>
> diff --git a/arch/riscv/kernel/vdso/hwprobe.c b/arch/riscv/kernel/vdso/hwprobe.c
> index 8f45500d0a6e..f40c21e44ef9 100644
> --- a/arch/riscv/kernel/vdso/hwprobe.c
> +++ b/arch/riscv/kernel/vdso/hwprobe.c
> @@ -14,7 +14,7 @@ extern int riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count,
>
>  static int riscv_vdso_get_values(struct riscv_hwprobe *pairs, size_t pair_count,
>                                  size_t cpusetsize, unsigned long *cpus,
> -                                unsigned int flags)
> +                                unsigned int flags, bool avail_test)
>  {
>         const struct vdso_arch_data *avd = &vdso_u_arch_data;
>         bool all_cpus = !cpusetsize && !cpus;
> @@ -27,7 +27,8 @@ static int riscv_vdso_get_values(struct riscv_hwprobe *pairs, size_t pair_count,
>          * homogeneous, then this function can handle requests for arbitrary
>          * masks.
>          */
> -       if (flags != 0 || (!all_cpus && !avd->homogeneous_cpus) || unlikely(!avd->ready))
> +       if (flags != 0 || (!all_cpus && !avd->homogeneous_cpus) ||
> +           unlikely(!avd->ready) || avail_test)
>                 return riscv_hwprobe(pairs, pair_count, cpusetsize, cpus, flags);
>
>         /* This is something we can handle, fill out the pairs. */
> @@ -48,7 +49,7 @@ static int riscv_vdso_get_values(struct riscv_hwprobe *pairs, size_t pair_count,
>
>  static int riscv_vdso_get_cpus(struct riscv_hwprobe *pairs, size_t pair_count,
>                                size_t cpusetsize, unsigned long *cpus,
> -                              unsigned int flags)
> +                              unsigned int flags, bool avail_test)
>  {
>         const struct vdso_arch_data *avd = &vdso_u_arch_data;
>         struct riscv_hwprobe *p = pairs;
> @@ -68,7 +69,8 @@ static int riscv_vdso_get_cpus(struct riscv_hwprobe *pairs, size_t pair_count,
>                 }
>         }
>
> -       if (empty_cpus || flags != RISCV_HWPROBE_WHICH_CPUS || !avd->homogeneous_cpus)
> +       if (empty_cpus || flags != RISCV_HWPROBE_WHICH_CPUS ||
> +           !avd->homogeneous_cpus || avail_test)
>                 return riscv_hwprobe(pairs, pair_count, cpusetsize, cpus, flags);
>
>         while (p < end) {
> @@ -105,10 +107,21 @@ int __vdso_riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count,
>                          size_t cpusetsize, unsigned long *cpus,
>                          unsigned int flags)
>  {
> +       struct riscv_hwprobe *p = pairs;
> +       bool avail_test = false;
> +       size_t i;
> +
> +       for (i = 0; i < pair_count; i++) {
> +               if (p[i].key == RISCV_HWPROBE_KEY_EXT_ENABLED) {
> +                       avail_test = true;

NIT: `avail_test` is very similar to `test_avail`, which confused me at first.
I would change `avail_test` to `has_avail_test` or `does_avail_test`
so its more readable.


> +                       break;
> +               }
> +       }
> +
>         if (flags & RISCV_HWPROBE_WHICH_CPUS)
>                 return riscv_vdso_get_cpus(pairs, pair_count, cpusetsize,
> -                                          cpus, flags);
> +                                          cpus, flags, avail_test);
>
>         return riscv_vdso_get_values(pairs, pair_count, cpusetsize,
> -                                    cpus, flags);
> +                                    cpus, flags, avail_test);
>  }
> diff --git a/arch/riscv/kernel/vendor_extensions/sifive_hwprobe.c b/arch/riscv/kernel/vendor_extensions/sifive_hwprobe.c
> index 1f77f6309763..379ea16bd89f 100644
> --- a/arch/riscv/kernel/vendor_extensions/sifive_hwprobe.c
> +++ b/arch/riscv/kernel/vendor_extensions/sifive_hwprobe.c
> @@ -6,17 +6,23 @@
>
>  #include <linux/cpumask.h>
>  #include <linux/types.h>
> +#include <asm/vector.h>
>
>  #include <uapi/asm/hwprobe.h>
>  #include <uapi/asm/vendor/sifive.h>
>
> -void hwprobe_isa_vendor_ext_sifive_0(struct riscv_hwprobe *pair, const struct cpumask *cpus)
> +void hwprobe_isa_vendor_ext_sifive_0(struct riscv_hwprobe *pair, const struct cpumask *cpus,
> +                                    bool test_avail)
>  {
> +       bool report_v = test_avail ? riscv_v_vstate_ctrl_user_allowed() : true;
> +
>         VENDOR_EXTENSION_SUPPORTED(pair, cpus,
>                                    riscv_isa_vendor_ext_list_sifive.per_hart_isa_bitmap, {
> -               VENDOR_EXT_KEY(XSFVQMACCDOD);
> -               VENDOR_EXT_KEY(XSFVQMACCQOQ);
> -               VENDOR_EXT_KEY(XSFVFNRCLIPXFQF);
> -               VENDOR_EXT_KEY(XSFVFWMACCQQQ);
> +               if (report_v) {
> +                       VENDOR_EXT_KEY(XSFVQMACCDOD);
> +                       VENDOR_EXT_KEY(XSFVQMACCQOQ);
> +                       VENDOR_EXT_KEY(XSFVFNRCLIPXFQF);
> +                       VENDOR_EXT_KEY(XSFVFWMACCQQQ);
> +               }
>         });
>  }
> diff --git a/arch/riscv/kernel/vendor_extensions/thead_hwprobe.c b/arch/riscv/kernel/vendor_extensions/thead_hwprobe.c
> index 2eba34011786..28c58a7123d5 100644
> --- a/arch/riscv/kernel/vendor_extensions/thead_hwprobe.c
> +++ b/arch/riscv/kernel/vendor_extensions/thead_hwprobe.c
> @@ -6,14 +6,19 @@
>
>  #include <linux/cpumask.h>
>  #include <linux/types.h>
> +#include <asm/vector.h>
>
>  #include <uapi/asm/hwprobe.h>
>  #include <uapi/asm/vendor/thead.h>
>
> -void hwprobe_isa_vendor_ext_thead_0(struct riscv_hwprobe *pair, const struct cpumask *cpus)
> +void hwprobe_isa_vendor_ext_thead_0(struct riscv_hwprobe *pair, const struct cpumask *cpus,
> +                                   bool test_avail)
>  {
> +       bool report_v = test_avail ? riscv_v_vstate_ctrl_user_allowed() : true;
> +
>         VENDOR_EXTENSION_SUPPORTED(pair, cpus,
>                                    riscv_isa_vendor_ext_list_thead.per_hart_isa_bitmap, {
> -               VENDOR_EXT_KEY(XTHEADVECTOR);
> +               if (report_v)
> +                       VENDOR_EXT_KEY(XTHEADVECTOR);
>         });
>  }
> --
> 2.43.0
>
>
> _______________________________________________
> linux-riscv mailing list
> linux-riscv@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-riscv
>


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH v3 2/3] riscv: hwprobe: export the availability of vector to user
  2026-07-25  0:15 ` [PATCH v3 2/3] riscv: hwprobe: export the availability of vector to user Andy Chiu
  2026-08-05 17:38   ` Jesse Taube
@ 2026-08-13 23:38   ` Mark Harris
  1 sibling, 0 replies; 3+ messages in thread
From: Mark Harris @ 2026-08-13 23:38 UTC (permalink / raw)
  To: tchiu
  Cc: Mark Harris, Jonathan Corbet, Shuah Khan, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Samuel Holland,
	bergner, kito.cheng, dfustini, greentime.hu, Andrew Jones,
	Guodong Xu, Aleksa Paunovic, Pincheng Wang, Xu Lu, Yao Zihong,
	Jingwei Wang, Deepak Gupta, Clément Léger, linux-doc,
	linux-riscv

Andy Chiu wrote:
> Userland IFUNC resolvers use hwprobe to decide whether to dispatch to
> vectorized routines. But RISCV_HWPROBE_KEY_IMA_EXT_0 only reports what
> is present in hardware, not what the calling process may actually use:
> when Vector is disabled for a process via
> prctl(PR_RISCV_V_SET_CONTROL, PR_RISCV_V_VSTATE_CTRL_OFF), it is still
> reported as present. A resolver that trusts this and runs a vector
> instruction crashes with SIGILL.
> 
> Add RISCV_HWPROBE_KEY_EXT_ENABLED, a positional modifier key that carries
> no value of its own. Within a single request, keys placed after it report
> extensions that are both present and enabled for the calling process,
> while keys before it keep reporting hardware presence. This masks out V
> and its V-dependent sub-extensions when V is disabled for the process, and
> lets userland obtain both views in one query:
> 
>       [ {IMA_EXT_0}, {EXT_ENABLED}, {IMA_EXT_0} ]
>           present       modifier       enabled
> 
> The enabled view depends on per-process state, so it cannot be served from
> the vDSO's process-independent cache; requests carrying the modifier are
> deferred to the syscall. Unknown keys are still reported as -1, so the
> feature is detectable and existing users are unaffected.

Currently, each key has a value and the order of the keys does not
matter.  If the interface needed to be extended to support
process-specific values, or thread-specific values (the prctl() is
thread-specific), I would expect either new keys to retrieve those
process-specific or thread-specific values, or a new flag bit to
indicate that existing keys should be reinterpreted in a new manner.
Using a key as a modifier with no value and changing the keys to
be order-dependent seems like an unnecessarily confusing ugly hack
that we would have to live with for decades to come, just to slightly
simplify code needed in the short term to handle older kernels.

Additionally, the approach of falling back to the syscall whenever
the modifier key is used could lead to each check for the vector
extension, in an ifunc resolver or almost anywhere else, having to
perform a syscall.  Callers wanting to use other extensions may
even use this modifier when checking for them, because given the
choice of knowing whether the extension is potentially available
or is available, the latter sounds like what they should be using
to be future-proof, even if there is not currently a way to disable
the extension.  That could lead to a syscall for every extension
check, defeating the vDSO cache.

On some non-RISC-V platforms glibc allows its own use of specific
CPU capabilities to be disabled through tunables (e.g.,
GLIBC_TUNABLES=glibc.cpu.hwcaps=-AVX2 ./my_x86_64_program), and as
of glibc 2.44 tunables can also be set in /etc/tunables.conf and
applied system-wide, even to specific programs.  This is more
convenient than a prctl and it would be nice if RISC-V extensions
could also be disabled for specific programs through tunables, for
any use where its availability is checked beforehand (in an ifunc
resolver or elsewhere), without the need to go to the kernel for
each check.

Because ifunc resolvers may not have access to external symbols
beyond __riscv_hwprobe(), it is really attractive to be able to
obtain extension availability information using the same function.
But that doesn't mean that the kernel has to be involved.  The
function is in libc, so it could call the vDSO function and then
optionally modify the resulting values according to its own
process-specific information about enabled or disabled extensions.
This extra information would ideally come from the initial hwcaps
(which reflect the vector prctl) and any tunables, so it is
process-wide, can support arbitrary extensions, and can be checked
without any kernel syscalls and without callers having to know or
care whether a particular extension can be disabled.  If the prctl
was used to re-enable vector in some thread that may not affect it,
but that seems like the desired behavior, at least for ifunc
resolvers, since they are assumed to produce the same result in any
thread and at any point during the process lifetime.

If implemented using a new flag, the libc __riscv_hwprobe() function
could just call the existing vDSO function but with the new flag
masked out, and then if the flag is set, modify the resulting values.
That would probably be the simplest and easiest to understand API
and would not require kernel syscalls.

 - Mark

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-08-13 23:41 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <20260725001614.2578617-1-tchiu@tenstorrent.com>
2026-07-25  0:15 ` [PATCH v3 2/3] riscv: hwprobe: export the availability of vector to user Andy Chiu
2026-08-05 17:38   ` Jesse Taube
2026-08-13 23:38   ` Mark Harris

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