bpf_prog_pack followup
Subsystems (7)
Song Liu
reviewerv4 cover bpf_prog_pack followup
Changes v3 => v4:
1. Shorten CC list on 4/8, so it is not dropped by the mail list.
Changes v2 => v3:
1. Fix issues reported by kernel test robot <[email protected]>.
Changes v1 => v2:
1. Add WARN to set_vm_flush_reset_perms() on huge pages. (Rick Edgecombe)
2. Simplify select_bpf_prog_pack_size. (Rick Edgecombe)
As of 5.18-rc6, x86_64 uses bpf_prog_pack on 4kB pages. This set contains a few followups:
1/8 - 3/8 fills unused part of bpf_prog_pack with illegal instructions. 4/8 - 5/8 enables bpf_prog_pack on 2MB pages.
The primary goal of bpf_prog_pack is to reduce iTLB miss rate and reduce direct memory mapping fragmentation. This leads to non-trivial performance improvements.
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.
bpf_prog_pack on 2MB pages may use slightly more memory for systems without many BPF programs. However, such waste in memory (<2MB) is within noisy for modern x86_64 systems.
Song Liu (8):
bpf: fill new bpf_prog_pack with illegal instructions x86/alternative: introduce text_poke_set
bpf: introduce bpf_arch_text_invalidate for bpf_prog_pack
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/include/asm/text-patching.h | 1 + arch/x86/kernel/alternative.c | 67 +++++++++++++++++++++++----- arch/x86/kernel/module.c | 21 +++++++++ arch/x86/net/bpf_jit_comp.c | 5 +++ include/linux/bpf.h | 1 + include/linux/moduleloader.h | 5 +++ include/linux/vmalloc.h | 7 +++ kernel/bpf/core.c | 43 ++++++++++-------- kernel/module.c | 8 ++++ mm/vmalloc.c | 5 +++ 10 files changed, 134 insertions(+), 29 deletions(-)
--
2.30.2
Song Liu
reviewerPATCH v4 1/8 bpf: fill new bpf_prog_pack with illegal instructions
bpf_prog_pack enables sharing huge pages among multiple BPF programs. These pages are marked as executable before the JIT engine fill it with BPF programs. To make these pages safe, fill the hole bpf_prog_pack with illegal instructions before making it executable.
--- kernel/bpf/core.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
kernel/bpf/core.c +6 -4
@@ -873,7 +873,7 @@ static size_t select_bpf_prog_pack_size(void)return size; } -static struct bpf_prog_pack *alloc_new_pack (void ) +static struct bpf_prog_pack *alloc_new_pack (bpf_jit_fill_hole_t bpf_fill_ill_insns) {struct bpf_prog_pack *pack;
@@ -886,6 +886,7 @@ static struct bpf_prog_pack *alloc_new_pack(void)kfree (pack);return NULL ; } +bpf_fill_ill_insns (pack->ptr, bpf_prog_pack_size);bitmap_zero (pack->bitmap, bpf_prog_pack_size /BPF_PROG_CHUNK_SIZE );list_add_tail (&pack->list, &pack_list);
@@ -895,7 +896,7 @@ static struct bpf_prog_pack *alloc_new_pack(void)return pack; } -static void *bpf_prog_pack_alloc (u32 size) +static void *bpf_prog_pack_alloc (u32 size,bpf_jit_fill_hole_t bpf_fill_ill_insns) {unsigned int nbits =BPF_PROG_SIZE_TO_NBITS (size);struct bpf_prog_pack *pack;
@@ -910,6 +911,7 @@ static void *bpf_prog_pack_alloc(u32 size) size =round_up (size,PAGE_SIZE ); ptr =module_alloc (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 );
@@ -923,7 +925,7 @@ static void *bpf_prog_pack_alloc(u32 size) goto found_free_area; } - pack =alloc_new_pack (); + pack =alloc_new_pack (bpf_fill_ill_insns);if (!pack) goto out;
@@ -1102,7 +1104,7 @@ bpf_jit_binary_pack_alloc(unsigned int proglen, u8 **image_ptr,if (bpf_jit_charge_modmem (size))return NULL ; - ro_header =bpf_prog_pack_alloc (size); + ro_header =bpf_prog_pack_alloc (size, bpf_fill_ill_insns);if (!ro_header) {bpf_jit_uncharge_modmem (size);return NULL ;
Signature · 1 line
2.30.2
Song Liu
reviewerPATCH v4 2/8 x86/alternative: introduce text_poke_set
In reply to Song LiuIntroduce a memset like API for text_poke. This will be used to fill the unused RX memory with illegal instructions.
--- arch/x86/include/asm/text-patching.h | 1 + arch/x86/kernel/alternative.c | 67 +++++++++++++++++++++++----- 2 files changed, 58 insertions(+), 10 deletions(-)
arch/x86/include/asm/text-patching.h +1 -0
@@ -45,6 +45,7 @@ extern void *text_poke(void *addr, const void *opcode, size_t len);extern void text_poke_sync (void );extern void *text_poke_kgdb (void *addr,const void *opcode,size_t len);extern void *text_poke_copy (void *addr,const void *opcode,size_t len); +extern void *text_poke_set (void *addr,int c,size_t len);extern int poke_int3_handler (struct pt_regs *regs);extern void text_poke_bp (void *addr,const void *opcode,size_t len,const void *emulate);
arch/x86/kernel/alternative.c +57 -10
103 lines not on this page · raw message
Signature · 1 line
2.30.2
Song Liu
reviewerPATCH v4 3/8 bpf: introduce bpf_arch_text_invalidate for bpf_prog_pack
In reply to Song LiuIntroduce bpf_arch_text_invalidate and use it to fill unused part of the bpf_prog_pack with illegal instructions when a BPF program is freed.
--- arch/x86/net/bpf_jit_comp.c | 5 +++++ include/linux/bpf.h | 1 + kernel/bpf/core.c | 8 ++++++++ 3 files changed, 14 insertions(+)
arch/x86/net/bpf_jit_comp.c +5 -0
@@ -228,6 +228,11 @@ static void jit_fill_hole(void *area, unsigned int size)memset (area,0xcc , size); } +int bpf_arch_text_invalidate (void *dst,size_t len) +{ +return IS_ERR_OR_NULL (text_poke_set (dst,0xcc , len)); +} +struct jit_context {int cleanup_addr;/* Epilogue code offset */
include/linux/bpf.h +1 -0
@@ -2365,6 +2365,7 @@ int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type t, void *addr1, void *addr2);void *bpf_arch_text_copy (void *dst,void *src,size_t len); +int bpf_arch_text_invalidate (void *dst,size_t len);struct btf_id_set ;bool btf_id_set_contains (const struct btf_id_set *set,u32 id);
kernel/bpf/core.c +8 -0
@@ -968,6 +968,9 @@ static void bpf_prog_pack_free(struct bpf_binary_header *hdr) nbits =BPF_PROG_SIZE_TO_NBITS (hdr->size); pos = ((unsigned long )hdr - (unsigned long )pack_ptr) >>BPF_PROG_CHUNK_SHIFT ; +WARN_ONCE (bpf_arch_text_invalidate (hdr, hdr->size), +"bpf_prog_pack bug: missing bpf_arch_text_invalidate?\n" ); +bitmap_clear (pack->bitmap, pos, nbits);if (bitmap_find_next_zero_area (pack->bitmap,bpf_prog_chunk_count (),0 ,bpf_prog_chunk_count (),0 ) ==0 ) {
@@ -2740,6 +2743,11 @@ void * __weak bpf_arch_text_copy(void *dst, void *src, size_t len)return ERR_PTR (-ENOTSUPP ); } +int __weak bpf_arch_text_invalidate (void *dst,size_t len) +{ +return -ENOTSUPP ; +} +DEFINE_STATIC_KEY_FALSE (bpf_stats_enabled_key);EXPORT_SYMBOL (bpf_stats_enabled_key);
Signature · 1 line
2.30.2
Re: [PATCH v4 bpf-next 3/8] bpf: introduce bpf_arch_text_invalidate for bpf_prog_pack
Quoted 43 lines · Song Liu
Introduce
bpf_arch_text_invalidateand use it to fill unused part of thebpf_prog_packwith illegal instructions when a BPF program is freed.Signed-off-by Song Liu <[email protected]>--- arch/x86/net/bpf_jit_comp.c | 5 +++++ include/linux/bpf.h | 1 + kernel/bpf/core.c | 8 ++++++++ 3 files changed, 14 insertions(+)arch/x86/net/bpf_jit_comp.c +5
-0@@ -228,6 +228,11 @@ static void jit_fill_hole(void *area, unsigned int size)memset (area,0xcc , size); } +int bpf_arch_text_invalidate (void *dst,size_t len) +{ +return IS_ERR_OR_NULL (text_poke_set (dst,0xcc , len)); +} +struct jit_context {int cleanup_addr;/* Epilogue code offset */ include/linux/bpf.h +1
-0@@ -2365,6 +2365,7 @@ int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type t, void *addr1, void *addr2);void *bpf_arch_text_copy (void *dst,void *src,size_t len); +int bpf_arch_text_invalidate (void *dst,size_t len);struct btf_id_set ;bool btf_id_set_contains (const struct btf_id_set *set,u32 id);kernel/bpf/core.c +0
-0
kernel/bpf/core.c static void bpf_prog_pack_free(struct bpf_binary_header *hdr) · line 972
Quoted 5 lines
kernel/bpf/core.c +2
-0@@ -968,6 +968,9 @@ static void bpf_prog_pack_free(struct bpf_binary_header *hdr) nbits =BPF_PROG_SIZE_TO_NBITS (hdr->size); pos = ((unsigned long )hdr - (unsigned long )pack_ptr) >>BPF_PROG_CHUNK_SHIFT ; +WARN_ONCE (bpf_arch_text_invalidate (hdr, hdr->size), +"bpf_prog_pack bug: missing bpf_arch_text_invalidate?\n" );
Why is this a WARN?
What happens if bpf_arch_text_invalidate() is implemented but returns an error?
Quoted 20 lines
kernel/bpf/core.c +6
-0+bitmap_clear (pack->bitmap, pos, nbits);if (bitmap_find_next_zero_area (pack->bitmap,bpf_prog_chunk_count (),0 ,bpf_prog_chunk_count (),0 ) ==0 ) {@@ -2740,6 +2743,11 @@ void * __weak bpf_arch_text_copy(void *dst, void *src, size_t len)return ERR_PTR (-ENOTSUPP ); } +int __weak bpf_arch_text_invalidate (void *dst,size_t len) +{ +return -ENOTSUPP ; +} +DEFINE_STATIC_KEY_FALSE (bpf_stats_enabled_key);EXPORT_SYMBOL (bpf_stats_enabled_key);Signature · 1 line
2.30.2
Signature · 2 lines
Sincerely yours, Mike.
Song Liu
reviewerPATCH v4 4/8 module: introduce module_alloc_huge
In reply to Song LiuIntroduce 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).
---
Note: This conflicts with the module.c => module/ split in modules-next. Current patch is for module.c in the bpf-next tree. After the split, __weak module_alloc_huge() should be added to kernel/module/main.c.
--- arch/x86/kernel/module.c | 21 +++++++++++++++++++++ include/linux/moduleloader.h | 5 +++++ kernel/module.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.c +8 -0
@@ -2839,6 +2839,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
Song Liu
reviewerPATCH v4 5/8 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
Re: [PATCH v4 bpf-next 5/8] bpf: use module_alloc_huge for bpf_prog_pack
Quoted 60 lines · Song Liu
Use
module_alloc_hugeforbpf_prog_packso that BPF programs sit onPMD_SIZEpages. 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 ofPAGE_SIZEbpf_prog_pack(which improve system throughput by ~0.5%).Also, remove
set_vm_flush_reset_perms()fromalloc_new_pack()and use set_memory_[nx|rw] inbpf_prog_pack_free(). This is becauseVM_FLUSH_RESET_PERMSdoes not work with huge pages yet. [1][1] https://lore.kernel.org/bpf/[email protected]/
Suggested-by Rick Edgecombe <[email protected]>Signed-off-by Song Liu <[email protected]>--- kernel/bpf/core.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-)kernel/bpf/core.c +3
-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 ); }
kernel/bpf/core.c static void bpf_prog_pack_free(struct bpf_binary_header *hdr) · line 951
Quoted 5 lines
kernel/bpf/core.c +2
-0@@ -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 );
set_memory_{nx,rw} can fail. Please take care of error handling.
kernel/bpf/core.c static void bpf_prog_pack_free(struct bpf_binary_header *hdr) · line 979
Quoted 9 lines
kernel/bpf/core.c +2
-0module_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 );
ditto.
Quoted 7 lines
kernel/bpf/core.c +0
-0module_memfree (pack->ptr);kfree (pack); }Signature · 1 line
2.30.2
Signature · 2 lines
Sincerely yours, Mike.
Re: [PATCH v4 bpf-next 5/8] bpf: use module_alloc_huge for bpf_prog_pack
Quoted 11 lines · Mike Rapoport
Review static void bpf_prog_pack_free(struct · line 954
Quoted 9 lines
(quoted diff) +2
-0@@ -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);set_memory_{nx,rw} can fail. Please take care of error handling.
I think there is nothing to do here, actually. At least on the freeing part.
When called on a vmalloc primary address, the set_memory() calls will try to first change the permissions on the vmalloc alias, then try to change it on the direct map. Yea, it can fail from failing to allocate a page from a split, but whatever memory managed to change earlier will also succeed to change back on reset. The set_memory() functions don't rollback on failure. So all the modules callers depend on this logic of a second resetting call to reset anything that succeeded to change on the first call. The split will have already happened for any memory that succeeded to change, and the order of the changes is the same.
As for the primary alias, for 4k vmallocs, it will always succeed, and set_memory() does this part first, so set_memory_x() will (crucially) always succeed for kernel text mappings. For 2MB vmalloc pages, the primary alias should succeed if the set_memory() call is 2MB aligned.
So pre-VM_FLUSH_RESET_PERMS (and it also has pretty similar logic), they all went something like this:
Allocate:
1. ptr = vmalloc();
2. set_memory_ro(ptr); <-Could fail halfway though
Free:
3. set_memory_rw(ptr); <-Could also fail halfway though and return an error, but only after the split work done in step 2.
4. vfree(ptr);
It's pretty horrible. BPF people once tried to be more proper and change it to:
ptr = vmalloc()
if (set_memory_ro())
{
vfree(ptr);
}It looks correct, but this caused RO pages to be freed if set_memory_ro() half succeeded in changing permissions. If there is an error on reset, it means the first set_memory() call failed to change everything, but anything that succeeded is reset. So the right thing to do is to free the pages in either case.
We could fail to allocate a pack if the initial set_memory() calls failed, but we still have to call set_memory_rw(), etc on free and ignore any errors.
As an aside, this is one of the reasons it's hard to improve things incrementally here. Everything is carefully balanced like a house of cards. The solution IMO, is to change the interface so this type of logic is in one place instead of scattered in the callers.
Re: [PATCH v4 bpf-next 5/8] bpf: use module_alloc_huge for bpf_prog_pack
In reply to Song LiuQuoted 8 lines · Song Liu
Use
module_alloc_hugeforbpf_prog_packso that BPF programs sit onPMD_SIZEpages. 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 ofPAGE_SIZEbpf_prog_pack(which improve system throughput by ~0.5%).
Quoted 8 lines
Also, remove
set_vm_flush_reset_perms()fromalloc_new_pack()and use set_memory_[nx|rw] inbpf_prog_pack_free(). This is becauseVM_FLUSH_RESET_PERMSdoes not work with huge pages yet. [1][1]
https://lore.kernel.org/bpf/[email protected]/Suggested-by Rick Edgecombe <[email protected]>Signed-off-by Song Liu <[email protected]>
Hi,
Per request, just wanted to make it clear I'm personally on board with side-stepping VM_FLUSH_RESET_PERMS here because the way things are headed it may change anyway, and this already does a better job at minimizing eBPF JIT kernel shootdowns than VM_FLUSH_RESET_PERMS did when it was introduced. The warning added in the other patch will prevent accidents like the first version of the series. So that aspect seems ok to me.
In general I think this series still needs more x86/mm scrutiny though.
Thanks,
Rick
Song Liu
reviewerPATCH v4 6/8 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
@@ -238,6 +238,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 v4 7/8 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
@@ -242,11 +242,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 v4 8/8 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
This series was applied to bpf/bpf-next.git (master) by Daniel Borkmann <[email protected]>:
Hello:
This series was applied to bpf/bpf-next.git (master) by Daniel Borkmann <[email protected]>:
Quoted 11 lines · On Fri, 20 May 2022 16:57:50 -0700 you wrote:
Changes v3 => v4:
1. Shorten CC list on 4/8, so it is not dropped by the mail list.Changes v2 => v3:
1. Fix issues reported by kernel test robot <[email protected]>.Changes v1 => v2:
1. Add WARN toset_vm_flush_reset_perms()on huge pages. (Rick Edgecombe)
2. Simplifyselect_bpf_prog_pack_size. (Rick Edgecombe)[...]
Here is the summary with links:
- [v4,bpf-next,1/8] bpf: fill new bpf_prog_pack with illegal instructions
https://git.kernel.org/bpf/bpf-next/c/d88bb5eed04c - [v4,bpf-next,2/8] x86/alternative: introduce text_poke_set https://git.kernel.org/bpf/bpf-next/c/aadd1b678ebe - [v4,bpf-next,3/8] bpf: introduce bpf_arch_text_invalidate for bpf_prog_pack https://git.kernel.org/bpf/bpf-next/c/fe736565efb7
- [v4,bpf-next,4/8] module: introduce module_alloc_huge
(no matching commit)
- [v4,bpf-next,5/8] bpf: use module_alloc_huge for bpf_prog_pack (no matching commit)
- [v4,bpf-next,6/8] vmalloc: WARN for set_vm_flush_reset_perms() on huge pages (no matching commit)
- [v4,bpf-next,7/8] vmalloc: introduce huge_vmalloc_supported
(no matching commit)
- [v4,bpf-next,8/8] bpf: simplify select_bpf_prog_pack_size
(no matching commit)
You are awesome, thank you!
Signature · 2 lines
Deet-doot-dot, I am a bot. https://korg.docs.kernel.org/patchwork/pwbot.html
Hi Song,
On Fri, May 20, 2022 at 04:57:50PM -0700, Song Liu wrote:
... ...
Quoted 11 lines
The primary goal of
bpf_prog_packis to reduce iTLB miss rate and reduce direct memory mapping fragmentation. This leads to non-trivial performance improvements.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.
I'm evaluationg performance impact due to direct memory mapping fragmentation and seeing the above, I wonder: is the performance improve mostly due to prog pack and hugepage instead of less direct mapping fragmentation?
I can understand that when progs are packed together, iTLB miss rate will be reduced and thus, performance can be improved. But I don't see immediately how direct mapping fragmentation can impact performance since the bpf code are running from the module alias addresses, not the direct mapping addresses IIUC?
I appreciate it if you can shed some light on performance impact direct mapping fragmentation can cause, thanks.
Song Liu
reviewerHi Aaron,
Quoted 24 lines · Aaron Lu
Hi Song,
On Fri, May 20, 2022 at 04:57:50PM -0700, Song Liu wrote:
... ...
Quoted 11 lines
The primary goal of
bpf_prog_packis to reduce iTLB miss rate and reduce direct memory mapping fragmentation. This leads to non-trivial performance improvements.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.I'm evaluationg performance impact due to direct memory mapping fragmentation and seeing the above, I wonder: is the performance improve mostly due to prog pack and hugepage instead of less direct mapping fragmentation?
Quoted 5 lines
I can understand that when progs are packed together, iTLB miss rate will be reduced and thus, performance can be improved. But I don't see immediately how direct mapping fragmentation can impact performance since the bpf code are running from the module alias addresses, not the direct mapping addresses IIUC?
You are right that BPF code runs from module alias addresses. However, to protect text from overwrites, we use set_memory_x() and set_memory_ro() for the BPF code. These two functions will set permissions for all aliases of the memory, including the direct map, and thus cause fragmentation of the direct map. Does this make sense?
Thanks,
Song
Quoted 33 lines · Song Liu
Hi Aaron,
Quoted 24 lines · Aaron Lu
Hi Song,
On Fri, May 20, 2022 at 04:57:50PM -0700, Song Liu wrote:
... ...
Quoted 11 lines
The primary goal of
bpf_prog_packis to reduce iTLB miss rate and reduce direct memory mapping fragmentation. This leads to non-trivial performance improvements.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.I'm evaluationg performance impact due to direct memory mapping fragmentation and seeing the above, I wonder: is the performance improve mostly due to prog pack and hugepage instead of less direct mapping fragmentation?
Quoted 5 lines
I can understand that when progs are packed together, iTLB miss rate will be reduced and thus, performance can be improved. But I don't see immediately how direct mapping fragmentation can impact performance since the bpf code are running from the module alias addresses, not the direct mapping addresses IIUC?
Quoted 5 lines
You are right that BPF code runs from module alias addresses. However, to protect text from overwrites, we use
set_memory_x()andset_memory_ro()for the BPF code. These two functions will set permissions for all aliases of the memory, including the direct map, and thus cause fragmentation of the direct map. Does this make sense?
Guess I didn't make it clear.
I understand that set_memory_XXX() will cause direct mapping split and thus, fragmented. What is not clear to me is, how much impact does direct mapping fragmentation have on performance, in your case and in general?
In your case, I guess the performance gain is due to code gets packed together and iTLB gets reduced. When code are a lot, packing them together as a hugepage is a further gain. In the meantime, direct mapping split (or not) seems to be a side effect of this packing, but it doesn't have a direct impact on performance.
One thing I can imagine is, when an area of direct mapping gets splited due to permission reason, when that reason is gone(like module unload or bpf code unload), those areas will remain fragmented and that can cause later operations that touch these same areas using more dTLBs and that can be bad for performance, but it's hard to say how much impact this can cause though.
Regards,
Aaron
Song Liu
reviewerQuoted 54 lines · Aaron Lu
Quoted 33 lines · Song Liu
Hi Aaron,
Quoted 24 lines · Aaron Lu
Hi Song,
On Fri, May 20, 2022 at 04:57:50PM -0700, Song Liu wrote:
... ...
Quoted 11 lines
The primary goal of
bpf_prog_packis to reduce iTLB miss rate and reduce direct memory mapping fragmentation. This leads to non-trivial performance improvements.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.I'm evaluationg performance impact due to direct memory mapping fragmentation and seeing the above, I wonder: is the performance improve mostly due to prog pack and hugepage instead of less direct mapping fragmentation?
Quoted 5 lines
I can understand that when progs are packed together, iTLB miss rate will be reduced and thus, performance can be improved. But I don't see immediately how direct mapping fragmentation can impact performance since the bpf code are running from the module alias addresses, not the direct mapping addresses IIUC?
Quoted 5 lines
You are right that BPF code runs from module alias addresses. However, to protect text from overwrites, we use
set_memory_x()andset_memory_ro()for the BPF code. These two functions will set permissions for all aliases of the memory, including the direct map, and thus cause fragmentation of the direct map. Does this make sense?Guess I didn't make it clear.
I understand that
set_memory_XXX()will cause direct mapping split and thus, fragmented. What is not clear to me is, how much impact does direct mapping fragmentation have on performance, in your case and in general?In your case, I guess the performance gain is due to code gets packed together and iTLB gets reduced. When code are a lot, packing them together as a hugepage is a further gain. In the meantime, direct mapping split (or not) seems to be a side effect of this packing, but it doesn't have a direct impact on performance.
Quoted 6 lines
One thing I can imagine is, when an area of direct mapping gets splited due to permission reason, when that reason is gone(like module unload or bpf code unload), those areas will remain fragmented and that can cause later operations that touch these same areas using more dTLBs and that can be bad for performance, but it's hard to say how much impact this can cause though.
Yes, we have data showing the direct mapping remaining fragmented can cause non-trivial performance degradation. For our web workload, the difference is in the order of 1%.
Thanks,
Song
Quoted 62 lines · Song Liu
Quoted 54 lines · Aaron Lu
Quoted 33 lines · Song Liu
Hi Aaron,
Quoted 24 lines · Aaron Lu
Hi Song,
On Fri, May 20, 2022 at 04:57:50PM -0700, Song Liu wrote:
... ...
Quoted 11 lines
The primary goal of
bpf_prog_packis to reduce iTLB miss rate and reduce direct memory mapping fragmentation. This leads to non-trivial performance improvements.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.I'm evaluationg performance impact due to direct memory mapping fragmentation and seeing the above, I wonder: is the performance improve mostly due to prog pack and hugepage instead of less direct mapping fragmentation?
Quoted 5 lines
I can understand that when progs are packed together, iTLB miss rate will be reduced and thus, performance can be improved. But I don't see immediately how direct mapping fragmentation can impact performance since the bpf code are running from the module alias addresses, not the direct mapping addresses IIUC?
Quoted 5 lines
You are right that BPF code runs from module alias addresses. However, to protect text from overwrites, we use
set_memory_x()andset_memory_ro()for the BPF code. These two functions will set permissions for all aliases of the memory, including the direct map, and thus cause fragmentation of the direct map. Does this make sense?Guess I didn't make it clear.
I understand that
set_memory_XXX()will cause direct mapping split and thus, fragmented. What is not clear to me is, how much impact does direct mapping fragmentation have on performance, in your case and in general?In your case, I guess the performance gain is due to code gets packed together and iTLB gets reduced. When code are a lot, packing them together as a hugepage is a further gain. In the meantime, direct mapping split (or not) seems to be a side effect of this packing, but it doesn't have a direct impact on performance.
Quoted 6 lines
One thing I can imagine is, when an area of direct mapping gets splited due to permission reason, when that reason is gone(like module unload or bpf code unload), those areas will remain fragmented and that can cause later operations that touch these same areas using more dTLBs and that can be bad for performance, but it's hard to say how much impact this can cause though.
Quoted 3 lines
Yes, we have data showing the direct mapping remaining fragmented can cause non-trivial performance degradation. For our web workload, the difference is in the order of 1%.
Many thanks for the info, really appreciate it.
Regards,
Aaron
Luis Chamberlain
maintainerQuoted 18 lines · Aaron Lu
Hi Song,
On Fri, May 20, 2022 at 04:57:50PM -0700, Song Liu wrote:
... ...
Quoted 11 lines
The primary goal of
bpf_prog_packis to reduce iTLB miss rate and reduce direct memory mapping fragmentation. This leads to non-trivial performance improvements.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.
Quoted 2 lines
I'm evaluationg performance impact due to direct memory mapping fragmentation
BTW how exactly are you doing this?
Luis
Quoted 12 lines
and seeing the above, I wonder: is the performance improve mostly due to prog pack and hugepage instead of less direct mapping fragmentation?
I can understand that when progs are packed together, iTLB miss rate will be reduced and thus, performance can be improved. But I don't see immediately how direct mapping fragmentation can impact performance since the bpf code are running from the module alias addresses, not the direct mapping addresses IIUC?
I appreciate it if you can shed some light on performance impact direct mapping fragmentation can cause, thanks.
Quoted 19 lines · Luis Chamberlain
Quoted 18 lines · Aaron Lu
Hi Song,
On Fri, May 20, 2022 at 04:57:50PM -0700, Song Liu wrote:
... ...
Quoted 11 lines
The primary goal of
bpf_prog_packis to reduce iTLB miss rate and reduce direct memory mapping fragmentation. This leads to non-trivial performance improvements.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.
Quoted 4 lines
Quoted 2 lines
I'm evaluationg performance impact due to direct memory mapping fragmentation
BTW how exactly are you doing this?
Right now I'm mostly collecting materials from the web :-)
Zhengjun has run some extensive microbenmarks with different page size for direct mapping and on different server machines a while ago, here is his report:
https://lore.kernel.org/linux-mm/[email protected]/
Quoting part of the conclusion:
"
This leads us to conclude that although 1G mappings are a good default choice, there is no compelling evidence that it must be the only choice, or that folks deriving benefits (like hardening) from smaller mapping sizes should avoid the smaller mapping sizes. "
I searched the archive and found there is performance problem when kernel text huge mapping gets splitted:
https://lore.kernel.org/lkml/[email protected]/
But I haven't found a report complaining direct mapping fragmentation yet.