drm/nouveau/nv50-: separate CHANNEL_GPFIFO handling out from CHANNEL_DMA

Primarily a cleanup to allow for changes in newer CHANNEL_GPFIFO classes
to be more easily implemented.

Compared to the prior implementation, this submits userspace push buffer
segments as subroutines and uses the NV_RAMUSERD_TOP_LEVEL_GET registers
to track the main (kernel) push buffer progress.

Fixes a number of sporadic failures seen during piglit runs.

Signed-off-by: Ben Skeggs <bskeggs@nvidia.com>
Reviewed-by: Dave Airlie <airlied@redhat.com>
Reviewed-by: Timur Tabi <ttabi@nvidia.com>
Tested-by: Timur Tabi <ttabi@nvidia.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
This commit is contained in:
Ben Skeggs
2024-06-19 14:15:22 +10:00
committed by Dave Airlie
parent 627664de4b
commit d1fb887a08
14 changed files with 344 additions and 149 deletions

View File

@@ -0,0 +1,56 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
*/
#ifndef __NVIF_CHAN_H__
#define __NVIF_CHAN_H__
#include "push.h"
struct nvif_chan {
const struct nvif_chan_func {
struct {
u32 (*read_get)(struct nvif_chan *);
} push;
struct {
u32 (*read_get)(struct nvif_chan *);
void (*push)(struct nvif_chan *, bool main, u64 addr, u32 size,
bool no_prefetch);
void (*kick)(struct nvif_chan *);
} gpfifo;
} *func;
struct {
struct nvif_map map;
} userd;
struct {
struct nvif_map map;
u32 cur;
u32 max;
int free;
} gpfifo;
struct nvif_push push;
struct nvif_user *usermode;
u32 doorbell_token;
};
int nvif_chan_dma_wait(struct nvif_chan *, u32 push_nr);
void nvif_chan_gpfifo_ctor(const struct nvif_chan_func *, void *userd, void *gpfifo, u32 gpfifo_size,
void *push, u64 push_addr, u32 push_size, struct nvif_chan *);
int nvif_chan_gpfifo_wait(struct nvif_chan *, u32 gpfifo_nr, u32 push_nr);
void nvif_chan_gpfifo_push(struct nvif_chan *, u64 addr, u32 size, bool no_prefetch);
int nvif_chan506f_ctor(struct nvif_chan *, void *userd, void *gpfifo, u32 gpfifo_size,
void *push, u64 push_addr, u32 push_size);
u32 nvif_chan506f_read_get(struct nvif_chan *);
u32 nvif_chan506f_gpfifo_read_get(struct nvif_chan *);
void nvif_chan506f_gpfifo_push(struct nvif_chan *, bool main, u64 addr, u32 size, bool no_prefetch);
int nvif_chanc36f_ctor(struct nvif_chan *, void *userd, void *gpfifo, u32 gpfifo_size,
void *push, u64 push_addr, u32 push_size,
struct nvif_user *usermode, u32 doorbell_token);
#endif

View File

