From 59e4089278bd052b440293356605ce524e4944db Mon Sep 17 00:00:00 2001 From: Alex Waterman Date: Thu, 17 Aug 2017 16:16:49 -0700 Subject: gpu: nvgpu: Separate vidmem alloc from Linux code Split the core vidmem allocation from the Linux component of vidmem allocation. The core vidmem allocation allocates the nvgpu_mem struct that defines the vidmem buffer in the core MM code. The Linux code now allocates some Linux specific stuff (dma_buf, etc) and also allocates the core vidmem buf. JIRA NVGPU-30 JIRA NVGPU-138 Change-Id: I88e87e0abd5ec714610eacc6eac17e148bcee3ce Signed-off-by: Alex Waterman Reviewed-on: https://git-master.nvidia.com/r/1540708 Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/gpu/nvgpu/common/linux/ioctl_ctrl.c | 8 ++- drivers/gpu/nvgpu/common/linux/vidmem.c | 80 +++++++++++--------------- drivers/gpu/nvgpu/common/linux/vm.c | 1 + drivers/gpu/nvgpu/common/mm/vidmem.c | 80 +++++++++++++++++++++++++- drivers/gpu/nvgpu/gk20a/dbg_gpu_gk20a.c | 3 +- drivers/gpu/nvgpu/include/nvgpu/linux/vidmem.h | 60 +++++++++++++++++++ drivers/gpu/nvgpu/include/nvgpu/vidmem.h | 70 +++++++++++++--------- 7 files changed, 221 insertions(+), 81 deletions(-) create mode 100644 drivers/gpu/nvgpu/include/nvgpu/linux/vidmem.h diff --git a/drivers/gpu/nvgpu/common/linux/ioctl_ctrl.c b/drivers/gpu/nvgpu/common/linux/ioctl_ctrl.c index 7180256a..30988462 100644 --- a/drivers/gpu/nvgpu/common/linux/ioctl_ctrl.c +++ b/drivers/gpu/nvgpu/common/linux/ioctl_ctrl.c @@ -26,6 +26,9 @@ #include #include #include +#include + +#include #include "ioctl_ctrl.h" #include "ioctl_tsg.h" @@ -33,9 +36,8 @@ #include "gk20a/gk20a.h" #include "gk20a/platform_gk20a.h" #include "gk20a/fence_gk20a.h" -#include "os_linux.h" -#include +#include "os_linux.h" #define HZ_TO_MHZ(a) ((a > 0xF414F9CD7ULL) ? 0xffff : (a >> 32) ? \ (u32) ((a * 0x10C8ULL) >> 32) : (u16) ((u32) a/MHZ)) @@ -693,7 +695,7 @@ static int nvgpu_gpu_alloc_vidmem(struct gk20a *g, return -EINVAL; } - fd = nvgpu_vidmem_buf_alloc(g, args->in.size); + fd = nvgpu_vidmem_export_linux(g, args->in.size); if (fd < 0) return fd; diff --git a/drivers/gpu/nvgpu/common/linux/vidmem.c b/drivers/gpu/nvgpu/common/linux/vidmem.c index 5d47c858..ea8e552f 100644 --- a/drivers/gpu/nvgpu/common/linux/vidmem.c +++ b/drivers/gpu/nvgpu/common/linux/vidmem.c @@ -25,6 +25,7 @@ #include #include +#include #include "gk20a/gk20a.h" #include "gk20a/mm_gk20a.h" @@ -73,14 +74,16 @@ static void gk20a_vidbuf_unmap_dma_buf(struct dma_buf_attachment *attach, static void gk20a_vidbuf_release(struct dma_buf *dmabuf) { struct nvgpu_vidmem_buf *buf = dmabuf->priv; + struct nvgpu_vidmem_linux *linux_buf = buf->priv; + struct gk20a *g = buf->g; gk20a_dbg_fn(""); - if (buf->dmabuf_priv) - buf->dmabuf_priv_delete(buf->dmabuf_priv); + if (linux_buf && linux_buf->dmabuf_priv_delete) + linux_buf->dmabuf_priv_delete(linux_buf->dmabuf_priv); - nvgpu_dma_free(buf->g, buf->mem); - nvgpu_kfree(buf->g, buf); + nvgpu_kfree(g, linux_buf); + nvgpu_vidmem_buf_free(g, buf); } static void *gk20a_vidbuf_kmap(struct dma_buf *dmabuf, unsigned long page_num) @@ -105,9 +108,10 @@ static int gk20a_vidbuf_set_private(struct dma_buf *dmabuf, struct device *dev, void *priv, void (*delete)(void *priv)) { struct nvgpu_vidmem_buf *buf = dmabuf->priv; + struct nvgpu_vidmem_linux *linux_buf = buf->priv; - buf->dmabuf_priv = priv; - buf->dmabuf_priv_delete = delete; + linux_buf->dmabuf_priv = priv; + linux_buf->dmabuf_priv_delete = delete; return 0; } @@ -116,8 +120,9 @@ static void *gk20a_vidbuf_get_private(struct dma_buf *dmabuf, struct device *dev) { struct nvgpu_vidmem_buf *buf = dmabuf->priv; + struct nvgpu_vidmem_linux *linux_buf = buf->priv; - return buf->dmabuf_priv; + return linux_buf->dmabuf_priv; } static const struct dma_buf_ops gk20a_vidbuf_ops = { @@ -153,66 +158,45 @@ struct gk20a *nvgpu_vidmem_buf_owner(struct dma_buf *dmabuf) return buf->g; } -int nvgpu_vidmem_buf_alloc(struct gk20a *g, size_t bytes) +int nvgpu_vidmem_export_linux(struct gk20a *g, size_t bytes) { struct nvgpu_vidmem_buf *buf; - int err = 0, fd; + struct nvgpu_vidmem_linux *priv; + int err, fd; - gk20a_dbg_fn(""); - - buf = nvgpu_kzalloc(g, sizeof(*buf)); - if (!buf) + priv = nvgpu_kzalloc(g, sizeof(*priv)); + if (!priv) return -ENOMEM; - buf->g = g; - - if (!g->mm.vidmem.cleared) { - nvgpu_mutex_acquire(&g->mm.vidmem.first_clear_mutex); - if (!g->mm.vidmem.cleared) { - err = nvgpu_vidmem_clear_all(g); - if (err) { - nvgpu_err(g, - "failed to clear whole vidmem"); - goto err_kfree; - } - } - nvgpu_mutex_release(&g->mm.vidmem.first_clear_mutex); + buf = nvgpu_vidmem_user_alloc(g, bytes); + if (!buf) { + err = -ENOMEM; + goto fail; } - buf->mem = nvgpu_kzalloc(g, sizeof(struct nvgpu_mem)); - if (!buf->mem) - goto err_kfree; - - buf->mem->mem_flags |= NVGPU_MEM_FLAG_USER_MEM; - - err = nvgpu_dma_alloc_vid(g, bytes, buf->mem); - if (err) - goto err_memfree; - - buf->dmabuf = gk20a_vidbuf_export(buf); - if (IS_ERR(buf->dmabuf)) { - err = PTR_ERR(buf->dmabuf); - goto err_bfree; + priv->dmabuf = gk20a_vidbuf_export(buf); + if (IS_ERR(priv->dmabuf)) { + err = PTR_ERR(priv->dmabuf); + goto fail; } + buf->priv = priv; + fd = tegra_alloc_fd(current->files, 1024, O_RDWR); if (fd < 0) { /* ->release frees what we have done */ - dma_buf_put(buf->dmabuf); + dma_buf_put(priv->dmabuf); return fd; } /* fclose() on this drops one ref, freeing the dma buf */ - fd_install(fd, buf->dmabuf->file); + fd_install(fd, priv->dmabuf->file); return fd; -err_bfree: - nvgpu_dma_free(g, buf->mem); -err_memfree: - nvgpu_kfree(g, buf->mem); -err_kfree: - nvgpu_kfree(g, buf); +fail: + nvgpu_kfree(g, priv); + nvgpu_vidmem_buf_free(g, buf); return err; } diff --git a/drivers/gpu/nvgpu/common/linux/vm.c b/drivers/gpu/nvgpu/common/linux/vm.c index d0f87148..45058321 100644 --- a/drivers/gpu/nvgpu/common/linux/vm.c +++ b/drivers/gpu/nvgpu/common/linux/vm.c @@ -26,6 +26,7 @@ #include #include +#include #include #include "gk20a/gk20a.h" diff --git a/drivers/gpu/nvgpu/common/mm/vidmem.c b/drivers/gpu/nvgpu/common/mm/vidmem.c index c95cedec..18f46c03 100644 --- a/drivers/gpu/nvgpu/common/mm/vidmem.c +++ b/drivers/gpu/nvgpu/common/mm/vidmem.c @@ -22,6 +22,7 @@ #include +#include #include #include @@ -34,7 +35,7 @@ void nvgpu_vidmem_destroy(struct gk20a *g) nvgpu_alloc_destroy(&g->mm.vidmem.allocator); } -int nvgpu_vidmem_clear_all(struct gk20a *g) +static int __nvgpu_vidmem_do_clear_all(struct gk20a *g) { struct mm_gk20a *mm = &g->mm; struct gk20a_fence *gk20a_fence_out = NULL; @@ -257,3 +258,80 @@ struct nvgpu_mem *nvgpu_vidmem_get_pending_alloc(struct mm_gk20a *mm) return mem; } + +static int nvgpu_vidmem_clear_all(struct gk20a *g) +{ + int err; + + if (g->mm.vidmem.cleared) + return 0; + + nvgpu_mutex_acquire(&g->mm.vidmem.first_clear_mutex); + if (!g->mm.vidmem.cleared) { + err = __nvgpu_vidmem_do_clear_all(g); + if (err) { + nvgpu_mutex_release(&g->mm.vidmem.first_clear_mutex); + nvgpu_err(g, "failed to clear whole vidmem"); + return err; + } + } + nvgpu_mutex_release(&g->mm.vidmem.first_clear_mutex); + + return 0; +} + +struct nvgpu_vidmem_buf *nvgpu_vidmem_user_alloc(struct gk20a *g, size_t bytes) +{ + struct nvgpu_vidmem_buf *buf; + int err; + + err = nvgpu_vidmem_clear_all(g); + if (err) + return NULL; + + buf = nvgpu_kzalloc(g, sizeof(*buf)); + if (!buf) + return NULL; + + buf->g = g; + buf->mem = nvgpu_kzalloc(g, sizeof(*buf->mem)); + if (!buf->mem) + goto fail; + + err = nvgpu_dma_alloc_vid(g, bytes, buf->mem); + if (err) + goto fail; + + /* + * Alerts the DMA API that when we free this vidmem buf we have to + * clear it to avoid leaking data to userspace. + */ + buf->mem->mem_flags |= NVGPU_MEM_FLAG_USER_MEM; + + return buf; + +fail: + /* buf will never be NULL here. */ + nvgpu_kfree(g, buf->mem); + nvgpu_kfree(g, buf); + return NULL; +} + +void nvgpu_vidmem_buf_free(struct gk20a *g, struct nvgpu_vidmem_buf *buf) +{ + /* + * In some error paths it's convenient to be able to "free" a NULL buf. + */ + if (!buf) + return; + + nvgpu_dma_free(g, buf->mem); + + /* + * We don't free buf->mem here. This is handled by nvgpu_dma_free()! + * Since these buffers are cleared in the background the nvgpu_mem + * struct must live on through that. We transfer ownership here to the + * DMA API and let the DMA API free the buffer. + */ + nvgpu_kfree(g, buf); +} diff --git a/drivers/gpu/nvgpu/gk20a/dbg_gpu_gk20a.c b/drivers/gpu/nvgpu/gk20a/dbg_gpu_gk20a.c index 83bd0156..c2885807 100644 --- a/drivers/gpu/nvgpu/gk20a/dbg_gpu_gk20a.c +++ b/drivers/gpu/nvgpu/gk20a/dbg_gpu_gk20a.c @@ -33,7 +33,8 @@ #include #include #include -#include + +#include #include "gk20a.h" #include "gk20a/platform_gk20a.h" diff --git a/drivers/gpu/nvgpu/include/nvgpu/linux/vidmem.h b/drivers/gpu/nvgpu/include/nvgpu/linux/vidmem.h new file mode 100644 index 00000000..76bbb05b --- /dev/null +++ b/drivers/gpu/nvgpu/include/nvgpu/linux/vidmem.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __NVGPU_LINUX_VIDMEM_H__ +#define __NVGPU_LINUX_VIDMEM_H__ + +#include + +struct dma_buf; + +struct gk20a; + +#ifdef CONFIG_GK20A_VIDMEM +struct gk20a *nvgpu_vidmem_buf_owner(struct dma_buf *dmabuf); +int nvgpu_vidmem_export_linux(struct gk20a *g, size_t bytes); + +int nvgpu_vidmem_buf_access_memory(struct gk20a *g, struct dma_buf *dmabuf, + void *buffer, u64 offset, u64 size, u32 cmd); + +#else /* !CONFIG_GK20A_VIDMEM */ +static inline struct gk20a *nvgpu_vidmem_buf_owner(struct dma_buf *dmabuf) +{ + return NULL; +} + +static inline int nvgpu_vidmem_export_linux(struct gk20a *g, size_t bytes) +{ + return -ENOSYS; +} + +static inline int nvgpu_vidmem_buf_access_memory(struct gk20a *g, + struct dma_buf *dmabuf, + void *buffer, u64 offset, + u64 size, u32 cmd) +{ + return -ENOSYS; +} + +#endif + +struct nvgpu_vidmem_linux { + struct dma_buf *dmabuf; + void *dmabuf_priv; + void (*dmabuf_priv_delete)(void *); +}; + +#endif diff --git a/drivers/gpu/nvgpu/include/nvgpu/vidmem.h b/drivers/gpu/nvgpu/include/nvgpu/vidmem.h index 1b250f90..b89c710d 100644 --- a/drivers/gpu/nvgpu/include/nvgpu/vidmem.h +++ b/drivers/gpu/nvgpu/include/nvgpu/vidmem.h @@ -25,9 +25,9 @@ #include #include +#include struct scatterlist; -struct dma_buf; struct work_struct; struct gk20a; @@ -35,37 +35,58 @@ struct mm_gk20a; struct nvgpu_mem; struct nvgpu_vidmem_buf { - struct gk20a *g; - struct nvgpu_mem *mem; - struct dma_buf *dmabuf; - void *dmabuf_priv; - void (*dmabuf_priv_delete)(void *); + /* + * Must be a pointer since control of this mem is passed over to the + * vidmem background clearing thread when the vidmem buf is freed. + */ + struct nvgpu_mem *mem; + + struct gk20a *g; + + /* + * Filled in by each OS - this holds the necessary data to export this + * buffer to userspace. This will eventually be replaced by a struct + * which shall be defined in the OS specific vidmem.h header file. + */ + void *priv; }; #if defined(CONFIG_GK20A_VIDMEM) +/** + * nvgpu_vidmem_user_alloc - Allocates a vidmem buffer for userspace + * + * @g - The GPU. + * @bytes - Size of the buffer in bytes. + * + * Allocate a generic (OS agnostic) vidmem buffer. This does not allocate the OS + * specific interfacing for userspace sharing. Instead is is expected that the + * OS specific code will allocate that OS specific data and add it to this + * buffer. + * + * The buffer allocated here is intended to use used by userspace, hence the + * extra struct over nvgpu_mem. If a vidmem buffer is needed by the kernel + * driver only then a simple nvgpu_dma_alloc_vid() or the like is sufficient. + * + * Returns a pointer to a vidmem buffer on success, 0 otherwise. + */ +struct nvgpu_vidmem_buf *nvgpu_vidmem_user_alloc(struct gk20a *g, size_t bytes); + +void nvgpu_vidmem_buf_free(struct gk20a *g, struct nvgpu_vidmem_buf *buf); + struct nvgpu_page_alloc *nvgpu_vidmem_get_page_alloc(struct scatterlist *sgl); void nvgpu_vidmem_set_page_alloc(struct scatterlist *sgl, u64 addr); bool nvgpu_addr_is_vidmem_page_alloc(u64 addr); -int nvgpu_vidmem_buf_alloc(struct gk20a *g, size_t bytes); int nvgpu_vidmem_get_space(struct gk20a *g, u64 *space); struct nvgpu_mem *nvgpu_vidmem_get_pending_alloc(struct mm_gk20a *mm); void nvgpu_vidmem_destroy(struct gk20a *g); int nvgpu_vidmem_init(struct mm_gk20a *mm); -int nvgpu_vidmem_clear_all(struct gk20a *g); void nvgpu_vidmem_clear_mem_worker(struct work_struct *work); int nvgpu_vidmem_clear(struct gk20a *g, struct nvgpu_mem *mem); -/* - * Will need to be moved later on once we have the Linux vidmem.h file. - */ -struct gk20a *nvgpu_vidmem_buf_owner(struct dma_buf *dmabuf); -int nvgpu_vidmem_buf_access_memory(struct gk20a *g, struct dma_buf *dmabuf, - void *buffer, u64 offset, u64 size, u32 cmd); - #else /* !defined(CONFIG_GK20A_VIDMEM) */ /* @@ -91,6 +112,12 @@ static inline int nvgpu_vidmem_buf_alloc(struct gk20a *g, size_t bytes) { return -ENOSYS; } + +static inline void nvgpu_vidmem_buf_free(struct gk20a *g, + struct nvgpu_vidmem_buf *buf) +{ +} + static inline int nvgpu_vidmem_get_space(struct gk20a *g, u64 *space) { return -ENOSYS; @@ -121,19 +148,6 @@ static inline int nvgpu_vidmem_clear(struct gk20a *g, return -ENOSYS; } -static inline struct gk20a *nvgpu_vidmem_buf_owner(struct dma_buf *dmabuf) -{ - return NULL; -} - -static inline int nvgpu_vidmem_buf_access_memory(struct gk20a *g, - struct dma_buf *dmabuf, - void *buffer, u64 offset, - u64 size, u32 cmd) -{ - return -ENOSYS; -} - #endif /* !defined(CONFIG_GK20A_VIDMEM) */ #endif /* __NVGPU_VIDMEM_H__ */ -- cgit v1.2.2