sparc64: add the segment boundary checking to IOMMUs while merging SG entries

Some IOMMUs allocate memory areas spanning LLD's segment boundary limit. It
forces low level drivers to have a workaround to adjust scatter lists that the
IOMMU builds. We are in the process of making all the IOMMUs respect the
segment boundary limits to remove such work around in LLDs.

SPARC64 IOMMUs were rewritten to use the IOMMU helper functions and the commit
89c94f2f70d093f59b55d3ea8042d13889169346 made the IOMMUs not allocate memory
areas spanning the segment boundary limit.

However, SPARC64 IOMMUs allocate memory areas first then try to merge them
(while some IOMMUs walk through all the sg entries to see how they can be
merged first and allocate memory areas). So SPARC64 IOMMUs also need the
boundary limit checking when they try to merge sg entries.

Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by FUJITA Tomonori and committed by David S. Miller f0880257 76cc86ee

+33 -4
+10 -2
arch/sparc64/kernel/iommu.c
··· 516 516 unsigned long flags, handle, prot, ctx; 517 517 dma_addr_t dma_next = 0, dma_addr; 518 518 unsigned int max_seg_size; 519 + unsigned long seg_boundary_size; 519 520 int outcount, incount, i; 520 521 struct strbuf *strbuf; 521 522 struct iommu *iommu; 523 + unsigned long base_shift; 522 524 523 525 BUG_ON(direction == DMA_NONE); 524 526 ··· 551 549 outs->dma_length = 0; 552 550 553 551 max_seg_size = dma_get_max_seg_size(dev); 552 + seg_boundary_size = ALIGN(dma_get_seg_boundary(dev) + 1, 553 + IO_PAGE_SIZE) >> IO_PAGE_SHIFT; 554 + base_shift = iommu->page_table_map_base >> IO_PAGE_SHIFT; 554 555 for_each_sg(sglist, s, nelems, i) { 555 - unsigned long paddr, npages, entry, slen; 556 + unsigned long paddr, npages, entry, out_entry = 0, slen; 556 557 iopte_t *base; 557 558 558 559 slen = s->length; ··· 598 593 * - allocated dma_addr isn't contiguous to previous allocation 599 594 */ 600 595 if ((dma_addr != dma_next) || 601 - (outs->dma_length + s->length > max_seg_size)) { 596 + (outs->dma_length + s->length > max_seg_size) || 597 + (is_span_boundary(out_entry, base_shift, 598 + seg_boundary_size, outs, s))) { 602 599 /* Can't merge: create a new segment */ 603 600 segstart = s; 604 601 outcount++; ··· 614 607 /* This is a new segment, fill entries */ 615 608 outs->dma_address = dma_addr; 616 609 outs->dma_length = slen; 610 + out_entry = entry; 617 611 } 618 612 619 613 /* Calculate next page pointer for contiguous check */
+13
arch/sparc64/kernel/iommu_common.h
··· 12 12 #include <linux/mm.h> 13 13 #include <linux/scatterlist.h> 14 14 #include <linux/device.h> 15 + #include <linux/iommu-helper.h> 15 16 16 17 #include <asm/iommu.h> 17 18 #include <asm/scatterlist.h> ··· 57 56 } 58 57 59 58 return npages; 59 + } 60 + 61 + static inline int is_span_boundary(unsigned long entry, 62 + unsigned long shift, 63 + unsigned long boundary_size, 64 + struct scatterlist *outs, 65 + struct scatterlist *sg) 66 + { 67 + unsigned long paddr = SG_ENT_PHYS_ADDRESS(outs); 68 + int nr = iommu_num_pages(paddr, outs->dma_length + sg->length); 69 + 70 + return iommu_is_span_boundary(entry, nr, shift, boundary_size); 60 71 } 61 72 62 73 extern unsigned long iommu_range_alloc(struct device *dev,
+10 -2
arch/sparc64/kernel/pci_sun4v.c
··· 335 335 unsigned long flags, handle, prot; 336 336 dma_addr_t dma_next = 0, dma_addr; 337 337 unsigned int max_seg_size; 338 + unsigned long seg_boundary_size; 338 339 int outcount, incount, i; 339 340 struct iommu *iommu; 341 + unsigned long base_shift; 340 342 long err; 341 343 342 344 BUG_ON(direction == DMA_NONE); ··· 364 362 iommu_batch_start(dev, prot, ~0UL); 365 363 366 364 max_seg_size = dma_get_max_seg_size(dev); 365 + seg_boundary_size = ALIGN(dma_get_seg_boundary(dev) + 1, 366 + IO_PAGE_SIZE) >> IO_PAGE_SHIFT; 367 + base_shift = iommu->page_table_map_base >> IO_PAGE_SHIFT; 367 368 for_each_sg(sglist, s, nelems, i) { 368 - unsigned long paddr, npages, entry, slen; 369 + unsigned long paddr, npages, entry, out_entry = 0, slen; 369 370 370 371 slen = s->length; 371 372 /* Sanity check */ ··· 411 406 * - allocated dma_addr isn't contiguous to previous allocation 412 407 */ 413 408 if ((dma_addr != dma_next) || 414 - (outs->dma_length + s->length > max_seg_size)) { 409 + (outs->dma_length + s->length > max_seg_size) || 410 + (is_span_boundary(out_entry, base_shift, 411 + seg_boundary_size, outs, s))) { 415 412 /* Can't merge: create a new segment */ 416 413 segstart = s; 417 414 outcount++; ··· 427 420 /* This is a new segment, fill entries */ 428 421 outs->dma_address = dma_addr; 429 422 outs->dma_length = slen; 423 + out_entry = entry; 430 424 } 431 425 432 426 /* Calculate next page pointer for contiguous check */