diff options
Diffstat (limited to 'kernel/dma/swiotlb.c')
| -rw-r--r-- | kernel/dma/swiotlb.c | 68 | 
1 files changed, 37 insertions, 31 deletions
| diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c index fe1ccb53596f..df68d29740a0 100644 --- a/kernel/dma/swiotlb.c +++ b/kernel/dma/swiotlb.c @@ -763,16 +763,18 @@ static void swiotlb_dyn_free(struct rcu_head *rcu)  }  /** - * swiotlb_find_pool() - find the IO TLB pool for a physical address + * __swiotlb_find_pool() - find the IO TLB pool for a physical address   * @dev:        Device which has mapped the DMA buffer.   * @paddr:      Physical address within the DMA buffer.   *   * Find the IO TLB memory pool descriptor which contains the given physical - * address, if any. + * address, if any. This function is for use only when the dev is known to + * be using swiotlb. Use swiotlb_find_pool() for the more general case + * when this condition is not met.   *   * Return: Memory pool which contains @paddr, or %NULL if none.   */ -struct io_tlb_pool *swiotlb_find_pool(struct device *dev, phys_addr_t paddr) +struct io_tlb_pool *__swiotlb_find_pool(struct device *dev, phys_addr_t paddr)  {  	struct io_tlb_mem *mem = dev->dma_io_tlb_mem;  	struct io_tlb_pool *pool; @@ -855,9 +857,8 @@ static unsigned int swiotlb_align_offset(struct device *dev,   * Bounce: copy the swiotlb buffer from or back to the original dma location   */  static void swiotlb_bounce(struct device *dev, phys_addr_t tlb_addr, size_t size, -			   enum dma_data_direction dir) +			   enum dma_data_direction dir, struct io_tlb_pool *mem)  { -	struct io_tlb_pool *mem = swiotlb_find_pool(dev, tlb_addr);  	int index = (tlb_addr - mem->start) >> IO_TLB_SHIFT;  	phys_addr_t orig_addr = mem->slots[index].orig_addr;  	size_t alloc_size = mem->slots[index].alloc_size; @@ -1243,7 +1244,7 @@ found:  	 * that was made by swiotlb_dyn_alloc() on a third CPU (cf. multicopy  	 * atomicity).  	 * -	 * See also the comment in is_swiotlb_buffer(). +	 * See also the comment in swiotlb_find_pool().  	 */  	smp_mb(); @@ -1435,13 +1436,13 @@ phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr,  	 * hardware behavior.  Use of swiotlb is supposed to be transparent,  	 * i.e. swiotlb must not corrupt memory by clobbering unwritten bytes.  	 */ -	swiotlb_bounce(dev, tlb_addr, mapping_size, DMA_TO_DEVICE); +	swiotlb_bounce(dev, tlb_addr, mapping_size, DMA_TO_DEVICE, pool);  	return tlb_addr;  } -static void swiotlb_release_slots(struct device *dev, phys_addr_t tlb_addr) +static void swiotlb_release_slots(struct device *dev, phys_addr_t tlb_addr, +				  struct io_tlb_pool *mem)  { -	struct io_tlb_pool *mem = swiotlb_find_pool(dev, tlb_addr);  	unsigned long flags;  	unsigned int offset = swiotlb_align_offset(dev, 0, tlb_addr);  	int index, nslots, aindex; @@ -1499,17 +1500,16 @@ static void swiotlb_release_slots(struct device *dev, phys_addr_t tlb_addr)   * swiotlb_del_transient() - delete a transient memory pool   * @dev:	Device which mapped the buffer.   * @tlb_addr:	Physical address within a bounce buffer. + * @pool:       Pointer to the transient memory pool to be checked and deleted.   *   * Check whether the address belongs to a transient SWIOTLB memory pool.   * If yes, then delete the pool.   *   * Return: %true if @tlb_addr belonged to a transient pool that was released.   */ -static bool swiotlb_del_transient(struct device *dev, phys_addr_t tlb_addr) +static bool swiotlb_del_transient(struct device *dev, phys_addr_t tlb_addr, +		struct io_tlb_pool *pool)  { -	struct io_tlb_pool *pool; - -	pool = swiotlb_find_pool(dev, tlb_addr);  	if (!pool->transient)  		return false; @@ -1522,7 +1522,7 @@ static bool swiotlb_del_transient(struct device *dev, phys_addr_t tlb_addr)  #else  /* !CONFIG_SWIOTLB_DYNAMIC */  static inline bool swiotlb_del_transient(struct device *dev, -					 phys_addr_t tlb_addr) +		phys_addr_t tlb_addr, struct io_tlb_pool *pool)  {  	return false;  } @@ -1532,36 +1532,39 @@ static inline bool swiotlb_del_transient(struct device *dev,  /*   * tlb_addr is the physical address of the bounce buffer to unmap.   */ -void swiotlb_tbl_unmap_single(struct device *dev, phys_addr_t tlb_addr, -			      size_t mapping_size, enum dma_data_direction dir, -			      unsigned long attrs) +void __swiotlb_tbl_unmap_single(struct device *dev, phys_addr_t tlb_addr, +		size_t mapping_size, enum dma_data_direction dir, +		unsigned long attrs, struct io_tlb_pool *pool)  {  	/*  	 * First, sync the memory before unmapping the entry  	 */  	if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&  	    (dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL)) -		swiotlb_bounce(dev, tlb_addr, mapping_size, DMA_FROM_DEVICE); +		swiotlb_bounce(dev, tlb_addr, mapping_size, +						DMA_FROM_DEVICE, pool); -	if (swiotlb_del_transient(dev, tlb_addr)) +	if (swiotlb_del_transient(dev, tlb_addr, pool))  		return; -	swiotlb_release_slots(dev, tlb_addr); +	swiotlb_release_slots(dev, tlb_addr, pool);  } -void swiotlb_sync_single_for_device(struct device *dev, phys_addr_t tlb_addr, -		size_t size, enum dma_data_direction dir) +void __swiotlb_sync_single_for_device(struct device *dev, phys_addr_t tlb_addr, +		size_t size, enum dma_data_direction dir, +		struct io_tlb_pool *pool)  {  	if (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL) -		swiotlb_bounce(dev, tlb_addr, size, DMA_TO_DEVICE); +		swiotlb_bounce(dev, tlb_addr, size, DMA_TO_DEVICE, pool);  	else  		BUG_ON(dir != DMA_FROM_DEVICE);  } -void swiotlb_sync_single_for_cpu(struct device *dev, phys_addr_t tlb_addr, -		size_t size, enum dma_data_direction dir) +void __swiotlb_sync_single_for_cpu(struct device *dev, phys_addr_t tlb_addr, +		size_t size, enum dma_data_direction dir, +		struct io_tlb_pool *pool)  {  	if (dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL) -		swiotlb_bounce(dev, tlb_addr, size, DMA_FROM_DEVICE); +		swiotlb_bounce(dev, tlb_addr, size, DMA_FROM_DEVICE, pool);  	else  		BUG_ON(dir != DMA_TO_DEVICE);  } @@ -1585,8 +1588,9 @@ dma_addr_t swiotlb_map(struct device *dev, phys_addr_t paddr, size_t size,  	/* Ensure that the address returned is DMA'ble */  	dma_addr = phys_to_dma_unencrypted(dev, swiotlb_addr);  	if (unlikely(!dma_capable(dev, dma_addr, size, true))) { -		swiotlb_tbl_unmap_single(dev, swiotlb_addr, size, dir, -			attrs | DMA_ATTR_SKIP_CPU_SYNC); +		__swiotlb_tbl_unmap_single(dev, swiotlb_addr, size, dir, +			attrs | DMA_ATTR_SKIP_CPU_SYNC, +			swiotlb_find_pool(dev, swiotlb_addr));  		dev_WARN_ONCE(dev, 1,  			"swiotlb addr %pad+%zu overflow (mask %llx, bus limit %llx).\n",  			&dma_addr, size, *dev->dma_mask, dev->bus_dma_limit); @@ -1764,7 +1768,7 @@ struct page *swiotlb_alloc(struct device *dev, size_t size)  	if (unlikely(!PAGE_ALIGNED(tlb_addr))) {  		dev_WARN_ONCE(dev, 1, "Cannot allocate pages from non page-aligned swiotlb addr 0x%pa.\n",  			      &tlb_addr); -		swiotlb_release_slots(dev, tlb_addr); +		swiotlb_release_slots(dev, tlb_addr, pool);  		return NULL;  	} @@ -1774,11 +1778,13 @@ struct page *swiotlb_alloc(struct device *dev, size_t size)  bool swiotlb_free(struct device *dev, struct page *page, size_t size)  {  	phys_addr_t tlb_addr = page_to_phys(page); +	struct io_tlb_pool *pool; -	if (!is_swiotlb_buffer(dev, tlb_addr)) +	pool = swiotlb_find_pool(dev, tlb_addr); +	if (!pool)  		return false; -	swiotlb_release_slots(dev, tlb_addr); +	swiotlb_release_slots(dev, tlb_addr, pool);  	return true;  } | 
