mirror of
git://nv-tegra.nvidia.com/linux-nvgpu.git
synced 2025-12-22 17:36:20 +03:00
Move vm_priv.h to <nvgpu/linux/vm.h> and rename nvgpu_vm_map() to nvgpu_vm_map_linux(). Also remove a redundant unmap function from the unmap path. These changes are the beginning of reworking the nvgpu Linux mapping and unmapping code. The rest of this patch is just the necessary changes to use the new map function naming and the new path to the Linux vm header. Patch Series Goal ----------------- There's two major goals for this patch series. Note that these goals are not achieved in this patch. There will be subsequent patches. 1. Remove all last vestiges of Linux code from common/mm/vm.c 2. Implement map caching in the common/mm/vm.c code To accomplish this firstly the VM mapping code needs to have the struct nvgpu_mapped_buf data struct be completely Linux free. That means implementing an abstraction for this to hold the Linux stuff that mapped buffers carry about (SGT, dma_buf). This is why the vm_priv.h code has been moved: it will need to be included by the <nvgpu/vm.h> header so that the OS specific struct can be pulled into struct nvgpu_mapped_buf. Next renaming the nvgpu_vm_map() to nvgpu_vm_map_linux() is in preparation for adding a new nvgpu_vm_map() that handles the map caching with nvgpu_mapped_buf. The mapping code is fairly straight forward: nvgpu_vm_map does OS generic stuff; each OS then calls this function from an nvgpu_vm_map_<OS>() or the like that does any OS specific adjustments/management. Freeing buffers is much more tricky however. The maps are all reference counted since userspace does not track buffers and expects us to handle this instead. Ugh! Since there's ref-counts the free code will require a callback into the OS specific code since the OS specific code cannot free a buffer directly. THis make's the path for freeing a buffer quite convoluted. JIRA NVGPU-30 JIRA NVGPU-71 Change-Id: I5e0975f60663a0d6cf0a6bd90e099f51e02c2395 Signed-off-by: Alex Waterman <alexw@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/1578896 GVS: Gerrit_Virtual_Submit Reviewed-by: David Martinez Nieto <dmartineznie@nvidia.com> Reviewed-by: Terje Bergstrom <tbergstrom@nvidia.com>
254 lines
5.9 KiB
C
254 lines
5.9 KiB
C
/*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <linux/dma-buf.h>
|
|
|
|
#include <linux/platform/tegra/tegra_fd.h>
|
|
|
|
#include <nvgpu/dma.h>
|
|
#include <nvgpu/enabled.h>
|
|
#include <nvgpu/vidmem.h>
|
|
#include <nvgpu/nvgpu_mem.h>
|
|
#include <nvgpu/page_allocator.h>
|
|
|
|
#include <nvgpu/linux/vm.h>
|
|
#include <nvgpu/linux/dma.h>
|
|
#include <nvgpu/linux/vidmem.h>
|
|
|
|
#include "gk20a/gk20a.h"
|
|
#include "gk20a/mm_gk20a.h"
|
|
|
|
bool nvgpu_addr_is_vidmem_page_alloc(u64 addr)
|
|
{
|
|
return !!(addr & 1ULL);
|
|
}
|
|
|
|
void nvgpu_vidmem_set_page_alloc(struct scatterlist *sgl, u64 addr)
|
|
{
|
|
/* set bit 0 to indicate vidmem allocation */
|
|
sg_dma_address(sgl) = (addr | 1ULL);
|
|
}
|
|
|
|
struct nvgpu_page_alloc *nvgpu_vidmem_get_page_alloc(struct scatterlist *sgl)
|
|
{
|
|
u64 addr;
|
|
|
|
addr = sg_dma_address(sgl);
|
|
|
|
if (nvgpu_addr_is_vidmem_page_alloc(addr))
|
|
addr = addr & ~1ULL;
|
|
else
|
|
WARN_ON(1);
|
|
|
|
return (struct nvgpu_page_alloc *)(uintptr_t)addr;
|
|
}
|
|
|
|
static struct sg_table *gk20a_vidbuf_map_dma_buf(
|
|
struct dma_buf_attachment *attach, enum dma_data_direction dir)
|
|
{
|
|
struct nvgpu_vidmem_buf *buf = attach->dmabuf->priv;
|
|
|
|
return buf->mem->priv.sgt;
|
|
}
|
|
|
|
static void gk20a_vidbuf_unmap_dma_buf(struct dma_buf_attachment *attach,
|
|
struct sg_table *sgt,
|
|
enum dma_data_direction dir)
|
|
{
|
|
}
|
|
|
|
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;
|
|
|
|
vidmem_dbg(g, "Releasing Linux VIDMEM buf: dmabuf=0x%p size=%zuKB",
|
|
dmabuf, buf->mem->size >> 10);
|
|
|
|
if (linux_buf && linux_buf->dmabuf_priv_delete)
|
|
linux_buf->dmabuf_priv_delete(linux_buf->dmabuf_priv);
|
|
|
|
nvgpu_kfree(g, linux_buf);
|
|
nvgpu_vidmem_buf_free(g, buf);
|
|
|
|
gk20a_put(g);
|
|
}
|
|
|
|
static void *gk20a_vidbuf_kmap(struct dma_buf *dmabuf, unsigned long page_num)
|
|
{
|
|
WARN_ON("Not supported");
|
|
return NULL;
|
|
}
|
|
|
|
static void *gk20a_vidbuf_kmap_atomic(struct dma_buf *dmabuf,
|
|
unsigned long page_num)
|
|
{
|
|
WARN_ON("Not supported");
|
|
return NULL;
|
|
}
|
|
|
|
static int gk20a_vidbuf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
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;
|
|
|
|
linux_buf->dmabuf_priv = priv;
|
|
linux_buf->dmabuf_priv_delete = delete;
|
|
|
|
return 0;
|
|
}
|
|
|
|
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 linux_buf->dmabuf_priv;
|
|
}
|
|
|
|
static const struct dma_buf_ops gk20a_vidbuf_ops = {
|
|
.map_dma_buf = gk20a_vidbuf_map_dma_buf,
|
|
.unmap_dma_buf = gk20a_vidbuf_unmap_dma_buf,
|
|
.release = gk20a_vidbuf_release,
|
|
.kmap_atomic = gk20a_vidbuf_kmap_atomic,
|
|
.kmap = gk20a_vidbuf_kmap,
|
|
.mmap = gk20a_vidbuf_mmap,
|
|
.set_drvdata = gk20a_vidbuf_set_private,
|
|
.get_drvdata = gk20a_vidbuf_get_private,
|
|
};
|
|
|
|
static struct dma_buf *gk20a_vidbuf_export(struct nvgpu_vidmem_buf *buf)
|
|
{
|
|
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
|
|
|
|
exp_info.priv = buf;
|
|
exp_info.ops = &gk20a_vidbuf_ops;
|
|
exp_info.size = buf->mem->size;
|
|
exp_info.flags = O_RDWR;
|
|
|
|
return dma_buf_export(&exp_info);
|
|
}
|
|
|
|
struct gk20a *nvgpu_vidmem_buf_owner(struct dma_buf *dmabuf)
|
|
{
|
|
struct nvgpu_vidmem_buf *buf = dmabuf->priv;
|
|
|
|
if (dmabuf->ops != &gk20a_vidbuf_ops)
|
|
return NULL;
|
|
|
|
return buf->g;
|
|
}
|
|
|
|
int nvgpu_vidmem_export_linux(struct gk20a *g, size_t bytes)
|
|
{
|
|
struct nvgpu_vidmem_buf *buf = NULL;
|
|
struct nvgpu_vidmem_linux *priv;
|
|
int err, fd;
|
|
|
|
/*
|
|
* This ref is released when the dma_buf is closed.
|
|
*/
|
|
if (!gk20a_get(g))
|
|
return -ENODEV;
|
|
|
|
priv = nvgpu_kzalloc(g, sizeof(*priv));
|
|
if (!priv) {
|
|
err = -ENOMEM;
|
|
goto fail;
|
|
}
|
|
|
|
buf = nvgpu_vidmem_user_alloc(g, bytes);
|
|
if (!buf) {
|
|
err = -ENOMEM;
|
|
goto fail;
|
|
}
|
|
|
|
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(priv->dmabuf);
|
|
return fd;
|
|
}
|
|
|
|
/* fclose() on this drops one ref, freeing the dma buf */
|
|
fd_install(fd, priv->dmabuf->file);
|
|
|
|
vidmem_dbg(g, "Alloced Linux VIDMEM buf: dmabuf=0x%p size=%zuKB",
|
|
priv->dmabuf, buf->mem->size >> 10);
|
|
|
|
return fd;
|
|
|
|
fail:
|
|
nvgpu_vidmem_buf_free(g, buf);
|
|
nvgpu_kfree(g, priv);
|
|
gk20a_put(g);
|
|
|
|
vidmem_dbg(g, "Failed to alloc Linux VIDMEM buf: %d", err);
|
|
return err;
|
|
}
|
|
|
|
int nvgpu_vidmem_buf_access_memory(struct gk20a *g, struct dma_buf *dmabuf,
|
|
void *buffer, u64 offset, u64 size, u32 cmd)
|
|
{
|
|
struct nvgpu_vidmem_buf *vidmem_buf;
|
|
struct nvgpu_mem *mem;
|
|
int err = 0;
|
|
|
|
if (gk20a_dmabuf_aperture(g, dmabuf) != APERTURE_VIDMEM)
|
|
return -EINVAL;
|
|
|
|
vidmem_buf = dmabuf->priv;
|
|
mem = vidmem_buf->mem;
|
|
|
|
switch (cmd) {
|
|
case NVGPU_DBG_GPU_IOCTL_ACCESS_FB_MEMORY_CMD_READ:
|
|
nvgpu_mem_rd_n(g, mem, offset, buffer, size);
|
|
break;
|
|
|
|
case NVGPU_DBG_GPU_IOCTL_ACCESS_FB_MEMORY_CMD_WRITE:
|
|
nvgpu_mem_wr_n(g, mem, offset, buffer, size);
|
|
break;
|
|
|
|
default:
|
|
err = -EINVAL;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
void __nvgpu_mem_free_vidmem_alloc(struct gk20a *g, struct nvgpu_mem *vidmem)
|
|
{
|
|
nvgpu_free(vidmem->allocator,
|
|
(u64)nvgpu_vidmem_get_page_alloc(vidmem->priv.sgt->sgl));
|
|
nvgpu_free_sgtable(g, &vidmem->priv.sgt);
|
|
}
|