Commit 26a7e094 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Konrad Rzeszutek Wilk
Browse files

swiotlb: refactor swiotlb_tbl_map_single



Split out a bunch of a self-contained helpers to make the function easier
to follow.

Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Acked-by: default avatarJianxiong Gao <jxgao@google.com>
Tested-by: default avatarJianxiong Gao <jxgao@google.com>
Signed-off-by: default avatarKonrad Rzeszutek Wilk <konrad.wilk@oracle.com>
parent ca10d0f8
Loading
Loading
Loading
Loading
+89 −90
Original line number Diff line number Diff line
@@ -468,134 +468,133 @@ static void swiotlb_bounce(phys_addr_t orig_addr, phys_addr_t tlb_addr,
	}
}

phys_addr_t swiotlb_tbl_map_single(struct device *hwdev, phys_addr_t orig_addr,
		size_t mapping_size, size_t alloc_size,
		enum dma_data_direction dir, unsigned long attrs)
{
	dma_addr_t tbl_dma_addr = phys_to_dma_unencrypted(hwdev, io_tlb_start);
	unsigned long flags;
	phys_addr_t tlb_addr;
	unsigned int nslots, stride, index, wrap;
	int i;
	unsigned long mask;
	unsigned long offset_slots;
	unsigned long max_slots;
	unsigned long tmp_io_tlb_used;

	if (no_iotlb_memory)
		panic("Can not allocate SWIOTLB buffer earlier and can't now provide you with the DMA bounce buffer");

	if (mem_encrypt_active())
		pr_warn_once("Memory encryption is active and system is using DMA bounce buffers\n");
#define slot_addr(start, idx)	((start) + ((idx) << IO_TLB_SHIFT))

	if (mapping_size > alloc_size) {
		dev_warn_once(hwdev, "Invalid sizes (mapping: %zd bytes, alloc: %zd bytes)",
			      mapping_size, alloc_size);
		return (phys_addr_t)DMA_MAPPING_ERROR;
/*
 * Carefully handle integer overflow which can occur when boundary_mask == ~0UL.
 */
static inline unsigned long get_max_slots(unsigned long boundary_mask)
{
	if (boundary_mask == ~0UL)
		return 1UL << (BITS_PER_LONG - IO_TLB_SHIFT);
	return nr_slots(boundary_mask + 1);
}

	mask = dma_get_seg_boundary(hwdev);

	tbl_dma_addr &= mask;

	offset_slots = nr_slots(tbl_dma_addr);
static unsigned int wrap_index(unsigned int index)
{
	if (index >= io_tlb_nslabs)
		return 0;
	return index;
}

/*
	 * Carefully handle integer overflow which can occur when mask == ~0UL.
 * Find a suitable number of IO TLB entries size that will fit this request and
 * allocate a buffer from that IO TLB pool.
 */
	max_slots = mask + 1
		    ? nr_slots(mask + 1)
		    : 1UL << (BITS_PER_LONG - IO_TLB_SHIFT);
static int find_slots(struct device *dev, size_t alloc_size)
{
	unsigned long boundary_mask = dma_get_seg_boundary(dev);
	dma_addr_t tbl_dma_addr =
		phys_to_dma_unencrypted(dev, io_tlb_start) & boundary_mask;
	unsigned long max_slots = get_max_slots(boundary_mask);
	unsigned int nslots = nr_slots(alloc_size), stride = 1;
	unsigned int index, wrap, count = 0, i;
	unsigned long flags;

	BUG_ON(!nslots);

	/*
	 * For mappings greater than or equal to a page, we limit the stride
	 * (and hence alignment) to a page size.
	 */
	nslots = nr_slots(alloc_size);
	if (alloc_size >= PAGE_SIZE)
		stride = (1 << (PAGE_SHIFT - IO_TLB_SHIFT));
	else
		stride = 1;

	BUG_ON(!nslots);
		stride <<= (PAGE_SHIFT - IO_TLB_SHIFT);

	/*
	 * Find suitable number of IO TLB entries size that will fit this
	 * request and allocate a buffer from that IO TLB pool.
	 */
	spin_lock_irqsave(&io_tlb_lock, flags);

	if (unlikely(nslots > io_tlb_nslabs - io_tlb_used))
		goto not_found;

	index = ALIGN(io_tlb_index, stride);
	if (index >= io_tlb_nslabs)
		index = 0;
	wrap = index;

	index = wrap = wrap_index(ALIGN(io_tlb_index, stride));
	do {
		while (iommu_is_span_boundary(index, nslots, offset_slots,
					      max_slots)) {
			index += stride;
			if (index >= io_tlb_nslabs)
				index = 0;
			if (index == wrap)
				goto not_found;
		}

		/*
		 * If we find a slot that indicates we have 'nslots' number of
		 * contiguous buffers, we allocate the buffers from that slot
		 * and mark the entries as '0' indicating unavailable.
		 */
		if (io_tlb_list[index] >= nslots) {
			int count = 0;
		if (!iommu_is_span_boundary(index, nslots,
					    nr_slots(tbl_dma_addr),
					    max_slots)) {
			if (io_tlb_list[index] >= nslots)
				goto found;
		}
		index = wrap_index(index + stride);
	} while (index != wrap);

			for (i = index; i < (int) (index + nslots); i++)
not_found:
	spin_unlock_irqrestore(&io_tlb_lock, flags);
	return -1;

found:
	for (i = index; i < index + nslots; i++)
		io_tlb_list[i] = 0;
	for (i = index - 1;
	     io_tlb_offset(i) != IO_TLB_SEGSIZE - 1 &&
	     io_tlb_list[i]; i--)
		io_tlb_list[i] = ++count;
			tlb_addr = io_tlb_start + (index << IO_TLB_SHIFT);

	/*
			 * Update the indices to avoid searching in the next
			 * round.
	 * Update the indices to avoid searching in the next round.
	 */
			io_tlb_index = ((index + nslots) < io_tlb_nslabs
					? (index + nslots) : 0);
	if (index + nslots < io_tlb_nslabs)
		io_tlb_index = index + nslots;
	else
		io_tlb_index = 0;
	io_tlb_used += nslots;

			goto found;
	spin_unlock_irqrestore(&io_tlb_lock, flags);
	return index;
}
		index += stride;
		if (index >= io_tlb_nslabs)
			index = 0;
	} while (index != wrap);

not_found:
	tmp_io_tlb_used = io_tlb_used;
phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr,
		size_t mapping_size, size_t alloc_size,
		enum dma_data_direction dir, unsigned long attrs)
{
	unsigned int index, i;
	phys_addr_t tlb_addr;

