1 /* $NetBSD: i915_gem_busy.c,v 1.4 2023/05/12 10:13:37 riastradh Exp $ */ 2 3 /* 4 * SPDX-License-Identifier: MIT 5 * 6 * Copyright 2014-2016 Intel Corporation 7 */ 8 9 #include <sys/cdefs.h> 10 __KERNEL_RCSID(0, "$NetBSD: i915_gem_busy.c,v 1.4 2023/05/12 10:13:37 riastradh Exp $"); 11 12 #include "gt/intel_engine.h" 13 14 #include "i915_gem_ioctls.h" 15 #include "i915_gem_object.h" 16 17 static __always_inline u32 __busy_read_flag(u16 id) 18 { 19 if (id == (u16)I915_ENGINE_CLASS_INVALID) 20 return 0xffff0000u; 21 22 GEM_BUG_ON(id >= 16); 23 return 0x10000u << id; 24 } 25 26 static __always_inline u32 __busy_write_id(u16 id) 27 { 28 /* 29 * The uABI guarantees an active writer is also amongst the read 30 * engines. This would be true if we accessed the activity tracking 31 * under the lock, but as we perform the lookup of the object and 32 * its activity locklessly we can not guarantee that the last_write 33 * being active implies that we have set the same engine flag from 34 * last_read - hence we always set both read and write busy for 35 * last_write. 36 */ 37 if (id == (u16)I915_ENGINE_CLASS_INVALID) 38 return 0xffffffffu; 39 40 return (id + 1) | __busy_read_flag(id); 41 } 42 43 static __always_inline unsigned int 44 __busy_set_if_active(const struct dma_fence *fence, u32 (*flag)(u16 id)) 45 { 46 const struct i915_request *rq; 47 48 /* 49 * We have to check the current hw status of the fence as the uABI 50 * guarantees forward progress. We could rely on the idle worker 51 * to eventually flush us, but to minimise latency just ask the 52 * hardware. 53 * 54 * Note we only report on the status of native fences. 55 */ 56 if (!dma_fence_is_i915(fence)) 57 return 0; 58 59 /* opencode to_request() in order to avoid const warnings */ 60 rq = const_container_of(fence, struct i915_request, fence); 61 if (i915_request_completed(rq)) 62 return 0; 63 64 /* Beware type-expansion follies! */ 65 BUILD_BUG_ON(!typecheck(u16, rq->engine->uabi_class)); 66 return flag(rq->engine->uabi_class); 67 } 68 69 static __always_inline unsigned int 70 busy_check_reader(const struct dma_fence *fence) 71 { 72 return __busy_set_if_active(fence, __busy_read_flag); 73 } 74 75 static __always_inline unsigned int 76 busy_check_writer(const struct dma_fence *fence) 77 { 78 if (!fence) 79 return 0; 80 81 return __busy_set_if_active(fence, __busy_write_id); 82 } 83 84 int 85 i915_gem_busy_ioctl(struct drm_device *dev, void *data, 86 struct drm_file *file) 87 { 88 struct drm_i915_gem_busy *args = data; 89 struct drm_i915_gem_object *obj; 90 struct dma_resv_list *list; 91 unsigned int seq; 92 int err; 93 94 err = -ENOENT; 95 rcu_read_lock(); 96 obj = i915_gem_object_lookup_rcu(file, args->handle); 97 if (!obj) 98 goto out; 99 100 /* 101 * A discrepancy here is that we do not report the status of 102 * non-i915 fences, i.e. even though we may report the object as idle, 103 * a call to set-domain may still stall waiting for foreign rendering. 104 * This also means that wait-ioctl may report an object as busy, 105 * where busy-ioctl considers it idle. 106 * 107 * We trade the ability to warn of foreign fences to report on which 108 * i915 engines are active for the object. 109 * 110 * Alternatively, we can trade that extra information on read/write 111 * activity with 112 * args->busy = 113 * !dma_resv_test_signaled_rcu(obj->resv, true); 114 * to report the overall busyness. This is what the wait-ioctl does. 115 * 116 */ 117 retry: 118 seq = raw_read_seqcount(&obj->base.resv->seq); 119 120 /* Translate the exclusive fence to the READ *and* WRITE engine */ 121 args->busy = 122 busy_check_writer(rcu_dereference(obj->base.resv->fence_excl)); 123 124 /* Translate shared fences to READ set of engines */ 125 list = rcu_dereference(obj->base.resv->fence); 126 if (list) { 127 unsigned int shared_count = list->shared_count, i; 128 129 for (i = 0; i < shared_count; ++i) { 130 struct dma_fence *fence = 131 rcu_dereference(list->shared[i]); 132 133 if (read_seqcount_retry(&obj->base.resv->seq, seq)) 134 goto retry; 135 args->busy |= busy_check_reader(fence); 136 } 137 } 138 139 if (args->busy && read_seqcount_retry(&obj->base.resv->seq, seq)) 140 goto retry; 141 142 err = 0; 143 out: 144 rcu_read_unlock(); 145 return err; 146 } 147