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 <dt@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvgpu/+/2512647
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
This commit is contained in:
dt
2021-04-09 06:54:50 +00:00
committed by mobile promotions
parent cfc1281223
commit 639ca4edfb
9 changed files with 355 additions and 21 deletions

View File

@@ -120,7 +120,9 @@ ioctl:
os/linux/ioctl_prof.c, os/linux/ioctl_prof.c,
os/linux/ioctl_prof.h, os/linux/ioctl_prof.h,
os/linux/ioctl_tsg.c, 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: kmem:
sources: [ os/linux/kmem.c, os/linux/kmem_priv.h ] sources: [ os/linux/kmem.c, os/linux/kmem_priv.h ]

View File

@@ -419,7 +419,8 @@ nvgpu-y += \
os/linux/ecc_sysfs.o \ os/linux/ecc_sysfs.o \
os/linux/bsearch.o \ os/linux/bsearch.o \
os/linux/sdl/sdl_stub.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 nvgpu-$(CONFIG_NVGPU_VPR) += os/linux/vpr.o

View File

@@ -268,6 +268,7 @@ int nvgpu_probe(struct gk20a *g,
{ {
struct device *dev = dev_from_gk20a(g); struct device *dev = dev_from_gk20a(g);
struct gk20a_platform *platform = dev_get_drvdata(dev); struct gk20a_platform *platform = dev_get_drvdata(dev);
struct nvgpu_os_linux *l = nvgpu_os_linux_from_gk20a(g);
int err = 0; int err = 0;
nvgpu_init_vars(g); nvgpu_init_vars(g);
@@ -294,11 +295,22 @@ int nvgpu_probe(struct gk20a *g,
} }
nvgpu_init_mm_vars(g); nvgpu_init_mm_vars(g);
err = gk20a_power_node_init(dev);
if (err) {
nvgpu_err(g, "power_node creation failed");
return err;
}
/* platform probe can defer do user init only if probe succeeds */ /*
* 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); err = gk20a_user_init(dev);
if (err) if (err)
return err; return err;
l->dev_nodes_created = true;
}
/* /*
* Note that for runtime suspend to work the clocks have to be setup * Note that for runtime suspend to work the clocks have to be setup

View File

@@ -1,7 +1,7 @@
/* /*
* NVGPU IOCTLs * 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 * This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License, * under the terms and conditions of the GNU General Public License,
@@ -30,12 +30,21 @@
#include "ioctl_tsg.h" #include "ioctl_tsg.h"
#include "ioctl_dbg.h" #include "ioctl_dbg.h"
#include "ioctl_prof.h" #include "ioctl_prof.h"
#include "power_ops.h"
#include "ioctl.h" #include "ioctl.h"
#include "module.h" #include "module.h"
#include "os_linux.h" #include "os_linux.h"
#include "fecs_trace_linux.h" #include "fecs_trace_linux.h"
#include "platform_gk20a.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 = { const struct file_operations gk20a_channel_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.release = gk20a_channel_release, .release = gk20a_channel_release,
@@ -161,6 +170,7 @@ struct nvgpu_dev_node {
}; };
static const struct nvgpu_dev_node dev_node_list[] = { static const struct nvgpu_dev_node dev_node_list[] = {
{"power", &gk20a_power_node_ops, false },
{"as", &gk20a_as_ops, false }, {"as", &gk20a_as_ops, false },
{"channel", &gk20a_channel_ops, false }, {"channel", &gk20a_channel_ops, false },
{"ctrl", &gk20a_ctrl_ops, true }, {"ctrl", &gk20a_ctrl_ops, true },
@@ -332,6 +342,8 @@ void gk20a_user_deinit(struct device *dev)
class_destroy(class->class); class_destroy(class->class);
nvgpu_kfree(g, class); nvgpu_kfree(g, class);
} }
l->dev_nodes_created = false;
} }
static struct nvgpu_class *nvgpu_create_class(struct gk20a *g, const char *class_name) 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; 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; struct nvgpu_class *class;
u32 count = 0U; u32 count = 0U;
if (g->pci_class != 0U) { if (g->pci_class != 0U) {
if (power_node) {
class = nvgpu_create_class(g, "nvidia-pci-gpu-power");
} else {
class = nvgpu_create_class(g, "nvidia-pci-gpu"); class = nvgpu_create_class(g, "nvidia-pci-gpu");
}
if (class == NULL) { if (class == NULL) {
return -ENOMEM; return -ENOMEM;
} }
class->class->devnode = nvgpu_pci_devnode; class->class->devnode = nvgpu_pci_devnode;
count++; count++;
} else {
if (power_node) {
class = nvgpu_create_class(g, "nvidia-gpu-power");
} else { } else {
class = nvgpu_create_class(g, "nvidia-gpu"); class = nvgpu_create_class(g, "nvidia-gpu");
}
if (class == NULL) { if (class == NULL) {
return -ENOMEM; return -ENOMEM;
} }
@@ -484,20 +507,34 @@ static int nvgpu_prepare_default_dev_node_class_list(struct gk20a *g, u32 *num_c
count++; count++;
} }
if (power_node) {
class->power_node = true;
}
/* /*
* V2 device node names hierarchy. * V2 device node names hierarchy.
* This hierarchy will replace above hierarchy in second phase. * This hierarchy will replace above hierarchy in second phase.
* Both legacy and V2 device node hierarchies will co-exist until then. * Both legacy and V2 device node hierarchies will co-exist until then.
*/ */
if (g->pci_class != 0U) { if (g->pci_class != 0U) {
if (power_node) {
class = nvgpu_create_class(g, "nvidia-pci-gpu-v2-power");
} else {
class = nvgpu_create_class(g, "nvidia-pci-gpu-v2"); class = nvgpu_create_class(g, "nvidia-pci-gpu-v2");
}
if (class == NULL) { if (class == NULL) {
return -ENOMEM; return -ENOMEM;
} }
class->class->devnode = nvgpu_pci_devnode_v2; class->class->devnode = nvgpu_pci_devnode_v2;
count++; count++;
} else {
if (power_node) {
class = nvgpu_create_class(g, "nvidia-gpu-v2-power");
} else { } else {
class = nvgpu_create_class(g, "nvidia-gpu-v2"); class = nvgpu_create_class(g, "nvidia-gpu-v2");
}
if (class == NULL) { if (class == NULL) {
return -ENOMEM; return -ENOMEM;
} }
@@ -505,18 +542,23 @@ static int nvgpu_prepare_default_dev_node_class_list(struct gk20a *g, u32 *num_c
count++; count++;
} }
if (power_node) {
class->power_node = true;
}
*num_classes = count; *num_classes = count;
return 0; 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; int err;
if (nvgpu_is_enabled(g, NVGPU_SUPPORT_MIG)) { if (nvgpu_is_enabled(g, NVGPU_SUPPORT_MIG)) {
err = nvgpu_prepare_mig_dev_node_class_list(g, num_classes); err = nvgpu_prepare_mig_dev_node_class_list(g, num_classes);
} else { } 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; 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) 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 (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; return false;
} }
} }
@@ -546,6 +592,70 @@ static bool check_valid_class(struct gk20a *g, struct nvgpu_class *class)
return true; 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 gk20a_user_init(struct device *dev)
{ {
int err; int err;
@@ -558,10 +668,13 @@ int gk20a_user_init(struct device *dev)
struct nvgpu_cdev *cdev; struct nvgpu_cdev *cdev;
u32 cdev_index; u32 cdev_index;
if (!l->cdev_list_init_done) {
nvgpu_init_list_node(&l->cdev_list_head); nvgpu_init_list_node(&l->cdev_list_head);
nvgpu_init_list_node(&l->class_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) { if (err != 0) {
return err; return err;
} }
@@ -581,7 +694,11 @@ int gk20a_user_init(struct device *dev)
continue; 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])) { if (!check_valid_dev_node(g, class, &dev_node_list[cdev_index])) {
continue; continue;
} }
@@ -607,7 +724,7 @@ int gk20a_user_init(struct device *dev)
} }
} }
l->num_cdevs = total_cdevs; l->num_cdevs += total_cdevs;
return 0; return 0;
fail: fail:

