#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)
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;
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 */
}
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;
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];
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;
}
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;
{
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)
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