Skip to content

Commit f0a2d8f

Browse files
committed
drm/amdgpu: bounded mman.gtt_window_lock acquisition via gtt_lock_timeout_ms
Add a new module parameter `gtt_lock_timeout_ms` (default 0 = unbounded, byte-identical to the current mutex_lock() behaviour) that caps the wait time for `adev->mman.gtt_window_lock` in the three amdgpu_ttm.c paths that take it: - amdgpu_ttm_copy_mem_to_mem() (SDMA buffer<->buffer copy) - amdgpu_ttm_clear_buffer() (SDMA buffer clear/wipe) - amdgpu_fill_buffer() (SDMA buffer fill) When `gtt_lock_timeout_ms > 0`, the helper `amdgpu_ttm_lock_gtt_window()` replaces the unbounded `mutex_lock()` with a trylock-and-sleep loop bounded by a wall-clock deadline. On timeout the helper returns -ETIME so the caller fails fast instead of parking on a wedged SDMA ring. Values below 100 ms are clamped up to 100 ms to avoid mis-configurations turning the lock-acquisition path into a fail-only path. Pending signals are propagated as -ERESTARTSYS. The default of 0 preserves stock behaviour byte-identically: the helper degrades to a plain `mutex_lock()` and returns 0. This avoids any behaviour change for users who do not opt in. ## Motivation In multi-tenant HIP/AMDGPU serving workloads, a single wedged SDMA ring can park every caller of amdgpu_ttm_copy_mem_to_mem() / amdgpu_ttm_clear_buffer() / amdgpu_fill_buffer() on the global `gtt_window_lock` mutex for minutes-plus while a single in-flight copy waits for the ring. The hold time is bounded only by the SDMA recovery path (which itself may need on the order of tens of seconds). The legacy unbounded mutex_lock() then converts the SDMA hang into a system-wide GTT-window stall affecting all VRAM-touching ioctls from every tenant on the device. `gtt_lock_timeout_ms` lets operators opt the GTT window contention path into a bounded-wait failure mode (return -ETIME) so the higher- level survival policy can choose whether to retry, re-queue, or surface the error to the application. ## Test plan - gtt_lock_timeout_ms unset / 0: stock behaviour, all existing tests pass byte-identically. - gtt_lock_timeout_ms=4000: under multi-tenant SDMA load, observed ~4 second worst-case wait on the lock (matching the configured deadline), with -ETIME propagated back to the caller cleanly. Reviewers may suggest using mutex_lock_killable_timeout() if preferred over the explicit trylock-and-sleep loop; the current form keeps the dependency surface minimal (no API additions). Signed-off-by: chun-wan <chun-wan@users.noreply.github.com>
1 parent d2762fd commit f0a2d8f

3 files changed

Lines changed: 84 additions & 3 deletions

File tree

drivers/gpu/drm/amd/amdgpu/amdgpu.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ extern int amdgpu_rebar;
275275
extern int amdgpu_wbrf;
276276
extern int amdgpu_user_queue;
277277
extern int amdgpu_ptl;
278+
extern int amdgpu_gtt_lock_timeout_ms;
278279

279280
#define AMDGPU_VM_MAX_NUM_CTX 4096
280281
#define AMDGPU_SG_THRESHOLD (256*1024*1024)

drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1157,6 +1157,32 @@ module_param_named(user_queue, amdgpu_user_queue, int, 0444);
11571157
MODULE_PARM_DESC(ptl, "Enable PTL (-1 = auto, 0 = disable (default), 1 = enable, 2 = permanently disable)");
11581158
module_param_named(ptl, amdgpu_ptl, int, 0444);
11591159

1160+
/**
1161+
* DOC: gtt_lock_timeout_ms (int)
1162+
*
1163+
* Maximum milliseconds to wait acquiring `mman.gtt_window_lock` in
1164+
* amdgpu_ttm_copy_mem_to_mem(), amdgpu_ttm_clear_buffer() and
1165+
* amdgpu_fill_buffer() before returning -ETIME.
1166+
*
1167+
* Default 0 keeps the historical unbounded mutex_lock() behaviour.
1168+
* Any positive value converts the acquisition to a bounded
1169+
* trylock-and-sleep loop, returning -ETIME at the deadline so the
1170+
* caller can fail fast instead of parking on a wedged ring/queue.
1171+
* Values below 100 ms are clamped up to 100 ms to avoid mis-
1172+
* configurations turning the GTT window contention path into a
1173+
* fail-only path.
1174+
*
1175+
* Validated under multi-tenant HIP/AMDGPU serving load where SDMA
1176+
* ring stalls were observed to park the GTT-window holder for
1177+
* minutes-plus. With gtt_lock_timeout_ms=4000 the callers exit
1178+
* with -ETIME and the higher-level survival policy decides whether
1179+
* to retry, re-queue, or surface the error to the application.
1180+
*/
1181+
int amdgpu_gtt_lock_timeout_ms;
1182+
MODULE_PARM_DESC(gtt_lock_timeout_ms,
1183+
"Max ms to wait on mman.gtt_window_lock acquisition before returning -ETIME (default 0 = unbounded mutex_lock)");
1184+
module_param_named(gtt_lock_timeout_ms, amdgpu_gtt_lock_timeout_ms, int, 0644);
1185+
11601186
/* These devices are not supported by amdgpu.
11611187
* They are supported by the mach64, r128, radeon drivers
11621188
*/

drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,51 @@ static int amdgpu_ttm_map_buffer(struct ttm_buffer_object *bo,
307307
* move and different for a BO to BO copy.
308308
*
309309
*/
310+
/*
311+
* amdgpu_ttm_lock_gtt_window - bounded acquire of mman.gtt_window_lock
312+
* @adev: amdgpu device whose mman.gtt_window_lock to acquire
313+
*
314+
* If the gtt_lock_timeout_ms module parameter is 0 (the default),
315+
* this function degrades to a plain mutex_lock(), preserving the
316+
* historical behaviour byte-identically.
317+
*
318+
* If gtt_lock_timeout_ms is positive, the lock is acquired via a
319+
* trylock-and-msleep loop bounded by a wall-clock deadline. On
320+
* timeout the helper returns -ETIME so the caller can fail fast.
321+
* Values below 100 ms are clamped up to 100 ms. Pending signals
322+
* are propagated as -ERESTARTSYS.
323+
*
324+
* Return: 0 on lock acquired, -ETIME on deadline, -ERESTARTSYS on
325+
* signal. Callers must call mutex_unlock(&adev->mman.gtt_window_lock)
326+
* on success.
327+
*/
328+
static int amdgpu_ttm_lock_gtt_window(struct amdgpu_device *adev)
329+
{
330+
int timeout_ms = READ_ONCE(amdgpu_gtt_lock_timeout_ms);
331+
ktime_t deadline;
332+
333+
if (timeout_ms <= 0) {
334+
mutex_lock(&adev->mman.gtt_window_lock);
335+
return 0;
336+
}
337+
338+
if (timeout_ms < 100)
339+
timeout_ms = 100;
340+
341+
if (mutex_trylock(&adev->mman.gtt_window_lock))
342+
return 0;
343+
344+
deadline = ktime_add_ms(ktime_get(), timeout_ms);
345+
while (!ktime_after(ktime_get(), deadline)) {
346+
if (mutex_trylock(&adev->mman.gtt_window_lock))
347+
return 0;
348+
if (schedule_timeout_interruptible(msecs_to_jiffies(1)) ||
349+
signal_pending(current))
350+
return -ERESTARTSYS;
351+
}
352+
return -ETIME;
353+
}
354+
310355
__attribute__((nonnull))
311356
static int amdgpu_ttm_copy_mem_to_mem(struct amdgpu_device *adev,
312357
const struct amdgpu_copy_mem *src,
@@ -331,7 +376,10 @@ static int amdgpu_ttm_copy_mem_to_mem(struct amdgpu_device *adev,
331376
amdgpu_res_first(src->mem, src->offset, size, &src_mm);
332377
amdgpu_res_first(dst->mem, dst->offset, size, &dst_mm);
333378

334-
mutex_lock(&adev->mman.gtt_window_lock);
379+
r = amdgpu_ttm_lock_gtt_window(adev);
380+
if (r)
381+
return r;
382+
335383
while (src_mm.remaining) {
336384
uint64_t from, to, cur_size, tiling_flags;
337385
uint32_t num_type, data_format, max_com, write_compress_disable;
@@ -2838,7 +2886,10 @@ int amdgpu_ttm_clear_buffer(struct amdgpu_bo *bo,
28382886

28392887
amdgpu_res_first(bo->tbo.resource, 0, amdgpu_bo_size(bo), &cursor);
28402888

2841-
mutex_lock(&adev->mman.gtt_window_lock);
2889+
r = amdgpu_ttm_lock_gtt_window(adev);
2890+
if (r)
2891+
return r;
2892+
28422893
while (cursor.remaining) {
28432894
struct dma_fence *next = NULL;
28442895
u64 size;
@@ -2892,7 +2943,10 @@ int amdgpu_fill_buffer(struct amdgpu_bo *bo,
28922943

28932944
amdgpu_res_first(bo->tbo.resource, 0, amdgpu_bo_size(bo), &dst);
28942945

2895-
mutex_lock(&adev->mman.gtt_window_lock);
2946+
r = amdgpu_ttm_lock_gtt_window(adev);
2947+
if (r)
2948+
return r;
2949+
28962950
while (dst.remaining) {
28972951
struct dma_fence *next;
28982952
uint64_t cur_size, to;

0 commit comments

Comments
 (0)