diff options
Diffstat (limited to 'mm/kmsan/hooks.c')
| -rw-r--r-- | mm/kmsan/hooks.c | 55 | 
1 files changed, 47 insertions, 8 deletions
diff --git a/mm/kmsan/hooks.c b/mm/kmsan/hooks.c index 3807502766a3..ec0da72e65aa 100644 --- a/mm/kmsan/hooks.c +++ b/mm/kmsan/hooks.c @@ -148,35 +148,74 @@ void kmsan_vunmap_range_noflush(unsigned long start, unsigned long end)   * into the virtual memory. If those physical pages already had shadow/origin,   * those are ignored.   */ -void kmsan_ioremap_page_range(unsigned long start, unsigned long end, -			      phys_addr_t phys_addr, pgprot_t prot, -			      unsigned int page_shift) +int kmsan_ioremap_page_range(unsigned long start, unsigned long end, +			     phys_addr_t phys_addr, pgprot_t prot, +			     unsigned int page_shift)  {  	gfp_t gfp_mask = GFP_KERNEL | __GFP_ZERO;  	struct page *shadow, *origin;  	unsigned long off = 0; -	int nr; +	int nr, err = 0, clean = 0, mapped;  	if (!kmsan_enabled || kmsan_in_runtime()) -		return; +		return 0;  	nr = (end - start) / PAGE_SIZE;  	kmsan_enter_runtime(); -	for (int i = 0; i < nr; i++, off += PAGE_SIZE) { +	for (int i = 0; i < nr; i++, off += PAGE_SIZE, clean = i) {  		shadow = alloc_pages(gfp_mask, 1);  		origin = alloc_pages(gfp_mask, 1); -		__vmap_pages_range_noflush( +		if (!shadow || !origin) { +			err = -ENOMEM; +			goto ret; +		} +		mapped = __vmap_pages_range_noflush(  			vmalloc_shadow(start + off),  			vmalloc_shadow(start + off + PAGE_SIZE), prot, &shadow,  			PAGE_SHIFT); -		__vmap_pages_range_noflush( +		if (mapped) { +			err = mapped; +			goto ret; +		} +		shadow = NULL; +		mapped = __vmap_pages_range_noflush(  			vmalloc_origin(start + off),  			vmalloc_origin(start + off + PAGE_SIZE), prot, &origin,  			PAGE_SHIFT); +		if (mapped) { +			__vunmap_range_noflush( +				vmalloc_shadow(start + off), +				vmalloc_shadow(start + off + PAGE_SIZE)); +			err = mapped; +			goto ret; +		} +		origin = NULL; +	} +	/* Page mapping loop finished normally, nothing to clean up. */ +	clean = 0; + +ret: +	if (clean > 0) { +		/* +		 * Something went wrong. Clean up shadow/origin pages allocated +		 * on the last loop iteration, then delete mappings created +		 * during the previous iterations. +		 */ +		if (shadow) +			__free_pages(shadow, 1); +		if (origin) +			__free_pages(origin, 1); +		__vunmap_range_noflush( +			vmalloc_shadow(start), +			vmalloc_shadow(start + clean * PAGE_SIZE)); +		__vunmap_range_noflush( +			vmalloc_origin(start), +			vmalloc_origin(start + clean * PAGE_SIZE));  	}  	flush_cache_vmap(vmalloc_shadow(start), vmalloc_shadow(end));  	flush_cache_vmap(vmalloc_origin(start), vmalloc_origin(end));  	kmsan_leave_runtime(); +	return err;  }  void kmsan_iounmap_page_range(unsigned long start, unsigned long end)  |