Add mm/mm_map virtual memory mapping list

The task_group specific list can be used to store information about
mmappings.

For a driver or filesystem performing mmap can also enable munmap by
adding an item to this list using mm_map_add(). The item is then
returned in the corresponding munmap call.

Signed-off-by: Jukka Laitinen <jukkax@ssrc.tii.ae>
This commit is contained in:
Jukka Laitinen 2022-04-07 16:17:21 +04:00 committed by Xiang Xiao
parent 6e4ddf78bb
commit 7f8bec7070
7 changed files with 517 additions and 4 deletions

View file

@ -44,7 +44,7 @@ struct task_group_s;
struct mm_map_entry_s
{
FAR struct mm_map_entry *flink; /* this is used as sq_entry_t */
FAR const void *vaddr;
FAR void *vaddr;
size_t length;
off_t offset;
int prot;
@ -63,7 +63,7 @@ struct mm_map_entry_s
*/
int (*munmap)(FAR struct task_group_s *group,
FAR struct mm_map_entry_s *map,
FAR struct mm_map_entry_s *entry,
FAR void *start,
size_t length);
};
@ -73,11 +73,159 @@ struct mm_map_entry_s
struct mm_map_s
{
sq_queue_t mm_map_sq;
mutex_t mm_map_mutex;
rmutex_t mm_map_mutex;
};
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
#endif /* __INCLUDE_NUTTX_MM_MM_MAP_H */
/****************************************************************************
* Name: mm_map_lock
*
* Description:
* Get exclusive access current task_group's mm_map
*
* Input Parameters:
* None
*
* Returned Value:
* OK on success
* A negated errno value on failure
*
****************************************************************************/
int mm_map_lock(void);
/****************************************************************************
* Name: mm_map_unlock
*
* Description:
* Relinquish exclusive access to current task_group's mm_map
*
* Input Parameters:
* None
*
* Returned Value:
* None
*
****************************************************************************/
void mm_map_unlock(void);
/****************************************************************************
* Name: mm_map_initialize
*
* Description:
* Initialization function, called only by group_initialize
*
* Input Parameters:
* mm - Pointer to the mm_map structure to be initialized
*
* Returned Value:
* None
*
****************************************************************************/
void mm_map_initialize(FAR struct mm_map_s *mm);
/****************************************************************************
* Name: mm_map_destroy
*
* Description:
* Uninitialization function, called only by group_release
*
* Input Parameters:
* mm - Pointer to the mm_map structure to be initialized
*
* Returned Value:
* None
*
****************************************************************************/
void mm_map_destroy(FAR struct mm_map_s *mm);
/****************************************************************************
* Name: mm_map_add
*
* Description:
* Adds a virtual memory area into the list of mappings
*
* Input Parameters:
* entry - A pointer to mm_map_entry_s, mapping info to be added
*
* Returned Value:
* OK Added successfully
* -EINVAL: Invalid attempt to get the semaphore
* -EINTR: The wait was interrupted by the receipt of a signal.
* -ENOMEM: Out of memory
*
****************************************************************************/
int mm_map_add(FAR struct mm_map_entry_s *entry);
/****************************************************************************
* Name: mm_map_next
*
* Description:
* Returns the next mapping in the list, following the argument.
* Can be used to iterate through all the mappings. Returns the first
* mapping when the argument "entry" is NULL.
*
* Input Parameters:
* entry - Pointer to a single mapping in this task group or NULL to get
* the first one
*
* Returned Value:
* Pointer to the next mapping
*
****************************************************************************/
FAR struct mm_map_entry_s *mm_map_next(
FAR const struct mm_map_entry_s *entry);
/****************************************************************************
* Name: mm_map_find
*
* Description:
* Find the first mapping matching address and length
*
* Input Parameters:
* vaddr - Start address of the mapped area
* length - Length of the mapping
*
* Returned Value:
* Pointer to the mapping, NULL if not found
*
****************************************************************************/
FAR struct mm_map_entry_s *mm_map_find(FAR const void *vaddr,
size_t length);
/****************************************************************************
* Name: mm_map_remove
*
* Description:
* Removes a virtual memory area from the list of mappings
* Sets the given pointer argument to NULL after successful removal
*
* Input Parameters:
* mm - Pointer to the list of entries, from which the entry is
* removed. If passed mm is NULL, the function doesn't do
* anything, but just returns OK.
*
* entry - Pointer to the entry to be removed. If the passed entry is
* NULL the function doesn't do anything but just returns OK
*
* Returned Value:
* OK: Removed successfully
* -EINVAL: Invalid attempt to get the semaphore
* -EINTR: The wait was interrupted by the receipt of a signal.
* -ENOENT: Memory area not found
*
****************************************************************************/
int mm_map_remove(FAR struct mm_map_s *mm,
FAR struct mm_map_entry_s *entry);
#endif /* __INCLUDE_NUTTX_MM_MAP_H */

