Skip to content

Possible issues when allocate page-sized memory in snapshot management #150

@iaoing

Description

@iaoing

Issue

In snapshot management, NOVA uses kmalloc to allocate the page-sized memory, and then checks the alignment, as the below code shows.

new_page = (unsigned long)kmalloc(PAGE_SIZE,
GFP_KERNEL);
/* Aligned to PAGE_SIZE */
if (!new_page || ENTRY_LOC(new_page)) {
nova_dbg("%s: failed\n", __func__);
kfree((void *)new_page);
return -ENOMEM;
}

However, the allocated memory space might not be aligned to the page size. I have encountered this situation where the allocation is successful but the alignment is not satisfied, but I am not sure whether it was caused by VMs or small DRAM size that was used in the VM.

The documentation of kernel-5.1 does not say the alignment is guaranteed (https://www.kernel.org/doc/html/v5.1/core-api/memory-allocation.html). There also have discussions regarding the alignment of kmalloc (https://lwn.net/Articles/787740/).

From kernel-5.4, the documentation confirms the alignment guarantee of kmalloc. However, it also suggests to use the page allocator for large allocations. The blow code is the APIs of the page allocator.

#define alloc_page(gfp_mask) alloc_pages(gfp_mask, 0)
#define alloc_page_vma(gfp_mask, vma, addr) \
alloc_pages_vma(gfp_mask, 0, vma, addr, numa_node_id(), false)
#define alloc_page_vma_node(gfp_mask, vma, addr, node) \
alloc_pages_vma(gfp_mask, 0, vma, addr, node, false)
extern unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order);
extern unsigned long get_zeroed_page(gfp_t gfp_mask);
void *alloc_pages_exact(size_t size, gfp_t gfp_mask);
void free_pages_exact(void *virt, size_t size);
void * __meminit alloc_pages_exact_nid(int nid, size_t size, gfp_t gfp_mask);
#define __get_free_page(gfp_mask) \
__get_free_pages((gfp_mask), 0)
#define __get_dma_pages(gfp_mask, order) \
__get_free_pages((gfp_mask) | GFP_DMA, (order))
extern void __free_pages(struct page *page, unsigned int order);
extern void free_pages(unsigned long addr, unsigned int order);
extern void free_unref_page(struct page *page);
extern void free_unref_page_list(struct list_head *list);
struct page_frag_cache;
extern void __page_frag_cache_drain(struct page *page, unsigned int count);
extern void *page_frag_alloc(struct page_frag_cache *nc,
unsigned int fragsz, gfp_t gfp_mask);
extern void page_frag_free(void *addr);
#define __free_page(page) __free_pages((page), 0)
#define free_page(addr) free_pages((addr), 0)

Fix

Replacing kmalloc as __get_free_page and kfree as free_page, as below code shows

// new_page = (unsigned long)kmalloc(PAGE_SIZE, GFP_KERNEL);
new_page = __get_free_page(GFP_KERNEL);
if (!new_page || ENTRY_LOC(new_page)) {
    // kfree((void *)new_page);
    free_page((unsigned long)new_page);
    nova_err(sb, "%s: allocation failed\n", __func__);
    return -ENOMEM;
}

// and other places to fix

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions