kernel/0102-refactor-MMZ-vb-and-heap-MMZ-code-moved-in-kernel.patch

5937 lines
162 KiB
Diff
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

From 4b762de47916d2f2cd54071cad33b163b0df8448 Mon Sep 17 00:00:00 2001
From: linmin <linmin@eswincomputing.com>
Date: Wed, 19 Jun 2024 16:23:06 +0800
Subject: [PATCH 102/219] refactor(MMZ vb and heap):MMZ code moved in kernel
Changelogs:
1.Moved es_buddy source code to drivers/memory/eswin/es_buddy
2.Moved es_proc source code to drivers/memory/eswin/es_proc
3.Moved mmz_vb source code to drivers/memory/eswin/es_mmz_vb
4.Moved es_rsvmem_heap source code to drivers/memory/eswin/es_rsvmem_heap
Signed-off-by: linmin <linmin@eswincomputing.com>
Reviewed-by: ningyu <ningyu@eswincomputing.com>
---
arch/riscv/configs/win2030_defconfig | 4 +
drivers/memory/eswin/Kconfig | 4 +
drivers/memory/eswin/Makefile | 8 +
drivers/memory/eswin/buddy.h | 44 +-
drivers/memory/eswin/es_buddy/Kconfig | 6 +
drivers/memory/eswin/es_buddy/Makefile | 3 +
.../eswin/es_buddy/buddy_allocator/buddy.c | 222 ++
drivers/memory/eswin/es_buddy/es_buddy.c | 186 ++
drivers/memory/eswin/es_buddy/es_buddy.h | 52 +
drivers/memory/eswin/es_mmz_vb/Kconfig | 6 +
drivers/memory/eswin/es_mmz_vb/Makefile | 5 +
.../eswin/es_mmz_vb/include/linux/mmz_vb.h | 58 +
.../es_mmz_vb/include/linux/mmz_vb_type.h | 17 +
drivers/memory/eswin/es_mmz_vb/mmz_vb.c | 2375 +++++++++++++++++
drivers/memory/eswin/es_proc/Kconfig | 7 +
drivers/memory/eswin/es_proc/Makefile | 7 +
drivers/memory/eswin/es_proc/es_proc.c | 233 ++
.../eswin/es_proc/include/linux/es_proc.h | 40 +
drivers/memory/eswin/es_rsvmem_heap/Kconfig | 6 +
drivers/memory/eswin/es_rsvmem_heap/Makefile | 8 +
.../dmabuf-heap-import-helper.c | 652 +++++
.../es_rsvmem_heap/eswin_rsvmem_common.c | 447 ++++
.../eswin/es_rsvmem_heap/eswin_rsvmem_heap.c | 634 +++++
.../include/linux/mem_perf_api.h | 123 +
.../include/uapi/linux/eswin_rsvmem_common.h | 56 +
include/linux/dmabuf-heap-import-helper.h | 100 +
include/linux/eswin_rsvmem_common.h | 94 +
include/uapi/linux/es_vb_user.h | 58 +
include/uapi/linux/mmz_vb.h | 175 ++
29 files changed, 5606 insertions(+), 24 deletions(-)
create mode 100644 drivers/memory/eswin/es_buddy/Kconfig
create mode 100644 drivers/memory/eswin/es_buddy/Makefile
create mode 100644 drivers/memory/eswin/es_buddy/buddy_allocator/buddy.c
create mode 100644 drivers/memory/eswin/es_buddy/es_buddy.c
create mode 100644 drivers/memory/eswin/es_buddy/es_buddy.h
create mode 100644 drivers/memory/eswin/es_mmz_vb/Kconfig
create mode 100644 drivers/memory/eswin/es_mmz_vb/Makefile
create mode 100644 drivers/memory/eswin/es_mmz_vb/include/linux/mmz_vb.h
create mode 100644 drivers/memory/eswin/es_mmz_vb/include/linux/mmz_vb_type.h
create mode 100644 drivers/memory/eswin/es_mmz_vb/mmz_vb.c
create mode 100644 drivers/memory/eswin/es_proc/Kconfig
create mode 100644 drivers/memory/eswin/es_proc/Makefile
create mode 100644 drivers/memory/eswin/es_proc/es_proc.c
create mode 100644 drivers/memory/eswin/es_proc/include/linux/es_proc.h
create mode 100644 drivers/memory/eswin/es_rsvmem_heap/Kconfig
create mode 100644 drivers/memory/eswin/es_rsvmem_heap/Makefile
create mode 100644 drivers/memory/eswin/es_rsvmem_heap/dmabuf-heap-import-helper.c
create mode 100644 drivers/memory/eswin/es_rsvmem_heap/eswin_rsvmem_common.c
create mode 100644 drivers/memory/eswin/es_rsvmem_heap/eswin_rsvmem_heap.c
create mode 100644 drivers/memory/eswin/es_rsvmem_heap/include/linux/mem_perf_api.h
create mode 100644 drivers/memory/eswin/es_rsvmem_heap/include/uapi/linux/eswin_rsvmem_common.h
create mode 100644 include/linux/dmabuf-heap-import-helper.h
create mode 100644 include/linux/eswin_rsvmem_common.h
create mode 100644 include/uapi/linux/es_vb_user.h
create mode 100644 include/uapi/linux/mmz_vb.h
diff --git a/arch/riscv/configs/win2030_defconfig b/arch/riscv/configs/win2030_defconfig
index 370b8dbe4a2e..32b31b28bdbd 100644
--- a/arch/riscv/configs/win2030_defconfig
+++ b/arch/riscv/configs/win2030_defconfig
@@ -736,6 +736,10 @@ CONFIG_RPMSG_VIRTIO=y
CONFIG_ARCH_ESWIN_EIC770X_SOC_FAMILY=y
CONFIG_EXTCON=y
CONFIG_MEMORY=y
+CONFIG_ESWIN_BUDDY=y
+CONFIG_ESWIN_PROC=y
+CONFIG_ESWIN_RSVMEM_HEAP=y
+CONFIG_ESWIN_MMZ_VB=y
CONFIG_PWM=y
CONFIG_PWM_ESWIN=y
CONFIG_RESET_ESWIN_WIN2030=y
diff --git a/drivers/memory/eswin/Kconfig b/drivers/memory/eswin/Kconfig
index 8f85e3cb466b..013e2f376dd3 100644
--- a/drivers/memory/eswin/Kconfig
+++ b/drivers/memory/eswin/Kconfig
@@ -25,5 +25,9 @@ config ESWIN_RSV_MEMBLOCK
If unsure, say "n".
source "drivers/memory/eswin/codacache/Kconfig"
+source "drivers/memory/eswin/es_buddy/Kconfig"
+source "drivers/memory/eswin/es_proc/Kconfig"
+source "drivers/memory/eswin/es_rsvmem_heap/Kconfig"
+source "drivers/memory/eswin/es_mmz_vb/Kconfig"
endif
diff --git a/drivers/memory/eswin/Makefile b/drivers/memory/eswin/Makefile
index 1b732b2a439d..3e5e09bab5dd 100644
--- a/drivers/memory/eswin/Makefile
+++ b/drivers/memory/eswin/Makefile
@@ -2,3 +2,11 @@
obj-$(CONFIG_ESWIN_MC) += eswin_cpuid_hartid_convert.o
obj-$(CONFIG_ESWIN_RSV_MEMBLOCK) += eswin_memblock.o
obj-$(CONFIG_ESWIN_CODACACHE_CONTROLLER) += codacache/
+obj-$(CONFIG_ESWIN_BUDDY) += es_buddy/
+obj-$(CONFIG_ESWIN_PROC) += es_proc/
+obj-$(CONFIG_ESWIN_RSVMEM_HEAP) += es_rsvmem_heap/
+obj-$(CONFIG_ESWIN_RSVMEM_HEAP) += es_mmz_vb/
+
+ES_MEM_HEADER := drivers/memory/eswin/
+
+COPY_HEADERS := $(shell cp $(ES_MEM_HEADER)/*.h include/linux)
\ No newline at end of file
diff --git a/drivers/memory/eswin/buddy.h b/drivers/memory/eswin/buddy.h
index 2c40d1116ad8..ff47325f2d27 100644
--- a/drivers/memory/eswin/buddy.h
+++ b/drivers/memory/eswin/buddy.h
@@ -1,3 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Header file of ESWIN internal buddy allocator
+ *
+ * Copyright 2024 Beijing ESWIN Computing Technology Co., Ltd.
+ * Authors:
+ * LinMin<linmin@eswincomputing.com>
+ *
+ */
+
#ifndef __BUDDY_H__
#define __BUDDY_H__
@@ -38,13 +48,11 @@
#define es_spin_lock(esLock)
#define es_spin_unlock(esLock)
#endif
-/*
- * <20><><EFBFBD>Page<67><65><EFBFBD><EFBFBD><EFBFBD><EFBFBD>״̬
- * */
+
enum esPageflags_e{
- enPG_head, //<2F><><EFBFBD><EFBFBD>buddyϵͳ<CFB5>ڣ<EFBFBD><DAA3>׸<EFBFBD>ҳ
- enPG_tail, //<2F><><EFBFBD><EFBFBD>buddyϵͳ<CFB5>ڣ<EFBFBD><DAA3><EFBFBD>ҳ֮<D2B3><D6AE><EFBFBD>ҳ<EFBFBD><D2B3>
- enPG_buddy, //<2F><>buddyϵͳ<CFB5><CDB3>
+ enPG_head,
+ enPG_tail,
+ enPG_buddy,
};
#define BUDDY_PAGE_SHIFT PAGE_SHIFT//(12UL)
@@ -110,11 +118,7 @@ void buddy_free_pages(struct mem_zone *zone,
struct esPage_s *page);
unsigned long buddy_num_free_page(struct mem_zone *zone);
-/*
- * ҳ<><D2B3>Ϊ<EFBFBD><CEAA><EFBFBD>һ<E0A3BA><D2BB><EFBFBD>ǵ<EFBFBD>ҳ<EFBFBD><D2B3>zero page<67><65>,
- * һ<><D2BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ҳ<EFBFBD><D2B3>compound page<67><65><EFBFBD><EFBFBD>
- * <20><><EFBFBD>ҳ<EFBFBD>ĵ<EFBFBD>һ<EFBFBD><D2BB><EFBFBD><EFBFBD>head<61><64><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊtail<69><6C>
- * */
+
static inline void __esSetPageHead(struct esPage_s *page)
{
page->flags |= (1UL<<enPG_head);
@@ -160,9 +164,7 @@ static inline int esPageBuddy(struct esPage_s *page)
return (page->flags & (1UL<<enPG_buddy));
}
-/*
- * <20><><EFBFBD><EFBFBD>ҳ<EFBFBD><D2B3>order<65><72>PG_buddy<64><79>־
- * */
+
static inline void set_page_order_buddy(struct esPage_s *page, unsigned long order)
{
page->order = order;
@@ -175,9 +177,7 @@ static inline void rmv_page_order_buddy(struct esPage_s *page)
__esClearPageBuddy(page);
}
-/*
- * <20><><EFBFBD><EFBFBD>buddyҳ
- * */
+
static inline unsigned long
__find_buddy_index(unsigned long page_idx, unsigned int order)
{
@@ -190,21 +190,17 @@ __find_combined_index(unsigned long page_idx, unsigned int order)
return (page_idx & ~(1 << order));
}
-/*
- * Linux<75>ں˽<DABA><CBBD><EFBFBD><EFBFBD>ҳ<EFBFBD><D2B3>order<65><72>¼<EFBFBD>ڵڶ<DAB5><DAB6><EFBFBD>ҳ<EFBFBD><D2B3><EFBFBD>prevָ<76><D6B8><EFBFBD><EFBFBD>
- * <20><>ϵͳ<CFB5><CDB3><EFBFBD><EFBFBD><EFBFBD>ҳ<EFBFBD><D2B3>order<65><72>¼<EFBFBD><C2BC><EFBFBD>׸<EFBFBD>ҳ<EFBFBD><D2B3><EFBFBD>page->order<65><72><EFBFBD><EFBFBD>
- * */
+
static inline unsigned long esCompound_order(struct esPage_s *page)
{
if (!esPageHead(page))
- return 0; //<2F><>ҳ
- //return (unsigned long)page[1].lru.prev;
+ return 0;
+
return page->order;
}
static inline void esSet_compound_order(struct esPage_s *page, unsigned long order)
{
- //page[1].lru.prev = (void *)order;
page->order = order;
}
diff --git a/drivers/memory/eswin/es_buddy/Kconfig b/drivers/memory/eswin/es_buddy/Kconfig
new file mode 100644
index 000000000000..94d755f4ba86
--- /dev/null
+++ b/drivers/memory/eswin/es_buddy/Kconfig
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config ESWIN_BUDDY
+ tristate "ESWIN buddy allocator for reserved memory"
+ help
+ buddy allocator api.
diff --git a/drivers/memory/eswin/es_buddy/Makefile b/drivers/memory/eswin/es_buddy/Makefile
new file mode 100644
index 000000000000..4115fd0901d7
--- /dev/null
+++ b/drivers/memory/eswin/es_buddy/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_ESWIN_BUDDY) += es_buddy.o buddy_allocator/buddy.o
+
diff --git a/drivers/memory/eswin/es_buddy/buddy_allocator/buddy.c b/drivers/memory/eswin/es_buddy/buddy_allocator/buddy.c
new file mode 100644
index 000000000000..56f084d0fc46
--- /dev/null
+++ b/drivers/memory/eswin/es_buddy/buddy_allocator/buddy.c
@@ -0,0 +1,222 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Internal APIs for ESWIN buddy allocator
+ *
+ * Copyright 2024 Beijing ESWIN Computing Technology Co., Ltd.
+ * Authors:
+ * LinMin<linmin@eswincomputing.com>
+ *
+ */
+
+#include "../../buddy.h"
+
+#define SIZE_4GB 0x100000000
+static int is_4G_boundary_page(struct mem_block *memblock, unsigned long page_idx)
+{
+ struct page *kpage;
+
+ kpage = memblock->kPageStart + page_idx;
+ if (!((page_to_phys(kpage) + BUDDY_PAGE_SIZE) % SIZE_4GB))
+ return 1;
+
+ return 0;
+}
+
+void buddy_system_init(struct mem_block *memblock,
+ struct esPage_s *start_page,
+ unsigned long start_addr,
+ unsigned long page_num)
+{
+ unsigned long i;
+ struct esPage_s *page = NULL;
+ struct es_free_area_s *area = NULL;
+ // init memory zone
+ struct mem_zone *zone = &memblock->zone;
+ zone->page_num = page_num;
+ zone->page_size = BUDDY_PAGE_SIZE;
+ zone->first_page = start_page;
+ zone->start_addr = start_addr;
+ zone->end_addr = start_addr + page_num * BUDDY_PAGE_SIZE;
+ // TODO: init zone->lock
+ #ifdef RUN_IN_KERNEL
+ buddy_spin_lock_init(&zone->lock);
+ #endif
+ // init each area
+ for (i = 0; i < BUDDY_MAX_ORDER; i++)
+ {
+ area = zone->free_area + i;
+ INIT_LIST_HEAD(&area->free_list);
+ area->nr_free = 0;
+ }
+ memset(start_page, 0, page_num * sizeof(struct esPage_s));
+ // init and free each page
+ for (i = 0; i < page_num; i++)
+ {
+ page = zone->first_page + i;
+ INIT_LIST_HEAD(&page->lru);
+ /* Reserve 4kB at (4GB-4k) alignment address boundary. This is a workaround for g2d.
+ The g2d hardware has problem with accessing the 4GB alignment address boundray,
+ such as the address at 4GB, 8GB, 12GB and 16GB.
+ */
+ if (is_4G_boundary_page(memblock, i)) {
+ memblock->page_num--;
+ continue;
+ }
+
+ buddy_free_pages(zone, page);
+ }
+}
+
+static void prepare_compound_pages(struct esPage_s *page, unsigned long order)
+{
+ unsigned long i;
+ unsigned long nr_pages = (1UL<<order);
+
+ esSet_compound_order(page, order);
+ __esSetPageHead(page);
+ for(i = 1; i < nr_pages; i++)
+ {
+ struct esPage_s *p = page + i;
+ __SetPageTail(p);
+ p->first_page = page;
+ }
+}
+
+static void expand(struct mem_zone *zone, struct esPage_s *page,
+ unsigned long low_order, unsigned long high_order,
+ struct es_free_area_s *area)
+{
+ unsigned long size = (1U << high_order);
+ while (high_order > low_order)
+ {
+ area--;
+ high_order--;
+ size >>= 1;
+ list_add(&page[size].lru, &area->free_list);
+ area->nr_free++;
+ // set page order
+ set_page_order_buddy(&page[size], high_order);
+ }
+}
+
+static struct esPage_s *__alloc_page(unsigned long order,
+ struct mem_zone *zone)
+{
+ struct esPage_s *page = NULL;
+ struct es_free_area_s *area = NULL;
+ unsigned long current_order = 0;
+
+ for (current_order = order;
+ current_order < BUDDY_MAX_ORDER; current_order++)
+ {
+ area = zone->free_area + current_order;
+ if (list_empty(&area->free_list)) {
+ continue;
+ }
+ // remove closest size page
+ page = list_entry(area->free_list.next, struct esPage_s, lru);
+ list_del(&page->lru);
+ rmv_page_order_buddy(page);
+ area->nr_free--;
+ // expand to lower order
+ expand(zone, page, order, current_order, area);
+ // compound page
+ if (order > 0)
+ prepare_compound_pages(page, order);
+ else // single page
+ page->order = 0;
+ return page;
+ }
+ return NULL;
+}
+
+struct esPage_s *buddy_get_pages(struct mem_zone *zone,
+ unsigned long order)
+{
+ struct esPage_s *page = NULL;
+
+ if (order >= BUDDY_MAX_ORDER)
+ {
+ BUDDY_BUG(__FILE__, __LINE__);
+ return NULL;
+ }
+ //TODO: lock zone->lock
+ buddy_spin_lock(&zone->lock);
+ page = __alloc_page(order, zone);
+ //TODO: unlock zone->lock
+ buddy_spin_unlock(&zone->lock);
+ return page;
+}
+
+static int destroy_compound_pages(struct esPage_s *page, unsigned long order)
+{
+ int bad = 0;
+ unsigned long i;
+ unsigned long nr_pages = (1UL<<order);
+
+ __esClearPageHead(page);
+ for(i = 1; i < nr_pages; i++)
+ {
+ struct esPage_s *p = page + i;
+ if( !esPageTail(p) || p->first_page != page )
+ {
+ bad++;
+ BUDDY_BUG(__FILE__, __LINE__);
+ }
+ __ClearPageTail(p);
+ }
+ return bad;
+}
+
+#define PageCompound(page) \
+ (page->flags & ((1UL<<enPG_head)|(1UL<<enPG_tail)))
+
+#define page_is_buddy(page,order) \
+ (esPageBuddy(page) && (page->order == order))
+
+void buddy_free_pages(struct mem_zone *zone,
+ struct esPage_s *page)
+{
+ unsigned long order = esCompound_order(page);
+ unsigned long buddy_idx = 0, combinded_idx = 0;
+ unsigned long page_idx = page - zone->first_page;
+ //TODO: lock zone->lock
+ buddy_spin_lock(&zone->lock);
+ if (PageCompound(page))
+ if (destroy_compound_pages(page, order))
+ BUDDY_BUG(__FILE__, __LINE__);
+
+ while (order < BUDDY_MAX_ORDER-1)
+ {
+ struct esPage_s *buddy;
+ // find and delete buddy to combine
+ buddy_idx = __find_buddy_index(page_idx, order);
+ buddy = page + (buddy_idx - page_idx);
+ if (!page_is_buddy(buddy, order))
+ break;
+ list_del(&buddy->lru);
+ zone->free_area[order].nr_free--;
+ // remove buddy's flag and order
+ rmv_page_order_buddy(buddy);
+ // update page and page_idx after combined
+ combinded_idx = __find_combined_index(page_idx, order);
+ page = page + (combinded_idx - page_idx);
+ page_idx = combinded_idx;
+ order++;
+ }
+ set_page_order_buddy(page, order);
+ list_add(&page->lru, &zone->free_area[order].free_list);
+ zone->free_area[order].nr_free++;
+ //TODO: unlock zone->lock
+ buddy_spin_unlock(&zone->lock);
+}
+
+unsigned long buddy_num_free_page(struct mem_zone *zone)
+{
+ unsigned long i, ret;
+ for (i = 0, ret = 0; i < BUDDY_MAX_ORDER; i++)
+ {
+ ret += zone->free_area[i].nr_free * (1UL<<i);
+ }
+ return ret;
+}
diff --git a/drivers/memory/eswin/es_buddy/es_buddy.c b/drivers/memory/eswin/es_buddy/es_buddy.c
new file mode 100644
index 000000000000..bc05cf473af6
--- /dev/null
+++ b/drivers/memory/eswin/es_buddy/es_buddy.c
@@ -0,0 +1,186 @@
+/*
+ * ESWIN buddy allocator.
+ * eswin_rsvmem initializes the reserved memory in dst that has compatible = "eswin-reserve-memory" and
+ * no-map property. Each of these memory region will be treated as one memory block and managed by eswin
+ * buddy system. Users can allocate/frree pages from/to these memory blocks via es_alloc_pages/es_free_pages.
+ *
+ * Copyright 2024 Beijing ESWIN Computing Technology Co., Ltd.
+ * Authors:
+ * LinMin<linmin@eswincomputing.com>
+ *
+ */
+
+#define pr_fmt(fmt) "eswin_buddy: " fmt
+
+#include <linux/memblock.h>
+#include <linux/err.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/log2.h>
+#include <linux/highmem.h>
+#include <linux/io.h>
+#include <linux/kmemleak.h>
+
+#include "../eswin_memblock.h"
+#include "es_buddy.h"
+
+
+static void es_buddy_system_init(struct mem_block *memblock,
+ unsigned long start_addr,
+ unsigned long page_num)
+{
+ struct esPage_s *start_page = memblock->esPagesStart;
+
+ es_spin_lock_init(&memblock->esLock);
+ buddy_system_init(memblock, start_page, start_addr, page_num);
+}
+
+struct page *es_alloc_pages(struct mem_block *memblock,
+ unsigned long order)
+{
+ struct esPage_s *page;
+ struct page *kpage;
+ struct mem_zone *zone = &memblock->zone;
+ unsigned long page_idx;
+
+ es_spin_lock(&memblock->esLock);
+ page = buddy_get_pages(zone, order);
+ if (NULL == page) {
+ es_spin_unlock(&memblock->esLock);
+ return NULL;
+ }
+
+ page_idx = page - zone->first_page;
+ kpage = memblock->kPageStart + page_idx;
+
+ if (order > 0) {
+ __SetPageHead(kpage);
+ set_compound_order(kpage, order);
+ }
+ es_spin_unlock(&memblock->esLock);
+
+ buddy_print("%s:input order=%ld, esCompound_order(page)=%ld, kCompound_order(kpage)=%d, page_size(kpage)=0x%lx, phys_addr=0x%llx\n",
+ __func__, order, esCompound_order(page), compound_order(kpage), page_size(kpage), page_to_phys(kpage));
+ return kpage;
+}
+EXPORT_SYMBOL(es_alloc_pages);
+
+void es_free_pages(struct mem_block *memblock,
+ struct page *kpage)
+{
+ struct mem_zone *zone = &memblock->zone;
+ unsigned long page_idx = kpage - memblock->kPageStart;
+ struct esPage_s *page = zone->first_page + page_idx;
+ unsigned long order = esCompound_order(page);
+
+ buddy_print("%s:esCompound_order(page)=%ld, kCompound_order(kpage)=%d, page_idx=0x%lx, page_size(kpage)=0x%lx, phys_addr=0x%llx\n",
+ __func__, esCompound_order(page), compound_order(kpage), page_idx, page_size(kpage), page_to_phys(kpage));
+ es_spin_lock(&memblock->esLock);
+ buddy_free_pages(zone, page);
+
+ if (order > 0) {
+ ClearPageHead(kpage);
+ }
+ es_spin_unlock(&memblock->esLock);
+
+}
+EXPORT_SYMBOL(es_free_pages);
+
+unsigned long es_num_free_pages(struct mem_block *memblock)
+{
+ struct mem_zone *zone = &memblock->zone;
+
+ return buddy_num_free_page(zone);
+}
+EXPORT_SYMBOL(es_num_free_pages);
+
+void *es_page_to_virt(struct mem_zone *zone,
+ struct esPage_s *page)
+{
+ unsigned long page_idx = 0;
+ unsigned long address = 0;
+
+ page_idx = page - zone->first_page;
+ address = zone->start_addr + page_idx * BUDDY_PAGE_SIZE;
+
+ return (void *)address;
+}
+
+struct esPage_s *es_virt_to_page(struct mem_zone *zone, void *ptr)
+{
+ unsigned long page_idx = 0;
+ struct esPage_s *page = NULL;
+ unsigned long address = (unsigned long)ptr;
+
+ if((address<zone->start_addr)||(address>zone->end_addr))
+ {
+ buddy_print("start_addr=0x%lx, end_addr=0x%lx, address=0x%lx\n",
+ zone->start_addr, zone->end_addr, address);
+ BUDDY_BUG(__FILE__, __LINE__);
+ return NULL;
+ }
+ page_idx = (address - zone->start_addr)>>BUDDY_PAGE_SHIFT;
+
+ page = zone->first_page + page_idx;
+ return page;
+}
+
+static int do_rsvmem_buddy_init(struct mem_block *memblock, void *data)
+{
+ int pages_size;
+
+ pr_debug("eswin buddy init for %s\n", memblock->name);
+ /* alloc esPage_s for all the pages to manage the pages*/
+ pages_size = memblock->page_num * sizeof(struct esPage_s);
+ memblock->esPagesStart = (struct esPage_s*)vmalloc(pages_size);
+ if (!memblock->esPagesStart) {
+ pr_err("%s:%d, failed to buddy init for %s\n",
+ __func__, __LINE__, memblock->name);
+ return -ENOMEM;
+ }
+ es_buddy_system_init(memblock, 0, memblock->page_num);
+
+ return 0;
+}
+static int eswin_rsvmem_buddy_init(void)
+{
+ int ret = 0;
+
+ ret = eswin_rsvmem_for_each_block(do_rsvmem_buddy_init, NULL);
+
+ return ret;
+}
+
+static int do_rsvmem_buddy_uninit(struct mem_block *memblock, void *data)
+{
+ unsigned long numFreePages = 0;
+
+ if (NULL == memblock->esPagesStart)
+ return 0;
+
+ numFreePages = es_num_free_pages(memblock);
+ pr_debug("%s: free mem=0x%lx\n",
+ memblock->name, numFreePages<<PAGE_SHIFT);
+ if (numFreePages == memblock->zone.page_num) {
+ vfree((void*)memblock->esPagesStart);
+ }
+ else {
+ pr_err("%s: %ld outof %ld pages still in use, skip destroy memblock!\n",
+ memblock->name, numFreePages, memblock->zone.page_num);
+
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static void __exit eswin_rsvmem_buddy_uninit(void)
+{
+ eswin_rsvmem_for_each_block(do_rsvmem_buddy_uninit, NULL);
+}
+
+subsys_initcall(eswin_rsvmem_buddy_init);
+module_exit(eswin_rsvmem_buddy_uninit);
+MODULE_LICENSE("GPL v2");
\ No newline at end of file
diff --git a/drivers/memory/eswin/es_buddy/es_buddy.h b/drivers/memory/eswin/es_buddy/es_buddy.h
new file mode 100644
index 000000000000..7861a6cbcab8
--- /dev/null
+++ b/drivers/memory/eswin/es_buddy/es_buddy.h
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Header file of ESWIN buddy allocator
+ *
+ * Copyright 2024 Beijing ESWIN Computing Technology Co., Ltd.
+ * Authors:
+ * LinMin<linmin@eswincomputing.com>
+ *
+ */
+
+#ifndef __ESWIN_BUDDY_H__
+#define __ESWIN_BUDDY_H__
+
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/version.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,6,0)
+#include <linux/mm_types.h>
+#include <asm/atomic.h>
+#endif
+#include <linux/numa.h>
+#include "../buddy.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,6,0)
+static inline void folio_set_order(struct folio *folio, unsigned int order)
+{
+ if (WARN_ON_ONCE(!order || !folio_test_large(folio)))
+ return;
+
+ folio->_flags_1 = (folio->_flags_1 & ~0xffUL) | order;
+#ifdef CONFIG_64BIT
+ folio->_folio_nr_pages = 1U << order;
+#endif
+}
+
+static inline void prep_compound_head(struct page *page, unsigned int order)
+{
+ struct folio *folio = (struct folio *)page;
+
+ folio_set_order(folio, order);
+ atomic_set(&folio->_entire_mapcount, -1);
+ atomic_set(&folio->_nr_pages_mapped, 0);
+ atomic_set(&folio->_pincount, 0);
+}
+#define set_compound_order(kpage, order) prep_compound_head(kpage, order)
+#endif
+
+extern struct page *es_alloc_pages(struct mem_block *memblock, unsigned long order);
+extern void es_free_pages(struct mem_block *memblock, struct page *kpage);
+extern unsigned long es_num_free_pages(struct mem_block *memblock);
+
+#endif
diff --git a/drivers/memory/eswin/es_mmz_vb/Kconfig b/drivers/memory/eswin/es_mmz_vb/Kconfig
new file mode 100644
index 000000000000..e290be281abe
--- /dev/null
+++ b/drivers/memory/eswin/es_mmz_vb/Kconfig
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config ESWIN_MMZ_VB
+ tristate "ESWIN MMZ reserved memory VB"
+ help
+ ESWIN MMZ reserved memory VB device.
diff --git a/drivers/memory/eswin/es_mmz_vb/Makefile b/drivers/memory/eswin/es_mmz_vb/Makefile
new file mode 100644
index 000000000000..fe2bfe6291b1
--- /dev/null
+++ b/drivers/memory/eswin/es_mmz_vb/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_ESWIN_MMZ_VB) += mmz_vb.o
+
+
+
diff --git a/drivers/memory/eswin/es_mmz_vb/include/linux/mmz_vb.h b/drivers/memory/eswin/es_mmz_vb/include/linux/mmz_vb.h
new file mode 100644
index 000000000000..e167311a238d
--- /dev/null
+++ b/drivers/memory/eswin/es_mmz_vb/include/linux/mmz_vb.h
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2024 Beijing ESWIN Computing Technology Co., Ltd.
+ * Authors:
+ * LinMin<linmin@eswincomputing.com>
+ *
+ */
+
+#ifndef __MMZ_VB_H__
+#define __MMZ_VB_H__
+
+#include <linux/types.h>
+#include "../../../eswin_memblock.h"
+#include "../../../es_buddy/es_buddy.h"
+
+
+/**
+ * select whether the block will be memset to 0 while exporting it as dmabuf.
+ * 1: memset
+ * 0: do NOT memset
+*/
+// #define MMZ_VB_DMABUF_MEMSET 1
+
+#define MMZ_VB_VALID_FD_FLAGS (O_CLOEXEC | O_ACCMODE)
+
+#define VB_K_POOL_MAX_ID INT_MAX
+
+/* one block info organized in kernel */
+typedef struct esVB_K_BLOCK_INFO_S {
+ struct page *cma_pages;
+ struct sg_table sg_table; // for buddy allocator
+ struct esVB_K_POOL_INFO_S *pool;
+ int nr;
+}VB_K_BLOCK_INFO_S;
+
+/* one pool info organized in kernel */
+typedef struct esVB_K_POOL_INFO_S {
+ s32 poolId;
+ struct esVB_POOL_CONFIG_S poolCfg;
+ unsigned long *bitmap; // used for block get/release managment
+ struct esVB_K_BLOCK_INFO_S *blocks; // point to the block array
+ struct esVB_K_MMZ_S *partitions; // poiont to the partitions
+ struct hlist_node node;
+ spinlock_t lock;
+ enum esVB_UID_E enVbUid;
+ unsigned long flag;
+}VB_K_POOL_INFO_S;
+
+/* MMZs info in kernel */
+typedef struct esVB_K_MMZ_S {
+ u32 partCnt;
+ // pool is found via idr api
+ struct idr pool_idr;
+ struct rw_semaphore idr_lock;
+ struct mem_block *mem_blocks[ES_VB_MAX_MMZs];
+}VB_K_MMZ_S;
+
+#endif
diff --git a/drivers/memory/eswin/es_mmz_vb/include/linux/mmz_vb_type.h b/drivers/memory/eswin/es_mmz_vb/include/linux/mmz_vb_type.h
new file mode 100644
index 000000000000..8adecbbc4d6b
--- /dev/null
+++ b/drivers/memory/eswin/es_mmz_vb/include/linux/mmz_vb_type.h
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2024 Beijing ESWIN Computing Technology Co., Ltd.
+ * Authors:
+ * LinMin<linmin@eswincomputing.com>
+ *
+ */
+
+#ifndef __MMZ_VB_TYPE_H__
+#define __MMZ_VB_TYPE_H__
+
+typedef unsigned int ES_U32;
+typedef unsigned long long ES_U64;
+typedef char ES_CHAR;
+typedef ES_U32 VB_POOL;
+
+#endif
diff --git a/drivers/memory/eswin/es_mmz_vb/mmz_vb.c b/drivers/memory/eswin/es_mmz_vb/mmz_vb.c
new file mode 100644
index 000000000000..2c2f829817ad
--- /dev/null
+++ b/drivers/memory/eswin/es_mmz_vb/mmz_vb.c
@@ -0,0 +1,2375 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ESWIN MMZ VB driver. MMZ VB stands for Media Memory Zone Video Buffer.
+ *
+ * Copyright 2024 Beijing ESWIN Computing Technology Co., Ltd.
+ * Authors:
+ * LinMin<linmin@eswincomputing.com>
+ *
+ */
+
+#include <linux/dma-buf.h>
+#include <linux/dma-heap.h>
+#include <linux/dma-map-ops.h>
+#include <linux/highmem.h>
+#include <linux/bitfield.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include <linux/dmaengine.h>
+#include <linux/uaccess.h> /* For copy_to_user/put_user/... */
+#include <linux/of_address.h>
+#include <crypto/hash.h>
+#include <linux/delay.h>
+#include <linux/hashtable.h>
+#include <linux/es_proc.h>
+#include <linux/dmabuf-heap-import-helper.h>
+#include "include/linux/mmz_vb_type.h" /*must include before es_vb_user.h*/
+#include <uapi/linux/es_vb_user.h>
+#include <uapi/linux/mmz_vb.h>
+#include "include/linux/mmz_vb.h"
+
+MODULE_IMPORT_NS(DMA_BUF);
+
+#define DRIVER_NAME "mmz_vb"
+#define MMZ_VB_DMABUF_NAME "mmz_vb_dmabuf"
+#define MMZ_VB_DMABUF_SPLITTED_NAME "mmz_vb_dmabuf_splitted"
+
+#define vb_fmt(fmt) "[%s-MMZ_VB]: " fmt
+#define info_fmt(fmt) vb_fmt("%s[%d]: " fmt), "INFO", \
+ __func__, __LINE__
+#define dbg_fmt(fmt) vb_fmt("%s[%d]: " fmt), "DEBUG", \
+ __func__, __LINE__
+#define err_fmt(fmt) vb_fmt("%s[%d]: " fmt), "ERROR", \
+ __func__, __LINE__
+
+#define vb_info(fmt, args...) \
+ do { \
+ printk(KERN_INFO info_fmt(fmt), ##args); \
+ } while (0)
+
+#define vb_debug(fmt, args...) \
+ do { \
+ printk(KERN_DEBUG dbg_fmt(fmt), ##args); \
+ } while (0)
+#define vb_err(fmt, args...) \
+ do { \
+ printk(KERN_ERR err_fmt(fmt), ##args); \
+ } while (0)
+
+static struct device *mmz_vb_dev;
+static struct mmz_vb_priv *g_mmz_vb_priv = NULL;
+
+struct mmz_vb_priv {
+ struct device *dev;
+ struct esVB_K_MMZ_S partitions;
+ atomic_t allocBlkcnt; /*total block allocated*/
+ struct hlist_head ht[VB_UID_MAX][ES_VB_MAX_MOD_POOL];
+ struct rw_semaphore pool_lock[VB_UID_MAX];
+ unsigned long cfg_flag[VB_UID_MAX]; /*flag for pVbConfig*/
+ struct esVB_CONFIG_S *pVbConfig[VB_UID_MAX];
+ struct mutex cfg_lock[VB_UID_MAX];
+};
+
+#define do_vb_pool_size(pPool) (pPool->poolCfg.blkCnt * pPool->poolCfg.blkSize)
+static int vb_find_pool_by_id_unlock(VB_POOL poolId, struct esVB_K_POOL_INFO_S **ppPool);
+static int vb_find_pool_by_id(VB_POOL poolId, struct esVB_K_POOL_INFO_S **ppPool);
+static int vb_pool_size(VB_POOL poolId, u64 *pPoolSize);
+// static int vb_flush_pool(struct esVB_FLUSH_POOL_CMD_S *flushPoolCmd);
+static int vb_get_block(struct esVB_GET_BLOCK_CMD_S *getBlkCmd, struct esVB_K_BLOCK_INFO_S **ppBlk);
+static void vb_release_block(struct esVB_K_BLOCK_INFO_S *pBlk);
+static int vb_pool_get_free_block_cnt_unlock(struct esVB_K_POOL_INFO_S *pool);
+static int vb_is_splitted_blk(int fd, bool *isSplittedBlk);
+static int vb_blk_to_pool(struct esVB_BLOCK_TO_POOL_CMD_S *blkToPoolCmd);
+static int vb_get_blk_offset(struct esVB_GET_BLOCKOFFSET_CMD_S *getBlkOffsetCmd);
+static int vb_split_dmabuf(struct esVB_SPLIT_DMABUF_CMD_S *splitDmabufCmd);
+static int vb_get_dmabuf_refcnt(struct esVB_DMABUF_REFCOUNT_CMD_S *getDmabufRefCntCmd);
+static int vb_retrieve_mem_node(struct esVB_RETRIEVE_MEM_NODE_CMD_S *retrieveMemNodeCmd);
+static int vb_get_dmabuf_size(struct esVB_DMABUF_SIZE_CMD_S *getDmabufSizeCmd);
+static int mmz_vb_pool_exit(void);
+static int mmz_vb_init_memory_region(void);
+
+/**
+ * vb dmabuf releated struct
+ *
+ */
+struct mmz_vb_buffer {
+ struct esVB_K_BLOCK_INFO_S *pBlk;
+ struct list_head attachments;
+ struct mutex lock;
+ unsigned long len;
+ struct sg_table *table; // for buddy allocator
+ struct page **pages;
+ int vmap_cnt;
+ void *vaddr;
+};
+
+struct mmz_vb_attachment {
+ struct device *dev;
+ struct sg_table *table;
+ struct list_head list;
+ bool mapped;
+};
+
+static struct sg_table *dup_sg_table(struct sg_table *table)
+{
+ struct sg_table *new_table;
+ int ret, i;
+ struct scatterlist *sg, *new_sg;
+
+ new_table = kzalloc(sizeof(*new_table), GFP_KERNEL);
+ if (!new_table)
+ return ERR_PTR(-ENOMEM);
+
+ ret = sg_alloc_table(new_table, table->orig_nents, GFP_KERNEL);
+ if (ret) {
+ kfree(new_table);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ new_sg = new_table->sgl;
+ for_each_sgtable_sg(table, sg, i) {
+ sg_set_page(new_sg, sg_page(sg), sg->length, sg->offset);
+ new_sg = sg_next(new_sg);
+ }
+
+ return new_table;
+}
+
+static int mmz_vb_attach(struct dma_buf *dmabuf,
+ struct dma_buf_attachment *attachment)
+{
+ struct mmz_vb_buffer *buffer = dmabuf->priv;
+ struct mmz_vb_attachment *a;
+ struct sg_table *table;
+
+ a = kzalloc(sizeof(*a), GFP_KERNEL);
+ if (!a)
+ return -ENOMEM;
+
+ table = dup_sg_table(buffer->table);
+ if (IS_ERR(table)) {
+ kfree(a);
+ return -ENOMEM;
+ }
+
+ a->table = table;
+ a->dev = attachment->dev;
+ INIT_LIST_HEAD(&a->list);
+ a->mapped = false;
+
+ attachment->priv = a;
+
+ mutex_lock(&buffer->lock);
+ list_add(&a->list, &buffer->attachments);
+ mutex_unlock(&buffer->lock);
+
+ return 0;
+}
+
+static void mmz_vb_detach(struct dma_buf *dmabuf,
+ struct dma_buf_attachment *attachment)
+{
+ struct mmz_vb_buffer *buffer = dmabuf->priv;
+ struct mmz_vb_attachment *a = attachment->priv;
+
+ mutex_lock(&buffer->lock);
+ list_del(&a->list);
+ mutex_unlock(&buffer->lock);
+
+ sg_free_table(a->table);
+ kfree(a->table);
+ kfree(a);
+}
+
+static struct sg_table *mmz_vb_map_dma_buf(struct dma_buf_attachment *attachment,
+ enum dma_data_direction direction)
+{
+ struct mmz_vb_attachment *a = attachment->priv;
+ struct sg_table *table =a->table;
+ int ret;
+
+ /* Skipt cache sync, since it takes a lot of time when import to device.
+ * It's the user's responsibility for guaranteeing the cache coherency by
+ flusing cache explicitly before importing to device.
+ */
+ ret = dma_map_sgtable(attachment->dev, table, direction, DMA_ATTR_SKIP_CPU_SYNC);
+
+ if (ret)
+ return ERR_PTR(-ENOMEM);
+ a->mapped = true;
+ return table;
+}
+
+static void mmz_vb_unmap_dma_buf(struct dma_buf_attachment *attachment,
+ struct sg_table *table,
+ enum dma_data_direction direction)
+{
+ struct mmz_vb_attachment *a = attachment->priv;
+
+ a->mapped = false;
+
+ /* Skipt cache sync, since it takes a lot of time when unmap from device.
+ * It's the user's responsibility for guaranteeing the cache coherency after
+ the device has done processing the data.(For example, CPU do NOT read untill
+ the device has done)
+ */
+ dma_unmap_sgtable(attachment->dev, table, direction, DMA_ATTR_SKIP_CPU_SYNC);
+}
+
+static int mmz_vb_dma_buf_begin_cpu_access(struct dma_buf *dmabuf,
+ enum dma_data_direction direction)
+{
+ struct mmz_vb_buffer *buffer = dmabuf->priv;
+ struct sg_table *table = buffer->table;
+ struct scatterlist *sg;
+ int i;
+
+ mutex_lock(&buffer->lock);
+
+ if (buffer->vmap_cnt)
+ invalidate_kernel_vmap_range(buffer->vaddr, buffer->len);
+
+ /* Since the cache sync was skipped when mmz_vb_map_dma_buf/mmz_vb_unmap_dma_buf,
+ So force cache sync here when user call ES_SYS_MemFlushCache, eventhough there
+ is no device attached to this dmabuf.
+ */
+ #ifndef QEMU_DEBUG
+ for_each_sg(table->sgl, sg, table->orig_nents, i)
+ arch_sync_dma_for_cpu(sg_phys(sg), sg->length, direction);
+ #endif
+ mutex_unlock(&buffer->lock);
+
+ return 0;
+}
+
+static int mmz_vb_dma_buf_end_cpu_access(struct dma_buf *dmabuf,
+ enum dma_data_direction direction)
+{
+ struct mmz_vb_buffer *buffer = dmabuf->priv;
+ struct sg_table *table = buffer->table;
+ struct scatterlist *sg;
+ int i;
+
+ mutex_lock(&buffer->lock);
+
+ if (buffer->vmap_cnt)
+ flush_kernel_vmap_range(buffer->vaddr, buffer->len);
+
+ /* Since the cache sync was skipped while mmz_vb_map_dma_buf/mmz_vb_unmap_dma_buf,
+ So force cache sync here when user call ES_SYS_MemFlushCache, eventhough there
+ is no device attached to this dmabuf.
+ */
+ #ifndef QEMU_DEBUG
+ for_each_sg(table->sgl, sg, table->orig_nents, i)
+ arch_sync_dma_for_device(sg_phys(sg), sg->length, direction);
+ #endif
+ mutex_unlock(&buffer->lock);
+
+ return 0;
+}
+
+#if 0
+static int mmz_vb_sync_cache_internal(struct dma_buf *dmabuf, enum dma_data_direction direction)
+{
+ struct mmz_vb_buffer *buffer = dmabuf->priv;
+ struct sg_table *table = buffer->table;
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(table->sgl, sg, table->orig_nents, i)
+ arch_sync_dma_for_device(sg_phys(sg), sg->length, direction);
+
+
+ return 0;
+}
+#endif
+
+static int mmz_vb_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
+{
+ struct mmz_vb_buffer *buffer = dmabuf->priv;
+ struct sg_table *table = buffer->table;
+ unsigned long addr = vma->vm_start;
+ unsigned long pgoff = vma->vm_pgoff, mapsize = 0;
+ unsigned long size_remaining = vma->vm_end - vma->vm_start;
+ struct scatterlist *sg;
+ struct page *page = NULL;
+ unsigned int nents = 0;
+ int i;
+ int ret;
+
+ if ((vma->vm_flags & (VM_SHARED | VM_MAYSHARE)) == 0)
+ return -EINVAL;
+
+ /* vm_private_data will be used by eswin-ipc-scpu.c.
+ ipc will import this dmabuf to get iova.
+ */
+ vma->vm_private_data = dmabuf;
+
+ /* support mman flag MAP_SHARED_VALIDATE | VM_NORESERVE, used to map uncached memory to user space.
+ The cache needs to be flush first since there might be dirty data in cache.
+ */
+ if (vma->vm_flags & VM_NORESERVE) {
+ vm_flags_clear(vma, VM_NORESERVE);
+ #ifndef QEMU_DEBUG
+ vma->vm_page_prot = pgprot_dmacoherent(vma->vm_page_prot);
+ #endif
+ /* skip sync cache, users should guarantee the cache is clean after done using it in
+ cached mode(i.e, ES_SYS_Mmap(SYS_CACHE_MODE_CACHED))
+ */
+ #if 0
+ pr_debug("%s uncached user memory, flush cache firstly!\n", __func__);
+ if (mmz_vb_sync_cache_internal(dmabuf, DMA_TO_DEVICE)) {
+ vb_err("%s, failed to flush cache!\n",__func__);
+ return -EINVAL;
+ }
+ #endif
+ }
+ pr_debug("%s, size_remaining:0x%lx, pgoff:0x%lx, dmabuf->size:0x%lx, start_phys:0x%llx\n",
+ __func__, size_remaining, pgoff, dmabuf->size, sg_phys(table->sgl));
+ for_each_sg(table->sgl, sg, table->orig_nents, i) {
+ pr_debug("sgl:%d, phys:0x%llx, length:0x%x\n", i, sg_phys(sg), sg->length);
+ if (pgoff >= (sg->length >> PAGE_SHIFT)) {
+ pgoff -= (sg->length >> PAGE_SHIFT);
+ continue;
+ }
+
+ page = sg_page(sg);
+ if (nents == 0) {
+ mapsize = sg->length - (pgoff << PAGE_SHIFT);
+ mapsize = min(size_remaining, mapsize);
+ ret = remap_pfn_range(vma, addr, page_to_pfn(page) + pgoff, mapsize,
+ vma->vm_page_prot);
+ pr_debug("nents:%d, sgl:%d, pgoff:0x%lx, mapsize:0x%lx, phys:0x%llx\n",
+ nents, i, pgoff, mapsize, pfn_to_phys(page_to_pfn(page) + pgoff));
+ }
+ else {
+ mapsize = min((unsigned int)size_remaining, (sg->length));
+ ret = remap_pfn_range(vma, addr, page_to_pfn(page), mapsize,
+ vma->vm_page_prot);
+ pr_debug("nents:%d, sgl:%d, mapsize:0x%lx, phys:0x%llx\n", nents, i, mapsize, page_to_phys(page));
+ }
+ pgoff = 0;
+ nents++;
+
+ if (ret)
+ return ret;
+
+ addr += mapsize;
+ size_remaining -= mapsize;
+ if (size_remaining == 0)
+ return 0;
+ }
+
+ return 0;
+}
+
+static void *mmz_vb_do_vmap(struct dma_buf *dmabuf)
+{
+ struct mmz_vb_buffer *buffer = dmabuf->priv;
+ struct esVB_K_POOL_INFO_S *pool = buffer->pBlk->pool;
+ pgprot_t prot = PAGE_KERNEL;
+ struct sg_table *table = buffer->table;
+ int npages = PAGE_ALIGN(buffer->len) / PAGE_SIZE;
+ struct page **pages = vmalloc(sizeof(struct page *) * npages);
+ struct page **tmp = pages;
+ struct sg_page_iter piter;
+ void *vaddr;
+
+ if (!pages)
+ return ERR_PTR(-ENOMEM);
+
+ for_each_sgtable_page(table, &piter, 0) {
+ WARN_ON(tmp - pages >= npages);
+ *tmp++ = sg_page_iter_page(&piter);
+ }
+
+ /* The property of this dmabuf in kernel space is determined by SYS_CACHE_MODE_E of the pool . */
+ if (pool->poolCfg.enRemapMode == SYS_CACHE_MODE_NOCACHE) {
+ pr_debug("%s uncached kernel buffer!\n", __func__);
+ #ifndef QEMU_DEBUG
+ prot = pgprot_dmacoherent(PAGE_KERNEL);
+ #endif
+ }
+ else {
+ pr_debug("%s cached kernel buffer!\n", __func__);
+ }
+
+ vaddr = vmap(pages, npages, VM_MAP, prot);
+ vfree(pages);
+
+ if (!vaddr)
+ return ERR_PTR(-ENOMEM);
+
+ return vaddr;
+}
+
+static int mmz_vb_vmap(struct dma_buf *dmabuf, struct dma_buf_map *map)
+{
+ struct mmz_vb_buffer *buffer = dmabuf->priv;
+ void *vaddr;
+ int ret = 0;
+
+ mutex_lock(&buffer->lock);
+ if (buffer->vmap_cnt) {
+ buffer->vmap_cnt++;
+ dma_buf_map_set_vaddr(map, buffer->vaddr);
+ goto out;
+ }
+
+ vaddr = mmz_vb_do_vmap(dmabuf);
+ if (IS_ERR(vaddr)) {
+ ret = PTR_ERR(vaddr);
+ goto out;
+ }
+ buffer->vaddr = vaddr;
+ buffer->vmap_cnt++;
+ dma_buf_map_set_vaddr(map, buffer->vaddr);
+out:
+ mutex_unlock(&buffer->lock);
+
+ return ret;
+}
+
+static void mmz_vb_vunmap(struct dma_buf *dmabuf, struct dma_buf_map *map)
+{
+ struct mmz_vb_buffer *buffer = dmabuf->priv;
+
+ mutex_lock(&buffer->lock);
+ if (!--buffer->vmap_cnt) {
+ vunmap(buffer->vaddr);
+ buffer->vaddr = NULL;
+ }
+ mutex_unlock(&buffer->lock);
+ dma_buf_map_clear(map);
+}
+
+static void mmz_vb_dma_buf_release(struct dma_buf *dmabuf)
+{
+ struct mmz_vb_buffer *buffer = dmabuf->priv;
+
+ if (buffer->vmap_cnt > 0) {
+ WARN(1, "%s: buffer still mapped in the kernel\n", __func__);
+ vunmap(buffer->vaddr);
+ buffer->vaddr = NULL;
+ }
+
+ /* release block. In fact, release block to pool */
+ vb_release_block(buffer->pBlk);
+
+ kfree(buffer);
+}
+
+static const struct dma_buf_ops mmz_vb_buf_ops = {
+ .attach = mmz_vb_attach,
+ .detach = mmz_vb_detach,
+ .map_dma_buf = mmz_vb_map_dma_buf,
+ .unmap_dma_buf = mmz_vb_unmap_dma_buf,
+ .begin_cpu_access = mmz_vb_dma_buf_begin_cpu_access,
+ .end_cpu_access = mmz_vb_dma_buf_end_cpu_access,
+ .mmap = mmz_vb_mmap,
+ .vmap = mmz_vb_vmap,
+ .vunmap = mmz_vb_vunmap,
+ .release = mmz_vb_dma_buf_release,
+};
+
+static const unsigned int orders[] = {MAX_ORDER-1, 9, 0};
+#define NUM_ORDERS ARRAY_SIZE(orders)
+
+static struct page *alloc_largest_available(struct mem_block *memblock,
+ unsigned long size,
+ unsigned int max_order)
+{
+ struct page *page;
+ int i;
+
+ for (i = 0; i < NUM_ORDERS; i++) {
+ if (size < (PAGE_SIZE << orders[i]))
+ continue;
+ if (max_order < orders[i])
+ continue;
+
+ page = es_alloc_pages(memblock, orders[i]);
+ if (!page)
+ continue;
+ return page;
+ }
+ return NULL;
+}
+
+static int vb_blk_pages_allocate(struct mem_block *memblock, struct esVB_K_BLOCK_INFO_S *blocks, unsigned long len)
+{
+ unsigned long size_remaining = len;
+ unsigned int max_order = orders[0];
+ struct sg_table *table;
+ struct scatterlist *sg;
+ struct list_head pages;
+ struct page *page, *tmp_page;
+ int i, ret = -ENOMEM;
+
+ INIT_LIST_HEAD(&pages);
+ i = 0;
+ while (size_remaining > 0) {
+ /*
+ * Avoid trying to allocate memory if the process
+ * has been killed by SIGKILL
+ */
+ if (fatal_signal_pending(current)) {
+ ret = -EINTR;
+ goto free_buffer;
+ }
+
+ page = alloc_largest_available(memblock, size_remaining, max_order);
+ if (!page)
+ goto free_buffer;
+
+ list_add_tail(&page->lru, &pages);
+ size_remaining -= page_size(page);
+ max_order = compound_order(page);
+ i++;
+ // pr_debug("page_size(page)=0x%lx, phys_addr=0x%llx, max_order=%d\n",
+ // page_size(page), page_to_phys(page), max_order);
+
+ }
+
+ table = &blocks->sg_table;
+ if (sg_alloc_table(table, i, GFP_KERNEL))
+ goto free_buffer;
+
+ sg = table->sgl;
+ list_for_each_entry_safe(page, tmp_page, &pages, lru) {
+ sg_set_page(sg, page, page_size(page), 0);
+ sg = sg_next(sg);
+ list_del(&page->lru);
+ }
+
+ return 0;
+
+free_buffer:
+ list_for_each_entry_safe(page, tmp_page, &pages, lru)
+ es_free_pages(memblock, page);
+
+
+ return ret;
+}
+
+static void vb_blk_pages_release(struct mem_block *memblock, struct esVB_K_BLOCK_INFO_S *blocks)
+{
+ struct sg_table *table;
+ struct scatterlist *sg;
+ int i;
+
+ table = &blocks->sg_table;
+ for_each_sgtable_sg(table, sg, i) {
+ struct page *page = sg_page(sg);
+ // pr_debug("%s:%d,page_size(page)=0x%lx, phys_addr=0x%llx\n",
+ // __func__, __LINE__, page_size(page), page_to_phys(page));
+ es_free_pages(memblock, page);
+ }
+ sg_free_table(table);
+
+}
+
+static int vb_blk_dmabuf_alloc(struct esVB_GET_BLOCK_CMD_S *getBlkCmd)
+{
+ struct mmz_vb_buffer *buffer;
+ struct esVB_K_BLOCK_INFO_S *pBlk;
+ DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
+ size_t size;
+ struct dma_buf *dmabuf;
+ #ifdef MMZ_VB_DMABUF_MEMSET
+ struct sg_table *table;
+ struct scatterlist *sg;
+ int i;
+ #endif
+ int fd;
+ int ret = -ENOMEM;
+
+ buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&buffer->attachments);
+ mutex_init(&buffer->lock);
+
+ ret = vb_get_block(getBlkCmd, &pBlk);
+ /* try to get a required block from pool */
+ if (ret) {
+ vb_err("failed to get block from pool!!!\n");
+ goto free_buffer;
+ }
+
+ size = pBlk->pool->poolCfg.blkSize;
+ buffer->len = size;
+ buffer->table = &pBlk->sg_table;
+ #ifdef MMZ_VB_DMABUF_MEMSET
+ /*TODO: Clear the pages, sg_virt dose not work because vb memory is reserved as no-map!!!!*/
+ #if 0
+ {
+ table = buffer->table;
+ for_each_sg(table->sgl, sg, table->orig_nents, i)
+ memset(sg_virt(sg), 0, sg->length);
+ }
+ #endif
+ #endif
+ buffer->pBlk = pBlk;
+
+ /* create the dmabuf */
+ exp_info.exp_name = MMZ_VB_DMABUF_NAME;
+ exp_info.ops = &mmz_vb_buf_ops;
+ exp_info.size = buffer->len;
+ exp_info.flags = O_RDWR | O_CLOEXEC;
+ exp_info.priv = buffer;
+ dmabuf = dma_buf_export(&exp_info);
+ if (IS_ERR(dmabuf)) {
+ ret = PTR_ERR(dmabuf);
+ goto release_block;
+ }
+
+ fd = dma_buf_fd(dmabuf, MMZ_VB_VALID_FD_FLAGS);
+ if (fd < 0) {
+ dma_buf_put(dmabuf);
+ /* just return, as put will call release and that will free */
+ return fd;
+ }
+
+ getBlkCmd->getBlkResp.fd = fd;
+ getBlkCmd->getBlkResp.actualBlkSize = size;
+ getBlkCmd->getBlkResp.nr = pBlk->nr;
+ return 0;
+
+release_block:
+ vb_release_block(pBlk);
+free_buffer:
+ kfree(buffer);
+
+ return ret;
+}
+
+static int vb_ioctl_get_blk(void __user *user_getBlkCmd)
+{
+ int ret = 0;
+ struct esVB_GET_BLOCK_CMD_S *getBlkCmd;
+
+ getBlkCmd = kzalloc(sizeof(*getBlkCmd), GFP_KERNEL);
+ if (!getBlkCmd) {
+ return -ENOMEM;
+ }
+ if (copy_from_user(getBlkCmd, user_getBlkCmd, sizeof(*getBlkCmd))) {
+ ret = -EFAULT;
+ goto out_free;
+ }
+
+ ret = vb_blk_dmabuf_alloc(getBlkCmd);
+ if (ret) {
+ goto out_free;
+ }
+
+ if (copy_to_user(user_getBlkCmd, getBlkCmd, sizeof(*getBlkCmd)))
+ ret = -EFAULT;
+
+out_free:
+ kfree(getBlkCmd);
+ return ret;
+}
+
+static int vb_ioctl_pool_size(void __user *user_getPoolSizeCmd)
+{
+ int ret = 0;
+ struct esVB_GET_POOLSIZE_CMD_S *getPoolSizeCmd;
+
+ getPoolSizeCmd = kzalloc(sizeof(*getPoolSizeCmd), GFP_KERNEL);
+ if (!getPoolSizeCmd) {
+ return -ENOMEM;
+ }
+ if (copy_from_user(getPoolSizeCmd, user_getPoolSizeCmd, sizeof(*getPoolSizeCmd))) {
+ ret = -EFAULT;
+ goto out_free;
+ }
+
+ ret = vb_pool_size(getPoolSizeCmd->poolId, &getPoolSizeCmd->poolSize);
+ if (ret) {
+ goto out_free;
+ }
+
+ if (copy_to_user(user_getPoolSizeCmd, getPoolSizeCmd, sizeof(*getPoolSizeCmd)))
+ ret = -EFAULT;
+
+out_free:
+ kfree(getPoolSizeCmd);
+ return ret;
+}
+#if 0
+static int vb_ioctl_flush_pool(void __user *user_flushPoolCmd)
+{
+ int ret = 0;
+ struct esVB_FLUSH_POOL_CMD_S *flushPoolCmd;
+
+ flushPoolCmd = kzalloc(sizeof(*flushPoolCmd), GFP_KERNEL);
+ if (!flushPoolCmd) {
+ return -ENOMEM;
+ }
+ if (copy_from_user(flushPoolCmd, user_flushPoolCmd, sizeof(*flushPoolCmd))) {
+ ret = -EFAULT;
+ goto out_free;
+ }
+
+ ret = vb_flush_pool(flushPoolCmd);
+ if (ret) {
+ goto out_free;
+ }
+
+ if (copy_to_user(user_flushPoolCmd, flushPoolCmd, sizeof(*flushPoolCmd)))
+ ret = -EFAULT;
+
+out_free:
+ kfree(flushPoolCmd);
+ return ret;
+}
+#endif
+
+static int vb_ioctl_blk_to_pool(void __user *user_blkToPoolCmd)
+{
+ int ret = 0;
+ struct esVB_BLOCK_TO_POOL_CMD_S *blkToPoolCmd;
+
+ blkToPoolCmd = kzalloc(sizeof(*blkToPoolCmd), GFP_KERNEL);
+ if (!blkToPoolCmd) {
+ return -ENOMEM;
+ }
+ if (copy_from_user(blkToPoolCmd, user_blkToPoolCmd, sizeof(*blkToPoolCmd))) {
+ ret = -EFAULT;
+ goto out_free;
+ }
+
+ ret = vb_blk_to_pool(blkToPoolCmd);
+ if (ret)
+ goto out_free;
+
+ if (copy_to_user(user_blkToPoolCmd, blkToPoolCmd, sizeof(*blkToPoolCmd)))
+ ret = -EFAULT;
+
+out_free:
+ kfree(blkToPoolCmd);
+ return ret;
+}
+
+static int vb_ioctl_get_blk_offset(void __user *user_getBlkOffsetCmd)
+{
+ int ret = 0;
+ struct esVB_GET_BLOCKOFFSET_CMD_S *getBlkOffsetCmd;
+
+ getBlkOffsetCmd = kzalloc(sizeof(*getBlkOffsetCmd), GFP_KERNEL);
+ if (!getBlkOffsetCmd) {
+ return -ENOMEM;
+ }
+ if (copy_from_user(getBlkOffsetCmd, user_getBlkOffsetCmd, sizeof(*getBlkOffsetCmd))) {
+ ret = -EFAULT;
+ goto out_free;
+ }
+
+ ret = vb_get_blk_offset(getBlkOffsetCmd);
+ if (ret) {
+ goto out_free;
+ }
+
+ if (copy_to_user(user_getBlkOffsetCmd, getBlkOffsetCmd, sizeof(*getBlkOffsetCmd)))
+ ret = -EFAULT;
+
+out_free:
+ kfree(getBlkOffsetCmd);
+ return ret;
+}
+
+static int vb_ioctl_split_dmabuf(void __user *user_splitDmabufCmd)
+{
+ int ret = 0;
+ struct esVB_SPLIT_DMABUF_CMD_S *splitDmabufCmd;
+
+ splitDmabufCmd = kzalloc(sizeof(*splitDmabufCmd), GFP_KERNEL);
+ if (!splitDmabufCmd) {
+ return -ENOMEM;
+ }
+ if (copy_from_user(splitDmabufCmd, user_splitDmabufCmd, sizeof(*splitDmabufCmd))) {
+ ret = -EFAULT;
+ goto out_free;
+ }
+
+ ret = vb_split_dmabuf(splitDmabufCmd);
+ if (ret) {
+ goto out_free;
+ }
+
+ if (copy_to_user(user_splitDmabufCmd, splitDmabufCmd, sizeof(*splitDmabufCmd)))
+ ret = -EFAULT;
+
+out_free:
+ kfree(splitDmabufCmd);
+ return ret;
+}
+
+static int vb_ioctl_get_dmabuf_refcnt(void __user *user_getDmabufRefCntCmd)
+{
+ int ret = 0;
+ struct esVB_DMABUF_REFCOUNT_CMD_S *getDmabufRefCntCmd;
+
+ getDmabufRefCntCmd = kzalloc(sizeof(*getDmabufRefCntCmd), GFP_KERNEL);
+ if (!getDmabufRefCntCmd) {
+ return -ENOMEM;
+ }
+ if (copy_from_user(getDmabufRefCntCmd, user_getDmabufRefCntCmd, sizeof(*getDmabufRefCntCmd))) {
+ ret = -EFAULT;
+ goto out_free;
+ }
+
+ ret = vb_get_dmabuf_refcnt(getDmabufRefCntCmd);
+ if (ret)
+ goto out_free;
+
+ if (copy_to_user(user_getDmabufRefCntCmd, getDmabufRefCntCmd, sizeof(*getDmabufRefCntCmd)))
+ ret = -EFAULT;
+
+out_free:
+ kfree(getDmabufRefCntCmd);
+ return ret;
+}
+
+static int vb_ioctl_retrieve_mem_node(void __user *user_retrieveMemNodeCmd)
+{
+ int ret = 0;
+ struct esVB_RETRIEVE_MEM_NODE_CMD_S *retrieveMemNodeCmd;
+
+ retrieveMemNodeCmd = kzalloc(sizeof(*retrieveMemNodeCmd), GFP_KERNEL);
+ if (!retrieveMemNodeCmd) {
+ return -ENOMEM;
+ }
+ if (copy_from_user(retrieveMemNodeCmd, user_retrieveMemNodeCmd, sizeof(*retrieveMemNodeCmd))) {
+ ret = -EFAULT;
+ goto out_free;
+ }
+
+ ret = vb_retrieve_mem_node(retrieveMemNodeCmd);
+ if (ret)
+ goto out_free;
+
+ if (copy_to_user(user_retrieveMemNodeCmd, retrieveMemNodeCmd, sizeof(*retrieveMemNodeCmd)))
+ ret = -EFAULT;
+
+out_free:
+ kfree(retrieveMemNodeCmd);
+ return ret;
+}
+
+static int vb_ioctl_get_dmabuf_size(void __user *user_getDmabufSizeCmd)
+{
+ int ret = 0;
+ struct esVB_DMABUF_SIZE_CMD_S *getDmabufSizeCmd;
+
+ getDmabufSizeCmd = kzalloc(sizeof(*getDmabufSizeCmd), GFP_KERNEL);
+ if (!getDmabufSizeCmd) {
+ return -ENOMEM;
+ }
+ if (copy_from_user(getDmabufSizeCmd, user_getDmabufSizeCmd, sizeof(*getDmabufSizeCmd))) {
+ ret = -EFAULT;
+ goto out_free;
+ }
+
+ ret = vb_get_dmabuf_size(getDmabufSizeCmd);
+ if (ret)
+ goto out_free;
+
+ if (copy_to_user(user_getDmabufSizeCmd, getDmabufSizeCmd, sizeof(*getDmabufSizeCmd)))
+ ret = -EFAULT;
+
+out_free:
+ kfree(getDmabufSizeCmd);
+ return ret;
+}
+
+static int mmz_vb_assign_pool_id(struct esVB_K_POOL_INFO_S *pool)
+{
+ int ret = 0;
+ struct mmz_vb_priv *vb_priv = g_mmz_vb_priv;
+ struct esVB_K_MMZ_S *partitions = &vb_priv->partitions;
+
+ down_write(&partitions->idr_lock);
+ ret = idr_alloc(&partitions->pool_idr, pool, 0, VB_K_POOL_MAX_ID,
+ GFP_KERNEL);
+ if (ret >= 0) {
+ pool->poolId = ret;
+ }
+ up_write(&partitions->idr_lock);
+
+ return ret < 0 ? ret : 0;
+}
+
+static int mmz_vb_remove_pool_id(struct esVB_K_POOL_INFO_S *pool, bool is_lock)
+{
+ int ret = 0;
+ struct mmz_vb_priv *vb_priv = g_mmz_vb_priv;
+ struct esVB_K_MMZ_S *partitions = &vb_priv->partitions;
+
+ if (is_lock) {
+ down_write(&partitions->idr_lock);
+ }
+
+ idr_remove(&partitions->pool_idr, pool->poolId);
+
+ if (is_lock) {
+ up_write(&partitions->idr_lock);
+ }
+ return ret < 0 ? ret : 0;
+}
+
+static int mmz_pool_insert_list(struct esVB_K_POOL_INFO_S *pool, enum esVB_UID_E uid)
+{
+ struct mmz_vb_priv *vb_priv = g_mmz_vb_priv;
+
+ if (uid <= VB_UID_PRIVATE || uid >= VB_UID_MAX) {
+ dev_err(mmz_vb_dev, "%s %d, invalid uid %d\n",__func__,__LINE__, uid);
+ return -EINVAL;
+ }
+ down_write(&vb_priv->pool_lock[uid]);
+ hash_add(vb_priv->ht[uid], &pool->node, pool->poolCfg.blkSize);
+ up_write(&vb_priv->pool_lock[uid]);
+ return 0;
+}
+
+static struct mem_block *vb_get_memblock(const char *memBlkName)
+{
+ struct mmz_vb_priv *vb_priv = g_mmz_vb_priv;
+ struct esVB_K_MMZ_S *partitions = &vb_priv->partitions;
+ struct mem_block *memblock = NULL, *rsvmem_block = NULL;
+ int i;
+
+ for (i = 0; i < partitions->partCnt; i++) {
+ rsvmem_block = partitions->mem_blocks[i];
+ if (!strcmp(memBlkName, rsvmem_block->name)){
+ memblock = rsvmem_block;
+ break;
+ }
+ }
+
+ return memblock;
+}
+
+static int mmz_vb_do_create_pool(struct esVB_POOL_CONFIG_S *pool_cfg,
+ struct esVB_K_POOL_INFO_S **pool_out)
+{
+ int i;
+ int ret = 0;
+ struct esVB_K_POOL_INFO_S *pool;
+ struct esVB_K_BLOCK_INFO_S *blocks;
+ struct mem_block *memblock = NULL;
+ const char *memBlkName = pool_cfg->mmzName;
+ size_t size;
+
+ // 0.find the memblock
+ memblock = vb_get_memblock(memBlkName);
+ if (NULL == memblock) {
+ vb_err("%s NOT found!\n", memBlkName);
+ return -EINVAL;
+ }
+
+ // 1.init pool
+ pool = devm_kzalloc(mmz_vb_dev, sizeof(struct esVB_K_POOL_INFO_S), GFP_KERNEL);
+ if (!pool) {
+ dev_err(mmz_vb_dev, "%s %d, faild to alloc pool cb\n",__func__,__LINE__);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ pool->blocks = vmalloc(sizeof(struct esVB_K_BLOCK_INFO_S) * pool_cfg->blkCnt);
+ if (!pool->blocks) {
+ dev_err(mmz_vb_dev, "%s %d, faild to alloc blocks cb\n",__func__,__LINE__);
+ ret = -ENOMEM;
+ goto out_free_pool;
+ }
+
+ spin_lock_init(&pool->lock);
+ memcpy(&pool->poolCfg, pool_cfg, sizeof(struct esVB_POOL_CONFIG_S));
+
+ pool->bitmap = bitmap_zalloc(pool_cfg->blkCnt, GFP_KERNEL);
+ if (!pool->bitmap) {
+ dev_err(mmz_vb_dev, "%s %d, faild to alloc bitmap\n",__func__,__LINE__);
+ ret = -ENOMEM;
+ goto out_free_block_arrays;
+ }
+
+ // 2. make blkSize align
+ size = PAGE_ALIGN(pool_cfg->blkSize);
+ /* If len >= 1MB, align len with 2M to improve performance of SMMU */
+ if (size/(PAGE_SIZE << 8)) {
+ size = ALIGN(size, (PAGE_SIZE << 9));
+ }
+ pool_cfg->blkSize = size;
+ pool->poolCfg.blkSize = pool_cfg->blkSize;
+ dev_dbg(mmz_vb_dev, "blkSize(0x%llx) from pool creation is "
+ "aligned to 0x%lx to improve performance.\n",
+ pool_cfg->blkSize, size);
+
+ // 3. alloc pages for blocks
+ for (i = 0; i < pool_cfg->blkCnt; i++) {
+ blocks = &pool->blocks[i];
+ blocks->nr = i;
+ blocks->pool = pool;
+ ret = vb_blk_pages_allocate(memblock, blocks, pool_cfg->blkSize);
+ if (ret) {
+ while (--i >= 0) {
+ vb_blk_pages_release(memblock, &pool->blocks[i]);
+ }
+ dev_err(mmz_vb_dev, "%s %d, faild to alloc block page!\n", __func__,__LINE__);
+ ret = -ENOMEM;
+ goto out_free_bitmap;
+ }
+ }
+ // 4. everthing is ok, add pool to idr
+ ret = mmz_vb_assign_pool_id(pool);
+ if (0 != ret) {
+ dev_err(mmz_vb_dev, "%s %d, faild to assign pool id\n",__func__,__LINE__);
+ ret = -EINVAL;
+ goto out_free_block_pages;
+ }
+ *pool_out = pool;
+ return ret;
+
+out_free_block_pages:
+ for (i = 0; i < pool_cfg->blkCnt; i++) {
+ vb_blk_pages_release(memblock, &pool->blocks[i]);
+ }
+out_free_bitmap:
+ bitmap_free(pool->bitmap);;
+out_free_block_arrays:
+ vfree(pool->blocks);
+out_free_pool:
+ devm_kfree(mmz_vb_dev, pool);
+out:
+ return ret;
+}
+
+static int vb_pool_config_check(struct esVB_POOL_CONFIG_S *pool_cfg)
+{
+ struct mmz_vb_priv *vb_priv = g_mmz_vb_priv;
+ struct mem_block *memblock = NULL;
+ const char *memBlkName = pool_cfg->mmzName;
+ unsigned long numFreePages = 0;
+ u64 req_size;
+
+ if (NULL == vb_priv) {
+ return 0;
+ }
+
+ // find the memblock
+ memblock = vb_get_memblock(memBlkName);
+ if (NULL == memblock) {
+ vb_err("%s NOT found!\n", memBlkName);
+ return -EINVAL;
+ }
+
+ req_size = pool_cfg->blkCnt * PAGE_ALIGN(pool_cfg->blkSize);
+ numFreePages = es_num_free_pages(memblock);
+ if (numFreePages < (req_size >> PAGE_SHIFT)) {
+ dev_err(mmz_vb_dev, "%s %d, (%s)out of memory, request pool size %llu "
+ "free %ld!\n",
+ __func__,__LINE__,
+ memBlkName, req_size, (numFreePages << PAGE_SHIFT));
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int vb_ioctl_create_pool(void __user *user_cmd)
+{
+ int ret = 0;
+ struct esVB_CREATE_POOL_CMD_S cmd;
+ struct esVB_CREATE_POOL_REQ_S *req;
+ struct esVB_CREATE_POOL_RESP_S *rsp;
+ struct esVB_POOL_CONFIG_S *pool_cfg;
+ struct esVB_K_POOL_INFO_S *pool = NULL;
+
+ if (copy_from_user(&cmd, user_cmd, sizeof(cmd))) {
+ ret = -EFAULT;
+ goto out_free;
+ }
+ req = &cmd.PoolReq;
+ pool_cfg = &req->req;
+ ret = vb_pool_config_check(pool_cfg);
+ if (ret) {
+ goto out_free;
+ }
+ ret = mmz_vb_do_create_pool(pool_cfg, &pool);
+ if (ret) {
+ goto out_free;
+ }
+ pool->enVbUid = VB_UID_PRIVATE;
+ rsp = &cmd.PoolResp;
+ rsp->PoolId = pool->poolId;
+ dev_dbg(mmz_vb_dev, "[%s %d]:create pool, PoolId %d!\n",__func__,__LINE__, rsp->PoolId);
+ if (copy_to_user(user_cmd, &cmd, sizeof(cmd)))
+ ret = -EFAULT;
+ else
+ ret = 0;
+
+out_free:
+ return ret;
+}
+
+/**
+ * mmz_vb_do_destory_pool - do the pool destory operation
+ * @pool: The pool
+ * @is_lock: when set true, will lock the idr when remove the idr id.
+ * @is_force: when set true, will still destory the bool even the bitmap is not empty.
+ */
+static int mmz_vb_do_destory_pool(struct esVB_K_POOL_INFO_S *pool, bool is_lock, bool is_force)
+{
+ struct esVB_POOL_CONFIG_S *poolCfg = &pool->poolCfg;
+ const char *memBlkName = poolCfg->mmzName;
+ struct mem_block *memblock = NULL;
+ struct esVB_K_BLOCK_INFO_S *blocks = NULL;
+ int ret = 0;
+ int i;
+
+ // find the memblock
+ memblock = vb_get_memblock(memBlkName);
+ if (NULL == memblock) {
+ vb_err("%s NOT found!\n", memBlkName);
+ return -EINVAL;
+ }
+
+ if (!bitmap_empty(pool->bitmap, pool->poolCfg.blkCnt)) {
+ if (true == is_force) {
+ dev_info(mmz_vb_dev, "%s %d, non-empty pool, still destory it!\n",__func__,__LINE__);
+ } else {
+ dev_info(mmz_vb_dev, "%s %d, non-empty pool, can not destory!\n",__func__,__LINE__);
+ ret = -ENOTEMPTY;
+ goto out;
+ }
+ }
+
+
+ blocks = pool->blocks;
+ for (i = 0; i < poolCfg->blkCnt; i++) {
+ vb_blk_pages_release(memblock, &blocks[i]);
+ }
+ mmz_vb_remove_pool_id(pool, is_lock);
+ if (pool->enVbUid >= VB_UID_COMMON && pool->enVbUid < VB_UID_MAX) {
+ hash_del(&pool->node);
+ }
+ bitmap_free(pool->bitmap);
+ vfree(pool->blocks);
+out:
+ if (0 == ret) {
+ devm_kfree(mmz_vb_dev, pool);
+ }
+ return ret;
+}
+
+static int vb_ioctl_destory_pool(void __user *user_cmd)
+{
+ int ret = 0;
+ struct esVB_K_POOL_INFO_S *pool = NULL;
+ struct esVB_DESTORY_POOL_CMD_S cmd;
+ struct esVB_DESTORY_POOL_REQ_S *req = NULL;
+ struct mmz_vb_priv *vb_priv = g_mmz_vb_priv;
+ struct esVB_K_MMZ_S *partitions = &vb_priv->partitions;
+
+ if (copy_from_user(&cmd, user_cmd, sizeof(cmd))) {
+ return -EFAULT;
+ }
+ req = &cmd.req;
+ dev_dbg(mmz_vb_dev, "[%s %d]:destory pool, PoolId %d!\n",__func__,__LINE__, req->PoolId);
+ down_write(&partitions->idr_lock);
+ ret = vb_find_pool_by_id_unlock(req->PoolId, &pool);
+ if (ret) {
+ up_write(&partitions->idr_lock);
+ dev_err(mmz_vb_dev, "%s %d, faild to find pool, PoolId %d\n",
+ __func__,__LINE__, req->PoolId);
+ return ret;
+ }
+ ret = mmz_vb_do_destory_pool(pool, false, false);
+ if (-ENOTEMPTY == ret) {
+ set_bit(MMZ_VB_POOL_FLAG_DESTORY, &pool->flag);
+ up_write(&partitions->idr_lock);
+ dev_info(mmz_vb_dev, "%s %d, pool %d not empty, waiting to destory\n",
+ __func__,__LINE__, req->PoolId);
+ return 0;
+ } else if (ret) {
+ up_write(&partitions->idr_lock);
+ dev_err(mmz_vb_dev, "%s %d, faild to destory pool, PoolId %d\n",
+ __func__,__LINE__, req->PoolId);
+ return ret;
+ }
+ up_write(&partitions->idr_lock);
+ return 0;
+}
+
+/*check whether the VbConfig is legal*/
+static int vb_config_check(struct esVB_CONFIG_S *pVbConfig)
+{
+ int i;
+ struct esVB_POOL_CONFIG_S *pool_cfg = NULL;
+ int ret;
+
+ if (pVbConfig->poolCnt > ES_VB_MAX_MOD_POOL) {
+ dev_err(mmz_vb_dev, "%s %d, poolCnt %d exceed the limit %d!\n",
+ __func__,__LINE__, pVbConfig->poolCnt, ES_VB_MAX_MOD_POOL);
+ return -EINVAL;
+ }
+ for (i = 0; i < pVbConfig->poolCnt; i++) {
+ pool_cfg = &pVbConfig->poolCfgs[i];
+ ret = vb_pool_config_check(pool_cfg);
+ if (0 != ret) {
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int vb_ioctl_set_config(void __user *user_cmd)
+{
+ struct esVB_SET_CFG_CMD_S *cmd;
+ struct esVB_SET_CFG_REQ_S *req;
+ enum esVB_UID_E enVbUid;
+ struct esVB_CONFIG_S *pVbConfig = NULL;
+ struct esVB_CONFIG_S *vb_cfg = NULL;
+ struct mmz_vb_priv *vb_priv = g_mmz_vb_priv;
+ int ret = 0;
+
+ cmd = devm_kzalloc(mmz_vb_dev, sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ dev_err(mmz_vb_dev, "%s %d, uid %d, failed to alloc memory!\n",
+ __func__,__LINE__, enVbUid);
+ ret = -ENOMEM;
+ goto out;
+ }
+ if (copy_from_user(cmd, user_cmd, sizeof(*cmd))) {
+ ret = -EFAULT;
+ goto out_free_cmd;
+ }
+ req = &cmd->CfgReq;
+ enVbUid = req->uid;
+ pVbConfig = &req->cfg;
+ if (enVbUid <= VB_UID_PRIVATE || enVbUid >= VB_UID_MAX) {
+ dev_err(mmz_vb_dev, "%s %d, invaild uid %d!\n", __func__,__LINE__, enVbUid);
+ ret = -EFAULT;
+ goto out_free_cmd;
+ }
+ ret = vb_config_check(pVbConfig);
+ if (ret) {
+ dev_err(mmz_vb_dev, "%s %d, uid %d, vbConfig check fail!\n",
+ __func__,__LINE__, enVbUid);
+ goto out_free_cmd;
+ }
+ mutex_lock(&vb_priv->cfg_lock[enVbUid]);
+ if (NULL != vb_priv->pVbConfig[enVbUid]) {
+ if (test_bit(MMZ_VB_CFG_FLAG_INIT, &vb_priv->cfg_flag[enVbUid])) {
+ dev_err(mmz_vb_dev, "%s %d, uid %d cfg already exist and init!\n",
+ __func__,__LINE__, enVbUid);
+ ret = -EFAULT;
+ goto out_unlock;
+ } else {
+ /*release the old config*/
+ devm_kfree(mmz_vb_dev, vb_priv->pVbConfig[enVbUid]);
+ vb_priv->pVbConfig[enVbUid] = NULL;
+ }
+ }
+ vb_cfg = devm_kzalloc(mmz_vb_dev, sizeof(struct esVB_CONFIG_S), GFP_KERNEL);
+ if (!vb_cfg) {
+ dev_err(mmz_vb_dev, "%s %d, uid %d, failed to alloc memory!\n",
+ __func__,__LINE__, enVbUid);
+ ret = -ENOMEM;
+ goto out_unlock;
+ }
+ memcpy(vb_cfg, pVbConfig, sizeof(struct esVB_CONFIG_S));
+ vb_priv->pVbConfig[enVbUid] = vb_cfg;
+out_unlock:
+ mutex_unlock(&vb_priv->cfg_lock[enVbUid]);
+out_free_cmd:
+ devm_kfree(mmz_vb_dev, cmd);
+out:
+ return ret;
+}
+
+static int vb_ioctl_get_config(void __user *user_cmd)
+{
+ struct esVB_GET_CFG_CMD_S *cmd;
+ struct esVB_GET_CFG_REQ_S *req;
+ struct esVB_GET_CFG_RSP_S *rsp;
+ enum esVB_UID_E enVbUid;
+ struct esVB_CONFIG_S *vb_cfg = NULL;
+ struct mmz_vb_priv *vb_priv = g_mmz_vb_priv;
+ int ret = 0;
+
+ cmd = devm_kzalloc(mmz_vb_dev, sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ dev_err(mmz_vb_dev, "%s %d, uid %d, failed to alloc memory!\n",
+ __func__,__LINE__, enVbUid);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (copy_from_user(cmd, user_cmd, sizeof(*cmd))) {
+ ret = -EFAULT;
+ goto out_free_cmd;
+ }
+ req = &cmd->req;
+ enVbUid = req->uid;
+ if (enVbUid <= VB_UID_PRIVATE || enVbUid >= VB_UID_MAX) {
+ dev_err(mmz_vb_dev, "%s %d, invaild uid %d!\n",__func__,__LINE__, enVbUid);
+ ret = -EFAULT;
+ goto out_free_cmd;
+ }
+ mutex_lock(&vb_priv->cfg_lock[enVbUid]);
+ vb_cfg = vb_priv->pVbConfig[enVbUid];
+ if (NULL == vb_cfg) {
+ dev_err(mmz_vb_dev, "%s %d, uid %d cfg not exist!\n", __func__,__LINE__, enVbUid);
+ ret = -EFAULT;
+ goto out_unlock;
+ }
+ rsp = &cmd->rsp;
+ memcpy(&rsp->cfg, vb_cfg, sizeof(struct esVB_CONFIG_S));
+ if (copy_to_user(user_cmd, cmd, sizeof(*cmd))) {
+ ret = -EFAULT;
+ goto out_unlock;
+ }
+out_unlock:
+ mutex_unlock(&vb_priv->cfg_lock[enVbUid]);
+out_free_cmd:
+ devm_kfree(mmz_vb_dev, cmd);
+out:
+ return ret;
+}
+
+static int vb_ioctl_init_config(void __user *user_cmd)
+{
+ VB_INIT_CFG_CMD_S cmd;
+ VB_INIT_CFG_REQ_S *req = NULL;
+ enum esVB_UID_E enVbUid;
+ struct esVB_CONFIG_S *vb_cfg = NULL;
+ struct mmz_vb_priv *vb_priv = g_mmz_vb_priv;
+ int i;
+ int ret = 0;
+ struct esVB_POOL_CONFIG_S *pool_cfg;
+ struct esVB_K_POOL_INFO_S *pool[ES_VB_MAX_MOD_POOL] = {NULL};
+
+ if (copy_from_user(&cmd, user_cmd, sizeof(cmd))) {
+ ret = -EFAULT;
+ goto out;
+ }
+ req = &cmd.req;
+ enVbUid = req->uid;
+
+ if (enVbUid < VB_UID_COMMON || enVbUid >= VB_UID_MAX) {
+ dev_err(mmz_vb_dev, "%s %d, invaild uid %d!\n",__func__,__LINE__, enVbUid);
+ ret = -EFAULT;
+ goto out;
+ }
+ mutex_lock(&vb_priv->cfg_lock[enVbUid]);
+ vb_cfg = vb_priv->pVbConfig[enVbUid];
+ if (NULL == vb_cfg) {
+ dev_err(mmz_vb_dev, "%s %d, uid %d cfg not exist!\n", __func__,__LINE__, enVbUid);
+ ret = -EFAULT;
+ goto out_unlock;
+ }
+ if (test_bit(MMZ_VB_CFG_FLAG_INIT, &vb_priv->cfg_flag[enVbUid])) {
+ dev_err(mmz_vb_dev, "%s %d, uid %d cfg already initialized!\n", __func__,__LINE__, enVbUid);
+ ret = -EPERM;
+ goto out_unlock;
+ }
+
+ for (i = 0; i < vb_cfg->poolCnt; i++) {
+ pool_cfg = &vb_cfg->poolCfgs[i];
+ ret = mmz_vb_do_create_pool(pool_cfg, &pool[i]);
+ if (0 != ret) {
+ while(--i >= 0) {
+ ret = mmz_vb_do_destory_pool(pool[i], true, false);
+ if (ret) {
+ dev_err(mmz_vb_dev, "%s %d, faild to destory pool!\n",
+ __func__,__LINE__);
+ }
+ }
+ dev_err(mmz_vb_dev, "%s %d, faild to create pool!\n",__func__, __LINE__);
+ goto out_unlock;
+ }
+ mmz_pool_insert_list(pool[i], enVbUid);
+ pool[i]->enVbUid = enVbUid;
+ }
+ set_bit(MMZ_VB_CFG_FLAG_INIT, &vb_priv->cfg_flag[enVbUid]);
+out_unlock:
+ mutex_unlock(&vb_priv->cfg_lock[enVbUid]);
+out:
+ return ret;
+}
+
+static int vb_ioctl_uninit_config(void __user *user_cmd)
+{
+ struct esVB_UNINIT_CFG_CMD_S cmd;
+ enum esVB_UID_E enVbUid;
+ int ret;
+ struct mmz_vb_priv *vb_priv = g_mmz_vb_priv;
+ struct esVB_K_POOL_INFO_S *pool = NULL;
+ unsigned long bkt = 0;
+ struct hlist_node *tmp_node = NULL;
+
+ if (copy_from_user(&cmd, user_cmd, sizeof(cmd))) {
+ return -EFAULT;
+ }
+ enVbUid = cmd.req.uid;
+
+ if (enVbUid <= VB_UID_PRIVATE || enVbUid >= VB_UID_MAX) {
+ dev_err(mmz_vb_dev, "%s %d, invaild uid %d!\n",__func__,__LINE__, enVbUid);
+ return -EFAULT;
+ }
+ mutex_lock(&vb_priv->cfg_lock[enVbUid]);
+ if (!test_bit(MMZ_VB_CFG_FLAG_INIT, &vb_priv->cfg_flag[enVbUid])) {
+ dev_err(mmz_vb_dev, "%s %d, uid %d cfg not initialized!\n", __func__,__LINE__, enVbUid);
+ mutex_unlock(&vb_priv->cfg_lock[enVbUid]);
+ return -EINVAL;
+ }
+ mutex_unlock(&vb_priv->cfg_lock[enVbUid]);
+
+ down_write(&vb_priv->pool_lock[enVbUid]);
+ hash_for_each_safe(vb_priv->ht[enVbUid], bkt, tmp_node, pool, node) {
+ ret = mmz_vb_do_destory_pool(pool, true, false);
+ if (ret) {
+ dev_err(mmz_vb_dev, "%s %d, faild to destory pool, PoolId %d, enVbUid %d\n",
+ __func__,__LINE__, pool->poolId, enVbUid);
+ up_write(&vb_priv->pool_lock[enVbUid]);
+ return ret;
+ }
+ }
+ up_write(&vb_priv->pool_lock[enVbUid]);
+
+ mutex_lock(&vb_priv->cfg_lock[enVbUid]);
+ devm_kfree(mmz_vb_dev, vb_priv->pVbConfig[enVbUid]);
+ vb_priv->pVbConfig[enVbUid] = NULL;
+ clear_bit(MMZ_VB_CFG_FLAG_INIT, &vb_priv->cfg_flag[enVbUid]);
+ mutex_unlock(&vb_priv->cfg_lock[enVbUid]);
+ return 0;
+}
+
+static long mmz_vb_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+ void __user *argp;
+
+ argp = (void __user *)arg;
+ switch (cmd) {
+ case MMZ_VB_IOCTL_GET_BLOCK:
+ return vb_ioctl_get_blk(argp);
+ case MMZ_VB_IOCTL_CREATE_POOL:
+ return vb_ioctl_create_pool(argp);
+ case MMZ_VB_IOCTL_DESTORY_POOL:
+ return vb_ioctl_destory_pool(argp);
+ case MMZ_VB_IOCTL_SET_CFG:
+ return vb_ioctl_set_config(argp);
+ case MMZ_VB_IOCTL_GET_CFG:
+ return vb_ioctl_get_config(argp);
+ case MMZ_VB_IOCTL_INIT_CFG:
+ return vb_ioctl_init_config(argp);
+ case MMZ_VB_IOCTL_UNINIT_CFG:
+ return vb_ioctl_uninit_config(argp);
+ case MMZ_VB_IOCTL_POOL_SIZE:
+ return vb_ioctl_pool_size(argp);
+#if 0
+ case MMZ_VB_IOCTL_FLUSH_POOL:
+ return vb_ioctl_flush_pool(argp);
+#endif
+ case MMZ_VB_IOCTL_BLOCK_TO_POOL:
+ return vb_ioctl_blk_to_pool(argp);
+ case MMZ_VB_IOCTL_GET_BLOCK_OFFSET:
+ return vb_ioctl_get_blk_offset(argp);
+ case MMZ_VB_IOCTL_SPLIT_DMABUF:
+ return vb_ioctl_split_dmabuf(argp);
+ case MMZ_VB_IOCTL_DMABUF_REFCOUNT:
+ return vb_ioctl_get_dmabuf_refcnt(argp);
+ case MMZ_VB_IOCTL_RETRIEVE_MEM_NODE:
+ return vb_ioctl_retrieve_mem_node(argp);
+ case MMZ_VB_IOCTL_DMABUF_SIZE:
+ return vb_ioctl_get_dmabuf_size(argp);
+ default:
+ pr_debug("Invalid IOCTL CMD!!!\n");
+ return -EINVAL;
+ }
+ pr_debug("%s:%d, success!\n", __func__, __LINE__);
+ return ret;
+}
+
+static int mmz_vb_open(struct inode *inode, struct file *file)
+{
+
+ pr_debug("%s:%d, success!\n", __func__, __LINE__);
+
+ return 0;
+}
+
+static int mmz_vb_release(struct inode *inode, struct file *file)
+{
+ pr_debug("%s:%d, success!\n", __func__, __LINE__);
+
+ return 0;
+}
+
+/* mem = mmap(0, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, addr_pha);
+* vma->vm_pgoff indicats the pool ID
+*/
+static int mmz_vb_mmap_pool(struct file *file, struct vm_area_struct *vma)
+{
+ int ret = 0;
+ size_t size = vma->vm_end - vma->vm_start;
+ VB_POOL poolId = (VB_POOL)vma->vm_pgoff;
+ unsigned long addr = vma->vm_start;
+ struct esVB_K_POOL_INFO_S *pPool = NULL;
+ struct esVB_K_BLOCK_INFO_S *pBlk = NULL;
+ u64 poolSize, blkSize;
+ u32 i;
+
+ ret = vb_find_pool_by_id(poolId, &pPool);
+ if (ret)
+ return ret;
+
+ poolSize = do_vb_pool_size(pPool);
+ /* is the mmap size equal to poolSize? */
+ if (size != poolSize)
+ return -EINVAL;
+
+ /* pool is mmapped as uncached memory */
+ #ifndef QEMU_DEBUG
+ vma->vm_page_prot = pgprot_dmacoherent(vma->vm_page_prot);
+ #endif
+
+ blkSize = pPool->poolCfg.blkSize;
+ pBlk = pPool->blocks;
+ for (i = 0; i < pPool->poolCfg.blkCnt; i++) {
+ struct sg_table *table = &pBlk->sg_table;
+
+ /* mmap for one block */
+ struct scatterlist *sg;
+ int j;
+
+ for_each_sg(table->sgl, sg, table->orig_nents, j) {
+ struct page *page = sg_page(sg);
+ ret = remap_pfn_range(vma, addr, page_to_pfn(page), sg->length,
+ vma->vm_page_prot);
+ if (ret)
+ return ret;
+ addr += sg->length;
+ if (addr >= vma->vm_end)
+ return 0;
+ }
+ pBlk++;
+ }
+
+ pr_debug("%s:%d, success!\n", __func__, __LINE__);
+
+ return ret;
+}
+
+static struct file_operations mmz_vb_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .unlocked_ioctl = mmz_vb_unlocked_ioctl,
+ .open = mmz_vb_open,
+ .release = mmz_vb_release,
+ .mmap = mmz_vb_mmap_pool,
+};
+
+static struct miscdevice mmz_vb_miscdev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = DRIVER_NAME,
+ .fops = &mmz_vb_fops,
+};
+
+static char es_mmz_name_prefix[] = "mmz_nid_";
+static int mmz_vb_init_partitions(void)
+{
+ int ret = 0;
+ struct mmz_vb_priv *mmz_vb_priv = g_mmz_vb_priv;
+ struct esVB_K_MMZ_S *partitions;
+
+ if (NULL == mmz_vb_priv)
+ return -EFAULT;
+
+ partitions = &mmz_vb_priv->partitions;;
+ init_rwsem(&partitions->idr_lock);
+ idr_init(&partitions->pool_idr);
+
+ partitions->partCnt = mmz_vb_init_memory_region();
+ if (partitions->partCnt == 0) {
+ vb_err("No VB memory block was found or correctly initialized!\n");
+ ret = -EFAULT;
+ }
+
+ return ret;
+}
+
+static int mmz_vb_idr_iterate_show(int id, void *p, void *data)
+{
+ struct esVB_K_POOL_INFO_S *pool = (struct esVB_K_POOL_INFO_S *)p;
+ struct esVB_POOL_CONFIG_S *pool_cfg;
+ es_proc_entry_t *s = (es_proc_entry_t *)data;
+
+ spin_lock(&pool->lock);
+ pool_cfg = &pool->poolCfg;
+ es_seq_printf(s, "\t Uid %d, PoolId %d, blkSize 0x%llx, blkCnt %d, "
+ "RemapMode %d, mmzName %s, allocated blkCnt %d\n\r", pool->enVbUid,
+ pool->poolId, pool_cfg->blkSize, pool_cfg->blkCnt,
+ pool_cfg->enRemapMode, pool_cfg->mmzName,
+ pool_cfg->blkCnt - vb_pool_get_free_block_cnt_unlock(pool));
+ spin_unlock(&pool->lock);
+ return 0;
+}
+
+static int mmz_vb_proc_show(es_proc_entry_t *s)
+{
+ int i;
+ struct mmz_vb_priv *vb_priv = g_mmz_vb_priv;
+ struct esVB_K_MMZ_S *partitions = &vb_priv->partitions;
+ unsigned long numFreePages = 0;
+ struct mem_block *memblock = NULL, *rsvmem_block = NULL;
+ int ret;
+
+ es_seq_printf(s, "\nModule: [VB], Build Time[xx]\n");
+ /*
+ use es_seq_printf to show more debug info
+ */
+ es_seq_printf(s, "-----MMZ REGION CONFIG-----\n\r");
+ for (i = 0; i < partitions->partCnt; i++) {
+ rsvmem_block = partitions->mem_blocks[i];
+ memblock = vb_get_memblock(rsvmem_block->name);
+ if (NULL == memblock) {
+ vb_err("%s NOT found!\n", rsvmem_block->name);
+ return -EINVAL;
+ }
+ numFreePages = es_num_free_pages(memblock);
+ es_seq_printf(s, "\tmemblock: %s, total size(0x%lx), free mem size(0x%lx)\n\r",
+ rsvmem_block->name,
+ memblock->page_num << PAGE_SHIFT,
+ numFreePages << PAGE_SHIFT);
+ }
+ es_seq_printf(s, "-----POOL CONFIG-----\n\r");
+ ret = idr_for_each(&partitions->pool_idr, mmz_vb_idr_iterate_show, s);
+ if (ret) {
+ dev_err(mmz_vb_dev, "%s %d, failed to iterate vb pool ret %d\n",
+ __func__,__LINE__, ret);
+ return ret;
+ }
+ return 0;
+}
+
+int mmz_vb_proc_store(struct es_proc_dir_entry *entry, const char *buf,
+ int count, long long *ppos)
+{
+ int ret;
+
+ ret = mmz_vb_pool_exit();
+ if (0 != ret) {
+ dev_err(mmz_vb_dev, "%s %d, failed to release vb pool "
+ "when exit, ret %d\n", __func__,__LINE__, ret);
+ }
+ return count;
+}
+
+void mmz_vb_vb_dbg_init(void)
+{
+ es_proc_entry_t *proc = NULL;
+
+ proc = es_create_proc_entry(PROC_ENTRY_VB, NULL);
+
+ if (proc == NULL) {
+ vb_err("Kernel: Register vb proc failed!\n");
+ return;
+ }
+ proc->read = mmz_vb_proc_show;
+ /*NULL means use the default routine*/
+ proc->write = mmz_vb_proc_store;
+ proc->open = NULL;
+}
+
+static int __init mmz_vb_init(void)
+{
+ int i;
+ int ret = 0;
+ struct device *dev;
+
+ g_mmz_vb_priv = kzalloc(sizeof(struct mmz_vb_priv), GFP_KERNEL);
+ if (!g_mmz_vb_priv) {
+ vb_err("Failed to alloc priv data for mmz_vb driver!!!\n");
+ return -ENOMEM;
+ }
+ ret = misc_register(&mmz_vb_miscdev);
+ if(ret) {
+ vb_err ("cannot register miscdev (err=%d)\n", ret);
+ goto free_vb_priv;
+ }
+ mmz_vb_dev = mmz_vb_miscdev.this_device;
+ g_mmz_vb_priv->dev = mmz_vb_dev;
+ for (i = 0; i < VB_UID_MAX; i++) {
+ hash_init(g_mmz_vb_priv->ht[i]);
+ mutex_init(&g_mmz_vb_priv->cfg_lock[i]);
+ init_rwsem(&g_mmz_vb_priv->pool_lock[i]);
+ }
+
+ ret = mmz_vb_init_partitions();
+ if (ret) {
+ goto deregister_vb;
+ }
+ atomic_set(&g_mmz_vb_priv->allocBlkcnt, 0);
+
+ mmz_vb_vb_dbg_init();
+
+ dev = mmz_vb_dev;
+ if (!dev->dma_mask) {
+ dev->dma_mask = &dev->coherent_dma_mask;
+ }
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48));
+ if (ret)
+ vb_err("Unable to set coherent mask\n");
+ return 0;
+
+deregister_vb:
+ misc_deregister(&mmz_vb_miscdev);
+free_vb_priv:
+ kfree(g_mmz_vb_priv);
+ return ret;
+}
+
+static int mmz_vb_pool_exit(void)
+{
+ struct mmz_vb_priv *vb_priv = g_mmz_vb_priv;
+ struct esVB_K_MMZ_S *partitions = &vb_priv->partitions;
+ struct esVB_K_POOL_INFO_S *pool = NULL;
+ int ret = 0;
+ u32 id = 0;
+ int i;
+
+ down_write(&partitions->idr_lock);
+ idr_for_each_entry(&partitions->pool_idr, pool, id) {
+ ret = mmz_vb_do_destory_pool(pool, false, true);
+ if (ret) {
+ dev_err(mmz_vb_dev, "%s %d, failed to destory vb pool, ret %d\n",
+ __func__,__LINE__, ret);
+ continue;
+ }
+ }
+
+ up_write(&partitions->idr_lock);
+
+ atomic_set(&vb_priv->allocBlkcnt, 0);
+ for (i = 0; i < VB_UID_MAX; i++) {
+ if (NULL != vb_priv->pVbConfig[i]) {
+ devm_kfree(mmz_vb_dev, vb_priv->pVbConfig[i]);
+ vb_priv->pVbConfig[i] = NULL;
+ }
+ }
+ memset(vb_priv->cfg_flag, 0, sizeof(unsigned long) * VB_UID_MAX);
+ return 0;
+}
+
+static void __exit mmz_vb_exit(void)
+{
+ struct mmz_vb_priv *vb_priv = g_mmz_vb_priv;
+ int ret = 0;
+
+ ret = mmz_vb_pool_exit();
+ if (0 != ret) {
+ dev_err(mmz_vb_dev, "%s %d, failed to release vb pool "
+ "when exit, ret %d\n", __func__,__LINE__, ret);
+ }
+ es_remove_proc_entry(PROC_ENTRY_VB, NULL);
+ misc_deregister(&mmz_vb_miscdev);
+ kfree(vb_priv);
+}
+
+module_init(mmz_vb_init);
+module_exit(mmz_vb_exit);
+
+MODULE_DESCRIPTION("MMZ VB Driver");
+MODULE_AUTHOR("Lin MIn <linmin@eswincomputing.com>");
+MODULE_LICENSE("GPL v2");
+
+
+static int vb_find_pool_by_id_unlock(VB_POOL poolId, struct esVB_K_POOL_INFO_S **ppPool)
+{
+ struct mmz_vb_priv *vb_priv = g_mmz_vb_priv;
+ struct esVB_K_MMZ_S *partitions = &vb_priv->partitions;
+ struct esVB_K_POOL_INFO_S *pool = NULL;
+
+ pool = idr_find(&partitions->pool_idr, poolId);
+ if (!pool) {
+ dev_err(mmz_vb_dev, "%s %d, faild to find pool by id %d\n",
+ __func__,__LINE__, poolId);
+ return -EINVAL;
+ }
+ *ppPool = pool;
+ return 0;
+}
+
+static int vb_find_pool_by_id(VB_POOL poolId, struct esVB_K_POOL_INFO_S **ppPool)
+{
+ struct mmz_vb_priv *vb_priv = g_mmz_vb_priv;
+ struct esVB_K_MMZ_S *partitions = &vb_priv->partitions;
+ int ret;
+
+ down_read(&partitions->idr_lock);
+ ret = vb_find_pool_by_id_unlock(poolId, ppPool);
+ up_read(&partitions->idr_lock);
+ return ret;
+}
+
+static int vb_pool_size(VB_POOL poolId, u64 *pPoolSize)
+{
+ int ret = 0;
+ struct esVB_K_POOL_INFO_S *pPool;
+
+ ret = vb_find_pool_by_id(poolId, &pPool);
+ if (ret) {
+ vb_info("failed to find pool %d\n", poolId);
+ return ret;
+ }
+
+ *pPoolSize = do_vb_pool_size(pPool);
+
+ return ret;
+}
+#if 0
+static int vb_flush_pool(struct esVB_FLUSH_POOL_CMD_S *flushPoolCmd)
+{
+ int ret = 0;
+ struct esVB_K_POOL_INFO_S *pPool = NULL;
+ struct esVB_K_BLOCK_INFO_S *pBlk = NULL;
+ u64 blkSize, poolSize = 0;
+ u64 offset_inPool = 0, offset_inBlk = 0, size, left_size = 0;
+ u64 phy_addr;
+ u32 i;
+
+ ret = vb_find_pool_by_id(flushPoolCmd->poolId, &pPool);
+ if (ret) {
+ vb_info("%s,failed to find pool %d\n", __func__, flushPoolCmd->poolId);
+ return ret;
+ }
+
+ poolSize = do_vb_pool_size(pPool);
+ if ((flushPoolCmd->offset + flushPoolCmd->size - 1) >= poolSize)
+ return -EINVAL;
+
+ // find the block according to the offset
+ blkSize = pPool->poolCfg.blkSize;
+ pBlk = pPool->blocks;
+ left_size = flushPoolCmd->size;
+ for (i = 0; i < pPool->poolCfg.blkCnt; i++) {
+ if ((offset_inPool + blkSize -1) >= flushPoolCmd->offset)
+ break;
+ offset_inPool += blkSize;
+ pBlk++;
+ }
+ offset_inBlk = flushPoolCmd->offset - offset_inPool;
+ for (; i < pPool->poolCfg.blkCnt; i++) {
+ struct page *page = pBlk->cma_pages;
+ size = min(left_size, (blkSize - offset_inBlk));
+ phy_addr = page_to_phys(page) + offset_inBlk;
+ arch_sync_dma_for_device(phy_addr, size, DMA_TO_DEVICE);
+ left_size -= size;
+ if (left_size == 0)
+ break;
+ pBlk++;
+ offset_inBlk = 0;
+ }
+
+ return ret;
+}
+#endif
+static int vb_pool_get_free_block_cnt_unlock(struct esVB_K_POOL_INFO_S *pool)
+{
+ int count = 0;
+ int start = 0;
+ struct esVB_POOL_CONFIG_S *pool_cfg = &pool->poolCfg;
+ int nr;
+
+ while (true) {
+ nr = find_next_zero_bit(pool->bitmap, pool_cfg->blkCnt, start);
+ if (likely(nr < pool_cfg->blkCnt)) {
+ count++;
+ start = nr + 1;
+ } else {
+ break;
+ }
+ }
+ return count;
+}
+
+static int vb_pool_get_block(struct esVB_K_POOL_INFO_S *pool,
+ struct esVB_K_BLOCK_INFO_S **ppBlk)
+{
+ unsigned int nr = -1U;
+ struct esVB_POOL_CONFIG_S *pool_cfg;
+ int ret = -EINVAL;
+ struct mmz_vb_priv *vb_priv = g_mmz_vb_priv;
+
+ spin_lock(&pool->lock);
+ pool_cfg = &pool->poolCfg;
+ nr = find_next_zero_bit(pool->bitmap, pool_cfg->blkCnt, 0);
+ if (likely(nr < pool_cfg->blkCnt)) {
+ ret = 0;
+ *ppBlk = &pool->blocks[nr];
+ bitmap_set(pool->bitmap, nr, 1);
+ if (atomic_inc_return(&vb_priv->allocBlkcnt) == 1) {
+ __module_get(THIS_MODULE);
+ }
+ } else {
+ dev_warn(mmz_vb_dev, "%s %d, pool %d used up, blkSize 0x%llx,"
+ "blkCnt 0x%x\n",__func__,__LINE__, pool->poolId,
+ pool_cfg->blkSize, pool_cfg->blkCnt);
+ }
+ spin_unlock(&pool->lock);
+ return ret;
+}
+
+static int vb_get_block(struct esVB_GET_BLOCK_CMD_S *getBlkCmd,
+ struct esVB_K_BLOCK_INFO_S **ppBlk)
+{
+ int ret = -EINVAL;
+ struct mmz_vb_priv *vb_priv = g_mmz_vb_priv;
+ struct esVB_K_MMZ_S *partitions = &vb_priv->partitions;
+ struct esVB_GET_BLOCK_REQ_S *req = &getBlkCmd->getBlkReq;
+ struct esVB_K_POOL_INFO_S *pool = NULL, *pool_tmp = NULL;
+ struct esVB_POOL_CONFIG_S *pool_cfg;
+ unsigned long bkt = 0;
+
+ if (VB_UID_PRIVATE == req->uid) {
+ down_read(&partitions->idr_lock);
+ ret = vb_find_pool_by_id_unlock(req->poolId, &pool);
+ if (ret) {
+ up_read(&partitions->idr_lock);
+ dev_err(mmz_vb_dev, "%s %d, failed to find pool by id %d!\n",__func__,__LINE__, req->poolId);
+ return -EINVAL;
+ }
+ if (test_bit(MMZ_VB_POOL_FLAG_DESTORY, &pool->flag)) {
+ up_read(&partitions->idr_lock);
+ dev_err(mmz_vb_dev, "%s %d, pool %d is in destory state, not allow "
+ "to alloc block!\n",__func__,__LINE__, req->poolId);
+ return -ENOTSUPP;
+ }
+ pool_cfg = &pool->poolCfg;
+ if (req->blkSize > pool_cfg->blkSize) {
+ up_read(&partitions->idr_lock);
+ dev_err(mmz_vb_dev, "%s %d, pool blkSize 0x%llx is "
+ "smaller than request size 0x%llx\n",__func__,__LINE__,
+ pool_cfg->blkSize, req->blkSize);
+ return -EINVAL;
+ }
+ ret = vb_pool_get_block(pool, ppBlk);
+ up_read(&partitions->idr_lock);
+ } else if (req->uid >= VB_UID_COMMON && req->uid < VB_UID_MAX) {
+ down_read(&vb_priv->pool_lock[req->uid]);
+ /*try to get block for the exact block size */
+ hash_for_each_possible(vb_priv->ht[req->uid], pool, node, PAGE_ALIGN(req->blkSize)) {
+ pool_cfg = &pool->poolCfg;
+ if (PAGE_ALIGN(req->blkSize) == pool_cfg->blkSize &&
+ !strcmp(req->mmzName, pool_cfg->mmzName)) {
+ ret = vb_pool_get_block(pool, ppBlk);
+ if (0 == ret) {
+ break;
+ }
+ }
+ }
+ /*try to get block from the pool whose block size > req->blkSize*/
+ if (0 != ret) {
+ hash_for_each(vb_priv->ht[req->uid], bkt, pool, node) {
+ pool_cfg = &pool->poolCfg;
+ if (req->blkSize < pool_cfg->blkSize &&
+ !strcmp(req->mmzName, pool_cfg->mmzName)) {
+ /*get the pool size which is closest to the req->blkSize*/
+ if ((NULL == pool_tmp || (pool_tmp->poolCfg.blkSize > pool->poolCfg.blkSize))
+ && vb_pool_get_free_block_cnt_unlock(pool)) {
+ pool_tmp = pool;
+ }
+ }
+ }
+ if (NULL != pool_tmp) {
+ ret = vb_pool_get_block(pool_tmp, ppBlk);
+ }
+ }
+ up_read(&vb_priv->pool_lock[req->uid]);
+ } else {
+ dev_err(mmz_vb_dev, "%s %d, invaild uid %d\n",__func__,__LINE__, req->uid);
+ }
+ return ret;
+}
+
+static void vb_release_block(struct esVB_K_BLOCK_INFO_S *pBlk)
+{
+ struct esVB_K_POOL_INFO_S *pool;
+ struct mmz_vb_priv *vb_priv = g_mmz_vb_priv;
+ struct esVB_K_MMZ_S *partitions = &vb_priv->partitions;
+ bool need_destory = false;
+ struct rw_semaphore *lock;
+ int ret;
+
+ pool = pBlk->pool;
+
+ lock = VB_UID_PRIVATE == pool->enVbUid ? \
+ &partitions->idr_lock : &vb_priv->pool_lock[pool->enVbUid];
+ /*
+ usually we don't need to destory pool here.
+ so just get read lock first.
+ */
+ down_read(lock);
+ spin_lock(&pool->lock);
+ bitmap_clear(pool->bitmap, pBlk->nr, 1);
+ if (bitmap_empty(pool->bitmap, pool->poolCfg.blkCnt) &&
+ test_bit(MMZ_VB_POOL_FLAG_DESTORY, &pool->flag)) {
+ need_destory = true;
+ }
+ spin_unlock(&pool->lock);
+ up_read(lock);
+ if (atomic_dec_return(&vb_priv->allocBlkcnt) == 0) {
+ module_put(THIS_MODULE);
+ }
+
+ if (true == need_destory) {
+ down_write(lock);
+ ret = mmz_vb_do_destory_pool(pool, false, false);
+ if (ret) {
+ dev_err(mmz_vb_dev, "%s %d, faild to destory pool, enVbUid %d, PoolId %d, ret %d\n",
+ __func__,__LINE__, pool->enVbUid, pool->poolId, ret);
+ }
+ up_write(lock);
+ }
+}
+
+static int vb_is_splitted_blk(int fd, bool *isSplittedBlk)
+{
+ int ret = 0;
+ struct dma_buf *dmabuf;
+
+ /* get dmabuf handle */
+ dmabuf = dma_buf_get(fd);
+ if (IS_ERR(dmabuf)) {
+ return -EINVAL;
+ }
+
+ if (strncmp(dmabuf->exp_name, MMZ_VB_DMABUF_NAME, sizeof(MMZ_VB_DMABUF_NAME))) {
+ vb_err("It's NOT a mmz_vb buffer!!!\n");
+ dma_buf_put(dmabuf);
+ return -EINVAL;
+ }
+
+ if (!strncmp(dmabuf->exp_name, MMZ_VB_DMABUF_SPLITTED_NAME, sizeof(MMZ_VB_DMABUF_SPLITTED_NAME)))
+ *isSplittedBlk = true;
+ else
+ *isSplittedBlk = false;
+
+ dma_buf_put(dmabuf);
+ return ret;
+}
+
+static int vb_blk_to_pool(struct esVB_BLOCK_TO_POOL_CMD_S *blkToPoolCmd)
+{
+ int ret = 0;
+ struct dma_buf *dmabuf;
+ struct mmz_vb_buffer *buffer;
+ struct esw_export_buffer_info *splittedBuffer;
+ struct dma_buf *blkDmabuf;
+ bool isSplittedBlk;
+
+ /* get dmabuf handle */
+ dmabuf = dma_buf_get(blkToPoolCmd->fd);
+ if (IS_ERR(dmabuf)) {
+ return -EINVAL;
+ }
+
+ ret = vb_is_splitted_blk(blkToPoolCmd->fd, &isSplittedBlk);
+ if (ret) {
+ dma_buf_put(dmabuf);
+ ret = -EINVAL;
+ goto out_put_dmabuf;
+ }
+
+ if (true == isSplittedBlk) { // This is a splitted block
+ splittedBuffer = dmabuf->priv;
+ blkDmabuf = dma_buf_get(splittedBuffer->dbuf_fd);
+ if (IS_ERR(blkDmabuf)) {
+ ret = -EINVAL;
+ goto out_put_dmabuf;
+ }
+ buffer = blkDmabuf->priv;
+ blkToPoolCmd->poolId = buffer->pBlk->pool->poolId;
+ dma_buf_put(blkDmabuf);
+ }
+ else { // This is a real block
+ buffer = dmabuf->priv;
+ blkToPoolCmd->poolId = buffer->pBlk->pool->poolId;
+ }
+
+out_put_dmabuf:
+ dma_buf_put(dmabuf);
+ return ret;
+}
+
+static int vb_get_blk_offset(struct esVB_GET_BLOCKOFFSET_CMD_S *getBlkOffsetCmd)
+{
+ int ret = 0;
+ struct dma_buf *dmabuf;
+ struct mmz_vb_buffer *buffer;
+ struct esw_export_buffer_info *splittedBuffer;
+ struct dma_buf *blkDmabuf;
+ __u64 blkSize, offsetInPool;
+
+ bool isSplittedBlk;
+
+ dmabuf = dma_buf_get(getBlkOffsetCmd->fd);
+ if (IS_ERR(dmabuf)) {
+ return -EINVAL;
+ }
+
+ ret = vb_is_splitted_blk(getBlkOffsetCmd->fd, &isSplittedBlk);
+ if (ret) {
+ ret = -EINVAL;
+ goto out_put_dmabuf;
+ }
+
+ if (true == isSplittedBlk) { // It's a splitted block
+ splittedBuffer = dmabuf->priv;
+ blkDmabuf = dma_buf_get(splittedBuffer->dbuf_fd);
+ if (IS_ERR(blkDmabuf)) {
+ ret = -EINVAL;
+ goto out_put_dmabuf;
+ }
+ buffer = blkDmabuf->priv;
+ blkSize = buffer->len;
+ offsetInPool = blkSize * buffer->pBlk->nr + splittedBuffer->slice.offset;
+ dma_buf_put(blkDmabuf);
+ }
+ else { // It's a real block
+ buffer = dmabuf->priv;
+ blkSize = buffer->len;
+ offsetInPool = blkSize * buffer->pBlk->nr;
+ }
+ getBlkOffsetCmd->offset = offsetInPool;
+
+out_put_dmabuf:
+ dma_buf_put(dmabuf);
+
+ return ret;
+}
+
+static int vb_split_dmabuf(struct esVB_SPLIT_DMABUF_CMD_S *splitDmabufCmd)
+{
+ int ret = 0;
+ struct dma_buf *dmabuf;
+ char splittedBuffer_ExpName[ES_MAX_MMZ_NAME_LEN];
+ int i;
+
+ if (!splitDmabufCmd->len)
+ return -EINVAL;
+
+ /* get dmabuf handle */
+ dmabuf = dma_buf_get(splitDmabufCmd->fd);
+ if (IS_ERR(dmabuf)) {
+ return -EINVAL;
+ }
+
+ if (strstr(dmabuf->exp_name, "_splitted")) { // It's a splitted dmabuf already, can't be splitted further
+ vb_err("Can't split a splitted buffer!!!\n");
+ ret = -EINVAL;
+ goto out_put_dmabuf;
+ }
+
+ /* offset and len must be paged aligned */
+ if (!PAGE_ALIGNED(splitDmabufCmd->offset) || !PAGE_ALIGNED(splitDmabufCmd->len)) {
+ vb_err("splitted offset or len is not page aligned!!!\n");
+ ret = -EINVAL;
+ goto out_put_dmabuf;
+ }
+
+ if (splitDmabufCmd->offset + splitDmabufCmd->len > dmabuf->size) {
+ vb_err("Splitted offset(0x%llx)+len(0x%llx) exceed the size(0x%llx) of the original buffer!!!\n",
+ splitDmabufCmd->offset, splitDmabufCmd->len, (__u64)dmabuf->size);
+ ret = -EINVAL;
+ goto out_put_dmabuf;
+ }
+
+ /* Apend "_splitted" to the splitted buffer expname, so that it is identified by the suffix */
+ i = snprintf(splittedBuffer_ExpName, sizeof(splittedBuffer_ExpName), "%s_splitted", dmabuf->exp_name);
+ if (i > sizeof(splittedBuffer_ExpName)) {
+ vb_err("Length of name(%d) for the the splitted buffer exceed the max name len(%ld)!!!\n",
+ i, sizeof(splittedBuffer_ExpName));
+ ret = -EINVAL;
+ goto out_put_dmabuf;
+ }
+
+ splitDmabufCmd->slice_fd = esw_common_dmabuf_split_export(splitDmabufCmd->fd, splitDmabufCmd->offset,
+ splitDmabufCmd->len, dmabuf->file->f_flags, splittedBuffer_ExpName);
+ if (splitDmabufCmd->slice_fd < 0) {
+ vb_err("Failed to split buffer, errVal %d\n", splitDmabufCmd->slice_fd);
+ ret = -EFAULT;
+ goto out_put_dmabuf;
+ }
+
+out_put_dmabuf:
+ dma_buf_put(dmabuf);
+
+ return ret;
+}
+
+static int vb_get_dmabuf_refcnt(struct esVB_DMABUF_REFCOUNT_CMD_S *getDmabufRefCntCmd)
+{
+ int ret = 0;
+ struct dma_buf *dmabuf;
+
+ /* get dmabuf handle */
+ dmabuf = dma_buf_get(getDmabufRefCntCmd->fd);
+ if (IS_ERR(dmabuf)) {
+ return -EINVAL;
+ }
+
+ /* minus 1 because it was +1 by dma_buf_get */
+ getDmabufRefCntCmd->refCnt = file_count(dmabuf->file) - 1;
+
+ dma_buf_put(dmabuf);
+ return ret;
+}
+
+#define PAGE_IN_SPRAM_DIE0(page) ((page_to_phys(page)>=0x59000000) && (page_to_phys(page)<0x59400000))
+#define PAGE_IN_SPRAM_DIE1(page) ((page_to_phys(page)>=0x79000000) && (page_to_phys(page)<0x79400000))
+static int do_vb_retrive_mem_node(struct dma_buf *dmabuf, int *nid)
+{
+ int ret = 0;
+ struct dma_buf_attachment *attach;
+ struct sg_table *sgt;
+ struct page *page = NULL;
+
+ get_dma_buf(dmabuf);
+ attach = dma_buf_attach(dmabuf, mmz_vb_dev);
+ if (IS_ERR(attach)) {
+ ret = PTR_ERR(attach);
+ /* put dmabuf back */
+ dma_buf_put(dmabuf);
+ return ret;
+ }
+
+ sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
+ if (IS_ERR(sgt)) {
+ ret = PTR_ERR(sgt);
+ dma_buf_detach(dmabuf, attach);
+ dma_buf_put(dmabuf);
+ return ret;
+ }
+
+ page = sg_page(sgt->sgl);
+ if (unlikely(PAGE_IN_SPRAM_DIE0(page))) {
+ *nid = 0;
+ }
+ else if(unlikely(PAGE_IN_SPRAM_DIE1(page))) {
+ *nid = 1;
+ }
+ else
+ *nid = page_to_nid(page);
+
+ dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL);
+ /* detach */
+ dma_buf_detach(dmabuf, attach);
+ /* put dmabuf back */
+ dma_buf_put(dmabuf);
+
+ pr_debug("%s, mem node is %d\n", __func__, *nid);
+ return ret;
+}
+
+static int vb_retrieve_mem_node(struct esVB_RETRIEVE_MEM_NODE_CMD_S *retrieveMemNodeCmd)
+{
+ int ret = 0;
+ struct dma_buf *dmabuf;
+ struct vm_area_struct *vma = NULL;
+ vm_flags_t vm_flags;
+ struct mm_struct *mm = current->mm;
+ u64 vaddr;
+
+ /* If cpu_vaddr is NULL, then try to retrieve mem node id by fd */
+ if (retrieveMemNodeCmd->cpu_vaddr == NULL) {
+ /* get dmabuf handle */
+ dmabuf = dma_buf_get(retrieveMemNodeCmd->fd);
+ if (IS_ERR(dmabuf)) {
+ return PTR_ERR(dmabuf);
+ }
+
+ ret = do_vb_retrive_mem_node(dmabuf, &retrieveMemNodeCmd->numa_node);
+ /* put dmabuf back */
+ dma_buf_put(dmabuf);
+ }
+ else {
+ vaddr = (u64)retrieveMemNodeCmd->cpu_vaddr;
+ mmap_read_lock(mm);
+ vma = vma_lookup(mm, vaddr & PAGE_MASK);
+ if (!vma) {
+ pr_err("Failed to vma_lookup!\n");
+ return -EFAULT;
+ }
+ vm_flags = vma->vm_flags;
+ mmap_read_unlock(mm);
+
+ if (!(vm_flags & (VM_IO | VM_PFNMAP)) || (NULL == vma->vm_private_data)) {
+ pr_debug("This vaddr is NOT mmapped with VM_PFNMAP!\n");
+ return -EFAULT;
+ }
+ dmabuf = vma->vm_private_data;
+ ret = do_vb_retrive_mem_node(dmabuf, &retrieveMemNodeCmd->numa_node);
+ }
+
+ return ret;
+}
+
+static int vb_get_dmabuf_size(struct esVB_DMABUF_SIZE_CMD_S *getDmabufSizeCmd)
+{
+ int ret = 0;
+ struct dma_buf *dmabuf;
+
+ /* get dmabuf handle */
+ dmabuf = dma_buf_get(getDmabufSizeCmd->fd);
+ if (IS_ERR(dmabuf)) {
+ return -EINVAL;
+ }
+
+ /* minus 1 because it was +1 by dma_buf_get */
+ getDmabufSizeCmd->size = dmabuf->size;
+
+ dma_buf_put(dmabuf);
+
+ return ret;
+}
+
+static int mmz_vb_init_memory_region(void)
+{
+ struct mmz_vb_priv *mmz_vb_priv = g_mmz_vb_priv;
+ struct esVB_K_MMZ_S *partitions = &mmz_vb_priv->partitions;
+ int nid, part = 0;
+ int partitionID = 0;
+ char blkName[BLOCK_MAX_NAME];
+ struct mem_block *memblock = NULL;
+
+ for (nid = 0; nid < 2; nid++) {
+ for (part = 0; part < 2; part++) {
+ snprintf(blkName, sizeof(blkName), "%s%d_part_%d", es_mmz_name_prefix, nid, part);
+ memblock = eswin_rsvmem_get_memblock(blkName);
+ if (memblock) {
+ partitions->mem_blocks[partitionID] = memblock;
+ dev_info(mmz_vb_dev, "%s was found successfully\n", blkName);
+ partitionID++;
+ }
+ else {
+ dev_dbg(mmz_vb_dev, "%s was NOT found\n", blkName);
+ }
+ }
+ }
+
+ /* Indicate how many VB memory block have been correctly initialized */
+ return partitionID;
+}
diff --git a/drivers/memory/eswin/es_proc/Kconfig b/drivers/memory/eswin/es_proc/Kconfig
new file mode 100644
index 000000000000..d1a2f8220e9b
--- /dev/null
+++ b/drivers/memory/eswin/es_proc/Kconfig
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config ESWIN_PROC
+ tristate "ESWIN MMZ proc interface"
+ help
+ MMZ proc interface for user space.
+
diff --git a/drivers/memory/eswin/es_proc/Makefile b/drivers/memory/eswin/es_proc/Makefile
new file mode 100644
index 000000000000..2afd49103e81
--- /dev/null
+++ b/drivers/memory/eswin/es_proc/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_ESWIN_PROC) += es_proc.o
+ccflags-y := -DDEBUG
+
+ES_PROC_HEADER := drivers/memory/eswin/es_proc/include/linux
+
+COPY_HEADERS := $(shell cp $(ES_PROC_HEADER)/*.h include/linux)
diff --git a/drivers/memory/eswin/es_proc/es_proc.c b/drivers/memory/eswin/es_proc/es_proc.c
new file mode 100644
index 000000000000..b0eb521b247c
--- /dev/null
+++ b/drivers/memory/eswin/es_proc/es_proc.c
@@ -0,0 +1,233 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ESWIN proc APIs for MMZ_VB
+ *
+ * Copyright 2024 Beijing ESWIN Computing Technology Co., Ltd.
+ * Authors:
+ * HuangYiFeng<huangyifeng@eswincomputing.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/printk.h>
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include "include/linux/es_proc.h"
+
+static struct list_head list;
+static es_proc_entry_t *proc_entry = NULL;
+
+static int es_seq_show(struct seq_file *s, void *p)
+{
+ es_proc_entry_t *oldsentry = s->private;
+ es_proc_entry_t sentry;
+
+ if (oldsentry == NULL) {
+ pr_err("%s %d- parameter invalid!\n", __func__,__LINE__);
+ return -1;
+ }
+ memset(&sentry, 0, sizeof(es_proc_entry_t));
+ /* only these two parameters are used */
+ sentry.seqfile = s;
+ sentry.private = oldsentry->private;
+ oldsentry->read(&sentry);
+ return 0;
+}
+
+static ssize_t es_procwrite(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ es_proc_entry_t *item = pde_data(file_inode(file));
+
+ if ((item != NULL) && (item->write != NULL)) {
+ return item->write(item, buf, count, (long long *)ppos);
+ }
+
+ return -ENOSYS;
+}
+
+static int es_procopen(struct inode *inode, struct file *file)
+{
+ es_proc_entry_t *sentry = pde_data(inode);
+
+ if ((sentry != NULL) && (sentry->open != NULL)) {
+ sentry->open(sentry);
+ }
+ return single_open(file, es_seq_show, sentry);
+}
+
+static const struct proc_ops es_proc_ops = {
+ .proc_open = es_procopen,
+ .proc_read = seq_read,
+ .proc_write = es_procwrite,
+ .proc_lseek = seq_lseek,
+ .proc_release = single_release
+};
+
+es_proc_entry_t *es_create_proc(const char *name, es_proc_entry_t *parent)
+{
+ struct proc_dir_entry *entry = NULL;
+ es_proc_entry_t *sentry = NULL;
+
+ sentry = kzalloc(sizeof(struct es_proc_dir_entry), GFP_KERNEL);
+ if (sentry == NULL) {
+ pr_err("%s %d - kmalloc failed!\n",__func__,__LINE__);
+ return NULL;
+ }
+
+ strncpy(sentry->name, name, sizeof(sentry->name) - 1);
+
+ if (parent == NULL) {
+ entry = proc_create_data(name, 0, NULL, &es_proc_ops, sentry);
+ } else {
+ entry = proc_create_data(name, 0, parent->proc_dir_entry, &es_proc_ops, sentry);
+ }
+ if (entry == NULL) {
+ pr_err("%s %d - create_proc_entry failed!\n",__func__,__LINE__);
+ kfree(sentry);
+ sentry = NULL;
+ return NULL;
+ }
+ sentry->proc_dir_entry = entry;
+ sentry->open = NULL;
+
+ list_add_tail(&(sentry->node), &list);
+ return sentry;
+}
+
+void es_remove_proc(const char *name, es_proc_entry_t *parent)
+{
+ struct es_proc_dir_entry *sproc = NULL;
+
+ if (name == NULL) {
+ pr_err("%s %d - parameter invalid!\n",__func__,__LINE__);
+ return;
+ }
+ if (parent != NULL) {
+ remove_proc_entry(name, parent->proc_dir_entry);
+ } else {
+ remove_proc_entry(name, NULL);
+ }
+ list_for_each_entry(sproc, &list, node) {
+ if (strncmp(sproc->name, name, sizeof(sproc->name)) == 0) {
+ list_del(&(sproc->node));
+ break;
+ }
+ }
+ if (sproc != NULL) {
+ kfree(sproc);
+ }
+}
+
+es_proc_entry_t *es_create_proc_entry(const char *name,
+ es_proc_entry_t *parent)
+{
+ parent = proc_entry;
+
+ return es_create_proc(name, parent);
+}
+EXPORT_SYMBOL(es_create_proc_entry);
+
+void es_remove_proc_entry(const char *name, es_proc_entry_t *parent)
+{
+ parent = proc_entry;
+ es_remove_proc(name, parent);
+ return;
+}
+EXPORT_SYMBOL(es_remove_proc_entry);
+
+es_proc_entry_t *es_proc_mkdir(const char *name, es_proc_entry_t *parent)
+{
+ struct proc_dir_entry *proc = NULL;
+ struct es_proc_dir_entry *sproc = NULL;
+
+ sproc = kzalloc(sizeof(struct es_proc_dir_entry), GFP_KERNEL);
+ if (sproc == NULL) {
+ pr_err("%s %d - kmalloc failed!\n",__func__,__LINE__);
+ return NULL;
+ }
+
+ strncpy(sproc->name, name, sizeof(sproc->name) - 1);
+
+ if (parent != NULL) {
+ proc = proc_mkdir_data(name, 0, parent->proc_dir_entry, sproc);
+ } else {
+ proc = proc_mkdir_data(name, 0, NULL, sproc);
+ }
+ if (proc == NULL) {
+ pr_err("%s %d - proc_mkdir failed!\n",__func__,__LINE__);
+ kfree(sproc);
+ sproc = NULL;
+ return NULL;
+ }
+ sproc->proc_dir_entry = proc;
+
+ list_add_tail(&(sproc->node), &list);
+ return sproc;
+}
+EXPORT_SYMBOL(es_proc_mkdir);
+
+void es_remove_proc_root(const char *name, es_proc_entry_t *parent)
+{
+ struct es_proc_dir_entry *sproc = NULL;
+
+ if (name == NULL) {
+ pr_err("%s %d - parameter invalid!\n",__func__,__LINE__);
+ return;
+ }
+ if (parent != NULL) {
+ remove_proc_entry(name, parent->proc_dir_entry);
+ } else {
+ remove_proc_entry(name, NULL);
+ }
+ list_for_each_entry(sproc, &list, node) {
+ if (strncmp(sproc->name, name, sizeof(sproc->name)) == 0) {
+ list_del(&(sproc->node));
+ break;
+ }
+ }
+ if (sproc != NULL) {
+ kfree(sproc);
+ }
+}
+
+int es_seq_printf(es_proc_entry_t *entry, const char *fmt, ...)
+{
+ struct seq_file *s = (struct seq_file *)(entry->seqfile);
+ va_list args;
+ int r = 0;
+
+ va_start(args, fmt);
+ seq_vprintf(s, fmt, args);
+ va_end(args);
+
+ return r;
+}
+EXPORT_SYMBOL(es_seq_printf);
+
+static int __init es_proc_init(void)
+{
+ INIT_LIST_HEAD(&list);
+ proc_entry = es_proc_mkdir("umap", NULL);
+ if (proc_entry == NULL) {
+ pr_err("init, proc mkdir error!\n");
+ return -EPERM;
+ }
+ return 0;
+}
+
+static void __exit es_proc_exit(void)
+{
+ es_remove_proc_root("umap", NULL);
+}
+
+module_init(es_proc_init);
+module_exit(es_proc_exit);
+
+MODULE_DESCRIPTION("ES Procfile Driver");
+MODULE_AUTHOR("huangyifeng@eswincomputing.com");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/memory/eswin/es_proc/include/linux/es_proc.h b/drivers/memory/eswin/es_proc/include/linux/es_proc.h
new file mode 100644
index 000000000000..65c73460e11b
--- /dev/null
+++ b/drivers/memory/eswin/es_proc/include/linux/es_proc.h
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Header file of es_proc.c
+ *
+ * Copyright 2024 Beijing ESWIN Computing Technology Co., Ltd.
+ * Authors:
+ * HuangYiFeng<huangyifeng@eswincomputing.com>
+ *
+ */
+
+#ifndef __ES_PROC__
+#define __ES_PROC__
+
+#define PROC_ENTRY_VI "vi"
+#define PROC_ENTRY_VO "vo"
+#define PROC_ENTRY_VB "vb"
+#define PROC_ENTRY_ISP "isp"
+
+// proc
+typedef struct es_proc_dir_entry {
+ char name[50];
+ void *proc_dir_entry;
+ int (*open)(struct es_proc_dir_entry *entry);
+ int (*read)(struct es_proc_dir_entry *entry);
+ int (*write)(struct es_proc_dir_entry *entry, const char *buf,
+ int count, long long *);
+ void *private;
+ void *seqfile;
+ struct list_head node;
+} es_proc_entry_t;
+
+extern es_proc_entry_t *es_create_proc_entry(const char *name,
+ es_proc_entry_t *parent);
+extern es_proc_entry_t *es_proc_mkdir(const char *name,
+ es_proc_entry_t *parent);
+extern void es_remove_proc_entry(const char *name, es_proc_entry_t *parent);
+extern int es_seq_printf(es_proc_entry_t *entry, const char *fmt, ...)
+ __attribute__((format(printf, 2, 3)));
+
+#endif
diff --git a/drivers/memory/eswin/es_rsvmem_heap/Kconfig b/drivers/memory/eswin/es_rsvmem_heap/Kconfig
new file mode 100644
index 000000000000..3b425da42e2f
--- /dev/null
+++ b/drivers/memory/eswin/es_rsvmem_heap/Kconfig
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config ESWIN_RSVMEM_HEAP
+ tristate "ESWIN reserved memory heap"
+ help
+ ESWIN reserved memory heap device.
diff --git a/drivers/memory/eswin/es_rsvmem_heap/Makefile b/drivers/memory/eswin/es_rsvmem_heap/Makefile
new file mode 100644
index 000000000000..e468c514bb7b
--- /dev/null
+++ b/drivers/memory/eswin/es_rsvmem_heap/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_ESWIN_RSVMEM_HEAP) += eswin_rsvmem_heap.o eswin_rsvmem_common.o dmabuf-heap-import-helper.o
+
+
+ES_RSVMEM_HEADER := drivers/memory/eswin/es_rsvmem_heap/include/linux
+
+COPY_HEADERS := $(shell cp $(ES_RSVMEM_HEADER)/*.h include/linux)
+
diff --git a/drivers/memory/eswin/es_rsvmem_heap/dmabuf-heap-import-helper.c b/drivers/memory/eswin/es_rsvmem_heap/dmabuf-heap-import-helper.c
new file mode 100644
index 000000000000..fdbcfe7e6c70
--- /dev/null
+++ b/drivers/memory/eswin/es_rsvmem_heap/dmabuf-heap-import-helper.c
@@ -0,0 +1,652 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ESWIN DMABUF heap helper APIs
+ *
+ * Copyright 2024 Beijing ESWIN Computing Technology Co., Ltd.
+ * Authors:
+ * LinMin<linmin@eswincomputing.com>
+ *
+ */
+
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/scatterlist.h>
+#include <linux/dma-heap.h>
+#include <linux/dmabuf-heap-import-helper.h>
+
+struct drm_prime_member {
+ struct dma_buf *dma_buf;
+ uint64_t handle;
+
+ struct rb_node dmabuf_rb;
+ struct rb_node handle_rb;
+};
+
+static int dmabuf_heap_add_buf_handle(struct dmaheap_file_private *prime_fpriv,
+ struct dma_buf *dma_buf, uint64_t handle)
+{
+ struct drm_prime_member *member;
+ struct rb_node **p, *rb;
+
+ member = kmalloc(sizeof(*member), GFP_KERNEL);
+ if (!member)
+ return -ENOMEM;
+
+ get_dma_buf(dma_buf);
+ member->dma_buf = dma_buf;
+ member->handle = handle;
+
+ rb = NULL;
+ p = &prime_fpriv->dmabufs.rb_node;
+ while (*p) {
+ struct drm_prime_member *pos;
+
+ rb = *p;
+ pos = rb_entry(rb, struct drm_prime_member, dmabuf_rb);
+ if (dma_buf > pos->dma_buf)
+ p = &rb->rb_right;
+ else
+ p = &rb->rb_left;
+ }
+ rb_link_node(&member->dmabuf_rb, rb, p);
+ rb_insert_color(&member->dmabuf_rb, &prime_fpriv->dmabufs);
+
+ rb = NULL;
+ p = &prime_fpriv->handles.rb_node;
+ while (*p) {
+ struct drm_prime_member *pos;
+
+ rb = *p;
+ pos = rb_entry(rb, struct drm_prime_member, handle_rb);
+ if (handle > pos->handle)
+ p = &rb->rb_right;
+ else
+ p = &rb->rb_left;
+ }
+ rb_link_node(&member->handle_rb, rb, p);
+ rb_insert_color(&member->handle_rb, &prime_fpriv->handles);
+
+ return 0;
+}
+
+static int dmabuf_heap_lookup_buf_handle(struct dmaheap_file_private *prime_fpriv,
+ struct dma_buf *dma_buf,
+ uint64_t *handle)
+{
+ struct rb_node *rb;
+
+ rb = prime_fpriv->dmabufs.rb_node;
+ while (rb) {
+ struct drm_prime_member *member;
+
+ member = rb_entry(rb, struct drm_prime_member, dmabuf_rb);
+ if (member->dma_buf == dma_buf) {
+ *handle = member->handle;
+ return 0;
+ } else if (member->dma_buf < dma_buf) {
+ rb = rb->rb_right;
+ } else {
+ rb = rb->rb_left;
+ }
+ }
+
+ return -ENOENT;
+}
+
+static void _dmabuf_heap_remove_buf_handle(struct dmaheap_file_private *prime_fpriv,
+ struct dma_buf *dma_buf)
+{
+ struct rb_node *rb;
+
+ rb = prime_fpriv->dmabufs.rb_node;
+ while (rb) {
+ struct drm_prime_member *member;
+
+ member = rb_entry(rb, struct drm_prime_member, dmabuf_rb);
+ if (member->dma_buf == dma_buf) {
+ rb_erase(&member->handle_rb, &prime_fpriv->handles);
+ rb_erase(&member->dmabuf_rb, &prime_fpriv->dmabufs);
+
+ dma_buf_put(dma_buf);
+ kfree(member);
+ return;
+ } else if (member->dma_buf < dma_buf) {
+ rb = rb->rb_right;
+ } else {
+ rb = rb->rb_left;
+ }
+ }
+}
+
+void common_dmabuf_heap_import_init(struct heap_root *root, struct device *dev)
+{
+ memset(root, 0, sizeof(*root));
+
+ mutex_init(&root->lock);
+ INIT_LIST_HEAD(&root->header);
+
+ root->dev = dev;
+}
+EXPORT_SYMBOL(common_dmabuf_heap_import_init);
+
+void common_dmabuf_heap_import_uninit(struct heap_root *root)
+{
+ struct heap_mem *h, *tmp;
+
+ list_for_each_entry_safe(h, tmp, &root->header, list) {
+ common_dmabuf_heap_release(h);
+ }
+}
+EXPORT_SYMBOL(common_dmabuf_heap_import_uninit);
+
+static struct heap_mem *dmabuf_heap_import(struct heap_root *root, int fd)
+{
+ struct dma_buf *dma_buf;
+ struct dma_buf_attachment *attach;
+ struct sg_table *sgt;
+
+ uint64_t handle;
+ struct heap_mem *heap_obj;
+ int ret;
+
+ /* get dmabuf handle */
+ dma_buf = dma_buf_get(fd);
+ if (IS_ERR(dma_buf))
+ return ERR_CAST(dma_buf);
+
+ mutex_lock(&root->lock);
+
+ ret = dmabuf_heap_lookup_buf_handle(&root->fp, dma_buf, &handle);
+ if (ret == 0) {
+ heap_obj = (struct heap_mem *)handle;
+ dma_buf_put(dma_buf);
+ kref_get(&heap_obj->refcount);
+ mutex_unlock(&root->lock);
+ return heap_obj;
+ }
+
+ heap_obj = kzalloc(sizeof(*heap_obj), GFP_KERNEL);
+ if (!heap_obj) {
+ mutex_unlock(&root->lock);
+ dma_buf_put(dma_buf);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ attach = dma_buf_attach(dma_buf, root->dev);
+ if (IS_ERR(attach)) {
+ ret = PTR_ERR(attach);
+ goto clean_up;
+ }
+
+ sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
+ if (IS_ERR(sgt)) {
+ ret = PTR_ERR(sgt);
+ goto fail_detach;
+ }
+
+ heap_obj->dbuf_fd = fd;
+ heap_obj->dbuf = dma_buf;
+
+ heap_obj->import_attach = attach;
+ heap_obj->sgt = sgt;
+
+ heap_obj->root = root;
+ heap_obj->vaddr = NULL;
+ heap_obj->dir = DMA_BIDIRECTIONAL;
+
+ /* get_dma_buf was called in dmabuf_heap_add_buf_handle()*/
+ ret = dmabuf_heap_add_buf_handle(&root->fp, dma_buf, (uint64_t)heap_obj);
+ if (ret) {
+ goto fail_add_handle;
+ }
+
+ kref_init(&heap_obj->refcount);
+
+ list_add(&heap_obj->list, &root->header);
+
+ mutex_unlock(&root->lock);
+
+ dma_buf_put(dma_buf);
+
+ return heap_obj;
+
+fail_add_handle:
+fail_detach:
+ dma_buf_detach(dma_buf, attach);
+clean_up:
+ kfree(heap_obj);
+ mutex_unlock(&root->lock);
+ dma_buf_put(dma_buf);
+
+ return ERR_PTR(ret);
+}
+
+static struct heap_mem *dmabuf_heap_import_with_dma_buf_st(struct heap_root *root, struct dma_buf *dma_buf)
+{
+ struct dma_buf_attachment *attach;
+ struct sg_table *sgt;
+
+ uint64_t handle;
+ struct heap_mem *heap_obj;
+ int ret;
+
+ mutex_lock(&root->lock);
+
+ ret = dmabuf_heap_lookup_buf_handle(&root->fp, dma_buf, &handle);
+ if (ret == 0) {
+ heap_obj = (struct heap_mem *)handle;
+ kref_get(&heap_obj->refcount);
+ mutex_unlock(&root->lock);
+ return heap_obj;
+ }
+
+ heap_obj = kzalloc(sizeof(*heap_obj), GFP_KERNEL);
+ if (!heap_obj) {
+ mutex_unlock(&root->lock);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ attach = dma_buf_attach(dma_buf, root->dev);
+ if (IS_ERR(attach)) {
+ ret = PTR_ERR(attach);
+ goto clean_up;
+ }
+
+ sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
+ if (IS_ERR(sgt)) {
+ ret = PTR_ERR(sgt);
+ goto fail_detach;
+ }
+
+ heap_obj->dbuf_fd = -1;
+ heap_obj->dbuf = dma_buf;
+
+ heap_obj->import_attach = attach;
+ heap_obj->sgt = sgt;
+
+ heap_obj->root = root;
+ heap_obj->vaddr = NULL;
+ heap_obj->dir = DMA_BIDIRECTIONAL;
+
+ /* get_dma_buf was called in dmabuf_heap_add_buf_handle()*/
+ ret = dmabuf_heap_add_buf_handle(&root->fp, dma_buf, (uint64_t)heap_obj);
+ if (ret) {
+ goto fail_add_handle;
+ }
+
+ kref_init(&heap_obj->refcount);
+
+ list_add(&heap_obj->list, &root->header);
+
+ mutex_unlock(&root->lock);
+
+ return heap_obj;
+
+fail_add_handle:
+fail_detach:
+ dma_buf_detach(dma_buf, attach);
+clean_up:
+ kfree(heap_obj);
+ mutex_unlock(&root->lock);
+
+ return ERR_PTR(ret);
+}
+
+struct heap_mem *common_dmabuf_lookup_heapobj_by_fd(struct heap_root *root, int fd)
+{
+ int ret = 0;
+ struct dma_buf *dma_buf;
+ struct heap_mem *heap_obj;
+
+ /* get dmabuf handle */
+ dma_buf = dma_buf_get(fd);
+ if (IS_ERR(dma_buf))
+ return NULL;
+
+ mutex_lock(&root->lock);
+ ret = dmabuf_heap_lookup_buf_handle(&root->fp, dma_buf, (uint64_t *)&heap_obj);
+ mutex_unlock(&root->lock);
+
+ dma_buf_put(dma_buf);
+ if (0 == ret)
+ return heap_obj;
+ else
+ return NULL;
+}
+EXPORT_SYMBOL(common_dmabuf_lookup_heapobj_by_fd);
+
+struct heap_mem *common_dmabuf_lookup_heapobj_by_dma_buf_st(struct heap_root *root, struct dma_buf *dma_buf)
+{
+ int ret = 0;
+ struct heap_mem *heap_obj;
+
+ pr_debug("%s:dma_buf=0x%px, file_count=%ld\n",
+ __func__, dma_buf, file_count(dma_buf->file));
+ mutex_lock(&root->lock);
+ ret = dmabuf_heap_lookup_buf_handle(&root->fp, dma_buf, (uint64_t *)&heap_obj);
+ mutex_unlock(&root->lock);
+
+ if (0 == ret)
+ return heap_obj;
+ else
+ return NULL;
+}
+EXPORT_SYMBOL(common_dmabuf_lookup_heapobj_by_dma_buf_st);
+
+struct heap_mem *common_dmabuf_heap_import_from_user(struct heap_root *root, int fd)
+{
+ return dmabuf_heap_import(root, fd);
+}
+EXPORT_SYMBOL(common_dmabuf_heap_import_from_user);
+
+struct heap_mem *common_dmabuf_heap_import_from_user_with_dma_buf_st(struct heap_root *root, struct dma_buf *dma_buf)
+{
+ return dmabuf_heap_import_with_dma_buf_st(root, dma_buf);
+}
+EXPORT_SYMBOL(common_dmabuf_heap_import_from_user_with_dma_buf_st);
+
+static void __common_dmabuf_heap_release(struct kref *kref)
+{
+ struct heap_root *root;
+ struct heap_mem *heap_obj = container_of(kref, struct heap_mem, refcount);
+
+ WARN_ON(!heap_obj);
+ if (!heap_obj)
+ return;
+
+ root = heap_obj->root;
+ WARN_ON(!mutex_is_locked(&root->lock));
+ list_del(&heap_obj->list);
+
+ common_dmabuf_heap_umap_vaddr(heap_obj);
+
+ dma_buf_unmap_attachment(heap_obj->import_attach, heap_obj->sgt, heap_obj->dir);
+
+ dma_buf_detach(heap_obj->dbuf, heap_obj->import_attach);
+
+ /* dma_buf_put was called in _dmabuf_heap_remove_buf_handle()*/
+ _dmabuf_heap_remove_buf_handle(&root->fp, heap_obj->dbuf);
+
+ kfree(heap_obj);
+}
+
+void common_dmabuf_heap_release(struct heap_mem *heap_obj)
+{
+ struct heap_root *root = heap_obj->root;
+
+ mutex_lock(&root->lock);
+ kref_put(&heap_obj->refcount, __common_dmabuf_heap_release);
+ mutex_unlock(&root->lock);
+}
+EXPORT_SYMBOL(common_dmabuf_heap_release);
+
+void *common_dmabuf_heap_map_vaddr(struct heap_mem *heap_obj)
+{
+ struct dma_buf_map map;
+ int ret;
+
+ WARN_ON(!heap_obj);
+ if (!heap_obj)
+ return NULL;
+
+ if (heap_obj->vaddr)
+ return heap_obj->vaddr;
+
+ ret = dma_buf_vmap(heap_obj->dbuf, &map);
+ if (ret)
+ return NULL;
+
+ WARN_ON_ONCE(map.is_iomem);
+ heap_obj->vaddr = map.vaddr;
+
+ return heap_obj->vaddr;
+}
+EXPORT_SYMBOL(common_dmabuf_heap_map_vaddr);
+
+void common_dmabuf_heap_umap_vaddr(struct heap_mem *heap_obj)
+{
+ struct dma_buf_map map;
+
+ WARN_ON(!heap_obj);
+ if (heap_obj && heap_obj->vaddr) {
+ map.vaddr = heap_obj->vaddr;
+ map.is_iomem = 0;
+ dma_buf_vunmap(heap_obj->dbuf, &map);
+ heap_obj->vaddr = NULL;
+ }
+}
+EXPORT_SYMBOL(common_dmabuf_heap_umap_vaddr);
+
+struct heap_mem *
+common_dmabuf_heap_import_from_kernel(struct heap_root *root, char *name, size_t len, unsigned int fd_flags)
+{
+ int dbuf_fd;
+
+ dbuf_fd = eswin_heap_kalloc(name, len, O_RDWR | fd_flags, 0);
+ if (dbuf_fd < 0) {
+ return ERR_PTR(dbuf_fd);
+ }
+
+ return dmabuf_heap_import(root, dbuf_fd);
+}
+EXPORT_SYMBOL(common_dmabuf_heap_import_from_kernel);
+
+struct esw_exp_attachment {
+ struct heap_mem *hmem;
+ struct sg_table table;
+ struct device *dev;
+ struct heap_root root;
+};
+
+// #define PRINT_ORIGINAL_SPLITTERS 1
+static int esw_common_heap_attach(struct dma_buf *dmabuf,
+ struct dma_buf_attachment *attachment)
+{
+ struct esw_export_buffer_info *buffer = dmabuf->priv;
+ struct esw_exp_attachment *a;
+ int out_mapped_nents[1];
+ int ret = 0;
+ struct sg_table *sgt = NULL;
+ struct scatterlist *sg;
+ int i;
+ size_t size, len;
+
+ a = kzalloc(sizeof(*a), GFP_KERNEL);
+ if (!a)
+ return -ENOMEM;
+
+ a->dev = attachment->dev;
+
+ common_dmabuf_heap_import_init(&a->root, a->dev);
+ a->hmem = common_dmabuf_heap_import_from_user(&a->root, buffer->dbuf_fd);
+ if (IS_ERR(a->hmem))
+ return PTR_ERR(a->hmem);
+
+ ret = sg_split(a->hmem->sgt->sgl, a->hmem->sgt->nents, buffer->slice.offset, 1, &buffer->slice.len,
+ &a->table.sgl, &out_mapped_nents[0], GFP_KERNEL);
+ if (ret) {
+ common_dmabuf_heap_release(a->hmem);
+ kfree(a);
+ return ret;
+ }
+ a->table.nents = out_mapped_nents[0];
+ a->table.orig_nents = out_mapped_nents[0];
+ sgt = &a->table;
+ #ifdef PRINT_ORIGINAL_SPLITTERS
+ {
+ pr_info("%s:orig:sgt->orig_nents=%d, out_mapped_nents[0]=%d\n",
+ __func__, sgt->orig_nents, out_mapped_nents[0]);
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+ pr_info("orig[%d]:sg->length=0x%x, sg_dma_len=0x%x, sg_phys=0x%lx\n",
+ i, sg->length, sg_dma_len(sg), (unsigned long)sg_phys(sg));
+ }
+ }
+ #endif
+ /* Re-format the splitted sg list in the actual slice len */
+ {
+ size = buffer->slice.len;
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+ if (sg->length >= size) {
+ sg->length = size;
+ sg_dma_len(sg) = size;
+ sg_mark_end(sg);
+ pr_debug("refmt[%d]:sg->length=0x%x, sg_dma_len=0x%x, sg_phys=0x%lx\n",
+ i, sg->length, sg_dma_len(sg), (unsigned long)sg_phys(sg));
+ break;
+ }
+ len = min_t(size_t, size, sg->length);
+ size -= len;
+ pr_debug("refmt[%d]:sg->length=0x%x, sg_dma_len=0x%x, sg_phys=0x%lx\n",
+ i, sg->length, sg_dma_len(sg), (unsigned long)sg_phys(sg));
+ }
+ sgt->orig_nents = sgt->nents = i + 1;
+ }
+
+ attachment->priv = a;
+
+ return ret;
+}
+
+static void esw_common_heap_detach(struct dma_buf *dmabuf,
+ struct dma_buf_attachment *attachment)
+{
+ struct esw_exp_attachment *a = attachment->priv;
+
+ kfree(a->table.sgl);
+ common_dmabuf_heap_release(a->hmem);
+ common_dmabuf_heap_import_uninit(&a->root);
+ kfree(a);
+}
+
+static struct sg_table *esw_common_map_dma_buf(struct dma_buf_attachment *attachment,
+ enum dma_data_direction direction)
+{
+ struct esw_exp_attachment *a = attachment->priv;
+ return &a->table;
+}
+
+static void esw_common_unmap_dma_buf(struct dma_buf_attachment *attachment,
+ struct sg_table *table,
+ enum dma_data_direction direction)
+{
+}
+
+static int esw_common_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
+{
+ struct esw_export_buffer_info *buffer = dmabuf->priv;
+ // printk("%s enter\n", __func__);
+ return dma_buf_mmap(buffer->dmabuf, vma, buffer->slice.offset >> PAGE_SHIFT);
+}
+
+static int esw_common_dma_buf_begin_cpu_access(struct dma_buf *dmabuf,
+ enum dma_data_direction direction)
+{
+ struct esw_export_buffer_info *buffer = dmabuf->priv;
+ return dma_buf_begin_cpu_access(buffer->dmabuf, direction);
+}
+
+static int esw_common_dma_buf_end_cpu_access(struct dma_buf *dmabuf,
+ enum dma_data_direction direction)
+{
+ struct esw_export_buffer_info *buffer = dmabuf->priv;
+ return dma_buf_end_cpu_access(buffer->dmabuf, direction);
+}
+
+static int esw_common_heap_vmap(struct dma_buf *dmabuf, struct dma_buf_map *map)
+{
+ struct esw_export_buffer_info *buffer = dmabuf->priv;
+ struct dma_buf_map pmap;
+ int ret;
+
+ ret = dma_buf_vmap(buffer->dmabuf, &pmap);
+
+ map->is_iomem = false;
+ map->vaddr_iomem = pmap.vaddr_iomem + buffer->slice.offset;
+
+ return ret;
+}
+
+static void esw_common_heap_vunmap(struct dma_buf *dmabuf, struct dma_buf_map *map)
+{
+ struct esw_export_buffer_info *buffer = dmabuf->priv;
+ struct dma_buf_map pmap = *map;
+
+ pmap.vaddr_iomem -= buffer->slice.offset;
+ dma_buf_vunmap(buffer->dmabuf, &pmap);
+}
+
+static void esw_common_dma_buf_release(struct dma_buf *dmabuf)
+{
+ struct esw_export_buffer_info *buffer = dmabuf->priv;
+
+ // printk("%s %d\n", __func__, __LINE__);
+
+ dma_buf_put(buffer->dmabuf);
+ kfree(buffer);
+}
+
+static const struct dma_buf_ops esw_common_buf_ops = {
+ .attach = esw_common_heap_attach,
+ .detach = esw_common_heap_detach,
+ .map_dma_buf = esw_common_map_dma_buf,
+ .unmap_dma_buf = esw_common_unmap_dma_buf,
+ .begin_cpu_access = esw_common_dma_buf_begin_cpu_access,
+ .end_cpu_access = esw_common_dma_buf_end_cpu_access,
+ .mmap = esw_common_mmap,
+ .vmap = esw_common_heap_vmap,
+ .vunmap = esw_common_heap_vunmap,
+ .release = esw_common_dma_buf_release,
+};
+
+int esw_common_dmabuf_split_export(int dbuf_fd, unsigned int offset, size_t len, int fd_flags, char *name)
+{
+ struct esw_export_buffer_info *buffer_info;
+ DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
+ int fd;
+ struct dma_buf *dmabuf;
+
+ buffer_info = kzalloc(sizeof(*buffer_info), GFP_KERNEL);
+ if (!buffer_info)
+ return -ENOMEM;
+
+ buffer_info->dbuf_fd = dbuf_fd;
+ buffer_info->fd_flags = fd_flags;
+ buffer_info->slice.offset = offset;
+ buffer_info->slice.len = len;
+ snprintf(buffer_info->name, sizeof(buffer_info->name), "%s", name);
+
+ buffer_info->dmabuf = dma_buf_get(buffer_info->dbuf_fd);
+ if (IS_ERR(buffer_info->dmabuf))
+ return PTR_ERR(buffer_info->dmabuf);
+
+// printk("input slice: oft=0x%d, len=%lu\n", buffer_info->slice.offset, buffer_info->slice.len);
+
+ buffer_info->slice.offset = PAGE_ALIGN(buffer_info->slice.offset);
+ buffer_info->slice.len = PAGE_ALIGN(buffer_info->slice.len);
+
+// printk("align slice: oft=0x%d, len=%lu\n", buffer_info->slice.offset, buffer_info->slice.len);
+
+ /* create the dmabuf */
+ exp_info.exp_name = buffer_info->name;
+ exp_info.ops = &esw_common_buf_ops;
+ exp_info.size = buffer_info->slice.len;
+ exp_info.flags = buffer_info->fd_flags;
+ exp_info.priv = buffer_info;
+
+ dmabuf = dma_buf_export(&exp_info);
+ if (IS_ERR(dmabuf)) {
+ return PTR_ERR(dmabuf);
+ }
+
+ fd = dma_buf_fd(dmabuf, buffer_info->fd_flags);
+ if (fd < 0) {
+ dma_buf_put(dmabuf);
+ /* put the splitted dmabuf, the esw_common_dma_buf_release will be called,
+ the parent dmabuf will be put and the buffer_info will be free at that time */
+ }
+ return fd;
+}
+
+EXPORT_SYMBOL(esw_common_dmabuf_split_export);
diff --git a/drivers/memory/eswin/es_rsvmem_heap/eswin_rsvmem_common.c b/drivers/memory/eswin/es_rsvmem_heap/eswin_rsvmem_common.c
new file mode 100644
index 000000000000..067f30627a46
--- /dev/null
+++ b/drivers/memory/eswin/es_rsvmem_heap/eswin_rsvmem_common.c
@@ -0,0 +1,447 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ESWIN heap APIs
+ *
+ * Copyright 2024 Beijing ESWIN Computing Technology Co., Ltd.
+ * Authors:
+ * LinMin<linmin@eswincomputing.com>
+ *
+ */
+
+#include <linux/cdev.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/dma-buf.h>
+#include <linux/err.h>
+#include <linux/xarray.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/nospec.h>
+#include <linux/uaccess.h>
+#include <linux/syscalls.h>
+#include <linux/eswin_rsvmem_common.h>
+#include "include/uapi/linux/eswin_rsvmem_common.h"
+
+#define DEVNAME "eswin_heap"
+
+#define NUM_HEAP_MINORS 128
+
+/**
+ * struct eswin_heap - represents a dmabuf heap in the system
+ * @name: used for debugging/device-node name
+ * @ops: ops struct for this heap
+ * @heap_devt heap device node
+ * @list list head connecting to list of heaps
+ * @heap_cdev heap char device
+ *
+ * Represents a heap of memory from which buffers can be made.
+ */
+struct eswin_heap {
+ const char *name;
+ const struct eswin_heap_ops *ops;
+ void *priv;
+ dev_t heap_devt;
+ struct list_head list;
+ struct cdev heap_cdev;
+};
+
+static LIST_HEAD(heap_list);
+static DEFINE_MUTEX(heap_list_lock);
+static dev_t eswin_heap_devt;
+static struct class *eswin_heap_class;
+static DEFINE_XARRAY_ALLOC(eswin_heap_minors);
+
+static int eswin_heap_buffer_alloc(struct eswin_heap *heap, size_t len,
+ unsigned int fd_flags,
+ unsigned int heap_flags)
+{
+ struct dma_buf *dmabuf;
+ int fd;
+
+ /*
+ * Allocations from all heaps have to begin
+ * and end on page boundaries.
+ */
+ len = PAGE_ALIGN(len);
+ if (!len)
+ return -EINVAL;
+
+ dmabuf = heap->ops->allocate(heap, len, fd_flags, heap_flags);
+ if (IS_ERR(dmabuf))
+ return PTR_ERR(dmabuf);
+
+ fd = dma_buf_fd(dmabuf, fd_flags);
+ if (fd < 0) {
+ dma_buf_put(dmabuf);
+ /* just return, as put will call release and that will free */
+ }
+ return fd;
+}
+
+static int eswin_heap_open(struct inode *inode, struct file *file)
+{
+ struct eswin_heap *heap;
+
+ heap = xa_load(&eswin_heap_minors, iminor(inode));
+ if (!heap) {
+ pr_err("eswin_heap: minor %d unknown.\n", iminor(inode));
+ return -ENODEV;
+ }
+
+ /* instance data as context */
+ file->private_data = heap;
+ nonseekable_open(inode, file);
+
+ return 0;
+}
+
+static long eswin_heap_ioctl_allocate(struct file *file, void *data)
+{
+ struct eswin_heap_allocation_data *heap_allocation = data;
+ struct eswin_heap *heap = file->private_data;
+ int fd;
+
+ if (heap_allocation->fd)
+ return -EINVAL;
+
+ if (heap_allocation->fd_flags & ~ESWIN_HEAP_VALID_FD_FLAGS)
+ return -EINVAL;
+
+ if (heap_allocation->heap_flags & ~ESWIN_HEAP_VALID_HEAP_FLAGS)
+ return -EINVAL;
+
+ fd = eswin_heap_buffer_alloc(heap, heap_allocation->len,
+ heap_allocation->fd_flags,
+ heap_allocation->heap_flags);
+ if (fd < 0)
+ return fd;
+
+ heap_allocation->fd = fd;
+
+ return 0;
+}
+
+static unsigned int eswin_heap_ioctl_cmds[] = {
+ ESWIN_HEAP_IOCTL_ALLOC,
+};
+
+static long eswin_heap_ioctl(struct file *file, unsigned int ucmd,
+ unsigned long arg)
+{
+ char stack_kdata[128];
+ char *kdata = stack_kdata;
+ unsigned int kcmd;
+ unsigned int in_size, out_size, drv_size, ksize;
+ int nr = _IOC_NR(ucmd);
+ int ret = 0;
+
+ if (nr >= ARRAY_SIZE(eswin_heap_ioctl_cmds))
+ return -EINVAL;
+
+ nr = array_index_nospec(nr, ARRAY_SIZE(eswin_heap_ioctl_cmds));
+ /* Get the kernel ioctl cmd that matches */
+ kcmd = eswin_heap_ioctl_cmds[nr];
+
+ /* Figure out the delta between user cmd size and kernel cmd size */
+ drv_size = _IOC_SIZE(kcmd);
+ out_size = _IOC_SIZE(ucmd);
+ in_size = out_size;
+ if ((ucmd & kcmd & IOC_IN) == 0)
+ in_size = 0;
+ if ((ucmd & kcmd & IOC_OUT) == 0)
+ out_size = 0;
+ ksize = max(max(in_size, out_size), drv_size);
+
+ /* If necessary, allocate buffer for ioctl argument */
+ if (ksize > sizeof(stack_kdata)) {
+ kdata = kmalloc(ksize, GFP_KERNEL);
+ if (!kdata)
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(kdata, (void __user *)arg, in_size) != 0) {
+ ret = -EFAULT;
+ goto err;
+ }
+
+ /* zero out any difference between the kernel/user structure size */
+ if (ksize > in_size)
+ memset(kdata + in_size, 0, ksize - in_size);
+
+ switch (kcmd) {
+ case ESWIN_HEAP_IOCTL_ALLOC:
+ ret = eswin_heap_ioctl_allocate(file, kdata);
+ break;
+ default:
+ ret = -ENOTTY;
+ goto err;
+ }
+
+ if (copy_to_user((void __user *)arg, kdata, out_size) != 0)
+ ret = -EFAULT;
+err:
+ if (kdata != stack_kdata)
+ kfree(kdata);
+ return ret;
+}
+
+static const struct file_operations eswin_heap_fops = {
+ .owner = THIS_MODULE,
+ .open = eswin_heap_open,
+ .unlocked_ioctl = eswin_heap_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = eswin_heap_ioctl,
+#endif
+};
+
+/**
+ * eswin_heap_get_drvdata() - get per-subdriver data for the heap
+ * @heap: DMA-Heap to retrieve private data for
+ *
+ * Returns:
+ * The per-subdriver data for the heap.
+ */
+void *eswin_heap_get_drvdata(struct eswin_heap *heap)
+{
+ return heap->priv;
+}
+
+/**
+ * eswin_heap_get_name() - get heap name
+ * @heap: DMA-Heap to retrieve private data for
+ *
+ * Returns:
+ * The char* for the heap name.
+ */
+const char *eswin_heap_get_name(struct eswin_heap *heap)
+{
+ return heap->name;
+}
+
+struct eswin_heap *eswin_heap_add(const struct eswin_heap_export_info *exp_info)
+{
+ struct eswin_heap *heap, *h = NULL, *err_ret;
+ struct device *dev_ret;
+ unsigned int minor;
+ int ret;
+
+ if (!exp_info->name || !strcmp(exp_info->name, "")) {
+ pr_err("eswin_heap: Cannot add heap without a name\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (!exp_info->ops || !exp_info->ops->allocate) {
+ pr_err("eswin_heap: Cannot add heap with invalid ops struct\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ /* check the name is unique */
+ mutex_lock(&heap_list_lock);
+ list_for_each_entry(h, &heap_list, list) {
+ if (!strcmp(h->name, exp_info->name)) {
+ mutex_unlock(&heap_list_lock);
+ pr_err("eswin_heap: Already registered heap named %s\n",
+ exp_info->name);
+ return ERR_PTR(-EINVAL);
+ }
+ }
+ mutex_unlock(&heap_list_lock);
+
+ heap = kzalloc(sizeof(*heap), GFP_KERNEL);
+ if (!heap)
+ return ERR_PTR(-ENOMEM);
+
+ heap->name = exp_info->name;
+ heap->ops = exp_info->ops;
+ heap->priv = exp_info->priv;
+
+ /* Find unused minor number */
+ ret = xa_alloc(&eswin_heap_minors, &minor, heap,
+ XA_LIMIT(0, NUM_HEAP_MINORS - 1), GFP_KERNEL);
+ if (ret < 0) {
+ pr_err("eswin_heap: Unable to get minor number for heap\n");
+ err_ret = ERR_PTR(ret);
+ goto err0;
+ }
+
+ /* Create device */
+ heap->heap_devt = MKDEV(MAJOR(eswin_heap_devt), minor);
+
+ cdev_init(&heap->heap_cdev, &eswin_heap_fops);
+ ret = cdev_add(&heap->heap_cdev, heap->heap_devt, 1);
+ if (ret < 0) {
+ pr_err("eswin_heap: Unable to add char device\n");
+ err_ret = ERR_PTR(ret);
+ goto err1;
+ }
+
+ dev_ret = device_create(eswin_heap_class,
+ NULL,
+ heap->heap_devt,
+ NULL,
+ heap->name);
+ if (IS_ERR(dev_ret)) {
+ pr_err("eswin_heap: Unable to create device\n");
+ err_ret = ERR_CAST(dev_ret);
+ goto err2;
+ }
+ /* Add heap to the list */
+ mutex_lock(&heap_list_lock);
+ list_add(&heap->list, &heap_list);
+ mutex_unlock(&heap_list_lock);
+
+ return heap;
+
+err2:
+ cdev_del(&heap->heap_cdev);
+err1:
+ xa_erase(&eswin_heap_minors, minor);
+err0:
+ kfree(heap);
+ return err_ret;
+}
+
+int eswin_heap_delete(struct eswin_heap *heap)
+{
+ struct eswin_heap *h, *tmp;
+ int ret = -1;
+
+ if (!heap->name || !strcmp(heap->name, "")) {
+ pr_err("eswin_heap: Cannot delet heap without a name\n");
+ return -EINVAL;
+ }
+
+ /* find the heaplist by the heap name */
+ mutex_lock(&heap_list_lock);
+ list_for_each_entry_safe(h, tmp, &heap_list, list) {
+ if (!strcmp(h->name, heap->name)) {
+ pr_info("eswin_heap: deleted heap %s\n",
+ heap->name);
+ device_destroy(eswin_heap_class, h->heap_devt);
+ cdev_del(&h->heap_cdev);
+ xa_erase(&eswin_heap_minors, MINOR(h->heap_devt));
+ list_del(&h->list);
+ kfree(h);
+ ret = 0;
+ }
+ }
+ mutex_unlock(&heap_list_lock);
+
+ if (ret) {
+ pr_err("eswin_heap: heap named %s NOT found!\n", heap->name);
+ }
+
+ return ret;
+
+}
+
+int eswin_heap_delete_by_name(const char *name)
+{
+ struct eswin_heap *h, *tmp;
+ int ret = -1;
+
+ if (!name || !strcmp(name, "")) {
+ pr_err("eswin_heap: Cannot delet heap without a name\n");
+ return -EINVAL;
+ }
+
+ /* find the heaplist by the heap name */
+ mutex_lock(&heap_list_lock);
+ list_for_each_entry_safe(h, tmp, &heap_list, list) {
+ if (!strcmp(h->name, name)) {
+ pr_info("eswin_heap: deleted heap %s\n",
+ name);
+ device_destroy(eswin_heap_class, h->heap_devt);
+ cdev_del(&h->heap_cdev);
+ xa_erase(&eswin_heap_minors, MINOR(h->heap_devt));
+ list_del(&h->list);
+ kfree(h);
+ ret = 0;
+ }
+ }
+ mutex_unlock(&heap_list_lock);
+
+ if (ret) {
+ pr_err("eswin_heap: heap named %s NOT found!\n", name);
+ }
+
+ return ret;
+}
+
+int eswin_heap_kalloc(char *name, size_t len, unsigned int fd_flags, unsigned int heap_flags)
+{
+ struct eswin_heap *heap = NULL;
+#if 0
+ struct eswin_heap *h = NULL;
+ /* check the name is unique */
+ mutex_lock(&heap_list_lock);
+ list_for_each_entry(h, &heap_list, list) {
+ if (!strcmp(h->name, name)) {
+ heap = h;
+ break;
+ }
+ }
+ mutex_unlock(&heap_list_lock);
+#else
+ char *dev_path = NULL;
+ struct file *file;
+ int ret;
+
+ dev_path = kasprintf(GFP_KERNEL, "/dev/dma_heap/%s", name);
+ file = filp_open(dev_path, O_RDWR, 0);
+ if (IS_ERR(file)) {
+ ret = PTR_ERR(file);
+ pr_err("failed to open file %s: (%d)\n",
+ dev_path, ret);
+ return ret;
+ }
+ heap = file->private_data;
+#endif
+ if (!heap) {
+ printk("ERROR: Can't find this heap %s\n", name);
+ return -ENODEV;
+ }
+
+ return eswin_heap_buffer_alloc(heap, len, fd_flags, heap_flags);
+}
+EXPORT_SYMBOL(eswin_heap_kalloc);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,6,0)
+static char *eswin_heap_devnode(const struct device *dev, umode_t *mode)
+#else
+static char *eswin_heap_devnode(struct device *dev, umode_t *mode)
+#endif
+
+{
+ // return kasprintf(GFP_KERNEL, "eswin_heap/%s", dev_name(dev));
+ /* create device node under dma_heap instead of eswin_heap, so that memory lib can
+ avoid the diverseness.
+ */
+ return kasprintf(GFP_KERNEL, "dma_heap/%s", dev_name(dev));
+}
+
+int eswin_heap_init(void)
+{
+ int ret;
+
+ ret = alloc_chrdev_region(&eswin_heap_devt, 0, NUM_HEAP_MINORS, DEVNAME);
+ if (ret)
+ return ret;
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(6,6,0)
+ eswin_heap_class = class_create(DEVNAME);
+ #else
+ eswin_heap_class = class_create(THIS_MODULE, DEVNAME);
+ #endif
+ if (IS_ERR(eswin_heap_class)) {
+ unregister_chrdev_region(eswin_heap_devt, NUM_HEAP_MINORS);
+ return PTR_ERR(eswin_heap_class);
+ }
+ eswin_heap_class->devnode = eswin_heap_devnode;
+
+ return 0;
+}
+
+void eswin_heap_uninit(void)
+{
+ class_destroy(eswin_heap_class);
+}
diff --git a/drivers/memory/eswin/es_rsvmem_heap/eswin_rsvmem_heap.c b/drivers/memory/eswin/es_rsvmem_heap/eswin_rsvmem_heap.c
new file mode 100644
index 000000000000..804258f5ec74
--- /dev/null
+++ b/drivers/memory/eswin/es_rsvmem_heap/eswin_rsvmem_heap.c
@@ -0,0 +1,634 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ESWIN rerserved memory heap.
+ * eswin_rsvmem_heap creates heap for the reserved memory that has compatible = "eswin-reserve-memory";
+ * property and no-map property.
+ *
+ * Copyright 2024 Beijing ESWIN Computing Technology Co., Ltd.
+ * Authors:
+ * LinMin<linmin@eswincomputing.com>
+ *
+ */
+
+#include <linux/dma-buf.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/highmem.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/dma-map-ops.h>
+#include <linux/eswin_rsvmem_common.h>
+#include "../eswin_memblock.h"
+#include "../es_buddy/es_buddy.h"
+#include "include/uapi/linux/eswin_rsvmem_common.h"
+
+static const unsigned int orders[] = {MAX_ORDER-1, 9, 0};
+#define NUM_ORDERS ARRAY_SIZE(orders)
+
+struct eswin_rsvmem_heap {
+ struct eswin_heap *heap;
+ struct mem_block *memblock;
+};
+
+struct eswin_rsvmem_heap_buffer {
+ struct eswin_rsvmem_heap *heap;
+ struct list_head attachments;
+ struct mutex lock;
+ unsigned long len;
+ struct sg_table sg_table; // for buddy allocator
+ struct page **pages;
+ int vmap_cnt;
+ void *vaddr;
+ unsigned long fd_flags; // for vmap to determin the cache or non-cached mapping
+};
+
+struct eswin_heap_attachment {
+ struct device *dev;
+ struct sg_table *table;
+ struct list_head list;
+ bool mapped;
+};
+
+static struct sg_table *dup_sg_table(struct sg_table *table)
+{
+ struct sg_table *new_table;
+ int ret, i;
+ struct scatterlist *sg, *new_sg;
+
+ new_table = kzalloc(sizeof(*new_table), GFP_KERNEL);
+ if (!new_table)
+ return ERR_PTR(-ENOMEM);
+
+ ret = sg_alloc_table(new_table, table->orig_nents, GFP_KERNEL);
+ if (ret) {
+ kfree(new_table);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ new_sg = new_table->sgl;
+ for_each_sgtable_sg(table, sg, i) {
+ sg_set_page(new_sg, sg_page(sg), sg->length, sg->offset);
+ new_sg = sg_next(new_sg);
+ }
+
+ return new_table;
+}
+
+static int eswin_rsvmem_heap_attach(struct dma_buf *dmabuf,
+ struct dma_buf_attachment *attachment)
+{
+ struct eswin_rsvmem_heap_buffer *buffer = dmabuf->priv;
+ struct eswin_heap_attachment *a;
+ struct sg_table *table;
+
+ a = kzalloc(sizeof(*a), GFP_KERNEL);
+ if (!a)
+ return -ENOMEM;
+
+ table = dup_sg_table(&buffer->sg_table);
+ if (IS_ERR(table)) {
+ kfree(a);
+ return -ENOMEM;
+ }
+
+ a->table = table;
+ a->dev = attachment->dev;
+ INIT_LIST_HEAD(&a->list);
+ a->mapped = false;
+
+ attachment->priv = a;
+
+ mutex_lock(&buffer->lock);
+ list_add(&a->list, &buffer->attachments);
+ mutex_unlock(&buffer->lock);
+
+ return 0;
+}
+
+static void eswin_rsvmem_heap_detach(struct dma_buf *dmabuf,
+ struct dma_buf_attachment *attachment)
+{
+ struct eswin_rsvmem_heap_buffer *buffer = dmabuf->priv;
+ struct eswin_heap_attachment *a = attachment->priv;
+
+ mutex_lock(&buffer->lock);
+ list_del(&a->list);
+ mutex_unlock(&buffer->lock);
+
+ sg_free_table(a->table);
+ kfree(a->table);
+ kfree(a);
+}
+
+static struct sg_table *eswin_rsvmem_heap_map_dma_buf(struct dma_buf_attachment *attachment,
+ enum dma_data_direction direction)
+{
+ struct eswin_heap_attachment *a = attachment->priv;
+ struct sg_table *table =a->table;
+ int ret;
+ unsigned long attrs = DMA_ATTR_SKIP_CPU_SYNC;
+
+ /* Skipt cache sync, since it takes a lot of time when import to device.
+ * It's the user's responsibility for guaranteeing the cache coherency by
+ flusing cache explicitly before importing to device.
+ */
+ ret = dma_map_sgtable(attachment->dev, table, direction, attrs);
+
+ if (ret)
+ return ERR_PTR(-ENOMEM);
+ a->mapped = true;
+ return table;
+}
+
+static void eswin_rsvmem_heap_unmap_dma_buf(struct dma_buf_attachment *attachment,
+ struct sg_table *table,
+ enum dma_data_direction direction)
+{
+ struct eswin_heap_attachment *a = attachment->priv;
+ unsigned long attrs = DMA_ATTR_SKIP_CPU_SYNC;
+
+ a->mapped = false;
+
+ /* Skipt cache sync, since it takes a lot of time when unmap from device.
+ * It's the user's responsibility for guaranteeing the cache coherency after
+ the device has done processing the data.(For example, CPU do NOT read untill
+ the device has done)
+ */
+ dma_unmap_sgtable(attachment->dev, table, direction, attrs);
+}
+
+static int eswin_rsvmem_dma_buf_begin_cpu_access(struct dma_buf *dmabuf,
+ enum dma_data_direction direction)
+{
+ struct eswin_rsvmem_heap_buffer *buffer = dmabuf->priv;
+ struct sg_table *table = &buffer->sg_table;
+ struct scatterlist *sg;
+ int i;
+
+ mutex_lock(&buffer->lock);
+
+ if (buffer->vmap_cnt)
+ invalidate_kernel_vmap_range(buffer->vaddr, buffer->len);
+
+ /* Since the cache sync was skipped when eswin_rsvmem_heap_map_dma_buf/eswin_rsvmem_heap_unmap_dma_buf,
+ So force cache sync here when user call ES_SYS_MemFlushCache, eventhough there
+ is no device attached to this dmabuf.
+ */
+ #ifndef QEMU_DEBUG
+ for_each_sg(table->sgl, sg, table->orig_nents, i)
+ arch_sync_dma_for_cpu(sg_phys(sg), sg->length, direction);
+
+ #endif
+ mutex_unlock(&buffer->lock);
+
+ return 0;
+}
+
+static int eswin_rsvmem_dma_buf_end_cpu_access(struct dma_buf *dmabuf,
+ enum dma_data_direction direction)
+{
+ struct eswin_rsvmem_heap_buffer *buffer = dmabuf->priv;
+ struct sg_table *table = &buffer->sg_table;
+ struct scatterlist *sg;
+ int i;
+
+ mutex_lock(&buffer->lock);
+
+ if (buffer->vmap_cnt)
+ flush_kernel_vmap_range(buffer->vaddr, buffer->len);
+
+ /* Since the cache sync was skipped while eswin_rsvmem_heap_map_dma_buf/eswin_rsvmem_heap_unmap_dma_buf,
+ So force cache sync here when user call ES_SYS_MemFlushCache, eventhough there
+ is no device attached to this dmabuf.
+ */
+ #ifndef QEMU_DEBUG
+ for_each_sg(table->sgl, sg, table->orig_nents, i)
+ arch_sync_dma_for_device(sg_phys(sg), sg->length, direction);
+ #endif
+ mutex_unlock(&buffer->lock);
+
+ return 0;
+}
+
+#if 0
+static int eswin_rsvmem_sync_cache_internal(struct dma_buf *dmabuf, enum dma_data_direction direction)
+{
+ struct eswin_rsvmem_heap_buffer *buffer = dmabuf->priv;
+ struct sg_table *table = &buffer->sg_table;
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(table->sgl, sg, table->orig_nents, i)
+ arch_sync_dma_for_device(sg_phys(sg), sg->length, direction);
+
+
+ return 0;
+}
+#endif
+
+static int eswin_rsvmem_heap_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
+{
+ struct eswin_rsvmem_heap_buffer *buffer = dmabuf->priv;
+ struct eswin_heap *heap = buffer->heap->heap;
+ struct sg_table *table = &buffer->sg_table;
+ unsigned long addr = vma->vm_start;
+ unsigned long pgoff = vma->vm_pgoff, mapsize = 0;
+ unsigned long size_remaining = vma->vm_end - vma->vm_start;//vma_pages(vma);
+ struct scatterlist *sg;
+ struct page *page = NULL;
+ unsigned int nents = 0;
+ int i;
+ int ret;
+ const char *heap_name = NULL;
+
+ /* Mapping secure_memory with cached proprty to user space for CPU is NOT permitted */
+ heap_name = eswin_heap_get_name(heap);
+ if (unlikely(!strncmp("secure_memory", heap_name, 13))) {
+ if (!(vma->vm_flags & VM_NORESERVE))
+ return -EPERM;
+ }
+
+ if ((vma->vm_flags & (VM_SHARED | VM_MAYSHARE)) == 0)
+ return -EINVAL;
+
+ /* vm_private_data will be used by eswin-ipc-scpu.c.
+ ipc will import this dmabuf to get iova.
+ */
+ vma->vm_private_data = dmabuf;
+
+ /* support mman flag MAP_SHARED_VALIDATE | VM_NORESERVE, used to map uncached memory to user space.
+ Users should guarantee this buffer has been flushed to cache already.
+ */
+ if (vma->vm_flags & VM_NORESERVE) {
+ vm_flags_clear(vma, VM_NORESERVE);
+ #ifndef QEMU_DEBUG
+ vma->vm_page_prot = pgprot_dmacoherent(vma->vm_page_prot);
+ #endif
+ /* skip sync cache, users should guarantee the cache is clean after done using it in
+ cached mode(i.e, ES_SYS_Mmap(SYS_CACHE_MODE_CACHED))
+ */
+ }
+ pr_debug("%s, size_remaining:0x%lx, pgoff:0x%lx, dmabuf->size:0x%lx, start_phys:0x%llx\n",
+ __func__, size_remaining, pgoff, dmabuf->size, sg_phys(table->sgl));
+ for_each_sg(table->sgl, sg, table->orig_nents, i) {
+ pr_debug("sgl:%d, phys:0x%llx\n", i, sg_phys(sg));
+ if (pgoff >= (sg->length >> PAGE_SHIFT)) {
+ pgoff -= (sg->length >> PAGE_SHIFT);
+ continue;
+ }
+
+ page = sg_page(sg);
+ if (nents == 0) {
+ mapsize = sg->length - (pgoff << PAGE_SHIFT);
+ mapsize = min(size_remaining, mapsize);
+ ret = remap_pfn_range(vma, addr, page_to_pfn(page) + pgoff, mapsize,
+ vma->vm_page_prot);
+ pr_debug("nents:%d, sgl:%d, pgoff:0x%lx, mapsize:0x%lx, phys:0x%llx\n",
+ nents, i, pgoff, mapsize, pfn_to_phys(page_to_pfn(page) + pgoff));
+ }
+ else {
+ mapsize = min((unsigned int)size_remaining, (sg->length));
+ ret = remap_pfn_range(vma, addr, page_to_pfn(page), mapsize,
+ vma->vm_page_prot);
+ pr_debug("nents:%d, sgl:%d, mapsize:0x%lx, phys:0x%llx\n", nents, i, mapsize, page_to_phys(page));
+ }
+ pgoff = 0;
+ nents++;
+
+ if (ret)
+ return ret;
+
+ addr += mapsize;
+ size_remaining -= mapsize;
+ if (size_remaining == 0)
+ return 0;
+ }
+
+ return 0;
+}
+
+static void *eswin_rsvmem_heap_do_vmap(struct dma_buf *dmabuf)
+{
+ struct eswin_rsvmem_heap_buffer *buffer = dmabuf->priv;
+ pgprot_t prot = PAGE_KERNEL;
+ struct sg_table *table = &buffer->sg_table;
+ int npages = PAGE_ALIGN(buffer->len) / PAGE_SIZE;
+ struct page **pages = vmalloc(sizeof(struct page *) * npages);
+ struct page **tmp = pages;
+ struct sg_page_iter piter;
+ void *vaddr;
+
+ if (!pages)
+ return ERR_PTR(-ENOMEM);
+
+ for_each_sgtable_page(table, &piter, 0) {
+ WARN_ON(tmp - pages >= npages);
+ *tmp++ = sg_page_iter_page(&piter);
+ }
+
+ /* The property of this dmabuf in kernel space is determined by heap alloc with fd_flag. */
+ if (buffer->fd_flags & O_DSYNC) {
+ #ifndef QEMU_DEBUG
+ prot = pgprot_dmacoherent(PAGE_KERNEL);
+ #endif
+ pr_debug("%s syport uncached kernel dmabuf!, prot=0x%x\n", __func__, (unsigned int)pgprot_val(prot));
+ }
+ else {
+ pr_debug("%s memport cached kernel dmabuf!\n", __func__);
+ }
+
+ vaddr = vmap(pages, npages, VM_MAP, prot);
+ vfree(pages);
+
+ if (!vaddr)
+ return ERR_PTR(-ENOMEM);
+
+ return vaddr;
+}
+
+static int eswin_rsvmem_heap_vmap(struct dma_buf *dmabuf, struct dma_buf_map *map)
+{
+ struct eswin_rsvmem_heap_buffer *buffer = dmabuf->priv;
+ void *vaddr;
+ int ret = 0;
+
+ mutex_lock(&buffer->lock);
+ if (buffer->vmap_cnt) {
+ buffer->vmap_cnt++;
+ dma_buf_map_set_vaddr(map, buffer->vaddr);
+ goto out;
+ }
+
+ vaddr = eswin_rsvmem_heap_do_vmap(dmabuf);
+ if (IS_ERR(vaddr)) {
+ ret = PTR_ERR(vaddr);
+ goto out;
+ }
+ buffer->vaddr = vaddr;
+ buffer->vmap_cnt++;
+ dma_buf_map_set_vaddr(map, buffer->vaddr);
+out:
+ mutex_unlock(&buffer->lock);
+
+ return ret;
+}
+
+static void eswin_rsvmem_heap_vunmap(struct dma_buf *dmabuf, struct dma_buf_map *map)
+{
+ struct eswin_rsvmem_heap_buffer *buffer = dmabuf->priv;
+
+ mutex_lock(&buffer->lock);
+ if (!--buffer->vmap_cnt) {
+ vunmap(buffer->vaddr);
+ buffer->vaddr = NULL;
+ }
+ mutex_unlock(&buffer->lock);
+ dma_buf_map_clear(map);
+}
+
+static void eswin_rsvmem_heap_dma_buf_release(struct dma_buf *dmabuf)
+{
+ struct eswin_rsvmem_heap_buffer *buffer = dmabuf->priv;
+ struct sg_table *table;
+ struct scatterlist *sg;
+ int i;
+
+ table = &buffer->sg_table;
+ if (buffer->vmap_cnt > 0) {
+ WARN(1, "%s: buffer still mapped in the kernel\n", __func__);
+ vunmap(buffer->vaddr);
+ buffer->vaddr = NULL;
+ }
+
+ for_each_sgtable_sg(table, sg, i) {
+ struct page *page = sg_page(sg);
+ // pr_debug("%s:%d,page_size(page)=0x%lx, phys_addr=0x%llx\n",
+ // __func__, __LINE__, page_size(page), page_to_phys(page));
+ es_free_pages(buffer->heap->memblock, page);
+ }
+ sg_free_table(table);
+
+ kfree(buffer);
+}
+
+static const struct dma_buf_ops eswin_rsvmem_heap_buf_ops = {
+ .attach = eswin_rsvmem_heap_attach,
+ .detach = eswin_rsvmem_heap_detach,
+ .map_dma_buf = eswin_rsvmem_heap_map_dma_buf,
+ .unmap_dma_buf = eswin_rsvmem_heap_unmap_dma_buf,
+ .begin_cpu_access = eswin_rsvmem_dma_buf_begin_cpu_access,
+ .end_cpu_access = eswin_rsvmem_dma_buf_end_cpu_access,
+ .mmap = eswin_rsvmem_heap_mmap,
+ .vmap = eswin_rsvmem_heap_vmap,
+ .vunmap = eswin_rsvmem_heap_vunmap,
+ .release = eswin_rsvmem_heap_dma_buf_release,
+};
+
+static struct page *alloc_largest_available(struct mem_block *memblock,
+ unsigned long size,
+ unsigned int max_order)
+{
+ struct page *page;
+ int i;
+
+ for (i = 0; i < NUM_ORDERS; i++) {
+ if (size < (PAGE_SIZE << orders[i]))
+ continue;
+ if (max_order < orders[i])
+ continue;
+
+ page = es_alloc_pages(memblock, orders[i]);
+ if (!page)
+ continue;
+ return page;
+ }
+ return NULL;
+}
+
+static struct dma_buf *eswin_rsvmem_heap_allocate(struct eswin_heap *heap,
+ unsigned long len,
+ unsigned long fd_flags,
+ unsigned long heap_flags)
+{
+ struct eswin_rsvmem_heap *rsvmem_heap = eswin_heap_get_drvdata(heap);
+ struct eswin_rsvmem_heap_buffer *buffer;
+ DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
+ unsigned long size_remaining = len;
+ unsigned int max_order = orders[0];
+ struct dma_buf *dmabuf;
+ struct sg_table *table;
+ struct scatterlist *sg;
+ struct list_head pages;
+ struct page *page, *tmp_page;
+ int i, ret = -ENOMEM;
+ const char *heap_name = NULL;
+
+ /* Mapping secure_memory with cached proprty to kernel space for CPU is NOT permitted */
+ heap_name = eswin_heap_get_name(rsvmem_heap->heap);
+ if (unlikely(!strncmp("secure_memory", heap_name, 13))) {
+ if (!(fd_flags & O_DSYNC))
+ return ERR_PTR(-EINVAL);
+ }
+
+ buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
+ if (!buffer)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&buffer->attachments);
+ mutex_init(&buffer->lock);
+ buffer->heap = rsvmem_heap;
+ buffer->len = len;
+ buffer->fd_flags = fd_flags;
+
+ INIT_LIST_HEAD(&pages);
+ i = 0;
+ while (size_remaining > 0) {
+ /*
+ * Avoid trying to allocate memory if the process
+ * has been killed by SIGKILL
+ */
+ if (fatal_signal_pending(current)) {
+ ret = -EINTR;
+ goto free_buffer;
+ }
+
+ page = alloc_largest_available(rsvmem_heap->memblock, size_remaining, max_order);
+ if (!page)
+ goto free_buffer;
+
+ list_add_tail(&page->lru, &pages);
+ size_remaining -= page_size(page);
+ max_order = compound_order(page);
+ i++;
+ }
+
+ table = &buffer->sg_table;
+ if (sg_alloc_table(table, i, GFP_KERNEL))
+ goto free_buffer;
+
+ sg = table->sgl;
+ list_for_each_entry_safe(page, tmp_page, &pages, lru) {
+ sg_set_page(sg, page, page_size(page), 0);
+ sg = sg_next(sg);
+ list_del(&page->lru);
+ }
+
+ /* create the dmabuf */
+ exp_info.exp_name = eswin_heap_get_name(heap);
+ exp_info.ops = &eswin_rsvmem_heap_buf_ops;
+ exp_info.size = buffer->len;
+ exp_info.flags = O_RDWR | O_CLOEXEC;
+ exp_info.priv = buffer;
+ dmabuf = dma_buf_export(&exp_info);
+ if (IS_ERR(dmabuf)) {
+ ret = PTR_ERR(dmabuf);
+ goto free_pages;
+ }
+ return dmabuf;
+
+free_pages:
+ for_each_sgtable_sg(table, sg, i) {
+ struct page *p = sg_page(sg);
+
+ es_free_pages(rsvmem_heap->memblock, p);
+ }
+ sg_free_table(table);
+free_buffer:
+ list_for_each_entry_safe(page, tmp_page, &pages, lru)
+ es_free_pages(rsvmem_heap->memblock, page);
+ kfree(buffer);
+
+ return ERR_PTR(ret);
+}
+
+static const struct eswin_heap_ops eswin_rsvmem_heap_ops = {
+ .allocate = eswin_rsvmem_heap_allocate,
+};
+
+static int __add_eswin_rsvmem_heap(struct mem_block *memblock, void *data)
+{
+ struct eswin_rsvmem_heap *rsvmem_heap;
+ struct eswin_heap_export_info exp_info;
+
+ rsvmem_heap = kzalloc(sizeof(*rsvmem_heap), GFP_KERNEL);
+ if (!rsvmem_heap)
+ return -ENOMEM;
+ rsvmem_heap->memblock = memblock;
+
+ exp_info.name = eswin_rsvmem_get_name(memblock);
+ exp_info.ops = &eswin_rsvmem_heap_ops;
+ exp_info.priv = rsvmem_heap;
+
+ rsvmem_heap->heap = eswin_heap_add(&exp_info);
+ if (IS_ERR(rsvmem_heap->heap)) {
+ int ret = PTR_ERR(rsvmem_heap->heap);
+
+ kfree(rsvmem_heap);
+ return ret;
+ }
+
+ pr_info("%s for %s successfully!\n", __func__, exp_info.name);
+
+ return 0;
+}
+
+static char *es_heap_name_prefix[] = {
+ "mmz_nid_",
+ "secure_memory"
+};
+#define NUM_ESWIN_RSVMEM_HEAPS ARRAY_SIZE(es_heap_name_prefix)
+
+static int do_add_eswin_rsvmem_heap(struct mem_block *memblock, void *data)
+{
+ int ret = 0;
+ char *prefix = data;
+ const char *rsvmem_name = eswin_rsvmem_get_name(memblock);
+
+ if (strncmp(rsvmem_name, prefix, strlen(prefix)) == 0)
+ ret = __add_eswin_rsvmem_heap(memblock, NULL);
+
+ return ret;
+}
+static int add_eswin_rsvmem_heaps(void)
+{
+ int i;
+ int ret;
+
+ ret = eswin_heap_init();
+ if (ret)
+ return ret;
+
+ for (i = 0; i < NUM_ESWIN_RSVMEM_HEAPS; i++) {
+ eswin_rsvmem_for_each_block(do_add_eswin_rsvmem_heap, es_heap_name_prefix[i]);
+ }
+
+ return 0;
+}
+
+static int do_delete_eswin_rsvmem_heap(struct mem_block *memblock, void *data)
+{
+ int ret = 0;
+ char *prefix = data;
+ const char *rsvmem_name = eswin_rsvmem_get_name(memblock);
+
+ if (strncmp(rsvmem_name, prefix, strlen(prefix)) == 0)
+ ret = eswin_heap_delete_by_name(rsvmem_name);
+
+ return ret;
+}
+static void __exit delete_eswin_rsvmem_heaps(void)
+{
+ int i;
+
+ for (i = 0; i < NUM_ESWIN_RSVMEM_HEAPS; i++) {
+ eswin_rsvmem_for_each_block(do_delete_eswin_rsvmem_heap, es_heap_name_prefix[i]);
+ }
+ eswin_heap_uninit();
+}
+module_init(add_eswin_rsvmem_heaps);
+module_exit(delete_eswin_rsvmem_heaps);
+MODULE_IMPORT_NS(DMA_BUF);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/memory/eswin/es_rsvmem_heap/include/linux/mem_perf_api.h b/drivers/memory/eswin/es_rsvmem_heap/include/linux/mem_perf_api.h
new file mode 100644
index 000000000000..a0357b43d914
--- /dev/null
+++ b/drivers/memory/eswin/es_rsvmem_heap/include/linux/mem_perf_api.h
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Performance test APIs for ESWIN memory
+ *
+ * Copyright 2024 Beijing ESWIN Computing Technology Co., Ltd.
+ * Authors:
+ * LinMin<linmin@eswincomputing.com>
+ *
+ */
+#ifndef __MEM_PERF_API_H__
+#define __MEM_PERF_API_H__
+
+#define IN_KERNEL 1
+
+#if IN_KERNEL
+#include <linux/list.h>
+#define alloc_mem_perf_info(size) kmalloc(size, GFP_KERNEL)
+#define free_mem_perf_info(info) kfree(info)
+#define PRINT_INFO(fmt, ...) pr_info(fmt, ##__VA_ARGS__)
+#else
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "list.h"
+#define alloc_mem_perf_info(size) malloc(size)
+#define free_mem_perf_info(info) free(info)
+#define PRINT_INFO(fmt, ...) printf("%s" fmt, "[ES_DMA_INF]", ##__VA_ARGS__)
+#endif
+
+typedef unsigned long long uint64;
+
+struct mem_perf_info {
+ struct list_head node;
+ char func_name[64];
+ uint64 cycles_start;
+ uint64 cycles_end;
+ uint64 cycles_elapased;
+};
+
+#if defined(CONFIG_RISCV)
+static inline int metal_timer_get_cyclecount(unsigned long long *mcc)
+{
+ unsigned long cycles;
+ asm volatile ("rdtime %0" : "=r" (cycles));
+ *mcc = cycles;
+
+ return 0;
+}
+#else
+static inline int metal_timer_get_cyclecount(unsigned long long *mcc)
+{
+ return 0;
+}
+#endif
+
+//struct mem_perf_info *lookup_mem_api_from_list()
+static inline struct mem_perf_info *memperf_record_cycle_start(const char *func_name, struct list_head *mem_perf_list_head)
+{
+ struct mem_perf_info *m_perf_i;
+
+ m_perf_i = alloc_mem_perf_info(sizeof(*m_perf_i));
+ if (!m_perf_i) {
+ PRINT_INFO("mem perf info alloc failed!\n");
+ return NULL;
+ }
+
+ sprintf(m_perf_i->func_name, "%s", func_name);
+ list_add_tail(&m_perf_i->node, mem_perf_list_head);
+ metal_timer_get_cyclecount(&m_perf_i->cycles_start);
+ m_perf_i->cycles_end = m_perf_i->cycles_start;
+
+ return m_perf_i;
+}
+
+static inline int memperf_record_cycle_end(struct mem_perf_info *m_perf_i)
+{
+ if (NULL == m_perf_i)
+ return -1;
+
+ metal_timer_get_cyclecount(&m_perf_i->cycles_end);
+
+ return 0;
+}
+
+#if defined(CONFIG_RISCV)
+static inline int memperf_print_records(struct list_head *mem_perf_list_head)
+{
+ struct mem_perf_info *m_perf_i;
+ uint64 total_cycles = 0;
+
+ list_for_each_entry(m_perf_i, mem_perf_list_head, node) {
+ m_perf_i->cycles_elapased = m_perf_i->cycles_end - m_perf_i->cycles_start;
+ total_cycles += m_perf_i->cycles_elapased;
+ }
+ PRINT_INFO("Total cycles:%lld, %lld us\n", total_cycles, total_cycles*1000*1000/32768);
+ list_for_each_entry(m_perf_i, mem_perf_list_head, node) {
+ PRINT_INFO("cycle_elapsed:%lld---%%\%lld.%2lld, %s\n",
+ m_perf_i->cycles_elapased, (100*m_perf_i->cycles_elapased)/total_cycles,
+ (10000*m_perf_i->cycles_elapased)%total_cycles, m_perf_i->func_name);
+ }
+
+ return 0;
+}
+#else
+static inline int memperf_print_records(struct list_head *mem_perf_list_head)
+{
+ return 0;
+}
+#endif
+
+static inline int memperf_free_records_list(struct list_head *mem_perf_list_head)
+{
+ struct mem_perf_info *m_perf_i, *tmp;
+
+ list_for_each_entry_safe(m_perf_i, tmp, mem_perf_list_head, node) {
+ list_del(&m_perf_i->node);
+ free_mem_perf_info(m_perf_i);
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/drivers/memory/eswin/es_rsvmem_heap/include/uapi/linux/eswin_rsvmem_common.h b/drivers/memory/eswin/es_rsvmem_heap/include/uapi/linux/eswin_rsvmem_common.h
new file mode 100644
index 000000000000..7d863001cfc3
--- /dev/null
+++ b/drivers/memory/eswin/es_rsvmem_heap/include/uapi/linux/eswin_rsvmem_common.h
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ESWIN Heaps Userspace API
+ *
+ * Copyright 2024 Beijing ESWIN Computing Technology Co., Ltd.
+ * Authors:
+ * LinMin<linmin@eswincomputing.com>
+ *
+ */
+
+#ifndef _UAPI_ESWIN_HEAPS_H
+#define _UAPI_ESWIN_HEAPS_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/**
+ * DOC: DMABUF Heaps Userspace API
+ */
+/* Valid FD_FLAGS are O_CLOEXEC, O_RDONLY, O_WRONLY, O_RDWR, O_SYNC */
+#define ESWIN_HEAP_VALID_FD_FLAGS (O_CLOEXEC | O_ACCMODE | O_SYNC)
+
+/* Add HEAP_SPRAM_FORCE_CONTIGUOUS heap flags for ESWIN SPRAM HEAP */
+#define HEAP_FLAGS_SPRAM_FORCE_CONTIGUOUS (1 << 0)
+#define ESWIN_HEAP_VALID_HEAP_FLAGS (HEAP_FLAGS_SPRAM_FORCE_CONTIGUOUS)
+
+/**
+ * struct eswin_heap_allocation_data - metadata passed from userspace for
+ * allocations
+ * @len: size of the allocation
+ * @fd: will be populated with a fd which provides the
+ * handle to the allocated dma-buf
+ * @fd_flags: file descriptor flags used when allocating
+ * @heap_flags: flags passed to heap
+ *
+ * Provided by userspace as an argument to the ioctl
+ */
+struct eswin_heap_allocation_data {
+ __u64 len;
+ __u32 fd;
+ __u32 fd_flags;
+ __u64 heap_flags;
+};
+
+#define ESWIN_HEAP_IOC_MAGIC 'H'
+
+/**
+ * DOC: ESWIN_HEAP_IOCTL_ALLOC - allocate memory from pool
+ *
+ * Takes a eswin_heap_allocation_data struct and returns it with the fd field
+ * populated with the dmabuf handle of the allocation.
+ */
+#define ESWIN_HEAP_IOCTL_ALLOC _IOWR(ESWIN_HEAP_IOC_MAGIC, 0x0,\
+ struct eswin_heap_allocation_data)
+
+#endif /* _UAPI_ESWIN_HEAPS_H */
diff --git a/include/linux/dmabuf-heap-import-helper.h b/include/linux/dmabuf-heap-import-helper.h
new file mode 100644
index 000000000000..6fd339ee8ff3
--- /dev/null
+++ b/include/linux/dmabuf-heap-import-helper.h
@@ -0,0 +1,100 @@
+#ifndef _DMABUF_HEAP_IMPORT_H_
+#define _DMABUF_HEAP_IMPORT_H_
+
+#include <linux/rbtree.h>
+#include <linux/dma-buf.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+#include <linux/scatterlist.h>
+#include <linux/eswin_rsvmem_common.h>
+
+#define SYSTEM_DEV_NODE "system"
+#define CMA_DEV_NODE_RES "reserved"
+#define CMA_DEV_NODE_DFT "linux,cma"
+#define SYSTEM_COHERENT_DEV_NODE "system_coherent"
+
+struct dmaheap_file_private {
+ /* private: */
+ struct rb_root dmabufs;
+ struct rb_root handles;
+};
+
+struct heap_root {
+ struct dmaheap_file_private fp;
+ struct device *dev;
+ struct list_head header;
+ struct mutex lock;
+};
+
+struct heap_mem {
+ /* refcount is also protected by lock in the struct heap_root */
+ struct kref refcount;
+
+ int dbuf_fd;
+ struct dma_buf *dbuf;
+ struct dma_buf_attachment *import_attach;
+
+ struct heap_root *root;
+ struct list_head list;
+ struct rb_node *rb;
+
+ struct sg_table *sgt;
+ void *vaddr;
+
+ enum dma_data_direction dir;
+};
+
+struct esw_export_buffer_info {
+ char name[64];
+ int fd_flags;
+
+ int dbuf_fd;
+ struct dma_buf *dmabuf;
+
+ struct esw_slice_buffer {
+ __u64 offset;
+ size_t len;
+ } slice;
+};
+
+static int inline common_dmabuf_heap_begin_cpu_access(struct heap_mem *heap_obj)
+{
+ return dma_buf_begin_cpu_access(heap_obj->dbuf, heap_obj->dir);
+}
+
+static int inline common_dmabuf_heap_end_cpu_access(struct heap_mem *heap_obj)
+{
+ return dma_buf_end_cpu_access(heap_obj->dbuf, heap_obj->dir);
+}
+
+static inline size_t common_dmabuf_heap_get_size(struct heap_mem *heap_obj)
+{
+ WARN_ON(!heap_obj);
+ return (heap_obj != NULL) ? heap_obj->dbuf->size : 0;
+}
+
+static inline void common_dmabuf_heap_set_dir(struct heap_mem *heap_obj, enum dma_data_direction dir)
+{
+ WARN_ON(!heap_obj);
+ if (heap_obj)
+ heap_obj->dir = dir;
+}
+
+void common_dmabuf_heap_import_init(struct heap_root *root, struct device *dev);
+void common_dmabuf_heap_import_uninit(struct heap_root *root);
+
+struct heap_mem *common_dmabuf_lookup_heapobj_by_fd(struct heap_root *root, int fd);
+struct heap_mem *common_dmabuf_lookup_heapobj_by_dma_buf_st(struct heap_root *root, struct dma_buf *dma_buf);
+
+struct heap_mem *common_dmabuf_heap_import_from_user(struct heap_root *root, int fd);
+struct heap_mem *common_dmabuf_heap_import_from_user_with_dma_buf_st(struct heap_root *root, struct dma_buf *dma_buf);
+void common_dmabuf_heap_release(struct heap_mem *heap_obj);
+
+void *common_dmabuf_heap_map_vaddr(struct heap_mem *heap_obj);
+void common_dmabuf_heap_umap_vaddr(struct heap_mem *heap_obj);
+
+struct heap_mem *common_dmabuf_heap_import_from_kernel(struct heap_root *root, char *name, size_t len, unsigned int fd_flags);
+
+int esw_common_dmabuf_split_export(int dbuf_fd, unsigned int offset, size_t len, int fd_flags, char *name);
+
+#endif
diff --git a/include/linux/eswin_rsvmem_common.h b/include/linux/eswin_rsvmem_common.h
new file mode 100644
index 000000000000..33862d7292d7
--- /dev/null
+++ b/include/linux/eswin_rsvmem_common.h
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * DMABUF Heaps Allocation Infrastructure
+ *
+ * Copyright (C) 2011 Google, Inc.
+ * Copyright (C) 2019 Linaro Ltd.
+ */
+
+#ifndef _ESWIN_HEAPS_H
+#define _ESWIN_HEAPS_H
+
+#include <linux/cdev.h>
+#include <linux/types.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,6,0)
+#define dma_buf_map iosys_map
+#define dma_buf_map_set_vaddr iosys_map_set_vaddr
+#define dma_buf_map_clear iosys_map_clear
+#else
+#define vm_flags_clear(vma, flags) (vma->vm_flags &= ~flags)
+#endif
+
+struct eswin_heap;
+
+/**
+ * struct eswin_heap_ops - ops to operate on a given heap
+ * @allocate: allocate dmabuf and return struct dma_buf ptr
+ *
+ * allocate returns dmabuf on success, ERR_PTR(-errno) on error.
+ */
+struct eswin_heap_ops {
+ struct dma_buf *(*allocate)(struct eswin_heap *heap,
+ unsigned long len,
+ unsigned long fd_flags,
+ unsigned long heap_flags);
+};
+
+/**
+ * struct eswin_heap_export_info - information needed to export a new dmabuf heap
+ * @name: used for debugging/device-node name
+ * @ops: ops struct for this heap
+ * @priv: heap exporter private data
+ *
+ * Information needed to export a new dmabuf heap.
+ */
+struct eswin_heap_export_info {
+ const char *name;
+ const struct eswin_heap_ops *ops;
+ void *priv;
+};
+
+/**
+ * eswin_heap_get_drvdata() - get per-heap driver data
+ * @heap: DMA-Heap to retrieve private data for
+ *
+ * Returns:
+ * The per-heap data for the heap.
+ */
+void *eswin_heap_get_drvdata(struct eswin_heap *heap);
+
+/**
+ * eswin_heap_get_name() - get heap name
+ * @heap: DMA-Heap to retrieve private data for
+ *
+ * Returns:
+ * The char* for the heap name.
+ */
+const char *eswin_heap_get_name(struct eswin_heap *heap);
+
+/**
+ * eswin_heap_add - adds a heap to dmabuf heaps
+ * @exp_info: information needed to register this heap
+ */
+struct eswin_heap *eswin_heap_add(const struct eswin_heap_export_info *exp_info);
+
+/**
+ * eswin_heap_delete - delete a heap from dmabuf heaps
+ * @heap: heap needed to delete
+ */
+int eswin_heap_delete(struct eswin_heap *heap);
+
+/**
+ * eswin_heap_delete_by_name - find and delete a heap from dmabuf heaps by name
+ * @name: heap name needed to delete
+ */
+int eswin_heap_delete_by_name(const char *name);
+
+int eswin_heap_kalloc(char *name, size_t len, unsigned int fd_flags, unsigned int heap_flags);
+
+int eswin_heap_init(void);
+void eswin_heap_uninit(void);
+#define dma_heap_kalloc(name, len, fd_flags, heap_flags) eswin_heap_kalloc(name, len, fd_flags, heap_flags)
+#endif /* _ESWIN_HEAPS_H */
diff --git a/include/uapi/linux/es_vb_user.h b/include/uapi/linux/es_vb_user.h
new file mode 100644
index 000000000000..db3bda6d0d69
--- /dev/null
+++ b/include/uapi/linux/es_vb_user.h
@@ -0,0 +1,58 @@
+#ifndef ES_VB_USER_H
+#define ES_VB_USER_H
+
+/**
+ * mmz vb configurations
+*/
+#define ES_VB_MAX_MMZs 4
+
+#define ES_VB_INVALID_POOLID (-1U)
+
+#define ES_VB_MAX_MOD_POOL 16
+
+#define ES_MAX_MMZ_NAME_LEN 64
+
+/**
+ * mmz vb pool or block struct definition
+ */
+typedef enum esVB_UID_E {
+ VB_UID_PRIVATE = 0,
+ VB_UID_COMMON,
+ VB_UID_VI,
+ VB_UID_VO,
+ VB_UID_VPS,
+ VB_UID_VENC,
+ VB_UID_VDEC,
+ VB_UID_HAE,
+ VB_UID_USER,
+ VB_UID_BUTT,
+ VB_UID_MAX,
+} VB_UID_E;
+
+typedef enum {
+ SYS_CACHE_MODE_NOCACHE = 0,
+ SYS_CACHE_MODE_CACHED = 1,
+ SYS_CACHE_MODE_LLC = 2,
+ SYS_CACHE_MODE_BUTT,
+} SYS_CACHE_MODE_E;
+
+typedef struct esVB_POOL_CONFIG_S {
+ ES_U64 blkSize;
+ ES_U32 blkCnt;
+ SYS_CACHE_MODE_E enRemapMode;
+ ES_CHAR mmzName[ES_MAX_MMZ_NAME_LEN];
+} VB_POOL_CONFIG_S;
+
+typedef struct esVB_CONFIG_S {
+ ES_U32 poolCnt;
+ VB_POOL_CONFIG_S poolCfgs[ES_VB_MAX_MOD_POOL];
+} VB_CONFIG_S;
+
+typedef struct ES_DEV_BUF {
+ ES_U64 memFd;
+ ES_U64 offset;
+ ES_U64 size;
+ ES_U64 reserve;
+} ES_DEV_BUF_S;
+
+#endif
diff --git a/include/uapi/linux/mmz_vb.h b/include/uapi/linux/mmz_vb.h
new file mode 100644
index 000000000000..49ebac96f6e1
--- /dev/null
+++ b/include/uapi/linux/mmz_vb.h
@@ -0,0 +1,175 @@
+#ifndef _MMZ_VB_UAPI_H_
+#define _MMZ_VB_UAPI_H_
+
+#include <linux/types.h>
+
+/*vb cfg flag*/
+#define MMZ_VB_CFG_FLAG_INIT (1 << 0)
+
+/*vb pool flag*/
+#define MMZ_VB_POOL_FLAG_DESTORY (1 << 0)
+
+/*set cfg cmd*/
+typedef struct esVB_SET_CFG_REQ_S {
+ enum esVB_UID_E uid;
+ struct esVB_CONFIG_S cfg;
+}VB_SET_CFG_REQ_S;
+
+typedef struct esVB_SET_CFG_CMD_S {
+ struct esVB_SET_CFG_REQ_S CfgReq;
+}VB_SET_CFG_CMD_S;
+
+/*get cfg cmd*/
+typedef struct esVB_GET_CFG_REQ_S {
+ enum esVB_UID_E uid;
+}VB_Get_CFG_REQ_S;
+
+typedef struct esVB_GET_CFG_RSP_S {
+ struct esVB_CONFIG_S cfg;
+}VB_Get_CFG_RSP_S;
+
+typedef struct esVB_GET_CFG_CMD_S {
+ struct esVB_GET_CFG_REQ_S req;
+ struct esVB_GET_CFG_RSP_S rsp;
+}VB_GET_CFG_CMD_S;
+
+/*Init cfg cmd*/
+typedef struct esVB_INIT_CFG_REQ_S {
+ enum esVB_UID_E uid;
+}VB_INIT_CFG_REQ_S;
+
+typedef struct esVB_INIT_CFG_CMD_S {
+ struct esVB_INIT_CFG_REQ_S req;
+}VB_INIT_CFG_CMD_S;
+
+/*UnInit cfg cmd*/
+typedef struct esVB_UNINIT_CFG_REQ_S {
+ enum esVB_UID_E uid;
+}VB_UNINIT_CFG_REQ_S;
+
+typedef struct esVB_UNINIT_CFG_CMD_S {
+ struct esVB_UNINIT_CFG_REQ_S req;
+}VB_UNINIT_CFG_CMD_S;
+
+/*create pool cmd*/
+typedef struct esVB_CREATE_POOL_REQ_S {
+ struct esVB_POOL_CONFIG_S req;
+}VB_CREATE_POOL_REQ_S;
+
+typedef struct esVB_CREATE_POOL_RESP_S {
+ __u32 PoolId;
+} VB_CREATE_POOL_RESP_S;
+
+typedef struct esVB_CREATE_POOL_CMD_S {
+ struct esVB_CREATE_POOL_REQ_S PoolReq;
+ struct esVB_CREATE_POOL_RESP_S PoolResp;
+}VB_CREATE_POOL_CMD_S;
+
+/*destory pool cmd*/
+typedef struct esVB_DESTORY_POOL_REQ_S {
+ __u32 PoolId;
+}VB_DESTORY_POOL_REQ_S;
+
+typedef struct esVB_DESTORY_POOL_RESP_S {
+ __u32 Result;
+}VB_DESTORY_POOL_RESP_S;
+
+typedef struct esVB_DESTORY_POOL_CMD_S {
+ struct esVB_DESTORY_POOL_REQ_S req;
+ struct esVB_DESTORY_POOL_RESP_S rsp;
+}VB_DESTORY_POOL_CMD_S;
+
+typedef struct esVB_GET_BLOCK_REQ_S {
+ enum esVB_UID_E uid;
+ VB_POOL poolId;
+ __u64 blkSize;
+ char mmzName[ES_MAX_MMZ_NAME_LEN];
+}VB_GET_BLOCK_REQ_S;
+typedef struct esVB_GET_BLOCK_RESP_S {
+ __u64 actualBlkSize;
+ int fd;
+ int nr; /*bitmap index in the pool*/
+}VB_GET_BLOCK_RESP_S;
+typedef struct esVB_GET_BLOCK_CMD_S
+{
+ struct esVB_GET_BLOCK_REQ_S getBlkReq;
+ struct esVB_GET_BLOCK_RESP_S getBlkResp;
+}VB_GET_BLOCK_CMD_S;
+
+//corresponding to MMZ_VB_IOCTL_POOL_SIZE
+typedef struct esVB_GET_POOLSIZE_CMD_S
+{
+ VB_POOL poolId;
+ __u64 poolSize;
+}VB_GET_POOLSIZE_CMD_S;
+
+//corresponding to MMZ_VB_IOCTL_FLUSH_POOL
+typedef struct esVB_FLUSH_POOL_CMD_S
+{
+ VB_POOL poolId;
+ __u64 offset; // offset addr in the pool
+ __u64 size; // size to be flushed
+}VB_FLUSH_POOL_CMD_S;
+
+//corresponding to MMZ_VB_IOCTL_BLOCK_TO_POOL
+typedef struct esVB_BLOCK_TO_POOL_CMD_S
+{
+ int fd; // Input: The dmabuf_fd of the block
+ VB_POOL poolId; //Output: The pool which the block belongs to;
+}VB_BLOCK_TO_POOL_CMD_S;
+
+//corresponding to MMZ_VB_IOCTL_GET_BLOCK_OFFSET
+typedef struct esVB_GET_BLOCKOFFSET_CMD_S
+{
+ int fd; // Input: The dmabuf_fd, it might be the real block or the splittedBlock
+ __u64 offset; // Output: The offset in pool
+}VB_GET_BLOCKOFFSET_CMD_S;
+
+//corresponding to MMZ_VB_IOCTL_SPLIT_DMABUF
+typedef struct esVB_SPLIT_DMABUF_CMD_S {
+ int fd; /* Input: The original dmabuf fd to be splitted */
+ int slice_fd; /* Outpu: splitted dmabuf fd */
+ __u64 offset; /* Input: offset of the buffer relative to the original dmabuf */
+ __u64 len; /* size of the buffer to be splitted */
+}VB_BLOCK_SPLIT_CMD_S;
+
+//corresponding to MMZ_VB_IOCTL_DMABUF_REFCOUNT
+typedef struct esVB_DMABUF_REFCOUNT_CMD_S
+{
+ int fd; // Input: The dmabuf_fd
+ __u64 refCnt; // Output: The file_count of the dmabuf
+}VB_DMABUF_REFCOUNT_CMD_S;
+
+//corresponding to MMZ_VB_IOCTL_RETRIEVE_MEM_NODE
+typedef struct esVB_RETRIEVE_MEM_NODE_CMD_S
+{
+ int fd; // Input: The dmabuf_fd
+ void *cpu_vaddr; // Input: The virtual addr of cpu in user space
+ int numa_node; // Ouput: return the NUMA node id of the memory
+}VB_RETRIEVE_MEM_NODE_CMD_S;
+
+//corresponding to MMZ_VB_IOCTL_DMABUF_SIZE
+typedef struct esVB_DMABUF_SIZE_CMD_S
+{
+ int fd; // Input: The dmabuf_fd
+ __u64 size; // Output: The size of the dmabuf
+}VB_DMABUF_SIZE_CMD_S;
+
+#define MMZ_VB_IOC_MAGIC 'M'
+#define MMZ_VB_IOCTL_GET_BLOCK _IOWR(MMZ_VB_IOC_MAGIC, 0x0, struct esVB_GET_BLOCK_CMD_S)
+#define MMZ_VB_IOCTL_SET_CFG _IOWR(MMZ_VB_IOC_MAGIC, 0x1, struct esVB_SET_CFG_CMD_S)
+#define MMZ_VB_IOCTL_GET_CFG _IOWR(MMZ_VB_IOC_MAGIC, 0x2, struct esVB_GET_CFG_CMD_S)
+#define MMZ_VB_IOCTL_INIT_CFG _IOWR(MMZ_VB_IOC_MAGIC, 0x3, struct esVB_INIT_CFG_CMD_S)
+#define MMZ_VB_IOCTL_UNINIT_CFG _IOWR(MMZ_VB_IOC_MAGIC, 0x4, struct esVB_UNINIT_CFG_CMD_S)
+#define MMZ_VB_IOCTL_CREATE_POOL _IOWR(MMZ_VB_IOC_MAGIC, 0x5, struct esVB_CREATE_POOL_CMD_S)
+#define MMZ_VB_IOCTL_DESTORY_POOL _IOWR(MMZ_VB_IOC_MAGIC, 0x6, struct esVB_DESTORY_POOL_CMD_S)
+#define MMZ_VB_IOCTL_POOL_SIZE _IOR(MMZ_VB_IOC_MAGIC, 0x7, struct esVB_GET_POOLSIZE_CMD_S)
+#define MMZ_VB_IOCTL_FLUSH_POOL _IOW(MMZ_VB_IOC_MAGIC, 0x8, struct esVB_FLUSH_POOL_CMD_S)
+#define MMZ_VB_IOCTL_BLOCK_TO_POOL _IOR(MMZ_VB_IOC_MAGIC, 0x9, struct esVB_BLOCK_TO_POOL_CMD_S)
+#define MMZ_VB_IOCTL_GET_BLOCK_OFFSET _IOR(MMZ_VB_IOC_MAGIC, 0xa, struct esVB_GET_BLOCKOFFSET_CMD_S)
+#define MMZ_VB_IOCTL_SPLIT_DMABUF _IOWR(MMZ_VB_IOC_MAGIC, 0xb, struct esVB_SPLIT_DMABUF_CMD_S)
+#define MMZ_VB_IOCTL_DMABUF_REFCOUNT _IOR(MMZ_VB_IOC_MAGIC, 0xc, struct esVB_DMABUF_REFCOUNT_CMD_S)
+#define MMZ_VB_IOCTL_RETRIEVE_MEM_NODE _IOR(MMZ_VB_IOC_MAGIC, 0xd, struct esVB_RETRIEVE_MEM_NODE_CMD_S)
+#define MMZ_VB_IOCTL_DMABUF_SIZE _IOR(MMZ_VB_IOC_MAGIC, 0xe, struct esVB_DMABUF_SIZE_CMD_S)
+
+#endif
--
2.47.0