From 639ca4edfb5c4a376003da90f724c1b7c4014a31 Mon Sep 17 00:00:00 2001
From: dt
Date: Fri, 9 Apr 2021 06:54:50 +0000
Subject: [PATCH] gpu: nvgpu: mig: Defer dev_nodes creation and create new
power node to support MIG
- This is deferring the dev_nodes creation after power_on to
select the MIG config and to create the dev_nodes as per the
selected MIG config.
- The patch is adding a device node to issue power on. The
nodes are:
for igpu :/dev/nvgpu/igpu0/power
for dgpu:/dev/nvgpu/dgpu-0001:01:00.0/power
To issue power on :
echo "1" > /dev/nvgpu/igpu0/power
echo "1" > /dev/nvgpu/dgpu-0001:01:00.0/power
JIRA NVGPU-6633
Change-Id: Ic4f1f3e42724cc788dcfaf0e881d188fd3bd1ce1
Signed-off-by: dt
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvgpu/+/2512647
Tested-by: mobile promotions
Reviewed-by: mobile promotions
---
arch/nvgpu-linux.yaml | 4 +-
drivers/gpu/nvgpu/Makefile | 3 +-
drivers/gpu/nvgpu/os/linux/driver_common.c | 20 ++-
drivers/gpu/nvgpu/os/linux/ioctl.c | 145 +++++++++++++++++--
drivers/gpu/nvgpu/os/linux/ioctl.h | 4 +-
drivers/gpu/nvgpu/os/linux/module.c | 15 ++
drivers/gpu/nvgpu/os/linux/os_linux.h | 2 +
drivers/gpu/nvgpu/os/linux/power_ops.c | 154 +++++++++++++++++++++
drivers/gpu/nvgpu/os/linux/power_ops.h | 29 ++++
9 files changed, 355 insertions(+), 21 deletions(-)
create mode 100644 drivers/gpu/nvgpu/os/linux/power_ops.c
create mode 100644 drivers/gpu/nvgpu/os/linux/power_ops.h
diff --git a/arch/nvgpu-linux.yaml b/arch/nvgpu-linux.yaml
index 416caa3c3..d7b9dc4bd 100644
--- a/arch/nvgpu-linux.yaml
+++ b/arch/nvgpu-linux.yaml
@@ -120,7 +120,9 @@ ioctl:
os/linux/ioctl_prof.c,
os/linux/ioctl_prof.h,
os/linux/ioctl_tsg.c,
- os/linux/ioctl_tsg.h ]
+ os/linux/ioctl_tsg.h,
+ os/linux/power_ops.c,
+ os/linux/power_ops.h ]
kmem:
sources: [ os/linux/kmem.c, os/linux/kmem_priv.h ]
diff --git a/drivers/gpu/nvgpu/Makefile b/drivers/gpu/nvgpu/Makefile
index d94a37019..a5e748478 100644
--- a/drivers/gpu/nvgpu/Makefile
+++ b/drivers/gpu/nvgpu/Makefile
@@ -419,7 +419,8 @@ nvgpu-y += \
os/linux/ecc_sysfs.o \
os/linux/bsearch.o \
os/linux/sdl/sdl_stub.o \
- os/linux/dmabuf_priv.o
+ os/linux/dmabuf_priv.o \
+ os/linux/power_ops.o
nvgpu-$(CONFIG_NVGPU_VPR) += os/linux/vpr.o
diff --git a/drivers/gpu/nvgpu/os/linux/driver_common.c b/drivers/gpu/nvgpu/os/linux/driver_common.c
index 1f1daf957..22ac511c6 100644
--- a/drivers/gpu/nvgpu/os/linux/driver_common.c
+++ b/drivers/gpu/nvgpu/os/linux/driver_common.c
@@ -268,6 +268,7 @@ int nvgpu_probe(struct gk20a *g,
{
struct device *dev = dev_from_gk20a(g);
struct gk20a_platform *platform = dev_get_drvdata(dev);
+ struct nvgpu_os_linux *l = nvgpu_os_linux_from_gk20a(g);
int err = 0;
nvgpu_init_vars(g);
@@ -294,11 +295,22 @@ int nvgpu_probe(struct gk20a *g,
}
nvgpu_init_mm_vars(g);
-
- /* platform probe can defer do user init only if probe succeeds */
- err = gk20a_user_init(dev);
- if (err)
+ err = gk20a_power_node_init(dev);
+ if (err) {
+ nvgpu_err(g, "power_node creation failed");
return err;
+ }
+
+ /*
+ * TODO: While removing the legacy nodes the following condition
+ * need to be removed.
+ */
+ if (platform->platform_chip_id == TEGRA_210) {
+ err = gk20a_user_init(dev);
+ if (err)
+ return err;
+ l->dev_nodes_created = true;
+ }
/*
* Note that for runtime suspend to work the clocks have to be setup
diff --git a/drivers/gpu/nvgpu/os/linux/ioctl.c b/drivers/gpu/nvgpu/os/linux/ioctl.c
index e024970b9..0b731002f 100644
--- a/drivers/gpu/nvgpu/os/linux/ioctl.c
+++ b/drivers/gpu/nvgpu/os/linux/ioctl.c
@@ -1,7 +1,7 @@
/*
* NVGPU IOCTLs
*
- * Copyright (c) 2011-2020, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2011-2021, 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,
@@ -30,12 +30,21 @@
#include "ioctl_tsg.h"
#include "ioctl_dbg.h"
#include "ioctl_prof.h"
+#include "power_ops.h"
#include "ioctl.h"
#include "module.h"
#include "os_linux.h"
#include "fecs_trace_linux.h"
#include "platform_gk20a.h"
+const struct file_operations gk20a_power_node_ops = {
+ .owner = THIS_MODULE,
+ .release = gk20a_power_release,
+ .open = gk20a_power_open,
+ .read = gk20a_power_read,
+ .write = gk20a_power_write,
+};
+
const struct file_operations gk20a_channel_ops = {
.owner = THIS_MODULE,
.release = gk20a_channel_release,
@@ -161,6 +170,7 @@ struct nvgpu_dev_node {
};
static const struct nvgpu_dev_node dev_node_list[] = {
+ {"power", &gk20a_power_node_ops, false },
{"as", &gk20a_as_ops, false },
{"channel", &gk20a_channel_ops, false },
{"ctrl", &gk20a_ctrl_ops, true },
@@ -332,6 +342,8 @@ void gk20a_user_deinit(struct device *dev)
class_destroy(class->class);
nvgpu_kfree(g, class);
}
+
+ l->dev_nodes_created = false;
}
static struct nvgpu_class *nvgpu_create_class(struct gk20a *g, const char *class_name)
@@ -463,20 +475,31 @@ static int nvgpu_prepare_mig_dev_node_class_list(struct gk20a *g, u32 *num_class
return 0;
}
-static int nvgpu_prepare_default_dev_node_class_list(struct gk20a *g, u32 *num_classes)
+static int nvgpu_prepare_default_dev_node_class_list(struct gk20a *g,
+ u32 *num_classes, bool power_node)
{
struct nvgpu_class *class;
u32 count = 0U;
if (g->pci_class != 0U) {
- class = nvgpu_create_class(g, "nvidia-pci-gpu");
+ if (power_node) {
+ class = nvgpu_create_class(g, "nvidia-pci-gpu-power");
+ } else {
+ class = nvgpu_create_class(g, "nvidia-pci-gpu");
+ }
+
if (class == NULL) {
return -ENOMEM;
}
class->class->devnode = nvgpu_pci_devnode;
count++;
} else {
- class = nvgpu_create_class(g, "nvidia-gpu");
+ if (power_node) {
+ class = nvgpu_create_class(g, "nvidia-gpu-power");
+ } else {
+ class = nvgpu_create_class(g, "nvidia-gpu");
+ }
+
if (class == NULL) {
return -ENOMEM;
}
@@ -484,20 +507,34 @@ static int nvgpu_prepare_default_dev_node_class_list(struct gk20a *g, u32 *num_c
count++;
}
+ if (power_node) {
+ class->power_node = true;
+ }
+
/*
* V2 device node names hierarchy.
* This hierarchy will replace above hierarchy in second phase.
* Both legacy and V2 device node hierarchies will co-exist until then.
*/
if (g->pci_class != 0U) {
- class = nvgpu_create_class(g, "nvidia-pci-gpu-v2");
+ if (power_node) {
+ class = nvgpu_create_class(g, "nvidia-pci-gpu-v2-power");
+ } else {
+ class = nvgpu_create_class(g, "nvidia-pci-gpu-v2");
+ }
+
if (class == NULL) {
return -ENOMEM;
}
class->class->devnode = nvgpu_pci_devnode_v2;
count++;
} else {
- class = nvgpu_create_class(g, "nvidia-gpu-v2");
+ if (power_node) {
+ class = nvgpu_create_class(g, "nvidia-gpu-v2-power");
+ } else {
+ class = nvgpu_create_class(g, "nvidia-gpu-v2");
+ }
+
if (class == NULL) {
return -ENOMEM;
}
@@ -505,18 +542,23 @@ static int nvgpu_prepare_default_dev_node_class_list(struct gk20a *g, u32 *num_c
count++;
}
+ if (power_node) {
+ class->power_node = true;
+ }
+
*num_classes = count;
return 0;
}
-static int nvgpu_prepare_dev_node_class_list(struct gk20a *g, u32 *num_classes)
+static int nvgpu_prepare_dev_node_class_list(struct gk20a *g, u32 *num_classes,
+ bool power_node)
{
int err;
if (nvgpu_is_enabled(g, NVGPU_SUPPORT_MIG)) {
err = nvgpu_prepare_mig_dev_node_class_list(g, num_classes);
} else {
- err = nvgpu_prepare_default_dev_node_class_list(g, num_classes);
+ err = nvgpu_prepare_default_dev_node_class_list(g, num_classes, power_node);
}
return err;
@@ -537,8 +579,12 @@ static bool check_valid_dev_node(struct gk20a *g, struct nvgpu_class *class,
static bool check_valid_class(struct gk20a *g, struct nvgpu_class *class)
{
+ if (class->power_node) {
+ return false;
+ }
+
if (nvgpu_is_enabled(g, NVGPU_SUPPORT_MIG)) {
- if (class->instance_type == NVGPU_MIG_TYPE_PHYSICAL) {
+ if ((class->instance_type == NVGPU_MIG_TYPE_PHYSICAL)) {
return false;
}
}
@@ -546,6 +592,70 @@ static bool check_valid_class(struct gk20a *g, struct nvgpu_class *class)
return true;
}
+int gk20a_power_node_init(struct device *dev)
+{
+ int err;
+ dev_t devno;
+ struct gk20a *g = gk20a_from_dev(dev);
+ struct nvgpu_os_linux *l = nvgpu_os_linux_from_gk20a(g);
+ struct nvgpu_class *class;
+ u32 total_cdevs;
+ u32 num_classes;
+ struct nvgpu_cdev *cdev;
+
+ if (!l->cdev_list_init_done) {
+ nvgpu_init_list_node(&l->cdev_list_head);
+ nvgpu_init_list_node(&l->class_list_head);
+ l->cdev_list_init_done = true;
+ }
+
+ err = nvgpu_prepare_dev_node_class_list(g, &num_classes, true);
+ if (err != 0) {
+ return err;
+ }
+
+ total_cdevs = num_classes;
+ err = alloc_chrdev_region(&devno, 0, total_cdevs, dev_name(dev));
+
+ if (err) {
+ dev_err(dev, "failed to allocate devno\n");
+ goto fail;
+ }
+
+ l->cdev_region = devno;
+ nvgpu_list_for_each_entry(class, &l->class_list_head, nvgpu_class, list_entry) {
+ cdev = nvgpu_kzalloc(g, sizeof(*cdev));
+ if (cdev == NULL) {
+ dev_err(dev, "failed to allocate cdev\n");
+ goto fail;
+ }
+
+ /*
+ * dev_node_list[0] is the power node to issue
+ * power-on to the GPU.
+ */
+ err = gk20a_create_device(dev, devno++,
+ dev_node_list[0].name,
+ &cdev->cdev, &cdev->node,
+ dev_node_list[0].fops,
+ class);
+ if (err) {
+ goto fail;
+ }
+
+ cdev->class = class->class;
+ nvgpu_init_list_node(&cdev->list_entry);
+ nvgpu_list_add(&cdev->list_entry, &l->cdev_list_head);
+ }
+
+ l->num_cdevs = total_cdevs;
+ return 0;
+fail:
+ gk20a_user_deinit(dev);
+ return err;
+
+}
+
int gk20a_user_init(struct device *dev)
{
int err;
@@ -558,10 +668,13 @@ int gk20a_user_init(struct device *dev)
struct nvgpu_cdev *cdev;
u32 cdev_index;
- nvgpu_init_list_node(&l->cdev_list_head);
- nvgpu_init_list_node(&l->class_list_head);
+ if (!l->cdev_list_init_done) {
+ nvgpu_init_list_node(&l->cdev_list_head);
+ nvgpu_init_list_node(&l->class_list_head);
+ l->cdev_list_init_done = true;
+ }
- err = nvgpu_prepare_dev_node_class_list(g, &num_classes);
+ err = nvgpu_prepare_dev_node_class_list(g, &num_classes, false);
if (err != 0) {
return err;
}
@@ -581,7 +694,11 @@ int gk20a_user_init(struct device *dev)
continue;
}
- for (cdev_index = 0; cdev_index < num_cdevs; cdev_index++) {
+ /*
+ * As we created the power node with power class already, the
+ * index is starting from one.
+ */
+ for (cdev_index = 1; cdev_index < num_cdevs; cdev_index++) {
if (!check_valid_dev_node(g, class, &dev_node_list[cdev_index])) {
continue;
}
@@ -607,7 +724,7 @@ int gk20a_user_init(struct device *dev)
}
}
- l->num_cdevs = total_cdevs;
+ l->num_cdevs += total_cdevs;
return 0;
fail:
diff --git a/drivers/gpu/nvgpu/os/linux/ioctl.h b/drivers/gpu/nvgpu/os/linux/ioctl.h
index 406e62bbe..7d0aa1306 100644
--- a/drivers/gpu/nvgpu/os/linux/ioctl.h
+++ b/drivers/gpu/nvgpu/os/linux/ioctl.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2017-2021, 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,
@@ -50,6 +50,7 @@ struct nvgpu_class {
struct nvgpu_cdev_class_priv_data *priv_data;
enum nvgpu_mig_gpu_instance_type instance_type;
+ bool power_node;
};
static inline struct nvgpu_class *
@@ -60,6 +61,7 @@ nvgpu_class_from_list_entry(struct nvgpu_list_node *node)
};
int gk20a_user_init(struct device *dev);
+int gk20a_power_node_init(struct device *dev);
void gk20a_user_deinit(struct device *dev);
struct gk20a *nvgpu_get_gk20a_from_cdev(struct nvgpu_cdev *cdev);
diff --git a/drivers/gpu/nvgpu/os/linux/module.c b/drivers/gpu/nvgpu/os/linux/module.c
index 923e8e85d..a091eba67 100644
--- a/drivers/gpu/nvgpu/os/linux/module.c
+++ b/drivers/gpu/nvgpu/os/linux/module.c
@@ -438,6 +438,18 @@ int gk20a_pm_finalize_poweron(struct device *dev)
if (err)
goto done;
+ /**
+ * TODO: Need to add nvgpu_early_poweron() sequence before
+ * creating device nodes.
+ */
+ if (!l->dev_nodes_created) {
+ err = gk20a_user_init(dev);
+ if (err) {
+ goto done;
+ }
+ l->dev_nodes_created = true;
+ }
+
if (g->sim) {
if (g->sim->sim_init_late)
err = g->sim->sim_init_late(g);
@@ -530,6 +542,9 @@ done:
if (err != 0) {
nvgpu_disable_irqs(g);
nvgpu_remove_sim_support_linux(g);
+ if (l->dev_nodes_created) {
+ gk20a_user_deinit(dev);
+ }
}
nvgpu_mutex_release(&g->power_lock);
diff --git a/drivers/gpu/nvgpu/os/linux/os_linux.h b/drivers/gpu/nvgpu/os/linux/os_linux.h
index 329a29d0f..de84c3486 100644
--- a/drivers/gpu/nvgpu/os/linux/os_linux.h
+++ b/drivers/gpu/nvgpu/os/linux/os_linux.h
@@ -78,6 +78,8 @@ struct nvgpu_os_linux {
struct nvgpu_list_node class_list_head;
struct nvgpu_list_node cdev_list_head;
u32 num_cdevs;
+ bool dev_nodes_created;
+ bool cdev_list_init_done;
dev_t cdev_region;
diff --git a/drivers/gpu/nvgpu/os/linux/power_ops.c b/drivers/gpu/nvgpu/os/linux/power_ops.c
new file mode 100644
index 000000000..ab0c7ed90
--- /dev/null
+++ b/drivers/gpu/nvgpu/os/linux/power_ops.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2021, 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 .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "ioctl.h"
+
+#include "platform_gk20a.h"
+#include "os_linux.h"
+
+#define NVGPU_DRIVER_POWER_ON_NEEDED 1
+
+int gk20a_power_open(struct inode *inode, struct file *filp)
+{
+ struct gk20a *g;
+ struct nvgpu_cdev *cdev;
+
+ cdev = container_of(inode->i_cdev, struct nvgpu_cdev, cdev);
+ g = nvgpu_get_gk20a_from_cdev(cdev);
+ filp->private_data = g;
+
+ g = nvgpu_get(g);
+ if (!g) {
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+int gk20a_power_read(struct file *filp, char __user *buf,
+ size_t size, loff_t *off)
+{
+ struct gk20a *g = filp->private_data;
+ u32 power_status = 0U;
+ char power_out[2] = "";
+
+ if (!g) {
+ return -ENODEV;
+ }
+
+ power_status = g->power_on_state;
+ power_out[0] = power_status + '0';
+
+ if (size < sizeof(power_out)) {
+ return -EINVAL;
+ }
+
+ if (*off >= (loff_t)sizeof(power_out)) {
+ return 0;
+ }
+
+ if (*off + (loff_t)size > (loff_t)sizeof(power_out)) {
+ size = sizeof(u32) - *off;
+ }
+
+ if (copy_to_user(buf, (char *)power_out + *off, size)) {
+ return -EINVAL;
+ }
+
+ *off += size;
+ return size;
+}
+
+
+int gk20a_power_write(struct file *filp, const char __user *buf,
+ size_t size, loff_t *off)
+{
+ struct gk20a *g = filp->private_data;
+ u32 power_status = 0U;
+ int err = 0;
+ unsigned char *userinput = NULL;
+
+ if (!g) {
+ return -ENODEV;
+ }
+
+ userinput = (unsigned char *)kzalloc(size, GFP_KERNEL);
+ if (!userinput) {
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(userinput, buf, size)) {
+ kfree(userinput);
+ return -EFAULT;
+ }
+
+ if (kstrtouint(userinput, 10, &power_status)) {
+ kfree(userinput);
+ return -EINVAL;
+ }
+
+ if (power_status == NVGPU_DRIVER_POWER_ON_NEEDED) {
+ if ((g->power_on_state == NVGPU_STATE_POWERING_ON) ||
+ (g->power_on_state == NVGPU_STATE_POWERED_ON)) {
+ goto free_input;
+ }
+
+ err = gk20a_busy(g);
+ if (err) {
+ nvgpu_err(g, "power_node_write failed at busy");
+ kfree(userinput);
+ return err;
+ }
+
+ gk20a_idle(g);
+ } else {
+ nvgpu_err(g, "1 is the valid value to power-on the GPU");
+ kfree(userinput);
+ return -EINVAL;
+ }
+
+free_input:
+ *off += size;
+ kfree(userinput);
+ return size;
+}
+
+int gk20a_power_release(struct inode *inode, struct file *filp)
+{
+ struct gk20a *g;
+ struct nvgpu_cdev *cdev;
+
+ cdev = container_of(inode->i_cdev, struct nvgpu_cdev, cdev);
+ g = nvgpu_get_gk20a_from_cdev(cdev);
+ nvgpu_put(g);
+ return 0;
+}
diff --git a/drivers/gpu/nvgpu/os/linux/power_ops.h b/drivers/gpu/nvgpu/os/linux/power_ops.h
new file mode 100644
index 000000000..2d0c682db
--- /dev/null
+++ b/drivers/gpu/nvgpu/os/linux/power_ops.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2021, 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_POWER_LINUX_H
+#define NVGPU_POWER_LINUX_H
+
+#include
+
+int gk20a_power_open(struct inode *inode, struct file *filp);
+ssize_t gk20a_power_read(struct file *filp, char __user *buf,
+ size_t size, loff_t *off);
+ssize_t gk20a_power_write(struct file *filp, const char __user *buf,
+ size_t size, loff_t *off);
+int gk20a_power_release(struct inode *inode, struct file *filp);
+
+#endif