diff options
| author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2023-08-30 16:06:38 -0700 | 
|---|---|---|
| committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2023-08-30 16:06:38 -0700 | 
| commit | 1ac731c529cd4d6adbce134754b51ff7d822b145 (patch) | |
| tree | 143ab3f35ca5f3b69f583c84e6964b17139c2ec1 /kernel/dma/swiotlb.c | |
| parent | 07b4c950f27bef0362dc6ad7ee713aab61d58149 (diff) | |
| parent | 54116d442e001e1b6bd482122043b1870998a1f3 (diff) | |
Merge branch 'next' into for-linus
Prepare input updates for 6.6 merge window.
Diffstat (limited to 'kernel/dma/swiotlb.c')
| -rw-r--r-- | kernel/dma/swiotlb.c | 173 | 
1 files changed, 105 insertions, 68 deletions
| diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c index 03e3251cd9d2..af2e304c672c 100644 --- a/kernel/dma/swiotlb.c +++ b/kernel/dma/swiotlb.c @@ -73,8 +73,6 @@ static bool swiotlb_force_disable;  struct io_tlb_mem io_tlb_default_mem; -phys_addr_t swiotlb_unencrypted_base; -  static unsigned long default_nslabs = IO_TLB_DEFAULT_SIZE >> IO_TLB_SHIFT;  static unsigned long default_nareas; @@ -202,34 +200,6 @@ static inline unsigned long nr_slots(u64 val)  }  /* - * Remap swioltb memory in the unencrypted physical address space - * when swiotlb_unencrypted_base is set. (e.g. for Hyper-V AMD SEV-SNP - * Isolation VMs). - */ -#ifdef CONFIG_HAS_IOMEM -static void *swiotlb_mem_remap(struct io_tlb_mem *mem, unsigned long bytes) -{ -	void *vaddr = NULL; - -	if (swiotlb_unencrypted_base) { -		phys_addr_t paddr = mem->start + swiotlb_unencrypted_base; - -		vaddr = memremap(paddr, bytes, MEMREMAP_WB); -		if (!vaddr) -			pr_err("Failed to map the unencrypted memory %pa size %lx.\n", -			       &paddr, bytes); -	} - -	return vaddr; -} -#else -static void *swiotlb_mem_remap(struct io_tlb_mem *mem, unsigned long bytes) -{ -	return NULL; -} -#endif - -/*   * Early SWIOTLB allocation may be too early to allow an architecture to   * perform the desired operations.  This function allows the architecture to   * call SWIOTLB when the operations are possible.  It needs to be called @@ -238,18 +208,12 @@ static void *swiotlb_mem_remap(struct io_tlb_mem *mem, unsigned long bytes)  void __init swiotlb_update_mem_attributes(void)  {  	struct io_tlb_mem *mem = &io_tlb_default_mem; -	void *vaddr;  	unsigned long bytes;  	if (!mem->nslabs || mem->late_alloc)  		return; -	vaddr = phys_to_virt(mem->start);  	bytes = PAGE_ALIGN(mem->nslabs << IO_TLB_SHIFT); -	set_memory_decrypted((unsigned long)vaddr, bytes >> PAGE_SHIFT); - -	mem->vaddr = swiotlb_mem_remap(mem, bytes); -	if (!mem->vaddr) -		mem->vaddr = vaddr; +	set_memory_decrypted((unsigned long)mem->vaddr, bytes >> PAGE_SHIFT);  }  static void swiotlb_init_io_tlb_mem(struct io_tlb_mem *mem, phys_addr_t start, @@ -280,13 +244,6 @@ static void swiotlb_init_io_tlb_mem(struct io_tlb_mem *mem, phys_addr_t start,  		mem->slots[i].alloc_size = 0;  	} -	/* -	 * If swiotlb_unencrypted_base is set, the bounce buffer memory will -	 * be remapped and cleared in swiotlb_update_mem_attributes. -	 */ -	if (swiotlb_unencrypted_base) -		return; -  	memset(vaddr, 0, bytes);  	mem->vaddr = vaddr;  	return; @@ -609,6 +566,40 @@ static unsigned int wrap_area_index(struct io_tlb_mem *mem, unsigned int index)  }  /* + * Track the total used slots with a global atomic value in order to have + * correct information to determine the high water mark. The mem_used() + * function gives imprecise results because there's no locking across + * multiple areas. + */ +#ifdef CONFIG_DEBUG_FS +static void inc_used_and_hiwater(struct io_tlb_mem *mem, unsigned int nslots) +{ +	unsigned long old_hiwater, new_used; + +	new_used = atomic_long_add_return(nslots, &mem->total_used); +	old_hiwater = atomic_long_read(&mem->used_hiwater); +	do { +		if (new_used <= old_hiwater) +			break; +	} while (!atomic_long_try_cmpxchg(&mem->used_hiwater, +					  &old_hiwater, new_used)); +} + +static void dec_used(struct io_tlb_mem *mem, unsigned int nslots) +{ +	atomic_long_sub(nslots, &mem->total_used); +} + +#else /* !CONFIG_DEBUG_FS */ +static void inc_used_and_hiwater(struct io_tlb_mem *mem, unsigned int nslots) +{ +} +static void dec_used(struct io_tlb_mem *mem, unsigned int nslots) +{ +} +#endif /* CONFIG_DEBUG_FS */ + +/*   * Find a suitable number of IO TLB entries size that will fit this request and   * allocate a buffer from that IO TLB pool.   */ @@ -623,10 +614,10 @@ static int swiotlb_do_find_slots(struct device *dev, int area_index,  		phys_to_dma_unencrypted(dev, mem->start) & boundary_mask;  	unsigned long max_slots = get_max_slots(boundary_mask);  	unsigned int iotlb_align_mask = -		dma_get_min_align_mask(dev) & ~(IO_TLB_SIZE - 1); +		dma_get_min_align_mask(dev) | alloc_align_mask;  	unsigned int nslots = nr_slots(alloc_size), stride; -	unsigned int index, wrap, count = 0, i;  	unsigned int offset = swiotlb_align_offset(dev, orig_addr); +	unsigned int index, slots_checked, count = 0, i;  	unsigned long flags;  	unsigned int slot_base;  	unsigned int slot_index; @@ -635,29 +626,34 @@ static int swiotlb_do_find_slots(struct device *dev, int area_index,  	BUG_ON(area_index >= mem->nareas);  	/* +	 * For allocations of PAGE_SIZE or larger only look for page aligned +	 * allocations. +	 */ +	if (alloc_size >= PAGE_SIZE) +		iotlb_align_mask |= ~PAGE_MASK; +	iotlb_align_mask &= ~(IO_TLB_SIZE - 1); + +	/*  	 * For mappings with an alignment requirement don't bother looping to -	 * unaligned slots once we found an aligned one.  For allocations of -	 * PAGE_SIZE or larger only look for page aligned allocations. +	 * unaligned slots once we found an aligned one.  	 */  	stride = (iotlb_align_mask >> IO_TLB_SHIFT) + 1; -	if (alloc_size >= PAGE_SIZE) -		stride = max(stride, stride << (PAGE_SHIFT - IO_TLB_SHIFT)); -	stride = max(stride, (alloc_align_mask >> IO_TLB_SHIFT) + 1);  	spin_lock_irqsave(&area->lock, flags);  	if (unlikely(nslots > mem->area_nslabs - area->used))  		goto not_found;  	slot_base = area_index * mem->area_nslabs; -	index = wrap = wrap_area_index(mem, ALIGN(area->index, stride)); +	index = area->index; -	do { +	for (slots_checked = 0; slots_checked < mem->area_nslabs; ) {  		slot_index = slot_base + index;  		if (orig_addr &&  		    (slot_addr(tbl_dma_addr, slot_index) &  		     iotlb_align_mask) != (orig_addr & iotlb_align_mask)) {  			index = wrap_area_index(mem, index + 1); +			slots_checked++;  			continue;  		} @@ -673,7 +669,8 @@ static int swiotlb_do_find_slots(struct device *dev, int area_index,  				goto found;  		}  		index = wrap_area_index(mem, index + stride); -	} while (index != wrap); +		slots_checked += stride; +	}  not_found:  	spin_unlock_irqrestore(&area->lock, flags); @@ -693,12 +690,11 @@ found:  	/*  	 * Update the indices to avoid searching in the next round.  	 */ -	if (index + nslots < mem->area_nslabs) -		area->index = index + nslots; -	else -		area->index = 0; +	area->index = wrap_area_index(mem, index + nslots);  	area->used += nslots;  	spin_unlock_irqrestore(&area->lock, flags); + +	inc_used_and_hiwater(mem, nslots);  	return slot_index;  } @@ -831,6 +827,8 @@ static void swiotlb_release_slots(struct device *dev, phys_addr_t tlb_addr)  		mem->slots[i].list = ++count;  	area->used -= nslots;  	spin_unlock_irqrestore(&area->lock, flags); + +	dec_used(mem, nslots);  }  /* @@ -925,34 +923,73 @@ bool is_swiotlb_active(struct device *dev)  }  EXPORT_SYMBOL_GPL(is_swiotlb_active); +#ifdef CONFIG_DEBUG_FS +  static int io_tlb_used_get(void *data, u64 *val)  { -	*val = mem_used(&io_tlb_default_mem); +	struct io_tlb_mem *mem = data; + +	*val = mem_used(mem); +	return 0; +} + +static int io_tlb_hiwater_get(void *data, u64 *val) +{ +	struct io_tlb_mem *mem = data; + +	*val = atomic_long_read(&mem->used_hiwater); +	return 0; +} + +static int io_tlb_hiwater_set(void *data, u64 val) +{ +	struct io_tlb_mem *mem = data; + +	/* Only allow setting to zero */ +	if (val != 0) +		return -EINVAL; + +	atomic_long_set(&mem->used_hiwater, val);  	return 0;  } +  DEFINE_DEBUGFS_ATTRIBUTE(fops_io_tlb_used, io_tlb_used_get, NULL, "%llu\n"); +DEFINE_DEBUGFS_ATTRIBUTE(fops_io_tlb_hiwater, io_tlb_hiwater_get, +				io_tlb_hiwater_set, "%llu\n");  static void swiotlb_create_debugfs_files(struct io_tlb_mem *mem,  					 const char *dirname)  { +	atomic_long_set(&mem->total_used, 0); +	atomic_long_set(&mem->used_hiwater, 0); +  	mem->debugfs = debugfs_create_dir(dirname, io_tlb_default_mem.debugfs);  	if (!mem->nslabs)  		return;  	debugfs_create_ulong("io_tlb_nslabs", 0400, mem->debugfs, &mem->nslabs); -	debugfs_create_file("io_tlb_used", 0400, mem->debugfs, NULL, +	debugfs_create_file("io_tlb_used", 0400, mem->debugfs, mem,  			&fops_io_tlb_used); +	debugfs_create_file("io_tlb_used_hiwater", 0600, mem->debugfs, mem, +			&fops_io_tlb_hiwater);  } -static int __init __maybe_unused swiotlb_create_default_debugfs(void) +static int __init swiotlb_create_default_debugfs(void)  {  	swiotlb_create_debugfs_files(&io_tlb_default_mem, "swiotlb");  	return 0;  } -#ifdef CONFIG_DEBUG_FS  late_initcall(swiotlb_create_default_debugfs); -#endif + +#else  /* !CONFIG_DEBUG_FS */ + +static inline void swiotlb_create_debugfs_files(struct io_tlb_mem *mem, +						const char *dirname) +{ +} + +#endif	/* CONFIG_DEBUG_FS */  #ifdef CONFIG_DMA_RESTRICTED_POOL @@ -995,6 +1032,11 @@ static int rmem_swiotlb_device_init(struct reserved_mem *rmem,  	/* Set Per-device io tlb area to one */  	unsigned int nareas = 1; +	if (PageHighMem(pfn_to_page(PHYS_PFN(rmem->base)))) { +		dev_err(dev, "Restricted DMA pool must be accessible within the linear mapping."); +		return -EINVAL; +	} +  	/*  	 * Since multiple devices can share the same pool, the private data,  	 * io_tlb_mem struct, will be initialized by the first device attached @@ -1056,11 +1098,6 @@ static int __init rmem_swiotlb_setup(struct reserved_mem *rmem)  	    of_get_flat_dt_prop(node, "no-map", NULL))  		return -EINVAL; -	if (PageHighMem(pfn_to_page(PHYS_PFN(rmem->base)))) { -		pr_err("Restricted DMA pool must be accessible within the linear mapping."); -		return -EINVAL; -	} -  	rmem->ops = &rmem_swiotlb_ops;  	pr_info("Reserved memory: created restricted DMA pool at %pa, size %ld MiB\n",  		&rmem->base, (unsigned long)rmem->size / SZ_1M); | 
