From f58f0e81712cbc253484f17c2af5603ee131ddc4 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Michal=20Mal=C3=BD?= Date: Sat, 26 Jul 2014 01:45:41 +0200 Subject: [PATCH] Use delayed workqueue instead of timer --- klgd.c | 120 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 64 insertions(+), 56 deletions(-) diff --git a/klgd.c b/klgd.c index e4bd32e..4a8535b 100644 --- a/klgd.c +++ b/klgd.c @@ -1,25 +1,27 @@ #include +#include #include -#include +#include #include +#include #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 -- 2.43.5