mirror of
https://github.com/drasko/codezero.git
synced 2026-04-21 03:09:04 +02:00
Fix to l4_mutex_control for the unexpected sleeping with mutex held
when preemption occurs after call to wait_on_prepare
This commit is contained in:
@@ -29,6 +29,7 @@ static inline void mutex_init(struct mutex *mutex)
|
|||||||
int mutex_trylock(struct mutex *mutex);
|
int mutex_trylock(struct mutex *mutex);
|
||||||
int mutex_lock(struct mutex *mutex);
|
int mutex_lock(struct mutex *mutex);
|
||||||
void mutex_unlock(struct mutex *mutex);
|
void mutex_unlock(struct mutex *mutex);
|
||||||
|
void mutex_unlock_async(struct mutex *mutex);
|
||||||
|
|
||||||
/* NOTE: Since spinlocks guard mutex acquiring & sleeping, no locks needed */
|
/* NOTE: Since spinlocks guard mutex acquiring & sleeping, no locks needed */
|
||||||
static inline int mutex_inc(unsigned int *cnt)
|
static inline int mutex_inc(unsigned int *cnt)
|
||||||
|
|||||||
@@ -54,7 +54,8 @@ void mutex_queue_head_lock()
|
|||||||
|
|
||||||
void mutex_queue_head_unlock()
|
void mutex_queue_head_unlock()
|
||||||
{
|
{
|
||||||
mutex_unlock(&mutex_queue_head.mutex_control_mutex);
|
/* Async unlock because in some cases preemption may be disabled here */
|
||||||
|
mutex_unlock_async(&mutex_queue_head.mutex_control_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -170,11 +171,18 @@ int mutex_control_lock(unsigned long mutex_address)
|
|||||||
|
|
||||||
/* Prepare to wait on the contenders queue */
|
/* Prepare to wait on the contenders queue */
|
||||||
CREATE_WAITQUEUE_ON_STACK(wq, current);
|
CREATE_WAITQUEUE_ON_STACK(wq, current);
|
||||||
|
|
||||||
|
/* Disable to protect from sleeping by preemption */
|
||||||
|
preempt_disable();
|
||||||
|
|
||||||
wait_on_prepare(&mutex_queue->wqh_contenders, &wq);
|
wait_on_prepare(&mutex_queue->wqh_contenders, &wq);
|
||||||
|
|
||||||
/* Release lock */
|
/* Release lock */
|
||||||
mutex_queue_head_unlock();
|
mutex_queue_head_unlock();
|
||||||
|
|
||||||
|
/* Now safe to sleep voluntarily or by preemption */
|
||||||
|
preempt_enable();
|
||||||
|
|
||||||
/* Initiate prepared wait */
|
/* Initiate prepared wait */
|
||||||
return wait_on_prepared_wait();
|
return wait_on_prepared_wait();
|
||||||
}
|
}
|
||||||
@@ -213,11 +221,19 @@ int mutex_control_unlock(unsigned long mutex_address)
|
|||||||
|
|
||||||
/* Prepare to wait on the lock holders queue */
|
/* Prepare to wait on the lock holders queue */
|
||||||
CREATE_WAITQUEUE_ON_STACK(wq, current);
|
CREATE_WAITQUEUE_ON_STACK(wq, current);
|
||||||
|
|
||||||
|
/* Disable to protect from sleeping by preemption */
|
||||||
|
preempt_disable();
|
||||||
|
|
||||||
|
/* Prepare to wait */
|
||||||
wait_on_prepare(&mutex_queue->wqh_holders, &wq);
|
wait_on_prepare(&mutex_queue->wqh_holders, &wq);
|
||||||
|
|
||||||
/* Release lock */
|
/* Release lock first */
|
||||||
mutex_queue_head_unlock();
|
mutex_queue_head_unlock();
|
||||||
|
|
||||||
|
/* Now safe to sleep voluntarily or by preemption */
|
||||||
|
preempt_enable();
|
||||||
|
|
||||||
/* Initiate prepared wait */
|
/* Initiate prepared wait */
|
||||||
return wait_on_prepared_wait();
|
return wait_on_prepared_wait();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ int mutex_lock(struct mutex *mutex)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mutex_unlock(struct mutex *mutex)
|
static inline void mutex_unlock_common(struct mutex *mutex, int sync)
|
||||||
{
|
{
|
||||||
spin_lock(&mutex->wqh.slock);
|
spin_lock(&mutex->wqh.slock);
|
||||||
__mutex_unlock(&mutex->lock);
|
__mutex_unlock(&mutex->lock);
|
||||||
@@ -152,8 +152,8 @@ void mutex_unlock(struct mutex *mutex)
|
|||||||
BUG_ON(mutex->wqh.sleepers < 0);
|
BUG_ON(mutex->wqh.sleepers < 0);
|
||||||
if (mutex->wqh.sleepers > 0) {
|
if (mutex->wqh.sleepers > 0) {
|
||||||
struct waitqueue *wq = link_to_struct(mutex->wqh.task_list.next,
|
struct waitqueue *wq = link_to_struct(mutex->wqh.task_list.next,
|
||||||
struct waitqueue,
|
struct waitqueue,
|
||||||
task_list);
|
task_list);
|
||||||
struct ktcb *sleeper = wq->task;
|
struct ktcb *sleeper = wq->task;
|
||||||
|
|
||||||
task_unset_wqh(sleeper);
|
task_unset_wqh(sleeper);
|
||||||
@@ -168,7 +168,10 @@ void mutex_unlock(struct mutex *mutex)
|
|||||||
* but it may potentially starve the sleeper causing
|
* but it may potentially starve the sleeper causing
|
||||||
* non-determinism. We may consider priorities here.
|
* non-determinism. We may consider priorities here.
|
||||||
*/
|
*/
|
||||||
sched_resume_sync(sleeper);
|
if (sync)
|
||||||
|
sched_resume_sync(sleeper);
|
||||||
|
else
|
||||||
|
sched_resume_async(sleeper);
|
||||||
|
|
||||||
/* Don't iterate, wake only one task. */
|
/* Don't iterate, wake only one task. */
|
||||||
return;
|
return;
|
||||||
@@ -176,3 +179,13 @@ void mutex_unlock(struct mutex *mutex)
|
|||||||
spin_unlock(&mutex->wqh.slock);
|
spin_unlock(&mutex->wqh.slock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void mutex_unlock(struct mutex *mutex)
|
||||||
|
{
|
||||||
|
mutex_unlock_common(mutex, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mutex_unlock_async(struct mutex *mutex)
|
||||||
|
{
|
||||||
|
mutex_unlock_common(mutex, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user