if (!refcount_dec_and_test(&vml->mmap_count)) return;
guard(mutex)(&arena->lock); /* update link list under lock */
list_del(&vml->head);
vma->vm_private_data = NULL;
kfree(vml);
}
guard(mutex)(&arena->lock);
page = vmalloc_to_page((void *)kaddr); if (page) /* already have a page vmap-ed */ goto out;
if (arena->map.map_flags & BPF_F_SEGV_ON_FAULT) /* User space requested to segfault when page is not allocated by bpf prog */ return VM_FAULT_SIGSEGV;
ret = range_tree_clear(&arena->rt, vmf->pgoff, 1); if (ret) return VM_FAULT_SIGSEGV;
/* Account into memcg of the process that created bpf_arena */
ret = bpf_map_alloc_pages(map, NUMA_NO_NODE, 1, &page); if (ret) {
range_tree_set(&arena->rt, vmf->pgoff, 1); return VM_FAULT_SIGSEGV;
}
if (pgoff) return -EINVAL; if (len > SZ_4G) return -E2BIG;
/* if user_vm_start was specified at arena creation time */ if (arena->user_vm_start) { if (len > arena->user_vm_end - arena->user_vm_start) return -E2BIG; if (len != arena->user_vm_end - arena->user_vm_start) return -EINVAL; if (addr != arena->user_vm_start) return -EINVAL;
}
ret = mm_get_unmapped_area(current->mm, filp, addr, len * 2, 0, flags); if (IS_ERR_VALUE(ret)) return ret; if ((ret >> 32) == ((ret + len - 1) >> 32)) return ret; if (WARN_ON_ONCE(arena->user_vm_start)) /* checks at map creation time should prevent this */ return -EFAULT; return round_up(ret, SZ_4G);
}
static u64 clear_lo32(u64 val)
{ return val & ~(u64)~0U;
}
/* *Allocatepagesandvmapthemintokernelvmallocarea. *Laterthepageswillbemmapedintouserspacevma.
*/ staticlong arena_alloc_pages(struct bpf_arena *arena, long uaddr, long page_cnt, int node_id)
{ /* user_vm_end/start are fixed before bpf prog runs */ long page_cnt_max = (arena->user_vm_end - arena->user_vm_start) >> PAGE_SHIFT;
u64 kern_vm_start = bpf_arena_get_kern_vm_start(arena); struct page **pages; long pgoff = 0;
u32 uaddr32; int ret, i;
if (page_cnt > page_cnt_max) return0;
if (uaddr) { if (uaddr & ~PAGE_MASK) return0;
pgoff = compute_pgoff(arena, uaddr); if (pgoff > page_cnt_max - page_cnt) /* requested address will be outside of user VMA */ return0;
}
/* zeroing is needed, since alloc_pages_bulk() only fills in non-zero entries */
pages = kvcalloc(page_cnt, sizeof(struct page *), GFP_KERNEL); if (!pages) return0;
guard(mutex)(&arena->lock);
if (uaddr) {
ret = is_range_tree_set(&arena->rt, pgoff, page_cnt); if (ret) goto out_free_pages;
ret = range_tree_clear(&arena->rt, pgoff, page_cnt);
} else {
ret = pgoff = range_tree_find(&arena->rt, page_cnt); if (pgoff >= 0)
ret = range_tree_clear(&arena->rt, pgoff, page_cnt);
} if (ret) goto out_free_pages;
ret = bpf_map_alloc_pages(&arena->map, node_id, page_cnt, pages); if (ret) goto out;
uaddr32 = (u32)(arena->user_vm_start + pgoff * PAGE_SIZE); /* Earlier checks made sure that uaddr32 + page_cnt * PAGE_SIZE - 1 *willnotoverflow32-bit.Lower32-bitneedtorepresent *contiguoususeraddressrange. *Mapthesepagesatkern_vm_startbase. *kern_vm_start+uaddr32+page_cnt*PAGE_SIZE-1canoverflow *lower32-bitandit'sok.
*/
ret = vm_area_map_pages(arena->kern_vm, kern_vm_start + uaddr32,
kern_vm_start + uaddr32 + page_cnt * PAGE_SIZE, pages); if (ret) { for (i = 0; i < page_cnt; i++)
__free_page(pages[i]); goto out;
}
kvfree(pages); return clear_lo32(arena->user_vm_start) + uaddr32;
out:
range_tree_set(&arena->rt, pgoff, page_cnt);
out_free_pages:
kvfree(pages); return0;
}
/* *Ifpageispresentinvmallocarea,unmapitfromvmallocarea, *unmapitfromalluserspacevma-s, *andfreeit.
*/ staticvoid zap_pages(struct bpf_arena *arena, long uaddr, long page_cnt)
{ struct vma_list *vml;
if (page_cnt > 1) /* bulk zap if multiple pages being freed */
zap_pages(arena, full_uaddr, page_cnt);
kaddr = bpf_arena_get_kern_vm_start(arena) + uaddr; for (i = 0; i < page_cnt; i++, kaddr += PAGE_SIZE, full_uaddr += PAGE_SIZE) {
page = vmalloc_to_page((void *)kaddr); if (!page) continue; if (page_cnt == 1 && page_mapped(page)) /* mapped by some user process */ /* Optimization for the common case of page_cnt==1: *Ifpagewasn'tmappedintosomeuservmathere *isnoneedtocallzap_pageswhichisslow.When *page_cntisbigit'sfastertodothebatchedzap.
*/
zap_pages(arena, full_uaddr, 1);
vm_area_unmap_pages(arena->kern_vm, kaddr, kaddr + PAGE_SIZE);
__free_page(page);
}
}
/* *Reserveanarenavirtualaddressrangewithoutpopulatingit.Thiscallstops *bpf_arena_alloc_pagesfromaddingpagestothisrange.
*/ staticint arena_reserve_pages(struct bpf_arena *arena, long uaddr, u32 page_cnt)
{ long page_cnt_max = (arena->user_vm_end - arena->user_vm_start) >> PAGE_SHIFT; long pgoff; int ret;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.