Commit 2e353685 authored by Alex Elder's avatar Alex Elder Committed by Greg Kroah-Hartman

greybus: embed workqueue structure in struct gbuf

A Greybus buffer containing outbound data is submitted to to the
underlying driver to be sent over a CPort.  Sending that data could
be deferred, so the submit operation completes asynchronously.  When
the send is done, a callback occurs, and the buffer is "completed",
and the buffer's completion routine is called.  The buffer is then
freed.

If data arrives on the CPort, greybus_cport_in_data() is called
to allocate a Greybus buffer and copy the received data into it.
Once that's done the buffer is completed, again allowing the
buffer's completion routine to finish any final tasks before
freeing the buffer.

We use a workqueue to schedule calling the buffer's completion
function.  This patch does two things related to the work queue:
    - Renames the work queue "gbuf_workqueue" so its name more
      directly describes its purpose
    - Moves the work_struct needed for scheduling completions
      into the struct greybuf.  Previously a separate type
      was used, and dynamically allocated *at interrupt time*
      to hold this work_struct.  We can now do away with that.
Signed-off-by: default avatarAlex Elder <elder@linaro.org>
Signed-off-by: default avatarGreg Kroah-Hartman <greg@kroah.com>
parent 217b870e
...@@ -19,9 +19,13 @@ ...@@ -19,9 +19,13 @@
#include "greybus.h" #include "greybus.h"
static void cport_process_event(struct work_struct *work);
static struct kmem_cache *gbuf_head_cache; static struct kmem_cache *gbuf_head_cache;
/* Workqueue to handle Greybus buffer completions. */
static struct workqueue_struct *gbuf_workqueue;
static struct gbuf *__alloc_gbuf(struct greybus_module *gmod, static struct gbuf *__alloc_gbuf(struct greybus_module *gmod,
struct gmod_cport *cport, struct gmod_cport *cport,
gbuf_complete_t complete, gbuf_complete_t complete,
...@@ -37,6 +41,7 @@ static struct gbuf *__alloc_gbuf(struct greybus_module *gmod, ...@@ -37,6 +41,7 @@ static struct gbuf *__alloc_gbuf(struct greybus_module *gmod,
kref_init(&gbuf->kref); kref_init(&gbuf->kref);
gbuf->gmod = gmod; gbuf->gmod = gmod;
gbuf->cport = cport; gbuf->cport = cport;
INIT_WORK(&gbuf->event, cport_process_event);
gbuf->complete = complete; gbuf->complete = complete;
gbuf->context = context; gbuf->context = context;
...@@ -129,41 +134,13 @@ int greybus_kill_gbuf(struct gbuf *gbuf) ...@@ -129,41 +134,13 @@ int greybus_kill_gbuf(struct gbuf *gbuf)
return -ENOMEM; return -ENOMEM;
} }
struct cport_msg {
struct gbuf *gbuf;
struct work_struct event;
};
static struct workqueue_struct *cport_workqueue;
static void cport_process_event(struct work_struct *work) static void cport_process_event(struct work_struct *work)
{ {
struct cport_msg *cm; struct gbuf *gbuf = container_of(work, struct gbuf, event);
struct gbuf *gbuf;
cm = container_of(work, struct cport_msg, event);
gbuf = cm->gbuf;
/* call the gbuf handler */ /* Call the completion handler, then drop our reference */
gbuf->complete(gbuf); gbuf->complete(gbuf);
greybus_put_gbuf(gbuf);
/* free all the memory */
greybus_free_gbuf(gbuf);
kfree(cm);
}
static void cport_create_event(struct gbuf *gbuf)
{
struct cport_msg *cm;
/* Slow alloc, does it matter??? */
cm = kmalloc(sizeof(*cm), GFP_ATOMIC);
/* Queue up the cport message to be handled in user context */
cm->gbuf = gbuf;
INIT_WORK(&cm->event, cport_process_event);
queue_work(cport_workqueue, &cm->event);
} }
#define MAX_CPORTS 1024 #define MAX_CPORTS 1024
...@@ -237,21 +214,21 @@ void greybus_cport_in_data(struct greybus_host_device *hd, int cport, u8 *data, ...@@ -237,21 +214,21 @@ void greybus_cport_in_data(struct greybus_host_device *hd, int cport, u8 *data,
gbuf->transfer_buffer_length = length; gbuf->transfer_buffer_length = length;
gbuf->actual_length = length; gbuf->actual_length = length;
cport_create_event(gbuf); queue_work(gbuf_workqueue, &gbuf->event);
} }
EXPORT_SYMBOL_GPL(greybus_cport_in_data); EXPORT_SYMBOL_GPL(greybus_cport_in_data);
/* Can be called in interrupt context, do the work and get out of here */ /* Can be called in interrupt context, do the work and get out of here */
void greybus_gbuf_finished(struct gbuf *gbuf) void greybus_gbuf_finished(struct gbuf *gbuf)
{ {
cport_create_event(gbuf); queue_work(gbuf_workqueue, &gbuf->event);
} }
EXPORT_SYMBOL_GPL(greybus_gbuf_finished); EXPORT_SYMBOL_GPL(greybus_gbuf_finished);
int gb_gbuf_init(void) int gb_gbuf_init(void)
{ {
cport_workqueue = alloc_workqueue("greybus_gbuf", 0, 1); gbuf_workqueue = alloc_workqueue("greybus_gbuf", 0, 1);
if (!cport_workqueue) if (!gbuf_workqueue)
return -ENOMEM; return -ENOMEM;
gbuf_head_cache = kmem_cache_create("gbuf_head_cache", gbuf_head_cache = kmem_cache_create("gbuf_head_cache",
...@@ -261,6 +238,6 @@ int gb_gbuf_init(void) ...@@ -261,6 +238,6 @@ int gb_gbuf_init(void)
void gb_gbuf_exit(void) void gb_gbuf_exit(void)
{ {
destroy_workqueue(cport_workqueue); destroy_workqueue(gbuf_workqueue);
kmem_cache_destroy(gbuf_head_cache); kmem_cache_destroy(gbuf_head_cache);
} }
...@@ -133,6 +133,7 @@ struct gbuf { ...@@ -133,6 +133,7 @@ struct gbuf {
unsigned int direction : 1; /* 0 is out, 1 is in */ unsigned int direction : 1; /* 0 is out, 1 is in */
void *context; void *context;
struct work_struct event;
gbuf_complete_t complete; gbuf_complete_t complete;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment