]> Devoid-pointer.net GitWeb - KLGD.git/commitdiff
Use delayed workqueue instead of timer
authorMichal Malý <madcatxster@devoid-pointer.net>
Fri, 25 Jul 2014 23:45:41 +0000 (01:45 +0200)
committerMichal Malý <madcatxster@devoid-pointer.net>
Fri, 25 Jul 2014 23:45:41 +0000 (01:45 +0200)
klgd.c

diff --git a/klgd.c b/klgd.c
index e4bd32e9ccd31164db5377ab2a49c46574db4d42..4a8535b83a3fae476f3e0d8fe381a298a3801edf 100644 (file)
--- a/klgd.c
+++ b/klgd.c
@@ -1,25 +1,27 @@
 #include <asm/atomic.h>
+#include <linux/module.h>
 #include <linux/slab.h>
-#include <linux/spinlock.h>
+#include <linux/mutex.h>
 #include <linux/string.h>
+#include <linux/workqueue.h>
 #include "klgd.h"
 
 struct klgd_main_private {
-       atomic_t can_send_commands;
+       struct delayed_work work;
+       bool can_send_commands;
        void *device_context;
        unsigned long earliest_update;
        struct klgd_command_stream *last_stream;
        size_t plugin_count;
        struct klgd_plugin **plugins;
-       atomic_t send_asap;
-       spinlock_t stream_lock;
-       struct timer_list timer;
+       unsigned int send_asap;
+       struct mutex stream_mlock;
+       struct workqueue_struct *wq;
 
        int (*send_command_stream)(void *dev_ctx, struct klgd_command_stream *stream);
 };
 
 void klgd_free_stream(struct klgd_command_stream *s);
-void klgd_timer_fired(unsigned long ctx);
 void klgd_schedule_update(struct klgd_main *ctx);
 
 bool klgd_append_stream(struct klgd_command_stream *target, struct klgd_command_stream *source)
@@ -30,7 +32,7 @@ bool klgd_append_stream(struct klgd_command_stream *target, struct klgd_command_
        if (!source->count)
                return true;
 
-       temp = krealloc(target->commands, sizeof(struct klgd_command *) * (target->count + source->count), GFP_ATOMIC);
+       temp = krealloc(target->commands, sizeof(struct klgd_command *) * (target->count + source->count), GFP_KERNEL);
        if (!temp)
                return false;
 
@@ -42,13 +44,15 @@ bool klgd_append_stream(struct klgd_command_stream *target, struct klgd_command_
        return true;
 }
 
-void klgd_build_command_stream(struct klgd_main *ctx)
+/**
+ * Called with stream_mlock held
+ */
+void klgd_build_command_stream(struct klgd_main_private *priv)
 {
-       struct klgd_main_private *priv = ctx->private;
        const unsigned long now = jiffies;
        size_t idx;
 
-       struct klgd_command_stream *s = kzalloc(sizeof(struct klgd_command_stream), GFP_ATOMIC);
+       struct klgd_command_stream *s = kzalloc(sizeof(struct klgd_command_stream), GFP_KERNEL);
        if (!s)
                return; /* FIXME: Try to do an update later when some memory might be available */
 
@@ -63,12 +67,31 @@ void klgd_build_command_stream(struct klgd_main *ctx)
        }
 
        if (s->count) {
-               atomic_set(&priv->can_send_commands, 0);
+               priv->can_send_commands = false;
                priv->last_stream = s;
                priv->send_command_stream(priv->device_context, s);
        }
 }
 
