Module: xenomai-gch
Branch: for-forge
Commit: a86964bf54170b9992e3f19ab7d315cdf0c46689
URL:    
http://git.xenomai.org/?p=xenomai-gch.git;a=commit;h=a86964bf54170b9992e3f19ab7d315cdf0c46689

Author: Gilles Chanteperdrix <gilles.chanteperd...@xenomai.org>
Date:   Mon Dec 23 21:03:04 2013 +0100

cobalt/fd: add common fd implementation

---

 include/cobalt/kernel/fd.h   |   67 ++++++++++++
 include/cobalt/kernel/ppd.h  |    1 +
 kernel/cobalt/Makefile       |    1 +
 kernel/cobalt/fd.c           |  235 ++++++++++++++++++++++++++++++++++++++++++
 kernel/cobalt/init.c         |    9 +-
 kernel/cobalt/posix/select.c |    9 ++
 kernel/cobalt/shadow.c       |    3 +
 7 files changed, 324 insertions(+), 1 deletion(-)

diff --git a/include/cobalt/kernel/fd.h b/include/cobalt/kernel/fd.h
new file mode 100644
index 0000000..54ef678
--- /dev/null
+++ b/include/cobalt/kernel/fd.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2013 Gilles Chanteperdrix <gilles.chanteperd...@xenomai.org>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _COBALT_KERNEL_FD_H
+#define _COBALT_KERNEL_FD_H
+
+struct xnfd;
+struct xnselector;
+struct xnsys_ppd;
+
+struct xnfd_ops {
+       void (*destroy)(struct xnfd *fd);
+       int (*select_bind)(struct xnfd *fd, struct xnselector *selector,
+                       unsigned type, unsigned index);
+};
+
+struct xnfd {
+       unsigned magic;
+       struct mm_struct *mm;
+       int ufd;
+       struct xnfd_ops *ops;
+       unsigned refs;
+       struct hlist_node hlink; /* Link in global hash */
+       struct list_head link;   /* Link in per-process queue */
+};
+
+int xnfd_enter(struct xnfd *xnfd, unsigned magic, int ufd, 
+       struct xnsys_ppd *p, struct xnfd_ops *ops);
+
+struct xnfd *xnfd_get(int ufd, struct mm_struct *mm, unsigned magic);
+
+int xnfd_put(struct xnfd *xnfd);
+
+int xnfd_close(int ufd, struct mm_struct *mm, unsigned magic);
+
+static inline int xnfd_getfd(struct xnfd *xnfd)
+{
+       return xnfd->ufd;
+}
+
+int xnfd_valid_p(int ufd, struct mm_struct *mm);
+
+int xnfd_select_bind(int ufd, struct mm_struct *mm,
+               struct xnselector *selector, unsigned type);
+
+void xnfd_cleanup(struct xnsys_ppd *p);
+
+int xnfd_mount(void);
+
+void xnfd_umount(void);
+
+#endif /* _COBALT_KERNEL_FD_H */
diff --git a/include/cobalt/kernel/ppd.h b/include/cobalt/kernel/ppd.h
index 05e32b3..367f3e0 100644
--- a/include/cobalt/kernel/ppd.h
+++ b/include/cobalt/kernel/ppd.h
@@ -49,6 +49,7 @@ struct xnsys_ppd {
        unsigned long mayday_addr;
        atomic_t refcnt;
        char *exe_path;
+       struct list_head fds;
 };
 
 extern struct xnsys_ppd __xnsys_global_ppd;
diff --git a/kernel/cobalt/Makefile b/kernel/cobalt/Makefile
index 7e65f10..e5e3355 100644
--- a/kernel/cobalt/Makefile
+++ b/kernel/cobalt/Makefile
@@ -5,6 +5,7 @@ xenomai-y :=    apc.o           \
                assert.o        \
                bufd.o          \
                clock.o         \
+               fd.o            \
                heap.o          \
                init.o          \
                intr.o          \
