FAILED: patch "[PATCH] drm/nouveau/uvmm: fix NULL deref unwinding an OP_MAP_SPARSE" 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 412a6ceb56d501ef2f8202e26ab4b5d4dfbca566
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026090959-unbalance-tubby-e048@gregkh' --subject-prefix 'PATCH 6.6.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 412a6ceb56d501ef2f8202e26ab4b5d4dfbca566 Mon Sep 17 00:00:00 2001
From: Zhenhao Wan <[email protected]>
Date: Tue, 11 Aug 2026 16:46:28 +0800
Subject: [PATCH] drm/nouveau/uvmm: fix NULL deref unwinding an OP_MAP_SPARSE
op
Each bind_job_op is zeroed by kzalloc_obj() in bind_job_op_from_uop(), and the OP_MAP_SPARSE case in nouveau_uvmm_bind_job_submit() only creates a region, so op->ops stays NULL for a successfully processed sparse map.
If a later op in the same job fails, the reverse unwind loop revisits that op and calls drm_gpuva_ops_free(&uvmm->base, op->ops) unconditionally. drm_gpuva_ops_free() dereferences its argument right away (list_for_each_entry_safe on &ops->list), so a NULL op->ops oopses. The path is reachable by any render-node fd holder, since NOUVEAU_VM_BIND is DRM_RENDER_ALLOW.
Guard the free with IS_ERR_OR_NULL(), as nouveau_uvmm_bind_job_cleanup() already does for the identical free.
drivers/gpu/drm/nouveau/nouveau_uvmm.c +2 -1
@@ -1489,7 +1489,8 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job,break ; } -drm_gpuva_ops_free (&uvmm->base, op->ops); +if (!IS_ERR_OR_NULL (op->ops)) +drm_gpuva_ops_free (&uvmm->base, op->ops); op->ops =NULL ; op->reg =NULL ; }
Sasha Levin
maintainerPATCH 1/3 drm/gpuvm: rename struct drm_gpuva_manager to struct drm_gpuvm
From: Danilo Krummrich <[email protected]>
[ Upstream commit f72c2db47080523d5e0f3c20846c96ed31c35648 ]
Rename struct drm_gpuva_manager to struct drm_gpuvm including corresponding functions. This way the GPUVA manager's structures align very well with the documentation of VM_BIND [1] and VM_BIND locking [2].
It also provides a better foundation for the naming of data structures and functions introduced for implementing a common dma-resv per GPU-VM including tracking of external and evicted objects in subsequent patches.
[1] Documentation/gpu/drm-vm-bind-async.rst
[2] Documentation/gpu/drm-vm-bind-locking.rst
Stable-dep-of: 412a6ceb56d5 ("drm/nouveau/uvmm: fix NULL deref unwinding an OP_MAP_SPARSE op")
---
drivers/gpu/drm/Makefile | 2 +-
drivers/gpu/drm/drm_debugfs.c | 16 +-
.../gpu/drm/{drm_gpuva_mgr.c => drm_gpuvm.c} | 404 +++++++++---------
drivers/gpu/drm/nouveau/nouveau_exec.c | 2 +-
drivers/gpu/drm/nouveau/nouveau_uvmm.c | 28 +-
drivers/gpu/drm/nouveau/nouveau_uvmm.h | 6 +-
include/drm/drm_debugfs.h | 6 +-
include/drm/{drm_gpuva_mgr.h => drm_gpuvm.h} | 155 ++++---
8 files changed, 309 insertions(+), 310 deletions(-)
rename drivers/gpu/drm/{drm_gpuva_mgr.c => drm_gpuvm.c} (78%)
rename include/drm/{drm_gpuva_mgr.h => drm_gpuvm.h} (78%)drivers/gpu/drm/Makefile +1 -1
@@ -45,7 +45,7 @@ drm-y := \ drm_vblank.o \ drm_vblank_work.o \ drm_vma_manager.o \ - drm_gpuva_mgr.o \ + drm_gpuvm.o \ drm_writeback.o drm-$(CONFIG_DRM_LEGACY) += \ drm_agpsupport.o \
drivers/gpu/drm/drm_debugfs.c +8 -8
@@ -40,7 +40,7 @@#include <drm/drm_file.h> #include <drm/drm_gem.h> #include <drm/drm_managed.h> -#include <drm/drm_gpuva_mgr.h> +#include <drm/drm_gpuvm.h> #include "drm_crtc_internal.h" #include "drm_internal.h"
@@ -182,31 +182,31 @@ static const struct file_operations drm_debugfs_fops = {/** * drm_debugfs_gpuva_info - dump the given DRM GPU VA space * @m: pointer to the &seq_file to write -* @mgr: the &drm_gpuva_manager representing the GPU VA space +* @gpuvm: the &drm_gpuvm representing the GPU VA space * * Dumps the GPU VA mappings of a given DRM GPU VA manager. * * For each DRM GPU VA space drivers should call this function from their * &drm_info_list's show callback. * -* Returns: 0 on success, -ENODEV if the &mgr is not initialized +* Returns: 0 on success, -ENODEV if the &gpuvm is not initialized */ int drm_debugfs_gpuva_info (struct seq_file *m, -struct drm_gpuva_manager *mgr) +struct drm_gpuvm *gpuvm) { -struct drm_gpuva *va, *kva = &mgr->kernel_alloc_node; +struct drm_gpuva *va, *kva = &gpuvm->kernel_alloc_node; -if (!mgr->name) +if (!gpuvm->name)return -ENODEV ;seq_printf (m,"DRM GPU VA space (%s) [0x%016llx;0x%016llx]\n" , - mgr->name, mgr->mm_start, mgr->mm_start + mgr->mm_range); + gpuvm->name, gpuvm->mm_start, gpuvm->mm_start + gpuvm->mm_range);seq_printf (m,"Kernel reserved node [0x%016llx;0x%016llx]\n" , kva->va.addr, kva->va.addr + kva->va.range);seq_puts (m,"\n" );seq_puts (m," VAs | start | range | end | object | object offset\n" );seq_puts (m,"-------------------------------------------------------------------------------------------------------------\n" ); -drm_gpuva_for_each_va (va, mgr) { +drm_gpuvm_for_each_va (va, gpuvm) {if (unlikely (va == kva))continue ;
drivers/gpu/drm/drm_gpuvm.c renamed from drivers/gpu/drm/drm_gpuva_mgr.c +202 -202
1,031 lines not on this page · raw message
drivers/gpu/drm/nouveau/nouveau_exec.c +1 -1
@@ -107,7 +107,7 @@ nouveau_exec_job_submit(struct nouveau_job *job)drm_exec_until_all_locked (exec) {struct drm_gpuva *va; -drm_gpuva_for_each_va (va, &uvmm->umgr) { +drm_gpuvm_for_each_va (va, &uvmm->umgr) {if (unlikely (va == &uvmm->umgr.kernel_alloc_node))continue ;
drivers/gpu/drm/nouveau/nouveau_uvmm.c +14 -14
@@ -329,7 +329,7 @@ nouveau_uvma_region_create(struct nouveau_uvmm *uvmm,struct nouveau_uvma_region *reg;int ret; -if (!drm_gpuva_interval_empty (&uvmm->umgr, addr, range)) +if (!drm_gpuvm_interval_empty (&uvmm->umgr, addr, range))return -ENOSPC ; ret =nouveau_uvma_region_alloc (®);
@@ -384,7 +384,7 @@ nouveau_uvma_region_empty(struct nouveau_uvma_region *reg) {struct nouveau_uvmm *uvmm = reg->uvmm; -return drm_gpuva_interval_empty (&uvmm->umgr, +return drm_gpuvm_interval_empty (&uvmm->umgr, reg->va.addr, reg->va.range); }
@@ -444,7 +444,7 @@ op_map_prepare_unwind(struct nouveau_uvma *uvma)static void op_unmap_prepare_unwind (struct drm_gpuva *va) { -drm_gpuva_insert (va->mgr, va); +drm_gpuva_insert (va->vm, va); } static void
@@ -1194,7 +1194,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job) goto unwind_continue; } - op->ops =drm_gpuva_sm_unmap_ops_create (&uvmm->umgr, + op->ops =drm_gpuvm_sm_unmap_ops_create (&uvmm->umgr, op->va.addr, op->va.range);if (IS_ERR (op->ops)) {
@@ -1240,7 +1240,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job) } } - op->ops = drm_gpuva_sm_map_ops_create(&uvmm->umgr, + op->ops = drm_gpuvm_sm_map_ops_create(&uvmm->umgr, op->va.addr, op->va.range, op->gem.obj,
@@ -1264,7 +1264,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job)break ; }case OP_UNMAP : - op->ops =drm_gpuva_sm_unmap_ops_create (&uvmm->umgr, + op->ops =drm_gpuvm_sm_unmap_ops_create (&uvmm->umgr, op->va.addr, op->va.range);if (IS_ERR (op->ops)) {
@@ -1838,11 +1838,11 @@ nouveau_uvmm_init(struct nouveau_uvmm *uvmm, struct nouveau_cli *cli, uvmm->kernel_managed_addr = kernel_managed_addr; uvmm->kernel_managed_size = kernel_managed_size; -drm_gpuva_manager_init (&uvmm->umgr, cli->name, -NOUVEAU_VA_SPACE_START , -NOUVEAU_VA_SPACE_END , - kernel_managed_addr, kernel_managed_size, -NULL ); +drm_gpuvm_init (&uvmm->umgr, cli->name, +NOUVEAU_VA_SPACE_START , +NOUVEAU_VA_SPACE_END , + kernel_managed_addr, kernel_managed_size, +NULL ); ret = nvif_vmm_ctor(&cli->mmu, "uvmm", cli->vmm.vmm.object.oclass, RAW,
@@ -1857,7 +1857,7 @@ nouveau_uvmm_init(struct nouveau_uvmm *uvmm, struct nouveau_cli *cli,return 0 ; out_free_gpuva_mgr: -drm_gpuva_manager_destroy (&uvmm->umgr); +drm_gpuvm_destroy (&uvmm->umgr); out_unlock:mutex_unlock (&cli->mutex);return ret;
@@ -1879,7 +1879,7 @@ nouveau_uvmm_fini(struct nouveau_uvmm *uvmm)wait_event (entity->job.wq,list_empty (&entity->job.list.head));nouveau_uvmm_lock (uvmm); -drm_gpuva_for_each_va_safe (va, next, &uvmm->umgr) { +drm_gpuvm_for_each_va_safe (va, next, &uvmm->umgr) {struct nouveau_uvma *uvma =uvma_from_va (va);struct drm_gem_object *obj = va->gem.obj;
@@ -1912,7 +1912,7 @@ nouveau_uvmm_fini(struct nouveau_uvmm *uvmm)mutex_lock (&cli->mutex);nouveau_vmm_fini (&uvmm->vmm); -drm_gpuva_manager_destroy (&uvmm->umgr); +drm_gpuvm_destroy (&uvmm->umgr);mutex_unlock (&cli->mutex);dma_resv_fini (&uvmm->resv);
drivers/gpu/drm/nouveau/nouveau_uvmm.h +3 -3
@@ -3,13 +3,13 @@ #ifndef __NOUVEAU_UVMM_H__ #define __NOUVEAU_UVMM_H__ -#include <drm/drm_gpuva_mgr.h> +#include <drm/drm_gpuvm.h> #include "nouveau_drv.h" struct nouveau_uvmm { struct nouveau_vmm vmm; - struct drm_gpuva_manager umgr; + struct drm_gpuvm umgr; struct maple_tree region_mt; struct mutex mutex; struct dma_resv resv;
@@ -44,7 +44,7 @@ struct nouveau_uvma {#define uvmm_from_mgr (x) container_of((x), struct nouveau_uvmm, umgr)#define uvma_from_va (x) container_of((x), struct nouveau_uvma, va) -#define to_uvmm (x) uvmm_from_mgr((x)->va.mgr) +#define to_uvmm (x) uvmm_from_mgr((x)->va.vm)struct nouveau_uvmm_bind_job {struct nouveau_job base;
include/drm/drm_debugfs.h +3 -3
@@ -35,7 +35,7 @@#include <linux/types.h> #include <linux/seq_file.h> -#include <drm/drm_gpuva_mgr.h> +#include <drm/drm_gpuvm.h> /** * DRM_DEBUGFS_GPUVA_INFO - &drm_info_list entry to dump a GPU VA space
@@ -152,7 +152,7 @@ void drm_debugfs_add_files(struct drm_device *dev, const struct drm_debugfs_info *files, int count);int drm_debugfs_gpuva_info (struct seq_file *m, -struct drm_gpuva_manager *mgr); +struct drm_gpuvm *gpuvm);#else static inline void drm_debugfs_create_files(const struct drm_info_list *files, int count, struct dentry *root,
@@ -176,7 +176,7 @@ static inline void drm_debugfs_add_files(struct drm_device *dev, {}static inline int drm_debugfs_gpuva_info (struct seq_file *m, -struct drm_gpuva_manager *mgr) +struct drm_gpuvm *gpuvm) {return 0 ; }
include/drm/drm_gpuvm.h renamed from include/drm/drm_gpuva_mgr.h +77 -78
353 lines not on this page · raw message
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/3 drm/nouveau: uvmm: rename 'umgr' to 'base'
From: Danilo Krummrich <[email protected]>
[ Upstream commit 78f54469b871db5ba8ea49abd4e5994e97bd525b ]
Rename struct drm_gpuvm within struct nouveau_uvmm from 'umgr' to base.
Stable-dep-of: 412a6ceb56d5 ("drm/nouveau/uvmm: fix NULL deref unwinding an OP_MAP_SPARSE op")
--- drivers/gpu/drm/nouveau/nouveau_debugfs.c | 2 +- drivers/gpu/drm/nouveau/nouveau_exec.c | 4 +-- drivers/gpu/drm/nouveau/nouveau_uvmm.c | 32 +++++++++++------------ drivers/gpu/drm/nouveau/nouveau_uvmm.h | 6 ++--- 4 files changed, 22 insertions(+), 22 deletions(-)
drivers/gpu/drm/nouveau/nouveau_debugfs.c +1 -1
@@ -231,7 +231,7 @@ nouveau_debugfs_gpuva(struct seq_file *m, void *data)continue ;nouveau_uvmm_lock (uvmm); -drm_debugfs_gpuva_info (m, &uvmm->umgr); +drm_debugfs_gpuva_info (m, &uvmm->base);seq_puts (m,"\n" );nouveau_debugfs_gpuva_regions (m, uvmm);nouveau_uvmm_unlock (uvmm);
drivers/gpu/drm/nouveau/nouveau_exec.c +2 -2
@@ -107,8 +107,8 @@ nouveau_exec_job_submit(struct nouveau_job *job)drm_exec_until_all_locked (exec) {struct drm_gpuva *va; -drm_gpuvm_for_each_va (va, &uvmm->umgr) { -if (unlikely (va == &uvmm->umgr.kernel_alloc_node)) +drm_gpuvm_for_each_va (va, &uvmm->base) { +if (unlikely (va == &uvmm->base.kernel_alloc_node))continue ; ret =drm_exec_prepare_obj (exec, va->gem.obj,1 );
drivers/gpu/drm/nouveau/nouveau_uvmm.c +16 -16
@@ -329,7 +329,7 @@ nouveau_uvma_region_create(struct nouveau_uvmm *uvmm,struct nouveau_uvma_region *reg;int ret; -if (!drm_gpuvm_interval_empty (&uvmm->umgr, addr, range)) +if (!drm_gpuvm_interval_empty (&uvmm->base, addr, range))return -ENOSPC ; ret =nouveau_uvma_region_alloc (®);
@@ -384,7 +384,7 @@ nouveau_uvma_region_empty(struct nouveau_uvma_region *reg) {struct nouveau_uvmm *uvmm = reg->uvmm; -return drm_gpuvm_interval_empty (&uvmm->umgr, +return drm_gpuvm_interval_empty (&uvmm->base, reg->va.addr, reg->va.range); }
@@ -589,7 +589,7 @@ op_map_prepare(struct nouveau_uvmm *uvmm, uvma->region = args->region; uvma->kind = args->kind; -drm_gpuva_map (&uvmm->umgr, &uvma->va, op); +drm_gpuva_map (&uvmm->base, &uvma->va, op);/* Keep a reference until this uvma is destroyed. */ nouveau_uvma_gem_get (uvma);
@@ -1194,7 +1194,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job) goto unwind_continue; } - op->ops =drm_gpuvm_sm_unmap_ops_create (&uvmm->umgr, + op->ops =drm_gpuvm_sm_unmap_ops_create (&uvmm->base, op->va.addr, op->va.range);if (IS_ERR (op->ops)) {
@@ -1205,7 +1205,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job) ret =nouveau_uvmm_sm_unmap_prepare (uvmm, &op->new, op->ops);if (ret) { -drm_gpuva_ops_free (&uvmm->umgr, op->ops); +drm_gpuva_ops_free (&uvmm->base, op->ops); op->ops =NULL ; op->reg =NULL ; goto unwind_continue;
@@ -1240,7 +1240,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job) } } - op->ops = drm_gpuvm_sm_map_ops_create(&uvmm->umgr, + op->ops = drm_gpuvm_sm_map_ops_create(&uvmm->base, op->va.addr, op->va.range, op->gem.obj,
@@ -1256,7 +1256,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job) op->va.range, op->flags & 0xff);if (ret) { -drm_gpuva_ops_free (&uvmm->umgr, op->ops); +drm_gpuva_ops_free (&uvmm->base, op->ops); op->ops =NULL ; goto unwind_continue; }
@@ -1264,7 +1264,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job)break ; }case OP_UNMAP : - op->ops =drm_gpuvm_sm_unmap_ops_create (&uvmm->umgr, + op->ops =drm_gpuvm_sm_unmap_ops_create (&uvmm->base, op->va.addr, op->va.range);if (IS_ERR (op->ops)) {
@@ -1275,7 +1275,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job) ret =nouveau_uvmm_sm_unmap_prepare (uvmm, &op->new, op->ops);if (ret) { -drm_gpuva_ops_free (&uvmm->umgr, op->ops); +drm_gpuva_ops_free (&uvmm->base, op->ops); op->ops =NULL ; goto unwind_continue; }
@@ -1406,7 +1406,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job)break ; } -drm_gpuva_ops_free (&uvmm->umgr, op->ops); +drm_gpuva_ops_free (&uvmm->base, op->ops); op->ops =NULL ; op->reg =NULL ; }
@@ -1511,7 +1511,7 @@ nouveau_uvmm_bind_job_free_work_fn(struct work_struct *work) }if (!IS_ERR_OR_NULL (op->ops)) -drm_gpuva_ops_free (&uvmm->umgr, op->ops); +drm_gpuva_ops_free (&uvmm->base, op->ops);if (obj)drm_gem_object_put (obj);
@@ -1838,7 +1838,7 @@ nouveau_uvmm_init(struct nouveau_uvmm *uvmm, struct nouveau_cli *cli, uvmm->kernel_managed_addr = kernel_managed_addr; uvmm->kernel_managed_size = kernel_managed_size; - drm_gpuvm_init(&uvmm->umgr, cli->name, + drm_gpuvm_init(&uvmm->base, cli->name, NOUVEAU_VA_SPACE_START, NOUVEAU_VA_SPACE_END, kernel_managed_addr, kernel_managed_size,
@@ -1857,7 +1857,7 @@ nouveau_uvmm_init(struct nouveau_uvmm *uvmm, struct nouveau_cli *cli,return 0 ; out_free_gpuva_mgr: -drm_gpuvm_destroy (&uvmm->umgr); +drm_gpuvm_destroy (&uvmm->base); out_unlock:mutex_unlock (&cli->mutex);return ret;
@@ -1879,11 +1879,11 @@ nouveau_uvmm_fini(struct nouveau_uvmm *uvmm)wait_event (entity->job.wq,list_empty (&entity->job.list.head));nouveau_uvmm_lock (uvmm); -drm_gpuvm_for_each_va_safe (va, next, &uvmm->umgr) { +drm_gpuvm_for_each_va_safe (va, next, &uvmm->base) {struct nouveau_uvma *uvma =uvma_from_va (va);struct drm_gem_object *obj = va->gem.obj; -if (unlikely (va == &uvmm->umgr.kernel_alloc_node)) +if (unlikely (va == &uvmm->base.kernel_alloc_node))continue ;drm_gpuva_remove (va);
@@ -1912,7 +1912,7 @@ nouveau_uvmm_fini(struct nouveau_uvmm *uvmm)mutex_lock (&cli->mutex);nouveau_vmm_fini (&uvmm->vmm); -drm_gpuvm_destroy (&uvmm->umgr); +drm_gpuvm_destroy (&uvmm->base);mutex_unlock (&cli->mutex);dma_resv_fini (&uvmm->resv);
drivers/gpu/drm/nouveau/nouveau_uvmm.h +3 -3
@@ -8,8 +8,8 @@#include "nouveau_drv.h" struct nouveau_uvmm { +struct drm_gpuvm base;struct nouveau_vmm vmm; -struct drm_gpuvm umgr;struct maple_tree region_mt;struct mutex mutex;struct dma_resv resv;
@@ -41,10 +41,10 @@ struct nouveau_uvma {u8 kind; }; -#define uvmm_from_mgr (x) container_of((x), struct nouveau_uvmm, umgr) +#define uvmm_from_gpuvm (x) container_of((x), struct nouveau_uvmm, base)#define uvma_from_va (x) container_of((x), struct nouveau_uvma, va) -#define to_uvmm (x) uvmm_from_mgr((x)->va.vm) +#define to_uvmm (x) uvmm_from_gpuvm((x)->va.vm)struct nouveau_uvmm_bind_job {struct nouveau_job base;
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/3 drm/nouveau/uvmm: fix NULL deref unwinding an OP_MAP_SPARSE op
In reply to Sasha LevinFrom: Zhenhao Wan <[email protected]>
[ Upstream commit 412a6ceb56d501ef2f8202e26ab4b5d4dfbca566 ]
Each bind_job_op is zeroed by kzalloc_obj() in bind_job_op_from_uop(), and the OP_MAP_SPARSE case in nouveau_uvmm_bind_job_submit() only creates a region, so op->ops stays NULL for a successfully processed sparse map.
If a later op in the same job fails, the reverse unwind loop revisits that op and calls drm_gpuva_ops_free(&uvmm->base, op->ops) unconditionally. drm_gpuva_ops_free() dereferences its argument right away (list_for_each_entry_safe on &ops->list), so a NULL op->ops oopses. The path is reachable by any render-node fd holder, since NOUVEAU_VM_BIND is DRM_RENDER_ALLOW.
Guard the free with IS_ERR_OR_NULL(), as nouveau_uvmm_bind_job_cleanup() already does for the identical free.
--- drivers/gpu/drm/nouveau/nouveau_uvmm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/gpu/drm/nouveau/nouveau_uvmm.c +2 -1
@@ -1406,7 +1406,8 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job)break ; } -drm_gpuva_ops_free (&uvmm->base, op->ops); +if (!IS_ERR_OR_NULL (op->ops)) +drm_gpuva_ops_free (&uvmm->base, op->ops); op->ops =NULL ; op->reg =NULL ; }
Signature · 1 line
2.53.0