View File

@@ -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 * This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License, * 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; struct nvgpu_cdev_class_priv_data *priv_data;
enum nvgpu_mig_gpu_instance_type instance_type; enum nvgpu_mig_gpu_instance_type instance_type;
bool power_node;
}; };
static inline struct nvgpu_class * 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_user_init(struct device *dev);
int gk20a_power_node_init(struct device *dev);
void gk20a_user_deinit(struct device *dev); void gk20a_user_deinit(struct device *dev);
struct gk20a *nvgpu_get_gk20a_from_cdev(struct nvgpu_cdev *cdev); struct gk20a *nvgpu_get_gk20a_from_cdev(struct nvgpu_cdev *cdev);

View File

@@ -438,6 +438,18 @@ int gk20a_pm_finalize_poweron(struct device *dev)
if (err) if (err)
goto done; 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) {
if (g->sim->sim_init_late) if (g->sim->sim_init_late)
err = g->sim->sim_init_late(g); err = g->sim->sim_init_late(g);
@@ -530,6 +542,9 @@ done:
if (err != 0) { if (err != 0) {
nvgpu_disable_irqs(g); nvgpu_disable_irqs(g);
nvgpu_remove_sim_support_linux(g); nvgpu_remove_sim_support_linux(g);
if (l->dev_nodes_created) {
gk20a_user_deinit(dev);
}
} }
nvgpu_mutex_release(&g->power_lock); nvgpu_mutex_release(&g->power_lock);

View File

@@ -78,6 +78,8 @@ struct nvgpu_os_linux {
struct nvgpu_list_node class_list_head; struct nvgpu_list_node class_list_head;
struct nvgpu_list_node cdev_list_head; struct nvgpu_list_node cdev_list_head;
u32 num_cdevs; u32 num_cdevs;
bool dev_nodes_created;
bool cdev_list_init_done;
dev_t cdev_region; dev_t cdev_region;

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/file.h>
#include <linux/slab.h>
#include <linux/anon_inodes.h>
#include <linux/fs.h>
#include <uapi/linux/nvgpu.h>
#include <nvgpu/kmem.h>
#include <nvgpu/log.h>
#include <nvgpu/enabled.h>
#include <nvgpu/sizes.h>
#include <nvgpu/list.h>
#include <nvgpu/gk20a.h>
#include <nvgpu/nvgpu_init.h>
#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;
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef NVGPU_POWER_LINUX_H
#define NVGPU_POWER_LINUX_H
#include <nvgpu/types.h>
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