summaryrefslogtreecommitdiff
path: root/components/mm
diff options
context:
space:
mode:
authorgeniusgogo <[email protected]>2023-10-17 13:07:59 +0800
committerGitHub <[email protected]>2023-10-17 13:07:59 +0800
commitecd29fda6005ef19c625f194f9311259ba907a78 (patch)
treed0e272f5783ac4a3048238e14a37bcd88b5dc3e6 /components/mm
parent7d64cdcf58969b1b2b7e9cfb3aac9e46cb456ae3 (diff)
Sync dfs lwp (#8123)
Diffstat (limited to 'components/mm')
-rw-r--r--components/mm/SConscript10
-rw-r--r--components/mm/mm_anon.c620
-rw-r--r--components/mm/mm_aspace.c984
-rw-r--r--components/mm/mm_aspace.h104
-rw-r--r--components/mm/mm_fault.c91
-rw-r--r--components/mm/mm_fault.h69
-rw-r--r--components/mm/mm_flag.h15
-rw-r--r--components/mm/mm_kmem.c28
-rw-r--r--components/mm/mm_object.c206
-rw-r--r--components/mm/mm_page.c56
-rw-r--r--components/mm/mm_page.h4
-rw-r--r--components/mm/mm_private.h40
12 files changed, 2032 insertions, 195 deletions
diff --git a/components/mm/SConscript b/components/mm/SConscript
index e42fa9f0c8..9c76dd8b41 100644
--- a/components/mm/SConscript
+++ b/components/mm/SConscript
@@ -1,14 +1,14 @@
import os
from building import *
-objs = []
-src = []
+objs = []
if GetDepend('ARCH_ARM_CORTEX_A') or GetDepend('ARCH_ARMV8') or GetDepend('ARCH_RISCV64'):
cwd = GetCurrentDir()
- src += ['avl_adpt.c', 'ioremap.c', 'mm_aspace.c', 'mm_fault.c', 'mm_kmem.c', 'mm_object.c', 'mm_page.c']
- if GetDepend('RT_USING_MEMBLOCK'):
- src += ['mm_memblock.c']
+ src = Glob('*.c') + Glob('*_gcc.S')
+ if not GetDepend('RT_USING_MEMBLOCK'):
+ SrcRemove(src, ['mm_memblock.c'])
+
CPPPATH = [cwd]
group = DefineGroup('mm', src, depend = ['ARCH_MM_MMU'], CPPPATH = CPPPATH)
diff --git a/components/mm/mm_anon.c b/components/mm/mm_anon.c
new file mode 100644
index 0000000000..e14300a08f
--- /dev/null
+++ b/components/mm/mm_anon.c
@@ -0,0 +1,620 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-08-19 Shell Support PRIVATE mapping and COW
+ */
+
+#define DBG_TAG "mm.anon"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#include <string.h>
+#include "mm_private.h"
+#include <mmu.h>
+
+/**
+ * Anonymous Object directly represent the mappings without backup files in the
+ * aspace. Their only backup is in the aspace->pgtbl.
+ */
+
+typedef struct rt_private_ctx {
+ struct rt_mem_obj mem_obj;
+ rt_aspace_t backup_aspace;
+ /* both varea and aspace can holds a reference */
+ rt_atomic_t reference;
+ /* readonly `private` is shared object */
+ long readonly;
+} *rt_private_ctx_t;
+
+rt_inline rt_aspace_t _anon_obj_get_backup(rt_mem_obj_t mobj)
+{
+ rt_private_ctx_t pctx;
+ rt_aspace_t backup;
+ pctx = rt_container_of(mobj, struct rt_private_ctx, mem_obj);
+ backup = pctx->backup_aspace;
+ return backup;
+}
+
+rt_inline rt_atomic_t *_anon_obj_get_reference(rt_mem_obj_t mobj)
+{
+ rt_private_ctx_t pctx;
+ pctx = rt_container_of(mobj, struct rt_private_ctx, mem_obj);
+ return &pctx->reference;
+}
+
+rt_inline rt_private_ctx_t _anon_mobj_to_pctx(rt_mem_obj_t mobj)
+{
+ return rt_container_of(mobj, struct rt_private_ctx, mem_obj);
+}
+
+static long rt_aspace_anon_ref_inc(rt_mem_obj_t aobj)
+{
+ long rc;
+ if (aobj)
+ {
+ rc = rt_atomic_add(_anon_obj_get_reference(aobj), 1);
+ LOG_D("%s(aobj=%p) Cur %ld", __func__, aobj, rc + 1);
+ }
+ else
+ rc = -1;
+ return rc;
+}
+
+rt_err_t rt_aspace_anon_ref_dec(rt_mem_obj_t aobj)
+{
+ rt_err_t rc;
+ rt_aspace_t aspace;
+ rt_private_ctx_t pctx;
+ long former_reference;
+
+ if (aobj)
+ {
+ pctx = _anon_mobj_to_pctx(aobj);
+ RT_ASSERT(pctx);
+
+ former_reference = rt_atomic_add(_anon_obj_get_reference(aobj), -1);
+ LOG_D("%s(aobj=%p) Cur %ld", __func__, aobj, former_reference - 1);
+ if (pctx->readonly)
+ {
+ if (former_reference - 1 <= pctx->readonly)
+ {
+ void *pgtbl;
+ RT_ASSERT(former_reference - 1 == pctx->readonly);
+ aspace = _anon_obj_get_backup(aobj);
+
+ pctx->readonly = 0;
+ pgtbl = aspace->page_table;
+ rt_aspace_delete(aspace);
+ rt_hw_mmu_pgtbl_delete(pgtbl);
+ }
+ }
+ else if (former_reference < 2)
+ {
+ aspace = _anon_obj_get_backup(aobj);
+ aspace->private_object = RT_NULL;
+
+ rt_free(pctx);
+ }
+ rc = RT_EOK;
+ }
+ else
+ {
+ rc = -RT_EINVAL;
+ }
+
+ return rc;
+}
+
+static const char *_anon_get_name(rt_varea_t varea)
+{
+ return varea->aspace == _anon_obj_get_backup(varea->mem_obj) ? "anonymous" : "reference";
+}
+
+static void _anon_varea_open(struct rt_varea *varea)
+{
+ rt_aspace_anon_ref_inc(varea->mem_obj);
+
+ if (varea->aspace == _anon_obj_get_backup(varea->mem_obj))
+ varea->offset = MM_PA_TO_OFF(varea->start);
+
+ varea->data = NULL;
+}
+
+static void _anon_varea_close(struct rt_varea *varea)
+{
+ rt_aspace_anon_ref_dec(varea->mem_obj);
+}
+
+static rt_err_t _anon_varea_expand(struct rt_varea *varea, void *new_vaddr, rt_size_t size)
+{
+ return RT_EOK;
+}
+
+static rt_err_t _anon_varea_shrink(rt_varea_t varea, void *new_start, rt_size_t size)
+{
+ return rt_mm_dummy_mapper.on_varea_shrink(varea, new_start, size);
+}
+
+static rt_err_t _anon_varea_split(struct rt_varea *existed, void *unmap_start, rt_size_t unmap_len, struct rt_varea *subset)
+{
+ _anon_varea_open(subset);
+ return rt_mm_dummy_mapper.on_varea_split(existed, unmap_start, unmap_len, subset);
+}
+
+static rt_err_t _anon_varea_merge(struct rt_varea *merge_to, struct rt_varea *merge_from)
+{
+ _anon_varea_close(merge_from);
+ return rt_mm_dummy_mapper.on_varea_merge(merge_to, merge_from);
+}
+
+rt_inline void _map_page_in_varea(rt_aspace_t asapce, rt_varea_t varea,
+ struct rt_aspace_fault_msg *msg, char *fault_addr)
+{
+ if (rt_varea_map_page(varea, fault_addr, msg->response.vaddr) == RT_EOK)
+ {
+ msg->response.status = MM_FAULT_STATUS_OK_MAPPED;
+ }
+ else
+ {
+ /* revoke the allocated page */
+ rt_varea_pgmgr_pop(varea, msg->response.vaddr, ARCH_PAGE_SIZE);
+ msg->response.status = MM_FAULT_STATUS_UNRECOVERABLE;
+ LOG_W("%s: failed to map page into varea", __func__);
+ }
+}
+
+static void *_get_page_from_backup(rt_aspace_t backup, rt_base_t offset_in_mobj)
+{
+ void *frame_pa;
+ char *backup_addr;
+ rt_varea_t backup_varea;
+ void *rc = RT_NULL;
+
+ backup_addr = (char *)(offset_in_mobj << MM_PAGE_SHIFT);
+ backup_varea = rt_aspace_query(backup, backup_addr);
+
+ if (backup_varea)
+ {
+ /* synchronize between multiple request by aspace lock of backup */
+ WR_LOCK(backup);
+
+ frame_pa = rt_hw_mmu_v2p(backup, backup_addr);
+ if (frame_pa == ARCH_MAP_FAILED)
+ {
+ /* provide the page in backup varea */
+ struct rt_aspace_fault_msg msg;
+ msg.fault_op = MM_FAULT_OP_WRITE;
+ msg.fault_type = MM_FAULT_TYPE_PAGE_FAULT;
+ msg.fault_vaddr = backup_addr;
+ msg.off = offset_in_mobj;
+ rt_mm_fault_res_init(&msg.response);
+
+ rt_mm_dummy_mapper.on_page_fault(backup_varea, &msg);
+ if (msg.response.status != MM_FAULT_STATUS_UNRECOVERABLE)
+ {
+ _map_page_in_varea(backup, backup_varea, &msg, backup_addr);
+ if (msg.response.status == MM_FAULT_STATUS_OK_MAPPED)
+ {
+ rc = msg.response.vaddr;
+ }
+ }
+ }
+ else
+ {
+ rc = rt_kmem_p2v(frame_pa);
+ if (!rc)
+ RT_ASSERT(0 && "No kernel address of target page frame");
+ }
+ WR_UNLOCK(backup);
+ }
+ else
+ {
+ /* out of range error */
+ LOG_E("(backup_addr=%p): Page request out of range", backup_addr);
+ }
+
+ return rc;
+}
+
+/* get the backup page in kernel for the address in user space */
+static void _anon_page_fault(struct rt_varea *varea, struct rt_aspace_fault_msg *msg)
+{
+ void *paddr;
+ char *frame_ka;
+ rt_aspace_t from_aspace = varea->aspace;
+ rt_aspace_t backup = _anon_obj_get_backup(varea->mem_obj);
+
+ RDWR_LOCK(from_aspace);
+
+ /**
+ * if the page is already mapped(this may caused by data race while other
+ * thread success to take the lock and mapped the page before this), return okay
+ */
+ paddr = rt_hw_mmu_v2p(from_aspace, msg->fault_vaddr);
+ if (paddr == ARCH_MAP_FAILED)
+ {
+ if (backup == from_aspace)
+ {
+ rt_mm_dummy_mapper.on_page_fault(varea, msg);
+ if (msg->response.status != MM_FAULT_STATUS_UNRECOVERABLE)
+ {
+ _map_page_in_varea(backup, varea, msg, msg->fault_vaddr);
+ }
+ }
+ else
+ {
+ frame_ka = _get_page_from_backup(backup, msg->off);
+ if (frame_ka)
+ {
+ msg->response.vaddr = frame_ka;
+ msg->response.size = ARCH_PAGE_SIZE;
+ _map_page_in_varea(from_aspace, varea, msg, msg->fault_vaddr);
+ }
+ }
+ }
+ else
+ {
+ msg->response.status = MM_FAULT_STATUS_OK_MAPPED;
+ }
+ RDWR_UNLOCK(from_aspace);
+}
+
+static void read_by_mte(rt_aspace_t aspace, struct rt_aspace_io_msg *iomsg)
+{
+ if (rt_aspace_page_get_phy(aspace, iomsg->fault_vaddr, iomsg->buffer_vaddr) == RT_EOK)
+ iomsg->response.status = MM_FAULT_STATUS_OK;
+}
+
+static void _anon_page_read(struct rt_varea *varea, struct rt_aspace_io_msg *iomsg)
+{
+ rt_aspace_t from_aspace = varea->aspace;
+
+ if (rt_hw_mmu_v2p(from_aspace, iomsg->fault_vaddr) == ARCH_MAP_FAILED)
+ {
+ struct rt_aspace_fault_msg msg;
+ msg.fault_op = MM_FAULT_OP_READ;
+ msg.fault_type = MM_FAULT_TYPE_PAGE_FAULT;
+ msg.fault_vaddr = iomsg->fault_vaddr;
+ msg.off = iomsg->off;
+ rt_mm_fault_res_init(&msg.response);
+
+ _anon_page_fault(varea, &msg);
+ if (msg.response.status != MM_FAULT_STATUS_UNRECOVERABLE)
+ {
+ read_by_mte(from_aspace, iomsg);
+ }
+ }
+ else
+ {
+ read_by_mte(from_aspace, iomsg);
+ }
+}
+
+static void write_by_mte(rt_aspace_t aspace, struct rt_aspace_io_msg *iomsg)
+{
+ if (rt_aspace_page_put_phy(aspace, iomsg->fault_vaddr, iomsg->buffer_vaddr) == RT_EOK)
+ iomsg->response.status = MM_FAULT_STATUS_OK;
+}
+
+static void _anon_page_write(struct rt_varea *varea, struct rt_aspace_io_msg *iomsg)
+{
+ rt_aspace_t from_aspace = varea->aspace;
+
+ if (rt_hw_mmu_v2p(from_aspace, iomsg->fault_vaddr) == ARCH_MAP_FAILED)
+ {
+ struct rt_aspace_fault_msg msg;
+ msg.fault_op = MM_FAULT_OP_WRITE;
+ msg.fault_type = MM_FAULT_TYPE_PAGE_FAULT;
+ msg.fault_vaddr = iomsg->fault_vaddr;
+ msg.off = iomsg->off;
+ rt_mm_fault_res_init(&msg.response);
+
+ _anon_page_fault(varea, &msg);
+ if (msg.response.status != MM_FAULT_STATUS_UNRECOVERABLE)
+ {
+ write_by_mte(from_aspace, iomsg);
+ }
+ }
+ else
+ {
+ write_by_mte(from_aspace, iomsg);
+ }
+}
+
+static struct rt_private_ctx _priv_obj = {
+ .mem_obj.get_name = _anon_get_name,
+ .mem_obj.on_page_fault = _anon_page_fault,
+ .mem_obj.hint_free = NULL,
+ .mem_obj.on_varea_open = _anon_varea_open,
+ .mem_obj.on_varea_close = _anon_varea_close,
+ .mem_obj.on_varea_shrink = _anon_varea_shrink,
+ .mem_obj.on_varea_split = _anon_varea_split,
+ .mem_obj.on_varea_expand = _anon_varea_expand,
+ .mem_obj.on_varea_merge = _anon_varea_merge,
+ .mem_obj.page_read = _anon_page_read,
+ .mem_obj.page_write = _anon_page_write,
+};
+
+rt_inline rt_private_ctx_t rt_private_obj_create_n_bind(rt_aspace_t aspace)
+{
+ rt_private_ctx_t private_object;
+ private_object = rt_malloc(sizeof(struct rt_private_ctx));
+ if (private_object)
+ {
+ memcpy(&private_object->mem_obj, &_priv_obj, sizeof(_priv_obj));
+
+ /* hold a init ref from backup aspace */
+ rt_atomic_store(&private_object->reference, 1);
+
+ private_object->readonly = RT_FALSE;
+ private_object->backup_aspace = aspace;
+ aspace->private_object = &private_object->mem_obj;
+ }
+
+ return private_object;
+}
+
+rt_inline rt_mem_obj_t _get_private_obj(rt_aspace_t aspace)
+{
+ rt_private_ctx_t priv;
+ rt_mem_obj_t rc;
+ rc = aspace->private_object;
+ if (!aspace->private_object)
+ {
+ priv = rt_private_obj_create_n_bind(aspace);
+ if (priv)
+ {
+ rc = &priv->mem_obj;
+ aspace->private_object = rc;
+ }
+ }
+ return rc;
+}
+
+static int _override_map(rt_varea_t varea, rt_aspace_t aspace, void *fault_vaddr, struct rt_aspace_fault_msg *msg, void *page)
+{
+ int rc = MM_FAULT_FIXABLE_FALSE;
+ rt_mem_obj_t private_object;
+ rt_varea_t map_varea = RT_NULL;
+ rt_err_t error;
+ rt_size_t flags;
+ rt_size_t attr;
+
+ LOG_D("%s", __func__);
+
+ private_object = _get_private_obj(aspace);
+
+ if (private_object)
+ {
+ flags = varea->flag | MMF_MAP_FIXED;
+ /* don't prefetch and do it latter */
+ flags &= ~MMF_PREFETCH;
+ attr = rt_hw_mmu_attr_add_perm(varea->attr, RT_HW_MMU_PROT_USER | RT_HW_MMU_PROT_WRITE);
+
+ /* override existing mapping at fault_vaddr */
+ error = _mm_aspace_map(
+ aspace, &map_varea, &fault_vaddr, ARCH_PAGE_SIZE, attr,
+ flags, private_object, MM_PA_TO_OFF(fault_vaddr));
+
+ if (error == RT_EOK)
+ {
+ msg->response.status = MM_FAULT_STATUS_OK;
+ msg->response.vaddr = page;
+ msg->response.size = ARCH_PAGE_SIZE;
+ if (rt_varea_map_with_msg(map_varea, msg) != RT_EOK)
+ {
+ LOG_E("%s: fault_va=%p,(priv_va=%p,priv_sz=0x%lx) at %s", __func__, msg->fault_vaddr, map_varea->start, map_varea->size, VAREA_NAME(map_varea));
+ RT_ASSERT(0 && "should never failed");
+ }
+ RT_ASSERT(rt_hw_mmu_v2p(aspace, msg->fault_vaddr) == (page + PV_OFFSET));
+ rc = MM_FAULT_FIXABLE_TRUE;
+ rt_varea_pgmgr_insert(map_varea, page);
+ }
+ else
+ {
+ /* private object will be release on destruction of aspace */
+ rt_free(map_varea);
+ }
+ }
+ else
+ {
+ LOG_I("%s: out of memory", __func__);
+ rc = MM_FAULT_FIXABLE_FALSE;
+ }
+
+ return rc;
+}
+
+/**
+ * replace an existing mapping to a private one, this is identical to:
+ * => aspace_unmap(ex_varea, )
+ * => aspace_map()
+ */
+int rt_varea_fix_private_locked(rt_varea_t ex_varea, void *pa,
+ struct rt_aspace_fault_msg *msg,
+ rt_bool_t dont_copy)
+{
+ /**
+ * todo: READ -> WRITE lock here
+ */
+ void *page;
+ void *fault_vaddr;
+ rt_aspace_t aspace;
+ rt_mem_obj_t ex_obj;
+ int rc = MM_FAULT_FIXABLE_FALSE;
+ ex_obj = ex_varea->mem_obj;
+
+ if (ex_obj)
+ {
+ fault_vaddr = msg->fault_vaddr;
+ aspace = ex_varea->aspace;
+ RT_ASSERT(!!aspace);
+
+ /**
+ * todo: what if multiple pages are required?
+ */
+ if (aspace->private_object == ex_obj)
+ {
+ RT_ASSERT(0 && "recursion");
+ }
+ else if (ex_obj->page_read)
+ {
+ page = rt_pages_alloc_ext(0, PAGE_ANY_AVAILABLE);
+ if (page)
+ {
+ /** setup message & fetch the data from source object */
+ if (!dont_copy)
+ {
+ struct rt_aspace_io_msg io_msg;
+ rt_mm_io_msg_init(&io_msg, msg->off, msg->fault_vaddr, page);
+ ex_obj->page_read(ex_varea, &io_msg);
+ /**
+ * Note: if ex_obj have mapped into varea, it's still okay since
+ * we will override it latter
+ */
+ if (io_msg.response.status != MM_FAULT_STATUS_UNRECOVERABLE)
+ {
+ rc = _override_map(ex_varea, aspace, fault_vaddr, msg, page);
+ }
+ else
+ {
+ rt_pages_free(page, 0);
+ LOG_I("%s: page read(va=%p) fault from %s(start=%p,size=%p)", __func__,
+ msg->fault_vaddr, VAREA_NAME(ex_varea), ex_varea->start, ex_varea->size);
+ }
+ }
+ else
+ {
+ rc = _override_map(ex_varea, aspace, fault_vaddr, msg, page);
+ }
+ }
+ else
+ {
+ LOG_I("%s: pages allocation failed", __func__);
+ }
+ }
+ else
+ {
+ LOG_I("%s: no page read method provided from %s", __func__, VAREA_NAME(ex_varea));
+ }
+ }
+ else
+ {
+ LOG_I("%s: unavailable memory object", __func__);
+ }
+
+ return rc;
+}
+
+int rt_aspace_map_private(rt_aspace_t aspace, void **addr, rt_size_t length,
+ rt_size_t attr, mm_flag_t flags)
+{
+ int rc;
+ rt_mem_obj_t priv_obj;
+
+ if (flags & MMF_STATIC_ALLOC)
+ {
+ rc = -RT_EINVAL;
+ }
+ else
+ {
+ priv_obj = _get_private_obj(aspace);
+ if (priv_obj)
+ {
+ flags |= MMF_MAP_PRIVATE;
+ flags &= ~MMF_PREFETCH;
+ rc = rt_aspace_map(aspace, addr, length, attr, flags, priv_obj, 0);
+ }
+ else
+ {
+ rc = -RT_ENOMEM;
+ }
+ }
+ return rc;
+}
+
+static int _release_shared(rt_varea_t varea, void *arg)
+{
+ rt_aspace_t src = varea->aspace;
+ rt_mem_obj_t mem_obj = varea->mem_obj;
+
+ if (mem_obj != _get_private_obj(src))
+ {
+ _varea_uninstall_locked(varea);
+ if (VAREA_NOT_STATIC(varea))
+ {
+ rt_free(varea);
+ }
+ }
+
+ return 0;
+}
+
+static rt_err_t _convert_readonly(rt_aspace_t aspace, long base_reference)
+{
+ rt_mem_obj_t aobj;
+ rt_private_ctx_t pctx;
+ aobj = _get_private_obj(aspace);
+ pctx = _anon_mobj_to_pctx(aobj);
+
+ LOG_D("Ref(cur=%d,base=%d)", pctx->reference, base_reference);
+ rt_aspace_traversal(aspace, _release_shared, 0);
+ pctx->readonly = base_reference;
+ return 0;
+}
+
+rt_inline void _switch_aspace(rt_aspace_t *pa, rt_aspace_t *pb)
+{
+ rt_aspace_t temp;
+ temp = *pa;
+ *pa = *pb;
+ *pb = temp;
+}
+
+rt_err_t rt_aspace_fork(rt_aspace_t *psrc, rt_aspace_t *pdst)
+{
+ rt_err_t rc;
+ void *pgtbl;
+ rt_aspace_t backup;
+ rt_aspace_t src = *psrc;
+ rt_aspace_t dst = *pdst;
+ long base_reference;
+
+ pgtbl = rt_hw_mmu_pgtbl_create();
+ if (pgtbl)
+ {
+ backup = rt_aspace_create(src->start, src->size, pgtbl);
+ if (backup)
+ {
+ WR_LOCK(src);
+ base_reference = rt_atomic_load(_anon_obj_get_reference(src->private_object));
+ rc = rt_aspace_duplicate_locked(src, dst);
+ WR_UNLOCK(src);
+
+ if (!rc)
+ {
+ /* WR_LOCK(dst) is not necessary since dst is not available currently */
+ rc = rt_aspace_duplicate_locked(dst, backup);
+ if (!rc)
+ {
+ _switch_aspace(psrc, &backup);
+ _convert_readonly(backup, base_reference);
+ }
+ }
+ }
+ else
+ {
+ rc = -RT_ENOMEM;
+ }
+ }
+ else
+ {
+ rc = -RT_ENOMEM;
+ }
+
+ return rc;
+}
diff --git a/components/mm/mm_aspace.c b/components/mm/mm_aspace.c
index 6249e44ac3..42278e684f 100644
--- a/components/mm/mm_aspace.c
+++ b/components/mm/mm_aspace.c
@@ -1,40 +1,41 @@
/*
- * Copyright (c) 2006-2022, RT-Thread Development Team
+ * Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-11-14 WangXiaoyao the first version
+ * 2023-08-17 Shell Add unmap_range for MAP_PRIVATE
+ * Support MAP_FIXED in aspace_map(), and
+ * Add better support of permission in mmap
*/
/**
* @brief Virtual Address Space
*/
-#include <rtdef.h>
#include <rtthread.h>
-#include <stddef.h>
-#include <stdint.h>
#define DBG_TAG "mm.aspace"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#include "avl_adpt.h"
-#include "mm_aspace.h"
-#include "mm_fault.h"
-#include "mm_flag.h"
-#include "mm_page.h"
#include "mm_private.h"
#include <mmu.h>
#include <tlb.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#define ALIGNED(addr) (!((rt_size_t)(addr) & ARCH_PAGE_MASK))
+
static void *_find_free(rt_aspace_t aspace, void *prefer, rt_size_t req_size,
void *limit_start, rt_size_t limit_size,
mm_flag_t flags);
-static void _varea_uninstall(rt_varea_t varea);
+static int _unmap_range_locked(rt_aspace_t aspace, void *addr, size_t length);
struct rt_aspace rt_kernel_space;
@@ -56,6 +57,7 @@ rt_err_t rt_aspace_init(rt_aspace_t aspace, void *start, rt_size_t length, void
aspace->page_table = pgtbl;
aspace->start = start;
aspace->size = length;
+ aspace->private_object = RT_NULL;
err = _aspace_bst_init(aspace);
if (err == RT_EOK)
@@ -102,21 +104,25 @@ rt_aspace_t rt_aspace_create(void *start, rt_size_t length, void *pgtbl)
void rt_aspace_detach(rt_aspace_t aspace)
{
+ rt_varea_t varea;
+
WR_LOCK(aspace);
- rt_varea_t varea = ASPACE_VAREA_FIRST(aspace);
+ varea = ASPACE_VAREA_FIRST(aspace);
while (varea)
{
rt_varea_t prev = varea;
- _varea_uninstall(varea);
-
varea = ASPACE_VAREA_NEXT(varea);
- if (!(prev->flag & MMF_STATIC_ALLOC))
+
+ _varea_uninstall_locked(prev);
+ if (VAREA_NOT_STATIC(prev))
{
rt_free(prev);
}
}
WR_UNLOCK(aspace);
+ rt_aspace_anon_ref_dec(aspace->private_object);
+
rt_mutex_detach(&aspace->bst_lock);
}
@@ -127,16 +133,29 @@ void rt_aspace_delete(rt_aspace_t aspace)
rt_free(aspace);
}
-static int _do_named_map(rt_aspace_t aspace, void *vaddr, rt_size_t length,
- rt_size_t offset, rt_size_t attr)
+rt_inline rt_size_t _get_effect_attr(rt_aspace_t aspace, rt_varea_t varea)
+{
+ rt_size_t attr = varea->attr;
+
+ /* not write permission for user on private mapping */
+ if (rt_varea_is_private_locked(varea))
+ attr = rt_hw_mmu_attr_rm_perm(attr, RT_HW_MMU_PROT_USER | RT_HW_MMU_PROT_WRITE);
+
+ return attr;
+}
+
+static int _do_named_map(rt_aspace_t aspace, rt_varea_t varea, void *vaddr,
+ rt_size_t length, rt_size_t offset, rt_size_t attr)
{
LOG_D("%s: va %p length %p", __func__, vaddr, length);
int err = RT_EOK;
/* it's ensured by caller that (void*)end will not overflow */
void *phyaddr = (void *)(offset << MM_PAGE_SHIFT);
+ void *ret;
- void *ret = rt_hw_mmu_map(aspace, vaddr, phyaddr, length, attr);
+ attr = _get_effect_attr(aspace, varea);
+ ret = rt_hw_mmu_map(aspace, vaddr, phyaddr, length, attr);
if (ret == RT_NULL)
{
err = -RT_ERROR;
@@ -160,11 +179,13 @@ rt_inline void _do_page_fault(struct rt_aspace_fault_msg *msg, rt_size_t off,
msg->response.vaddr = 0;
msg->response.size = 0;
+ RT_ASSERT(mem_obj->on_page_fault);
mem_obj->on_page_fault(varea, msg);
}
-int _varea_map_with_msg(rt_varea_t varea, struct rt_aspace_fault_msg *msg)
+int rt_varea_map_with_msg(rt_varea_t varea, struct rt_aspace_fault_msg *msg)
{
+ rt_aspace_t aspace;
int err = -RT_ERROR;
if (msg->response.status == MM_FAULT_STATUS_OK)
{
@@ -182,9 +203,15 @@ int _varea_map_with_msg(rt_varea_t varea, struct rt_aspace_fault_msg *msg)
else
{
void *map;
+ rt_size_t attr;
void *v_addr = msg->fault_vaddr;
void *p_addr = store + PV_OFFSET;
- map = rt_hw_mmu_map(varea->aspace, v_addr, p_addr, store_sz, varea->attr);
+
+ aspace = varea->aspace;
+ RT_ASSERT(aspace);
+
+ attr = _get_effect_attr(aspace, varea);
+ map = rt_hw_mmu_map(aspace, v_addr, p_addr, store_sz, attr);
if (!map)
{
@@ -202,12 +229,9 @@ int _varea_map_with_msg(rt_varea_t varea, struct rt_aspace_fault_msg *msg)
{
if (rt_hw_mmu_v2p(varea->aspace, msg->fault_vaddr) == ARCH_MAP_FAILED)
{
- LOG_W("%s: no page is mapped on %p", __func__, msg->fault_vaddr);
- }
- else
- {
- err = RT_EOK;
+ LOG_D("%s: no page is mapped on %p", __func__, msg->fault_vaddr);
}
+ err = RT_EOK;
}
else
{
@@ -234,7 +258,7 @@ static int _do_prefetch(rt_aspace_t aspace, rt_varea_t varea, void *start,
struct rt_aspace_fault_msg msg;
_do_page_fault(&msg, off, vaddr, varea->mem_obj, varea);
- if (_varea_map_with_msg(varea, &msg))
+ if (rt_varea_map_with_msg(varea, &msg))
{
err = -RT_ENOMEM;
break;
@@ -253,30 +277,210 @@ static int _do_prefetch(rt_aspace_t aspace, rt_varea_t varea, void *start,
return err;
}
-/* caller must hold the aspace lock */
-static int _varea_install(rt_aspace_t aspace, rt_varea_t varea, rt_mm_va_hint_t hint)
+static rt_err_t _expand_varea(rt_varea_t varea, void *new_va, rt_size_t size)
{
- void *alloc_va;
- int err = RT_EOK;
+ rt_err_t error;
+ rt_aspace_t aspace;
+ void *old_va;
- /**
- * find a suitable va range.
- * even though this is sleepable, it's still ok for startup routine
- */
- alloc_va =
- _find_free(aspace, hint->prefer, hint->map_size, hint->limit_start,
- hint->limit_range_size, hint->flags);
+ if (varea->mem_obj && varea->mem_obj->on_varea_expand)
+ error = varea->mem_obj->on_varea_expand(varea, new_va, size);
+ else
+ error = -RT_EPERM;
- /* TODO try merge surrounding regions to optimize memory footprint */
+ if (error == RT_EOK)
+ {
+ aspace = varea->aspace;
+ old_va = varea->start;
+ varea->size = size;
- if (alloc_va != RT_NULL)
+ if (old_va != new_va)
+ {
+ varea->start = new_va;
+ varea->offset += ((long)new_va - (long)old_va) >> MM_PAGE_SHIFT;
+ _aspace_bst_remove(aspace, varea);
+ _aspace_bst_insert(aspace, varea);
+ }
+ }
+ return error;
+}
+
+struct _mapping_property {
+ rt_size_t attr;
+ rt_size_t flags;
+ rt_size_t offset;
+ struct rt_mem_obj *mem_obj;
+};
+
+#define INIT_PROP(obj,off,fl,attr) \
+ {.mem_obj = (obj), \
+ .offset = (off), \
+ .flags = (fl), \
+ .attr = (attr),}
+
+static rt_bool_t _contiguous_offset(rt_varea_t neighbour, rt_size_t map_size,
+ struct _mapping_property *prop)
+{
+ rt_size_t n_off = neighbour->offset;
+ rt_size_t map_off = prop->offset;
+ return n_off < map_off ?
+ n_off + (neighbour->size >> MM_PAGE_SHIFT) == map_off :
+ map_off + (map_size >> MM_PAGE_SHIFT) == n_off;
+}
+
+static rt_bool_t _compatible(rt_varea_t neighbour, rt_size_t map_size,
+ struct _mapping_property *prop)
+{
+ return (prop->attr == neighbour->attr && prop->flags == neighbour->flag &&
+ prop->mem_obj == neighbour->mem_obj &&
+ _contiguous_offset(neighbour, map_size, prop));
+}
+
+rt_inline rt_err_t _migrate_and_release_varea(rt_aspace_t aspace, rt_varea_t to, rt_varea_t from,
+ rt_err_t (*on_varea_merge)(struct rt_varea *to, struct rt_varea *from))
+{
+ rt_err_t error;
+ error = on_varea_merge(to, from);
+ if (error == RT_EOK)
+ {
+ /* uninstall operand & release the varea */
+ _aspace_bst_remove(aspace, from);
+ if (!(from->flag & MMF_STATIC_ALLOC))
+ rt_free(from);
+
+ to->size += from->size;
+ }
+ return error;
+}
+
+static rt_varea_t _merge_surrounding(rt_aspace_t aspace, rt_varea_t operand,
+ struct _mapping_property *prop)
+{
+ int again;
+ rt_err_t error;
+ int can_merge_fw;
+ int can_merge_bw;
+ rt_varea_t neighbour;
+ char *operand_start;
+ size_t operand_size;
+ rt_err_t (*on_varea_merge)(struct rt_varea *to, struct rt_varea *from);
+
+ if (operand->mem_obj && operand->mem_obj->on_varea_merge)
+ {
+ on_varea_merge = operand->mem_obj->on_varea_merge;
+ do {
+ operand_start = operand->start;
+ operand_size = operand->size;
+ LOG_D("search op_start=%p,op_size=0x%lx", operand_start, operand_size);
+
+ /* find a compatible neighbour if any and setup direction */
+ can_merge_fw = can_merge_bw = 0;
+ neighbour = _aspace_bst_search(aspace, operand_start - 1);
+ if (!neighbour || !_compatible(neighbour, operand_size, prop))
+ {
+ neighbour = _aspace_bst_search(aspace, operand_start + operand_size);
+ if (neighbour && _compatible(neighbour, operand_size, prop))
+ can_merge_bw = 1;
+ }
+ else
+ can_merge_fw = 1;
+
+ if (can_merge_fw || can_merge_bw)
+ {
+ /* merge operand with its predecessor or successor */
+ if (can_merge_fw)
+ {
+ error = _migrate_and_release_varea(aspace, neighbour, operand, on_varea_merge);
+ operand = neighbour;
+ }
+ else
+ error = _migrate_and_release_varea(aspace, operand, neighbour, on_varea_merge);
+
+ if (error == RT_EOK)
+ again = 1;
+ }
+ else
+ again = 0;
+
+ } while (again);
+ }
+ return operand;
+}
+
+/**
+ * Brief: expand and merge surrounding until not possible and
+ * setup the pvarea if new virt address region is installed
+ */
+static rt_bool_t _try_expand_and_merge_okay(rt_aspace_t aspace, rt_varea_t *pvarea,
+ void *alloc_va, rt_mm_va_hint_t hint,
+ struct _mapping_property *prop)
+{
+ int can_expand_fw;
+ int can_expand_bw;
+ rt_varea_t neighbour;
+ rt_varea_t new_region_at = RT_NULL;
+ rt_bool_t install_ok = RT_FALSE;
+ char *operand_start = alloc_va;
+ size_t operand_size = hint->map_size;
+
+ /* find a compatible neighbour if any and setup direction */
+ LOG_D("search op_start=%p,op_size=0x%lx", operand_start, operand_size);
+ can_expand_fw = can_expand_bw = 0;
+ neighbour = _aspace_bst_search(aspace, operand_start - 1);
+ if (!neighbour || !_compatible(neighbour, operand_size, prop))
+ {
+ neighbour = _aspace_bst_search(aspace, operand_start + operand_size);
+ if (neighbour && _compatible(neighbour, operand_size, prop))
+ can_expand_bw = 1;
+ }
+ else
+ can_expand_fw = 1;
+
+ if (can_expand_fw || can_expand_bw)
+ {
+ /* expand varea at head or tailing */
+ if (can_expand_fw)
+ operand_start = neighbour->start;
+ operand_size += neighbour->size;
+
+ LOG_D("expand op_start=%p,op_size=0x%lx", operand_start, operand_size);
+
+ if (_expand_varea(neighbour, operand_start, operand_size) == RT_EOK)
+ {
+ new_region_at = _merge_surrounding(aspace, neighbour, prop);
+ *pvarea = new_region_at;
+ install_ok = RT_TRUE;
+ }
+ }
+
+ return install_ok;
+}
+
+static rt_varea_t _varea_create(void *start, rt_size_t size);
+
+static int _insert_new_varea(rt_aspace_t aspace, rt_varea_t *pvarea,
+ void *alloc_va, rt_mm_va_hint_t hint)
+{
+ int err;
+ rt_varea_t varea = *pvarea;
+ if (varea == RT_NULL)
+ {
+ /* no preallocate buffer is provided, then create one */
+ varea = _varea_create(hint->prefer, hint->map_size);
+ hint->flags &= ~MMF_STATIC_ALLOC;
+ *pvarea = varea;
+ }
+
+ if (varea)
{
varea->start = alloc_va;
_aspace_bst_insert(aspace, varea);
+ err = RT_EOK;
}
else
{
- err = -RT_ENOSPC;
+ LOG_W("%s: Out of memory", __func__);
+ err = -RT_ENOMEM;
}
return err;
@@ -298,10 +502,64 @@ static inline void _varea_post_install(rt_varea_t varea, rt_aspace_t aspace,
}
/**
+ * Install new virtual address region into address space
+ * The pvarea will be set to the varea where new virt memory installed which can
+ * be a newly created or existed one.
+ *
+ * Note: caller must hold the aspace lock
+ */
+static int _varea_install(rt_aspace_t aspace, rt_varea_t *pvarea,
+ rt_mm_va_hint_t hint, struct _mapping_property *prop,
+ void **pva)
+{
+ void *alloc_va;
+ int err = RT_EOK;
+
+ if (hint->flags & MMF_MAP_FIXED)
+ {
+ alloc_va = hint->prefer;
+ err = _unmap_range_locked(aspace, alloc_va, hint->map_size);
+ if (err != RT_EOK)
+ {
+ /* Note: MAP_FIXED must failed if unable to unmap existing mapping */
+ LOG_I("%s: unmap range failed in %p with size 0x%lx, error=%d", __func__, alloc_va, hint->map_size, err);
+ }
+ }
+ else
+ {
+ alloc_va =
+ _find_free(aspace, hint->prefer, hint->map_size, hint->limit_start,
+ hint->limit_range_size, hint->flags);
+ if (alloc_va == RT_NULL)
+ err = -RT_ENOSPC;
+ }
+
+ if (alloc_va != RT_NULL)
+ {
+ /* TODO: fix to private mapping directly */
+ if (!_try_expand_and_merge_okay(aspace, pvarea, alloc_va, hint, prop))
+ {
+ err = _insert_new_varea(aspace, pvarea, alloc_va, hint);
+
+ if (err == RT_EOK)
+ _varea_post_install(*pvarea, aspace, prop->attr, prop->flags,
+ prop->mem_obj, prop->offset);
+ }
+
+ if (err == RT_EOK)
+ {
+ RT_ASSERT(*pvarea);
+ *pva = alloc_va;
+ }
+ }
+
+ return err;
+}
+
+/**
* restore context modified by varea install
- * caller must NOT hold the aspace lock
*/
-static void _varea_uninstall(rt_varea_t varea)
+void _varea_uninstall_locked(rt_varea_t varea)
{
rt_aspace_t aspace = varea->aspace;
@@ -313,16 +571,16 @@ static void _varea_uninstall(rt_varea_t varea)
rt_varea_pgmgr_pop_all(varea);
- WR_LOCK(aspace);
_aspace_bst_remove(aspace, varea);
- WR_UNLOCK(aspace);
}
-static int _mm_aspace_map(rt_aspace_t aspace, rt_varea_t varea, rt_size_t attr,
- mm_flag_t flags, rt_mem_obj_t mem_obj,
- rt_size_t offset)
+int _mm_aspace_map(rt_aspace_t aspace, rt_varea_t *pvarea, void **addr,
+ rt_size_t length, rt_size_t attr, mm_flag_t flags,
+ rt_mem_obj_t mem_obj, rt_size_t offset)
{
int err = RT_EOK;
+ rt_varea_t varea;
+ struct _mapping_property prop = INIT_PROP(mem_obj, offset, flags, attr);
WR_LOCK(aspace);
@@ -331,8 +589,8 @@ static int _mm_aspace_map(rt_aspace_t aspace, rt_varea_t varea, rt_size_t attr,
* .limit_start & .limit_range_size have default to be in range of aspace
* .flags is from parameter, and will be fill in varea if install successfully
*/
- struct rt_mm_va_hint hint = {.prefer = varea->start,
- .map_size = varea->size,
+ struct rt_mm_va_hint hint = {.prefer = *addr,
+ .map_size = length,
.limit_start = aspace->start,
.limit_range_size = aspace->size,
.flags = flags};
@@ -344,30 +602,34 @@ static int _mm_aspace_map(rt_aspace_t aspace, rt_varea_t varea, rt_size_t attr,
}
/* try to allocate a virtual address region for varea */
- err = _varea_install(aspace, varea, &hint);
- WR_UNLOCK(aspace);
+ err = _varea_install(aspace, pvarea, &hint, &prop, addr);
if (err == RT_EOK)
{
- /* fill in varea data */
- _varea_post_install(varea, aspace, attr, flags, mem_obj, offset);
-
+ varea = *pvarea;
if (MMF_TEST_CNTL(flags, MMF_PREFETCH))
{
/* do the MMU & TLB business */
err = _do_prefetch(aspace, varea, varea->start, varea->size);
if (err)
{
+ LOG_I("%s: failed to prefetch page into varea(%s)",
+ __func__, VAREA_NAME(varea));
+
/* restore data structure and MMU */
- _varea_uninstall(varea);
+ _varea_uninstall_locked(varea);
+ if (!(varea->flag & MMF_STATIC_ALLOC))
+ rt_free(varea);
}
}
}
+ WR_UNLOCK(aspace);
+
return err;
}
-rt_varea_t _varea_create(void *start, rt_size_t size)
+static rt_varea_t _varea_create(void *start, rt_size_t size)
{
rt_varea_t varea;
varea = (rt_varea_t)rt_malloc(sizeof(struct rt_varea));
@@ -385,13 +647,14 @@ rt_varea_t _varea_create(void *start, rt_size_t size)
static inline int _not_in_range(void *start, rt_size_t length,
void *limit_start, rt_size_t limit_size)
{
- if (start != RT_NULL)
- LOG_D("%s: [%p : %p] [%p : %p]", __func__, start, length, limit_start, limit_size);
/* assuming (base + length) will not overflow except (0) */
- return start != RT_NULL
+ int rc = start != RT_NULL
? (_IS_OVERFLOW(start, length) || start < limit_start ||
_IS_OVERSIZE(start, length, limit_start, limit_size))
: length > limit_size;
+ if (rc)
+ LOG_D("%s: [%p : %p] [%p : %p]", __func__, start, length, limit_start, limit_size);
+ return rc;
}
static inline int _not_align(void *start, rt_size_t length, rt_size_t mask)
@@ -411,9 +674,10 @@ int rt_aspace_map(rt_aspace_t aspace, void **addr, rt_size_t length,
rt_size_t attr, mm_flag_t flags, rt_mem_obj_t mem_obj,
rt_size_t offset)
{
- /* TODO check not in atomic context: irq, spinlock, local intr disable... */
int err;
- rt_varea_t varea;
+ rt_varea_t varea = RT_NULL;
+
+ RT_DEBUG_SCHEDULER_AVAILABLE(1);
if (!aspace || !addr || !mem_obj || length == 0)
{
@@ -433,26 +697,9 @@ int rt_aspace_map(rt_aspace_t aspace, void **addr, rt_size_t length,
}
else
{
- /* allocate the varea and fill in start and size */
- varea = _varea_create(*addr, length);
-
- if (varea)
- {
- err = _mm_aspace_map(aspace, varea, attr, flags, mem_obj, offset);
- if (err != RT_EOK)
- {
- rt_free(varea);
- }
- else
- {
- *addr = varea->start;
- }
- }
- else
- {
- LOG_W("%s: memory allocation failed", __func__);
- err = -RT_ENOMEM;
- }
+ RT_ASSERT((length & ARCH_PAGE_MASK) == 0);
+ RT_ASSERT(((long)*addr & ARCH_PAGE_MASK) == 0);
+ err = _mm_aspace_map(aspace, &varea, addr, length, attr, flags, mem_obj, offset);
}
if (err != RT_EOK)
@@ -485,7 +732,11 @@ int rt_aspace_map_static(rt_aspace_t aspace, rt_varea_t varea, void **addr,
varea->size = length;
varea->start = *addr;
flags |= MMF_STATIC_ALLOC;
- err = _mm_aspace_map(aspace, varea, attr, flags, mem_obj, offset);
+
+ /**
+ * todo: fix if mapping expand, the static varea is not used at all
+ */
+ err = _mm_aspace_map(aspace, &varea, addr, length, attr, flags, mem_obj, offset);
}
if (err != RT_EOK)
@@ -526,24 +777,22 @@ int _mm_aspace_map_phy(rt_aspace_t aspace, rt_varea_t varea,
}
else
{
- WR_LOCK(aspace);
- err = _varea_install(aspace, varea, hint);
- WR_UNLOCK(aspace);
+ struct _mapping_property prop = INIT_PROP(0, pa_off, hint->flags, attr);
+ WR_LOCK(aspace);
+ err = _varea_install(aspace, &varea, hint, &prop, &vaddr);
if (err == RT_EOK)
{
- _varea_post_install(varea, aspace, attr, hint->flags, NULL, pa_off);
-
- vaddr = varea->start;
-
- err = _do_named_map(aspace, varea->start, varea->size,
+ err = _do_named_map(aspace, varea, varea->start, varea->size,
(rt_size_t)pa_off, attr);
if (err != RT_EOK)
{
- _varea_uninstall(varea);
+ _varea_uninstall_locked(varea);
}
}
+
+ WR_UNLOCK(aspace);
}
if (ret_va)
@@ -567,6 +816,7 @@ int rt_aspace_map_phy(rt_aspace_t aspace, rt_mm_va_hint_t hint, rt_size_t attr,
rt_varea_t varea = _varea_create(hint->prefer, hint->map_size);
if (varea)
{
+ hint->flags &= ~MMF_STATIC_ALLOC;
err = _mm_aspace_map_phy(aspace, varea, hint, attr, pa_off, ret_va);
if (err != RT_EOK)
{
@@ -596,7 +846,7 @@ int rt_aspace_map_phy_static(rt_aspace_t aspace, rt_varea_t varea,
{
varea->start = hint->prefer;
varea->size = hint->map_size;
- hint->flags |= (MMF_MAP_FIXED | MMF_STATIC_ALLOC);
+ hint->flags |= (MMF_STATIC_ALLOC);
LOG_D("%s: start %p size %p phy at %p", __func__, varea->start, varea->size, pa_off << MM_PAGE_SHIFT);
err = _mm_aspace_map_phy(aspace, varea, hint, attr, pa_off, ret_va);
}
@@ -608,42 +858,216 @@ int rt_aspace_map_phy_static(rt_aspace_t aspace, rt_varea_t varea,
return err;
}
-void _aspace_unmap(rt_aspace_t aspace, void *addr)
+int _aspace_unmap(rt_aspace_t aspace, void *addr)
{
+ int error;
+ rt_varea_t varea;
+
WR_LOCK(aspace);
- rt_varea_t varea = _aspace_bst_search(aspace, addr);
- WR_UNLOCK(aspace);
+ varea = _aspace_bst_search(aspace, addr);
if (varea == RT_NULL)
{
- LOG_I("%s: No such entry found at %p\n", __func__, addr);
+ LOG_D("%s: No such entry found at %p\n", __func__, addr);
+ error = -RT_ENOENT;
}
-
- _varea_uninstall(varea);
- if (!(varea->flag & MMF_STATIC_ALLOC))
+ else
{
- rt_free(varea);
+ _varea_uninstall_locked(varea);
+ if (!(varea->flag & MMF_STATIC_ALLOC))
+ {
+ rt_free(varea);
+ }
+ error = RT_EOK;
}
+
+ WR_UNLOCK(aspace);
+ return error;
}
int rt_aspace_unmap(rt_aspace_t aspace, void *addr)
{
+ int error;
if (!aspace)
{
LOG_I("%s: Invalid input", __func__);
- return -RT_EINVAL;
+ error = -RT_EINVAL;
+ }
+ else if (_not_in_range(addr, 1, aspace->start, aspace->size))
+ {
+ LOG_I("%s: %lx not in range of aspace[%lx:%lx]", __func__, addr,
+ aspace->start, (char *)aspace->start + aspace->size);
+ error = -RT_EINVAL;
+ }
+ else
+ {
+ error = _aspace_unmap(aspace, addr);
}
- if (_not_in_range(addr, 1, aspace->start, aspace->size))
+ return error;
+}
+
+/**
+ * modify the property of existed varea by shrink its size. Mem_obj is
+ * notified to released the resource.
+ */
+static rt_err_t _shrink_varea(rt_varea_t varea, void *new_va, rt_size_t size)
+{
+ rt_err_t error;
+ rt_aspace_t aspace;
+ void *old_va;
+
+ if (varea->mem_obj && varea->mem_obj->on_varea_shrink)
+ error = varea->mem_obj->on_varea_shrink(varea, new_va, size);
+ else
+ error = -RT_EPERM;
+
+ if (error == RT_EOK)
+ {
+ aspace = varea->aspace;
+ old_va = varea->start;
+ varea->size = size;
+
+ if (old_va != new_va)
+ {
+ varea->start = new_va;
+ varea->offset += ((long)new_va - (long)old_va) >> MM_PAGE_SHIFT;
+ _aspace_bst_remove(aspace, varea);
+ _aspace_bst_insert(aspace, varea);
+ }
+ }
+ return error;
+}
+
+static rt_err_t _split_varea(rt_varea_t existed, char *ex_end, char *unmap_start, char *unmap_end, rt_size_t unmap_len)
+{
+ int error;
+ size_t rela_offset;
+ rt_varea_t subset;
+ char *subset_start;
+ size_t subset_size;
+
+ if (existed->mem_obj && existed->mem_obj->on_varea_split)
+ {
+ subset_start = unmap_end;
+ subset_size = ex_end - subset_start;
+ subset = _varea_create(subset_start, subset_size);
+ if (subset)
+ {
+ rela_offset = MM_PA_TO_OFF(subset_start - (char *)existed->start);
+ subset->aspace = existed->aspace;
+ subset->attr = existed->attr;
+ subset->mem_obj = existed->mem_obj;
+ subset->flag = existed->flag & ~MMF_STATIC_ALLOC;
+ subset->offset = existed->offset + rela_offset;
+ subset->frames = NULL;
+
+ error = existed->mem_obj->on_varea_split(existed, unmap_start, unmap_len, subset);
+ if (error == RT_EOK)
+ {
+ existed->size = unmap_start - (char *)existed->start;
+ _aspace_bst_insert(existed->aspace, subset);
+ }
+
+ if (error != RT_EOK)
+ rt_free(subset);
+ }
+ else
+ error = -RT_ENOMEM;
+ }
+ else
+ error = -RT_EPERM;
+
+ return error;
+}
+
+/* remove overlapped pages from varea */
+static int _remove_overlapped_varea(rt_varea_t existed, char *unmap_start, rt_size_t unmap_len)
+{
+ int error;
+ char *ex_start = existed->start;
+ char *ex_end = ex_start + existed->size;
+ char *unmap_end = unmap_start + unmap_len;
+
+ if (ex_start < unmap_start)
+ {
+ if (ex_end > unmap_end)
+ error = _split_varea(existed, ex_end, unmap_start, unmap_end, unmap_len);
+ else
+ error = _shrink_varea(existed, ex_start, unmap_start - ex_start);
+ }
+ else if (ex_end > unmap_end)
+ error = _shrink_varea(existed, unmap_end, ex_end - unmap_end);
+ else
+ {
+ _varea_uninstall_locked(existed);
+ if (VAREA_NOT_STATIC(existed))
+ {
+ rt_free(existed);
+ }
+ error = RT_EOK;
+ }
+
+ return error;
+}
+
+static int _unmap_range_locked(rt_aspace_t aspace, void *addr, size_t length)
+{
+ int error = RT_EOK;
+ rt_varea_t existed;
+ struct _mm_range unmap_range;
+
+ unmap_range.start = addr;
+ unmap_range.end = addr + length - 1;
+
+ existed = _aspace_bst_search_overlap(aspace, unmap_range);
+ while (existed)
+ {
+ error = _remove_overlapped_varea(existed, addr, length);
+
+ if (error == RT_EOK)
+ existed = _aspace_bst_search_overlap(aspace, unmap_range);
+ else
+ break;
+ }
+
+ return error;
+}
+
+int rt_aspace_unmap_range(rt_aspace_t aspace, void *addr, size_t length)
+{
+ int error;
+
+ if (!aspace)
+ {
+ LOG_I("%s: Invalid input", __func__);
+ error = -RT_EINVAL;
+ }
+ else if (_not_in_range(addr, length, aspace->start, aspace->size))
{
LOG_I("%s: %lx not in range of aspace[%lx:%lx]", __func__, addr,
aspace->start, (char *)aspace->start + aspace->size);
- return -RT_EINVAL;
+ error = -RT_EINVAL;
}
+ else if (!ALIGNED(addr))
+ {
+ LOG_I("%s(addr=%p): Unaligned address", __func__, addr);
+ error = -RT_EINVAL;
+ }
+ else
+ {
+ /**
+ * Brief: re-arrange the address space to remove existing pages mapping
+ * in [unmap_start, unmap_start + unmap_len)
+ */
+ length = RT_ALIGN(length, ARCH_PAGE_SIZE);
- _aspace_unmap(aspace, addr);
+ WR_LOCK(aspace);
+ error = _unmap_range_locked(aspace, addr, length);
+ WR_UNLOCK(aspace);
+ }
- return RT_EOK;
+ return error;
}
static inline void *_lower(void *a, void *b)
@@ -843,6 +1267,7 @@ int rt_varea_map_page(rt_varea_t varea, void *vaddr, void *page)
{
err = _do_named_map(
varea->aspace,
+ varea,
vaddr,
ARCH_PAGE_SIZE,
MM_PA_TO_OFF(page_pa),
@@ -853,8 +1278,6 @@ int rt_varea_map_page(rt_varea_t varea, void *vaddr, void *page)
return err;
}
-#define ALIGNED(addr) (!((rt_size_t)(addr) & ARCH_PAGE_MASK))
-
int rt_varea_unmap_page(rt_varea_t varea, void *vaddr)
{
void *va_aligned = (void *)RT_ALIGN_DOWN((rt_base_t)vaddr, ARCH_PAGE_SIZE);
@@ -883,6 +1306,7 @@ int rt_varea_map_range(rt_varea_t varea, void *vaddr, void *paddr, rt_size_t len
{
err = _do_named_map(
varea->aspace,
+ varea,
vaddr,
length,
MM_PA_TO_OFF(paddr),
@@ -986,3 +1410,353 @@ void rt_aspace_print_all(rt_aspace_t aspace)
{
rt_aspace_traversal(aspace, _dump, NULL);
}
+
+static int _count_vsz(rt_varea_t varea, void *arg)
+{
+ rt_base_t *pvsz = arg;
+ RT_ASSERT(varea);
+ *pvsz = *pvsz + varea->size;
+ return 0;
+}
+
+rt_base_t rt_aspace_count_vsz(rt_aspace_t aspace)
+{
+ rt_base_t vsz = 0;
+ rt_aspace_traversal(aspace, _count_vsz, &vsz);
+ return vsz;
+}
+
+static int _dup_varea(rt_varea_t src_varea, void *arg)
+{
+ int err;
+ rt_aspace_t dst = arg;
+ rt_aspace_t src = src_varea->aspace;
+
+ void *pa = RT_NULL;
+ void *va = RT_NULL;
+ rt_mem_obj_t mem_obj = src_varea->mem_obj;
+
+ if (!mem_obj)
+ {
+ /* duplicate a physical mapping */
+ pa = rt_hw_mmu_v2p(src, (void *)src_varea->start);
+ RT_ASSERT(pa != ARCH_MAP_FAILED);
+ struct rt_mm_va_hint hint = {.flags = src_varea->flag,
+ .limit_range_size = dst->size,
+ .limit_start = dst->start,
+ .prefer = src_varea->start,
+ .map_size = src_varea->size};
+ err = rt_aspace_map_phy(dst, &hint, src_varea->attr,
+ MM_PA_TO_OFF(pa), &va);
+ if (err != RT_EOK)
+ {
+ LOG_W("%s: aspace map failed at %p with size %p", __func__,
+ src_varea->start, src_varea->size);
+ }
+ }
+ else
+ {
+ /* duplicate a mem_obj backing mapping */
+ rt_base_t flags = src_varea->flag | MMF_MAP_FIXED;
+ flags &= ~MMF_STATIC_ALLOC;
+ flags &= ~MMF_PREFETCH;
+ va = src_varea->start;
+
+ err = rt_aspace_map(dst, &va, src_varea->size, src_varea->attr,
+ flags, mem_obj, src_varea->offset);
+ if (err != RT_EOK)
+ {
+ LOG_W("%s: aspace map failed at %p with size %p", __func__,
+ src_varea->start, src_varea->size);
+ }
+ }
+
+ if (va != (void *)src_varea->start)
+ {
+ return -1;
+ }
+ return 0;
+}
+
+struct _compare_param {
+ rt_aspace_t dst;
+ int rc;
+};
+
+rt_err_t rt_aspace_duplicate_locked(rt_aspace_t src, rt_aspace_t dst)
+{
+ return rt_aspace_traversal(src, _dup_varea, dst);
+}
+
+rt_inline int _varea_same(rt_varea_t a, rt_varea_t b)
+{
+ return a->attr == b->attr && a->flag == b->flag && a->mem_obj == b->mem_obj;
+}
+
+rt_inline void _dump_varea(rt_varea_t varea)
+{
+ LOG_W("%s(attr=0x%lx, flags=0x%lx, start=0x%lx, size=0x%lx, mem_obj=%p)", VAREA_NAME(varea), varea->attr, varea->flag, varea->start, varea->size, varea->mem_obj);
+}
+
+static int _compare_varea(rt_varea_t src_varea, void *arg)
+{
+ struct _compare_param *param = arg;
+ rt_varea_t dst_varea;
+ rt_aspace_t dst = param->dst;
+ rt_aspace_t src = src_varea->aspace;
+
+ dst_varea = _aspace_bst_search(dst, src_varea->start);
+ if (dst_varea)
+ {
+ char *buf1 = rt_pages_alloc_ext(0, PAGE_ANY_AVAILABLE);
+ char *buf2 = rt_pages_alloc_ext(0, PAGE_ANY_AVAILABLE);
+ char *vend = src_varea->start + src_varea->size;
+ for (char *i = src_varea->start; i < vend; i += ARCH_PAGE_SIZE)
+ {
+ int rc;
+ rt_aspace_page_get(src, i, buf1);
+ rt_aspace_page_get(dst, i, buf2);
+ rc = memcmp(buf1, buf2, ARCH_PAGE_SIZE);
+ if (rc)
+ {
+ if (param->rc == 0)
+ param->rc = rc;
+ LOG_E("%s(a_varea=%s, b_varea=%s)", __func__, VAREA_NAME(src_varea), VAREA_NAME(dst_varea));
+ _dump_varea(src_varea);
+ _dump_varea(dst_varea);
+ RT_ASSERT(0);
+ }
+ }
+
+ rt_pages_free(buf1, 0);
+ rt_pages_free(buf2, 0);
+ }
+ else
+ {
+ param->rc = -RT_ENOENT;
+ }
+
+ return 0;
+}
+
+rt_err_t rt_aspace_compare(rt_aspace_t src, rt_aspace_t dst)
+{
+ struct _compare_param param = {.rc = 0, .dst = dst};
+ rt_aspace_traversal(src, _compare_varea, &param);
+ return param.rc;
+}
+
+/* dst are page aligned */
+rt_inline rt_err_t _page_put(rt_varea_t varea, void *page_va, void *buffer)
+{
+ struct rt_aspace_io_msg iomsg;
+ rt_err_t rc;
+
+ rt_mm_io_msg_init(&iomsg, VAREA_VA_TO_OFFSET(varea, page_va), page_va, buffer);
+ varea->mem_obj->page_write(varea, &iomsg);
+
+ if (iomsg.response.status == MM_FAULT_STATUS_UNRECOVERABLE)
+ rc = -RT_ERROR;
+ else
+ rc = RT_EOK;
+ return rc;
+}
+
+/* dst are page aligned */
+rt_inline rt_err_t _page_get(rt_varea_t varea, void *page_va, void *buffer)
+{
+ struct rt_aspace_io_msg iomsg;
+ rt_err_t rc;
+
+ rt_mm_io_msg_init(&iomsg, VAREA_VA_TO_OFFSET(varea, page_va), page_va, buffer);
+ varea->mem_obj->page_read(varea, &iomsg);
+
+ if (iomsg.response.status == MM_FAULT_STATUS_UNRECOVERABLE)
+ rc = -RT_ERROR;
+ else
+ rc = RT_EOK;
+ return rc;
+}
+
+#ifdef RT_USING_SMART
+#include "lwp.h"
+rt_inline rt_aspace_t _current_uspace(void)
+{
+ rt_lwp_t this_proc = lwp_self();
+ return this_proc ? this_proc->aspace : RT_NULL;
+}
+#else
+rt_inline rt_aspace_t _current_uspace(void)
+{
+ return RT_NULL;
+}
+#endif
+
+rt_err_t rt_aspace_page_get_phy(rt_aspace_t aspace, void *page_va, void *buffer)
+{
+ rt_err_t rc = -RT_ERROR;
+
+ char *frame_ka = rt_hw_mmu_v2p(aspace, page_va);
+ if (frame_ka != ARCH_MAP_FAILED)
+ {
+ frame_ka = rt_kmem_p2v(frame_ka);
+ if (frame_ka)
+ {
+ rt_memcpy(buffer, frame_ka, ARCH_PAGE_SIZE);
+ rc = RT_EOK;
+ }
+ else if (aspace == _current_uspace() || aspace == &rt_kernel_space)
+ {
+ /* direct IO */
+ rt_memcpy(buffer, page_va, ARCH_PAGE_SIZE);
+ rc = RT_EOK;
+ }
+ else
+ {
+ /* user memory region remap ? */
+ LOG_W("%s(aspace=0x%lx,va=%p): Operation not support",
+ __func__, aspace, page_va);
+ rc = -RT_ENOSYS;
+ }
+ }
+ else
+ {
+ LOG_W("%s(aspace=0x%lx,va=%p): PTE not existed",
+ __func__, aspace, page_va);
+ rc = -RT_ENOENT;
+ }
+ return rc;
+}
+
+rt_err_t rt_aspace_page_put_phy(rt_aspace_t aspace, void *page_va, void *buffer)
+{
+ rt_err_t rc = -RT_ERROR;
+
+ char *frame_ka = rt_hw_mmu_v2p(aspace, page_va);
+ if (frame_ka != ARCH_MAP_FAILED)
+ {
+ frame_ka = rt_kmem_p2v(frame_ka);
+ if (frame_ka)
+ {
+ rt_memcpy(frame_ka, buffer, ARCH_PAGE_SIZE);
+ rc = RT_EOK;
+ }
+ else if (aspace == _current_uspace() || aspace == &rt_kernel_space)
+ {
+ /* direct IO */
+ rt_memcpy(page_va, buffer, ARCH_PAGE_SIZE);
+ rc = RT_EOK;
+ }
+ else
+ {
+ /* user memory region remap ? */
+ LOG_W("%s(aspace=0x%lx,va=%p): Operation not support",
+ __func__, aspace, page_va);
+ rc = -RT_ENOSYS;
+ }
+ }
+ else
+ {
+ LOG_W("%s(aspace=0x%lx,va=%p): PTE not existed",
+ __func__, aspace, page_va);
+ rc = -RT_ENOENT;
+ }
+
+ return rc;
+}
+
+rt_err_t rt_aspace_page_put(rt_aspace_t aspace, void *page_va, void *buffer)
+{
+ rt_err_t rc = -RT_ERROR;
+ rt_varea_t varea;
+
+ RT_ASSERT(aspace);
+ RD_LOCK(aspace);
+ varea = _aspace_bst_search(aspace, page_va);
+ if (varea && ALIGNED(page_va))
+ {
+ if (varea->mem_obj)
+ {
+ if (varea->mem_obj->page_write)
+ {
+ if (rt_varea_is_private_locked(varea))
+ {
+ RDWR_LOCK(aspace);
+ struct rt_aspace_fault_msg msg;
+ msg.fault_op = MM_FAULT_OP_WRITE;
+ msg.fault_type = MM_FAULT_TYPE_ACCESS_FAULT;
+ msg.fault_vaddr = page_va;
+ rc = rt_varea_fix_private_locked(varea, rt_hw_mmu_v2p(aspace, page_va),
+ &msg, RT_TRUE);
+ RDWR_UNLOCK(aspace);
+ if (rc == MM_FAULT_FIXABLE_TRUE)
+ {
+ varea = _aspace_bst_search(aspace, page_va);
+ rc = _page_put(varea, page_va, buffer);
+ }
+ else
+ rc = -RT_ERROR;
+ }
+ else
+ rc = _page_put(varea, page_va, buffer);
+ }
+ else
+ {
+ rc = -RT_EINVAL;
+ LOG_I("%s: Operation not allowed", __func__);
+ }
+ }
+ else
+ {
+ rc = rt_aspace_page_put_phy(aspace, page_va, buffer);
+ }
+ }
+ else
+ rc = -RT_EINVAL;
+ RD_UNLOCK(aspace);
+
+ return rc;
+}
+
+rt_err_t rt_aspace_page_get(rt_aspace_t aspace, void *page_va, void *buffer)
+{
+ rt_err_t rc = -RT_ERROR;
+ rt_varea_t varea;
+
+ /* TODO: cache the last search item */
+ RT_ASSERT(aspace);
+ RD_LOCK(aspace);
+ varea = _aspace_bst_search(aspace, page_va);
+ if (varea && ALIGNED(page_va))
+ {
+ if (varea->mem_obj)
+ {
+ if (varea->mem_obj->page_read)
+ {
+ rc = _page_get(varea, page_va, buffer);
+ }
+ else
+ {
+ LOG_I("%s: Operation not allowed", __func__);
+ }
+ }
+ else
+ {
+ rc = rt_aspace_page_get_phy(aspace, page_va, buffer);
+ }
+ }
+ else
+ {
+ rc = -RT_EINVAL;
+ LOG_D("%s(va=%p,varea=0x%lx): Invalid address",
+ __func__, page_va, varea);
+ }
+ RD_UNLOCK(aspace);
+
+ return rc;
+}
+
+rt_varea_t rt_aspace_query(rt_aspace_t aspace, void *vaddr)
+{
+ return _aspace_bst_search(aspace, vaddr);
+}
diff --git a/components/mm/mm_aspace.h b/components/mm/mm_aspace.h
index 6c24040368..5748647903 100644
--- a/components/mm/mm_aspace.h
+++ b/components/mm/mm_aspace.h
@@ -1,23 +1,26 @@
/*
- * Copyright (c) 2006-2022, RT-Thread Development Team
+ * Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-11-14 WangXiaoyao the first version
+ * 2023-08-17 Shell Add unmap_range for MAP_PRIVATE
*/
#ifndef __MM_ASPACE_H__
#define __MM_ASPACE_H__
#include <rthw.h>
#include <rtthread.h>
-#include <stddef.h>
#include "avl_adpt.h"
#include "mm_fault.h"
#include "mm_flag.h"
+#include <stddef.h>
+#include <string.h>
+
#define MM_PAGE_SHIFT 12
#define MM_PA_TO_OFF(pa) ((uintptr_t)(pa) >> MM_PAGE_SHIFT)
#define PV_OFFSET (rt_kmem_pvoff())
@@ -55,6 +58,7 @@ typedef struct rt_aspace
struct _aspace_tree tree;
struct rt_mutex bst_lock;
+ struct rt_mem_obj *private_object;
rt_uint64_t asid;
} *rt_aspace_t;
@@ -97,7 +101,25 @@ typedef struct rt_mem_obj
/* do post close bushiness like def a ref */
void (*on_varea_close)(struct rt_varea *varea);
- void (*on_page_offload)(struct rt_varea *varea, void *vaddr, rt_size_t size);
+ /* do preparation for address space modification of varea */
+ rt_err_t (*on_varea_shrink)(struct rt_varea *varea, void *new_vaddr, rt_size_t size);
+ /* do preparation for address space modification of varea */
+ rt_err_t (*on_varea_expand)(struct rt_varea *varea, void *new_vaddr, rt_size_t size);
+ /**
+ * this is like an on_varea_open() to `subset`, and an on_varea_shrink() to `existed`
+ * while resource can migrate from `existed` to `subset` at the same time
+ */
+ rt_err_t (*on_varea_split)(struct rt_varea *existed, void *unmap_start,
+ rt_size_t unmap_len, struct rt_varea *subset);
+ /**
+ * this is like a on_varea_expand() to `merge_to` and on_varea_close() to `merge_from`
+ * while resource can migrate from `merge_from` to `merge_to` at the same time
+ */
+ rt_err_t (*on_varea_merge)(struct rt_varea *merge_to, struct rt_varea *merge_from);
+
+ /* dynamic mem_obj API */
+ void (*page_read)(struct rt_varea *varea, struct rt_aspace_io_msg *msg);
+ void (*page_write)(struct rt_varea *varea, struct rt_aspace_io_msg *msg);
const char *(*get_name)(rt_varea_t varea);
} *rt_mem_obj_t;
@@ -110,6 +132,8 @@ enum rt_mmu_cntl
MMU_CNTL_CACHE,
MMU_CNTL_READONLY,
MMU_CNTL_READWRITE,
+ MMU_CNTL_OFFLOAD,
+ MMU_CNTL_INSTALL,
MMU_CNTL_DUMMY_END,
};
@@ -122,8 +146,11 @@ enum rt_mmu_cntl
#define WR_UNLOCK(aspace) \
rt_thread_self() ? rt_mutex_release(&(aspace)->bst_lock) : 0
+/* FIXME: fix rd_lock */
#define RD_LOCK(aspace) WR_LOCK(aspace)
#define RD_UNLOCK(aspace) WR_UNLOCK(aspace)
+#define RDWR_LOCK(aspace) ((void)aspace)
+#define RDWR_UNLOCK(aspace) ((void)aspace)
rt_aspace_t rt_aspace_create(void *start, rt_size_t length, void *pgtbl);
@@ -176,14 +203,50 @@ int rt_aspace_map_phy_static(rt_aspace_t aspace, rt_varea_t varea,
rt_mm_va_hint_t hint, rt_size_t attr, rt_size_t pa_off,
void **ret_va);
+/** map a private memory region to aspace */
+int rt_aspace_map_private(rt_aspace_t aspace, void **addr, rt_size_t length,
+ rt_size_t attr, mm_flag_t flags);
+
/**
- * @brief Remove any mappings overlap the range [addr, addr + bytes)
+ * @brief Remove mappings containing address specified by addr
*
- * @param aspace
+ * @param aspace target virtual address space
+ * @param addr addresses that mapping to be removed contains
+ * @return int rt errno
+ */
+int rt_aspace_unmap(rt_aspace_t aspace, void *addr);
+
+/**
+ * @brief Remove pages of existed mappings in the range [addr, addr+length)
+ * Length is automatically rounded up to the next multiple of the page size.
+ *
+ * @param aspace target virtual address space
+ * @param addr the beginning of the range of pages to be unmapped
+ * @param length length of range in bytes
+ * @return int rt errno
+ */
+int rt_aspace_unmap_range(rt_aspace_t aspace, void *addr, size_t length);
+
+/**
+ * @brief Remove pages of existed mappings in the range [addr, addr+length)
+ * Length is automatically rounded up to the next multiple of the page size.
+ *
+ * @param aspace target virtual address space
+ * @param addr the beginning of the range of pages to be unmapped
+ * @param length length of range in bytes
+ * @return int rt errno
+ */
+int rt_aspace_unmap_range(rt_aspace_t aspace, void *addr, size_t length);
+
+/**
+ * @brief Remove pages of existed mappings in the range [addr, addr+length)
+ * Length is automatically rounded up to the next multiple of the page size.
+ *
+ * @param aspace target virtual address space
* @param addr
* @return int
*/
-int rt_aspace_unmap(rt_aspace_t aspace, void *addr);
+int rt_aspace_unmap_range(rt_aspace_t aspace, void *addr, size_t length);
int rt_aspace_control(rt_aspace_t aspace, void *addr, enum rt_mmu_cntl cmd);
@@ -191,14 +254,28 @@ int rt_aspace_load_page(rt_aspace_t aspace, void *addr, rt_size_t npage);
int rt_aspace_offload_page(rt_aspace_t aspace, void *addr, rt_size_t npage);
+rt_err_t rt_aspace_page_put(rt_aspace_t aspace, void *page_va, void *buffer);
+
+rt_err_t rt_aspace_page_get(rt_aspace_t aspace, void *page_va, void *buffer);
+
int rt_aspace_traversal(rt_aspace_t aspace,
int (*fn)(rt_varea_t varea, void *arg), void *arg);
void rt_aspace_print_all(rt_aspace_t aspace);
+rt_base_t rt_aspace_count_vsz(rt_aspace_t aspace);
+
+rt_varea_t rt_aspace_query(rt_aspace_t aspace, void *vaddr);
+
+rt_err_t rt_aspace_duplicate_locked(rt_aspace_t src, rt_aspace_t dst);
+rt_err_t rt_aspace_fork(rt_aspace_t *psrc, rt_aspace_t *pdst);
+rt_err_t rt_aspace_compare(rt_aspace_t src, rt_aspace_t dst);
+
/**
* @brief Map one page to varea
*
+ * @note caller should take the read/write lock
+ *
* @param varea target varea
* @param addr user address
* @param page the page frame to be mapped
@@ -209,6 +286,8 @@ int rt_varea_map_page(rt_varea_t varea, void *vaddr, void *page);
/**
* @brief Unmap one page in varea
*
+ * @note caller should take the read/write lock
+ *
* @param varea target varea
* @param addr user address
* @param page the page frame to be mapped
@@ -252,7 +331,16 @@ int rt_varea_unmap_range(rt_varea_t varea, void *vaddr, rt_size_t length);
*/
void rt_varea_pgmgr_insert(rt_varea_t varea, void *page_addr);
-rt_ubase_t rt_kmem_pvoff(void);
+rt_inline rt_mem_obj_t rt_mem_obj_create(rt_mem_obj_t source)
+{
+ rt_mem_obj_t target;
+ target = rt_malloc(sizeof(*target));
+ if (target)
+ memcpy(target, source, sizeof(*target));
+ return target;
+}
+
+const rt_ubase_t rt_kmem_pvoff(void);
void rt_kmem_pvoff_set(rt_ubase_t pvoff);
@@ -260,6 +348,8 @@ int rt_kmem_map_phy(void *va, void *pa, rt_size_t length, rt_size_t attr);
void *rt_kmem_v2p(void *vaddr);
+void *rt_kmem_p2v(void *paddr);
+
void rt_kmem_list(void);
#endif /* __MM_ASPACE_H__ */
diff --git a/components/mm/mm_fault.c b/components/mm/mm_fault.c
index a73c173acf..4dcef168a5 100644
--- a/components/mm/mm_fault.c
+++ b/components/mm/mm_fault.c
@@ -6,6 +6,7 @@
* Change Logs:
* Date Author Notes
* 2022-12-06 WangXiaoyao the first version
+ * 2023-08-19 Shell Support PRIVATE mapping and COW
*/
#include <rtthread.h>
@@ -23,27 +24,21 @@
#include <mmu.h>
#include <tlb.h>
-#define UNRECOVERABLE 0
-#define RECOVERABLE 1
-
static int _fetch_page(rt_varea_t varea, struct rt_aspace_fault_msg *msg)
{
- int err = UNRECOVERABLE;
- msg->response.status = MM_FAULT_STATUS_UNRECOVERABLE;
- msg->response.vaddr = 0;
- msg->response.size = 0;
+ int err = MM_FAULT_FIXABLE_FALSE;
if (varea->mem_obj && varea->mem_obj->on_page_fault)
{
varea->mem_obj->on_page_fault(varea, msg);
- err = _varea_map_with_msg(varea, msg);
- err = (err == RT_EOK ? RECOVERABLE : UNRECOVERABLE);
+ err = rt_varea_map_with_msg(varea, msg);
+ err = (err == RT_EOK ? MM_FAULT_FIXABLE_TRUE : MM_FAULT_FIXABLE_FALSE);
}
return err;
}
static int _read_fault(rt_varea_t varea, void *pa, struct rt_aspace_fault_msg *msg)
{
- int err = UNRECOVERABLE;
+ int err = MM_FAULT_FIXABLE_FALSE;
if (msg->fault_type == MM_FAULT_TYPE_PAGE_FAULT)
{
RT_ASSERT(pa == ARCH_MAP_FAILED);
@@ -59,19 +54,37 @@ static int _read_fault(rt_varea_t varea, void *pa, struct rt_aspace_fault_msg *m
static int _write_fault(rt_varea_t varea, void *pa, struct rt_aspace_fault_msg *msg)
{
- int err = UNRECOVERABLE;
- if (msg->fault_type == MM_FAULT_TYPE_PAGE_FAULT)
+ rt_aspace_t aspace = varea->aspace;
+ int err = MM_FAULT_FIXABLE_FALSE;
+
+ if (rt_varea_is_private_locked(varea))
+ {
+ if (VAREA_IS_WRITABLE(varea) && (
+ msg->fault_type == MM_FAULT_TYPE_ACCESS_FAULT ||
+ msg->fault_type == MM_FAULT_TYPE_PAGE_FAULT))
+ {
+ RDWR_LOCK(aspace);
+ err = rt_varea_fix_private_locked(varea, pa, msg, RT_FALSE);
+ RDWR_UNLOCK(aspace);
+ if (err == MM_FAULT_FIXABLE_FALSE)
+ LOG_I("%s: fix private failure", __func__);
+ }
+ else
+ {
+ LOG_I("%s: No permission on %s(attr=0x%lx)", __func__, VAREA_NAME(varea), varea->attr);
+ }
+ }
+ else if (msg->fault_type == MM_FAULT_TYPE_PAGE_FAULT)
{
RT_ASSERT(pa == ARCH_MAP_FAILED);
RT_ASSERT(!(varea->flag & MMF_PREFETCH));
err = _fetch_page(varea, msg);
- }
- else if (msg->fault_type == MM_FAULT_TYPE_ACCESS_FAULT &&
- varea->flag & MMF_COW)
- {
+ if (err == MM_FAULT_FIXABLE_FALSE)
+ LOG_I("%s: page fault failure", __func__);
}
else
{
+ LOG_D("%s: can not fix", __func__);
/* signal a fault to user? */
}
return err;
@@ -79,7 +92,7 @@ static int _write_fault(rt_varea_t varea, void *pa, struct rt_aspace_fault_msg *
static int _exec_fault(rt_varea_t varea, void *pa, struct rt_aspace_fault_msg *msg)
{
- int err = UNRECOVERABLE;
+ int err = MM_FAULT_FIXABLE_FALSE;
if (msg->fault_type == MM_FAULT_TYPE_PAGE_FAULT)
{
RT_ASSERT(pa == ARCH_MAP_FAILED);
@@ -91,10 +104,13 @@ static int _exec_fault(rt_varea_t varea, void *pa, struct rt_aspace_fault_msg *m
int rt_aspace_fault_try_fix(rt_aspace_t aspace, struct rt_aspace_fault_msg *msg)
{
- int err = UNRECOVERABLE;
+ int err = MM_FAULT_FIXABLE_FALSE;
uintptr_t va = (uintptr_t)msg->fault_vaddr;
va &= ~ARCH_PAGE_MASK;
msg->fault_vaddr = (void *)va;
+ rt_mm_fault_res_init(&msg->response);
+
+ RT_DEBUG_SCHEDULER_AVAILABLE(1);
if (aspace)
{
@@ -105,21 +121,34 @@ int rt_aspace_fault_try_fix(rt_aspace_t aspace, struct rt_aspace_fault_msg *msg)
if (varea)
{
void *pa = rt_hw_mmu_v2p(aspace, msg->fault_vaddr);
- msg->off = ((char *)msg->fault_vaddr - (char *)varea->start) >> ARCH_PAGE_SHIFT;
-
- /* permission checked by fault op */
- switch (msg->fault_op)
+ if (pa != ARCH_MAP_FAILED && msg->fault_type == MM_FAULT_TYPE_PAGE_FAULT)
{
- case MM_FAULT_OP_READ:
- err = _read_fault(varea, pa, msg);
- break;
- case MM_FAULT_OP_WRITE:
- err = _write_fault(varea, pa, msg);
- break;
- case MM_FAULT_OP_EXECUTE:
- err = _exec_fault(varea, pa, msg);
- break;
+ LOG_D("%s(fault=%p) has already fixed", __func__, msg->fault_vaddr);
+ err = MM_FAULT_FIXABLE_TRUE;
}
+ else
+ {
+ LOG_D("%s(varea=%s,fault=%p,fault_op=%d,phy=%p)", __func__, VAREA_NAME(varea), msg->fault_vaddr, msg->fault_op, pa);
+ msg->off = varea->offset + ((long)msg->fault_vaddr - (long)varea->start) / ARCH_PAGE_SIZE;
+
+ /* permission checked by fault op */
+ switch (msg->fault_op)
+ {
+ case MM_FAULT_OP_READ:
+ err = _read_fault(varea, pa, msg);
+ break;
+ case MM_FAULT_OP_WRITE:
+ err = _write_fault(varea, pa, msg);
+ break;
+ case MM_FAULT_OP_EXECUTE:
+ err = _exec_fault(varea, pa, msg);
+ break;
+ }
+ }
+ }
+ else
+ {
+ LOG_I("%s: varea not found at 0x%lx", __func__, msg->fault_vaddr);
}
RD_UNLOCK(aspace);
}
diff --git a/components/mm/mm_fault.h b/components/mm/mm_fault.h
index eeb413025f..28671aef74 100644
--- a/components/mm/mm_fault.h
+++ b/components/mm/mm_fault.h
@@ -20,12 +20,8 @@
#define MM_FAULT_STATUS_OK_MAPPED 1
#define MM_FAULT_STATUS_UNRECOVERABLE 4
-struct rt_mm_fault_res
-{
- void *vaddr;
- rt_size_t size;
- int status;
-};
+#define MM_FAULT_FIXABLE_FALSE 0
+#define MM_FAULT_FIXABLE_TRUE 1
enum rt_mm_fault_op
{
@@ -36,10 +32,41 @@ enum rt_mm_fault_op
enum rt_mm_fault_type
{
+ /**
+ * Occurs when an instruction attempts to access a memory address that it
+ * does not have permission to access
+ */
MM_FAULT_TYPE_ACCESS_FAULT,
+
+ /**
+ * Occurs when a load or store instruction accesses a virtual memory
+ * address that is not currently mapped to a physical memory page
+ */
MM_FAULT_TYPE_PAGE_FAULT,
+
+ /**
+ * Occurs like a SIGBUS
+ */
MM_FAULT_TYPE_BUS_ERROR,
+
MM_FAULT_TYPE_GENERIC,
+ __PRIVATE_PAGE_INSERT,
+};
+
+enum rt_mm_hint_prefetch
+{
+ MM_FAULT_HINT_PREFETCH_NONE,
+ MM_FAULT_HINT_PREFETCH_READY,
+};
+
+struct rt_mm_fault_res
+{
+ void *vaddr;
+ rt_size_t size;
+ int status;
+
+ /* hint for prefetch strategy */
+ enum rt_mm_hint_prefetch hint;
};
struct rt_aspace_fault_msg
@@ -52,8 +79,36 @@ struct rt_aspace_fault_msg
struct rt_mm_fault_res response;
};
+struct rt_aspace_io_msg
+{
+ /* offset in varea */
+ rt_size_t off;
+ /* fault address in target address space */
+ void *fault_vaddr;
+ /* read/write buffer in kernel space */
+ void *buffer_vaddr;
+
+ struct rt_mm_fault_res response;
+};
+
+rt_inline void rt_mm_fault_res_init(struct rt_mm_fault_res *res)
+{
+ res->vaddr = RT_NULL;
+ res->size = 0;
+ res->hint = MM_FAULT_HINT_PREFETCH_NONE;
+ res->status = MM_FAULT_STATUS_UNRECOVERABLE;
+}
+
+rt_inline void rt_mm_io_msg_init(struct rt_aspace_io_msg *io, rt_size_t off, void *fault_vaddr, void *buffer_vaddr)
+{
+ io->off = off;
+ io->fault_vaddr = fault_vaddr;
+ io->buffer_vaddr = buffer_vaddr;
+ rt_mm_fault_res_init(&io->response);
+}
+
struct rt_aspace;
-/* MMU base page fault handler, return 1 is fixable */
+/* MMU base page fault handler, MM_FAULT_FIXABLE_TRUE/MM_FAULT_FIXABLE_FALSE will be returned */
int rt_aspace_fault_try_fix(struct rt_aspace *aspace, struct rt_aspace_fault_msg *msg);
#endif /* __MM_FAULT_H__ */
diff --git a/components/mm/mm_flag.h b/components/mm/mm_flag.h
index 4104a60b8f..6d58027b36 100644
--- a/components/mm/mm_flag.h
+++ b/components/mm/mm_flag.h
@@ -10,6 +10,8 @@
#ifndef __MM_FLAG_H__
#define __MM_FLAG_H__
+#include <rtthread.h>
+
/**
* @brief mm_flag_t
* |max ------- 7|6 ----- 0|
@@ -27,10 +29,19 @@ typedef unsigned long mm_flag_t;
enum mm_flag_cntl
{
/**
- * @brief Indicate a possible COW mapping
+ * @brief Modifications to the mapped data shall be visible only to the
+ * aspace only and shall not change the underlying object. It is
+ * unspecified whether modifications to the underlying object done after
+ * the MAP_PRIVATE mapping is established are visible through the
+ * MAP_PRIVATE mapping.
*/
MMF_MAP_PRIVATE = _DEF_FLAG(0),
- MMF_COW = _DEF_FLAG(1),
+
+ /**
+ * @brief Same as MMF_MAP_PRIVATE, except the modification after mapping is
+ * invisible to the varea
+ */
+ MMF_MAP_PRIVATE_DONT_SYNC = _DEF_FLAG(1),
/**
* @brief [POSIX MAP_FIXED] When MAP_FIXED is set in the flags argument, the
diff --git a/components/mm/mm_kmem.c b/components/mm/mm_kmem.c
index f981b36a83..7299435cb3 100644
--- a/components/mm/mm_kmem.c
+++ b/components/mm/mm_kmem.c
@@ -27,7 +27,7 @@ void rt_kmem_list(void) __attribute__((alias("list_kmem")));
static rt_ubase_t rt_pv_offset;
-rt_ubase_t rt_kmem_pvoff(void)
+const rt_ubase_t rt_kmem_pvoff(void)
{
return rt_pv_offset;
}
@@ -78,3 +78,29 @@ void *rt_kmem_v2p(void *vaddr)
{
return rt_hw_mmu_v2p(&rt_kernel_space, vaddr);
}
+
+void *rt_kmem_p2v(void *paddr)
+{
+ char *rc;
+ char *linear_va;
+ char *linear_pa;
+ if (paddr != ARCH_MAP_FAILED)
+ {
+ linear_va = (char *)paddr - PV_OFFSET;
+ linear_pa = rt_kmem_v2p(linear_va);
+ if (linear_pa != paddr)
+ {
+ rc = RT_NULL;
+ }
+ else
+ {
+ rc = linear_va;
+ }
+ }
+ else
+ {
+ rc = RT_NULL;
+ }
+
+ return rc;
+}
diff --git a/components/mm/mm_object.c b/components/mm/mm_object.c
index 3b4ccddc89..f26b3173f5 100644
--- a/components/mm/mm_object.c
+++ b/components/mm/mm_object.c
@@ -6,8 +6,13 @@
* Change Logs:
* Date Author Notes
* 2022-11-30 WangXiaoyao the first version
+ * 2023-08-19 Shell Support varea modification handler
*/
+#define DBG_TAG "mm.object"
+#define DBG_LVL DBG_INFO
+#include "rtdbg.h"
+
#include <rtthread.h>
#include "mm_aspace.h"
@@ -15,15 +20,38 @@
#include "mm_page.h"
#include <mmu.h>
-#define DBG_TAG "mm.object"
-#define DBG_LVL DBG_INFO
-#include "rtdbg.h"
+#include <string.h>
+#include <stdlib.h>
+
+/** varea based dummy memory object whose data comes directly from page frame */
static const char *get_name(rt_varea_t varea)
{
return "dummy-mapper";
}
+static rt_bool_t _varea_pgmgr_frame_is_member(rt_varea_t varea, rt_page_t frame)
+{
+ rt_page_t iter;
+ rt_bool_t rc = RT_FALSE;
+
+ if (varea->frames)
+ {
+ iter = varea->frames;
+ do
+ {
+ if (iter == frame)
+ {
+ rc = RT_TRUE;
+ break;
+ }
+ iter = iter->next;
+ } while (iter);
+ }
+
+ return rc;
+}
+
void rt_varea_pgmgr_insert(rt_varea_t varea, void *page_addr)
{
rt_page_t page = rt_page_addr2page(page_addr);
@@ -31,10 +59,12 @@ void rt_varea_pgmgr_insert(rt_varea_t varea, void *page_addr)
if (varea->frames == NULL)
{
varea->frames = page;
- page->next = NULL;
+ page->pre = RT_NULL;
+ page->next = RT_NULL;
}
else
{
+ page->pre = RT_NULL;
varea->frames->pre = page;
page->next = varea->frames;
varea->frames = page;
@@ -52,6 +82,8 @@ void rt_varea_pgmgr_pop_all(rt_varea_t varea)
rt_pages_free(pg_va, 0);
page = next;
}
+
+ varea->frames = RT_NULL;
}
void rt_varea_pgmgr_pop(rt_varea_t varea, void *vaddr, rt_size_t size)
@@ -60,9 +92,16 @@ void rt_varea_pgmgr_pop(rt_varea_t varea, void *vaddr, rt_size_t size)
while (vaddr != vend)
{
rt_page_t page = rt_page_addr2page(vaddr);
- page->pre->next = page->next;
- page->next->pre = page->pre;
- rt_pages_free(vaddr, 0);
+ if (_varea_pgmgr_frame_is_member(varea, page))
+ {
+ if (page->pre)
+ page->pre->next = page->next;
+ if (page->next)
+ page->next->pre = page->pre;
+ if (varea->frames == page)
+ varea->frames = page->next;
+ rt_pages_free(vaddr, 0);
+ }
vaddr = (char *)vaddr + ARCH_PAGE_SIZE;
}
}
@@ -94,9 +133,149 @@ static void on_varea_close(struct rt_varea *varea)
{
}
-static void on_page_offload(rt_varea_t varea, void *vaddr, rt_size_t size)
+static rt_err_t on_varea_expand(struct rt_varea *varea, void *new_vaddr, rt_size_t size)
+{
+ return RT_EOK;
+}
+
+static void _remove_pages(rt_varea_t varea, void *rm_start, void *rm_end)
{
- rt_varea_pgmgr_pop(varea, vaddr, size);
+ void *page_va;
+
+ RT_ASSERT(!((rt_ubase_t)rm_start & ARCH_PAGE_MASK));
+ RT_ASSERT(!((rt_ubase_t)rm_end & ARCH_PAGE_MASK));
+ while (rm_start != rm_end)
+ {
+ page_va = rt_hw_mmu_v2p(varea->aspace, rm_start);
+
+ if (page_va != ARCH_MAP_FAILED)
+ {
+ page_va -= PV_OFFSET;
+ LOG_D("%s: free page %p", __func__, page_va);
+ rt_varea_unmap_page(varea, rm_start);
+ rt_varea_pgmgr_pop(varea, page_va, ARCH_PAGE_SIZE);
+ }
+ rm_start += ARCH_PAGE_SIZE;
+ }
+}
+
+static rt_err_t on_varea_shrink(rt_varea_t varea, void *new_start, rt_size_t size)
+{
+ char *varea_start = varea->start;
+ void *rm_start;
+ void *rm_end;
+
+ if (varea_start == (char *)new_start)
+ {
+ rm_start = varea_start + size;
+ rm_end = varea_start + varea->size;
+ }
+ else /* if (varea_start < (char *)new_start) */
+ {
+ RT_ASSERT(varea_start < (char *)new_start);
+ rm_start = varea_start;
+ rm_end = new_start;
+ }
+
+ _remove_pages(varea, rm_start, rm_end);
+ return RT_EOK;
+}
+
+static rt_err_t on_varea_split(struct rt_varea *existed, void *unmap_start, rt_size_t unmap_len, struct rt_varea *subset)
+{
+ void *sub_start = subset->start;
+ void *sub_end = sub_start + subset->size;
+ void *page_va;
+
+ _remove_pages(existed, unmap_start, (char *)unmap_start + unmap_len);
+
+ RT_ASSERT(!((rt_ubase_t)sub_start & ARCH_PAGE_MASK));
+ RT_ASSERT(!((rt_ubase_t)sub_end & ARCH_PAGE_MASK));
+ while (sub_start != sub_end)
+ {
+ page_va = rt_hw_mmu_v2p(existed->aspace, sub_start);
+
+ if (page_va != ARCH_MAP_FAILED)
+ {
+ rt_page_t frame;
+ page_va = rt_kmem_p2v(page_va);
+ if (page_va)
+ {
+ frame = rt_page_addr2page(page_va);
+ if (frame && _varea_pgmgr_frame_is_member(existed, frame))
+ {
+ LOG_D("%s: free page %p", __func__, page_va);
+ rt_page_ref_inc(page_va, 0);
+ rt_varea_pgmgr_pop(existed, page_va, ARCH_PAGE_SIZE);
+ rt_varea_pgmgr_insert(subset, page_va);
+ }
+ }
+ }
+ sub_start += ARCH_PAGE_SIZE;
+ }
+
+ return RT_EOK;
+}
+
+static rt_err_t on_varea_merge(struct rt_varea *merge_to, struct rt_varea *merge_from)
+{
+ /* transport page */
+ void *mr_start = merge_from->start;
+ void *mr_end = mr_start + merge_from->size;
+ void *page_va;
+
+ RT_ASSERT(!((rt_ubase_t)mr_start & ARCH_PAGE_MASK));
+ RT_ASSERT(!((rt_ubase_t)mr_end & ARCH_PAGE_MASK));
+ while (mr_start != mr_end)
+ {
+ page_va = rt_hw_mmu_v2p(merge_from->aspace, mr_start);
+ if (page_va != ARCH_MAP_FAILED)
+ {
+ rt_page_t frame;
+ page_va = rt_kmem_p2v(page_va);
+ if (page_va)
+ {
+ frame = rt_page_addr2page(page_va);
+ if (frame && _varea_pgmgr_frame_is_member(merge_from, frame))
+ {
+ LOG_D("%s: free page %p", __func__, page_va);
+ rt_page_ref_inc(page_va, 0);
+ rt_varea_pgmgr_pop(merge_from, page_va, ARCH_PAGE_SIZE);
+ rt_varea_pgmgr_insert(merge_to, page_va);
+ }
+ }
+ }
+ mr_start += ARCH_PAGE_SIZE;
+ }
+ return RT_EOK;
+}
+
+static void page_read(struct rt_varea *varea, struct rt_aspace_io_msg *msg)
+{
+ char *dst_k;
+ rt_aspace_t aspace = varea->aspace;
+ dst_k = rt_hw_mmu_v2p(aspace, msg->fault_vaddr);
+ if (dst_k != ARCH_MAP_FAILED)
+ {
+ RT_ASSERT(!((long)dst_k & ARCH_PAGE_MASK));
+ dst_k = (void *)((char *)dst_k - PV_OFFSET);
+ memcpy(msg->buffer_vaddr, dst_k, ARCH_PAGE_SIZE);
+ msg->response.status = MM_FAULT_STATUS_OK;
+ }
+}
+
+static void page_write(struct rt_varea *varea, struct rt_aspace_io_msg *msg)
+{
+ void *dst_k;
+ rt_aspace_t aspace = varea->aspace;
+ dst_k = rt_hw_mmu_v2p(aspace, msg->fault_vaddr);
+ if (dst_k != ARCH_MAP_FAILED)
+ {
+ RT_ASSERT(!((long)dst_k & ARCH_PAGE_MASK));
+ dst_k = (void *)((char *)dst_k - PV_OFFSET);
+ memcpy(dst_k, msg->buffer_vaddr, ARCH_PAGE_SIZE);
+ msg->response.status = MM_FAULT_STATUS_OK;
+ }
}
struct rt_mem_obj rt_mm_dummy_mapper = {
@@ -105,5 +284,12 @@ struct rt_mem_obj rt_mm_dummy_mapper = {
.hint_free = NULL,
.on_varea_open = on_varea_open,
.on_varea_close = on_varea_close,
- .on_page_offload = on_page_offload,
+
+ .on_varea_shrink = on_varea_shrink,
+ .on_varea_split = on_varea_split,
+ .on_varea_expand = on_varea_expand,
+ .on_varea_merge = on_varea_merge,
+
+ .page_write = page_write,
+ .page_read = page_read,
};
diff --git a/components/mm/mm_page.c b/components/mm/mm_page.c
index 99110ab435..0ddb392b63 100644
--- a/components/mm/mm_page.c
+++ b/components/mm/mm_page.c
@@ -91,10 +91,13 @@ static rt_page_t _trace_head;
#define TRACE_ALLOC(pg, size) _trace_alloc(pg, __builtin_return_address(0), size)
#define TRACE_FREE(pgaddr, size) _trace_free(pgaddr, __builtin_return_address(0), size)
+static long _alloc_cnt;
+
void rt_page_leak_trace_start()
{
// TODO multicore safety
_trace_head = NULL;
+ _alloc_cnt = 0;
enable = 1;
}
MSH_CMD_EXPORT(rt_page_leak_trace_start, start page leak tracer);
@@ -104,16 +107,18 @@ static void _collect()
rt_page_t page = _trace_head;
if (!page)
{
- rt_kputs("ok!\n");
+ LOG_RAW("ok! ALLOC CNT %ld\n", _alloc_cnt);
}
-
- while (page)
+ else
{
- rt_page_t next = page->next;
- void *pg_va = rt_page_page2addr(page);
- LOG_W("LEAK: %p, allocator: %p, size bits: %lx", pg_va, page->caller, page->trace_size);
- rt_pages_free(pg_va, page->trace_size);
- page = next;
+ while (page)
+ {
+ rt_page_t next = page->tl_next;
+ void *pg_va = rt_page_page2addr(page);
+ LOG_W("LEAK: %p, allocator: %p, size bits: %lx", pg_va, page->caller, page->trace_size);
+ rt_pages_free(pg_va, page->trace_size);
+ page = next;
+ }
}
}
@@ -129,11 +134,13 @@ static void _trace_alloc(rt_page_t page, void *caller, size_t size_bits)
{
if (enable)
{
+ char *page_va = rt_page_page2addr(page);
page->caller = caller;
page->trace_size = size_bits;
page->tl_prev = NULL;
page->tl_next = NULL;
+ _alloc_cnt++;
if (_trace_head == NULL)
{
_trace_head = page;
@@ -147,12 +154,12 @@ static void _trace_alloc(rt_page_t page, void *caller, size_t size_bits)
}
}
-void _report(rt_page_t page, size_bits, char *msg)
+void _report(rt_page_t page, size_t size_bits, char *msg)
{
void *pg_va = rt_page_page2addr(page);
LOG_W("%s: %p, allocator: %p, size bits: %lx", msg, pg_va, page->caller, page->trace_size);
- rt_kputs("backtrace\n");
- rt_hw_backtrace(0, 0);
+ LOG_RAW("backtrace\n");
+ rt_backtrace();
}
static void _trace_free(rt_page_t page, void *caller, size_t size_bits)
@@ -162,25 +169,26 @@ static void _trace_free(rt_page_t page, void *caller, size_t size_bits)
/* free after free */
if (page->trace_size == 0xabadcafe)
{
- _report("free after free")
+ _report(page, size_bits, "free after free");
return ;
}
else if (page->trace_size != size_bits)
{
- rt_kprintf("free with size bits %lx\n", size_bits);
- _report("incompatible size bits parameter");
+ LOG_RAW("free with size bits %lx\n", size_bits);
+ _report(page, size_bits, "incompatible size bits parameter");
return ;
}
- if (page->ref_cnt == 1)
+ if (page->ref_cnt == 0)
{
+ _alloc_cnt--;
if (page->tl_prev)
page->tl_prev->tl_next = page->tl_next;
if (page->tl_next)
page->tl_next->tl_prev = page->tl_prev;
if (page == _trace_head)
- _trace_head = page->next;
+ _trace_head = page->tl_next;
page->tl_prev = NULL;
page->tl_next = NULL;
@@ -531,7 +539,7 @@ static rt_page_t *_flag_to_page_list(size_t flags)
return page_list;
}
-static void *_do_pages_alloc(rt_uint32_t size_bits, size_t flags)
+rt_inline void *_do_pages_alloc(rt_uint32_t size_bits, size_t flags)
{
void *alloc_buf = RT_NULL;
struct rt_page *p;
@@ -613,27 +621,27 @@ void list_page(void)
struct rt_page *lp = page_list_low[i];
struct rt_page *hp = page_list_high[i];
- rt_kprintf("level %d ", i);
+ LOG_RAW("level %d ", i);
while (lp)
{
free += (1UL << i);
- rt_kprintf("[0x%08p]", rt_page_page2addr(lp));
+ LOG_RAW("[0x%08p]", rt_page_page2addr(lp));
lp = lp->next;
}
while (hp)
{
free += (1UL << i);
- rt_kprintf("[0x%08p]", rt_page_page2addr(hp));
+ LOG_RAW("[0x%08p]", rt_page_page2addr(hp));
hp = hp->next;
}
- rt_kprintf("\n");
+ LOG_RAW("\n");
}
rt_hw_interrupt_enable(level);
- rt_kprintf("-------------------------------\n");
- rt_kprintf("Page Summary:\n => free/installed: 0x%lx/0x%lx (%ld/%ld KB)\n", free, installed, PGNR2SIZE(free), PGNR2SIZE(installed));
- rt_kprintf("-------------------------------\n");
+ LOG_RAW("-------------------------------\n");
+ LOG_RAW("Page Summary:\n => free/installed: 0x%lx/0x%lx (%ld/%ld KB)\n", free, installed, PGNR2SIZE(free), PGNR2SIZE(installed));
+ LOG_RAW("-------------------------------\n");
}
MSH_CMD_EXPORT(list_page, show page info);
diff --git a/components/mm/mm_page.h b/components/mm/mm_page.h
index a0171d6672..e1557b923d 100644
--- a/components/mm/mm_page.h
+++ b/components/mm/mm_page.h
@@ -35,7 +35,7 @@
#ifdef RT_DEBUGING_PAGE_LEAK
-#define DEBUG_FIELD { \
+#define DEBUG_FIELD struct { \
/* trace list */ \
struct rt_page *tl_next; \
struct rt_page *tl_prev; \
@@ -50,7 +50,7 @@ DEF_PAGE_T(
struct rt_page *next; /* same level next */
struct rt_page *pre; /* same level pre */
- DEBUG_FIELD
+ DEBUG_FIELD;
rt_uint32_t size_bits; /* if is ARCH_ADDRESS_WIDTH_BITS, means not free */
rt_uint32_t ref_cnt; /* page group ref count */
diff --git a/components/mm/mm_private.h b/components/mm/mm_private.h
index 364ddab717..3fd3b88f86 100644
--- a/components/mm/mm_private.h
+++ b/components/mm/mm_private.h
@@ -11,6 +11,10 @@
#define __MM_PRIVATE_H__
#include "mm_aspace.h"
+#include "mm_fault.h"
+#include "mm_flag.h"
+#include "mm_page.h"
+
#include <rtdef.h>
#include <stddef.h>
@@ -29,6 +33,17 @@
* where both start and end are inclusive.
*/
+#define VAREA_NOT_STATIC(varea) (!((varea)->flag & MMF_STATIC_ALLOC))
+#define VAREA_NAME(varea) \
+ ((!varea->mem_obj || !varea->mem_obj->get_name) \
+ ? "unknow" \
+ : varea->mem_obj->get_name(varea))
+#define VAREA_IS_WRITABLE(varea) \
+ (rt_hw_mmu_attr_test_perm(varea->attr, \
+ RT_HW_MMU_PROT_USER | RT_HW_MMU_PROT_WRITE))
+#define VAREA_VA_TO_OFFSET(varea, va) \
+ ((varea)->offset + MM_PA_TO_OFF((long)(va) - (long)(varea)->start))
+
struct _mm_range
{
void *start;
@@ -94,6 +109,29 @@ void rt_varea_pgmgr_pop(rt_varea_t varea, void *vaddr, rt_size_t size);
void rt_varea_pgmgr_pop_all(rt_varea_t varea);
-int _varea_map_with_msg(rt_varea_t varea, struct rt_aspace_fault_msg *msg);
+int rt_varea_fix_private_locked(rt_varea_t ex_varea, void *pa,
+ struct rt_aspace_fault_msg *msg,
+ rt_bool_t dont_copy);
+
+int rt_varea_map_with_msg(rt_varea_t varea, struct rt_aspace_fault_msg *msg);
+
+void _varea_uninstall_locked(rt_varea_t varea);
+
+int _mm_aspace_map(rt_aspace_t aspace, rt_varea_t *pvarea, void **addr,
+ rt_size_t length, rt_size_t attr, mm_flag_t flags,
+ rt_mem_obj_t mem_obj, rt_size_t offset);
+
+rt_inline rt_bool_t rt_varea_is_private_locked(rt_varea_t varea)
+{
+ rt_base_t flags = varea->flag;
+ return !!(
+ (flags & (MMF_MAP_PRIVATE | MMF_MAP_PRIVATE_DONT_SYNC))
+ && (varea->aspace->private_object != varea->mem_obj)
+ );
+}
+
+rt_err_t rt_aspace_anon_ref_dec(rt_mem_obj_t aobj);
+rt_err_t rt_aspace_page_get_phy(rt_aspace_t aspace, void *page_va, void *buffer);
+rt_err_t rt_aspace_page_put_phy(rt_aspace_t aspace, void *page_va, void *buffer);
#endif /* __MM_PRIVATE_H__ */