diff --git a/include/linux/execmem.h b/include/linux/execmem.h
index 7436aa547818..a159a073270a 100644
--- a/include/linux/execmem.h
+++ b/include/linux/execmem.h
@@ -127,6 +127,17 @@ void *execmem_alloc(enum execmem_type type, size_t size);
  */
 void execmem_free(void *ptr);
 
+/**
+ * execmem_vmap - create virtual mapping for executable memory
+ * @type: type of the allocation
+ * @size: size of the virtual mapping in bytes
+ *
+ * Maps virtually contiguous area that can be populated with executable code.
+ *
+ * Return: the area descriptor on success or %NULL on failure.
+ */
+struct vm_struct *execmem_vmap(enum execmem_type type, size_t size);
+
 /**
  * execmem_update_copy - copy an update to executable memory
  * @dst:  destination address to update
diff --git a/include/linux/vmalloc.h b/include/linux/vmalloc.h
index 9a012cd4fad2..9d64cc6f24d1 100644
--- a/include/linux/vmalloc.h
+++ b/include/linux/vmalloc.h
@@ -202,6 +202,9 @@ extern int remap_vmalloc_range_partial(struct vm_area_struct *vma,
 extern int remap_vmalloc_range(struct vm_area_struct *vma, void *addr,
 							unsigned long pgoff);
 
+int vmap_pages_range(unsigned long addr, unsigned long end,
+		pgprot_t prot, struct page **pages, unsigned int page_shift);
+
 /*
  * Architectures can set this mask to a combination of PGTBL_P?D_MODIFIED values
  * and let generic vmalloc and ioremap code know when arch_sync_kernel_mappings()
@@ -239,6 +242,12 @@ extern struct vm_struct *__get_vm_area_caller(unsigned long size,
 					unsigned long flags,
 					unsigned long start, unsigned long end,
 					const void *caller);
+struct vm_struct *__get_vm_area_node(unsigned long size,
+				     unsigned long align, unsigned long shift,
+				     unsigned long flags, unsigned long start,
+				     unsigned long end, int node, gfp_t gfp_mask,
+				     const void *caller);
+
 void free_vm_area(struct vm_struct *area);
 extern struct vm_struct *remove_vm_area(const void *addr);
 extern struct vm_struct *find_vm_area(const void *addr);
diff --git a/lib/alloc_tag.c b/lib/alloc_tag.c
index b10e7f17eeda..648f32d52b8d 100644
--- a/lib/alloc_tag.c
+++ b/lib/alloc_tag.c
@@ -8,6 +8,7 @@
 #include <linux/proc_fs.h>
 #include <linux/seq_buf.h>
 #include <linux/seq_file.h>
+#include <linux/vmalloc.h>
 
 static struct codetag_type *alloc_tag_cttype;
 
@@ -153,6 +154,7 @@ static void __init procfs_init(void)
 #ifdef CONFIG_MODULES
 
 static struct maple_tree mod_area_mt = MTREE_INIT(mod_area_mt, MT_FLAGS_ALLOC_RANGE);
+static struct vm_struct *vm_module_tags;
 /* A dummy object used to indicate an unloaded module */
 static struct module unloaded_mod;
 /* A dummy object used to indicate a module prepended area */
@@ -195,6 +197,25 @@ static void clean_unused_module_areas_locked(void)
 	}
 }
 
