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