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/18 03:01:29 UTC

[nuttx] 01/03: fs/shm: Add initial implementation for posix shared memory

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 a0ebddaac17e5213ef7bfc50a158d5a5a99e0595
Author: Jukka Laitinen <ju...@ssrc.tii.ae>
AuthorDate: Thu Mar 24 08:52:46 2022 +0200

    fs/shm: Add initial implementation for posix shared memory
    
    This implements the file system logic for posix shared memory:
    shm_open, shm_unlink, mmap, munmap and close
    
    For flat and protected builds the memory simply allocated from (user) heap
    
    For kernel build the memory is allocated from page pool and mapped with MMU
    
    This doesn't yet support protection flags or re-sizing already truncated shared
    memory area.
    
    Co-authored-by: Ville Juven <vi...@unikie.com>
    Signed-off-by: Jukka Laitinen <ju...@ssrc.tii.ae>
---
 fs/shm/Kconfig       |   8 +-
 fs/shm/Make.defs     |   8 +-
 fs/shm/shm_open.c    | 181 ++++++++++++++++++++++++
 fs/shm/shm_unlink.c  | 162 ++++++++++++++++++++++
 fs/shm/shmfs.c       | 383 +++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/shm/shmfs.h       |  70 ++++++++++
 fs/shm/shmfs_alloc.c | 143 +++++++++++++++++++
 7 files changed, 946 insertions(+), 9 deletions(-)

diff --git a/fs/shm/Kconfig b/fs/shm/Kconfig
index b0f75d129f..7ec28209a0 100644
--- a/fs/shm/Kconfig
+++ b/fs/shm/Kconfig
@@ -3,16 +3,16 @@
 # see the file kconfig-language.txt in the NuttX tools repository.
 #
 
-config FS_SHM
+config FS_SHMFS
 	bool "Shared memory support"
 	default n
-	depends on MM_SHM
+	select ARCH_VMA_MAPPING if BUILD_KERNEL
 	---help---
 		Include support for shm_open() and shm_close.
 
-if FS_SHM
+if FS_SHMFS
 
-config FS_SHM_VFS_PATH
+config FS_SHMFS_VFS_PATH
 	string "Path to shared memory object storage"
 	default "/var/shm"
 	---help---
diff --git a/fs/shm/Make.defs b/fs/shm/Make.defs
index dfcfed0d81..f686789c6a 100644
--- a/fs/shm/Make.defs
+++ b/fs/shm/Make.defs
@@ -18,13 +18,11 @@
 #
 ############################################################################
 
-# Include POSIX message queue support
+ifeq ($(CONFIG_FS_SHMFS),y)
 
-ifeq ($(CONFIG_FS_SHM),y)
+CSRCS += shm_open.c shm_unlink.c shmfs.c shmfs_alloc.c
 
-CSRCS += shm_open.c shm_close.c
-
-# Include POSIX message queue build support
+# Include POSIX shm build support
 
 DEPPATH += --dep-path shm
 VPATH += :shm
