From: Michal MalĂ˝ Date: Wed, 13 Aug 2014 08:44:40 +0000 (+0200) Subject: MAJOR CHANEGS: X-Git-Url: https://gitweb.devoid-pointer.net/?a=commitdiff_plain;h=eaa870b3b23b741fb611363a997d99c8117ab6c9;p=KLGD.git MAJOR CHANEGS: - Add helper functions for command and stream manipulation - Change KLGD x Plugin relationship. Plugins can and must lock the KLGD state when their internal data is manipulated - Try to send the command stream again if a recoverable error occurs during sending --- diff --git a/klgd.c b/klgd.c index b5e56b9..da11a7a 100644 --- a/klgd.c +++ b/klgd.c @@ -10,6 +10,8 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Michal \"MadCatX\" Maly"); MODULE_DESCRIPTION("Pluginable framework of helper functions to handle gaming devices"); +#define TRYAGAIN_DELAY 5 + struct klgd_main_private { struct delayed_work work; bool can_send_commands; @@ -19,21 +21,67 @@ struct klgd_main_private { size_t plugin_count; struct klgd_plugin **plugins; unsigned int send_asap; - struct mutex stream_mlock; + struct mutex plugins_lock; struct workqueue_struct *wq; - enum klgd_send_status (*send_command_stream)(void *dev_ctx, struct klgd_command_stream *stream); + enum klgd_send_status (*send_command_stream)(void *dev_ctx, const struct klgd_command_stream *stream); }; static void klgd_free_stream(struct klgd_command_stream *s); static void klgd_notify_commands_sent_internal(struct klgd_main_private *priv); -static void klgd_schedule_update(struct klgd_main *ctx); +static void klgd_schedule_update(struct klgd_main_private *priv); + +struct klgd_command * klgd_alloc_cmd(const size_t length) +{ + struct klgd_command *cmd = kzalloc(sizeof(struct klgd_command) * length, GFP_KERNEL); + if (!cmd) + return NULL; + cmd->length = length; + return cmd; +} +EXPORT_SYMBOL_GPL(klgd_alloc_cmd); + +struct klgd_command_stream * klgd_alloc_stream(void) +{ + return kzalloc(sizeof(struct klgd_command_stream), GFP_KERNEL); +} +EXPORT_SYMBOL_GPL(klgd_alloc_stream); + +bool klgd_append_cmd(struct klgd_command_stream *target, const struct klgd_command *cmd) +{ + const struct klgd_command **temp; + + if (!target) { + printk(KERN_NOTICE "Cannot append to NULL stream\n"); + return false; + } + if (!cmd) { + printk(KERN_NOTICE "Cannot append NULL cmd\n"); + return false; + } + + temp = krealloc(target->commands, sizeof(struct klgd_command *) * (target->count + 1), GFP_KERNEL); + if (!temp) + return false; + + target->commands = temp; + target->commands[target->count] = cmd; + target->count++; + + return true; +} +EXPORT_SYMBOL_GPL(klgd_append_cmd); + -static bool klgd_append_stream(struct klgd_command_stream *target, struct klgd_command_stream *source) +static bool klgd_append_stream(struct klgd_command_stream *target, const struct klgd_command_stream *source) { - struct klgd_command **temp; + const struct klgd_command **temp; size_t idx; + /* We got NULL command, skip it */ + if (!source) + return true; + /* We got command of zero length, skip it */ if (!source->count) return true; @@ -50,7 +98,7 @@ static bool klgd_append_stream(struct klgd_command_stream *target, struct klgd_c } /** - * Called with stream_mlock held + * Called with plugins_lock held */ static enum klgd_send_status klgd_build_command_stream(struct klgd_main_private *priv) { @@ -59,23 +107,24 @@ static enum klgd_send_status klgd_build_command_stream(struct klgd_main_private struct klgd_command_stream *s = kzalloc(sizeof(struct klgd_command_stream), GFP_KERNEL); if (!s) - return KLGD_SS_DONE; /* FIXME: Try to do an update later when some memory might be available */ + return KLGD_SS_TRYAGAIN; for (idx = 0; idx < priv->plugin_count; idx++) { struct klgd_plugin *plugin = priv->plugins[idx]; struct klgd_command_stream *ss = plugin->get_commands(plugin, now); - /* FIXME: Same as above */ if (!klgd_append_stream(s, ss)) { klgd_free_stream(s); - return KLGD_SS_DONE; + return KLGD_SS_TRYAGAIN; } } if (s->count) { priv->can_send_commands = false; priv->last_stream = s; + printk(KERN_NOTICE "KLGD: Sending command stream\n"); return priv->send_command_stream(priv->device_context, s); } + printk(KERN_NOTICE "KLGD: Command stream is empty\n"); return KLGD_SS_DONE; } @@ -83,24 +132,51 @@ static 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); + mutex_lock(&priv->plugins_lock); + printk(KERN_NOTICE "KLGD/WQ: Plugins state locked\n"); if (priv->can_send_commands) { int ret; - pr_debug("Timer fired, can send commands now\n"); + printk(KERN_NOTICE "KLGD/WQ: Timer fired, can send commands now\n"); ret = klgd_build_command_stream(priv); - if (ret == KLGD_SS_DONE) + switch (ret) { + case KLGD_SS_DONE: klgd_notify_commands_sent_internal(priv); + break; + case KLGD_SS_TRYAGAIN: + queue_delayed_work(priv->wq, &priv->work, TRYAGAIN_DELAY); + printk(KERN_NOTICE "KLGD/WQ: Plugins state unlocked\n"); + mutex_unlock(&priv->plugins_lock); + return; + case KLGD_SS_RUNNING: + printk(KERN_NOTICE "KLGD/WQ: Sending command stream - async\n"); + break; + default: + /* TODO: Error handling */ + break; + } } else { - pr_debug("Timer fired, last stream of commands is still being processed\n"); + printk(KERN_NOTICE "KLGD/WQ: Timer fired, last stream of commands is still being processed\n"); priv->send_asap++; } + klgd_schedule_update(priv); + printk(KERN_NOTICE "KLGD/WQ: Plugins state unlocked\n"); + mutex_unlock(&priv->plugins_lock); +} + +static void klgd_free_stream(struct klgd_command_stream *s) +{ + size_t idx; + + if (!s) + return; - klgd_schedule_update(m); - mutex_unlock(&priv->stream_mlock); + for (idx = 0; idx < s->count; idx++) { + kfree(s->commands[idx]->bytes); + kfree(s->commands[idx]); + } } void klgd_deinit(struct klgd_main *ctx) @@ -111,7 +187,7 @@ void klgd_deinit(struct klgd_main *ctx) cancel_delayed_work(&priv->work); flush_workqueue(priv->wq); destroy_workqueue(priv->wq); - pr_debug("Workqueue terminated\n"); + printk(KERN_NOTICE "KLGD deinit, workqueue terminated\n"); for (idx = 0; idx < priv->plugin_count; idx++) { struct klgd_plugin *plugin = priv->plugins[idx]; @@ -121,26 +197,15 @@ void klgd_deinit(struct klgd_main *ctx) if (plugin->deinit) plugin->deinit(plugin); + kfree(plugin); } kfree(priv->plugins); kfree(priv); } +EXPORT_SYMBOL_GPL(klgd_deinit); -static void klgd_free_stream(struct klgd_command_stream *s) -{ - size_t idx; - - if (!s) - return; - - for (idx = 0; idx < s->count; idx++) { - kfree(s->commands[idx]->bytes); - kfree(s->commands[idx]); - } -} - -int klgd_init(struct klgd_main *ctx, void *dev_ctx, enum klgd_send_status (*callback)(void *, struct klgd_command_stream *), const size_t plugin_count) +int klgd_init(struct klgd_main *ctx, void *dev_ctx, enum klgd_send_status (*callback)(void *, const struct klgd_command_stream *), const size_t plugin_count) { struct klgd_main_private *priv = ctx->private; int ret; @@ -154,17 +219,17 @@ int klgd_init(struct klgd_main *ctx, void *dev_ctx, enum klgd_send_status (*call priv = kzalloc(sizeof(struct klgd_main_private), GFP_KERNEL); if (!priv) { - printk(KERN_ERR "KLGD: No memory for KLGD data\n"); + printk(KERN_ERR "No memory for KLGD data\n"); return -ENOMEM; } - mutex_init(&priv->stream_mlock); + mutex_init(&priv->plugins_lock); 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 (!priv->plugins) { - printk(KERN_ERR "KLGD: No memory for KLGD plugins\n"); + printk(KERN_ERR "No memory for KLGD plugins\n"); ret = -ENOMEM; goto err_out; } @@ -185,15 +250,24 @@ err_out: return ret; } +EXPORT_SYMBOL_GPL(klgd_init); + +void klgd_lock_plugins(struct mutex *lock) +{ + mutex_lock(lock); + printk(KERN_DEBUG "KLGD: Plugins state locked\n"); +} +EXPORT_SYMBOL_GPL(klgd_lock_plugins); void klgd_notify_commands_sent(struct klgd_main *ctx) { struct klgd_main_private *priv = ctx->private; - mutex_lock(&priv->stream_mlock); + mutex_lock(&priv->plugins_lock); klgd_notify_commands_sent_internal(priv); - mutex_unlock(&priv->stream_mlock); + mutex_unlock(&priv->plugins_lock); } +EXPORT_SYMBOL_GPL(klgd_notify_commands_sent); /** * Called with stream_lock held @@ -212,26 +286,6 @@ static void klgd_notify_commands_sent_internal(struct klgd_main_private *priv) } } -int klgd_post_event(struct klgd_main *ctx, size_t idx, void *data) -{ - struct klgd_plugin *plugin = ctx->private->plugins[idx]; - int ret; - - if (!plugin || idx >= ctx->private->plugin_count) - return -EINVAL; - - 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); - mutex_unlock(&ctx->private->stream_mlock); - return 0; -} - int klgd_register_plugin(struct klgd_main *ctx, size_t idx, struct klgd_plugin *plugin) { struct klgd_main_private *priv = ctx->private; @@ -239,20 +293,27 @@ int klgd_register_plugin(struct klgd_main *ctx, size_t idx, struct klgd_plugin * if (priv->plugins[idx]) return -EINVAL; - priv->plugins[idx] = kzalloc(sizeof(struct klgd_plugin), GFP_KERNEL); - if (!priv->plugins[idx]) - return -ENOMEM; - + plugin->plugins_lock = &priv->plugins_lock; priv->plugins[idx] = plugin; if (plugin->init) - plugin->init(plugin); + return plugin->init(plugin); return 0; } +EXPORT_SYMBOL_GPL(klgd_register_plugin); -static void klgd_schedule_update(struct klgd_main *ctx) +void klgd_unlock_plugins_sched(struct mutex *lock) +{ + struct klgd_main_private *priv = container_of(lock, struct klgd_main_private, plugins_lock); + + klgd_schedule_update(priv); + mutex_unlock(lock); + printk(KERN_DEBUG "KLGD: Plugins state unlocked\n"); +} +EXPORT_SYMBOL_GPL(klgd_unlock_plugins_sched); + +static void klgd_schedule_update(struct klgd_main_private *priv) { - struct klgd_main_private *priv = ctx->private; const unsigned long now = jiffies; unsigned int events = 0; unsigned long earliest; @@ -265,25 +326,17 @@ static void klgd_schedule_update(struct klgd_main *ctx) if (plugin->get_update_time(plugin, now, &t)) { if (!events) earliest = t; - else { - if (time_before(t, earliest)) - earliest = t; - } + else if (time_before(t, earliest)) + earliest = t; events++; } } if (!events) { - pr_debug("No events, deactivating timer\n"); + printk(KERN_NOTICE "No events, deactivating timer\n"); cancel_delayed_work(&priv->work); } else { - pr_debug("Events: %u, earliest: %lu, now: %lu\n", events, earliest, now); + printk(KERN_NOTICE "Events: %u, earliest: %lu, now: %lu\n", events, earliest, now); queue_delayed_work(priv->wq, &priv->work, earliest - now); } } - -EXPORT_SYMBOL_GPL(klgd_deinit); -EXPORT_SYMBOL_GPL(klgd_init); -EXPORT_SYMBOL_GPL(klgd_notify_commands_sent); -EXPORT_SYMBOL_GPL(klgd_post_event); -EXPORT_SYMBOL_GPL(klgd_register_plugin); diff --git a/klgd.h b/klgd.h index b829aff..0bcd8d8 100644 --- a/klgd.h +++ b/klgd.h @@ -1,16 +1,17 @@ enum klgd_send_status { KLGD_SS_RUNNING, KLGD_SS_DONE, - KLGD_SS_FAILED + KLGD_SS_FAILED, + KLGD_SS_TRYAGAIN }; struct klgd_command { - const char *bytes; + char *bytes; size_t length; }; struct klgd_command_stream { - struct klgd_command **commands; + const struct klgd_command **commands; size_t count; }; @@ -20,18 +21,20 @@ struct klgd_main { struct klgd_plugin { struct klgd_plugin_private *private; + struct mutex *plugins_lock; void (*deinit)(struct klgd_plugin *ctx); struct klgd_command_stream *(*get_commands)(struct klgd_plugin *ctx, const unsigned long now); bool (*get_update_time)(struct klgd_plugin *ctx, const unsigned long now, unsigned long *t); int (*init)(struct klgd_plugin *ctx); - bool (*needs_attention)(struct klgd_plugin *ctx); - int (*post_event)(struct klgd_plugin *ctx, void *data); }; +struct klgd_command * klgd_alloc_cmd(const size_t length); +struct klgd_command_stream * klgd_alloc_stream(void); +bool klgd_append_cmd(struct klgd_command_stream *target, const struct klgd_command *cmd); void klgd_deinit(struct klgd_main *ctx); -int klgd_init(struct klgd_main *ctx, void *dev_ctx, enum klgd_send_status (*callback)(void *, struct klgd_command_stream *), const unsigned long plugin_count); +int klgd_init(struct klgd_main *ctx, void *dev_ctx, enum klgd_send_status (*callback)(void *, const struct klgd_command_stream *), const unsigned long plugin_count); +void klgd_lock_plugins(struct mutex *lock); void klgd_notify_commands_sent(struct klgd_main *ctx); -int klgd_post_event(struct klgd_main *ctx, const size_t idx, void *data); int klgd_register_plugin(struct klgd_main *ctx, const size_t idx, struct klgd_plugin *plugin); - +void klgd_unlock_plugins_sched(struct mutex *lock);