DF-4507 / static_proof.log
=== [1] The only vm_lowmem references in the whole kernel tree ===
# (expect: only ttm_page_alloc.c REGISTER/DEREGISTER — no DECLARATION, no INVOKE)
NO OTHER REFS: vm_lowmem is never declared/invoked anywhere
447: manager->lowmem_handler = EVENTHANDLER_REGISTER(vm_lowmem,
453: EVENTHANDLER_DEREGISTER(vm_lowmem, manager->lowmem_handler);
=== [2] Every EVENTHANDLER_INVOKE site in the kernel (none fire vm_lowmem) ===
sys/bus/u4b/usb_device.c:1351:EVENTHANDLER_INVOKE
sys/dev/acpica/acpi.c:3028:EVENTHANDLER_INVOKE
sys/dev/acpica/acpi.c:3040:EVENTHANDLER_INVOKE
sys/dev/acpica/acpi.c:3052:EVENTHANDLER_INVOKE
sys/dev/acpica/acpi.c:3064:EVENTHANDLER_INVOKE
sys/dev/acpica/acpi_lid.c:175:EVENTHANDLER_INVOKE
sys/dev/acpica/acpi_lid.c:177:EVENTHANDLER_INVOKE
sys/kern/kern_shutdown.c:278:EVENTHANDLER_INVOKE
sys/kern/kern_shutdown.c:402:EVENTHANDLER_INVOKE
sys/kern/kern_shutdown.c:405:EVENTHANDLER_INVOKE
sys/kern/subr_power.c:105:EVENTHANDLER_INVOKE
sys/net/bpf.c:299:EVENTHANDLER_INVOKE
sys/net/bpf.c:328:EVENTHANDLER_INVOKE
sys/net/if.c:1120:EVENTHANDLER_INVOKE
sys/net/if.c:1138:EVENTHANDLER_INVOKE
sys/net/if.c:1190:EVENTHANDLER_INVOKE
sys/net/if.c:1227:EVENTHANDLER_INVOKE
sys/net/if.c:1822:EVENTHANDLER_INVOKE
sys/net/if.c:1844:EVENTHANDLER_INVOKE
sys/net/if.c:1860:EVENTHANDLER_INVOKE
sys/net/if.c:2206:EVENTHANDLER_INVOKE
sys/net/if.c:2232:EVENTHANDLER_INVOKE
sys/net/if.c:2382:EVENTHANDLER_INVOKE
sys/net/if.c:750:EVENTHANDLER_INVOKE
sys/net/if.c:958:EVENTHANDLER_INVOKE
sys/net/if_clone.c:180:EVENTHANDLER_INVOKE
sys/net/lagg/if_lagg.c:261:EVENTHANDLER_INVOKE
sys/net/lagg/if_lagg.c:287:EVENTHANDLER_INVOKE
sys/net/lagg/if_lagg.c:473:EVENTHANDLER_INVOKE
sys/net/tap/if_tap.c:433:EVENTHANDLER_INVOKE
sys/net/tun/if_tun.c:330:EVENTHANDLER_INVOKE
sys/netinet/in.c:634:EVENTHANDLER_INVOKE
sys/netinet/in.c:691:EVENTHANDLER_INVOKE
sys/netinet/in.c:726:EVENTHANDLER_INVOKE
sys/netinet6/in6.c:803:EVENTHANDLER_INVOKE
sys/netinet6/in6.c:852:EVENTHANDLER_INVOKE
sys/sys/eventhandler.h:114:EVENTHANDLER_INVOKE
COUNT=0: EVENTHANDLER_INVOKE(vm_lowmem) does not exist
=== [3] register_shrinker is never defined (only called from dead code) ===
sys/dev/drm/i915/i915_gem_shrinker.c:525: WARN_ON(register_shrinker(&i915->mm.shrinker));
sys/dev/drm/i915/i915_gem_shrinker.c:546: unregister_shrinker(&i915->mm.shrinker);
sys/dev/drm/ttm/ttm_page_alloc_dma.c:161: * @shrinker: The structure used by [un|]register_shrinker
sys/dev/drm/ttm/ttm_page_alloc_dma.c:1149: return register_shrinker(&manager->mm_shrink);
sys/dev/drm/ttm/ttm_page_alloc_dma.c:1154: unregister_shrinker(&manager->mm_shrink);
# i915's call is inside '#if 0':
i915->mm.shrinker.seeks = DEFAULT_SEEKS;
#if 0
i915->mm.shrinker.batch = 4096;
#endif
#if 0
WARN_ON(register_shrinker(&i915->mm.shrinker));
i915->mm.oom_notifier.notifier_call = i915_gem_shrinker_oom;
WARN_ON(register_oom_notifier(&i915->mm.oom_notifier));
i915->mm.vmap_notifier.notifier_call = i915_gem_shrinker_vmap;
WARN_ON(register_vmap_purge_notifier(&i915->mm.vmap_notifier));
#endif
# dma pool variant is preprocessed out:
*/
#if defined(CONFIG_SWIOTLB) || defined(CONFIG_INTEL_IOMMU)
#define pr_fmt(fmt) "[TTM] " fmt
# the shrinker shim defines types only — no registration API:
#include <linux/types.h>
struct shrink_control {
gfp_t gfp_mask;
unsigned long nr_to_scan;
};
struct shrinker {
unsigned long (*count_objects)(struct shrinker *,
struct shrink_control *sc);
unsigned long (*scan_objects)(struct shrinker *,
struct shrink_control *sc);
int seeks;
};
#define DEFAULT_SEEKS 2
#define SHRINK_STOP (~0UL)
#endif /* _LINUX_SHRINKER_H_ */
=== [4] ttm_page_alloc.c itself is LIVE (no guard) and registers the callback ===
static void ttm_pool_mm_shrink_init(struct ttm_pool_manager *manager)
{
manager->mm_shrink.count_objects = ttm_pool_shrink_count;
manager->mm_shrink.scan_objects = ttm_pool_shrink_scan;
manager->mm_shrink.seeks = 1;
manager->lowmem_handler = EVENTHANDLER_REGISTER(vm_lowmem,
ttm_pool_shrink_scan, manager, EVENTHANDLER_PRI_ANY);
}
static void ttm_pool_mm_shrink_fini(struct ttm_pool_manager *manager)
{
EVENTHANDLER_DEREGISTER(vm_lowmem, manager->lowmem_handler);
}
=== [5] The only LIVE reclamation path: put-path trim at options.max_size ===
/* Check that we don't go over the pool limit */
npages = 0;
if (pool->npages > _manager->options.max_size) {
npages = pool->npages - _manager->options.max_size;
/* free at least NUM_PAGES_TO_ALLOC number of pages
* to reduce calls to set_memory_wb */
if (npages < NUM_PAGES_TO_ALLOC)
npages = NUM_PAGES_TO_ALLOC;
}
spin_unlock_irqrestore(&pool->lock, irq_flags);
if (npages)
ttm_page_pool_free(pool, npages, false);
}
# max_size source (half the kernel zone, in pages):
zone->name, (unsigned long long)zone->max_mem >> 10);
}
ttm_page_alloc_init(glob, glob->zone_kernel->max_mem/(2*PAGE_SIZE));
ttm_dma_page_alloc_init(glob, glob->zone_kernel->max_mem/(2*PAGE_SIZE));
return 0;
=== [6] Signature landmine if the event ever gets wired naively ===
# vm_lowmem handlers would be invoked as func(arg, <int>); the registered
# ttm_pool_shrink_scan expects (struct shrinker *, struct shrink_control *)
# and dereferences sc at line 398 (sc->gfp_mask = M_WAITOK) and 418:
static unsigned long
ttm_pool_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)
{
static DEFINE_MUTEX(lock);
static unsigned start_pool;
unsigned i;
unsigned pool_offset;
struct ttm_page_pool *pool;
int shrink_pages = 100; /* XXXKIB */
unsigned long freed = 0;
unsigned int nr_free_pool;
#ifdef __DragonFly__
sc->gfp_mask = M_WAITOK;
#endif
if (!mutex_trylock(&lock))
return SHRINK_STOP;
pool_offset = ++start_pool % NUM_POOLS;
/* select start pool in round robin fashion */
for (i = 0; i < NUM_POOLS; ++i) {
unsigned nr_free = shrink_pages;
unsigned page_nr;
if (shrink_pages == 0)
break;
pool = &_manager->pools[(i + pool_offset)%NUM_POOLS];
page_nr = (1 << pool->order);
/* OK to use static buffer since global mutex is held. */
nr_free_pool = roundup(nr_free, page_nr) >> pool->order;
shrink_pages = ttm_page_pool_free(pool, nr_free_pool, true);
freed += (nr_free_pool - shrink_pages) << pool->order;
if (freed >= sc->nr_to_scan)
break;
shrink_pages <<= pool->order;
}
mutex_unlock(&lock);
return freed;
}