bpf_prog_pack followup
Subsystems (8)
Song Liu
reviewerv5 cover bpf_prog_pack followup
This set is the second half of v4 [1].
Changes v4 => v5:
1. Rebase and resolve conflicts due to module.c split.
2. Update experiment results (below).
For our web service production benchmark, bpf_prog_pack on 4kB pages gives 0.5% to 0.7% more throughput than not using bpf_prog_pack. bpf_prog_pack on 2MB pages 0.6% to 0.9% more throughput than not using bpf_prog_pack. Note that 0.5% is a huge improvement for our fleet. I believe this is also significant for other companies with many thousand servers.
Update: Further experiments (suggested by Rick Edgecombe) showed that most of benefit on the web service benchmark came from less direct map fragmentation. The experiment is as follows:
Side A: 2MB bpf prog pack on a single 2MB page;
Side B: 2MB bpf prog pack on 512x 4kB pages;
The system only uses about 200kB for BPF programs, but 2MB is allocated for bpf_prog_pack (for both A and B). Therefore, direct map fragmentation caused by BPF programs is elminated, and we are only measuring the performance difference of 1x 2MB page vs. ~50 4kB pages (we only use about 50 out of the 512 pages). For these two sides, the difference in system throughput is within the noise. I also measured iTLB-load-misses caused by bpf programs, which is ~300/s for case A, and ~1600/s for case B. The overall iTLB-load-misses is about 1.5M/s on these hosts. Therefore, we can clearly see 2MB page reduces iTLB misses, but the difference is not enough to have visible impact on system throughput.
Of course, the impact of iTLB miss will be more significant for systems with more BPF programs loaded.
[1] https://lore.kernel.org/bpf/[email protected]/
Song Liu (5):
module: introduce module_alloc_huge
bpf: use module_alloc_huge for bpf_prog_pack
vmalloc: WARN for set_vm_flush_reset_perms() on huge pages
vmalloc: introduce huge_vmalloc_supported
bpf: simplify select_bpf_prog_pack_size
arch/x86/kernel/module.c | 21 +++++++++++++++++++++ include/linux/moduleloader.h | 5 +++++ include/linux/vmalloc.h | 7 +++++++ kernel/bpf/core.c | 25 ++++++++++--------------- kernel/module/main.c | 8 ++++++++ mm/vmalloc.c | 5 +++++ 6 files changed, 56 insertions(+), 15 deletions(-)
--
2.30.2
Song Liu
reviewerPATCH v5 1/5 module: introduce module_alloc_huge
Introduce module_alloc_huge, which allocates huge page backed memory in module memory space. The primary user of this memory is bpf_prog_pack (multiple BPF programs sharing a huge page).
--- arch/x86/kernel/module.c | 21 +++++++++++++++++++++ include/linux/moduleloader.h | 5 +++++ kernel/module/main.c | 8 ++++++++ 3 files changed, 34 insertions(+)
arch/x86/kernel/module.c +21 -0
@@ -86,6 +86,27 @@ void *module_alloc(unsigned long size)return p; } +void *module_alloc_huge (unsigned long size) +{ +gfp_t gfp_mask =GFP_KERNEL ; +void *p; + +if (PAGE_ALIGN (size) >MODULES_LEN ) +return NULL ; + + p =__vmalloc_node_range (size,MODULE_ALIGN , +MODULES_VADDR +get_module_load_offset (), +MODULES_END , gfp_mask,PAGE_KERNEL , +VM_DEFER_KMEMLEAK |VM_ALLOW_HUGE_VMAP , +NUMA_NO_NODE ,__builtin_return_address (0 )); +if (p && (kasan_alloc_module_shadow (p, size, gfp_mask) <0 )) { +vfree (p); +return NULL ; + } + +return p; +} +#ifdef CONFIG_X86_32 int apply_relocate(Elf32_Shdr *sechdrs, const char *strtab,
include/linux/moduleloader.h +5 -0
@@ -26,6 +26,11 @@ unsigned int arch_mod_section_prepend(struct module *mod, unsigned int section);sections. Returns NULL on failure. */ void *module_alloc (unsigned long size); +/* Allocator used for allocating memory in module memory space. If size is +* greater than PMD_SIZE, allow using huge pages. Returns NULL on failure. +*/ +void *module_alloc_huge (unsigned long size); +/* Free memory returned from module_alloc. */ void module_memfree (void *module_region);
kernel/module/main.c +8 -0
@@ -1613,6 +1613,14 @@ void * __weak module_alloc(unsigned long size) NUMA_NO_NODE, __builtin_return_address(0)); } +void *__weak module_alloc_huge (unsigned long size) +{ +return __vmalloc_node_range (size,1 ,VMALLOC_START ,VMALLOC_END , +GFP_KERNEL ,PAGE_KERNEL_EXEC , +VM_FLUSH_RESET_PERMS |VM_ALLOW_HUGE_VMAP , +NUMA_NO_NODE ,__builtin_return_address (0 )); +} +bool __weak module_init_section (const char *name) {return strstarts (name,".init" );
Signature · 1 line
2.30.2
Luis Chamberlain
maintainerRe: [PATCH v5 bpf-next 1/5] module: introduce module_alloc_huge
Quoted 5 lines · Song Liu
Introduce
module_alloc_huge, which allocates huge page backed memory in module memory space. The primary user of this memory isbpf_prog_pack(multiple BPF programs sharing a huge page).Signed-off-by Song Liu <[email protected]>
I see mm not Cc'd. I'd like review from them.
Quoted 24 lines
--- arch/x86/kernel/module.c | 21 +++++++++++++++++++++ include/linux/moduleloader.h | 5 +++++ kernel/module/main.c | 8 ++++++++ 3 files changed, 34 insertions(+)arch/x86/kernel/module.c +10
-0@@ -86,6 +86,27 @@ void *module_alloc(unsigned long size)return p; } +void *module_alloc_huge (unsigned long size) +{ +gfp_t gfp_mask =GFP_KERNEL ; +void *p; + +if (PAGE_ALIGN (size) >MODULES_LEN ) +return NULL ; + + p = __vmalloc_node_range(size, MODULE_ALIGN, + MODULES_VADDR + get_module_load_offset(),
arch/x86/kernel/module.c void *module_alloc(unsigned long size) · line 108
Quoted 10 lines
arch/x86/kernel/module.c +10
-0+MODULES_END , gfp_mask,PAGE_KERNEL , +VM_DEFER_KMEMLEAK |VM_ALLOW_HUGE_VMAP , + NUMA_NO_NODE, __builtin_return_address(0)); +if (p && (kasan_alloc_module_shadow (p, size, gfp_mask) <0 )) { +vfree (p); +return NULL ; + } + +return p; +}
1) When things like kernel/bpf/core.c start using a module alloc it is time to consider genearlizing this.
2) How we free is important, and each arch does something funky for this. This is not addressed here.
And yes I welcome generalizing generic module_alloc() too as suggested before. The concern on my part is the sloppiness this enables.
Luis
Quoted 42 lines
arch/x86/kernel/module.c +1
-0+#ifdef CONFIG_X86_32 int apply_relocate(Elf32_Shdr *sechdrs, const char *strtab,include/linux/moduleloader.h +5
-0@@ -26,6 +26,11 @@ unsigned int arch_mod_section_prepend(struct module *mod, unsigned int section);sections. Returns NULL on failure. */ void *module_alloc (unsigned long size); +/* Allocator used for allocating memory in module memory space. If size is +* greater than PMD_SIZE, allow using huge pages. Returns NULL on failure. +*/ +void *module_alloc_huge (unsigned long size); +/* Free memory returned from module_alloc. */ void module_memfree (void *module_region);kernel/module/main.c +8
-0@@ -1613,6 +1613,14 @@ void * __weak module_alloc(unsigned long size) NUMA_NO_NODE, __builtin_return_address(0)); } +void *__weak module_alloc_huge (unsigned long size) +{ +return __vmalloc_node_range (size,1 ,VMALLOC_START ,VMALLOC_END , +GFP_KERNEL ,PAGE_KERNEL_EXEC , +VM_FLUSH_RESET_PERMS |VM_ALLOW_HUGE_VMAP , +NUMA_NO_NODE ,__builtin_return_address (0 )); +} +bool __weak module_init_section (const char *name) {return strstarts (name,".init" );Signature · 1 line
2.30.2
Re: [PATCH v5 bpf-next 1/5] module: introduce module_alloc_huge
Quoted 12 lines · Luis Chamberlain
Quoted 5 lines · Song Liu
Introduce
module_alloc_huge, which allocates huge page backed memory in module memory space. The primary user of this memory isbpf_prog_pack(multiple BPF programs sharing a huge page).Signed-off-by Song Liu <[email protected]>I see mm not Cc'd. I'd like review from them.
I will CC mm in the next version (or resend). Thanks for the reminder.
Quoted 30 lines
Quoted 30 lines
---
arch/x86/kernel/module.c| 21 +++++++++++++++++++++include/linux/moduleloader.h| 5 +++++kernel/module/main.c| 8 ++++++++3 files changed, 34 insertions(+)arch/x86/kernel/module.c +15
-0@@ -86,6 +86,27 @@ void *module_alloc(unsigned long size)return p; } +void *module_alloc_huge (unsigned long size) +{ +gfp_t gfp_mask =GFP_KERNEL ; +void *p; + +if (PAGE_ALIGN (size) >MODULES_LEN ) +return NULL ; + + p =__vmalloc_node_range (size,MODULE_ALIGN , +MODULES_VADDR +get_module_load_offset (), +MODULES_END , gfp_mask,PAGE_KERNEL , +VM_DEFER_KMEMLEAK |VM_ALLOW_HUGE_VMAP , +NUMA_NO_NODE ,__builtin_return_address (0 )); +if (p && (kasan_alloc_module_shadow (p, size, gfp_mask) <0 )) { +vfree (p);
Quoted 8 lines
arch/x86/kernel/module.c void *module_alloc(unsigned long size) · line 108
Quoted 5 lines
arch/x86/kernel/module.c +5
-0+return NULL ; + } + +return p; +}1) When things like
kernel/bpf/core.cstart using a module alloc it is time to consider genearlizing this.
I am not quite sure what the generalization would look like. IMHO, the ideal case would have:
a) A kernel_text_rw_allocator, similar to current module_alloc.
b) A kernel_text_ro_allocator, similar to current bpf_prog_pack_alloc. This is built on top of kernel_text_rw_allocator. Different allocations could share a page, thus it requires text_poke like support from the arch.
c) If the arch supports text_poke, kprobes, ftrace trampolines, and bpf trampolines should use kernel_text_ro_allocator.
d) Major archs should support CONFIG_ARCH_WANTS_MODULES_DATA_IN_VMALLOC, and they should use kernel_text_ro_allocator for module text.
Does this sound reasonable to you?
I tried to enable CONFIG_ARCH_WANTS_MODULES_DATA_IN_VMALLOC for x86_64, but that doesn't really work. Do we have plan to make this combination work?
Quoted 3 lines
2) How we free is important, and each arch does something funky for this. This is not addressed here.
How should we address this? IIUC, x86_64 just calls vfree.
Quoted 3 lines
And yes I welcome generalizing generic
module_alloc()too as suggested before. The concern on my part is the sloppiness this enables.
One question I have is, does module_alloc (or kernel_text_*_allocator above) belong to module code, or mm code (maybe vmalloc)?
I am planning to let BPF trampoline use bpf_prog_pack on x86_64, which is another baby step of c) above.
Thanks,
Song
Luis Chamberlain
maintainerRe: [PATCH v5 bpf-next 1/5] module: introduce module_alloc_huge
Quoted 38 lines · Song Liu
Quoted 17 lines · Luis Chamberlain
Quoted 15 lines · Song Liu
arch/x86/kernel/module.c +15
-0+void *module_alloc_huge (unsigned long size) +{ +gfp_t gfp_mask =GFP_KERNEL ; +void *p; + +if (PAGE_ALIGN (size) >MODULES_LEN ) +return NULL ; + + p =__vmalloc_node_range (size,MODULE_ALIGN , +MODULES_VADDR +get_module_load_offset (), +MODULES_END , gfp_mask,PAGE_KERNEL , +VM_DEFER_KMEMLEAK |VM_ALLOW_HUGE_VMAP , +NUMA_NO_NODE ,__builtin_return_address (0 )); +if (p && (kasan_alloc_module_shadow (p, size, gfp_mask) <0 )) { +vfree (p);Quoted 8 lines
arch/x86/kernel/module.c void *module_alloc(unsigned long size) · line 108
Quoted 5 lines
arch/x86/kernel/module.c +5
-0+return NULL ; + } + +return p; +}1) When things like
kernel/bpf/core.cstart using a module alloc it is time to consider genearlizing this.I am not quite sure what the generalization would look like. IMHO, the ideal case would have:
a) Akernel_text_rw_allocator, similar to currentmodule_alloc.
b) Akernel_text_ro_allocator, similar to currentbpf_prog_pack_alloc. This is built on top ofkernel_text_rw_allocator. Different allocations could share a page, thus it requirestext_pokelike support from the arch.
c) If the arch supportstext_poke, kprobes, ftrace trampolines, and bpf trampolines should usekernel_text_ro_allocator.
d) Major archs should supportCONFIG_ARCH_WANTS_MODULES_DATA_IN_VMALLOC, and they should usekernel_text_ro_allocatorfor module text.
Quoted 1 line
Does this sound reasonable to you?
Yes, and a respective free call may have an arch specific stuff which removes exec stuff.
In so far as the bikeshedding, I do think this is generic so vmalloc_text_*() suffices or vmalloc_exec_*() take your pick for a starter and let the world throw in their preference.
Quoted 3 lines
I tried to enable
CONFIG_ARCH_WANTS_MODULES_DATA_IN_VMALLOCforx86_64, but that doesn't really work. Do we have plan to make this combination work?
Oh nice.
Good stuff. Perhaps it just requires a little love from mm folks. Don't beat yourself up if it does not yet. We can work towards that later.
Quoted 4 lines
Quoted 2 lines
2) How we free is important, and each arch does something funky for this. This is not addressed here.
How should we address this? IIUC,
x86_64just calls vfree.
That's not the case for all archs is it? I'm talking about the generic module_alloc() too. I'd like to see that go the way we discussed above.
Quoted 5 lines
Quoted 2 lines
And yes I welcome generalizing generic
module_alloc()too as suggested before. The concern on my part is the sloppiness this enables.One question I have is, does
module_alloc(or kernel_text_*_allocator above) belong to module code, or mm code (maybe vmalloc)?
The evolution of its uses indicates it is growing outside of modules and so mm should be the new home.
Quoted 2 lines
I am planning to let BPF trampoline use
bpf_prog_packonx86_64, which is another baby step of c) above.
OK!
Luis
Song Liu
reviewerPATCH v5 2/5 bpf: use module_alloc_huge for bpf_prog_pack
In reply to Song LiuUse module_alloc_huge for bpf_prog_pack so that BPF programs sit on PMD_SIZE pages. This benefits system performance by reducing iTLB miss rate. Benchmark of a real web service workload shows this change gives another ~0.2% performance boost on top of PAGE_SIZE bpf_prog_pack (which improve system throughput by ~0.5%).
Also, remove set_vm_flush_reset_perms() from alloc_new_pack() and use set_memory_[nx|rw] in bpf_prog_pack_free(). This is because VM_FLUSH_RESET_PERMS does not work with huge pages yet. [1]
[1] https://lore.kernel.org/bpf/[email protected]/
--- kernel/bpf/core.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-)
kernel/bpf/core.c +7 -5
@@ -857,7 +857,7 @@ static size_t select_bpf_prog_pack_size(void)void *ptr; size =BPF_HPAGE_SIZE *num_online_nodes (); - ptr =module_alloc (size); + ptr =module_alloc_huge (size);/* Test whether we can get huge pages. If not just use PAGE_SIZE * packs.
@@ -881,7 +881,7 @@ static struct bpf_prog_pack *alloc_new_pack(bpf_jit_fill_hole_t bpf_fill_ill_ins GFP_KERNEL);if (!pack)return NULL ; - pack->ptr =module_alloc (bpf_prog_pack_size); + pack->ptr =module_alloc_huge (bpf_prog_pack_size);if (!pack->ptr) {kfree (pack);return NULL ;
@@ -890,7 +890,6 @@ static struct bpf_prog_pack *alloc_new_pack(bpf_jit_fill_hole_t bpf_fill_ill_insbitmap_zero (pack->bitmap, bpf_prog_pack_size /BPF_PROG_CHUNK_SIZE );list_add_tail (&pack->list, &pack_list); -set_vm_flush_reset_perms (pack->ptr);set_memory_ro ((unsigned long )pack->ptr, bpf_prog_pack_size /PAGE_SIZE );set_memory_x ((unsigned long )pack->ptr, bpf_prog_pack_size /PAGE_SIZE );return pack;
@@ -909,10 +908,9 @@ static void *bpf_prog_pack_alloc(u32 size, bpf_jit_fill_hole_t bpf_fill_ill_insnif (size > bpf_prog_pack_size) { size =round_up (size,PAGE_SIZE ); - ptr =module_alloc (size); + ptr =module_alloc_huge (size);if (ptr) {bpf_fill_ill_insns (ptr, size); -set_vm_flush_reset_perms (ptr);set_memory_ro ((unsigned long )ptr, size /PAGE_SIZE );set_memory_x ((unsigned long )ptr, size /PAGE_SIZE ); }
@@ -949,6 +947,8 @@ static void bpf_prog_pack_free(struct bpf_binary_header *hdr)mutex_lock (&pack_mutex);if (hdr->size > bpf_prog_pack_size) { +set_memory_nx ((unsigned long )hdr, hdr->size /PAGE_SIZE ); +set_memory_rw ((unsigned long )hdr, hdr->size /PAGE_SIZE );module_memfree (hdr); goto out; }
@@ -975,6 +975,8 @@ static void bpf_prog_pack_free(struct bpf_binary_header *hdr)if (bitmap_find_next_zero_area (pack->bitmap,bpf_prog_chunk_count (),0 ,bpf_prog_chunk_count (),0 ) ==0 ) {list_del (&pack->list); +set_memory_nx ((unsigned long )pack->ptr, bpf_prog_pack_size /PAGE_SIZE ); +set_memory_rw ((unsigned long )pack->ptr, bpf_prog_pack_size /PAGE_SIZE );module_memfree (pack->ptr);kfree (pack); }
Signature · 1 line
2.30.2
Song Liu
reviewerPATCH v5 3/5 vmalloc: WARN for set_vm_flush_reset_perms() on huge pages
In reply to Song LiuVM_FLUSH_RESET_PERMS is not yet ready for huge pages, add a WARN to catch misuse soon.
--- include/linux/vmalloc.h | 1 + 1 file changed, 1 insertion(+)
include/linux/vmalloc.h +1 -0
@@ -239,6 +239,7 @@ static inline void set_vm_flush_reset_perms(void *addr) {struct vm_struct *vm =find_vm_area (addr); +WARN_ON_ONCE (is_vm_area_hugepages (addr));if (vm) vm->flags |=VM_FLUSH_RESET_PERMS ; }
Signature · 1 line
2.30.2
Song Liu
reviewerPATCH v5 4/5 vmalloc: introduce huge_vmalloc_supported
In reply to Song Liuhuge_vmalloc_supported() exposes vmap_allow_huge so that users of vmalloc APIs could know whether vmalloc will return huge pages.
--- include/linux/vmalloc.h | 6 ++++++ mm/vmalloc.c | 5 +++++ 2 files changed, 11 insertions(+)
include/linux/vmalloc.h +6 -0
@@ -243,11 +243,17 @@ static inline void set_vm_flush_reset_perms(void *addr)if (vm) vm->flags |= VM_FLUSH_RESET_PERMS; } +bool huge_vmalloc_supported (void );#else static inline void set_vm_flush_reset_perms (void *addr) { } + +static inline bool huge_vmalloc_supported (void ) +{ +return false; +}#endif /* for /proc/kcore */
mm/vmalloc.c +5 -0
@@ -72,6 +72,11 @@ early_param("nohugevmalloc", set_nohugevmalloc);static const bool vmap_allow_huge = false;#endif /* CONFIG_HAVE_ARCH_HUGE_VMALLOC */ +bool huge_vmalloc_supported (void ) +{ +return vmap_allow_huge; +} +bool is_vmalloc_addr (const void *x) {unsigned long addr = (unsigned long )kasan_reset_tag (x);
Signature · 1 line
2.30.2
Song Liu
reviewerPATCH v5 5/5 bpf: simplify select_bpf_prog_pack_size
In reply to Song LiuUse huge_vmalloc_supported to simplify select_bpf_prog_pack_size, so that we don't allocate some huge pages and free them immediately.
--- kernel/bpf/core.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-)
kernel/bpf/core.c +4 -11
@@ -854,22 +854,15 @@ static LIST_HEAD(pack_list);static size_t select_bpf_prog_pack_size (void ) {size_t size; -void *ptr; - - size =BPF_HPAGE_SIZE *num_online_nodes (); - ptr =module_alloc_huge (size); -/* Test whether we can get huge pages. If not just use PAGE_SIZE -* packs. -*/ -if (!ptr || !is_vm_area_hugepages (ptr)) { +if (huge_vmalloc_supported ()) { + size =BPF_HPAGE_SIZE *num_online_nodes (); + bpf_prog_pack_mask =BPF_HPAGE_MASK ; + }else { size =PAGE_SIZE ; bpf_prog_pack_mask =PAGE_MASK ; - }else { - bpf_prog_pack_mask =BPF_HPAGE_MASK ; } -vfree (ptr);return size; }
Signature · 1 line
2.30.2
oops, wrong address for x86@.
Quoted 55 lines · Song Liu
This set is the second half of v4 [1].
Changes v4 => v5:
1. Rebase and resolve conflicts due tomodule.csplit.
2. Update experiment results (below).For our web service production benchmark,
bpf_prog_packon 4kB pages gives 0.5% to 0.7% more throughput than not usingbpf_prog_pack.bpf_prog_packon 2MB pages 0.6% to 0.9% more throughput than not usingbpf_prog_pack. Note that 0.5% is a huge improvement for our fleet. I believe this is also significant for other companies with many thousand servers.Update: Further experiments (suggested by Rick Edgecombe) showed that most of benefit on the web service benchmark came from less direct map fragmentation. The experiment is as follows:
Side A: 2MB bpf prog pack on a single 2MB page;
Side B: 2MB bpf prog pack on 512x 4kB pages;The system only uses about 200kB for BPF programs, but 2MB is allocated for
bpf_prog_pack(for both A and B). Therefore, direct map fragmentation caused by BPF programs is elminated, and we are only measuring the performance difference of 1x 2MB page vs. ~50 4kB pages (we only use about 50 out of the 512 pages). For these two sides, the difference in system throughput is within the noise. I also measured iTLB-load-misses caused by bpf programs, which is ~300/s for case A, and ~1600/s for case B. The overall iTLB-load-misses is about 1.5M/s on these hosts. Therefore, we can clearly see 2MB page reduces iTLB misses, but the difference is not enough to have visible impact on system throughput.Of course, the impact of iTLB miss will be more significant for systems with more BPF programs loaded.
[1] https://lore.kernel.org/bpf/[email protected]/
Song Liu (5):
module: introducemodule_alloc_huge
bpf: usemodule_alloc_hugeforbpf_prog_pack
vmalloc: WARN forset_vm_flush_reset_perms()on huge pages
vmalloc: introducehuge_vmalloc_supported
bpf: simplifyselect_bpf_prog_pack_sizearch/x86/kernel/module.c | 21 +++++++++++++++++++++ include/linux/moduleloader.h | 5 +++++ include/linux/vmalloc.h | 7 +++++++ kernel/bpf/core.c | 25 ++++++++++--------------- kernel/module/main.c | 8 ++++++++ mm/vmalloc.c | 5 +++++ 6 files changed, 56 insertions(+), 15 deletions(-)--
2.30.2