Using iMX8MP GPU on a process that call mlockall(MCL_FUTURE) causes kernel BUG() A customer reported an issue related to the use of the iMX8MP GPU (drawing using EGL). When the process that initializes the GPU previously called mlockall(MCL_FUTURE), as soon as the CMA area is mapped into userspace, the kernel reports the following BUG(): Kernel BUG at remap_pfn_range_internal+0x23c/0x2c8
Internal error: Oops - BUG: 00000000f2000800 [#1] PREEMPT SMP
CPU: 0 PID: 3197 Comm: galcore_window_ Tainted: G
O
6.6.142-7.7.0-devel #1-Torizon
Hardware name: Toradex Verdin iMX8M Plus WB on Verdin Development Board (DT)
pc : remap_pfn_range_internal+0x23c/0x2c8
x27: 00000000a2100000
x20: 0068000000000fcb
x19: 0000007f90000000
^^^^^^^^^^^^^^^^^^^^^^ the PTE already present
Call trace:
remap_pfn_range_internal+0x23c/0x2c8
remap_pfn_range+0x24/0x58
dma_direct_mmap+0xf4/0x150
dma_mmap_attrs+0x18/0x3c
_CMAFSLMapUser+0x9c/0x150
[galcore]
gckOS_LockPages+0xe4/0x148
[galcore]
gckKERNEL_MapVideoMemory+0x90/0x1dc [galcore]
gckVIDMEM_NODE_LockCPU+0x1b4/0x250 [galcore]
_LockVideoMemory.isra.0+0x1f4/0x28c [galcore]
gckKERNEL_Dispatch+0x210/0x1730 [galcore]
gckDEVICE_Dispatch+0xcc/0x220
[galcore]
drv_ioctl+0x340/0x444
[galcore]
__arm64_sys_ioctl+0xac/0xf0
Kernel panic - not syncing: Oops - BUG: Fatal exception mlockall(MCL_FUTURE) is commonly used by real time applications, and the customer who reported the issue is using CODESYS to drive their HMI. We executed an AI-assisted analysis of the issue, and uncovered a plausible explanation for the reason the issue is being triggered: The CMA allocator in the galcore driver doesn't include an .mmap() hook. During the _CMAFSLMapUser() execution, it calls mmap() to get a vma, which is then used to locate the CMA area using find_vma(). The way mmap() is called causes the kernel to lazilly allocate a SHM area to fullfil the request. In normal cases, this allocation gets discarded moments later when the CMA are is found and remapped. However, when MCL_FUTURE is active the kernel cannot lazilly allocate the SHM area anymore, so it goes and populates the entire area before mmap() returns. In this case we have valid pages on this address, and the later call to remap_pfn_range() would end up silently discarding the memory management housekeeping information, which is exactly what the BUG() prevents. I'll attach a zip file which contains the source code of a program that reproduces the issue consistently without the need to run CODESYS. The AI agent suggested the following patch to fix it: diff -uNr a/hal/kernel/inc/gc_hal_options.h b/hal/kernel/inc/gc_hal_options.h
--- a/hal/kernel/inc/gc_hal_options.h 2026-09-04 13:00:44.596621860 +0000
+++ b/hal/kernel/inc/gc_hal_options.h 2026-09-04 13:01:15.643862002 +0000
@@ -1471,9 +1471,17 @@
* Enable this macro can replace the /dev/zero by anon_inode:
* [galcore] in /proc/ /maps.
* Without the macro, run 'cat /proc/ /maps' will print "/dev/zero".
+ *
+ * It is also what gives the allocators an ->mmap handler, which is
+ * required so that the reservation made by vm_mmap() is created as a
+ * device mapping (VM_IO | VM_PFNMAP) rather than as ordinary anonymous
+ * memory. Without it, a process that has called mlockall(MCL_FUTURE)
+ * gets the range pre-faulted inside vm_mmap(), and the subsequent
+ * remap_pfn_range() then hits BUG_ON(!pte_none()) in remap_pte_range().
+ * See tmp_mmap() in gc_hal_kernel_allocator.c.
*/
#ifndef gcdANON_FILE_FOR_ALLOCATOR
-# define gcdANON_FILE_FOR_ALLOCATOR 0
+# define gcdANON_FILE_FOR_ALLOCATOR 1
#endif
/*
diff -uNr a/hal/os/linux/kernel/allocator/freescale/gc_hal_kernel_allocator_cma.c b/hal/os/linux/kernel/allocator/freescale/gc_hal_kernel_allocator_cma.c
--- a/hal/os/linux/kernel/allocator/freescale/gc_hal_kernel_allocator_cma.c 2026-09-04 13:00:44.605314869 +0000
+++ b/hal/os/linux/kernel/allocator/freescale/gc_hal_kernel_allocator_cma.c 2026-09-04 13:01:15.644216883 +0000
@@ -400,7 +400,13 @@
gcmkHEADER_ARG("Allocator=%p Mdl=%p Cacheable=%d", Allocator, Mdl, Cacheable);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
+#if gcdANON_FILE_FOR_ALLOCATOR
+ /* Same as the gfp, dma and reserved_mem allocators: go through the
+ * allocator's anon file so that its ->mmap handler runs. */
+ userLogical = (gctPOINTER)vm_mmap(Allocator->anon_file,
+# else
userLogical = (gctPOINTER)vm_mmap(gcvNULL,
+# endif
0L,
Mdl->numPages * PAGE_SIZE,
PROT_READ | PROT_WRITE,
diff -uNr a/hal/os/linux/kernel/gc_hal_kernel_allocator.c b/hal/os/linux/kernel/gc_hal_kernel_allocator.c
--- a/hal/os/linux/kernel/gc_hal_kernel_allocator.c 2026-09-04 13:00:44.603242857 +0000
+++ b/hal/os/linux/kernel/gc_hal_kernel_allocator.c 2026-09-04 13:01:15.644041018 +0000
@@ -104,6 +104,36 @@
static int
tmp_mmap(struct file *fp, struct vm_area_struct *vma)
{
+ /*
+ * Declare the reservation as a device mapping with raw PFNs, before
+ * mmap() returns it to the caller. remap_pfn_range() sets both flags
+ * anyway; setting them here only makes them effective from the moment
+ * the VMA is created, and that is what matters:
+ *
+ * - the kernel treats VM_IO | VM_PFNMAP as VM_SPECIAL, documented in
+ * include/linux/mm.h as "Special vmas that are non-mergable,
+ * non-mlock()able". mmap_region() therefore clears VM_LOCKED from
+ * this VMA and leaves mm->locked_vm alone, and __mm_populate()
+ * skips it outright ("if (vma->vm_flags & (VM_IO | VM_PFNMAP))
+ * continue;" in mm/gup.c).
+ *
+ * - so a process that has called mlockall(MCL_FUTURE) no longer has
+ * this range pre-faulted inside vm_mmap(). Every other mapping in
+ * that process keeps being locked and pre-faulted as before; only
+ * device memory, which is neither pageable nor swappable and gains
+ * nothing from being pre-faulted, is left out.
+ *
+ * - and the allocator's own remap_pfn_range(), a few microseconds
+ * later, therefore finds an empty range instead of one the kernel
+ * has just populated, so it no longer trips
+ * BUG_ON(!pte_none(ptep_get(pte))) in remap_pte_range().
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0)
+ vm_flags_set(vma, VM_IO | VM_PFNMAP);
+#else
+ vma->vm_flags |= VM_IO | VM_PFNMAP;
+#endif
+
return 0;
} The upstream driver uses a different mechanism which bypasses this issue. Other galcore allocators use the same pattern (calling vm_mmap to get a vma) and thus are likely succeptible to the same BUG(). Could you please look into this and fix the galcore driver? This issue prevents a real use case from working properly, and is affecting our customer directly. Thank you, Rafael
View full article