Merge tag 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mst/vhost
[linux-2.6-block.git] / kernel / dma / swiotlb.c
index c873f9cc21460536f52edd26966a7b6cd668948d..9f5851064aea6854b426fa34ffe3ab10588666fe 100644 (file)
@@ -34,6 +34,9 @@
 #include <linux/scatterlist.h>
 #include <linux/mem_encrypt.h>
 #include <linux/set_memory.h>
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+#endif
 
 #include <asm/io.h>
 #include <asm/dma.h>
@@ -72,6 +75,11 @@ phys_addr_t io_tlb_start, io_tlb_end;
  */
 static unsigned long io_tlb_nslabs;
 
+/*
+ * The number of used IO TLB block
+ */
+static unsigned long io_tlb_used;
+
 /*
  * This is a free list describing the number of free entries available from
  * each index
@@ -385,7 +393,7 @@ void __init swiotlb_exit(void)
 }
 
 /*
- * Bounce: copy the swiotlb buffer back to the original dma location
+ * Bounce: copy the swiotlb buffer from or back to the original dma location
  */
 static void swiotlb_bounce(phys_addr_t orig_addr, phys_addr_t tlb_addr,
                           size_t size, enum dma_data_direction dir)
@@ -475,6 +483,10 @@ phys_addr_t swiotlb_tbl_map_single(struct device *hwdev,
         * request and allocate a buffer from that IO TLB pool.
         */
        spin_lock_irqsave(&io_tlb_lock, flags);
+
+       if (unlikely(nslots > io_tlb_nslabs - io_tlb_used))
+               goto not_found;
+
        index = ALIGN(io_tlb_index, stride);
        if (index >= io_tlb_nslabs)
                index = 0;
@@ -524,6 +536,7 @@ not_found:
                dev_warn(hwdev, "swiotlb buffer is full (sz: %zd bytes)\n", size);
        return DMA_MAPPING_ERROR;
 found:
+       io_tlb_used += nslots;
        spin_unlock_irqrestore(&io_tlb_lock, flags);
 
        /*
@@ -584,6 +597,8 @@ void swiotlb_tbl_unmap_single(struct device *hwdev, phys_addr_t tlb_addr,
                 */
                for (i = index - 1; (OFFSET(i, IO_TLB_SEGSIZE) != IO_TLB_SEGSIZE -1) && io_tlb_list[i]; i--)
                        io_tlb_list[i] = ++count;
+
+               io_tlb_used -= nslots;
        }
        spin_unlock_irqrestore(&io_tlb_lock, flags);
 }
@@ -651,18 +666,6 @@ bool swiotlb_map(struct device *dev, phys_addr_t *phys, dma_addr_t *dma_addr,
        return true;
 }
 
-/*
- * Return whether the given device DMA address mask can be supported
- * properly.  For example, if your device can only drive the low 24-bits
- * during bus mastering, then you would pass 0x00ffffff as the mask to
- * this function.
- */
-int
-swiotlb_dma_supported(struct device *hwdev, u64 mask)
-{
-       return __phys_to_dma(hwdev, io_tlb_end - 1) <= mask;
-}
-
 size_t swiotlb_max_mapping_size(struct device *dev)
 {
        return ((size_t)1 << IO_TLB_SHIFT) * IO_TLB_SEGSIZE;
@@ -676,3 +679,36 @@ bool is_swiotlb_active(void)
         */
        return io_tlb_end != 0;
 }
+
+#ifdef CONFIG_DEBUG_FS
+
+static int __init swiotlb_create_debugfs(void)
+{
+       struct dentry *d_swiotlb_usage;
+       struct dentry *ent;
+
+       d_swiotlb_usage = debugfs_create_dir("swiotlb", NULL);
+
+       if (!d_swiotlb_usage)
+               return -ENOMEM;
+
+       ent = debugfs_create_ulong("io_tlb_nslabs", 0400,
+                                  d_swiotlb_usage, &io_tlb_nslabs);
+       if (!ent)
+               goto fail;
+
+       ent = debugfs_create_ulong("io_tlb_used", 0400,
+                                  d_swiotlb_usage, &io_tlb_used);
+       if (!ent)
+               goto fail;
+
+       return 0;
+
+fail:
+       debugfs_remove_recursive(d_swiotlb_usage);
+       return -ENOMEM;
+}
+
+late_initcall(swiotlb_create_debugfs);
+
+#endif