Module: xenomai-forge
Branch: next
Commit: 5f8c21ce4b6d7accaf6b0c9da7b20890775ccb5a
URL:    
http://git.xenomai.org/?p=xenomai-forge.git;a=commit;h=5f8c21ce4b6d7accaf6b0c9da7b20890775ccb5a

Author: Philippe Gerum <r...@xenomai.org>
Date:   Wed Sep 10 21:29:27 2014 +0200

cobalt/rtdm, cobalt/posix/process: extend mmap() support to MMU-less

A file mapping on no-MMU requires the backing device to advertise
direct mapping capabilities (i.e. BDI_CAP_MAP_DIRECT), which a file
hooked on the anon inode doesn't.

To fix this, we issue our internal mapping requests (i.e. for memdev
heaps and mayday page) on a file pointer to /dev/mem instead, on
MMU-mess systems (only).

In addition, the .get_unmapped_area handler is added to the RTDM file
descriptor operations, since this will be a prerequisite for allowing
drivers to honor mapping requests to (quasi-)memory devices on
MMU-less the same way.

---

 include/cobalt/kernel/rtdm/fd.h |   43 +++++++++
 kernel/cobalt/posix/io.c        |    2 +-
 kernel/cobalt/posix/memory.c    |   47 +++++++---
 kernel/cobalt/posix/process.c   |   77 +++++-----------
 kernel/cobalt/rtdm/drvlib.c     |  187 +++++++++++++++++++++++++--------------
 5 files changed, 218 insertions(+), 138 deletions(-)

diff --git a/include/cobalt/kernel/rtdm/fd.h b/include/cobalt/kernel/rtdm/fd.h
index 815ef5d..4282209 100644
--- a/include/cobalt/kernel/rtdm/fd.h
+++ b/include/cobalt/kernel/rtdm/fd.h
@@ -227,10 +227,48 @@ int rtdm_select_handler(struct rtdm_fd *fd, struct 
xnselector *selector,
  *
  * @see @c mmap() in POSIX.1-2001,
  * http://pubs.opengroup.org/onlinepubs/7908799/xsh/mmap.html
+ *
+ * @note The address hint passed to the mmap() request is deliberately
+ * ignored by RTDM.
  */
 int rtdm_mmap_handler(struct rtdm_fd *fd, struct vm_area_struct *vma);
 
 /**
+ * Allocate mapping region in address space
+ *
+ * When present, this optional handler should return the start address
+ * of a free region in the process's address space, large enough to
+ * cover the ongoing mmap() operation. If unspecified, the default
+ * architecture-defined handler is invoked.
+ *
+ * Most drivers can omit this handler, except on MMU-less platforms
+ * (see second note).
+ *
+ * @param[in] fd File descriptor
+ * @param[in] len Length of the requested region
+ * @param[in] pgoff Page frame number to map to (see second note).
+ * @param[in] flags Requested mapping flags
+ *
+ * @return The start address of the mapping region on success. On
+ * failure, a negative error code should be returned, with -ENOSYS
+ * meaning that the driver does not want to provide such information,
+ * in which case the ongoing mmap() operation will fail.
+ *
+ * @note The address hint passed to the mmap() request is deliberately
+ * ignored by RTDM, and therefore not passed to this handler.
+ *
+ * @note On MMU-less platforms, this handler is required because RTDM
+ * issues mapping requests over a shareable character device
+ * internally. In such context, the RTDM core may pass a null @a pgoff
+ * argument to the handler, for probing for the logical start address
+ * of the memory region to map to. Otherwise, when @a pgoff is
+ * non-zero, pgoff << PAGE_SHIFT is usually returned.
+ */
+unsigned long
+rtdm_get_unmapped_area_handler(struct rtdm_fd *fd,
+                              unsigned long len, unsigned long pgoff,
+                              unsigned long flags);
+/**
  * @anchor rtdm_fd_ops
  * @brief RTDM file operation descriptor.
  *
@@ -282,6 +320,11 @@ struct rtdm_fd_ops {
        /** See rtdm_mmap_handler(). */
        int (*mmap)(struct rtdm_fd *fd,
                    struct vm_area_struct *vma);
+       /** See rtdm_get_unmapped_area_handler(). */
+       unsigned long (*get_unmapped_area)(struct rtdm_fd *fd,
+                                          unsigned long len,
+                                          unsigned long pgoff,
+                                          unsigned long flags);
 };
 
 /** @} File operation handlers */
