Beispiel #1
0
static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum,
                                    long npages, unsigned long uaddr,
                                    enum dma_data_direction direction,
                                    struct dma_attrs *attrs)
{
    u64 rc = 0;
    u64 proto_tce;
    u64 *tcep;
    u64 rpn;
    long l, limit;
    long tcenum_start = tcenum, npages_start = npages;
    int ret = 0;

    if (npages == 1) {
        return tce_build_pSeriesLP(tbl, tcenum, npages, uaddr,
                                   direction, attrs);
    }

    tcep = __get_cpu_var(tce_page);

    /* This is safe to do since interrupts are off when we're called
     * from iommu_alloc{,_sg}()
     */
    if (!tcep) {
        tcep = (u64 *)__get_free_page(GFP_ATOMIC);
        /* If allocation fails, fall back to the loop implementation */
        if (!tcep) {
            return tce_build_pSeriesLP(tbl, tcenum, npages, uaddr,
                                       direction, attrs);
        }
        __get_cpu_var(tce_page) = tcep;
    }

    rpn = (virt_to_abs(uaddr)) >> TCE_SHIFT;
    proto_tce = TCE_PCI_READ;
    if (direction != DMA_TO_DEVICE)
        proto_tce |= TCE_PCI_WRITE;

    /* We can map max one pageful of TCEs at a time */
    do {
        /*
         * Set up the page with TCE data, looping through and setting
         * the values.
         */
        limit = min_t(long, npages, 4096/TCE_ENTRY_SIZE);

        for (l = 0; l < limit; l++) {
            tcep[l] = proto_tce | (rpn & TCE_RPN_MASK) << TCE_RPN_SHIFT;
            rpn++;
        }

        rc = plpar_tce_put_indirect((u64)tbl->it_index,
                                    (u64)tcenum << 12,
                                    (u64)virt_to_abs(tcep),
                                    limit);

        npages -= limit;
        tcenum += limit;
    } while (npages > 0 && !rc);

    if (unlikely(rc == H_NOT_ENOUGH_RESOURCES)) {
        ret = (int)rc;
        tce_freemulti_pSeriesLP(tbl, tcenum_start,
                                (npages_start - (npages + limit)));
        return ret;
    }

    if (rc && printk_ratelimit()) {
        printk("tce_buildmulti_pSeriesLP: plpar_tce_put failed. rc=%lld\n", rc);
        printk("\tindex   = 0x%llx\n", (u64)tbl->it_index);
        printk("\tnpages  = 0x%llx\n", (u64)npages);
        printk("\ttce[0] val = 0x%llx\n", tcep[0]);
        show_stack(current, (unsigned long *)__get_SP());
    }
    return ret;
}
static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum,
				     long npages, unsigned long uaddr,
				     enum dma_data_direction direction,
				     struct dma_attrs *attrs)
{
	u64 rc = 0;
	u64 proto_tce;
	u64 *tcep;
	u64 rpn;
	long l, limit;
	long tcenum_start = tcenum, npages_start = npages;
	int ret = 0;

	if (npages == 1) {
		return tce_build_pSeriesLP(tbl, tcenum, npages, uaddr,
		                           direction, attrs);
	}

	tcep = __get_cpu_var(tce_page);

	if (!tcep) {
		tcep = (u64 *)__get_free_page(GFP_ATOMIC);
		
		if (!tcep) {
			return tce_build_pSeriesLP(tbl, tcenum, npages, uaddr,
					    direction, attrs);
		}
		__get_cpu_var(tce_page) = tcep;
	}

	rpn = (virt_to_abs(uaddr)) >> TCE_SHIFT;
	proto_tce = TCE_PCI_READ;
	if (direction != DMA_TO_DEVICE)
		proto_tce |= TCE_PCI_WRITE;

	
	do {
		limit = min_t(long, npages, 4096/TCE_ENTRY_SIZE);

		for (l = 0; l < limit; l++) {
			tcep[l] = proto_tce | (rpn & TCE_RPN_MASK) << TCE_RPN_SHIFT;
			rpn++;
		}

		rc = plpar_tce_put_indirect((u64)tbl->it_index,
					    (u64)tcenum << 12,
					    (u64)virt_to_abs(tcep),
					    limit);

		npages -= limit;
		tcenum += limit;
	} while (npages > 0 && !rc);

	if (unlikely(rc == H_NOT_ENOUGH_RESOURCES)) {
		ret = (int)rc;
		tce_freemulti_pSeriesLP(tbl, tcenum_start,
		                        (npages_start - (npages + limit)));
		return ret;
	}

	if (rc && printk_ratelimit()) {
		printk("tce_buildmulti_pSeriesLP: plpar_tce_put failed. rc=%lld\n", rc);
		printk("\tindex   = 0x%llx\n", (u64)tbl->it_index);
		printk("\tnpages  = 0x%llx\n", (u64)npages);
		printk("\ttce[0] val = 0x%llx\n", tcep[0]);
		show_stack(current, (unsigned long *)__get_SP());
	}
	return ret;
}