Commit fe528c13 authored by Felix Kuehling's avatar Felix Kuehling Committed by Oded Gabbay

drm/amdkfd: Fix event destruction with pending waiters

When an event with pending waiters is destroyed, those waiters may
end up sleeping forever unless they are notified and woken up.
Implement the notification by clearing the waiter->event pointer,
which becomes invalid anyway, when the event is freed, and waking
up the waiting tasks.

Waiters on an event that's destroyed return failure.
Signed-off-by: default avatarFelix Kuehling <Felix.Kuehling@amd.com>
Acked-by: default avatarOded Gabbay <oded.gabbay@gmail.com>
Signed-off-by: default avatarOded Gabbay <oded.gabbay@gmail.com>
parent fdf0c833
...@@ -345,18 +345,24 @@ void kfd_event_init_process(struct kfd_process *p) ...@@ -345,18 +345,24 @@ void kfd_event_init_process(struct kfd_process *p)
static void destroy_event(struct kfd_process *p, struct kfd_event *ev) static void destroy_event(struct kfd_process *p, struct kfd_event *ev)
{ {
/* Wake up pending waiters. They will return failure */
while (!list_empty(&ev->waiters)) {
struct kfd_event_waiter *waiter =
list_first_entry(&ev->waiters, struct kfd_event_waiter,
waiters);
waiter->event = NULL;
/* _init because free_waiters will call list_del */
list_del_init(&waiter->waiters);
wake_up_process(waiter->sleeping_task);
}
if (ev->signal_page) { if (ev->signal_page) {
release_event_notification_slot(ev->signal_page, release_event_notification_slot(ev->signal_page,
ev->signal_slot_index); ev->signal_slot_index);
p->signal_event_count--; p->signal_event_count--;
} }
/*
* Abandon the list of waiters. Individual waiting threads will
* clean up their own data.
*/
list_del(&ev->waiters);
hash_del(&ev->events); hash_del(&ev->events);
kfree(ev); kfree(ev);
} }
...@@ -646,22 +652,36 @@ static void init_event_waiter_add_to_waitlist(struct kfd_event_waiter *waiter) ...@@ -646,22 +652,36 @@ static void init_event_waiter_add_to_waitlist(struct kfd_event_waiter *waiter)
list_add(&waiter->waiters, &ev->waiters); list_add(&waiter->waiters, &ev->waiters);
} }
static bool test_event_condition(bool all, uint32_t num_events, /* test_event_condition - Test condition of events being waited for
* @all: Return completion only if all events have signaled
* @num_events: Number of events to wait for
* @event_waiters: Array of event waiters, one per event
*
* Returns KFD_IOC_WAIT_RESULT_COMPLETE if all (or one) event(s) have
* signaled. Returns KFD_IOC_WAIT_RESULT_TIMEOUT if no (or not all)
* events have signaled. Returns KFD_IOC_WAIT_RESULT_FAIL if any of
* the events have been destroyed.
*/
static uint32_t test_event_condition(bool all, uint32_t num_events,
struct kfd_event_waiter *event_waiters) struct kfd_event_waiter *event_waiters)
{ {
uint32_t i; uint32_t i;
uint32_t activated_count = 0; uint32_t activated_count = 0;
for (i = 0; i < num_events; i++) { for (i = 0; i < num_events; i++) {
if (!event_waiters[i].event)
return KFD_IOC_WAIT_RESULT_FAIL;
if (event_waiters[i].activated) { if (event_waiters[i].activated) {
if (!all) if (!all)
return true; return KFD_IOC_WAIT_RESULT_COMPLETE;
activated_count++; activated_count++;
} }
} }
return activated_count == num_events; return activated_count == num_events ?
KFD_IOC_WAIT_RESULT_COMPLETE : KFD_IOC_WAIT_RESULT_TIMEOUT;
} }
/* /*
...@@ -745,11 +765,6 @@ int kfd_wait_on_events(struct kfd_process *p, ...@@ -745,11 +765,6 @@ int kfd_wait_on_events(struct kfd_process *p,
mutex_lock(&p->event_mutex); mutex_lock(&p->event_mutex);
/* Set to something unreasonable - this is really
* just a bool for now.
*/
*wait_result = KFD_IOC_WAIT_RESULT_TIMEOUT;
for (i = 0; i < num_events; i++) { for (i = 0; i < num_events; i++) {
struct kfd_event_data event_data; struct kfd_event_data event_data;
...@@ -766,17 +781,22 @@ int kfd_wait_on_events(struct kfd_process *p, ...@@ -766,17 +781,22 @@ int kfd_wait_on_events(struct kfd_process *p,
} }
/* Check condition once. */ /* Check condition once. */
if (test_event_condition(all, num_events, event_waiters)) { *wait_result = test_event_condition(all, num_events, event_waiters);
*wait_result = KFD_IOC_WAIT_RESULT_COMPLETE; if (*wait_result == KFD_IOC_WAIT_RESULT_COMPLETE) {
ret = copy_signaled_event_data(num_events, ret = copy_signaled_event_data(num_events,
event_waiters, events); event_waiters, events);
goto out_unlock; goto out_unlock;
} else { } else if (WARN_ON(*wait_result == KFD_IOC_WAIT_RESULT_FAIL)) {
/* Add to wait lists if we need to wait. */ /* This should not happen. Events shouldn't be
for (i = 0; i < num_events; i++) * destroyed while we're holding the event_mutex
init_event_waiter_add_to_waitlist(&event_waiters[i]); */
goto out_unlock;
} }
/* Add to wait lists if we need to wait. */
for (i = 0; i < num_events; i++)
init_event_waiter_add_to_waitlist(&event_waiters[i]);
mutex_unlock(&p->event_mutex); mutex_unlock(&p->event_mutex);
while (true) { while (true) {
...@@ -809,15 +829,13 @@ int kfd_wait_on_events(struct kfd_process *p, ...@@ -809,15 +829,13 @@ int kfd_wait_on_events(struct kfd_process *p,
*/ */
set_current_state(TASK_INTERRUPTIBLE); set_current_state(TASK_INTERRUPTIBLE);
if (test_event_condition(all, num_events, event_waiters)) { *wait_result = test_event_condition(all, num_events,
*wait_result = KFD_IOC_WAIT_RESULT_COMPLETE; event_waiters);
if (*wait_result != KFD_IOC_WAIT_RESULT_TIMEOUT)
break; break;
}
if (timeout <= 0) { if (timeout <= 0)
*wait_result = KFD_IOC_WAIT_RESULT_TIMEOUT;
break; break;
}
timeout = schedule_timeout(timeout); timeout = schedule_timeout(timeout);
} }
...@@ -837,6 +855,8 @@ int kfd_wait_on_events(struct kfd_process *p, ...@@ -837,6 +855,8 @@ int kfd_wait_on_events(struct kfd_process *p,
out: out:
if (ret) if (ret)
*wait_result = KFD_IOC_WAIT_RESULT_FAIL; *wait_result = KFD_IOC_WAIT_RESULT_FAIL;
else if (*wait_result == KFD_IOC_WAIT_RESULT_FAIL)
ret = -EIO;
return ret; return ret;
} }
......
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