Extend the WAIT_EVENT wait loop with optional userspace condition
evaluation.

When condition fields are provided, WAIT_EVENT only completes when both
a matching event record exists and the userspace condition passes.

The condition is evaluated with copy_from_user() in the WAIT_EVENT path
before consuming the matching event record.

Cc: Alex Deucher <[email protected]>
Cc: Christian König <[email protected]>
Signed-off-by: Srinivasan Shanmugam <[email protected]>
---
 .../gpu/drm/amd/amdgpu/amdgpu_wait_event.c    | 106 +++++++++++++++++-
 1 file changed, 102 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_wait_event.c 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_wait_event.c
index c055b201c428..f900a7f5f90e 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_wait_event.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_wait_event.c
@@ -29,6 +29,43 @@
 #include "amdgpu.h"
 #include "amdgpu_wait_event.h"
 
+static int amdgpu_wait_event_do_compare(u64 addr, u64 value, u64 mask, u16 op)
+{
+       u64 rvalue;
+       bool passed;
+
+       if (op == DRM_AMDGPU_WAIT_COND_NONE)
+               return 0;
+
+       if (copy_from_user(&rvalue, u64_to_user_ptr(addr), sizeof(rvalue)))
+               return -EFAULT;
+
+       switch (op) {
+       case DRM_AMDGPU_WAIT_COND_EQ:
+               passed = (rvalue & mask) == (value & mask);
+               break;
+       case DRM_AMDGPU_WAIT_COND_NEQ:
+               passed = (rvalue & mask) != (value & mask);
+               break;
+       case DRM_AMDGPU_WAIT_COND_GT:
+               passed = (rvalue & mask) > (value & mask);
+               break;
+       case DRM_AMDGPU_WAIT_COND_GTE:
+               passed = (rvalue & mask) >= (value & mask);
+               break;
+       case DRM_AMDGPU_WAIT_COND_LT:
+               passed = (rvalue & mask) < (value & mask);
+               break;
+       case DRM_AMDGPU_WAIT_COND_LTE:
+               passed = (rvalue & mask) <= (value & mask);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return passed ? 0 : 1;
+}
+
 static long amdgpu_wait_event_to_jiffies(__s64 timeout_ns)
 {
        unsigned long long t;
@@ -99,6 +136,25 @@ amdgpu_wait_event_pop_match(struct amdgpu_wait_event_mgr 
*mgr,
        return found;
 }
 
+static struct amdgpu_wait_event_record *
+amdgpu_wait_event_peek_match(struct amdgpu_wait_event_mgr *mgr,
+                            const struct drm_amdgpu_wait_event *args)
+{
+       struct amdgpu_wait_event_record *rec, *found = NULL;
+       unsigned long flags;
+
+       spin_lock_irqsave(&mgr->lock, flags);
+       list_for_each_entry(rec, &mgr->pending, node) {
+               if (amdgpu_wait_event_match(args, rec)) {
+                       found = rec;
+                       break;
+               }
+       }
+       spin_unlock_irqrestore(&mgr->lock, flags);
+
+       return found;
+}
+
 static int amdgpu_wait_event_push_common(struct amdgpu_wait_event_mgr *mgr,
                                         struct drm_amdgpu_wait_event_data 
*data)
 {
@@ -245,6 +301,18 @@ int amdgpu_wait_event_drm_ioctl(struct drm_device *dev, 
void *data,
        if (args->out_size < sizeof(struct drm_amdgpu_wait_event_data))
                return -EINVAL;
 
+       if (args->cond_op > DRM_AMDGPU_WAIT_COND_LTE)
+               return -EINVAL;
+
+       if (args->reserved0 || args->reserved1)
+               return -EINVAL;
+
+       if (args->cond_addr & 0x7)
+               return -EINVAL;
+
+       if (args->cond_op != DRM_AMDGPU_WAIT_COND_NONE && !args->cond_addr)
+               return -EINVAL;
+
        if (amdgpu_wait_event_type_is_queue_scoped(args->event_type)) {
                if (!args->queue_id)
                        return -EINVAL;
@@ -257,9 +325,22 @@ int amdgpu_wait_event_drm_ioctl(struct drm_device *dev, 
void *data,
        timeout = amdgpu_wait_event_to_jiffies(args->timeout_ns);
 
        for (;;) {
-               rec = amdgpu_wait_event_pop_match(mgr, args);
-               if (rec)
-                       break;
+               rec = amdgpu_wait_event_peek_match(mgr, args);
+               if (rec) {
+                       ret = amdgpu_wait_event_do_compare(args->cond_addr,
+                                                          args->cond_value,
+                                                          args->cond_mask,
+                                                          args->cond_op);
+                       if (ret < 0)
+                               return ret;
+
+                       if (ret == 0) {
+                               rec = amdgpu_wait_event_pop_match(mgr, args);
+                               if (rec)
+                                       break;
+                               continue;
+                       }
+               }
 
                if (READ_ONCE(mgr->dead))
                        return -EIO;
@@ -267,8 +348,25 @@ int amdgpu_wait_event_drm_ioctl(struct drm_device *dev, 
void *data,
                if (signal_pending(current))
                        return -ERESTARTSYS;
 
-               if (!timeout)
+               if (!timeout) {
+                       rec = amdgpu_wait_event_peek_match(mgr, args);
+                       if (rec) {
+                               ret = 
amdgpu_wait_event_do_compare(args->cond_addr,
+                                                                  
args->cond_value,
+                                                                  
args->cond_mask,
+                                                                  
args->cond_op);
+                               if (ret < 0)
+                                       return ret;
+
+                               if (ret == 0) {
+                                       rec = amdgpu_wait_event_pop_match(mgr, 
args);
+                                       if (rec)
+                                               break;
+                                       continue;
+                               }
+                       }
                        return -ETIME;
+               }
 
                timeout = wait_event_interruptible_timeout(
                        mgr->wq,
-- 
2.34.1

Reply via email to