All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/1] drm/amdkfd: Improve concurrency of event handling
@ 2022-03-02 20:06 Felix Kuehling
  2022-03-02 22:53 ` Keely, Sean
  2022-03-03  7:25 ` Christian König
  0 siblings, 2 replies; 4+ messages in thread
From: Felix Kuehling @ 2022-03-02 20:06 UTC (permalink / raw)
  To: amd-gfx; +Cc: sean.keely

Use rcu_read_lock to read p->event_idr concurrently with other readers
and writers. Use p->event_mutex only for creating and destroying events
and in kfd_wait_on_events.

Protect the contents of the kfd_event structure with a per-event
spinlock that can be taken inside the rcu_read_lock critical section.

This eliminates contention of p->event_mutex in set_event, which tends
to be on the critical path for dispatch latency even when busy waiting
is used. It also eliminates lock contention in event interrupt handlers.
Since the p->event_mutex is now used much less, the impact of requiring
it in kfd_wait_on_events should also be much smaller.

This should improve event handling latency for processes using multiple
GPUs concurrently.

Signed-off-by: Felix Kuehling <Felix.Kuehling@amd.com>
---
 drivers/gpu/drm/amd/amdkfd/kfd_events.c | 119 +++++++++++++++---------
 drivers/gpu/drm/amd/amdkfd/kfd_events.h |   1 +
 2 files changed, 78 insertions(+), 42 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_events.c
index deecccebe5b6..1726306715b2 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_events.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.c
@@ -128,8 +128,8 @@ static int allocate_event_notification_slot(struct kfd_process *p,
 }
 
 /*
- * Assumes that p->event_mutex is held and of course that p is not going
- * away (current or locked).
+ * Assumes that p->event_mutex or rcu_readlock is held and of course that p is
+ * not going away.
  */
 static struct kfd_event *lookup_event_by_id(struct kfd_process *p, uint32_t id)
 {
@@ -251,15 +251,18 @@ static void destroy_event(struct kfd_process *p, struct kfd_event *ev)
 	struct kfd_event_waiter *waiter;
 
 	/* Wake up pending waiters. They will return failure */
+	spin_lock(&ev->lock);
 	list_for_each_entry(waiter, &ev->wq.head, wait.entry)
-		waiter->event = NULL;
+		WRITE_ONCE(waiter->event, NULL);
 	wake_up_all(&ev->wq);
+	spin_unlock(&ev->lock);
 
 	if (ev->type == KFD_EVENT_TYPE_SIGNAL ||
 	    ev->type == KFD_EVENT_TYPE_DEBUG)
 		p->signal_event_count--;
 
 	idr_remove(&p->event_idr, ev->event_id);
+	synchronize_rcu();
 	kfree(ev);
 }
 
@@ -392,6 +395,7 @@ int kfd_event_create(struct file *devkfd, struct kfd_process *p,
 	ev->auto_reset = auto_reset;
 	ev->signaled = false;
 
+	spin_lock_init(&ev->lock);
 	init_waitqueue_head(&ev->wq);
 
 	*event_page_offset = 0;
@@ -466,6 +470,7 @@ int kfd_criu_restore_event(struct file *devkfd,
 	ev->auto_reset = ev_priv->auto_reset;
 	ev->signaled = ev_priv->signaled;
 
+	spin_lock_init(&ev->lock);
 	init_waitqueue_head(&ev->wq);
 
 	mutex_lock(&p->event_mutex);
@@ -609,13 +614,13 @@ static void set_event(struct kfd_event *ev)
 
 	/* Auto reset if the list is non-empty and we're waking
 	 * someone. waitqueue_active is safe here because we're
-	 * protected by the p->event_mutex, which is also held when
+	 * protected by the ev->lock, 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(waiter, &ev->wq.head, wait.entry)
-		waiter->activated = true;
+		WRITE_ONCE(waiter->activated, true);
 
 	wake_up_all(&ev->wq);
 }
@@ -626,16 +631,18 @@ int kfd_set_event(struct kfd_process *p, uint32_t event_id)
 	int ret = 0;
 	struct kfd_event *ev;
 
-	mutex_lock(&p->event_mutex);
+	rcu_read_lock();
 
 	ev = lookup_event_by_id(p, event_id);
+	spin_lock(&ev->lock);
 
 	if (ev && event_can_be_cpu_signaled(ev))
 		set_event(ev);
 	else
 		ret = -EINVAL;
 
-	mutex_unlock(&p->event_mutex);
+	spin_unlock(&ev->lock);
+	rcu_read_unlock();
 	return ret;
 }
 
@@ -650,23 +657,25 @@ int kfd_reset_event(struct kfd_process *p, uint32_t event_id)
 	int ret = 0;
 	struct kfd_event *ev;
 
-	mutex_lock(&p->event_mutex);
+	rcu_read_lock();
 
 	ev = lookup_event_by_id(p, event_id);
+	spin_lock(&ev->lock);
 
 	if (ev && event_can_be_cpu_signaled(ev))
 		reset_event(ev);
 	else
 		ret = -EINVAL;
 
-	mutex_unlock(&p->event_mutex);
+	spin_unlock(&ev->lock);
+	rcu_read_unlock();
 	return ret;
 
 }
 
 static void acknowledge_signal(struct kfd_process *p, struct kfd_event *ev)
 {
-	page_slots(p->signal_page)[ev->event_id] = UNSIGNALED_EVENT_SLOT;
+	WRITE_ONCE(page_slots(p->signal_page)[ev->event_id], UNSIGNALED_EVENT_SLOT);
 }
 
 static void set_event_from_interrupt(struct kfd_process *p,
@@ -674,7 +683,9 @@ static void set_event_from_interrupt(struct kfd_process *p,
 {
 	if (ev && event_can_be_gpu_signaled(ev)) {
 		acknowledge_signal(p, ev);
+		spin_lock(&ev->lock);
 		set_event(ev);
+		spin_unlock(&ev->lock);
 	}
 }
 
@@ -693,7 +704,7 @@ void kfd_signal_event_interrupt(u32 pasid, uint32_t partial_id,
 	if (!p)
 		return; /* Presumably process exited. */
 
-	mutex_lock(&p->event_mutex);
+	rcu_read_lock();
 
 	if (valid_id_bits)
 		ev = lookup_signaled_event_by_partial_id(p, partial_id,
@@ -721,7 +732,7 @@ void kfd_signal_event_interrupt(u32 pasid, uint32_t partial_id,
 				if (id >= KFD_SIGNAL_EVENT_LIMIT)
 					break;
 
-				if (slots[id] != UNSIGNALED_EVENT_SLOT)
+				if (READ_ONCE(slots[id]) != UNSIGNALED_EVENT_SLOT)
 					set_event_from_interrupt(p, ev);
 			}
 		} else {
@@ -730,14 +741,14 @@ void kfd_signal_event_interrupt(u32 pasid, uint32_t partial_id,
 			 * only signaled events from the IDR.
 			 */
 			for (id = 0; id < KFD_SIGNAL_EVENT_LIMIT; id++)
-				if (slots[id] != UNSIGNALED_EVENT_SLOT) {
+				if (READ_ONCE(slots[id]) != UNSIGNALED_EVENT_SLOT) {
 					ev = lookup_event_by_id(p, id);
 					set_event_from_interrupt(p, ev);
 				}
 		}
 	}
 
-	mutex_unlock(&p->event_mutex);
+	rcu_read_unlock();
 	kfd_unref_process(p);
 }
 
@@ -767,9 +778,11 @@ static int init_event_waiter_get_status(struct kfd_process *p,
 	if (!ev)
 		return -EINVAL;
 
+	spin_lock(&ev->lock);
 	waiter->event = ev;
 	waiter->activated = ev->signaled;
 	ev->signaled = ev->signaled && !ev->auto_reset;
+	spin_unlock(&ev->lock);
 
 	return 0;
 }
@@ -781,8 +794,11 @@ static void init_event_waiter_add_to_waitlist(struct kfd_event_waiter *waiter)
 	/* Only add to the wait list if we actually need to
 	 * wait on this event.
 	 */
-	if (!waiter->activated)
+	if (!waiter->activated) {
+		spin_lock(&ev->lock);
 		add_wait_queue(&ev->wq, &waiter->wait);
+		spin_unlock(&ev->lock);
+	}
 }
 
 /* test_event_condition - Test condition of events being waited for
@@ -802,10 +818,10 @@ static uint32_t test_event_condition(bool all, uint32_t num_events,
 	uint32_t activated_count = 0;
 
 	for (i = 0; i < num_events; i++) {
-		if (!event_waiters[i].event)
+		if (!READ_ONCE(event_waiters[i].event))
 			return KFD_IOC_WAIT_RESULT_FAIL;
 
-		if (event_waiters[i].activated) {
+		if (READ_ONCE(event_waiters[i].activated)) {
 			if (!all)
 				return KFD_IOC_WAIT_RESULT_COMPLETE;
 
@@ -834,6 +850,8 @@ static int copy_signaled_event_data(uint32_t num_events,
 	for (i = 0; i < num_events; i++) {
 		waiter = &event_waiters[i];
 		event = waiter->event;
+		if (!event)
+			return -EINVAL; /* event was destroyed */
 		if (waiter->activated && event->type == KFD_EVENT_TYPE_MEMORY) {
 			dst = &data[i].memory_exception_data;
 			src = &event->memory_exception_data;
@@ -844,11 +862,8 @@ static int copy_signaled_event_data(uint32_t num_events,
 	}
 
 	return 0;
-
 }
 
-
-
 static long user_timeout_to_jiffies(uint32_t user_timeout_ms)
 {
 	if (user_timeout_ms == KFD_EVENT_TIMEOUT_IMMEDIATE)
@@ -872,9 +887,12 @@ static void free_waiters(uint32_t num_events, struct kfd_event_waiter *waiters)
 	uint32_t i;
 
 	for (i = 0; i < num_events; i++)
-		if (waiters[i].event)
+		if (waiters[i].event) {
+			spin_lock(&waiters[i].event->lock);
 			remove_wait_queue(&waiters[i].event->wq,
 					  &waiters[i].wait);
+			spin_unlock(&waiters[i].event->lock);
+		}
 
 	kfree(waiters);
 }
@@ -898,6 +916,9 @@ int kfd_wait_on_events(struct kfd_process *p,
 		goto out;
 	}
 
+	/* Use p->event_mutex here to protect against concurrent creation and
+	 * destruction of events while we initialize event_waiters.
+	 */
 	mutex_lock(&p->event_mutex);
 
 	for (i = 0; i < num_events; i++) {
@@ -976,14 +997,19 @@ int kfd_wait_on_events(struct kfd_process *p,
 	}
 	__set_current_state(TASK_RUNNING);
 
+	mutex_lock(&p->event_mutex);
 	/* copy_signaled_event_data may sleep. So this has to happen
 	 * after the task state is set back to RUNNING.
+	 *
+	 * The event may also have been destroyed after signaling. So
+	 * copy_signaled_event_data also must confirm that the event
+	 * still exists. Therefore this must be under the p->event_mutex
+	 * which is also held when events are destroyed.
 	 */
 	if (!ret && *wait_result == KFD_IOC_WAIT_RESULT_COMPLETE)
 		ret = copy_signaled_event_data(num_events,
 					       event_waiters, events);
 
-	mutex_lock(&p->event_mutex);
 out_unlock:
 	free_waiters(num_events, event_waiters);
 	mutex_unlock(&p->event_mutex);
@@ -1042,8 +1068,7 @@ int kfd_event_mmap(struct kfd_process *p, struct vm_area_struct *vma)
 }
 
 /*
- * Assumes that p->event_mutex is held and of course
- * that p is not going away (current or locked).
+ * Assumes that p is not going away.
  */
 static void lookup_events_by_type_and_signal(struct kfd_process *p,
 		int type, void *event_data)
@@ -1055,6 +1080,8 @@ static void lookup_events_by_type_and_signal(struct kfd_process *p,
 
 	ev_data = (struct kfd_hsa_memory_exception_data *) event_data;
 
+	rcu_read_lock();
+
 	id = KFD_FIRST_NONSIGNAL_EVENT_ID;
 	idr_for_each_entry_continue(&p->event_idr, ev, id)
 		if (ev->type == type) {
@@ -1062,9 +1089,11 @@ static void lookup_events_by_type_and_signal(struct kfd_process *p,
 			dev_dbg(kfd_device,
 					"Event found: id %X type %d",
 					ev->event_id, ev->type);
+			spin_lock(&ev->lock);
 			set_event(ev);
 			if (ev->type == KFD_EVENT_TYPE_MEMORY && ev_data)
 				ev->memory_exception_data = *ev_data;
+			spin_unlock(&ev->lock);
 		}
 
 	if (type == KFD_EVENT_TYPE_MEMORY) {
@@ -1087,6 +1116,8 @@ static void lookup_events_by_type_and_signal(struct kfd_process *p,
 				p->lead_thread->pid, p->pasid);
 		}
 	}
+
+	rcu_read_unlock();
 }
 
 #ifdef KFD_SUPPORT_IOMMU_V2
@@ -1162,16 +1193,10 @@ void kfd_signal_iommu_event(struct kfd_dev *dev, u32 pasid,
 
 	if (KFD_GC_VERSION(dev) != IP_VERSION(9, 1, 0) &&
 	    KFD_GC_VERSION(dev) != IP_VERSION(9, 2, 2) &&
-	    KFD_GC_VERSION(dev) != IP_VERSION(9, 3, 0)) {
-		mutex_lock(&p->event_mutex);
-
-		/* Lookup events by type and signal them */
+	    KFD_GC_VERSION(dev) != IP_VERSION(9, 3, 0))
 		lookup_events_by_type_and_signal(p, KFD_EVENT_TYPE_MEMORY,
 				&memory_exception_data);
 
-		mutex_unlock(&p->event_mutex);
-	}
-
 	kfd_unref_process(p);
 }
 #endif /* KFD_SUPPORT_IOMMU_V2 */
