diff --git a/mm/execmem.c b/mm/execmem.c index ba277790e313..00dd6324cae0 100644 --- a/mm/execmem.c +++ b/mm/execmem.c @@ -149,7 +149,15 @@ static void execmem_cache_clean(struct work_struct *work) if (vm && get_vm_area_size(vm) == size && IS_ALIGNED(size, PMD_SIZE) && IS_ALIGNED(mas.index, PMD_SIZE)) { - mas_store_gfp(&mas, NULL, GFP_KERNEL); + /* + * Preallocate to ensure mas_store does not fail + * If there is no memory for the tree update, bail out, + * next execmem_free() might be more lucky + */ + if (mas_preallocate(&mas, NULL, GFP_KERNEL)) + break; + + mas_store_prealloc(&mas, NULL); vfree(area); } } @@ -219,30 +227,34 @@ static void *execmem_cache_alloc_locked(struct execmem_range *range, size_t size addr = mas_free.index; last = mas_free.last; + mas_set_range(&mas_free, addr, addr + size - 1); + if (mas_preallocate(&mas_free, NULL, GFP_KERNEL)) + return NULL; + /* insert allocated size to busy_areas at range [addr, addr + size) */ mas_set_range(&mas_busy, addr, addr + size - 1); err = mas_store_gfp(&mas_busy, (void *)addr, GFP_KERNEL); if (err) - return NULL; + goto err_destroy_mas_free; - mas_store_gfp(&mas_free, NULL, GFP_KERNEL); + mas_store_prealloc(&mas_free, NULL); if (area_size > size) { - void *ptr = (void *)(addr + size); - /* * re-insert remaining free size to free_areas at range * [addr + size, last] + * the range matches an existing entry, so this cannot allocate */ + ptr = (void *)(addr + size); mas_set_range(&mas_free, addr + size, last); - err = mas_store_gfp(&mas_free, ptr, GFP_KERNEL); - if (err) { - mas_store_gfp(&mas_busy, NULL, GFP_KERNEL); - return NULL; - } + mas_store_gfp(&mas_free, ptr, GFP_KERNEL); } ptr = (void *)addr; return ptr; + +err_destroy_mas_free: + mas_destroy(&mas_free); + return NULL; } static void *__execmem_cache_alloc(struct execmem_range *range, size_t size)