Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-comfy-aimdo for
openSUSE:Factory checked in at 2026-10-22 18:24:28
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-comfy-aimdo (Old)
and /work/SRC/openSUSE:Factory/.python-comfy-aimdo.new.2252680 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-comfy-aimdo"
Thu Oct 22 18:24:28 2026 rev:9 rq:1387097 version:0.6.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-comfy-aimdo/python-comfy-aimdo.changes
2026-10-08 13:28:21.941247449 +0200
+++
/work/SRC/openSUSE:Factory/.python-comfy-aimdo.new.2252680/python-comfy-aimdo.changes
2026-10-22 18:24:31.348275791 +0200
@@ -1,0 +2,21 @@
+Thu Oct 22 11:42:44 UTC 2026 - Martin Pluskal <[email protected]>
+
+- Update to 0.6.0:
+ * VBAR writeback on eviction: the owner now saves whole tensors
+ back to RAM before the pages are unmapped, and keeps them mapped
+ when writeback fails; ModelVBAR gains an optional
+ pageout_callback plus close()
+ * VBAR failures now surface: unpin, set_watermark and free_memory
+ raise on error instead of silently passing (all native paths,
+ which this build does not compile)
+ * New whole-tensor descriptor writes through the transfer queue
+ (host_buffer.write_device_to_file/write_host_to_file wrappers
+ around the native calls)
+ * MCDM adapters now resolve through a DXCore LUID lookup
+ (Windows-only native helper)
+ * torch floor stays >= 2.8.0: the sdist still declares no install
+ dependencies and requires-python is unchanged (>= 3.9)
+- Keep comfy-aimdo-detect-vendor-without-local-version.patch
+ (control.py unchanged, still applies cleanly)
+
+-------------------------------------------------------------------
Old:
----
comfy-aimdo-0.5.6.tar.gz
New:
----
comfy-aimdo-0.6.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-comfy-aimdo.spec ++++++
--- /var/tmp/diff_new_pack.K5G4gR/_old 2026-10-22 18:24:32.360316776 +0200
+++ /var/tmp/diff_new_pack.K5G4gR/_new 2026-10-22 18:24:32.362316858 +0200
@@ -18,7 +18,7 @@
%{?sle15_python_module_pythons}
Name: python-comfy-aimdo
-Version: 0.5.6
+Version: 0.6.0
Release: 0
Summary: AI Model Dynamic Offloader for ComfyUI (pure-Python fallback)
License: GPL-3.0-only
++++++ comfy-aimdo-0.5.6.tar.gz -> comfy-aimdo-0.6.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/comfy-aimdo-0.5.6/.github/workflows/build-wheels.yml
new/comfy-aimdo-0.6.0/.github/workflows/build-wheels.yml
--- old/comfy-aimdo-0.5.6/.github/workflows/build-wheels.yml 2026-10-07
21:16:15.000000000 +0200
+++ new/comfy-aimdo-0.6.0/.github/workflows/build-wheels.yml 2026-10-21
13:56:45.000000000 +0200
@@ -72,12 +72,12 @@
src/*.c src-cuda/dispatch.c src-win/*.c ^
/Isrc /Isrc-win /IC:/Detours/include /FIcompiler.h
/Fe:comfy_aimdo/aimdo.dll ^
/link /LIBPATH:%DETOURS_LIB_DIR% ^
- cfgmgr32.lib dxgi.lib dxguid.lib setupapi.lib detours.lib
onecore.lib
+ cfgmgr32.lib dxgi.lib dxguid.lib dxcore.lib setupapi.lib
detours.lib onecore.lib
cl.exe /LD /O2 /D__HIP_PLATFORM_AMD__ ^
src/*.c src-hip/dispatch.c src-win/*.c ^
/Isrc /Isrc-win /IC:/Detours/include /FIcompiler.h
/Fe:comfy_aimdo/aimdo_rocm.dll ^
/link /LIBPATH:%DETOURS_LIB_DIR% ^
- cfgmgr32.lib dxgi.lib dxguid.lib setupapi.lib detours.lib
onecore.lib
+ cfgmgr32.lib dxgi.lib dxguid.lib dxcore.lib setupapi.lib
detours.lib onecore.lib
shell: cmd
- name: Build default Stub Wheel
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/comfy-aimdo-0.5.6/comfy_aimdo/host_buffer.py
new/comfy-aimdo-0.6.0/comfy_aimdo/host_buffer.py
--- old/comfy-aimdo-0.5.6/comfy_aimdo/host_buffer.py 2026-10-07
21:16:15.000000000 +0200
+++ new/comfy-aimdo-0.6.0/comfy_aimdo/host_buffer.py 2026-10-21
13:56:45.000000000 +0200
@@ -44,6 +44,13 @@
]
lib.hostbuf_file_reader_read.restype = ctypes.c_bool
+ lib.hostbuf_file_writer_write.argtypes = [ctypes.c_int, ctypes.c_uint64,
ctypes.c_uint64,
+ ctypes.c_uint64, ctypes.c_void_p,
ctypes.c_uint64]
+ lib.hostbuf_file_writer_write.restype = ctypes.c_bool
+
+ lib.xfer_file_write.argtypes = [ctypes.c_uint64, ctypes.c_uint64,
ctypes.c_void_p, ctypes.c_size_t]
+ lib.xfer_file_write.restype = ctypes.c_bool
+
lib.hostbuf_file_reader_cleanup.argtypes = []
lib.hostbuf_register.argtypes = [ctypes.c_void_p, ctypes.c_uint64,
ctypes.c_uint64]
@@ -71,6 +78,18 @@
raise RuntimeError("hostbuf_file_reader_read failed")
+def write_device_to_file(file_obj, file_offset, size, stream, device_ptr,
device):
+ """Write through the bounded staging ring; the source stream and file
writes finish before return."""
+ if not lib.hostbuf_file_writer_write(int(device), _file_handle(file_obj),
+ int(file_offset), int(size),
int(stream) or None, int(device_ptr)):
+ raise RuntimeError("hostbuf_file_writer_write failed")
+
+
+def write_host_to_file(file_obj, file_offset, size, host_ptr):
+ if not lib.xfer_file_write(_file_handle(file_obj), int(file_offset),
int(host_ptr), int(size)):
+ raise RuntimeError("xfer_file_write failed")
+
+
def cleanup_file_reader():
lib.hostbuf_file_reader_cleanup()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/comfy-aimdo-0.5.6/comfy_aimdo/model_vbar.py
new/comfy-aimdo-0.6.0/comfy_aimdo/model_vbar.py
--- old/comfy-aimdo-0.5.6/comfy_aimdo/model_vbar.py 2026-10-07
21:16:15.000000000 +0200
+++ new/comfy-aimdo-0.6.0/comfy_aimdo/model_vbar.py 2026-10-21
13:56:45.000000000 +0200
@@ -1,17 +1,20 @@
import ctypes
+import logging
from . import control
lib = control.lib
+PageoutCallback = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_uint64,
ctypes.c_uint64)
# Bindings
if lib is not None:
- lib.vbar_allocate.argtypes = [ctypes.c_void_p, ctypes.c_uint64,
ctypes.c_int]
+ lib.vbar_allocate.argtypes = [ctypes.c_void_p, ctypes.c_uint64,
ctypes.c_int, PageoutCallback]
lib.vbar_allocate.restype = ctypes.c_void_p
lib.vbar_set_watermark_limit.argtypes = [ctypes.c_void_p, ctypes.c_void_p,
ctypes.c_uint64]
lib.vbar_set_watermark.argtypes = [ctypes.c_void_p, ctypes.c_void_p,
ctypes.c_uint64]
+ lib.vbar_set_watermark.restype = ctypes.c_bool
lib.vbars_reset_watermark_limits.argtypes = [ctypes.c_void_p]
@@ -28,12 +31,13 @@
lib.vbar_fault.restype = ctypes.c_int
lib.vbar_unpin.argtypes = [ctypes.c_void_p, ctypes.c_void_p,
ctypes.c_uint64, ctypes.c_uint64]
+ lib.vbar_unpin.restype = ctypes.c_bool
lib.vbar_loaded_size.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
lib.vbar_loaded_size.restype = ctypes.c_size_t
lib.vbar_free_memory.argtypes = [ctypes.c_void_p, ctypes.c_void_p,
ctypes.c_uint64]
- lib.vbar_free_memory.restype = ctypes.c_uint64
+ lib.vbar_free_memory.restype = ctypes.c_int64
lib.vbars_analyze.argtypes = [ctypes.c_void_p, ctypes.c_bool]
lib.vbars_analyze.restype = ctypes.c_uint64
@@ -47,9 +51,20 @@
lib.vbar_get_residency.argtypes = [ctypes.c_void_p, ctypes.c_void_p,
ctypes.POINTER(ctypes.c_uint8), ctypes.c_size_t]
class ModelVBAR:
- def __init__(self, size, device):
+ def __init__(self, size, device, pageout_callback=None):
+ """The optional callback completes writeback before returning, without
reentering VBAR APIs."""
+ def invoke(offset, size):
+ try:
+ pageout_callback(offset, size)
+ return True
+ except BaseException:
+ # ctypes cannot unwind a Python exception through the native
caller.
+ logging.exception("VBAR pageout callback failed")
+ return False
+
+ self._pageout_callback = PageoutCallback(invoke) if pageout_callback
is not None else PageoutCallback()
self._devctx = control.get_devctx(device)
- self._ptr = lib.vbar_allocate(self._devctx, int(size), device)
+ self._ptr = lib.vbar_allocate(self._devctx, int(size), device,
self._pageout_callback)
if not self._ptr:
raise MemoryError("VBAR allocation failed")
self.device = device
@@ -95,7 +110,8 @@
def unpin(self, alloc, size):
offset = alloc - self.base_addr
- lib.vbar_unpin(self._devctx, self._ptr, offset, size)
+ if not lib.vbar_unpin(self._devctx, self._ptr, offset, size):
+ raise RuntimeError("VBAR unpin failed")
def loaded_size(self):
return lib.vbar_loaded_size(self._devctx, self._ptr)
@@ -104,10 +120,14 @@
lib.vbar_set_watermark_limit(self._devctx, self._ptr, size_bytes)
def set_watermark(self, size_bytes):
- lib.vbar_set_watermark(self._devctx, self._ptr, size_bytes)
+ if not lib.vbar_set_watermark(self._devctx, self._ptr, size_bytes):
+ raise RuntimeError("VBAR set_watermark failed")
def free_memory(self, size_bytes):
- return lib.vbar_free_memory(self._devctx, self._ptr, int(size_bytes))
+ freed = lib.vbar_free_memory(self._devctx, self._ptr, int(size_bytes))
+ if freed < 0:
+ raise RuntimeError("VBAR free_memory failed")
+ return freed
def get_nr_pages(self):
return lib.vbar_get_nr_pages(self._devctx, self._ptr)
@@ -125,12 +145,16 @@
lib.vbar_get_residency(self._devctx, self._ptr, buf, nr_pages)
return list(buf)
- def __del__(self):
+ def close(self):
ptr = getattr(self, "_ptr", None)
aimdo_lib = getattr(control, "lib", None)
if aimdo_lib is not None and ptr:
aimdo_lib.vbar_free(self._devctx, ptr)
self._ptr = None
+ self._pageout_callback = None
+
+ def __del__(self):
+ self.close()
def vbar_fault(alloc):
vbar, offset, size = alloc
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/comfy-aimdo-0.5.6/src/control.h
new/comfy-aimdo-0.6.0/src/control.h
--- old/comfy-aimdo-0.5.6/src/control.h 2026-10-07 21:16:15.000000000 +0200
+++ new/comfy-aimdo-0.6.0/src/control.h 2026-10-21 13:56:45.000000000 +0200
@@ -57,6 +57,7 @@
#endif
#if defined(_WIN32) || defined(_WIN64)
void *_wddm_adapter; /* IDXGIAdapter3* */
+ void *_dxcore_adapter; /* IDXCoreAdapter* */
uint64_t _wddm_timestamp_last_check;
#endif
} AimdoContext;
@@ -96,6 +97,7 @@
#endif
#if defined(_WIN32) || defined(_WIN64)
#define g_wddm_adapter (*(IDXGIAdapter3
**)&g_devctx->_wddm_adapter)
+#define g_dxcore_adapter (*(IDXCoreAdapter
**)&g_devctx->_dxcore_adapter)
#define wddm_timestamp_last_check (g_devctx->_wddm_timestamp_last_check)
#endif
#define control_timestamp_last_check (g_devctx->_control_timestamp_last_check)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/comfy-aimdo-0.5.6/src/gpu_abi.h
new/comfy-aimdo-0.6.0/src/gpu_abi.h
--- old/comfy-aimdo-0.5.6/src/gpu_abi.h 2026-10-07 21:16:15.000000000 +0200
+++ new/comfy-aimdo-0.6.0/src/gpu_abi.h 2026-10-21 13:56:45.000000000 +0200
@@ -46,6 +46,7 @@
typedef enum cudaError_enum {
CUDA_SUCCESS = 0,
CUDA_ERROR_OUT_OF_MEMORY = 2,
+ CUDA_ERROR_UNKNOWN = 999,
} cudaError_enum;
typedef enum CUevent_flags_enum {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/comfy-aimdo-0.5.6/src/gpu_dispatch.h
new/comfy-aimdo-0.6.0/src/gpu_dispatch.h
--- old/comfy-aimdo-0.5.6/src/gpu_dispatch.h 2026-10-07 21:16:15.000000000
+0200
+++ new/comfy-aimdo-0.6.0/src/gpu_dispatch.h 2026-10-21 13:56:45.000000000
+0200
@@ -44,6 +44,8 @@
typedef CUresult (CUDAAPI *PFN_cuMemRelease)(CUmemGenericAllocationHandle
handle);
typedef CUresult (CUDAAPI *PFN_cuMemcpyHtoDAsync)(CUdeviceptr dst, const void
*src,
size_t bytes, CUstream
hStream);
+typedef CUresult (CUDAAPI *PFN_cuMemcpyDtoHAsync)(void *dst, CUdeviceptr src,
+ size_t bytes, CUstream
hStream);
typedef CUresult (CUDAAPI *PFN_cuEventCreate)(CUevent *phEvent, unsigned int
flags);
typedef CUresult (CUDAAPI *PFN_cuEventDestroy)(CUevent hEvent);
typedef CUresult (CUDAAPI *PFN_cuEventRecord)(CUevent hEvent, CUstream
hStream);
@@ -81,6 +83,7 @@
PFN_cuMemUnmap p_cuMemUnmap;
PFN_cuMemRelease p_cuMemRelease;
PFN_cuMemcpyHtoDAsync p_cuMemcpyHtoDAsync;
+ PFN_cuMemcpyDtoHAsync p_cuMemcpyDtoHAsync;
PFN_cuEventCreate p_cuEventCreate;
PFN_cuEventDestroy p_cuEventDestroy;
PFN_cuEventRecord p_cuEventRecord;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/comfy-aimdo-0.5.6/src/hostbuf-file-reader.c
new/comfy-aimdo-0.6.0/src/hostbuf-file-reader.c
--- old/comfy-aimdo-0.5.6/src/hostbuf-file-reader.c 2026-10-07
21:16:15.000000000 +0200
+++ new/comfy-aimdo-0.6.0/src/hostbuf-file-reader.c 2026-10-21
13:56:45.000000000 +0200
@@ -49,10 +49,9 @@
return slot;
}
-SHARED_EXPORT
-bool hostbuf_file_reader_read(int device, uint64_t file_handle, uint64_t
file_offset,
- uint64_t size, cudaStream_t stream,
- uint64_t device_ptr, bool mark_cold) {
+static bool hostbuf_file_transfer(int device, uint64_t file_handle, uint64_t
file_offset,
+ uint64_t size, cudaStream_t stream,
+ uint64_t device_ptr, bool mark_cold, bool
write) {
if (size == 0) {
return true;
}
@@ -65,7 +64,6 @@
while (size) {
HostbufFileReaderSlot *slot = g_devctx->_hostbuf_file_reader_active <
0 ? NULL :
&g_devctx->_hostbuf_file_reader_slots[g_devctx->_hostbuf_file_reader_active];
- size_t chunk;
if (!slot || slot->stream != (CUstream)stream ||
(slot->offset + size >= HOSTBUF_FILE_READER_WINDOW &&
@@ -76,24 +74,39 @@
}
}
- chunk = (size_t)MIN(size, HOSTBUF_FILE_READER_WINDOW - slot->offset);
- if (!xfer_file_read(file_handle, file_offset, slot->buffer +
slot->offset,
- chunk, mark_cold)) {
- log(AIMDO_LOG_ERROR, "%s: file read failed handle=0x%llx
offset=%llu size=%zu\n",
+ size_t chunk = (size_t)MIN(size, HOSTBUF_FILE_READER_WINDOW -
slot->offset);
+ void *buffer = slot->buffer + slot->offset;
+ slot->offset += chunk;
+
+ if (!write && !xfer_file_read(file_handle, file_offset, buffer, chunk,
mark_cold)) {
+ log(AIMDO_LOG_ERROR, "%s: xfer_file_read failed handle=0x%llx
offset=%llu size=%zu\n",
__func__, (ull)file_handle, (ull)file_offset, chunk);
return false;
}
- CUresult copy_result = cuMemcpyHtoDAsync((CUdeviceptr)device_ptr,
- slot->buffer + slot->offset,
- chunk, (CUstream)stream);
+
+ CUresult copy_result = write ? cuMemcpyDtoHAsync(buffer,
(CUdeviceptr)device_ptr, chunk, (CUstream)stream) :
+
cuMemcpyHtoDAsync((CUdeviceptr)device_ptr, buffer, chunk, (CUstream)stream);
if (!CHECK_CU(copy_result)) {
- log(AIMDO_LOG_ERROR, "%s: device copy failed result=%d
device_ptr=%p device=%d stream=%p size=%zu\n",
- __func__, (int)copy_result, (void *)(uintptr_t)device_ptr,
device,
- (void *)stream, chunk);
+ log(AIMDO_LOG_ERROR, "%s: %s failed result=%d device_ptr=%p
device=%d stream=%p size=%zu\n",
+ __func__, write ? "cuMemcpyDtoHAsync" : "cuMemcpyHtoDAsync",
(int)copy_result,
+ (void *)(uintptr_t)device_ptr, device, (void *)stream, chunk);
return false;
}
- slot->offset += chunk;
+ if (write) {
+ CUresult sync_result = cuCtxSynchronize();
+ if (!CHECK_CU(sync_result)) {
+ log(AIMDO_LOG_ERROR, "%s: cuCtxSynchronize failed result=%d
device_ptr=%p device=%d stream=%p size=%zu\n",
+ __func__, (int)sync_result, (void *)(uintptr_t)device_ptr,
device, (void *)stream, chunk);
+ return false;
+ }
+ if (!xfer_file_write(file_handle, file_offset, buffer, chunk)) {
+ log(AIMDO_LOG_ERROR, "%s: xfer_file_write failed handle=0x%llx
offset=%llu size=%zu\n",
+ __func__, (ull)file_handle, (ull)file_offset, chunk);
+ return false;
+ }
+ }
+
file_offset += chunk;
device_ptr += chunk;
size -= chunk;
@@ -103,6 +116,19 @@
}
SHARED_EXPORT
+bool hostbuf_file_reader_read(int device, uint64_t file_handle, uint64_t
file_offset,
+ uint64_t size, cudaStream_t stream,
+ uint64_t device_ptr, bool mark_cold) {
+ return hostbuf_file_transfer(device, file_handle, file_offset, size,
stream, device_ptr, mark_cold, false);
+}
+
+SHARED_EXPORT
+bool hostbuf_file_writer_write(int device, uint64_t file_handle, uint64_t
file_offset,
+ uint64_t size, cudaStream_t stream, uint64_t
device_ptr) {
+ return hostbuf_file_transfer(device, file_handle, file_offset, size,
stream, device_ptr, false, true);
+}
+
+SHARED_EXPORT
void hostbuf_file_reader_cleanup(void) {
if (!g_devctx) {
return;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/comfy-aimdo-0.5.6/src/malloc-graph.c
new/comfy-aimdo-0.6.0/src/malloc-graph.c
--- old/comfy-aimdo-0.5.6/src/malloc-graph.c 2026-10-07 21:16:15.000000000
+0200
+++ new/comfy-aimdo-0.6.0/src/malloc-graph.c 2026-10-21 13:56:45.000000000
+0200
@@ -423,10 +423,14 @@
static CUresult create_page(MallocGraph *g, PhysicalPage **page) {
CUresult r;
- vbars_free(budget_deficit(MG_PAGE));
+ if (!vbars_free(budget_deficit(MG_PAGE))) {
+ return CUDA_ERROR_UNKNOWN;
+ }
r = physical_page_alloc(page, MG_PAGE, g->device);
if (r == CUDA_ERROR_OUT_OF_MEMORY) {
- vbars_free(MG_PAGE);
+ if (!vbars_free(MG_PAGE)) {
+ return CUDA_ERROR_UNKNOWN;
+ }
r = physical_page_alloc(page, MG_PAGE, g->device);
}
return r;
@@ -861,13 +865,14 @@
return offset;
}
-bool malloc_graph_alloc(CUdeviceptr *ptr, size_t size, CUstream stream) {
+bool malloc_graph_alloc(CUdeviceptr *ptr, size_t size, CUstream stream, int
*result) {
MallocGraph *g = active_graph;
if (!g || graph_failed(g) || g->paused || stream != g->stream) {
return false;
}
+ *result = CUDA_ERROR_OUT_OF_MEMORY;
*ptr = 0;
size_t va = 0;
@@ -879,6 +884,7 @@
*ptr = type == EV_ALLOC_SMALL
? virtual_range_get(g->small_base) + match->value
: virtual_range_get(g->base) + match->value * MG_PAGE;
+ *result = CUDA_SUCCESS;
return true;
}
RETURN_G_FAILED(g->failed, true);
@@ -902,7 +908,11 @@
PhysicalPage **page = &g->small_physical_pages[g->small_pages];
CUdeviceptr addr = virtual_range_get(g->small_base) +
g->small_pages * MG_PAGE;
CUresult r = create_page(g, page);
- RETURN_G_FAILED(r, true);
+ if (r) {
+ g->failed = true;
+ *result = r;
+ return true;
+ }
g->small_pages++;
RETURN_G_FAILED(map_reference(g, addr, *page,
&g->small_mapped_pages[g->small_pages - 1]),
true);
@@ -922,6 +932,7 @@
track_allocation(g, allocation);
add_used(g, bytes);
*ptr = virtual_range_get(g->small_base) + offset;
+ *result = CUDA_SUCCESS;
return true;
}
@@ -967,7 +978,12 @@
(g->allocations.physical_live[p] || rogue_phys(g, p))) {
p++;
}
- RETURN_G_FAILED(map_page(g, va + j, p), true);
+ CUresult r = map_page(g, va + j, p);
+ if (r) {
+ g->failed = true;
+ *result = r;
+ return true;
+ }
}
g->allocations.physical_live[g->va_phys[va + j]] = true;
}
@@ -979,6 +995,7 @@
track_allocation(g, allocation);
add_used(g, pages * MG_PAGE);
*ptr = virtual_range_get(g->base) + va * MG_PAGE;
+ *result = CUDA_SUCCESS;
return true;
}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/comfy-aimdo-0.5.6/src/model-vbar.c
new/comfy-aimdo-0.6.0/src/model-vbar.c
--- old/comfy-aimdo-0.5.6/src/model-vbar.c 2026-10-07 21:16:15.000000000
+0200
+++ new/comfy-aimdo-0.6.0/src/model-vbar.c 2026-10-21 13:56:45.000000000
+0200
@@ -23,6 +23,8 @@
void *lower;
size_t resident_count;
+ uint64_t pageout_size;
+ bool (*pageout_callback)(uint64_t offset, uint64_t size);
ResidentPage residency_map[1]; /* Must be last! */
} ModelVBAR;
@@ -91,12 +93,25 @@
return (uint64_t)calculated_total_vram;
}
-static inline bool mod1(ModelVBAR *mv, size_t page_nr, bool do_free, bool
do_unpin) {
+/* -1: writeback failed, 0: retained, 1: freed. */
+static inline int mod1(ModelVBAR *mv, size_t page_nr, bool do_free, bool
do_unpin) {
ResidentPage *rp = &mv->residency_map[page_nr];
CUdeviceptr vaddr = mv->vbar + page_nr * VBAR_PAGE_SIZE;
do_free = do_free && rp->handle && (do_unpin || rp->pin_count == 0);
if (do_free) {
+ if (mv->pageout_callback) {
+ uint64_t offset = page_nr * VBAR_PAGE_SIZE;
+ size_t size = MIN(VBAR_PAGE_SIZE, mv->pageout_size - offset);
+ /* Also cover eviction while malloc-graph synchronization is
paused. */
+ if (!CHECK_CU(cuCtxSynchronize())) {
+ return -1;
+ }
+ /* The owner saves complete tensors intersecting this range before
unmapping. */
+ if (!mv->pageout_callback(offset, size)) {
+ return -1;
+ }
+ }
CHECK_CU(cuMemUnmap(vaddr, VBAR_PAGE_SIZE));
unmap_workaround(vaddr, VBAR_PAGE_SIZE);
CHECK_CU(cuMemRelease(rp->handle));
@@ -110,7 +125,7 @@
return do_free;
}
-size_t vbars_free(ssize_t size) {
+bool vbars_free(ssize_t size) {
size_t pages_needed;
bool dirty = false;
bool sync = !malloc_graph_sync_paused();
@@ -119,7 +134,7 @@
vbars_dirty = true;
if (size <= 0) {
- return 0;
+ return true;
}
pages_needed = VBAR_GET_PAGE_NR_UP((size_t)size);
@@ -136,9 +151,11 @@
}
dirty = true;
}
- if (mod1(i, i->watermark - 1, true, false)) {
- pages_needed--;
+ int freed = mod1(i, i->watermark - 1, true, false);
+ if (freed < 0) {
+ return false;
}
+ pages_needed -= freed;
}
}
@@ -146,7 +163,7 @@
CHECK_CU(cuCtxSynchronize());
}
- return pages_needed;
+ return true;
}
static inline size_t move_cursor_to_absent(ModelVBAR *mv, size_t cursor) {
@@ -165,7 +182,7 @@
return cursor;
}
-static void vbars_free_for_vbar(ModelVBAR *mv, size_t target, ssize_t surplus)
{
+static bool vbars_free_for_vbar(ModelVBAR *mv, size_t target, ssize_t surplus)
{
size_t cursor = move_cursor_to_absent(mv, 0);
bool synced = false;
@@ -183,7 +200,11 @@
CHECK_CU(cuCtxSynchronize());
synced = true;
}
- if (mod1(i, i->watermark - 1, true, false)) {
+ int freed = mod1(i, i->watermark - 1, true, false);
+ if (freed < 0) {
+ return false;
+ }
+ if (freed) {
surplus += (ssize_t)VBAR_PAGE_SIZE;
cursor = spend_surplus_on_cursor(mv, target, cursor, &surplus);
}
@@ -193,6 +214,7 @@
if (synced) {
CHECK_CU(cuCtxSynchronize());
}
+ return true;
}
static inline void remove_vbar(ModelVBAR *mv) {
@@ -214,8 +236,10 @@
lowest_priority.higher = mv;
}
+/* The optional callback completes writeback before returning. No VBAR
reentry. */
SHARED_EXPORT
-void *vbar_allocate(void *devctx, uint64_t size, int device) {
+void *vbar_allocate(void *devctx, uint64_t size, int device,
+ bool (*pageout_callback)(uint64_t, uint64_t)) {
ModelVBAR *mv;
set_devctx((AimdoContext *)devctx);
@@ -230,13 +254,16 @@
if (nr_pages_max < nr_pages) {
nr_pages = nr_pages_max;
}
- size = (uint64_t)nr_pages * VBAR_PAGE_SIZE;
if (!(mv = calloc(1, sizeof(*mv) + nr_pages *
sizeof(mv->residency_map[0])))) {
log(CRITICAL, "Host OOM\n");
return NULL;
}
+ mv->pageout_size = size;
+ mv->pageout_callback = pageout_callback;
+ size = (uint64_t)nr_pages * VBAR_PAGE_SIZE;
+
/* FIXME: Do I care about alignment? Does Cuda just look after itself? */
if (!CHECK_CU(cuMemAddressReserve(&mv->vbar, size, 0, 0, 0))) {
log(AIMDO_LOG_ERROR, "Could not reseve Virtual Address space for
VBAR\n");
@@ -264,7 +291,7 @@
}
SHARED_EXPORT
-void vbar_set_watermark(void *devctx, void *vbar, uint64_t size) {
+bool vbar_set_watermark(void *devctx, void *vbar, uint64_t size) {
ModelVBAR *mv = (ModelVBAR *)vbar;
size_t watermark = VBAR_GET_PAGE_NR_UP(size);
@@ -280,11 +307,14 @@
if (watermark < mv->watermark) {
CHECK_CU(cuCtxSynchronize());
for (size_t page_nr = watermark; page_nr < mv->watermark; page_nr++) {
- mod1(mv, page_nr, true, false);
+ if (mod1(mv, page_nr, true, false) < 0) {
+ return false;
+ }
}
}
mv->watermark = watermark;
+ return true;
}
SHARED_EXPORT
@@ -360,7 +390,9 @@
* as the allocator is unreliable as it may not actually be called
reliably when you
* really need to know you have spilled.
*/
- vbars_free(budget_deficit(0));
+ if (!vbars_free(budget_deficit(0))) {
+ return VBAR_FAULT_ERROR;
+ }
if (page_end > mv->watermark) {
log(VVERBOSE, "VBAR Allocation is above watermark\n");
@@ -378,11 +410,13 @@
}
if (!miss_alloc_checked) {
- vbars_free_for_vbar(mv, page_end,
+ if (!vbars_free_for_vbar(mv, page_end,
(ssize_t)vram_capacity -
((ssize_t)(total_vram_usage +
(page_end - page_nr) *
VBAR_PAGE_SIZE) +
- (ssize_t)simple_vram_headroom));
+ (ssize_t)simple_vram_headroom))) {
+ return VBAR_FAULT_ERROR;
+ }
miss_alloc_checked = true;
if (page_end > mv->watermark) {
@@ -400,7 +434,9 @@
return VBAR_FAULT_ERROR;
}
log(DEBUG, "VBAR allocator attempt exceeds available VRAM ...\n");
- vbars_free(VBAR_PAGE_SIZE);
+ if (!vbars_free(VBAR_PAGE_SIZE)) {
+ return VBAR_FAULT_ERROR;
+ }
if (page_end > mv->watermark) {
log(DEBUG, "VBAR allocation cancelled due to backup-free
watermark reduction\n");
return VBAR_FAULT_OOM;
@@ -427,7 +463,7 @@
}
SHARED_EXPORT
-void vbar_unpin(void *devctx, void *vbar, uint64_t offset, uint64_t size) {
+bool vbar_unpin(void *devctx, void *vbar, uint64_t offset, uint64_t size) {
ModelVBAR *mv = (ModelVBAR *)vbar;
set_devctx((AimdoContext *)devctx);
@@ -445,13 +481,18 @@
if (rp->pin_count) {
rp->pin_count--;
}
- mod1(mv, page_nr, page_nr >= mv->watermark, false);
+ }
+ /* Release every consumer pin even if a subsequent writeback fails. */
+ for (uint64_t page_nr = VBAR_GET_PAGE_NR(offset); page_nr < page_end &&
page_nr < mv->nr_pages; page_nr++) {
+ if (mod1(mv, page_nr, page_nr >= mv->watermark, false) < 0) {
+ return false;
+ }
}
if (page_end > mv->watermark) {
CHECK_CU(cuCtxSynchronize());
}
-
+ return true;
}
SHARED_EXPORT
@@ -465,6 +506,7 @@
CHECK_CU(cuCtxSynchronize());
+ mv->pageout_callback = NULL; /* Destruction discards rather than pages
out. */
for (uint64_t page_nr = 0; page_nr < mv->nr_pages; page_nr++) {
mod1(mv, page_nr, true, true);
}
@@ -511,7 +553,7 @@
}
SHARED_EXPORT
-uint64_t vbar_free_memory(void *devctx, void *vbar, uint64_t size) {
+int64_t vbar_free_memory(void *devctx, void *vbar, uint64_t size) {
ModelVBAR *mv = (ModelVBAR *)vbar;
size_t pages_to_free = VBAR_GET_PAGE_NR_UP(size);
size_t pages_freed = 0;
@@ -527,13 +569,15 @@
/* In theory we should never have pins here, but
* respect pins if it really comes up.
*/
- if (mod1(mv, mv->watermark - 1, true, false)) {
- pages_to_free--;
- pages_freed++;
+ int freed = mod1(mv, mv->watermark - 1, true, false);
+ if (freed < 0) {
+ return -1;
}
+ pages_to_free -= freed;
+ pages_freed += freed;
}
CHECK_CU(cuCtxSynchronize());
- return (uint64_t)pages_freed * VBAR_PAGE_SIZE;
+ return (int64_t)pages_freed * VBAR_PAGE_SIZE;
}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/comfy-aimdo-0.5.6/src/plat.h
new/comfy-aimdo-0.6.0/src/plat.h
--- old/comfy-aimdo-0.5.6/src/plat.h 2026-10-07 21:16:15.000000000 +0200
+++ new/comfy-aimdo-0.6.0/src/plat.h 2026-10-21 13:56:45.000000000 +0200
@@ -90,6 +90,7 @@
#define cuMemUnmap g_cuda.p_cuMemUnmap
#define cuMemRelease g_cuda.p_cuMemRelease
#define cuMemcpyHtoDAsync g_cuda.p_cuMemcpyHtoDAsync
+#define cuMemcpyDtoHAsync g_cuda.p_cuMemcpyDtoHAsync
#define cuEventCreate g_cuda.p_cuEventCreate
#define cuEventDestroy g_cuda.p_cuEventDestroy
#define cuEventRecord g_cuda.p_cuEventRecord
@@ -231,7 +232,7 @@
#endif
/* model_vbar.c */
-size_t vbars_free(ssize_t size);
+bool vbars_free(ssize_t size);
SHARED_EXPORT
uint64_t vbars_analyze(void *devctx, bool only_dirty);
@@ -246,7 +247,7 @@
int aimdo_cuda_free_async(CUdeviceptr devPtr, CUstream hStream,
CUresult (*true_cuMemFreeAsync)(CUdeviceptr,
CUstream));
-bool malloc_graph_alloc(CUdeviceptr *ptr, size_t size, CUstream stream);
+bool malloc_graph_alloc(CUdeviceptr *ptr, size_t size, CUstream stream, int
*result);
bool malloc_graph_free(CUdeviceptr ptr, CUstream stream, int *result);
bool malloc_graph_sync_paused(void);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/comfy-aimdo-0.5.6/src/pyt-cu-plug-alloc-async.c
new/comfy-aimdo-0.6.0/src/pyt-cu-plug-alloc-async.c
--- old/comfy-aimdo-0.5.6/src/pyt-cu-plug-alloc-async.c 2026-10-07
21:16:15.000000000 +0200
+++ new/comfy-aimdo-0.6.0/src/pyt-cu-plug-alloc-async.c 2026-10-21
13:56:45.000000000 +0200
@@ -164,7 +164,10 @@
return true_cuMemAlloc_v2(devPtr, size);
}
- vbars_free(budget_deficit(size + CUDA_MALLOC_HEADROOM));
+ *devPtr = 0;
+ if (!vbars_free(budget_deficit(size + CUDA_MALLOC_HEADROOM))) {
+ return CUDA_ERROR_UNKNOWN;
+ }
if (CHECK_CU(true_cuMemAlloc_v2(&dptr, size))) {
*devPtr = dptr;
@@ -172,7 +175,9 @@
return 0;
}
- vbars_free(size + CUDA_MALLOC_HEADROOM);
+ if (!vbars_free(size + CUDA_MALLOC_HEADROOM)) {
+ return CUDA_ERROR_UNKNOWN;
+ }
status = true_cuMemAlloc_v2(&dptr, size);
if (CHECK_CU(status)) {
*devPtr = dptr;
@@ -223,18 +228,23 @@
if (!set_devctx_for_current_cuda_device()) {
return true_cuMemAllocAsync(devPtr, size, hStream);
}
- if (malloc_graph_alloc(devPtr, size, hStream)) {
- return *devPtr ? 0 : CUDA_ERROR_OUT_OF_MEMORY;
+ if (malloc_graph_alloc(devPtr, size, hStream, &status)) {
+ return status;
}
- vbars_free(budget_deficit(MIN(size, malloc_async_clamp)));
+ *devPtr = 0;
+ if (!vbars_free(budget_deficit(MIN(size, malloc_async_clamp)))) {
+ return CUDA_ERROR_UNKNOWN;
+ }
status = true_cuMemAllocAsync(&dptr, size, hStream);
if (CHECK_CU(status)) {
*devPtr = dptr;
goto success;
}
- vbars_free(size);
+ if (!vbars_free(size)) {
+ return CUDA_ERROR_UNKNOWN;
+ }
status = true_cuMemAllocAsync(&dptr, size, hStream);
if (CHECK_CU(status)) {
*devPtr = dptr;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/comfy-aimdo-0.5.6/src/vrambuf.c
new/comfy-aimdo-0.6.0/src/vrambuf.c
--- old/comfy-aimdo-0.5.6/src/vrambuf.c 2026-10-07 21:16:15.000000000 +0200
+++ new/comfy-aimdo-0.6.0/src/vrambuf.c 2026-10-21 13:56:45.000000000 +0200
@@ -109,7 +109,9 @@
grow_to = buf->max_size;
}
- vbars_free(budget_deficit(grow_to - buf->allocated));
+ if (!vbars_free(budget_deficit(grow_to - buf->allocated))) {
+ return false;
+ }
while (buf->allocated < grow_to) {
size_t to_allocate = grow_to - buf->allocated;
if (to_allocate > VRAM_CHUNK_SIZE) {
@@ -121,7 +123,9 @@
return false;
}
log(DEBUG, "Pytorch allocator attempt exceeds available VRAM
...\n");
- vbars_free(VRAM_CHUNK_SIZE);
+ if (!vbars_free(VRAM_CHUNK_SIZE)) {
+ return false;
+ }
if ((err = three_stooges(buf->base_ptr + buf->allocated,
to_allocate, buf->device, &handle)) != CUDA_SUCCESS) {
bool is_oom = err == CUDA_ERROR_OUT_OF_MEMORY;
log(is_oom ? INFO : AIMDO_LOG_ERROR, "VRAM Allocation failed
(%s)\n", is_oom ? "OOM" : "error");
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/comfy-aimdo-0.5.6/src/xfer-file.c
new/comfy-aimdo-0.6.0/src/xfer-file.c
--- old/comfy-aimdo-0.5.6/src/xfer-file.c 2026-10-07 21:16:15.000000000
+0200
+++ new/comfy-aimdo-0.6.0/src/xfer-file.c 2026-10-21 13:56:45.000000000
+0200
@@ -19,6 +19,7 @@
uint8_t *destination;
size_t size;
bool mark_cold;
+ bool write;
XferFileWait *wait;
} XferFileTask;
@@ -62,8 +63,8 @@
(void)arg;
while (xfer_file_task_pop(&g_xfer_file_reader, &task)) {
- ok = xfer_file_read_at(task.file_handle, task.offset, task.destination,
- task.size, task.mark_cold);
+ ok = task.write ? xfer_file_write_at(task.file_handle, task.offset,
task.destination, task.size) :
+ xfer_file_read_at(task.file_handle, task.offset,
task.destination, task.size, task.mark_cold);
mutex_lock(task.wait->mutex);
task.wait->failed = !ok || task.wait->failed;
if (--task.wait->pending == 0) {
@@ -74,8 +75,8 @@
return 0;
}
-bool xfer_file_read(XferFileHandle file_handle, uint64_t offset, void
*destination,
- size_t size, bool mark_cold) {
+static bool xfer_file_transfer(XferFileHandle file_handle, uint64_t offset,
void *destination,
+ size_t size, bool mark_cold, bool write) {
XferFileWait wait = {
.mutex = mutex_create(),
.condvar = condvar_create(),
@@ -94,6 +95,7 @@
.destination = (uint8_t *)destination + done,
.size = MIN(XFER_FILE_CHUNK_SIZE, size - done),
.mark_cold = mark_cold,
+ .write = write,
.wait = &wait,
};
@@ -123,6 +125,16 @@
return ok;
}
+bool xfer_file_read(XferFileHandle file_handle, uint64_t offset, void
*destination,
+ size_t size, bool mark_cold) {
+ return xfer_file_transfer(file_handle, offset, destination, size,
mark_cold, false);
+}
+
+SHARED_EXPORT
+bool xfer_file_write(XferFileHandle file_handle, uint64_t offset, void
*source, size_t size) {
+ return xfer_file_transfer(file_handle, offset, source, size, false, true);
+}
+
bool xfer_file_init(void) {
memset(&g_xfer_file_reader, 0, sizeof(g_xfer_file_reader));
g_xfer_file_reader.mutex = mutex_create();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/comfy-aimdo-0.5.6/src/xfer-file.h
new/comfy-aimdo-0.6.0/src/xfer-file.h
--- old/comfy-aimdo-0.5.6/src/xfer-file.h 2026-10-07 21:16:15.000000000
+0200
+++ new/comfy-aimdo-0.6.0/src/xfer-file.h 2026-10-21 13:56:45.000000000
+0200
@@ -10,5 +10,10 @@
void xfer_file_cleanup(void);
bool xfer_file_read(XferFileHandle file_handle, uint64_t offset, void
*destination,
size_t size, bool mark_cold);
+SHARED_EXPORT
+bool xfer_file_write(XferFileHandle file_handle, uint64_t offset, void *source,
+ size_t size);
bool xfer_file_read_at(XferFileHandle file_handle, uint64_t offset, void
*destination,
size_t size, bool mark_cold);
+bool xfer_file_write_at(XferFileHandle file_handle, uint64_t offset, void
*source,
+ size_t size);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/comfy-aimdo-0.5.6/src-cuda/dispatch.c
new/comfy-aimdo-0.6.0/src-cuda/dispatch.c
--- old/comfy-aimdo-0.5.6/src-cuda/dispatch.c 2026-10-07 21:16:15.000000000
+0200
+++ new/comfy-aimdo-0.6.0/src-cuda/dispatch.c 2026-10-21 13:56:45.000000000
+0200
@@ -77,6 +77,7 @@
{ (void **)&g_cuda.p_cuMemUnmap, "cuMemUnmap",
CU_GET_PROC_ADDRESS_LEGACY_STREAM },
{ (void **)&g_cuda.p_cuMemRelease, "cuMemRelease",
CU_GET_PROC_ADDRESS_LEGACY_STREAM },
{ (void **)&g_cuda.p_cuMemcpyHtoDAsync, "cuMemcpyHtoDAsync",
CU_GET_PROC_ADDRESS_LEGACY_STREAM },
+ { (void **)&g_cuda.p_cuMemcpyDtoHAsync, "cuMemcpyDtoHAsync",
CU_GET_PROC_ADDRESS_LEGACY_STREAM },
{ (void **)&g_cuda.p_cuEventCreate, "cuEventCreate",
CU_GET_PROC_ADDRESS_LEGACY_STREAM },
{ (void **)&g_cuda.p_cuEventDestroy, "cuEventDestroy",
CU_GET_PROC_ADDRESS_LEGACY_STREAM },
{ (void **)&g_cuda.p_cuEventRecord, "cuEventRecord",
CU_GET_PROC_ADDRESS_LEGACY_STREAM },
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/comfy-aimdo-0.5.6/src-hip/dispatch.c
new/comfy-aimdo-0.6.0/src-hip/dispatch.c
--- old/comfy-aimdo-0.5.6/src-hip/dispatch.c 2026-10-07 21:16:15.000000000
+0200
+++ new/comfy-aimdo-0.6.0/src-hip/dispatch.c 2026-10-21 13:56:45.000000000
+0200
@@ -58,6 +58,7 @@
{ (void **)&g_cuda.p_cuMemUnmap, "hipMemUnmap" },
{ (void **)&g_cuda.p_cuMemRelease, "hipMemRelease" },
{ (void **)&g_cuda.p_cuMemcpyHtoDAsync, "hipMemcpyHtoDAsync" },
+ { (void **)&g_cuda.p_cuMemcpyDtoHAsync, "hipMemcpyDtoHAsync" },
{ (void **)&g_cuda.p_cuEventCreate, "hipEventCreateWithFlags" },
{ (void **)&g_cuda.p_cuEventDestroy, "hipEventDestroy" },
{ (void **)&g_cuda.p_cuEventRecord, "hipEventRecord" },
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/comfy-aimdo-0.5.6/src-posix/xfer-file-plat.c
new/comfy-aimdo-0.6.0/src-posix/xfer-file-plat.c
--- old/comfy-aimdo-0.5.6/src-posix/xfer-file-plat.c 2026-10-07
21:16:15.000000000 +0200
+++ new/comfy-aimdo-0.6.0/src-posix/xfer-file-plat.c 2026-10-21
13:56:45.000000000 +0200
@@ -7,18 +7,21 @@
#include <fcntl.h>
#include <unistd.h>
-bool xfer_file_read_at(XferFileHandle file_handle, uint64_t offset, void
*destination,
- size_t size, bool mark_cold) {
+static bool xfer_file_transfer_at(XferFileHandle file_handle, uint64_t offset,
void *buffer,
+ size_t size, bool mark_cold, bool write) {
int fd = (int)file_handle;
size_t done = 0;
while (done < size) {
- ssize_t n = pread(fd, (char *)destination + done, size - done,
- (off_t)(offset + done));
+ ssize_t n = write ? pwrite(fd, (char *)buffer + done, size - done,
(off_t)(offset + done)) :
+ pread(fd, (char *)buffer + done, size - done,
(off_t)(offset + done));
+ if (n < 0 && errno == EINTR) {
+ continue;
+ }
if (n <= 0) {
- log(AIMDO_LOG_ERROR, "%s: pread failed at %llu (errno=%d)\n",
__func__,
- (ull)(offset + done), errno);
+ log(AIMDO_LOG_ERROR, "%s: %s failed at %llu (errno=%d)\n",
__func__,
+ write ? "pwrite" : "pread", (ull)(offset + done), errno);
return false;
}
if (mark_cold) {
@@ -34,3 +37,12 @@
return true;
}
+
+bool xfer_file_read_at(XferFileHandle file_handle, uint64_t offset, void
*destination,
+ size_t size, bool mark_cold) {
+ return xfer_file_transfer_at(file_handle, offset, destination, size,
mark_cold, false);
+}
+
+bool xfer_file_write_at(XferFileHandle file_handle, uint64_t offset, void
*source, size_t size) {
+ return xfer_file_transfer_at(file_handle, offset, source, size, false,
true);
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/comfy-aimdo-0.5.6/src-win/dxcore_c.h
new/comfy-aimdo-0.6.0/src-win/dxcore_c.h
--- old/comfy-aimdo-0.5.6/src-win/dxcore_c.h 1970-01-01 01:00:00.000000000
+0100
+++ new/comfy-aimdo-0.6.0/src-win/dxcore_c.h 2026-10-21 13:56:45.000000000
+0200
@@ -0,0 +1,93 @@
+#ifndef _DXCORE_MINIMAL_C_H_
+#define _DXCORE_MINIMAL_C_H_
+
+#include <windows.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <initguid.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+DEFINE_GUID(IID_IDXCoreAdapterFactory, 0x78ee5945, 0xc36e, 0x4b13, 0xa6, 0x69,
0x00, 0x5d, 0xd1, 0x1c, 0x0f, 0x06);
+DEFINE_GUID(IID_IDXCoreAdapter, 0xf0db4c7f, 0xfe5a, 0x42a2, 0xbd, 0x62, 0xf2,
0xa6, 0xcf, 0x6f, 0xc8, 0x3e);
+
+typedef enum DXCoreAdapterProperty {
+ DXCoreAdapterProperty_DriverDescription = 2
+} DXCoreAdapterProperty;
+
+typedef enum DXCoreAdapterState {
+ DXCoreAdapterState_AdapterMemoryBudget = 1
+} DXCoreAdapterState;
+
+typedef enum DXCoreSegmentGroup {
+ DXCoreSegmentGroup_Local = 0,
+ DXCoreSegmentGroup_NonLocal = 1
+} DXCoreSegmentGroup;
+
+typedef struct DXCoreAdapterMemoryBudgetNodeSegmentGroup {
+ uint32_t nodeIndex;
+ DXCoreSegmentGroup segmentGroup;
+} DXCoreAdapterMemoryBudgetNodeSegmentGroup;
+
+typedef struct DXCoreAdapterMemoryBudget {
+ uint64_t budget;
+ uint64_t currentUsage;
+ uint64_t availableForReservation;
+ uint64_t currentReservation;
+} DXCoreAdapterMemoryBudget;
+
+typedef struct IDXCoreAdapter IDXCoreAdapter;
+typedef struct IDXCoreAdapterFactory IDXCoreAdapterFactory;
+
+/* Ask your agent to define extra functions if needed.
+ * We maintain this form to inventory what is used.
+ * Unused slots retain their names and ABI positions as void pointers.
+ */
+/* --- IDXCoreAdapter --- */
+typedef struct IDXCoreAdapterVtbl {
+ // IUnknown methods
+ void *QueryInterface;
+ void *AddRef;
+ ULONG(STDMETHODCALLTYPE* Release)(IDXCoreAdapter* This);
+
+ // IDXCoreAdapter methods
+ void *IsValid;
+ void *IsAttributeSupported;
+ void *IsPropertySupported;
+ HRESULT(STDMETHODCALLTYPE* GetProperty)(IDXCoreAdapter* This,
DXCoreAdapterProperty property, size_t bufferSize, void* propertyData);
+ void *GetPropertySize;
+ void *IsQueryStateSupported;
+ HRESULT(STDMETHODCALLTYPE* QueryState)(IDXCoreAdapter* This,
DXCoreAdapterState state, size_t inputStateDetailsSize, const void*
inputStateDetails, size_t outputBufferSize, void* outputBuffer);
+ void *IsSetStateSupported;
+ void *SetState;
+ void *GetFactory;
+} IDXCoreAdapterVtbl;
+
+struct IDXCoreAdapter { IDXCoreAdapterVtbl* lpVtbl; };
+
+/* --- IDXCoreAdapterFactory --- */
+typedef struct IDXCoreAdapterFactoryVtbl {
+ // IUnknown
+ void *QueryInterface;
+ void *AddRef;
+ ULONG(STDMETHODCALLTYPE* Release)(IDXCoreAdapterFactory* This);
+
+ // IDXCoreAdapterFactory
+ void *CreateAdapterList;
+ HRESULT(STDMETHODCALLTYPE* GetAdapterByLuid)(IDXCoreAdapterFactory* This,
const LUID* adapterLUID, REFIID riid, void** ppvAdapter);
+ void *IsNotificationTypeSupported;
+ void *RegisterEventNotification;
+ void *UnregisterEventNotification;
+} IDXCoreAdapterFactoryVtbl;
+
+struct IDXCoreAdapterFactory { IDXCoreAdapterFactoryVtbl* lpVtbl; };
+
+STDAPI DXCoreCreateAdapterFactory(REFIID riid, void** ppvFactory);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _DXCORE_MINIMAL_C_H_
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/comfy-aimdo-0.5.6/src-win/shmem-detect.c
new/comfy-aimdo-0.6.0/src-win/shmem-detect.c
--- old/comfy-aimdo-0.5.6/src-win/shmem-detect.c 2026-10-07
21:16:15.000000000 +0200
+++ new/comfy-aimdo-0.6.0/src-win/shmem-detect.c 2026-10-21
13:56:45.000000000 +0200
@@ -3,6 +3,7 @@
#include <windows.h>
#include <dxgi1_4.h>
+#include "dxcore_c.h"
#if defined(__HIP_PLATFORM_AMD__)
typedef union {
@@ -33,6 +34,10 @@
g_wddm_adapter->lpVtbl->Release(g_wddm_adapter);
g_wddm_adapter = NULL;
}
+ if (g_dxcore_adapter) {
+ g_dxcore_adapter->lpVtbl->Release(g_dxcore_adapter);
+ g_dxcore_adapter = NULL;
+ }
#if defined(__HIP_PLATFORM_AMD__)
AimdoHipDeviceProp hip_props = {0};
@@ -51,7 +56,34 @@
fail_code++;
+ {
+ IDXCoreAdapterFactory *dxcore_factory = NULL;
+ if (SUCCEEDED(DXCoreCreateAdapterFactory(&IID_IDXCoreAdapterFactory,
(void **)&dxcore_factory))) {
+ IDXCoreAdapter *d_adapter = NULL;
+ if
(SUCCEEDED(dxcore_factory->lpVtbl->GetAdapterByLuid(dxcore_factory, &cuda_luid,
&IID_IDXCoreAdapter, (void **)&d_adapter))) {
+ DXCoreAdapterMemoryBudgetNodeSegmentGroup nodeSegmentGroup =
{0};
+ DXCoreAdapterMemoryBudget test_budget = {0};
+ nodeSegmentGroup.segmentGroup = DXCoreSegmentGroup_Local;
+
+ if (SUCCEEDED(d_adapter->lpVtbl->QueryState(d_adapter,
DXCoreAdapterState_AdapterMemoryBudget, sizeof(nodeSegmentGroup),
&nodeSegmentGroup, sizeof(test_budget), &test_budget))) {
+ g_dxcore_adapter = d_adapter;
+ d_adapter->lpVtbl->GetProperty(d_adapter,
DXCoreAdapterProperty_DriverDescription, sizeof(adapter_name), adapter_name);
+ adapter_name[sizeof(adapter_name) - 1] = '\0';
+ log(INFO,
+ "comfy-aimdo DXCore adapter match: %s
runtime_luid=%08lx:%08lx\n",
+ adapter_name,
+ (unsigned long)(unsigned int)cuda_luid.HighPart,
+ (unsigned long)cuda_luid.LowPart);
+ } else {
+ d_adapter->lpVtbl->Release(d_adapter);
+ }
+ }
+ dxcore_factory->lpVtbl->Release(dxcore_factory);
+ }
+ }
+
if (FAILED(CreateDXGIFactory1(&IID_IDXGIFactory4, (void **)&factory))) {
+ if (g_dxcore_adapter) return true;
goto fail;
}
@@ -86,11 +118,20 @@
adapter->lpVtbl->Release(adapter);
}
-fail:
- g_wddm_adapter = NULL;
if (factory) {
factory->lpVtbl->Release(factory);
}
+
+ if (g_dxcore_adapter) {
+ return true;
+ }
+
+fail:
+ g_wddm_adapter = NULL;
+ if (g_dxcore_adapter) {
+ g_dxcore_adapter->lpVtbl->Release(g_dxcore_adapter);
+ g_dxcore_adapter = NULL;
+ }
log(WARNING, "comfy-aimdo WDDM init failed (%d). aimdo is blind to the
driver sysmem fallback policy\n", fail_code);
return false;
}
@@ -111,6 +152,7 @@
DXGI_QUERY_VIDEO_MEMORY_INFO info;
uint64_t effective_budget = vram_capacity;
size_t free_vram = 0, total_vram = 0;
+ bool budget_queried = false;
bool used_nvml = false;
uint64_t now = GET_TICK();
@@ -121,9 +163,32 @@
wddm_timestamp_last_check = now;
total_vram_last_check = total_vram_usage;
- if (g_wddm_adapter) {
+ *prevailing_deficit_method = "Physical Capacity (Fallback)";
+
+ if (g_dxcore_adapter) {
+ DXCoreAdapterMemoryBudgetNodeSegmentGroup nodeSegmentGroup = {0};
+ DXCoreAdapterMemoryBudget budget = {0};
+ nodeSegmentGroup.segmentGroup = DXCoreSegmentGroup_Local;
+
+ if (SUCCEEDED(g_dxcore_adapter->lpVtbl->QueryState(g_dxcore_adapter,
DXCoreAdapterState_AdapterMemoryBudget, sizeof(nodeSegmentGroup),
&nodeSegmentGroup, sizeof(budget), &budget))) {
+ effective_budget = budget.budget;
+ *prevailing_deficit_method = "DXCore budget";
+ budget_queried = true;
+ log(DEBUG,
+ "%s: DXCore budget=%zu MB usage=%zu MB reservation=%zu MB
available=%zu MB\n",
+ __func__, (size_t)(budget.budget / M),
(size_t)(budget.currentUsage / M),
+ (size_t)(budget.currentReservation / M),
+ (size_t)(budget.availableForReservation / M));
+ } else {
+ log(WARNING, "comfy-aimdo DXCore VRAM query failed. Falling back
to WDDM...\n");
+ }
+ }
+
+ if (!budget_queried && g_wddm_adapter) {
if
(SUCCEEDED(g_wddm_adapter->lpVtbl->QueryVideoMemoryInfo(g_wddm_adapter, 0,
DXGI_MEMORY_SEGMENT_GROUP_LOCAL, &info))) {
effective_budget = info.Budget;
+ *prevailing_deficit_method = "WDDM budget";
+ budget_queried = true;
log(DEBUG,
"%s: WDDM budget=%zu MB usage=%zu MB reservation=%zu MB
available=%zu MB\n",
__func__, (size_t)(info.Budget / M),
(size_t)(info.CurrentUsage / M),
@@ -135,7 +200,6 @@
}
deficit_sync = (ssize_t)(total_vram_usage + WDDM_BUDGET_HEADROOM) -
(ssize_t)effective_budget;
- *prevailing_deficit_method = "WDDM budget";
#if defined(AIMDO_CUDA)
used_nvml = nvml_device && aimdo_nvml_memory_info(nvml_device, &free_vram,
&total_vram);
@@ -158,10 +222,14 @@
return true;
}
-void aimdo_wddm_cleanup()
+void aimdo_wddm_cleanup(void)
{
if (g_wddm_adapter) {
g_wddm_adapter->lpVtbl->Release(g_wddm_adapter);
g_wddm_adapter = NULL;
}
+ if (g_dxcore_adapter) {
+ g_dxcore_adapter->lpVtbl->Release(g_dxcore_adapter);
+ g_dxcore_adapter = NULL;
+ }
}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/comfy-aimdo-0.5.6/src-win/xfer-file-plat.c
new/comfy-aimdo-0.6.0/src-win/xfer-file-plat.c
--- old/comfy-aimdo-0.5.6/src-win/xfer-file-plat.c 2026-10-07
21:16:15.000000000 +0200
+++ new/comfy-aimdo-0.6.0/src-win/xfer-file-plat.c 2026-10-21
13:56:45.000000000 +0200
@@ -3,12 +3,11 @@
#include <windows.h>
-bool xfer_file_read_at(XferFileHandle file_handle, uint64_t offset, void
*destination,
- size_t size, bool mark_cold) {
+static bool xfer_file_transfer_at(XferFileHandle file_handle, uint64_t offset,
void *buffer,
+ size_t size, bool write) {
HANDLE handle = (HANDLE)(uintptr_t)file_handle;
size_t done = 0;
- (void)mark_cold;
while (done < size) {
DWORD got = 0;
DWORD requested = (DWORD)MIN((uint64_t)0x7ffff000,
@@ -27,12 +26,14 @@
__func__, err, (ull)(offset + done), requested);
return false;
}
- if (!ReadFile(handle, (char *)destination + done, requested, &got,
&overlapped)) {
+ BOOL ok = write ? WriteFile(handle, (char *)buffer + done, requested,
&got, &overlapped) :
+ ReadFile(handle, (char *)buffer + done, requested,
&got, &overlapped);
+ if (!ok) {
DWORD err = GetLastError();
if (err != ERROR_IO_PENDING) {
- log(AIMDO_LOG_ERROR, "%s: ReadFile failed error=%lu handle=%p
offset=%llu size=%lu\n",
- __func__, err, (void *)handle, (ull)(offset + done),
requested);
+ log(AIMDO_LOG_ERROR, "%s: %s failed error=%lu handle=%p
offset=%llu size=%lu\n",
+ __func__, write ? "WriteFile" : "ReadFile", err, (void
*)handle, (ull)(offset + done), requested);
CloseHandle(event);
return false;
}
@@ -47,7 +48,7 @@
}
CloseHandle(event);
if (got == 0) {
- log(AIMDO_LOG_ERROR, "%s: zero-byte read handle=%p offset=%llu
size=%lu\n",
+ log(AIMDO_LOG_ERROR, "%s: zero-byte transfer handle=%p offset=%llu
size=%lu\n",
__func__, (void *)handle, (ull)(offset + done), requested);
return false;
}
@@ -56,3 +57,13 @@
return true;
}
+
+bool xfer_file_read_at(XferFileHandle file_handle, uint64_t offset, void
*destination,
+ size_t size, bool mark_cold) {
+ (void)mark_cold;
+ return xfer_file_transfer_at(file_handle, offset, destination, size,
false);
+}
+
+bool xfer_file_write_at(XferFileHandle file_handle, uint64_t offset, void
*source, size_t size) {
+ return xfer_file_transfer_at(file_handle, offset, source, size, true);
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/comfy-aimdo-0.5.6/tests/vbar-pageout-cuda.py
new/comfy-aimdo-0.6.0/tests/vbar-pageout-cuda.py
--- old/comfy-aimdo-0.5.6/tests/vbar-pageout-cuda.py 1970-01-01
01:00:00.000000000 +0100
+++ new/comfy-aimdo-0.6.0/tests/vbar-pageout-cuda.py 2026-10-21
13:56:45.000000000 +0200
@@ -0,0 +1,144 @@
+"""Run on a disposable CUDA host with the matching native Aimdo build."""
+import ctypes
+import os
+
+os.environ.setdefault("PYTORCH_ALLOC_CONF", "backend:cudaMallocAsync")
+
+import torch
+import comfy_aimdo.control as aimdo
+
+assert aimdo.init("cuda") and aimdo.init_device(0)
+from comfy_aimdo.model_vbar import ModelVBAR, vbar_signature_compare
+from comfy_aimdo.torch import get_tensor_from_raw_ptr
+from comfy_aimdo.vram_buffer import VRAMBuffer
+
+PAGE = 32 * 1024 ** 2
+device = torch.device("cuda", 0)
+size = PAGE + 123
+
+for pinned in (False, True):
+ calls = []
+ buffer = torch.full((size + 22,), 177, dtype=torch.uint8,
pin_memory=pinned)
+ saved = False
+
+ def pageout(offset, length):
+ global saved
+ calls.append((offset, length))
+ if not saved:
+ value = get_tensor_from_raw_ptr(vbar.base_addr, size, device)
+ buffer[11:-11].copy_(value)
+ saved = True
+
+ vbar = ModelVBAR(size, 0, pageout_callback=pageout)
+ signature = vbar.fault(vbar.base_addr, size)
+ assert signature is not None
+ value = get_tensor_from_raw_ptr(vbar.base_addr, size, device)
+ value[:PAGE].fill_(31)
+ value[PAGE:].fill_(79)
+ vbar.set_watermark(0)
+ assert not calls # Live consumers still pin the pages.
+ del value
+ vbar.unpin(vbar.base_addr, size)
+ assert calls == [(0, PAGE), (PAGE, 123)]
+ assert bool((buffer[:11] == 177).all() & (buffer[-11:] == 177).all())
+ assert bool((buffer[11:11 + PAGE] == 31).all() & (buffer[11 + PAGE:-11] ==
79).all())
+
+ # Fault only allocates: the owner supplies the contents and detects new
pages.
+ vbar.set_watermark(size)
+ restored = vbar.fault(vbar.base_addr, size)
+ assert restored is not None and not vbar_signature_compare(signature,
restored)
+ value = get_tensor_from_raw_ptr(vbar.base_addr, size, device)
+ value.copy_(buffer[11:-11])
+ assert bool((value[:PAGE] == 31).all() & (value[PAGE:] == 79).all())
+ del value
+ vbar.unpin(vbar.base_addr, size)
+ vbar.set_watermark(0)
+ assert vbar.loaded_size() == 0
+ vbar.set_watermark(size)
+ assert vbar.fault(vbar.base_addr, size) is not None
+ calls.clear()
+ vbar.close()
+ assert not calls # Destruction never requests backing.
+ print("PAGEOUT_PASS pinned=", pinned, flush=True)
+
+cuda = ctypes.CDLL("nvcuda.dll" if os.name == "nt" else "libcuda.so.1")
+cuda.cuMemAlloc_v2.argtypes = [ctypes.POINTER(ctypes.c_uint64),
ctypes.c_size_t]
+cuda.cuMemAlloc_v2.restype = ctypes.c_int
+cuda.cuMemAllocAsync.argtypes = [ctypes.POINTER(ctypes.c_uint64),
ctypes.c_size_t, ctypes.c_void_p]
+cuda.cuMemAllocAsync.restype = ctypes.c_int
+stream = torch.cuda.current_stream()
+headroom = aimdo.get_simple_vram_headroom()
+
+for operation in ("watermark", "free_memory", "unpin", "fault", "grow",
"malloc", "malloc_async", "graph_small", "graph_large"):
+ failed = True
+
+ def pageout(offset, length):
+ if failed:
+ raise OSError("injected writeback failure")
+
+ vbar = ModelVBAR(2 * PAGE, 0, pageout_callback=pageout)
+ assert vbar.fault(vbar.base_addr, 2 * PAGE) is not None
+ value = get_tensor_from_raw_ptr(vbar.base_addr, 2 * PAGE, device)
+ value.fill_(79)
+ torch.cuda.synchronize()
+ if operation == "unpin":
+ vbar.set_watermark(0)
+ else:
+ vbar.unpin(vbar.base_addr, 2 * PAGE)
+ graph = target = None
+ if operation in ("watermark", "free_memory", "unpin"):
+ action = {
+ "watermark": lambda: vbar.set_watermark(0),
+ "free_memory": lambda: vbar.free_memory(2 * PAGE),
+ "unpin": lambda: vbar.unpin(vbar.base_addr, 2 * PAGE),
+ }[operation]
+ else:
+ # Force eviction without actually filling the GPU. Failure may
originate
+ # in a different VBAR from the one whose fault requested the
reclamation.
+ aimdo.set_simple_vram_headroom(1 << 60)
+ if operation == "fault":
+ target = ModelVBAR(PAGE, 0)
+ action = lambda: target.fault(target.base_addr, PAGE)
+ elif operation == "grow":
+ target = VRAMBuffer(PAGE, 0)
+ action = lambda: target.get(PAGE)
+ else:
+ if operation.startswith("graph_"):
+ graph = aimdo.record(stream)
+ pointer = ctypes.c_uint64(123)
+ if operation == "malloc":
+ result = cuda.cuMemAlloc_v2(ctypes.byref(pointer), 1024)
+ else:
+ amount = PAGE if operation == "graph_large" else 1024
+ result = cuda.cuMemAllocAsync(ctypes.byref(pointer), amount,
stream.cuda_stream)
+ assert result == 999 and pointer.value == 0, (operation, result,
pointer.value)
+ action = None
+ if action is not None:
+ try:
+ action()
+ except RuntimeError:
+ pass
+ else:
+ raise AssertionError(f"{operation}: writeback error was lost")
+ aimdo.set_simple_vram_headroom(headroom)
+ if graph is not None:
+ graph.abort()
+ del graph, target
+ assert vbar.get_residency() == [1, 1], operation # Retained, with no
leaked consumer pins.
+ assert bool((value == 79).all()), operation
+ del value
+
+ # The client repairs its callback. No Aimdo error latch to clear, and a
+ # subsequent fault must neither fail nor consume an error from an earlier
call.
+ failed = False
+ vbar.prioritize()
+ assert vbar.fault(vbar.base_addr, 2 * PAGE) is not None
+ vbar.unpin(vbar.base_addr, 2 * PAGE)
+ assert vbar.free_memory(2 * PAGE) == 2 * PAGE
+ assert vbar.loaded_size() == 0
+ vbar.close()
+ print("PAGEOUT_FAILURE_RECOVERED", operation, flush=True)
+
+del vbar, buffer
+torch.cuda.synchronize()
+aimdo.deinit()