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