staticvoid *io_mem_alloc_compound(struct page **pages, int nr_pages,
size_t size, gfp_t gfp)
{ struct page *page; int i, order;
order = get_order(size); if (order > MAX_PAGE_ORDER) return ERR_PTR(-ENOMEM); elseif (order)
gfp |= __GFP_COMP;
page = alloc_pages(gfp, order); if (!page) return ERR_PTR(-ENOMEM);
for (i = 0; i < nr_pages; i++)
pages[i] = page + i;
return page_address(page);
}
struct page **io_pin_pages(unsignedlong uaddr, unsignedlong len, int *npages)
{ unsignedlong start, end, nr_pages; struct page **pages; int ret;
if (check_add_overflow(uaddr, len, &end)) return ERR_PTR(-EOVERFLOW); if (check_add_overflow(end, PAGE_SIZE - 1, &end)) return ERR_PTR(-EOVERFLOW);
end = end >> PAGE_SHIFT;
start = uaddr >> PAGE_SHIFT;
nr_pages = end - start; if (WARN_ON_ONCE(!nr_pages)) return ERR_PTR(-EINVAL); if (WARN_ON_ONCE(nr_pages > INT_MAX)) return ERR_PTR(-EOVERFLOW);
ret = pin_user_pages_fast(uaddr, nr_pages, FOLL_WRITE | FOLL_LONGTERM,
pages); /* success, mapped all pages */ if (ret == nr_pages) {
*npages = nr_pages; return pages;
}
/* partial map, or didn't map anything */ if (ret >= 0) { /* if we did partial map, release any pages we did get */ if (ret)
unpin_user_pages(pages, ret);
ret = -EFAULT;
}
kvfree(pages); return ERR_PTR(ret);
}
enum { /* memory was vmap'ed for the kernel, freeing the region vunmap's it */
IO_REGION_F_VMAP = 1, /* memory is provided by user and pinned by the kernel */
IO_REGION_F_USER_PROVIDED = 2, /* only the first page in the array is ref'ed */
IO_REGION_F_SINGLE_REF = 4,
};
void io_free_region(struct io_ring_ctx *ctx, struct io_mapped_region *mr)
{ if (mr->pages) { long nr_refs = mr->nr_pages;
if (mr->flags & IO_REGION_F_SINGLE_REF)
nr_refs = 1;
if (mr->flags & IO_REGION_F_USER_PROVIDED)
unpin_user_pages(mr->pages, nr_refs); else
release_pages(mr->pages, nr_refs);
kvfree(mr->pages);
} if ((mr->flags & IO_REGION_F_VMAP) && mr->ptr)
vunmap(mr->ptr); if (mr->nr_pages && ctx->user)
__io_unaccount_mem(ctx->user, mr->nr_pages);
int io_create_region(struct io_ring_ctx *ctx, struct io_mapped_region *mr, struct io_uring_region_desc *reg, unsignedlong mmap_offset)
{ int nr_pages, ret;
u64 end;
if (WARN_ON_ONCE(mr->pages || mr->ptr || mr->nr_pages)) return -EFAULT; if (memchr_inv(®->__resv, 0, sizeof(reg->__resv))) return -EINVAL; if (reg->flags & ~IORING_MEM_REGION_TYPE_USER) return -EINVAL; /* user_addr should be set IFF it's a user memory backed region */ if ((reg->flags & IORING_MEM_REGION_TYPE_USER) != !!reg->user_addr) return -EFAULT; if (!reg->size || reg->mmap_offset || reg->id) return -EINVAL; if ((reg->size >> PAGE_SHIFT) > INT_MAX) return -E2BIG; if ((reg->user_addr | reg->size) & ~PAGE_MASK) return -EINVAL; if (check_add_overflow(reg->user_addr, reg->size, &end)) return -EOVERFLOW;
nr_pages = reg->size >> PAGE_SHIFT; if (ctx->user) {
ret = __io_account_mem(ctx->user, nr_pages); if (ret) return ret;
}
mr->nr_pages = nr_pages;
if (reg->flags & IORING_MEM_REGION_TYPE_USER)
ret = io_region_pin_pages(ctx, mr, reg); else
ret = io_region_allocate_pages(ctx, mr, reg, mmap_offset); if (ret) goto out_free;
ret = io_region_init_ptr(mr); if (ret) goto out_free; return 0;
out_free:
io_free_region(ctx, mr); return ret;
}
int io_create_region_mmap_safe(struct io_ring_ctx *ctx, struct io_mapped_region *mr, struct io_uring_region_desc *reg, unsignedlong mmap_offset)
{ struct io_mapped_region tmp_mr; int ret;
memcpy(&tmp_mr, mr, sizeof(tmp_mr));
ret = io_create_region(ctx, &tmp_mr, reg, mmap_offset); if (ret) return ret;
/* * Once published mmap can find it without holding only the ->mmap_lock * and not ->uring_lock.
*/
guard(mutex)(&ctx->mmap_lock);
memcpy(mr, &tmp_mr, sizeof(tmp_mr)); return 0;
}
/* * Do not allow to map to user-provided address to avoid breaking the * aliasing rules. Userspace is not able to guess the offset address of * kernel kmalloc()ed memory area.
*/ if (addr) return -EINVAL;
guard(mutex)(&ctx->mmap_lock);
ptr = io_uring_validate_mmap_request(filp, pgoff, len); if (IS_ERR(ptr)) return -ENOMEM;
/* * Some architectures have strong cache aliasing requirements. * For such architectures we need a coherent mapping which aliases * kernel memory *and* userspace memory. To achieve that: * - use a NULL file pointer to reference physical memory, and * - use the kernel virtual address of the shared io_uring context * (instead of the userspace-provided address, which has to be 0UL * anyway). * - use the same pgoff which the get_unmapped_area() uses to * calculate the page colouring. * For architectures without such aliasing requirements, the * architecture will return any suitable mapping because addr is 0.
*/
filp = NULL;
flags |= MAP_SHARED;
pgoff = 0; /* has been translated to ptr above */ #ifdef SHM_COLOUR
addr = (uintptr_t) ptr;
pgoff = addr >> PAGE_SHIFT; #else
addr = 0UL; #endif return mm_get_unmapped_area(current->mm, filp, addr, len, pgoff, flags);
}
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 ist noch experimentell.