On 02-11-2026 16:42, Matthew Auld wrote:
On 02/11/2026 10:18, Himal Prasad Ghimiray wrote:
Parse the NUMA node property before BO creation and store it on the BO.
Offline or out of range nodes, and BOs without a system memory
placement, are rejected with -EINVAL.

Drop the explicit DRM_XE_GEM_CREATE_EXTENSION_SET_PROPERTY check, already
covered by the property bounds check.

Signed-off-by: Himal Prasad Ghimiray <[email protected]>
---
  drivers/gpu/drm/xe/tests/xe_bo.c      |  12 +--
  drivers/gpu/drm/xe/tests/xe_dma_buf.c |   4 +-
  drivers/gpu/drm/xe/tests/xe_migrate.c |   8 +-
  drivers/gpu/drm/xe/xe_bo.c            | 102 +++++++++++++++++++++-----
  drivers/gpu/drm/xe/xe_bo.h            |   5 +-
  drivers/gpu/drm/xe/xe_dma_buf.c       |   3 +-
  6 files changed, 100 insertions(+), 34 deletions(-)

diff --git a/drivers/gpu/drm/xe/tests/xe_bo.c b/drivers/gpu/drm/xe/tests/xe_bo.c
index 6a17e13d58cf..6f61f5c618f1 100644
--- a/drivers/gpu/drm/xe/tests/xe_bo.c
+++ b/drivers/gpu/drm/xe/tests/xe_bo.c
@@ -89,7 +89,7 @@ static void run_only_leaf(struct kunit *test,
        bo = xe_bo_create_user(xe, NULL, li->alloc_size,
                     DRM_XE_GEM_CPU_CACHING_WC,
-                   XE_BO_FLAG_VRAM0, NULL);
+                   XE_BO_FLAG_VRAM0, NUMA_NO_NODE, NULL);
      if (IS_ERR(bo)) {
          page_size_alloc_restore(xe, &saved);
          if (PTR_ERR(bo) == -ENOSPC) {
@@ -183,7 +183,7 @@ static void xe_bo_page_size_alloc_mixed_bos(struct kunit *test)
           */
          bo = xe_bo_create_user(xe, NULL, SZ_1G,
                         DRM_XE_GEM_CPU_CACHING_WC,
-                       XE_BO_FLAG_VRAM0, NULL);
+                       XE_BO_FLAG_VRAM0, NUMA_NO_NODE, NULL);
          if (IS_ERR(bo)) {
              int err = PTR_ERR(bo);
  @@ -366,7 +366,7 @@ static void ccs_test_run_tile(struct xe_device *xe, struct xe_tile *tile,
          kunit_info(test, "Testing system memory\n");
        bo = xe_bo_create_user(xe, NULL, SZ_1M, DRM_XE_GEM_CPU_CACHING_WC,
-                   bo_flags, exec);
+                   bo_flags, NUMA_NO_NODE, exec);
      if (IS_ERR(bo)) {
          KUNIT_FAIL(test, "Failed to create bo.\n");
          return;
@@ -445,7 +445,7 @@ static int evict_test_run_tile(struct xe_device *xe, struct xe_tile *tile, struc
          xe_vm_lock(vm, false);
          bo = xe_bo_create_user(xe, vm, 0x10000,
                         DRM_XE_GEM_CPU_CACHING_WC,
-                       bo_flags, exec);
+                       bo_flags, NUMA_NO_NODE, exec);
          xe_vm_unlock(vm);
          if (IS_ERR(bo)) {
              KUNIT_FAIL(test, "bo create err=%pe\n", bo);
@@ -454,7 +454,7 @@ static int evict_test_run_tile(struct xe_device *xe, struct xe_tile *tile, struc
            external = xe_bo_create_user(xe, NULL, 0x10000,
                           DRM_XE_GEM_CPU_CACHING_WC,
-                         bo_flags, NULL);
+                         bo_flags, NUMA_NO_NODE, NULL);
          if (IS_ERR(external)) {
              KUNIT_FAIL(test, "external bo create err=%pe\n", external);
              goto cleanup_bo;
@@ -719,7 +719,7 @@ static int shrink_test_run_device(struct xe_device *xe)           /* We can create bos using WC caching here. But it is slower. */
          bo = xe_bo_create_user(xe, NULL, XE_BO_SHRINK_SIZE,
                         DRM_XE_GEM_CPU_CACHING_WB,
-                       XE_BO_FLAG_SYSTEM, NULL);
+                       XE_BO_FLAG_SYSTEM, NUMA_NO_NODE, NULL);
          if (IS_ERR(bo)) {
              if (bo != ERR_PTR(-ENOMEM) && bo != ERR_PTR(-ENOSPC) &&
                  bo != ERR_PTR(-EINTR) && bo != ERR_PTR(-ERESTARTSYS))
diff --git a/drivers/gpu/drm/xe/tests/xe_dma_buf.c b/drivers/gpu/drm/xe/tests/xe_dma_buf.c
index 792376e965f1..876ea545d1a7 100644
--- a/drivers/gpu/drm/xe/tests/xe_dma_buf.c
+++ b/drivers/gpu/drm/xe/tests/xe_dma_buf.c
@@ -127,7 +127,7 @@ static void xe_test_dmabuf_import_same_driver(struct xe_device *xe)
        kunit_info(test, "running %s\n", __func__);
      bo = xe_bo_create_user(xe, NULL, size, DRM_XE_GEM_CPU_CACHING_WC,
-                   params->mem_mask, NULL);
+                   params->mem_mask, NUMA_NO_NODE, NULL);
      if (IS_ERR(bo)) {
          KUNIT_FAIL(test, "xe_bo_create() failed with err=%ld\n",
                 PTR_ERR(bo));
@@ -215,7 +215,7 @@ static void xe_test_dmabuf_move_notify_no_resource(struct xe_device *xe)
        kunit_info(test, "running %s\n", __func__);
      bo = xe_bo_create_user(xe, NULL, PAGE_SIZE, DRM_XE_GEM_CPU_CACHING_WC,
-                   params->mem_mask, NULL);
+                   params->mem_mask, NUMA_NO_NODE, NULL);
      if (IS_ERR(bo)) {
          KUNIT_FAIL(test, "xe_bo_create() failed with err=%ld\n",
                 PTR_ERR(bo));
diff --git a/drivers/gpu/drm/xe/tests/xe_migrate.c b/drivers/gpu/drm/xe/tests/xe_migrate.c
index 56c49d8507d7..69b64e3a23b8 100644
--- a/drivers/gpu/drm/xe/tests/xe_migrate.c
+++ b/drivers/gpu/drm/xe/tests/xe_migrate.c
@@ -649,7 +649,7 @@ static void validate_ccs_test_run_tile(struct xe_device *xe, struct xe_tile *til
                     DRM_XE_GEM_CPU_CACHING_WC,
                     XE_BO_FLAG_SYSTEM |
                     XE_BO_FLAG_NEEDS_CPU_ACCESS |
-                   XE_BO_FLAG_PINNED, NULL);
+                   XE_BO_FLAG_PINNED, NUMA_NO_NODE, NULL);
        if (IS_ERR(sys_bo)) {
          KUNIT_FAIL(test, "xe_bo_create() failed with err=%ld\n",
@@ -675,7 +675,7 @@ static void validate_ccs_test_run_tile(struct xe_device *xe, struct xe_tile *til
      ccs_bo = xe_bo_create_user(xe, NULL, SZ_4M,
                     DRM_XE_GEM_CPU_CACHING_WC,
                     bo_flags | XE_BO_FLAG_NEEDS_CPU_ACCESS |
-                   XE_BO_FLAG_PINNED, NULL);
+                   XE_BO_FLAG_PINNED, NUMA_NO_NODE, NULL);
        if (IS_ERR(ccs_bo)) {
          KUNIT_FAIL(test, "xe_bo_create() failed with err=%ld\n",
@@ -700,7 +700,7 @@ static void validate_ccs_test_run_tile(struct xe_device *xe, struct xe_tile *til
      vram_bo = xe_bo_create_user(xe, NULL, SZ_4M,
                      DRM_XE_GEM_CPU_CACHING_WC,
                      bo_flags | XE_BO_FLAG_NEEDS_CPU_ACCESS |
-                    XE_BO_FLAG_PINNED, NULL);
+                    XE_BO_FLAG_PINNED, NUMA_NO_NODE, NULL);
      if (IS_ERR(vram_bo)) {
          KUNIT_FAIL(test, "xe_bo_create() failed with err=%ld\n",
                 PTR_ERR(vram_bo));
@@ -979,7 +979,7 @@ static void access_memory_test_run_tile(struct xe_device *xe,
        bo = xe_bo_create_user(xe, NULL, ACCESS_MEM_BO_SIZE,
                     DRM_XE_GEM_CPU_CACHING_WC,
-                   bo_flags | XE_BO_FLAG_NEEDS_CPU_ACCESS, NULL);
+                   bo_flags | XE_BO_FLAG_NEEDS_CPU_ACCESS, NUMA_NO_NODE, NULL);
      if (IS_ERR(bo)) {
          KUNIT_FAIL(test, "Failed to create bo: %pe\n", bo);
          goto free_buf;
diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c
index fc9bfb436b8e..bbbc33737264 100644
--- a/drivers/gpu/drm/xe/xe_bo.c
+++ b/drivers/gpu/drm/xe/xe_bo.c
@@ -1538,7 +1538,7 @@ int xe_bo_notifier_prepare_pinned(struct xe_bo *bo)           backup = xe_bo_init_locked(xe, NULL, NULL, bo->ttm.base.resv, NULL, xe_bo_size(bo),
                         DRM_XE_GEM_CPU_CACHING_WB, ttm_bo_type_kernel,
                         XE_BO_FLAG_SYSTEM | XE_BO_FLAG_NEEDS_CPU_ACCESS |
-                       XE_BO_FLAG_PINNED, NULL, &exec);
+                       XE_BO_FLAG_PINNED, NULL, NUMA_NO_NODE, &exec);
          if (IS_ERR(backup)) {
              drm_exec_retry_on_contention(&exec);
              ret = PTR_ERR(backup);
@@ -1680,7 +1680,7 @@ int xe_bo_evict_pinned(struct xe_bo *bo)
                             xe_bo_size(bo),
                             DRM_XE_GEM_CPU_CACHING_WB, ttm_bo_type_kernel,                              XE_BO_FLAG_SYSTEM | XE_BO_FLAG_NEEDS_CPU_ACCESS |
-                           XE_BO_FLAG_PINNED, NULL, &exec);
+                           XE_BO_FLAG_PINNED, NULL, NUMA_NO_NODE, &exec);
              if (IS_ERR(backup)) {
                  drm_exec_retry_on_contention(&exec);
                  ret = PTR_ERR(backup);
@@ -2480,7 +2480,7 @@ struct xe_bo *xe_bo_init_locked(struct xe_device *xe, struct xe_bo *bo,
                  struct ttm_lru_bulk_move *bulk, size_t size,
                  u16 cpu_caching, enum ttm_bo_type type,
                  u32 flags, struct dma_buf *dma_buf,
-                struct drm_exec *exec)
+                int numa_node, struct drm_exec *exec)
  {
      struct ttm_operation_ctx ctx = {
          .interruptible = true,
@@ -2546,7 +2546,7 @@ struct xe_bo *xe_bo_init_locked(struct xe_device *xe, struct xe_bo *bo,
      bo->tile = tile;
      bo->flags = flags;
      bo->cpu_caching = cpu_caching;
-    bo->numa_node = NUMA_NO_NODE;
+    bo->numa_node = numa_node;
      bo->ttm.base.funcs = &xe_gem_object_funcs;
      bo->ttm.priority = XE_BO_PRIORITY_NORMAL;
      INIT_LIST_HEAD(&bo->pinned_link);
@@ -2681,7 +2681,7 @@ __xe_bo_create_locked(struct xe_device *xe,
                struct xe_tile *tile, struct xe_vm *vm,
                size_t size, u64 start, u64 end,
                u16 cpu_caching, enum ttm_bo_type type, u32 flags,
-              u64 alignment, struct drm_exec *exec)
+              u64 alignment, int numa_node, struct drm_exec *exec)
  {
      struct xe_bo *bo = NULL;
      int err;
@@ -2706,7 +2706,7 @@ __xe_bo_create_locked(struct xe_device *xe,
                     vm && !xe_vm_in_fault_mode(vm) &&
                     flags & XE_BO_FLAG_USER ?
                     &vm->lru_bulk_move : NULL, size,
-                   cpu_caching, type, flags, NULL, exec);
+                   cpu_caching, type, flags, NULL, numa_node, exec);
      if (IS_ERR(bo))
          return bo;
  @@ -2781,13 +2781,13 @@ struct xe_bo *xe_bo_create_locked(struct xe_device *xe, struct xe_tile *tile,
                    struct drm_exec *exec)
  {
      return __xe_bo_create_locked(xe, tile, vm, size, 0, ~0ULL, 0, type,
-                     flags, 0, exec);
+                     flags, 0, NUMA_NO_NODE, exec);
  }
    static struct xe_bo *xe_bo_create_novm(struct xe_device *xe, struct xe_tile *tile,
                         size_t size, u16 cpu_caching,
                         enum ttm_bo_type type, u32 flags,
-                       u64 alignment, bool intr)
+                       u64 alignment, int numa_node, bool intr)
  {
      struct xe_validation_ctx ctx;
      struct drm_exec exec;
@@ -2797,7 +2797,8 @@ static struct xe_bo *xe_bo_create_novm(struct xe_device *xe, struct xe_tile *til       xe_validation_guard(&ctx, &xe->val, &exec, (struct xe_val_flags) {.interruptible = intr},
                  ret) {
          bo = __xe_bo_create_locked(xe, tile, NULL, size, 0, ~0ULL,
-                       cpu_caching, type, flags, alignment, &exec);
+                       cpu_caching, type, flags, alignment,
+                       numa_node, &exec);
          drm_exec_retry_on_contention(&exec);
          if (IS_ERR(bo)) {
              ret = PTR_ERR(bo);
@@ -2966,7 +2967,8 @@ static void xe_bo_debug_mixed_mode_cur_index_advance(struct xe_device *xe,
  struct xe_bo *xe_bo_create_user(struct xe_device *xe,
                  struct xe_vm *vm, size_t size,
                  u16 cpu_caching,
-                u32 flags, struct drm_exec *exec)
+                u32 flags, int numa_node,
+                struct drm_exec *exec)
  {
      struct xe_bo *bo;
      int err = 0;
@@ -2983,12 +2985,13 @@ struct xe_bo *xe_bo_create_user(struct xe_device *xe,
          xe_assert(xe, exec);
          bo = __xe_bo_create_locked(xe, NULL, vm, size, 0, ~0ULL,
                         cpu_caching, ttm_bo_type_device,
-                       flags, 0, exec);
+                       flags, 0, numa_node, exec);
          if (!IS_ERR(bo))
              xe_bo_unlock_vm_held(bo);
      } else {
          bo = xe_bo_create_novm(xe, NULL, size, cpu_caching,
-                       ttm_bo_type_device, flags, 0, true);
+                       ttm_bo_type_device, flags, 0,
+                       numa_node, true);
      }
        return bo;
@@ -3022,7 +3025,7 @@ struct xe_bo *xe_bo_create_pin_range_novm(struct xe_device *xe, struct xe_tile *         xe_validation_guard(&ctx, &xe->val, &exec, (struct xe_val_flags) {}, err) {
          bo = __xe_bo_create_locked(xe, tile, NULL, size, start, end,
-                       0, type, flags, 0, &exec);
+                       0, type, flags, 0, NUMA_NO_NODE, &exec);
          if (IS_ERR(bo)) {
              drm_exec_retry_on_contention(&exec);
              err = PTR_ERR(bo);
@@ -3061,7 +3064,7 @@ static struct xe_bo *xe_bo_create_pin_map_at_aligned(struct xe_device *xe,         bo = __xe_bo_create_locked(xe, tile, vm, size, start, end, 0, type,                      flags | XE_BO_FLAG_NEEDS_CPU_ACCESS | XE_BO_FLAG_PINNED,
-                   alignment, exec);
+                   alignment, NUMA_NO_NODE, exec);
      if (IS_ERR(bo))
          return bo;
  @@ -3604,10 +3607,15 @@ static int gem_create_user_ext_set_property(struct xe_device *xe,
      if (XE_IOCTL_DBG(xe, err))
          return -EFAULT;
  +    if (XE_IOCTL_DBG(xe, ext.pad))
+        return -EINVAL;
+
+    /* The NUMA node property is parsed and applied before BO creation. */
+    if (ext.property == DRM_XE_GEM_CREATE_SET_PROPERTY_NUMA_NODE)
+        return 0;
+
      if (XE_IOCTL_DBG(xe, ext.property >=
-             ARRAY_SIZE(gem_create_set_property_funcs)) ||
-        XE_IOCTL_DBG(xe, ext.pad) ||
-        XE_IOCTL_DBG(xe, ext.property != DRM_XE_GEM_CREATE_EXTENSION_SET_PROPERTY))
+             ARRAY_SIZE(gem_create_set_property_funcs)))
          return -EINVAL;
        idx = array_index_nospec(ext.property, ARRAY_SIZE(gem_create_set_property_funcs)); @@ -3658,6 +3666,56 @@ static int gem_create_user_extensions(struct xe_device *xe, struct xe_bo *bo,
      return 0;
  }
  +/*
+ * Parse a host NUMA node request from the extension chain. The node must be + * known before the BO is created, because the backing pages are allocated + * during creation. Returns the requested node in @numa_node (NUMA_NO_NODE if + * none or a negative value is given) or a negative errno on an invalid request.

Would it make sense to rework this and have something like:

struct gem_create_extension_data {
    u32 pxp_key_instance;
    int numa_node;
}

Which is parsed up front before the bo create? And then in some way we just pass that along to create_user() or manual bo init after? Otherwise here we look to be open coding a duplicate extension parser flow, rather than using gem_create_set_property_funcs? I think parsing before bo create make more sense anyway, like you do here?


Makes sense, will explore this.


+ */
+static int gem_create_parse_numa_node(struct xe_device *xe, u64 extensions,
+                      u32 bo_flags, int *numa_node)
+{
+    struct drm_xe_user_extension base;
+    struct drm_xe_ext_set_property ext;
+    unsigned int i = 0;
+
+    *numa_node = NUMA_NO_NODE;
+
+    while (extensions && i++ < MAX_USER_EXTENSIONS) {
+        u64 __user *address = u64_to_user_ptr(extensions);
+        s64 nid;
+
+        if (XE_IOCTL_DBG(xe, copy_from_user(&base, address, sizeof(base))))
+            return -EFAULT;
+
+        if (base.name != DRM_XE_GEM_CREATE_EXTENSION_SET_PROPERTY)
+            goto next;
+
+        if (XE_IOCTL_DBG(xe, copy_from_user(&ext, address, sizeof(ext))))
+            return -EFAULT;
+
+        if (ext.property != DRM_XE_GEM_CREATE_SET_PROPERTY_NUMA_NODE)
+            goto next;
+
+        nid = (s64)ext.value;
+        *numa_node = NUMA_NO_NODE;
+        if (nid < 0)
+            goto next;
+
+        if (XE_IOCTL_DBG(xe, nid >= MAX_NUMNODES || !node_online(nid)))

I think needs nospec dance for nid?


sure



+            return -EINVAL;
+
+        if (XE_IOCTL_DBG(xe, !(bo_flags & XE_BO_FLAG_SYSTEM)))
+            return -EINVAL;
+
+        *numa_node = nid;
+next:
+        extensions = base.next_extension;
+    }
+
+    return 0;
+}
+
  int xe_gem_create_ioctl(struct drm_device *dev, void *data,
              struct drm_file *file)
  {
@@ -3669,6 +3727,7 @@ int xe_gem_create_ioctl(struct drm_device *dev, void *data,
      struct xe_vm *vm = NULL;
      struct xe_bo *bo;
      unsigned int bo_flags;
+    int numa_node;
      u32 handle;
      int err;
  @@ -3744,6 +3803,11 @@ int xe_gem_create_ioctl(struct drm_device *dev, void *data,
               args->cpu_caching == DRM_XE_GEM_CPU_CACHING_WB))
          return -EINVAL;
  +    err = gem_create_parse_numa_node(xe, args->extensions, bo_flags,
+                     &numa_node);
+    if (err)
+        return err;
+
      if (args->vm_id) {
          vm = xe_vm_lookup(xef, args->vm_id);
          if (XE_IOCTL_DBG(xe, !vm))
@@ -3760,7 +3824,7 @@ int xe_gem_create_ioctl(struct drm_device *dev, void *data,
                  break;
          }
          bo = xe_bo_create_user(xe, vm, args->size, args->cpu_caching,
-                       bo_flags, &exec);
+                       bo_flags, numa_node, &exec);
          drm_exec_retry_on_contention(&exec);
          if (IS_ERR(bo)) {
              err = PTR_ERR(bo);
@@ -4231,7 +4295,7 @@ int xe_bo_dumb_create(struct drm_file *file_priv,
                     DRM_XE_GEM_CPU_CACHING_WC,
XE_BO_FLAG_VRAM_IF_DGFX(xe_device_get_root_tile(xe)) |
                     XE_BO_FLAG_FORCE_WC |
-                   XE_BO_FLAG_NEEDS_CPU_ACCESS, NULL);
+                   XE_BO_FLAG_NEEDS_CPU_ACCESS, NUMA_NO_NODE, NULL);
      if (IS_ERR(bo))
          return PTR_ERR(bo);
  diff --git a/drivers/gpu/drm/xe/xe_bo.h b/drivers/gpu/drm/xe/xe_bo.h
index 77b3a02e9b8e..225c6a8676a6 100644
--- a/drivers/gpu/drm/xe/xe_bo.h
+++ b/drivers/gpu/drm/xe/xe_bo.h
@@ -122,13 +122,14 @@ struct xe_bo *xe_bo_init_locked(struct xe_device *xe, struct xe_bo *bo,
                  struct ttm_lru_bulk_move *bulk, size_t size,
                  u16 cpu_caching, enum ttm_bo_type type,
                  u32 flags, struct dma_buf *dma_buf,
-                struct drm_exec *exec);
+                int numa_node, struct drm_exec *exec);
  struct xe_bo *xe_bo_create_locked(struct xe_device *xe, struct xe_tile *tile,
                    struct xe_vm *vm, size_t size,
                    enum ttm_bo_type type, u32 flags,
                    struct drm_exec *exec);
  struct xe_bo *xe_bo_create_user(struct xe_device *xe, struct xe_vm *vm, size_t size,
-                u16 cpu_caching, u32 flags, struct drm_exec *exec);
+                u16 cpu_caching, u32 flags, int numa_node,
+                struct drm_exec *exec);
  struct xe_bo *xe_bo_create_pin_map(struct xe_device *xe, struct xe_tile *tile,
                     struct xe_vm *vm, size_t size,
                     enum ttm_bo_type type, u32 flags,
diff --git a/drivers/gpu/drm/xe/xe_dma_buf.c b/drivers/gpu/drm/xe/xe_dma_buf.c
index 5bf13dc4b61d..b5351310a07b 100644
--- a/drivers/gpu/drm/xe/xe_dma_buf.c
+++ b/drivers/gpu/drm/xe/xe_dma_buf.c
@@ -343,7 +343,8 @@ xe_dma_buf_create_obj(struct drm_device *dev, struct dma_buf *dma_buf)             bo = xe_bo_init_locked(xe, NULL, NULL, resv, NULL, dma_buf->size,
                         0, /* Will require 1way or 2way for vm_bind */
-                       ttm_bo_type_sg, XE_BO_FLAG_SYSTEM, dma_buf, &exec);
+                       ttm_bo_type_sg, XE_BO_FLAG_SYSTEM, dma_buf,
+                       NUMA_NO_NODE, &exec);
          drm_exec_retry_on_contention(&exec);
          if (IS_ERR(bo)) {
              ret = PTR_ERR(bo);

Reply via email to