Skip to content

Commit

Permalink
lx_emul: manage page structs per buffer range
Browse files Browse the repository at this point in the history
The management of Linux page structs is now tied to the life time of DMA
buffers. Thus, page structs are created when a buffer is allocated and
deallocated only when a buffer is freed - not on lx_emul_mem_free()
because DMA buffers are cached. Page struct refcounting was entirely
reworked in lx_emul/shadow/mm/page_alloc.c.

Fixes #4809
  • Loading branch information
chelmuth committed Dec 5, 2023
1 parent bac756c commit cfe7915
Show file tree
Hide file tree
Showing 24 changed files with 294 additions and 318 deletions.
2 changes: 1 addition & 1 deletion repos/dde_linux/lib/import/import-lx_emul_common.inc
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ CC_C_OPT += -Wno-restrict -Wno-maybe-uninitialized -Werror=date-time
CC_C_OPT += -Wno-alloc-size-larger-than -Wimplicit-fallthrough=5 -Werror=date-time
CC_C_OPT += -Werror=incompatible-pointer-types -Werror=designated-init
CC_C_OPT += -Wno-packed-not-aligned -Wno-unused-but-set-variable
CC_C_OPT += -Wno-discarded-qualifiers
CC_C_OPT += -Wno-discarded-qualifiers -Wno-unused-function

# avoid build errors because with CC_OLEVEL = -O0/-Og - not supported by Linux
override CC_OLEVEL := -O2
Expand Down
21 changes: 10 additions & 11 deletions repos/dde_linux/src/include/lx_emul/page_virt.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*
* \brief Lx_emul support for page-struct management
* \author Norman Feske
* \author Christian Helmuth
* \date 2021-07-01
*/

Expand All @@ -27,29 +28,27 @@ struct page;

void lx_emul_associate_page_with_virt_addr(struct page *, void const *virt);
void lx_emul_disassociate_page_from_virt_addr(void const *virt);
struct page *lx_emul_associated_page(void const *virt, unsigned long size);
struct page *lx_emul_associated_page(void const *virt);


/**
* Return page struct for the page at a given virtual address
* Return page struct for the page at a given virtual address (virt_to_page.cc)
*
* If no page struct exists for the virtual address, it is created.
* As in Linux page structs of contiguous pages of attached DMA/RAM buffers
* (i.e., page ranges) are contiguous too.
*/
struct page *lx_emul_virt_to_pages(void const *virt, unsigned long num);
struct page *lx_emul_virt_to_page(void const *virt);


/**
* Release page structs for specified virtual-address range
*
* \param size size of range in bytes
* Release page structs for specified virtual-address range (virt_to_page.c)
*/
void lx_emul_forget_pages(void const *virt, unsigned long size);

void lx_emul_remove_page_range(void const *virt_addr, unsigned long size);

/**
* Perform unit test
* Initialize page structs for specified virtual-address range (virt_to_page.c)
*/
void lx_emul_associate_page_selftest(void);
void lx_emul_add_page_range(void const *virt_addr, unsigned long size);


#ifdef __cplusplus
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ typedef struct page *pgtable_t;
#define page_to_phys(p) __pa((p)->virtual)
#define page_to_virt(p) ((p)->virtual)

static inline struct page *virt_to_page(void const *v) { return lx_emul_virt_to_pages(v, 1U); }
static inline struct page *virt_to_page(void const *v) { return lx_emul_virt_to_page(v); }

/* needed by mm/internal.h */
#define pfn_valid(pfn) (pfn != 0UL)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ extern u64 vabits_actual;
#define page_to_phys(p) __pa((p)->virtual)
#define page_to_virt(p) ((p)->virtual)

static inline struct page *virt_to_page(void const *v) { return lx_emul_virt_to_pages(v, 1U); }
static inline struct page *virt_to_page(void const *v) { return lx_emul_virt_to_page(v); }

#define pfn_to_page(pfn) ( (struct page *)(__va(pfn << PAGE_SHIFT)) )
#define page_to_pfn(page) ( page_to_phys(page) >> PAGE_SHIFT )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ typedef struct page *pgtable_t;
#define pfn_to_virt(pfn) __va((pfn) << PAGE_SHIFT)
#define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT)