diff --git a/kernel/cobalt/posix/io.c b/kernel/cobalt/posix/io.c
index bafc5e2..4d549bb 100644
--- a/kernel/cobalt/posix/io.c
+++ b/kernel/cobalt/posix/io.c
@@ -169,7 +169,7 @@ COBALT_SYSCALL(mmap, lostage,
                     void __user **u_addrp))
 {
        struct _rtdm_mmap_request rma;
-       void *u_addr;
+       void *u_addr = NULL;
        int ret;
 
        if (__xn_copy_from_user(&rma, u_rma, sizeof(rma)))
diff --git a/kernel/cobalt/posix/memory.c b/kernel/cobalt/posix/memory.c
index 126b8c7..78cae01 100644
--- a/kernel/cobalt/posix/memory.c
+++ b/kernel/cobalt/posix/memory.c
@@ -99,6 +99,27 @@ static int umm_mmap(struct rtdm_fd *fd, struct 
vm_area_struct *vma)
        return 0;
 }
 
+#ifndef CONFIG_MMU
+static unsigned long umm_get_unmapped_area(struct rtdm_fd *fd,
+                                          unsigned long len,
+                                          unsigned long pgoff,
+                                          unsigned long flags)
+{
+       struct cobalt_umm *umm;
+
+       umm = umm_from_fd(fd);
+       if (umm == NULL)
+               return -ENODEV;
+
+       if (pgoff == 0)
+               return (unsigned long)xnheap_get_membase(&umm->heap);
+
+       return pgoff << PAGE_SHIFT;
+}
+#else
+#define umm_get_unmapped_area  NULL
+#endif
+
 static int stat_umm(struct rtdm_fd *fd,
                    struct cobalt_umm __user *u_stat)
 {
@@ -189,13 +210,14 @@ static int sysmem_ioctl_nrt(struct rtdm_fd *fd,
 }
 
 static struct rtdm_device private_umm_device = {
-       .struct_version         =       RTDM_DEVICE_STRUCT_VER,
-       .device_flags           =       RTDM_NAMED_DEVICE,
-       .context_size           =       0,
+       .struct_version                 =       RTDM_DEVICE_STRUCT_VER,
+       .device_flags                   =       RTDM_NAMED_DEVICE,
+       .context_size                   =       0,
        .ops = {
-               .ioctl_rt       =       umm_ioctl_rt,
-               .ioctl_nrt      =       umm_ioctl_nrt,
-               .mmap           =       umm_mmap,
+               .ioctl_rt               =       umm_ioctl_rt,
+               .ioctl_nrt              =       umm_ioctl_nrt,
+               .mmap                   =       umm_mmap,
+               .get_unmapped_area      =       umm_get_unmapped_area,
        },
        .device_class           =       RTDM_CLASS_MEMORY,
        .device_sub_class       =       UMM_PRIVATE,
@@ -208,13 +230,14 @@ static struct rtdm_device private_umm_device = {
 };
 
 static struct rtdm_device shared_umm_device = {
-       .struct_version         =       RTDM_DEVICE_STRUCT_VER,
-       .device_flags           =       RTDM_NAMED_DEVICE,
-       .context_size           =       0,
+       .struct_version                 =       RTDM_DEVICE_STRUCT_VER,
+       .device_flags                   =       RTDM_NAMED_DEVICE,
+       .context_size                   =       0,
        .ops = {
-               .ioctl_rt       =       umm_ioctl_rt,
-               .ioctl_nrt      =       umm_ioctl_nrt,
-               .mmap           =       umm_mmap,
+               .ioctl_rt               =       umm_ioctl_rt,
+               .ioctl_nrt              =       umm_ioctl_nrt,
+               .mmap                   =       umm_mmap,
+               .get_unmapped_area      =       umm_get_unmapped_area,
        },
        .device_class           =       RTDM_CLASS_MEMORY,
        .device_sub_class       =       UMM_SHARED,
diff --git a/kernel/cobalt/posix/process.c b/kernel/cobalt/posix/process.c
index d0b7826..6dc45d8 100644
--- a/kernel/cobalt/posix/process.c
+++ b/kernel/cobalt/posix/process.c
@@ -51,7 +51,7 @@
 #include <cobalt/uapi/signal.h>
 #include <cobalt/uapi/syscall.h>
 #include <trace/events/cobalt-core.h>
-#include <rtdm/fd.h>
+#include <rtdm/driver.h>
 #include <asm/xenomai/features.h>
 #include <asm/xenomai/syscall.h>
 #include <asm-generic/xenomai/mayday.h>
@@ -809,57 +809,7 @@ int ipipe_trap_hook(struct ipipe_trap_data *data)
        return KEVENT_PROPAGATE;
 }
 
-#ifdef CONFIG_MMU
-
-#define mayday_unmapped_area  NULL
-
-#else /* !CONFIG_MMU */
-
-static unsigned long mayday_unmapped_area(struct file *file,
-                                         unsigned long addr,
-                                         unsigned long len,
-                                         unsigned long pgoff,
-                                         unsigned long flags)
-{
-       return (unsigned long)mayday_page;
-}
-
-#endif /* !CONFIG_MMU */
-
-static int mayday_map(struct file *filp, struct vm_area_struct *vma)
-{
-       struct page *page = vmalloc_to_page(mayday_page);
-
-       vma->vm_pgoff = (unsigned long)mayday_page >> PAGE_SHIFT;
-#ifdef CONFIG_MMU
-       return vm_insert_page(vma, vma->vm_start, page);
-#else
-       return remap_pfn_range(vma, vma->vm_start, page_to_pfn(page),
-                              PAGE_SHIFT, vma->vm_page_prot);
-#endif
-}
-
-static struct file_operations mayday_fops = {
-       .mmap = mayday_map,
-       .get_unmapped_area = mayday_unmapped_area
-};
-
-static unsigned long map_mayday_page(struct task_struct *p)
-{
-       unsigned long u_addr;
-       struct file *filp;
-
-       filp = anon_inode_getfile("[mayday]", &mayday_fops, NULL, O_RDONLY);
-       if (IS_ERR(filp))
-               return 0;
-
-       u_addr = vm_mmap(filp, 0, PAGE_SIZE, PROT_EXEC|PROT_READ, MAP_SHARED, 
0);
-       filp_close(filp, p->files);
-
-       return IS_ERR_VALUE(u_addr) ? 0UL : u_addr;
-}
-
-static inline int mayday_init_page(void)
+static inline int init_mayday_page(void)
 {
        mayday_page = vmalloc(PAGE_SIZE);
        if (mayday_page == NULL) {
@@ -872,12 +822,25 @@ static inline int mayday_init_page(void)
        return 0;
 }
 
-static inline void mayday_cleanup_page(void)
+static inline void free_mayday_page(void)
 {
        if (mayday_page)
                vfree(mayday_page);
 }
 
+static inline unsigned long map_mayday_page(void)
+{
+       void __user *u_addr = NULL;
+       int ret;
+
+       ret = rtdm_mmap_to_user(NULL, mayday_page, PAGE_SIZE,
+                               PROT_READ|PROT_EXEC, &u_addr, NULL, NULL);
+       if (ret)
+               return 0UL;
+
+       return (unsigned long)u_addr;
+}
+
 #ifdef CONFIG_SMP
 
 static int handle_setaffinity_event(struct ipipe_cpu_migration_data *d)
@@ -1350,7 +1313,7 @@ static int attach_process(struct cobalt_process *process)
 
        cobalt_umm_set_name(&p->umm, "private heap[%d]", current->pid);
 
-       p->mayday_addr = map_mayday_page(current);
+       p->mayday_addr = map_mayday_page();
        if (p->mayday_addr == 0) {
                printk(XENO_WARN
                       "%s[%d] cannot map MAYDAY page\n",
@@ -1471,7 +1434,7 @@ int cobalt_process_init(void)
         * Setup the mayday page early, before userland can mess with
         * real-time ops.
         */
-       ret = mayday_init_page();
+       ret = init_mayday_page();
        if (ret)
                goto fail_mayday;
 
@@ -1497,7 +1460,7 @@ fail_register:
        cobalt_memdev_cleanup();
 fail_memdev:
        xnsynch_destroy(&yield_sync);
-       mayday_cleanup_page();
+       free_mayday_page();
 fail_mayday:
        xndebug_cleanup();
 fail_debug:
@@ -1514,7 +1477,7 @@ void cobalt_process_cleanup(void)
        ipipe_set_hooks(&xnsched_realtime_domain, 0);
        ipipe_set_hooks(ipipe_root_domain, 0);
 
-       mayday_cleanup_page();
+       free_mayday_page();
        xndebug_cleanup();
 
        if (process_hash) {
diff --git a/kernel/cobalt/rtdm/drvlib.c b/kernel/cobalt/rtdm/drvlib.c
index e147b6e..ce104d2 100644
--- a/kernel/cobalt/rtdm/drvlib.c
+++ b/kernel/cobalt/rtdm/drvlib.c
@@ -1476,13 +1476,9 @@ void rtdm_nrtsig_pend(rtdm_nrtsig_t *nrt_sig);
 
 struct mmap_tramp_data {
        struct rtdm_fd *fd;
+       struct file_operations *fops;
        int (*mmap_handler)(struct rtdm_fd *fd,
                            struct vm_area_struct *vma);
-       unsigned long
-       (*unmapped_area_handler)(struct file *filp,
-                                struct mmap_tramp_data *tramp_data,
-                                unsigned long addr, unsigned long len,
-                                unsigned long pgoff, unsigned long flags);
 };
 
 struct mmap_helper_data {
@@ -1495,51 +1491,43 @@ struct mmap_helper_data {
 
 static int mmap_kmem_helper(struct vm_area_struct *vma, void *va)
 {
-       unsigned long vaddr, maddr, len;
-       phys_addr_t paddr;
+       unsigned long addr, len, pfn, to;
        int ret;
-
-       vaddr = (unsigned long)va;
-       paddr = __pa(vaddr);
-       maddr = vma->vm_start;
+       
+       to = (unsigned long)va;
+       addr = vma->vm_start;
        len = vma->vm_end - vma->vm_start;
 
-       if (!XENO_ASSERT(COBALT, vaddr == PAGE_ALIGN(vaddr)))
+       if (to != PAGE_ALIGN(to) || (len & ~PAGE_MASK) != 0)
                return -EINVAL;
 
-#ifdef CONFIG_MMU
-       /* Catch vmalloc memory */
-       if (vaddr >= VMALLOC_START && vaddr < VMALLOC_END) {
-               if (!XENO_ASSERT(COBALT, (len & ~PAGE_MASK) == 0))
-                       return -EINVAL;
-
-               while (len >= PAGE_SIZE) {
-                       if (vm_insert_page(vma, maddr,
-                                          vmalloc_to_page((void *)vaddr)))
+#ifndef CONFIG_MMU
+       pfn = __pa(to) >> PAGE_SHIFT;
+       ret = remap_pfn_range(vma, addr, pfn, len, PAGE_SHARED);
+#else
+       if (to < VMALLOC_START || to >= VMALLOC_END) {
+               /* logical address. */
+               pfn = __pa(to) >> PAGE_SHIFT;
+               ret = remap_pfn_range(vma, addr, pfn, len, PAGE_SHARED);
+               if (ret)
+                       return ret;
+       } else {
+               /* vmalloc memory. */
+               while (len > 0) {
+                       struct page *page = vmalloc_to_page((void *)to);
+                       if (vm_insert_page(vma, addr, page))
                                return -EAGAIN;
-                       maddr += PAGE_SIZE;
-                       vaddr += PAGE_SIZE;
+                       addr += PAGE_SIZE;
+                       to += PAGE_SIZE;
                        len -= PAGE_SIZE;
                }
-
-               if (xnarch_machdesc.prefault)
-                       xnarch_machdesc.prefault(vma);
-
-               return 0;
        }
-#else
-       vma->vm_pgoff = paddr >> PAGE_SHIFT;
-#endif /* CONFIG_MMU */
-
-       ret = remap_pfn_range(vma, maddr, paddr >> PAGE_SHIFT,
-                             len, PAGE_SHARED);
-       if (ret)
-               return ret;
 
        if (xnarch_machdesc.prefault)
                xnarch_machdesc.prefault(vma);
+#endif
 
-       return 0;
+       return ret;
 }
 
 static int mmap_iomem_helper(struct vm_area_struct *vma, phys_addr_t pa)
@@ -1584,20 +1572,25 @@ static int mmap_buffer_helper(struct rtdm_fd *fd, 
struct vm_area_struct *vma)
 static int mmap_trampoline(struct file *filp, struct vm_area_struct *vma)
 {
        struct mmap_tramp_data *tramp_data = filp->private_data;
+       int ret;
 
        vma->vm_private_data = tramp_data;
 
-       return tramp_data->mmap_handler(tramp_data->fd, vma);
+       ret = tramp_data->mmap_handler(tramp_data->fd, vma);
+       if (ret)
+               return ret;
+
+       return 0;
 }
 
 #ifndef CONFIG_MMU
 
-static
-unsigned long unmapped_area_helper(struct file *filp,
-                                  struct mmap_tramp_data *tramp_data,
-                                  unsigned long addr, unsigned long len,
-                                  unsigned long pgoff, unsigned long flags)
+static unsigned long
+internal_get_unmapped_area(struct file *filp,
+                          unsigned long addr, unsigned long len,
+                          unsigned long pgoff, unsigned long flags)
 {
+       struct mmap_tramp_data *tramp_data = filp->private_data;
        struct mmap_helper_data *helper_data;
        unsigned long pa;
 
@@ -1609,29 +1602,34 @@ unsigned long unmapped_area_helper(struct file *filp,
        return (unsigned long)helper_data->src_vaddr;
 }
 
-static unsigned long
-unmapped_area_trampoline(struct file *filp,
-                        unsigned long addr, unsigned long len,
-                        unsigned long pgoff, unsigned long flags)
+static int do_rtdm_mmap(struct mmap_tramp_data *tramp_data,
+                       size_t len, off_t offset, int prot, int flags,
+                       void **pptr)
 {
-       struct mmap_tramp_data *tramp_data = filp->private_data;
+       const struct file_operations *old_fops;
+       unsigned long u_addr;
+       struct file *filp;
 
-       if (tramp_data->unmapped_area_handler == NULL)
-               return -ENOSYS; /* We don't know. */
+       filp = filp_open("/dev/mem", O_RDWR, 0);
+       if (IS_ERR(filp))
+               return PTR_ERR(filp);
 
-       return tramp_data->unmapped_area_handler(filp, tramp_data, addr,
-                                                len, pgoff, flags);
-}
+       old_fops = filp->f_op;
+       filp->f_op = tramp_data->fops;
+       filp->private_data = tramp_data;
+       u_addr = vm_mmap(filp, (unsigned long)*pptr, len, prot, flags, offset);
+       filp_close(filp, current->files);
+       filp->f_op = old_fops;
 
-#else
-#define unmapped_area_helper      NULL
-#define unmapped_area_trampoline  NULL
-#endif
+       if (IS_ERR_VALUE(u_addr))
+               return (int)u_addr;
 
-static struct file_operations mmap_fops = {
-       .mmap = mmap_trampoline,
-       .get_unmapped_area = unmapped_area_trampoline
-};
+       *pptr = (void *)u_addr;
+
+       return 0;
+}
+
+#else /* CONFIG_MMU */
 
 static int do_rtdm_mmap(struct mmap_tramp_data *tramp_data,
                        size_t len, off_t offset, int prot, int flags,
@@ -1640,10 +1638,7 @@ static int do_rtdm_mmap(struct mmap_tramp_data 
*tramp_data,
        unsigned long u_addr;
        struct file *filp;
 
-       if (!XENO_ASSERT(COBALT, xnsched_root_p()))
-               return -EPERM;
-
-       filp = anon_inode_getfile("[rtdm]", &mmap_fops, tramp_data, O_RDWR);
+       filp = anon_inode_getfile("[rtdm]", tramp_data->fops, tramp_data, 
O_RDWR);
        if (IS_ERR(filp))
                return PTR_ERR(filp);
 
@@ -1658,15 +1653,65 @@ static int do_rtdm_mmap(struct mmap_tramp_data 
*tramp_data,
        return 0;
 }
 
+#define internal_get_unmapped_area  NULL
+
+#endif /* CONFIG_MMU */
+
+static struct file_operations internal_mmap_fops = {
+       .mmap = mmap_trampoline,
+       .get_unmapped_area = internal_get_unmapped_area
+};
+
+static unsigned long
+driver_get_unmapped_area(struct file *filp,
+                        unsigned long addr, unsigned long len,
+                        unsigned long pgoff, unsigned long flags)
+{
+       struct mmap_tramp_data *tramp_data = filp->private_data;
+       struct rtdm_fd *fd = tramp_data->fd;
+
+       if (fd->ops->get_unmapped_area)
+               return fd->ops->get_unmapped_area(fd, len, pgoff, flags);
+
+#ifdef CONFIG_MMU
+       /* Run default handler. */
+       return current->mm->get_unmapped_area(filp, addr, len, pgoff, flags);
+#else
+       return -ENODEV;
+#endif
+}
+
+static struct file_operations driver_mmap_fops = {
+       .mmap = mmap_trampoline,
+       .get_unmapped_area = driver_get_unmapped_area
+};
+
 int __rtdm_mmap_from_fdop(struct rtdm_fd *fd, size_t len, off_t offset,
                          int prot, int flags, void *__user *pptr)
 {
        struct mmap_tramp_data tramp_data = {
                .fd = fd,
+               .fops = &driver_mmap_fops,
                .mmap_handler = fd->ops->mmap,
-               .unmapped_area_handler = NULL,
        };
 
+#ifndef CONFIG_MMU
+       /*
+        * XXX: A .get_unmapped_area handler must be provided in the
+        * nommu case. We use this to force the memory management code
+        * not to share VM regions for distinct areas to map to, as it
+        * would otherwise do since all requests originate from the
+        * same file (i.e. from /dev/mem, see do_mmap_pgoff() in the
+        * nommu case).
+        *
+        * We could solve this by implementing a virtual filesystem
+        * for RTDM devices, which would go a long way toward a better
+        * integration with the regular device driver model.
+        */
+       if (fd->ops->get_unmapped_area)
+               offset = fd->ops->get_unmapped_area(fd, len, 0, flags);
+#endif
+
        return do_rtdm_mmap(&tramp_data, len, offset, prot, flags, pptr);
 }
 
@@ -1727,8 +1772,8 @@ int rtdm_mmap_to_user(struct rtdm_fd *fd,
        struct mmap_helper_data helper_data = {
                .tramp_data = {
                        .fd = fd,
+                       .fops = &internal_mmap_fops,
                        .mmap_handler = mmap_buffer_helper,
-                       .unmapped_area_handler = unmapped_area_helper,
                },
                .src_vaddr = src_addr,
                .src_paddr = 0,
@@ -1736,6 +1781,9 @@ int rtdm_mmap_to_user(struct rtdm_fd *fd,
                .vm_private_data = vm_private_data
        };
 
+       if (!XENO_ASSERT(COBALT, xnsched_root_p()))
+               return -EPERM;
+
        return do_rtdm_mmap(&helper_data.tramp_data, len, 0, prot, MAP_SHARED, 
pptr);
 }
 EXPORT_SYMBOL_GPL(rtdm_mmap_to_user);
@@ -1793,8 +1841,8 @@ int rtdm_iomap_to_user(struct rtdm_fd *fd,
        struct mmap_helper_data helper_data = {
                .tramp_data = {
                        .fd = fd,
+                       .fops = &internal_mmap_fops,
                        .mmap_handler = mmap_buffer_helper,
-                       .unmapped_area_handler = unmapped_area_helper,
                },
                .src_vaddr = NULL,
                .src_paddr = src_addr,
@@ -1802,6 +1850,9 @@ int rtdm_iomap_to_user(struct rtdm_fd *fd,
                .vm_private_data = vm_private_data
        };
 
+       if (!XENO_ASSERT(COBALT, xnsched_root_p()))
+               return -EPERM;
+
        return do_rtdm_mmap(&helper_data.tramp_data, len, 0, prot, MAP_SHARED, 
pptr);
 }
 EXPORT_SYMBOL_GPL(rtdm_iomap_to_user);


_______________________________________________
Xenomai-git mailing list
Xenomai-git@xenomai.org
http://www.xenomai.org/mailman/listinfo/xenomai-git

Reply via email to