[PATCH v4 24/35] vdso/datastore: Allocate data pages dynamically

Thomas Weißschuh posted 35 patches 2 months ago
There is a newer version of this series
[PATCH v4 24/35] vdso/datastore: Allocate data pages dynamically
Posted by Thomas Weißschuh 2 months ago
Allocating the datapages as part of the kernel image does not work on
SPARC. It is also problematic with regards to dcache aliasing as there is
no guarantee that the virtual addresses used by the kernel are compatible
with those used by userspace.

Allocate the data pages through the page allocator instead.
Unused pages in the vDSO VMA are still allocated to keep the virtual
addresses aligned.

These pages are used by both the timekeeping, random pool and architecture
initialization code. Introduce a new early initialization step, to make
sure they are available when needed.

Signed-off-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
Tested-by: Andreas Larsson <andreas@gaisler.com>
Reviewed-by: Andreas Larsson <andreas@gaisler.com>
---
 include/linux/vdso_datastore.h |  6 ++++++
 init/main.c                    |  2 ++
 lib/vdso/datastore.c           | 44 ++++++++++++++++++++++--------------------
 3 files changed, 31 insertions(+), 21 deletions(-)

diff --git a/include/linux/vdso_datastore.h b/include/linux/vdso_datastore.h
index a91fa24b06e09321fdff8c2c7bdfbc1b206db574..0b530428db711e58660e797d9d3cf5dce60217fe 100644
--- a/include/linux/vdso_datastore.h
+++ b/include/linux/vdso_datastore.h
@@ -2,9 +2,15 @@
 #ifndef _LINUX_VDSO_DATASTORE_H
 #define _LINUX_VDSO_DATASTORE_H
 
+#ifdef CONFIG_HAVE_GENERIC_VDSO
 #include <linux/mm_types.h>
 
 extern const struct vm_special_mapping vdso_vvar_mapping;
 struct vm_area_struct *vdso_install_vvar_mapping(struct mm_struct *mm, unsigned long addr);
 
+void __init vdso_setup_data_pages(void);
+#else /* !CONFIG_HAVE_GENERIC_VDSO */
+static inline void vdso_setup_data_pages(void) { }
+#endif /* CONFIG_HAVE_GENERIC_VDSO */
+
 #endif /* _LINUX_VDSO_DATASTORE_H */
diff --git a/init/main.c b/init/main.c
index 07a3116811c5d72cbab48410493b3d0f89d1f1b2..01fa389eb33d58e13388bfaf6a821fe8523f2c76 100644
--- a/init/main.c
+++ b/init/main.c
@@ -104,6 +104,7 @@
 #include <linux/pidfs.h>
 #include <linux/ptdump.h>
 #include <linux/time_namespace.h>
+#include <linux/vdso_datastore.h>
 #include <net/net_namespace.h>
 
 #include <asm/io.h>
@@ -1020,6 +1021,7 @@ void start_kernel(void)
 	srcu_init();
 	hrtimers_init();
 	softirq_init();
+	vdso_setup_data_pages();
 	timekeeping_init();
 	time_init();
 
diff --git a/lib/vdso/datastore.c b/lib/vdso/datastore.c
index 6e5feb4a95b85f5a1cbdced7cdeddc593fcbad40..67799e8919c202e0800cb78ff70919f9604ac492 100644
--- a/lib/vdso/datastore.c
+++ b/lib/vdso/datastore.c
@@ -1,41 +1,43 @@
 // SPDX-License-Identifier: GPL-2.0-only
 
-#include <linux/linkage.h>
 #include <linux/mm.h>
 #include <linux/time_namespace.h>
 #include <linux/types.h>
 #include <linux/vdso_datastore.h>
 #include <vdso/datapage.h>
 
-/*
- * The vDSO data page.
- */
 #ifdef CONFIG_GENERIC_GETTIMEOFDAY
-static union {
-	struct vdso_time_data	data;
-	u8			page[PAGE_SIZE];
-} vdso_time_data_store __page_aligned_data;
-struct vdso_time_data *vdso_k_time_data = &vdso_time_data_store.data;
-static_assert(sizeof(vdso_time_data_store) == PAGE_SIZE);
+struct vdso_time_data *vdso_k_time_data;
+static_assert(sizeof(struct vdso_time_data) <= PAGE_SIZE);
 #endif /* CONFIG_GENERIC_GETTIMEOFDAY */
 
 #ifdef CONFIG_VDSO_GETRANDOM