View file

@ -43,6 +43,7 @@
#include <nuttx/mm/shm.h>
#include <nuttx/fs/fs.h>
#include <nuttx/net/net.h>
#include <nuttx/mm/map.h>
#include <arch/arch.h>
@ -180,6 +181,14 @@
# define TCB_REG_OFF(reg) (reg * sizeof(uint32_t))
#endif
/* Get a pointer to the process' memory map struct from the task_group */
#define get_group_mm(group) (group ? &group->tg_mm_map : NULL)
/* Get a pointer to current the process' memory map struct */
#define get_current_mm() (get_group_mm(nxsched_self()->group))
/****************************************************************************
* Public Type Definitions
****************************************************************************/
@ -506,6 +515,10 @@ struct task_group_s
struct group_shm_s tg_shm; /* Task shared memory logic */
#endif
/* Virtual memory mapping info ********************************************/
struct mm_map_s tg_mm_map; /* Task mmappings */
};
/* struct tcb_s *************************************************************/

View file

@ -32,6 +32,7 @@ include circbuf/Make.defs
include mempool/Make.defs
include kasan/Make.defs
include ubsan/Make.defs
include map/Make.defs
BINDIR ?= bin

28
mm/map/Make.defs Normal file
View file

@ -0,0 +1,28 @@
############################################################################
# mm/map/Make.defs
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership. The
# ASF licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the
# License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
############################################################################
# Virtual memory map list support
CSRCS += mm_map.c
# Add the shared memory directory to the build
DEPPATH += --dep-path map
VPATH += :map

315
mm/map/mm_map.c Normal file
View file

