📄 sba_iommu.c
字号:
for (; mask ; mask <<= o, bitshiftcnt += o) { if(0 == ((*res_ptr) & mask)) { *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 */}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -