int xen_swiotlb_init()

in swiotlb-xen.c [155:218]


int xen_swiotlb_init(void)
{
	enum xen_swiotlb_err m_ret = XEN_SWIOTLB_UNKNOWN;
	unsigned long bytes = swiotlb_size_or_default();
	unsigned long nslabs = bytes >> IO_TLB_SHIFT;
	unsigned int order, repeat = 3;
	int rc = -ENOMEM;
	char *start;

	if (io_tlb_default_mem.nslabs) {
		pr_warn("swiotlb buffer already initialized\n");
		return -EEXIST;
	}

retry:
	m_ret = XEN_SWIOTLB_ENOMEM;
	order = get_order(bytes);

	/*
	 * Get IO TLB memory from any location.
	 */
#define SLABS_PER_PAGE (1 << (PAGE_SHIFT - IO_TLB_SHIFT))
#define IO_TLB_MIN_SLABS ((1<<20) >> IO_TLB_SHIFT)
	while ((SLABS_PER_PAGE << order) > IO_TLB_MIN_SLABS) {
		start = (void *)xen_get_swiotlb_free_pages(order);
		if (start)
			break;
		order--;
	}
	if (!start)
		goto exit;
	if (order != get_order(bytes)) {
		pr_warn("Warning: only able to allocate %ld MB for software IO TLB\n",
			(PAGE_SIZE << order) >> 20);
		nslabs = SLABS_PER_PAGE << order;
		bytes = nslabs << IO_TLB_SHIFT;
	}

	/*
	 * And replace that memory with pages under 4GB.
	 */
	rc = xen_swiotlb_fixup(start, nslabs);
	if (rc) {
		free_pages((unsigned long)start, order);
		m_ret = XEN_SWIOTLB_EFIXUP;
		goto error;
	}
	rc = swiotlb_late_init_with_tbl(start, nslabs);
	if (rc)
		return rc;
	swiotlb_set_max_segment(PAGE_SIZE);
	return 0;
error:
	if (nslabs > 1024 && repeat--) {
		/* Min is 2MB */
		nslabs = max(1024UL, ALIGN(nslabs >> 1, IO_TLB_SEGSIZE));
		bytes = nslabs << IO_TLB_SHIFT;
		pr_info("Lowering to %luMB\n", bytes >> 20);
		goto retry;
	}
exit:
	pr_err("%s (rc:%d)\n", xen_swiotlb_error(m_ret), rc);
	return rc;
}