Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/xe/uapi: Add UAPI for querying VMA count and memory attributes

Introduce the DRM_IOCTL_XE_VM_QUERY_MEMORY_RANGE_ATTRS ioctl to allow
userspace to query memory attributes of VMAs within a user specified
virtual address range.

Userspace first calls the ioctl with num_mem_ranges = 0,
sizeof_mem_ranges_attr = 0 and vector_of_vma_mem_attr = NULL to retrieve
the number of memory ranges (vmas) and size of each memory range attribute.
Then, it allocates a buffer of that size and calls the ioctl again to fill
the buffer with memory range attributes.

This two-step interface allows userspace to first query the required
buffer size, then retrieve detailed attributes efficiently.

v2 (Matthew Brost)
- Use same ioctl to overload functionality

v3
- Add kernel-doc

v4
- Make uapi future proof by passing struct size (Matthew Brost)
- make lock interruptible (Matthew Brost)
- set reserved bits to zero (Matthew Brost)
- s/__copy_to_user/copy_to_user (Matthew Brost)
- Avod using VMA term in uapi (Thomas)
- xe_vm_put(vm) is missing (Shuicheng)

v5
- Nits
- Fix kernel-doc

Cc: Matthew Brost <matthew.brost@intel.com>
Cc: Shuicheng Lin <shuicheng.lin@intel.com>
Cc: Thomas Hellström <thomas.hellstrom@linux.intel.com>
Reviewed-by: Matthew Brost <matthew.brost@intel.com>
Link: https://lore.kernel.org/r/20250821173104.3030148-21-himal.prasad.ghimiray@intel.com
Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>