static inline struct page *virt_to_page(void const *v) { return lx_emul_virt_to_pages(v, 1U); }
static inline struct page *virt_to_page(void const *v) { return lx_emul_virt_to_page(v); }
#define page_to_virt(p) ((p)->virtual)

#define virt_addr_valid(kaddr) ((unsigned long)kaddr != 0UL)
Expand Down
2 changes: 2 additions & 0 deletions repos/dde_linux/src/include/lx_emul/shared_dma_buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ lx_emul_shared_dma_buffer_allocate(unsigned long size);

void lx_emul_shared_dma_buffer_free(struct genode_shared_dataspace * ds);

void * lx_emul_shared_dma_buffer_virt_addr(struct genode_shared_dataspace * ds);

#ifdef __cplusplus
}
#endif
Expand Down
151 changes: 151 additions & 0 deletions repos/dde_linux/src/include/lx_emul/shmem_file.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/*
* \brief Shared-memory file utility
* \author Christian Helmuth
* \author Josef Söntgen
* \date 2023-12-04
*
* This utility implements limited shared-memory file semantics as required by
* Linux graphics drivers (e.g., intel_fb and lima_gpu_drv)
*/

/*
* Copyright (C) 2023 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/

#include <lx_emul/shared_dma_buffer.h>
#include <linux/shmem_fs.h>

struct shmem_file_buffer
{
struct genode_shared_dataspace *dataspace;
void *addr;
struct page *pages;
};


struct file *shmem_file_setup(char const *name, loff_t size,
unsigned long flags)
{
struct file *f;
struct inode *inode;
struct address_space *mapping;
struct shmem_file_buffer *private_data;
loff_t const nrpages = DIV_ROUND_UP(size, PAGE_SIZE);

if (!size)
return (struct file*)ERR_PTR(-EINVAL);

f = kzalloc(sizeof (struct file), 0);
if (!f) {
return (struct file*)ERR_PTR(-ENOMEM);
}

inode = kzalloc(sizeof (struct inode), 0);
if (!inode) {
goto err_inode;
}

mapping = kzalloc(sizeof (struct address_space), 0);
if (!mapping) {
goto err_mapping;
}

private_data = kzalloc(sizeof (struct shmem_file_buffer), 0);
if (!private_data) {
goto err_private_data;
}

private_data->dataspace = lx_emul_shared_dma_buffer_allocate(nrpages * PAGE_SIZE);
if (!private_data->dataspace)
goto err_private_data_addr;

private_data->addr = lx_emul_shared_dma_buffer_virt_addr(private_data->dataspace);
private_data->pages = lx_emul_virt_to_page(private_data->addr);

mapping->private_data = private_data;
mapping->nrpages = nrpages;

inode->i_mapping = mapping;

atomic_long_set(&f->f_count, 1);
f->f_inode = inode;
f->f_mapping = mapping;
f->f_flags = flags;
f->f_mode = OPEN_FMODE(flags);
f->f_mode |= FMODE_OPENED;

return f;

err_private_data_addr:
kfree(private_data);
err_private_data:
kfree(mapping);
err_mapping:
kfree(inode);
err_inode:
kfree(f);
return (struct file*)ERR_PTR(-ENOMEM);
}


struct page *shmem_read_mapping_page_gfp(struct address_space *mapping,
pgoff_t index, gfp_t gfp)
{
struct page *p;
struct shmem_file_buffer *private_data;

if (index > mapping->nrpages)
return NULL;

private_data = mapping->private_data;

p = private_data->pages;
return (p + index);
}


#include <linux/pagevec.h>

void __pagevec_release(struct pagevec * pvec)
{
/* XXX check if we have to call release_pages */
pagevec_reinit(pvec);
}


#include <linux/file.h>

static void _free_file(struct file *file)
{
struct inode *inode;
struct address_space *mapping;
struct shmem_file_buffer *private_data;

mapping = file->f_mapping;
inode = file->f_inode;

if (mapping) {
private_data = mapping->private_data;

kfree(private_data);
kfree(mapping);
}

kfree(inode);
kfree(file->f_path.dentry);
kfree(file);
}


