* [PATCH v2 2/3] riscv: module: Allocate PLT entries for R_RISCV_PLT32
2025-04-09 17:14 [PATCH v2 1/3] riscv: module: Fix out-of-bounds relocation access Samuel Holland
@ 2025-04-09 17:14 ` Samuel Holland
2025-04-09 18:53 ` Andrew Jones
2025-04-09 17:14 ` [PATCH v2 3/3] riscv: module: Optimize PLT/GOT entry counting Samuel Holland
` (4 subsequent siblings)
5 siblings, 1 reply; 11+ messages in thread
From: Samuel Holland @ 2025-04-09 17:14 UTC (permalink / raw)
To: Palmer Dabbelt, Alexandre Ghiti, linux-riscv
Cc: Andrew Jones, Pinkesh Vaghela, Pritesh Patel, Darshan Prajapati,
Samuel Holland, Albert Ou, Charlie Jenkins, Paul Walmsley,
linux-kernel
apply_r_riscv_plt32_rela() may need to emit a PLT entry for the
referenced symbol, so there must be space allocated in the PLT.
Fixes: 8fd6c5142395 ("riscv: Add remaining module relocations")
Signed-off-by: Samuel Holland <samuel.holland@sifive.com>
---
Changes in v2:
- New patch for v2
arch/riscv/kernel/module-sections.c | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/arch/riscv/kernel/module-sections.c b/arch/riscv/kernel/module-sections.c
index e264e59e596e..91d0b355ceef 100644
--- a/arch/riscv/kernel/module-sections.c
+++ b/arch/riscv/kernel/module-sections.c
@@ -73,16 +73,17 @@ static bool duplicate_rela(const Elf_Rela *rela, int idx)
static void count_max_entries(Elf_Rela *relas, int num,
unsigned int *plts, unsigned int *gots)
{
- unsigned int type, i;
-
- for (i = 0; i < num; i++) {
- type = ELF_RISCV_R_TYPE(relas[i].r_info);
- if (type == R_RISCV_CALL_PLT) {
+ for (int i = 0; i < num; i++) {
+ switch (ELF_R_TYPE(relas[i].r_info)) {
+ case R_RISCV_CALL_PLT:
+ case R_RISCV_PLT32:
if (!duplicate_rela(relas, i))
(*plts)++;
- } else if (type == R_RISCV_GOT_HI20) {
+ break;
+ case R_RISCV_GOT_HI20:
if (!duplicate_rela(relas, i))
(*gots)++;
+ break;
}
}
}
--
2.47.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [PATCH v2 2/3] riscv: module: Allocate PLT entries for R_RISCV_PLT32
2025-04-09 17:14 ` [PATCH v2 2/3] riscv: module: Allocate PLT entries for R_RISCV_PLT32 Samuel Holland
@ 2025-04-09 18:53 ` Andrew Jones
0 siblings, 0 replies; 11+ messages in thread
From: Andrew Jones @ 2025-04-09 18:53 UTC (permalink / raw)
To: Samuel Holland
Cc: Palmer Dabbelt, Alexandre Ghiti, linux-riscv, Pinkesh Vaghela,
Pritesh Patel, Darshan Prajapati, Albert Ou, Charlie Jenkins,
Paul Walmsley, linux-kernel
On Wed, Apr 09, 2025 at 10:14:50AM -0700, Samuel Holland wrote:
> apply_r_riscv_plt32_rela() may need to emit a PLT entry for the
> referenced symbol, so there must be space allocated in the PLT.
>
> Fixes: 8fd6c5142395 ("riscv: Add remaining module relocations")
> Signed-off-by: Samuel Holland <samuel.holland@sifive.com>
> ---
>
> Changes in v2:
> - New patch for v2
>
> arch/riscv/kernel/module-sections.c | 13 +++++++------
> 1 file changed, 7 insertions(+), 6 deletions(-)
>
> diff --git a/arch/riscv/kernel/module-sections.c b/arch/riscv/kernel/module-sections.c
> index e264e59e596e..91d0b355ceef 100644
> --- a/arch/riscv/kernel/module-sections.c
> +++ b/arch/riscv/kernel/module-sections.c
> @@ -73,16 +73,17 @@ static bool duplicate_rela(const Elf_Rela *rela, int idx)
> static void count_max_entries(Elf_Rela *relas, int num,
> unsigned int *plts, unsigned int *gots)
> {
> - unsigned int type, i;
> -
> - for (i = 0; i < num; i++) {
> - type = ELF_RISCV_R_TYPE(relas[i].r_info);
> - if (type == R_RISCV_CALL_PLT) {
> + for (int i = 0; i < num; i++) {
> + switch (ELF_R_TYPE(relas[i].r_info)) {
I see ELF_R_TYPE() is equivalent to ELF_RISCV_R_TYPE(). So OK.
> + case R_RISCV_CALL_PLT:
> + case R_RISCV_PLT32:
> if (!duplicate_rela(relas, i))
> (*plts)++;
> - } else if (type == R_RISCV_GOT_HI20) {
> + break;
> + case R_RISCV_GOT_HI20:
> if (!duplicate_rela(relas, i))
> (*gots)++;
> + break;
> }
> }
> }
> --
> 2.47.0
>
Reviewed-by: Andrew Jones <ajones@ventanamicro.com>
Thanks,
drew
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 3/3] riscv: module: Optimize PLT/GOT entry counting
2025-04-09 17:14 [PATCH v2 1/3] riscv: module: Fix out-of-bounds relocation access Samuel Holland
2025-04-09 17:14 ` [PATCH v2 2/3] riscv: module: Allocate PLT entries for R_RISCV_PLT32 Samuel Holland
@ 2025-04-09 17:14 ` Samuel Holland
2025-04-09 19:07 ` Andrew Jones
2025-04-09 19:11 ` [PATCH v2 1/3] riscv: module: Fix out-of-bounds relocation access Maxim Kochetkov
` (3 subsequent siblings)
5 siblings, 1 reply; 11+ messages in thread
From: Samuel Holland @ 2025-04-09 17:14 UTC (permalink / raw)
To: Palmer Dabbelt, Alexandre Ghiti, linux-riscv
Cc: Andrew Jones, Pinkesh Vaghela, Pritesh Patel, Darshan Prajapati,
Samuel Holland, Albert Ou, Paul Walmsley, linux-kernel
perf reports that 99.63% of the cycles from `modprobe amdgpu` are spent
inside module_frob_arch_sections(). This is because amdgpu.ko contains
about 300000 relocations in its .rela.text section, and the algorithm in
count_max_entries() takes quadratic time.
Apply two optimizations from the arm64 code, which together reduce the
total execution time by 99.58%. First, sort the relocations so duplicate
entries are adjacent. Second, reduce the number of relocations that must
be sorted by filtering to only relocations that need PLT/GOT entries, as
done in commit d4e0340919fb ("arm64/module: Optimize module load time by
optimizing PLT counting").
Unlike the arm64 code, here the filtering and sorting is done in a
scratch buffer, because the HI20 relocation search optimization in
apply_relocate_add() depends on the original order of the relocations.
This allows accumulating PLT/GOT relocations across sections so sorting
and counting is only done once per module.
Signed-off-by: Samuel Holland <samuel.holland@sifive.com>
---
Changes in v2:
- Include R_RISCV_PLT32 relocations when computing the PLT size
- Accumulate PLT/GOT relocations across relocation sections
arch/riscv/kernel/module-sections.c | 81 +++++++++++++++++++++++------
1 file changed, 65 insertions(+), 16 deletions(-)
diff --git a/arch/riscv/kernel/module-sections.c b/arch/riscv/kernel/module-sections.c
index 91d0b355ceef..75551ac6504c 100644
--- a/arch/riscv/kernel/module-sections.c
+++ b/arch/riscv/kernel/module-sections.c
@@ -9,6 +9,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleloader.h>
+#include <linux/sort.h>
unsigned long module_emit_got_entry(struct module *mod, unsigned long val)
{
@@ -55,44 +56,70 @@ unsigned long module_emit_plt_entry(struct module *mod, unsigned long val)
return (unsigned long)&plt[i];
}
-static int is_rela_equal(const Elf_Rela *x, const Elf_Rela *y)
+#define cmp_3way(a, b) ((a) < (b) ? -1 : (a) > (b))
+
+static int cmp_rela(const void *a, const void *b)
{
- return x->r_info == y->r_info && x->r_addend == y->r_addend;
+ const Elf_Rela *x = a, *y = b;
+ int i;
+
+ /* sort by type, symbol index and addend */
+ i = cmp_3way(x->r_info, y->r_info);
+ if (i == 0)
+ i = cmp_3way(x->r_addend, y->r_addend);
+ return i;
}
static bool duplicate_rela(const Elf_Rela *rela, int idx)
{
- int i;
- for (i = 0; i < idx; i++) {
- if (is_rela_equal(&rela[i], &rela[idx]))
- return true;
- }
- return false;
+ /*
+ * Entries are sorted by type, symbol index and addend. That means
+ * that, if a duplicate entry exists, it must be in the preceding slot.
+ */
+ return idx > 0 && cmp_rela(rela + idx, rela + idx - 1) == 0;
}
-static void count_max_entries(Elf_Rela *relas, int num,
+static void count_max_entries(const Elf_Rela *relas, size_t num,
unsigned int *plts, unsigned int *gots)
{
- for (int i = 0; i < num; i++) {
+ for (size_t i = 0; i < num; i++) {
+ if (duplicate_rela(relas, i))
+ continue;
+
switch (ELF_R_TYPE(relas[i].r_info)) {
case R_RISCV_CALL_PLT:
case R_RISCV_PLT32:
- if (!duplicate_rela(relas, i))
- (*plts)++;
+ (*plts)++;
break;
case R_RISCV_GOT_HI20:
- if (!duplicate_rela(relas, i))
- (*gots)++;
+ (*gots)++;
break;
+ default:
+ unreachable();
}
}
}
+static bool rela_needs_plt_got_entry(const Elf_Rela *rela)
+{
+ switch (ELF_R_TYPE(rela->r_info)) {
+ case R_RISCV_CALL_PLT:
+ case R_RISCV_GOT_HI20:
+ case R_RISCV_PLT32:
+ return true;
+ default:
+ return false;
+ }
+}
+
int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
char *secstrings, struct module *mod)
{
+ size_t num_scratch_relas = 0;
unsigned int num_plts = 0;
unsigned int num_gots = 0;
+ Elf_Rela *scratch = NULL;
+ size_t scratch_size = 0;
int i;
/*
@@ -122,9 +149,10 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
/* Calculate the maxinum number of entries */
for (i = 0; i < ehdr->e_shnum; i++) {
+ size_t num_relas = sechdrs[i].sh_size / sizeof(Elf_Rela);
Elf_Rela *relas = (void *)ehdr + sechdrs[i].sh_offset;
- int num_rela = sechdrs[i].sh_size / sizeof(Elf_Rela);
Elf_Shdr *dst_sec = sechdrs + sechdrs[i].sh_info;
+ size_t scratch_size_needed;
if (sechdrs[i].sh_type != SHT_RELA)
continue;
@@ -133,7 +161,28 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
if (!(dst_sec->sh_flags & SHF_EXECINSTR))
continue;
- count_max_entries(relas, num_rela, &num_plts, &num_gots);
+ /*
+ * apply_relocate_add() relies on HI20 and LO12 relocation pairs being
+ * close together, so sort a copy of the section to avoid interfering.
+ */
+ scratch_size_needed = (num_scratch_relas + num_relas) * sizeof(*scratch);
+ if (scratch_size_needed > scratch_size) {
+ scratch_size = scratch_size_needed;
+ scratch = kvrealloc(scratch, scratch_size, GFP_KERNEL);
+ if (!scratch)
+ return -ENOMEM;
+ }
+
+ for (size_t j = 0; j < num_relas; j++)
+ if (rela_needs_plt_got_entry(&relas[j]))
+ scratch[num_scratch_relas++] = relas[j];
+ }
+
+ if (scratch) {
+ /* sort the accumulated PLT/GOT relocations so duplicates are adjacent */
+ sort(scratch, num_scratch_relas, sizeof(*scratch), cmp_rela, NULL);
+ count_max_entries(scratch, num_scratch_relas, &num_plts, &num_gots);
+ kvfree(scratch);
}
mod->arch.plt.shdr->sh_type = SHT_NOBITS;
--
2.47.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [PATCH v2 3/3] riscv: module: Optimize PLT/GOT entry counting
2025-04-09 17:14 ` [PATCH v2 3/3] riscv: module: Optimize PLT/GOT entry counting Samuel Holland
@ 2025-04-09 19:07 ` Andrew Jones
2025-04-09 19:18 ` Samuel Holland
0 siblings, 1 reply; 11+ messages in thread
From: Andrew Jones @ 2025-04-09 19:07 UTC (permalink / raw)
To: Samuel Holland
Cc: Palmer Dabbelt, Alexandre Ghiti, linux-riscv, Pinkesh Vaghela,
Pritesh Patel, Darshan Prajapati, Albert Ou, Paul Walmsley,
linux-kernel
On Wed, Apr 09, 2025 at 10:14:51AM -0700, Samuel Holland wrote:
> perf reports that 99.63% of the cycles from `modprobe amdgpu` are spent
> inside module_frob_arch_sections(). This is because amdgpu.ko contains
> about 300000 relocations in its .rela.text section, and the algorithm in
> count_max_entries() takes quadratic time.
>
> Apply two optimizations from the arm64 code, which together reduce the
> total execution time by 99.58%. First, sort the relocations so duplicate
> entries are adjacent. Second, reduce the number of relocations that must
> be sorted by filtering to only relocations that need PLT/GOT entries, as
> done in commit d4e0340919fb ("arm64/module: Optimize module load time by
> optimizing PLT counting").
>
> Unlike the arm64 code, here the filtering and sorting is done in a
> scratch buffer, because the HI20 relocation search optimization in
> apply_relocate_add() depends on the original order of the relocations.
> This allows accumulating PLT/GOT relocations across sections so sorting
> and counting is only done once per module.
>
> Signed-off-by: Samuel Holland <samuel.holland@sifive.com>
> ---
>
> Changes in v2:
> - Include R_RISCV_PLT32 relocations when computing the PLT size
> - Accumulate PLT/GOT relocations across relocation sections
>
> arch/riscv/kernel/module-sections.c | 81 +++++++++++++++++++++++------
> 1 file changed, 65 insertions(+), 16 deletions(-)
>
> diff --git a/arch/riscv/kernel/module-sections.c b/arch/riscv/kernel/module-sections.c
> index 91d0b355ceef..75551ac6504c 100644
> --- a/arch/riscv/kernel/module-sections.c
> +++ b/arch/riscv/kernel/module-sections.c
> @@ -9,6 +9,7 @@
> #include <linux/kernel.h>
> #include <linux/module.h>
> #include <linux/moduleloader.h>
> +#include <linux/sort.h>
>
> unsigned long module_emit_got_entry(struct module *mod, unsigned long val)
> {
> @@ -55,44 +56,70 @@ unsigned long module_emit_plt_entry(struct module *mod, unsigned long val)
> return (unsigned long)&plt[i];
> }
>
> -static int is_rela_equal(const Elf_Rela *x, const Elf_Rela *y)
> +#define cmp_3way(a, b) ((a) < (b) ? -1 : (a) > (b))
> +
> +static int cmp_rela(const void *a, const void *b)
> {
> - return x->r_info == y->r_info && x->r_addend == y->r_addend;
> + const Elf_Rela *x = a, *y = b;
> + int i;
> +
> + /* sort by type, symbol index and addend */
> + i = cmp_3way(x->r_info, y->r_info);
> + if (i == 0)
> + i = cmp_3way(x->r_addend, y->r_addend);
> + return i;
> }
>
> static bool duplicate_rela(const Elf_Rela *rela, int idx)
> {
> - int i;
> - for (i = 0; i < idx; i++) {
> - if (is_rela_equal(&rela[i], &rela[idx]))
> - return true;
> - }
> - return false;
> + /*
> + * Entries are sorted by type, symbol index and addend. That means
> + * that, if a duplicate entry exists, it must be in the preceding slot.
> + */
> + return idx > 0 && cmp_rela(rela + idx, rela + idx - 1) == 0;
> }
>
> -static void count_max_entries(Elf_Rela *relas, int num,
> +static void count_max_entries(const Elf_Rela *relas, size_t num,
> unsigned int *plts, unsigned int *gots)
> {
> - for (int i = 0; i < num; i++) {
> + for (size_t i = 0; i < num; i++) {
> + if (duplicate_rela(relas, i))
> + continue;
> +
> switch (ELF_R_TYPE(relas[i].r_info)) {
> case R_RISCV_CALL_PLT:
> case R_RISCV_PLT32:
> - if (!duplicate_rela(relas, i))
> - (*plts)++;
> + (*plts)++;
> break;
> case R_RISCV_GOT_HI20:
> - if (!duplicate_rela(relas, i))
> - (*gots)++;
> + (*gots)++;
> break;
> + default:
> + unreachable();
> }
> }
> }
>
> +static bool rela_needs_plt_got_entry(const Elf_Rela *rela)
> +{
> + switch (ELF_R_TYPE(rela->r_info)) {
> + case R_RISCV_CALL_PLT:
> + case R_RISCV_GOT_HI20:
> + case R_RISCV_PLT32:
> + return true;
> + default:
> + return false;
> + }
> +}
> +
> int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
> char *secstrings, struct module *mod)
> {
> + size_t num_scratch_relas = 0;
> unsigned int num_plts = 0;
> unsigned int num_gots = 0;
> + Elf_Rela *scratch = NULL;
> + size_t scratch_size = 0;
> int i;
>
> /*
> @@ -122,9 +149,10 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
>
> /* Calculate the maxinum number of entries */
> for (i = 0; i < ehdr->e_shnum; i++) {
> + size_t num_relas = sechdrs[i].sh_size / sizeof(Elf_Rela);
> Elf_Rela *relas = (void *)ehdr + sechdrs[i].sh_offset;
> - int num_rela = sechdrs[i].sh_size / sizeof(Elf_Rela);
> Elf_Shdr *dst_sec = sechdrs + sechdrs[i].sh_info;
> + size_t scratch_size_needed;
>
> if (sechdrs[i].sh_type != SHT_RELA)
> continue;
> @@ -133,7 +161,28 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
> if (!(dst_sec->sh_flags & SHF_EXECINSTR))
> continue;
>
> - count_max_entries(relas, num_rela, &num_plts, &num_gots);
> + /*
> + * apply_relocate_add() relies on HI20 and LO12 relocation pairs being
> + * close together, so sort a copy of the section to avoid interfering.
> + */
> + scratch_size_needed = (num_scratch_relas + num_relas) * sizeof(*scratch);
> + if (scratch_size_needed > scratch_size) {
> + scratch_size = scratch_size_needed;
Maybe not worth it, but when i is less than ehdr->e_shnum - 1 (we still
have more sections to look at) we could use scratch_size_needed * 2, or
something, in order to hopefully reduce the number of times kvrealloc()
is called.
> + scratch = kvrealloc(scratch, scratch_size, GFP_KERNEL);
> + if (!scratch)
> + return -ENOMEM;
> + }
> +
> + for (size_t j = 0; j < num_relas; j++)
> + if (rela_needs_plt_got_entry(&relas[j]))
> + scratch[num_scratch_relas++] = relas[j];
> + }
> +
> + if (scratch) {
> + /* sort the accumulated PLT/GOT relocations so duplicates are adjacent */
> + sort(scratch, num_scratch_relas, sizeof(*scratch), cmp_rela, NULL);
> + count_max_entries(scratch, num_scratch_relas, &num_plts, &num_gots);
> + kvfree(scratch);
> }
>
> mod->arch.plt.shdr->sh_type = SHT_NOBITS;
> --
> 2.47.0
>
Reviewed-by: Andrew Jones <ajones@ventanamicro.com>
Thanks,
drew
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [PATCH v2 3/3] riscv: module: Optimize PLT/GOT entry counting
2025-04-09 19:07 ` Andrew Jones
@ 2025-04-09 19:18 ` Samuel Holland
2025-04-09 19:24 ` Andrew Jones
0 siblings, 1 reply; 11+ messages in thread
From: Samuel Holland @ 2025-04-09 19:18 UTC (permalink / raw)
To: Andrew Jones
Cc: Palmer Dabbelt, Alexandre Ghiti, linux-riscv, Pinkesh Vaghela,
Pritesh Patel, Darshan Prajapati, Albert Ou, Paul Walmsley,
linux-kernel
Hi Drew,
Thanks for the review again!
On 2025-04-09 2:07 PM, Andrew Jones wrote:
> On Wed, Apr 09, 2025 at 10:14:51AM -0700, Samuel Holland wrote:
>> perf reports that 99.63% of the cycles from `modprobe amdgpu` are spent
>> inside module_frob_arch_sections(). This is because amdgpu.ko contains
>> about 300000 relocations in its .rela.text section, and the algorithm in
>> count_max_entries() takes quadratic time.
>>
>> Apply two optimizations from the arm64 code, which together reduce the
>> total execution time by 99.58%. First, sort the relocations so duplicate
>> entries are adjacent. Second, reduce the number of relocations that must
>> be sorted by filtering to only relocations that need PLT/GOT entries, as
>> done in commit d4e0340919fb ("arm64/module: Optimize module load time by
>> optimizing PLT counting").
>>
>> Unlike the arm64 code, here the filtering and sorting is done in a
>> scratch buffer, because the HI20 relocation search optimization in
>> apply_relocate_add() depends on the original order of the relocations.
>> This allows accumulating PLT/GOT relocations across sections so sorting
>> and counting is only done once per module.
>>
>> Signed-off-by: Samuel Holland <samuel.holland@sifive.com>
>> ---
>>
>> Changes in v2:
>> - Include R_RISCV_PLT32 relocations when computing the PLT size
>> - Accumulate PLT/GOT relocations across relocation sections
>>
>> arch/riscv/kernel/module-sections.c | 81 +++++++++++++++++++++++------
>> 1 file changed, 65 insertions(+), 16 deletions(-)
>>
>> diff --git a/arch/riscv/kernel/module-sections.c b/arch/riscv/kernel/module-sections.c
>> index 91d0b355ceef..75551ac6504c 100644
>> --- a/arch/riscv/kernel/module-sections.c
>> +++ b/arch/riscv/kernel/module-sections.c
>> @@ -9,6 +9,7 @@
>> #include <linux/kernel.h>
>> #include <linux/module.h>
>> #include <linux/moduleloader.h>
>> +#include <linux/sort.h>
>>
>> unsigned long module_emit_got_entry(struct module *mod, unsigned long val)
>> {
>> @@ -55,44 +56,70 @@ unsigned long module_emit_plt_entry(struct module *mod, unsigned long val)
>> return (unsigned long)&plt[i];
>> }
>>
>> -static int is_rela_equal(const Elf_Rela *x, const Elf_Rela *y)
>> +#define cmp_3way(a, b) ((a) < (b) ? -1 : (a) > (b))
>> +
>> +static int cmp_rela(const void *a, const void *b)
>> {
>> - return x->r_info == y->r_info && x->r_addend == y->r_addend;
>> + const Elf_Rela *x = a, *y = b;
>> + int i;
>> +
>> + /* sort by type, symbol index and addend */
>> + i = cmp_3way(x->r_info, y->r_info);
>> + if (i == 0)
>> + i = cmp_3way(x->r_addend, y->r_addend);
>> + return i;
>> }
>>
>> static bool duplicate_rela(const Elf_Rela *rela, int idx)
>> {
>> - int i;
>> - for (i = 0; i < idx; i++) {
>> - if (is_rela_equal(&rela[i], &rela[idx]))
>> - return true;
>> - }
>> - return false;
>> + /*
>> + * Entries are sorted by type, symbol index and addend. That means
>> + * that, if a duplicate entry exists, it must be in the preceding slot.
>> + */
>> + return idx > 0 && cmp_rela(rela + idx, rela + idx - 1) == 0;
>> }
>>
>> -static void count_max_entries(Elf_Rela *relas, int num,
>> +static void count_max_entries(const Elf_Rela *relas, size_t num,
>> unsigned int *plts, unsigned int *gots)
>> {
>> - for (int i = 0; i < num; i++) {
>> + for (size_t i = 0; i < num; i++) {
>> + if (duplicate_rela(relas, i))
>> + continue;
>> +
>> switch (ELF_R_TYPE(relas[i].r_info)) {
>> case R_RISCV_CALL_PLT:
>> case R_RISCV_PLT32:
>> - if (!duplicate_rela(relas, i))
>> - (*plts)++;
>> + (*plts)++;
>> break;
>> case R_RISCV_GOT_HI20:
>> - if (!duplicate_rela(relas, i))
>> - (*gots)++;
>> + (*gots)++;
>> break;
>> + default:
>> + unreachable();
>> }
>> }
>> }
>>
>> +static bool rela_needs_plt_got_entry(const Elf_Rela *rela)
>> +{
>> + switch (ELF_R_TYPE(rela->r_info)) {
>> + case R_RISCV_CALL_PLT:
>> + case R_RISCV_GOT_HI20:
>> + case R_RISCV_PLT32:
>> + return true;
>> + default:
>> + return false;
>> + }
>> +}
>> +
>> int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
>> char *secstrings, struct module *mod)
>> {
>> + size_t num_scratch_relas = 0;
>> unsigned int num_plts = 0;
>> unsigned int num_gots = 0;
>> + Elf_Rela *scratch = NULL;
>> + size_t scratch_size = 0;
>> int i;
>>
>> /*
>> @@ -122,9 +149,10 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
>>
>> /* Calculate the maxinum number of entries */
>> for (i = 0; i < ehdr->e_shnum; i++) {
>> + size_t num_relas = sechdrs[i].sh_size / sizeof(Elf_Rela);
>> Elf_Rela *relas = (void *)ehdr + sechdrs[i].sh_offset;
>> - int num_rela = sechdrs[i].sh_size / sizeof(Elf_Rela);
>> Elf_Shdr *dst_sec = sechdrs + sechdrs[i].sh_info;
>> + size_t scratch_size_needed;
>>
>> if (sechdrs[i].sh_type != SHT_RELA)
>> continue;
>> @@ -133,7 +161,28 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
>> if (!(dst_sec->sh_flags & SHF_EXECINSTR))
>> continue;
>>
>> - count_max_entries(relas, num_rela, &num_plts, &num_gots);
>> + /*
>> + * apply_relocate_add() relies on HI20 and LO12 relocation pairs being
>> + * close together, so sort a copy of the section to avoid interfering.
>> + */
>> + scratch_size_needed = (num_scratch_relas + num_relas) * sizeof(*scratch);
>> + if (scratch_size_needed > scratch_size) {
>> + scratch_size = scratch_size_needed;
>
> Maybe not worth it, but when i is less than ehdr->e_shnum - 1 (we still
> have more sections to look at) we could use scratch_size_needed * 2, or
> something, in order to hopefully reduce the number of times kvrealloc()
> is called.
I tried to keep the code as simple as possible, so I suppose it's not obvious
what's going on here. The current code takes advantage of the fact that one of
the relocation sections (.rela.text) is generally an order of magnitude or more
larger than the others, is usually the first section processed, and much fewer
than half of the relocations require a PLT/GOT entry. So in the common case:
num_relas (.rela.text) > num_scratch_relas (all sections) + num_relas (any
other section)
and there will only be one allocation, for .rela.text.
Regards,
Samuel
>> + scratch = kvrealloc(scratch, scratch_size, GFP_KERNEL);
>> + if (!scratch)
>> + return -ENOMEM;
>> + }
>> +
>> + for (size_t j = 0; j < num_relas; j++)
>> + if (rela_needs_plt_got_entry(&relas[j]))
>> + scratch[num_scratch_relas++] = relas[j];
>> + }
>> +
>> + if (scratch) {
>> + /* sort the accumulated PLT/GOT relocations so duplicates are adjacent */
>> + sort(scratch, num_scratch_relas, sizeof(*scratch), cmp_rela, NULL);
>> + count_max_entries(scratch, num_scratch_relas, &num_plts, &num_gots);
>> + kvfree(scratch);
>> }
>>
>> mod->arch.plt.shdr->sh_type = SHT_NOBITS;
>> --
>> 2.47.0
>>
>
> Reviewed-by: Andrew Jones <ajones@ventanamicro.com>
>
> Thanks,
> drew
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [PATCH v2 3/3] riscv: module: Optimize PLT/GOT entry counting
2025-04-09 19:18 ` Samuel Holland
@ 2025-04-09 19:24 ` Andrew Jones
0 siblings, 0 replies; 11+ messages in thread
From: Andrew Jones @ 2025-04-09 19:24 UTC (permalink / raw)
To: Samuel Holland
Cc: Palmer Dabbelt, Alexandre Ghiti, linux-riscv, Pinkesh Vaghela,
Pritesh Patel, Darshan Prajapati, Albert Ou, Paul Walmsley,
linux-kernel
On Wed, Apr 09, 2025 at 02:18:29PM -0500, Samuel Holland wrote:
> Hi Drew,
>
> Thanks for the review again!
>
> On 2025-04-09 2:07 PM, Andrew Jones wrote:
> > On Wed, Apr 09, 2025 at 10:14:51AM -0700, Samuel Holland wrote:
> >> perf reports that 99.63% of the cycles from `modprobe amdgpu` are spent
> >> inside module_frob_arch_sections(). This is because amdgpu.ko contains
> >> about 300000 relocations in its .rela.text section, and the algorithm in
> >> count_max_entries() takes quadratic time.
> >>
> >> Apply two optimizations from the arm64 code, which together reduce the
> >> total execution time by 99.58%. First, sort the relocations so duplicate
> >> entries are adjacent. Second, reduce the number of relocations that must
> >> be sorted by filtering to only relocations that need PLT/GOT entries, as
> >> done in commit d4e0340919fb ("arm64/module: Optimize module load time by
> >> optimizing PLT counting").
> >>
> >> Unlike the arm64 code, here the filtering and sorting is done in a
> >> scratch buffer, because the HI20 relocation search optimization in
> >> apply_relocate_add() depends on the original order of the relocations.
> >> This allows accumulating PLT/GOT relocations across sections so sorting
> >> and counting is only done once per module.
> >>
> >> Signed-off-by: Samuel Holland <samuel.holland@sifive.com>
> >> ---
> >>
> >> Changes in v2:
> >> - Include R_RISCV_PLT32 relocations when computing the PLT size
> >> - Accumulate PLT/GOT relocations across relocation sections
> >>
> >> arch/riscv/kernel/module-sections.c | 81 +++++++++++++++++++++++------
> >> 1 file changed, 65 insertions(+), 16 deletions(-)
> >>
> >> diff --git a/arch/riscv/kernel/module-sections.c b/arch/riscv/kernel/module-sections.c
> >> index 91d0b355ceef..75551ac6504c 100644
> >> --- a/arch/riscv/kernel/module-sections.c
> >> +++ b/arch/riscv/kernel/module-sections.c
> >> @@ -9,6 +9,7 @@
> >> #include <linux/kernel.h>
> >> #include <linux/module.h>
> >> #include <linux/moduleloader.h>
> >> +#include <linux/sort.h>
> >>
> >> unsigned long module_emit_got_entry(struct module *mod, unsigned long val)
> >> {
> >> @@ -55,44 +56,70 @@ unsigned long module_emit_plt_entry(struct module *mod, unsigned long val)
> >> return (unsigned long)&plt[i];
> >> }
> >>
> >> -static int is_rela_equal(const Elf_Rela *x, const Elf_Rela *y)
> >> +#define cmp_3way(a, b) ((a) < (b) ? -1 : (a) > (b))
> >> +
> >> +static int cmp_rela(const void *a, const void *b)
> >> {
> >> - return x->r_info == y->r_info && x->r_addend == y->r_addend;
> >> + const Elf_Rela *x = a, *y = b;
> >> + int i;
> >> +
> >> + /* sort by type, symbol index and addend */
> >> + i = cmp_3way(x->r_info, y->r_info);
> >> + if (i == 0)
> >> + i = cmp_3way(x->r_addend, y->r_addend);
> >> + return i;
> >> }
> >>
> >> static bool duplicate_rela(const Elf_Rela *rela, int idx)
> >> {
> >> - int i;
> >> - for (i = 0; i < idx; i++) {
> >> - if (is_rela_equal(&rela[i], &rela[idx]))
> >> - return true;
> >> - }
> >> - return false;
> >> + /*
> >> + * Entries are sorted by type, symbol index and addend. That means
> >> + * that, if a duplicate entry exists, it must be in the preceding slot.
> >> + */
> >> + return idx > 0 && cmp_rela(rela + idx, rela + idx - 1) == 0;
> >> }
> >>
> >> -static void count_max_entries(Elf_Rela *relas, int num,
> >> +static void count_max_entries(const Elf_Rela *relas, size_t num,
> >> unsigned int *plts, unsigned int *gots)
> >> {
> >> - for (int i = 0; i < num; i++) {
> >> + for (size_t i = 0; i < num; i++) {
> >> + if (duplicate_rela(relas, i))
> >> + continue;
> >> +
> >> switch (ELF_R_TYPE(relas[i].r_info)) {
> >> case R_RISCV_CALL_PLT:
> >> case R_RISCV_PLT32:
> >> - if (!duplicate_rela(relas, i))
> >> - (*plts)++;
> >> + (*plts)++;
> >> break;
> >> case R_RISCV_GOT_HI20:
> >> - if (!duplicate_rela(relas, i))
> >> - (*gots)++;
> >> + (*gots)++;
> >> break;
> >> + default:
> >> + unreachable();
> >> }
> >> }
> >> }
> >>
> >> +static bool rela_needs_plt_got_entry(const Elf_Rela *rela)
> >> +{
> >> + switch (ELF_R_TYPE(rela->r_info)) {
> >> + case R_RISCV_CALL_PLT:
> >> + case R_RISCV_GOT_HI20:
> >> + case R_RISCV_PLT32:
> >> + return true;
> >> + default:
> >> + return false;
> >> + }
> >> +}
> >> +
> >> int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
> >> char *secstrings, struct module *mod)
> >> {
> >> + size_t num_scratch_relas = 0;
> >> unsigned int num_plts = 0;
> >> unsigned int num_gots = 0;
> >> + Elf_Rela *scratch = NULL;
> >> + size_t scratch_size = 0;
> >> int i;
> >>
> >> /*
> >> @@ -122,9 +149,10 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
> >>
> >> /* Calculate the maxinum number of entries */
> >> for (i = 0; i < ehdr->e_shnum; i++) {
> >> + size_t num_relas = sechdrs[i].sh_size / sizeof(Elf_Rela);
> >> Elf_Rela *relas = (void *)ehdr + sechdrs[i].sh_offset;
> >> - int num_rela = sechdrs[i].sh_size / sizeof(Elf_Rela);
> >> Elf_Shdr *dst_sec = sechdrs + sechdrs[i].sh_info;
> >> + size_t scratch_size_needed;
> >>
> >> if (sechdrs[i].sh_type != SHT_RELA)
> >> continue;
> >> @@ -133,7 +161,28 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
> >> if (!(dst_sec->sh_flags & SHF_EXECINSTR))
> >> continue;
> >>
> >> - count_max_entries(relas, num_rela, &num_plts, &num_gots);
> >> + /*
> >> + * apply_relocate_add() relies on HI20 and LO12 relocation pairs being
> >> + * close together, so sort a copy of the section to avoid interfering.
> >> + */
> >> + scratch_size_needed = (num_scratch_relas + num_relas) * sizeof(*scratch);
> >> + if (scratch_size_needed > scratch_size) {
> >> + scratch_size = scratch_size_needed;
> >
> > Maybe not worth it, but when i is less than ehdr->e_shnum - 1 (we still
> > have more sections to look at) we could use scratch_size_needed * 2, or
> > something, in order to hopefully reduce the number of times kvrealloc()
> > is called.
>
> I tried to keep the code as simple as possible, so I suppose it's not obvious
> what's going on here. The current code takes advantage of the fact that one of
> the relocation sections (.rela.text) is generally an order of magnitude or more
> larger than the others, is usually the first section processed, and much fewer
> than half of the relocations require a PLT/GOT entry. So in the common case:
>
> num_relas (.rela.text) > num_scratch_relas (all sections) + num_relas (any
> other section)
>
> and there will only be one allocation, for .rela.text.
Sounds good.
Thanks,
drew
>
> Regards,
> Samuel
>
> >> + scratch = kvrealloc(scratch, scratch_size, GFP_KERNEL);
> >> + if (!scratch)
> >> + return -ENOMEM;
> >> + }
> >> +
> >> + for (size_t j = 0; j < num_relas; j++)
> >> + if (rela_needs_plt_got_entry(&relas[j]))
> >> + scratch[num_scratch_relas++] = relas[j];
> >> + }
> >> +
> >> + if (scratch) {
> >> + /* sort the accumulated PLT/GOT relocations so duplicates are adjacent */
> >> + sort(scratch, num_scratch_relas, sizeof(*scratch), cmp_rela, NULL);
> >> + count_max_entries(scratch, num_scratch_relas, &num_plts, &num_gots);
> >> + kvfree(scratch);
> >> }
> >>
> >> mod->arch.plt.shdr->sh_type = SHT_NOBITS;
> >> --
> >> 2.47.0
> >>
> >
> > Reviewed-by: Andrew Jones <ajones@ventanamicro.com>
> >
> > Thanks,
> > drew
>
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 1/3] riscv: module: Fix out-of-bounds relocation access
2025-04-09 17:14 [PATCH v2 1/3] riscv: module: Fix out-of-bounds relocation access Samuel Holland
2025-04-09 17:14 ` [PATCH v2 2/3] riscv: module: Allocate PLT entries for R_RISCV_PLT32 Samuel Holland
2025-04-09 17:14 ` [PATCH v2 3/3] riscv: module: Optimize PLT/GOT entry counting Samuel Holland
@ 2025-04-09 19:11 ` Maxim Kochetkov
2025-04-11 11:21 ` Alexandre Ghiti
` (2 subsequent siblings)
5 siblings, 0 replies; 11+ messages in thread
From: Maxim Kochetkov @ 2025-04-09 19:11 UTC (permalink / raw)
To: Samuel Holland, Palmer Dabbelt, Alexandre Ghiti, linux-riscv
Cc: Andrew Jones, Pinkesh Vaghela, Pritesh Patel, Darshan Prajapati,
Albert Ou, Amma Lee, Charlie Jenkins, Clément Léger,
Jinjie Ruan, Luis Chamberlain, Mike Rapoport (IBM), Paul Walmsley,
linux-kernel
09.04.2025 20:14, Samuel Holland пишет:
> The current code allows rel[j] to access one element past the end of the
> relocation section. Simplify to num_relocations which is equivalent to
> the existing size expression.
>
> Fixes: 080c4324fa5e ("riscv: optimize ELF relocation function in riscv")
> Signed-off-by: Samuel Holland <samuel.holland@sifive.com>
Reviewed-by: Maxim Kochetkov <fido_max@inbox.ru>
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [PATCH v2 1/3] riscv: module: Fix out-of-bounds relocation access
2025-04-09 17:14 [PATCH v2 1/3] riscv: module: Fix out-of-bounds relocation access Samuel Holland
` (2 preceding siblings ...)
2025-04-09 19:11 ` [PATCH v2 1/3] riscv: module: Fix out-of-bounds relocation access Maxim Kochetkov
@ 2025-04-11 11:21 ` Alexandre Ghiti
2025-04-16 18:42 ` patchwork-bot+linux-riscv
2025-06-02 22:12 ` patchwork-bot+linux-riscv
5 siblings, 0 replies; 11+ messages in thread
From: Alexandre Ghiti @ 2025-04-11 11:21 UTC (permalink / raw)
To: Samuel Holland, Palmer Dabbelt, linux-riscv
Cc: Andrew Jones, Pinkesh Vaghela, Pritesh Patel, Darshan Prajapati,
Albert Ou, Amma Lee, Charlie Jenkins, Clément Léger,
Jinjie Ruan, Luis Chamberlain, Maxim Kochetkov,
Mike Rapoport (IBM), Paul Walmsley, linux-kernel
Hi Samuel,
On 09/04/2025 19:14, Samuel Holland wrote:
> The current code allows rel[j] to access one element past the end of the
> relocation section. Simplify to num_relocations which is equivalent to
> the existing size expression.
>
> Fixes: 080c4324fa5e ("riscv: optimize ELF relocation function in riscv")
> Signed-off-by: Samuel Holland <samuel.holland@sifive.com>
> ---
>
> Changes in v2:
> - New patch for v2
>
> arch/riscv/kernel/module.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/arch/riscv/kernel/module.c b/arch/riscv/kernel/module.c
> index 47d0ebeec93c..060f576cc988 100644
> --- a/arch/riscv/kernel/module.c
> +++ b/arch/riscv/kernel/module.c
> @@ -859,7 +859,7 @@ int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab,
> }
>
> j++;
> - if (j > sechdrs[relsec].sh_size / sizeof(*rel))
> + if (j == num_relocations)
> j = 0;
>
> } while (j_idx != j);
Reviewed-by: Alexandre Ghiti <alexghiti@rivosinc.com>
Thanks,
Alex
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [PATCH v2 1/3] riscv: module: Fix out-of-bounds relocation access
2025-04-09 17:14 [PATCH v2 1/3] riscv: module: Fix out-of-bounds relocation access Samuel Holland
` (3 preceding siblings ...)
2025-04-11 11:21 ` Alexandre Ghiti
@ 2025-04-16 18:42 ` patchwork-bot+linux-riscv
2025-06-02 22:12 ` patchwork-bot+linux-riscv
5 siblings, 0 replies; 11+ messages in thread
From: patchwork-bot+linux-riscv @ 2025-04-16 18:42 UTC (permalink / raw)
To: Samuel Holland
Cc: linux-riscv, palmer, alex, ajones, pinkesh.vaghela, pritesh.patel,
darshan.prajapati, aou, lixiaoyun, charlie, cleger, ruanjinjie,
mcgrof, fido_max, rppt, paul.walmsley, linux-kernel
Hello:
This series was applied to riscv/linux.git (fixes)
by Alexandre Ghiti <alexghiti@rivosinc.com>:
On Wed, 9 Apr 2025 10:14:49 -0700 you wrote:
> The current code allows rel[j] to access one element past the end of the
> relocation section. Simplify to num_relocations which is equivalent to
> the existing size expression.
>
> Fixes: 080c4324fa5e ("riscv: optimize ELF relocation function in riscv")
> Signed-off-by: Samuel Holland <samuel.holland@sifive.com>
>
> [...]
Here is the summary with links:
- [v2,1/3] riscv: module: Fix out-of-bounds relocation access
https://git.kernel.org/riscv/c/0b4cce68efb9
- [v2,2/3] riscv: module: Allocate PLT entries for R_RISCV_PLT32
https://git.kernel.org/riscv/c/1ee1313f4722
- [v2,3/3] riscv: module: Optimize PLT/GOT entry counting
(no matching commit)
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [PATCH v2 1/3] riscv: module: Fix out-of-bounds relocation access
2025-04-09 17:14 [PATCH v2 1/3] riscv: module: Fix out-of-bounds relocation access Samuel Holland
` (4 preceding siblings ...)
2025-04-16 18:42 ` patchwork-bot+linux-riscv
@ 2025-06-02 22:12 ` patchwork-bot+linux-riscv
5 siblings, 0 replies; 11+ messages in thread
From: patchwork-bot+linux-riscv @ 2025-06-02 22:12 UTC (permalink / raw)
To: Samuel Holland
Cc: linux-riscv, palmer, alex, ajones, pinkesh.vaghela, pritesh.patel,
darshan.prajapati, aou, lixiaoyun, charlie, cleger, ruanjinjie,
mcgrof, fido_max, rppt, paul.walmsley, linux-kernel
Hello:
This series was applied to riscv/linux.git (for-next)
by Palmer Dabbelt <palmer@dabbelt.com>:
On Wed, 9 Apr 2025 10:14:49 -0700 you wrote:
> The current code allows rel[j] to access one element past the end of the
> relocation section. Simplify to num_relocations which is equivalent to
> the existing size expression.
>
> Fixes: 080c4324fa5e ("riscv: optimize ELF relocation function in riscv")
> Signed-off-by: Samuel Holland <samuel.holland@sifive.com>
>
> [...]
Here is the summary with links:
- [v2,1/3] riscv: module: Fix out-of-bounds relocation access
(no matching commit)
- [v2,2/3] riscv: module: Allocate PLT entries for R_RISCV_PLT32
(no matching commit)
- [v2,3/3] riscv: module: Optimize PLT/GOT entry counting
https://git.kernel.org/riscv/c/e9aa38ea39b2
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 11+ messages in thread