229 lines
7.0 KiB
Diff
229 lines
7.0 KiB
Diff
From e17dcffc546d510f3a2576f10033d050f22ca4f8 Mon Sep 17 00:00:00 2001
|
|
From: linmin <linmin@eswincomputing.com>
|
|
Date: Tue, 3 Dec 2024 14:21:54 +0800
|
|
Subject: [PATCH 330/416] WIN2030-14758:feat:Add API for remapping malloc
|
|
buffer
|
|
|
|
Changelogs:
|
|
1.Add remap_malloc_buf() API in dmabuf-heap-import-helper.c for remapping the
|
|
buffer which is allocated by malloc() from user space as uncached memory.
|
|
So that the access to the memory from CPU and devices is coherent.
|
|
|
|
Signed-off-by: linmin <linmin@eswincomputing.com>
|
|
Change-Id: I7f13eac26eaa7de0fb5e8c2077b2bb153d42fb22
|
|
---
|
|
.../dmabuf-heap-import-helper.c | 187 ++++++++++++++++++
|
|
include/linux/dmabuf-heap-import-helper.h | 2 +
|
|
2 files changed, 189 insertions(+)
|
|
|
|
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
|
|
index d9ba68d918ef..283fbdfb1c07 100644
|
|
--- a/drivers/memory/eswin/es_rsvmem_heap/dmabuf-heap-import-helper.c
|
|
+++ b/drivers/memory/eswin/es_rsvmem_heap/dmabuf-heap-import-helper.c
|
|
@@ -1169,3 +1169,190 @@ static void __exit split_dmabuf_exit(void)
|
|
|
|
module_init(split_dmabuf_init);
|
|
module_exit(split_dmabuf_exit);
|
|
+
|
|
+
|
|
+static int vmf_replace_pages(struct vm_area_struct *vma, unsigned long addr,
|
|
+ struct page **pages, unsigned long num, pgprot_t prot)
|
|
+{
|
|
+ struct mm_struct *const mm = vma->vm_mm;
|
|
+ unsigned long remaining_pages_total = num;
|
|
+ unsigned long pfn;
|
|
+ pte_t *pte, entry;
|
|
+ spinlock_t *ptl;
|
|
+ unsigned long newprot_val = pgprot_val(prot);
|
|
+ pgprot_t new_prot;
|
|
+ u32 i = 0;
|
|
+
|
|
+ while (remaining_pages_total) {
|
|
+ pte = get_locked_pte(mm, addr, &ptl);
|
|
+ if (!pte)
|
|
+ return VM_FAULT_OOM;
|
|
+
|
|
+ entry = ptep_get(pte);
|
|
+ pfn = page_to_pfn(pages[i]);
|
|
+ pr_debug("page_to_pfn(pages[%d])=0x%lx, pte_pfn(entry)=0x%lx, pte_val(entry)=0x%lx\n",
|
|
+ i, pfn, pte_pfn(entry), pte_val(entry));
|
|
+
|
|
+ newprot_val = (pte_val(entry) & (~_PAGE_PFN_MASK)) | newprot_val;
|
|
+ if (newprot_val == (pte_val(entry) & (~_PAGE_PFN_MASK)))
|
|
+ goto SKIP_PAGE;
|
|
+
|
|
+ new_prot = __pgprot(newprot_val);
|
|
+ entry = mk_pte(pages[i], new_prot);
|
|
+ pr_debug("page_to_pfn(pages[%d])=0x%lx, pte_pfn(entry)=0x%lx, modified pte_val(entry)=0x%lx\n",
|
|
+ i, page_to_pfn(pages[i]), pte_pfn(entry), pte_val(entry));
|
|
+ set_pte_at(vma->vm_mm, addr, pte, entry);
|
|
+ update_mmu_cache(vma, addr, pte);
|
|
+
|
|
+SKIP_PAGE:
|
|
+ addr += PAGE_SIZE;
|
|
+ pte_unmap_unlock(pte, ptl);
|
|
+ remaining_pages_total--;
|
|
+ i++;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int zap_and_replace_pages(struct vm_area_struct *vma, u64 addr, size_t len, pgprot_t prot)
|
|
+{
|
|
+ struct page **pages;
|
|
+ u32 offset;
|
|
+ unsigned long nr_pages;
|
|
+ u64 first, last;
|
|
+ u64 addr_aligned = ALIGN_DOWN(addr, PAGE_SIZE);
|
|
+ u32 i;
|
|
+ int ret = -ENOMEM;
|
|
+
|
|
+ if (!len) {
|
|
+ pr_err("invalid userptr size.\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ /* offset into first page */
|
|
+ offset = offset_in_page(addr);
|
|
+
|
|
+ /* Calculate number of pages */
|
|
+ first = (addr & PAGE_MASK) >> PAGE_SHIFT;
|
|
+ last = ((addr + len - 1) & PAGE_MASK) >> PAGE_SHIFT;
|
|
+ nr_pages = last - first + 1;
|
|
+ pr_debug("%s:%d, addr=0x%llx(addr_aligned=0x%llx), len=0x%lx, nr_pages=0x%lx(fist:0x%llx,last:0x%llx)\n",
|
|
+ __func__, __LINE__, addr, addr_aligned, len, nr_pages, first, last);
|
|
+
|
|
+ /* alloc array to storing the pages */
|
|
+ pages = kvmalloc_array(nr_pages,
|
|
+ sizeof(struct page *),
|
|
+ GFP_KERNEL);
|
|
+ if (!pages)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ ret = get_user_pages_fast(addr_aligned,
|
|
+ nr_pages,
|
|
+ FOLL_FORCE | FOLL_WRITE,
|
|
+ pages);
|
|
+
|
|
+ if (ret != nr_pages) {
|
|
+ nr_pages = (ret >= 0) ? ret : 0;
|
|
+ pr_err("get_user_pages_fast, err=%d [0x%lx]\n",
|
|
+ ret, nr_pages);
|
|
+ ret = ret < 0 ? ret : -EINVAL;
|
|
+ goto free_pages_list;
|
|
+ }
|
|
+ #if 0
|
|
+ for (i = 0; i < nr_pages; i++) {
|
|
+ pr_debug("page_to_pfn(pages[%i])=0x%lx\n", i, page_to_pfn(pages[i]));
|
|
+ }
|
|
+ #endif
|
|
+
|
|
+ pr_debug("%s, vma->vm_start 0x%lx, vma->vm_end 0x%lx, (vm_end - vm_start) 0x%lx, vma->vm_pgoff 0x%lx, user_va 0x%llx, len 0x%lx, nr_pages 0x%lx\n",
|
|
+ __func__, vma->vm_start, vma->vm_end,
|
|
+ (vma->vm_end - vma->vm_start), vma->vm_pgoff, addr, len, nr_pages);
|
|
+
|
|
+ /* construct new page table entry for the pages*/
|
|
+ ret = vmf_replace_pages(vma, addr_aligned,
|
|
+ pages, nr_pages, prot);
|
|
+ if (ret) {
|
|
+ pr_err("err %d, failed to vmf_replace_pages!!!\n", ret);
|
|
+ ret = -EFAULT;
|
|
+ goto free_user_pages;
|
|
+ }
|
|
+
|
|
+ /* Flush cache if the access to the user virtual address is uncached. */
|
|
+ if (pgprot_val(prot) & _PAGE_UNCACHE) {
|
|
+ for (i = 0; i < nr_pages; i++) {
|
|
+ /* flush cache*/
|
|
+ arch_sync_dma_for_device(page_to_phys(pages[i]), PAGE_SIZE, DMA_BIDIRECTIONAL);
|
|
+ /* put page back */
|
|
+ put_page(pages[i]);
|
|
+ }
|
|
+ }
|
|
+ else {
|
|
+ for (i = 0; i < nr_pages; i++) {
|
|
+ /* put page back */
|
|
+ put_page(pages[i]);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ kvfree(pages);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+free_user_pages:
|
|
+ for (i = 0; i < nr_pages; i++) {
|
|
+ /* put page back */
|
|
+ put_page(pages[i]);
|
|
+ }
|
|
+free_pages_list:
|
|
+ kvfree(pages);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * remap_malloc_buf - remap a range of memory allocated by malloc() API from user space.
|
|
+ * Normally, the CPU access to the user virtual address which is allocated by mallc() API is
|
|
+ * through cache. This remap_malloc_buf() API is to re-construct the pte table entry for the
|
|
+ * corresponding pages of the user virtual address as uncached memory, so that CPU access to
|
|
+ * the virtual address is uncached.
|
|
+ * @addr: virtual address which is got by malloc API from user space
|
|
+ * @len: the length of the memory allocated by malloc API
|
|
+ * @uncaced: if true, remap the memory as uncached, otherwise cached
|
|
+ *
|
|
+ * Return 0 if success.
|
|
+ *
|
|
+ * If uncached flag is true, the memory range of this virtual address will be flushed to make
|
|
+ * sure all the dirty data is evicted.
|
|
+ *
|
|
+ */
|
|
+int remap_malloc_buf(unsigned long addr, size_t len, bool uncaced)
|
|
+{
|
|
+ struct vm_area_struct *vma = NULL;
|
|
+ struct mm_struct *mm = current->mm;
|
|
+ pgprot_t prot;
|
|
+ int ret = 0;
|
|
+
|
|
+ if (!len) {
|
|
+ pr_err("Invalid userptr size!!!\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ mmap_read_lock(mm);
|
|
+ vma = vma_lookup(mm, addr);
|
|
+ if (!vma) {
|
|
+ pr_err("%s, vma_lookup failed!\n", __func__);
|
|
+ mmap_read_unlock(mm);
|
|
+ return -EFAULT;
|
|
+ }
|
|
+
|
|
+ pgprot_val(prot) = 0;
|
|
+ /* If true, add uncached property so that pfn_pte will use the pfn of system port to
|
|
+ constructs the page table entry.
|
|
+ Be carefull, do NOT change the value of the original vma->vm_page_prot*/
|
|
+ if (uncaced)
|
|
+ prot = pgprot_dmacoherent(prot);
|
|
+
|
|
+ ret = zap_and_replace_pages(vma, addr, len, prot);
|
|
+ mmap_read_unlock(mm);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(remap_malloc_buf);
|
|
\ No newline at end of file
|
|
diff --git a/include/linux/dmabuf-heap-import-helper.h b/include/linux/dmabuf-heap-import-helper.h
|
|
index 9a474f6594e7..d4acd7c5177c 100644
|
|
--- a/include/linux/dmabuf-heap-import-helper.h
|
|
+++ b/include/linux/dmabuf-heap-import-helper.h
|
|
@@ -132,4 +132,6 @@ struct heap_mem *common_dmabuf_heap_rsv_iova_map(struct heap_root *root, int fd,
|
|
void common_dmabuf_heap_rsv_iova_unmap(struct heap_mem *heap_obj);
|
|
void common_dmabuf_heap_rsv_iova_uninit(struct heap_root *root);
|
|
|
|
+int remap_malloc_buf(unsigned long addr, size_t len, bool uncaced);
|
|
+
|
|
#endif
|
|
--
|
|
2.47.0
|
|
|