Re: [PATCH v6 bpf-next 6/7] bpf: introduce bpf_prog_pack allocator

From: Song Liu
Date: Fri Jan 21 2022 - 19:23:32 EST




> On Jan 21, 2022, at 3:55 PM, Alexei Starovoitov <alexei.starovoitov@xxxxxxxxx> wrote:
>
> On Fri, Jan 21, 2022 at 11:49 AM Song Liu <song@xxxxxxxxxx> wrote:
>>
>> +static struct bpf_binary_header *
>> +__bpf_jit_binary_alloc(unsigned int proglen, u8 **image_ptr,
>> + unsigned int alignment,
>> + bpf_jit_fill_hole_t bpf_fill_ill_insns,
>> + u32 round_up_to)
>> +{
>> + struct bpf_binary_header *hdr;
>> + u32 size, hole, start;
>> +
>> + WARN_ON_ONCE(!is_power_of_2(alignment) ||
>> + alignment > BPF_IMAGE_ALIGNMENT);
>> +
>> + /* Most of BPF filters are really small, but if some of them
>> + * fill a page, allow at least 128 extra bytes to insert a
>> + * random section of illegal instructions.
>> + */
>> + size = round_up(proglen + sizeof(*hdr) + 128, round_up_to);
>> +
>> + if (bpf_jit_charge_modmem(size))
>> + return NULL;
>> + hdr = bpf_jit_alloc_exec(size);
>> + if (!hdr) {
>> + bpf_jit_uncharge_modmem(size);
>> + return NULL;
>> + }
>> +
>> + /* Fill space with illegal/arch-dep instructions. */
>> + bpf_fill_ill_insns(hdr, size);
>> +
>> + hdr->size = size;
>> + hole = min_t(unsigned int, size - (proglen + sizeof(*hdr)),
>> + PAGE_SIZE - sizeof(*hdr));
>
> It probably should be 'round_up_to' instead of PAGE_SIZE ?

Actually, some of these change is not longer needed after the following
change in v6:

4. Change fall back round_up_to in bpf_jit_binary_alloc_pack() from
BPF_PROG_MAX_PACK_PROG_SIZE to PAGE_SIZE.

My initial thought (last year) was if we allocate more than 2MB (either
2.1MB or 3.9MB), we round up to 4MB to save page table entries.
However, when I revisited this earlier today, I thought we should still
round up to PAGE_SIZE to save memory

Right now, I am not sure which way is better. What do you think? If we
round up to PAGE_SIZE, we don't need split out __bpf_jit_binary_alloc().

>
>> + start = (get_random_int() % hole) & ~(alignment - 1);
>> +
>> + /* Leave a random number of instructions before BPF code. */
>> + *image_ptr = &hdr->image[start];
>> +
>> + return hdr;
>> +}
>> +
>> struct bpf_binary_header *
>> bpf_jit_binary_alloc(unsigned int proglen, u8 **image_ptr,
>> unsigned int alignment,
>> bpf_jit_fill_hole_t bpf_fill_ill_insns)
>> +{
>> + return __bpf_jit_binary_alloc(proglen, image_ptr, alignment,
>> + bpf_fill_ill_insns, PAGE_SIZE);
>> +}
>> +
>> +struct bpf_binary_header *
>> +bpf_jit_binary_alloc_pack(unsigned int proglen, u8 **image_ptr,
>> + unsigned int alignment,
>> + bpf_jit_fill_hole_t bpf_fill_ill_insns)
>> {
>> struct bpf_binary_header *hdr;
>> u32 size, hole, start;
>> @@ -875,11 +1034,16 @@ bpf_jit_binary_alloc(unsigned int proglen, u8 **image_ptr,
>> * fill a page, allow at least 128 extra bytes to insert a
>> * random section of illegal instructions.
>> */
>> - size = round_up(proglen + sizeof(*hdr) + 128, PAGE_SIZE);
>> + size = round_up(proglen + sizeof(*hdr) + 128, BPF_PROG_CHUNK_SIZE);
>> +
>> + /* for too big program, use __bpf_jit_binary_alloc. */
>> + if (size > BPF_PROG_MAX_PACK_PROG_SIZE)
>> + return __bpf_jit_binary_alloc(proglen, image_ptr, alignment,
>> + bpf_fill_ill_insns, PAGE_SIZE);
>>
>> if (bpf_jit_charge_modmem(size))
>> return NULL;
>> - hdr = bpf_jit_alloc_exec(size);
>> + hdr = bpf_prog_pack_alloc(size);
>> if (!hdr) {
>> bpf_jit_uncharge_modmem(size);
>> return NULL;
>> @@ -888,9 +1052,8 @@ bpf_jit_binary_alloc(unsigned int proglen, u8 **image_ptr,
>> /* Fill space with illegal/arch-dep instructions. */
>> bpf_fill_ill_insns(hdr, size);
>>
>> - hdr->size = size;
>
> I'm missing where it's assigned.
> Looks like hdr->size stays zero, so free is never performed?

This is read only memory, so we set it in bpf_fill_ill_insns(). There was a
comment in x86/bpf_jit_comp.c. I guess we also need a comment here.

>
>> hole = min_t(unsigned int, size - (proglen + sizeof(*hdr)),
>> - PAGE_SIZE - sizeof(*hdr));
>> + BPF_PROG_CHUNK_SIZE - sizeof(*hdr));
>
> Before this change size - (proglen + sizeof(*hdr)) would
> be at least 128 and potentially bigger than PAGE_SIZE
> when extra 128 crossed page boundary.
> Hence min() was necessary with the 2nd arg being PAGE_SIZE - sizeof(*hdr).
>
> With new code size - (proglen + sizeof(*hdr)) would
> be between 128 and 128+64
> while BPF_PROG_CHUNK_SIZE - sizeof(*hdr) is a constant less than 64.
> What is the point of min() ?

Yeah, I guess I didn't finish my math homework here. Will fix it in the
next version.