void fput(struct file *file)
{
if (!file)
return;

if (atomic_long_sub_and_test(1, &file->f_count)) {
_free_file(file);
}
}
4 changes: 3 additions & 1 deletion repos/dde_linux/src/include/lx_kit/memory.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*
* \brief Lx_kit memory allocation backend
* \author Stefan Kalkowski
* \author Christian Helmuth
* \date 2021-03-25
*/

Expand Down Expand Up @@ -99,7 +100,8 @@ class Lx_kit::Mem_allocator
void free_buffer(void *addr);
Dataspace_capability attached_dataspace_cap(void *addr);

void * alloc(size_t size, size_t align);
void * alloc(size_t size, size_t align,
void (*new_range_cb)(void const *virt_addr, unsigned long size));
addr_t dma_addr(void * addr);
addr_t virt_addr(void * dma_addr);
addr_t virt_region_start(void * virt_addr);
Expand Down
8 changes: 3 additions & 5 deletions repos/dde_linux/src/lib/lx_emul/alloc.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,15 @@

extern "C" void * lx_emul_mem_alloc_aligned(unsigned long size, unsigned long align)
{
void * const ptr = Lx_kit::env().memory.alloc(size, align);
lx_emul_forget_pages(ptr, size);
void * const ptr = Lx_kit::env().memory.alloc(size, align, &lx_emul_add_page_range);
return ptr;
};


extern "C" void * lx_emul_mem_alloc_aligned_uncached(unsigned long size,
unsigned long align)
{
void * const ptr = Lx_kit::env().uncached_memory.alloc(size, align);
lx_emul_forget_pages(ptr, size);
void * const ptr = Lx_kit::env().uncached_memory.alloc(size, align, &lx_emul_add_page_range);
return ptr;
};

Expand All @@ -53,7 +51,7 @@ extern "C" unsigned long lx_emul_mem_virt_addr(void * dma_addr)
if (ret)
return ret;
if (!(ret = Lx_kit::env().uncached_memory.virt_addr(dma_addr)))
Genode::error(__func__, " called with invalid addr ", dma_addr);
Genode::error(__func__, " called with invalid dma_addr ", dma_addr);
return ret;
}

Expand Down
17 changes: 6 additions & 11 deletions repos/dde_linux/src/lib/lx_emul/page_virt.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*
* \brief Lx_emul backend for page-struct management
* \author Norman Feske
* \author Christian Helmuth
* \date 2021-07-01
*/

Expand Down Expand Up @@ -35,28 +36,22 @@ struct Lx_emul::Page_info
return key.virt > other_key.virt;
}

struct Query_virt_range
struct Query_virt_addr
{
addr_t virt;
size_t size;

bool matches(Page_info const &page_info) const
{
size_t const page_size = 4096;

Lx_kit::Byte_range page_range { page_info.key.virt, page_size };
Lx_kit::Byte_range virt_range { virt, size };
Lx_kit::Byte_range virt_range { virt, 1 };

return page_range.intersects(virt_range);
}

Key key() const { return Key { virt }; }
};

struct Query_virt_addr : Query_virt_range
{
Query_virt_addr(void const *virt) : Query_virt_range{(addr_t)virt, 1} { }
};
};


Expand All @@ -75,13 +70,13 @@ extern "C" void lx_emul_associate_page_with_virt_addr(struct page *page, void co

void lx_emul_disassociate_page_from_virt_addr(void const *virt)
{
page_registry().remove(Lx_emul::Page_info::Query_virt_addr(virt));
page_registry().remove(Lx_emul::Page_info::Query_virt_addr { (addr_t)virt });
}


struct page *lx_emul_associated_page(void const *virt, unsigned long size)
struct page *lx_emul_associated_page(void const *virt)
{
Lx_emul::Page_info::Query_virt_range query { .virt = (addr_t)virt, .size = size };
Lx_emul::Page_info::Query_virt_addr query { (addr_t)virt };

struct page *page_ptr = nullptr;
page_registry().apply(query, [&] (Lx_emul::Page_info const &page_info) {
Expand Down
Loading

0 comments on commit cfe7915

Please sign in to comment.