@@ -1188,12 +1213,7 @@ void kfd_signal_hw_exception_event(u32 pasid)
 	if (!p)
 		return; /* Presumably process exited. */
 
-	mutex_lock(&p->event_mutex);
-
-	/* Lookup events by type and signal them */
 	lookup_events_by_type_and_signal(p, KFD_EVENT_TYPE_HW_EXCEPTION, NULL);
-
-	mutex_unlock(&p->event_mutex);
 	kfd_unref_process(p);
 }
 
@@ -1229,16 +1249,19 @@ void kfd_signal_vm_fault_event(struct kfd_dev *dev, u32 pasid,
 			info->prot_write ? 1 : 0;
 		memory_exception_data.failure.imprecise = 0;
 	}
-	mutex_lock(&p->event_mutex);
+
+	rcu_read_lock();
 
 	id = KFD_FIRST_NONSIGNAL_EVENT_ID;
 	idr_for_each_entry_continue(&p->event_idr, ev, id)
 		if (ev->type == KFD_EVENT_TYPE_MEMORY) {
+			spin_lock(&ev->lock);
 			ev->memory_exception_data = memory_exception_data;
 			set_event(ev);
+			spin_unlock(&ev->lock);
 		}
 
-	mutex_unlock(&p->event_mutex);
+	rcu_read_unlock();
 	kfd_unref_process(p);
 }
 
@@ -1272,22 +1295,28 @@ void kfd_signal_reset_event(struct kfd_dev *dev)
 			continue;
 		}
 
-		mutex_lock(&p->event_mutex);
+		rcu_read_lock();
+
 		id = KFD_FIRST_NONSIGNAL_EVENT_ID;
 		idr_for_each_entry_continue(&p->event_idr, ev, id) {
 			if (ev->type == KFD_EVENT_TYPE_HW_EXCEPTION) {
+				spin_lock(&ev->lock);
 				ev->hw_exception_data = hw_exception_data;
 				ev->hw_exception_data.gpu_id = user_gpu_id;
 				set_event(ev);
+				spin_unlock(&ev->lock);
 			}
 			if (ev->type == KFD_EVENT_TYPE_MEMORY &&
 			    reset_cause == KFD_HW_EXCEPTION_ECC) {
+				spin_lock(&ev->lock);
 				ev->memory_exception_data = memory_exception_data;
 				ev->memory_exception_data.gpu_id = user_gpu_id;
 				set_event(ev);
+				spin_unlock(&ev->lock);
 			}
 		}
-		mutex_unlock(&p->event_mutex);
+
+		rcu_read_unlock();
 	}
 	srcu_read_unlock(&kfd_processes_srcu, idx);
 }
@@ -1320,19 +1349,25 @@ void kfd_signal_poison_consumed_event(struct kfd_dev *dev, u32 pasid)
 	memory_exception_data.gpu_id = user_gpu_id;
 	memory_exception_data.failure.imprecise = true;
 
-	mutex_lock(&p->event_mutex);
+	rcu_read_lock();
+
 	idr_for_each_entry_continue(&p->event_idr, ev, id) {
 		if (ev->type == KFD_EVENT_TYPE_HW_EXCEPTION) {
+			spin_lock(&ev->lock);
 			ev->hw_exception_data = hw_exception_data;
 			set_event(ev);
+			spin_unlock(&ev->lock);
 		}
 
 		if (ev->type == KFD_EVENT_TYPE_MEMORY) {
+			spin_lock(&ev->lock);
 			ev->memory_exception_data = memory_exception_data;
 			set_event(ev);
+			spin_unlock(&ev->lock);
 		}
 	}
