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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions