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
