From 8c52af9b59304586ade80c2f48dab4ce951b2fcf Mon Sep 17 00:00:00 2001 From: Ketan Patil Date: Sun, 14 Jul 2024 07:11:51 +0000 Subject: [PATCH] video: tegra: nvmap: Add hugetlbfs support - For NvRmHeap_GpuMem, we are switching from a carveout to huge pages obtained from hugetlbfs. - Allocate a handle from VA using get_user_pages, when allocation is requested from GPU heap. - Introduce a new field to indicate that the pages are allocated from hugetlbfs. This field will be useful while returning the correct heap while querying the handle params for a handle which was created using hugetlbfs. - Update the query heap API for GpuMem heap to return the huge pages memory values from meminfo. Bug 4510173 Change-Id: I0dbef4c4e95969f8e3975a6dc58f10255db0635e Signed-off-by: Ketan Patil Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3174720 Tested-by: mobile promotions Reviewed-by: mobile promotions --- drivers/video/tegra/nvmap/nvmap_alloc.c | 10 ++- drivers/video/tegra/nvmap/nvmap_ioctl.c | 104 +++++++++++++++++++++++- drivers/video/tegra/nvmap/nvmap_priv.h | 7 +- include/uapi/linux/nvmap.h | 1 + 4 files changed, 114 insertions(+), 8 deletions(-) diff --git a/drivers/video/tegra/nvmap/nvmap_alloc.c b/drivers/video/tegra/nvmap/nvmap_alloc.c index 440263db..f2924fbf 100644 --- a/drivers/video/tegra/nvmap/nvmap_alloc.c +++ b/drivers/video/tegra/nvmap/nvmap_alloc.c @@ -247,7 +247,8 @@ static void alloc_handle(struct nvmap_client *client, static int alloc_handle_from_va(struct nvmap_client *client, struct nvmap_handle *h, ulong vaddr, - u32 flags) + u32 flags, + unsigned int heap_mask) { size_t nr_page = h->size >> PAGE_SHIFT; struct page **pages; @@ -276,6 +277,8 @@ static int alloc_handle_from_va(struct nvmap_client *client, h->heap_type = NVMAP_HEAP_IOVMM; h->heap_pgalloc = true; h->from_va = true; + if (heap_mask & NVMAP_HEAP_CARVEOUT_GPU) + h->has_hugetlbfs_pages = true; mb(); h->alloc = true; return ret; @@ -424,7 +427,8 @@ out: int nvmap_alloc_handle_from_va(struct nvmap_client *client, struct nvmap_handle *h, ulong addr, - unsigned int flags) + unsigned int flags, + unsigned int heap_mask) { int err = -ENOMEM; int tag; @@ -455,7 +459,7 @@ int nvmap_alloc_handle_from_va(struct nvmap_client *client, client->task->pid, task_comm); } - err = alloc_handle_from_va(client, h, addr, flags); + err = alloc_handle_from_va(client, h, addr, flags, heap_mask); if (err) { pr_err("alloc_handle_from_va failed %d", err); nvmap_handle_put(h); diff --git a/drivers/video/tegra/nvmap/nvmap_ioctl.c b/drivers/video/tegra/nvmap/nvmap_ioctl.c index 488a16c2..f345cf8a 100644 --- a/drivers/video/tegra/nvmap/nvmap_ioctl.c +++ b/drivers/video/tegra/nvmap/nvmap_ioctl.c @@ -200,6 +200,7 @@ int nvmap_ioctl_alloc(struct file *filp, void __user *arg) bool is_ro = false; int err; long dmabuf_ref = 0; + size_t old_size; if (copy_from_user(&op, arg, sizeof(op))) return -EFAULT; @@ -219,6 +220,16 @@ int nvmap_ioctl_alloc(struct file *filp, void __user *arg) if (IS_ERR_OR_NULL(handle)) return -EINVAL; + if (op.heap_mask & NVMAP_HEAP_CARVEOUT_GPU) { + /* + * In case of Gpu carveout, the handle size needs to be aligned to 2MB. + */ + old_size = handle->size; + handle->size = ALIGN_2MB(handle->size); + err = nvmap_alloc_handle_from_va(client, handle, op.va, op.flags, op.heap_mask); + goto alloc_op_done; + } + if (!is_nvmap_memory_available(handle->size, op.heap_mask, op.numa_nid)) { nvmap_handle_put(handle); return -ENOMEM; @@ -234,6 +245,7 @@ int nvmap_ioctl_alloc(struct file *filp, void __user *arg) op.flags & (~NVMAP_HANDLE_KIND_SPECIFIED), NVMAP_IVM_INVALID_PEER); +alloc_op_done: if (!err && !is_nvmap_id_ro(client, op.handle, &is_ro)) { mutex_lock(&handle->lock); dmabuf = is_ro ? handle->dmabuf_ro : handle->dmabuf; @@ -249,6 +261,8 @@ int nvmap_ioctl_alloc(struct file *filp, void __user *arg) is_ro ? "RO" : "RW"); } + if ((op.heap_mask & NVMAP_HEAP_CARVEOUT_GPU) && err) + handle->size = old_size; nvmap_handle_put(handle); return err; } @@ -426,7 +440,7 @@ int nvmap_ioctl_create_from_va(struct file *filp, void __user *arg) handle = ref->handle; err = nvmap_alloc_handle_from_va(client, handle, - op.va, op.flags); + op.va, op.flags, 0); if (err) { nvmap_free_handle(client, handle, is_ro); return err; @@ -978,7 +992,20 @@ int nvmap_ioctl_get_handle_parameters(struct file *filp, void __user *arg) if (!handle->alloc) { op.heap = 0; } else { - op.heap = handle->heap_type; + /* + * When users specify GPU heap to allocate from, it means the + * allocation is done from hugetlbfs. But the heap_type stored + * in handle struct would still be IOVMM heap, as the pages are + * from system memory and not from any carveout. Also, a lot + * of nvmap APIs treat carveout and system memory in different ways + * hence it's necessary to store IOVMM heap in heap_type, but while + * querying the handle params, return GPU heap for such handles to + * be consistent. + */ + if (handle->has_hugetlbfs_pages) + op.heap = NVMAP_HEAP_CARVEOUT_GPU; + else + op.heap = handle->heap_type; } /* heap_number, only valid for IVM carveout */ @@ -991,8 +1018,10 @@ int nvmap_ioctl_get_handle_parameters(struct file *filp, void __user *arg) * If heap type is IOVMM, check if it has flag set for contiguous memory * allocation request. Otherwise, if handle belongs to any carveout * then all allocations are contiguous, hence set contig flag to true. + * When the handle is allocated from hugetlbfs, then return contig as false, + * since the entire buffer may not be contiguous. */ - if (handle->alloc && + if (handle->alloc && !handle->has_hugetlbfs_pages && ((handle->heap_type == NVMAP_HEAP_IOVMM && handle->userflags & NVMAP_HANDLE_PHYS_CONTIG) || handle->heap_type != NVMAP_HEAP_IOVMM)) { @@ -1194,6 +1223,58 @@ static int compute_memory_stat(u64 *total, u64 *free, int numa_id) return -EINVAL; } +/* + * This function calculates HugePages_Total and HugePages_Free by parsing + * /sys/devices/system/node/nodeX/meminfo file + */ +static int compute_hugetlbfs_stat(u64 *total, u64 *free, int numa_id) +{ + struct file *file; + char meminfo_path[64] = {'\0'}; + u8 *buf; + loff_t pos = 0; + char *buffer, *ptr; + unsigned int huge_total, huge_free; + bool total_found = false, free_found = false; + int nid, rc; + + sprintf(meminfo_path, "/sys/devices/system/node/node%d/meminfo", numa_id); + file = filp_open(meminfo_path, O_RDONLY, 0); + if (IS_ERR(file)) { + pr_err("Could not open file:%s\n", meminfo_path); + return -EINVAL; + } + + buf = nvmap_altalloc(MEMINFO_SIZE * sizeof(*buf)); + if (!buf) { + pr_err("Memory allocation failed\n"); + filp_close(file, NULL); + return -ENOMEM; + } + + rc = kernel_read(file, buf, MEMINFO_SIZE - 1, &pos); + buf[rc] = '\n'; + filp_close(file, NULL); + buffer = buf; + ptr = buf; + while ((ptr = strsep(&buffer, "\n")) != NULL) { + if (!ptr[0]) + continue; + else if (sscanf(ptr, "Node %d HugePages_Total: %u\n", &nid, &huge_total) == 2) + total_found = true; + else if (sscanf(ptr, "Node %d HugePages_Free: %u\n", &nid, &huge_free) == 2) + free_found = true; + } + + nvmap_altfree(buf, MEMINFO_SIZE * sizeof(*buf)); + if (nid == numa_id && total_found && free_found) { + *total = (u64)huge_total * SIZE_2MB; + *free = (u64)huge_free * SIZE_2MB; + return 0; + } + return -EINVAL; +} + /* * This function calculates allocatable free memory using following formula: * free_mem = avail mem - cma free @@ -1271,7 +1352,22 @@ static int nvmap_query_heap_params(void __user *arg, bool is_numa_aware) /* To Do: select largest free block */ op.largest_free_block = PAGE_SIZE; - if (type & NVMAP_HEAP_CARVEOUT_MASK) { + /* + * Special case: GPU heap + * When user is querying the GPU heap, that means the buffer was allocated from + * hugetlbfs, so we need to return the HugePages_Total, HugePages_Free values + */ + if (type & NVMAP_HEAP_CARVEOUT_GPU) { + if (!is_numa_aware) + numa_id = 0; + + ret = compute_hugetlbfs_stat(&op.total, &op.free, numa_id); + if (ret) + goto exit; + + op.largest_free_block = SIZE_2MB; + op.granule_size = SIZE_2MB; + } else if (type & NVMAP_HEAP_CARVEOUT_MASK) { for (i = 0; i < nvmap_dev->nr_carveouts; i++) { if ((type & nvmap_dev->heaps[i].heap_bit) && (is_numa_aware ? diff --git a/drivers/video/tegra/nvmap/nvmap_priv.h b/drivers/video/tegra/nvmap/nvmap_priv.h index 0b84bc5d..fd5f1784 100644 --- a/drivers/video/tegra/nvmap/nvmap_priv.h +++ b/drivers/video/tegra/nvmap/nvmap_priv.h @@ -43,6 +43,9 @@ #include +#define SIZE_2MB 0x200000 +#define ALIGN_2MB(size) ((size + SIZE_2MB - 1) & ~(SIZE_2MB - 1)) + #define DMA_ERROR_CODE (~(dma_addr_t)0) #define __DMA_ATTR(attrs) attrs @@ -251,6 +254,7 @@ struct nvmap_handle { wait_queue_head_t waitq; int numa_id; u64 serial_id; + bool has_hugetlbfs_pages; }; struct nvmap_handle_info { @@ -513,7 +517,8 @@ int nvmap_alloc_handle(struct nvmap_client *client, int nvmap_alloc_handle_from_va(struct nvmap_client *client, struct nvmap_handle *h, ulong addr, - unsigned int flags); + unsigned int flags, + unsigned int heap_mask); void nvmap_free_handle(struct nvmap_client *c, struct nvmap_handle *h, bool is_ro); diff --git a/include/uapi/linux/nvmap.h b/include/uapi/linux/nvmap.h index a2d388bd..19ee6416 100644 --- a/include/uapi/linux/nvmap.h +++ b/include/uapi/linux/nvmap.h @@ -94,6 +94,7 @@ struct nvmap_alloc_handle { __u32 flags; /* wb/wc/uc/iwb etc. */ __u32 align; /* min alignment necessary */ __s32 numa_nid; /* NUMA node id */ + __u64 va; /* virtual address to get huge pages from */ }; struct nvmap_alloc_ivm_handle {