2021-09-30 11:27:35

by Christophe Leroy

[permalink] [raw]
Subject: [PATCH v3 1/4] mm: Create a new system state and fix core_kernel_text()

core_kernel_text() considers that until system_state in at least
SYSTEM_RUNNING, init memory is valid.

But init memory is freed a few lines before setting SYSTEM_RUNNING,
so we have a small period of time when core_kernel_text() is wrong.

Create an intermediate system state called SYSTEM_FREEING_INIT that
is set before starting freeing init memory, and use it in
core_kernel_text() to report init memory invalid earlier.

Cc: Gerald Schaefer <[email protected]>
Cc: Kefeng Wang <[email protected]>
Signed-off-by: Christophe Leroy <[email protected]>
---
v3: No change
v2: New
---
include/linux/kernel.h | 1 +
init/main.c | 2 ++
kernel/extable.c | 2 +-
3 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index 2776423a587e..471bc0593679 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -248,6 +248,7 @@ extern bool early_boot_irqs_disabled;
extern enum system_states {
SYSTEM_BOOTING,
SYSTEM_SCHEDULING,
+ SYSTEM_FREEING_INITMEM,
SYSTEM_RUNNING,
SYSTEM_HALT,
SYSTEM_POWER_OFF,
diff --git a/init/main.c b/init/main.c
index 3f7216934441..c457d393fdd4 100644
--- a/init/main.c
+++ b/init/main.c
@@ -1505,6 +1505,8 @@ static int __ref kernel_init(void *unused)
kernel_init_freeable();
/* need to finish all async __init code before freeing the memory */
async_synchronize_full();
+
+ system_state = SYSTEM_FREEING_INITMEM;
kprobe_free_init_mem();
ftrace_free_init_mem();
kgdb_free_init_mem();
diff --git a/kernel/extable.c b/kernel/extable.c
index b0ea5eb0c3b4..290661f68e6b 100644
--- a/kernel/extable.c
+++ b/kernel/extable.c
@@ -76,7 +76,7 @@ int notrace core_kernel_text(unsigned long addr)
addr < (unsigned long)_etext)
return 1;

