nouveau: handle realloc failure inside cli_kref_set

Closes: https://gitlab.freedesktop.org/mesa/mesa/-/issues/11477
Fixes: 821f4c8d99 ("nouveau: import libdrm_nouveau")
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/30374>
(cherry picked from commit 801078cbf8)
This commit is contained in:
Karol Herbst 2024-07-26 00:42:59 +02:00 committed by Eric Engestrom
parent fdb48555da
commit f2a0f842e2
3 changed files with 30 additions and 10 deletions

View file

@ -7224,7 +7224,7 @@
"description": "nouveau: handle realloc failure inside cli_kref_set",
"nominated": true,
"nomination_type": 1,
"resolution": 0,
"resolution": 1,
"main_sha": null,
"because_sha": "821f4c8d99a3068758db834a5c219082a9609b3c",
"notes": null

View file

@ -94,7 +94,8 @@ PUSH_KICK(struct nouveau_pushbuf *push)
{
struct nouveau_pushbuf_priv *ppush = push->user_priv;
simple_mtx_lock(&ppush->screen->fence.lock);
nouveau_pushbuf_kick(push);
int ASSERTED ret = nouveau_pushbuf_kick(push);
assert(!ret);
simple_mtx_unlock(&ppush->screen->fence.lock);
}

View file

@ -832,13 +832,18 @@ cli_kref_get(struct nouveau_client *client, struct nouveau_bo *bo)
return kref;
}
static inline void
static inline int
cli_kref_set(struct nouveau_client *client, struct nouveau_bo *bo,
struct drm_nouveau_gem_pushbuf_bo *kref, struct nouveau_pushbuf *push)
{
struct nouveau_client_priv *pcli = nouveau_client(client);
if (pcli->kref_nr <= bo->handle) {
pcli->kref = realloc(pcli->kref, sizeof(*pcli->kref) * bo->handle * 2);
void *new_ptr = realloc(pcli->kref, sizeof(*pcli->kref) * bo->handle * 2);
if (!new_ptr) {
err("Failed to realloc memory, expect faulty rendering.\n");
return -ENOMEM;
}
pcli->kref = new_ptr;
while (pcli->kref_nr < bo->handle * 2) {
pcli->kref[pcli->kref_nr].kref = NULL;
pcli->kref[pcli->kref_nr].push = NULL;
@ -847,6 +852,7 @@ cli_kref_set(struct nouveau_client *client, struct nouveau_bo *bo,
}
pcli->kref[bo->handle].kref = kref;
pcli->kref[bo->handle].push = push;
return 0;
}
int
@ -1171,7 +1177,9 @@ pushbuf_flush(struct nouveau_pushbuf *push)
kref = krec->buffer;
for (i = 0; i < krec->nr_buffer; i++, kref++) {
struct nouveau_bo *bo = (void *)(unsigned long)kref->user_priv;
cli_kref_set(push->client, bo, NULL, NULL);
ret = cli_kref_set(push->client, bo, NULL, NULL);
if (ret)
return ret;
nouveau_bo_ref(NULL, &bo);
}
@ -1267,6 +1275,7 @@ pushbuf_kref(struct nouveau_pushbuf *push, struct nouveau_bo *bo, uint32_t flags
struct nouveau_pushbuf *fpush;
struct drm_nouveau_gem_pushbuf_bo *kref;
uint32_t domains, domains_wr, domains_rd;
int ret;
domains = 0;
if (flags & NOUVEAU_BO_VRAM)
@ -1322,7 +1331,9 @@ pushbuf_kref(struct nouveau_pushbuf *push, struct nouveau_bo *bo, uint32_t flags
else
kref->presumed.domain = NOUVEAU_GEM_DOMAIN_GART;
cli_kref_set(push->client, bo, kref, push);
ret = cli_kref_set(push->client, bo, kref, push);
if (ret)
return NULL;
p_atomic_inc(&nouveau_bo(bo)->refcnt);
}
@ -1369,22 +1380,26 @@ pushbuf_krel(struct nouveau_pushbuf *push, struct nouveau_bo *bo,
return reloc;
}
static void
static int
pushbuf_refn_fail(struct nouveau_pushbuf *push, int sref, int srel)
{
struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push);
struct nouveau_pushbuf_krec *krec = nvpb->krec;
struct drm_nouveau_gem_pushbuf_bo *kref;
int ret;
kref = krec->buffer + sref;
while (krec->nr_buffer-- > sref) {
struct nouveau_bo *bo = (void *)(unsigned long)kref->user_priv;
cli_kref_set(push->client, bo, NULL, NULL);
ret = cli_kref_set(push->client, bo, NULL, NULL);
if (ret)
return ret;
nouveau_bo_ref(NULL, &bo);
kref++;
}
krec->nr_buffer = sref;
krec->nr_reloc = srel;
return 0;
}
static int
@ -1406,7 +1421,9 @@ pushbuf_refn(struct nouveau_pushbuf *push, bool retry,
}
if (ret) {
pushbuf_refn_fail(push, sref, krec->nr_reloc);
ret = pushbuf_refn_fail(push, sref, krec->nr_reloc);
if (ret)
return ret;
if (retry) {
pushbuf_flush(push);
nouveau_pushbuf_space(push, 0, 0, 0);
@ -1457,7 +1474,9 @@ pushbuf_validate(struct nouveau_pushbuf *push, bool retry)
list_inithead(&bctx->pending);
if (ret) {
pushbuf_refn_fail(push, sref, srel);
ret = pushbuf_refn_fail(push, sref, srel);
if (ret)
return ret;
if (retry) {
pushbuf_flush(push);
return pushbuf_validate(push, false);