/* Note that the TLB lookup logic uses bitwise concatenation, notaddition,sotherequiredarenaalignmentisbasedon thesizeofthewindow.Retainthealignparametersothat
particular systems can over-align the arena. */ if (align < mem_size)
align = mem_size;
/* Must be called with the arena lock held */ staticlong
iommu_arena_find_pages(struct device *dev, struct pci_iommu_arena *arena, long n, long mask)
{ unsignedlong *ptes; long i, p, nent; int pass = 0; unsignedlong base; unsignedlong boundary_size;
base = arena->dma_base >> PAGE_SHIFT;
boundary_size = dma_get_seg_boundary_nr_pages(dev, PAGE_SHIFT);
/* Search forward for the first mask-aligned sequence of N free ptes */
ptes = arena->ptes;
nent = arena->size >> PAGE_SHIFT;
p = ALIGN(arena->next_entry, mask + 1);
i = 0;
again: while (i < n && p+i < nent) { if (!i && iommu_is_span_boundary(p, n, base, boundary_size)) {
p = ALIGN(p + 1, mask + 1); goto again;
}
if (ptes[p+i]) {
p = ALIGN(p + i + 1, mask + 1);
i = 0;
} else {
i = i + 1;
}
}
if (i < n) { if (pass < 1) { /* *Reachedtheend.FlushtheTLBandrestart *thesearchfromthebeginning.
*/
alpha_mv.mv_pci_tbi(arena->hose, 0, -1);
pass++;
p = 0;
i = 0; goto again;
} else return -1;
}
/* Success. It's the responsibility of the caller to mark them
in use before releasing the lock */ return p;
}
staticlong
iommu_arena_alloc(struct device *dev, struct pci_iommu_arena *arena, long n, unsignedint align)
{ unsignedlong flags; unsignedlong *ptes; long i, p, mask;
spin_lock_irqsave(&arena->lock, flags);
/* Search for N empty ptes */
ptes = arena->ptes;
mask = max(align, arena->align_entry) - 1;
p = iommu_arena_find_pages(dev, arena, n, mask); if (p < 0) {
spin_unlock_irqrestore(&arena->lock, flags); return -1;
}
/* Success. Mark them all in use, ie not zero and invalid fortheiommutlbthatcouldloadthemfromunderus. Thechipspecificbitswillfillthisinwithsomething
kosher when we return. */ for (i = 0; i < n; ++i)
ptes[p+i] = IOMMU_INVALID_PTE;
arena->next_entry = p + n;
spin_unlock_irqrestore(&arena->lock, flags);
return p;
}
staticvoid
iommu_arena_free(struct pci_iommu_arena *arena, long ofs, long n)
{ unsignedlong *p; long i;
p = arena->ptes + ofs; for (i = 0; i < n; ++i)
p[i] = 0;
}
/* *TrueifthemachinesupportsDACaddressing,andDEVcan *makeuseofitgivenMASK.
*/ staticint pci_dac_dma_supported(struct pci_dev *dev, u64 mask)
{
dma_addr_t dac_offset = alpha_mv.pci_dac_offset; int ok = 1;
/* If this is not set, the machine doesn't support DAC at all. */ if (dac_offset == 0)
ok = 0;
/* The device has to be able to address our DAC bit. */ if ((dac_offset & dev->dma_mask) != dac_offset)
ok = 0;
/* If both conditions above are met, we are fine. */
DBGA("pci_dac_dma_supported %s from %ps\n",
str_yes_no(ok), __builtin_return_address(0));
return ok;
}
/* Map a single buffer of the indicated size for PCI DMA in streaming mode.The32-bitPCIbusmasteringaddresstouseisreturned. Oncethedeviceisgiventhedmaaddress,thedeviceownsthismemory
until either pci_unmap_single or pci_dma_sync_single is performed. */
#if !DEBUG_NODIRECT /* First check to see if we can use the direct map window. */ if (paddr + size + __direct_map_base - 1 <= max_dma
&& paddr + size <= __direct_map_size) {
ret = paddr + __direct_map_base;
DBGA2("pci_map_single: [%p,%zx] -> direct %llx from %ps\n",
cpu_addr, size, ret, __builtin_return_address(0));
return ret;
} #endif
/* Next, use DAC if selected earlier. */ if (dac_allowed) {
ret = paddr + alpha_mv.pci_dac_offset;
/* If the machine doesn't define a pci_tbi routine, we have to assumeitdoesn'tsupportsgmapping,and,sincewetriedto
use direct_map above, it now must be considered an error. */ if (! alpha_mv.mv_pci_tbi) {
printk_once(KERN_WARNING "pci_map_single: no HW sg\n"); return DMA_MAPPING_ERROR;
}
/* Helper for generic DMA-mapping functions. */ staticstruct pci_dev *alpha_gendev_to_pci(struct device *dev)
{ if (dev && dev_is_pci(dev)) return to_pci_dev(dev);
/* Assume that non-PCI devices asking for DMA are either ISA or EISA,
BUG() otherwise. */
BUG_ON(!isa_bridge);
/* Assume non-busmaster ISA DMA when dma_mask is not set (the ISA
bridge is bus master then). */ if (!dev || !dev->dma_mask || !*dev->dma_mask) return isa_bridge;
/* For EISA bus masters, return isa_bridge (it might have smaller
dma_mask due to wiring limitations). */ if (*dev->dma_mask >= isa_bridge->dma_mask) return isa_bridge;
/* This assumes ISA bus master with dma_mask 0xffffff. */ return NULL;
}
/* Unmap a single streaming mode DMA translation. The DMA_ADDR and SIZEmustmatchwhatwasprovidedforinapreviouspci_map_single call.Allotherusagesareundefined.Afterthiscall,readsby thecputothebufferareguaranteedtoseewhateverthedevice
wrote there. */
/* If we're freeing ptes above the `next_entry' pointer (they mayhavesnuckbackintotheTLBsincethelastwrapflush),
we need to flush the TLB before reallocating the latter. */ if (dma_ofs >= arena->next_entry)
alpha_mv.mv_pci_tbi(hose, dma_addr, dma_addr + size - 1);
/* Allocate and map kernel buffer using consistent mode DMA for PCI device.Returnsnon-NULLcpu-viewpointertothebufferif successfulandsets*DMA_ADDRPtothepcisidedmaaddressaswell,
else DMA_ADDRP is undefined. */
try_again:
cpu_addr = (void *)__get_free_pages(gfp | __GFP_ZERO, order); if (! cpu_addr) {
printk(KERN_INFO "pci_alloc_consistent: " "get_free_pages failed from %ps\n",
__builtin_return_address(0)); /* ??? Really atomic allocation? Otherwise we could play
with vmalloc and sg if we can't find contiguous memory. */ return NULL;
}
memset(cpu_addr, 0, size);
*dma_addrp = pci_map_single_1(pdev, cpu_addr, size, 0); if (*dma_addrp == DMA_MAPPING_ERROR) {
free_pages((unsignedlong)cpu_addr, order); if (alpha_mv.mv_pci_tbi || (gfp & GFP_DMA)) return NULL; /* The address doesn't fit required mask and we
do not have iommu. Try again with GFP_DMA. */
gfp |= GFP_DMA; goto try_again;
}
DBGA2("pci_alloc_consistent: %zx -> [%p,%llx] from %ps\n",
size, cpu_addr, *dma_addrp, __builtin_return_address(0));
return cpu_addr;
}
/* Free and unmap a consistent DMA buffer. CPU_ADDR and DMA_ADDR must bevaluesthatwerereturnedfrompci_alloc_consistent.SIZEmust bethesameaswhataspassedintopci_alloc_consistent. ReferencestothememoryandmappingsassociatedwithCPU_ADDRor
DMA_ADDR past this call are illegal. */
DBGA2("pci_free_consistent: [%llx,%zx] from %ps\n",
dma_addr, size, __builtin_return_address(0));
}
/* Classify the elements of the scatterlist. Write dma_address ofeachelementwith: 0:Followersallphysicallyadjacent. 1:Followersallvirtuallyadjacent. -1:Notleader,physicallyadjacenttoprevious. -2:Notleader,virtuallyadjacenttoprevious. Writedma_lengthofeachleaderwiththecombinedlengthsof
the mergable followers. */
/* we will not marge sg without device. */
max_seg_size = dev ? dma_get_max_seg_size(dev) : 0; for (++sg; sg < end; ++sg) { unsignedlong addr, len;
addr = SG_ENT_PHYS_ADDRESS(sg);
len = sg->length;
if (leader_length + len > max_seg_size) goto new_segment;
#if !DEBUG_NODIRECT /* If everything is physically contiguous, and the addresses
fall into the direct-map window, use it. */ if (leader->dma_address == 0
&& paddr + size + __direct_map_base - 1 <= max_dma
&& paddr + size <= __direct_map_size) {
out->dma_address = paddr + __direct_map_base;
out->dma_length = size;
DBGA(" sg_fill: [%p,%lx] -> direct %llx\n",
__va(paddr), size, out->dma_address);
return0;
} #endif
/* If physically contiguous and DAC is available, use it. */ if (leader->dma_address == 0 && dac_allowed) {
out->dma_address = paddr + alpha_mv.pci_dac_offset;
out->dma_length = size;
/* All virtually contiguous. We need to find the length of each
physically contiguous subsegment to fill in the ptes. */
ptes = &arena->ptes[dma_ofs];
sg = leader; do { #if DEBUG_ALLOC > 0 struct scatterlist *last_sg = sg; #endif
/* Some allocation failed while mapping the scatterlist
entries. Unmap them now. */ if (out > start)
dma_unmap_sg(&pdev->dev, start, out - start, dir); return -ENOMEM;
}
/* Unmap a set of streaming mode DMA translations. Again, cpu read rulesconcerningcallsherearethesameasforpci_unmap_single()
above. */
/* If we're freeing ptes above the `next_entry' pointer (they mayhavesnuckbackintotheTLBsincethelastwrapflush),
we need to flush the TLB before reallocating the latter. */ if ((fend - arena->dma_base) >> PAGE_SHIFT >= arena->next_entry)
alpha_mv.mv_pci_tbi(hose, fbeg, fend);
/* If there exists a direct map, and the mask fits either theentiredirectmappedspaceorthetotalsystemmemoryas
shifted by the map base */ if (__direct_map_size != 0
&& (__direct_map_base + __direct_map_size - 1 <= mask ||
__direct_map_base + (max_low_pfn << PAGE_SHIFT) - 1 <= mask)) return1;
/* Check that we have a scatter-gather arena that fits. */
hose = pdev ? pdev->sysdata : pci_isa_hose;
arena = hose->sg_isa; if (arena && arena->dma_base + arena->size - 1 <= mask) return1;
arena = hose->sg_pci; if (arena && arena->dma_base + arena->size - 1 <= mask) return1;
/* As last resort try ZONE_DMA. */ if (!__direct_map_base && MAX_DMA_ADDRESS - IDENT_ADDR - 1 <= mask) return1;
return0;
}
/* *AGPGARTextensionstotheIOMMU
*/ int
iommu_reserve(struct pci_iommu_arena *arena, long pg_count, long align_mask)
{ unsignedlong flags; unsignedlong *ptes; long i, p;
if (!arena) return -EINVAL;
spin_lock_irqsave(&arena->lock, flags);
/* Search for N empty ptes. */
ptes = arena->ptes;
p = iommu_arena_find_pages(NULL, arena, pg_count, align_mask); if (p < 0) {
spin_unlock_irqrestore(&arena->lock, flags); return -1;
}
/* Success. Mark them all reserved (ie not zero and invalid) fortheiommutlbthatcouldloadthemfromunderus.
They will be filled in with valid bits by _bind() */ for (i = 0; i < pg_count; ++i)
ptes[p+i] = IOMMU_RESERVED_PTE;
arena->next_entry = p + pg_count;
spin_unlock_irqrestore(&arena->lock, flags);
return p;
}
int
iommu_release(struct pci_iommu_arena *arena, long pg_start, long pg_count)
{ unsignedlong *ptes; long i;
if (!arena) return -EINVAL;
ptes = arena->ptes;
/* Make sure they're all reserved first... */ for(i = pg_start; i < pg_start + pg_count; i++) if (ptes[i] != IOMMU_RESERVED_PTE) return -EBUSY;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.