-static union {
-	struct vdso_rng_data	data;
-	u8			page[PAGE_SIZE];
-} vdso_rng_data_store __page_aligned_data;
-struct vdso_rng_data *vdso_k_rng_data = &vdso_rng_data_store.data;
-static_assert(sizeof(vdso_rng_data_store) == PAGE_SIZE);
+struct vdso_rng_data *vdso_k_rng_data;
+static_assert(sizeof(struct vdso_rng_data) <= PAGE_SIZE);
 #endif /* CONFIG_VDSO_GETRANDOM */
 
 #ifdef CONFIG_ARCH_HAS_VDSO_ARCH_DATA
-static union {
-	struct vdso_arch_data	data;
-	u8			page[VDSO_ARCH_DATA_SIZE];
-} vdso_arch_data_store __page_aligned_data;
-struct vdso_arch_data *vdso_k_arch_data = &vdso_arch_data_store.data;
+struct vdso_arch_data *vdso_k_arch_data;
 #endif /* CONFIG_ARCH_HAS_VDSO_ARCH_DATA */
 
+void __init vdso_setup_data_pages(void)
+{
+	unsigned int order = get_order(VDSO_NR_PAGES * PAGE_SIZE);
+	struct folio *folio = folio_alloc(GFP_KERNEL, order);
+
+	if (!folio)
+		panic("Unable to allocate VDSO storage pages");
+
+	if (IS_ENABLED(CONFIG_GENERIC_GETTIMEOFDAY))
+		vdso_k_time_data = page_address(folio_page(folio, VDSO_TIME_PAGE_OFFSET));
+
+	if (IS_ENABLED(CONFIG_VDSO_GETRANDOM))
+		vdso_k_rng_data = page_address(folio_page(folio, VDSO_RNG_PAGE_OFFSET));
+
+	if (IS_ENABLED(CONFIG_ARCH_HAS_VDSO_ARCH_DATA))
+		vdso_k_arch_data = page_address(folio_page(folio, VDSO_ARCH_PAGES_START));
+}
+
 static vm_fault_t vvar_fault(const struct vm_special_mapping *sm,
 			     struct vm_area_struct *vma, struct vm_fault *vmf)
 {

-- 
2.51.0

Re: [PATCH v4 24/35] vdso/datastore: Allocate data pages dynamically
Posted by Heiko Carstens 1 month, 2 weeks ago
On Tue, Oct 14, 2025 at 08:49:10AM +0200, Thomas Weißschuh wrote:
> Allocating the datapages as part of the kernel image does not work on
> SPARC. It is also problematic with regards to dcache aliasing as there is
> no guarantee that the virtual addresses used by the kernel are compatible
> with those used by userspace.
> 
> Allocate the data pages through the page allocator instead.
> Unused pages in the vDSO VMA are still allocated to keep the virtual
> addresses aligned.
> 
> These pages are used by both the timekeeping, random pool and architecture
> initialization code. Introduce a new early initialization step, to make
> sure they are available when needed.
> 
> Signed-off-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
> Tested-by: Andreas Larsson <andreas@gaisler.com>
> Reviewed-by: Andreas Larsson <andreas@gaisler.com>
> ---
>  include/linux/vdso_datastore.h |  6 ++++++
>  init/main.c                    |  2 ++
>  lib/vdso/datastore.c           | 44 ++++++++++++++++++++++--------------------
>  3 files changed, 31 insertions(+), 21 deletions(-)

...

> +void __init vdso_setup_data_pages(void)
> +{
> +	unsigned int order = get_order(VDSO_NR_PAGES * PAGE_SIZE);
> +	struct folio *folio = folio_alloc(GFP_KERNEL, order);

I'm seeing random hangs on s390 too with our CI, but unfortunately I cannot
reproduce it manually. But looking at one of the dumps it looks to me like the
vdso time page contains (more or less) random junk at the end. Or in other
words, shouldn't this be:

	struct folio *folio = folio_alloc(GFP_KERNEL | __GFP_ZERO, order);

? At least that is a difference to before as far as I can tell.
Re: [PATCH v4 24/35] vdso/datastore: Allocate data pages dynamically
Posted by Aithal, Srikanth 1 month, 1 week ago
On 11/5/2025 9:04 PM, Heiko Carstens wrote:
> On Tue, Oct 14, 2025 at 08:49:10AM +0200, Thomas Weißschuh wrote:
>> Allocating the datapages as part of the kernel image does not work on
>> SPARC. It is also problematic with regards to dcache aliasing as there is
>> no guarantee that the virtual addresses used by the kernel are compatible
>> with those used by userspace.
>>
>> Allocate the data pages through the page allocator instead.
>> Unused pages in the vDSO VMA are still allocated to keep the virtual
>> addresses aligned.
>>
>> These pages are used by both the timekeeping, random pool and architecture
>> initialization code. Introduce a new early initialization step, to make
>> sure they are available when needed.
>>
>> Signed-off-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
>> Tested-by: Andreas Larsson <andreas@gaisler.com>
>> Reviewed-by: Andreas Larsson <andreas@gaisler.com>
>> ---
>>   include/linux/vdso_datastore.h |  6 ++++++
>>   init/main.c                    |  2 ++
>>   lib/vdso/datastore.c           | 44 ++++++++++++++++++++++--------------------
>>   3 files changed, 31 insertions(+), 21 deletions(-)
> 
> ...
> 
>> +void __init vdso_setup_data_pages(void)
>> +{
>> +	unsigned int order = get_order(VDSO_NR_PAGES * PAGE_SIZE);
>> +	struct folio *folio = folio_alloc(GFP_KERNEL, order);
> 
> I'm seeing random hangs on s390 too with our CI, but unfortunately I cannot
> reproduce it manually. But looking at one of the dumps it looks to me like the
> vdso time page contains (more or less) random junk at the end. Or in other
> words, shouldn't this be:
> 
> 	struct folio *folio = folio_alloc(GFP_KERNEL | __GFP_ZERO, order);
> 
> ? At least that is a difference to before as far as I can tell.

I was also hitting random hangs with an x86 KVM guest boot on an AMD64 
platform. The bisection landed on this commit as the culprit.
I see that v5 has been posted. I am in the process of testing that 
version and will reply to the v5 thread with the results.

Thank you
Srikanth Aithal <sraithal@amd.com>
Re: [PATCH v4 24/35] vdso/datastore: Allocate data pages dynamically
Posted by Thomas Weißschuh 1 month, 2 weeks ago
On Wed, Nov 05, 2025 at 04:34:26PM +0100, Heiko Carstens wrote:
> On Tue, Oct 14, 2025 at 08:49:10AM +0200, Thomas Weißschuh wrote:
> > Allocating the datapages as part of the kernel image does not work on
> > SPARC. It is also problematic with regards to dcache aliasing as there is
> > no guarantee that the virtual addresses used by the kernel are compatible
> > with those used by userspace.
> > 
> > Allocate the data pages through the page allocator instead.
> > Unused pages in the vDSO VMA are still allocated to keep the virtual
> > addresses aligned.
> > 
> > These pages are used by both the timekeeping, random pool and architecture
> > initialization code. Introduce a new early initialization step, to make
> > sure they are available when needed.
> > 
> > Signed-off-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
> > Tested-by: Andreas Larsson <andreas@gaisler.com>
> > Reviewed-by: Andreas Larsson <andreas@gaisler.com>
> > ---
> >  include/linux/vdso_datastore.h |  6 ++++++
> >  init/main.c                    |  2 ++
> >  lib/vdso/datastore.c           | 44 ++++++++++++++++++++++--------------------
> >  3 files changed, 31 insertions(+), 21 deletions(-)
> 
> ...
> 
> > +void __init vdso_setup_data_pages(void)
> > +{
> > +	unsigned int order = get_order(VDSO_NR_PAGES * PAGE_SIZE);
> > +	struct folio *folio = folio_alloc(GFP_KERNEL, order);
> 
> I'm seeing random hangs on s390 too with our CI, but unfortunately I cannot
> reproduce it manually. But looking at one of the dumps it looks to me like the
> vdso time page contains (more or less) random junk at the end. Or in other
> words, shouldn't this be:
> 
> 	struct folio *folio = folio_alloc(GFP_KERNEL | __GFP_ZERO, order);
> 
> ? At least that is a difference to before as far as I can tell.

Thanks! This perfectly explains all the weird issues. It also does fix
the issue on the affected hardware which Mark kindly let me use.
I'll test it some more and send a fixed series tomorrow.


Thanks again!
Thomas
Re: [tip: timers/vdso] vdso/datastore: Allocate data pages dynamically
Posted by Lad Prabhakar 1 month, 2 weeks ago
This commit breaks boot on Renesas arm64 RZ/V2H and RZ/V2N platforms.

The boot process doesn't complete anymore with no obvious error logs to
indicate the cause of the failure.

Reverting the following two commits fixes the boot issue:
  10d91dac2ea5 ("vdso/datastore: Allocate data pages dynamically")
  6a011a228293 ("vdso/datastore: Map pages through struct page")

Cheers,
Prabhakar