You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2023/01/10 10:34:33 UTC

[nuttx] 02/06: Add mm/mm_map virtual memory mapping list

This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 7f8bec7070077967224af219c733dc4ff6147202
Author: Jukka Laitinen <ju...@ssrc.tii.ae>
AuthorDate: Thu Apr 7 16:17:21 2022 +0400

    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 <ju...@ssrc.tii.ae>
---
 include/nuttx/mm/map.h     | 156 +++++++++++++++++++++-
 include/nuttx/sched.h      |  13 ++
 mm/Makefile                |   1 +
 mm/map/Make.defs           |  28 ++++
 mm/map/mm_map.c            | 315 +++++++++++++++++++++++++++++++++++++++++++++
 sched/group/group_create.c |   4 +
 sched/group/group_leave.c  |   4 +
 7 files changed, 517 insertions(+), 4 deletions(-)

diff --git a/include/nuttx/mm/map.h b/include/nuttx/mm/map.h
index f845054e34..ac7d0c442e 100644
--- a/include/nuttx/mm/map.h
+++ b/include/nuttx/mm/map.h
@@ -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 */
diff --git a/include/nuttx/sched.h b/include/nuttx/sched.h
index bf8db3ff81..ce450a8137 100644
--- a/include/nuttx/sched.h
+++ b/include/nuttx/sched.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 *************************************************************/
diff --git a/mm/Makefile b/mm/Makefile
index 1d1d5f2413..a9d0c2272b 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -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
 
diff --git a/mm/map/Make.defs b/mm/map/Make.defs
new file mode 100644
index 0000000000..1d1f602d2b
--- /dev/null
+++ b/mm/map/Make.defs
@@ -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
diff --git a/mm/map/mm_map.c b/mm/map/mm_map.c
new file mode 100644
index 0000000000..22126bb4b7
--- /dev/null
+++ b/mm/map/mm_map.c
@@ -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__) */
diff --git a/sched/group/group_create.c b/sched/group/group_create.c
index fe7533629a..ac31f659f9 100644
--- a/sched/group/group_create.c
+++ b/sched/group/group_create.c
@@ -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. */
 
diff --git a/sched/group/group_leave.c b/sched/group/group_leave.c
index 2b1d11fdc0..4aa4797e78 100644
--- a/sched/group/group_leave.c
+++ b/sched/group/group_leave.c
@@ -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 */