|
@@ -1,9 +1,9 @@
|
|
|
/*
|
|
|
** IA64 System Bus Adapter (SBA) I/O MMU manager
|
|
|
**
|
|
|
-** (c) Copyright 2002-2004 Alex Williamson
|
|
|
+** (c) Copyright 2002-2005 Alex Williamson
|
|
|
** (c) Copyright 2002-2003 Grant Grundler
|
|
|
-** (c) Copyright 2002-2004 Hewlett-Packard Company
|
|
|
+** (c) Copyright 2002-2005 Hewlett-Packard Company
|
|
|
**
|
|
|
** Portions (c) 2000 Grant Grundler (from parisc I/O MMU code)
|
|
|
** Portions (c) 1999 Dave S. Miller (from sparc64 I/O MMU code)
|
|
@@ -459,21 +459,32 @@ get_iovp_order (unsigned long size)
|
|
|
* sba_search_bitmap - find free space in IO PDIR resource bitmap
|
|
|
* @ioc: IO MMU structure which owns the pdir we are interested in.
|
|
|
* @bits_wanted: number of entries we need.
|
|
|
+ * @use_hint: use res_hint to indicate where to start looking
|
|
|
*
|
|
|
* Find consecutive free bits in resource bitmap.
|
|
|
* Each bit represents one entry in the IO Pdir.
|
|
|
* Cool perf optimization: search for log2(size) bits at a time.
|
|
|
*/
|
|
|
static SBA_INLINE unsigned long
|
|
|
-sba_search_bitmap(struct ioc *ioc, unsigned long bits_wanted)
|
|
|
+sba_search_bitmap(struct ioc *ioc, unsigned long bits_wanted, int use_hint)
|
|
|
{
|
|
|
- unsigned long *res_ptr = ioc->res_hint;
|
|
|
+ unsigned long *res_ptr;
|
|
|
unsigned long *res_end = (unsigned long *) &(ioc->res_map[ioc->res_size]);
|
|
|
- unsigned long pide = ~0UL;
|
|
|
+ unsigned long flags, pide = ~0UL;
|
|
|
|
|
|
ASSERT(((unsigned long) ioc->res_hint & (sizeof(unsigned long) - 1UL)) == 0);
|
|
|
ASSERT(res_ptr < res_end);
|
|
|
|
|
|
+ spin_lock_irqsave(&ioc->res_lock, flags);
|
|
|
+
|
|
|
+ /* Allow caller to force a search through the entire resource space */
|
|
|
+ if (likely(use_hint)) {
|
|
|
+ res_ptr = ioc->res_hint;
|
|
|
+ } else {
|
|
|
+ res_ptr = (ulong *)ioc->res_map;
|
|
|
+ ioc->res_bitshift = 0;
|
|
|
+ }
|
|
|
+
|
|
|
/*
|
|
|
* N.B. REO/Grande defect AR2305 can cause TLB fetch timeouts
|
|
|
* if a TLB entry is purged while in use. sba_mark_invalid()
|
|
@@ -570,10 +581,12 @@ 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);
|
|
|
}
|
|
|
|
|
@@ -594,36 +607,36 @@ sba_alloc_range(struct ioc *ioc, size_t size)
|
|
|
unsigned long itc_start;
|
|
|
#endif
|
|
|
unsigned long pide;
|
|
|
- unsigned long flags;
|
|
|
|
|
|
ASSERT(pages_needed);
|
|
|
ASSERT(0 == (size & ~iovp_mask));
|
|
|
|
|
|
- spin_lock_irqsave(&ioc->res_lock, flags);
|
|
|
-
|
|
|
#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);
|
|
|
+ pide = sba_search_bitmap(ioc, pages_needed, 1);
|
|
|
if (unlikely(pide >= (ioc->res_size << 3))) {
|
|
|
- pide = sba_search_bitmap(ioc, pages_needed);
|
|
|
+ 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(&ioc->saved_lock);
|
|
|
+ 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]);
|
|
|
+ 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);
|
|
@@ -631,10 +644,11 @@ sba_alloc_range(struct ioc *ioc, size_t size)
|
|
|
}
|
|
|
ioc->saved_cnt = 0;
|
|
|
READ_REG(ioc->ioc_hpa+IOC_PCOM); /* flush purges */
|
|
|
+ spin_unlock(&ioc->res_lock);
|
|
|
}
|
|
|
- spin_unlock(&ioc->saved_lock);
|
|
|
+ spin_unlock_irqrestore(&ioc->saved_lock, flags);
|
|
|
|
|
|
- pide = sba_search_bitmap(ioc, pages_needed);
|
|
|
+ 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);
|
|
@@ -664,8 +678,6 @@ sba_alloc_range(struct ioc *ioc, size_t size)
|
|
|
(uint) ((unsigned long) ioc->res_hint - (unsigned long) ioc->res_map),
|
|
|
ioc->res_bitshift );
|
|
|
|
|
|
- spin_unlock_irqrestore(&ioc->res_lock, flags);
|
|
|
-
|
|
|
return (pide);
|
|
|
}
|
|
|
|
|
@@ -950,6 +962,30 @@ sba_map_single(struct device *dev, void *addr, size_t size, int dir)
|
|
|
return SBA_IOVA(ioc, iovp, offset);
|
|
|
}
|
|
|
|
|
|
+#ifdef ENABLE_MARK_CLEAN
|
|
|
+static SBA_INLINE void
|
|
|
+sba_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.
|
|
@@ -995,6 +1031,10 @@ void sba_unmap_single(struct device *dev, dma_addr_t iova, size_t size, int dir)
|
|
|
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);
|
|
@@ -1021,30 +1061,6 @@ void sba_unmap_single(struct device *dev, dma_addr_t iova, size_t size, int dir)
|
|
|
READ_REG(ioc->ioc_hpa+IOC_PCOM); /* flush purges */
|
|
|
spin_unlock_irqrestore(&ioc->res_lock, flags);
|
|
|
#endif /* DELAYED_RESOURCE_CNT == 0 */
|
|
|
-#ifdef ENABLE_MARK_CLEAN
|
|
|
- if (dir == DMA_FROM_DEVICE) {
|
|
|
- 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 {
|
|
|
- size_t byte_cnt = size;
|
|
|
-
|
|
|
- do {
|
|
|
- addr = phys_to_virt(ioc->pdir_base[off] &
|
|
|
- ~0xE000000000000FFFULL);
|
|
|
- mark_clean(addr, min(byte_cnt, iovp_size));
|
|
|
- off++;
|
|
|
- byte_cnt -= iovp_size;
|
|
|
-
|
|
|
- } while (byte_cnt > 0);
|
|
|
- }
|
|
|
- }
|
|
|
-#endif
|
|
|
}
|
|
|
|
|
|
|