staging: vchiq: rework remove_event handling
I had started the removal of semaphores in this driver without knowing that Nicolas Saenz Julienne also worked on this. In case of the "remote event" infrastructure, my solution seemed significantly better, so I'm proposing this as a change on top. The problem with using either semaphores or completions here is that it's an overly complex way of waking up a thread, and it looks like the 'count' of the semaphore can easily get out of sync, even though I found it hard to come up with a specific example. Changing it to a 'wait_queue_head_t' instead of a completion simplifies this by letting us wait directly on the 'event->fired' variable that is set by the videocore. Another simplification is passing the wait queue directly into the helper functions instead of going through the fragile logic of recording the offset inside of a structure as part of a shared memory variable. This also avoids one uncached memory read and should be faster. Note that I'm changing it back to 'killable' after the previous patch changed 'killable' to 'interruptible', apparently based on a misunderstanding of the subtle down_interruptible() macro override in vchiq_killable.h. Fixes: f27e47bc6b8b ("staging: vchiq: use completions instead of semaphores") Signed-off-by: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
8bdf15fa67
commit
852b2876a8
@ -418,26 +418,23 @@ vchiq_set_conn_state(VCHIQ_STATE_T *state, VCHIQ_CONNSTATE_T newstate)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
remote_event_create(REMOTE_EVENT_T *event)
|
remote_event_create(wait_queue_head_t *wq, REMOTE_EVENT_T *event)
|
||||||
{
|
{
|
||||||
event->armed = 0;
|
event->armed = 0;
|
||||||
/* Don't clear the 'fired' flag because it may already have been set
|
/* Don't clear the 'fired' flag because it may already have been set
|
||||||
** by the other side. */
|
** by the other side. */
|
||||||
|
init_waitqueue_head(wq);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
remote_event_wait(VCHIQ_STATE_T *state, REMOTE_EVENT_T *event)
|
remote_event_wait(wait_queue_head_t *wq, REMOTE_EVENT_T *event)
|
||||||
{
|
{
|
||||||
if (!event->fired) {
|
if (!event->fired) {
|
||||||
event->armed = 1;
|
event->armed = 1;
|
||||||
dsb(sy);
|
dsb(sy);
|
||||||
if (!event->fired) {
|
if (wait_event_killable(*wq, event->fired)) {
|
||||||
if (wait_for_completion_interruptible(
|
event->armed = 0;
|
||||||
(struct completion *)
|
return 0;
|
||||||
((char *)state + event->event))) {
|
|
||||||
event->armed = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
event->armed = 0;
|
event->armed = 0;
|
||||||
wmb();
|
wmb();
|
||||||
@ -448,26 +445,26 @@ remote_event_wait(VCHIQ_STATE_T *state, REMOTE_EVENT_T *event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
remote_event_signal_local(VCHIQ_STATE_T *state, REMOTE_EVENT_T *event)
|
remote_event_signal_local(wait_queue_head_t *wq, REMOTE_EVENT_T *event)
|
||||||
{
|
{
|
||||||
event->armed = 0;
|
event->armed = 0;
|
||||||
complete((struct completion *)((char *)state + event->event));
|
wake_up_all(wq);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
remote_event_poll(VCHIQ_STATE_T *state, REMOTE_EVENT_T *event)
|
remote_event_poll(wait_queue_head_t *wq, REMOTE_EVENT_T *event)
|
||||||
{
|
{
|
||||||
if (event->fired && event->armed)
|
if (event->fired && event->armed)
|
||||||
remote_event_signal_local(state, event);
|
remote_event_signal_local(wq, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
remote_event_pollall(VCHIQ_STATE_T *state)
|
remote_event_pollall(VCHIQ_STATE_T *state)
|
||||||
{
|
{
|
||||||
remote_event_poll(state, &state->local->sync_trigger);
|
remote_event_poll(&state->sync_trigger_event, &state->local->sync_trigger);
|
||||||
remote_event_poll(state, &state->local->sync_release);
|
remote_event_poll(&state->sync_release_event, &state->local->sync_release);
|
||||||
remote_event_poll(state, &state->local->trigger);
|
remote_event_poll(&state->trigger_event, &state->local->trigger);
|
||||||
remote_event_poll(state, &state->local->recycle);
|
remote_event_poll(&state->recycle_event, &state->local->recycle);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Round up message sizes so that any space at the end of a slot is always big
|
/* Round up message sizes so that any space at the end of a slot is always big
|
||||||
@ -551,7 +548,7 @@ request_poll(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, int poll_type)
|
|||||||
wmb();
|
wmb();
|
||||||
|
|
||||||
/* ... and ensure the slot handler runs. */
|
/* ... and ensure the slot handler runs. */
|
||||||
remote_event_signal_local(state, &state->local->trigger);
|
remote_event_signal_local(&state->trigger_event, &state->local->trigger);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called from queue_message, by the slot handler and application threads,
|
/* Called from queue_message, by the slot handler and application threads,
|
||||||
@ -1070,7 +1067,7 @@ queue_message_sync(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service,
|
|||||||
(mutex_lock_killable(&state->sync_mutex) != 0))
|
(mutex_lock_killable(&state->sync_mutex) != 0))
|
||||||
return VCHIQ_RETRY;
|
return VCHIQ_RETRY;
|
||||||
|
|
||||||
remote_event_wait(state, &local->sync_release);
|
remote_event_wait(&state->sync_release_event, &local->sync_release);
|
||||||
|
|
||||||
rmb();
|
rmb();
|
||||||
|
|
||||||
@ -1888,7 +1885,7 @@ slot_handler_func(void *v)
|
|||||||
while (1) {
|
while (1) {
|
||||||
DEBUG_COUNT(SLOT_HANDLER_COUNT);
|
DEBUG_COUNT(SLOT_HANDLER_COUNT);
|
||||||
DEBUG_TRACE(SLOT_HANDLER_LINE);
|
DEBUG_TRACE(SLOT_HANDLER_LINE);
|
||||||
remote_event_wait(state, &local->trigger);
|
remote_event_wait(&state->trigger_event, &local->trigger);
|
||||||
|
|
||||||
rmb();
|
rmb();
|
||||||
|
|
||||||
@ -1977,7 +1974,7 @@ recycle_func(void *v)
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
remote_event_wait(state, &local->recycle);
|
remote_event_wait(&state->recycle_event, &local->recycle);
|
||||||
|
|
||||||
process_free_queue(state, found, length);
|
process_free_queue(state, found, length);
|
||||||
}
|
}
|
||||||
@ -1999,7 +1996,7 @@ sync_func(void *v)
|
|||||||
int type;
|
int type;
|
||||||
unsigned int localport, remoteport;
|
unsigned int localport, remoteport;
|
||||||
|
|
||||||
remote_event_wait(state, &local->sync_trigger);
|
remote_event_wait(&state->sync_trigger_event, &local->sync_trigger);
|
||||||
|
|
||||||
rmb();
|
rmb();
|
||||||
|
|
||||||
@ -2194,11 +2191,6 @@ vchiq_init_state(VCHIQ_STATE_T *state, VCHIQ_SLOT_ZERO_T *slot_zero)
|
|||||||
|
|
||||||
init_completion(&state->connect);
|
init_completion(&state->connect);
|
||||||
mutex_init(&state->mutex);
|
mutex_init(&state->mutex);
|
||||||
init_completion(&state->trigger_event);
|
|
||||||
init_completion(&state->recycle_event);
|
|
||||||
init_completion(&state->sync_trigger_event);
|
|
||||||
init_completion(&state->sync_release_event);
|
|
||||||
|
|
||||||
mutex_init(&state->slot_mutex);
|
mutex_init(&state->slot_mutex);
|
||||||
mutex_init(&state->recycle_mutex);
|
mutex_init(&state->recycle_mutex);
|
||||||
mutex_init(&state->sync_mutex);
|
mutex_init(&state->sync_mutex);
|
||||||
@ -2230,24 +2222,17 @@ vchiq_init_state(VCHIQ_STATE_T *state, VCHIQ_SLOT_ZERO_T *slot_zero)
|
|||||||
state->data_use_count = 0;
|
state->data_use_count = 0;
|
||||||
state->data_quota = state->slot_queue_available - 1;
|
state->data_quota = state->slot_queue_available - 1;
|
||||||
|
|
||||||
local->trigger.event = offsetof(VCHIQ_STATE_T, trigger_event);
|
remote_event_create(&state->trigger_event, &local->trigger);
|
||||||
remote_event_create(&local->trigger);
|
|
||||||
local->tx_pos = 0;
|
local->tx_pos = 0;
|
||||||
|
remote_event_create(&state->recycle_event, &local->recycle);
|
||||||
local->recycle.event = offsetof(VCHIQ_STATE_T, recycle_event);
|
|
||||||
remote_event_create(&local->recycle);
|
|
||||||
local->slot_queue_recycle = state->slot_queue_available;
|
local->slot_queue_recycle = state->slot_queue_available;
|
||||||
|
remote_event_create(&state->sync_trigger_event, &local->sync_trigger);
|
||||||
local->sync_trigger.event = offsetof(VCHIQ_STATE_T, sync_trigger_event);
|
remote_event_create(&state->sync_release_event, &local->sync_release);
|
||||||
remote_event_create(&local->sync_trigger);
|
|
||||||
|
|
||||||
local->sync_release.event = offsetof(VCHIQ_STATE_T, sync_release_event);
|
|
||||||
remote_event_create(&local->sync_release);
|
|
||||||
|
|
||||||
/* At start-of-day, the slot is empty and available */
|
/* At start-of-day, the slot is empty and available */
|
||||||
((VCHIQ_HEADER_T *)SLOT_DATA_FROM_INDEX(state, local->slot_sync))->msgid
|
((VCHIQ_HEADER_T *)SLOT_DATA_FROM_INDEX(state, local->slot_sync))->msgid
|
||||||
= VCHIQ_MSGID_PADDING;
|
= VCHIQ_MSGID_PADDING;
|
||||||
remote_event_signal_local(state, &local->sync_release);
|
remote_event_signal_local(&state->sync_release_event, &local->sync_release);
|
||||||
|
|
||||||
local->debug[DEBUG_ENTRIES] = DEBUG_MAX;
|
local->debug[DEBUG_ENTRIES] = DEBUG_MAX;
|
||||||
|
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/completion.h>
|
#include <linux/completion.h>
|
||||||
#include <linux/kthread.h>
|
#include <linux/kthread.h>
|
||||||
|
#include <linux/wait.h>
|
||||||
|
|
||||||
#include "vchiq_cfg.h"
|
#include "vchiq_cfg.h"
|
||||||
|
|
||||||
@ -262,8 +263,7 @@ typedef struct vchiq_bulk_queue_struct {
|
|||||||
typedef struct remote_event_struct {
|
typedef struct remote_event_struct {
|
||||||
int armed;
|
int armed;
|
||||||
int fired;
|
int fired;
|
||||||
/* Contains offset from the beginning of the VCHIQ_STATE_T structure */
|
u32 __unused;
|
||||||
u32 event;
|
|
||||||
} REMOTE_EVENT_T;
|
} REMOTE_EVENT_T;
|
||||||
|
|
||||||
typedef struct opaque_platform_state_t *VCHIQ_PLATFORM_STATE_T;
|
typedef struct opaque_platform_state_t *VCHIQ_PLATFORM_STATE_T;
|
||||||
@ -426,16 +426,16 @@ struct vchiq_state_struct {
|
|||||||
struct task_struct *sync_thread;
|
struct task_struct *sync_thread;
|
||||||
|
|
||||||
/* Local implementation of the trigger remote event */
|
/* Local implementation of the trigger remote event */
|
||||||
struct completion trigger_event;
|
wait_queue_head_t trigger_event;
|
||||||
|
|
||||||
/* Local implementation of the recycle remote event */
|
/* Local implementation of the recycle remote event */
|
||||||
struct completion recycle_event;
|
wait_queue_head_t recycle_event;
|
||||||
|
|
||||||
/* Local implementation of the sync trigger remote event */
|
/* Local implementation of the sync trigger remote event */
|
||||||
struct completion sync_trigger_event;
|
wait_queue_head_t sync_trigger_event;
|
||||||
|
|
||||||
/* Local implementation of the sync release remote event */
|
/* Local implementation of the sync release remote event */
|
||||||
struct completion sync_release_event;
|
wait_queue_head_t sync_release_event;
|
||||||
|
|
||||||
char *tx_data;
|
char *tx_data;
|
||||||
char *rx_data;
|
char *rx_data;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user