-	mutex_unlock(&p->event_mutex);
+
+	rcu_read_unlock();
 
 	/* user application will handle SIGBUS signal */
 	send_sig(SIGBUS, p->lead_thread, 0);
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.h b/drivers/gpu/drm/amd/amdkfd/kfd_events.h
index 1238af11916e..55d376f56021 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_events.h
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.h
@@ -59,6 +59,7 @@ struct kfd_event {
 
 	int type;
 
+	spinlock_t lock;
 	wait_queue_head_t wq; /* List of event waiters. */
 
 	/* Only for signal events. */
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* RE: [PATCH 1/1] drm/amdkfd: Improve concurrency of event handling
  2022-03-02 20:06 [PATCH 1/1] drm/amdkfd: Improve concurrency of event handling Felix Kuehling
@ 2022-03-02 22:53 ` Keely, Sean
  2022-03-03  7:25 ` Christian König
  1 sibling, 0 replies; 4+ messages in thread
From: Keely, Sean @ 2022-03-02 22:53 UTC (permalink / raw)
  To: Kuehling, Felix, amd-gfx

[AMD Official Use Only]

LGTM.

Reviewed-by: Sean Keely <Sean.Keely@amd.com>

-----Original Message-----
From: Kuehling, Felix <Felix.Kuehling@amd.com>
Sent: Wednesday, March 02, 2022 2:06 PM
To: amd-gfx@lists.freedesktop.org
Cc: Keely, Sean <Sean.Keely@amd.com>
Subject: [PATCH 1/1] drm/amdkfd: Improve concurrency of event handling

Use rcu_read_lock to read p->event_idr concurrently with other readers
and writers. Use p->event_mutex only for creating and destroying events
and in kfd_wait_on_events.

Protect the contents of the kfd_event structure with a per-event
spinlock that can be taken inside the rcu_read_lock critical section.

This eliminates contention of p->event_mutex in set_event, which tends
to be on the critical path for dispatch latency even when busy waiting
is used. It also eliminates lock contention in event interrupt handlers.
Since the p->event_mutex is now used much less, the impact of requiring
it in kfd_wait_on_events should also be much smaller.

This should improve event handling latency for processes using multiple
GPUs concurrently.

Signed-off-by: Felix Kuehling <Felix.Kuehling@amd.com>
---
 drivers/gpu/drm/amd/amdkfd/kfd_events.c | 119 +++++++++++++++---------
 drivers/gpu/drm/amd/amdkfd/kfd_events.h |   1 +
 2 files changed, 78 insertions(+), 42 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_events.c
index deecccebe5b6..1726306715b2 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_events.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.c
@@ -128,8 +128,8 @@ static int allocate_event_notification_slot(struct kfd_process *p,
 }

 /*
- * Assumes that p->event_mutex is held and of course that p is not going
- * away (current or locked).
+ * Assumes that p->event_mutex or rcu_readlock is held and of course that p is
+ * not going away.
  */
 static struct kfd_event *lookup_event_by_id(struct kfd_process *p, uint32_t id)
 {
@@ -251,15 +251,18 @@ static void destroy_event(struct kfd_process *p, struct kfd_event *ev)
        struct kfd_event_waiter *waiter;

        /* Wake up pending waiters. They will return failure */
+       spin_lock(&ev->lock);
        list_for_each_entry(waiter, &ev->wq.head, wait.entry)
-               waiter->event = NULL;
+               WRITE_ONCE(waiter->event, NULL);
        wake_up_all(&ev->wq);
+       spin_unlock(&ev->lock);

        if (ev->type == KFD_EVENT_TYPE_SIGNAL ||
            ev->type == KFD_EVENT_TYPE_DEBUG)
                p->signal_event_count--;

        idr_remove(&p->event_idr, ev->event_id);
+       synchronize_rcu();
        kfree(ev);
 }

@@ -392,6 +395,7 @@ int kfd_event_create(struct file *devkfd, struct kfd_process *p,
        ev->auto_reset = auto_reset;
        ev->signaled = false;

+       spin_lock_init(&ev->lock);
        init_waitqueue_head(&ev->wq);

        *event_page_offset = 0;
@@ -466,6 +470,7 @@ int kfd_criu_restore_event(struct file *devkfd,
        ev->auto_reset = ev_priv->auto_reset;
        ev->signaled = ev_priv->signaled;

+       spin_lock_init(&ev->lock);
        init_waitqueue_head(&ev->wq);

        mutex_lock(&p->event_mutex);
@@ -609,13 +614,13 @@ static void set_event(struct kfd_event *ev)

        /* Auto reset if the list is non-empty and we're waking
         * someone. waitqueue_active is safe here because we're
-        * protected by the p->event_mutex, which is also held when
+        * protected by the ev->lock, 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(waiter, &ev->wq.head, wait.entry)
-               waiter->activated = true;
+               WRITE_ONCE(waiter->activated, true);

        wake_up_all(&ev->wq);
 }
@@ -626,16 +631,18 @@ int kfd_set_event(struct kfd_process *p, uint32_t event_id)
        int ret = 0;
        struct kfd_event *ev;

-       mutex_lock(&p->event_mutex);
+       rcu_read_lock();

        ev = lookup_event_by_id(p, event_id);
+       spin_lock(&ev->lock);

        if (ev && event_can_be_cpu_signaled(ev))
                set_event(ev);
        else
                ret = -EINVAL;

-       mutex_unlock(&p->event_mutex);
+       spin_unlock(&ev->lock);
+       rcu_read_unlock();
        return ret;
 }

@@ -650,23 +657,25 @@ int kfd_reset_event(struct kfd_process *p, uint32_t event_id)
        int ret = 0;
        struct kfd_event *ev;

-       mutex_lock(&p->event_mutex);
+       rcu_read_lock();

        ev = lookup_event_by_id(p, event_id);
+       spin_lock(&ev->lock);

        if (ev && event_can_be_cpu_signaled(ev))
                reset_event(ev);
        else
                ret = -EINVAL;

-       mutex_unlock(&p->event_mutex);
+       spin_unlock(&ev->lock);
+       rcu_read_unlock();
        return ret;

 }

 static void acknowledge_signal(struct kfd_process *p, struct kfd_event *ev)
 {
-       page_slots(p->signal_page)[ev->event_id] = UNSIGNALED_EVENT_SLOT;
+       WRITE_ONCE(page_slots(p->signal_page)[ev->event_id], UNSIGNALED_EVENT_SLOT);
 }

 static void set_event_from_interrupt(struct kfd_process *p,
@@ -674,7 +683,9 @@ static void set_event_from_interrupt(struct kfd_process *p,
 {
        if (ev && event_can_be_gpu_signaled(ev)) {
                acknowledge_signal(p, ev);
+               spin_lock(&ev->lock);
                set_event(ev);
+               spin_unlock(&ev->lock);
        }
 }

@@ -693,7 +704,7 @@ void kfd_signal_event_interrupt(u32 pasid, uint32_t partial_id,
        if (!p)
                return; /* Presumably process exited. */

-       mutex_lock(&p->event_mutex);
+       rcu_read_lock();

        if (valid_id_bits)
                ev = lookup_signaled_event_by_partial_id(p, partial_id,
@@ -721,7 +732,7 @@ void kfd_signal_event_interrupt(u32 pasid, uint32_t partial_id,
                                if (id >= KFD_SIGNAL_EVENT_LIMIT)
                                        break;

-                               if (slots[id] != UNSIGNALED_EVENT_SLOT)
+                               if (READ_ONCE(slots[id]) != UNSIGNALED_EVENT_SLOT)
                                        set_event_from_interrupt(p, ev);
                        }
                } else {
@@ -730,14 +741,14 @@ void kfd_signal_event_interrupt(u32 pasid, uint32_t partial_id,
                         * only signaled events from the IDR.
                         */
                        for (id = 0; id < KFD_SIGNAL_EVENT_LIMIT; id++)
-                               if (slots[id] != UNSIGNALED_EVENT_SLOT) {
+                               if (READ_ONCE(slots[id]) != UNSIGNALED_EVENT_SLOT) {
                                        ev = lookup_event_by_id(p, id);
                                        set_event_from_interrupt(p, ev);
                                }
                }
        }

-       mutex_unlock(&p->event_mutex);
+       rcu_read_unlock();
        kfd_unref_process(p);
 }

@@ -767,9 +778,11 @@ static int init_event_waiter_get_status(struct kfd_process *p,
        if (!ev)
                return -EINVAL;

+       spin_lock(&ev->lock);
        waiter->event = ev;
        waiter->activated = ev->signaled;
        ev->signaled = ev->signaled && !ev->auto_reset;
+       spin_unlock(&ev->lock);

        return 0;
 }
@@ -781,8 +794,11 @@ static void init_event_waiter_add_to_waitlist(struct kfd_event_waiter *waiter)
        /* Only add to the wait list if we actually need to
         * wait on this event.
         */
-       if (!waiter->activated)
+       if (!waiter->activated) {
+               spin_lock(&ev->lock);
                add_wait_queue(&ev->wq, &waiter->wait);
+               spin_unlock(&ev->lock);
+       }
 }

 /* test_event_condition - Test condition of events being waited for
@@ -802,10 +818,10 @@ static uint32_t test_event_condition(bool all, uint32_t num_events,
        uint32_t activated_count = 0;

        for (i = 0; i < num_events; i++) {
-               if (!event_waiters[i].event)
+               if (!READ_ONCE(event_waiters[i].event))
                        return KFD_IOC_WAIT_RESULT_FAIL;

-               if (event_waiters[i].activated) {
+               if (READ_ONCE(event_waiters[i].activated)) {
                        if (!all)
                                return KFD_IOC_WAIT_RESULT_COMPLETE;

@@ -834,6 +850,8 @@ static int copy_signaled_event_data(uint32_t num_events,
        for (i = 0; i < num_events; i++) {
                waiter = &event_waiters[i];
                event = waiter->event;
+               if (!event)
+                       return -EINVAL; /* event was destroyed */
                if (waiter->activated && event->type == KFD_EVENT_TYPE_MEMORY) {
                        dst = &data[i].memory_exception_data;
                        src = &event->memory_exception_data;
@@ -844,11 +862,8 @@ static int copy_signaled_event_data(uint32_t num_events,
        }

        return 0;
-
 }

-
-
 static long user_timeout_to_jiffies(uint32_t user_timeout_ms)
 {
        if (user_timeout_ms == KFD_EVENT_TIMEOUT_IMMEDIATE)
@@ -872,9 +887,12 @@ static void free_waiters(uint32_t num_events, struct kfd_event_waiter *waiters)
        uint32_t i;

        for (i = 0; i < num_events; i++)
-               if (waiters[i].event)
+               if (waiters[i].event) {
+                       spin_lock(&waiters[i].event->lock);
                        remove_wait_queue(&waiters[i].event->wq,
                                          &waiters[i].wait);
+                       spin_unlock(&waiters[i].event->lock);
+               }

        kfree(waiters);
 }
@@ -898,6 +916,9 @@ int kfd_wait_on_events(struct kfd_process *p,
                goto out;
        }

+       /* Use p->event_mutex here to protect against concurrent creation and
+        * destruction of events while we initialize event_waiters.
+        */
        mutex_lock(&p->event_mutex);

        for (i = 0; i < num_events; i++) {
@@ -976,14 +997,19 @@ int kfd_wait_on_events(struct kfd_process *p,
        }
        __set_current_state(TASK_RUNNING);

+       mutex_lock(&p->event_mutex);
        /* copy_signaled_event_data may sleep. So this has to happen
         * after the task state is set back to RUNNING.
+        *
+        * The event may also have been destroyed after signaling. So
+        * copy_signaled_event_data also must confirm that the event
+        * still exists. Therefore this must be under the p->event_mutex
+        * which is also held when events are destroyed.
         */
        if (!ret && *wait_result == KFD_IOC_WAIT_RESULT_COMPLETE)
                ret = copy_signaled_event_data(num_events,
                                               event_waiters, events);

-       mutex_lock(&p->event_mutex);
 out_unlock:
        free_waiters(num_events, event_waiters);
        mutex_unlock(&p->event_mutex);
@@ -1042,8 +1068,7 @@ int kfd_event_mmap(struct kfd_process *p, struct vm_area_struct *vma)
 }

 /*
- * Assumes that p->event_mutex is held and of course
- * that p is not going away (current or locked).
+ * Assumes that p is not going away.
  */
 static void lookup_events_by_type_and_signal(struct kfd_process *p,
                int type, void *event_data)
@@ -1055,6 +1080,8 @@ static void lookup_events_by_type_and_signal(struct kfd_process *p,

        ev_data = (struct kfd_hsa_memory_exception_data *) event_data;

+       rcu_read_lock();
+
        id = KFD_FIRST_NONSIGNAL_EVENT_ID;
        idr_for_each_entry_continue(&p->event_idr, ev, id)
                if (ev->type == type) {
@@ -1062,9 +1089,11 @@ static void lookup_events_by_type_and_signal(struct kfd_process *p,
                        dev_dbg(kfd_device,
                                        "Event found: id %X type %d",
                                        ev->event_id, ev->type);
+                       spin_lock(&ev->lock);
                        set_event(ev);
                        if (ev->type == KFD_EVENT_TYPE_MEMORY && ev_data)
                                ev->memory_exception_data = *ev_data;
+                       spin_unlock(&ev->lock);
                }

        if (type == KFD_EVENT_TYPE_MEMORY) {
@@ -1087,6 +1116,8 @@ static void lookup_events_by_type_and_signal(struct kfd_process *p,
                                p->lead_thread->pid, p->pasid);
                }
        }
+
+       rcu_read_unlock();
 }

 #ifdef KFD_SUPPORT_IOMMU_V2
@@ -1162,16 +1193,10 @@ void kfd_signal_iommu_event(struct kfd_dev *dev, u32 pasid,

        if (KFD_GC_VERSION(dev) != IP_VERSION(9, 1, 0) &&
            KFD_GC_VERSION(dev) != IP_VERSION(9, 2, 2) &&
-           KFD_GC_VERSION(dev) != IP_VERSION(9, 3, 0)) {
-               mutex_lock(&p->event_mutex);
-
-               /* Lookup events by type and signal them */
+           KFD_GC_VERSION(dev) != IP_VERSION(9, 3, 0))
                lookup_events_by_type_and_signal(p, KFD_EVENT_TYPE_MEMORY,
                                &memory_exception_data);

-               mutex_unlock(&p->event_mutex);
-       }
-
        kfd_unref_process(p);
 }
 #endif /* KFD_SUPPORT_IOMMU_V2 */
@@ -1188,12 +1213,7 @@ void kfd_signal_hw_exception_event(u32 pasid)
        if (!p)
                return; /* Presumably process exited. */

-       mutex_lock(&p->event_mutex);
-
-       /* Lookup events by type and signal them */
        lookup_events_by_type_and_signal(p, KFD_EVENT_TYPE_HW_EXCEPTION, NULL);
-
-       mutex_unlock(&p->event_mutex);
        kfd_unref_process(p);
 }