@ -0,0 +1,315 @@
/****************************************************************************
* mm/map/mm_map.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/mm/map.h>
#include <stdbool.h>
#include <stddef.h>
#include <nuttx/sched.h>
#include <nuttx/kmalloc.h>
#include <assert.h>
#include <debug.h>
#if defined(CONFIG_BUILD_FLAT) || defined(__KERNEL__)
/****************************************************************************
* Private Functions
****************************************************************************/
static bool in_range(FAR const void *start, size_t length,
FAR const void *range_start, size_t range_length)
{
FAR const char *u_start = (FAR const char *)start;
FAR const char *u_end = u_start + length;
FAR const char *r_start = (FAR const char *)range_start;
FAR const char *r_end = r_start + range_length;
return (u_start >= r_start && u_start < r_end && /* Start is in range. */
u_end >= r_start && u_end <= r_end); /* End is in range. */
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: mm_map_lock
*
* Description:
* Get exclusive access to task_group's mm_map
*
****************************************************************************/
int mm_map_lock(void)
{
return nxrmutex_lock(&get_current_mm()->mm_map_mutex);
}
/****************************************************************************
* Name: mm_map_unlock
*
* Description:
* Relinquish exclusive access to task_group's mm_map
*
****************************************************************************/
void mm_map_unlock(void)
{
DEBUGVERIFY(nxrmutex_unlock(&get_current_mm()->mm_map_mutex));
}
/****************************************************************************
* Name: mm_map_initialize
*
* Description:
* Allocates a task group specific mm_map stucture. Called when the group
* is initialized
*
****************************************************************************/
void mm_map_initialize(FAR struct mm_map_s *mm)
{
sq_init(&mm->mm_map_sq);
nxrmutex_init(&mm->mm_map_mutex);
}
/****************************************************************************
* Name: mm_map_destroy
*
* Description:
* De-allocates a task group specific mm_map stucture and the mm_map_mutex
*
****************************************************************************/
void mm_map_destroy(FAR struct mm_map_s *mm)
{
FAR struct mm_map_entry_s *entry;
while ((entry = (FAR struct mm_map_entry_s *)sq_remfirst(&mm->mm_map_sq)))
{
/* Pass null as group argument to indicate that actual MMU mappings
* must not be touched. The process is being deleted and we don't
* know in which context we are. Only kernel memory allocations
* need to be freed by drivers
*/
/* Unmap the whole region */
if (entry->munmap)
{
if (entry->munmap(NULL, entry, entry->vaddr, entry->length) < 0)
{
/* This would be an error in the driver. It has defined munmap,
* but is not able to munmap the full area which it has mapped
*/
merr("Driver munmap failed\n");
}
}
kmm_free(entry);
}
nxrmutex_destroy(&mm->mm_map_mutex);
}
/****************************************************************************
* Name: mm_map_add
*
* Description:
* Add a mapping to task group's mm_map list
*
****************************************************************************/
int mm_map_add(FAR struct mm_map_entry_s *entry)
{
FAR struct mm_map_s *mm = get_current_mm();
FAR struct mm_map_entry_s *new_entry;
int ret;
if (!entry)
{
return -EINVAL;
}
/* Copy the provided mapping and add to the list */
new_entry = kmm_malloc(sizeof(struct mm_map_entry_s));
if (!new_entry)
{
return -EINVAL;
}
*new_entry = *entry;
ret = nxrmutex_lock(&mm->mm_map_mutex);
if (ret < 0)
{
kmm_free(new_entry);
return ret;
}
sq_addfirst((sq_entry_t *)new_entry, &mm->mm_map_sq);
nxrmutex_unlock(&mm->mm_map_mutex);
return OK;
}
/****************************************************************************
* Name: mm_map_next
*
* Description:
* Returns the next mapping in the list.
*
****************************************************************************/
FAR struct mm_map_entry_s *mm_map_next(
FAR const struct mm_map_entry_s *entry)
{
FAR struct mm_map_s *mm = get_current_mm();
FAR struct mm_map_entry_s *next_entry = NULL;
if (nxrmutex_lock(&mm->mm_map_mutex) == OK)
{
if (entry == NULL)
{
next_entry = (struct mm_map_entry_s *)sq_peek(&mm->mm_map_sq);
}
else
{
next_entry = (struct mm_map_entry_s *)
sq_next(((sq_entry_t *)entry));
}
nxrmutex_unlock(&mm->mm_map_mutex);
}
return next_entry;
}
/****************************************************************************
* Name: mm_map_find
*
* Description:
* Find the first mapping containing the range from the task group's list
*
****************************************************************************/
FAR struct mm_map_entry_s *mm_map_find(FAR const void *vaddr, size_t length)
{
FAR struct mm_map_s *mm = get_current_mm();
FAR struct mm_map_entry_s *found_entry = NULL;
if (nxrmutex_lock(&mm->mm_map_mutex) == OK)
{
found_entry = (struct mm_map_entry_s *)sq_peek(&mm->mm_map_sq);
while (found_entry && !in_range(vaddr, length,
found_entry->vaddr,
found_entry->length))
{
found_entry = (struct mm_map_entry_s *)
sq_next(((sq_entry_t *)found_entry));
}
nxrmutex_unlock(&mm->mm_map_mutex);
}
return found_entry;
}
/****************************************************************************
* Name: mm_map_remove
*
* Description:
* Remove a mapping from the task group's list
*
****************************************************************************/
int mm_map_remove(FAR struct mm_map_s *mm,
FAR struct mm_map_entry_s *entry)
{
FAR struct mm_map_entry_s *prev_entry;
FAR struct mm_map_entry_s *removed_entry = NULL;
int ret;
if (!mm || !entry)
{
return OK;
}
ret = nxrmutex_lock(&mm->mm_map_mutex);
if (ret < 0)
{
return ret;
}
prev_entry = (struct mm_map_entry_s *)sq_peek(&mm->mm_map_sq);
/* Check if the list was empty */
if (!prev_entry)
{
nxrmutex_unlock(&mm->mm_map_mutex);
return -ENOENT;
}
/* Check if removing the first item */
if (entry == prev_entry)
{
sq_remfirst(&mm->mm_map_sq);
removed_entry = prev_entry;
}
else
{
/* Loop through the remaining items to find the one to be removed */
while ((removed_entry = (struct mm_map_entry_s *)
sq_next(((sq_entry_t *)prev_entry))))
{
if (entry == removed_entry)
{
sq_remafter((sq_entry_t *)prev_entry, &mm->mm_map_sq);
break;
}
prev_entry = removed_entry;
}
}
nxrmutex_unlock(&mm->mm_map_mutex);
/* If the item was removed, also delete the entry struct */
if (removed_entry)
{
kmm_free(removed_entry);
return OK;
}
return -ENOENT;
}
#endif /* defined(CONFIG_BUILD_FLAT) || defined(__KERNEL__) */

View file

@ -245,6 +245,10 @@ void group_initialize(FAR struct task_tcb_s *tcb)
DEBUGASSERT(tcb && tcb->cmn.group);
group = tcb->cmn.group;
/* Allocate mm_map list if required */
mm_map_initialize(&group->tg_mm_map);
#ifdef HAVE_GROUP_MEMBERS
/* Assign the PID of this new task as a member of the group. */

View file

@ -176,6 +176,10 @@ static inline void group_release(FAR struct task_group_s *group)
env_release(group);
#endif
/* Destroy the mm_map list */
mm_map_destroy(&group->tg_mm_map);
#if defined(CONFIG_BUILD_KERNEL) && defined(CONFIG_MM_SHM)
/* Release any resource held by shared memory virtual page allocator */