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 <ketanp@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3174720
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
This commit is contained in:
Ketan Patil
2024-07-14 07:11:51 +00:00
committed by mobile promotions
parent 9c92d38302
commit 8c52af9b59
4 changed files with 114 additions and 8 deletions

View File

@@ -247,7 +247,8 @@ static void alloc_handle(struct nvmap_client *client,
static int alloc_handle_from_va(struct nvmap_client *client, static int alloc_handle_from_va(struct nvmap_client *client,
struct nvmap_handle *h, struct nvmap_handle *h,
ulong vaddr, ulong vaddr,
u32 flags) u32 flags,
unsigned int heap_mask)
{ {
size_t nr_page = h->size >> PAGE_SHIFT; size_t nr_page = h->size >> PAGE_SHIFT;
struct page **pages; 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_type = NVMAP_HEAP_IOVMM;
h->heap_pgalloc = true; h->heap_pgalloc = true;
h->from_va = true; h->from_va = true;
if (heap_mask & NVMAP_HEAP_CARVEOUT_GPU)
h->has_hugetlbfs_pages = true;
mb(); mb();
h->alloc = true; h->alloc = true;
return ret; return ret;
@@ -424,7 +427,8 @@ out:
int nvmap_alloc_handle_from_va(struct nvmap_client *client, int nvmap_alloc_handle_from_va(struct nvmap_client *client,
struct nvmap_handle *h, struct nvmap_handle *h,
ulong addr, ulong addr,
unsigned int flags) unsigned int flags,
unsigned int heap_mask)
{ {
int err = -ENOMEM; int err = -ENOMEM;
int tag; int tag;
@@ -455,7 +459,7 @@ int nvmap_alloc_handle_from_va(struct nvmap_client *client,
client->task->pid, task_comm); 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) { if (err) {
pr_err("alloc_handle_from_va failed %d", err); pr_err("alloc_handle_from_va failed %d", err);
nvmap_handle_put(h); nvmap_handle_put(h);

View File

@@ -200,6 +200,7 @@ int nvmap_ioctl_alloc(struct file *filp, void __user *arg)
bool is_ro = false; bool is_ro = false;
int err; int err;
long dmabuf_ref = 0; long dmabuf_ref = 0;
size_t old_size;
if (copy_from_user(&op, arg, sizeof(op))) if (copy_from_user(&op, arg, sizeof(op)))
return -EFAULT; return -EFAULT;
@@ -219,6 +220,16 @@ int nvmap_ioctl_alloc(struct file *filp, void __user *arg)
if (IS_ERR_OR_NULL(handle)) if (IS_ERR_OR_NULL(handle))
return -EINVAL; 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)) { if (!is_nvmap_memory_available(handle->size, op.heap_mask, op.numa_nid)) {
nvmap_handle_put(handle); nvmap_handle_put(handle);
return -ENOMEM; return -ENOMEM;
@@ -234,6 +245,7 @@ int nvmap_ioctl_alloc(struct file *filp, void __user *arg)
op.flags & (~NVMAP_HANDLE_KIND_SPECIFIED), op.flags & (~NVMAP_HANDLE_KIND_SPECIFIED),
NVMAP_IVM_INVALID_PEER); NVMAP_IVM_INVALID_PEER);
alloc_op_done:
if (!err && !is_nvmap_id_ro(client, op.handle, &is_ro)) { if (!err && !is_nvmap_id_ro(client, op.handle, &is_ro)) {
mutex_lock(&handle->lock); mutex_lock(&handle->lock);
dmabuf = is_ro ? handle->dmabuf_ro : handle->dmabuf; 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"); is_ro ? "RO" : "RW");
} }
if ((op.heap_mask & NVMAP_HEAP_CARVEOUT_GPU) && err)
handle->size = old_size;
nvmap_handle_put(handle); nvmap_handle_put(handle);
return err; return err;
} }
@@ -426,7 +440,7 @@ int nvmap_ioctl_create_from_va(struct file *filp, void __user *arg)
handle = ref->handle; handle = ref->handle;
err = nvmap_alloc_handle_from_va(client, handle, err = nvmap_alloc_handle_from_va(client, handle,
op.va, op.flags); op.va, op.flags, 0);
if (err) { if (err) {
nvmap_free_handle(client, handle, is_ro); nvmap_free_handle(client, handle, is_ro);
return err; return err;
@@ -978,7 +992,20 @@ int nvmap_ioctl_get_handle_parameters(struct file *filp, void __user *arg)
if (!handle->alloc) { if (!handle->alloc) {
op.heap = 0; op.heap = 0;
} else { } 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 */ /* 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 * If heap type is IOVMM, check if it has flag set for contiguous memory
* allocation request. Otherwise, if handle belongs to any carveout * allocation request. Otherwise, if handle belongs to any carveout
* then all allocations are contiguous, hence set contig flag to true. * 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->heap_type == NVMAP_HEAP_IOVMM &&
handle->userflags & NVMAP_HANDLE_PHYS_CONTIG) || handle->userflags & NVMAP_HANDLE_PHYS_CONTIG) ||
handle->heap_type != NVMAP_HEAP_IOVMM)) { handle->heap_type != NVMAP_HEAP_IOVMM)) {
@@ -1194,6 +1223,58 @@ static int compute_memory_stat(u64 *total, u64 *free, int numa_id)
return -EINVAL; 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: * This function calculates allocatable free memory using following formula:
* free_mem = avail mem - cma free * 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 */ /* To Do: select largest free block */
op.largest_free_block = PAGE_SIZE; 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++) { for (i = 0; i < nvmap_dev->nr_carveouts; i++) {
if ((type & nvmap_dev->heaps[i].heap_bit) && if ((type & nvmap_dev->heaps[i].heap_bit) &&
(is_numa_aware ? (is_numa_aware ?

View File

@@ -43,6 +43,9 @@
#include <linux/fdtable.h> #include <linux/fdtable.h>
#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_ERROR_CODE (~(dma_addr_t)0)
#define __DMA_ATTR(attrs) attrs #define __DMA_ATTR(attrs) attrs
@@ -251,6 +254,7 @@ struct nvmap_handle {
wait_queue_head_t waitq; wait_queue_head_t waitq;
int numa_id; int numa_id;
u64 serial_id; u64 serial_id;
bool has_hugetlbfs_pages;
}; };
struct nvmap_handle_info { 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, int nvmap_alloc_handle_from_va(struct nvmap_client *client,
struct nvmap_handle *h, struct nvmap_handle *h,
ulong addr, 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); void nvmap_free_handle(struct nvmap_client *c, struct nvmap_handle *h, bool is_ro);

View File

@@ -94,6 +94,7 @@ struct nvmap_alloc_handle {
__u32 flags; /* wb/wc/uc/iwb etc. */ __u32 flags; /* wb/wc/uc/iwb etc. */
__u32 align; /* min alignment necessary */ __u32 align; /* min alignment necessary */
__s32 numa_nid; /* NUMA node id */ __s32 numa_nid; /* NUMA node id */
__u64 va; /* virtual address to get huge pages from */
}; };
struct nvmap_alloc_ivm_handle { struct nvmap_alloc_ivm_handle {