Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[linux] / drivers / xen / grant-table.c
index ba9f3ee..7bafa70 100644 (file)
@@ -45,6 +45,9 @@
 #include <linux/workqueue.h>
 #include <linux/ratelimit.h>
 #include <linux/moduleparam.h>
+#ifdef CONFIG_XEN_GRANT_DMA_ALLOC
+#include <linux/dma-mapping.h>
+#endif
 
 #include <xen/xen.h>
 #include <xen/interface/xen.h>
@@ -57,6 +60,7 @@
 #ifdef CONFIG_X86
 #include <asm/xen/cpuid.h>
 #endif
+#include <xen/mem-reservation.h>
 #include <asm/xen/hypercall.h>
 #include <asm/xen/interface.h>
 
@@ -769,29 +773,18 @@ void gnttab_free_auto_xlat_frames(void)
 }
 EXPORT_SYMBOL_GPL(gnttab_free_auto_xlat_frames);
 
-/**
- * gnttab_alloc_pages - alloc pages suitable for grant mapping into
- * @nr_pages: number of pages to alloc
- * @pages: returns the pages
- */
-int gnttab_alloc_pages(int nr_pages, struct page **pages)
+int gnttab_pages_set_private(int nr_pages, struct page **pages)
 {
        int i;
-       int ret;
-
-       ret = alloc_xenballooned_pages(nr_pages, pages);
-       if (ret < 0)
-               return ret;
 
        for (i = 0; i < nr_pages; i++) {
 #if BITS_PER_LONG < 64
                struct xen_page_foreign *foreign;
 
                foreign = kzalloc(sizeof(*foreign), GFP_KERNEL);
-               if (!foreign) {
-                       gnttab_free_pages(nr_pages, pages);
+               if (!foreign)
                        return -ENOMEM;
-               }
+
                set_page_private(pages[i], (unsigned long)foreign);
 #endif
                SetPagePrivate(pages[i]);
@@ -799,14 +792,30 @@ int gnttab_alloc_pages(int nr_pages, struct page **pages)
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(gnttab_alloc_pages);
+EXPORT_SYMBOL_GPL(gnttab_pages_set_private);
 
 /**
- * gnttab_free_pages - free pages allocated by gnttab_alloc_pages()
- * @nr_pages; number of pages to free
- * @pages: the pages
+ * gnttab_alloc_pages - alloc pages suitable for grant mapping into
+ * @nr_pages: number of pages to alloc
+ * @pages: returns the pages
  */
-void gnttab_free_pages(int nr_pages, struct page **pages)
+int gnttab_alloc_pages(int nr_pages, struct page **pages)
+{
+       int ret;
+
+       ret = alloc_xenballooned_pages(nr_pages, pages);
+       if (ret < 0)
+               return ret;
+
+       ret = gnttab_pages_set_private(nr_pages, pages);
+       if (ret < 0)
+               gnttab_free_pages(nr_pages, pages);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(gnttab_alloc_pages);
+
+void gnttab_pages_clear_private(int nr_pages, struct page **pages)
 {
        int i;
 
@@ -818,10 +827,114 @@ void gnttab_free_pages(int nr_pages, struct page **pages)
                        ClearPagePrivate(pages[i]);
                }
        }
+}
+EXPORT_SYMBOL_GPL(gnttab_pages_clear_private);
+
+/**
+ * gnttab_free_pages - free pages allocated by gnttab_alloc_pages()
+ * @nr_pages; number of pages to free
+ * @pages: the pages
+ */
+void gnttab_free_pages(int nr_pages, struct page **pages)
+{
+       gnttab_pages_clear_private(nr_pages, pages);
        free_xenballooned_pages(nr_pages, pages);
 }
 EXPORT_SYMBOL_GPL(gnttab_free_pages);
 
+#ifdef CONFIG_XEN_GRANT_DMA_ALLOC
+/**
+ * gnttab_dma_alloc_pages - alloc DMAable pages suitable for grant mapping into
+ * @args: arguments to the function
+ */
+int gnttab_dma_alloc_pages(struct gnttab_dma_alloc_args *args)
+{
+       unsigned long pfn, start_pfn;
+       size_t size;
+       int i, ret;
+
+       size = args->nr_pages << PAGE_SHIFT;
+       if (args->coherent)
+               args->vaddr = dma_alloc_coherent(args->dev, size,
+                                                &args->dev_bus_addr,
+                                                GFP_KERNEL | __GFP_NOWARN);
+       else
+               args->vaddr = dma_alloc_wc(args->dev, size,
+                                          &args->dev_bus_addr,
+                                          GFP_KERNEL | __GFP_NOWARN);
+       if (!args->vaddr) {
+               pr_debug("Failed to allocate DMA buffer of size %zu\n", size);
+               return -ENOMEM;
+       }
+
+       start_pfn = __phys_to_pfn(args->dev_bus_addr);
+       for (pfn = start_pfn, i = 0; pfn < start_pfn + args->nr_pages;
+                       pfn++, i++) {
+               struct page *page = pfn_to_page(pfn);
+
+               args->pages[i] = page;
+               args->frames[i] = xen_page_to_gfn(page);
+               xenmem_reservation_scrub_page(page);
+       }
+
+       xenmem_reservation_va_mapping_reset(args->nr_pages, args->pages);
+
+       ret = xenmem_reservation_decrease(args->nr_pages, args->frames);
+       if (ret != args->nr_pages) {
+               pr_debug("Failed to decrease reservation for DMA buffer\n");
+               ret = -EFAULT;
+               goto fail;
+       }
+
+       ret = gnttab_pages_set_private(args->nr_pages, args->pages);
+       if (ret < 0)
+               goto fail;
+
+       return 0;
+
+fail:
+       gnttab_dma_free_pages(args);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(gnttab_dma_alloc_pages);
+
+/**
+ * gnttab_dma_free_pages - free DMAable pages
+ * @args: arguments to the function
+ */
+int gnttab_dma_free_pages(struct gnttab_dma_alloc_args *args)
+{
+       size_t size;
+       int i, ret;
+
+       gnttab_pages_clear_private(args->nr_pages, args->pages);
+
+       for (i = 0; i < args->nr_pages; i++)
+               args->frames[i] = page_to_xen_pfn(args->pages[i]);
+
+       ret = xenmem_reservation_increase(args->nr_pages, args->frames);
+       if (ret != args->nr_pages) {
+               pr_debug("Failed to decrease reservation for DMA buffer\n");
+               ret = -EFAULT;
+       } else {
+               ret = 0;
+       }
+
+       xenmem_reservation_va_mapping_update(args->nr_pages, args->pages,
+                                            args->frames);
+
+       size = args->nr_pages << PAGE_SHIFT;
+       if (args->coherent)
+               dma_free_coherent(args->dev, size,
+                                 args->vaddr, args->dev_bus_addr);
+       else
+               dma_free_wc(args->dev, size,
+                           args->vaddr, args->dev_bus_addr);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(gnttab_dma_free_pages);
+#endif
+
 /* Handling of paged out grant targets (GNTST_eagain) */
 #define MAX_DELAY 256
 static inline void