mirror of
https://github.com/torvalds/linux.git
synced 2026-05-28 17:13:52 +02:00
drm fixes for 7.1-rc4
core: - fix for the fix for the handle change race. ttm: - avoid infinite loop in swap out - avoid infinite loop in BO shrinking - convert -EAGAIN from dmem_cgroup_try_charge to -ENOSPC bridge: - imx8qxp-pxl2dpi: avoid ERR_PTR with device_node cleanup i915: - Skip __i915_request_skip() for already signaled requests - Fix VSC dynamic range signaling for RGB formats [dp] xe: - Madvise fix around purgeability tracking - Restore engine mask for specific blitter style - Couple UAF fixes - Drop unused ggtt_balloon field amdgpu: - Userq fixes - DCN 3.2 fix - RAS fix - GC 12 fix gma500: - oaktrail_lvds: fix i2c handling loongson: - use managed cleanup for connector polling panfrost: - handle results from reservation locking correctly qaic: - check for integer overflows in mmap logic rocket: - handle results from reservation locking correctly -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEEKbZHaGwW9KfbeusDHTzWXnEhr4FAmoHsAcACgkQDHTzWXnE hr4J9Q/+IgTlh6a9M3U5uW8ndgWnKGkrNvww8R7V02eaosbDRnDMUrEvKLwMtjri qN5RokRviiWXkn6a1RI6X/rgLpKHSN5IXSENFwkGZfYLv7b14v3il+6xbGbhvkd5 JhZHxXg5GW/o2bx/CZzHrgfk0DtL8oiO+08m3gtp47/KHmZJtghKsepXUnRPIFms iujS0jsGbsKfBMgtWG/en+iJM2bEnBOElOMGDqJvmz+5id81OgKbq7ceHXvN4p7O 2AZjiL4IvQkpSyX9cRGlLQS/zpl59Ee1LEb8zptUAZx3d1taRjf7dkEPHMCVKFjz sLsbNpask6z/2wJlveAfbdkQ7ImmBtWioOAkVmulYxWaUfG829piC4KZtMtXThfc acyr2hdTPvTjt/iaaRqoCipdAw/YNCo6IsBpx+r8JynVVmFxh+PFFkdwGJ3piz6F LiZgPmn+NFnmL8cCHuOqtit1ySMai3mbnuqyctGxRi4XwG2X5otl8upyPXXirnUM AxLPzmXXtfk3X8xGRwtO+Cp9mrCuesVab9FOKTs78oUb1jTJYG0oBj6FmjNUgniz VxpqiAG0xTyRVVrrnAvbFscbsV8A5k4tGCFAdGr4LhbNfvir57W9LUU0Bv+ZNwnz 2dojMfuUmInEbXSjqWbkRAmsUa3EO0JLp4u3ousDLrqrFMD6fmU= =eHJD -----END PGP SIGNATURE----- Merge tag 'drm-fixes-2026-05-16' of https://gitlab.freedesktop.org/drm/kernel Pull drm fixes from Dave Airlie: "Weekly fixes pull, small and all over fixes, mostly xe and amdgpu, with some ttm and a core fix for the handle change pain. core: - fix for the fix for the handle change race ttm: - avoid infinite loop in swap out - avoid infinite loop in BO shrinking - convert -EAGAIN from dmem_cgroup_try_charge to -ENOSPC bridge: - imx8qxp-pxl2dpi: avoid ERR_PTR with device_node cleanup i915: - Skip __i915_request_skip() for already signaled requests - Fix VSC dynamic range signaling for RGB formats [dp] xe: - Madvise fix around purgeability tracking - Restore engine mask for specific blitter style - Couple UAF fixes - Drop unused ggtt_balloon field amdgpu: - Userq fixes - DCN 3.2 fix - RAS fix - GC 12 fix gma500: - oaktrail_lvds: fix i2c handling loongson: - use managed cleanup for connector polling panfrost: - handle results from reservation locking correctly qaic: - check for integer overflows in mmap logic rocket: - handle results from reservation locking correctly" * tag 'drm-fixes-2026-05-16' of https://gitlab.freedesktop.org/drm/kernel: (26 commits) drm: Replace old pointer to new idr drm/loongson: Use managed KMS polling drm/ttm: Fix ttm_bo_shrink() infinite LRU walk on backup failure drm/ttm: Convert -EAGAIN from dmem_cgroup_try_charge to -ENOSPC drm/gma500/oaktrail_lvds: fix i2c adapter leaks on init drm/gma500/oaktrail_lvds: fix hang on init failure drm/gma500/oaktrail_hdmi: fix i2c adapter leak on setup drm/xe: Drop unused ggtt_balloon field accel/qaic: Add overflow check to remap_pfn_range during mmap drm/i915/dp: Fix VSC dynamic range signaling for RGB formats drm/i915: skip __i915_request_skip() for already signaled requests drm/bridge: imx8qxp-pxl2dpi: avoid ERR_PTR with device_node cleanup drm/amdgpu/gfx_v12_0: set gfx.rs64_enable from PFP header on GFX12 drm/amd/ras: Fix CPER ring debugfs read overflow drm/amd/display: Wrap DCN32 phantom-plane allocation in DC_RUN_WITH_PREEMPTION_ENABLED drm/amdgpu: fix userq hang detection and reset drm/amdgpu: remove almost all calls to amdgpu_userq_detect_and_reset_queues drm/amdgpu: rework amdgpu_userq_signal_ioctl v3 drm/amdgpu: remove deadlocks from amdgpu_userq_pre_reset drm/xe/dma-buf: fix UAF with retry loop ...
This commit is contained in:
commit
6916d5703d
|
|
@ -606,8 +606,11 @@ static const struct vm_operations_struct drm_vm_ops = {
|
|||
static int qaic_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
|
||||
{
|
||||
struct qaic_bo *bo = to_qaic_bo(obj);
|
||||
unsigned long remap_start;
|
||||
unsigned long offset = 0;
|
||||
unsigned long remap_end;
|
||||
struct scatterlist *sg;
|
||||
unsigned long length;
|
||||
int ret = 0;
|
||||
|
||||
if (drm_gem_is_imported(obj))
|
||||
|
|
@ -615,11 +618,27 @@ static int qaic_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struc
|
|||
|
||||
for (sg = bo->sgt->sgl; sg; sg = sg_next(sg)) {
|
||||
if (sg_page(sg)) {
|
||||
/* if sg is too large for the VMA, so truncate it to fit */
|
||||
if (check_add_overflow(vma->vm_start, offset, &remap_start))
|
||||
return -EINVAL;
|
||||
if (check_add_overflow(remap_start, sg->length, &remap_end))
|
||||
return -EINVAL;
|
||||
|
||||
if (remap_end > vma->vm_end) {
|
||||
if (check_sub_overflow(vma->vm_end, remap_start, &length))
|
||||
return -EINVAL;
|
||||
} else {
|
||||
length = sg->length;
|
||||
}
|
||||
|
||||
if (length == 0)
|
||||
goto out;
|
||||
|
||||
ret = remap_pfn_range(vma, vma->vm_start + offset, page_to_pfn(sg_page(sg)),
|
||||
sg->length, vma->vm_page_prot);
|
||||
length, vma->vm_page_prot);
|
||||
if (ret)
|
||||
goto out;
|
||||
offset += sg->length;
|
||||
offset += length;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -145,6 +145,8 @@ int rocket_ioctl_prep_bo(struct drm_device *dev, void *data, struct drm_file *fi
|
|||
ret = dma_resv_wait_timeout(gem_obj->resv, DMA_RESV_USAGE_WRITE, true, timeout);
|
||||
if (!ret)
|
||||
ret = timeout ? -ETIMEDOUT : -EBUSY;
|
||||
else if (ret > 0)
|
||||
ret = 0;
|
||||
|
||||
shmem_obj = &to_rocket_bo(gem_obj)->base;
|
||||
|
||||
|
|
|
|||
|
|
@ -552,8 +552,9 @@ static ssize_t amdgpu_debugfs_ring_read(struct file *f, char __user *buf,
|
|||
size_t size, loff_t *pos)
|
||||
{
|
||||
struct amdgpu_ring *ring = file_inode(f)->i_private;
|
||||
uint32_t value, result, early[3];
|
||||
u32 value, result, early[3] = { 0 };
|
||||
uint64_t p;
|
||||
u32 avail_dw, start_dw, read_dw;
|
||||
loff_t i;
|
||||
int r;
|
||||
|
||||
|
|
@ -565,10 +566,10 @@ static ssize_t amdgpu_debugfs_ring_read(struct file *f, char __user *buf,
|
|||
|
||||
result = 0;
|
||||
|
||||
if (*pos < 12) {
|
||||
if (ring->funcs->type == AMDGPU_RING_TYPE_CPER)
|
||||
mutex_lock(&ring->adev->cper.ring_lock);
|
||||
if (ring->funcs->type == AMDGPU_RING_TYPE_CPER)
|
||||
mutex_lock(&ring->adev->cper.ring_lock);
|
||||
|
||||
if (*pos < 12) {
|
||||
early[0] = amdgpu_ring_get_rptr(ring) & ring->buf_mask;
|
||||
early[1] = amdgpu_ring_get_wptr(ring) & ring->buf_mask;
|
||||
early[2] = ring->wptr & ring->buf_mask;
|
||||
|
|
@ -600,13 +601,24 @@ static ssize_t amdgpu_debugfs_ring_read(struct file *f, char __user *buf,
|
|||
*pos += 4;
|
||||
}
|
||||
} else {
|
||||
early[0] = amdgpu_ring_get_rptr(ring) & ring->buf_mask;
|
||||
early[1] = amdgpu_ring_get_wptr(ring) & ring->buf_mask;
|
||||
|
||||
p = early[0];
|
||||
if (early[0] <= early[1])
|
||||
size = (early[1] - early[0]);
|
||||
avail_dw = early[1] - early[0];
|
||||
else
|
||||
size = ring->ring_size - (early[0] - early[1]);
|
||||
avail_dw = ring->buf_mask + 1 - (early[0] - early[1]);
|
||||
|
||||
while (size) {
|
||||
start_dw = (*pos > 12) ? ((*pos - 12) >> 2) : 0;
|
||||
if (start_dw >= avail_dw)
|
||||
goto out;
|
||||
|
||||
p = (p + start_dw) & ring->ptr_mask;
|
||||
avail_dw -= start_dw;
|
||||
read_dw = min_t(u32, avail_dw, size >> 2);
|
||||
|
||||
while (read_dw) {
|
||||
if (p == early[1])
|
||||
goto out;
|
||||
|
||||
|
|
@ -619,9 +631,10 @@ static ssize_t amdgpu_debugfs_ring_read(struct file *f, char __user *buf,
|
|||
|
||||
buf += 4;
|
||||
result += 4;
|
||||
size--;
|
||||
read_dw--;
|
||||
p++;
|
||||
p &= ring->ptr_mask;
|
||||
*pos += 4;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -106,9 +106,6 @@ amdgpu_userq_detect_and_reset_queues(struct amdgpu_userq_mgr *uq_mgr)
|
|||
int r = 0;
|
||||
int i;
|
||||
|
||||
/* Warning if current process mutex is not held */
|
||||
WARN_ON(!mutex_is_locked(&uq_mgr->userq_mutex));
|
||||
|
||||
if (unlikely(adev->debug_disable_gpu_ring_reset)) {
|
||||
dev_err(adev->dev, "userq reset disabled by debug mask\n");
|
||||
return 0;
|
||||
|
|
@ -127,9 +124,11 @@ amdgpu_userq_detect_and_reset_queues(struct amdgpu_userq_mgr *uq_mgr)
|
|||
*/
|
||||
for (i = 0; i < num_queue_types; i++) {
|
||||
int ring_type = queue_types[i];
|
||||
const struct amdgpu_userq_funcs *funcs = adev->userq_funcs[ring_type];
|
||||
const struct amdgpu_userq_funcs *funcs =
|
||||
adev->userq_funcs[ring_type];
|
||||
|
||||
if (!amdgpu_userq_is_reset_type_supported(adev, ring_type, AMDGPU_RESET_TYPE_PER_QUEUE))
|
||||
if (!amdgpu_userq_is_reset_type_supported(adev, ring_type,
|
||||
AMDGPU_RESET_TYPE_PER_QUEUE))
|
||||
continue;
|
||||
|
||||
if (atomic_read(&uq_mgr->userq_count[ring_type]) > 0 &&
|
||||
|
|
@ -150,38 +149,22 @@ amdgpu_userq_detect_and_reset_queues(struct amdgpu_userq_mgr *uq_mgr)
|
|||
|
||||
static void amdgpu_userq_hang_detect_work(struct work_struct *work)
|
||||
{
|
||||
struct amdgpu_usermode_queue *queue = container_of(work,
|
||||
struct amdgpu_usermode_queue,
|
||||
hang_detect_work.work);
|
||||
struct dma_fence *fence;
|
||||
struct amdgpu_userq_mgr *uq_mgr;
|
||||
struct amdgpu_usermode_queue *queue =
|
||||
container_of(work, struct amdgpu_usermode_queue,
|
||||
hang_detect_work.work);
|
||||
|
||||
if (!queue->userq_mgr)
|
||||
return;
|
||||
|
||||
uq_mgr = queue->userq_mgr;
|
||||
fence = READ_ONCE(queue->hang_detect_fence);
|
||||
/* Fence already signaled – no action needed */
|
||||
if (!fence || dma_fence_is_signaled(fence))
|
||||
return;
|
||||
|
||||
mutex_lock(&uq_mgr->userq_mutex);
|
||||
amdgpu_userq_detect_and_reset_queues(uq_mgr);
|
||||
mutex_unlock(&uq_mgr->userq_mutex);
|
||||
amdgpu_userq_detect_and_reset_queues(queue->userq_mgr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Start hang detection for a user queue fence. A delayed work will be scheduled
|
||||
* to check if the fence is still pending after the timeout period.
|
||||
*/
|
||||
* to reset the queues when the fence doesn't signal in time.
|
||||
*/
|
||||
void amdgpu_userq_start_hang_detect_work(struct amdgpu_usermode_queue *queue)
|
||||
{
|
||||
struct amdgpu_device *adev;
|
||||
unsigned long timeout_ms;
|
||||
|
||||
if (!queue || !queue->userq_mgr || !queue->userq_mgr->adev)
|
||||
return;
|
||||
|
||||
adev = queue->userq_mgr->adev;
|
||||
/* Determine timeout based on queue type */
|
||||
switch (queue->queue_type) {
|
||||
|
|
@ -199,8 +182,6 @@ void amdgpu_userq_start_hang_detect_work(struct amdgpu_usermode_queue *queue)
|
|||
break;
|
||||
}
|
||||
|
||||
/* Store the fence to monitor and schedule hang detection */
|
||||
WRITE_ONCE(queue->hang_detect_fence, queue->last_fence);
|
||||
schedule_delayed_work(&queue->hang_detect_work,
|
||||
msecs_to_jiffies(timeout_ms));
|
||||
}
|
||||
|
|
@ -210,18 +191,24 @@ void amdgpu_userq_process_fence_irq(struct amdgpu_device *adev, u32 doorbell)
|
|||
struct xarray *xa = &adev->userq_doorbell_xa;
|
||||
struct amdgpu_usermode_queue *queue;
|
||||
unsigned long flags;
|
||||
int r;
|
||||
|
||||
xa_lock_irqsave(xa, flags);
|
||||
queue = xa_load(xa, doorbell);
|
||||
if (queue)
|
||||
amdgpu_userq_fence_driver_process(queue->fence_drv);
|
||||
xa_unlock_irqrestore(xa, flags);
|
||||
}
|
||||
if (queue) {
|
||||
r = amdgpu_userq_fence_driver_process(queue->fence_drv);
|
||||
/*
|
||||
* We are in interrupt context here, this *can't* wait for
|
||||
* reset work to finish.
|
||||
*/
|
||||
if (r >= 0)
|
||||
cancel_delayed_work(&queue->hang_detect_work);
|
||||
|
||||
static void amdgpu_userq_init_hang_detect_work(struct amdgpu_usermode_queue *queue)
|
||||
{
|
||||
INIT_DELAYED_WORK(&queue->hang_detect_work, amdgpu_userq_hang_detect_work);
|
||||
queue->hang_detect_fence = NULL;
|
||||
/* Restart the timer when there are still fences pending */
|
||||
if (r == 1)
|
||||
amdgpu_userq_start_hang_detect_work(queue);
|
||||
}
|
||||
xa_unlock_irqrestore(xa, flags);
|
||||
}
|
||||
|
||||
static int amdgpu_userq_buffer_va_list_add(struct amdgpu_usermode_queue *queue,
|
||||
|
|
@ -345,23 +332,18 @@ static int amdgpu_userq_preempt_helper(struct amdgpu_usermode_queue *queue)
|
|||
struct amdgpu_device *adev = uq_mgr->adev;
|
||||
const struct amdgpu_userq_funcs *userq_funcs =
|
||||
adev->userq_funcs[queue->queue_type];
|
||||
bool found_hung_queue = false;
|
||||
int r = 0;
|
||||
int r;
|
||||
|
||||
if (queue->state == AMDGPU_USERQ_STATE_MAPPED) {
|
||||
r = userq_funcs->preempt(queue);
|
||||
if (r) {
|
||||
queue->state = AMDGPU_USERQ_STATE_HUNG;
|
||||
found_hung_queue = true;
|
||||
return r;
|
||||
} else {
|
||||
queue->state = AMDGPU_USERQ_STATE_PREEMPTED;
|
||||
}
|
||||
}
|
||||
|
||||
if (found_hung_queue)
|
||||
amdgpu_userq_detect_and_reset_queues(uq_mgr);
|
||||
|
||||
return r;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amdgpu_userq_restore_helper(struct amdgpu_usermode_queue *queue)
|
||||
|
|
@ -390,24 +372,21 @@ static int amdgpu_userq_unmap_helper(struct amdgpu_usermode_queue *queue)
|
|||
struct amdgpu_device *adev = uq_mgr->adev;
|
||||
const struct amdgpu_userq_funcs *userq_funcs =
|
||||
adev->userq_funcs[queue->queue_type];
|
||||
bool found_hung_queue = false;
|
||||
int r = 0;
|
||||
int r;
|
||||
|
||||
if ((queue->state == AMDGPU_USERQ_STATE_MAPPED) ||
|
||||
(queue->state == AMDGPU_USERQ_STATE_PREEMPTED)) {
|
||||
(queue->state == AMDGPU_USERQ_STATE_PREEMPTED)) {
|
||||
|
||||
r = userq_funcs->unmap(queue);
|
||||
if (r) {
|
||||
queue->state = AMDGPU_USERQ_STATE_HUNG;
|
||||
found_hung_queue = true;
|
||||
return r;
|
||||
} else {
|
||||
queue->state = AMDGPU_USERQ_STATE_UNMAPPED;
|
||||
}
|
||||
}
|
||||
|
||||
if (found_hung_queue)
|
||||
amdgpu_userq_detect_and_reset_queues(uq_mgr);
|
||||
|
||||
return r;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amdgpu_userq_map_helper(struct amdgpu_usermode_queue *queue)
|
||||
|
|
@ -416,19 +395,19 @@ static int amdgpu_userq_map_helper(struct amdgpu_usermode_queue *queue)
|
|||
struct amdgpu_device *adev = uq_mgr->adev;
|
||||
const struct amdgpu_userq_funcs *userq_funcs =
|
||||
adev->userq_funcs[queue->queue_type];
|
||||
int r = 0;
|
||||
int r;
|
||||
|
||||
if (queue->state == AMDGPU_USERQ_STATE_UNMAPPED) {
|
||||
r = userq_funcs->map(queue);
|
||||
if (r) {
|
||||
queue->state = AMDGPU_USERQ_STATE_HUNG;
|
||||
amdgpu_userq_detect_and_reset_queues(uq_mgr);
|
||||
return r;
|
||||
} else {
|
||||
queue->state = AMDGPU_USERQ_STATE_MAPPED;
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void amdgpu_userq_wait_for_last_fence(struct amdgpu_usermode_queue *queue)
|
||||
|
|
@ -648,13 +627,11 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que
|
|||
amdgpu_bo_unreserve(vm->root.bo);
|
||||
|
||||
mutex_lock(&uq_mgr->userq_mutex);
|
||||
queue->hang_detect_fence = NULL;
|
||||
amdgpu_userq_wait_for_last_fence(queue);
|
||||
|
||||
#if defined(CONFIG_DEBUG_FS)
|
||||
debugfs_remove_recursive(queue->debugfs_queue);
|
||||
#endif
|
||||
amdgpu_userq_detect_and_reset_queues(uq_mgr);
|
||||
r = amdgpu_userq_unmap_helper(queue);
|
||||
atomic_dec(&uq_mgr->userq_count[queue->queue_type]);
|
||||
amdgpu_userq_cleanup(queue);
|
||||
|
|
@ -800,6 +777,7 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args)
|
|||
}
|
||||
|
||||
queue->doorbell_index = index;
|
||||
mutex_init(&queue->fence_drv_lock);
|
||||
xa_init_flags(&queue->fence_drv_xa, XA_FLAGS_ALLOC);
|
||||
r = amdgpu_userq_fence_driver_alloc(adev, &queue->fence_drv);
|
||||
if (r) {
|
||||
|
|
@ -855,7 +833,8 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args)
|
|||
up_read(&adev->reset_domain->sem);
|
||||
|
||||
amdgpu_debugfs_userq_init(filp, queue, qid);
|
||||
amdgpu_userq_init_hang_detect_work(queue);
|
||||
INIT_DELAYED_WORK(&queue->hang_detect_work,
|
||||
amdgpu_userq_hang_detect_work);
|
||||
|
||||
args->out.queue_id = qid;
|
||||
atomic_inc(&uq_mgr->userq_count[queue->queue_type]);
|
||||
|
|
@ -873,6 +852,7 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args)
|
|||
amdgpu_bo_reserve(fpriv->vm.root.bo, true);
|
||||
amdgpu_userq_buffer_vas_list_cleanup(adev, queue);
|
||||
amdgpu_bo_unreserve(fpriv->vm.root.bo);
|
||||
mutex_destroy(&queue->fence_drv_lock);
|
||||
free_queue:
|
||||
kfree(queue);
|
||||
err_pm_runtime:
|
||||
|
|
@ -1262,7 +1242,6 @@ amdgpu_userq_evict_all(struct amdgpu_userq_mgr *uq_mgr)
|
|||
unsigned long queue_id;
|
||||
int ret = 0, r;
|
||||
|
||||
amdgpu_userq_detect_and_reset_queues(uq_mgr);
|
||||
/* Try to unmap all the queues in this process ctx */
|
||||
xa_for_each(&uq_mgr->userq_xa, queue_id, queue) {
|
||||
r = amdgpu_userq_preempt_helper(queue);
|
||||
|
|
@ -1270,9 +1249,11 @@ amdgpu_userq_evict_all(struct amdgpu_userq_mgr *uq_mgr)
|
|||
ret = r;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
if (ret) {
|
||||
drm_file_err(uq_mgr->file,
|
||||
"Couldn't unmap all the queues, eviction failed ret=%d\n", ret);
|
||||
amdgpu_userq_detect_and_reset_queues(uq_mgr);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -1372,7 +1353,6 @@ int amdgpu_userq_suspend(struct amdgpu_device *adev)
|
|||
uqm = queue->userq_mgr;
|
||||
cancel_delayed_work_sync(&uqm->resume_work);
|
||||
guard(mutex)(&uqm->userq_mutex);
|
||||
amdgpu_userq_detect_and_reset_queues(uqm);
|
||||
if (adev->in_s0ix)
|
||||
r = amdgpu_userq_preempt_helper(queue);
|
||||
else
|
||||
|
|
@ -1431,7 +1411,6 @@ int amdgpu_userq_stop_sched_for_enforce_isolation(struct amdgpu_device *adev,
|
|||
if (((queue->queue_type == AMDGPU_HW_IP_GFX) ||
|
||||
(queue->queue_type == AMDGPU_HW_IP_COMPUTE)) &&
|
||||
(queue->xcp_id == idx)) {
|
||||
amdgpu_userq_detect_and_reset_queues(uqm);
|
||||
r = amdgpu_userq_preempt_helper(queue);
|
||||
if (r)
|
||||
ret = r;
|
||||
|
|
@ -1504,23 +1483,21 @@ void amdgpu_userq_pre_reset(struct amdgpu_device *adev)
|
|||
{
|
||||
const struct amdgpu_userq_funcs *userq_funcs;
|
||||
struct amdgpu_usermode_queue *queue;
|
||||
struct amdgpu_userq_mgr *uqm;
|
||||
unsigned long queue_id;
|
||||
|
||||
/* TODO: We probably need a new lock for the queue state */
|
||||
xa_for_each(&adev->userq_doorbell_xa, queue_id, queue) {
|
||||
uqm = queue->userq_mgr;
|
||||
cancel_delayed_work_sync(&uqm->resume_work);
|
||||
if (queue->state == AMDGPU_USERQ_STATE_MAPPED) {
|
||||
amdgpu_userq_wait_for_last_fence(queue);
|
||||
userq_funcs = adev->userq_funcs[queue->queue_type];
|
||||
userq_funcs->unmap(queue);
|
||||
/* just mark all queues as hung at this point.
|
||||
* if unmap succeeds, we could map again
|
||||
* in amdgpu_userq_post_reset() if vram is not lost
|
||||
*/
|
||||
queue->state = AMDGPU_USERQ_STATE_HUNG;
|
||||
amdgpu_userq_fence_driver_force_completion(queue);
|
||||
}
|
||||
if (queue->state != AMDGPU_USERQ_STATE_MAPPED)
|
||||
continue;
|
||||
|
||||
userq_funcs = adev->userq_funcs[queue->queue_type];
|
||||
userq_funcs->unmap(queue);
|
||||
/* just mark all queues as hung at this point.
|
||||
* if unmap succeeds, we could map again
|
||||
* in amdgpu_userq_post_reset() if vram is not lost
|
||||
*/
|
||||
queue->state = AMDGPU_USERQ_STATE_HUNG;
|
||||
amdgpu_userq_fence_driver_force_completion(queue);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -66,6 +66,18 @@ struct amdgpu_usermode_queue {
|
|||
struct amdgpu_userq_obj db_obj;
|
||||
struct amdgpu_userq_obj fw_obj;
|
||||
struct amdgpu_userq_obj wptr_obj;
|
||||
|
||||
/**
|
||||
* @fence_drv_lock: Protecting @fence_drv_xa.
|
||||
*/
|
||||
struct mutex fence_drv_lock;
|
||||
|
||||
/**
|
||||
* @fence_drv_xa:
|
||||
*
|
||||
* References to the external fence drivers returned by wait_ioctl.
|
||||
* Dropped on the next signaled dma_fence or queue destruction.
|
||||
*/
|
||||
struct xarray fence_drv_xa;
|
||||
struct amdgpu_userq_fence_driver *fence_drv;
|
||||
struct dma_fence *last_fence;
|
||||
|
|
@ -73,7 +85,6 @@ struct amdgpu_usermode_queue {
|
|||
int priority;
|
||||
struct dentry *debugfs_queue;
|
||||
struct delayed_work hang_detect_work;
|
||||
struct dma_fence *hang_detect_fence;
|
||||
struct kref refcount;
|
||||
|
||||
struct list_head userq_va_list;
|
||||
|
|
|
|||
|
|
@ -121,6 +121,7 @@ amdgpu_userq_fence_driver_free(struct amdgpu_usermode_queue *userq)
|
|||
userq->last_fence = NULL;
|
||||
amdgpu_userq_walk_and_drop_fence_drv(&userq->fence_drv_xa);
|
||||
xa_destroy(&userq->fence_drv_xa);
|
||||
mutex_destroy(&userq->fence_drv_lock);
|
||||
/* Drop the queue's ownership reference to fence_drv explicitly */
|
||||
amdgpu_userq_fence_driver_put(userq->fence_drv);
|
||||
}
|
||||
|
|
@ -134,7 +135,14 @@ amdgpu_userq_fence_put_fence_drv_array(struct amdgpu_userq_fence *userq_fence)
|
|||
userq_fence->fence_drv_array_count = 0;
|
||||
}
|
||||
|
||||
void amdgpu_userq_fence_driver_process(struct amdgpu_userq_fence_driver *fence_drv)
|
||||
/*
|
||||
* Returns:
|
||||
* -ENOENT when no fences were processes
|
||||
* 1 when more fences are pending
|
||||
* 0 when no fences are pending any more
|
||||
*/
|
||||
int
|
||||
amdgpu_userq_fence_driver_process(struct amdgpu_userq_fence_driver *fence_drv)
|
||||
{
|
||||
struct amdgpu_userq_fence *userq_fence, *tmp;
|
||||
LIST_HEAD(to_be_signaled);
|
||||
|
|
@ -142,9 +150,6 @@ void amdgpu_userq_fence_driver_process(struct amdgpu_userq_fence_driver *fence_d
|
|||
unsigned long flags;
|
||||
u64 rptr;
|
||||
|
||||
if (!fence_drv)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&fence_drv->fence_list_lock, flags);
|
||||
rptr = amdgpu_userq_fence_read(fence_drv);
|
||||
|
||||
|
|
@ -157,6 +162,9 @@ void amdgpu_userq_fence_driver_process(struct amdgpu_userq_fence_driver *fence_d
|
|||
&userq_fence->link);
|
||||
spin_unlock_irqrestore(&fence_drv->fence_list_lock, flags);
|
||||
|
||||
if (list_empty(&to_be_signaled))
|
||||
return -ENOENT;
|
||||
|
||||
list_for_each_entry_safe(userq_fence, tmp, &to_be_signaled, link) {
|
||||
fence = &userq_fence->base;
|
||||
list_del_init(&userq_fence->link);
|
||||
|
|
@ -168,6 +176,8 @@ void amdgpu_userq_fence_driver_process(struct amdgpu_userq_fence_driver *fence_d
|
|||
dma_fence_put(fence);
|
||||
}
|
||||
|
||||
/* That doesn't need to be accurate so no locking */
|
||||
return list_empty(&fence_drv->fences) ? 0 : 1;
|
||||
}
|
||||
|
||||
void amdgpu_userq_fence_driver_destroy(struct kref *ref)
|
||||
|
|
@ -209,80 +219,84 @@ void amdgpu_userq_fence_driver_put(struct amdgpu_userq_fence_driver *fence_drv)
|
|||
kref_put(&fence_drv->refcount, amdgpu_userq_fence_driver_destroy);
|
||||
}
|
||||
|
||||
static int amdgpu_userq_fence_alloc(struct amdgpu_userq_fence **userq_fence)
|
||||
static int amdgpu_userq_fence_alloc(struct amdgpu_usermode_queue *userq,
|
||||
struct amdgpu_userq_fence **pfence)
|
||||
{
|
||||
*userq_fence = kmalloc(sizeof(**userq_fence), GFP_KERNEL);
|
||||
return *userq_fence ? 0 : -ENOMEM;
|
||||
struct amdgpu_userq_fence_driver *fence_drv = userq->fence_drv;
|
||||
struct amdgpu_userq_fence *userq_fence;
|
||||
void *entry;
|
||||
|
||||
userq_fence = kmalloc(sizeof(*userq_fence), GFP_KERNEL);
|
||||
if (!userq_fence)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Get the next unused entry, since we fill from the start this can be
|
||||
* used as size to allocate the array.
|
||||
*/
|
||||
mutex_lock(&userq->fence_drv_lock);
|
||||
XA_STATE(xas, &userq->fence_drv_xa, 0);
|
||||
|
||||
rcu_read_lock();
|
||||
do {
|
||||
entry = xas_find_marked(&xas, ULONG_MAX, XA_FREE_MARK);
|
||||
} while (xas_retry(&xas, entry));
|
||||
rcu_read_unlock();
|
||||
|
||||
userq_fence->fence_drv_array = kvmalloc_array(xas.xa_index,
|
||||
sizeof(fence_drv),
|
||||
GFP_KERNEL);
|
||||
if (!userq_fence->fence_drv_array) {
|
||||
mutex_unlock(&userq->fence_drv_lock);
|
||||
kfree(userq_fence);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
userq_fence->fence_drv_array_count = xas.xa_index;
|
||||
xa_extract(&userq->fence_drv_xa, (void **)userq_fence->fence_drv_array,
|
||||
0, ULONG_MAX, xas.xa_index, XA_PRESENT);
|
||||
xa_destroy(&userq->fence_drv_xa);
|
||||
|
||||
mutex_unlock(&userq->fence_drv_lock);
|
||||
|
||||
amdgpu_userq_fence_driver_get(fence_drv);
|
||||
userq_fence->fence_drv = fence_drv;
|
||||
|
||||
*pfence = userq_fence;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amdgpu_userq_fence_create(struct amdgpu_usermode_queue *userq,
|
||||
struct amdgpu_userq_fence *userq_fence,
|
||||
u64 seq, struct dma_fence **f)
|
||||
static void amdgpu_userq_fence_init(struct amdgpu_usermode_queue *userq,
|
||||
struct amdgpu_userq_fence *fence,
|
||||
u64 seq)
|
||||
{
|
||||
struct amdgpu_userq_fence_driver *fence_drv;
|
||||
struct dma_fence *fence;
|
||||
struct amdgpu_userq_fence_driver *fence_drv = userq->fence_drv;
|
||||
unsigned long flags;
|
||||
bool signaled = false;
|
||||
|
||||
fence_drv = userq->fence_drv;
|
||||
if (!fence_drv)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_init(&userq_fence->lock);
|
||||
INIT_LIST_HEAD(&userq_fence->link);
|
||||
fence = &userq_fence->base;
|
||||
userq_fence->fence_drv = fence_drv;
|
||||
|
||||
dma_fence_init64(fence, &amdgpu_userq_fence_ops, &userq_fence->lock,
|
||||
spin_lock_init(&fence->lock);
|
||||
dma_fence_init64(&fence->base, &amdgpu_userq_fence_ops, &fence->lock,
|
||||
fence_drv->context, seq);
|
||||
|
||||
amdgpu_userq_fence_driver_get(fence_drv);
|
||||
dma_fence_get(fence);
|
||||
/* Make sure the fence is visible to the hang detect worker */
|
||||
dma_fence_put(userq->last_fence);
|
||||
userq->last_fence = dma_fence_get(&fence->base);
|
||||
|
||||
if (!xa_empty(&userq->fence_drv_xa)) {
|
||||
struct amdgpu_userq_fence_driver *stored_fence_drv;
|
||||
unsigned long index, count = 0;
|
||||
int i = 0;
|
||||
|
||||
xa_lock(&userq->fence_drv_xa);
|
||||
xa_for_each(&userq->fence_drv_xa, index, stored_fence_drv)
|
||||
count++;
|
||||
|
||||
userq_fence->fence_drv_array =
|
||||
kvmalloc_objs(struct amdgpu_userq_fence_driver *, count,
|
||||
GFP_ATOMIC);
|
||||
|
||||
if (userq_fence->fence_drv_array) {
|
||||
xa_for_each(&userq->fence_drv_xa, index, stored_fence_drv) {
|
||||
userq_fence->fence_drv_array[i] = stored_fence_drv;
|
||||
__xa_erase(&userq->fence_drv_xa, index);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
userq_fence->fence_drv_array_count = i;
|
||||
xa_unlock(&userq->fence_drv_xa);
|
||||
} else {
|
||||
userq_fence->fence_drv_array = NULL;
|
||||
userq_fence->fence_drv_array_count = 0;
|
||||
}
|
||||
|
||||
/* Check if hardware has already processed the job */
|
||||
/* Check if hardware has already processed the fence */
|
||||
spin_lock_irqsave(&fence_drv->fence_list_lock, flags);
|
||||
if (!dma_fence_is_signaled(fence)) {
|
||||
list_add_tail(&userq_fence->link, &fence_drv->fences);
|
||||
if (!dma_fence_is_signaled(&fence->base)) {
|
||||
dma_fence_get(&fence->base);
|
||||
list_add_tail(&fence->link, &fence_drv->fences);
|
||||
} else {
|
||||
INIT_LIST_HEAD(&fence->link);
|
||||
signaled = true;
|
||||
dma_fence_put(fence);
|
||||
}
|
||||
spin_unlock_irqrestore(&fence_drv->fence_list_lock, flags);
|
||||
|
||||
if (signaled)
|
||||
amdgpu_userq_fence_put_fence_drv_array(userq_fence);
|
||||
|
||||
*f = fence;
|
||||
|
||||
return 0;
|
||||
amdgpu_userq_fence_put_fence_drv_array(fence);
|
||||
else
|
||||
amdgpu_userq_start_hang_detect_work(userq);
|
||||
}
|
||||
|
||||
static const char *amdgpu_userq_fence_get_driver_name(struct dma_fence *f)
|
||||
|
|
@ -403,11 +417,6 @@ static int amdgpu_userq_fence_read_wptr(struct amdgpu_device *adev,
|
|||
return r;
|
||||
}
|
||||
|
||||
static void amdgpu_userq_fence_cleanup(struct dma_fence *fence)
|
||||
{
|
||||
dma_fence_put(fence);
|
||||
}
|
||||
|
||||
static void
|
||||
amdgpu_userq_fence_driver_set_error(struct amdgpu_userq_fence *fence,
|
||||
int error)
|
||||
|
|
@ -451,13 +460,14 @@ int amdgpu_userq_signal_ioctl(struct drm_device *dev, void *data,
|
|||
const unsigned int num_read_bo_handles = args->num_bo_read_handles;
|
||||
struct amdgpu_fpriv *fpriv = filp->driver_priv;
|
||||
struct amdgpu_userq_mgr *userq_mgr = &fpriv->userq_mgr;
|
||||
|
||||
struct drm_gem_object **gobj_write, **gobj_read;
|
||||
u32 *syncobj_handles, num_syncobj_handles;
|
||||
struct amdgpu_userq_fence *userq_fence;
|
||||
struct amdgpu_usermode_queue *queue = NULL;
|
||||
struct drm_syncobj **syncobj = NULL;
|
||||
struct dma_fence *fence;
|
||||
struct amdgpu_usermode_queue *queue;
|
||||
struct amdgpu_userq_fence *fence;
|
||||
struct drm_syncobj **syncobj;
|
||||
struct drm_exec exec;
|
||||
void __user *ptr;
|
||||
int r, i, entry;
|
||||
u64 wptr;
|
||||
|
||||
|
|
@ -469,13 +479,14 @@ int amdgpu_userq_signal_ioctl(struct drm_device *dev, void *data,
|
|||
return -EINVAL;
|
||||
|
||||
num_syncobj_handles = args->num_syncobj_handles;
|
||||
syncobj_handles = memdup_array_user(u64_to_user_ptr(args->syncobj_handles),
|
||||
num_syncobj_handles, sizeof(u32));
|
||||
ptr = u64_to_user_ptr(args->syncobj_handles);
|
||||
syncobj_handles = memdup_array_user(ptr, num_syncobj_handles,
|
||||
sizeof(u32));
|
||||
if (IS_ERR(syncobj_handles))
|
||||
return PTR_ERR(syncobj_handles);
|
||||
|
||||
/* Array of pointers to the looked up syncobjs */
|
||||
syncobj = kmalloc_array(num_syncobj_handles, sizeof(*syncobj), GFP_KERNEL);
|
||||
syncobj = kmalloc_array(num_syncobj_handles, sizeof(*syncobj),
|
||||
GFP_KERNEL);
|
||||
if (!syncobj) {
|
||||
r = -ENOMEM;
|
||||
goto free_syncobj_handles;
|
||||
|
|
@ -489,21 +500,17 @@ int amdgpu_userq_signal_ioctl(struct drm_device *dev, void *data,
|
|||
}
|
||||
}
|
||||
|
||||
r = drm_gem_objects_lookup(filp,
|
||||
u64_to_user_ptr(args->bo_read_handles),
|
||||
num_read_bo_handles,
|
||||
&gobj_read);
|
||||
ptr = u64_to_user_ptr(args->bo_read_handles);
|
||||
r = drm_gem_objects_lookup(filp, ptr, num_read_bo_handles, &gobj_read);
|
||||
if (r)
|
||||
goto free_syncobj;
|
||||
|
||||
r = drm_gem_objects_lookup(filp,
|
||||
u64_to_user_ptr(args->bo_write_handles),
|
||||
num_write_bo_handles,
|
||||
ptr = u64_to_user_ptr(args->bo_write_handles);
|
||||
r = drm_gem_objects_lookup(filp, ptr, num_write_bo_handles,
|
||||
&gobj_write);
|
||||
if (r)
|
||||
goto put_gobj_read;
|
||||
|
||||
/* Retrieve the user queue */
|
||||
queue = amdgpu_userq_get(userq_mgr, args->queue_id);
|
||||
if (!queue) {
|
||||
r = -ENOENT;
|
||||
|
|
@ -512,73 +519,61 @@ int amdgpu_userq_signal_ioctl(struct drm_device *dev, void *data,
|
|||
|
||||
r = amdgpu_userq_fence_read_wptr(adev, queue, &wptr);
|
||||
if (r)
|
||||
goto put_gobj_write;
|
||||
goto put_queue;
|
||||
|
||||
r = amdgpu_userq_fence_alloc(&userq_fence);
|
||||
r = amdgpu_userq_fence_alloc(queue, &fence);
|
||||
if (r)
|
||||
goto put_gobj_write;
|
||||
goto put_queue;
|
||||
|
||||
/* We are here means UQ is active, make sure the eviction fence is valid */
|
||||
amdgpu_userq_ensure_ev_fence(&fpriv->userq_mgr, &fpriv->evf_mgr);
|
||||
|
||||
/* Create a new fence */
|
||||
r = amdgpu_userq_fence_create(queue, userq_fence, wptr, &fence);
|
||||
if (r) {
|
||||
mutex_unlock(&userq_mgr->userq_mutex);
|
||||
kfree(userq_fence);
|
||||
goto put_gobj_write;
|
||||
}
|
||||
/* Create the new fence */
|
||||
amdgpu_userq_fence_init(queue, fence, wptr);
|
||||
|
||||
dma_fence_put(queue->last_fence);
|
||||
queue->last_fence = dma_fence_get(fence);
|
||||
amdgpu_userq_start_hang_detect_work(queue);
|
||||
mutex_unlock(&userq_mgr->userq_mutex);
|
||||
|
||||
/*
|
||||
* This needs to come after the fence is created since
|
||||
* amdgpu_userq_ensure_ev_fence() can't be called while holding the resv
|
||||
* locks.
|
||||
*/
|
||||
drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT,
|
||||
(num_read_bo_handles + num_write_bo_handles));
|
||||
|
||||
/* Lock all BOs with retry handling */
|
||||
drm_exec_until_all_locked(&exec) {
|
||||
r = drm_exec_prepare_array(&exec, gobj_read, num_read_bo_handles, 1);
|
||||
r = drm_exec_prepare_array(&exec, gobj_read,
|
||||
num_read_bo_handles, 1);
|
||||
drm_exec_retry_on_contention(&exec);
|
||||
if (r) {
|
||||
amdgpu_userq_fence_cleanup(fence);
|
||||
if (r)
|
||||
goto exec_fini;
|
||||
}
|
||||
|
||||
r = drm_exec_prepare_array(&exec, gobj_write, num_write_bo_handles, 1);
|
||||
r = drm_exec_prepare_array(&exec, gobj_write,
|
||||
num_write_bo_handles, 1);
|
||||
drm_exec_retry_on_contention(&exec);
|
||||
if (r) {
|
||||
amdgpu_userq_fence_cleanup(fence);
|
||||
if (r)
|
||||
goto exec_fini;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < num_read_bo_handles; i++) {
|
||||
if (!gobj_read || !gobj_read[i]->resv)
|
||||
continue;
|
||||
|
||||
dma_resv_add_fence(gobj_read[i]->resv, fence,
|
||||
/* And publish the new fence in the BOs and syncobj */
|
||||
for (i = 0; i < num_read_bo_handles; i++)
|
||||
dma_resv_add_fence(gobj_read[i]->resv, &fence->base,
|
||||
DMA_RESV_USAGE_READ);
|
||||
}
|
||||
|
||||
for (i = 0; i < num_write_bo_handles; i++) {
|
||||
if (!gobj_write || !gobj_write[i]->resv)
|
||||
continue;
|
||||
|
||||
dma_resv_add_fence(gobj_write[i]->resv, fence,
|
||||
for (i = 0; i < num_write_bo_handles; i++)
|
||||
dma_resv_add_fence(gobj_write[i]->resv, &fence->base,
|
||||
DMA_RESV_USAGE_WRITE);
|
||||
}
|
||||
|
||||
/* Add the created fence to syncobj/BO's */
|
||||
for (i = 0; i < num_syncobj_handles; i++)
|
||||
drm_syncobj_replace_fence(syncobj[i], fence);
|
||||
|
||||
/* drop the reference acquired in fence creation function */
|
||||
dma_fence_put(fence);
|
||||
drm_syncobj_replace_fence(syncobj[i], &fence->base);
|
||||
|
||||
exec_fini:
|
||||
/* drop the reference acquired in fence creation function */
|
||||
dma_fence_put(&fence->base);
|
||||
|
||||
drm_exec_fini(&exec);
|
||||
put_queue:
|
||||
amdgpu_userq_put(queue);
|
||||
put_gobj_write:
|
||||
for (i = 0; i < num_write_bo_handles; i++)
|
||||
drm_gem_object_put(gobj_write[i]);
|
||||
|
|
@ -589,15 +584,11 @@ int amdgpu_userq_signal_ioctl(struct drm_device *dev, void *data,
|
|||
kvfree(gobj_read);
|
||||
free_syncobj:
|
||||
while (entry-- > 0)
|
||||
if (syncobj[entry])
|
||||
drm_syncobj_put(syncobj[entry]);
|
||||
drm_syncobj_put(syncobj[entry]);
|
||||
kfree(syncobj);
|
||||
free_syncobj_handles:
|
||||
kfree(syncobj_handles);
|
||||
|
||||
if (queue)
|
||||
amdgpu_userq_put(queue);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
|
@ -872,8 +863,10 @@ amdgpu_userq_wait_return_fence_info(struct drm_file *filp,
|
|||
* Otherwise, we would gather those references until we don't
|
||||
* have any more space left and crash.
|
||||
*/
|
||||
mutex_lock(&waitq->fence_drv_lock);
|
||||
r = xa_alloc(&waitq->fence_drv_xa, &index, fence_drv,
|
||||
xa_limit_32b, GFP_KERNEL);
|
||||
mutex_unlock(&waitq->fence_drv_lock);
|
||||
if (r)
|
||||
goto put_waitq;
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ void amdgpu_userq_fence_driver_put(struct amdgpu_userq_fence_driver *fence_drv);
|
|||
int amdgpu_userq_fence_driver_alloc(struct amdgpu_device *adev,
|
||||
struct amdgpu_userq_fence_driver **fence_drv_req);
|
||||
void amdgpu_userq_fence_driver_free(struct amdgpu_usermode_queue *userq);
|
||||
void amdgpu_userq_fence_driver_process(struct amdgpu_userq_fence_driver *fence_drv);
|
||||
int amdgpu_userq_fence_driver_process(struct amdgpu_userq_fence_driver *fence_drv);
|
||||
void amdgpu_userq_fence_driver_force_completion(struct amdgpu_usermode_queue *userq);
|
||||
void amdgpu_userq_fence_driver_destroy(struct kref *ref);
|
||||
int amdgpu_userq_signal_ioctl(struct drm_device *dev, void *data,
|
||||
|
|
|
|||
|
|
@ -602,6 +602,13 @@ static int gfx_v12_0_init_microcode(struct amdgpu_device *adev)
|
|||
"amdgpu/%s_pfp.bin", ucode_prefix);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
adev->gfx.rs64_enable = amdgpu_ucode_hdr_version(
|
||||
(union amdgpu_firmware_header *)
|
||||
adev->gfx.pfp_fw->data, 2, 0);
|
||||
if (adev->gfx.rs64_enable)
|
||||
dev_dbg(adev->dev, "CP RS64 enable\n");
|
||||
|
||||
amdgpu_gfx_cp_init_microcode(adev, AMDGPU_UCODE_ID_CP_RS64_PFP);
|
||||
amdgpu_gfx_cp_init_microcode(adev, AMDGPU_UCODE_ID_CP_RS64_PFP_P0_STACK);
|
||||
|
||||
|
|
|
|||
|
|
@ -92,9 +92,14 @@
|
|||
#include "dml/dcn32/dcn32_fpu.h"
|
||||
|
||||
#include "dc_state_priv.h"
|
||||
#include "dc_fpu.h"
|
||||
|
||||
#include "dml2_0/dml2_wrapper.h"
|
||||
|
||||
#if !defined(DC_RUN_WITH_PREEMPTION_ENABLED)
|
||||
#define DC_RUN_WITH_PREEMPTION_ENABLED(code) code
|
||||
#endif
|
||||
|
||||
#define DC_LOGGER_INIT(logger)
|
||||
|
||||
enum dcn32_clk_src_array_id {
|
||||
|
|
@ -1684,7 +1689,8 @@ static void dcn32_enable_phantom_plane(struct dc *dc,
|
|||
if (curr_pipe->top_pipe && curr_pipe->top_pipe->plane_state == curr_pipe->plane_state)
|
||||
phantom_plane = prev_phantom_plane;
|
||||
else
|
||||
phantom_plane = dc_state_create_phantom_plane(dc, context, curr_pipe->plane_state);
|
||||
DC_RUN_WITH_PREEMPTION_ENABLED(phantom_plane =
|
||||
dc_state_create_phantom_plane(dc, context, curr_pipe->plane_state));
|
||||
|
||||
if (!phantom_plane)
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -222,52 +222,58 @@ static const struct drm_bridge_funcs imx8qxp_pxl2dpi_bridge_funcs = {
|
|||
imx8qxp_pxl2dpi_bridge_atomic_get_output_bus_fmts,
|
||||
};
|
||||
|
||||
static struct device_node *
|
||||
static int
|
||||
imx8qxp_pxl2dpi_get_available_ep_from_port(struct imx8qxp_pxl2dpi *p2d,
|
||||
u32 port_id)
|
||||
u32 port_id,
|
||||
struct device_node **ep)
|
||||
{
|
||||
struct device_node *port, *ep;
|
||||
struct device_node *port;
|
||||
int ret = 0;
|
||||
int ep_cnt;
|
||||
|
||||
*ep = NULL;
|
||||
|
||||
port = of_graph_get_port_by_id(p2d->dev->of_node, port_id);
|
||||
if (!port) {
|
||||
DRM_DEV_ERROR(p2d->dev, "failed to get port@%u\n", port_id);
|
||||
return ERR_PTR(-ENODEV);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ep_cnt = of_get_available_child_count(port);
|
||||
if (ep_cnt == 0) {
|
||||
DRM_DEV_ERROR(p2d->dev, "no available endpoints of port@%u\n",
|
||||
port_id);
|
||||
ep = ERR_PTR(-ENODEV);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
} else if (ep_cnt > 1) {
|
||||
DRM_DEV_ERROR(p2d->dev,
|
||||
"invalid available endpoints of port@%u\n",
|
||||
port_id);
|
||||
ep = ERR_PTR(-EINVAL);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ep = of_get_next_available_child(port, NULL);
|
||||
if (!ep) {
|
||||
*ep = of_get_next_available_child(port, NULL);
|
||||
if (!*ep) {
|
||||
DRM_DEV_ERROR(p2d->dev,
|
||||
"failed to get available endpoint of port@%u\n",
|
||||
port_id);
|
||||
ep = ERR_PTR(-ENODEV);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
of_node_put(port);
|
||||
return ep;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int imx8qxp_pxl2dpi_find_next_bridge(struct imx8qxp_pxl2dpi *p2d)
|
||||
{
|
||||
struct device_node *ep __free(device_node) =
|
||||
imx8qxp_pxl2dpi_get_available_ep_from_port(p2d, 1);
|
||||
if (IS_ERR(ep))
|
||||
return PTR_ERR(ep);
|
||||
struct device_node *ep __free(device_node) = NULL;
|
||||
int ret;
|
||||
|
||||
ret = imx8qxp_pxl2dpi_get_available_ep_from_port(p2d, 1, &ep);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
struct device_node *remote __free(device_node) = of_graph_get_remote_port_parent(ep);
|
||||
if (!remote || !of_device_is_available(remote)) {
|
||||
|
|
@ -291,9 +297,9 @@ static int imx8qxp_pxl2dpi_set_pixel_link_sel(struct imx8qxp_pxl2dpi *p2d)
|
|||
struct of_endpoint endpoint;
|
||||
int ret;
|
||||
|
||||
ep = imx8qxp_pxl2dpi_get_available_ep_from_port(p2d, 0);
|
||||
if (IS_ERR(ep))
|
||||
return PTR_ERR(ep);
|
||||
ret = imx8qxp_pxl2dpi_get_available_ep_from_port(p2d, 0, &ep);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = of_graph_parse_endpoint(ep, &endpoint);
|
||||
if (ret) {
|
||||
|
|
|
|||
|
|
@ -1067,17 +1067,12 @@ int drm_gem_change_handle_ioctl(struct drm_device *dev, void *data,
|
|||
|
||||
spin_unlock(&file_priv->table_lock);
|
||||
|
||||
if (ret < 0)
|
||||
goto out_unlock;
|
||||
|
||||
if (obj->dma_buf) {
|
||||
ret = drm_prime_add_buf_handle(&file_priv->prime, obj->dma_buf,
|
||||
handle);
|
||||
if (ret < 0) {
|
||||
spin_lock(&file_priv->table_lock);
|
||||
idr_remove(&file_priv->object_idr, handle);
|
||||
idrobj = idr_replace(&file_priv->object_idr, obj, handle);
|
||||
WARN_ON(idrobj != NULL);
|
||||
spin_unlock(&file_priv->table_lock);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
|
@ -1089,7 +1084,9 @@ int drm_gem_change_handle_ioctl(struct drm_device *dev, void *data,
|
|||
|
||||
spin_lock(&file_priv->table_lock);
|
||||
idr_remove(&file_priv->object_idr, args->handle);
|
||||
idrobj = idr_replace(&file_priv->object_idr, obj, handle);
|
||||
spin_unlock(&file_priv->table_lock);
|
||||
WARN_ON(idrobj != NULL);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&file_priv->prime.lock);
|
||||
|
|
|
|||
|
|
@ -580,6 +580,7 @@ static int oaktrail_hdmi_get_modes(struct drm_connector *connector)
|
|||
} else {
|
||||
edid = (struct edid *)raw_edid;
|
||||
/* FIXME ? edid = drm_get_edid(connector, i2c_adap); */
|
||||
i2c_put_adapter(i2c_adap);
|
||||
}
|
||||
|
||||
if (edid) {
|
||||
|
|
|
|||
|
|
@ -293,7 +293,7 @@ void oaktrail_lvds_init(struct drm_device *dev,
|
|||
{
|
||||
struct gma_encoder *gma_encoder;
|
||||
struct gma_connector *gma_connector;
|
||||
struct gma_i2c_chan *ddc_bus;
|
||||
struct gma_i2c_chan *ddc_bus = NULL;
|
||||
struct drm_connector *connector;
|
||||
struct drm_encoder *encoder;
|
||||
struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
|
||||
|
|
@ -367,6 +367,8 @@ void oaktrail_lvds_init(struct drm_device *dev,
|
|||
if (edid == NULL && dev_priv->lpc_gpio_base) {
|
||||
ddc_bus = oaktrail_lvds_i2c_init(dev);
|
||||
if (!IS_ERR(ddc_bus)) {
|
||||
if (i2c_adap)
|
||||
i2c_put_adapter(i2c_adap);
|
||||
i2c_adap = &ddc_bus->base;
|
||||
edid = drm_get_edid(connector, i2c_adap);
|
||||
}
|
||||
|
|
@ -421,7 +423,10 @@ void oaktrail_lvds_init(struct drm_device *dev,
|
|||
|
||||
err_unlock:
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
gma_i2c_destroy(to_gma_i2c_chan(connector->ddc));
|
||||
if (!IS_ERR_OR_NULL(ddc_bus))
|
||||
gma_i2c_destroy(ddc_bus);
|
||||
else if (i2c_adap)
|
||||
i2c_put_adapter(i2c_adap);
|
||||
drm_encoder_cleanup(encoder);
|
||||
err_connector_cleanup:
|
||||
drm_connector_cleanup(connector);
|
||||
|
|
|
|||
|
|
@ -3119,8 +3119,13 @@ static void intel_dp_compute_vsc_colorimetry(const struct intel_crtc_state *crtc
|
|||
drm_WARN_ON(display->drm,
|
||||
vsc->bpc == 6 && vsc->pixelformat != DP_PIXELFORMAT_RGB);
|
||||
|
||||
/* all YCbCr are always limited range */
|
||||
vsc->dynamic_range = DP_DYNAMIC_RANGE_CTA;
|
||||
/* All YCbCr formats are always limited range. */
|
||||
if (vsc->pixelformat == DP_PIXELFORMAT_RGB)
|
||||
vsc->dynamic_range = crtc_state->limited_color_range ?
|
||||
DP_DYNAMIC_RANGE_CTA : DP_DYNAMIC_RANGE_VESA;
|
||||
else
|
||||
vsc->dynamic_range = DP_DYNAMIC_RANGE_CTA;
|
||||
|
||||
vsc->content_type = DP_CONTENT_TYPE_NOT_DEFINED;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -132,7 +132,8 @@ void __i915_request_reset(struct i915_request *rq, bool guilty)
|
|||
rcu_read_lock(); /* protect the GEM context */
|
||||
if (guilty) {
|
||||
i915_request_set_error_once(rq, -EIO);
|
||||
__i915_request_skip(rq);
|
||||
if (!i915_request_signaled(rq))
|
||||
__i915_request_skip(rq);
|
||||
banned = mark_guilty(rq);
|
||||
} else {
|
||||
i915_request_set_error_once(rq, -EAGAIN);
|
||||
|
|
|
|||
|
|
@ -293,7 +293,7 @@ static int lsdc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||
|
||||
vga_client_register(pdev, lsdc_vga_set_decode);
|
||||
|
||||
drm_kms_helper_poll_init(ddev);
|
||||
drmm_kms_helper_poll_init(ddev);
|
||||
|
||||
if (loongson_vblank) {
|
||||
ret = drm_vblank_init(ddev, descp->num_of_crtc);
|
||||
|
|
|
|||
|
|
@ -390,6 +390,8 @@ panfrost_ioctl_wait_bo(struct drm_device *dev, void *data,
|
|||
true, timeout);
|
||||
if (!ret)
|
||||
ret = timeout ? -ETIMEDOUT : -EBUSY;
|
||||
else if (ret > 0)
|
||||
ret = 0;
|
||||
|
||||
drm_gem_object_put(gem_obj);
|
||||
|
||||
|
|
|
|||
|
|
@ -739,7 +739,7 @@ static int ttm_bo_alloc_resource(struct ttm_buffer_object *bo,
|
|||
may_evict = (force_space && place->mem_type != TTM_PL_SYSTEM);
|
||||
ret = ttm_resource_alloc(bo, place, res, force_space ? &limit_pool : NULL);
|
||||
if (ret) {
|
||||
if (ret != -ENOSPC && ret != -EAGAIN) {
|
||||
if (ret != -ENOSPC) {
|
||||
dmem_cgroup_pool_state_put(limit_pool);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1177,17 +1177,13 @@ ttm_bo_swapout_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object *bo)
|
|||
bdev->funcs->swap_notify(bo);
|
||||
|
||||
if (ttm_tt_is_populated(tt)) {
|
||||
spin_lock(&bdev->lru_lock);
|
||||
ttm_resource_del_bulk_move(bo->resource, bo);
|
||||
spin_unlock(&bdev->lru_lock);
|
||||
|
||||
ret = ttm_tt_swapout(bdev, tt, swapout_walk->gfp_flags);
|
||||
|
||||
spin_lock(&bdev->lru_lock);
|
||||
if (ret)
|
||||
ttm_resource_add_bulk_move(bo->resource, bo);
|
||||
ttm_resource_move_to_lru_tail(bo->resource);
|
||||
spin_unlock(&bdev->lru_lock);
|
||||
if (!ret) {
|
||||
spin_lock(&bdev->lru_lock);
|
||||
ttm_resource_del_bulk_move_unevictable(bo->resource, bo);
|
||||
ttm_resource_move_to_lru_tail(bo->resource);
|
||||
spin_unlock(&bdev->lru_lock);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
|
|
|
|||
|
|
@ -1112,19 +1112,14 @@ long ttm_bo_shrink(struct ttm_operation_ctx *ctx, struct ttm_buffer_object *bo,
|
|||
if (lret < 0)
|
||||
return lret;
|
||||
|
||||
if (bo->bulk_move) {
|
||||
spin_lock(&bdev->lru_lock);
|
||||
ttm_resource_del_bulk_move(bo->resource, bo);
|
||||
spin_unlock(&bdev->lru_lock);
|
||||
}
|
||||
|
||||
lret = ttm_tt_backup(bdev, bo->ttm, (struct ttm_backup_flags)
|
||||
{.purge = flags.purge,
|
||||
.writeback = flags.writeback});
|
||||
|
||||
if (lret <= 0 && bo->bulk_move) {
|
||||
if (lret > 0) {
|
||||
spin_lock(&bdev->lru_lock);
|
||||
ttm_resource_add_bulk_move(bo->resource, bo);
|
||||
ttm_resource_del_bulk_move_unevictable(bo->resource, bo);
|
||||
ttm_resource_move_to_lru_tail(bo->resource);
|
||||
spin_unlock(&bdev->lru_lock);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -292,6 +292,19 @@ void ttm_resource_del_bulk_move(struct ttm_resource *res,
|
|||
ttm_lru_bulk_move_del(bo->bulk_move, res);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove a resource from its bulk_move, bypassing the unevictable check.
|
||||
* Use only when the resource is known to still be tracked in the range despite
|
||||
* the BO having just become unevictable; asserts that this is the case.
|
||||
*/
|
||||
void ttm_resource_del_bulk_move_unevictable(struct ttm_resource *res,
|
||||
struct ttm_buffer_object *bo)
|
||||
{
|
||||
WARN_ON_ONCE(!ttm_resource_unevictable(res, bo));
|
||||
if (bo->bulk_move)
|
||||
ttm_lru_bulk_move_del(bo->bulk_move, res);
|
||||
}
|
||||
|
||||
/* Move a resource to the LRU or bulk tail */
|
||||
void ttm_resource_move_to_lru_tail(struct ttm_resource *res)
|
||||
{
|
||||
|
|
@ -385,8 +398,11 @@ int ttm_resource_alloc(struct ttm_buffer_object *bo,
|
|||
|
||||
if (man->cg) {
|
||||
ret = dmem_cgroup_try_charge(man->cg, bo->base.size, &pool, ret_limit_pool);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
if (ret == -EAGAIN)
|
||||
ret = -ENOSPC;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = man->func->alloc(man, bo, place, res_ptr);
|
||||
|
|
|
|||
|
|
@ -897,10 +897,10 @@ void xe_bo_set_purgeable_state(struct xe_bo *bo,
|
|||
new_state == XE_MADV_PURGEABLE_PURGED);
|
||||
|
||||
/* Once purged, always purged - cannot transition out */
|
||||
xe_assert(xe, !(bo->madv_purgeable == XE_MADV_PURGEABLE_PURGED &&
|
||||
xe_assert(xe, !(bo->purgeable.state == XE_MADV_PURGEABLE_PURGED &&
|
||||
new_state != XE_MADV_PURGEABLE_PURGED));
|
||||
|
||||
bo->madv_purgeable = new_state;
|
||||
bo->purgeable.state = new_state;
|
||||
xe_bo_set_purgeable_shrinker(bo, new_state);
|
||||
}
|
||||
|
||||
|
|
@ -2368,7 +2368,7 @@ struct xe_bo *xe_bo_init_locked(struct xe_device *xe, struct xe_bo *bo,
|
|||
INIT_LIST_HEAD(&bo->vram_userfault_link);
|
||||
|
||||
/* Initialize purge advisory state */
|
||||
bo->madv_purgeable = XE_MADV_PURGEABLE_WILLNEED;
|
||||
bo->purgeable.state = XE_MADV_PURGEABLE_WILLNEED;
|
||||
|
||||
drm_gem_private_object_init(&xe->drm, &bo->ttm.base, size);
|
||||
|
||||
|
|
|
|||
|
|
@ -251,7 +251,7 @@ static inline bool xe_bo_is_protected(const struct xe_bo *bo)
|
|||
static inline bool xe_bo_is_purged(struct xe_bo *bo)
|
||||
{
|
||||
xe_bo_assert_held(bo);
|
||||
return bo->madv_purgeable == XE_MADV_PURGEABLE_PURGED;
|
||||
return bo->purgeable.state == XE_MADV_PURGEABLE_PURGED;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -268,11 +268,95 @@ static inline bool xe_bo_is_purged(struct xe_bo *bo)
|
|||
static inline bool xe_bo_madv_is_dontneed(struct xe_bo *bo)
|
||||
{
|
||||
xe_bo_assert_held(bo);
|
||||
return bo->madv_purgeable == XE_MADV_PURGEABLE_DONTNEED;
|
||||
return bo->purgeable.state == XE_MADV_PURGEABLE_DONTNEED;
|
||||
}
|
||||
|
||||
void xe_bo_set_purgeable_state(struct xe_bo *bo, enum xe_madv_purgeable_state new_state);
|
||||
|
||||
/**
|
||||
* xe_bo_willneed_get_locked() - Acquire a WILLNEED holder on a BO
|
||||
* @bo: Buffer object
|
||||
*
|
||||
* Increments willneed_count and, on a 0->1 transition, promotes the BO
|
||||
* from DONTNEED to WILLNEED. PURGED is terminal and is never modified.
|
||||
*
|
||||
* Caller must hold the BO's dma-resv lock.
|
||||
*/
|
||||
static inline void xe_bo_willneed_get_locked(struct xe_bo *bo)
|
||||
{
|
||||
xe_bo_assert_held(bo);
|
||||
|
||||
/* Imported BOs are owned externally; do not track purgeability. */
|
||||
if (drm_gem_is_imported(&bo->ttm.base))
|
||||
return;
|
||||
|
||||
if (bo->purgeable.willneed_count++ == 0 && xe_bo_madv_is_dontneed(bo))
|
||||
xe_bo_set_purgeable_state(bo, XE_MADV_PURGEABLE_WILLNEED);
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_bo_willneed_put_locked() - Release a WILLNEED holder on a BO
|
||||
* @bo: Buffer object
|
||||
*
|
||||
* Decrements willneed_count and, on a 1->0 transition, marks the BO
|
||||
* DONTNEED only if it still has VMAs (implying all active VMAs are
|
||||
* DONTNEED). If the last VMA is being removed, preserve the current BO
|
||||
* state to match the previous VMA-walk semantics.
|
||||
*
|
||||
* PURGED is terminal and the BO state is never modified.
|
||||
*
|
||||
* Caller must hold the BO's dma-resv lock.
|
||||
*/
|
||||
static inline void xe_bo_willneed_put_locked(struct xe_bo *bo)
|
||||
{
|
||||
xe_bo_assert_held(bo);
|
||||
|
||||
if (drm_gem_is_imported(&bo->ttm.base))
|
||||
return;
|
||||
|
||||
xe_assert(xe_bo_device(bo), bo->purgeable.willneed_count > 0);
|
||||
if (--bo->purgeable.willneed_count == 0 && bo->purgeable.vma_count > 0 &&
|
||||
!xe_bo_is_purged(bo))
|
||||
xe_bo_set_purgeable_state(bo, XE_MADV_PURGEABLE_DONTNEED);
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_bo_vma_count_inc_locked() - Account a new VMA on a BO
|
||||
* @bo: Buffer object
|
||||
*
|
||||
* Increments vma_count.
|
||||
*
|
||||
* Caller must hold the BO's dma-resv lock.
|
||||
*/
|
||||
static inline void xe_bo_vma_count_inc_locked(struct xe_bo *bo)
|
||||
{
|
||||
xe_bo_assert_held(bo);
|
||||
|
||||
if (drm_gem_is_imported(&bo->ttm.base))
|
||||
return;
|
||||
|
||||
bo->purgeable.vma_count++;
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_bo_vma_count_dec_locked() - Account a VMA removal on a BO
|
||||
* @bo: Buffer object
|
||||
*
|
||||
* Decrements vma_count.
|
||||
*
|
||||
* Caller must hold the BO's dma-resv lock.
|
||||
*/
|
||||
static inline void xe_bo_vma_count_dec_locked(struct xe_bo *bo)
|
||||
{
|
||||
xe_bo_assert_held(bo);
|
||||
|
||||
if (drm_gem_is_imported(&bo->ttm.base))
|
||||
return;
|
||||
|
||||
xe_assert(xe_bo_device(bo), bo->purgeable.vma_count > 0);
|
||||
bo->purgeable.vma_count--;
|
||||
}
|
||||
|
||||
static inline void xe_bo_unpin_map_no_vm(struct xe_bo *bo)
|
||||
{
|
||||
if (likely(bo)) {
|
||||
|
|
|
|||
|
|
@ -111,10 +111,32 @@ struct xe_bo {
|
|||
u64 min_align;
|
||||
|
||||
/**
|
||||
* @madv_purgeable: user space advise on BO purgeability, protected
|
||||
* by BO's dma-resv lock.
|
||||
* @purgeable: Purgeability state and accounting.
|
||||
*
|
||||
* All fields are protected by the BO's dma-resv lock.
|
||||
*/
|
||||
u32 madv_purgeable;
|
||||
struct {
|
||||
/**
|
||||
* @purgeable.state: BO purgeability state
|
||||
* (WILLNEED/DONTNEED/PURGED).
|
||||
*/
|
||||
u32 state;
|
||||
|
||||
/**
|
||||
* @purgeable.vma_count: Number of VMAs currently mapping this BO.
|
||||
*/
|
||||
u32 vma_count;
|
||||
|
||||
/**
|
||||
* @purgeable.willneed_count: Number of active WILLNEED holders.
|
||||
*
|
||||
* Counts WILLNEED VMAs plus active dma-buf exports for
|
||||
* non-imported BOs. The BO flips to DONTNEED on a 1->0
|
||||
* transition only when VMAs still exist; if the last VMA is
|
||||
* removed, the previous BO state is preserved.
|
||||
*/
|
||||
u32 willneed_count;
|
||||
} purgeable;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -193,6 +193,18 @@ static int xe_dma_buf_begin_cpu_access(struct dma_buf *dma_buf,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void xe_dma_buf_release(struct dma_buf *dmabuf)
|
||||
{
|
||||
struct drm_gem_object *obj = dmabuf->priv;
|
||||
struct xe_bo *bo = gem_to_xe_bo(obj);
|
||||
|
||||
xe_bo_lock(bo, false);
|
||||
xe_bo_willneed_put_locked(bo);
|
||||
xe_bo_unlock(bo);
|
||||
|
||||
drm_gem_dmabuf_release(dmabuf);
|
||||
}
|
||||
|
||||
static const struct dma_buf_ops xe_dmabuf_ops = {
|
||||
.attach = xe_dma_buf_attach,
|
||||
.detach = xe_dma_buf_detach,
|
||||
|
|
@ -200,7 +212,7 @@ static const struct dma_buf_ops xe_dmabuf_ops = {
|
|||
.unpin = xe_dma_buf_unpin,
|
||||
.map_dma_buf = xe_dma_buf_map,
|
||||
.unmap_dma_buf = xe_dma_buf_unmap,
|
||||
.release = drm_gem_dmabuf_release,
|
||||
.release = xe_dma_buf_release,
|
||||
.begin_cpu_access = xe_dma_buf_begin_cpu_access,
|
||||
.mmap = drm_gem_dmabuf_mmap,
|
||||
.vmap = drm_gem_dmabuf_vmap,
|
||||
|
|
@ -241,33 +253,33 @@ struct dma_buf *xe_gem_prime_export(struct drm_gem_object *obj, int flags)
|
|||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
xe_bo_willneed_get_locked(bo);
|
||||
xe_bo_unlock(bo);
|
||||
|
||||
ret = ttm_bo_setup_export(&bo->ttm, &ctx);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
goto out_put;
|
||||
|
||||
buf = drm_gem_prime_export(obj, flags);
|
||||
if (!IS_ERR(buf))
|
||||
buf->ops = &xe_dmabuf_ops;
|
||||
if (IS_ERR(buf)) {
|
||||
ret = PTR_ERR(buf);
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
buf->ops = &xe_dmabuf_ops;
|
||||
return buf;
|
||||
|
||||
out_put:
|
||||
xe_bo_lock(bo, false);
|
||||
xe_bo_willneed_put_locked(bo);
|
||||
out_unlock:
|
||||
xe_bo_unlock(bo);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Takes ownership of @storage: on success it is transferred to the returned
|
||||
* drm_gem_object; on failure it is freed before returning the error.
|
||||
* This matches the contract of xe_bo_init_locked() which frees @storage on
|
||||
* its error paths, so callers need not (and must not) free @storage after
|
||||
* this call.
|
||||
*/
|
||||
static struct drm_gem_object *
|
||||
xe_dma_buf_init_obj(struct drm_device *dev, struct xe_bo *storage,
|
||||
struct dma_buf *dma_buf)
|
||||
xe_dma_buf_create_obj(struct drm_device *dev, struct dma_buf *dma_buf)
|
||||
{
|
||||
struct dma_resv *resv = dma_buf->resv;
|
||||
struct xe_device *xe = to_xe_device(dev);
|
||||
|
|
@ -278,10 +290,8 @@ xe_dma_buf_init_obj(struct drm_device *dev, struct xe_bo *storage,
|
|||
int ret = 0;
|
||||
|
||||
dummy_obj = drm_gpuvm_resv_object_alloc(&xe->drm);
|
||||
if (!dummy_obj) {
|
||||
xe_bo_free(storage);
|
||||
if (!dummy_obj)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
dummy_obj->resv = resv;
|
||||
xe_validation_guard(&ctx, &xe->val, &exec, (struct xe_val_flags) {}, ret) {
|
||||
|
|
@ -290,8 +300,7 @@ xe_dma_buf_init_obj(struct drm_device *dev, struct xe_bo *storage,
|
|||
if (ret)
|
||||
break;
|
||||
|
||||
/* xe_bo_init_locked() frees storage on error */
|
||||
bo = xe_bo_init_locked(xe, storage, NULL, resv, NULL, dma_buf->size,
|
||||
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, &exec);
|
||||
drm_exec_retry_on_contention(&exec);
|
||||
|
|
@ -342,7 +351,6 @@ struct drm_gem_object *xe_gem_prime_import(struct drm_device *dev,
|
|||
const struct dma_buf_attach_ops *attach_ops;
|
||||
struct dma_buf_attachment *attach;
|
||||
struct drm_gem_object *obj;
|
||||
struct xe_bo *bo;
|
||||
|
||||
if (dma_buf->ops == &xe_dmabuf_ops) {
|
||||
obj = dma_buf->priv;
|
||||
|
|
@ -358,13 +366,15 @@ struct drm_gem_object *xe_gem_prime_import(struct drm_device *dev,
|
|||
}
|
||||
|
||||
/*
|
||||
* Don't publish the bo until we have a valid attachment, and a
|
||||
* valid attachment needs the bo address. So pre-create a bo before
|
||||
* creating the attachment and publish.
|
||||
* This needs to happen before the attach, since it will create a new
|
||||
* attachment for this, and add it to the list of attachments, at which
|
||||
* point it is globally visible, and at any point the export side can
|
||||
* call into on invalidate_mappings callback, which require a working
|
||||
* object.
|
||||
*/
|
||||
bo = xe_bo_alloc();
|
||||
if (IS_ERR(bo))
|
||||
return ERR_CAST(bo);
|
||||
obj = xe_dma_buf_create_obj(dev, dma_buf);
|
||||
if (IS_ERR(obj))
|
||||
return obj;
|
||||
|
||||
attach_ops = &xe_dma_buf_attach_ops;
|
||||
#if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST)
|
||||
|
|
@ -372,29 +382,15 @@ struct drm_gem_object *xe_gem_prime_import(struct drm_device *dev,
|
|||
attach_ops = test->attach_ops;
|
||||
#endif
|
||||
|
||||
attach = dma_buf_dynamic_attach(dma_buf, dev->dev, attach_ops, &bo->ttm.base);
|
||||
attach = dma_buf_dynamic_attach(dma_buf, dev->dev, attach_ops, obj);
|
||||
if (IS_ERR(attach)) {
|
||||
obj = ERR_CAST(attach);
|
||||
goto out_err;
|
||||
xe_bo_put(gem_to_xe_bo(obj));
|
||||
return ERR_CAST(attach);
|
||||
}
|
||||
|
||||
/*
|
||||
* xe_dma_buf_init_obj() takes ownership of bo on both success
|
||||
* and failure, so we must not touch bo after this call.
|
||||
*/
|
||||
obj = xe_dma_buf_init_obj(dev, bo, dma_buf);
|
||||
if (IS_ERR(obj)) {
|
||||
dma_buf_detach(dma_buf, attach);
|
||||
return obj;
|
||||
}
|
||||
get_dma_buf(dma_buf);
|
||||
obj->import_attach = attach;
|
||||
return obj;
|
||||
|
||||
out_err:
|
||||
xe_bo_free(bo);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST)
|
||||
|
|
|
|||
|
|
@ -144,6 +144,13 @@ struct xe_gt {
|
|||
u8 id;
|
||||
/** @info.has_indirect_ring_state: GT has indirect ring state support */
|
||||
u8 has_indirect_ring_state:1;
|
||||
/**
|
||||
* @info.has_xe2_blt_instructions: GT supports Xe2-style MEM_SET
|
||||
* and MEM_COPY blitter functionality. Note that despite the
|
||||
* name, some Xe1 platforms may also support this "Xe2-style"
|
||||
* feature.
|
||||
*/
|
||||
u8 has_xe2_blt_instructions:1;
|
||||
/**
|
||||
* @info.num_geometry_xecore_fuse_regs: Number of 32b-bit fuse
|
||||
* registers the geometry XeCore mask spans.
|
||||
|
|
|
|||
|
|
@ -1524,23 +1524,9 @@ static void emit_clear_main_copy(struct xe_gt *gt, struct xe_bb *bb,
|
|||
bb->len += len;
|
||||
}
|
||||
|
||||
static bool has_service_copy_support(struct xe_gt *gt)
|
||||
{
|
||||
/*
|
||||
* What we care about is whether the architecture was designed with
|
||||
* service copy functionality (specifically the new MEM_SET / MEM_COPY
|
||||
* instructions) so check the architectural engine list rather than the
|
||||
* actual list since these instructions are usable on BCS0 even if
|
||||
* all of the actual service copy engines (BCS1-BCS8) have been fused
|
||||
* off.
|
||||
*/
|
||||
return gt->info.engine_mask & GENMASK(XE_HW_ENGINE_BCS8,
|
||||
XE_HW_ENGINE_BCS1);
|
||||
}
|
||||
|
||||
static u32 emit_clear_cmd_len(struct xe_gt *gt)
|
||||
{
|
||||
if (has_service_copy_support(gt))
|
||||
if (gt->info.has_xe2_blt_instructions)
|
||||
return PVC_MEM_SET_CMD_LEN_DW;
|
||||
else
|
||||
return XY_FAST_COLOR_BLT_DW;
|
||||
|
|
@ -1549,7 +1535,7 @@ static u32 emit_clear_cmd_len(struct xe_gt *gt)
|
|||
static void emit_clear(struct xe_gt *gt, struct xe_bb *bb, u64 src_ofs,
|
||||
u32 size, u32 pitch, bool is_vram)
|
||||
{
|
||||
if (has_service_copy_support(gt))
|
||||
if (gt->info.has_xe2_blt_instructions)
|
||||
emit_clear_link_copy(gt, bb, src_ofs, size, pitch);
|
||||
else
|
||||
emit_clear_main_copy(gt, bb, src_ofs, size, pitch,
|
||||
|
|
|
|||
|
|
@ -851,6 +851,15 @@ static struct xe_gt *alloc_primary_gt(struct xe_tile *tile,
|
|||
gt->info.num_geometry_xecore_fuse_regs = graphics_desc->num_geometry_xecore_fuse_regs;
|
||||
gt->info.num_compute_xecore_fuse_regs = graphics_desc->num_compute_xecore_fuse_regs;
|
||||
|
||||
/*
|
||||
* Even if the service copy engines wind up being fused off, their
|
||||
* presence in the IP descriptor indicates that the platform supports
|
||||
* Xe2-style MEM_SET and MEM_COPY functionality.
|
||||
*/
|
||||
if (graphics_desc->hw_engine_mask & GENMASK(XE_HW_ENGINE_BCS8,
|
||||
XE_HW_ENGINE_BCS1))
|
||||
gt->info.has_xe2_blt_instructions = true;
|
||||
|
||||
/*
|
||||
* Before media version 13, the media IP was part of the primary GT
|
||||
* so we need to add the media engines to the primary GT's engine list.
|
||||
|
|
|
|||
|
|
@ -106,8 +106,6 @@ struct xe_tile {
|
|||
struct xe_lmtt lmtt;
|
||||
} pf;
|
||||
struct {
|
||||
/** @sriov.vf.ggtt_balloon: GGTT regions excluded from use. */
|
||||
struct xe_ggtt_node *ggtt_balloon[2];
|
||||
/** @sriov.vf.self_config: VF configuration data */
|
||||
struct xe_tile_sriov_vf_selfconfig self_config;
|
||||
} vf;
|
||||
|
|
|
|||
|
|
@ -1120,6 +1120,25 @@ static struct xe_vma *xe_vma_create(struct xe_vm *vm,
|
|||
|
||||
xe_bo_assert_held(bo);
|
||||
|
||||
/*
|
||||
* Reject only WILLNEED mappings on DONTNEED/PURGED BOs. This
|
||||
* gates new vm_bind ioctls (user supplies WILLNEED) while
|
||||
* still allowing partial-unbind / remap splits whose new VMAs
|
||||
* inherit the parent's DONTNEED attr. It must also run before
|
||||
* xe_bo_willneed_get_locked() below so a 0->1 holder bump
|
||||
* cannot silently promote DONTNEED back to WILLNEED.
|
||||
*/
|
||||
if (vma->attr.purgeable_state == XE_MADV_PURGEABLE_WILLNEED) {
|
||||
if (xe_bo_madv_is_dontneed(bo)) {
|
||||
xe_vma_free(vma);
|
||||
return ERR_PTR(-EBUSY);
|
||||
}
|
||||
if (xe_bo_is_purged(bo)) {
|
||||
xe_vma_free(vma);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
vm_bo = drm_gpuvm_bo_obtain_locked(vma->gpuva.vm, &bo->ttm.base);
|
||||
if (IS_ERR(vm_bo)) {
|
||||
xe_vma_free(vma);
|
||||
|
|
@ -1131,6 +1150,10 @@ static struct xe_vma *xe_vma_create(struct xe_vm *vm,
|
|||
vma->gpuva.gem.offset = bo_offset_or_userptr;
|
||||
drm_gpuva_link(&vma->gpuva, vm_bo);
|
||||
drm_gpuvm_bo_put(vm_bo);
|
||||
|
||||
xe_bo_vma_count_inc_locked(bo);
|
||||
if (vma->attr.purgeable_state == XE_MADV_PURGEABLE_WILLNEED)
|
||||
xe_bo_willneed_get_locked(bo);
|
||||
} else /* userptr or null */ {
|
||||
if (!is_null && !is_cpu_addr_mirror) {
|
||||
struct xe_userptr_vma *uvma = to_userptr_vma(vma);
|
||||
|
|
@ -1208,7 +1231,10 @@ static void xe_vma_destroy(struct xe_vma *vma, struct dma_fence *fence)
|
|||
xe_bo_assert_held(bo);
|
||||
|
||||
drm_gpuva_unlink(&vma->gpuva);
|
||||
xe_bo_recompute_purgeable_state(bo);
|
||||
|
||||
xe_bo_vma_count_dec_locked(bo);
|
||||
if (vma->attr.purgeable_state == XE_MADV_PURGEABLE_WILLNEED)
|
||||
xe_bo_willneed_put_locked(bo);
|
||||
}
|
||||
|
||||
xe_vm_assert_held(vm);
|
||||
|
|
@ -3016,7 +3042,7 @@ static void vm_bind_ioctl_ops_unwind(struct xe_vm *vm,
|
|||
* @res_evict: Allow evicting resources during validation
|
||||
* @validate: Perform BO validation
|
||||
* @request_decompress: Request BO decompression
|
||||
* @check_purged: Reject operation if BO is purged
|
||||
* @check_purged: Reject operation if BO is DONTNEED or PURGED
|
||||
*/
|
||||
struct xe_vma_lock_and_validate_flags {
|
||||
u32 res_evict : 1;
|
||||
|
|
@ -3030,6 +3056,7 @@ static int vma_lock_and_validate(struct drm_exec *exec, struct xe_vma *vma,
|
|||
{
|
||||
struct xe_bo *bo = xe_vma_bo(vma);
|
||||
struct xe_vm *vm = xe_vma_vm(vma);
|
||||
bool validate_bo = flags.validate;
|
||||
int err = 0;
|
||||
|
||||
if (bo) {
|
||||
|
|
@ -3044,7 +3071,11 @@ static int vma_lock_and_validate(struct drm_exec *exec, struct xe_vma *vma,
|
|||
err = -EINVAL; /* BO already purged */
|
||||
}
|
||||
|
||||
if (!err && flags.validate)
|
||||
/* Don't validate the BO for DONTNEED/PURGED remap remnants. */
|
||||
if (vma->attr.purgeable_state != XE_MADV_PURGEABLE_WILLNEED)
|
||||
validate_bo = false;
|
||||
|
||||
if (!err && validate_bo)
|
||||
err = xe_bo_validate(bo, vm,
|
||||
xe_vm_allow_vm_eviction(vm) &&
|
||||
flags.res_evict, exec);
|
||||
|
|
@ -3152,7 +3183,7 @@ static int op_lock_and_prep(struct drm_exec *exec, struct xe_vm *vm,
|
|||
op->map.immediate,
|
||||
.request_decompress =
|
||||
op->map.request_decompress,
|
||||
.check_purged = true,
|
||||
.check_purged = false,
|
||||
});
|
||||
break;
|
||||
case DRM_GPUVA_OP_REMAP:
|
||||
|
|
@ -3174,7 +3205,7 @@ static int op_lock_and_prep(struct drm_exec *exec, struct xe_vm *vm,
|
|||
.res_evict = res_evict,
|
||||
.validate = true,
|
||||
.request_decompress = false,
|
||||
.check_purged = true,
|
||||
.check_purged = false,
|
||||
});
|
||||
if (!err && op->remap.next)
|
||||
err = vma_lock_and_validate(exec, op->remap.next,
|
||||
|
|
@ -3182,7 +3213,7 @@ static int op_lock_and_prep(struct drm_exec *exec, struct xe_vm *vm,
|
|||
.res_evict = res_evict,
|
||||
.validate = true,
|
||||
.request_decompress = false,
|
||||
.check_purged = true,
|
||||
.check_purged = false,
|
||||
});
|
||||
break;
|
||||
case DRM_GPUVA_OP_UNMAP:
|
||||
|
|
@ -3211,9 +3242,11 @@ static int op_lock_and_prep(struct drm_exec *exec, struct xe_vm *vm,
|
|||
}
|
||||
|
||||
/*
|
||||
* Prefetch attempts to migrate BO's backing store without
|
||||
* repopulating it first. Purged BOs have no backing store
|
||||
* to migrate, so reject the operation.
|
||||
* PREFETCH is the only op that still gates on BO purge state.
|
||||
* MAP/REMAP handle this inside xe_vma_create() so partial
|
||||
* unbind on a DONTNEED BO still works. PREFETCH skips
|
||||
* xe_vma_create() and would migrate a BO with no backing
|
||||
* store, so reject DONTNEED/PURGED here.
|
||||
*/
|
||||
err = vma_lock_and_validate(exec,
|
||||
gpuva_to_vma(op->base.prefetch.va),
|
||||
|
|
|
|||
|
|
@ -185,147 +185,6 @@ static void madvise_pat_index(struct xe_device *xe, struct xe_vm *vm,
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_bo_is_dmabuf_shared() - Check if BO is shared via dma-buf
|
||||
* @bo: Buffer object
|
||||
*
|
||||
* Prevent marking imported or exported dma-bufs as purgeable.
|
||||
* For imported BOs, Xe doesn't own the backing store and cannot
|
||||
* safely reclaim pages (exporter or other devices may still be
|
||||
* using them). For exported BOs, external devices may have active
|
||||
* mappings we cannot track.
|
||||
*
|
||||
* Return: true if BO is imported or exported, false otherwise
|
||||
*/
|
||||
static bool xe_bo_is_dmabuf_shared(struct xe_bo *bo)
|
||||
{
|
||||
struct drm_gem_object *obj = &bo->ttm.base;
|
||||
|
||||
/* Imported: exporter owns backing store */
|
||||
if (drm_gem_is_imported(obj))
|
||||
return true;
|
||||
|
||||
/* Exported: external devices may be accessing */
|
||||
if (obj->dma_buf)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* enum xe_bo_vmas_purge_state - VMA purgeable state aggregation
|
||||
*
|
||||
* Distinguishes whether a BO's VMAs are all DONTNEED, have at least
|
||||
* one WILLNEED, or have no VMAs at all.
|
||||
*
|
||||
* Enum values align with XE_MADV_PURGEABLE_* states for consistency.
|
||||
*/
|
||||
enum xe_bo_vmas_purge_state {
|
||||
/** @XE_BO_VMAS_STATE_WILLNEED: At least one VMA is WILLNEED */
|
||||
XE_BO_VMAS_STATE_WILLNEED = 0,
|
||||
/** @XE_BO_VMAS_STATE_DONTNEED: All VMAs are DONTNEED */
|
||||
XE_BO_VMAS_STATE_DONTNEED = 1,
|
||||
/** @XE_BO_VMAS_STATE_NO_VMAS: BO has no VMAs */
|
||||
XE_BO_VMAS_STATE_NO_VMAS = 2,
|
||||
};
|
||||
|
||||
/*
|
||||
* xe_bo_recompute_purgeable_state() casts between xe_bo_vmas_purge_state and
|
||||
* xe_madv_purgeable_state. Enforce that WILLNEED=0 and DONTNEED=1 match across
|
||||
* both enums so the single-line cast is always valid.
|
||||
*/
|
||||
static_assert(XE_BO_VMAS_STATE_WILLNEED == (int)XE_MADV_PURGEABLE_WILLNEED,
|
||||
"VMA purge state WILLNEED must equal madv purgeable WILLNEED");
|
||||
static_assert(XE_BO_VMAS_STATE_DONTNEED == (int)XE_MADV_PURGEABLE_DONTNEED,
|
||||
"VMA purge state DONTNEED must equal madv purgeable DONTNEED");
|
||||
|
||||
/**
|
||||
* xe_bo_all_vmas_dontneed() - Determine BO VMA purgeable state
|
||||
* @bo: Buffer object
|
||||
*
|
||||
* Check all VMAs across all VMs to determine aggregate purgeable state.
|
||||
* Shared BOs require unanimous DONTNEED state from all mappings.
|
||||
*
|
||||
* Caller must hold BO dma-resv lock.
|
||||
*
|
||||
* Return: XE_BO_VMAS_STATE_DONTNEED if all VMAs are DONTNEED,
|
||||
* XE_BO_VMAS_STATE_WILLNEED if at least one VMA is not DONTNEED,
|
||||
* XE_BO_VMAS_STATE_NO_VMAS if BO has no VMAs
|
||||
*/
|
||||
static enum xe_bo_vmas_purge_state xe_bo_all_vmas_dontneed(struct xe_bo *bo)
|
||||
{
|
||||
struct drm_gpuvm_bo *vm_bo;
|
||||
struct drm_gpuva *gpuva;
|
||||
struct drm_gem_object *obj = &bo->ttm.base;
|
||||
bool has_vmas = false;
|
||||
|
||||
xe_bo_assert_held(bo);
|
||||
|
||||
/* Shared dma-bufs cannot be purgeable */
|
||||
if (xe_bo_is_dmabuf_shared(bo))
|
||||
return XE_BO_VMAS_STATE_WILLNEED;
|
||||
|
||||
drm_gem_for_each_gpuvm_bo(vm_bo, obj) {
|
||||
drm_gpuvm_bo_for_each_va(gpuva, vm_bo) {
|
||||
struct xe_vma *vma = gpuva_to_vma(gpuva);
|
||||
|
||||
has_vmas = true;
|
||||
|
||||
/* Any non-DONTNEED VMA prevents purging */
|
||||
if (vma->attr.purgeable_state != XE_MADV_PURGEABLE_DONTNEED)
|
||||
return XE_BO_VMAS_STATE_WILLNEED;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* No VMAs => preserve existing BO purgeable state.
|
||||
* Avoids incorrectly flipping DONTNEED -> WILLNEED when last VMA unmapped.
|
||||
*/
|
||||
if (!has_vmas)
|
||||
return XE_BO_VMAS_STATE_NO_VMAS;
|
||||
|
||||
return XE_BO_VMAS_STATE_DONTNEED;
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_bo_recompute_purgeable_state() - Recompute BO purgeable state from VMAs
|
||||
* @bo: Buffer object
|
||||
*
|
||||
* Walk all VMAs to determine if BO should be purgeable or not.
|
||||
* Shared BOs require unanimous DONTNEED state from all mappings.
|
||||
* If the BO has no VMAs the existing state is preserved.
|
||||
*
|
||||
* Locking: Caller must hold BO dma-resv lock. When iterating GPUVM lists,
|
||||
* VM lock must also be held (write) to prevent concurrent VMA modifications.
|
||||
* This is satisfied at both call sites:
|
||||
* - xe_vma_destroy(): holds vm->lock write
|
||||
* - madvise_purgeable(): holds vm->lock write (from madvise ioctl path)
|
||||
*
|
||||
* Return: nothing
|
||||
*/
|
||||
void xe_bo_recompute_purgeable_state(struct xe_bo *bo)
|
||||
{
|
||||
enum xe_bo_vmas_purge_state vma_state;
|
||||
|
||||
if (!bo)
|
||||
return;
|
||||
|
||||
xe_bo_assert_held(bo);
|
||||
|
||||
/*
|
||||
* Once purged, always purged. Cannot transition back to WILLNEED.
|
||||
* This matches i915 semantics where purged BOs are permanently invalid.
|
||||
*/
|
||||
if (bo->madv_purgeable == XE_MADV_PURGEABLE_PURGED)
|
||||
return;
|
||||
|
||||
vma_state = xe_bo_all_vmas_dontneed(bo);
|
||||
|
||||
if (vma_state != (enum xe_bo_vmas_purge_state)bo->madv_purgeable &&
|
||||
vma_state != XE_BO_VMAS_STATE_NO_VMAS)
|
||||
xe_bo_set_purgeable_state(bo, (enum xe_madv_purgeable_state)vma_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* madvise_purgeable - Handle purgeable buffer object advice
|
||||
* @xe: XE device
|
||||
|
|
@ -359,12 +218,6 @@ static void madvise_purgeable(struct xe_device *xe, struct xe_vm *vm,
|
|||
/* BO must be locked before modifying madv state */
|
||||
xe_bo_assert_held(bo);
|
||||
|
||||
/* Skip shared dma-bufs - no PTEs to zap */
|
||||
if (xe_bo_is_dmabuf_shared(bo)) {
|
||||
vmas[i]->skip_invalidation = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Once purged, always purged. Cannot transition back to WILLNEED.
|
||||
* This matches i915 semantics where purged BOs are permanently invalid.
|
||||
|
|
@ -377,13 +230,14 @@ static void madvise_purgeable(struct xe_device *xe, struct xe_vm *vm,
|
|||
|
||||
switch (op->purge_state_val.val) {
|
||||
case DRM_XE_VMA_PURGEABLE_STATE_WILLNEED:
|
||||
vmas[i]->attr.purgeable_state = XE_MADV_PURGEABLE_WILLNEED;
|
||||
vmas[i]->skip_invalidation = true;
|
||||
|
||||
xe_bo_recompute_purgeable_state(bo);
|
||||
/* Only act on a real DONTNEED -> WILLNEED transition. */
|
||||
if (vmas[i]->attr.purgeable_state == XE_MADV_PURGEABLE_DONTNEED) {
|
||||
vmas[i]->attr.purgeable_state = XE_MADV_PURGEABLE_WILLNEED;
|
||||
xe_bo_willneed_get_locked(bo);
|
||||
}
|
||||
break;
|
||||
case DRM_XE_VMA_PURGEABLE_STATE_DONTNEED:
|
||||
vmas[i]->attr.purgeable_state = XE_MADV_PURGEABLE_DONTNEED;
|
||||
/*
|
||||
* Don't zap PTEs at DONTNEED time -- pages are still
|
||||
* alive. The zap happens in xe_bo_move_notify() right
|
||||
|
|
@ -391,7 +245,11 @@ static void madvise_purgeable(struct xe_device *xe, struct xe_vm *vm,
|
|||
*/
|
||||
vmas[i]->skip_invalidation = true;
|
||||
|
||||
xe_bo_recompute_purgeable_state(bo);
|
||||
/* Only act on a real WILLNEED -> DONTNEED transition. */
|
||||
if (vmas[i]->attr.purgeable_state == XE_MADV_PURGEABLE_WILLNEED) {
|
||||
vmas[i]->attr.purgeable_state = XE_MADV_PURGEABLE_DONTNEED;
|
||||
xe_bo_willneed_put_locked(bo);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* Should never hit - values validated in madvise_args_are_sane() */
|
||||
|
|
|
|||
|
|
@ -13,6 +13,4 @@ struct xe_bo;
|
|||
int xe_vm_madvise_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file);
|
||||
|
||||
void xe_bo_recompute_purgeable_state(struct xe_bo *bo);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -448,6 +448,8 @@ void ttm_resource_add_bulk_move(struct ttm_resource *res,
|
|||
struct ttm_buffer_object *bo);
|
||||
void ttm_resource_del_bulk_move(struct ttm_resource *res,
|
||||
struct ttm_buffer_object *bo);
|
||||
void ttm_resource_del_bulk_move_unevictable(struct ttm_resource *res,
|
||||
struct ttm_buffer_object *bo);
|
||||
void ttm_resource_move_to_lru_tail(struct ttm_resource *res);
|
||||
|
||||
void ttm_resource_init(struct ttm_buffer_object *bo,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user