mirror of
git://nv-tegra.nvidia.com/linux-nvgpu.git
synced 2025-12-22 17:36:20 +03:00
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>
502 lines
12 KiB
C
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;
|
|
}
|
|
|