Files
Jon Hunter 10b813a25d gpu: nvgpu: Use conftest for VM flags
Using kernel version for detecting kernel changes does not work for some
3rd party Linux distributions that back port kernel changes to their
kernel. The conftest script has a test for detecting if the 'vm_flags'
variable can be set directly or if the appropriate helper functions must
be used. Update the NVGPU driver to use the definition provided by
conftest to determine if the 'vm_flags' variable can set set directly or
not.

Bug 4014315

Change-Id: I6ebfbfa622259e15560152bf70315451a52fba81
Signed-off-by: Jon Hunter <jonathanh@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvgpu/+/3164870
(cherry picked from commit 2c9097363d29a235eb5c41530cdd3896694599d2)
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvgpu/+/3172302
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Sagar Kamble <skamble@nvidia.com>
2024-07-10 07:24:02 -07:00

502 lines
12 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
// SPDX-FileCopyrightText: Copyright (c) 2017-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#if defined(CONFIG_NVIDIA_CONFTEST)
#include <nvidia/conftest.h>
#endif
#include <nvgpu/gk20a.h>
#include <nvgpu/gr/ctx.h>
#include <nvgpu/nvgpu_init.h>
#include <nvgpu/soc.h>
#include "common/gr/ctx_priv.h"
#include "common/gr/gr_priv.h"
#include "debug_gr.h"
#include "os_linux.h"
#include <linux/uaccess.h>
#include <linux/capability.h>
#include <linux/debugfs.h>
#include <linux/version.h>
#ifdef CONFIG_NVGPU_COMPRESSION
static int cbc_status_debug_show(struct seq_file *s, void *unused)
{
struct gk20a *g = s->private;
struct nvgpu_cbc *cbc = g->cbc;
if (!cbc) {
nvgpu_err(g, "cbc is not initialized");
return -EBADFD;
}
seq_printf(s, "cbc.compbit_backing_size: %u\n",
cbc->compbit_backing_size);
seq_printf(s, "cbc.comptags_per_cacheline: %u\n",
cbc->comptags_per_cacheline);
seq_printf(s, "cbc.gobs_per_comptagline_per_slice: %u\n",
cbc->gobs_per_comptagline_per_slice);
seq_printf(s, "cbc.max_comptag_lines: %u\n",
cbc->max_comptag_lines);
seq_printf(s, "cbc.comp_tags.size: %lu\n",
cbc->comp_tags.size);
seq_printf(s, "cbc.compbit_store.base_hw: %llu\n",
cbc->compbit_store.base_hw);
if (nvgpu_mem_is_valid(&cbc->compbit_store.mem)) {
seq_printf(s, "cbc.compbit_store.mem.aperture: %u\n",
cbc->compbit_store.mem.aperture);
seq_printf(s, "cbc.compbit_store.mem.size: %zu\n",
cbc->compbit_store.mem.size);
seq_printf(s, "cbc.compbit_store.mem.aligned_size: %zu\n",
cbc->compbit_store.mem.aligned_size);
seq_printf(s, "cbc.compbit_store.mem.gpu_va: %llu\n",
cbc->compbit_store.mem.gpu_va);
seq_printf(s, "cbc.compbit_store.mem.skip_wmb: %u\n",
cbc->compbit_store.mem.skip_wmb);
seq_printf(s, "cbc.compbit_store.mem.free_gpu_va: %u\n",
cbc->compbit_store.mem.free_gpu_va);
seq_printf(s, "cbc.compbit_store.mem.mem_flags: %lu\n",
cbc->compbit_store.mem.mem_flags);
seq_printf(s, "cbc.compbit_store.mem.cpu_va: %px\n",
cbc->compbit_store.mem.cpu_va);
seq_printf(s, "cbc.compbit_store.mem.pa: %llx\n",
nvgpu_mem_get_addr(g, &cbc->compbit_store.mem));
} else {
seq_printf(s, "cbc.compbit_store.mem: invalid\n");
}
return 0;
}
static int cbc_status_debug_open(struct inode *inode, struct file *file)
{
int err = 0;
struct gk20a *g = (struct gk20a *)inode->i_private;
if (!capable(CAP_SYS_ADMIN)) {
return -EPERM;
}
if (nvgpu_is_hypervisor_mode(g)) {
return -EPERM;
}
g = nvgpu_get(g);
if (!g) {
return -ENODEV;
}
err = gk20a_busy(g);
if (err < 0) {
nvgpu_err(g, "Couldn't power-up gpu");
goto put;
}
err = nvgpu_cbc_init_support(g);
if (err < 0) {
nvgpu_err(g, "cbc init failed");
goto idle;
}
err = single_open(file, cbc_status_debug_show, inode->i_private);
if (err < 0) {
nvgpu_err(g, "single open failed");
goto idle;
}
return err;
idle:
gk20a_idle(g);
put:
nvgpu_put(g);
return err;
}
static int cbc_status_debug_release(struct inode *inode, struct file *file)
{
struct gk20a *g = (struct gk20a *)inode->i_private;
gk20a_idle(g);
nvgpu_put(g);
return single_release(inode, file);
}
static const struct file_operations cbc_status_debug_fops = {
.open = cbc_status_debug_open,
.read = seq_read,
.llseek = seq_lseek,
.release = cbc_status_debug_release,
};
static ssize_t cbc_ctrl_debug_write_cmd(struct file *f, const char __user *cmd, size_t len, loff_t *off)
{
char cmd_buf[32];
struct gk20a *g = f->private_data;
struct nvgpu_cbc *cbc = g->cbc;
const size_t original_len = len;
int err = 0;
if (!cbc) {
nvgpu_err(g, "cbc is not initialized");
return -EINVAL;
}
if (len == 0 || len >= sizeof(cmd_buf)) {
nvgpu_err(g, "invalid cmd len: %zu", len);
return -EINVAL;
}
if (copy_from_user(cmd_buf, cmd, len)) {
nvgpu_err(g, "failed to read cmd");
return -EFAULT;
}
if (cmd_buf[len - 1] == '\n') {
len = len - 1;
}
cmd_buf[len] = '\x00';
if (strncmp("cbc_clean", cmd_buf, len) == 0) {
// Flush the comptag store to L2.
err = g->ops.cbc.ctrl(g, nvgpu_cbc_op_clean, 0, 0);
if (err == 0) {
// From from L2 to memory.
err = g->ops.mm.cache.l2_flush(g, false);
}
} else {
nvgpu_err(g, "Unknown cmd: %s", cmd_buf);
err = -EINVAL;
}
return err < 0 ? err : (ssize_t)original_len;
}
static int cbc_ctrl_debug_open(struct inode *inode, struct file *file)
{
int err = 0;
struct gk20a *g = inode->i_private;
if (!capable(CAP_SYS_ADMIN)) {
return -EPERM;
}
g = nvgpu_get(g);
if (!g) {
return -ENODEV;
}
err = gk20a_busy(g);
if (err < 0) {
nvgpu_err(g, "Couldn't power-up gpu");
goto put;
}
err = nvgpu_cbc_init_support(g);
if (err < 0) {
nvgpu_err(g, "cbc init failed");
goto idle;
}
file->private_data = g;
return err;
idle:
gk20a_idle(g);
put:
nvgpu_put(g);
return err;
}
static int cbc_ctrl_debug_release(struct inode *inode, struct file *file)
{
struct gk20a *g = file->private_data;
if (g) {
gk20a_idle(g);
nvgpu_put(g);
}
return 0;
}
static int cbc_ctrl_debug_mmap_cbc_store(struct file *f, struct vm_area_struct *vma)
{
struct gk20a *g = f->private_data;
struct nvgpu_cbc *cbc = g->cbc;
unsigned long mapping_size = 0U;
int err = 0;
u64 cbc_store_pa = 0;
pgprot_t prot = pgprot_noncached(vma->vm_page_prot);
if (vma->vm_flags & VM_WRITE) {
return -EPERM;
}
if (!(vma->vm_flags & VM_SHARED)) {
return -EINVAL;
}
if (!cbc) {
nvgpu_err(g, "cbc is not initialized");
err = -EINVAL;
goto done;
}
if (nvgpu_mem_is_valid(&cbc->compbit_store.mem) == 0) {
nvgpu_err(g, "cbc compbit store memory is not valid");
err = -EINVAL;
goto done;
}
mapping_size = (vma->vm_end - vma->vm_start);
if (mapping_size != cbc->compbit_store.mem.size) {
nvgpu_err(g, "mapping size (%lx) is unequal to store size (%lx)",
mapping_size, cbc->compbit_store.mem.size);
err = -EINVAL;
goto done;
}
if (vma->vm_pgoff != 0UL) {
err = -EINVAL;
goto done;
}
#if defined(NV_VM_AREA_STRUCT_HAS_CONST_VM_FLAGS) /* Linux v6.3 */
vm_flags_set(vma, VM_DONTCOPY | VM_DONTEXPAND | VM_NORESERVE |
VM_DONTDUMP | VM_PFNMAP);
vm_flags_clear(vma, VM_MAYWRITE);
#else
vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND | VM_NORESERVE |
VM_DONTDUMP | VM_PFNMAP;
vma->vm_flags &= ~VM_MAYWRITE;
#endif
cbc_store_pa = nvgpu_mem_get_addr(g, &cbc->compbit_store.mem);
err = remap_pfn_range(vma, vma->vm_start, cbc_store_pa >> PAGE_SHIFT,
mapping_size, prot);
if (err < 0) {
nvgpu_err(g, "Failed to remap %llx to user space", cbc_store_pa);
}
done:
return err;
}
static const struct file_operations cbc_ctrl_debug_fops = {
.open = cbc_ctrl_debug_open,
.release = cbc_ctrl_debug_release,
.write = cbc_ctrl_debug_write_cmd,
.mmap = cbc_ctrl_debug_mmap_cbc_store,
};
#endif /* CONFIG_NVGPU_COMPRESSION */
static int gr_default_attrib_cb_size_show(struct seq_file *s, void *data)
{
struct gk20a *g = s->private;
/* HAL might not be initialized yet */
if (g->ops.gr.init.get_attrib_cb_default_size == NULL)
return -EFAULT;
seq_printf(s, "%u\n", g->ops.gr.init.get_attrib_cb_default_size(g));
return 0;
}
static int gr_default_attrib_cb_size_open(struct inode *inode,
struct file *file)
{
return single_open(file, gr_default_attrib_cb_size_show,
inode->i_private);
}
static const struct file_operations gr_default_attrib_cb_size_fops= {
.open = gr_default_attrib_cb_size_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static ssize_t force_preemption_gfxp_read(struct file *file,
char __user *user_buf, size_t count, loff_t *ppos)
{
char buf[3];
struct gk20a *g = file->private_data;
if (g->gr->gr_ctx_desc == NULL) {
return -EFAULT;
}
if (g->gr->gr_ctx_desc->force_preemption_gfxp) {
buf[0] = 'Y';
} else {
buf[0] = 'N';
}
buf[1] = '\n';
buf[2] = 0x00;
return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
}
static ssize_t force_preemption_gfxp_write(struct file *file,
const char __user *user_buf, size_t count, loff_t *ppos)
{
char buf[32];
int buf_size;
bool val;
struct gk20a *g = file->private_data;
if (g->gr->gr_ctx_desc == NULL) {
return -EFAULT;
}
buf_size = min(count, (sizeof(buf)-1));
if (copy_from_user(buf, user_buf, buf_size)) {
return -EFAULT;
}
if (kstrtobool(buf, &val) == 0) {
g->gr->gr_ctx_desc->force_preemption_gfxp = val;
}
return count;
}
static struct file_operations force_preemption_gfxp_fops = {
.open = simple_open,
.read = force_preemption_gfxp_read,
.write = force_preemption_gfxp_write,
};
static ssize_t force_preemption_cilp_read(struct file *file,
char __user *user_buf, size_t count, loff_t *ppos)
{
char buf[3];
struct gk20a *g = file->private_data;
if (g->gr->gr_ctx_desc == NULL) {
return -EFAULT;
}
if (g->gr->gr_ctx_desc->force_preemption_cilp) {
buf[0] = 'Y';
} else {
buf[0] = 'N';
}
buf[1] = '\n';
buf[2] = 0x00;
return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
}
static ssize_t force_preemption_cilp_write(struct file *file,
const char __user *user_buf, size_t count, loff_t *ppos)
{
char buf[32];
int buf_size;
bool val;
struct gk20a *g = file->private_data;
if (g->gr->gr_ctx_desc == NULL) {
return -EFAULT;
}
buf_size = min(count, (sizeof(buf)-1));
if (copy_from_user(buf, user_buf, buf_size)) {
return -EFAULT;
}
if (kstrtobool(buf, &val) == 0) {
g->gr->gr_ctx_desc->force_preemption_cilp = val;
}
return count;
}
static struct file_operations force_preemption_cilp_fops = {
.open = simple_open,
.read = force_preemption_cilp_read,
.write = force_preemption_cilp_write,
};
static ssize_t dump_ctxsw_stats_on_channel_close_read(struct file *file,
char __user *user_buf, size_t count, loff_t *ppos)
{
char buf[3];
struct gk20a *g = file->private_data;
if (g->gr->gr_ctx_desc == NULL) {
return -EFAULT;
}
if (g->gr->gr_ctx_desc->dump_ctxsw_stats_on_channel_close) {
buf[0] = 'Y';
} else {
buf[0] = 'N';
}
buf[1] = '\n';
buf[2] = 0x00;
return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
}
static ssize_t dump_ctxsw_stats_on_channel_close_write(struct file *file,
const char __user *user_buf, size_t count, loff_t *ppos)
{
char buf[32];
int buf_size;
bool val;
struct gk20a *g = file->private_data;
if (g->gr->gr_ctx_desc == NULL) {
return -EFAULT;
}
buf_size = min(count, (sizeof(buf)-1));
if (copy_from_user(buf, user_buf, buf_size)) {
return -EFAULT;
}
if (kstrtobool(buf, &val) == 0) {
g->gr->gr_ctx_desc->dump_ctxsw_stats_on_channel_close = val;
}
return count;
}
static struct file_operations dump_ctxsw_stats_on_channel_close_fops = {
.open = simple_open,
.read = dump_ctxsw_stats_on_channel_close_read,
.write = dump_ctxsw_stats_on_channel_close_write,
};
int gr_gk20a_debugfs_init(struct gk20a *g)
{
struct nvgpu_os_linux *l = nvgpu_os_linux_from_gk20a(g);
struct dentry *d;
d = debugfs_create_file(
"gr_default_attrib_cb_size", S_IRUGO, l->debugfs, g,
&gr_default_attrib_cb_size_fops);
if (!d)
return -ENOMEM;
d = debugfs_create_file(
"force_preemption_gfxp", S_IRUGO|S_IWUSR, l->debugfs, g,
&force_preemption_gfxp_fops);
if (!d)
return -ENOMEM;
d = debugfs_create_file(
"force_preemption_cilp", S_IRUGO|S_IWUSR, l->debugfs, g,
&force_preemption_cilp_fops);
if (!d)
return -ENOMEM;
#ifdef CONFIG_NVGPU_COMPRESSION
d = debugfs_create_file("cbc_status", S_IRUSR, l->debugfs, g,
&cbc_status_debug_fops);
if (!d)
return -ENOMEM;
/* Using debugfs_create_file_unsafe to allow mmap */
d = debugfs_create_file_unsafe("cbc_ctrl", S_IWUSR,
l->debugfs, g, &cbc_ctrl_debug_fops);
if (!d)
return -ENOMEM;
#endif /* CONFIG_NVGPU_COMPRESSION */
if (!g->is_virtual) {
d = debugfs_create_file(
"dump_ctxsw_stats_on_channel_close", S_IRUGO|S_IWUSR,
l->debugfs, g,
&dump_ctxsw_stats_on_channel_close_fops);
if (!d)
return -ENOMEM;
}
return 0;
}