	spin_unlock_irqrestore(&io_tlb_lock, flags);
	if (!(attrs & DMA_ATTR_NO_WARN) && printk_ratelimit())
		dev_warn(hwdev, "swiotlb buffer is full (sz: %zd bytes), total %lu (slots), used %lu (slots)\n",
			 alloc_size, io_tlb_nslabs, tmp_io_tlb_used);
	if (no_iotlb_memory)
		panic("Can not allocate SWIOTLB buffer earlier and can't now provide you with the DMA bounce buffer");

	if (mem_encrypt_active())
		pr_warn_once("Memory encryption is active and system is using DMA bounce buffers\n");

	if (mapping_size > alloc_size) {
		dev_warn_once(dev, "Invalid sizes (mapping: %zd bytes, alloc: %zd bytes)",
			      mapping_size, alloc_size);
		return (phys_addr_t)DMA_MAPPING_ERROR;
found:
	io_tlb_used += nslots;
	spin_unlock_irqrestore(&io_tlb_lock, flags);
	}

	index = find_slots(dev, alloc_size);
	if (index == -1) {
		if (!(attrs & DMA_ATTR_NO_WARN))
			dev_warn_ratelimited(dev,
	"swiotlb buffer is full (sz: %zd bytes), total %lu (slots), used %lu (slots)\n",
				 alloc_size, io_tlb_nslabs, io_tlb_used);
		return (phys_addr_t)DMA_MAPPING_ERROR;
	}

	/*
	 * Save away the mapping from the original address to the DMA address.
	 * This is needed when we sync the memory.  Then we sync the buffer if
	 * needed.
	 */
	for (i = 0; i < nslots; i++)
		io_tlb_orig_addr[index+i] = orig_addr + (i << IO_TLB_SHIFT);
	for (i = 0; i < nr_slots(alloc_size); i++)
		io_tlb_orig_addr[index + i] = slot_addr(orig_addr, i);

	tlb_addr = slot_addr(io_tlb_start, index);
	if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
	    (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL))
		swiotlb_bounce(orig_addr, tlb_addr, mapping_size, DMA_TO_DEVICE);

	return tlb_addr;
}