diff --git a/drivers/gpu/drm/xe/xe_wait_user_fence.c b/drivers/gpu/drm/xe/xe_wait_user_fence.c index 098e2a4cff3f..c4420c0dbf9c 100644 --- a/drivers/gpu/drm/xe/xe_wait_user_fence.c +++ b/drivers/gpu/drm/xe/xe_wait_user_fence.c @@ -7,6 +7,7 @@ #include #include +#include #include #include "xe_device.h" @@ -84,6 +85,21 @@ static int check_hw_engines(struct xe_device *xe, DRM_XE_UFENCE_WAIT_VM_ERROR) #define MAX_OP DRM_XE_UFENCE_WAIT_LTE +static unsigned long to_jiffies_timeout(struct drm_xe_wait_user_fence *args) +{ + unsigned long timeout; + + if (args->flags & DRM_XE_UFENCE_WAIT_ABSTIME) + return drm_timeout_abs_to_jiffies(args->timeout); + + if (args->timeout == MAX_SCHEDULE_TIMEOUT || args->timeout == 0) + return args->timeout; + + timeout = nsecs_to_jiffies(args->timeout); + + return timeout ?: 1; +} + int xe_wait_user_fence_ioctl(struct drm_device *dev, void *data, struct drm_file *file) { @@ -98,7 +114,8 @@ int xe_wait_user_fence_ioctl(struct drm_device *dev, void *data, int err; bool no_engines = args->flags & DRM_XE_UFENCE_WAIT_SOFT_OP || args->flags & DRM_XE_UFENCE_WAIT_VM_ERROR; - unsigned long timeout = args->timeout; + unsigned long timeout; + ktime_t start; if (XE_IOCTL_ERR(xe, args->extensions) || XE_IOCTL_ERR(xe, args->pad) || XE_IOCTL_ERR(xe, args->reserved[0] || args->reserved[1])) @@ -152,8 +169,18 @@ int xe_wait_user_fence_ioctl(struct drm_device *dev, void *data, addr = vm->async_ops.error_capture.addr; } - if (XE_IOCTL_ERR(xe, timeout > MAX_SCHEDULE_TIMEOUT)) - return -EINVAL; + /* + * For negative timeout we want to wait "forever" by setting + * MAX_SCHEDULE_TIMEOUT. But we have to assign this value also + * to args->timeout to avoid being zeroed on the signal delivery + * (see arithmetics after wait). + */ + if (args->timeout < 0) + args->timeout = MAX_SCHEDULE_TIMEOUT; + + timeout = to_jiffies_timeout(args); + + start = ktime_get(); /* * FIXME: Very simple implementation at the moment, single wait queue @@ -192,17 +219,17 @@ int xe_wait_user_fence_ioctl(struct drm_device *dev, void *data, } else { remove_wait_queue(&xe->ufence_wq, &w_wait); } + + if (!(args->flags & DRM_XE_UFENCE_WAIT_ABSTIME)) { + args->timeout -= ktime_to_ns(ktime_sub(ktime_get(), start)); + if (args->timeout < 0) + args->timeout = 0; + } + if (XE_IOCTL_ERR(xe, err < 0)) return err; else if (XE_IOCTL_ERR(xe, !timeout)) return -ETIME; - /* - * Again very simple, return the time in jiffies that has past, may need - * a more precision - */ - if (args->flags & DRM_XE_UFENCE_WAIT_ABSTIME) - args->timeout = args->timeout - timeout; - return 0; } diff --git a/include/uapi/drm/xe_drm.h b/include/uapi/drm/xe_drm.h index 8e7be1551333..347351a8f618 100644 --- a/include/uapi/drm/xe_drm.h +++ b/include/uapi/drm/xe_drm.h @@ -904,8 +904,20 @@ struct drm_xe_wait_user_fence { #define DRM_XE_UFENCE_WAIT_U64 0xffffffffffffffffu /** @mask: comparison mask */ __u64 mask; - - /** @timeout: how long to wait before bailing, value in jiffies */ + /** + * @timeout: how long to wait before bailing, value in nanoseconds. + * Without DRM_XE_UFENCE_WAIT_ABSTIME flag set (relative timeout) + * it contains timeout expressed in nanoseconds to wait (fence will + * expire at now() + timeout). + * When DRM_XE_UFENCE_WAIT_ABSTIME flat is set (absolute timeout) wait + * will end at timeout (uses system MONOTONIC_CLOCK). + * Passing negative timeout leads to neverending wait. + * + * On relative timeout this value is updated with timeout left + * (for restarting the call in case of signal delivery). + * On absolute timeout this value stays intact (restarted call still + * expire at the same point of time). + */ __s64 timeout; /**