@@ -1229,16 +1249,19 @@ void kfd_signal_vm_fault_event(struct kfd_dev *dev, u32 pasid,
                        info->prot_write ? 1 : 0;
                memory_exception_data.failure.imprecise = 0;
        }
-       mutex_lock(&p->event_mutex);
+
+       rcu_read_lock();

        id = KFD_FIRST_NONSIGNAL_EVENT_ID;
        idr_for_each_entry_continue(&p->event_idr, ev, id)
                if (ev->type == KFD_EVENT_TYPE_MEMORY) {
+                       spin_lock(&ev->lock);
                        ev->memory_exception_data = memory_exception_data;
                        set_event(ev);
+                       spin_unlock(&ev->lock);
                }

-       mutex_unlock(&p->event_mutex);
+       rcu_read_unlock();
        kfd_unref_process(p);
 }

@@ -1272,22 +1295,28 @@ void kfd_signal_reset_event(struct kfd_dev *dev)
                        continue;
                }

-               mutex_lock(&p->event_mutex);
+               rcu_read_lock();
+
                id = KFD_FIRST_NONSIGNAL_EVENT_ID;
                idr_for_each_entry_continue(&p->event_idr, ev, id) {
                        if (ev->type == KFD_EVENT_TYPE_HW_EXCEPTION) {
+                               spin_lock(&ev->lock);
                                ev->hw_exception_data = hw_exception_data;
                                ev->hw_exception_data.gpu_id = user_gpu_id;
                                set_event(ev);
+                               spin_unlock(&ev->lock);
                        }
                        if (ev->type == KFD_EVENT_TYPE_MEMORY &&
                            reset_cause == KFD_HW_EXCEPTION_ECC) {
+                               spin_lock(&ev->lock);
                                ev->memory_exception_data = memory_exception_data;
                                ev->memory_exception_data.gpu_id = user_gpu_id;
                                set_event(ev);
+                               spin_unlock(&ev->lock);
                        }
                }
-               mutex_unlock(&p->event_mutex);
+
+               rcu_read_unlock();
        }
        srcu_read_unlock(&kfd_processes_srcu, idx);
 }
@@ -1320,19 +1349,25 @@ void kfd_signal_poison_consumed_event(struct kfd_dev *dev, u32 pasid)
        memory_exception_data.gpu_id = user_gpu_id;
        memory_exception_data.failure.imprecise = true;

-       mutex_lock(&p->event_mutex);
+       rcu_read_lock();
+
        idr_for_each_entry_continue(&p->event_idr, ev, id) {
                if (ev->type == KFD_EVENT_TYPE_HW_EXCEPTION) {
+                       spin_lock(&ev->lock);
                        ev->hw_exception_data = hw_exception_data;
                        set_event(ev);
+                       spin_unlock(&ev->lock);
                }

                if (ev->type == KFD_EVENT_TYPE_MEMORY) {
+                       spin_lock(&ev->lock);
                        ev->memory_exception_data = memory_exception_data;
                        set_event(ev);
+                       spin_unlock(&ev->lock);
                }
        }