diff --git a/kernel/cobalt/fd.c b/kernel/cobalt/fd.c
new file mode 100644
index 0000000..cc1af8b
--- /dev/null
+++ b/kernel/cobalt/fd.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2013 Gilles Chanteperdrix <gilles.chanteperd...@xenomai.org>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/list.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <cobalt/kernel/registry.h>
+#include <cobalt/kernel/lock.h>
+#include <cobalt/kernel/fd.h>
+#include <cobalt/kernel/ppd.h>
+
+static struct hlist_head *xnfd_hash;
+DEFINE_XNLOCK(xnfd_lock);
+static unsigned mm_mult, mm_shift, xnfd_hash_size;
+
+static unsigned __attribute__((pure))
+xnfd_crunch(int ufd, struct mm_struct *mm)
+{
+       unsigned long hash = (unsigned long)mm;
+       hash = ufd + (((unsigned long long)hash * mm_mult) >> mm_shift);
+       return hash % xnfd_hash_size;
+}
+
+static struct xnfd *xnfd_hash_search(int ufd, struct mm_struct *mm)
+{
+       unsigned bucket = xnfd_crunch(ufd, mm);
+       struct xnfd *xnfd;
+       
+       hlist_for_each_entry(xnfd, &xnfd_hash[bucket], hlink)
+               if (xnfd->ufd == ufd && xnfd->mm == mm)
+                       return xnfd;
+       
+       return NULL;
+}
+
+int xnfd_enter(struct xnfd *xnfd, unsigned magic, int ufd, 
+       struct xnsys_ppd *p, struct xnfd_ops *ops)
+{
+       unsigned bucket;
+       spl_t s;
+
+       if (magic == 0)
+               return -EINVAL;
+
+       xnfd->magic = magic;
+       xnfd->mm = p != &__xnsys_global_ppd ? p->ppd.key.mm : NULL;
+       xnfd->ufd = ufd;
+       xnfd->refs = 1;
+       xnfd->ops = ops;
+       bucket = xnfd_crunch(ufd, xnfd->mm);
+       
+       xnlock_get_irqsave(&xnfd_lock, s);
+#if XENO_DEBUG(COBALT)
+       if (xnfd_hash_search(ufd, xnfd->mm)) {
+               xnlock_put_irqrestore(&xnfd_lock, s);
+               return -EBUSY;
+       }
+#endif
+       hlist_add_head(&xnfd->hlink, &xnfd_hash[bucket]);
+       list_add(&xnfd->link, &p->fds);
+       xnlock_put_irqrestore(&xnfd_lock, s);
+       
+       return 0;
+}
+
+struct xnfd *xnfd_get(int ufd, struct mm_struct *mm, unsigned magic)
+{
+       struct xnfd *res;
+       spl_t s;
+       
+       xnlock_get_irqsave(&xnfd_lock, s);
+       res = xnfd_hash_search(ufd, mm);
+       if (res == NULL) {
+               res = ERR_PTR(-ENOENT);
+               goto err_unlock;
+       }
+       
+       if (res->magic != magic) {
+               res = ERR_PTR(-EBADF);
+               goto err_unlock;
+       }
+       
+       ++res->refs;
+  err_unlock:
+       xnlock_put_irqrestore(&xnfd_lock, s);
+       
+       return res;
+}
+
+static void xnfd_put_inner(struct xnfd *xnfd, spl_t s)
+{
+       int destroy;
+
+       destroy = --xnfd->refs == 0;
+       if (destroy) {
+               if (xnfd->magic)
+                       hlist_del(&xnfd->hlink);
+               list_del(&xnfd->link);
+       }
+       xnlock_put_irqrestore(&xnfd_lock, s);
+       
+       if (destroy)
+               xnfd->ops->destroy(xnfd);
+}
+
+int xnfd_put(struct xnfd *xnfd)
+{
+       spl_t s;
+       
+       xnlock_get_irqsave(&xnfd_lock, s);
+#if XENO_DEBUG(COBALT)
+       if (xnfd_hash_search(xnfd->ufd, xnfd->mm) != xnfd) {
+               xnlock_put_irqrestore(&xnfd_lock, s);
+               return -EBADF;
+       }
+#endif
+
+       xnfd_put_inner(xnfd, s);
+
+       return 0;
+}
+
+int xnfd_close(int ufd, struct mm_struct *mm, unsigned magic)
+{
+       struct xnfd *xnfd;
+       spl_t s;
+       
+       xnlock_get_irqsave(&xnfd_lock, s);
+       xnfd = xnfd_hash_search(ufd, mm);
+       if (xnfd == NULL || xnfd->magic != magic) {
+               xnlock_put_irqrestore(&xnfd_lock, s);
+               return -EBADF;
+       }
+
+       xnfd->magic = 0;
+       hlist_del(&xnfd->hlink);
+       
+       xnfd_put_inner(xnfd, s);
+
+       return 0;
+}
+
+int xnfd_valid_p(int ufd, struct mm_struct *mm)
+{
+       struct xnfd *xnfd;
+       spl_t s;
+
+       xnlock_get_irqsave(&xnfd_lock, s);
+       xnfd = xnfd_hash_search(ufd, mm);
+       xnlock_put_irqrestore(&xnfd_lock, s);
+       
+       return xnfd != NULL;
+}
+
+
+int xnfd_select_bind(int ufd, struct mm_struct *mm,
+               struct xnselector *selector, unsigned type)
+{
+       struct xnfd *xnfd;
+       spl_t s;
+       int rc;
+       
+       xnlock_get_irqsave(&xnfd_lock, s);
+       xnfd = xnfd_hash_search(ufd, mm);
+       if (xnfd == NULL) {
+               xnlock_put_irqrestore(&xnfd_lock, s);
+               return -ENOENT;
+       }
+       xnfd->refs++;
+       xnlock_put_irqrestore(&xnfd_lock, s);
+       
+       rc = xnfd->ops->select_bind(xnfd, selector, type, ufd);
+
+       xnlock_get_irqsave(&xnfd_lock, s);
+       xnfd_put_inner(xnfd, s);
+       
+       return rc;
+}
+
+void xnfd_cleanup(struct xnsys_ppd *p)
+{
+       struct xnfd *xnfd, *tmp;
+       spl_t s;
+       
+       xnlock_get_irqsave(&xnfd_lock, s);
+       list_for_each_entry_safe(xnfd, tmp, &p->fds, link) {
+               xnfd->refs = 1;
+               xnfd_put_inner(xnfd, s);
+               
+               xnlock_get_irqsave(&xnfd_lock, s);
+       }
+       xnlock_put_irqrestore(&xnfd_lock, s);
+}
+
+int xnfd_mount(void)
+{
+       unsigned i;
+       
+       xnfd_hash_size = xnregistry_hash_size();
+       xnfd_hash = kmalloc(xnfd_hash_size * sizeof(*xnfd_hash), GFP_KERNEL);
+       if (xnfd_hash == NULL)
+               return -ENOMEM;
+       
+       for (i = 0; i < xnfd_hash_size; i++)
+               INIT_HLIST_HEAD(&xnfd_hash[i]);
+
+       i = int_sqrt(xnfd_hash_size);
+       mm_shift = 32 - fls(i);
+       mm_mult = (i << mm_shift) / sizeof(struct mm_struct);
+       
+       INIT_LIST_HEAD(&__xnsys_global_ppd.fds);
+
+       return 0;
+}
+
+void xnfd_umount(void)
+{
+       kfree(xnfd_hash);
+}
diff --git a/kernel/cobalt/init.c b/kernel/cobalt/init.c
index bda790e..86edaa3 100644
--- a/kernel/cobalt/init.c
+++ b/kernel/cobalt/init.c
@@ -33,6 +33,7 @@
 #include <cobalt/kernel/pipe.h>
 #include <cobalt/kernel/select.h>
 #include <cobalt/kernel/vdso.h>
