⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sba_iommu.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
					*res_ptr |= mask;     /* mark resources busy! */					pide = ((unsigned long)res_ptr - (unsigned long)ioc->res_map);					pide <<= 3;	/* convert to bit address */					pide += bitshiftcnt;					ioc->res_bitshift = bitshiftcnt + bits_wanted;					goto found_it;				}			}			bitshiftcnt = 0;			mask = base_mask;		}	} else {		int qwords, bits, i;		unsigned long *end;		qwords = bits_wanted >> 6; /* /64 */		bits = bits_wanted - (qwords * BITS_PER_LONG);		end = res_end - qwords;		for (; res_ptr < end; res_ptr++) {			for (i = 0 ; i < qwords ; i++) {				if (res_ptr[i] != 0)					goto next_ptr;			}			if (bits && res_ptr[i] && (__ffs(res_ptr[i]) < bits))				continue;			/* Found it, mark it */			for (i = 0 ; i < qwords ; i++)				res_ptr[i] = ~0UL;			res_ptr[i] |= RESMAP_MASK(bits);			pide = ((unsigned long)res_ptr - (unsigned long)ioc->res_map);			pide <<= 3;	/* convert to bit address */			res_ptr += qwords;			ioc->res_bitshift = bits;			goto found_it;next_ptr:			;		}	}not_found:	prefetch(ioc->res_map);	ioc->res_hint = (unsigned long *) ioc->res_map;	ioc->res_bitshift = 0;	spin_unlock_irqrestore(&ioc->res_lock, flags);	return (pide);found_it:	ioc->res_hint = res_ptr;	spin_unlock_irqrestore(&ioc->res_lock, flags);	return (pide);}/** * sba_alloc_range - find free bits and mark them in IO PDIR resource bitmap * @ioc: IO MMU structure which owns the pdir we are interested in. * @size: number of bytes to create a mapping for * * Given a size, find consecutive unmarked and then mark those bits in the * resource bit map. */static intsba_alloc_range(struct ioc *ioc, size_t size){	unsigned int pages_needed = size >> iovp_shift;#ifdef PDIR_SEARCH_TIMING	unsigned long itc_start;#endif	unsigned long pide;	ASSERT(pages_needed);	ASSERT(0 == (size & ~iovp_mask));#ifdef PDIR_SEARCH_TIMING	itc_start = ia64_get_itc();#endif	/*	** "seek and ye shall find"...praying never hurts either...	*/	pide = sba_search_bitmap(ioc, pages_needed, 1);	if (unlikely(pide >= (ioc->res_size << 3))) {		pide = sba_search_bitmap(ioc, pages_needed, 0);		if (unlikely(pide >= (ioc->res_size << 3))) {#if DELAYED_RESOURCE_CNT > 0			unsigned long flags;			/*			** With delayed resource freeing, we can give this one more shot.  We're			** getting close to being in trouble here, so do what we can to make this			** one count.			*/			spin_lock_irqsave(&ioc->saved_lock, flags);			if (ioc->saved_cnt > 0) {				struct sba_dma_pair *d;				int cnt = ioc->saved_cnt;				d = &(ioc->saved[ioc->saved_cnt - 1]);				spin_lock(&ioc->res_lock);				while (cnt--) {					sba_mark_invalid(ioc, d->iova, d->size);					sba_free_range(ioc, d->iova, d->size);					d--;				}				ioc->saved_cnt = 0;				READ_REG(ioc->ioc_hpa+IOC_PCOM);	/* flush purges */				spin_unlock(&ioc->res_lock);			}			spin_unlock_irqrestore(&ioc->saved_lock, flags);			pide = sba_search_bitmap(ioc, pages_needed, 0);			if (unlikely(pide >= (ioc->res_size << 3)))				panic(__FILE__ ": I/O MMU @ %p is out of mapping resources\n",				      ioc->ioc_hpa);#else			panic(__FILE__ ": I/O MMU @ %p is out of mapping resources\n",			      ioc->ioc_hpa);#endif		}	}#ifdef PDIR_SEARCH_TIMING	ioc->avg_search[ioc->avg_idx++] = (ia64_get_itc() - itc_start) / pages_needed;	ioc->avg_idx &= SBA_SEARCH_SAMPLE - 1;#endif	prefetchw(&(ioc->pdir_base[pide]));#ifdef ASSERT_PDIR_SANITY	/* verify the first enable bit is clear */	if(0x00 != ((u8 *) ioc->pdir_base)[pide*PDIR_ENTRY_SIZE + 7]) {		sba_dump_pdir_entry(ioc, "sba_search_bitmap() botched it?", pide);	}#endif	DBG_RES("%s(%x) %d -> %lx hint %x/%x\n",		__FUNCTION__, size, pages_needed, pide,		(uint) ((unsigned long) ioc->res_hint - (unsigned long) ioc->res_map),		ioc->res_bitshift );	return (pide);}/** * sba_free_range - unmark bits in IO PDIR resource bitmap * @ioc: IO MMU structure which owns the pdir we are interested in. * @iova: IO virtual address which was previously allocated. * @size: number of bytes to create a mapping for * * clear bits in the ioc's resource map */static SBA_INLINE voidsba_free_range(struct ioc *ioc, dma_addr_t iova, size_t size){	unsigned long iovp = SBA_IOVP(ioc, iova);	unsigned int pide = PDIR_INDEX(iovp);	unsigned int ridx = pide >> 3;	/* convert bit to byte address */	unsigned long *res_ptr = (unsigned long *) &((ioc)->res_map[ridx & ~RESMAP_IDX_MASK]);	int bits_not_wanted = size >> iovp_shift;	unsigned long m;	/* Round up to power-of-two size: see AR2305 note above */	bits_not_wanted = 1UL << get_iovp_order(bits_not_wanted << iovp_shift);	for (; bits_not_wanted > 0 ; res_ptr++) {				if (unlikely(bits_not_wanted > BITS_PER_LONG)) {			/* these mappings start 64bit aligned */			*res_ptr = 0UL;			bits_not_wanted -= BITS_PER_LONG;			pide += BITS_PER_LONG;		} else {			/* 3-bits "bit" address plus 2 (or 3) bits for "byte" == bit in word */			m = RESMAP_MASK(bits_not_wanted) << (pide & (BITS_PER_LONG - 1));			bits_not_wanted = 0;			DBG_RES("%s( ,%x,%x) %x/%lx %x %p %lx\n", __FUNCTION__, (uint) iova, size,		        	bits_not_wanted, m, pide, res_ptr, *res_ptr);			ASSERT(m != 0);			ASSERT(bits_not_wanted);			ASSERT((*res_ptr & m) == m); /* verify same bits are set */			*res_ptr &= ~m;		}	}}/****************************************************************   "Dynamic DMA Mapping" support (aka "Coherent I/O")****************************************************************//** * sba_io_pdir_entry - fill in one IO PDIR entry * @pdir_ptr:  pointer to IO PDIR entry * @vba: Virtual CPU address of buffer to map * * SBA Mapping Routine * * Given a virtual address (vba, arg1) sba_io_pdir_entry() * loads the I/O PDIR entry pointed to by pdir_ptr (arg0). * Each IO Pdir entry consists of 8 bytes as shown below * (LSB == bit 0): * *  63                    40                                 11    7        0 * +-+---------------------+----------------------------------+----+--------+ * |V|        U            |            PPN[39:12]            | U  |   FF   | * +-+---------------------+----------------------------------+----+--------+ * *  V  == Valid Bit *  U  == Unused * PPN == Physical Page Number * * The physical address fields are filled with the results of virt_to_phys() * on the vba. */#if 1#define sba_io_pdir_entry(pdir_ptr, vba) *pdir_ptr = ((vba & ~0xE000000000000FFFULL)	\						      | 0x8000000000000000ULL)#elsevoid SBA_INLINEsba_io_pdir_entry(u64 *pdir_ptr, unsigned long vba){	*pdir_ptr = ((vba & ~0xE000000000000FFFULL) | 0x80000000000000FFULL);}#endif#ifdef ENABLE_MARK_CLEAN/** * Since DMA is i-cache coherent, any (complete) pages that were written via * DMA can be marked as "clean" so that lazy_mmu_prot_update() doesn't have to * flush them when they get mapped into an executable vm-area. */static voidmark_clean (void *addr, size_t size){	unsigned long pg_addr, end;	pg_addr = PAGE_ALIGN((unsigned long) addr);	end = (unsigned long) addr + size;	while (pg_addr + PAGE_SIZE <= end) {		struct page *page = virt_to_page((void *)pg_addr);		set_bit(PG_arch_1, &page->flags);		pg_addr += PAGE_SIZE;	}}#endif/** * sba_mark_invalid - invalidate one or more IO PDIR entries * @ioc: IO MMU structure which owns the pdir we are interested in. * @iova:  IO Virtual Address mapped earlier * @byte_cnt:  number of bytes this mapping covers. * * Marking the IO PDIR entry(ies) as Invalid and invalidate * corresponding IO TLB entry. The PCOM (Purge Command Register) * is to purge stale entries in the IO TLB when unmapping entries. * * The PCOM register supports purging of multiple pages, with a minium * of 1 page and a maximum of 2GB. Hardware requires the address be * aligned to the size of the range being purged. The size of the range * must be a power of 2. The "Cool perf optimization" in the * allocation routine helps keep that true. */static SBA_INLINE voidsba_mark_invalid(struct ioc *ioc, dma_addr_t iova, size_t byte_cnt){	u32 iovp = (u32) SBA_IOVP(ioc,iova);	int off = PDIR_INDEX(iovp);	/* Must be non-zero and rounded up */	ASSERT(byte_cnt > 0);	ASSERT(0 == (byte_cnt & ~iovp_mask));#ifdef ASSERT_PDIR_SANITY	/* Assert first pdir entry is set */	if (!(ioc->pdir_base[off] >> 60)) {		sba_dump_pdir_entry(ioc,"sba_mark_invalid()", PDIR_INDEX(iovp));	}#endif	if (byte_cnt <= iovp_size)	{		ASSERT(off < ioc->pdir_size);		iovp |= iovp_shift;     /* set "size" field for PCOM */#ifndef FULL_VALID_PDIR		/*		** clear I/O PDIR entry "valid" bit		** Do NOT clear the rest - save it for debugging.		** We should only clear bits that have previously		** been enabled.		*/		ioc->pdir_base[off] &= ~(0x80000000000000FFULL);#else		/*  		** If we want to maintain the PDIR as valid, put in		** the spill page so devices prefetching won't		** cause a hard fail.		*/		ioc->pdir_base[off] = (0x80000000000000FFULL | prefetch_spill_page);#endif	} else {		u32 t = get_iovp_order(byte_cnt) + iovp_shift;		iovp |= t;		ASSERT(t <= 31);   /* 2GB! Max value of "size" field */		do {			/* verify this pdir entry is enabled */			ASSERT(ioc->pdir_base[off]  >> 63);#ifndef FULL_VALID_PDIR			/* clear I/O Pdir entry "valid" bit first */			ioc->pdir_base[off] &= ~(0x80000000000000FFULL);#else			ioc->pdir_base[off] = (0x80000000000000FFULL | prefetch_spill_page);#endif			off++;			byte_cnt -= iovp_size;		} while (byte_cnt > 0);	}	WRITE_REG(iovp | ioc->ibase, ioc->ioc_hpa+IOC_PCOM);}/** * sba_map_single - map one buffer and return IOVA for DMA * @dev: instance of PCI owned by the driver that's asking. * @addr:  driver buffer to map. * @size:  number of bytes to map in driver buffer. * @dir:  R/W or both. * * See Documentation/DMA-mapping.txt */dma_addr_tsba_map_single(struct device *dev, void *addr, size_t size, int dir){	struct ioc *ioc;	dma_addr_t iovp;	dma_addr_t offset;	u64 *pdir_start;	int pide;#ifdef ASSERT_PDIR_SANITY	unsigned long flags;#endif#ifdef ALLOW_IOV_BYPASS	unsigned long pci_addr = virt_to_phys(addr);#endif#ifdef ALLOW_IOV_BYPASS	ASSERT(to_pci_dev(dev)->dma_mask);	/* 	** Check if the PCI device can DMA to ptr... if so, just return ptr 	*/	if (likely((pci_addr & ~to_pci_dev(dev)->dma_mask) == 0)) {		/* 		** Device is bit capable of DMA'ing to the buffer...		** just return the PCI address of ptr 		*/		DBG_BYPASS("sba_map_single() bypass mask/addr: 0x%lx/0x%lx\n",		           to_pci_dev(dev)->dma_mask, pci_addr);		return pci_addr;	}#endif	ioc = GET_IOC(dev);	ASSERT(ioc);	prefetch(ioc->res_hint);	ASSERT(size > 0);	ASSERT(size <= DMA_CHUNK_SIZE);	/* save offset bits */	offset = ((dma_addr_t) (long) addr) & ~iovp_mask;	/* round up to nearest iovp_size */	size = (size + offset + ~iovp_mask) & iovp_mask;#ifdef ASSERT_PDIR_SANITY	spin_lock_irqsave(&ioc->res_lock, flags);	if (sba_check_pdir(ioc,"Check before sba_map_single()"))		panic("Sanity check failed");	spin_unlock_irqrestore(&ioc->res_lock, flags);#endif	pide = sba_alloc_range(ioc, size);	iovp = (dma_addr_t) pide << iovp_shift;	DBG_RUN("%s() 0x%p -> 0x%lx\n",		__FUNCTION__, addr, (long) iovp | offset);	pdir_start = &(ioc->pdir_base[pide]);	while (size > 0) {		ASSERT(((u8 *)pdir_start)[7] == 0); /* verify availability */		sba_io_pdir_entry(pdir_start, (unsigned long) addr);		DBG_RUN("     pdir 0x%p %lx\n", pdir_start, *pdir_start);		addr += iovp_size;		size -= iovp_size;		pdir_start++;	}	/* force pdir update */	wmb();	/* form complete address */#ifdef ASSERT_PDIR_SANITY	spin_lock_irqsave(&ioc->res_lock, flags);	sba_check_pdir(ioc,"Check after sba_map_single()");	spin_unlock_irqrestore(&ioc->res_lock, flags);#endif	return SBA_IOVA(ioc, iovp, offset);}#ifdef ENABLE_MARK_CLEANstatic SBA_INLINE voidsba_mark_clean(struct ioc *ioc, dma_addr_t iova, size_t size){	u32	iovp = (u32) SBA_IOVP(ioc,iova);	int	off = PDIR_INDEX(iovp);	void	*addr;	if (size <= iovp_size) {		addr = phys_to_virt(ioc->pdir_base[off] &		                    ~0xE000000000000FFFULL);		mark_clean(addr, size);	} else {		do {			addr = phys_to_virt(ioc->pdir_base[off] &			                    ~0xE000000000000FFFULL);			mark_clean(addr, min(size, iovp_size));			off++;			size -= iovp_size;		} while (size > 0);	}}#endif/** * sba_unmap_single - unmap one IOVA and free resources * @dev: instance of PCI owned by the driver that's asking. * @iova:  IOVA of driver buffer previously mapped. * @size:  number of bytes mapped in driver buffer. * @dir:  R/W or both. * * See Documentation/DMA-mapping.txt */void sba_unmap_single(struct device *dev, dma_addr_t iova, size_t size, int dir){	struct ioc *ioc;#if DELAYED_RESOURCE_CNT > 0	struct sba_dma_pair *d;#endif	unsigned long flags;	dma_addr_t offset;	ioc = GET_IOC(dev);	ASSERT(ioc);#ifdef ALLOW_IOV_BYPASS	if (likely((iova & ioc->imask) != ioc->ibase)) {		/*		** Address does not fall w/in IOVA, must be bypassing		*/		DBG_BYPASS("sba_unmap_single() bypass addr: 0x%lx\n", iova);#ifdef ENABLE_MARK_CLEAN		if (dir == DMA_FROM_DEVICE) {			mark_clean(phys_to_virt(iova), size);		}#endif		return;	}#endif	offset = iova & ~iovp_mask;	DBG_RUN("%s() iovp 0x%lx/%x\n",		__FUNCTION__, (long) iova, size);	iova ^= offset;        /* clear offset bits */	size += offset;	size = ROUNDUP(size, iovp_size);#ifdef ENABLE_MARK_CLEAN	if (dir == DMA_FROM_DEVICE)		sba_mark_clean(ioc, iova, size);#endif#if DELAYED_RESOURCE_CNT > 0	spin_lock_irqsave(&ioc->saved_lock, flags);	d = &(ioc->saved[ioc->saved_cnt]);	d->iova = iova;	d->size = size;	if (unlikely(++(ioc->saved_cnt) >= DELAYED_RESOURCE_CNT)) {		int cnt = ioc->saved_cnt;		spin_lock(&ioc->res_lock);		while (cnt--) {			sba_mark_invalid(ioc, d->iova, d->size);			sba_free_range(ioc, d->iova, d->size);			d--;		}		ioc->saved_cnt = 0;		READ_REG(ioc->ioc_hpa+IOC_PCOM);	/* flush purges */		spin_unlock(&ioc->res_lock);	}	spin_unlock_irqrestore(&ioc->saved_lock, flags);#else /* DELAYED_RESOURCE_CNT == 0 */	spin_lock_irqsave(&ioc->res_lock, flags);	sba_mark_invalid(ioc, iova, size);	sba_free_range(ioc, iova, size);	READ_REG(ioc->ioc_hpa+IOC_PCOM);	/* flush purges */	spin_unlock_irqrestore(&ioc->res_lock, flags);#endif /* DELAYED_RESOURCE_CNT == 0 */}/** * sba_alloc_coherent - allocate/map shared mem for DMA * @dev: instance of PCI owned by the driver that's asking. * @size:  number of bytes mapped in driver buffer. * @dma_handle:  IOVA of new buffer.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -