[Intel-xe] [RFC 25/25] drm/xe/eudebug: vm open/pread/pwrite
Mika Kuoppala
mika.kuoppala at linux.intel.com
Mon Nov 6 11:18:45 UTC 2023
Debugger needs access to the client's vm to read and write. For
example inspecting ISA/ELF and setting up breakpoints.
Add ioctl to open target vm with debugger client and vm_handle
and hook up pread/pwrite possibility.
Open will take timeout argument so that standard fsync
can be used for explicit flushing between cpu/gpu for
the target vm.
TODO: userptr
TODO: flushes per engine
Signed-off-by: Mika Kuoppala <mika.kuoppala at linux.intel.com>
---
drivers/gpu/drm/xe/regs/xe_gt_regs.h | 24 ++
drivers/gpu/drm/xe/xe_eudebug.c | 384 +++++++++++++++++++++++++++
drivers/gpu/drm/xe/xe_eudebug.h | 5 +
include/uapi/drm/xe_drm_tmp.h | 18 ++
4 files changed, 431 insertions(+)
diff --git a/drivers/gpu/drm/xe/regs/xe_gt_regs.h b/drivers/gpu/drm/xe/regs/xe_gt_regs.h
index 8c4163004074..28d10e5a0a1b 100644
--- a/drivers/gpu/drm/xe/regs/xe_gt_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_gt_regs.h
@@ -414,6 +414,30 @@
#define RCU_DEBUG_1_RUNALONE_ACTIVE REG_BIT(2)
#define RCU_DEBUG_1_CONTEXT_ACTIVE REG_BIT(0)
+#define RCU_ASYNC_FLUSH XE_REG(0x149fc)
+#define RCU_ASYNC_FLUSH_IN_PROGRESS REG_BIT(31)
+#define RCU_ASYNC_FLUSH_ENGINE_ID_SHIFT 28
+#define RCU_ASYNC_FLUSH_ENGINE_ID_DECODE1 REG_BIT(26)
+#define RCU_ASYNC_FLUSH_AMFS REG_BIT(8)
+#define RCU_ASYNC_FLUSH_PREFETCH REG_BIT(7)
+#define RCU_ASYNC_FLUSH_DATA_PORT REG_BIT(6)
+#define RCU_ASYNC_FLUSH_DATA_CACHE REG_BIT(5)
+#define RCU_ASYNC_FLUSH_HDC_PIPELINE REG_BIT(4)
+#define RCU_ASYNC_INVALIDATE_HDC_PIPELINE REG_BIT(3)
+#define RCU_ASYNC_INVALIDATE_CONSTANT_CACHE REG_BIT(2)
+#define RCU_ASYNC_INVALIDATE_TEXTURE_CACHE REG_BIT(1)
+#define RCU_ASYNC_INVALIDATE_INSTRUCTION_CACHE REG_BIT(0)
+#define RCU_ASYNC_FLUSH_AND_INVALIDATE_ALL ( \
+ RCU_ASYNC_FLUSH_AMFS | \
+ RCU_ASYNC_FLUSH_PREFETCH | \
+ RCU_ASYNC_FLUSH_DATA_PORT | \
+ RCU_ASYNC_FLUSH_DATA_CACHE | \
+ RCU_ASYNC_FLUSH_HDC_PIPELINE | \
+ RCU_ASYNC_INVALIDATE_HDC_PIPELINE | \
+ RCU_ASYNC_INVALIDATE_CONSTANT_CACHE | \
+ RCU_ASYNC_INVALIDATE_TEXTURE_CACHE | \
+ RCU_ASYNC_INVALIDATE_INSTRUCTION_CACHE )
+
#define FORCEWAKE_ACK_GT XE_REG(0x130044)
#define FORCEWAKE_KERNEL BIT(0)
#define FORCEWAKE_USER BIT(1)
diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
index 57a3cf59aaba..b0e6f30f15d2 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.c
+++ b/drivers/gpu/drm/xe/xe_eudebug.c
@@ -10,8 +10,10 @@
#include <linux/anon_inodes.h>
#include <linux/poll.h>
#include <linux/delay.h>
+#include <linux/file.h>
#include <drm/drm_managed.h>
+#include <drm/drm_drv.h>
#include <uapi/drm/xe_drm_tmp.h>
#include "regs/xe_engine_regs.h"
@@ -525,6 +527,17 @@ static struct xe_file *find_client(struct xe_eudebug *d, const u32 id)
return NULL;
}
+static struct xe_vm *find_vm(struct xe_eudebug *d, const u32 id)
+{
+ struct xe_eudebug_handle *h;
+
+ h = find_resource(d->res, XE_EUDEBUG_RES_TYPE_VM, id);
+ if (h)
+ return (void *)h->key;
+
+ return NULL;
+}
+
static int xe_eudebug_add_handle(struct xe_eudebug *d,
int type,
void *p)
@@ -844,6 +857,8 @@ static long xe_eudebug_eu_control(struct xe_eudebug *d, const u64 arg)
return ret;
}
+static long xe_eudebug_vm_open_ioctl(struct xe_eudebug *d, unsigned long arg);
+
static long xe_eudebug_ioctl(struct file *file,
unsigned int cmd,
unsigned long arg)
@@ -863,6 +878,11 @@ static long xe_eudebug_ioctl(struct file *file,
eu_dbg(d, "ioctl cmd=EU_CONTROL ret=%ld\n", ret);
break;
+ case DRM_XE_EUDEBUG_IOCTL_VM_OPEN:
+ ret = xe_eudebug_vm_open_ioctl(d, arg);
+ eu_dbg(d, "ioctl cmd=VM_OPEN ret=%ld\n", ret);
+ break;
+
default:
ret = -EINVAL;
}
@@ -2166,3 +2186,367 @@ static void discovery_work_fn(struct work_struct *work)
complete_all(&d->discovery);
}
+
+static int xe_eudebug_vma_access(struct xe_vma *vma, u64 offset,
+ void *buf, u64 len, bool write)
+{
+ struct xe_device * const xe = xe_vma_vm(vma)->xe;
+ struct xe_bo *bo = NULL;
+ int ret;
+
+ if (offset + len > xe_vma_size(vma))
+ return -EINVAL;
+
+ if (!len)
+ return 0;
+
+ bo = xe_vma_bo(vma);
+
+ if (bo) {
+ struct iosys_map src;
+
+ dma_resv_lock(bo->ttm.base.resv, NULL);
+
+ ret = ttm_bo_vmap(&bo->ttm, &src);
+ if (!ret) {
+ if (write)
+ xe_map_memcpy_to(xe, &src, offset, buf, len);
+ else
+ xe_map_memcpy_from(xe, buf, &src, offset, len);
+
+ ttm_bo_vunmap(&bo->ttm, &src);
+ }
+ dma_resv_unlock(bo->ttm.base.resv);
+ } else {
+ /* XXX: Userptr */
+ ret = -EOPNOTSUPP;
+ }
+
+ return ret;
+}
+
+int xe_eudebug_vm_access(struct xe_vm *vm, u64 offset,
+ void *buf, u64 len, bool write)
+{
+ struct xe_vma *vma;
+ int ret;
+
+ down_write(&vm->lock);
+
+ vma = xe_vm_find_overlapping_vma(vm, offset, len);
+ if (vma) {
+ offset -= xe_vma_start(vma);
+ ret = xe_eudebug_vma_access(vma, offset, buf, len, write);
+ } else {
+ ret = -EINVAL;
+ }
+
+ up_write(&vm->lock);
+
+ return ret;
+}
+
+struct vm_file {
+ struct xe_eudebug *debugger;
+ struct xe_vm *vm;
+ u64 flags;
+ u64 client_id;
+ u64 vm_handle;
+ u64 timeout_ns;
+};
+
+static ssize_t xe_eudebug_vm_read(struct file *file,
+ char __user *buffer,
+ size_t count, loff_t *pos)
+{
+ struct vm_file *vmf = file->private_data;
+ struct xe_eudebug *d = vmf->debugger;
+ struct xe_vm *res_vm;
+ void *k_buffer;
+ int ret;
+
+ res_vm = find_vm(d, vmf->vm_handle);
+ if (!res_vm)
+ return 0;
+
+ if (res_vm != vmf->vm) {
+ eu_warn(d, "vm_read: vm handle mismatch client_handle=%llu, "
+ "vm_handle=%llu, flags=0x%llx, "
+ "pos=%llu, count=%lu\n",
+ vmf->client_id, vmf->vm_handle, vmf->flags, *pos, count);
+ return 0;
+ }
+
+ k_buffer = kzalloc(count, GFP_KERNEL);
+ if (!k_buffer)
+ return -ENOMEM;
+
+ ret = xe_eudebug_vm_access(vmf->vm, *pos, k_buffer, count, false);
+ if (!ret)
+ ret = copy_to_user(buffer, k_buffer, count);
+
+ kfree(k_buffer);
+
+ eu_dbg(d, "vm_read: client_handle=%llu, vm_handle=%llu, flags=0x%llx, "
+ "pos=%llu, count=%lu, ret=%d\n",
+ vmf->client_id, vmf->vm_handle, vmf->flags, *pos, count, ret);
+
+ return ret ?: count;
+}
+
+static ssize_t xe_eudebug_vm_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *pos)
+{
+ struct vm_file *vmf = file->private_data;
+ struct xe_eudebug *d = vmf->debugger;
+ struct xe_vm *res_vm;
+ void *k_buffer;
+ int ret;
+
+ res_vm = find_vm(d, vmf->vm_handle);
+ if (!res_vm)
+ return 0;
+
+ if (res_vm != vmf->vm) {
+ eu_warn(d, "vm_write: vm handle mismatch client_handle=%llu, "
+ "vm_handle=%llu, flags=0x%llx, "
+ "pos=%llu, count=%lu\n",
+ vmf->client_id, vmf->vm_handle, vmf->flags, *pos, count);
+ return 0;
+ }
+
+ k_buffer = kzalloc(count, GFP_KERNEL);
+ if (!k_buffer)
+ return -ENOMEM;
+
+ ret = copy_from_user(k_buffer, buffer, count);
+ if (!ret)
+ ret = xe_eudebug_vm_access(vmf->vm, *pos, k_buffer, count, true);
+
+ kfree(k_buffer);
+
+ eu_dbg(d, "vm_write: client_handle=%llu, vm_handle=%llu, flags=0x%llx, "
+ "pos=%llu, count=%lu, ret=%d\n",
+ vmf->client_id, vmf->vm_handle, vmf->flags, *pos, count, ret);
+
+ return ret ?: count;
+}
+
+static int engine_rcu_flush(struct xe_eudebug *d,
+ struct xe_hw_engine *hwe,
+ unsigned int timeout_us)
+{
+ const struct xe_reg psmi_addr = RING_PSMI_CTL(hwe->mmio_base);
+ struct xe_gt *gt = hwe->gt;
+ u32 mask = RCU_ASYNC_FLUSH_AND_INVALIDATE_ALL;
+ u32 psmi_ctrl;
+ u32 id;
+ int ret;
+
+ if (hwe->class == XE_ENGINE_CLASS_RENDER)
+ id = 0;
+ else if (hwe->class == XE_ENGINE_CLASS_COMPUTE)
+ id = hwe->instance + 1;
+ else
+ return -EINVAL;
+
+ if (id < 8)
+ mask |= id << RCU_ASYNC_FLUSH_ENGINE_ID_SHIFT;
+ else
+ mask |= (id - 8) << RCU_ASYNC_FLUSH_ENGINE_ID_SHIFT |
+ RCU_ASYNC_FLUSH_ENGINE_ID_DECODE1;
+
+ ret = xe_force_wake_get(gt_to_fw(gt), hwe->domain);
+ if (ret)
+ return ret;
+
+ /* Prevent concurrent flushes */
+ mutex_lock(&d->eu_lock);
+ psmi_ctrl = xe_mmio_read32(gt, psmi_addr);
+ if (!(psmi_ctrl & IDLE_MSG_DISABLE))
+ xe_mmio_write32(gt, psmi_addr,_MASKED_BIT_ENABLE(IDLE_MSG_DISABLE));
+
+ ret = xe_mmio_wait32(gt, RCU_ASYNC_FLUSH,
+ RCU_ASYNC_FLUSH_IN_PROGRESS, 0,
+ timeout_us, NULL, false);
+ if (ret)
+ goto out;
+
+ xe_mmio_write32(gt, RCU_ASYNC_FLUSH, mask);
+
+ ret = xe_mmio_wait32(gt, RCU_ASYNC_FLUSH,
+ RCU_ASYNC_FLUSH_IN_PROGRESS, 0,
+ timeout_us, NULL, false);
+out:
+ if (!(psmi_ctrl & IDLE_MSG_DISABLE))
+ xe_mmio_write32(gt, psmi_addr, _MASKED_BIT_DISABLE(IDLE_MSG_DISABLE));
+
+ mutex_unlock(&d->eu_lock);
+ xe_force_wake_put(gt_to_fw(gt), hwe->domain);
+
+ return ret;
+}
+
+static int xe_eudebug_vm_fsync(struct file *file, loff_t start, loff_t end, int datasync)
+{
+ struct vm_file *vmf = file->private_data;
+ struct xe_eudebug *d = vmf->debugger;
+ struct xe_gt *gt;
+ int gt_id;
+ int ret = -EINVAL;
+
+ eu_dbg(d, "vm_fsync: client_handle=%llu, vm_handle=%llu, flags=0x%llx, "
+ "start=%llu, end=%llu datasync=%d\n",
+ vmf->client_id, vmf->vm_handle, vmf->flags, start, end, datasync);
+
+ for_each_gt(gt, d->xe, gt_id) {
+ struct xe_hw_engine *hwe;
+ enum xe_hw_engine_id id;
+
+ /* XXX: vm open per engine? */
+ for_each_hw_engine(hwe, gt, id) {
+ if (hwe->class != XE_ENGINE_CLASS_RENDER &&
+ hwe->class != XE_ENGINE_CLASS_COMPUTE)
+ continue;
+
+ ret = engine_rcu_flush(d, hwe, vmf->timeout_ns * 1000ull);
+ if (ret)
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int xe_eudebug_vm_release(struct inode *inode, struct file *file)
+{
+ struct vm_file *vmf = file->private_data;
+ struct xe_eudebug *d = vmf->debugger;
+
+ eu_dbg(d, "vm_release: client_handle=%llu, vm_handle=%llu, flags=0x%llx",
+ vmf->client_id, vmf->vm_handle, vmf->flags);
+
+ drm_dev_get(&d->xe->drm);
+ xe_vm_put(vmf->vm);
+ xe_eudebug_put(d);
+ kfree(vmf);
+
+ return 0;
+}
+
+static const struct file_operations vm_fops = {
+ .owner = THIS_MODULE,
+ .llseek = generic_file_llseek,
+ .read = xe_eudebug_vm_read,
+ .write = xe_eudebug_vm_write,
+ .fsync = xe_eudebug_vm_fsync,
+ .mmap = NULL,
+ .release = xe_eudebug_vm_release,
+};
+
+static long
+xe_eudebug_vm_open_ioctl(struct xe_eudebug *d, unsigned long arg)
+{
+ struct drm_xe_eudebug_vm_open param;
+ struct xe_eudebug *d_ref = NULL;
+ struct vm_file *vmf = NULL;
+ struct xe_file *xef;
+ struct xe_vm *vm;
+ struct file *file;
+ long ret = 0;
+ int fd;
+
+ if (_IOC_SIZE(DRM_XE_EUDEBUG_IOCTL_VM_OPEN) != sizeof(param))
+ return -EINVAL;
+
+ if (!(_IOC_DIR(DRM_XE_EUDEBUG_IOCTL_VM_OPEN) & _IOC_WRITE))
+ return -EINVAL;
+
+ if (copy_from_user(¶m, (void __user *)arg, sizeof(param)))
+ return -EFAULT;
+
+ if (param.flags)
+ return -EINVAL;
+
+ if (xe_eudebug_detached(d))
+ return -ENOTCONN;
+
+ vm = NULL;
+ mutex_lock(&d->xe->clients.lock);
+ xef = find_client(d, param.client_handle);
+ if (!xef) {
+ mutex_unlock(&d->xe->clients.lock);
+ return -EINVAL;
+ }
+
+ d_ref = xe_eudebug_get(xef);
+ if (!d_ref) {
+ mutex_unlock(&d->xe->clients.lock);
+ return -EINVAL;
+ }
+
+ mutex_lock(&xef->vm.lock);
+ vm = find_vm(d, param.vm_handle);
+ if (vm)
+ xe_vm_get(vm);
+ mutex_unlock(&xef->vm.lock);
+ mutex_unlock(&d->xe->clients.lock);
+
+ XE_WARN_ON(d != d_ref);
+
+ if (!vm) {
+ ret = -EINVAL;
+ goto out_eudebug_put;
+ }
+
+ vmf = kmalloc(sizeof(*vmf), GFP_KERNEL);
+ if (!vmf) {
+ ret = -ENOMEM;
+ goto out_vm_put;
+ }
+
+ fd = get_unused_fd_flags(O_CLOEXEC);
+ if (fd < 0) {
+ ret = fd;
+ goto out_free;
+ }
+
+ vmf->debugger = d_ref;
+ vmf->vm = vm;
+ vmf->flags = param.flags;
+ vmf->client_id = param.client_handle;
+ vmf->vm_handle = param.vm_handle;
+ vmf->timeout_ns = param.timeout_ns;
+
+ file = anon_inode_getfile("[xe_eudebug.vm]", &vm_fops, vmf, O_RDWR);
+ if (IS_ERR(file)) {
+ ret = PTR_ERR(file);
+ goto out_file_put;
+ }
+
+ drm_dev_get(&d->xe->drm);
+
+ file->f_mode |= FMODE_PREAD | FMODE_PWRITE |
+ FMODE_READ | FMODE_WRITE | FMODE_LSEEK;
+
+ fd_install(fd, file);
+
+ eu_dbg(d, "vm_open: client_handle=%llu, handle=%llu, flags=0x%llx, fd=%d",
+ vmf->client_id, vmf->vm_handle, vmf->flags, fd);
+
+ XE_WARN_ON(ret);
+
+ return fd;
+
+out_file_put:
+ put_unused_fd(fd);
+out_free:
+ kfree(vmf);
+out_vm_put:
+ xe_vm_put(vm);
+out_eudebug_put:
+ xe_eudebug_put(d_ref);
+
+ return ret;
+}
diff --git a/drivers/gpu/drm/xe/xe_eudebug.h b/drivers/gpu/drm/xe/xe_eudebug.h
index 0f5e04603c9d..79ac6c38d123 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.h
+++ b/drivers/gpu/drm/xe/xe_eudebug.h
@@ -5,6 +5,8 @@
#ifndef _XE_EUDEBUG_H_
+#include <linux/types.h>
+
struct drm_device;
struct drm_file;
struct xe_device;
@@ -13,6 +15,7 @@ struct xe_vm;
struct xe_vma;
struct xe_exec_queue;
struct xe_hw_engine;
+struct xe_vma;
int xe_eudebug_connect_ioctl(struct drm_device *dev,
void *data,
@@ -34,4 +37,6 @@ void xe_eudebug_exec_queue_destroy(struct xe_file *xef, struct xe_exec_queue *q)
void xe_eudebug_vm_bind(struct xe_file *xef, struct xe_vm *vm, struct xe_vma *vma);
void xe_eudebug_vm_unbind(struct xe_file *xef, struct xe_vm *vm, struct xe_vma *vma);
+int xe_eudebug_vm_access(struct xe_vm *vm, u64 offset,
+ void *buf, u64 len, bool write);
#endif
diff --git a/include/uapi/drm/xe_drm_tmp.h b/include/uapi/drm/xe_drm_tmp.h
index 093884b70ca6..906bb0ef2d4a 100644
--- a/include/uapi/drm/xe_drm_tmp.h
+++ b/include/uapi/drm/xe_drm_tmp.h
@@ -22,6 +22,7 @@ extern "C" {
*/
#define DRM_XE_EUDEBUG_IOCTL_READ_EVENT _IO('j', 0x0)
#define DRM_XE_EUDEBUG_IOCTL_EU_CONTROL _IOWR('j', 0x2, struct drm_xe_eudebug_eu_control)
+#define DRM_XE_EUDEBUG_IOCTL_VM_OPEN _IOW('j', 0x1, struct drm_xe_eudebug_vm_open)
/* XXX: Document events to match their internal counterparts when moved to xe_drm.h */
struct drm_xe_eudebug_event {
@@ -147,6 +148,23 @@ struct drm_xe_eudebug_eu_control {
__u64 bitmask_ptr;
} __attribute__((packed));
+struct drm_xe_eudebug_vm_open {
+ /** @extensions: Pointer to the first extension struct, if any */
+ __u64 extensions;
+
+ /** @client_handle: id of client */
+ __u64 client_handle;
+
+ /** @vm_handle: id of vm */
+ __u64 vm_handle;
+
+ /** @flags: flags */
+ __u64 flags;
+
+ /** @timeout_ns: Timeout value in nanoseconds operations (fsync) */
+ __u64 timeout_ns;
+};
+
#if defined(__cplusplus)
}
#endif
--
2.34.1
More information about the Intel-xe
mailing list