+#include <cobalt/kernel/fd.h>
 #include "rtdm/internal.h"
 #include "posix/internal.h"
 #include "procfs.h"
@@ -387,10 +388,14 @@ static int __init xenomai_init(void)
        if (ret)
                goto cleanup_select;
 
-       ret = sys_init();
+       ret = xnfd_mount();
        if (ret)
                goto cleanup_shadow;
 
+       ret = sys_init();
+       if (ret)
+               goto cleanup_fd;
+
        ret = rtdm_init();
        if (ret)
                goto cleanup_sys;
@@ -408,6 +413,8 @@ cleanup_rtdm:
        rtdm_cleanup();
 cleanup_sys:
        sys_shutdown();
+cleanup_fd:
+       xnfd_umount();
 cleanup_shadow:
        xnshadow_cleanup();
 cleanup_select:
diff --git a/kernel/cobalt/posix/select.c b/kernel/cobalt/posix/select.c
index ff94f98..8f2326d 100644
--- a/kernel/cobalt/posix/select.c
+++ b/kernel/cobalt/posix/select.c
@@ -21,6 +21,7 @@
  */
 #include <linux/types.h>
 #include <linux/err.h>
+#include <cobalt/kernel/fd.h>
 #include <rtdm/rtdm_driver.h>
 #include "internal.h"
 #include "clock.h"