diff --git a/fs/shm/shm_open.c b/fs/shm/shm_open.c
new file mode 100644
index 0000000000..3f4df72fb2
--- /dev/null
+++ b/fs/shm/shm_open.c
@@ -0,0 +1,181 @@
+/****************************************************************************
+ * fs/shm/shm_open.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/config.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "inode/inode.h"
+#include "shm/shmfs.h"
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static int file_shm_open(FAR struct file *shm, FAR const char *name,
+                         int oflags, mode_t mode)
+{
+  FAR struct inode *inode;
+  struct inode_search_s desc;
+  char fullpath[sizeof(CONFIG_FS_SHMFS_VFS_PATH) + CONFIG_NAME_MAX + 2];
+  int ret;
+
+  /* Make sure that a non-NULL name is supplied */
+
+  if (!shm || !name)
+    {
+      return -EINVAL;
+    }
+
+  /* Remove any number of leading '/' */
+
+  while (*name == '/')
+    {
+      name++;
+    }
+
+  /* Empty name supplied? */
+
+  if (*name == '\0')
+    {
+      return -EINVAL;
+    }
+
+  /* Name too long? */
+
+  if (strnlen(name, CONFIG_NAME_MAX + 1) > CONFIG_NAME_MAX)
+    {
+      return -ENAMETOOLONG;
+    }
+
+  /* Get the full path to the shm object */
+
+  snprintf(fullpath, sizeof(fullpath),
+           CONFIG_FS_SHMFS_VFS_PATH "/%s", name);
+
+  /* Get the inode for this shm object */
+
+  SETUP_SEARCH(&desc, fullpath, false);
+
+  ret = inode_lock();
+  if (ret < 0)
+    {
+      goto errout_with_search;
+    }
+
+  ret = inode_find(&desc);
+  if (ret >= 0)
+    {
+      /* Something exists at this path.  Get the search results */
+
+      inode = desc.node;
+
+      /* Verify that the inode is an shm object */
+
+      if (!INODE_IS_SHM(inode))
+        {
+          ret = -EINVAL;
+          inode_release(inode);
+          goto errout_with_sem;
+        }
+
+      /* It exists and is an shm object.  Check if the caller wanted to
+       * create a new object with this name.
+       */
+
+      if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
+        {
+          ret = -EEXIST;
+          inode_release(inode);
+          goto errout_with_sem;
+        }
+    }
+  else
+    {
+      /* The shm does not exist. Were we asked to create it? */
+
+      if ((oflags & O_CREAT) == 0)
+        {
+          /* The shm does not exist and O_CREAT is not set */
+
+          ret = -ENOENT;
+          goto errout_with_sem;
+        }
+
+      /* Create an inode in the pseudo-filesystem at this path */
+
+      ret = inode_reserve(fullpath, mode, &inode);
+      if (ret < 0)
+        {
+          goto errout_with_sem;
+        }
+
+      INODE_SET_SHM(inode);
+      inode->u.i_ops = &shmfs_operations;
+      inode->i_private = NULL;
+      inode->i_crefs = 1;
+    }
+
+  /* Associate the inode with a file structure */
+
+  shm->f_oflags = oflags;
+  shm->f_pos = 0;
+  shm->f_inode = inode;
+  shm->f_priv = NULL;
+
+errout_with_sem:
+  inode_unlock();
+errout_with_search:
+  RELEASE_SEARCH(&desc);
+  return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int shm_open(FAR const char *name, int oflag, mode_t mode)
+{
+  struct file shm;
+  int ret;
+
+  ret = file_shm_open(&shm, name, oflag, mode);
+  if (ret < 0)
+    {
+      set_errno(-ret);
+      return -1;
+    }
+
+  ret = file_allocate(shm.f_inode, shm.f_oflags, shm.f_pos, shm.f_priv, 0,
+                      false);
+  if (ret < 0)
+    {
+      set_errno(-ret);
+      file_close(&shm);
+      return -1;
+    }
+
+  return ret;
+}
diff --git a/fs/shm/shm_unlink.c b/fs/shm/shm_unlink.c
new file mode 100644
index 0000000000..64c95629c5
--- /dev/null
+++ b/fs/shm/shm_unlink.c
@@ -0,0 +1,162 @@
+/****************************************************************************
+ * fs/shm/shm_unlink.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 <stdio.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "inode/inode.h"
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static int file_shm_unlink(FAR const char *name)
+{
+  FAR struct inode *inode;
+  struct inode_search_s desc;
+  char fullpath[sizeof(CONFIG_FS_SHMFS_VFS_PATH) + CONFIG_NAME_MAX + 2];
+  int ret;
+
+  /* Make sure that a non-NULL name is supplied */
+
+  if (!name)
+    {
+      return -ENOENT;
+    }
+
+  /* Remove any number of leading '/' */
+
+  while (*name == '/')
+    {
+      name++;
+    }
+
+  /* Empty name supplied? */
+
+  if (*name == '\0')
+    {
+      return -ENOENT;
+    }
+
+  /* Name too long? */
+
+  if (strnlen(name, CONFIG_NAME_MAX + 1) > CONFIG_NAME_MAX)
+    {
+      return -ENAMETOOLONG;
+    }
+
+  /* Get the full path to the shm object */
+
+  snprintf(fullpath, sizeof(fullpath),
+           CONFIG_FS_SHMFS_VFS_PATH "/%s", name);
+
+  /* Get the inode for this shm object */
+
+  SETUP_SEARCH(&desc, fullpath, false);
+
+  ret = inode_lock();
+  if (ret < 0)
+    {
+      goto errout_with_search;
+    }
+
+  ret = inode_find(&desc);
+  if (ret < 0)
+    {
+      /* There is no inode that includes in this path */
+
+      goto errout_with_sem;
+    }
+
+  /* Get the search results */
+
+  inode = desc.node;
+  DEBUGASSERT(inode != NULL);
+
+  /* Verify that what we found is, indeed, an shm inode */
+
+  if (!INODE_IS_SHM(inode))
+    {
+      ret = -ENOENT;
+      goto errout_with_inode;
+    }
+
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+  if (inode->u.i_ops->unlink)
+    {
+      /* Notify the shmfs driver that it has been unlinked */
+
+      ret = inode->u.i_ops->unlink(inode);
+      if (ret < 0)
+        {
+          goto errout_with_inode;
+        }
+    }
+#endif
+
+  /* Remove the old inode from the tree. If we hold a reference count
+   * on the inode, it will not be deleted now.  This will set the
+   * FSNODEFLAG_DELETED bit in the inode flags.
+   */
+
+  ret = inode_remove(fullpath);
+
+  /* inode_remove() should always fail with -EBUSY because we have a
+   * reference on the inode.  -EBUSY means that the inode was, indeed,
+   * unlinked but it could not be freed because there are references.
+   */
+
+  if (ret == -EBUSY)
+    {
+      ret = OK;
+    }
+
+  DEBUGASSERT(ret == OK);
+
+errout_with_inode:
+  inode_release(inode);
+errout_with_sem:
+  inode_unlock();
+errout_with_search:
+  RELEASE_SEARCH(&desc);
+  return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int shm_unlink(FAR const char *name)
+{
+  int ret = file_shm_unlink(name);
+
+  if (ret < 0)
+    {
+      set_errno(-ret);
+      return -1;
+    }
+
+  return 0;
+}
diff --git a/fs/shm/shmfs.c b/fs/shm/shmfs.c
new file mode 100644
index 0000000000..0a7cb5953a
--- /dev/null
+++ b/fs/shm/shmfs.c
@@ -0,0 +1,383 @@
+/****************************************************************************
+ * fs/shm/shmfs.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 <assert.h>
+
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/mm/map.h>
+
+#if defined (CONFIG_BUILD_KERNEL)
+#include <nuttx/arch.h>
+#include <nuttx/pgalloc.h>
+#include <nuttx/sched.h>
+#endif
+
+#include "shm/shmfs.h"
+#include "inode/inode.h"
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int shmfs_close(FAR struct file *filep);
+static ssize_t shmfs_read(FAR struct file *filep, FAR char *buffer,
+                          size_t buflen);
+static ssize_t shmfs_write(FAR struct file *filep, FAR const char *buffer,
+                           size_t buflen);
+static int shmfs_truncate(FAR struct file *filep, off_t length);
+
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+static int shmfs_unlink(FAR struct inode *inode);
+#endif
+
+static int shmfs_mmap(FAR struct file *filep,
+                      FAR struct mm_map_entry_s *entry);
+static int shmfs_munmap(FAR struct task_group_s *group,
+                        FAR struct mm_map_entry_s *entry,
+                        FAR void *start,
+                        size_t length);
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+const struct file_operations shmfs_operations =
+{
+  NULL,             /* open */
+  shmfs_close,      /* close */
+  shmfs_read,       /* read */
+  shmfs_write,      /* write */
+  NULL,             /* seek */
+  NULL,             /* ioctl */
+  shmfs_mmap,       /* mmap */
+  shmfs_truncate,   /* truncate */
+  NULL,             /* poll */
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+  shmfs_unlink      /* unlink */
+#endif
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: shmfs_read
+ ****************************************************************************/
+
+static ssize_t shmfs_read(FAR struct file *filep, FAR char *buffer,
+                          size_t buflen)
+{
+  return -ENOSYS;
+}
+
+/****************************************************************************
+ * Name: shmfs_write
+ ****************************************************************************/
+
+static ssize_t shmfs_write(FAR struct file *filep, FAR const char *buffer,
+                           size_t buflen)
+{
+  return -ENOSYS;
+}
+
+/****************************************************************************
+ * Name: shmfs_release
+ ****************************************************************************/
+
+static int shmfs_release(FAR struct inode *inode)
+{
+  /* If the file has been unlinked previously, delete the contents.
+   * The inode is released after this call, hence checking if i_crefs <= 1.
+   */
+
+  int ret = inode_lock();
+  if (ret >= 0)
+    {
+      if (inode->i_parent == NULL &&
+          inode->i_crefs <= 1)
+        {
+          shmfs_free_object(inode->i_private);
+          inode->i_private = NULL;
+          ret = OK;
+        }
+
+      inode_unlock();
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: shmfs_close
+ ****************************************************************************/
+
+static int shmfs_close(FAR struct file *filep)
+{
+  /* Release the shmfs object. The object gets deleted if no-one has
+   * reference to it (either mmap or open file) and the object has been
+   * unlinked
+   */
+
+  return shmfs_release(filep->f_inode);
+}
+
+/****************************************************************************
+ * Name: shmfs_truncate
+ ****************************************************************************/
+
+static int shmfs_truncate(FAR struct file *filep, off_t length)
+{
+  FAR struct shmfs_object_s *object;
+  int ret;
+
+  if (length == 0)
+    {
+      return -EINVAL;
+    }
+
+  ret = inode_lock();
+  if (ret >= 0)
+    {
+      object = filep->f_inode->i_private;
+      if (!object)
+        {
+          filep->f_inode->i_private = shmfs_alloc_object(length);
+          if (!filep->f_inode->i_private)
+            {
+              ret = -EFAULT;
+            }
+        }
+      else if (object->length != length)
+        {
+          /* This doesn't support resize */
+
+          ret = -EINVAL;
+        }
+
+      inode_unlock();
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: shmfs_unlink
+ ****************************************************************************/
+
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+static int shmfs_unlink(FAR struct inode *inode)
+{
+  int ret = inode_lock();
+
+  if (ret >= 0)
+    {
+      if (inode->i_crefs <= 1)
+        {
+          shmfs_free_object(inode->i_private);
+          inode->i_private = NULL;
+        }
+
+      inode_unlock();
+    }
+
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: shmfs_map_object
+ ****************************************************************************/
+
+static int shmfs_map_object(FAR struct shmfs_object_s *object,
+                            FAR void **vaddr)
+{
+  int ret = OK;
+
+#ifdef CONFIG_BUILD_KERNEL
+  /* Map the physical pages of the shm object with MMU. */
+
+  FAR struct tcb_s *tcb = nxsched_self();
+  FAR struct task_group_s *group = tcb->group;
+  FAR uintptr_t *pages = (FAR uintptr_t *)&object->paddr;
+  uintptr_t mapaddr;
+  unsigned int npages;
+
+  /* Find a free vaddr space that satisfies length */
+
+  mapaddr = (uintptr_t)vm_alloc_region(get_group_mm(group), 0,
+                                       object->length);
+  if (mapaddr == 0)
+    {
+      return -ENOMEM;
+    }
+
+  /* Convert the region size to pages */
+
+  npages = MM_NPAGES(object->length);
+
+  /* Map the memory to user virtual address space */
+
+  ret = up_shmat(pages, npages, mapaddr);
+  if (ret < 0)
+    {
+      vm_release_region(get_group_mm(group), (FAR void *)mapaddr,
+                        object->length);
+    }
+  else
+    {
+      *vaddr = (FAR void *)mapaddr;
+    }
+#else
+  /* Use the physical address directly */
+
+  *vaddr = object->paddr;
+#endif
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: shmfs_mmap
+ ****************************************************************************/
+
+static int shmfs_mmap(FAR struct file *filep,
+                      FAR struct mm_map_entry_s *entry)
+{
+  FAR struct shmfs_object_s *object;
+  int ret = -EINVAL;
+
+  DEBUGASSERT(filep->f_inode != NULL);
+
+  /* We don't support offset at the moment, just mapping the whole object
+   * object is NULL if it hasn't been truncated yet
+   */
+
+  if (entry->offset != 0)
+    {
+      return ret;
+    }
+
+  /* Keep the inode when mmapped, increase refcount */
+
+  ret = inode_addref(filep->f_inode);
+  if (ret >= 0)
+    {
+      object = (FAR struct shmfs_object_s *)filep->f_inode->i_private;
+      if (object)
+        {
+          ret = shmfs_map_object(object, &entry->vaddr);
+        }
+      else
+        {
+          ret = -EINVAL;
+        }
+
+      if (ret < 0)
+        {
+          inode_release(filep->f_inode);
+        }
+      else
+        {
+          entry->munmap = shmfs_munmap;
+          entry->priv.p = (FAR void *)filep->f_inode;
+          mm_map_add(entry);
+        }
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: shmfs_unmap_object
+ ****************************************************************************/
+
+static int shmfs_unmap_area(FAR struct task_group_s *group,
+                            FAR void *vaddr, size_t length)
+{
+  int ret = OK;
+
+#ifdef CONFIG_BUILD_KERNEL
+  unsigned int npages;
+
+  /* Convert the region size to pages */
+
+  if (group)
+    {
+      npages = MM_NPAGES(length);
+
+      /* Unmap the memory from user virtual address space */
+
+      ret = up_shmdt((uintptr_t)vaddr, npages);
+
+      /* Add the virtual memory back to the shared memory pool */
+
+      vm_release_region(get_group_mm(group), vaddr, length);
+    }
+#endif
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: shmfs_munmap
+ ****************************************************************************/
+
+static int shmfs_munmap(FAR struct task_group_s *group,
+                        FAR struct mm_map_entry_s *entry,
+                        FAR void *start,
+                        size_t length)
+{
+  int ret;
+
+  /* Partial unmap is not supported yet */
+
+  if (start != entry->vaddr || length != entry->length)
+    {
+      return -EINVAL;
+    }
+
+  /* Unmap the virtual memory area from the user's address space */
+
+  ret = shmfs_unmap_area(group, entry->vaddr, entry->length);
+
+  /* Release the shmfs object. The object gets deleted if no-one has
+   * reference to it (either mmap or open file) and the object has been
+   * unlinked
+   */
+
+  if (ret == OK)
+    {
+      ret = shmfs_release((FAR struct inode *)entry->priv.p);
+    }
+
+  /* Remove the mapping. */
+
+  if (ret == OK)
+    {
+      ret = mm_map_remove(get_group_mm(group), entry);
+    }
+
+  return ret;
+}
diff --git a/fs/shm/shmfs.h b/fs/shm/shmfs.h
new file mode 100644
index 0000000000..4c4c50cf42
--- /dev/null
+++ b/fs/shm/shmfs.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+ * fs/shm/shmfs.h
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __FS_SHM_SHMFS_H
+#define __FS_SHM_SHMFS_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/fs/fs.h>
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+extern const struct file_operations shmfs_operations;
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+struct shmfs_object_s
+{
+  /* Total number of bytes needed from physical memory. */
+
+  size_t length;
+
+  /* Vector of allocations from physical memory.
+   *
+   * - In flat and protected builds this is a pointer to the
+   *   allocated memory.
+   *
+   * - In kernel build this is start of a malloc'd vector of void pointers
+   *   and the length of the vector is MM_NPAGES(length).
+   */
+
+  FAR void *paddr;
+
+  /* The struct continues here as malloc'd array of void pointers
+   * if CONFIG_BUILD_KERNEL
+   */
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+FAR struct shmfs_object_s *shmfs_alloc_object(size_t length);
+
+void shmfs_free_object(FAR struct shmfs_object_s *object);
+
+#endif
diff --git a/fs/shm/shmfs_alloc.c b/fs/shm/shmfs_alloc.c
new file mode 100644
index 0000000000..7a396705c6
--- /dev/null
+++ b/fs/shm/shmfs_alloc.c
@@ -0,0 +1,143 @@
+/****************************************************************************
+ * fs/shm/shmfs_alloc.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 <stdbool.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/pgalloc.h>
+
+#include "shm/shmfs.h"
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+FAR struct shmfs_object_s *shmfs_alloc_object(size_t length)
+{
+  FAR struct shmfs_object_s *object;
+  bool allocated = false;
+
+#if defined(CONFIG_BUILD_FLAT)
+  /* in FLAT build, allocate the object metadata and the data in the same
+   * chunk in kernel heap
+   */
+
+  object = kmm_zalloc(sizeof(struct shmfs_object_s) + length);
+  if (object)
+    {
+      object->paddr = (FAR char *)object + sizeof(struct shmfs_object_s);
+      allocated = true;
+    }
+
+#elif defined(CONFIG_BUILD_PROTECTED)
+  /* in PROTECTED build, allocate the shm object in kernel heap, and shared
+   * memory in user heap
+   */
+
+  object = kmm_zalloc(sizeof(struct shmfs_object_s));
+  if (object)
+    {
+      object->paddr = kumm_zalloc(length);
+
+      if (object->paddr)
+        {
+           allocated = true;
+        }
+    }
+
+#elif defined(CONFIG_BUILD_KERNEL)
+  /* in KERNEL build, allocate the shared memory from page pool and store the
+   * physical address
+   */
+
+  size_t i = 0;
+  FAR void **pages;
+  size_t n_pages = MM_NPAGES(length);
+
+  object = kmm_zalloc(sizeof(struct shmfs_object_s) +
+                      (n_pages - 1) * sizeof(object->paddr));
+
+  if (object)
+    {
+      pages = &object->paddr;
+      for (; i < n_pages; i++)
+        {
+          pages[i] = (FAR void *)mm_pgalloc(1);
+          if (!pages[i])
+            {
+              break;
+            }
+        }
+    }
+
+  if (i == n_pages)
+    {
+      allocated = true;
+    }
+#endif
+
+  if (allocated)
+    {
+      object->length = length;
+    }
+  else
+    {
+      /* delete any partial allocation */
+
+      shmfs_free_object(object);
+      object = NULL;
+    }
+
+  return object;
+}
+
+void shmfs_free_object(FAR struct shmfs_object_s *object)
+{
+#if defined(CONFIG_BUILD_KERNEL)
+  size_t i;
+  size_t n_pages = MM_NPAGES(object->length);
+  FAR void **pages;
+#endif
+
+  if (object)
+    {
+#if defined (CONFIG_BUILD_PROTECTED)
+      kumm_free(object->paddr);
+#elif defined(CONFIG_BUILD_KERNEL)
+      pages = &object->paddr;
+      for (i = 0; i < n_pages; i++)
+        {
+          if (pages[i])
+            {
+              mm_pgfree((uintptr_t)pages[i], 1);
+            }
+        }
+#endif
+
+      /* Delete the object metadata
+       * (and the shared memory in case of FLAT build)
+       */
+
+      kmm_free(object);
+    }
+}