Module: xenomai-jki
Branch: queues/proc
Commit: c628ba4a3dad4273e15591b8bc78f7629fe4658e
URL:    
http://git.xenomai.org/?p=xenomai-jki.git;a=commit;h=c628ba4a3dad4273e15591b8bc78f7629fe4658e

Author: Jan Kiszka <jan.kis...@siemens.com>
Date:   Tue Apr  6 17:03:30 2010 +0200

RTDM: Early fd release with poll-free context reference tracking

---

 include/rtdm/rtdm_driver.h |   16 ++++-
 ksrc/skins/rtdm/core.c     |  168 ++++++++++++++++++++++++++++++-------------
 ksrc/skins/rtdm/device.c   |   45 ++++++++++--
 ksrc/skins/rtdm/internal.h |    8 +--
 4 files changed, 170 insertions(+), 67 deletions(-)

diff --git a/include/rtdm/rtdm_driver.h b/include/rtdm/rtdm_driver.h
index bd8a9d0..c856568 100644
--- a/include/rtdm/rtdm_driver.h
+++ b/include/rtdm/rtdm_driver.h
@@ -379,6 +379,7 @@ struct rtdm_operations {
 
 struct rtdm_devctx_reserved {
        void *owner;
+       struct list_head cleanup;
 };
 
 /**
@@ -560,19 +561,28 @@ int rtdm_dev_unregister(struct rtdm_device *device, 
unsigned int poll_delay);
 struct rtdm_dev_context *rtdm_context_get(int fd);
 
 #ifndef DOXYGEN_CPP /* Avoid static inline tags for RTDM in doxygen */
+
+#define CONTEXT_IS_LOCKED(context) \
+       (atomic_read(&(context)->close_lock_count) > 1 || \
+        (test_bit(RTDM_CLOSING, &(context)->context_flags) && \
+         atomic_read(&(context)->close_lock_count) > 0))
+
 static inline void rtdm_context_lock(struct rtdm_dev_context *context)
 {
-       XENO_ASSERT(RTDM, atomic_read(&context->close_lock_count) > 0,
+       XENO_ASSERT(RTDM, CONTEXT_IS_LOCKED(context),
                    /* just warn if context was a dangling pointer */);
        atomic_inc(&context->close_lock_count);
 }
 
+extern int rtdm_apc;
+
 static inline void rtdm_context_unlock(struct rtdm_dev_context *context)
 {
-       XENO_ASSERT(RTDM, atomic_read(&context->close_lock_count) > 0,
+       XENO_ASSERT(RTDM, CONTEXT_IS_LOCKED(context),
                    /* just warn if context was a dangling pointer */);
        smp_mb__before_atomic_dec();
-       atomic_dec(&context->close_lock_count);
+       if (unlikely(atomic_dec_and_test(&context->close_lock_count)))
+               rthal_apc_schedule(rtdm_apc);
 }
 
 static inline void rtdm_context_put(struct rtdm_dev_context *context)
diff --git a/ksrc/skins/rtdm/core.c b/ksrc/skins/rtdm/core.c
index e04b3f6..bf905df 100644
--- a/ksrc/skins/rtdm/core.c
+++ b/ksrc/skins/rtdm/core.c
@@ -26,7 +26,7 @@
  * @{
  */
 
-#include <linux/delay.h>
+#include <linux/workqueue.h>
 
 #include <nucleus/pod.h>
 #include <nucleus/ppd.h>
@@ -35,7 +35,7 @@
 
 #include "rtdm/internal.h"
 
-#define CLOSURE_RETRY_PERIOD   100     /* ms */
+#define CLOSURE_RETRY_PERIOD_MS        100
 
 #define FD_BITMAP_SIZE  ((RTDM_FD_MAX + BITS_PER_LONG-1) / BITS_PER_LONG)
 
@@ -44,6 +44,10 @@ struct rtdm_fildes fildes_table[RTDM_FD_MAX] =
 static unsigned long used_fildes[FD_BITMAP_SIZE];
 int open_fildes;       /* number of used descriptors */
 
+static DECLARE_WORK_FUNC(close_callback);
+static DECLARE_DELAYED_WORK_NODATA(close_work, close_callback);
+static LIST_HEAD(cleanup_queue);
+
 xntbase_t *rtdm_tbase;
 EXPORT_SYMBOL(rtdm_tbase);
 
@@ -81,8 +85,7 @@ struct rtdm_dev_context *rtdm_context_get(int fd)
        xnlock_get_irqsave(&rt_fildes_lock, s);
 
        context = fildes_table[fd].context;
-       if (unlikely(!context ||
-                    test_bit(RTDM_CLOSING, &context->context_flags))) {
+       if (unlikely(!context)) {
                xnlock_put_irqrestore(&rt_fildes_lock, s);
                return NULL;
        }
@@ -106,8 +109,10 @@ static int create_instance(struct rtdm_device *device,
        int fd;
        spl_t s;
 
-       /* Reset to NULL so that we can always use cleanup_instance to revert
-          also partially successful allocations */
+       /*
+        * Reset to NULL so that we can always use cleanup_files/instance to
+        * revert also partially successful allocations.
+        */
        *context_ptr = NULL;
        *fildes_ptr = NULL;
 
@@ -155,7 +160,7 @@ static int create_instance(struct rtdm_device *device,
 
        context->fd = fd;
        context->ops = &device->ops;
-       atomic_set(&context->close_lock_count, 0);
+       atomic_set(&context->close_lock_count, 1);
 
 #ifdef CONFIG_XENO_OPT_PERVASIVE
        ppd = xnshadow_ppd_get(__rtdm_muxid);
@@ -163,31 +168,34 @@ static int create_instance(struct rtdm_device *device,
 
        context->reserved.owner =
            ppd ? container_of(ppd, struct rtdm_process, ppd) : NULL;
+       INIT_LIST_HEAD(&context->reserved.cleanup);
 
        return 0;
 }
 
-static int cleanup_instance(struct rtdm_device *device,
-                           struct rtdm_dev_context *context,
-                           struct rtdm_fildes *fildes, int nrt_mem)
+static void __cleanup_fildes(struct rtdm_fildes *fildes)
 {
-       spl_t s;
-
-       xnlock_get_irqsave(&rt_fildes_lock, s);
+       clear_bit((fildes - fildes_table), used_fildes);
+       fildes->context = NULL;
+       open_fildes--;
+}
 
-       if (context && unlikely(atomic_read(&context->close_lock_count) > 1)) {
-               xnlock_put_irqrestore(&rt_fildes_lock, s);
-               return -EAGAIN;
-       }
+static void cleanup_fildes(struct rtdm_fildes *fildes)
+{
+       spl_t s;
 
-       if (fildes) {
-               clear_bit((fildes - fildes_table), used_fildes);
-               fildes->context = NULL;
-               open_fildes--;
-       }
+       if (!fildes)
+               return;
 
+       xnlock_get_irqsave(&rt_fildes_lock, s);
+       __cleanup_fildes(fildes);
        xnlock_put_irqrestore(&rt_fildes_lock, s);
+}
 
+static void cleanup_instance(struct rtdm_device *device,
+                            struct rtdm_dev_context *context,
+                            int nrt_mem)
+{
        if (context) {
                if (device->reserved.exclusive_context)
                        context->device = NULL;
@@ -200,10 +208,62 @@ static int cleanup_instance(struct rtdm_device *device,
        }
 
        rtdm_dereference_device(device);
+}
 
-       return 0;
+static DECLARE_WORK_FUNC(close_callback)
+{
+       struct rtdm_dev_context *context;
+       LIST_HEAD(deferred_list);
+       int reschedule = 0;
+       int err;
+       spl_t s;
+
+       xnlock_get_irqsave(&rt_fildes_lock, s);
+
+       while (!list_empty(&cleanup_queue)) {
+               context = list_first_entry(&cleanup_queue,
+                                          struct rtdm_dev_context,
+                                          reserved.cleanup);
+               list_del(&context->reserved.cleanup);
+               atomic_inc(&context->close_lock_count);
+
+               xnlock_put_irqrestore(&rt_fildes_lock, s);
+
+               err = context->ops->close_nrt(context, NULL);
+
+               if (err == -EAGAIN ||
+                   atomic_read(&context->close_lock_count) > 1) {
+                       atomic_dec(&context->close_lock_count);
+                       list_add_tail(&context->reserved.cleanup,
+                                     &deferred_list);
+                       if (err == -EAGAIN)
+                               reschedule = 1;
+               } else {
+                       trace_mark(xn_rtdm, fd_closed, "fd %d", context->fd);
+
+                       cleanup_instance(context->device, context,
+                                        test_bit(RTDM_CREATED_IN_NRT,
+                                                 &context->context_flags));
+               }
+
+               xnlock_get_irqsave(&rt_fildes_lock, s);
+       }
+
+       list_splice(&deferred_list, &cleanup_queue);
+
+       xnlock_put_irqrestore(&rt_fildes_lock, s);
+
+       if (reschedule)
+               schedule_delayed_work(&close_work,
+                                     (HZ * CLOSURE_RETRY_PERIOD_MS) / 1000);
 }
 
+void rtdm_apc_handler(void *cookie)
+{
+       schedule_delayed_work(&close_work, 0);
+}
+
+
 int __rt_dev_open(rtdm_user_info_t *user_info, const char *path, int oflag)
 {
        struct rtdm_device *device;
@@ -245,7 +305,8 @@ int __rt_dev_open(rtdm_user_info_t *user_info, const char 
*path, int oflag)
        return context->fd;
 
 cleanup_out:
-       cleanup_instance(device, context, fildes, nrt_mode);
+       cleanup_fildes(fildes);
+       cleanup_instance(device, context, nrt_mode);
 
 err_out:
        return ret;
@@ -296,7 +357,8 @@ int __rt_dev_socket(rtdm_user_info_t *user_info, int 
protocol_family,
        return context->fd;
 
 cleanup_out:
-       cleanup_instance(device, context, fildes, nrt_mode);
+       cleanup_fildes(fildes);
+       cleanup_instance(device, context, nrt_mode);
 
 err_out:
        return ret;
@@ -317,7 +379,6 @@ int __rt_dev_close(rtdm_user_info_t *user_info, int fd)
        if (unlikely((unsigned int)fd >= RTDM_FD_MAX))
                goto err_out;
 
-again:
        xnlock_get_irqsave(&rt_fildes_lock, s);
 
        context = fildes_table[fd].context;
@@ -327,47 +388,52 @@ again:
                goto err_out;   /* -EBADF */
        }
 
+       /* Avoid asymmetric close context by switching to nrt. */
+       if (unlikely(test_bit(RTDM_CREATED_IN_NRT, &context->context_flags)) &&
+           !nrt_mode) {
+               xnlock_put_irqrestore(&rt_fildes_lock, s);
+
+               ret = -ENOSYS;
+               goto err_out;
+       }
+
        set_bit(RTDM_CLOSING, &context->context_flags);
        atomic_inc(&context->close_lock_count);
 
+       __cleanup_fildes(&fildes_table[fd]);
+
        xnlock_put_irqrestore(&rt_fildes_lock, s);
 
        if (nrt_mode)
                ret = context->ops->close_nrt(context, user_info);
-       else {
-               /* Avoid asymmetric close context by switching to nrt. */
-               if (unlikely(
-                   test_bit(RTDM_CREATED_IN_NRT, &context->context_flags))) {
-                       ret = -ENOSYS;
-                       goto unlock_out;
-               }
+       else
                ret = context->ops->close_rt(context, user_info);
-       }
 
        XENO_ASSERT(RTDM, !rthal_local_irq_disabled(),
                    rthal_local_irq_enable(););
 
-       if (unlikely(ret == -EAGAIN) && nrt_mode) {
-               rtdm_context_unlock(context);
-               msleep(CLOSURE_RETRY_PERIOD);
-               goto again;
-       } else if (unlikely(ret < 0))
-               goto unlock_out;
+       xnlock_get_irqsave(&rt_fildes_lock, s);
 
-       ret = cleanup_instance(context->device, context, &fildes_table[fd],
-                              test_bit(RTDM_CREATED_IN_NRT,
-                              &context->context_flags));
-       if (ret < 0) {
-               rtdm_context_unlock(context);
+       if (ret == -EAGAIN || atomic_read(&context->close_lock_count) > 2) {
+               atomic_dec(&context->close_lock_count);
+               list_add(&context->reserved.cleanup, &cleanup_queue);
 
-               if (!nrt_mode)
-                       goto err_out;
+               xnlock_put_irqrestore(&rt_fildes_lock, s);
 
-               msleep(CLOSURE_RETRY_PERIOD);
-               goto again;
+               if (ret == -EAGAIN) {
+                       rthal_apc_schedule(rtdm_apc);
+                       ret = 0;
+               }
+               goto unlock_out;
        }
 
-       trace_mark(xn_rtdm, fd_closed, "fd %d", fd);
+       xnlock_put_irqrestore(&rt_fildes_lock, s);
+
+       trace_mark(xn_rtdm, fd_closed, "fd %d", context->fd);
+
+       cleanup_instance(context->device, context,
+                        test_bit(RTDM_CREATED_IN_NRT,
+                                 &context->context_flags));
 
        return ret;
 
@@ -402,7 +468,7 @@ void cleanup_owned_contexts(void *owner)
                                         fd);
 
                        ret = __rt_dev_close(NULL, fd);
-                       XENO_ASSERT(RTDM, ret >= 0 || ret == -EBADF,
+                       XENO_ASSERT(RTDM, ret == 0 || ret == -EBADF,
                                    /* only warn here */;);
                }
        }
diff --git a/ksrc/skins/rtdm/device.c b/ksrc/skins/rtdm/device.c
index 3955bc0..9f38737 100644
--- a/ksrc/skins/rtdm/device.c
+++ b/ksrc/skins/rtdm/device.c
@@ -58,6 +58,9 @@ struct list_head *rtdm_protocol_devices;      /* hash table */
 static int name_hashkey_mask;
 static int proto_hashkey_mask;
 
+int rtdm_apc;
+EXPORT_SYMBOL(rtdm_apc);
+
 DECLARE_MUTEX(nrt_dev_lock);
 DEFINE_XNLOCK(rt_dev_lock);
 
@@ -457,19 +460,28 @@ EXPORT_SYMBOL(rtdm_dev_unregister);
 
 int __init rtdm_dev_init(void)
 {
-       int i;
+       int err, i;
+
+       rtdm_apc = rthal_apc_alloc("deferred RTDM close", rtdm_apc_handler,
+                                  NULL);
+       if (rtdm_apc < 0)
+               return rtdm_apc;
 
        name_hashkey_mask = devname_hashtab_size - 1;
        proto_hashkey_mask = protocol_hashtab_size - 1;
        if (((devname_hashtab_size & name_hashkey_mask) != 0) ||
-           ((protocol_hashtab_size & proto_hashkey_mask) != 0))
-               return -EINVAL;
+           ((protocol_hashtab_size & proto_hashkey_mask) != 0)) {
+               err = -EINVAL;
+               goto err_out1;
+       }
 
        rtdm_named_devices = (struct list_head *)
            kmalloc(devname_hashtab_size * sizeof(struct list_head),
                    GFP_KERNEL);
-       if (!rtdm_named_devices)
-               return -ENOMEM;
+       if (!rtdm_named_devices) {
+               err = -ENOMEM;
+               goto err_out1;
+       }
 
        for (i = 0; i < devname_hashtab_size; i++)
                INIT_LIST_HEAD(&rtdm_named_devices[i]);
@@ -478,14 +490,33 @@ int __init rtdm_dev_init(void)
            kmalloc(protocol_hashtab_size * sizeof(struct list_head),
                    GFP_KERNEL);
        if (!rtdm_protocol_devices) {
-               kfree(rtdm_named_devices);
-               return -ENOMEM;
+               err = -ENOMEM;
+               goto err_out2;
        }
 
        for (i = 0; i < protocol_hashtab_size; i++)
                INIT_LIST_HEAD(&rtdm_protocol_devices[i]);
 
        return 0;
+
+err_out2:
+       kfree(rtdm_named_devices);
+
+err_out1:
+       rthal_apc_free(rtdm_apc);
+
+       return err;
+}
+
+void __exit rtdm_dev_cleanup(void)
+{
+       /*
+        * Note: no need to flush the cleanup_queue as no device is allowed
+        * to deregister as long as there are references.
+        */
+       rthal_apc_free(rtdm_apc);
+       kfree(rtdm_named_devices);
+       kfree(rtdm_protocol_devices);
 }
 
 /*...@}*/
diff --git a/ksrc/skins/rtdm/internal.h b/ksrc/skins/rtdm/internal.h
index 69299f8..3d39def 100644
--- a/ksrc/skins/rtdm/internal.h
+++ b/ksrc/skins/rtdm/internal.h
@@ -80,15 +80,11 @@ static inline void rtdm_dereference_device(struct 
rtdm_device *device)
 }
 
 int __init rtdm_dev_init(void);
-
-static inline void rtdm_dev_cleanup(void)
-{
-       kfree(rtdm_named_devices);
-       kfree(rtdm_protocol_devices);
-}
+void __exit rtdm_dev_cleanup(void);
 
 int rtdm_proc_register_device(struct rtdm_device *device);
 int __init rtdm_proc_init(void);
 void rtdm_proc_cleanup(void);
+void rtdm_apc_handler(void *cookie);
 
 #endif /* _RTDM_INTERNAL_H */


_______________________________________________
Xenomai-git mailing list
Xenomai-git@gna.org
https://mail.gna.org/listinfo/xenomai-git

Reply via email to