add default named_modules_to_munmap variable (#357)

* add default named_modules_to_munmap variable

Default this to handle to case of cloning a GGUFModelPatcher which then
expects it to exist on the clone.

I could never reproduce this condition, however 4 users have the error.
This error path is very sensitive to VRAM condtions and workflow and I
never got the details so its a bit of a guess.

But from a code point of view, its the right thing anyway

* nodes: carry the mmap_released state through a clone()

There are workflows where the same underlying model will be loaded
twice by two different ModelPatchers.

With the current code as-is, the first patcher will load fine and
intercept the pins while removing already done pins from the free
modules list.

It then will load again with the second MP however, this one will
not get the callbacks for the already pinned stuff and then rip
through and .to().to() them all. This destroys the pinning setup
while comfy core still keeps the modules registered as unpinned.
This causes a crash on einval when core goes to unpin the not pinned
tensors.

Comfy core now has a guard against unpinning the not-pinned, however
what I think is happening, is the RAM of the pin in freed in the
.to().to() and then malloc re-allocates it to new tensors with cuda
keeping its records of previous pinning. einval can happen when
you try and unpin a tensor in the middle of the block which is
definately possible on this use after free pattern.
This commit is contained in:
rattus
2025-11-07 23:31:20 +01:00
committed by GitHub
parent d4fbdb0139
commit 02dac863ee
+4 -2
View File
@@ -79,13 +79,14 @@ class GGUFModelPatcher(comfy.model_patcher.ModelPatcher):
def pin_weight_to_device(self, key):
op_key = key.rsplit('.', 1)[0]
if self.named_modules_to_munmap is not None and op_key in self.named_modules_to_munmap:
if not self.mmap_released and op_key in self.named_modules_to_munmap:
# TODO: possible to OOM, find better way to detach
self.named_modules_to_munmap[op_key].to(self.load_device).to(self.offload_device)
del self.named_modules_to_munmap[op_key]
super().pin_weight_to_device(key)
mmap_released = False
named_modules_to_munmap = {}
def load(self, *args, force_patch_weights=False, **kwargs):
if not self.mmap_released:
@@ -115,7 +116,7 @@ class GGUFModelPatcher(comfy.model_patcher.ModelPatcher):
# TODO: possible to OOM, find better way to detach
m.to(self.load_device).to(self.offload_device)
self.mmap_released = True
self.named_modules_to_munmap = None
self.named_modules_to_munmap = {}
def clone(self, *args, **kwargs):
src_cls = self.__class__
@@ -125,6 +126,7 @@ class GGUFModelPatcher(comfy.model_patcher.ModelPatcher):
self.__class__ = src_cls
# GGUF specific clone values below
n.patch_on_device = getattr(self, "patch_on_device", False)
n.mmap_released = getattr(self, "mmap_released", False)
if src_cls != GGUFModelPatcher:
n.size = 0 # force recalc
return n