- if (system_state < SYSTEM_RUNNING &&
+ if (system_state < SYSTEM_FREEING_INITMEM &&
init_kernel_text(addr))
return 1;
return 0;
--
2.31.1


2021-09-30 11:28:54

by Christophe Leroy

[permalink] [raw]
Subject: [PATCH v3 2/4] mm: Make generic arch_is_kernel_initmem_freed() do what it says

Commit 7a5da02de8d6 ("locking/lockdep: check for freed initmem in
static_obj()") added arch_is_kernel_initmem_freed() which is supposed
to report whether an object is part of already freed init memory.

For the time being, the generic version of arch_is_kernel_initmem_freed()
always reports 'false', allthough free_initmem() is generically called
on all architectures.

Therefore, change the generic version of arch_is_kernel_initmem_freed()
to check whether free_initmem() has been called. If so, then check
if a given address falls into init memory.

To ease the use of system_state, move it out of line into its only
caller which is lockdep.c

Cc: Gerald Schaefer <[email protected]>
Cc: Kefeng Wang <[email protected]>
Signed-off-by: Christophe Leroy <[email protected]>
---
v3: Move it out of sections.h into lockdep.c and fix the comment.

v2: Change to using the new SYSTEM_FREEING_INITMEM state
Signed-off-by: Christophe Leroy <[email protected]>
---
include/asm-generic/sections.h | 14 --------------
kernel/locking/lockdep.c | 15 +++++++++++++++
2 files changed, 15 insertions(+), 14 deletions(-)

diff --git a/include/asm-generic/sections.h b/include/asm-generic/sections.h
index d16302d3eb59..596ab2092289 100644
--- a/include/asm-generic/sections.h
+++ b/include/asm-generic/sections.h
@@ -80,20 +80,6 @@ static inline int arch_is_kernel_data(unsigned long addr)
}
#endif

-/*
- * Check if an address is part of freed initmem. This is needed on architectures
- * with virt == phys kernel mapping, for code that wants to check if an address
- * is part of a static object within [_stext, _end]. After initmem is freed,
- * memory can be allocated from it, and such allocations would then have
- * addresses within the range [_stext, _end].
- */
-#ifndef arch_is_kernel_initmem_freed
-static inline int arch_is_kernel_initmem_freed(unsigned long addr)
-{
- return 0;
-}
-#endif
-
/**
* memory_contains - checks if an object is contained within a memory region
* @begin: virtual address of the beginning of the memory region
diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index bf1c00c881e4..8e118caf835e 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -788,6 +788,21 @@ static int very_verbose(struct lock_class *class)
* Is this the address of a static object:
*/
#ifdef __KERNEL__
+/*
+ * Check if an address is part of freed initmem. After initmem is freed,
+ * memory can be allocated from it, and such allocations would then have
+ * addresses within the range [_stext, _end].
+ */
+#ifndef arch_is_kernel_initmem_freed
+static int arch_is_kernel_initmem_freed(unsigned long addr)
+{
+ if (system_state < SYSTEM_FREEING_INITMEM)
+ return 0;
+
+ return init_section_contains((void *)addr, 1);
+}
+#endif
+
static int static_obj(const void *obj)
{
unsigned long start = (unsigned long) &_stext,
--
2.31.1

2021-09-30 11:30:42

by Christophe Leroy

[permalink] [raw]
Subject: [PATCH v3 4/4] s390: Use generic version of arch_is_kernel_initmem_freed()

Generic version of arch_is_kernel_initmem_freed() now does the same
as s390 version.

Remove the s390 version.

Cc: Gerald Schaefer <[email protected]>
Cc: Kefeng Wang <[email protected]>
Acked-by: Heiko Carstens <[email protected]>
Signed-off-by: Christophe Leroy <[email protected]>
---
v3: No change
v2: No change
---
arch/s390/include/asm/sections.h | 12 ------------
arch/s390/mm/init.c | 3 ---
2 files changed, 15 deletions(-)

diff --git a/arch/s390/include/asm/sections.h b/arch/s390/include/asm/sections.h
index 85881dd48022..3fecaa4e8b74 100644
--- a/arch/s390/include/asm/sections.h
+++ b/arch/s390/include/asm/sections.h
@@ -2,20 +2,8 @@
#ifndef _S390_SECTIONS_H
#define _S390_SECTIONS_H

-#define arch_is_kernel_initmem_freed arch_is_kernel_initmem_freed
-
#include <asm-generic/sections.h>

-extern bool initmem_freed;
-
-static inline int arch_is_kernel_initmem_freed(unsigned long addr)
-{
- if (!initmem_freed)
- return 0;
- return addr >= (unsigned long)__init_begin &&
- addr < (unsigned long)__init_end;
-}
-
/*
* .boot.data section contains variables "shared" between the decompressor and
* the decompressed kernel. The decompressor will store values in them, and
diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c
index a04faf49001a..8c6f258a6183 100644
--- a/arch/s390/mm/init.c
+++ b/arch/s390/mm/init.c
@@ -58,8 +58,6 @@ unsigned long empty_zero_page, zero_page_mask;
EXPORT_SYMBOL(empty_zero_page);
EXPORT_SYMBOL(zero_page_mask);

-bool initmem_freed;
-
static void __init setup_zero_pages(void)
{
unsigned int order;
@@ -214,7 +212,6 @@ void __init mem_init(void)

void free_initmem(void)
{
- initmem_freed = true;
__set_memory((unsigned long)_sinittext,
(unsigned long)(_einittext - _sinittext) >> PAGE_SHIFT,
SET_MEMORY_RW | SET_MEMORY_NX);
--
2.31.1

2021-10-01 07:16:40

by Daniel Axtens

[permalink] [raw]
Subject: Re: [PATCH v3 2/4] mm: Make generic arch_is_kernel_initmem_freed() do what it says

> #ifdef __KERNEL__
> +/*
> + * Check if an address is part of freed initmem. After initmem is freed,
> + * memory can be allocated from it, and such allocations would then have
> + * addresses within the range [_stext, _end].
> + */
> +#ifndef arch_is_kernel_initmem_freed
> +static int arch_is_kernel_initmem_freed(unsigned long addr)
> +{
> + if (system_state < SYSTEM_FREEING_INITMEM)
> + return 0;
> +
> + return init_section_contains((void *)addr, 1);

Is init_section_contains sufficient here?

include/asm-generic/sections.h says:
* [__init_begin, __init_end]: contains .init.* sections, but .init.text.*
* may be out of this range on some architectures.
* [_sinittext, _einittext]: contains .init.text.* sections

init_section_contains only checks __init_*:
static inline bool init_section_contains(void *virt, size_t size)
{
return memory_contains(__init_begin, __init_end, virt, size);
}

Do we need to check against _sinittext and _einittext?

Your proposed generic code will work for powerpc and s390 because those
archs only test against __init_* anyway. I don't know if any platform
actually does place .init.text outside of __init_begin=>__init_end, but
the comment seems to suggest that they could.

Kind regards,
Daniel

2021-11-04 21:57:09

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH v3 2/4] mm: Make generic arch_is_kernel_initmem_freed() do what it says

On Fri, 01 Oct 2021 17:14:41 +1000 Daniel Axtens <[email protected]> wrote:

> > #ifdef __KERNEL__
> > +/*
> > + * Check if an address is part of freed initmem. After initmem is freed,
> > + * memory can be allocated from it, and such allocations would then have
> > + * addresses within the range [_stext, _end].
> > + */
> > +#ifndef arch_is_kernel_initmem_freed
> > +static int arch_is_kernel_initmem_freed(unsigned long addr)
> > +{
> > + if (system_state < SYSTEM_FREEING_INITMEM)
> > + return 0;
> > +
> > + return init_section_contains((void *)addr, 1);
>
> Is init_section_contains sufficient here?
>
> include/asm-generic/sections.h says:
> * [__init_begin, __init_end]: contains .init.* sections, but .init.text.*
> * may be out of this range on some architectures.
> * [_sinittext, _einittext]: contains .init.text.* sections
>
> init_section_contains only checks __init_*:
> static inline bool init_section_contains(void *virt, size_t size)
> {
> return memory_contains(__init_begin, __init_end, virt, size);
> }
>
> Do we need to check against _sinittext and _einittext?
>
> Your proposed generic code will work for powerpc and s390 because those
> archs only test against __init_* anyway. I don't know if any platform
> actually does place .init.text outside of __init_begin=>__init_end, but
> the comment seems to suggest that they could.
>

Christophe?

2021-11-05 19:22:54

by Christophe Leroy

[permalink] [raw]
Subject: Re: [PATCH v3 2/4] mm: Make generic arch_is_kernel_initmem_freed() do what it says



Le 04/11/2021 à 22:44, Andrew Morton a écrit :
> On Fri, 01 Oct 2021 17:14:41 +1000 Daniel Axtens <[email protected]> wrote:
>
>>> #ifdef __KERNEL__
>>> +/*
>>> + * Check if an address is part of freed initmem. After initmem is freed,
>>> + * memory can be allocated from it, and such allocations would then have
>>> + * addresses within the range [_stext, _end].
>>> + */
>>> +#ifndef arch_is_kernel_initmem_freed
>>> +static int arch_is_kernel_initmem_freed(unsigned long addr)
>>> +{
>>> + if (system_state < SYSTEM_FREEING_INITMEM)
>>> + return 0;
>>> +
>>> + return init_section_contains((void *)addr, 1);
>>
>> Is init_section_contains sufficient here?
>>
>> include/asm-generic/sections.h says:
>> * [__init_begin, __init_end]: contains .init.* sections, but .init.text.*
>> * may be out of this range on some architectures.
>> * [_sinittext, _einittext]: contains .init.text.* sections
>>
>> init_section_contains only checks __init_*:
>> static inline bool init_section_contains(void *virt, size_t size)
>> {
>> return memory_contains(__init_begin, __init_end, virt, size);
>> }
>>
>> Do we need to check against _sinittext and _einittext?
>>
>> Your proposed generic code will work for powerpc and s390 because those
>> archs only test against __init_* anyway. I don't know if any platform
>> actually does place .init.text outside of __init_begin=>__init_end, but
>> the comment seems to suggest that they could.
>>
>
> Christophe?
>

Sorry for answering late.

I've been thorugh free_initmem() in each architecture. The only sections
involved in the freeing actions are [__init_begin, __init_end], so I
think checking against __init_being, __init_end is enough.

If some architecture has init text outside of this section, then it is
not freed hence not necessary to check.

Christophe