brintos

brintos / linux-shallow public Read only

0
0
Text · 4.2 KiB · 5b4264e Raw
184 lines · c
1// SPDX-License-Identifier: MIT2/*3 * Copyright © 2022 Intel Corporation4 */5 6#include "xe_wait_user_fence.h"7 8#include <drm/drm_device.h>9#include <drm/drm_file.h>10#include <drm/drm_utils.h>11#include <uapi/drm/xe_drm.h>12 13#include "xe_device.h"14#include "xe_gt.h"15#include "xe_macros.h"16#include "xe_exec_queue.h"17 18static int do_compare(u64 addr, u64 value, u64 mask, u16 op)19{20	u64 rvalue;21	int err;22	bool passed;23 24	err = copy_from_user(&rvalue, u64_to_user_ptr(addr), sizeof(rvalue));25	if (err)26		return -EFAULT;27 28	switch (op) {29	case DRM_XE_UFENCE_WAIT_OP_EQ:30		passed = (rvalue & mask) == (value & mask);31		break;32	case DRM_XE_UFENCE_WAIT_OP_NEQ:33		passed = (rvalue & mask) != (value & mask);34		break;35	case DRM_XE_UFENCE_WAIT_OP_GT:36		passed = (rvalue & mask) > (value & mask);37		break;38	case DRM_XE_UFENCE_WAIT_OP_GTE:39		passed = (rvalue & mask) >= (value & mask);40		break;41	case DRM_XE_UFENCE_WAIT_OP_LT:42		passed = (rvalue & mask) < (value & mask);43		break;44	case DRM_XE_UFENCE_WAIT_OP_LTE:45		passed = (rvalue & mask) <= (value & mask);46		break;47	default:48		XE_WARN_ON("Not possible");49		return -EINVAL;50	}51 52	return passed ? 0 : 1;53}54 55#define VALID_FLAGS	DRM_XE_UFENCE_WAIT_FLAG_ABSTIME56#define MAX_OP		DRM_XE_UFENCE_WAIT_OP_LTE57 58static long to_jiffies_timeout(struct xe_device *xe,59			       struct drm_xe_wait_user_fence *args)60{61	unsigned long long t;62	long timeout;63 64	/*65	 * For negative timeout we want to wait "forever" by setting66	 * MAX_SCHEDULE_TIMEOUT. But we have to assign this value also67	 * to args->timeout to avoid being zeroed on the signal delivery68	 * (see arithmetics after wait).69	 */70	if (args->timeout < 0) {71		args->timeout = MAX_SCHEDULE_TIMEOUT;72		return MAX_SCHEDULE_TIMEOUT;73	}74 75	if (args->timeout == 0)76		return 0;77 78	/*79	 * Save the timeout to an u64 variable because nsecs_to_jiffies80	 * might return a value that overflows s32 variable.81	 */82	if (args->flags & DRM_XE_UFENCE_WAIT_FLAG_ABSTIME)83		t = drm_timeout_abs_to_jiffies(args->timeout);84	else85		t = nsecs_to_jiffies(args->timeout);86 87	/*88	 * Anything greater then MAX_SCHEDULE_TIMEOUT is meaningless,89	 * also we don't want to cap it at MAX_SCHEDULE_TIMEOUT because90	 * apparently user doesn't mean to wait forever, otherwise the91	 * args->timeout should have been set to a negative value.92	 */93	if (t > MAX_SCHEDULE_TIMEOUT)94		timeout = MAX_SCHEDULE_TIMEOUT - 1;95	else96		timeout = t;97 98	return timeout ?: 1;99}100 101int xe_wait_user_fence_ioctl(struct drm_device *dev, void *data,102			     struct drm_file *file)103{104	struct xe_device *xe = to_xe_device(dev);105	struct xe_file *xef = to_xe_file(file);106	DEFINE_WAIT_FUNC(w_wait, woken_wake_function);107	struct drm_xe_wait_user_fence *args = data;108	struct xe_exec_queue *q = NULL;109	u64 addr = args->addr;110	int err = 0;111	long timeout;112	ktime_t start;113 114	if (XE_IOCTL_DBG(xe, args->extensions) || XE_IOCTL_DBG(xe, args->pad) ||115	    XE_IOCTL_DBG(xe, args->pad2) ||116	    XE_IOCTL_DBG(xe, args->reserved[0] || args->reserved[1]))117		return -EINVAL;118 119	if (XE_IOCTL_DBG(xe, args->flags & ~VALID_FLAGS))120		return -EINVAL;121 122	if (XE_IOCTL_DBG(xe, args->op > MAX_OP))123		return -EINVAL;124 125	if (XE_IOCTL_DBG(xe, addr & 0x7))126		return -EINVAL;127 128	if (args->exec_queue_id) {129		q = xe_exec_queue_lookup(xef, args->exec_queue_id);130		if (XE_IOCTL_DBG(xe, !q))131			return -ENOENT;132	}133 134	timeout = to_jiffies_timeout(xe, args);135 136	start = ktime_get();137 138	add_wait_queue(&xe->ufence_wq, &w_wait);139	for (;;) {140		err = do_compare(addr, args->value, args->mask, args->op);141		if (err <= 0)142			break;143 144		if (signal_pending(current)) {145			err = -ERESTARTSYS;146			break;147		}148 149		if (q) {150			if (q->ops->reset_status(q)) {151				drm_info(&xe->drm, "exec queue reset detected\n");152				err = -EIO;153				break;154			}155		}156 157		if (!timeout) {158			LNL_FLUSH_WORKQUEUE(xe->ordered_wq);159			err = do_compare(addr, args->value, args->mask,160					 args->op);161			if (err <= 0) {162				drm_dbg(&xe->drm, "LNL_FLUSH_WORKQUEUE resolved ufence timeout\n");163				break;164			}165			err = -ETIME;166			break;167		}168 169		timeout = wait_woken(&w_wait, TASK_INTERRUPTIBLE, timeout);170	}171	remove_wait_queue(&xe->ufence_wq, &w_wait);172 173	if (!(args->flags & DRM_XE_UFENCE_WAIT_FLAG_ABSTIME)) {174		args->timeout -= ktime_to_ns(ktime_sub(ktime_get(), start));175		if (args->timeout < 0)176			args->timeout = 0;177	}178 179	if (q)180		xe_exec_queue_put(q);181 182	return err;183}184