+void klgd_delayed_work(struct work_struct *w)
+{
+       struct delayed_work *dw = container_of(w, struct delayed_work, work);
+       struct klgd_main_private *priv = container_of(dw, struct klgd_main_private, work);
+       struct klgd_main *m = container_of(&priv, struct klgd_main, private);
+
+       mutex_lock(&priv->stream_mlock);
+       if (priv->can_send_commands) {
+               pr_debug("Timer fired, can send commands now\n");
+               klgd_build_command_stream(priv);
+       } else {
+               pr_debug("Timer fired, last stream of commands is still being processed\n");
+               priv->send_asap++;
+       }
+
+       klgd_schedule_update(m);
+       mutex_unlock(&priv->stream_mlock);
+}
+
 void klgd_free_stream(struct klgd_command_stream *s)
 {
        size_t idx;
@@ -87,6 +110,10 @@ void klgd_deinit(struct klgd_main *ctx)
        struct klgd_main_private *priv = ctx->private;
        size_t idx;
 
+       cancel_delayed_work(&priv->work);
+       flush_workqueue(priv->wq);
+       destroy_workqueue(priv->wq);
+
        for (idx = 0; idx < priv->plugin_count; idx++) {
                struct klgd_plugin *plugin = priv->plugins[idx];
 
@@ -115,6 +142,10 @@ int klgd_init(struct klgd_main *ctx, void *dev_ctx, int (*callback)(void *, stru
        if (!ctx->private)
                return -ENOMEM;
 
+       mutex_init(&priv->stream_mlock);
+       priv->wq = create_singlethread_workqueue("klgd_processing_loop");
+       INIT_DELAYED_WORK(&priv->work, klgd_delayed_work);
+
        priv->plugins = kzalloc(sizeof(struct klgd_plugin *) * plugin_count, GFP_KERNEL);
        if (!ctx->private->plugins) {
                ret = -ENOMEM;
@@ -122,18 +153,16 @@ int klgd_init(struct klgd_main *ctx, void *dev_ctx, int (*callback)(void *, stru
        }
        priv->plugin_count = plugin_count;
 
-       atomic_set(&priv->can_send_commands, 1);
+       priv->can_send_commands = true;
        priv->device_context = dev_ctx;
        priv->last_stream = NULL;
-       spin_lock_init(&priv->stream_lock);
        priv->send_command_stream = callback;
-       atomic_set(&priv->send_asap, 0);
-
-       setup_timer(&priv->timer, klgd_timer_fired, (unsigned long)ctx);
+       priv->send_asap = 0;
 
        return 0;
 
 err_out:
+       destroy_workqueue(priv->wq);
        kfree(ctx->private);
 
        return ret;
@@ -143,34 +172,35 @@ void klgd_notify_commands_sent(struct klgd_main *ctx)
 {
        struct klgd_main_private *priv = ctx->private;
 
+       mutex_lock(&priv->stream_mlock);
        kfree(priv->last_stream);
 
-       if (atomic_read(&priv->send_asap)) {
-               unsigned long flags;
-
+       if (priv->send_asap) {
                pr_debug("Command stream processed, send a new one immediately\n");
-
-               spin_lock_irqsave(&priv->stream_lock, flags);
-               klgd_build_command_stream(ctx);
-               spin_unlock_irqrestore(&priv->stream_lock, flags);
-               atomic_set(&priv->send_asap, 0);
+               klgd_build_command_stream(priv);
+               priv->send_asap--;
        } else {
                pr_debug("Command stream processed, wait for timer\n");
-               atomic_set(&priv->can_send_commands, 1);
+               priv->can_send_commands = true;
        }
+       mutex_unlock(&priv->stream_mlock);
 }
 
 int klgd_post_event(struct klgd_main *ctx, size_t idx, void *data)
 {
-  struct klgd_plugin *plugin = ctx->private->plugins[idx];
-  int ret;
+       struct klgd_plugin *plugin = ctx->private->plugins[idx];
+       int ret;
 
-  ret = plugin->post_event(plugin, data);
-  if (ret)
-    return ret;
+       mutex_lock(&ctx->private->stream_mlock);
+       ret = plugin->post_event(plugin, data);
+       if (ret) {
+               mutex_unlock(&ctx->private->stream_mlock);
+               return ret;
+       }
 
-  klgd_schedule_update(ctx);
-  return 0;
+       klgd_schedule_update(ctx);
+       mutex_unlock(&ctx->private->stream_mlock);
+       return 0;
 }
 
 int klgd_register_plugin(struct klgd_main *ctx, size_t idx, struct klgd_plugin *plugin)
@@ -213,31 +243,9 @@ void klgd_schedule_update(struct klgd_main *ctx)
 
        if (!events) {
                pr_debug("No events, deactivating timer\n");
-               del_timer(&priv->timer);
+               cancel_delayed_work(&priv->work);
        } else {
                pr_debug("Events: %u, earliest: %lu, now: %lu\n", events, earliest, now);
-               mod_timer(&priv->timer, earliest);
-       }
-}
-
-void klgd_timer_fired(unsigned long ctx)
-{
-       struct klgd_main *m = (struct klgd_main *)ctx;
-       struct klgd_main_private *priv = m->private;
-
-
-       if (atomic_read(&priv->can_send_commands)) {
-               unsigned long flags;
-
-               pr_debug("Timer fired, can send commands now\n");
-
-               spin_lock_irqsave(&priv->stream_lock, flags);
-               klgd_build_command_stream(m);
-               spin_unlock_irqrestore(&priv->stream_lock, flags);
-       } else {
-               pr_debug("Timer fired, last stream of commands is still being processed\n");
-               atomic_set(&priv->send_asap, 1);
+               queue_delayed_work(priv->wq, &priv->work, earliest - now);
        }
-
-       klgd_schedule_update(m);
 }
\ No newline at end of file