• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2023 Intel Corporation
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20  * DEALINGS IN THE SOFTWARE.
21  */
22 
23 #include "xe/anv_queue.h"
24 
25 #include "anv_private.h"
26 
27 #include "common/xe/intel_engine.h"
28 #include "common/xe/intel_queue.h"
29 #include "common/intel_gem.h"
30 
31 #include "xe/anv_device.h"
32 
33 #include "drm-uapi/xe_drm.h"
34 #include "drm-uapi/gpu_scheduler.h"
35 
36 static enum drm_sched_priority
anv_vk_priority_to_drm_sched_priority(VkQueueGlobalPriorityKHR vk_priority)37 anv_vk_priority_to_drm_sched_priority(VkQueueGlobalPriorityKHR vk_priority)
38 {
39    switch (vk_priority) {
40    case VK_QUEUE_GLOBAL_PRIORITY_LOW_KHR:
41       return DRM_SCHED_PRIORITY_MIN;
42    case VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_KHR:
43       return DRM_SCHED_PRIORITY_NORMAL;
44    case VK_QUEUE_GLOBAL_PRIORITY_HIGH_KHR:
45       return DRM_SCHED_PRIORITY_HIGH;
46    default:
47       unreachable("Invalid priority");
48       return DRM_SCHED_PRIORITY_MIN;
49    }
50 }
51 
52 static void
destroy_engine(struct anv_device * device,uint32_t exec_queue_id)53 destroy_engine(struct anv_device *device, uint32_t exec_queue_id)
54 {
55    struct drm_xe_exec_queue_destroy destroy = {
56       .exec_queue_id = exec_queue_id,
57    };
58 
59    intel_ioctl(device->fd, DRM_IOCTL_XE_EXEC_QUEUE_DESTROY, &destroy);
60 }
61 
62 static VkResult
create_engine(struct anv_device * device,struct anv_queue * queue,const VkDeviceQueueCreateInfo * pCreateInfo,bool create_companion_rcs_engine)63 create_engine(struct anv_device *device,
64               struct anv_queue *queue,
65               const VkDeviceQueueCreateInfo *pCreateInfo,
66               bool create_companion_rcs_engine)
67 {
68    struct anv_physical_device *physical = device->physical;
69    uint32_t queue_family_index =
70       create_companion_rcs_engine ?
71       anv_get_first_render_queue_index(physical) :
72       pCreateInfo->queueFamilyIndex;
73    struct anv_queue_family *queue_family =
74       &physical->queue.families[queue_family_index];
75    const struct intel_query_engine_info *engines = physical->engine_info;
76    struct drm_xe_engine_class_instance *instances;
77    const VkDeviceQueueGlobalPriorityCreateInfoKHR *queue_priority =
78       vk_find_struct_const(pCreateInfo->pNext,
79                            DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_KHR);
80    const VkQueueGlobalPriorityKHR priority = queue_priority ?
81                                              queue_priority->globalPriority :
82                                              VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_KHR;
83 
84    /* As per spec, the driver implementation may deny requests to acquire
85     * a priority above the default priority (MEDIUM) if the caller does not
86     * have sufficient privileges. In this scenario VK_ERROR_NOT_PERMITTED_KHR
87     * is returned.
88     */
89    if (physical->max_context_priority >= VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_KHR) {
90       if (priority > physical->max_context_priority)
91          return vk_error(device, VK_ERROR_NOT_PERMITTED_KHR);
92    }
93 
94    instances = vk_alloc(&device->vk.alloc,
95                         sizeof(*instances) * queue_family->queueCount, 8,
96                         VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
97    if (!instances)
98       return VK_ERROR_OUT_OF_HOST_MEMORY;
99 
100    /* Build a list of all compatible HW engines */
101    uint32_t count = 0;
102    for (uint32_t i = 0; i < engines->num_engines; i++) {
103       const struct intel_engine_class_instance engine = engines->engines[i];
104       if (engine.engine_class != queue_family->engine_class)
105          continue;
106 
107       instances[count].engine_class = intel_engine_class_to_xe(engine.engine_class);
108       instances[count].engine_instance = engine.engine_instance;
109       instances[count++].gt_id = engine.gt_id;
110    }
111 
112    assert(device->vm_id != 0);
113    struct drm_xe_ext_set_property ext = {
114       .base.name = DRM_XE_EXEC_QUEUE_EXTENSION_SET_PROPERTY,
115       .property = DRM_XE_EXEC_QUEUE_SET_PROPERTY_PRIORITY,
116       .value = anv_vk_priority_to_drm_sched_priority(priority),
117    };
118    struct drm_xe_exec_queue_create create = {
119          /* Allows KMD to pick one of those engines for the submission queue */
120          .instances = (uintptr_t)instances,
121          .vm_id = device->vm_id,
122          .width = 1,
123          .num_placements = count,
124          .extensions = (uintptr_t)&ext,
125    };
126    int ret = intel_ioctl(device->fd, DRM_IOCTL_XE_EXEC_QUEUE_CREATE, &create);
127    vk_free(&device->vk.alloc, instances);
128    if (ret)
129       return vk_errorf(device, VK_ERROR_UNKNOWN, "Unable to create exec queue");
130 
131    if (create_companion_rcs_engine)
132       queue->companion_rcs_id = create.exec_queue_id;
133    else
134       queue->exec_queue_id = create.exec_queue_id;
135 
136    if (!create_companion_rcs_engine &&
137        queue_family->queueFlags & VK_QUEUE_SPARSE_BINDING_BIT) {
138       struct drm_xe_engine_class_instance bind_instance = {
139          .engine_class = DRM_XE_ENGINE_CLASS_VM_BIND,
140       };
141       create.num_placements = 1;
142       create.instances = (uintptr_t)&bind_instance;
143       create.extensions = 0;
144       ret = intel_ioctl(device->fd, DRM_IOCTL_XE_EXEC_QUEUE_CREATE, &create);
145       if (ret) {
146          destroy_engine(device, queue->exec_queue_id);
147          return vk_errorf(device, VK_ERROR_UNKNOWN, "Unable to create bind queue");
148       }
149       queue->bind_queue_id = create.exec_queue_id;
150    }
151 
152    return VK_SUCCESS;
153 }
154 
155 VkResult
anv_xe_create_engine(struct anv_device * device,struct anv_queue * queue,const VkDeviceQueueCreateInfo * pCreateInfo)156 anv_xe_create_engine(struct anv_device *device,
157                      struct anv_queue *queue,
158                      const VkDeviceQueueCreateInfo *pCreateInfo)
159 {
160    VkResult result = create_engine(device, queue, pCreateInfo,
161                                    false /* create_companion_rcs_engine */);
162 
163    if (result != VK_SUCCESS)
164       return result;
165 
166    if (queue->family->engine_class == INTEL_ENGINE_CLASS_COPY ||
167        queue->family->engine_class == INTEL_ENGINE_CLASS_COMPUTE) {
168       result = create_engine(device, queue, pCreateInfo,
169                              true /* create_companion_rcs_engine */);
170    }
171 
172    return result;
173 }
174 
175 /*
176  * Wait for all previous DRM_IOCTL_XE_EXEC calls over the
177  * drm_xe_exec_queue to complete.
178  **/
179 int
anv_xe_wait_exec_queue_idle(struct anv_device * device,uint32_t exec_queue_id)180 anv_xe_wait_exec_queue_idle(struct anv_device *device, uint32_t exec_queue_id)
181 {
182    struct drm_syncobj_wait syncobj_wait = {
183       .count_handles = 1,
184       .timeout_nsec = INT64_MAX,
185    };
186    uint32_t syncobj;
187    int ret = xe_queue_get_syncobj_for_idle(device->fd, exec_queue_id, &syncobj);
188 
189    if (ret) {
190       assert(ret == -ECANCELED);
191       return ret;
192    }
193 
194    syncobj_wait.handles = (uintptr_t)&syncobj;
195    ret = intel_ioctl(device->fd, DRM_IOCTL_SYNCOBJ_WAIT, &syncobj_wait);
196    assert(ret == 0);
197 
198    struct drm_syncobj_destroy syncobj_destroy = {
199       .handle = syncobj,
200    };
201    ret = intel_ioctl(device->fd, DRM_IOCTL_SYNCOBJ_DESTROY, &syncobj_destroy);
202    assert(ret == 0);
203 
204    return ret;
205 }
206 
207 static void
bind_engine_idle(struct anv_device * device,uint32_t exec_queue_id)208 bind_engine_idle(struct anv_device *device, uint32_t exec_queue_id)
209 {
210    struct drm_syncobj_create syncobj_create = {};
211    struct drm_xe_sync xe_sync = {
212       .type = DRM_XE_SYNC_TYPE_SYNCOBJ,
213       .flags = DRM_XE_SYNC_FLAG_SIGNAL,
214    };
215    struct drm_xe_vm_bind args = {
216       .vm_id = device->vm_id,
217       .num_binds = 0,
218       .exec_queue_id = exec_queue_id,
219       .bind = {},
220       .num_syncs = 1,
221       .syncs = (uintptr_t)&xe_sync,
222    };
223    struct drm_syncobj_destroy syncobj_destroy = {};
224    int ret = intel_ioctl(device->fd, DRM_IOCTL_SYNCOBJ_CREATE, &syncobj_create);
225 
226    assert(ret == 0);
227 
228    xe_sync.handle = syncobj_create.handle;
229    /* Using the special args.num_binds == 0 handling to get syncobj
230     * signaled when the last DRM_IOCTL_XE_VM_BIND is completed.
231     */
232    ret = intel_ioctl(device->fd, DRM_IOCTL_XE_VM_BIND, &args);
233    if (ret) {
234       /* exec_queue could have been banned, that is why it is being destroyed
235        * so no assert() here
236        */
237       goto error_bind;
238    }
239 
240    struct drm_syncobj_wait syncobj_wait = {
241       .count_handles = 1,
242       .timeout_nsec = INT64_MAX,
243       .handles = (uintptr_t)&syncobj_create.handle,
244    };
245    ret = intel_ioctl(device->fd, DRM_IOCTL_SYNCOBJ_WAIT, &syncobj_wait);
246    assert(ret == 0);
247 
248 error_bind:
249    syncobj_destroy.handle = syncobj_create.handle,
250    intel_ioctl(device->fd, DRM_IOCTL_SYNCOBJ_DESTROY, &syncobj_destroy);
251 }
252 
253 void
anv_xe_destroy_engine(struct anv_device * device,struct anv_queue * queue)254 anv_xe_destroy_engine(struct anv_device *device, struct anv_queue *queue)
255 {
256    /* Application could submit a workload and before it is done, destroy the
257     * queue causing job timeouts in Xe KMD as it don't have permanent
258     * exec queues.
259     */
260    anv_xe_wait_exec_queue_idle(device, queue->exec_queue_id);
261    destroy_engine(device, queue->exec_queue_id);
262 
263    if (queue->companion_rcs_id != 0) {
264       anv_xe_wait_exec_queue_idle(device, queue->companion_rcs_id);
265       destroy_engine(device, queue->companion_rcs_id);
266    }
267    if (queue->bind_queue_id != 0) {
268       bind_engine_idle(device, queue->bind_queue_id);
269       destroy_engine(device, queue->bind_queue_id);
270    }
271 }
272