@@ -16,7 +16,7 @@ struct nvif_object {
u32 handle;
s32 oclass;
void *priv; /*XXX: hack */
struct {
struct nvif_map {
void __iomem *ptr;
u64 size;
} map;

View File

@@ -31,6 +31,12 @@ struct nvif_push {
void (*kick)(struct nvif_push *push);
struct nvif_mem mem;
u64 addr;
struct {
u32 get;
u32 max;
} hw;
u32 *bgn;
u32 *cur;
@@ -41,7 +47,7 @@ struct nvif_push {
static inline __must_check int
PUSH_WAIT(struct nvif_push *push, u32 size)
{
if (push->cur + size >= push->end) {
if (push->cur + size > push->end) {
int ret = push->wait(push, size);
if (ret)
return ret;
@@ -55,7 +61,11 @@ PUSH_WAIT(struct nvif_push *push, u32 size)
static inline int
PUSH_KICK(struct nvif_push *push)
{
push->kick(push);
if (push->cur != push->bgn) {
push->kick(push);
push->bgn = push->cur;
}
return 0;
}

View File

@@ -416,7 +416,7 @@ nouveau_abi16_ioctl_channel_alloc(ABI16_IOCTL_ARGS)
*/
if (nouveau_cli_uvmm(cli)) {
ret = nouveau_sched_create(&chan->sched, drm, drm->sched_wq,
chan->chan->dma.ib_max);
chan->chan->chan.gpfifo.max);
if (ret)
goto done;
}

View File

@@ -424,25 +424,24 @@ nouveau_channel_init(struct nouveau_channel *chan, u32 vram, u32 gart)
}
/* initialise dma tracking parameters */
switch (chan->user.oclass) {
case NV03_CHANNEL_DMA:
case NV10_CHANNEL_DMA:
case NV17_CHANNEL_DMA:
case NV40_CHANNEL_DMA:
if (chan->user.oclass < NV50_CHANNEL_GPFIFO) {
chan->user_put = 0x40;
chan->user_get = 0x44;
chan->dma.max = (0x10000 / 4) - 2;
break;
default:
chan->user_put = 0x40;
chan->user_get = 0x44;
chan->user_get_hi = 0x60;
chan->dma.ib_base = 0x10000 / 4;
chan->dma.ib_max = NV50_DMA_IB_MAX;
chan->dma.ib_put = 0;
chan->dma.ib_free = chan->dma.ib_max - chan->dma.ib_put;
chan->dma.max = chan->dma.ib_base;
break;
} else
if (chan->user.oclass < VOLTA_CHANNEL_GPFIFO_A) {
ret = nvif_chan506f_ctor(&chan->chan, chan->userd->map.ptr,
(u8*)chan->push.buffer->kmap.virtual + 0x10000, 0x2000,
chan->push.buffer->kmap.virtual, chan->push.addr, 0x10000);
if (ret)
return ret;
} else {
ret = nvif_chanc36f_ctor(&chan->chan, chan->userd->map.ptr,
(u8*)chan->push.buffer->kmap.virtual + 0x10000, 0x2000,
chan->push.buffer->kmap.virtual, chan->push.addr, 0x10000,
&drm->client.device.user, chan->token);
if (ret)
return ret;
}
chan->dma.put = 0;

View File

@@ -3,13 +3,11 @@
#define __NOUVEAU_CHAN_H__
#include <nvif/object.h>
#include <nvif/event.h>
#include <nvif/push.h>
#include <nvif/chan.h>
struct nvif_device;
struct nouveau_channel {
struct {
struct nvif_push push;
} chan;
struct nvif_chan chan;
struct nouveau_cli *cli;
struct nouveau_vmm *vmm;
@@ -41,12 +39,7 @@ struct nouveau_channel {
int free;
int cur;
int put;
int ib_base;
int ib_max;
int ib_free;
int ib_put;
} dma;
u32 user_get_hi;
u32 user_get;
u32 user_put;

View File

@@ -43,8 +43,6 @@ READ_GET(struct nouveau_channel *chan, uint64_t *prev_get, int *timeout)
uint64_t val;
val = nvif_rd32(chan->userd, chan->user_get);
if (chan->user_get_hi)
val |= (uint64_t)nvif_rd32(chan->userd, chan->user_get_hi) << 32;
/* reset counter as long as GET is still advancing, this is
* to avoid misdetecting a GPU lockup if the GPU happens to
@@ -68,111 +66,12 @@ READ_GET(struct nouveau_channel *chan, uint64_t *prev_get, int *timeout)
return (val - chan->push.addr) >> 2;
}
void
nv50_dma_push(struct nouveau_channel *chan, u64 offset, u32 length,
bool no_prefetch)
{
struct nvif_user *user = &chan->cli->drm->client.device.user;
struct nouveau_bo *pb = chan->push.buffer;
int ip = (chan->dma.ib_put * 2) + chan->dma.ib_base;
BUG_ON(chan->dma.ib_free < 1);
WARN_ON(length > NV50_DMA_PUSH_MAX_LENGTH);
nouveau_bo_wr32(pb, ip++, lower_32_bits(offset));
nouveau_bo_wr32(pb, ip++, upper_32_bits(offset) | length << 8 |
(no_prefetch ? (1 << 31) : 0));
chan->dma.ib_put = (chan->dma.ib_put + 1) & chan->dma.ib_max;
mb();
/* Flush writes. */
nouveau_bo_rd32(pb, 0);
nvif_wr32(chan->userd, 0x8c, chan->dma.ib_put);
if (user->func && user->func->doorbell)
user->func->doorbell(user, chan->token);
chan->dma.ib_free--;
}
static int
nv50_dma_push_wait(struct nouveau_channel *chan, int count)
{
uint32_t cnt = 0, prev_get = 0;
while (chan->dma.ib_free < count) {
uint32_t get = nvif_rd32(chan->userd, 0x88);
if (get != prev_get) {
prev_get = get;
cnt = 0;
}
if ((++cnt & 0xff) == 0) {
udelay(1);
if (cnt > 100000)
return -EBUSY;
}
chan->dma.ib_free = get - chan->dma.ib_put;
if (chan->dma.ib_free <= 0)
chan->dma.ib_free += chan->dma.ib_max;
}
return 0;
}
static int
nv50_dma_wait(struct nouveau_channel *chan, int slots, int count)
{
uint64_t prev_get = 0;
int ret, cnt = 0;
ret = nv50_dma_push_wait(chan, slots + 1);
if (unlikely(ret))
return ret;
while (chan->dma.free < count) {
int get = READ_GET(chan, &prev_get, &cnt);
if (unlikely(get < 0)) {
if (get == -EINVAL)
continue;
return get;
}
if (get <= chan->dma.cur) {
chan->dma.free = chan->dma.max - chan->dma.cur;
if (chan->dma.free >= count)
break;
FIRE_RING(chan);
do {
get = READ_GET(chan, &prev_get, &cnt);
if (unlikely(get < 0)) {
if (get == -EINVAL)
continue;
return get;
}
} while (get == 0);
chan->dma.cur = 0;
chan->dma.put = 0;
}
chan->dma.free = get - chan->dma.cur - 1;
}
return 0;
}
int
nouveau_dma_wait(struct nouveau_channel *chan, int slots, int size)
nouveau_dma_wait(struct nouveau_channel *chan, int size)
{
uint64_t prev_get = 0;
int cnt = 0, get;
if (chan->dma.ib_max)
return nv50_dma_wait(chan, slots, size);
while (chan->dma.free < size) {
get = READ_GET(chan, &prev_get, &cnt);
if (unlikely(get == -EBUSY))

View File

@@ -30,9 +30,7 @@
#include "nouveau_bo.h"
#include "nouveau_chan.h"
int nouveau_dma_wait(struct nouveau_channel *, int slots, int size);
void nv50_dma_push(struct nouveau_channel *, u64 addr, u32 length,
bool no_prefetch);
int nouveau_dma_wait(struct nouveau_channel *, int size);
/*
* There's a hw race condition where you can't jump to your PUT offset,
@@ -67,7 +65,7 @@ RING_SPACE(struct nouveau_channel *chan, int size)
{
int ret;
ret = nouveau_dma_wait(chan, 1, size);
ret = nouveau_dma_wait(chan, size);
if (ret)
return ret;
@@ -94,12 +92,7 @@ FIRE_RING(struct nouveau_channel *chan)
return;
chan->accel_done = true;
if (chan->dma.ib_max) {
nv50_dma_push(chan, chan->push.addr + (chan->dma.put << 2),
(chan->dma.cur - chan->dma.put) << 2, false);
} else {
WRITE_PUT(chan->dma.cur);
}
WRITE_PUT(chan->dma.cur);
chan->dma.put = chan->dma.cur;
}

View File

@@ -10,6 +10,8 @@
#include "nouveau_sched.h"
#include "nouveau_uvmm.h"
#include <nvif/class.h>
/**
* DOC: Overview
*
@@ -131,7 +133,7 @@ nouveau_exec_job_run(struct nouveau_job *job)
struct nouveau_fence *fence = exec_job->fence;
int i, ret;
ret = nouveau_dma_wait(chan, exec_job->push.count + 1, 16);
ret = nvif_chan_gpfifo_wait(&chan->chan, exec_job->push.count + 1, 16);
if (ret) {
NV_PRINTK(err, job->cli, "nv50cal_space: %d\n", ret);
return ERR_PTR(ret);
@@ -141,7 +143,7 @@ nouveau_exec_job_run(struct nouveau_job *job)
struct drm_nouveau_exec_push *p = &exec_job->push.s[i];
bool no_prefetch = p->flags & DRM_NOUVEAU_EXEC_PUSH_NO_PREFETCH;
nv50_dma_push(chan, p->va, p->va_len, no_prefetch);
nvif_chan_gpfifo_push(&chan->chan, p->va, p->va_len, no_prefetch);
}
ret = nouveau_fence_emit(fence);
@@ -375,10 +377,10 @@ nouveau_exec_ioctl_exec(struct drm_device *dev,
if (unlikely(atomic_read(&chan->killed)))
return nouveau_abi16_put(abi16, -ENODEV);
if (!chan->dma.ib_max)
if (chan->user.oclass < NV50_CHANNEL_GPFIFO)
return nouveau_abi16_put(abi16, -ENOSYS);
push_max = nouveau_exec_push_max_from_ib_max(chan->dma.ib_max);
push_max = nouveau_exec_push_max_from_ib_max(chan->chan.gpfifo.max);
if (unlikely(req->push_count > push_max)) {
NV_PRINTK(err, cli, "pushbuf push count exceeds limit: %d max %d\n",
req->push_count, push_max);

View File

@@ -850,8 +850,8 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
}
}
if (chan->dma.ib_max) {
ret = nouveau_dma_wait(chan, req->nr_push + 1, 16);
if (chan->user.oclass >= NV50_CHANNEL_GPFIFO) {
ret = nvif_chan_gpfifo_wait(&chan->chan, req->nr_push + 1, 16);
if (ret) {
NV_PRINTK(err, cli, "nv50cal_space: %d\n", ret);
goto out;
@@ -864,7 +864,7 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
u32 length = push[i].length & ~NOUVEAU_GEM_PUSHBUF_NO_PREFETCH;
bool no_prefetch = push[i].length & NOUVEAU_GEM_PUSHBUF_NO_PREFETCH;
nv50_dma_push(chan, addr, length, no_prefetch);
nvif_chan_gpfifo_push(&chan->chan, addr, length, no_prefetch);
}
} else
if (drm->client.device.info.chipset >= 0x25) {
@@ -958,7 +958,7 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
u_free(push);
out_next:
if (chan->dma.ib_max) {
if (chan->user.oclass >= NV50_CHANNEL_GPFIFO) {
req->suffix0 = 0x00000000;
req->suffix1 = 0x00000000;
} else

View File

@@ -14,6 +14,11 @@ nvif-y += nvif/outp.o
nvif-y += nvif/timer.o
nvif-y += nvif/vmm.o
# Channel classes
nvif-y += nvif/chan.o
nvif-y += nvif/chan506f.o
nvif-y += nvif/chanc36f.o
# Usermode classes
nvif-y += nvif/user.o
nvif-y += nvif/userc361.o

View File

@@ -0,0 +1,127 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
*/
#include <nvif/chan.h>
static void
nvif_chan_gpfifo_push_kick(struct nvif_push *push)
{
struct nvif_chan *chan = container_of(push, typeof(*chan), push);
u32 put = push->bgn - (u32 *)chan->push.mem.object.map.ptr;
u32 cnt = push->cur - push->bgn;
chan->func->gpfifo.push(chan, true, chan->push.addr + (put << 2), cnt << 2, false);
chan->func->gpfifo.kick(chan);
}
static int
nvif_chan_gpfifo_push_wait(struct nvif_push *push, u32 push_nr)
{
struct nvif_chan *chan = container_of(push, typeof(*chan), push);
return nvif_chan_gpfifo_wait(chan, 1, push_nr);
}
void
nvif_chan_gpfifo_push(struct nvif_chan *chan, u64 addr, u32 size, bool no_prefetch)
{
chan->func->gpfifo.push(chan, false, addr, size, no_prefetch);
}
int
nvif_chan_gpfifo_wait(struct nvif_chan *chan, u32 gpfifo_nr, u32 push_nr)
{
struct nvif_push *push = &chan->push;
int ret = 0, time = 1000000;
/* Account for the GPFIFO entry needed to submit pushbuf. */
if (push_nr)
gpfifo_nr++;
/* Wait for space in main push buffer. */
if (push->cur + push_nr > push->end) {
ret = nvif_chan_dma_wait(chan, push_nr);
if (ret)
return ret;
}
/* Wait for GPFIFO space. */
while (chan->gpfifo.free < gpfifo_nr) {
chan->gpfifo.free = chan->func->gpfifo.read_get(chan) - chan->gpfifo.cur - 1;
if (chan->gpfifo.free < 0)
chan->gpfifo.free += chan->gpfifo.max + 1;
if (chan->gpfifo.free < gpfifo_nr) {
if (!time--)
return -ETIMEDOUT;
udelay(1);
}
}
return 0;
}
void
nvif_chan_gpfifo_ctor(const struct nvif_chan_func *func, void *userd, void *gpfifo, u32 gpfifo_size,
void *push, u64 push_addr, u32 push_size, struct nvif_chan *chan)
{
chan->func = func;
chan->userd.map.ptr = userd;
chan->gpfifo.map.ptr = gpfifo;
chan->gpfifo.max = (gpfifo_size >> 3) - 1;
chan->gpfifo.free = chan->gpfifo.max;
chan->push.mem.object.map.ptr = push;
chan->push.wait = nvif_chan_gpfifo_push_wait;
chan->push.kick = nvif_chan_gpfifo_push_kick;
chan->push.addr = push_addr;
chan->push.hw.max = push_size >> 2;
chan->push.bgn = chan->push.cur = chan->push.end = push;
}
int
nvif_chan_dma_wait(struct nvif_chan *chan, u32 nr)
{
struct nvif_push *push = &chan->push;
u32 cur = push->cur - (u32 *)push->mem.object.map.ptr;
u32 free, time = 1000000;
do {
u32 get = chan->func->push.read_get(chan);
if (get <= cur) {
free = push->hw.max - cur;
if (free >= nr)
break;
PUSH_KICK(push);
while (get == 0) {
get = chan->func->push.read_get(chan);
if (get == 0) {
if (!time--)
return -ETIMEDOUT;
udelay(1);
}
}
cur = 0;
}
free = get - cur - 1;
if (free < nr) {
if (!time--)
return -ETIMEDOUT;
udelay(1);
}
} while (free < nr);
push->bgn = (u32 *)push->mem.object.map.ptr + cur;
push->cur = push->bgn;
push->end = push->bgn + free;
return 0;
}

View File

@@ -0,0 +1,72 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
*/
#include <nvif/chan.h>
static void
nvif_chan506f_gpfifo_kick(struct nvif_chan *chan)
{
wmb();
nvif_wr32(&chan->userd, 0x8c, chan->gpfifo.cur);
}
void
nvif_chan506f_gpfifo_push(struct nvif_chan *chan, bool main, u64 addr, u32 size, bool no_prefetch)
{
u32 gpptr = chan->gpfifo.cur << 3;
if (WARN_ON(!chan->gpfifo.free))
return;
nvif_wr32(&chan->gpfifo, gpptr + 0, lower_32_bits(addr));
nvif_wr32(&chan->gpfifo, gpptr + 4, upper_32_bits(addr) |
(main ? 0 : BIT(9)) |
(size >> 2) << 10 |
(no_prefetch ? BIT(31) : 0));
chan->gpfifo.cur = (chan->gpfifo.cur + 1) & chan->gpfifo.max;
chan->gpfifo.free--;
if (!chan->gpfifo.free)
chan->push.end = chan->push.cur;
}
u32
nvif_chan506f_gpfifo_read_get(struct nvif_chan *chan)
{
return nvif_rd32(&chan->userd, 0x88);
}
u32
nvif_chan506f_read_get(struct nvif_chan *chan)
{
u32 tlgetlo = nvif_rd32(&chan->userd, 0x58);
u32 tlgethi = nvif_rd32(&chan->userd, 0x5c);
struct nvif_push *push = &chan->push;
/* Update cached GET pointer if TOP_LEVEL_GET is valid. */
if (tlgethi & BIT(31)) {
u64 tlget = ((u64)(tlgethi & 0xff) << 32) | tlgetlo;
push->hw.get = (tlget - push->addr) >> 2;
}
return push->hw.get;
}
static const struct nvif_chan_func
nvif_chan506f = {
.push.read_get = nvif_chan506f_read_get,
.gpfifo.read_get = nvif_chan506f_gpfifo_read_get,
.gpfifo.push = nvif_chan506f_gpfifo_push,
.gpfifo.kick = nvif_chan506f_gpfifo_kick,
};
int
nvif_chan506f_ctor(struct nvif_chan *chan, void *userd, void *gpfifo, u32 gpfifo_size,
void *push, u64 push_addr, u32 push_size)
{
nvif_chan_gpfifo_ctor(&nvif_chan506f, userd, gpfifo, gpfifo_size,
push, push_addr, push_size, chan);
return 0;
}

View File

@@ -0,0 +1,39 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
*/
#include <nvif/chan.h>
#include <nvif/user.h>
static void
nvif_chanc36f_gpfifo_kick(struct nvif_chan *chan)
{
struct nvif_user *usermode = chan->usermode;
nvif_wr32(&chan->userd, 0x8c, chan->gpfifo.cur);
wmb(); /* ensure CPU writes are flushed to BAR1 */
nvif_rd32(&chan->userd, 0); /* ensure BAR1 writes are flushed to vidmem */
usermode->func->doorbell(usermode, chan->doorbell_token);
}
static const struct nvif_chan_func
nvif_chanc36f = {
.push.read_get = nvif_chan506f_read_get,
.gpfifo.read_get = nvif_chan506f_gpfifo_read_get,
.gpfifo.push = nvif_chan506f_gpfifo_push,
.gpfifo.kick = nvif_chanc36f_gpfifo_kick,
};
int
nvif_chanc36f_ctor(struct nvif_chan *chan, void *userd, void *gpfifo, u32 gpfifo_size,
void *push, u64 push_addr, u32 push_size,
struct nvif_user *usermode, u32 doorbell_token)
{
nvif_chan_gpfifo_ctor(&nvif_chanc36f, userd, gpfifo, gpfifo_size,
push, push_addr, push_size, chan);
chan->usermode = usermode;
chan->doorbell_token = doorbell_token;
return 0;
}