-       mutex_unlock(&p->event_mutex);
+
+       rcu_read_unlock();

        /* user application will handle SIGBUS signal */
        send_sig(SIGBUS, p->lead_thread, 0);
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.h b/drivers/gpu/drm/amd/amdkfd/kfd_events.h
index 1238af11916e..55d376f56021 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_events.h
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.h
@@ -59,6 +59,7 @@ struct kfd_event {

        int type;

+       spinlock_t lock;
        wait_queue_head_t wq; /* List of event waiters. */

        /* Only for signal events. */
--
2.32.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH 1/1] drm/amdkfd: Improve concurrency of event handling
  2022-03-02 20:06 [PATCH 1/1] drm/amdkfd: Improve concurrency of event handling Felix Kuehling
  2022-03-02 22:53 ` Keely, Sean
@ 2022-03-03  7:25 ` Christian König
  2022-03-03 15:31   ` Felix Kuehling
  1 sibling, 1 reply; 4+ messages in thread
From: Christian König @ 2022-03-03  7:25 UTC (permalink / raw)
  To: Felix Kuehling, amd-gfx; +Cc: sean.keely

Am 02.03.22 um 21:06 schrieb Felix Kuehling:
> Use rcu_read_lock to read p->event_idr concurrently with other readers
> and writers. Use p->event_mutex only for creating and destroying events
> and in kfd_wait_on_events.

That might not necessary work as you expected. rcu_read_lock() does not 
provide barriers for the CPU cache.

> Protect the contents of the kfd_event structure with a per-event
> spinlock that can be taken inside the rcu_read_lock critical section.

That helps to prevent problems for the ev structure, but it doesn't 
protect the idr itself.

BTW: Why are you using an idr in the first place? I don't see the lookup 
used anywhere.

Regards,
Christian.

>
> This eliminates contention of p->event_mutex in set_event, which tends
> to be on the critical path for dispatch latency even when busy waiting
> is used. It also eliminates lock contention in event interrupt handlers.
> Since the p->event_mutex is now used much less, the impact of requiring
> it in kfd_wait_on_events should also be much smaller.
>
> This should improve event handling latency for processes using multiple
> GPUs concurrently.
>
> Signed-off-by: Felix Kuehling <Felix.Kuehling@amd.com>
> ---
>   drivers/gpu/drm/amd/amdkfd/kfd_events.c | 119 +++++++++++++++---------
>   drivers/gpu/drm/amd/amdkfd/kfd_events.h |   1 +
>   2 files changed, 78 insertions(+), 42 deletions(-)
>
> diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_events.c
> index deecccebe5b6..1726306715b2 100644
> --- a/drivers/gpu/drm/amd/amdkfd/kfd_events.c
> +++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.c
> @@ -128,8 +128,8 @@ static int allocate_event_notification_slot(struct kfd_process *p,
>   }
>   
>   /*
> - * Assumes that p->event_mutex is held and of course that p is not going
> - * away (current or locked).
> + * Assumes that p->event_mutex or rcu_readlock is held and of course that p is
> + * not going away.
>    */
>   static struct kfd_event *lookup_event_by_id(struct kfd_process *p, uint32_t id)
>   {
> @@ -251,15 +251,18 @@ static void destroy_event(struct kfd_process *p, struct kfd_event *ev)
>   	struct kfd_event_waiter *waiter;
>   
>   	/* Wake up pending waiters. They will return failure */
> +	spin_lock(&ev->lock);
>   	list_for_each_entry(waiter, &ev->wq.head, wait.entry)
> -		waiter->event = NULL;
> +		WRITE_ONCE(waiter->event, NULL);
>   	wake_up_all(&ev->wq);
> +	spin_unlock(&ev->lock);
>   
>   	if (ev->type == KFD_EVENT_TYPE_SIGNAL ||
>   	    ev->type == KFD_EVENT_TYPE_DEBUG)
>   		p->signal_event_count--;
>   
>   	idr_remove(&p->event_idr, ev->event_id);
> +	synchronize_rcu();
>   	kfree(ev);
>   }
>   
> @@ -392,6 +395,7 @@ int kfd_event_create(struct file *devkfd, struct kfd_process *p,
>   	ev->auto_reset = auto_reset;
>   	ev->signaled = false;
>   
> +	spin_lock_init(&ev->lock);
>   	init_waitqueue_head(&ev->wq);
>   
>   	*event_page_offset = 0;
> @@ -466,6 +470,7 @@ int kfd_criu_restore_event(struct file *devkfd,
>   	ev->auto_reset = ev_priv->auto_reset;
>   	ev->signaled = ev_priv->signaled;
>   
> +	spin_lock_init(&ev->lock);
>   	init_waitqueue_head(&ev->wq);
>   
>   	mutex_lock(&p->event_mutex);
> @@ -609,13 +614,13 @@ static void set_event(struct kfd_event *ev)
>   
>   	/* Auto reset if the list is non-empty and we're waking
>   	 * someone. waitqueue_active is safe here because we're
> -	 * protected by the p->event_mutex, which is also held when
> +	 * protected by the ev->lock, 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(waiter, &ev->wq.head, wait.entry)
> -		waiter->activated = true;
> +		WRITE_ONCE(waiter->activated, true);
>   
>   	wake_up_all(&ev->wq);
>   }
> @@ -626,16 +631,18 @@ int kfd_set_event(struct kfd_process *p, uint32_t event_id)
>   	int ret = 0;
>   	struct kfd_event *ev;
>   
> -	mutex_lock(&p->event_mutex);
> +	rcu_read_lock();
>   
>   	ev = lookup_event_by_id(p, event_id);
> +	spin_lock(&ev->lock);
>   
>   	if (ev && event_can_be_cpu_signaled(ev))
>   		set_event(ev);
>   	else
>   		ret = -EINVAL;
>   
> -	mutex_unlock(&p->event_mutex);
> +	spin_unlock(&ev->lock);
> +	rcu_read_unlock();
>   	return ret;
>   }
>   
> @@ -650,23 +657,25 @@ int kfd_reset_event(struct kfd_process *p, uint32_t event_id)
>   	int ret = 0;
>   	struct kfd_event *ev;
>   
> -	mutex_lock(&p->event_mutex);
> +	rcu_read_lock();
>   
>   	ev = lookup_event_by_id(p, event_id);
> +	spin_lock(&ev->lock);
>   
>   	if (ev && event_can_be_cpu_signaled(ev))
>   		reset_event(ev);
>   	else
>   		ret = -EINVAL;
>   
> -	mutex_unlock(&p->event_mutex);
> +	spin_unlock(&ev->lock);
> +	rcu_read_unlock();
>   	return ret;
>   
>   }
>   
>   static void acknowledge_signal(struct kfd_process *p, struct kfd_event *ev)
>   {
> -	page_slots(p->signal_page)[ev->event_id] = UNSIGNALED_EVENT_SLOT;
> +	WRITE_ONCE(page_slots(p->signal_page)[ev->event_id], UNSIGNALED_EVENT_SLOT);
>   }
>   
>   static void set_event_from_interrupt(struct kfd_process *p,
> @@ -674,7 +683,9 @@ static void set_event_from_interrupt(struct kfd_process *p,
>   {
>   	if (ev && event_can_be_gpu_signaled(ev)) {
>   		acknowledge_signal(p, ev);
> +		spin_lock(&ev->lock);
>   		set_event(ev);
> +		spin_unlock(&ev->lock);
>   	}
>   }
>   
> @@ -693,7 +704,7 @@ void kfd_signal_event_interrupt(u32 pasid, uint32_t partial_id,
>   	if (!p)
>   		return; /* Presumably process exited. */
>   
> -	mutex_lock(&p->event_mutex);
> +	rcu_read_lock();
>   
>   	if (valid_id_bits)
>   		ev = lookup_signaled_event_by_partial_id(p, partial_id,
> @@ -721,7 +732,7 @@ void kfd_signal_event_interrupt(u32 pasid, uint32_t partial_id,
>   				if (id >= KFD_SIGNAL_EVENT_LIMIT)
>   					break;
>   
> -				if (slots[id] != UNSIGNALED_EVENT_SLOT)
> +				if (READ_ONCE(slots[id]) != UNSIGNALED_EVENT_SLOT)
>   					set_event_from_interrupt(p, ev);
>   			}
>   		} else {
> @@ -730,14 +741,14 @@ void kfd_signal_event_interrupt(u32 pasid, uint32_t partial_id,
>   			 * only signaled events from the IDR.
>   			 */
>   			for (id = 0; id < KFD_SIGNAL_EVENT_LIMIT; id++)
> -				if (slots[id] != UNSIGNALED_EVENT_SLOT) {
> +				if (READ_ONCE(slots[id]) != UNSIGNALED_EVENT_SLOT) {
>   					ev = lookup_event_by_id(p, id);
>   					set_event_from_interrupt(p, ev);
>   				}
>   		}
>   	}
>   
> -	mutex_unlock(&p->event_mutex);
> +	rcu_read_unlock();
>   	kfd_unref_process(p);
>   }
>   
> @@ -767,9 +778,11 @@ static int init_event_waiter_get_status(struct kfd_process *p,
>   	if (!ev)
>   		return -EINVAL;
>   
> +	spin_lock(&ev->lock);
>   	waiter->event = ev;
>   	waiter->activated = ev->signaled;
>   	ev->signaled = ev->signaled && !ev->auto_reset;
> +	spin_unlock(&ev->lock);
>   
>   	return 0;
>   }
> @@ -781,8 +794,11 @@ static void init_event_waiter_add_to_waitlist(struct kfd_event_waiter *waiter)
>   	/* Only add to the wait list if we actually need to
>   	 * wait on this event.
>   	 */
> -	if (!waiter->activated)
> +	if (!waiter->activated) {
> +		spin_lock(&ev->lock);
>   		add_wait_queue(&ev->wq, &waiter->wait);
> +		spin_unlock(&ev->lock);
> +	}
>   }
>   
>   /* test_event_condition - Test condition of events being waited for
> @@ -802,10 +818,10 @@ static uint32_t test_event_condition(bool all, uint32_t num_events,
>   	uint32_t activated_count = 0;
>   
>   	for (i = 0; i < num_events; i++) {
> -		if (!event_waiters[i].event)
> +		if (!READ_ONCE(event_waiters[i].event))
>   			return KFD_IOC_WAIT_RESULT_FAIL;
>   
> -		if (event_waiters[i].activated) {
> +		if (READ_ONCE(event_waiters[i].activated)) {
>   			if (!all)
>   				return KFD_IOC_WAIT_RESULT_COMPLETE;
>   
> @@ -834,6 +850,8 @@ static int copy_signaled_event_data(uint32_t num_events,
>   	for (i = 0; i < num_events; i++) {
>   		waiter = &event_waiters[i];
>   		event = waiter->event;
> +		if (!event)
> +			return -EINVAL; /* event was destroyed */
>   		if (waiter->activated && event->type == KFD_EVENT_TYPE_MEMORY) {
>   			dst = &data[i].memory_exception_data;
>   			src = &event->memory_exception_data;
> @@ -844,11 +862,8 @@ static int copy_signaled_event_data(uint32_t num_events,
>   	}
>   
>   	return 0;
> -
>   }
>   
> -
> -
>   static long user_timeout_to_jiffies(uint32_t user_timeout_ms)
>   {
>   	if (user_timeout_ms == KFD_EVENT_TIMEOUT_IMMEDIATE)
> @@ -872,9 +887,12 @@ static void free_waiters(uint32_t num_events, struct kfd_event_waiter *waiters)
>   	uint32_t i;
>   
>   	for (i = 0; i < num_events; i++)
> -		if (waiters[i].event)
> +		if (waiters[i].event) {
> +			spin_lock(&waiters[i].event->lock);
>   			remove_wait_queue(&waiters[i].event->wq,
>   					  &waiters[i].wait);
> +			spin_unlock(&waiters[i].event->lock);
> +		}
>   
>   	kfree(waiters);
>   }
> @@ -898,6 +916,9 @@ int kfd_wait_on_events(struct kfd_process *p,
>   		goto out;
>   	}
>   
> +	/* Use p->event_mutex here to protect against concurrent creation and
> +	 * destruction of events while we initialize event_waiters.
> +	 */
>   	mutex_lock(&p->event_mutex);
>   
>   	for (i = 0; i < num_events; i++) {
> @@ -976,14 +997,19 @@ int kfd_wait_on_events(struct kfd_process *p,
>   	}
>   	__set_current_state(TASK_RUNNING);
>   
> +	mutex_lock(&p->event_mutex);
>   	/* copy_signaled_event_data may sleep. So this has to happen
>   	 * after the task state is set back to RUNNING.
> +	 *
> +	 * The event may also have been destroyed after signaling. So
> +	 * copy_signaled_event_data also must confirm that the event
> +	 * still exists. Therefore this must be under the p->event_mutex
> +	 * which is also held when events are destroyed.
>   	 */
>   	if (!ret && *wait_result == KFD_IOC_WAIT_RESULT_COMPLETE)
>   		ret = copy_signaled_event_data(num_events,
>   					       event_waiters, events);
>   
> -	mutex_lock(&p->event_mutex);
>   out_unlock:
>   	free_waiters(num_events, event_waiters);
>   	mutex_unlock(&p->event_mutex);
> @@ -1042,8 +1068,7 @@ int kfd_event_mmap(struct kfd_process *p, struct vm_area_struct *vma)
>   }
>   
>   /*
> - * Assumes that p->event_mutex is held and of course
> - * that p is not going away (current or locked).
> + * Assumes that p is not going away.
>    */
>   static void lookup_events_by_type_and_signal(struct kfd_process *p,
>   		int type, void *event_data)
> @@ -1055,6 +1080,8 @@ static void lookup_events_by_type_and_signal(struct kfd_process *p,
>   
>   	ev_data = (struct kfd_hsa_memory_exception_data *) event_data;
>   
> +	rcu_read_lock();
> +
>   	id = KFD_FIRST_NONSIGNAL_EVENT_ID;
>   	idr_for_each_entry_continue(&p->event_idr, ev, id)
>   		if (ev->type == type) {
> @@ -1062,9 +1089,11 @@ static void lookup_events_by_type_and_signal(struct kfd_process *p,
>   			dev_dbg(kfd_device,
>   					"Event found: id %X type %d",
>   					ev->event_id, ev->type);
> +			spin_lock(&ev->lock);
>   			set_event(ev);
>   			if (ev->type == KFD_EVENT_TYPE_MEMORY && ev_data)
>   				ev->memory_exception_data = *ev_data;
> +			spin_unlock(&ev->lock);
>   		}
>   
>   	if (type == KFD_EVENT_TYPE_MEMORY) {
> @@ -1087,6 +1116,8 @@ static void lookup_events_by_type_and_signal(struct kfd_process *p,
>   				p->lead_thread->pid, p->pasid);
>   		}
>   	}
> +
> +	rcu_read_unlock();
>   }
>   
>   #ifdef KFD_SUPPORT_IOMMU_V2
> @@ -1162,16 +1193,10 @@ void kfd_signal_iommu_event(struct kfd_dev *dev, u32 pasid,
>   
>   	if (KFD_GC_VERSION(dev) != IP_VERSION(9, 1, 0) &&
>   	    KFD_GC_VERSION(dev) != IP_VERSION(9, 2, 2) &&
> -	    KFD_GC_VERSION(dev) != IP_VERSION(9, 3, 0)) {
> -		mutex_lock(&p->event_mutex);
> -
> -		/* Lookup events by type and signal them */
> +	    KFD_GC_VERSION(dev) != IP_VERSION(9, 3, 0))
>   		lookup_events_by_type_and_signal(p, KFD_EVENT_TYPE_MEMORY,
>   				&memory_exception_data);
>   
> -		mutex_unlock(&p->event_mutex);
> -	}
> -
>   	kfd_unref_process(p);
>   }
>   #endif /* KFD_SUPPORT_IOMMU_V2 */
> @@ -1188,12 +1213,7 @@ void kfd_signal_hw_exception_event(u32 pasid)
>   	if (!p)
>   		return; /* Presumably process exited. */
>   
> -	mutex_lock(&p->event_mutex);
> -
> -	/* Lookup events by type and signal them */
>   	lookup_events_by_type_and_signal(p, KFD_EVENT_TYPE_HW_EXCEPTION, NULL);
> -
> -	mutex_unlock(&p->event_mutex);
>   	kfd_unref_process(p);
>   }
>   
> @@ -1229,16 +1249,19 @@ void kfd_signal_vm_fault_event(struct kfd_dev *dev, u32 pasid,
>   			info->prot_write ? 1 : 0;
>   		memory_exception_data.failure.imprecise = 0;
>   	}
> -	mutex_lock(&p->event_mutex);
> +
> +	rcu_read_lock();
>   
>   	id = KFD_FIRST_NONSIGNAL_EVENT_ID;
>   	idr_for_each_entry_continue(&p->event_idr, ev, id)
>   		if (ev->type == KFD_EVENT_TYPE_MEMORY) {
> +			spin_lock(&ev->lock);
>   			ev->memory_exception_data = memory_exception_data;
>   			set_event(ev);
> +			spin_unlock(&ev->lock);
>   		}
>   
> -	mutex_unlock(&p->event_mutex);
> +	rcu_read_unlock();
>   	kfd_unref_process(p);
>   }
>   
> @@ -1272,22 +1295,28 @@ void kfd_signal_reset_event(struct kfd_dev *dev)
>   			continue;
>   		}
>   
> -		mutex_lock(&p->event_mutex);
> +		rcu_read_lock();
> +
>   		id = KFD_FIRST_NONSIGNAL_EVENT_ID;
>   		idr_for_each_entry_continue(&p->event_idr, ev, id) {
>   			if (ev->type == KFD_EVENT_TYPE_HW_EXCEPTION) {
> +				spin_lock(&ev->lock);
>   				ev->hw_exception_data = hw_exception_data;
>   				ev->hw_exception_data.gpu_id = user_gpu_id;
>   				set_event(ev);
> +				spin_unlock(&ev->lock);
>   			}
>   			if (ev->type == KFD_EVENT_TYPE_MEMORY &&
>   			    reset_cause == KFD_HW_EXCEPTION_ECC) {
> +				spin_lock(&ev->lock);
>   				ev->memory_exception_data = memory_exception_data;
>   				ev->memory_exception_data.gpu_id = user_gpu_id;
>   				set_event(ev);
> +				spin_unlock(&ev->lock);
>   			}
>   		}
> -		mutex_unlock(&p->event_mutex);
> +
> +		rcu_read_unlock();
>   	}
>   	srcu_read_unlock(&kfd_processes_srcu, idx);
>   }
> @@ -1320,19 +1349,25 @@ void kfd_signal_poison_consumed_event(struct kfd_dev *dev, u32 pasid)
>   	memory_exception_data.gpu_id = user_gpu_id;
>   	memory_exception_data.failure.imprecise = true;
>   
> -	mutex_lock(&p->event_mutex);
> +	rcu_read_lock();
> +
>   	idr_for_each_entry_continue(&p->event_idr, ev, id) {
>   		if (ev->type == KFD_EVENT_TYPE_HW_EXCEPTION) {
> +			spin_lock(&ev->lock);
>   			ev->hw_exception_data = hw_exception_data;
>   			set_event(ev);
> +			spin_unlock(&ev->lock);
>   		}
>   
>   		if (ev->type == KFD_EVENT_TYPE_MEMORY) {
> +			spin_lock(&ev->lock);
>   			ev->memory_exception_data = memory_exception_data;
>   			set_event(ev);
> +			spin_unlock(&ev->lock);
>   		}
>   	}
> -	mutex_unlock(&p->event_mutex);
> +
> +	rcu_read_unlock();
>   
>   	/* user application will handle SIGBUS signal */
>   	send_sig(SIGBUS, p->lead_thread, 0);
> diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.h b/drivers/gpu/drm/amd/amdkfd/kfd_events.h
> index 1238af11916e..55d376f56021 100644
> --- a/drivers/gpu/drm/amd/amdkfd/kfd_events.h
> +++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.h
> @@ -59,6 +59,7 @@ struct kfd_event {
>   
>   	int type;
>   
> +	spinlock_t lock;
>   	wait_queue_head_t wq; /* List of event waiters. */
>   
>   	/* Only for signal events. */


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH 1/1] drm/amdkfd: Improve concurrency of event handling
  2022-03-03  7:25 ` Christian König
