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:
parent
6e4ddf78bb
commit
7f8bec7070
7 changed files with 517 additions and 4 deletions
|
@ -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 */
|
||||
|
|
|
@ -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 *************************************************************/
|
||||
|
|
|
@ -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
28
mm/map/Make.defs
Normal 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
315
mm/map/mm_map.c
Normal 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__) */
|
|
@ -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. */
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
Loading…
Reference in a new issue