]> err.no Git - linux-2.6/commitdiff
avoid endless loops in lib/swiotlb.c
authorJan Beulich <jbeulich@novell.com>
Thu, 13 Mar 2008 09:13:30 +0000 (09:13 +0000)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 13 Mar 2008 20:15:52 +0000 (13:15 -0700)
Commit 681cc5cd3efbeafca6386114070e0bfb5012e249 ("iommu sg merging:
swiotlb: respect the segment boundary limits") introduced two
possibilities for entering an endless loop in lib/swiotlb.c:

 - if max_slots is zero (possible if mask is ~0UL)
 - if the number of slots requested fits into a swiotlb segment, but is
   too large for the part of a segment which remains after considering
   offset_slots

This fixes them

Signed-off-by: Jan Beulich <jbeulich@novell.com>
Cc: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
Cc: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
lib/swiotlb.c

index 4bb5a11e18a289e6bcf3a89ca41b0a13fb0b40cc..025922807e6e648bb7dc4eda61bfd8fa609a221c 100644 (file)
@@ -310,7 +310,9 @@ map_single(struct device *hwdev, char *buffer, size_t size, int dir)
        start_dma_addr = virt_to_bus(io_tlb_start) & mask;
 
        offset_slots = ALIGN(start_dma_addr, 1 << IO_TLB_SHIFT) >> IO_TLB_SHIFT;
-       max_slots = ALIGN(mask + 1, 1 << IO_TLB_SHIFT) >> IO_TLB_SHIFT;
+       max_slots = mask + 1
+                   ? ALIGN(mask + 1, 1 << IO_TLB_SHIFT) >> IO_TLB_SHIFT
+                   : 1UL << (BITS_PER_LONG - IO_TLB_SHIFT);
 
        /*
         * For mappings greater than a page, we limit the stride (and
@@ -333,16 +335,18 @@ map_single(struct device *hwdev, char *buffer, size_t size, int dir)
                index = ALIGN(io_tlb_index, stride);
                if (index >= io_tlb_nslabs)
                        index = 0;
-
-               while (is_span_boundary(index, nslots, offset_slots,
-                                       max_slots)) {
-                       index += stride;
-                       if (index >= io_tlb_nslabs)
-                               index = 0;
-               }
                wrap = index;
 
                do {
+                       while (is_span_boundary(index, nslots, offset_slots,
+                                               max_slots)) {
+                               index += stride;
+                               if (index >= io_tlb_nslabs)
+                                       index = 0;
+                               if (index == wrap)
+                                       goto not_found;
+                       }
+
                        /*
                         * If we find a slot that indicates we have 'nslots'
                         * number of contiguous buffers, we allocate the
@@ -367,14 +371,12 @@ map_single(struct device *hwdev, char *buffer, size_t size, int dir)
 
                                goto found;
                        }
-                       do {
-                               index += stride;
-                               if (index >= io_tlb_nslabs)
-                                       index = 0;
-                       } while (is_span_boundary(index, nslots, offset_slots,
-                                                 max_slots));
+                       index += stride;
+                       if (index >= io_tlb_nslabs)
+                               index = 0;
                } while (index != wrap);
 
+  not_found:
                spin_unlock_irqrestore(&io_tlb_lock, flags);
                return NULL;
        }