@ 2022-03-03 15:31   ` Felix Kuehling
  0 siblings, 0 replies; 4+ messages in thread
From: Felix Kuehling @ 2022-03-03 15:31 UTC (permalink / raw)
  To: Christian König, amd-gfx; +Cc: sean.keely


Am 2022-03-03 um 02:25 schrieb Christian König:
> Am 02.03.22 um 21:06 schrieb Felix Kuehling:
>> Use rcu_read_lock to read p->event_idr concurrently with other readers
>> and writers. Use p->event_mutex only for creating and destroying events
>> and in kfd_wait_on_events.
>
> That might not necessary work as you expected. rcu_read_lock() does 
> not provide barriers for the CPU cache.
>
>> Protect the contents of the kfd_event structure with a per-event
>> spinlock that can be taken inside the rcu_read_lock critical section.
>
> That helps to prevent problems for the ev structure, but it doesn't 
> protect the idr itself.

idr_find can be used safely under rcu_read_lock according to this 
comment in idr.h:

  * idr_find() is able to be called locklessly, using RCU. The caller must
  * ensure calls to this function are made within rcu_read_lock() regions.
  * Other readers (lock-free or otherwise) and modifications may be running
  * concurrently.
  *
  * It is still required that the caller manage the synchronization and
  * lifetimes of the items. So if RCU lock-free lookups are used, typically
  * this would mean that the items have their own locks, or are amenable to
  * lock-free access; and that the items are freed by RCU (or only freed after
  * having been deleted from the idr tree *and* a synchronize_rcu() grace
  * period).

It was introduced by f9c46d6ea5ce ("idr: make idr_find rcu-safe") in 2008.


>
> BTW: Why are you using an idr in the first place? I don't see the 
> lookup used anywhere.

lookup_event_by_id is just a wrapper around idr_find. It's used in 
kfd_event_destroy, kfd_set_event, kfd_reset_event and 
kfd_wait_on_events. lookup_signaled_event_by_partial_id also uses 
idr_find. It's used in kfd_signal_event_interrupt.

Regards,
   Felix


>
> Regards,
> Christian.
>
>>
>> This eliminates contention of p->event_mutex in set_event, which tends
>> to be on the critical path for dispatch latency even when busy waiting
>> is used. It also eliminates lock contention in event interrupt handlers.
>> Since the p->event_mutex is now used much less, the impact of requiring
>> it in kfd_wait_on_events should also be much smaller.
>>
>> This should improve event handling latency for processes using multiple
>> GPUs concurrently.
>>
>> Signed-off-by: Felix Kuehling <Felix.Kuehling@amd.com>
>> ---
>>   drivers/gpu/drm/amd/amdkfd/kfd_events.c | 119 +++++++++++++++---------
>>   drivers/gpu/drm/amd/amdkfd/kfd_events.h |   1 +
>>   2 files changed, 78 insertions(+), 42 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.c 
>> b/drivers/gpu/drm/amd/amdkfd/kfd_events.c
>> index deecccebe5b6..1726306715b2 100644
>> --- a/drivers/gpu/drm/amd/amdkfd/kfd_events.c
>> +++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.c
>> @@ -128,8 +128,8 @@ static int 
>> allocate_event_notification_slot(struct kfd_process *p,
>>   }
>>     /*
>> - * Assumes that p->event_mutex is held and of course that p is not 
>> going
>> - * away (current or locked).
>> + * Assumes that p->event_mutex or rcu_readlock is held and of course 
>> that p is
>> + * not going away.
>>    */
>>   static struct kfd_event *lookup_event_by_id(struct kfd_process *p, 
>> uint32_t id)
>>   {
>> @@ -251,15 +251,18 @@ static void destroy_event(struct kfd_process 
>> *p, struct kfd_event *ev)
>>       struct kfd_event_waiter *waiter;
>>         /* Wake up pending waiters. They will return failure */
>> +    spin_lock(&ev->lock);
>>       list_for_each_entry(waiter, &ev->wq.head, wait.entry)
>> -        waiter->event = NULL;
>> +        WRITE_ONCE(waiter->event, NULL);
>>       wake_up_all(&ev->wq);
>> +    spin_unlock(&ev->lock);
>>         if (ev->type == KFD_EVENT_TYPE_SIGNAL ||
>>           ev->type == KFD_EVENT_TYPE_DEBUG)
>>           p->signal_event_count--;
>>         idr_remove(&p->event_idr, ev->event_id);
>> +    synchronize_rcu();
>>       kfree(ev);
>>   }
>>   @@ -392,6 +395,7 @@ int kfd_event_create(struct file *devkfd, 
>> struct kfd_process *p,
>>       ev->auto_reset = auto_reset;
>>       ev->signaled = false;
>>   +    spin_lock_init(&ev->lock);
>>       init_waitqueue_head(&ev->wq);
>>         *event_page_offset = 0;
>> @@ -466,6 +470,7 @@ int kfd_criu_restore_event(struct file *devkfd,
>>       ev->auto_reset = ev_priv->auto_reset;
>>       ev->signaled = ev_priv->signaled;
>>   +    spin_lock_init(&ev->lock);
>>       init_waitqueue_head(&ev->wq);
>>         mutex_lock(&p->event_mutex);
>> @@ -609,13 +614,13 @@ static void set_event(struct kfd_event *ev)
>>         /* Auto reset if the list is non-empty and we're waking
>>        * someone. waitqueue_active is safe here because we're
>> -     * protected by the p->event_mutex, which is also held when
>> +     * protected by the ev->lock, 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(waiter, &ev->wq.head, wait.entry)
>> -        waiter->activated = true;
>> +        WRITE_ONCE(waiter->activated, true);
>>         wake_up_all(&ev->wq);
>>   }
>> @@ -626,16 +631,18 @@ int kfd_set_event(struct kfd_process *p, 
>> uint32_t event_id)
>>       int ret = 0;
>>       struct kfd_event *ev;
>>   -    mutex_lock(&p->event_mutex);
>> +    rcu_read_lock();
>>         ev = lookup_event_by_id(p, event_id);
>> +    spin_lock(&ev->lock);
>>         if (ev && event_can_be_cpu_signaled(ev))
>>           set_event(ev);
>>       else
>>           ret = -EINVAL;
>>   -    mutex_unlock(&p->event_mutex);
>> +    spin_unlock(&ev->lock);
>> +    rcu_read_unlock();
>>       return ret;
>>   }
>>   @@ -650,23 +657,25 @@ int kfd_reset_event(struct kfd_process *p, 
>> uint32_t event_id)
>>       int ret = 0;
>>       struct kfd_event *ev;
>>   -    mutex_lock(&p->event_mutex);
>> +    rcu_read_lock();
>>         ev = lookup_event_by_id(p, event_id);
>> +    spin_lock(&ev->lock);
>>         if (ev && event_can_be_cpu_signaled(ev))
>>           reset_event(ev);
>>       else
>>           ret = -EINVAL;
>>   -    mutex_unlock(&p->event_mutex);
>> +    spin_unlock(&ev->lock);
>> +    rcu_read_unlock();
>>       return ret;
>>     }
>>     static void acknowledge_signal(struct kfd_process *p, struct 
>> kfd_event *ev)
>>   {
>> -    page_slots(p->signal_page)[ev->event_id] = UNSIGNALED_EVENT_SLOT;
>> +    WRITE_ONCE(page_slots(p->signal_page)[ev->event_id], 
>> UNSIGNALED_EVENT_SLOT);
>>   }
>>     static void set_event_from_interrupt(struct kfd_process *p,
>> @@ -674,7 +683,9 @@ static void set_event_from_interrupt(struct 
>> kfd_process *p,
>>   {
>>       if (ev && event_can_be_gpu_signaled(ev)) {
>>           acknowledge_signal(p, ev);
>> +        spin_lock(&ev->lock);
>>           set_event(ev);
>> +        spin_unlock(&ev->lock);
>>       }
>>   }
>>   @@ -693,7 +704,7 @@ void kfd_signal_event_interrupt(u32 pasid, 
>> uint32_t partial_id,
>>       if (!p)
>>           return; /* Presumably process exited. */
>>   -    mutex_lock(&p->event_mutex);
>> +    rcu_read_lock();
>>         if (valid_id_bits)
>>           ev = lookup_signaled_event_by_partial_id(p, partial_id,
>> @@ -721,7 +732,7 @@ void kfd_signal_event_interrupt(u32 pasid, 
>> uint32_t partial_id,
>>                   if (id >= KFD_SIGNAL_EVENT_LIMIT)
>>                       break;
>>   -                if (slots[id] != UNSIGNALED_EVENT_SLOT)
>> +                if (READ_ONCE(slots[id]) != UNSIGNALED_EVENT_SLOT)
>>                       set_event_from_interrupt(p, ev);
>>               }
>>           } else {
>> @@ -730,14 +741,14 @@ void kfd_signal_event_interrupt(u32 pasid, 
>> uint32_t partial_id,
>>                * only signaled events from the IDR.
>>                */
>>               for (id = 0; id < KFD_SIGNAL_EVENT_LIMIT; id++)
>> -                if (slots[id] != UNSIGNALED_EVENT_SLOT) {
>> +                if (READ_ONCE(slots[id]) != UNSIGNALED_EVENT_SLOT) {
>>                       ev = lookup_event_by_id(p, id);
>>                       set_event_from_interrupt(p, ev);
>>                   }
>>           }
>>       }
>>   -    mutex_unlock(&p->event_mutex);
>> +    rcu_read_unlock();
>>       kfd_unref_process(p);
>>   }
>>   @@ -767,9 +778,11 @@ static int init_event_waiter_get_status(struct 
>> kfd_process *p,
>>       if (!ev)
>>           return -EINVAL;
>>   +    spin_lock(&ev->lock);
>>       waiter->event = ev;
>>       waiter->activated = ev->signaled;
>>       ev->signaled = ev->signaled && !ev->auto_reset;
>> +    spin_unlock(&ev->lock);
>>         return 0;
>>   }
>> @@ -781,8 +794,11 @@ static void 
>> init_event_waiter_add_to_waitlist(struct kfd_event_waiter *waiter)
>>       /* Only add to the wait list if we actually need to
>>        * wait on this event.
>>        */
>> -    if (!waiter->activated)
>> +    if (!waiter->activated) {
>> +        spin_lock(&ev->lock);
>>           add_wait_queue(&ev->wq, &waiter->wait);
>> +        spin_unlock(&ev->lock);
>> +    }
>>   }
>>     /* test_event_condition - Test condition of events being waited for
>> @@ -802,10 +818,10 @@ static uint32_t test_event_condition(bool all, 
>> uint32_t num_events,
>>       uint32_t activated_count = 0;
>>         for (i = 0; i < num_events; i++) {
>> -        if (!event_waiters[i].event)
>> +        if (!READ_ONCE(event_waiters[i].event))
>>               return KFD_IOC_WAIT_RESULT_FAIL;
>>   -        if (event_waiters[i].activated) {
>> +        if (READ_ONCE(event_waiters[i].activated)) {
>>               if (!all)
>>                   return KFD_IOC_WAIT_RESULT_COMPLETE;
>>   @@ -834,6 +850,8 @@ static int copy_signaled_event_data(uint32_t 
>> num_events,
>>       for (i = 0; i < num_events; i++) {
>>           waiter = &event_waiters[i];
>>           event = waiter->event;
>> +        if (!event)
>> +            return -EINVAL; /* event was destroyed */
>>           if (waiter->activated && event->type == 
>> KFD_EVENT_TYPE_MEMORY) {
>>               dst = &data[i].memory_exception_data;
>>               src = &event->memory_exception_data;
>> @@ -844,11 +862,8 @@ static int copy_signaled_event_data(uint32_t 
>> num_events,
>>       }
>>         return 0;
>> -
>>   }
>>   -
>> -
>>   static long user_timeout_to_jiffies(uint32_t user_timeout_ms)
>>   {
>>       if (user_timeout_ms == KFD_EVENT_TIMEOUT_IMMEDIATE)
>> @@ -872,9 +887,12 @@ static void free_waiters(uint32_t num_events, 
>> struct kfd_event_waiter *waiters)
>>       uint32_t i;
>>         for (i = 0; i < num_events; i++)
>> -        if (waiters[i].event)
>> +        if (waiters[i].event) {
>> +            spin_lock(&waiters[i].event->lock);
>>               remove_wait_queue(&waiters[i].event->wq,
>>                         &waiters[i].wait);
>> +            spin_unlock(&waiters[i].event->lock);
>> +        }
>>         kfree(waiters);
>>   }
>> @@ -898,6 +916,9 @@ int kfd_wait_on_events(struct kfd_process *p,
>>           goto out;
>>       }
>>   +    /* Use p->event_mutex here to protect against concurrent 
>> creation and
>> +     * destruction of events while we initialize event_waiters.
>> +     */
>>       mutex_lock(&p->event_mutex);
>>         for (i = 0; i < num_events; i++) {
>> @@ -976,14 +997,19 @@ int kfd_wait_on_events(struct kfd_process *p,
>>       }
>>       __set_current_state(TASK_RUNNING);
>>   +    mutex_lock(&p->event_mutex);
>>       /* copy_signaled_event_data may sleep. So this has to happen
>>        * after the task state is set back to RUNNING.
>> +     *
>> +     * The event may also have been destroyed after signaling. So
>> +     * copy_signaled_event_data also must confirm that the event
>> +     * still exists. Therefore this must be under the p->event_mutex
>> +     * which is also held when events are destroyed.
>>        */
>>       if (!ret && *wait_result == KFD_IOC_WAIT_RESULT_COMPLETE)
>>           ret = copy_signaled_event_data(num_events,
>>                              event_waiters, events);
>>   -    mutex_lock(&p->event_mutex);
>>   out_unlock:
>>       free_waiters(num_events, event_waiters);
>>       mutex_unlock(&p->event_mutex);
>> @@ -1042,8 +1068,7 @@ int kfd_event_mmap(struct kfd_process *p, 
>> struct vm_area_struct *vma)
>>   }
>>     /*
>> - * Assumes that p->event_mutex is held and of course
>> - * that p is not going away (current or locked).
>> + * Assumes that p is not going away.
>>    */
>>   static void lookup_events_by_type_and_signal(struct kfd_process *p,
>>           int type, void *event_data)
>> @@ -1055,6 +1080,8 @@ static void 
>> lookup_events_by_type_and_signal(struct kfd_process *p,
>>         ev_data = (struct kfd_hsa_memory_exception_data *) event_data;
>>   +    rcu_read_lock();
>> +
>>       id = KFD_FIRST_NONSIGNAL_EVENT_ID;
>>       idr_for_each_entry_continue(&p->event_idr, ev, id)
>>           if (ev->type == type) {
>> @@ -1062,9 +1089,11 @@ static void 
>> lookup_events_by_type_and_signal(struct kfd_process *p,
>>               dev_dbg(kfd_device,
>>                       "Event found: id %X type %d",
>>                       ev->event_id, ev->type);
>> +            spin_lock(&ev->lock);
>>               set_event(ev);
>>               if (ev->type == KFD_EVENT_TYPE_MEMORY && ev_data)
>>                   ev->memory_exception_data = *ev_data;
>> +            spin_unlock(&ev->lock);
>>           }
>>         if (type == KFD_EVENT_TYPE_MEMORY) {
>> @@ -1087,6 +1116,8 @@ static void 
>> lookup_events_by_type_and_signal(struct kfd_process *p,
>>                   p->lead_thread->pid, p->pasid);
>>           }
>>       }
>> +
>> +    rcu_read_unlock();
>>   }
>>     #ifdef KFD_SUPPORT_IOMMU_V2
>> @@ -1162,16 +1193,10 @@ void kfd_signal_iommu_event(struct kfd_dev 
>> *dev, u32 pasid,
>>         if (KFD_GC_VERSION(dev) != IP_VERSION(9, 1, 0) &&
>>           KFD_GC_VERSION(dev) != IP_VERSION(9, 2, 2) &&
>> -        KFD_GC_VERSION(dev) != IP_VERSION(9, 3, 0)) {
>> -        mutex_lock(&p->event_mutex);
>> -
>> -        /* Lookup events by type and signal them */
>> +        KFD_GC_VERSION(dev) != IP_VERSION(9, 3, 0))
>>           lookup_events_by_type_and_signal(p, KFD_EVENT_TYPE_MEMORY,
>>                   &memory_exception_data);
>>   -        mutex_unlock(&p->event_mutex);
>> -    }
>> -
>>       kfd_unref_process(p);
>>   }
>>   #endif /* KFD_SUPPORT_IOMMU_V2 */
>> @@ -1188,12 +1213,7 @@ void kfd_signal_hw_exception_event(u32 pasid)
>>       if (!p)
>>           return; /* Presumably process exited. */
>>   -    mutex_lock(&p->event_mutex);
>> -
>> -    /* Lookup events by type and signal them */
>>       lookup_events_by_type_and_signal(p, 
>> KFD_EVENT_TYPE_HW_EXCEPTION, NULL);
>> -
>> -    mutex_unlock(&p->event_mutex);
>>       kfd_unref_process(p);
>>   }
>>   @@ -1229,16 +1249,19 @@ void kfd_signal_vm_fault_event(struct 
>> kfd_dev *dev, u32 pasid,
>>               info->prot_write ? 1 : 0;
>>           memory_exception_data.failure.imprecise = 0;
>>       }
>> -    mutex_lock(&p->event_mutex);
>> +
>> +    rcu_read_lock();
>>         id = KFD_FIRST_NONSIGNAL_EVENT_ID;
>>       idr_for_each_entry_continue(&p->event_idr, ev, id)
>>           if (ev->type == KFD_EVENT_TYPE_MEMORY) {
>> +            spin_lock(&ev->lock);
>>               ev->memory_exception_data = memory_exception_data;
>>               set_event(ev);
>> +            spin_unlock(&ev->lock);
>>           }
>>   -    mutex_unlock(&p->event_mutex);
>> +    rcu_read_unlock();
>>       kfd_unref_process(p);
>>   }
>>   @@ -1272,22 +1295,28 @@ void kfd_signal_reset_event(struct kfd_dev 
>> *dev)
>>               continue;
>>           }
>>   -        mutex_lock(&p->event_mutex);
>> +        rcu_read_lock();
>> +
>>           id = KFD_FIRST_NONSIGNAL_EVENT_ID;
>>           idr_for_each_entry_continue(&p->event_idr, ev, id) {
>>               if (ev->type == KFD_EVENT_TYPE_HW_EXCEPTION) {
>> +                spin_lock(&ev->lock);
>>                   ev->hw_exception_data = hw_exception_data;
>>                   ev->hw_exception_data.gpu_id = user_gpu_id;
>>                   set_event(ev);
>> +                spin_unlock(&ev->lock);
>>               }
>>               if (ev->type == KFD_EVENT_TYPE_MEMORY &&
>>                   reset_cause == KFD_HW_EXCEPTION_ECC) {
>> +                spin_lock(&ev->lock);
>>                   ev->memory_exception_data = memory_exception_data;
>>                   ev->memory_exception_data.gpu_id = user_gpu_id;
>>                   set_event(ev);
>> +                spin_unlock(&ev->lock);
>>               }
>>           }
>> -        mutex_unlock(&p->event_mutex);
>> +
>> +        rcu_read_unlock();
>>       }
>>       srcu_read_unlock(&kfd_processes_srcu, idx);
>>   }
>> @@ -1320,19 +1349,25 @@ void kfd_signal_poison_consumed_event(struct 
>> kfd_dev *dev, u32 pasid)
>>       memory_exception_data.gpu_id = user_gpu_id;
>>       memory_exception_data.failure.imprecise = true;
>>   -    mutex_lock(&p->event_mutex);
>> +    rcu_read_lock();
>> +
>>       idr_for_each_entry_continue(&p->event_idr, ev, id) {
>>           if (ev->type == KFD_EVENT_TYPE_HW_EXCEPTION) {
>> +            spin_lock(&ev->lock);
>>               ev->hw_exception_data = hw_exception_data;
>>               set_event(ev);
>> +            spin_unlock(&ev->lock);
>>           }
>>             if (ev->type == KFD_EVENT_TYPE_MEMORY) {
>> +            spin_lock(&ev->lock);
>>               ev->memory_exception_data = memory_exception_data;
>>               set_event(ev);
>> +            spin_unlock(&ev->lock);
>>           }
>>       }
>> -    mutex_unlock(&p->event_mutex);
>> +
>> +    rcu_read_unlock();
>>         /* user application will handle SIGBUS signal */
>>       send_sig(SIGBUS, p->lead_thread, 0);
>> diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.h 
>> b/drivers/gpu/drm/amd/amdkfd/kfd_events.h
>> index 1238af11916e..55d376f56021 100644
>> --- a/drivers/gpu/drm/amd/amdkfd/kfd_events.h
>> +++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.h
>> @@ -59,6 +59,7 @@ struct kfd_event {
>>         int type;
>>   +    spinlock_t lock;
>>       wait_queue_head_t wq; /* List of event waiters. */
>>         /* Only for signal events. */
>

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2022-03-03 15:32 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-03-02 20:06 [PATCH 1/1] drm/amdkfd: Improve concurrency of event handling Felix Kuehling
2022-03-02 22:53 ` Keely, Sean
2022-03-03  7:25 ` Christian König
2022-03-03 15:31   ` Felix Kuehling

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.