Commit 74e40716 authored by Felix Kuehling's avatar Felix Kuehling Committed by Oded Gabbay

drm/amdkfd: Use wait_queue_t to implement event waiting

Use standard wait queues for waiting and waking up waiting threads
instead of inventing our own. We still have our own wait loop
because the HSA event semantics require the ability to have one
thread waiting on multiple wait queues (events) at the same time.
Signed-off-by: default avatarKent Russell <kent.russell@amd.com>
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 ebf947fe
...@@ -33,22 +33,12 @@ ...@@ -33,22 +33,12 @@
#include <linux/device.h> #include <linux/device.h>
/* /*
* A task can only be on a single wait_queue at a time, but we need to support * Wrapper around wait_queue_entry_t
* waiting on multiple events (any/all).
* Instead of each event simply having a wait_queue with sleeping tasks, it
* has a singly-linked list of tasks.
* A thread that wants to sleep creates an array of these, one for each event
* and adds one to each event's waiter chain.
*/ */
struct kfd_event_waiter { struct kfd_event_waiter {
struct list_head waiters; wait_queue_entry_t wait;
struct task_struct *sleeping_task; struct kfd_event *event; /* Event to wait for */
bool activated; /* Becomes true when event is signaled */
/* Transitions to true when the event this belongs to is signaled. */
bool activated;
/* Event */
struct kfd_event *event;
}; };
/* /*
...@@ -344,17 +334,12 @@ void kfd_event_init_process(struct kfd_process *p) ...@@ -344,17 +334,12 @@ 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 */ struct kfd_event_waiter *waiter;
while (!list_empty(&ev->waiters)) {
struct kfd_event_waiter *waiter =
list_first_entry(&ev->waiters, struct kfd_event_waiter,
waiters);
/* Wake up pending waiters. They will return failure */
list_for_each_entry(waiter, &ev->wq.head, wait.entry)
waiter->event = NULL; waiter->event = NULL;
/* _init because free_waiters will call list_del */ wake_up_all(&ev->wq);
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,
...@@ -424,7 +409,7 @@ int kfd_event_create(struct file *devkfd, struct kfd_process *p, ...@@ -424,7 +409,7 @@ int kfd_event_create(struct file *devkfd, struct kfd_process *p,
ev->auto_reset = auto_reset; ev->auto_reset = auto_reset;
ev->signaled = false; ev->signaled = false;
INIT_LIST_HEAD(&ev->waiters); init_waitqueue_head(&ev->wq);
*event_page_offset = 0; *event_page_offset = 0;
...@@ -482,19 +467,18 @@ int kfd_event_destroy(struct kfd_process *p, uint32_t event_id) ...@@ -482,19 +467,18 @@ int kfd_event_destroy(struct kfd_process *p, uint32_t event_id)
static void set_event(struct kfd_event *ev) static void set_event(struct kfd_event *ev)
{ {
struct kfd_event_waiter *waiter; struct kfd_event_waiter *waiter;
struct kfd_event_waiter *next;
/* Auto reset if the list is non-empty and we're waking someone. */ /* Auto reset if the list is non-empty and we're waking
ev->signaled = !ev->auto_reset || list_empty(&ev->waiters); * someone. waitqueue_active is safe here because we're
* protected by the p->event_mutex, which is also held when
* updating the wait queues in kfd_wait_on_events.
*/
ev->signaled = !ev->auto_reset || !waitqueue_active(&ev->wq);
list_for_each_entry_safe(waiter, next, &ev->waiters, waiters) { list_for_each_entry(waiter, &ev->wq.head, wait.entry)
waiter->activated = true; waiter->activated = true;
/* _init because free_waiters will call list_del */ wake_up_all(&ev->wq);
list_del_init(&waiter->waiters);
wake_up_process(waiter->sleeping_task);
}
} }
/* Assumes that p is current. */ /* Assumes that p is current. */
...@@ -614,8 +598,7 @@ static struct kfd_event_waiter *alloc_event_waiters(uint32_t num_events) ...@@ -614,8 +598,7 @@ static struct kfd_event_waiter *alloc_event_waiters(uint32_t num_events)
GFP_KERNEL); GFP_KERNEL);
for (i = 0; (event_waiters) && (i < num_events) ; i++) { for (i = 0; (event_waiters) && (i < num_events) ; i++) {
INIT_LIST_HEAD(&event_waiters[i].waiters); init_wait(&event_waiters[i].wait);
event_waiters[i].sleeping_task = current;
event_waiters[i].activated = false; event_waiters[i].activated = false;
} }
...@@ -646,7 +629,7 @@ static void init_event_waiter_add_to_waitlist(struct kfd_event_waiter *waiter) ...@@ -646,7 +629,7 @@ static void init_event_waiter_add_to_waitlist(struct kfd_event_waiter *waiter)
* wait on this event. * wait on this event.
*/ */
if (!waiter->activated) if (!waiter->activated)
list_add(&waiter->waiters, &ev->waiters); add_wait_queue(&ev->wq, &waiter->wait);
} }
/* test_event_condition - Test condition of events being waited for /* test_event_condition - Test condition of events being waited for
...@@ -736,7 +719,9 @@ static void free_waiters(uint32_t num_events, struct kfd_event_waiter *waiters) ...@@ -736,7 +719,9 @@ static void free_waiters(uint32_t num_events, struct kfd_event_waiter *waiters)
uint32_t i; uint32_t i;
for (i = 0; i < num_events; i++) for (i = 0; i < num_events; i++)
list_del(&waiters[i].waiters); if (waiters[i].event)
remove_wait_queue(&waiters[i].event->wq,
&waiters[i].wait);
kfree(waiters); kfree(waiters);
} }
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <linux/hashtable.h> #include <linux/hashtable.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/wait.h>
#include "kfd_priv.h" #include "kfd_priv.h"
#include <uapi/linux/kfd_ioctl.h> #include <uapi/linux/kfd_ioctl.h>
...@@ -56,7 +57,7 @@ struct kfd_event { ...@@ -56,7 +57,7 @@ struct kfd_event {
int type; int type;
struct list_head waiters; /* List of kfd_event_waiter by waiters. */ wait_queue_head_t wq; /* List of event waiters. */
/* Only for signal events. */ /* Only for signal events. */
struct signal_page *signal_page; struct signal_page *signal_page;
......
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