Early DMA Allocator patch.

This commit is contained in:
Jeffrey Townsend
2017-01-04 23:41:42 +00:00
parent 50607a2e52
commit 830a9f7c7c
2 changed files with 336 additions and 0 deletions

View File

@@ -0,0 +1,335 @@
diff -urpN a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
--- a/arch/x86/kernel/setup.c 2016-11-20 01:17:41.000000000 +0000
+++ b/arch/x86/kernel/setup.c 2017-01-04 23:10:26.473086206 +0000
@@ -49,6 +49,7 @@
#include <asm/pci-direct.h>
#include <linux/init_ohci1394_dma.h>
#include <linux/kvm_para.h>
+#include <linux/early_dma_alloc.h>
#include <linux/dma-contiguous.h>
#include <linux/errno.h>
@@ -1243,6 +1244,10 @@ void __init setup_arch(char **cmdline_p)
arch_init_ideal_nops();
+#ifdef CONFIG_EARLY_DMA_ALLOC
+ eda_init();
+#endif
+
register_refined_jiffies(CLOCK_TICK_RATE);
#ifdef CONFIG_EFI
diff -urpN a/drivers/misc/Kconfig b/drivers/misc/Kconfig
--- a/drivers/misc/Kconfig 2016-11-20 01:17:41.000000000 +0000
+++ b/drivers/misc/Kconfig 2017-01-04 23:11:14.305087244 +0000
@@ -496,6 +496,34 @@ config USB_SWITCH_FSA9480
stereo and mono audio, video, microphone and UART data to use
a common connector port.
+config EARLY_DMA_ALLOC
+ bool "Early DMA Memory Allocator"
+ depends on HAS_DMA
+
+ ---help---
+ This driver locks down a region of DMA accessible memory
+ early in the boot process. This memory can be used by other
+ drivers that might rmmod/insmod, insuring the memory region
+ does not become fragmented.
+
+config EDA_DEF_SIZE
+ hex "EDA Default Region Size"
+ depends on EARLY_DMA_ALLOC
+ default 0x04000000
+ help
+ Default size of the reserved memory pool, if not altered by the
+ open firmware interface or kernel boot parameter. This memory
+ will not be accessable to the rest of the system. Default is
+ 64MB.
+
+config EDA_DEF_ALIGN
+ hex "EDA Default Alignment"
+ depends on EARLY_DMA_ALLOC
+ default 0x00100000
+ help
+ Default alignment of the memory region. Default is 1MB.
+
+
config LATTICE_ECP3_CONFIG
tristate "Lattice ECP3 FPGA bitstream configuration via SPI"
depends on SPI && SYSFS
diff -urpN a/drivers/misc/Makefile b/drivers/misc/Makefile
--- a/drivers/misc/Makefile 2016-11-20 01:17:41.000000000 +0000
+++ b/drivers/misc/Makefile 2017-01-04 23:12:15.781088578 +0000
@@ -56,3 +56,4 @@ obj-y += mic/
obj-$(CONFIG_GENWQE) += genwqe/
obj-$(CONFIG_ECHO) += echo/
obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
+obj-$(CONFIG_EARLY_DMA_ALLOC) += early_dma_alloc.o
diff -urpN a/drivers/misc/early_dma_alloc.c b/drivers/misc/early_dma_alloc.c
--- a/drivers/misc/early_dma_alloc.c 1970-01-01 00:00:00.000000000 +0000
+++ b/drivers/misc/early_dma_alloc.c 2017-01-04 23:13:09.209089737 +0000
@@ -0,0 +1,223 @@
+/*
+ * Early DMA Memory Allocator
+ *
+ * Copyright © 2013,2014 Cumulus Networks, Inc.
+ *
+ * Author: Curt Brune <curt@cumulusnetworks.com>
+ * Modified: Jonathan Toppins <jtoppins@cumulusnetworks.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+/*
+ * This driver allocates a region of DMA accessible memory, making it
+ * available to one other device driver.
+ *
+ * The client device driver may be unloaded and reloaded over time.
+ * This driver keeps the DMA region from becoming fragmented across
+ * module reloads.
+ *
+ * Memory Region Restrictions
+ * --------------------------
+ * The memory region allocated by EDA MUST exist below a 4GB limit. This
+ * is because EDA's primary (only at time of writing) user is the
+ * Broadcom BDE driver wich assumes a 32-bit physical address space and
+ * assumes paddr is no more than 32-bits wide. Furthermore, before porting
+ * the BDE driver to use EDA the BDE driver specifically checked if the
+ * memory region provided by highmem was less than 4GB. We assume Broadcom
+ * knew what they were doing and there is a specific reason why this 4GB
+ * limit is needed, so we enforce this limit by checking the physical address
+ * after allocation.
+ *
+ * Memory Region Size and Alignment
+ * --------------------------------
+ * This driver allows three ways for the user to define the DMA memory
+ * that will be created, listed in order of preference.
+ * 1. The user may specify on the kernel command line in the boot loader
+ * the "eda_mem" option, this option has the format "size@alignment",
+ * example: eda_mem=0x04000000@0x00100000
+ * 2. This driver looks for a device tree node compatible with
+ * "early-dma-alloc". The "region_size" property of the node contains
+ * the size, in bytes, of the desired DMA memory region. The
+ * "alignment" property contains the desired memory alignment of the
+ * region.
+ * 3. Finally if neither of the above are provided the Kbuild changable,
+ * compiled in default size and alignment will be used.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/of_platform.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/dma-mapping.h>
+#include <linux/bootmem.h>
+#include <linux/early_dma_alloc.h>
+
+#if (!defined CONFIG_EDA_DEF_SIZE) || \
+ (!defined CONFIG_EDA_DEF_ALIGN)
+#error incorrect kernel config - fix it
+#endif
+
+// #define DEBUG
+#if (defined DEBUG)
+#define eda_debug(fmt, ... ) \
+ printk(KERN_ERR "eda-debug:%s(): " fmt "\n", __func__ , \
+ ##__VA_ARGS__)
+#else
+#define eda_debug(fmt, ... )
+#endif
+
+#define eda_info(fmt, ... ) \
+ printk(KERN_INFO "eda: " fmt "\n", ##__VA_ARGS__)
+
+static uint32_t dma_size;
+static void *dma_vaddr;
+static u32 dma_align __initdata;
+static bool eda_cmdline __initdata;
+
+static int __init setup_eda_mem(char *str)
+{
+ char *endp;
+
+ dma_size = memparse(str, &endp) & PAGE_MASK;
+ if (*endp == '@')
+ dma_align = memparse(endp + 1, NULL) & PAGE_MASK;
+ eda_cmdline = true;
+ return 0;
+}
+early_param("eda_mem", setup_eda_mem);
+
+static int __init of_eda_init(uint32_t *size, u32 *align)
+#ifdef CONFIG_OF_FLATTREE
+{
+ int rc = -ENODEV;
+ struct device_node *np = NULL;
+ const u32 *region_sz_p = NULL;
+ const u32 *align_p = NULL;
+ u32 prop_sz = 0;
+
+ eda_debug("entry");
+
+ /* is a programming error make it really painful so it gets fixed */
+ BUG_ON(NULL == size || NULL == align);
+
+ np = of_find_compatible_node(NULL, NULL, "early-dma-alloc");
+ if (!np) {
+ printk(KERN_WARNING "WARN: Can not find `early-dma-alloc'"
+ " device tree node.\n");
+ goto cleanup;
+ }
+
+ region_sz_p = of_get_property(np, "region_size", &prop_sz);
+ if (!region_sz_p || (prop_sz != sizeof(*region_sz_p))) {
+ printk(KERN_ERR "ERROR: Can not find `region_size' property"
+ " in early-dma-alloc device tree node.\n");
+ goto cleanup;
+ }
+ *size = *region_sz_p;
+
+ align_p = of_get_property(np, "alignment", &prop_sz);
+ if (!align_p || (prop_sz != sizeof(*align_p))) {
+ printk(KERN_ERR "ERROR: Can not find `alignment' property in"
+ "early-dma-alloc device tree node.\n");
+ goto cleanup;
+ }
+ *align = *align_p;
+ rc = 0;
+
+ eda_debug("cleanup");
+
+cleanup:
+ of_node_put(np);
+ return rc;
+
+}
+#else
+{
+ return -ENODEV;
+}
+#endif
+
+int eda_dma_info_get(void **vaddr, uint32_t *paddr, uint32_t *size)
+{
+ eda_debug("entry");
+
+ if (!dma_vaddr)
+ return -ENOMEM;
+
+ if (!vaddr || !paddr || !size)
+ return -EINVAL;
+
+ *vaddr = dma_vaddr;
+ *paddr = (uint32_t) virt_to_phys(dma_vaddr);
+ *size = dma_size;
+
+ eda_debug("returning -- dma_vaddr: 0x%pK, dma_paddr: 0x%08x,"
+ " size: 0x%08x", *vaddr, *paddr, *size);
+
+ return 0;
+}
+EXPORT_SYMBOL(eda_dma_info_get);
+
+int __init eda_init(void)
+{
+ int rc = 0;
+
+ if (eda_cmdline) {
+ if (!dma_align)
+ dma_align = CONFIG_EDA_DEF_ALIGN;
+ if (!dma_size)
+ dma_size = CONFIG_EDA_DEF_SIZE;
+ eda_debug("size & alignment came from: kernel cmdline");
+ } else if (!of_eda_init(&dma_size, &dma_align)) {
+ eda_debug("size & alignment came from: open firmware entry");
+ } else {
+ dma_align = CONFIG_EDA_DEF_ALIGN;
+ dma_size = CONFIG_EDA_DEF_SIZE;
+ eda_debug("size & alignment came from: compiled in defaults");
+ }
+
+ dma_vaddr = __alloc_bootmem_low(dma_size, dma_align, 0);
+ /*
+ * enforce EDA's requirement to allocate the memory region below a
+ * 32-bit limit.
+ */
+ if (virt_to_phys(dma_vaddr) > 0xFFFFFFFFULL) {
+ rc = -ENOMEM;
+ printk(KERN_ERR "ERROR: DMA memory beyond 32-bit address"
+ " space not supported.\n");
+ goto cleanup;
+ }
+
+ eda_info("dma_vaddr: 0x%pK, dma_paddr: 0x%016llx, size: 0x%08x,"
+ " alignment: 0x%08x",
+ dma_vaddr, (unsigned long long) virt_to_phys(dma_vaddr),
+ dma_size, dma_align);
+cleanup:
+ if (rc && dma_vaddr) {
+ free_bootmem(dma_vaddr, dma_size);
+ }
+ if (rc) {
+ dma_vaddr = NULL;
+ dma_size = 0;
+ }
+ return rc;
+}
+EXPORT_SYMBOL(eda_init);
diff -urpN a/include/linux/early_dma_alloc.h b/include/linux/early_dma_alloc.h
--- a/include/linux/early_dma_alloc.h 1970-01-01 00:00:00.000000000 +0000
+++ b/include/linux/early_dma_alloc.h 2017-01-04 23:13:59.405090826 +0000
@@ -0,0 +1,36 @@
+/*
+ * Early DMA Memory Allocator
+ *
+ * Copyright © 2013 Cumulus Networks, Inc.
+ *
+ * Author: Curt Brune <curt@cumulusnetworks.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef EARLY_DMA_ALLOC_H__
+#define EARLY_DMA_ALLOC_H__
+
+#ifdef __KERNEL__
+
+#include <linux/types.h>
+
+extern int eda_init(void);
+extern int eda_dma_info_get(void** vaddr, uint32_t* paddr, uint32_t* size);
+
+#endif /* __KERNEL__ */
+
+#endif /* EARLY_DMA_ALLOC_H__ */

View File

@@ -16,3 +16,4 @@ driver-support-intel-igb-bcm5461X-phy.patch
driver-i2c-bus-intel-ismt-enable-param.patch
drivers-i2c-busses-i2c-isch-probe-param.patch
drivers-i2c-muxes-pca954x-deselect-on-exit.patch
driver-early-dma-allocator.patch