kernel/0330-WIN2030-14758-feat-Add-API-for-remapping-malloc-buff.patch
2025-02-27 20:57:56 -05:00

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