@@ -35,6 +36,9 @@ static int fd_valid_p(int fd)
        struct rtdm_dev_context *ctx;
        struct cobalt_process *cc;
 
+       if (xnfd_valid_p(fd, current->mm))
+               return 1;
+
        if (fd >= rtdm_fd_start) {
                ctx = rtdm_context_get(fd - rtdm_fd_start);
                if (ctx == NULL)
@@ -69,6 +73,11 @@ static int select_bind_one(struct xnselector *selector, 
unsigned type, int fd)
        const int rtdm_fd_start = __FD_SETSIZE - RTDM_FD_MAX;
        struct cobalt_process *cc;
        cobalt_assoc_t *assoc;
+       int rc;
+
+       rc = xnfd_select_bind(fd, current->mm, selector, type);
+       if (rc != -ENOENT)
+               return rc;
 
        if (fd >= rtdm_fd_start)
                return rtdm_select_bind(fd - rtdm_fd_start,
diff --git a/kernel/cobalt/shadow.c b/kernel/cobalt/shadow.c
index 8a958ad..e18a76b 100644
--- a/kernel/cobalt/shadow.c
+++ b/kernel/cobalt/shadow.c
@@ -58,6 +58,7 @@
 #include <cobalt/kernel/ppd.h>
 #include <cobalt/kernel/vdso.h>
 #include <cobalt/kernel/thread.h>
+#include <cobalt/kernel/fd.h>
 #include <asm/xenomai/features.h>
 #include <asm/xenomai/syscall.h>
 #include <asm-generic/xenomai/mayday.h>
@@ -1729,6 +1730,7 @@ static struct xnshadow_ppd *user_process_attach(void)
                exe_path = NULL; /* Not lethal, but weird. */
        }
        p->exe_path = exe_path;
+       INIT_LIST_HEAD(&p->fds);
        atomic_set(&p->refcnt, 1);
        atomic_inc(&personalities[user_muxid]->refcnt);
 
@@ -1743,6 +1745,7 @@ static void user_process_detach(struct xnshadow_ppd *ppd)
        if (p->exe_path)
                kfree(p->exe_path);
        xnheap_destroy_mapped(&p->sem_heap, post_ppd_release, NULL);
+       xnfd_cleanup(p);
        atomic_dec(&personalities[user_muxid]->refcnt);
 }
 


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

Reply via email to