+245 -1
+2
drivers/gpu/drm/xe/xe_device.c
··· 204 204 DRM_RENDER_ALLOW), 205 205 DRM_IOCTL_DEF_DRV(XE_OBSERVATION, xe_observation_ioctl, DRM_RENDER_ALLOW), 206 206 DRM_IOCTL_DEF_DRV(XE_MADVISE, xe_vm_madvise_ioctl, DRM_RENDER_ALLOW), 207 + DRM_IOCTL_DEF_DRV(XE_VM_QUERY_MEM_RANGE_ATTRS, xe_vm_query_vmas_attrs_ioctl, 208 + DRM_RENDER_ALLOW), 207 209 }; 208 210 209 211 static long xe_drm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+102
drivers/gpu/drm/xe/xe_vm.c
··· 2196 2196 return err; 2197 2197 } 2198 2198 2199 + static int xe_vm_query_vmas(struct xe_vm *vm, u64 start, u64 end) 2200 + { 2201 + struct drm_gpuva *gpuva; 2202 + u32 num_vmas = 0; 2203 + 2204 + lockdep_assert_held(&vm->lock); 2205 + drm_gpuvm_for_each_va_range(gpuva, &vm->gpuvm, start, end) 2206 + num_vmas++; 2207 + 2208 + return num_vmas; 2209 + } 2210 + 2211 + static int get_mem_attrs(struct xe_vm *vm, u32 *num_vmas, u64 start, 2212 + u64 end, struct drm_xe_mem_range_attr *attrs) 2213 + { 2214 + struct drm_gpuva *gpuva; 2215 + int i = 0; 2216 + 2217 + lockdep_assert_held(&vm->lock); 2218 + 2219 + drm_gpuvm_for_each_va_range(gpuva, &vm->gpuvm, start, end) { 2220 + struct xe_vma *vma = gpuva_to_vma(gpuva); 2221 + 2222 + if (i == *num_vmas) 2223 + return -ENOSPC; 2224 + 2225 + attrs[i].start = xe_vma_start(vma); 2226 + attrs[i].end = xe_vma_end(vma); 2227 + attrs[i].atomic.val = vma->attr.atomic_access; 2228 + attrs[i].pat_index.val = vma->attr.pat_index; 2229 + attrs[i].preferred_mem_loc.devmem_fd = vma->attr.preferred_loc.devmem_fd; 2230 + attrs[i].preferred_mem_loc.migration_policy = 2231 + vma->attr.preferred_loc.migration_policy; 2232 + 2233 + i++; 2234 + } 2235 + 2236 + *num_vmas = i; 2237 + return 0; 2238 + } 2239 + 2240 + int xe_vm_query_vmas_attrs_ioctl(struct drm_device *dev, void *data, struct drm_file *file) 2241 + { 2242 + struct xe_device *xe = to_xe_device(dev); 2243 + struct xe_file *xef = to_xe_file(file); 2244 + struct drm_xe_mem_range_attr *mem_attrs; 2245 + struct drm_xe_vm_query_mem_range_attr *args = data; 2246 + u64 __user *attrs_user = u64_to_user_ptr(args->vector_of_mem_attr); 2247 + struct xe_vm *vm; 2248 + int err = 0; 2249 + 2250 + if (XE_IOCTL_DBG(xe, 2251 + ((args->num_mem_ranges == 0 && 2252 + (attrs_user || args->sizeof_mem_range_attr != 0)) || 2253 + (args->num_mem_ranges > 0 && 2254 + (!attrs_user || 2255 + args->sizeof_mem_range_attr != 2256 + sizeof(struct drm_xe_mem_range_attr)))))) 2257 + return -EINVAL; 2258 + 2259 + vm = xe_vm_lookup(xef, args->vm_id); 2260 + if (XE_IOCTL_DBG(xe, !vm)) 2261 + return -EINVAL; 2262 + 2263 + err = down_read_interruptible(&vm->lock); 2264 + if (err) 2265 + goto put_vm; 2266 + 2267 + attrs_user = u64_to_user_ptr(args->vector_of_mem_attr); 2268 + 2269 + if (args->num_mem_ranges == 0 && !attrs_user) { 2270 + args->num_mem_ranges = xe_vm_query_vmas(vm, args->start, args->start + args->range); 2271 + args->sizeof_mem_range_attr = sizeof(struct drm_xe_mem_range_attr); 2272 + goto unlock_vm; 2273 + } 2274 + 2275 + mem_attrs = kvmalloc_array(args->num_mem_ranges, args->sizeof_mem_range_attr, 2276 + GFP_KERNEL | __GFP_ACCOUNT | 2277 + __GFP_RETRY_MAYFAIL | __GFP_NOWARN); 2278 + if (!mem_attrs) { 2279 + err = args->num_mem_ranges > 1 ? -ENOBUFS : -ENOMEM; 2280 + goto unlock_vm; 2281 + } 2282 + 2283 + memset(mem_attrs, 0, args->num_mem_ranges * args->sizeof_mem_range_attr); 2284 + err = get_mem_attrs(vm, &args->num_mem_ranges, args->start, 2285 + args->start + args->range, mem_attrs); 2286 + if (err) 2287 + goto free_mem_attrs; 2288 + 2289 + err = copy_to_user(attrs_user, mem_attrs, 2290 + args->sizeof_mem_range_attr * args->num_mem_ranges); 2291 + 2292 + free_mem_attrs: 2293 + kvfree(mem_attrs); 2294 + unlock_vm: 2295 + up_read(&vm->lock); 2296 + put_vm: 2297 + xe_vm_put(vm); 2298 + return err; 2299 + } 2300 + 2199 2301 static bool vma_matches(struct xe_vma *vma, u64 page_addr) 2200 2302 { 2201 2303 if (page_addr > xe_vma_end(vma) - 1 ||
+1 -1
drivers/gpu/drm/xe/xe_vm.h
··· 199 199 struct drm_file *file); 200 200 int xe_vm_bind_ioctl(struct drm_device *dev, void *data, 201 201 struct drm_file *file); 202 - 202 + int xe_vm_query_vmas_attrs_ioctl(struct drm_device *dev, void *data, struct drm_file *file); 203 203 void xe_vm_close_and_put(struct xe_vm *vm); 204 204 205 205 static inline bool xe_vm_in_fault_mode(struct xe_vm *vm)
+140
include/uapi/drm/xe_drm.h
··· 82 82 * - &DRM_IOCTL_XE_WAIT_USER_FENCE 83 83 * - &DRM_IOCTL_XE_OBSERVATION 84 84 * - &DRM_IOCTL_XE_MADVISE 85 + * - &DRM_IOCTL_XE_VM_QUERY_MEM_RANGE_ATTRS 85 86 */ 86 87 87 88 /* ··· 105 104 #define DRM_XE_WAIT_USER_FENCE 0x0a 106 105 #define DRM_XE_OBSERVATION 0x0b 107 106 #define DRM_XE_MADVISE 0x0c 107 + #define DRM_XE_VM_QUERY_MEM_RANGE_ATTRS 0x0d 108 108 109 109 /* Must be kept compact -- no holes */ 110 110 ··· 122 120 #define DRM_IOCTL_XE_WAIT_USER_FENCE DRM_IOWR(DRM_COMMAND_BASE + DRM_XE_WAIT_USER_FENCE, struct drm_xe_wait_user_fence) 123 121 #define DRM_IOCTL_XE_OBSERVATION DRM_IOW(DRM_COMMAND_BASE + DRM_XE_OBSERVATION, struct drm_xe_observation_param) 124 122 #define DRM_IOCTL_XE_MADVISE DRM_IOW(DRM_COMMAND_BASE + DRM_XE_MADVISE, struct drm_xe_madvise) 123 + #define DRM_IOCTL_XE_VM_QUERY_MEM_RANGE_ATTRS DRM_IOWR(DRM_COMMAND_BASE + DRM_XE_VM_QUERY_MEM_RANGE_ATTRS, struct drm_xe_vm_query_mem_range_attr) 125 124 126 125 /** 127 126 * DOC: Xe IOCTL Extensions ··· 2114 2111 2115 2112 /** @reserved: Reserved */ 2116 2113 __u64 reserved[2]; 2114 + }; 2115 + 2116 + /** 2117 + * struct drm_xe_mem_range_attr - Output of &DRM_IOCTL_XE_VM_QUERY_MEM_RANGES_ATTRS 2118 + * 2119 + * This structure is provided by userspace and filled by KMD in response to the 2120 + * DRM_IOCTL_XE_VM_QUERY_MEM_RANGES_ATTRS ioctl. It describes memory attributes of 2121 + * a memory ranges within a user specified address range in a VM. 2122 + * 2123 + * The structure includes information such as atomic access policy, 2124 + * page attribute table (PAT) index, and preferred memory location. 2125 + * Userspace allocates an array of these structures and passes a pointer to the 2126 + * ioctl to retrieve attributes for each memory ranges 2127 + * 2128 + * @extensions: Pointer to the first extension struct, if any 2129 + * @start: Start address of the memory range 2130 + * @end: End address of the virtual memory range 2131 + * 2132 + */ 2133 + struct drm_xe_mem_range_attr { 2134 + /** @extensions: Pointer to the first extension struct, if any */ 2135 + __u64 extensions; 2136 + 2137 + /** @start: start of the memory range */ 2138 + __u64 start; 2139 + 2140 + /** @end: end of the memory range */ 2141 + __u64 end; 2142 + 2143 + /** @preferred_mem_loc: preferred memory location */ 2144 + struct { 2145 + /** @preferred_mem_loc.devmem_fd: fd for preferred loc */ 2146 + __u32 devmem_fd; 2147 + 2148 + /** @preferred_mem_loc.migration_policy: Page migration policy */ 2149 + __u32 migration_policy; 2150 + } preferred_mem_loc; 2151 + 2152 + /** @atomic: Atomic access policy */ 2153 + struct { 2154 + /** @atomic.val: atomic attribute */ 2155 + __u32 val; 2156 + 2157 + /** @atomic.reserved: Reserved */ 2158 + __u32 reserved; 2159 + } atomic; 2160 + 2161 + /** @pat_index: Page attribute table index */ 2162 + struct { 2163 + /** @pat_index.val: PAT index */ 2164 + __u32 val; 2165 + 2166 + /** @pat_index.reserved: Reserved */ 2167 + __u32 reserved; 2168 + } pat_index; 2169 + 2170 + /** @reserved: Reserved */ 2171 + __u64 reserved[2]; 2172 + }; 2173 + 2174 + /** 2175 + * struct drm_xe_vm_query_mem_range_attr - Input of &DRM_IOCTL_XE_VM_QUERY_MEM_ATTRIBUTES 2176 + * 2177 + * This structure is used to query memory attributes of memory regions 2178 + * within a user specified address range in a VM. It provides detailed 2179 + * information about each memory range, including atomic access policy, 2180 + * page attribute table (PAT) index, and preferred memory location. 2181 + * 2182 + * Userspace first calls the ioctl with @num_mem_ranges = 0, 2183 + * @sizeof_mem_ranges_attr = 0 and @vector_of_vma_mem_attr = NULL to retrieve 2184 + * the number of memory regions and size of each memory range attribute. 2185 + * Then, it allocates a buffer of that size and calls the ioctl again to fill 2186 + * the buffer with memory range attributes. 2187 + * 2188 + * If second call fails with -ENOSPC, it means memory ranges changed between 2189 + * first call and now, retry IOCTL again with @num_mem_ranges = 0, 2190 + * @sizeof_mem_ranges_attr = 0 and @vector_of_vma_mem_attr = NULL followed by 2191 + * Second ioctl call. 2192 + * 2193 + * Example: 2194 + * 2195 + * .. code-block:: C 2196 + * struct drm_xe_vm_query_mem_range_attr query = { 2197 + * .vm_id = vm_id, 2198 + * .start = 0x100000, 2199 + * .range = 0x2000, 2200 + * }; 2201 + * 2202 + * // First ioctl call to get num of mem regions and sizeof each attribute 2203 + * ioctl(fd, DRM_IOCTL_XE_VM_QUERY_MEM_RANGE_ATTRS, &query); 2204 + * 2205 + * // Allocate buffer for the memory region attributes 2206 + * void *ptr = malloc(query.num_mem_ranges * query.sizeof_mem_range_attr); 2207 + * void *ptr_start = ptr; 2208 + * 2209 + * query.vector_of_mem_attr = (uintptr_t)ptr; 2210 + * 2211 + * // Second ioctl call to actually fill the memory attributes 2212 + * ioctl(fd, DRM_IOCTL_XE_VM_QUERY_MEM_RANGE_ATTRS, &query); 2213 + * 2214 + * // Iterate over the returned memory region attributes 2215 + * for (unsigned int i = 0; i < query.num_mem_ranges; ++i) { 2216 + * struct drm_xe_mem_range_attr *attr = (struct drm_xe_mem_range_attr *)ptr; 2217 + * 2218 + * // Do something with attr 2219 + * 2220 + * // Move pointer by one entry 2221 + * ptr += query.sizeof_mem_range_attr; 2222 + * } 2223 + * 2224 + * free(ptr_start); 2225 + */ 2226 + struct drm_xe_vm_query_mem_range_attr { 2227 + /** @extensions: Pointer to the first extension struct, if any */ 2228 + __u64 extensions; 2229 + 2230 + /** @vm_id: vm_id of the virtual range */ 2231 + __u32 vm_id; 2232 + 2233 + /** @num_mem_ranges: number of mem_ranges in range */ 2234 + __u32 num_mem_ranges; 2235 + 2236 + /** @start: start of the virtual address range */ 2237 + __u64 start; 2238 + 2239 + /** @range: size of the virtual address range */ 2240 + __u64 range; 2241 + 2242 + /** @sizeof_mem_range_attr: size of struct drm_xe_mem_range_attr */ 2243 + __u64 sizeof_mem_range_attr; 2244 + 2245 + /** @vector_of_mem_attr: userptr to array of struct drm_xe_mem_range_attr */ 2246 + __u64 vector_of_mem_attr; 2247 + 2248 + /** @reserved: Reserved */ 2249 + __u64 reserved[2]; 2250 + 2117 2251 }; 2118 2252 2119 2253 #if defined(__cplusplus)