+static int vm_module_tags_grow(unsigned long addr, unsigned long bytes)
+{
+	struct page **next_page = vm_module_tags->pages + vm_module_tags->nr_pages;
+	unsigned long more_pages = ALIGN(bytes, PAGE_SIZE) >> PAGE_SHIFT;
+	unsigned long nr;
+
+	nr = alloc_pages_bulk_array_node(GFP_KERNEL | __GFP_NOWARN,
+					 NUMA_NO_NODE, more_pages, next_page);
+	if (nr != more_pages)
+		return -ENOMEM;
+
+	vm_module_tags->nr_pages += nr;
+	if (vmap_pages_range(addr, addr + (nr << PAGE_SHIFT),
+			     PAGE_KERNEL, next_page, PAGE_SHIFT) < 0)
+		return -ENOMEM;
+
+	return 0;
+}
+
 static void *reserve_module_tags(struct module *mod, unsigned long size,
 				 unsigned int prepend, unsigned long align)
 {
@@ -202,7 +223,7 @@ static void *reserve_module_tags(struct module *mod, unsigned long size,
 	MA_STATE(mas, &mod_area_mt, 0, section_size - 1);
 	bool cleanup_done = false;
 	unsigned long offset;
-	void *ret;
+	void *ret = NULL;
 
 	/* If no tags return NULL */
 	if (size < sizeof(struct alloc_tag))
@@ -239,7 +260,7 @@ static void *reserve_module_tags(struct module *mod, unsigned long size,
 		goto repeat;
 	} else {
 		ret = ERR_PTR(-ENOMEM);
-		goto out;
+		goto unlock;
 	}
 
 found:
@@ -254,7 +275,7 @@ static void *reserve_module_tags(struct module *mod, unsigned long size,
 		mas_store(&mas, &prepend_mod);
 		if (mas_is_err(&mas)) {
 			ret = ERR_PTR(xa_err(mas.node));
-			goto out;
+			goto unlock;
 		}
 		mas.index = offset;
 		mas.last = offset + size - 1;
@@ -263,7 +284,7 @@ static void *reserve_module_tags(struct module *mod, unsigned long size,
 			ret = ERR_PTR(xa_err(mas.node));
 			mas.index = pad_start;
 			mas_erase(&mas);
-			goto out;
+			goto unlock;
 		}
 
 	} else {
@@ -271,18 +292,33 @@ static void *reserve_module_tags(struct module *mod, unsigned long size,
 		mas_store(&mas, mod);
 		if (mas_is_err(&mas)) {
 			ret = ERR_PTR(xa_err(mas.node));
-			goto out;
+			goto unlock;
 		}
 	}
+unlock:
+	mas_unlock(&mas);
+	if (IS_ERR(ret))
+		return ret;
 
-	if (module_tags.size < offset + size)
-		module_tags.size = offset + size;
+	if (module_tags.size < offset + size) {
+		unsigned long phys_size = vm_module_tags->nr_pages << PAGE_SHIFT;
 
-	ret = (struct alloc_tag *)(module_tags.start_addr + offset);
-out:
-	mas_unlock(&mas);
+		module_tags.size = offset + size;
+		if (phys_size < module_tags.size) {
+			int grow_res;
+
+			grow_res = vm_module_tags_grow(module_tags.start_addr + phys_size,
+						       module_tags.size - phys_size);
+			if (grow_res) {
+				static_branch_disable(&mem_alloc_profiling_key);
+				pr_warn("Failed to allocate tags memory for module %s. Memory profiling is disabled!\n",
+					mod->name);
+				return ERR_PTR(grow_res);
+			}
+		}
+	}
 
-	return ret;
+	return (struct alloc_tag *)(module_tags.start_addr + offset);
 }
 
 static void release_module_tags(struct module *mod, bool unused)
@@ -351,12 +387,23 @@ static void replace_module(struct module *mod, struct module *new_mod)
 
 static int __init alloc_mod_tags_mem(void)
 {
-	/* Allocate space to copy allocation tags */
-	module_tags.start_addr = (unsigned long)execmem_alloc(EXECMEM_MODULE_DATA,
-							      MODULE_ALLOC_TAG_VMAP_SIZE);
-	if (!module_tags.start_addr)
+	/* Map space to copy allocation tags */
+	vm_module_tags = execmem_vmap(EXECMEM_MODULE_DATA, MODULE_ALLOC_TAG_VMAP_SIZE);
+	if (!vm_module_tags) {
+		pr_err("Failed to map %lu bytes for module allocation tags\n",
+			MODULE_ALLOC_TAG_VMAP_SIZE);
+		module_tags.start_addr = 0;
 		return -ENOMEM;
+	}
 
+	vm_module_tags->pages = kmalloc_array(get_vm_area_size(vm_module_tags) >> PAGE_SHIFT,
+					sizeof(struct page *), GFP_KERNEL | __GFP_ZERO);
+	if (!vm_module_tags->pages) {
+		free_vm_area(vm_module_tags);
+		return -ENOMEM;
+	}
+
+	module_tags.start_addr = (unsigned long)vm_module_tags->addr;
 	module_tags.end_addr = module_tags.start_addr + MODULE_ALLOC_TAG_VMAP_SIZE;
 
 	return 0;
@@ -364,8 +411,13 @@ static int __init alloc_mod_tags_mem(void)
 
 static void __init free_mod_tags_mem(void)
 {
-	execmem_free((void *)module_tags.start_addr);
+	int i;
+
 	module_tags.start_addr = 0;
+	for (i = 0; i < vm_module_tags->nr_pages; i++)
+		__free_page(vm_module_tags->pages[i]);
+	kfree(vm_module_tags->pages);
+	free_vm_area(vm_module_tags);
 }
 
 #else /* CONFIG_MODULES */
diff --git a/mm/execmem.c b/mm/execmem.c
index 97706d8ed720..eb346f4eaaff 100644
--- a/mm/execmem.c
+++ b/mm/execmem.c
@@ -366,6 +366,22 @@ void execmem_free(void *ptr)
 		vfree(ptr);
 }
 
+struct vm_struct *execmem_vmap(enum execmem_type type, size_t size)
+{
+	struct execmem_range *range = &execmem_info->ranges[type];
+	struct vm_struct *area;
+
+	area = __get_vm_area_node(size, range->alignment, PAGE_SHIFT, VM_ALLOC,
+				  range->start, range->end, NUMA_NO_NODE,
+				  GFP_KERNEL, __builtin_return_address(0));
+	if (!area && range->fallback_start)
+		area = __get_vm_area_node(size, range->alignment, PAGE_SHIFT, VM_ALLOC,
+					  range->fallback_start, range->fallback_end,
+					  NUMA_NO_NODE, GFP_KERNEL, __builtin_return_address(0));
+
+	return area;
+}
+
 void *execmem_update_copy(void *dst, const void *src, size_t size)
 {
 	return text_poke_copy(dst, src, size);
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index 74c0a5eae210..7ed39d104201 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -653,7 +653,7 @@ int vmap_pages_range_noflush(unsigned long addr, unsigned long end,
  * RETURNS:
  * 0 on success, -errno on failure.
  */
-static int vmap_pages_range(unsigned long addr, unsigned long end,
+int vmap_pages_range(unsigned long addr, unsigned long end,
 		pgprot_t prot, struct page **pages, unsigned int page_shift)
 {
 	int err;
@@ -3106,7 +3106,7 @@ static void clear_vm_uninitialized_flag(struct vm_struct *vm)
 	vm->flags &= ~VM_UNINITIALIZED;
 }
 
-static struct vm_struct *__get_vm_area_node(unsigned long size,
+struct vm_struct *__get_vm_area_node(unsigned long size,
 		unsigned long align, unsigned long shift, unsigned long flags,
 		unsigned long start, unsigned long end, int node,
 		gfp_t gfp_mask, const void *caller)
