FAILED: patch "[PATCH] drm/nouveau: unsubscribe the channel-kill event before the" failed to apply to 6.6-stable tree
Subsystems (4)
[email protected]
maintainerThe patch below does not apply to the 6.6-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.6.y
git checkout FETCH_HEAD
git cherry-pick -x 511585987d27d8cb668acebd399fc4deda23404c
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026090931-importer-stinging-410c@gregkh' --subject-prefix 'PATCH 6.6.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 511585987d27d8cb668acebd399fc4deda23404c Mon Sep 17 00:00:00 2001
From: Marek Czernohous <[email protected]>
Date: Thu, 13 Aug 2026 01:13:27 +0200
Subject: [PATCH] drm/nouveau: unsubscribe the channel-kill event before the
fence context
nouveau_channel_del() tears the fence context down first and only drops the channel-kill subscription later, in the middle of the nvif object teardown:
if (chan->fence) nouveau_fence(chan->cli->drm)->context_del(chan); ... nvif_object_dtor(&chan->vram); nvif_event_dtor(&chan->kill);
The subscribed handler is nouveau_channel_killed(), which calls nouveau_channel_kill() and from there nouveau_fence_context_kill() on chan->fence. A kill event delivered in that window takes fctx->lock and walks fctx->pending on a fence context that context_del() has already freed.
Nothing reaches this below Fermi today, because the subscription is gated on FERMI_CHANNEL_GPFIFO and nothing kills a channel there. On Fermi and newer the window is real but narrow, since a kill has to land exactly while the channel is being destroyed. That is reason enough on its own, which is why this carries a Fixes: tag. The last patch in this series subscribes Tesla channels as well; nothing kills those today, so it does not widen the exposure now, but it is the groundwork for a recovery path that would, and the ordering is better fixed before that lands than alongside it.
Drop the subscription before anything it depends on is torn down.
drivers/gpu/drm/nouveau/nouveau_chan.c +8 -1
@@ -90,6 +90,14 @@ nouveau_channel_del(struct nouveau_channel **pchan) {struct nouveau_channel *chan = *pchan;if (chan) { +/* +* Drop the kill-event subscription first. Its handler +* dereferences chan->fence, which the fence context teardown +* below frees, so leaving it armed across the teardown leaves +* a window for a use-after-free. +*/ +nvif_event_dtor (&chan->kill); +if (chan->fence)nouveau_fence (chan->cli->drm)->context_del (chan);
@@ -100,7 +108,6 @@ nouveau_channel_del(struct nouveau_channel **pchan)nvif_object_dtor (&chan->nvsw);nvif_object_dtor (&chan->gart);nvif_object_dtor (&chan->vram); -nvif_event_dtor (&chan->kill);nvif_object_dtor (&chan->user);nvif_mem_dtor (&chan->mem_userd);nouveau_vma_del (&chan->sema.vma);
Sasha Levin
maintainerPATCH 1/3 drm/nouveau: remove nouveau_chan.device
From: Ben Skeggs <[email protected]>
[ Upstream commit 2eb58f22e2761b75e4f35847be03ddd425ae2b5e ]
nouveau_chan.device is always the same as nouveau_chan.cli.device, so there's no need to store it separately.
Stable-dep-of: 511585987d27 ("drm/nouveau: unsubscribe the channel-kill event before the fence context")
--- drivers/gpu/drm/nouveau/nouveau_chan.c | 6 +++--- drivers/gpu/drm/nouveau/nouveau_chan.h | 1 - 2 files changed, 3 insertions(+), 4 deletions(-)
drivers/gpu/drm/nouveau/nouveau_chan.c +3 -3
@@ -157,7 +157,6 @@ nouveau_channel_prep(struct nouveau_cli *cli,return -ENOMEM ; chan->cli = cli; - chan->device = device; chan->drm = drm; chan->vmm =nouveau_cli_vmm (cli);atomic_set (&chan->killed,0 );
@@ -365,8 +364,9 @@ nouveau_channel_ctor(struct nouveau_cli *cli, bool priv, u64 runm,static int nouveau_channel_init (struct nouveau_channel *chan,u32 vram,u32 gart) { -struct nvif_device *device = chan->device; -struct nouveau_drm *drm = chan->drm; +struct nouveau_cli *cli = chan->cli; +struct nouveau_drm *drm = cli->drm; +struct nvif_device *device = &cli->device;struct nv_dma_v0 args = {};int ret, i;
drivers/gpu/drm/nouveau/nouveau_chan.h +0 -1
@@ -13,7 +13,6 @@ struct nouveau_channel { } chan;struct nouveau_cli *cli; -struct nvif_device *device;struct nouveau_drm *drm;struct nouveau_vmm *vmm;
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/3 drm/nouveau: remove chan->drm
From: Ben Skeggs <[email protected]>
[ Upstream commit 3543e84e20fbd60e02264f757c77c7dbdfd1ee4a ]
The nouveau_cli that owns the channel is now stored in nouveau_chan, and it has a pointer to the drm device already.
Stable-dep-of: 511585987d27 ("drm/nouveau: unsubscribe the channel-kill event before the fence context")
--- drivers/gpu/drm/nouveau/dispnv04/crtc.c | 4 ++-- drivers/gpu/drm/nouveau/nouveau_bo0039.c | 2 +- drivers/gpu/drm/nouveau/nouveau_bo5039.c | 2 +- drivers/gpu/drm/nouveau/nouveau_chan.c | 23 +++++++++-------------- drivers/gpu/drm/nouveau/nouveau_chan.h | 1 - drivers/gpu/drm/nouveau/nouveau_dma.c | 2 +- drivers/gpu/drm/nouveau/nouveau_fence.c | 15 ++++++++------- drivers/gpu/drm/nouveau/nouveau_gem.c | 7 ++++--- drivers/gpu/drm/nouveau/nv17_fence.c | 6 +++--- drivers/gpu/drm/nouveau/nv50_fence.c | 2 +- drivers/gpu/drm/nouveau/nv84_fence.c | 8 ++++---- 11 files changed, 34 insertions(+), 38 deletions(-)
drivers/gpu/drm/nouveau/dispnv04/crtc.c +2 -2
@@ -1042,7 +1042,7 @@ nv04_finish_page_flip(struct nouveau_channel *chan, struct nv04_page_flip_state *ps) {struct nouveau_fence_chan *fctx = chan->fence; -struct nouveau_drm *drm = chan->drm; +struct nouveau_drm *drm = chan->cli->drm;struct drm_device *dev = drm->dev;struct nv04_page_flip_state *s;unsigned long flags;
@@ -1098,7 +1098,7 @@ nv04_page_flip_emit(struct nouveau_channel *chan, struct nouveau_fence **pfence) {struct nouveau_fence_chan *fctx = chan->fence; -struct nouveau_drm *drm = chan->drm; +struct nouveau_drm *drm = chan->cli->drm;struct drm_device *dev = drm->dev;struct nvif_push *push = chan->chan.push;unsigned long flags;
drivers/gpu/drm/nouveau/nouveau_bo0039.c +1 -1
@@ -104,6 +104,6 @@ nv04_bo_move_init(struct nouveau_channel *chan, u32 handle)return ret;PUSH_MTHD (push,NV039 ,SET_OBJECT , handle); -PUSH_MTHD (push,NV039 ,SET_CONTEXT_DMA_NOTIFIES , chan->drm->ntfy.handle); +PUSH_MTHD (push,NV039 ,SET_CONTEXT_DMA_NOTIFIES , chan->cli->drm->ntfy.handle);return 0 ; }
drivers/gpu/drm/nouveau/nouveau_bo5039.c +1 -1
@@ -144,7 +144,7 @@ nv50_bo_move_init(struct nouveau_channel *chan, u32 handle)return ret;PUSH_MTHD (push,NV5039 ,SET_OBJECT , handle); -PUSH_MTHD (push,NV5039 ,SET_CONTEXT_DMA_NOTIFY , chan->drm->ntfy.handle, +PUSH_MTHD (push,NV5039 ,SET_CONTEXT_DMA_NOTIFY , chan->cli->drm->ntfy.handle,SET_CONTEXT_DMA_BUFFER_IN , chan->vram.handle,SET_CONTEXT_DMA_BUFFER_OUT , chan->vram.handle);return 0 ;
drivers/gpu/drm/nouveau/nouveau_chan.c +9 -14
@@ -90,12 +90,10 @@ nouveau_channel_del(struct nouveau_channel **pchan) {struct nouveau_channel *chan = *pchan;if (chan) { -struct nouveau_cli *cli = (void *)chan->user.client; -if (chan->fence) -nouveau_fence (chan->drm)->context_del (chan); +nouveau_fence (chan->cli->drm)->context_del (chan); -if (cli) +if (nvif_object_constructed (&chan->user))nouveau_svmm_part (chan->vmm->svmm, chan->inst);nvif_object_dtor (&chan->blit);
@@ -157,7 +155,6 @@ nouveau_channel_prep(struct nouveau_cli *cli,return -ENOMEM ; chan->cli = cli; - chan->drm = drm; chan->vmm =nouveau_cli_vmm (cli);atomic_set (&chan->killed,0 );
@@ -229,12 +226,11 @@ nouveau_channel_prep(struct nouveau_cli *cli, args.limit = device->info.ram_user -1 ; } }else { -if (chan->drm->agp.bridge) { +if (drm->agp.bridge) { args.target =NV_DMA_V0_TARGET_AGP ; args.access =NV_DMA_V0_ACCESS_RDWR ; - args.start = chan->drm->agp.base; - args.limit = chan->drm->agp.base + - chan->drm->agp.size -1 ; + args.start = drm->agp.base; + args.limit = drm->agp.base + drm->agp.size -1 ; }else { args.target =NV_DMA_V0_TARGET_VM ; args.access =NV_DMA_V0_ACCESS_RDWR ;
@@ -421,12 +417,11 @@ nouveau_channel_init(struct nouveau_channel *chan, u32 vram, u32 gart) args.start =0 ; args.limit = chan->vmm->vmm.limit -1 ; }else -if (chan->drm->agp.bridge) { +if (drm->agp.bridge) { args.target =NV_DMA_V0_TARGET_AGP ; args.access =NV_DMA_V0_ACCESS_RDWR ; - args.start = chan->drm->agp.base; - args.limit = chan->drm->agp.base + - chan->drm->agp.size -1 ; + args.start = drm->agp.base; + args.limit = drm->agp.base + drm->agp.size -1 ; }else { args.target =NV_DMA_V0_TARGET_VM ; args.access =NV_DMA_V0_ACCESS_RDWR ;
@@ -491,7 +486,7 @@ nouveau_channel_init(struct nouveau_channel *chan, u32 vram, u32 gart) }/* initialise synchronisation */ -return nouveau_fence (chan->drm)->context_new (chan); +return nouveau_fence (drm)->context_new (chan); }int
drivers/gpu/drm/nouveau/nouveau_chan.h +0 -1
@@ -13,7 +13,6 @@ struct nouveau_channel { } chan;struct nouveau_cli *cli; -struct nouveau_drm *drm;struct nouveau_vmm *vmm;struct nvif_mem mem_userd;
drivers/gpu/drm/nouveau/nouveau_dma.c +1 -1
@@ -72,7 +72,7 @@ void nv50_dma_push(struct nouveau_channel *chan, u64 offset, u32 length, bool no_prefetch) { - struct nvif_user *user = &chan->drm->client.device.user; + 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;
drivers/gpu/drm/nouveau/nouveau_fence.c +8 -7
@@ -181,8 +181,9 @@ nouveau_fence_wait_uevent_handler(struct nvif_event *event, void *repv, u32 repcvoid nouveau_fence_context_new (struct nouveau_channel *chan,struct nouveau_fence_chan *fctx) { -struct nouveau_fence_priv *priv = (void *)chan->drm->fence; -struct nouveau_cli *cli = (void *)chan->user.client; +struct nouveau_cli *cli = chan->cli; +struct nouveau_drm *drm = cli->drm; +struct nouveau_fence_priv *priv = (void *)drm->fence;struct {struct nvif_event_v0 base;struct nvif_chan_event_v0 host;
@@ -193,11 +194,11 @@ nouveau_fence_context_new(struct nouveau_channel *chan, struct nouveau_fence_chaINIT_LIST_HEAD (&fctx->flip);INIT_LIST_HEAD (&fctx->pending);spin_lock_init (&fctx->lock); - fctx->context = chan->drm->runl[chan->runlist].context_base + chan->chid; + fctx->context = drm->runl[chan->runlist].context_base + chan->chid; -if (chan == chan->drm->cechan) +if (chan == drm->cechan)strcpy (fctx->name,"copy engine channel" ); -else if (chan == chan->drm->channel) +else if (chan == drm->channel)strcpy (fctx->name,"generic kernel channel" );else strcpy (fctx->name,nvxx_client (&cli->base)->name);
@@ -221,7 +222,7 @@ nouveau_fence_emit(struct nouveau_fence *fence) {struct nouveau_channel *chan =unrcu_pointer (fence->channel);struct nouveau_fence_chan *fctx = chan->fence; -struct nouveau_fence_priv *priv = (void *)chan->drm->fence; +struct nouveau_fence_priv *priv = (void *)chan->cli->drm->fence;int ret; fence->timeout = jiffies + (15 *HZ );
@@ -382,7 +383,7 @@ nouveau_fence_sync(struct nouveau_bo *nvbo, struct nouveau_channel *chan,if (i ==0 && usage ==DMA_RESV_USAGE_WRITE )continue ; - f =nouveau_local_fence (fence, chan->drm); + f =nouveau_local_fence (fence, chan->cli->drm);if (f) {struct nouveau_channel *prev;bool must_wait = true;
drivers/gpu/drm/nouveau/nouveau_gem.c +4 -3
@@ -558,10 +558,11 @@ validate_init(struct nouveau_channel *chan, struct drm_file *file_priv, }static int -validate_list (struct nouveau_channel *chan,struct nouveau_cli *cli, +validate_list (struct nouveau_channel *chan,struct list_head *list,struct drm_nouveau_gem_pushbuf_bo *pbbo) { -struct nouveau_drm *drm = chan->drm; +struct nouveau_cli *cli = chan->cli; +struct nouveau_drm *drm = cli->drm;struct nouveau_bo *nvbo;int ret, relocs =0 ;
@@ -633,7 +634,7 @@ nouveau_gem_pushbuf_validate(struct nouveau_channel *chan,return ret; } - ret =validate_list (chan, cli, &op->list, pbbo); + ret =validate_list (chan, &op->list, pbbo);if (unlikely (ret <0 )) {if (ret != -ERESTARTSYS )NV_PRINTK (err, cli,"validating bo list\n" );
drivers/gpu/drm/nouveau/nv17_fence.c +3 -3
@@ -36,8 +36,8 @@ int nv17_fence_sync(struct nouveau_fence *fence, struct nouveau_channel *prev, struct nouveau_channel *chan) { - struct nouveau_cli *cli = (void *)prev->user.client; -struct nv10_fence_priv *priv = chan->drm->fence; + struct nouveau_cli *cli = prev->cli; +struct nv10_fence_priv *priv = cli->drm->fence;struct nv10_fence_chan *fctx = chan->fence;struct nvif_push *ppush = prev->chan.push;struct nvif_push *npush = chan->chan.push;
@@ -76,7 +76,7 @@ nv17_fence_sync(struct nouveau_fence *fence,static int nv17_fence_context_new (struct nouveau_channel *chan) { -struct nv10_fence_priv *priv = chan->drm->fence; +struct nv10_fence_priv *priv = chan->cli->drm->fence;struct ttm_resource *reg = priv->bo->bo.resource;struct nv10_fence_chan *fctx;u32 start = reg->start *PAGE_SIZE ;
drivers/gpu/drm/nouveau/nv50_fence.c +1 -1
@@ -35,7 +35,7 @@static int nv50_fence_context_new (struct nouveau_channel *chan) { -struct nv10_fence_priv *priv = chan->drm->fence; +struct nv10_fence_priv *priv = chan->cli->drm->fence;struct nv10_fence_chan *fctx;struct ttm_resource *reg = priv->bo->bo.resource;u32 start = reg->start *PAGE_SIZE ;
drivers/gpu/drm/nouveau/nv84_fence.c +4 -4
@@ -79,7 +79,7 @@ nv84_fence_sync32(struct nouveau_channel *chan, u64 virtual, u32 sequence)static inline u32 nv84_fence_chid (struct nouveau_channel *chan) { -return chan->drm->runl[chan->runlist].chan_id_base + chan->chid; +return chan->cli->drm->runl[chan->runlist].chan_id_base + chan->chid; } static int
@@ -105,14 +105,14 @@ nv84_fence_sync(struct nouveau_fence *fence,static u32 nv84_fence_read (struct nouveau_channel *chan) { -struct nv84_fence_priv *priv = chan->drm->fence; +struct nv84_fence_priv *priv = chan->cli->drm->fence;return nouveau_bo_rd32 (priv->bo,nv84_fence_chid (chan) *16 /4 ); }static void nv84_fence_context_del (struct nouveau_channel *chan) { -struct nv84_fence_priv *priv = chan->drm->fence; +struct nv84_fence_priv *priv = chan->cli->drm->fence;struct nv84_fence_chan *fctx = chan->fence;nouveau_bo_wr32 (priv->bo,nv84_fence_chid (chan) *16 /4 , fctx->base.sequence);
@@ -127,7 +127,7 @@ nv84_fence_context_del(struct nouveau_channel *chan)int nv84_fence_context_new (struct nouveau_channel *chan) { -struct nv84_fence_priv *priv = chan->drm->fence; +struct nv84_fence_priv *priv = chan->cli->drm->fence;struct nv84_fence_chan *fctx;int ret;
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/3 drm/nouveau: unsubscribe the channel-kill event before the fence context
In reply to Sasha LevinFrom: Marek Czernohous <[email protected]>
[ Upstream commit 511585987d27d8cb668acebd399fc4deda23404c ]
nouveau_channel_del() tears the fence context down first and only drops the channel-kill subscription later, in the middle of the nvif object teardown:
if (chan->fence) nouveau_fence(chan->cli->drm)->context_del(chan); ... nvif_object_dtor(&chan->vram); nvif_event_dtor(&chan->kill);
The subscribed handler is nouveau_channel_killed(), which calls nouveau_channel_kill() and from there nouveau_fence_context_kill() on chan->fence. A kill event delivered in that window takes fctx->lock and walks fctx->pending on a fence context that context_del() has already freed.
Nothing reaches this below Fermi today, because the subscription is gated on FERMI_CHANNEL_GPFIFO and nothing kills a channel there. On Fermi and newer the window is real but narrow, since a kill has to land exactly while the channel is being destroyed. That is reason enough on its own, which is why this carries a Fixes: tag. The last patch in this series subscribes Tesla channels as well; nothing kills those today, so it does not widen the exposure now, but it is the groundwork for a recovery path that would, and the ordering is better fixed before that lands than alongside it.
Drop the subscription before anything it depends on is torn down.
--- drivers/gpu/drm/nouveau/nouveau_chan.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
drivers/gpu/drm/nouveau/nouveau_chan.c +8 -1
@@ -90,6 +90,14 @@ nouveau_channel_del(struct nouveau_channel **pchan) {struct nouveau_channel *chan = *pchan;if (chan) { +/* +* Drop the kill-event subscription first. Its handler +* dereferences chan->fence, which the fence context teardown +* below frees, so leaving it armed across the teardown leaves +* a window for a use-after-free. +*/ +nvif_event_dtor (&chan->kill); +if (chan->fence)nouveau_fence (chan->cli->drm)->context_del (chan);
@@ -100,7 +108,6 @@ nouveau_channel_del(struct nouveau_channel **pchan)nvif_object_dtor (&chan->nvsw);nvif_object_dtor (&chan->gart);nvif_object_dtor (&chan->vram); -nvif_event_dtor (&chan->kill);nvif_object_dtor (&chan->user);nvif_mem_dtor (&chan->mem_userd);nvif_object_dtor (&chan->push.ctxdma);
Signature · 1 line
2.53.0