diff mbox series

[v1,4/6] package/libnvidia-container: new package

Message ID 20200801213658.33869-4-christian@paral.in
State Superseded
Headers show
Series [v1,1/6] package/nvidia-modprobe: new package | expand

Commit Message

Christian Stewart Aug. 1, 2020, 9:36 p.m. UTC
The libnvidia-container package adds a library and CLI for GPU-backed
containers, agnostic to container runtime.

https://github.com/NVIDIA/libnvidia-container

Signed-off-by: Christian Stewart <christian@paral.in>
---
 package/Config.in                             |    1 +
 ...d-fixes-from-vowstar-portage-overlay.patch | 1890 +++++++++++++++++
 ...ve-dependency-handling-from-Makefile.patch |  698 ++++++
 package/libnvidia-container/Config.in         |   18 +
 .../libnvidia-container.hash                  |    3 +
 .../libnvidia-container.mk                    |   57 +
 6 files changed, 2667 insertions(+)
 create mode 100644 package/libnvidia-container/0001-Build-fixes-from-vowstar-portage-overlay.patch
 create mode 100644 package/libnvidia-container/0002-Remove-dependency-handling-from-Makefile.patch
 create mode 100644 package/libnvidia-container/Config.in
 create mode 100644 package/libnvidia-container/libnvidia-container.hash
 create mode 100644 package/libnvidia-container/libnvidia-container.mk

Comments

Asaf Kahlon Aug. 8, 2020, 8:01 a.m. UTC | #1
Hello,

On Sun, Aug 2, 2020 at 12:37 AM Christian Stewart <christian@paral.in> wrote:
>
> The libnvidia-container package adds a library and CLI for GPU-backed
> containers, agnostic to container runtime.
>
> https://github.com/NVIDIA/libnvidia-container
>
> Signed-off-by: Christian Stewart <christian@paral.in>
> ---
>  package/Config.in                             |    1 +
>  ...d-fixes-from-vowstar-portage-overlay.patch | 1890 +++++++++++++++++
>  ...ve-dependency-handling-from-Makefile.patch |  698 ++++++
>  package/libnvidia-container/Config.in         |   18 +
>  .../libnvidia-container.hash                  |    3 +
>  .../libnvidia-container.mk                    |   57 +
>  6 files changed, 2667 insertions(+)
>  create mode 100644 package/libnvidia-container/0001-Build-fixes-from-vowstar-portage-overlay.patch
>  create mode 100644 package/libnvidia-container/0002-Remove-dependency-handling-from-Makefile.patch
>  create mode 100644 package/libnvidia-container/Config.in
>  create mode 100644 package/libnvidia-container/libnvidia-container.hash
>  create mode 100644 package/libnvidia-container/libnvidia-container.mk
>
> diff --git a/package/Config.in b/package/Config.in
> index dfa02217d9..dd1c6e1395 100644
> --- a/package/Config.in
> +++ b/package/Config.in
> @@ -1511,6 +1511,7 @@ menu "Hardware handling"
>         source "package/libllcp/Config.in"
>         source "package/libmbim/Config.in"
>         source "package/libnfc/Config.in"
> +       source "package/libnvidia-container/Config.in"
>         source "package/libpciaccess/Config.in"
>         source "package/libphidget/Config.in"
>         source "package/libpri/Config.in"
> diff --git a/package/libnvidia-container/0001-Build-fixes-from-vowstar-portage-overlay.patch b/package/libnvidia-container/0001-Build-fixes-from-vowstar-portage-overlay.patch
> new file mode 100644
> index 0000000000..7232e76d97
> --- /dev/null
> +++ b/package/libnvidia-container/0001-Build-fixes-from-vowstar-portage-overlay.patch
> @@ -0,0 +1,1890 @@
> +From bd633a208446c86e11097e3cd3b019a086738ae3 Mon Sep 17 00:00:00 2001
> +From: Christian Stewart <christian@paral.in>
> +Date: Sun, 19 Jul 2020 09:56:42 -0700
> +Subject: [PATCH] Build fixes from vowstar portage overlay
> +
> +This commit brings in build fixes written by @vowstar in the overlay:
> +
> +https://github.com/vowstar/vowstar-overlay/tree/master/app-emulation/libnvidia-container/files
> +
> +Signed-off-by: Christian Stewart <christian@paral.in>
> +---
> + Makefile                    |  30 +-
> + mk/Dockerfile.debian        |   1 -
> + mk/Dockerfile.ubuntu        |   1 -
> + mk/common.mk                |   2 +-
> + src/nvc.c                   |   4 +-
> + src/nvidia-modprobe-utils.c | 794 ++++++++++++++++++++++++++++++++++++
> + src/nvidia-modprobe-utils.h | 157 +++++++
> + src/pci-enum.h              | 112 +++++
> + src/pci-sysfs.c             | 529 ++++++++++++++++++++++++
> + src/pci-sysfs.h             |  85 ++++
> + 10 files changed, 1696 insertions(+), 19 deletions(-)
> + create mode 100644 src/nvidia-modprobe-utils.c
> + create mode 100644 src/nvidia-modprobe-utils.h
> + create mode 100644 src/pci-enum.h
> + create mode 100644 src/pci-sysfs.c
> + create mode 100644 src/pci-sysfs.h
> +
> +diff --git a/Makefile b/Makefile
> +index c07863b..f1c56a9 100644
> +--- a/Makefile
> ++++ b/Makefile
> +@@ -4,21 +4,21 @@
> +
> + .PHONY: all tools shared static deps install uninstall dist depsclean mostlyclean clean distclean
> + .DEFAULT_GOAL := all
> +-
> ++STRIP  := @echo skipping: strip
> + ##### Global variables #####
> +
> +-WITH_LIBELF  ?= no
> +-WITH_TIRPC   ?= no
> ++WITH_LIBELF  ?= yes
> ++WITH_TIRPC   ?= yes
> + WITH_SECCOMP ?= yes
> +
> + ##### Global definitions #####
> +
> +-export prefix      = /usr/local
> ++export prefix      = /usr
> + export exec_prefix = $(prefix)
> + export bindir      = $(exec_prefix)/bin
> +-export libdir      = $(exec_prefix)/lib
> ++export libdir      = $(exec_prefix)/lib64
> + export docdir      = $(prefix)/share/doc
> +-export libdbgdir   = $(prefix)/lib/debug$(libdir)
> ++export libdbgdir   = $(prefix)/lib64/debug$(libdir)
> + export includedir  = $(prefix)/include
> + export pkgconfdir  = $(libdir)/pkgconfig
> +
> +@@ -52,6 +52,8 @@ LIB_SRCS     := $(SRCS_DIR)/driver.c        \
> +                 $(SRCS_DIR)/error_generic.c \
> +                 $(SRCS_DIR)/error.c         \
> +                 $(SRCS_DIR)/ldcache.c       \
> ++                $(SRCS_DIR)/pci-sysfs.c     \
> ++                $(SRCS_DIR)/nvidia-modprobe-utils.c \
> +                 $(SRCS_DIR)/nvc.c           \
> +                 $(SRCS_DIR)/nvc_ldcache.c   \
> +                 $(SRCS_DIR)/nvc_info.c      \
> +@@ -121,8 +123,8 @@ LDLIBS   := $(LDLIBS)
> + LIB_CPPFLAGS       = -DNV_LINUX -isystem $(DEPS_DIR)$(includedir) -include $(BUILD_DEFS)
> + LIB_CFLAGS         = -fPIC
> + LIB_LDFLAGS        = -L$(DEPS_DIR)$(libdir) -shared -Wl,-soname=$(LIB_SONAME)
> +-LIB_LDLIBS_STATIC  = -l:libnvidia-modprobe-utils.a
> +-LIB_LDLIBS_SHARED  = -ldl -lcap
> ++# LIB_LDLIBS_STATIC  = -l:libnvidia-modprobe-utils.a
> ++LIB_LDLIBS_SHARED  = -ldl -lcap -ltirpc
> + ifeq ($(WITH_LIBELF), yes)
> + LIB_CPPFLAGS       += -DWITH_LIBELF
> + LIB_LDLIBS_SHARED  += -lelf
> +@@ -131,7 +133,7 @@ LIB_LDLIBS_STATIC  += -l:libelf.a
> + endif
> + ifeq ($(WITH_TIRPC), yes)
> + LIB_CPPFLAGS       += -isystem $(DEPS_DIR)$(includedir)/tirpc -DWITH_TIRPC
> +-LIB_LDLIBS_STATIC  += -l:libtirpc.a
> ++# LIB_LDLIBS_STATIC  += -l:libtirpc.a
> + LIB_LDLIBS_SHARED  += -lpthread
> + endif
> + ifeq ($(WITH_SECCOMP), yes)
> +@@ -146,7 +148,7 @@ LIB_LDLIBS_SHARED  += $(LDLIBS)
> + LIB_LDLIBS         = $(LIB_LDLIBS_STATIC) $(LIB_LDLIBS_SHARED)
> +
> + # Binary flags (recursively expanded to handle target-specific flags)
> +-BIN_CPPFLAGS       = -include $(BUILD_DEFS) $(CPPFLAGS)
> ++BIN_CPPFLAGS       = -include $(BUILD_DEFS) $(CPPFLAGS) -DWITH_TIRPC
> + BIN_CFLAGS         = -I$(SRCS_DIR) -fPIE -flto $(CFLAGS)
> + BIN_LDFLAGS        = -L. -pie $(LDFLAGS) -Wl,-rpath='$$ORIGIN/../$$LIB'
> + BIN_LDLIBS         = -l:$(LIB_SHARED) -lcap $(LDLIBS)
> +@@ -220,12 +222,12 @@ static: $(LIB_STATIC)($(LIB_STATIC_OBJ))
> + deps: export DESTDIR:=$(DEPS_DIR)
> + deps: $(LIB_RPC_SRCS) $(BUILD_DEFS)
> +       $(MKDIR) -p $(DEPS_DIR)
> +-      $(MAKE) -f $(MAKE_DIR)/nvidia-modprobe.mk install
> ++      # $(MAKE) -f $(MAKE_DIR)/nvidia-modprobe.mk install
> + ifeq ($(WITH_LIBELF), no)
> +-      $(MAKE) -f $(MAKE_DIR)/elftoolchain.mk install
> ++      # $(MAKE) -f $(MAKE_DIR)/elftoolchain.mk install
> + endif
> + ifeq ($(WITH_TIRPC), yes)
> +-      $(MAKE) -f $(MAKE_DIR)/libtirpc.mk install
> ++      # $(MAKE) -f $(MAKE_DIR)/libtirpc.mk install
> + endif
> +
> + install: all
> +@@ -238,7 +240,7 @@ install: all
> +       $(LN) -sf $(LIB_SONAME) $(DESTDIR)$(libdir)/$(LIB_SYMLINK)
> +       $(LDCONFIG) -n $(DESTDIR)$(libdir)
> +       # Install debugging symbols
> +-      $(INSTALL) -m 644 $(DEBUG_DIR)/$(LIB_SONAME) $(DESTDIR)$(libdbgdir)
> ++      # $(INSTALL) -m 644 $(DEBUG_DIR)/$(LIB_SONAME) $(DESTDIR)$(libdbgdir)
> +       # Install configuration files
> +       $(MAKE_DIR)/$(LIB_PKGCFG).in "$(strip $(VERSION))" "$(strip $(LIB_LDLIBS_SHARED))" > $(DESTDIR)$(pkgconfdir)/$(LIB_PKGCFG)
> +       # Install binary files
> +diff --git a/mk/Dockerfile.debian b/mk/Dockerfile.debian
> +index 8e8a560..096e1d0 100644
> +--- a/mk/Dockerfile.debian
> ++++ b/mk/Dockerfile.debian
> +@@ -41,7 +41,6 @@ RUN make distclean && make -j"$(nproc)"
> + ENV DIST_DIR /dist
> + VOLUME $DIST_DIR
> + CMD bash -c " \
> +-        export DISTRIB=$(lsb_release -c -s); \
> +         export SECTION="" \
> +         make dist; \
> +         make deb; \
> +diff --git a/mk/Dockerfile.ubuntu b/mk/Dockerfile.ubuntu
> +index 8b8cea8..d1f64d0 100644
> +--- a/mk/Dockerfile.ubuntu
> ++++ b/mk/Dockerfile.ubuntu
> +@@ -40,7 +40,6 @@ RUN make distclean && make -j"$(nproc)"
> + ENV DIST_DIR /dist
> + VOLUME $DIST_DIR
> + CMD bash -c " \
> +-        export DISTRIB=$(lsb_release -c -s); \
> +         export SECTION="" \
> +         make dist; \
> +         make deb; \
> +diff --git a/mk/common.mk b/mk/common.mk
> +index 73bc27a..cdd93c9 100644
> +--- a/mk/common.mk
> ++++ b/mk/common.mk
> +@@ -21,7 +21,7 @@ DOCKER   ?= docker
> + UID      := $(shell id -u)
> + GID      := $(shell id -g)
> + DATE     := $(shell date -u --iso-8601=minutes)
> +-REVISION := $(shell git rev-parse HEAD)
> ++REVISION := 61f82bf25f0b3afaa75c6df8a0a6551ecfdf81f4
> + COMPILER := $(realpath $(shell which $(CC)))
> + PLATFORM ?= $(shell uname -m)
> +
> +diff --git a/src/nvc.c b/src/nvc.c
> +index 35ad5be..f1d9b62 100644
> +--- a/src/nvc.c
> ++++ b/src/nvc.c
> +@@ -14,8 +14,8 @@
> + #include <stdlib.h>
> + #include <unistd.h>
> +
> +-#include <pci-enum.h>
> +-#include <nvidia-modprobe-utils.h>
> ++#include "pci-enum.h"
> ++#include "nvidia-modprobe-utils.h"
> +
> + #include "nvc_internal.h"
> +
> +diff --git a/src/nvidia-modprobe-utils.c b/src/nvidia-modprobe-utils.c
> +new file mode 100644
> +index 0000000..d3f3233
> +--- /dev/null
> ++++ b/src/nvidia-modprobe-utils.c
> +@@ -0,0 +1,794 @@
> ++
> ++#if defined(NV_LINUX)
> ++
> ++#include <stdio.h>
> ++#include <stdlib.h>
> ++#include <string.h>
> ++#include <sys/types.h>
> ++#include <sys/stat.h>
> ++#include <unistd.h>
> ++#include <errno.h>
> ++#include <sys/types.h>
> ++#include <sys/wait.h>
> ++#include <fcntl.h>
> ++
> ++#include "nvidia-modprobe-utils.h"
> ++#include "pci-enum.h"
> ++
> ++#define NV_PROC_MODPROBE_PATH "/proc/sys/kernel/modprobe"
> ++#define NV_PROC_MODULES_PATH "/proc/modules"
> ++#define NV_PROC_DEVICES_PATH "/proc/devices"
> ++
> ++#define NV_PROC_MODPROBE_PATH_MAX        1024
> ++#define NV_MAX_MODULE_NAME_SIZE          16
> ++#define NV_MAX_PROC_REGISTRY_PATH_SIZE   NV_MAX_CHARACTER_DEVICE_FILE_STRLEN
> ++#define NV_MAX_LINE_LENGTH               256
> ++
> ++#define NV_NVIDIA_MODULE_NAME "nvidia"
> ++#define NV_PROC_REGISTRY_PATH "/proc/driver/nvidia/params"
> ++
> ++#define NV_NMODULE_NVIDIA_MODULE_NAME "nvidia%d"
> ++#define NV_NMODULE_PROC_REGISTRY_PATH "/proc/driver/nvidia/%d/params"
> ++
> ++#define NV_UVM_MODULE_NAME "nvidia-uvm"
> ++#define NV_UVM_DEVICE_NAME "/dev/nvidia-uvm"
> ++#define NV_UVM_TOOLS_DEVICE_NAME "/dev/nvidia-uvm-tools"
> ++
> ++#define NV_MODESET_MODULE_NAME "nvidia-modeset"
> ++
> ++#define NV_VGPU_VFIO_MODULE_NAME "nvidia-vgpu-vfio"
> ++
> ++#define NV_NVLINK_MODULE_NAME "nvidia-nvlink"
> ++#define NV_NVLINK_PROC_PERM_PATH "/proc/driver/nvidia-nvlink/permissions"
> ++
> ++#define NV_DEVICE_FILE_MODE_MASK (S_IRWXU|S_IRWXG|S_IRWXO)
> ++#define NV_DEVICE_FILE_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
> ++#define NV_DEVICE_FILE_UID 0
> ++#define NV_DEVICE_FILE_GID 0
> ++
> ++#define NV_MAKE_DEVICE(x,y) ((dev_t)((x) << 8 | (y)))
> ++
> ++#define NV_MAJOR_DEVICE_NUMBER 195
> ++
> ++#define NV_PCI_VENDOR_ID    0x10DE
> ++
> ++#define NV_MIN(a, b) (((a) < (b)) ? (a) : (b))
> ++
> ++/*
> ++ * Construct the nvidia kernel module name based on the input
> ++ * module instance provided.  If an error occurs, the null
> ++ * terminator will be written to nv_module_name[0].
> ++ */
> ++static __inline__ void assign_nvidia_kernel_module_name
> ++(
> ++    char nv_module_name[NV_MAX_MODULE_NAME_SIZE],
> ++    int module_instance
> ++)
> ++{
> ++    int ret;
> ++
> ++    if (is_multi_module(module_instance))
> ++    {
> ++        ret = snprintf(nv_module_name, NV_MAX_MODULE_NAME_SIZE,
> ++                       NV_NMODULE_NVIDIA_MODULE_NAME, module_instance);
> ++    }
> ++    else
> ++    {
> ++        ret = snprintf(nv_module_name, NV_MAX_MODULE_NAME_SIZE,
> ++                       NV_NVIDIA_MODULE_NAME);
> ++    }
> ++
> ++    if (ret <= 0)
> ++    {
> ++        goto fail;
> ++    }
> ++
> ++    nv_module_name[NV_MAX_MODULE_NAME_SIZE - 1] = '\0';
> ++
> ++    return;
> ++
> ++fail:
> ++
> ++    nv_module_name[0] = '\0';
> ++}
> ++
> ++
> ++/*
> ++ * Construct the proc registry path name based on the input
> ++ * module instance provided.  If an error occurs, the null
> ++ * terminator will be written to proc_path[0].
> ++ */
> ++static __inline__ void assign_proc_registry_path
> ++(
> ++    char proc_path[NV_MAX_PROC_REGISTRY_PATH_SIZE],
> ++    int module_instance
> ++)
> ++{
> ++    int ret;
> ++
> ++    if (is_multi_module(module_instance))
> ++    {
> ++        ret = snprintf(proc_path, NV_MAX_PROC_REGISTRY_PATH_SIZE,
> ++                       NV_NMODULE_PROC_REGISTRY_PATH, module_instance);
> ++    }
> ++    else
> ++    {
> ++        ret = snprintf(proc_path, NV_MAX_PROC_REGISTRY_PATH_SIZE,
> ++                       NV_PROC_REGISTRY_PATH);
> ++    }
> ++
> ++    if (ret <= 0)
> ++    {
> ++        goto fail;
> ++    }
> ++
> ++    proc_path[NV_MAX_PROC_REGISTRY_PATH_SIZE - 1] = '\0';
> ++
> ++    return;
> ++
> ++fail:
> ++
> ++    proc_path[0] = '\0';
> ++}
> ++
> ++
> ++/*
> ++ * Just like strcmp(3), except that differences between '-' and '_' are
> ++ * ignored. This is useful for comparing module names, where '-' and '_'
> ++ * are supposed to be treated interchangeably.
> ++ */
> ++static int modcmp(const char *a, const char *b)
> ++{
> ++    int i;
> ++
> ++    /* Walk both strings and compare each character */
> ++    for (i = 0; a[i] && b[i]; i++)
> ++    {
> ++        if (a[i] != b[i])
> ++        {
> ++            /* ignore differences between '-' and '_' */
> ++            if (((a[i] == '-') || (a[i] == '_')) &&
> ++                ((b[i] == '-') || (b[i] == '_')))
> ++            {
> ++                continue;
> ++            }
> ++
> ++            break;
> ++        }
> ++    }
> ++
> ++    /*
> ++     * If the strings are of unequal length, only one of a[i] or b[i] == '\0'.
> ++     * If they are the same length, both will be '\0', and the strings match.
> ++     */
> ++    return a[i] - b[i];
> ++}
> ++
> ++
> ++/*
> ++ * Check whether the specified module is loaded by reading
> ++ * NV_PROC_MODULES_PATH; returns 1 if the kernel module is loaded.
> ++ * Otherwise, it returns 0.
> ++ */
> ++static int is_kernel_module_loaded(const char *nv_module_name)
> ++{
> ++    FILE *fp;
> ++    char module_name[NV_MAX_MODULE_NAME_SIZE];
> ++    int module_loaded = 0;
> ++
> ++    fp = fopen(NV_PROC_MODULES_PATH, "r");
> ++
> ++    if (fp == NULL)
> ++    {
> ++        return 0;
> ++    }
> ++
> ++    while (fscanf(fp, "%15s%*[^\n]\n", module_name) == 1)
> ++    {
> ++        module_name[15] = '\0';
> ++        if (modcmp(module_name, nv_module_name) == 0)
> ++        {
> ++            module_loaded = 1;
> ++            break;
> ++        }
> ++    }
> ++
> ++    fclose(fp);
> ++
> ++    return module_loaded;
> ++}
> ++
> ++/*
> ++ * Attempt to redirect STDOUT and STDERR to /dev/null.
> ++ *
> ++ * This is only for the cosmetics of silencing warnings, so do not
> ++ * treat any errors here as fatal.
> ++ */
> ++static void silence_current_process(void)
> ++{
> ++    int dev_null_fd = open("/dev/null", O_RDWR);
> ++    if (dev_null_fd < 0)
> ++    {
> ++        return;
> ++    }
> ++
> ++    dup2(dev_null_fd, STDOUT_FILENO);
> ++    dup2(dev_null_fd, STDERR_FILENO);
> ++    close(dev_null_fd);
> ++}
> ++
> ++/*
> ++ * Attempt to load a kernel module; returns 1 if kernel module is
> ++ * successfully loaded.  Returns 0 if the kernel module could not be
> ++ * loaded.
> ++ *
> ++ * If any error is encountered and print_errors is non-0, then print the
> ++ * error to stderr.
> ++ */
> ++static int modprobe_helper(const int print_errors, const char *module_name)
> ++{
> ++    char modprobe_path[NV_PROC_MODPROBE_PATH_MAX];
> ++    int status = 1;
> ++    struct stat file_status;
> ++    pid_t pid;
> ++    const char *envp[] = { "PATH=/sbin", NULL };
> ++    FILE *fp;
> ++
> ++    /*
> ++     * Use PCI_BASE_CLASS_MASK to cover both types of DISPLAY controllers that
> ++     * NVIDIA ships (VGA = 0x300 and 3D = 0x302).
> ++     */
> ++    struct pci_id_match id_match = {
> ++        NV_PCI_VENDOR_ID,       /* Vendor ID    = 0x10DE                 */
> ++        PCI_MATCH_ANY,          /* Device ID    = any                    */
> ++        PCI_MATCH_ANY,          /* Subvendor ID = any                    */
> ++        PCI_MATCH_ANY,          /* Subdevice ID = any                    */
> ++        0x0300,                 /* Device Class = PCI_BASE_CLASS_DISPLAY */
> ++        PCI_BASE_CLASS_MASK,    /* Display Mask = base class only        */
> ++        0                       /* Initial number of matches             */
> ++    };
> ++
> ++    modprobe_path[0] = '\0';
> ++
> ++    if (module_name == NULL || module_name[0] == '\0') {
> ++        return 0;
> ++    }
> ++
> ++    /* If the kernel module is already loaded, nothing more to do: success. */
> ++
> ++    if (is_kernel_module_loaded(module_name))
> ++    {
> ++        return 1;
> ++    }
> ++
> ++    /*
> ++     * Before attempting to load the module, look for any NVIDIA PCI devices.
> ++     * If none exist, exit instead of attempting the modprobe, because doing so
> ++     * would issue error messages that are really irrelevant if there are no
> ++     * NVIDIA PCI devices present.
> ++     *
> ++     * If our check fails, for whatever reason, continue with the modprobe just
> ++     * in case.
> ++     */
> ++    status = pci_enum_match_id(&id_match);
> ++    if (status == 0 && id_match.num_matches == 0)
> ++    {
> ++        if (print_errors)
> ++        {
> ++            fprintf(stderr,
> ++                    "NVIDIA: no NVIDIA devices found\n");
> ++        }
> ++
> ++        return 0;
> ++    }
> ++
> ++    /* Only attempt to load the kernel module if root. */
> ++
> ++    if (geteuid() != 0)
> ++    {
> ++        return 0;
> ++    }
> ++
> ++    /* Attempt to read the full path to the modprobe executable from /proc. */
> ++
> ++    fp = fopen(NV_PROC_MODPROBE_PATH, "r");
> ++    if (fp != NULL)
> ++    {
> ++        char *str;
> ++        size_t n;
> ++
> ++        n = fread(modprobe_path, 1, sizeof(modprobe_path), fp);
> ++
> ++        /*
> ++         * Null terminate the string, but make sure 'n' is in the range
> ++         * [0, sizeof(modprobe_path)-1].
> ++         */
> ++        n = NV_MIN(n, sizeof(modprobe_path) - 1);
> ++        modprobe_path[n] = '\0';
> ++
> ++        /*
> ++         * If str was longer than a line, we might still have a
> ++         * newline in modprobe_path: if so, overwrite it with the nul
> ++         * terminator.
> ++         */
> ++        str = strchr(modprobe_path, '\n');
> ++        if (str != NULL)
> ++        {
> ++            *str = '\0';
> ++        }
> ++
> ++        fclose(fp);
> ++    }
> ++
> ++    /* If we couldn't read it from /proc, pick a reasonable default. */
> ++
> ++    if (modprobe_path[0] == '\0')
> ++    {
> ++        sprintf(modprobe_path, "/sbin/modprobe");
> ++    }
> ++
> ++    /* Do not attempt to exec(3) modprobe if it does not exist. */
> ++
> ++    if (stat(modprobe_path, &file_status) != 0 ||
> ++        !S_ISREG(file_status.st_mode) ||
> ++        (file_status.st_mode & S_IXUSR) != S_IXUSR)
> ++    {
> ++        return 0;
> ++    }
> ++
> ++    /* Fork and exec modprobe from the child process. */
> ++
> ++    switch (pid = fork())
> ++    {
> ++        case 0:
> ++
> ++            /*
> ++             * modprobe might complain in expected scenarios.  E.g.,
> ++             * `modprobe nvidia` on a Tegra system with dGPU where no nvidia.ko is
> ++             * present will complain:
> ++             *
> ++             *  "modprobe: FATAL: Module nvidia not found."
> ++             *
> ++             * Silence the current process to avoid such unwanted messages.
> ++             */
> ++            silence_current_process();
> ++
> ++            execle(modprobe_path, "modprobe",
> ++                   module_name, NULL, envp);
> ++
> ++            /* If execl(3) returned, then an error has occurred. */
> ++
> ++            if (print_errors)
> ++            {
> ++                fprintf(stderr,
> ++                        "NVIDIA: failed to execute `%s`: %s.\n",
> ++                        modprobe_path, strerror(errno));
> ++            }
> ++            exit(1);
> ++
> ++        case -1:
> ++            return 0;
> ++
> ++        default:
> ++            if (waitpid(pid, &status, 0) < 0)
> ++            {
> ++                return 0;
> ++            }
> ++            if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
> ++            {
> ++                return 1;
> ++            }
> ++            else
> ++            {
> ++                return 0;
> ++            }
> ++    }
> ++
> ++    return 1;
> ++}
> ++
> ++
> ++/*
> ++ * Attempt to load an NVIDIA kernel module
> ++ */
> ++int nvidia_modprobe(const int print_errors, int module_instance)
> ++{
> ++    char nv_module_name[NV_MAX_MODULE_NAME_SIZE];
> ++
> ++    assign_nvidia_kernel_module_name(nv_module_name, module_instance);
> ++
> ++    return modprobe_helper(print_errors, nv_module_name);
> ++}
> ++
> ++
> ++/*
> ++ * Determine the requested device file parameters: allow users to
> ++ * override the default UID/GID and/or mode of the NVIDIA device
> ++ * files, or even whether device file modification should be allowed;
> ++ * the attributes are managed globally, and can be adjusted via the
> ++ * appropriate kernel module parameters.
> ++ */
> ++static void init_device_file_parameters(uid_t *uid, gid_t *gid, mode_t *mode,
> ++                                        int *modify, const char *proc_path)
> ++{
> ++    FILE *fp;
> ++    char name[32];
> ++    unsigned int value;
> ++
> ++    *mode = NV_DEVICE_FILE_MODE;
> ++    *uid = NV_DEVICE_FILE_UID;
> ++    *gid = NV_DEVICE_FILE_GID;
> ++    *modify = 1;
> ++
> ++    if (proc_path == NULL || proc_path[0] == '\0')
> ++    {
> ++        return;
> ++    }
> ++
> ++    fp = fopen(proc_path, "r");
> ++
> ++    if (fp == NULL)
> ++    {
> ++        return;
> ++    }
> ++
> ++    while (fscanf(fp, "%31[^:]: %u\n", name, &value) == 2)
> ++    {
> ++        name[31] = '\0';
> ++        if (strcmp(name, "DeviceFileUID") == 0)
> ++        {
> ++            *uid = value;
> ++        }
> ++        if (strcmp(name, "DeviceFileGID") == 0)
> ++        {
> ++            *gid = value;
> ++        }
> ++        if (strcmp(name, "DeviceFileMode") == 0)
> ++        {
> ++            *mode = value;
> ++        }
> ++        if (strcmp(name, "ModifyDeviceFiles") == 0)
> ++        {
> ++            *modify = value;
> ++        }
> ++    }
> ++
> ++    fclose(fp);
> ++}
> ++
> ++/*
> ++ * A helper to query device file states.
> ++ */
> ++static int get_file_state_helper(
> ++    const char *path,
> ++    int major,
> ++    int minor,
> ++    const char *proc_path,
> ++    uid_t uid,
> ++    gid_t gid,
> ++    mode_t mode)
> ++{
> ++    dev_t dev = NV_MAKE_DEVICE(major, minor);
> ++    struct stat stat_buf;
> ++    int ret;
> ++    int state = 0;
> ++
> ++    ret = stat(path, &stat_buf);
> ++    if (ret == 0)
> ++    {
> ++        nvidia_update_file_state(&state, NvDeviceFileStateFileExists);
> ++
> ++        if (S_ISCHR(stat_buf.st_mode) && (stat_buf.st_rdev == dev))
> ++        {
> ++            nvidia_update_file_state(&state, NvDeviceFileStateChrDevOk);
> ++        }
> ++
> ++        if (((stat_buf.st_mode & NV_DEVICE_FILE_MODE_MASK) == mode) &&
> ++            (stat_buf.st_uid == uid) &&
> ++            (stat_buf.st_gid == gid))
> ++        {
> ++            nvidia_update_file_state(&state, NvDeviceFileStatePermissionsOk);
> ++        }
> ++    }
> ++
> ++    return state;
> ++}
> ++
> ++int nvidia_get_file_state(int minor, int module_instance)
> ++{
> ++    char path[NV_MAX_CHARACTER_DEVICE_FILE_STRLEN];
> ++    char proc_path[NV_MAX_PROC_REGISTRY_PATH_SIZE];
> ++    mode_t mode;
> ++    uid_t uid;
> ++    gid_t gid;
> ++    int modification_allowed;
> ++    int state = 0;
> ++
> ++    assign_device_file_name(path, minor, module_instance);
> ++    assign_proc_registry_path(proc_path, module_instance);
> ++
> ++    init_device_file_parameters(&uid, &gid, &mode, &modification_allowed,
> ++                                proc_path);
> ++
> ++    state = get_file_state_helper(path, NV_MAJOR_DEVICE_NUMBER, minor,
> ++                                  proc_path, uid, gid, mode);
> ++
> ++    return state;
> ++}
> ++
> ++/*
> ++ * Attempt to create the specified device file with the specified major
> ++ * and minor number.  If proc_path is specified, scan it for custom file
> ++ * permissions.  Returns 1 if the file is successfully created; returns 0
> ++ * if the file could not be created.
> ++ */
> ++int mknod_helper(int major, int minor, const char *path,
> ++                 const char *proc_path)
> ++{
> ++    dev_t dev = NV_MAKE_DEVICE(major, minor);
> ++    mode_t mode;
> ++    uid_t uid;
> ++    gid_t gid;
> ++    int modification_allowed;
> ++    int ret;
> ++    int state;
> ++    int do_mknod;
> ++
> ++    if (path == NULL || path[0] == '\0')
> ++    {
> ++        return 0;
> ++    }
> ++
> ++    init_device_file_parameters(&uid, &gid, &mode, &modification_allowed,
> ++                                proc_path);
> ++
> ++    /* If device file modification is not allowed, nothing to do: success. */
> ++
> ++    if (modification_allowed != 1)
> ++    {
> ++        return 1;
> ++    }
> ++
> ++    state = get_file_state_helper(path, major, minor,
> ++                                  proc_path, uid, gid, mode);
> ++
> ++    if (nvidia_test_file_state(state, NvDeviceFileStateFileExists) &&
> ++        nvidia_test_file_state(state, NvDeviceFileStateChrDevOk) &&
> ++        nvidia_test_file_state(state, NvDeviceFileStatePermissionsOk))
> ++    {
> ++        return 1;
> ++    }
> ++
> ++    /* If the stat(2) above failed, we need to create the device file. */
> ++
> ++    do_mknod = 0;
> ++
> ++    if (!nvidia_test_file_state(state, NvDeviceFileStateFileExists))
> ++    {
> ++        do_mknod = 1;
> ++    }
> ++
> ++    /*
> ++     * If the file exists but the file is either not a character device or has
> ++     * the wrong major/minor character device number, then we need to delete it
> ++     * and recreate it.
> ++     */
> ++    if (!do_mknod &&
> ++        !nvidia_test_file_state(state, NvDeviceFileStateChrDevOk))
> ++    {
> ++        ret = remove(path);
> ++        if (ret != 0)
> ++        {
> ++            return 0;
> ++        }
> ++        do_mknod = 1;
> ++    }
> ++
> ++    if (do_mknod)
> ++    {
> ++        ret = mknod(path, S_IFCHR | mode, dev);
> ++        if (ret != 0)
> ++        {
> ++            return 0;
> ++        }
> ++    }
> ++
> ++    /*
> ++     * Make sure the permissions and ownership are set correctly; if
> ++     * we created the device above and either of the below fails, then
> ++     * also delete the device file.
> ++     */
> ++    if ((chmod(path, mode) != 0) ||
> ++        (chown(path, uid, gid) != 0))
> ++    {
> ++        if (do_mknod)
> ++        {
> ++            remove(path);
> ++        }
> ++        return 0;
> ++    }
> ++
> ++    return 1;
> ++}
> ++
> ++
> ++/*
> ++ * Attempt to create a device file with the specified minor number for
> ++ * the specified NVIDIA module instance.
> ++ */
> ++int nvidia_mknod(int minor, int module_instance)
> ++{
> ++    char path[NV_MAX_CHARACTER_DEVICE_FILE_STRLEN];
> ++    char proc_path[NV_MAX_PROC_REGISTRY_PATH_SIZE];
> ++
> ++    assign_device_file_name(path, minor, module_instance);
> ++    assign_proc_registry_path(proc_path, module_instance);
> ++
> ++    return mknod_helper(NV_MAJOR_DEVICE_NUMBER, minor, path, proc_path);
> ++}
> ++
> ++
> ++/*
> ++ * Scan NV_PROC_DEVICES_PATH to find the major number of the character
> ++ * device with the specified name.  Returns the major number on success,
> ++ * or -1 on failure.
> ++ */
> ++int get_chardev_major(const char *name)
> ++{
> ++    int ret = -1;
> ++    char line[NV_MAX_LINE_LENGTH];
> ++    FILE *fp;
> ++
> ++    line[NV_MAX_LINE_LENGTH - 1] = '\0';
> ++
> ++    fp = fopen(NV_PROC_DEVICES_PATH, "r");
> ++    if (!fp)
> ++    {
> ++        goto done;
> ++    }
> ++
> ++    /* Find the beginning of the 'Character devices:' section */
> ++
> ++    while (fgets(line, NV_MAX_LINE_LENGTH - 1, fp))
> ++    {
> ++        if (strcmp(line, "Character devices:\n") == 0)
> ++        {
> ++            break;
> ++        }
> ++    }
> ++
> ++    if (ferror(fp)) {
> ++        goto done;
> ++    }
> ++
> ++    /* Search for the given module name */
> ++
> ++    while (fgets(line, NV_MAX_LINE_LENGTH - 1, fp))
> ++    {
> ++        char *found;
> ++
> ++        if (strcmp(line, "\n") == 0 )
> ++        {
> ++            /* we've reached the end of the 'Character devices:' section */
> ++            break;
> ++        }
> ++
> ++        found = strstr(line, name);
> ++
> ++        /* Check for a newline to avoid partial matches */
> ++
> ++        if (found && found[strlen(name)] == '\n')
> ++        {
> ++            int major;
> ++
> ++            /* Read the device major number */
> ++
> ++            if (sscanf(line, " %d %*s", &major) == 1)
> ++            {
> ++                ret = major;
> ++            }
> ++
> ++            break;
> ++        }
> ++    }
> ++
> ++done:
> ++
> ++    if (fp)
> ++    {
> ++        fclose(fp);
> ++    }
> ++
> ++    return ret;
> ++}
> ++
> ++
> ++/*
> ++ * Attempt to create the NVIDIA Unified Memory device file
> ++ */
> ++int nvidia_uvm_mknod(int base_minor)
> ++{
> ++    int major = get_chardev_major(NV_UVM_MODULE_NAME);
> ++
> ++    if (major < 0)
> ++    {
> ++        return 0;
> ++    }
> ++
> ++    return mknod_helper(major, base_minor, NV_UVM_DEVICE_NAME, NULL) &&
> ++           mknod_helper(major, base_minor + 1, NV_UVM_TOOLS_DEVICE_NAME, NULL);
> ++}
> ++
> ++
> ++/*
> ++ * Attempt to load the NVIDIA Unified Memory kernel module
> ++ */
> ++int nvidia_uvm_modprobe(void)
> ++{
> ++    return modprobe_helper(0, NV_UVM_MODULE_NAME);
> ++}
> ++
> ++
> ++/*
> ++ * Attempt to load the NVIDIA modeset driver.
> ++ */
> ++int nvidia_modeset_modprobe(void)
> ++{
> ++    return modprobe_helper(0, NV_MODESET_MODULE_NAME);
> ++}
> ++
> ++
> ++/*
> ++ * Attempt to create the NVIDIA modeset driver device file.
> ++ */
> ++int nvidia_modeset_mknod(void)
> ++{
> ++    char proc_path[NV_MAX_PROC_REGISTRY_PATH_SIZE];
> ++
> ++    assign_proc_registry_path(proc_path, NV_MODULE_INSTANCE_NONE);
> ++
> ++    return mknod_helper(NV_MAJOR_DEVICE_NUMBER,
> ++                        NV_MODESET_MINOR_DEVICE_NUM,
> ++                        NV_MODESET_DEVICE_NAME, proc_path);
> ++}
> ++
> ++/*
> ++ * Attempt to create the NVIDIA NVLink driver device file.
> ++ */
> ++int nvidia_nvlink_mknod(void)
> ++{
> ++    int major = get_chardev_major(NV_NVLINK_MODULE_NAME);
> ++
> ++    if (major < 0)
> ++    {
> ++        return 0;
> ++    }
> ++
> ++    return mknod_helper(major,
> ++                        0,
> ++                        NV_NVLINK_DEVICE_NAME,
> ++                        NV_NVLINK_PROC_PERM_PATH);
> ++}
> ++
> ++int nvidia_vgpu_vfio_mknod(int minor_num)
> ++{
> ++    int major = get_chardev_major(NV_VGPU_VFIO_MODULE_NAME);
> ++    char vgpu_dev_name[NV_MAX_CHARACTER_DEVICE_FILE_STRLEN];
> ++    char proc_path[NV_MAX_PROC_REGISTRY_PATH_SIZE];
> ++
> ++    if (major < 0)
> ++    {
> ++        return 0;
> ++    }
> ++
> ++    assign_proc_registry_path(proc_path, NV_MODULE_INSTANCE_NONE);
> ++
> ++    snprintf(vgpu_dev_name, NV_MAX_CHARACTER_DEVICE_FILE_STRLEN,
> ++             NV_VGPU_VFIO_DEVICE_NAME, minor_num);
> ++
> ++    vgpu_dev_name[NV_MAX_CHARACTER_DEVICE_FILE_STRLEN - 1] = '\0';
> ++
> ++    return mknod_helper(major, minor_num, vgpu_dev_name, proc_path);
> ++}
> ++
> ++#endif /* NV_LINUX */
> +\ No newline at end of file
> +diff --git a/src/nvidia-modprobe-utils.h b/src/nvidia-modprobe-utils.h
> +new file mode 100644
> +index 0000000..e06b4a4
> +--- /dev/null
> ++++ b/src/nvidia-modprobe-utils.h
> +@@ -0,0 +1,157 @@
> ++/*
> ++ * Copyright (c) 2013, NVIDIA CORPORATION.
> ++ *
> ++ * Permission is hereby granted, free of charge, to any person
> ++ * obtaining a copy of this software and associated documentation
> ++ * files (the "Software"), to deal in the Software without
> ++ * restriction, including without limitation the rights to use, copy,
> ++ * modify, merge, publish, distribute, sublicense, and/or sell copies
> ++ * of the Software, and to permit persons to whom the Software is
> ++ * furnished to do so, subject to the following conditions:
> ++ *
> ++ * The above copyright notice and this permission notice shall be
> ++ * included in all copies or substantial portions of the Software.
> ++ *
> ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> ++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> ++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
> ++ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
> ++ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
> ++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> ++ * SOFTWARE.
> ++ *
> ++ * This file provides utility functions on Linux for loading the
> ++ * NVIDIA kernel module and creating NVIDIA device files.
> ++ */
> ++
> ++#ifndef __NVIDIA_MODPROBE_UTILS_H__
> ++#define __NVIDIA_MODPROBE_UTILS_H__
> ++
> ++#include <stdio.h>
> ++
> ++#define NV_MAX_CHARACTER_DEVICE_FILE_STRLEN  128
> ++#define NV_MODULE_INSTANCE_NONE              -1
> ++#define NV_MODULE_INSTANCE_ZERO              0
> ++#define NV_MAX_MODULE_INSTANCES              8
> ++#define NV_CTL_DEVICE_NUM                    255
> ++#define NV_MODESET_MINOR_DEVICE_NUM          254
> ++
> ++#define NV_FRONTEND_CONTROL_DEVICE_MINOR_MAX NV_CTL_DEVICE_NUM
> ++
> ++#define NV_DEVICE_FILE_PATH "/dev/nvidia%d"
> ++#define NV_CTRL_DEVICE_FILE_PATH "/dev/nvidiactl"
> ++#define NV_MODESET_DEVICE_NAME "/dev/nvidia-modeset"
> ++#define NV_VGPU_VFIO_DEVICE_NAME "/dev/nvidia-vgpu%d"
> ++#define NV_NVLINK_DEVICE_NAME "/dev/nvidia-nvlink"
> ++
> ++#define NV_NMODULE_CTRL_DEVICE_FILE_PATH "/dev/nvidiactl%d"
> ++
> ++#define NV_FRONTEND_CONTROL_DEVICE_MINOR_MIN \
> ++    (NV_FRONTEND_CONTROL_DEVICE_MINOR_MAX - \
> ++     NV_MAX_MODULE_INSTANCES)
> ++
> ++#define NV_FRONTEND_IS_CONTROL_DEVICE(x) \
> ++    ((x <= NV_FRONTEND_CONTROL_DEVICE_MINOR_MAX) && \
> ++     (x > NV_FRONTEND_CONTROL_DEVICE_MINOR_MIN))
> ++
> ++#if defined(NV_LINUX)
> ++
> ++typedef enum
> ++{
> ++    NvDeviceFileStateFileExists = 0,
> ++    NvDeviceFileStateChrDevOk,
> ++    NvDeviceFileStatePermissionsOk
> ++} NvDeviceFileState;
> ++
> ++static __inline__ void nvidia_update_file_state(int *state,
> ++                                                NvDeviceFileState value)
> ++{
> ++    *state |= (1 << value);
> ++}
> ++
> ++static __inline__ int nvidia_test_file_state(int state,
> ++                                             NvDeviceFileState value)
> ++{
> ++    return !!(state & (1 << value));
> ++}
> ++
> ++int nvidia_get_file_state(int minor, int module_instance);
> ++int nvidia_modprobe(const int print_errors, int module_instance);
> ++int nvidia_mknod(int minor, int module_instance);
> ++int nvidia_uvm_modprobe(void);
> ++int nvidia_uvm_mknod(int base_minor);
> ++int nvidia_modeset_modprobe(void);
> ++int nvidia_modeset_mknod(void);
> ++int nvidia_vgpu_vfio_mknod(int minor_num);
> ++int nvidia_nvlink_mknod(void);
> ++
> ++int mknod_helper(int major, int minor, const char *path, const char *proc_path);
> ++int get_chardev_major(const char *name);
> ++
> ++#endif /* NV_LINUX */
> ++
> ++/*
> ++ * Detect use of multiple kernel module instances. Use a single
> ++ * module instance unless instance != NV_MODULE_INSTANCE_NONE
> ++ */
> ++static __inline__ int is_multi_module(int module_instance)
> ++{
> ++    return (module_instance != NV_MODULE_INSTANCE_NONE);
> ++}
> ++
> ++
> ++/*
> ++ * Construct the device file name, based on 'minor'.  If an error
> ++ * occurs, the nul terminator will be written to name[0].
> ++ */
> ++static __inline__ void assign_device_file_name
> ++(
> ++    char name[NV_MAX_CHARACTER_DEVICE_FILE_STRLEN],
> ++    int minor,
> ++    int module_instance
> ++)
> ++{
> ++    int ret;
> ++
> ++    if ((minor < 0) || (minor > NV_CTL_DEVICE_NUM))
> ++    {
> ++        goto fail;
> ++    }
> ++
> ++    if (!is_multi_module(module_instance) && minor == NV_CTL_DEVICE_NUM)
> ++    {
> ++        ret = snprintf(name,
> ++                       NV_MAX_CHARACTER_DEVICE_FILE_STRLEN,
> ++                       NV_CTRL_DEVICE_FILE_PATH);
> ++    }
> ++    else if (is_multi_module(module_instance) &&
> ++             NV_FRONTEND_IS_CONTROL_DEVICE(minor))
> ++    {
> ++        ret = snprintf(name,
> ++                       NV_MAX_CHARACTER_DEVICE_FILE_STRLEN,
> ++                       NV_NMODULE_CTRL_DEVICE_FILE_PATH,
> ++                       module_instance);
> ++    }
> ++    else
> ++    {
> ++        ret = snprintf(name,
> ++                       NV_MAX_CHARACTER_DEVICE_FILE_STRLEN,
> ++                       NV_DEVICE_FILE_PATH, minor);
> ++    }
> ++
> ++    if (ret <= 0)
> ++    {
> ++        goto fail;
> ++    }
> ++
> ++    name[NV_MAX_CHARACTER_DEVICE_FILE_STRLEN - 1] = '\0';
> ++
> ++    return;
> ++
> ++fail:
> ++
> ++    name[0] = '\0';
> ++}
> ++
> ++#endif /* __NVIDIA_MODPROBE_UTILS_H__ */
> +\ No newline at end of file
> +diff --git a/src/pci-enum.h b/src/pci-enum.h
> +new file mode 100644
> +index 0000000..73b8497
> +--- /dev/null
> ++++ b/src/pci-enum.h
> +@@ -0,0 +1,112 @@
> ++/*
> ++ * (C) Copyright IBM Corporation 2006
> ++ *
> ++ * Copyright (c) 2007 Paulo R. Zanoni, Tiago Vignatti
> ++ *
> ++ * Copyright 2009 Red Hat, Inc.
> ++ *
> ++ * Copyright (c) 2014 NVIDIA Corporation
> ++ *
> ++ * All Rights Reserved.
> ++ *
> ++ * Permission is hereby granted, free of charge, to any person obtaining a copy
> ++ * of this software and associated documentation files (the "Software"), to deal
> ++ * in the Software without restriction, including without limitation the rights
> ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> ++ * copies of the Software, and to permit persons to whom the Software is
> ++ * furnished to do so, subject to the following conditions:
> ++ *
> ++ * The above copyright notice and this permission notice shall be included in
> ++ * all copies or substantial portions of the Software.
> ++ *
> ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> ++ * THE SOFTWARE.
> ++ */
> ++
> ++/**
> ++ * pci-enum.h
> ++ *
> ++ * Based on libpciaccess/include/pciaccess.h from libpciaccess-0.12.1, which
> ++ * can be found here:
> ++ *
> ++ * http://cgit.freedesktop.org/xorg/lib/libpciaccess
> ++ *
> ++ * Original authors: Ian Romanick <idr@us.ibm.com>, Paulo R. Zanoni,
> ++ *                   Tiago Vignatti
> ++ */
> ++
> ++#ifndef PCI_ENUM_H
> ++#define PCI_ENUM_H
> ++
> ++#include <inttypes.h>
> ++
> ++struct pci_id_match;
> ++
> ++#ifdef __cplusplus
> ++extern "C" {
> ++#endif
> ++
> ++int pci_enum_match_id(struct pci_id_match *);
> ++
> ++#ifdef __cplusplus
> ++}
> ++#endif
> ++
> ++#define PCI_MATCH_ANY  (~0U)
> ++
> ++#define PCI_BASE_CLASS_MASK 0xff00
> ++#define PCI_SUB_CLASS_MASK  0x00ff
> ++#define PCI_FULL_CLASS_MASK PCI_BASE_CLASS_MASK | PCI_SUB_CLASS_MASK
> ++
> ++/**
> ++ * Compare two PCI ID values (either vendor or device).  This is used
> ++ * internally to compare the fields of pci_id_match to the fields of
> ++ * pci_device.
> ++ */
> ++#define PCI_ID_COMPARE(a, b) \
> ++    (((a) == PCI_MATCH_ANY) || ((a) == (b)))
> ++
> ++/**
> ++ */
> ++struct pci_id_match {
> ++    /**
> ++     * Device/vendor matching controls
> ++     *
> ++     * Control the search based on the device, vendor, subdevice, or subvendor
> ++     * IDs.  Setting any of these fields to PCI_MATCH_ANY will cause the field
> ++     * to not be used in the comparison.
> ++     */
> ++    /*@{*/
> ++    uint32_t    vendor_id;
> ++    uint32_t    device_id;
> ++    uint32_t    subvendor_id;
> ++    uint32_t    subdevice_id;
> ++    /*@}*/
> ++
> ++
> ++    /**
> ++     * Device class matching controls
> ++     *
> ++     * Device's class and subclass. The class is at bits [15:8], subclass is at
> ++     * bits [7:0].
> ++     */
> ++    /*@{*/
> ++    uint16_t    device_class;
> ++    uint16_t    device_class_mask;
> ++    /*@}*/
> ++
> ++    /**
> ++     * Match results
> ++     *
> ++     * Specifies the number of devices found that match this criteria.
> ++     */
> ++    /*@{*/
> ++    uint16_t    num_matches;
> ++};
> ++
> ++#endif /* PCI_ENUM_H */
> +\ No newline at end of file
> +diff --git a/src/pci-sysfs.c b/src/pci-sysfs.c
> +new file mode 100644
> +index 0000000..210bf40
> +--- /dev/null
> ++++ b/src/pci-sysfs.c
> +@@ -0,0 +1,529 @@
> ++/*
> ++ * (C) Copyright IBM Corporation 2006
> ++ *
> ++ * Copyright (c) 2014-2018 NVIDIA Corporation
> ++ *
> ++ * All Rights Reserved.
> ++ *
> ++ * Permission is hereby granted, free of charge, to any person obtaining a copy
> ++ * of this software and associated documentation files (the "Software"), to deal
> ++ * in the Software without restriction, including without limitation the rights
> ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> ++ * copies of the Software, and to permit persons to whom the Software is
> ++ * furnished to do so, subject to the following conditions:
> ++ *
> ++ * The above copyright notice and this permission notice shall be included in
> ++ * all copies or substantial portions of the Software.
> ++ *
> ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> ++ * THE SOFTWARE.
> ++ */
> ++
> ++/**
> ++ * pcienum-sysfs.c
> ++ *
> ++ * Based on libpciaccess/src/linux_sysfs.c from libpciaccess-0.12.1, which was
> ++ * found here:
> ++ *
> ++ * http://cgit.freedesktop.org/xorg/lib/libpciaccess
> ++ *
> ++ * Access PCI subsystem using Linux's sysfs interface.  This interface is
> ++ * available starting somewhere in the late 2.5.x kernel phase, and is the
> ++ * preferred method on all 2.6.x kernels.
> ++ *
> ++ * Original author: Ian Romanick <idr@us.ibm.com>
> ++ */
> ++
> ++#if defined(NV_LINUX)
> ++
> ++#include <stdlib.h>
> ++#include <string.h>
> ++#include <stdio.h>
> ++#include <unistd.h>
> ++#include <sys/types.h>
> ++#include <sys/stat.h>
> ++#include <fcntl.h>
> ++#include <sys/mman.h>
> ++#include <dirent.h>
> ++#include <errno.h>
> ++#include <sys/time.h>
> ++#include <time.h>
> ++#include <limits.h>
> ++
> ++#include "pci-enum.h"
> ++#include "pci-sysfs.h"
> ++
> ++#define SYS_BUS_PCI                     "/sys/bus/pci/"
> ++#define SYS_BUS_PCI_DEVICES SYS_BUS_PCI "devices"
> ++#define SYS_BUS_PCI_RESCAN  SYS_BUS_PCI "rescan"
> ++#define PCI_DBDF_FORMAT                 "%04x:%02x:%02x.%1u"
> ++#define SYSFS_PCI_BRIDGE_RESCAN_FMT     SYS_BUS_PCI_DEVICES "/" PCI_DBDF_FORMAT "/rescan"
> ++#define SYSFS_RESCAN_STRING             "1\n"
> ++#define SYSFS_RESCAN_STRING_SIZE        2
> ++#define PCI_CAP_TTL_MAX                 20
> ++#define SYSFS_PATH_SIZE                 256
> ++
> ++#define BAIL_ON_IO_ERR(buf, err, cnt, action)   \
> ++do  {                                           \
> ++    if (((err) != 0) || ((cnt) < sizeof(buf)))  \
> ++    {                                           \
> ++        (err) = ((err) == 0) ? EIO : (err);     \
> ++        action;                                 \
> ++    }                                           \
> ++} while (0)
> ++
> ++static int pci_sysfs_read_cfg(uint16_t, uint16_t, uint16_t, uint16_t, uint16_t, void *,
> ++                              uint16_t size, uint16_t *);
> ++
> ++static int find_matches(struct pci_id_match *match);
> ++
> ++/**
> ++ * Attempt to access PCI subsystem using Linux's sysfs interface to enumerate
> ++ * the matched devices.
> ++ */
> ++int
> ++pci_enum_match_id(struct pci_id_match *match)
> ++{
> ++    int err = 0;
> ++    struct stat st;
> ++
> ++
> ++    /*
> ++     * If the directory "/sys/bus/pci/devices" exists, then the PCI subsystem
> ++     * can be accessed using this interface.
> ++     */
> ++    match->num_matches = 0;
> ++    if (stat(SYS_BUS_PCI_DEVICES, &st) == 0)
> ++    {
> ++        err = find_matches(match);
> ++    }
> ++    else
> ++    {
> ++        err = errno;
> ++    }
> ++
> ++    return err;
> ++}
> ++
> ++
> ++/**
> ++ * The sysfs lookup method uses the directory entries in /sys/bus/pci/devices
> ++ * to enumerate all PCI devices, and then uses a file in each that is mapped to
> ++ * the device's PCI config space to extract the data to match against.
> ++ */
> ++static int
> ++find_matches(struct pci_id_match *match)
> ++{
> ++    struct dirent *d;
> ++    DIR *sysfs_pci_dir;
> ++    int err = 0;
> ++
> ++    sysfs_pci_dir = opendir(SYS_BUS_PCI_DEVICES);
> ++    if (sysfs_pci_dir == NULL)
> ++    {
> ++        return errno;
> ++    }
> ++
> ++    while ((d = readdir(sysfs_pci_dir)) != NULL)
> ++    {
> ++        uint8_t config[48];
> ++        uint16_t bytes;
> ++        unsigned dom, bus, dev, func;
> ++        uint16_t vendor_id, device_id, subvendor_id, subdevice_id;
> ++        uint16_t device_class;
> ++
> ++        /* Ignore the . and .. dirents */
> ++        if ((strcmp(d->d_name, ".") == 0) || (strcmp(d->d_name, "..") == 0))
> ++        {
> ++            continue;
> ++        }
> ++
> ++        sscanf(d->d_name, PCI_DBDF_FORMAT,
> ++               & dom, & bus, & dev, & func);
> ++
> ++        err = pci_sysfs_read_cfg(dom, bus, dev, func, 0, config, 48, & bytes);
> ++        if ((bytes == 48) && !err)
> ++        {
> ++            vendor_id = (uint16_t)config[0] + ((uint16_t)config[1] << 8);
> ++            device_id = (uint16_t)config[2] + ((uint16_t)config[3] << 8);
> ++            device_class = (uint16_t)config[10] +
> ++                ((uint16_t)config[11] << 8);
> ++            subvendor_id = (uint16_t)config[44] +
> ++                ((uint16_t)config[45] << 8);
> ++            subdevice_id = (uint16_t)config[46] +
> ++                ((uint16_t)config[47] << 8);
> ++
> ++            /*
> ++             * This logic, originally in common_iterator.c, will tell if
> ++             * this device is a match for the search criteria.
> ++             */
> ++            if (PCI_ID_COMPARE(match->vendor_id,    vendor_id)    &&
> ++                PCI_ID_COMPARE(match->device_id,    device_id)    &&
> ++                PCI_ID_COMPARE(match->subvendor_id, subvendor_id) &&
> ++                PCI_ID_COMPARE(match->subdevice_id, subdevice_id) &&
> ++                ((device_class & match->device_class_mask) ==
> ++                    match->device_class))
> ++            {
> ++                match->num_matches++;
> ++            }
> ++        }
> ++
> ++        if (err)
> ++        {
> ++            break;
> ++        }
> ++    }
> ++
> ++    closedir(sysfs_pci_dir);
> ++    return err;
> ++}
> ++
> ++static int
> ++pci_sysfs_read_cfg(uint16_t domain, uint16_t bus, uint16_t device,
> ++                   uint16_t function, uint16_t off, void *data,
> ++                   uint16_t size, uint16_t *bytes_read)
> ++{
> ++    char name[SYSFS_PATH_SIZE];
> ++    uint16_t temp_size = size;
> ++    int err = 0;
> ++    int fd;
> ++    char *data_bytes = data;
> ++
> ++    if (bytes_read != NULL)
> ++    {
> ++        *bytes_read = 0;
> ++    }
> ++
> ++    /*
> ++     * Each device has a directory under sysfs.  Within that directory there
> ++     * is a file named "config".  This file used to access the PCI config
> ++     * space.  It is used here to obtain most of the information about the
> ++     * device.
> ++     */
> ++    snprintf(name, SYSFS_PATH_SIZE - 1, "%s/" PCI_DBDF_FORMAT "/config",
> ++             SYS_BUS_PCI_DEVICES, domain, bus, device, function);
> ++
> ++    fd = open(name, O_RDONLY);
> ++    if (fd < 0)
> ++    {
> ++        return errno;
> ++    }
> ++
> ++    if (off != 0)
> ++    {
> ++        if (lseek(fd, (off_t) off, SEEK_SET) < 0)
> ++        {
> ++            close(fd);
> ++            return errno;
> ++        }
> ++    }
> ++
> ++    while (temp_size > 0)
> ++    {
> ++        const ssize_t bytes = read(fd, data_bytes, temp_size);
> ++
> ++        /*
> ++         * If zero bytes were read, then we assume it's the end of the
> ++         * config file.
> ++         */
> ++        if (bytes <= 0)
> ++        {
> ++            err = errno;
> ++            break;
> ++        }
> ++
> ++        temp_size -= bytes;
> ++        data_bytes += bytes;
> ++    }
> ++
> ++    if (bytes_read != NULL)
> ++    {
> ++        *bytes_read = size - temp_size;
> ++    }
> ++
> ++    close(fd);
> ++    return err;
> ++}
> ++
> ++static int
> ++pci_sysfs_write_cfg(uint16_t domain, uint16_t bus, uint16_t device,
> ++                   uint16_t function, uint16_t off, void *data,
> ++                   uint16_t size, uint16_t *bytes_written)
> ++{
> ++    char name[SYSFS_PATH_SIZE];
> ++    uint16_t temp_size = size;
> ++    int err = 0;
> ++    int fd;
> ++    char *data_bytes = data;
> ++
> ++    if (bytes_written != NULL)
> ++    {
> ++        *bytes_written = 0;
> ++    }
> ++
> ++    /*
> ++     * Each device has a directory under sysfs.  Within that directory there
> ++     * is a file named "config".  This file used to access the PCI config
> ++     * space.
> ++     */
> ++    snprintf(name, SYSFS_PATH_SIZE - 1, "%s/" PCI_DBDF_FORMAT "/config",
> ++             SYS_BUS_PCI_DEVICES, domain, bus, device, function);
> ++
> ++    fd = open(name, O_WRONLY);
> ++    if (fd < 0)
> ++    {
> ++        return errno;
> ++    }
> ++
> ++    if (off != 0)
> ++    {
> ++        if (lseek(fd, (off_t) off, SEEK_SET) < 0)
> ++        {
> ++            close(fd);
> ++            return errno;
> ++        }
> ++    }
> ++
> ++    while (temp_size > 0)
> ++    {
> ++        const ssize_t bytes = write(fd, data_bytes, temp_size);
> ++
> ++        if (bytes < 0)
> ++        {
> ++            err = errno;
> ++            break;
> ++        }
> ++        /*
> ++         * If zero bytes were written, then we assume it's the end of the
> ++         * config file.
> ++         */
> ++        if (bytes == 0)
> ++        {
> ++            break;
> ++        }
> ++
> ++        temp_size -= bytes;
> ++        data_bytes += bytes;
> ++    }
> ++
> ++    if (bytes_written != NULL)
> ++    {
> ++        *bytes_written = size - temp_size;
> ++    }
> ++
> ++    close(fd);
> ++    return err;
> ++}
> ++
> ++int
> ++pci_rescan(uint16_t domain, uint8_t bus, uint8_t slot, uint8_t function)
> ++{
> ++    char const                      *node;
> ++    char                            node_buf[SYSFS_PATH_SIZE];
> ++    int                             node_fd;
> ++    ssize_t                         cnt;
> ++
> ++    if ((domain | bus | slot | function) == 0)
> ++    {
> ++        /* rescan the entire PCI tree */
> ++        node = SYS_BUS_PCI_RESCAN;
> ++    }
> ++    else
> ++    {
> ++        snprintf(node_buf, sizeof(node_buf) - 1, SYSFS_PCI_BRIDGE_RESCAN_FMT,
> ++                domain, bus, slot, function);
> ++        node = node_buf;
> ++    }
> ++
> ++    node_fd = open(node, O_WRONLY);
> ++
> ++    if (node_fd < 0)
> ++    {
> ++        return errno;
> ++    }
> ++
> ++    cnt = write(node_fd, SYSFS_RESCAN_STRING, SYSFS_RESCAN_STRING_SIZE);
> ++
> ++    close(node_fd);
> ++
> ++    return cnt == SYSFS_RESCAN_STRING_SIZE ? 0 : EIO;
> ++}
> ++
> ++int
> ++pci_find_parent_bridge(pci_info_t *p_gpu_info, pci_info_t *p_bridge_info)
> ++{
> ++    char    gpu_path[SYSFS_PATH_SIZE];
> ++    char    bridge_path[PATH_MAX];
> ++    char    *p_node;
> ++
> ++    snprintf(gpu_path, SYSFS_PATH_SIZE - 1, "%s/" PCI_DBDF_FORMAT "/..", SYS_BUS_PCI_DEVICES,
> ++            p_gpu_info->domain, p_gpu_info->bus,
> ++            p_gpu_info->dev, p_gpu_info->ftn);
> ++
> ++    if (realpath(gpu_path, bridge_path) == NULL)
> ++    {
> ++        return errno;
> ++    }
> ++
> ++    p_node = strrchr(bridge_path, '/');
> ++
> ++    if (p_node == NULL)
> ++    {
> ++        return ENOENT;
> ++    }
> ++
> ++    ++p_node;
> ++
> ++    if (sscanf(p_node, PCI_DBDF_FORMAT,
> ++                &p_bridge_info->domain, &p_bridge_info->bus,
> ++                &p_bridge_info->dev, &p_bridge_info->ftn) != 4)
> ++    {
> ++        return ENOENT;
> ++    }
> ++
> ++    return 0;
> ++}
> ++
> ++static int
> ++pci_find_pcie_caps(uint16_t domain, uint8_t bus, uint8_t device, uint8_t ftn, uint8_t *p_caps)
> ++{
> ++    unsigned    ttl;
> ++    uint8_t     off;
> ++    uint8_t     cap_id;
> ++    int         err = ENXIO;
> ++    uint16_t    cnt;
> ++
> ++    for (off = PCI_CAPABILITY_LIST, ttl = PCI_CAP_TTL_MAX; ttl; --ttl)
> ++    {
> ++        err = pci_sysfs_read_cfg(domain, bus, device, ftn, off,
> ++                                &off, sizeof(off), &cnt);
> ++        BAIL_ON_IO_ERR(off, err, cnt, break);
> ++
> ++        /* Capabilities must reside above the std config header */
> ++        if ((off < PCI_STD_HEADER_SIZEOF) || (off == 0xff))
> ++        {
> ++            break;
> ++        }
> ++
> ++        /* Clear the reserved bits */
> ++        off &= ~3;
> ++
> ++        err = pci_sysfs_read_cfg(domain, bus, device, ftn, off + PCI_CAP_LIST_ID,
> ++                                &cap_id, sizeof(cap_id), &cnt);
> ++        BAIL_ON_IO_ERR(cap_id, err, cnt, break);
> ++
> ++        if (cap_id == PCI_CAP_ID_EXP)
> ++        {
> ++            goto found;
> ++        }
> ++
> ++        if (cap_id == 0xff)
> ++        {
> ++            break;
> ++        }
> ++
> ++        off += PCI_CAP_LIST_NEXT;
> ++    }
> ++    return err;
> ++found:
> ++    *p_caps = off;
> ++    return 0;
> ++}
> ++
> ++int
> ++pci_bridge_link_set_enable(uint16_t domain, uint8_t bus, uint8_t device, uint8_t ftn, int enable)
> ++{
> ++    uint8_t         pcie_caps = 0;
> ++    uint16_t        reg;
> ++    uint32_t        cap_reg;
> ++    uint16_t        cnt;
> ++    int             err;
> ++    struct timeval  start;
> ++    struct timeval  curr;
> ++    struct timeval  diff;
> ++    struct timespec delay = {0, PCI_LINK_DELAY_NS};
> ++    struct timespec dlllar_disable_delay = {0, PCI_LINK_DLLLAR_DISABLE_DELAY_NS};
> ++
> ++    err = pci_find_pcie_caps(domain, bus, device, ftn, &pcie_caps);
> ++
> ++    if (err != 0)
> ++    {
> ++        return err;
> ++    }
> ++
> ++    err = pci_sysfs_read_cfg(domain, bus, device, ftn, pcie_caps + PCI_EXP_LNKCTL,
> ++                            &reg, sizeof(reg), &cnt);
> ++    BAIL_ON_IO_ERR(reg, err, cnt, return err);
> ++
> ++    if (enable)
> ++    {
> ++        reg &= ~PCI_EXP_LNKCTL_LD;
> ++    }
> ++    else
> ++    {
> ++        reg |= PCI_EXP_LNKCTL_LD;
> ++    }
> ++
> ++    err = pci_sysfs_write_cfg(domain, bus, device, ftn, pcie_caps + PCI_EXP_LNKCTL,
> ++                            &reg, sizeof(reg), &cnt);
> ++    BAIL_ON_IO_ERR(reg, err, cnt, return err);
> ++
> ++    if (enable)
> ++    {
> ++        /*
> ++         * Data Link Layer Link Active Reporting must be capable for
> ++         * zero power capable downstream port. But old controller might
> ++         * not implement it. In this case, we wait for 30 ms.
> ++         */
> ++        err = pci_sysfs_read_cfg(domain, bus, device, ftn, pcie_caps + PCI_EXP_LNKCAP,
> ++                                &cap_reg, sizeof(cap_reg), &cnt);
> ++        BAIL_ON_IO_ERR(cap_reg, err, cnt, return err);
> ++
> ++        if (cap_reg & PCI_EXP_LNKCAP_DLLLARC)
> ++        {
> ++            /* wait for the link to go up and then sleep for 100 ms */
> ++
> ++            gettimeofday(&start, NULL);
> ++
> ++            for (;;)
> ++            {
> ++                err = pci_sysfs_read_cfg(domain, bus, device, ftn, pcie_caps + PCI_EXP_LNKSTA,
> ++                                    &reg, sizeof(reg), &cnt);
> ++                BAIL_ON_IO_ERR(reg, err, cnt, return err);
> ++
> ++                if ((reg & PCI_EXP_LNKSTA_DLLLA) != 0)
> ++                {
> ++                    break;
> ++                }
> ++
> ++                gettimeofday(&curr, NULL);
> ++                timersub(&curr, &start, &diff);
> ++
> ++                if ((diff.tv_sec > 0) || (diff.tv_usec >= PCI_LINK_WAIT_US))
> ++                {
> ++                    return ETIME;
> ++                }
> ++            }
> ++        }
> ++        else
> ++        {
> ++            /*
> ++             * Measured the time on DGX1 for link to become established in a bridge,
> ++             * where the DLLLA reporting is supported and its approximately ~9ms,
> ++             * so wait for 30ms where DLLLA reporting is not supported.
> ++             */
> ++            PCI_NANOSLEEP(&dlllar_disable_delay, NULL);
> ++        }
> ++
> ++        PCI_NANOSLEEP(&delay, NULL);
> ++    }
> ++
> ++    return err;
> ++}
> ++
> ++#endif /* defined(NV_LINUX) */
> +\ No newline at end of file
> +diff --git a/src/pci-sysfs.h b/src/pci-sysfs.h
> +new file mode 100644
> +index 0000000..1fc695b
> +--- /dev/null
> ++++ b/src/pci-sysfs.h
> +@@ -0,0 +1,85 @@
> ++/*
> ++ * Copyright (c) 2016-2018, NVIDIA CORPORATION.
> ++ *
> ++ * Permission is hereby granted, free of charge, to any person
> ++ * obtaining a copy of this software and associated documentation
> ++ * files (the "Software"), to deal in the Software without
> ++ * restriction, including without limitation the rights to use, copy,
> ++ * modify, merge, publish, distribute, sublicense, and/or sell copies
> ++ * of the Software, and to permit persons to whom the Software is
> ++ * furnished to do so, subject to the following conditions:
> ++ *
> ++ * The above copyright notice and this permission notice shall be
> ++ * included in all copies or substantial portions of the Software.
> ++ *
> ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> ++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> ++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
> ++ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
> ++ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
> ++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> ++ * SOFTWARE.
> ++ *
> ++ * This file provides utility functions on Linux for interfacing
> ++ * with the sysfs/PCI kernel facility.
> ++ */
> ++
> ++#ifndef __PCI_SYSFS_H__
> ++#define __PCI_SYSFS_H__
> ++
> ++#if defined(NV_LINUX)
> ++
> ++#include <linux/pci.h>
> ++
> ++#if !defined(PCI_STD_HEADER_SIZEOF)
> ++#define PCI_STD_HEADER_SIZEOF   64
> ++#endif
> ++#if !defined(PCI_CAP_ID_EXP)
> ++#define  PCI_CAP_ID_EXP         0x10    /* PCI Express */
> ++#endif
> ++#if !defined(PCI_EXP_LNKCAP)
> ++#define PCI_EXP_LNKCAP          12          /* Link Capabilities */
> ++#endif
> ++#if !defined(PCI_EXP_LNKCAP_DLLLARC)
> ++#define PCI_EXP_LNKCAP_DLLLARC  0x00100000  /* Data Link Layer Link Active Reporting Capable */
> ++#endif
> ++#if !defined(PCI_EXP_LNKCTL)
> ++#define PCI_EXP_LNKCTL          16      /* Link Control */
> ++#endif
> ++#if !defined(PCI_EXP_LNKCTL_LD)
> ++#define  PCI_EXP_LNKCTL_LD      0x0010  /* Link Disable */
> ++#endif
> ++#if !defined(PCI_EXP_LNKSTA)
> ++#define PCI_EXP_LNKSTA          18      /* Link Status */
> ++#endif
> ++#if !defined(PCI_EXP_LNKSTA_DLLLA)
> ++#define  PCI_EXP_LNKSTA_DLLLA   0x2000  /* Data Link Layer Link Active */
> ++#endif
> ++
> ++#define PCI_LINK_WAIT_US                 200000      /* 200 ms, must be less than 1000000 (1s) */
> ++#define PCI_LINK_DELAY_NS                100000000   /* 100 ms */
> ++#define PCI_LINK_DLLLAR_DISABLE_DELAY_NS 30000000    /* 30ms */
> ++
> ++#if (_POSIX_C_SOURCE >= 199309L)
> ++#define PCI_NANOSLEEP(ts, rem)  nanosleep(ts, rem)
> ++#elif !(_POSIX_C_SOURCE >= 200809L)
> ++#define PCI_NANOSLEEP(ts, rem)  usleep((ts)->tv_sec * 1000000 + ((ts)->tv_nsec + 999) / 1000)
> ++#else
> ++#define PCI_NANOSLEEP(ts, rem)  sleep((ts)->tv_sec + ((ts)->tv_nsec + 999999999) / 1000000000)
> ++#endif
> ++
> ++typedef struct  {
> ++    unsigned    domain;
> ++    unsigned    bus;
> ++    unsigned    dev;
> ++    unsigned    ftn;
> ++}   pci_info_t;
> ++
> ++int pci_rescan(uint16_t domain, uint8_t bus, uint8_t slot, uint8_t function);
> ++int pci_find_parent_bridge(pci_info_t *p_gpu_info, pci_info_t *p_bridge_info);
> ++int pci_bridge_link_set_enable(uint16_t domain, uint8_t bus, uint8_t device, uint8_t ftn, int enable);
> ++
> ++#endif /* NV_LINUX */
> ++
> ++#endif /* __PCI_SYSFS_H__ */
> +\ No newline at end of file
> +--
> +2.27.0
> +
> diff --git a/package/libnvidia-container/0002-Remove-dependency-handling-from-Makefile.patch b/package/libnvidia-container/0002-Remove-dependency-handling-from-Makefile.patch
> new file mode 100644
> index 0000000000..d4ba9dfe80
> --- /dev/null
> +++ b/package/libnvidia-container/0002-Remove-dependency-handling-from-Makefile.patch
> @@ -0,0 +1,698 @@
> +From 6752d8d5e315eb3f061498a9c35558f90f9600e2 Mon Sep 17 00:00:00 2001
> +From: Christian Stewart <christian@paral.in>
> +Date: Sat, 18 Jul 2020 15:26:22 -0700
> +Subject: [PATCH] Remove dependency handling from Makefile
> +
> +Buildroot will handle this for the makefile.
> +
> +Signed-off-by: Christian Stewart <christian@paral.in>
> +---
> + Makefile                    |  54 +++------
> + mk/nvidia-modprobe.mk       |  55 ---------
> + src/nvc.c                   |   6 +-
> + src/nvidia-modprobe-utils.c | 225 ++++++++++++++++--------------------
> + src/nvidia-modprobe-utils.h |  53 ++-------
> + 5 files changed, 128 insertions(+), 265 deletions(-)
> + delete mode 100644 mk/nvidia-modprobe.mk
> +
> +diff --git a/Makefile b/Makefile
> +index f1c56a9..80780d1 100644
> +--- a/Makefile
> ++++ b/Makefile
> +@@ -2,13 +2,13 @@
> + # Copyright (c) 2017-2018, NVIDIA CORPORATION. All rights reserved.
> + #
> +
> +-.PHONY: all tools shared static deps install uninstall dist depsclean mostlyclean clean distclean
> ++.PHONY: all tools shared static install uninstall dist mostlyclean clean distclean
> + .DEFAULT_GOAL := all
> +-STRIP  := @echo skipping: strip
> ++
> + ##### Global variables #####
> +
> +-WITH_LIBELF  ?= yes
> +-WITH_TIRPC   ?= yes
> ++WITH_LIBELF  ?= no
> ++WITH_TIRPC   ?= no
> + WITH_SECCOMP ?= yes
> +
> + ##### Global definitions #####
> +@@ -16,15 +16,14 @@ WITH_SECCOMP ?= yes
> + export prefix      = /usr
> + export exec_prefix = $(prefix)
> + export bindir      = $(exec_prefix)/bin
> +-export libdir      = $(exec_prefix)/lib64
> ++export libdir      = $(exec_prefix)/lib
> + export docdir      = $(prefix)/share/doc
> +-export libdbgdir   = $(prefix)/lib64/debug$(libdir)
> ++export libdbgdir   = $(prefix)/lib/debug$(libdir)
> + export includedir  = $(prefix)/include
> + export pkgconfdir  = $(libdir)/pkgconfig
> +
> + export PKG_DIR     ?= $(CURDIR)/pkg
> + export SRCS_DIR    ?= $(CURDIR)/src
> +-export DEPS_DIR    ?= $(CURDIR)/deps
> + export DIST_DIR    ?= $(CURDIR)/dist
> + export MAKE_DIR    ?= $(CURDIR)/mk
> + export DEBUG_DIR   ?= $(CURDIR)/.debug
> +@@ -120,9 +119,9 @@ LDFLAGS  := -Wl,-zrelro -Wl,-znow -Wl,-zdefs -Wl,--gc-sections $(LDFLAGS)
> + LDLIBS   := $(LDLIBS)
> +
> + # Library flags (recursively expanded to handle target-specific flags)
> +-LIB_CPPFLAGS       = -DNV_LINUX -isystem $(DEPS_DIR)$(includedir) -include $(BUILD_DEFS)
> ++LIB_CPPFLAGS       = -DNV_LINUX -include $(BUILD_DEFS)
> + LIB_CFLAGS         = -fPIC
> +-LIB_LDFLAGS        = -L$(DEPS_DIR)$(libdir) -shared -Wl,-soname=$(LIB_SONAME)
> ++LIB_LDFLAGS        = -shared -Wl,-soname=$(LIB_SONAME)
> + # LIB_LDLIBS_STATIC  = -l:libnvidia-modprobe-utils.a
> + LIB_LDLIBS_SHARED  = -ldl -lcap -ltirpc
> + ifeq ($(WITH_LIBELF), yes)
> +@@ -132,7 +131,7 @@ else
> + LIB_LDLIBS_STATIC  += -l:libelf.a
> + endif
> + ifeq ($(WITH_TIRPC), yes)
> +-LIB_CPPFLAGS       += -isystem $(DEPS_DIR)$(includedir)/tirpc -DWITH_TIRPC
> ++LIB_CPPFLAGS       += -DWITH_TIRPC
> + # LIB_LDLIBS_STATIC  += -l:libtirpc.a
> + LIB_LDLIBS_SHARED  += -lpthread
> + endif
> +@@ -176,15 +175,15 @@ $(LIB_RPC_SRCS): $(LIB_RPC_SPEC)
> +       $(RM) $@
> +       cd $(dir $@) && $(RPCGEN) $(RPCGENFLAGS) -C -M -N -o $(notdir $@) $(LIB_RPC_SPEC)
> +
> +-$(LIB_OBJS): %.lo: %.c | deps
> ++$(LIB_OBJS): %.lo: %.c
> +       $(CC) $(LIB_CFLAGS) $(LIB_CPPFLAGS) -MMD -MF $*.d -c $(OUTPUT_OPTION) $<
> +
> +-$(BIN_OBJS): %.o: %.c | shared
> ++$(BIN_OBJS): %.o: %.c
> +       $(CC) $(BIN_CFLAGS) $(BIN_CPPFLAGS) -MMD -MF $*.d -c $(OUTPUT_OPTION) $<
> +
> + -include $(DEPENDENCIES)
> +
> +-$(LIB_SHARED): $(LIB_OBJS)
> ++$(LIB_SHARED): $(BUILD_DEFS) $(SRCS_DIR)/driver_rpc.h $(LIB_OBJS)
> +       $(MKDIR) -p $(DEBUG_DIR)
> +       $(CC) $(LIB_CFLAGS) $(LIB_CPPFLAGS) $(LIB_LDFLAGS) $(OUTPUT_OPTION) $^ $(LIB_SCRIPT) $(LIB_LDLIBS)
> +       $(OBJCPY) --only-keep-debug $@ $(LIB_SONAME)
> +@@ -198,7 +197,7 @@ $(LIB_STATIC_OBJ): $(LIB_OBJS)
> +       $(OBJCPY) --localize-hidden $@
> +       $(STRIP) --strip-unneeded -R .comment $@
> +
> +-$(BIN_NAME): $(BIN_OBJS)
> ++$(BIN_NAME): $(BUILD_DEFS) $(SRCS_DIR)/driver_rpc.h $(LIB_SHARED) $(BIN_OBJS)
> +       $(CC) $(BIN_CFLAGS) $(BIN_CPPFLAGS) $(BIN_LDFLAGS) $(OUTPUT_OPTION) $^ $(BIN_SCRIPT) $(BIN_LDLIBS)
> +       $(STRIP) --strip-unneeded -R .comment $@
> +
> +@@ -219,17 +218,6 @@ shared: $(LIB_SHARED)
> +
> + static: $(LIB_STATIC)($(LIB_STATIC_OBJ))
> +
> +-deps: export DESTDIR:=$(DEPS_DIR)
> +-deps: $(LIB_RPC_SRCS) $(BUILD_DEFS)
> +-      $(MKDIR) -p $(DEPS_DIR)
> +-      # $(MAKE) -f $(MAKE_DIR)/nvidia-modprobe.mk install
> +-ifeq ($(WITH_LIBELF), no)
> +-      # $(MAKE) -f $(MAKE_DIR)/elftoolchain.mk install
> +-endif
> +-ifeq ($(WITH_TIRPC), yes)
> +-      # $(MAKE) -f $(MAKE_DIR)/libtirpc.mk install
> +-endif
> +-
> + install: all
> +       $(INSTALL) -d -m 755 $(addprefix $(DESTDIR),$(includedir) $(bindir) $(libdir) $(docdir) $(libdbgdir) $(pkgconfdir))
> +       # Install header files
> +@@ -237,8 +225,7 @@ install: all
> +       # Install library files
> +       $(INSTALL) -m 644 $(LIB_STATIC) $(DESTDIR)$(libdir)
> +       $(INSTALL) -m 755 $(LIB_SHARED) $(DESTDIR)$(libdir)
> +-      $(LN) -sf $(LIB_SONAME) $(DESTDIR)$(libdir)/$(LIB_SYMLINK)
> +-      $(LDCONFIG) -n $(DESTDIR)$(libdir)
> ++      $(LN) -sf $(LIB_SHARED) $(DESTDIR)$(libdir)/$(LIB_SYMLINK)
> +       # Install debugging symbols
> +       # $(INSTALL) -m 644 $(DEBUG_DIR)/$(LIB_SONAME) $(DESTDIR)$(libdbgdir)
> +       # Install configuration files
> +@@ -268,23 +255,12 @@ dist: install
> +       $(TAR) --numeric-owner --owner=0 --group=0 -C $(dir $(DESTDIR)) -caf $(DESTDIR)_$(ARCH).tar.xz $(notdir $(DESTDIR))
> +       $(RM) -r $(DESTDIR)
> +
> +-depsclean:
> +-      $(RM) $(BUILD_DEFS)
> +-      -$(MAKE) -f $(MAKE_DIR)/nvidia-modprobe.mk clean
> +-ifeq ($(WITH_LIBELF), no)
> +-      -$(MAKE) -f $(MAKE_DIR)/elftoolchain.mk clean
> +-endif
> +-ifeq ($(WITH_TIRPC), yes)
> +-      -$(MAKE) -f $(MAKE_DIR)/libtirpc.mk clean
> +-endif
> +-
> + mostlyclean:
> +       $(RM) $(LIB_OBJS) $(LIB_STATIC_OBJ) $(BIN_OBJS) $(DEPENDENCIES)
> +
> +-clean: mostlyclean depsclean
> ++clean: mostlyclean
> +
> + distclean: clean
> +-      $(RM) -r $(DEPS_DIR) $(DIST_DIR) $(DEBUG_DIR)
> +       $(RM) $(LIB_RPC_SRCS) $(LIB_STATIC) $(LIB_SHARED) $(BIN_NAME)
> +
> + deb: DESTDIR:=$(DIST_DIR)/$(LIB_NAME)_$(VERSION)_$(ARCH)
> +diff --git a/mk/nvidia-modprobe.mk b/mk/nvidia-modprobe.mk
> +deleted file mode 100644
> +index ad399de..0000000
> +--- a/mk/nvidia-modprobe.mk
> ++++ /dev/null
> +@@ -1,55 +0,0 @@
> +-#
> +-# Copyright (c) 2017-2018, NVIDIA CORPORATION. All rights reserved.
> +-#
> +-
> +-include $(MAKE_DIR)/common.mk
> +-
> +-##### Source definitions #####
> +-
> +-VERSION        := 396.51
> +-PREFIX         := nvidia-modprobe-$(VERSION)
> +-URL            := https://github.com/NVIDIA/nvidia-modprobe/archive/$(VERSION).tar.gz
> +-
> +-SRCS_DIR       := $(DEPS_DIR)/src/$(PREFIX)
> +-MODPROBE_UTILS := $(SRCS_DIR)/modprobe-utils
> +-
> +-LIB_STATIC     := $(MODPROBE_UTILS)/libnvidia-modprobe-utils.a
> +-LIB_INCS       := $(MODPROBE_UTILS)/nvidia-modprobe-utils.h \
> +-                  $(MODPROBE_UTILS)/pci-enum.h
> +-LIB_SRCS       := $(MODPROBE_UTILS)/nvidia-modprobe-utils.c \
> +-                  $(MODPROBE_UTILS)/pci-sysfs.c
> +-
> +-##### Flags definitions #####
> +-
> +-ARFLAGS  := -rU
> +-CPPFLAGS := -D_FORTIFY_SOURCE=2 -DNV_LINUX
> +-CFLAGS   := -O2 -g -fdata-sections -ffunction-sections -fstack-protector -fno-strict-aliasing -fPIC
> +-
> +-##### Private rules #####
> +-
> +-LIB_OBJS := $(LIB_SRCS:.c=.o)
> +-
> +-$(SRCS_DIR)/.download_stamp:
> +-      $(MKDIR) -p $(SRCS_DIR)
> +-      $(CURL) --progress-bar -fSL $(URL) | \
> +-      $(TAR) -C $(SRCS_DIR) --strip-components=1 -xz $(PREFIX)/modprobe-utils
> +-      @touch $@
> +-
> +-$(LIB_SRCS): $(SRCS_DIR)/.download_stamp
> +-
> +-##### Public rules #####
> +-
> +-.PHONY: all install clean
> +-
> +-all: $(LIB_STATIC)
> +-
> +-$(LIB_STATIC): $(LIB_OBJS)
> +-      $(AR) rs $@ $^
> +-
> +-install: all
> +-      $(INSTALL) -d -m 755 $(addprefix $(DESTDIR),$(includedir) $(libdir))
> +-      $(INSTALL) -m 644 $(LIB_INCS) $(DESTDIR)$(includedir)
> +-      $(INSTALL) -m 644 $(LIB_STATIC) $(DESTDIR)$(libdir)
> +-
> +-clean:
> +-      $(RM) $(LIB_OBJS) $(LIB_STATIC)
> +diff --git a/src/nvc.c b/src/nvc.c
> +index f1d9b62..74ea61c 100644
> +--- a/src/nvc.c
> ++++ b/src/nvc.c
> +@@ -190,13 +190,13 @@ load_kernel_modules(struct error *err, const char *root)
> +                 }
> +
> +                 log_info("loading kernel module nvidia");
> +-                if (nvidia_modprobe(0, -1) == 0)
> ++                if (nvidia_modprobe(0) == 0)
> +                         log_err("could not load kernel module nvidia");
> +                 else {
> +-                        if (nvidia_mknod(NV_CTL_DEVICE_MINOR, -1) == 0)
> ++                        if (nvidia_mknod(NV_CTL_DEVICE_MINOR) == 0)
> +                                 log_err("could not create kernel module device node");
> +                         for (int i = 0; i < (int)devs.num_matches; ++i) {
> +-                                if (nvidia_mknod(i, -1) == 0)
> ++                                if (nvidia_mknod(i) == 0)
> +                                         log_err("could not create kernel module device node");
> +                         }
> +                 }
> +diff --git a/src/nvidia-modprobe-utils.c b/src/nvidia-modprobe-utils.c
> +index d3f3233..fca21cf 100644
> +--- a/src/nvidia-modprobe-utils.c
> ++++ b/src/nvidia-modprobe-utils.c
> +@@ -1,3 +1,29 @@
> ++/*
> ++ * Copyright (c) 2013, NVIDIA CORPORATION.
> ++ *
> ++ * Permission is hereby granted, free of charge, to any person
> ++ * obtaining a copy of this software and associated documentation
> ++ * files (the "Software"), to deal in the Software without
> ++ * restriction, including without limitation the rights to use, copy,
> ++ * modify, merge, publish, distribute, sublicense, and/or sell copies
> ++ * of the Software, and to permit persons to whom the Software is
> ++ * furnished to do so, subject to the following conditions:
> ++ *
> ++ * The above copyright notice and this permission notice shall be
> ++ * included in all copies or substantial portions of the Software.
> ++ *
> ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> ++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> ++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
> ++ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
> ++ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
> ++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> ++ * SOFTWARE.
> ++ *
> ++ * This file provides utility functions on Linux for loading the
> ++ * NVIDIA kernel module and creating NVIDIA device files.
> ++ */
> +
> + #if defined(NV_LINUX)
> +
> +@@ -27,9 +53,6 @@
> + #define NV_NVIDIA_MODULE_NAME "nvidia"
> + #define NV_PROC_REGISTRY_PATH "/proc/driver/nvidia/params"
> +
> +-#define NV_NMODULE_NVIDIA_MODULE_NAME "nvidia%d"
> +-#define NV_NMODULE_PROC_REGISTRY_PATH "/proc/driver/nvidia/%d/params"
> +-
> + #define NV_UVM_MODULE_NAME "nvidia-uvm"
> + #define NV_UVM_DEVICE_NAME "/dev/nvidia-uvm"
> + #define NV_UVM_TOOLS_DEVICE_NAME "/dev/nvidia-uvm-tools"
> +@@ -41,6 +64,9 @@
> + #define NV_NVLINK_MODULE_NAME "nvidia-nvlink"
> + #define NV_NVLINK_PROC_PERM_PATH "/proc/driver/nvidia-nvlink/permissions"
> +
> ++#define NV_NVSWITCH_MODULE_NAME "nvidia-nvswitch"
> ++#define NV_NVSWITCH_PROC_PERM_PATH "/proc/driver/nvidia-nvswitch/permissions"
> ++
> + #define NV_DEVICE_FILE_MODE_MASK (S_IRWXU|S_IRWXG|S_IRWXO)
> + #define NV_DEVICE_FILE_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
> + #define NV_DEVICE_FILE_UID 0
> +@@ -54,84 +80,6 @@
> +
> + #define NV_MIN(a, b) (((a) < (b)) ? (a) : (b))
> +
> +-/*
> +- * Construct the nvidia kernel module name based on the input
> +- * module instance provided.  If an error occurs, the null
> +- * terminator will be written to nv_module_name[0].
> +- */
> +-static __inline__ void assign_nvidia_kernel_module_name
> +-(
> +-    char nv_module_name[NV_MAX_MODULE_NAME_SIZE],
> +-    int module_instance
> +-)
> +-{
> +-    int ret;
> +-
> +-    if (is_multi_module(module_instance))
> +-    {
> +-        ret = snprintf(nv_module_name, NV_MAX_MODULE_NAME_SIZE,
> +-                       NV_NMODULE_NVIDIA_MODULE_NAME, module_instance);
> +-    }
> +-    else
> +-    {
> +-        ret = snprintf(nv_module_name, NV_MAX_MODULE_NAME_SIZE,
> +-                       NV_NVIDIA_MODULE_NAME);
> +-    }
> +-
> +-    if (ret <= 0)
> +-    {
> +-        goto fail;
> +-    }
> +-
> +-    nv_module_name[NV_MAX_MODULE_NAME_SIZE - 1] = '\0';
> +-
> +-    return;
> +-
> +-fail:
> +-
> +-    nv_module_name[0] = '\0';
> +-}
> +-
> +-
> +-/*
> +- * Construct the proc registry path name based on the input
> +- * module instance provided.  If an error occurs, the null
> +- * terminator will be written to proc_path[0].
> +- */
> +-static __inline__ void assign_proc_registry_path
> +-(
> +-    char proc_path[NV_MAX_PROC_REGISTRY_PATH_SIZE],
> +-    int module_instance
> +-)
> +-{
> +-    int ret;
> +-
> +-    if (is_multi_module(module_instance))
> +-    {
> +-        ret = snprintf(proc_path, NV_MAX_PROC_REGISTRY_PATH_SIZE,
> +-                       NV_NMODULE_PROC_REGISTRY_PATH, module_instance);
> +-    }
> +-    else
> +-    {
> +-        ret = snprintf(proc_path, NV_MAX_PROC_REGISTRY_PATH_SIZE,
> +-                       NV_PROC_REGISTRY_PATH);
> +-    }
> +-
> +-    if (ret <= 0)
> +-    {
> +-        goto fail;
> +-    }
> +-
> +-    proc_path[NV_MAX_PROC_REGISTRY_PATH_SIZE - 1] = '\0';
> +-
> +-    return;
> +-
> +-fail:
> +-
> +-    proc_path[0] = '\0';
> +-}
> +-
> +-
> + /*
> +  * Just like strcmp(3), except that differences between '-' and '_' are
> +  * ignored. This is useful for comparing module names, where '-' and '_'
> +@@ -370,18 +318,20 @@ static int modprobe_helper(const int print_errors, const char *module_name)
> +             return 0;
> +
> +         default:
> +-            if (waitpid(pid, &status, 0) < 0)
> +-            {
> +-                return 0;
> +-            }
> +-            if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
> +-            {
> +-                return 1;
> +-            }
> +-            else
> +-            {
> +-                return 0;
> +-            }
> ++            /*
> ++             * waitpid(2) is not always guaranteed to return success even if
> ++             * the child terminated normally.  For example, if the process
> ++             * explicitly configured the handling of the SIGCHLD signal
> ++             * to SIG_IGN, then waitpid(2) will instead block until all
> ++             * children terminate and return the error ECHILD, regardless
> ++             * of the child's exit codes.
> ++             *
> ++             * Hence, ignore waitpid(2) error codes and instead check
> ++             * whether the desired kernel module is loaded.
> ++             */
> ++            waitpid(pid, NULL, 0);
> ++
> ++            return is_kernel_module_loaded(module_name);
> +     }
> +
> +     return 1;
> +@@ -391,13 +341,9 @@ static int modprobe_helper(const int print_errors, const char *module_name)
> + /*
> +  * Attempt to load an NVIDIA kernel module
> +  */
> +-int nvidia_modprobe(const int print_errors, int module_instance)
> ++int nvidia_modprobe(const int print_errors)
> + {
> +-    char nv_module_name[NV_MAX_MODULE_NAME_SIZE];
> +-
> +-    assign_nvidia_kernel_module_name(nv_module_name, module_instance);
> +-
> +-    return modprobe_helper(print_errors, nv_module_name);
> ++    return modprobe_helper(print_errors, NV_NVIDIA_MODULE_NAME);
> + }
> +
> +
> +@@ -494,24 +440,22 @@ static int get_file_state_helper(
> +     return state;
> + }
> +
> +-int nvidia_get_file_state(int minor, int module_instance)
> ++int nvidia_get_file_state(int minor)
> + {
> +     char path[NV_MAX_CHARACTER_DEVICE_FILE_STRLEN];
> +-    char proc_path[NV_MAX_PROC_REGISTRY_PATH_SIZE];
> +     mode_t mode;
> +     uid_t uid;
> +     gid_t gid;
> +     int modification_allowed;
> +     int state = 0;
> +
> +-    assign_device_file_name(path, minor, module_instance);
> +-    assign_proc_registry_path(proc_path, module_instance);
> ++    assign_device_file_name(path, minor);
> +
> +     init_device_file_parameters(&uid, &gid, &mode, &modification_allowed,
> +-                                proc_path);
> ++                                NV_PROC_REGISTRY_PATH);
> +
> +     state = get_file_state_helper(path, NV_MAJOR_DEVICE_NUMBER, minor,
> +-                                  proc_path, uid, gid, mode);
> ++                                  NV_PROC_REGISTRY_PATH, uid, gid, mode);
> +
> +     return state;
> + }
> +@@ -522,8 +466,8 @@ int nvidia_get_file_state(int minor, int module_instance)
> +  * permissions.  Returns 1 if the file is successfully created; returns 0
> +  * if the file could not be created.
> +  */
> +-int mknod_helper(int major, int minor, const char *path,
> +-                 const char *proc_path)
> ++static int mknod_helper(int major, int minor, const char *path,
> ++                        const char *proc_path)
> + {
> +     dev_t dev = NV_MAKE_DEVICE(major, minor);
> +     mode_t mode;
> +@@ -616,15 +560,13 @@ int mknod_helper(int major, int minor, const char *path,
> +  * Attempt to create a device file with the specified minor number for
> +  * the specified NVIDIA module instance.
> +  */
> +-int nvidia_mknod(int minor, int module_instance)
> ++int nvidia_mknod(int minor)
> + {
> +     char path[NV_MAX_CHARACTER_DEVICE_FILE_STRLEN];
> +-    char proc_path[NV_MAX_PROC_REGISTRY_PATH_SIZE];
> +
> +-    assign_device_file_name(path, minor, module_instance);
> +-    assign_proc_registry_path(proc_path, module_instance);
> ++    assign_device_file_name(path, minor);
> +
> +-    return mknod_helper(NV_MAJOR_DEVICE_NUMBER, minor, path, proc_path);
> ++    return mknod_helper(NV_MAJOR_DEVICE_NUMBER, minor, path, NV_PROC_REGISTRY_PATH);
> + }
> +
> +
> +@@ -633,7 +575,7 @@ int nvidia_mknod(int minor, int module_instance)
> +  * device with the specified name.  Returns the major number on success,
> +  * or -1 on failure.
> +  */
> +-int get_chardev_major(const char *name)
> ++static int get_chardev_major(const char *name)
> + {
> +     int ret = -1;
> +     char line[NV_MAX_LINE_LENGTH];
> +@@ -743,13 +685,9 @@ int nvidia_modeset_modprobe(void)
> +  */
> + int nvidia_modeset_mknod(void)
> + {
> +-    char proc_path[NV_MAX_PROC_REGISTRY_PATH_SIZE];
> +-
> +-    assign_proc_registry_path(proc_path, NV_MODULE_INSTANCE_NONE);
> +-
> +     return mknod_helper(NV_MAJOR_DEVICE_NUMBER,
> +                         NV_MODESET_MINOR_DEVICE_NUM,
> +-                        NV_MODESET_DEVICE_NAME, proc_path);
> ++                        NV_MODESET_DEVICE_NAME, NV_PROC_REGISTRY_PATH);
> + }
> +
> + /*
> +@@ -770,25 +708,62 @@ int nvidia_nvlink_mknod(void)
> +                         NV_NVLINK_PROC_PERM_PATH);
> + }
> +
> ++/*
> ++ * Attempt to create the NVIDIA NVSwitch driver device files.
> ++ */
> ++int nvidia_nvswitch_mknod(int minor)
> ++{
> ++    int major = 0;
> ++    char name[NV_MAX_CHARACTER_DEVICE_FILE_STRLEN];
> ++    int ret;
> ++
> ++    major = get_chardev_major(NV_NVSWITCH_MODULE_NAME);
> ++
> ++    if (major < 0)
> ++    {
> ++        return 0;
> ++    }
> ++
> ++    if (minor == NV_NVSWITCH_CTL_MINOR)
> ++    {
> ++        ret = snprintf(name, NV_MAX_CHARACTER_DEVICE_FILE_STRLEN,
> ++                       NV_NVSWITCH_CTL_NAME);
> ++    }
> ++    else
> ++    {
> ++        ret = snprintf(name, NV_MAX_CHARACTER_DEVICE_FILE_STRLEN,
> ++                       NV_NVSWITCH_DEVICE_NAME, minor);
> ++    }
> ++
> ++    if (ret <= 0)
> ++    {
> ++        return 0;
> ++    }
> ++
> ++    return mknod_helper(major, minor, name, NV_NVSWITCH_PROC_PERM_PATH);
> ++}
> ++
> + int nvidia_vgpu_vfio_mknod(int minor_num)
> + {
> +     int major = get_chardev_major(NV_VGPU_VFIO_MODULE_NAME);
> +     char vgpu_dev_name[NV_MAX_CHARACTER_DEVICE_FILE_STRLEN];
> +-    char proc_path[NV_MAX_PROC_REGISTRY_PATH_SIZE];
> ++    int ret;
> +
> +     if (major < 0)
> +     {
> +         return 0;
> +     }
> +
> +-    assign_proc_registry_path(proc_path, NV_MODULE_INSTANCE_NONE);
> +-
> +-    snprintf(vgpu_dev_name, NV_MAX_CHARACTER_DEVICE_FILE_STRLEN,
> +-             NV_VGPU_VFIO_DEVICE_NAME, minor_num);
> ++    ret = snprintf(vgpu_dev_name, NV_MAX_CHARACTER_DEVICE_FILE_STRLEN,
> ++                   NV_VGPU_VFIO_DEVICE_NAME, minor_num);
> ++    if (ret <= 0)
> ++    {
> ++        return 0;
> ++    }
> +
> +     vgpu_dev_name[NV_MAX_CHARACTER_DEVICE_FILE_STRLEN - 1] = '\0';
> +
> +-    return mknod_helper(major, minor_num, vgpu_dev_name, proc_path);
> ++    return mknod_helper(major, minor_num, vgpu_dev_name, NV_PROC_REGISTRY_PATH);
> + }
> +
> +-#endif /* NV_LINUX */
> +\ No newline at end of file
> ++#endif /* NV_LINUX */
> +diff --git a/src/nvidia-modprobe-utils.h b/src/nvidia-modprobe-utils.h
> +index e06b4a4..5ab3355 100644
> +--- a/src/nvidia-modprobe-utils.h
> ++++ b/src/nvidia-modprobe-utils.h
> +@@ -31,29 +31,17 @@
> + #include <stdio.h>
> +
> + #define NV_MAX_CHARACTER_DEVICE_FILE_STRLEN  128
> +-#define NV_MODULE_INSTANCE_NONE              -1
> +-#define NV_MODULE_INSTANCE_ZERO              0
> +-#define NV_MAX_MODULE_INSTANCES              8
> + #define NV_CTL_DEVICE_NUM                    255
> + #define NV_MODESET_MINOR_DEVICE_NUM          254
> +-
> +-#define NV_FRONTEND_CONTROL_DEVICE_MINOR_MAX NV_CTL_DEVICE_NUM
> ++#define NV_NVSWITCH_CTL_MINOR                255
> +
> + #define NV_DEVICE_FILE_PATH "/dev/nvidia%d"
> + #define NV_CTRL_DEVICE_FILE_PATH "/dev/nvidiactl"
> + #define NV_MODESET_DEVICE_NAME "/dev/nvidia-modeset"
> + #define NV_VGPU_VFIO_DEVICE_NAME "/dev/nvidia-vgpu%d"
> + #define NV_NVLINK_DEVICE_NAME "/dev/nvidia-nvlink"
> +-
> +-#define NV_NMODULE_CTRL_DEVICE_FILE_PATH "/dev/nvidiactl%d"
> +-
> +-#define NV_FRONTEND_CONTROL_DEVICE_MINOR_MIN \
> +-    (NV_FRONTEND_CONTROL_DEVICE_MINOR_MAX - \
> +-     NV_MAX_MODULE_INSTANCES)
> +-
> +-#define NV_FRONTEND_IS_CONTROL_DEVICE(x) \
> +-    ((x <= NV_FRONTEND_CONTROL_DEVICE_MINOR_MAX) && \
> +-     (x > NV_FRONTEND_CONTROL_DEVICE_MINOR_MIN))
> ++#define NV_NVSWITCH_CTL_NAME "/dev/nvidia-nvswitchctl"
> ++#define NV_NVSWITCH_DEVICE_NAME "/dev/nvidia-nvswitch%d"
> +
> + #if defined(NV_LINUX)
> +
> +@@ -76,31 +64,19 @@ static __inline__ int nvidia_test_file_state(int state,
> +     return !!(state & (1 << value));
> + }
> +
> +-int nvidia_get_file_state(int minor, int module_instance);
> +-int nvidia_modprobe(const int print_errors, int module_instance);
> +-int nvidia_mknod(int minor, int module_instance);
> ++int nvidia_get_file_state(int minor);
> ++int nvidia_modprobe(const int print_errors);
> ++int nvidia_mknod(int minor);
> + int nvidia_uvm_modprobe(void);
> + int nvidia_uvm_mknod(int base_minor);
> + int nvidia_modeset_modprobe(void);
> + int nvidia_modeset_mknod(void);
> + int nvidia_vgpu_vfio_mknod(int minor_num);
> + int nvidia_nvlink_mknod(void);
> +-
> +-int mknod_helper(int major, int minor, const char *path, const char *proc_path);
> +-int get_chardev_major(const char *name);
> ++int nvidia_nvswitch_mknod(int minor);
> +
> + #endif /* NV_LINUX */
> +
> +-/*
> +- * Detect use of multiple kernel module instances. Use a single
> +- * module instance unless instance != NV_MODULE_INSTANCE_NONE
> +- */
> +-static __inline__ int is_multi_module(int module_instance)
> +-{
> +-    return (module_instance != NV_MODULE_INSTANCE_NONE);
> +-}
> +-
> +-
> + /*
> +  * Construct the device file name, based on 'minor'.  If an error
> +  * occurs, the nul terminator will be written to name[0].
> +@@ -108,8 +84,7 @@ static __inline__ int is_multi_module(int module_instance)
> + static __inline__ void assign_device_file_name
> + (
> +     char name[NV_MAX_CHARACTER_DEVICE_FILE_STRLEN],
> +-    int minor,
> +-    int module_instance
> ++    int minor
> + )
> + {
> +     int ret;
> +@@ -119,20 +94,12 @@ static __inline__ void assign_device_file_name
> +         goto fail;
> +     }
> +
> +-    if (!is_multi_module(module_instance) && minor == NV_CTL_DEVICE_NUM)
> ++    if (minor == NV_CTL_DEVICE_NUM)
> +     {
> +         ret = snprintf(name,
> +                        NV_MAX_CHARACTER_DEVICE_FILE_STRLEN,
> +                        NV_CTRL_DEVICE_FILE_PATH);
> +     }
> +-    else if (is_multi_module(module_instance) &&
> +-             NV_FRONTEND_IS_CONTROL_DEVICE(minor))
> +-    {
> +-        ret = snprintf(name,
> +-                       NV_MAX_CHARACTER_DEVICE_FILE_STRLEN,
> +-                       NV_NMODULE_CTRL_DEVICE_FILE_PATH,
> +-                       module_instance);
> +-    }
> +     else
> +     {
> +         ret = snprintf(name,
> +@@ -154,4 +121,4 @@ fail:
> +     name[0] = '\0';
> + }
> +
> +-#endif /* __NVIDIA_MODPROBE_UTILS_H__ */
> +\ No newline at end of file
> ++#endif /* __NVIDIA_MODPROBE_UTILS_H__ */
> +--
> +2.27.0
> +
I must say I don't know how to address those big patches.
Are they a part from an upstream patch or something?

> diff --git a/package/libnvidia-container/Config.in b/package/libnvidia-container/Config.in
> new file mode 100644
> index 0000000000..7a452c3635
> --- /dev/null
> +++ b/package/libnvidia-container/Config.in
> @@ -0,0 +1,18 @@
> +config BR2_PACKAGE_LIBNVIDIA_CONTAINER
> +       bool "libnvidia-container"
> +       depends on BR2_SHARED_LIBS
> +       depends on BR2_TOOLCHAIN_HAS_THREADS # tirpc
> +       depends on BR2_TOOLCHAIN_USES_GLIBC # fexecve
> +       select BR2_PACKAGE_ELFUTILS
> +       select BR2_PACKAGE_LIBCAP
> +       select BR2_PACKAGE_LIBTIRPC
> +       select BR2_PACKAGE_NVIDIA_MODPROBE
> +       help
> +         The libnvidia-container package adds a library and CLI for
> +         GPU-backed containers, agnostic to container runtime.
> +
> +         https://github.com/NVIDIA/libnvidia-container
> +
> +comment "libnvidia-container needs a shared glibc toolchain w/ threads"
> +       depends on !BR2_TOOLCHAIN_HAS_THREADS || !BR2_TOOLCHAN_USES_GLIBC || \
> +               !BR2_SHARED_LIBS
> diff --git a/package/libnvidia-container/libnvidia-container.hash b/package/libnvidia-container/libnvidia-container.hash
> new file mode 100644
> index 0000000000..d356eb2b1e
> --- /dev/null
> +++ b/package/libnvidia-container/libnvidia-container.hash
> @@ -0,0 +1,3 @@
> +# Locally computed:
> +sha256 fd447629fd65d171b68edb62fa2e581c67fdb450ff540f486987ab826150d06e  libnvidia-container-1.2.0.tar.gz
> +sha256 cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30  LICENSE
> diff --git a/package/libnvidia-container/libnvidia-container.mk b/package/libnvidia-container/libnvidia-container.mk
> new file mode 100644
> index 0000000000..d337d0a528
> --- /dev/null
> +++ b/package/libnvidia-container/libnvidia-container.mk
> @@ -0,0 +1,57 @@
> +################################################################################
> +#
> +# libnvidia-container
> +#
> +################################################################################
> +
> +LIBNVIDIA_CONTAINER_VERSION = 1.2.0
> +LIBNVIDIA_CONTAINER_SITE = $(call github,NVIDIA,libnvidia-container,v$(LIBNVIDIA_CONTAINER_VERSION))
> +LIBNVIDIA_CONTAINER_LICENSE = Apache-2.0
> +LIBNVIDIA_CONTAINER_LICENSE_FILES = LICENSE
> +
> +LIBNVIDIA_CONTAINER_DEPENDENCIES = elfutils libcap libtirpc nvidia-modprobe \
> +       host-pkgconf host-elfutils host-libcap
> +
> +LIBNVIDIA_CONTAINER_INCLUDE_DIRS += \
> +       -I$(HOST_DIR)/include -I$(STAGING_DIR)/usr/include \
> +       -I$(STAGING_DIR)/usr/include/nvidia-modprobe-utils
I don't think it's a good idea to mix include flags from both host and
target directories.

> +
> +LIBNVIDIA_CONTAINER_MAKE_OPTS = \
> +       AR="$(TARGET_AR)" STRIP="$(TARGET_STRIP)" \
> +       CC="$(TARGET_CC)" CFLAGS="$(TARGET_CFLAGS) \
> +       $(LIBNVIDIA_CONTAINER_INCLUDE_DIRS) -D_GNU_SOURCE" \
> +       CXX="$(TARGET_CXX)" CPPFLAGS="$(TARGET_CXXFLAGS)" \
> +       LD="$(TARGET_LD)" LDFLAGS="$(TARGET_LDFLAGS)" \
> +       OBJCPY="$(TARGET_OBJCOPY)" \
> +       RPCGEN="$(HOST_DIR)/bin/rpcgen" \
> +       WITH_LIBELF=yes \
> +       WITH_TIRPC=no
Most of the above definitions can be acheived from TARGET_CONFIGURE_OPTS.
I suggest to use TARGET_CONFIGURE_OPTS and define another variable for
the additional definitions needed by this package.

> +
> +ifeq ($(BR2_PACKAGE_LIBSECCOMP),y)
> +LIBNVIDIA_CONTAINER_MAKE_OPTS += WITH_SECCOMP=yes
> +LIBNVIDIA_CONTAINER_DEPENDENCIES += libseccomp
> +else
> +LIBNVIDIA_CONTAINER_MAKE_OPTS += WITH_SECCOMP=no
> +endif
> +
> +define LIBNVIDIA_CONTAINER_BUILD_CMDS
> +       $(TARGET_MAKE_ENV) $(MAKE) -C $(@D) \
> +               $(LIBNVIDIA_CONTAINER_MAKE_OPTS) \
> +               shared tools
> +endef
> +
> +define LIBNVIDIA_CONTAINER_INSTALL_STAGING_CMDS
> +       $(TARGET_MAKE_ENV) $(MAKE) -C $(@D) \
> +               $(LIBNVIDIA_CONTAINER_MAKE_OPTS) \
> +               DESTDIR="$(STAGING_DIR)" \
> +               install
> +endef
> +
> +define LIBNVIDIA_CONTAINER_INSTALL_TARGET_CMDS
> +       $(TARGET_MAKE_ENV) $(MAKE) -C $(@D) \
> +               $(LIBNVIDIA_CONTAINER_MAKE_OPTS) \
> +               DESTDIR="$(TARGET_DIR)" \
> +               install
> +endef
> +
> +$(eval $(generic-package))
> --
> 2.27.0
>
> _______________________________________________
> buildroot mailing list
> buildroot@busybox.net
> http://lists.busybox.net/mailman/listinfo/buildroot
Regards,
Asaf.
Christian Stewart Aug. 8, 2020, 12:26 p.m. UTC | #2
Hi Asaf,


On Sat, Aug 8, 2020 at 1:01 AM Asaf Kahlon <asafka7@gmail.com> wrote:
> On Sun, Aug 2, 2020 at 12:37 AM Christian Stewart <christian@paral.in> wrote:
> >
> > The libnvidia-container package adds a library and CLI for GPU-backed
> > containers, agnostic to container runtime.
> >
> > https://github.com/NVIDIA/libnvidia-container
> >
> > Signed-off-by: Christian Stewart <christian@paral.in>
> > ---

[snip]

> > +
> > +diff --git a/src/nvc.c b/src/nvc.c
> > +index 35ad5be..f1d9b62 100644
> > +--- a/src/nvc.c
> > ++++ b/src/nvc.c
> > +@@ -14,8 +14,8 @@
> > + #include <stdlib.h>
> > + #include <unistd.h>
> > +
> > +-#include <pci-enum.h>
> > +-#include <nvidia-modprobe-utils.h>
> > ++#include "pci-enum.h"
> > ++#include "nvidia-modprobe-utils.h"
> > +
> > + #include "nvc_internal.h"
> > +
> > +diff --git a/src/nvidia-modprobe-utils.c b/src/nvidia-modprobe-utils.c
> > +new file mode 100644
> > +index 0000000..d3f3233
> > +--- /dev/null
> > ++++ b/src/nvidia-modprobe-utils.c
> > +@@ -0,0 +1,794 @@
> > ++
> > ++#if defined(NV_LINUX)
> > ++
> > ++#include <stdio.h>

[snip]

> > ++
> > ++    return err;
> > ++}
> > ++
> > ++#endif /* defined(NV_LINUX) */
> > +\ No newline at end of file
> > +diff --git a/src/pci-sysfs.h b/src/pci-sysfs.h
> > +new file mode 100644
> > +index 0000000..1fc695b
> > +--- /dev/null
> > ++++ b/src/pci-sysfs.h
> > +@@ -0,0 +1,85 @@

[snip]

> > ++#endif /* __PCI_SYSFS_H__ */
> > +\ No newline at end of file
> > +--
> > +2.27.0
> > +
> > diff --git a/package/libnvidia-container/0002-Remove-dependency-handling-from-Makefile.patch b/package/libnvidia-container/0002-Remove-dependency-handling-from-Makefile.patch
> > new file mode 100644
> > index 0000000000..d4ba9dfe80
> > --- /dev/null
> > +++ b/package/libnvidia-container/0002-Remove-dependency-handling-from-Makefile.patch
> > @@ -0,0 +1,698 @@
> > +From 6752d8d5e315eb3f061498a9c35558f90f9600e2 Mon Sep 17 00:00:00 2001
> > +From: Christian Stewart <christian@paral.in>
> > +Date: Sat, 18 Jul 2020 15:26:22 -0700
> > +Subject: [PATCH] Remove dependency handling from Makefile
> > +
> > +Buildroot will handle this for the makefile.
> > +
> > +Signed-off-by: Christian Stewart <christian@paral.in>
> > +---
> > + Makefile                    |  54 +++------
> > + mk/nvidia-modprobe.mk       |  55 ---------
> > + src/nvc.c                   |   6 +-
> > + src/nvidia-modprobe-utils.c | 225 ++++++++++++++++--------------------
> > + src/nvidia-modprobe-utils.h |  53 ++-------
> > + 5 files changed, 128 insertions(+), 265 deletions(-)
> > + delete mode 100644 mk/nvidia-modprobe.mk
> > +
> > +diff --git a/Makefile b/Makefile
> > +index f1c56a9..80780d1 100644
> > +--- a/Makefile
> > ++++ b/Makefile
> > +@@ -2,13 +2,13 @@

[snip]

> > +
> > + deb: DESTDIR:=$(DIST_DIR)/$(LIB_NAME)_$(VERSION)_$(ARCH)
> > +diff --git a/mk/nvidia-modprobe.mk b/mk/nvidia-modprobe.mk
> > +deleted file mode 100644
> > +index ad399de..0000000
> > +--- a/mk/nvidia-modprobe.mk
> > ++++ /dev/null
> > +@@ -1,55 +0,0 @@
> > +-#
> > +-# Copyright (c) 2017-2018, NVIDIA CORPORATION. All rights reserved.
> > +-#
> > +-
> > +-include $(MAKE_DIR)/common.mk
> > +-
> > +-##### Source definitions #####
> > +-
> > +-VERSION        := 396.51
> > +-PREFIX         := nvidia-modprobe-$(VERSION)
> > +-URL            := https://github.com/NVIDIA/nvidia-modprobe/archive/$(VERSION).tar.gz
> > +-
> > +-SRCS_DIR       := $(DEPS_DIR)/src/$(PREFIX)
> > +-MODPROBE_UTILS := $(SRCS_DIR)/modprobe-utils
> > +-
> > +-LIB_STATIC     := $(MODPROBE_UTILS)/libnvidia-modprobe-utils.a
> > +-LIB_INCS       := $(MODPROBE_UTILS)/nvidia-modprobe-utils.h \
> > +-                  $(MODPROBE_UTILS)/pci-enum.h
> > +-LIB_SRCS       := $(MODPROBE_UTILS)/nvidia-modprobe-utils.c \
> > +-                  $(MODPROBE_UTILS)/pci-sysfs.c
> > +-
> > +-##### Flags definitions #####
> > +-
> > +-ARFLAGS  := -rU
> > +-CPPFLAGS := -D_FORTIFY_SOURCE=2 -DNV_LINUX
> > +-CFLAGS   := -O2 -g -fdata-sections -ffunction-sections -fstack-protector -fno-strict-aliasing -fPIC
> > +-
> > +-##### Private rules #####
> > +-
> > +-LIB_OBJS := $(LIB_SRCS:.c=.o)
> > +-
> > +-$(SRCS_DIR)/.download_stamp:
> > +-      $(MKDIR) -p $(SRCS_DIR)
> > +-      $(CURL) --progress-bar -fSL $(URL) | \
> > +-      $(TAR) -C $(SRCS_DIR) --strip-components=1 -xz $(PREFIX)/modprobe-utils
> > +-      @touch $@
> > +-
> > +-$(LIB_SRCS): $(SRCS_DIR)/.download_stamp
> > +-
> > +-##### Public rules #####
> > +-
> > +-.PHONY: all install clean
> > +-
> > +-all: $(LIB_STATIC)
> > +-
> > +-$(LIB_STATIC): $(LIB_OBJS)
> > +-      $(AR) rs $@ $^
> > +-
> > +-install: all
> > +-      $(INSTALL) -d -m 755 $(addprefix $(DESTDIR),$(includedir) $(libdir))
> > +-      $(INSTALL) -m 644 $(LIB_INCS) $(DESTDIR)$(includedir)
> > +-      $(INSTALL) -m 644 $(LIB_STATIC) $(DESTDIR)$(libdir)
> > +-
> > +-clean:
> > +-      $(RM) $(LIB_OBJS) $(LIB_STATIC)
> > +diff --git a/src/nvc.c b/src/nvc.c
> > +index f1d9b62..74ea61c 100644
> > +--- a/src/nvc.c
> > ++++ b/src/nvc.c
> > +@@ -190,13 +190,13 @@ load_kernel_modules(struct error *err, const char *root)
> > +                 }
> > +
> > +                 log_info("loading kernel module nvidia");
> > +-                if (nvidia_modprobe(0, -1) == 0)
> > ++                if (nvidia_modprobe(0) == 0)
> > +                         log_err("could not load kernel module nvidia");
> > +                 else {
> > +-                        if (nvidia_mknod(NV_CTL_DEVICE_MINOR, -1) == 0)
> > ++                        if (nvidia_mknod(NV_CTL_DEVICE_MINOR) == 0)
> > +                                 log_err("could not create kernel module device node");
> > +                         for (int i = 0; i < (int)devs.num_matches; ++i) {
> > +-                                if (nvidia_mknod(i, -1) == 0)
> > ++                                if (nvidia_mknod(i) == 0)
> > +                                         log_err("could not create kernel module device node");
> > +                         }
> > +                 }
> > +diff --git a/src/nvidia-modprobe-utils.c b/src/nvidia-modprobe-utils.c
> > +index d3f3233..fca21cf 100644
> > +--- a/src/nvidia-modprobe-utils.c
> > ++++ b/src/nvidia-modprobe-utils.c
> > +@@ -1,3 +1,29 @@

[snip]

> > +     else
> > +     {
> > +         ret = snprintf(name,
> > +@@ -154,4 +121,4 @@ fail:
> > +     name[0] = '\0';
> > + }
> > +
> > +-#endif /* __NVIDIA_MODPROBE_UTILS_H__ */
> > +\ No newline at end of file
> > ++#endif /* __NVIDIA_MODPROBE_UTILS_H__ */
> > +--
> > +2.27.0
> > +

> I must say I don't know how to address those big patches.
> Are they a part from an upstream patch or something?

See: https://github.com/vowstar/vowstar-overlay/tree/master/app-emulation/libnvidia-container/files

The patches bring in required modprobe-utils.c pci-sysfs.c which
originate from nvidia-modprobe. As nvidia-modprobe is an executable
and not a library, and the source files for modprobe-utils are not
available anywhere in libnvidia-container (yet are referenced), I had
to add the files as a patch.

>
> > diff --git a/package/libnvidia-container/Config.in b/package/libnvidia-container/Config.in
> > new file mode 100644
> > index 0000000000..7a452c3635
> > --- /dev/null
> > +++ b/package/libnvidia-container/Config.in
> > @@ -0,0 +1,18 @@
> > +config BR2_PACKAGE_LIBNVIDIA_CONTAINER
> > +       bool "libnvidia-container"
> > +       depends on BR2_SHARED_LIBS
> > +       depends on BR2_TOOLCHAIN_HAS_THREADS # tirpc
> > +       depends on BR2_TOOLCHAIN_USES_GLIBC # fexecve
> > +       select BR2_PACKAGE_ELFUTILS
> > +       select BR2_PACKAGE_LIBCAP
> > +       select BR2_PACKAGE_LIBTIRPC
> > +       select BR2_PACKAGE_NVIDIA_MODPROBE
> > +       help
> > +         The libnvidia-container package adds a library and CLI for
> > +         GPU-backed containers, agnostic to container runtime.
> > +
> > +         https://github.com/NVIDIA/libnvidia-container
> > +
> > +comment "libnvidia-container needs a shared glibc toolchain w/ threads"
> > +       depends on !BR2_TOOLCHAIN_HAS_THREADS || !BR2_TOOLCHAN_USES_GLIBC || \
> > +               !BR2_SHARED_LIBS
> > diff --git a/package/libnvidia-container/libnvidia-container.hash b/package/libnvidia-container/libnvidia-container.hash
> > new file mode 100644
> > index 0000000000..d356eb2b1e
> > --- /dev/null
> > +++ b/package/libnvidia-container/libnvidia-container.hash
> > @@ -0,0 +1,3 @@
> > +# Locally computed:
> > +sha256 fd447629fd65d171b68edb62fa2e581c67fdb450ff540f486987ab826150d06e  libnvidia-container-1.2.0.tar.gz
> > +sha256 cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30  LICENSE
> > diff --git a/package/libnvidia-container/libnvidia-container.mk b/package/libnvidia-container/libnvidia-container.mk
> > new file mode 100644
> > index 0000000000..d337d0a528
> > --- /dev/null
> > +++ b/package/libnvidia-container/libnvidia-container.mk
> > @@ -0,0 +1,57 @@
> > +################################################################################
> > +#
> > +# libnvidia-container
> > +#
> > +################################################################################
> > +
> > +LIBNVIDIA_CONTAINER_VERSION = 1.2.0
> > +LIBNVIDIA_CONTAINER_SITE = $(call github,NVIDIA,libnvidia-container,v$(LIBNVIDIA_CONTAINER_VERSION))
> > +LIBNVIDIA_CONTAINER_LICENSE = Apache-2.0
> > +LIBNVIDIA_CONTAINER_LICENSE_FILES = LICENSE
> > +
> > +LIBNVIDIA_CONTAINER_DEPENDENCIES = elfutils libcap libtirpc nvidia-modprobe \
> > +       host-pkgconf host-elfutils host-libcap
> > +
> > +LIBNVIDIA_CONTAINER_INCLUDE_DIRS += \
> > +       -I$(HOST_DIR)/include -I$(STAGING_DIR)/usr/include \
> > +       -I$(STAGING_DIR)/usr/include/nvidia-modprobe-utils

> I don't think it's a good idea to mix include flags from both host and
> target directories.

Given that the header was included in one of the patches, these
INCLUDE_DIRS lines are actually unnecessary. (Removed for v2).


> > +LIBNVIDIA_CONTAINER_MAKE_OPTS = \
> > +       AR="$(TARGET_AR)" STRIP="$(TARGET_STRIP)" \
> > +       CC="$(TARGET_CC)" CFLAGS="$(TARGET_CFLAGS) \
> > +       $(LIBNVIDIA_CONTAINER_INCLUDE_DIRS) -D_GNU_SOURCE" \
> > +       CXX="$(TARGET_CXX)" CPPFLAGS="$(TARGET_CXXFLAGS)" \
> > +       LD="$(TARGET_LD)" LDFLAGS="$(TARGET_LDFLAGS)" \
> > +       OBJCPY="$(TARGET_OBJCOPY)" \
> > +       RPCGEN="$(HOST_DIR)/bin/rpcgen" \
> > +       WITH_LIBELF=yes \
> > +       WITH_TIRPC=no

> Most of the above definitions can be acheived from TARGET_CONFIGURE_OPTS.
> I suggest to use TARGET_CONFIGURE_OPTS and define another variable for
> the additional definitions needed by this package.

You're right, I've simplified it for V2:

LIBNVIDIA_CONTAINER_MAKE_OPTS = \
CFLAGS="$(TARGET_CFLAGS) -D_GNU_SOURCE" \
OBJCPY="$(TARGET_OBJCOPY)" \
RPCGEN="$(HOST_DIR)/bin/rpcgen" \
WITH_LIBELF=yes \
WITH_TIRPC=no

define LIBNVIDIA_CONTAINER_BUILD_CMDS
$(TARGET_MAKE_ENV) $(MAKE) -C $(@D) \
$(TARGET_CONFIGURE_OPTS) \
$(LIBNVIDIA_CONTAINER_MAKE_OPTS) \
shared tools
endef

Thanks for the review.

Best regards,
Christian
diff mbox series

Patch

diff --git a/package/Config.in b/package/Config.in
index dfa02217d9..dd1c6e1395 100644
--- a/package/Config.in
+++ b/package/Config.in
@@ -1511,6 +1511,7 @@  menu "Hardware handling"
 	source "package/libllcp/Config.in"
 	source "package/libmbim/Config.in"
 	source "package/libnfc/Config.in"
+	source "package/libnvidia-container/Config.in"
 	source "package/libpciaccess/Config.in"
 	source "package/libphidget/Config.in"
 	source "package/libpri/Config.in"
diff --git a/package/libnvidia-container/0001-Build-fixes-from-vowstar-portage-overlay.patch b/package/libnvidia-container/0001-Build-fixes-from-vowstar-portage-overlay.patch
new file mode 100644
index 0000000000..7232e76d97
--- /dev/null
+++ b/package/libnvidia-container/0001-Build-fixes-from-vowstar-portage-overlay.patch
@@ -0,0 +1,1890 @@ 
+From bd633a208446c86e11097e3cd3b019a086738ae3 Mon Sep 17 00:00:00 2001
+From: Christian Stewart <christian@paral.in>
+Date: Sun, 19 Jul 2020 09:56:42 -0700
+Subject: [PATCH] Build fixes from vowstar portage overlay
+
+This commit brings in build fixes written by @vowstar in the overlay:
+
+https://github.com/vowstar/vowstar-overlay/tree/master/app-emulation/libnvidia-container/files
+
+Signed-off-by: Christian Stewart <christian@paral.in>
+---
+ Makefile                    |  30 +-
+ mk/Dockerfile.debian        |   1 -
+ mk/Dockerfile.ubuntu        |   1 -
+ mk/common.mk                |   2 +-
+ src/nvc.c                   |   4 +-
+ src/nvidia-modprobe-utils.c | 794 ++++++++++++++++++++++++++++++++++++
+ src/nvidia-modprobe-utils.h | 157 +++++++
+ src/pci-enum.h              | 112 +++++
+ src/pci-sysfs.c             | 529 ++++++++++++++++++++++++
+ src/pci-sysfs.h             |  85 ++++
+ 10 files changed, 1696 insertions(+), 19 deletions(-)
+ create mode 100644 src/nvidia-modprobe-utils.c
+ create mode 100644 src/nvidia-modprobe-utils.h
+ create mode 100644 src/pci-enum.h
+ create mode 100644 src/pci-sysfs.c
+ create mode 100644 src/pci-sysfs.h
+
+diff --git a/Makefile b/Makefile
+index c07863b..f1c56a9 100644
+--- a/Makefile
++++ b/Makefile
+@@ -4,21 +4,21 @@
+ 
+ .PHONY: all tools shared static deps install uninstall dist depsclean mostlyclean clean distclean
+ .DEFAULT_GOAL := all
+-
++STRIP  := @echo skipping: strip
+ ##### Global variables #####
+ 
+-WITH_LIBELF  ?= no
+-WITH_TIRPC   ?= no
++WITH_LIBELF  ?= yes
++WITH_TIRPC   ?= yes
+ WITH_SECCOMP ?= yes
+ 
+ ##### Global definitions #####
+ 
+-export prefix      = /usr/local
++export prefix      = /usr
+ export exec_prefix = $(prefix)
+ export bindir      = $(exec_prefix)/bin
+-export libdir      = $(exec_prefix)/lib
++export libdir      = $(exec_prefix)/lib64
+ export docdir      = $(prefix)/share/doc
+-export libdbgdir   = $(prefix)/lib/debug$(libdir)
++export libdbgdir   = $(prefix)/lib64/debug$(libdir)
+ export includedir  = $(prefix)/include
+ export pkgconfdir  = $(libdir)/pkgconfig
+ 
+@@ -52,6 +52,8 @@ LIB_SRCS     := $(SRCS_DIR)/driver.c        \
+                 $(SRCS_DIR)/error_generic.c \
+                 $(SRCS_DIR)/error.c         \
+                 $(SRCS_DIR)/ldcache.c       \
++                $(SRCS_DIR)/pci-sysfs.c     \
++                $(SRCS_DIR)/nvidia-modprobe-utils.c \
+                 $(SRCS_DIR)/nvc.c           \
+                 $(SRCS_DIR)/nvc_ldcache.c   \
+                 $(SRCS_DIR)/nvc_info.c      \
+@@ -121,8 +123,8 @@ LDLIBS   := $(LDLIBS)
+ LIB_CPPFLAGS       = -DNV_LINUX -isystem $(DEPS_DIR)$(includedir) -include $(BUILD_DEFS)
+ LIB_CFLAGS         = -fPIC
+ LIB_LDFLAGS        = -L$(DEPS_DIR)$(libdir) -shared -Wl,-soname=$(LIB_SONAME)
+-LIB_LDLIBS_STATIC  = -l:libnvidia-modprobe-utils.a
+-LIB_LDLIBS_SHARED  = -ldl -lcap
++# LIB_LDLIBS_STATIC  = -l:libnvidia-modprobe-utils.a
++LIB_LDLIBS_SHARED  = -ldl -lcap -ltirpc
+ ifeq ($(WITH_LIBELF), yes)
+ LIB_CPPFLAGS       += -DWITH_LIBELF
+ LIB_LDLIBS_SHARED  += -lelf
+@@ -131,7 +133,7 @@ LIB_LDLIBS_STATIC  += -l:libelf.a
+ endif
+ ifeq ($(WITH_TIRPC), yes)
+ LIB_CPPFLAGS       += -isystem $(DEPS_DIR)$(includedir)/tirpc -DWITH_TIRPC
+-LIB_LDLIBS_STATIC  += -l:libtirpc.a
++# LIB_LDLIBS_STATIC  += -l:libtirpc.a
+ LIB_LDLIBS_SHARED  += -lpthread
+ endif
+ ifeq ($(WITH_SECCOMP), yes)
+@@ -146,7 +148,7 @@ LIB_LDLIBS_SHARED  += $(LDLIBS)
+ LIB_LDLIBS         = $(LIB_LDLIBS_STATIC) $(LIB_LDLIBS_SHARED)
+ 
+ # Binary flags (recursively expanded to handle target-specific flags)
+-BIN_CPPFLAGS       = -include $(BUILD_DEFS) $(CPPFLAGS)
++BIN_CPPFLAGS       = -include $(BUILD_DEFS) $(CPPFLAGS) -DWITH_TIRPC
+ BIN_CFLAGS         = -I$(SRCS_DIR) -fPIE -flto $(CFLAGS)
+ BIN_LDFLAGS        = -L. -pie $(LDFLAGS) -Wl,-rpath='$$ORIGIN/../$$LIB'
+ BIN_LDLIBS         = -l:$(LIB_SHARED) -lcap $(LDLIBS)
+@@ -220,12 +222,12 @@ static: $(LIB_STATIC)($(LIB_STATIC_OBJ))
+ deps: export DESTDIR:=$(DEPS_DIR)
+ deps: $(LIB_RPC_SRCS) $(BUILD_DEFS)
+ 	$(MKDIR) -p $(DEPS_DIR)
+-	$(MAKE) -f $(MAKE_DIR)/nvidia-modprobe.mk install
++	# $(MAKE) -f $(MAKE_DIR)/nvidia-modprobe.mk install
+ ifeq ($(WITH_LIBELF), no)
+-	$(MAKE) -f $(MAKE_DIR)/elftoolchain.mk install
++	# $(MAKE) -f $(MAKE_DIR)/elftoolchain.mk install
+ endif
+ ifeq ($(WITH_TIRPC), yes)
+-	$(MAKE) -f $(MAKE_DIR)/libtirpc.mk install
++	# $(MAKE) -f $(MAKE_DIR)/libtirpc.mk install
+ endif
+ 
+ install: all
+@@ -238,7 +240,7 @@ install: all
+ 	$(LN) -sf $(LIB_SONAME) $(DESTDIR)$(libdir)/$(LIB_SYMLINK)
+ 	$(LDCONFIG) -n $(DESTDIR)$(libdir)
+ 	# Install debugging symbols
+-	$(INSTALL) -m 644 $(DEBUG_DIR)/$(LIB_SONAME) $(DESTDIR)$(libdbgdir)
++	# $(INSTALL) -m 644 $(DEBUG_DIR)/$(LIB_SONAME) $(DESTDIR)$(libdbgdir)
+ 	# Install configuration files
+ 	$(MAKE_DIR)/$(LIB_PKGCFG).in "$(strip $(VERSION))" "$(strip $(LIB_LDLIBS_SHARED))" > $(DESTDIR)$(pkgconfdir)/$(LIB_PKGCFG)
+ 	# Install binary files
+diff --git a/mk/Dockerfile.debian b/mk/Dockerfile.debian
+index 8e8a560..096e1d0 100644
+--- a/mk/Dockerfile.debian
++++ b/mk/Dockerfile.debian
+@@ -41,7 +41,6 @@ RUN make distclean && make -j"$(nproc)"
+ ENV DIST_DIR /dist
+ VOLUME $DIST_DIR
+ CMD bash -c " \
+-        export DISTRIB=$(lsb_release -c -s); \
+         export SECTION="" \
+         make dist; \
+         make deb; \
+diff --git a/mk/Dockerfile.ubuntu b/mk/Dockerfile.ubuntu
+index 8b8cea8..d1f64d0 100644
+--- a/mk/Dockerfile.ubuntu
++++ b/mk/Dockerfile.ubuntu
+@@ -40,7 +40,6 @@ RUN make distclean && make -j"$(nproc)"
+ ENV DIST_DIR /dist
+ VOLUME $DIST_DIR
+ CMD bash -c " \
+-        export DISTRIB=$(lsb_release -c -s); \
+         export SECTION="" \
+         make dist; \
+         make deb; \
+diff --git a/mk/common.mk b/mk/common.mk
+index 73bc27a..cdd93c9 100644
+--- a/mk/common.mk
++++ b/mk/common.mk
+@@ -21,7 +21,7 @@ DOCKER   ?= docker
+ UID      := $(shell id -u)
+ GID      := $(shell id -g)
+ DATE     := $(shell date -u --iso-8601=minutes)
+-REVISION := $(shell git rev-parse HEAD)
++REVISION := 61f82bf25f0b3afaa75c6df8a0a6551ecfdf81f4
+ COMPILER := $(realpath $(shell which $(CC)))
+ PLATFORM ?= $(shell uname -m)
+ 
+diff --git a/src/nvc.c b/src/nvc.c
+index 35ad5be..f1d9b62 100644
+--- a/src/nvc.c
++++ b/src/nvc.c
+@@ -14,8 +14,8 @@
+ #include <stdlib.h>
+ #include <unistd.h>
+ 
+-#include <pci-enum.h>
+-#include <nvidia-modprobe-utils.h>
++#include "pci-enum.h"
++#include "nvidia-modprobe-utils.h"
+ 
+ #include "nvc_internal.h"
+ 
+diff --git a/src/nvidia-modprobe-utils.c b/src/nvidia-modprobe-utils.c
+new file mode 100644
+index 0000000..d3f3233
+--- /dev/null
++++ b/src/nvidia-modprobe-utils.c
+@@ -0,0 +1,794 @@
++
++#if defined(NV_LINUX)
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <unistd.h>
++#include <errno.h>
++#include <sys/types.h>
++#include <sys/wait.h>
++#include <fcntl.h>
++
++#include "nvidia-modprobe-utils.h"
++#include "pci-enum.h"
++
++#define NV_PROC_MODPROBE_PATH "/proc/sys/kernel/modprobe"
++#define NV_PROC_MODULES_PATH "/proc/modules"
++#define NV_PROC_DEVICES_PATH "/proc/devices"
++
++#define NV_PROC_MODPROBE_PATH_MAX        1024
++#define NV_MAX_MODULE_NAME_SIZE          16
++#define NV_MAX_PROC_REGISTRY_PATH_SIZE   NV_MAX_CHARACTER_DEVICE_FILE_STRLEN
++#define NV_MAX_LINE_LENGTH               256
++
++#define NV_NVIDIA_MODULE_NAME "nvidia"
++#define NV_PROC_REGISTRY_PATH "/proc/driver/nvidia/params"
++
++#define NV_NMODULE_NVIDIA_MODULE_NAME "nvidia%d"
++#define NV_NMODULE_PROC_REGISTRY_PATH "/proc/driver/nvidia/%d/params"
++
++#define NV_UVM_MODULE_NAME "nvidia-uvm"
++#define NV_UVM_DEVICE_NAME "/dev/nvidia-uvm"
++#define NV_UVM_TOOLS_DEVICE_NAME "/dev/nvidia-uvm-tools"
++
++#define NV_MODESET_MODULE_NAME "nvidia-modeset"
++
++#define NV_VGPU_VFIO_MODULE_NAME "nvidia-vgpu-vfio"
++
++#define NV_NVLINK_MODULE_NAME "nvidia-nvlink"
++#define NV_NVLINK_PROC_PERM_PATH "/proc/driver/nvidia-nvlink/permissions"
++
++#define NV_DEVICE_FILE_MODE_MASK (S_IRWXU|S_IRWXG|S_IRWXO)
++#define NV_DEVICE_FILE_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
++#define NV_DEVICE_FILE_UID 0
++#define NV_DEVICE_FILE_GID 0
++
++#define NV_MAKE_DEVICE(x,y) ((dev_t)((x) << 8 | (y)))
++
++#define NV_MAJOR_DEVICE_NUMBER 195
++
++#define NV_PCI_VENDOR_ID    0x10DE
++
++#define NV_MIN(a, b) (((a) < (b)) ? (a) : (b))
++
++/*
++ * Construct the nvidia kernel module name based on the input
++ * module instance provided.  If an error occurs, the null
++ * terminator will be written to nv_module_name[0].
++ */
++static __inline__ void assign_nvidia_kernel_module_name
++(
++    char nv_module_name[NV_MAX_MODULE_NAME_SIZE],
++    int module_instance
++)
++{
++    int ret;
++
++    if (is_multi_module(module_instance))
++    {
++        ret = snprintf(nv_module_name, NV_MAX_MODULE_NAME_SIZE,
++                       NV_NMODULE_NVIDIA_MODULE_NAME, module_instance);
++    }
++    else
++    {
++        ret = snprintf(nv_module_name, NV_MAX_MODULE_NAME_SIZE,
++                       NV_NVIDIA_MODULE_NAME);
++    }
++
++    if (ret <= 0)
++    {
++        goto fail;
++    }
++
++    nv_module_name[NV_MAX_MODULE_NAME_SIZE - 1] = '\0';
++
++    return;
++
++fail:
++
++    nv_module_name[0] = '\0';
++}
++
++
++/*
++ * Construct the proc registry path name based on the input
++ * module instance provided.  If an error occurs, the null
++ * terminator will be written to proc_path[0].
++ */
++static __inline__ void assign_proc_registry_path
++(
++    char proc_path[NV_MAX_PROC_REGISTRY_PATH_SIZE],
++    int module_instance
++)
++{
++    int ret;
++
++    if (is_multi_module(module_instance))
++    {
++        ret = snprintf(proc_path, NV_MAX_PROC_REGISTRY_PATH_SIZE,
++                       NV_NMODULE_PROC_REGISTRY_PATH, module_instance);
++    }
++    else
++    {
++        ret = snprintf(proc_path, NV_MAX_PROC_REGISTRY_PATH_SIZE,
++                       NV_PROC_REGISTRY_PATH);
++    }
++
++    if (ret <= 0)
++    {
++        goto fail;
++    }
++
++    proc_path[NV_MAX_PROC_REGISTRY_PATH_SIZE - 1] = '\0';
++
++    return;
++
++fail:
++
++    proc_path[0] = '\0';
++}
++
++
++/*
++ * Just like strcmp(3), except that differences between '-' and '_' are
++ * ignored. This is useful for comparing module names, where '-' and '_'
++ * are supposed to be treated interchangeably.
++ */
++static int modcmp(const char *a, const char *b)
++{
++    int i;
++
++    /* Walk both strings and compare each character */
++    for (i = 0; a[i] && b[i]; i++)
++    {
++        if (a[i] != b[i])
++        {
++            /* ignore differences between '-' and '_' */
++            if (((a[i] == '-') || (a[i] == '_')) &&
++                ((b[i] == '-') || (b[i] == '_')))
++            {
++                continue;
++            }
++
++            break;
++        }
++    }
++
++    /*
++     * If the strings are of unequal length, only one of a[i] or b[i] == '\0'.
++     * If they are the same length, both will be '\0', and the strings match.
++     */
++    return a[i] - b[i];
++}
++
++
++/*
++ * Check whether the specified module is loaded by reading
++ * NV_PROC_MODULES_PATH; returns 1 if the kernel module is loaded.
++ * Otherwise, it returns 0.
++ */
++static int is_kernel_module_loaded(const char *nv_module_name)
++{
++    FILE *fp;
++    char module_name[NV_MAX_MODULE_NAME_SIZE];
++    int module_loaded = 0;
++
++    fp = fopen(NV_PROC_MODULES_PATH, "r");
++
++    if (fp == NULL)
++    {
++        return 0;
++    }
++
++    while (fscanf(fp, "%15s%*[^\n]\n", module_name) == 1)
++    {
++        module_name[15] = '\0';
++        if (modcmp(module_name, nv_module_name) == 0)
++        {
++            module_loaded = 1;
++            break;
++        }
++    }
++
++    fclose(fp);
++
++    return module_loaded;
++}
++
++/*
++ * Attempt to redirect STDOUT and STDERR to /dev/null.
++ *
++ * This is only for the cosmetics of silencing warnings, so do not
++ * treat any errors here as fatal.
++ */
++static void silence_current_process(void)
++{
++    int dev_null_fd = open("/dev/null", O_RDWR);
++    if (dev_null_fd < 0)
++    {
++        return;
++    }
++
++    dup2(dev_null_fd, STDOUT_FILENO);
++    dup2(dev_null_fd, STDERR_FILENO);
++    close(dev_null_fd);
++}
++
++/*
++ * Attempt to load a kernel module; returns 1 if kernel module is
++ * successfully loaded.  Returns 0 if the kernel module could not be
++ * loaded.
++ *
++ * If any error is encountered and print_errors is non-0, then print the
++ * error to stderr.
++ */
++static int modprobe_helper(const int print_errors, const char *module_name)
++{
++    char modprobe_path[NV_PROC_MODPROBE_PATH_MAX];
++    int status = 1;
++    struct stat file_status;
++    pid_t pid;
++    const char *envp[] = { "PATH=/sbin", NULL };
++    FILE *fp;
++
++    /*
++     * Use PCI_BASE_CLASS_MASK to cover both types of DISPLAY controllers that
++     * NVIDIA ships (VGA = 0x300 and 3D = 0x302).
++     */
++    struct pci_id_match id_match = {
++        NV_PCI_VENDOR_ID,       /* Vendor ID    = 0x10DE                 */
++        PCI_MATCH_ANY,          /* Device ID    = any                    */
++        PCI_MATCH_ANY,          /* Subvendor ID = any                    */
++        PCI_MATCH_ANY,          /* Subdevice ID = any                    */
++        0x0300,                 /* Device Class = PCI_BASE_CLASS_DISPLAY */
++        PCI_BASE_CLASS_MASK,    /* Display Mask = base class only        */
++        0                       /* Initial number of matches             */
++    };
++
++    modprobe_path[0] = '\0';
++
++    if (module_name == NULL || module_name[0] == '\0') {
++        return 0;
++    }
++
++    /* If the kernel module is already loaded, nothing more to do: success. */
++
++    if (is_kernel_module_loaded(module_name))
++    {
++        return 1;
++    }
++
++    /*
++     * Before attempting to load the module, look for any NVIDIA PCI devices.
++     * If none exist, exit instead of attempting the modprobe, because doing so
++     * would issue error messages that are really irrelevant if there are no
++     * NVIDIA PCI devices present.
++     *
++     * If our check fails, for whatever reason, continue with the modprobe just
++     * in case.
++     */
++    status = pci_enum_match_id(&id_match);
++    if (status == 0 && id_match.num_matches == 0)
++    {
++        if (print_errors)
++        {
++            fprintf(stderr,
++                    "NVIDIA: no NVIDIA devices found\n");
++        }
++
++        return 0;
++    }
++
++    /* Only attempt to load the kernel module if root. */
++
++    if (geteuid() != 0)
++    {
++        return 0;
++    }
++
++    /* Attempt to read the full path to the modprobe executable from /proc. */
++
++    fp = fopen(NV_PROC_MODPROBE_PATH, "r");
++    if (fp != NULL)
++    {
++        char *str;
++        size_t n;
++
++        n = fread(modprobe_path, 1, sizeof(modprobe_path), fp);
++
++        /*
++         * Null terminate the string, but make sure 'n' is in the range
++         * [0, sizeof(modprobe_path)-1].
++         */
++        n = NV_MIN(n, sizeof(modprobe_path) - 1);
++        modprobe_path[n] = '\0';
++
++        /*
++         * If str was longer than a line, we might still have a
++         * newline in modprobe_path: if so, overwrite it with the nul
++         * terminator.
++         */
++        str = strchr(modprobe_path, '\n');
++        if (str != NULL)
++        {
++            *str = '\0';
++        }
++
++        fclose(fp);
++    }
++
++    /* If we couldn't read it from /proc, pick a reasonable default. */
++
++    if (modprobe_path[0] == '\0')
++    {
++        sprintf(modprobe_path, "/sbin/modprobe");
++    }
++
++    /* Do not attempt to exec(3) modprobe if it does not exist. */
++
++    if (stat(modprobe_path, &file_status) != 0 ||
++        !S_ISREG(file_status.st_mode) ||
++        (file_status.st_mode & S_IXUSR) != S_IXUSR)
++    {
++        return 0;
++    }
++
++    /* Fork and exec modprobe from the child process. */
++
++    switch (pid = fork())
++    {
++        case 0:
++
++            /*
++             * modprobe might complain in expected scenarios.  E.g.,
++             * `modprobe nvidia` on a Tegra system with dGPU where no nvidia.ko is
++             * present will complain:
++             *
++             *  "modprobe: FATAL: Module nvidia not found."
++             *
++             * Silence the current process to avoid such unwanted messages.
++             */
++            silence_current_process();
++
++            execle(modprobe_path, "modprobe",
++                   module_name, NULL, envp);
++
++            /* If execl(3) returned, then an error has occurred. */
++
++            if (print_errors)
++            {
++                fprintf(stderr,
++                        "NVIDIA: failed to execute `%s`: %s.\n",
++                        modprobe_path, strerror(errno));
++            }
++            exit(1);
++
++        case -1:
++            return 0;
++
++        default:
++            if (waitpid(pid, &status, 0) < 0)
++            {
++                return 0;
++            }
++            if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
++            {
++                return 1;
++            }
++            else
++            {
++                return 0;
++            }
++    }
++
++    return 1;
++}
++
++
++/*
++ * Attempt to load an NVIDIA kernel module
++ */
++int nvidia_modprobe(const int print_errors, int module_instance)
++{
++    char nv_module_name[NV_MAX_MODULE_NAME_SIZE];
++
++    assign_nvidia_kernel_module_name(nv_module_name, module_instance);
++
++    return modprobe_helper(print_errors, nv_module_name);
++}
++
++
++/*
++ * Determine the requested device file parameters: allow users to
++ * override the default UID/GID and/or mode of the NVIDIA device
++ * files, or even whether device file modification should be allowed;
++ * the attributes are managed globally, and can be adjusted via the
++ * appropriate kernel module parameters.
++ */
++static void init_device_file_parameters(uid_t *uid, gid_t *gid, mode_t *mode,
++                                        int *modify, const char *proc_path)
++{
++    FILE *fp;
++    char name[32];
++    unsigned int value;
++
++    *mode = NV_DEVICE_FILE_MODE;
++    *uid = NV_DEVICE_FILE_UID;
++    *gid = NV_DEVICE_FILE_GID;
++    *modify = 1;
++
++    if (proc_path == NULL || proc_path[0] == '\0')
++    {
++        return;
++    }
++
++    fp = fopen(proc_path, "r");
++
++    if (fp == NULL)
++    {
++        return;
++    }
++
++    while (fscanf(fp, "%31[^:]: %u\n", name, &value) == 2)
++    {
++        name[31] = '\0';
++        if (strcmp(name, "DeviceFileUID") == 0)
++        {
++            *uid = value;
++        }
++        if (strcmp(name, "DeviceFileGID") == 0)
++        {
++            *gid = value;
++        }
++        if (strcmp(name, "DeviceFileMode") == 0)
++        {
++            *mode = value;
++        }
++        if (strcmp(name, "ModifyDeviceFiles") == 0)
++        {
++            *modify = value;
++        }
++    }
++
++    fclose(fp);
++}
++
++/* 
++ * A helper to query device file states.
++ */
++static int get_file_state_helper(
++    const char *path,
++    int major,
++    int minor,
++    const char *proc_path,
++    uid_t uid,
++    gid_t gid,
++    mode_t mode)
++{
++    dev_t dev = NV_MAKE_DEVICE(major, minor);
++    struct stat stat_buf;
++    int ret;
++    int state = 0;
++
++    ret = stat(path, &stat_buf);
++    if (ret == 0)
++    {
++        nvidia_update_file_state(&state, NvDeviceFileStateFileExists);
++
++        if (S_ISCHR(stat_buf.st_mode) && (stat_buf.st_rdev == dev))
++        {
++            nvidia_update_file_state(&state, NvDeviceFileStateChrDevOk);
++        }
++
++        if (((stat_buf.st_mode & NV_DEVICE_FILE_MODE_MASK) == mode) &&
++            (stat_buf.st_uid == uid) &&
++            (stat_buf.st_gid == gid))
++        {
++            nvidia_update_file_state(&state, NvDeviceFileStatePermissionsOk);
++        }
++    }
++
++    return state;
++}
++
++int nvidia_get_file_state(int minor, int module_instance)
++{
++    char path[NV_MAX_CHARACTER_DEVICE_FILE_STRLEN];
++    char proc_path[NV_MAX_PROC_REGISTRY_PATH_SIZE];
++    mode_t mode;
++    uid_t uid;
++    gid_t gid;
++    int modification_allowed;
++    int state = 0;
++
++    assign_device_file_name(path, minor, module_instance);
++    assign_proc_registry_path(proc_path, module_instance);
++
++    init_device_file_parameters(&uid, &gid, &mode, &modification_allowed,
++                                proc_path);
++
++    state = get_file_state_helper(path, NV_MAJOR_DEVICE_NUMBER, minor,
++                                  proc_path, uid, gid, mode);
++
++    return state;
++}
++
++/*
++ * Attempt to create the specified device file with the specified major
++ * and minor number.  If proc_path is specified, scan it for custom file
++ * permissions.  Returns 1 if the file is successfully created; returns 0
++ * if the file could not be created.
++ */
++int mknod_helper(int major, int minor, const char *path,
++                 const char *proc_path)
++{
++    dev_t dev = NV_MAKE_DEVICE(major, minor);
++    mode_t mode;
++    uid_t uid;
++    gid_t gid;
++    int modification_allowed;
++    int ret;
++    int state;
++    int do_mknod;
++
++    if (path == NULL || path[0] == '\0')
++    {
++        return 0;
++    }
++
++    init_device_file_parameters(&uid, &gid, &mode, &modification_allowed,
++                                proc_path);
++
++    /* If device file modification is not allowed, nothing to do: success. */
++
++    if (modification_allowed != 1)
++    {
++        return 1;
++    }
++
++    state = get_file_state_helper(path, major, minor,
++                                  proc_path, uid, gid, mode);
++
++    if (nvidia_test_file_state(state, NvDeviceFileStateFileExists) &&
++        nvidia_test_file_state(state, NvDeviceFileStateChrDevOk) &&
++        nvidia_test_file_state(state, NvDeviceFileStatePermissionsOk))
++    {
++        return 1;
++    }
++
++    /* If the stat(2) above failed, we need to create the device file. */
++
++    do_mknod = 0;
++
++    if (!nvidia_test_file_state(state, NvDeviceFileStateFileExists))
++    {
++        do_mknod = 1;
++    }
++
++    /*
++     * If the file exists but the file is either not a character device or has
++     * the wrong major/minor character device number, then we need to delete it
++     * and recreate it.
++     */
++    if (!do_mknod &&
++        !nvidia_test_file_state(state, NvDeviceFileStateChrDevOk))
++    {
++        ret = remove(path);
++        if (ret != 0)
++        {
++            return 0;
++        }
++        do_mknod = 1;
++    }
++
++    if (do_mknod)
++    {
++        ret = mknod(path, S_IFCHR | mode, dev);
++        if (ret != 0)
++        {
++            return 0;
++        }
++    }
++
++    /*
++     * Make sure the permissions and ownership are set correctly; if
++     * we created the device above and either of the below fails, then
++     * also delete the device file.
++     */
++    if ((chmod(path, mode) != 0) ||
++        (chown(path, uid, gid) != 0))
++    {
++        if (do_mknod)
++        {
++            remove(path);
++        }
++        return 0;
++    }
++
++    return 1;
++}
++
++
++/*
++ * Attempt to create a device file with the specified minor number for
++ * the specified NVIDIA module instance.
++ */
++int nvidia_mknod(int minor, int module_instance)
++{
++    char path[NV_MAX_CHARACTER_DEVICE_FILE_STRLEN];
++    char proc_path[NV_MAX_PROC_REGISTRY_PATH_SIZE];
++
++    assign_device_file_name(path, minor, module_instance);
++    assign_proc_registry_path(proc_path, module_instance);
++
++    return mknod_helper(NV_MAJOR_DEVICE_NUMBER, minor, path, proc_path);
++}
++
++
++/*
++ * Scan NV_PROC_DEVICES_PATH to find the major number of the character
++ * device with the specified name.  Returns the major number on success,
++ * or -1 on failure.
++ */
++int get_chardev_major(const char *name)
++{
++    int ret = -1;
++    char line[NV_MAX_LINE_LENGTH];
++    FILE *fp;
++
++    line[NV_MAX_LINE_LENGTH - 1] = '\0';
++
++    fp = fopen(NV_PROC_DEVICES_PATH, "r");
++    if (!fp)
++    {
++        goto done;
++    }
++
++    /* Find the beginning of the 'Character devices:' section */
++
++    while (fgets(line, NV_MAX_LINE_LENGTH - 1, fp))
++    {
++        if (strcmp(line, "Character devices:\n") == 0)
++        {
++            break;
++        }
++    }
++
++    if (ferror(fp)) {
++        goto done;
++    }
++
++    /* Search for the given module name */
++
++    while (fgets(line, NV_MAX_LINE_LENGTH - 1, fp))
++    {
++        char *found;
++
++        if (strcmp(line, "\n") == 0 )
++        {
++            /* we've reached the end of the 'Character devices:' section */
++            break;
++        }
++
++        found = strstr(line, name);
++
++        /* Check for a newline to avoid partial matches */
++
++        if (found && found[strlen(name)] == '\n')
++        {
++            int major;
++
++            /* Read the device major number */
++
++            if (sscanf(line, " %d %*s", &major) == 1)
++            {
++                ret = major;
++            }
++
++            break;
++        }
++    }
++
++done:
++
++    if (fp)
++    {
++        fclose(fp);
++    }
++
++    return ret;
++}
++
++
++/*
++ * Attempt to create the NVIDIA Unified Memory device file
++ */
++int nvidia_uvm_mknod(int base_minor)
++{
++    int major = get_chardev_major(NV_UVM_MODULE_NAME);
++
++    if (major < 0)
++    {
++        return 0;
++    }
++
++    return mknod_helper(major, base_minor, NV_UVM_DEVICE_NAME, NULL) &&
++           mknod_helper(major, base_minor + 1, NV_UVM_TOOLS_DEVICE_NAME, NULL);
++}
++
++
++/*
++ * Attempt to load the NVIDIA Unified Memory kernel module
++ */
++int nvidia_uvm_modprobe(void)
++{
++    return modprobe_helper(0, NV_UVM_MODULE_NAME);
++}
++
++
++/*
++ * Attempt to load the NVIDIA modeset driver.
++ */
++int nvidia_modeset_modprobe(void)
++{
++    return modprobe_helper(0, NV_MODESET_MODULE_NAME);
++}
++
++
++/*
++ * Attempt to create the NVIDIA modeset driver device file.
++ */
++int nvidia_modeset_mknod(void)
++{
++    char proc_path[NV_MAX_PROC_REGISTRY_PATH_SIZE];
++
++    assign_proc_registry_path(proc_path, NV_MODULE_INSTANCE_NONE);
++
++    return mknod_helper(NV_MAJOR_DEVICE_NUMBER,
++                        NV_MODESET_MINOR_DEVICE_NUM,
++                        NV_MODESET_DEVICE_NAME, proc_path);
++}
++
++/*
++ * Attempt to create the NVIDIA NVLink driver device file.
++ */
++int nvidia_nvlink_mknod(void)
++{
++    int major = get_chardev_major(NV_NVLINK_MODULE_NAME);
++
++    if (major < 0)
++    {
++        return 0;
++    }
++
++    return mknod_helper(major,
++                        0,
++                        NV_NVLINK_DEVICE_NAME,
++                        NV_NVLINK_PROC_PERM_PATH);
++}
++
++int nvidia_vgpu_vfio_mknod(int minor_num)
++{
++    int major = get_chardev_major(NV_VGPU_VFIO_MODULE_NAME);
++    char vgpu_dev_name[NV_MAX_CHARACTER_DEVICE_FILE_STRLEN];
++    char proc_path[NV_MAX_PROC_REGISTRY_PATH_SIZE];
++
++    if (major < 0)
++    {
++        return 0;
++    }
++
++    assign_proc_registry_path(proc_path, NV_MODULE_INSTANCE_NONE);
++
++    snprintf(vgpu_dev_name, NV_MAX_CHARACTER_DEVICE_FILE_STRLEN,
++             NV_VGPU_VFIO_DEVICE_NAME, minor_num);
++
++    vgpu_dev_name[NV_MAX_CHARACTER_DEVICE_FILE_STRLEN - 1] = '\0';
++
++    return mknod_helper(major, minor_num, vgpu_dev_name, proc_path);
++}
++
++#endif /* NV_LINUX */
+\ No newline at end of file
+diff --git a/src/nvidia-modprobe-utils.h b/src/nvidia-modprobe-utils.h
+new file mode 100644
+index 0000000..e06b4a4
+--- /dev/null
++++ b/src/nvidia-modprobe-utils.h
+@@ -0,0 +1,157 @@
++/*
++ * Copyright (c) 2013, NVIDIA CORPORATION.
++ *
++ * Permission is hereby granted, free of charge, to any person
++ * obtaining a copy of this software and associated documentation
++ * files (the "Software"), to deal in the Software without
++ * restriction, including without limitation the rights to use, copy,
++ * modify, merge, publish, distribute, sublicense, and/or sell copies
++ * of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be
++ * included in all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
++ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
++ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
++ * SOFTWARE.
++ *
++ * This file provides utility functions on Linux for loading the
++ * NVIDIA kernel module and creating NVIDIA device files.
++ */
++
++#ifndef __NVIDIA_MODPROBE_UTILS_H__
++#define __NVIDIA_MODPROBE_UTILS_H__
++
++#include <stdio.h>
++
++#define NV_MAX_CHARACTER_DEVICE_FILE_STRLEN  128
++#define NV_MODULE_INSTANCE_NONE              -1
++#define NV_MODULE_INSTANCE_ZERO              0
++#define NV_MAX_MODULE_INSTANCES              8
++#define NV_CTL_DEVICE_NUM                    255
++#define NV_MODESET_MINOR_DEVICE_NUM          254
++
++#define NV_FRONTEND_CONTROL_DEVICE_MINOR_MAX NV_CTL_DEVICE_NUM
++
++#define NV_DEVICE_FILE_PATH "/dev/nvidia%d"
++#define NV_CTRL_DEVICE_FILE_PATH "/dev/nvidiactl"
++#define NV_MODESET_DEVICE_NAME "/dev/nvidia-modeset"
++#define NV_VGPU_VFIO_DEVICE_NAME "/dev/nvidia-vgpu%d"
++#define NV_NVLINK_DEVICE_NAME "/dev/nvidia-nvlink"
++
++#define NV_NMODULE_CTRL_DEVICE_FILE_PATH "/dev/nvidiactl%d"
++
++#define NV_FRONTEND_CONTROL_DEVICE_MINOR_MIN \
++    (NV_FRONTEND_CONTROL_DEVICE_MINOR_MAX - \
++     NV_MAX_MODULE_INSTANCES)
++
++#define NV_FRONTEND_IS_CONTROL_DEVICE(x) \
++    ((x <= NV_FRONTEND_CONTROL_DEVICE_MINOR_MAX) && \
++     (x > NV_FRONTEND_CONTROL_DEVICE_MINOR_MIN))
++
++#if defined(NV_LINUX)
++
++typedef enum
++{
++    NvDeviceFileStateFileExists = 0,
++    NvDeviceFileStateChrDevOk,
++    NvDeviceFileStatePermissionsOk
++} NvDeviceFileState;
++
++static __inline__ void nvidia_update_file_state(int *state,
++                                                NvDeviceFileState value)
++{
++    *state |= (1 << value);
++}
++
++static __inline__ int nvidia_test_file_state(int state,
++                                             NvDeviceFileState value)
++{
++    return !!(state & (1 << value));
++}
++
++int nvidia_get_file_state(int minor, int module_instance);
++int nvidia_modprobe(const int print_errors, int module_instance);
++int nvidia_mknod(int minor, int module_instance);
++int nvidia_uvm_modprobe(void);
++int nvidia_uvm_mknod(int base_minor);
++int nvidia_modeset_modprobe(void);
++int nvidia_modeset_mknod(void);
++int nvidia_vgpu_vfio_mknod(int minor_num);
++int nvidia_nvlink_mknod(void);
++
++int mknod_helper(int major, int minor, const char *path, const char *proc_path);
++int get_chardev_major(const char *name);
++
++#endif /* NV_LINUX */
++
++/*
++ * Detect use of multiple kernel module instances. Use a single 
++ * module instance unless instance != NV_MODULE_INSTANCE_NONE
++ */
++static __inline__ int is_multi_module(int module_instance)
++{
++    return (module_instance != NV_MODULE_INSTANCE_NONE);
++}
++
++
++/*
++ * Construct the device file name, based on 'minor'.  If an error
++ * occurs, the nul terminator will be written to name[0].
++ */
++static __inline__ void assign_device_file_name
++(
++    char name[NV_MAX_CHARACTER_DEVICE_FILE_STRLEN],
++    int minor,
++    int module_instance
++)
++{
++    int ret;
++
++    if ((minor < 0) || (minor > NV_CTL_DEVICE_NUM))
++    {
++        goto fail;
++    }
++
++    if (!is_multi_module(module_instance) && minor == NV_CTL_DEVICE_NUM)
++    {
++        ret = snprintf(name,
++                       NV_MAX_CHARACTER_DEVICE_FILE_STRLEN,
++                       NV_CTRL_DEVICE_FILE_PATH);
++    }
++    else if (is_multi_module(module_instance) && 
++             NV_FRONTEND_IS_CONTROL_DEVICE(minor))
++    {
++        ret = snprintf(name,
++                       NV_MAX_CHARACTER_DEVICE_FILE_STRLEN,
++                       NV_NMODULE_CTRL_DEVICE_FILE_PATH,
++                       module_instance);
++    }
++    else
++    {
++        ret = snprintf(name,
++                       NV_MAX_CHARACTER_DEVICE_FILE_STRLEN,
++                       NV_DEVICE_FILE_PATH, minor);
++    }
++
++    if (ret <= 0)
++    {
++        goto fail;
++    }
++
++    name[NV_MAX_CHARACTER_DEVICE_FILE_STRLEN - 1] = '\0';
++
++    return;
++
++fail:
++
++    name[0] = '\0';
++}
++
++#endif /* __NVIDIA_MODPROBE_UTILS_H__ */
+\ No newline at end of file
+diff --git a/src/pci-enum.h b/src/pci-enum.h
+new file mode 100644
+index 0000000..73b8497
+--- /dev/null
++++ b/src/pci-enum.h
+@@ -0,0 +1,112 @@
++/*
++ * (C) Copyright IBM Corporation 2006
++ *
++ * Copyright (c) 2007 Paulo R. Zanoni, Tiago Vignatti
++ *
++ * Copyright 2009 Red Hat, Inc.
++ *
++ * Copyright (c) 2014 NVIDIA Corporation
++ *
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ * 
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ * 
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++
++/**
++ * pci-enum.h
++ * 
++ * Based on libpciaccess/include/pciaccess.h from libpciaccess-0.12.1, which
++ * can be found here:
++ *
++ * http://cgit.freedesktop.org/xorg/lib/libpciaccess
++ *
++ * Original authors: Ian Romanick <idr@us.ibm.com>, Paulo R. Zanoni,
++ *                   Tiago Vignatti
++ */
++
++#ifndef PCI_ENUM_H
++#define PCI_ENUM_H
++
++#include <inttypes.h>
++
++struct pci_id_match;
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++int pci_enum_match_id(struct pci_id_match *);
++
++#ifdef __cplusplus
++}
++#endif
++
++#define PCI_MATCH_ANY  (~0U)
++
++#define PCI_BASE_CLASS_MASK 0xff00
++#define PCI_SUB_CLASS_MASK  0x00ff
++#define PCI_FULL_CLASS_MASK PCI_BASE_CLASS_MASK | PCI_SUB_CLASS_MASK
++
++/**
++ * Compare two PCI ID values (either vendor or device).  This is used
++ * internally to compare the fields of pci_id_match to the fields of
++ * pci_device.
++ */
++#define PCI_ID_COMPARE(a, b) \
++    (((a) == PCI_MATCH_ANY) || ((a) == (b)))
++
++/**
++ */
++struct pci_id_match {
++    /**
++     * Device/vendor matching controls
++     * 
++     * Control the search based on the device, vendor, subdevice, or subvendor
++     * IDs.  Setting any of these fields to PCI_MATCH_ANY will cause the field
++     * to not be used in the comparison.
++     */
++    /*@{*/
++    uint32_t    vendor_id;
++    uint32_t    device_id;
++    uint32_t    subvendor_id;
++    uint32_t    subdevice_id;
++    /*@}*/
++
++
++    /**
++     * Device class matching controls
++     * 
++     * Device's class and subclass. The class is at bits [15:8], subclass is at
++     * bits [7:0].
++     */
++    /*@{*/
++    uint16_t    device_class;
++    uint16_t    device_class_mask;
++    /*@}*/
++
++    /**
++     * Match results
++     *
++     * Specifies the number of devices found that match this criteria.
++     */
++    /*@{*/
++    uint16_t    num_matches;
++};
++
++#endif /* PCI_ENUM_H */
+\ No newline at end of file
+diff --git a/src/pci-sysfs.c b/src/pci-sysfs.c
+new file mode 100644
+index 0000000..210bf40
+--- /dev/null
++++ b/src/pci-sysfs.c
+@@ -0,0 +1,529 @@
++/*
++ * (C) Copyright IBM Corporation 2006
++ *
++ * Copyright (c) 2014-2018 NVIDIA Corporation
++ *
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ * 
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ * 
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++
++/**
++ * pcienum-sysfs.c
++ *
++ * Based on libpciaccess/src/linux_sysfs.c from libpciaccess-0.12.1, which was
++ * found here:
++ *
++ * http://cgit.freedesktop.org/xorg/lib/libpciaccess
++ *
++ * Access PCI subsystem using Linux's sysfs interface.  This interface is
++ * available starting somewhere in the late 2.5.x kernel phase, and is the
++ * preferred method on all 2.6.x kernels.
++ *
++ * Original author: Ian Romanick <idr@us.ibm.com>
++ */
++
++#if defined(NV_LINUX)
++
++#include <stdlib.h>
++#include <string.h>
++#include <stdio.h>
++#include <unistd.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <fcntl.h>
++#include <sys/mman.h>
++#include <dirent.h>
++#include <errno.h>
++#include <sys/time.h>
++#include <time.h>
++#include <limits.h>
++
++#include "pci-enum.h"
++#include "pci-sysfs.h"
++
++#define SYS_BUS_PCI                     "/sys/bus/pci/"
++#define SYS_BUS_PCI_DEVICES SYS_BUS_PCI "devices"
++#define SYS_BUS_PCI_RESCAN  SYS_BUS_PCI "rescan"
++#define PCI_DBDF_FORMAT                 "%04x:%02x:%02x.%1u"
++#define SYSFS_PCI_BRIDGE_RESCAN_FMT     SYS_BUS_PCI_DEVICES "/" PCI_DBDF_FORMAT "/rescan"
++#define SYSFS_RESCAN_STRING             "1\n"
++#define SYSFS_RESCAN_STRING_SIZE        2
++#define PCI_CAP_TTL_MAX                 20
++#define SYSFS_PATH_SIZE                 256
++
++#define BAIL_ON_IO_ERR(buf, err, cnt, action)   \
++do  {                                           \
++    if (((err) != 0) || ((cnt) < sizeof(buf)))  \
++    {                                           \
++        (err) = ((err) == 0) ? EIO : (err);     \
++        action;                                 \
++    }                                           \
++} while (0)
++
++static int pci_sysfs_read_cfg(uint16_t, uint16_t, uint16_t, uint16_t, uint16_t, void *,
++                              uint16_t size, uint16_t *);
++
++static int find_matches(struct pci_id_match *match);
++
++/**
++ * Attempt to access PCI subsystem using Linux's sysfs interface to enumerate
++ * the matched devices.
++ */
++int
++pci_enum_match_id(struct pci_id_match *match)
++{
++    int err = 0;
++    struct stat st;
++
++
++    /* 
++     * If the directory "/sys/bus/pci/devices" exists, then the PCI subsystem
++     * can be accessed using this interface.
++     */
++    match->num_matches = 0;
++    if (stat(SYS_BUS_PCI_DEVICES, &st) == 0)
++    {
++        err = find_matches(match);
++    }
++    else
++    {
++        err = errno;
++    }
++
++    return err;
++}
++
++
++/**
++ * The sysfs lookup method uses the directory entries in /sys/bus/pci/devices
++ * to enumerate all PCI devices, and then uses a file in each that is mapped to
++ * the device's PCI config space to extract the data to match against.
++ */
++static int
++find_matches(struct pci_id_match *match)
++{
++    struct dirent *d;
++    DIR *sysfs_pci_dir;
++    int err = 0;
++
++    sysfs_pci_dir = opendir(SYS_BUS_PCI_DEVICES);
++    if (sysfs_pci_dir == NULL)
++    {
++        return errno;
++    }
++
++    while ((d = readdir(sysfs_pci_dir)) != NULL)
++    {
++        uint8_t config[48];
++        uint16_t bytes;
++        unsigned dom, bus, dev, func;
++        uint16_t vendor_id, device_id, subvendor_id, subdevice_id;
++        uint16_t device_class;
++
++        /* Ignore the . and .. dirents */
++        if ((strcmp(d->d_name, ".") == 0) || (strcmp(d->d_name, "..") == 0))
++        {
++            continue;
++        }
++
++        sscanf(d->d_name, PCI_DBDF_FORMAT,
++               & dom, & bus, & dev, & func);
++
++        err = pci_sysfs_read_cfg(dom, bus, dev, func, 0, config, 48, & bytes);
++        if ((bytes == 48) && !err)
++        {
++            vendor_id = (uint16_t)config[0] + ((uint16_t)config[1] << 8);
++            device_id = (uint16_t)config[2] + ((uint16_t)config[3] << 8);
++            device_class = (uint16_t)config[10] +
++                ((uint16_t)config[11] << 8);
++            subvendor_id = (uint16_t)config[44] +
++                ((uint16_t)config[45] << 8);
++            subdevice_id = (uint16_t)config[46] +
++                ((uint16_t)config[47] << 8);
++
++            /*
++             * This logic, originally in common_iterator.c, will tell if
++             * this device is a match for the search criteria.
++             */
++            if (PCI_ID_COMPARE(match->vendor_id,    vendor_id)    &&
++                PCI_ID_COMPARE(match->device_id,    device_id)    &&
++                PCI_ID_COMPARE(match->subvendor_id, subvendor_id) &&
++                PCI_ID_COMPARE(match->subdevice_id, subdevice_id) &&
++                ((device_class & match->device_class_mask) ==
++                    match->device_class))
++            {
++                match->num_matches++;
++            }
++        }
++
++        if (err)
++        {
++            break;
++        }
++    }
++
++    closedir(sysfs_pci_dir);
++    return err;
++}
++
++static int
++pci_sysfs_read_cfg(uint16_t domain, uint16_t bus, uint16_t device,
++                   uint16_t function, uint16_t off, void *data,
++                   uint16_t size, uint16_t *bytes_read)
++{
++    char name[SYSFS_PATH_SIZE];
++    uint16_t temp_size = size;
++    int err = 0;
++    int fd;
++    char *data_bytes = data;
++
++    if (bytes_read != NULL)
++    {
++        *bytes_read = 0;
++    }
++
++    /*
++     * Each device has a directory under sysfs.  Within that directory there
++     * is a file named "config".  This file used to access the PCI config
++     * space.  It is used here to obtain most of the information about the
++     * device.
++     */
++    snprintf(name, SYSFS_PATH_SIZE - 1, "%s/" PCI_DBDF_FORMAT "/config",
++             SYS_BUS_PCI_DEVICES, domain, bus, device, function);
++
++    fd = open(name, O_RDONLY);
++    if (fd < 0)
++    {
++        return errno;
++    }
++
++    if (off != 0)
++    {
++        if (lseek(fd, (off_t) off, SEEK_SET) < 0)
++        {
++            close(fd);
++            return errno;
++        }
++    }
++
++    while (temp_size > 0)
++    {
++        const ssize_t bytes = read(fd, data_bytes, temp_size);
++
++        /*
++         * If zero bytes were read, then we assume it's the end of the
++         * config file.
++         */
++        if (bytes <= 0)
++        {
++            err = errno;
++            break;
++        }
++
++        temp_size -= bytes;
++        data_bytes += bytes;
++    }
++    
++    if (bytes_read != NULL)
++    {
++        *bytes_read = size - temp_size;
++    }
++
++    close(fd);
++    return err;
++}
++
++static int
++pci_sysfs_write_cfg(uint16_t domain, uint16_t bus, uint16_t device,
++                   uint16_t function, uint16_t off, void *data,
++                   uint16_t size, uint16_t *bytes_written)
++{
++    char name[SYSFS_PATH_SIZE];
++    uint16_t temp_size = size;
++    int err = 0;
++    int fd;
++    char *data_bytes = data;
++
++    if (bytes_written != NULL)
++    {
++        *bytes_written = 0;
++    }
++
++    /*
++     * Each device has a directory under sysfs.  Within that directory there
++     * is a file named "config".  This file used to access the PCI config
++     * space.
++     */
++    snprintf(name, SYSFS_PATH_SIZE - 1, "%s/" PCI_DBDF_FORMAT "/config",
++             SYS_BUS_PCI_DEVICES, domain, bus, device, function);
++
++    fd = open(name, O_WRONLY);
++    if (fd < 0)
++    {
++        return errno;
++    }
++
++    if (off != 0)
++    {
++        if (lseek(fd, (off_t) off, SEEK_SET) < 0)
++        {
++            close(fd);
++            return errno;
++        }
++    }
++
++    while (temp_size > 0)
++    {
++        const ssize_t bytes = write(fd, data_bytes, temp_size);
++
++        if (bytes < 0)
++        {
++            err = errno;
++            break;
++        }
++        /*
++         * If zero bytes were written, then we assume it's the end of the
++         * config file.
++         */
++        if (bytes == 0)
++        {
++            break;
++        }
++
++        temp_size -= bytes;
++        data_bytes += bytes;
++    }
++    
++    if (bytes_written != NULL)
++    {
++        *bytes_written = size - temp_size;
++    }
++
++    close(fd);
++    return err;
++}
++
++int
++pci_rescan(uint16_t domain, uint8_t bus, uint8_t slot, uint8_t function)
++{
++    char const                      *node;
++    char                            node_buf[SYSFS_PATH_SIZE];
++    int                             node_fd;
++    ssize_t                         cnt;
++
++    if ((domain | bus | slot | function) == 0)
++    {
++        /* rescan the entire PCI tree */
++        node = SYS_BUS_PCI_RESCAN;
++    }
++    else
++    {
++        snprintf(node_buf, sizeof(node_buf) - 1, SYSFS_PCI_BRIDGE_RESCAN_FMT,
++                domain, bus, slot, function);
++        node = node_buf;
++    }
++
++    node_fd = open(node, O_WRONLY);
++
++    if (node_fd < 0)
++    {
++        return errno;
++    }
++
++    cnt = write(node_fd, SYSFS_RESCAN_STRING, SYSFS_RESCAN_STRING_SIZE);
++
++    close(node_fd);
++
++    return cnt == SYSFS_RESCAN_STRING_SIZE ? 0 : EIO;
++}
++
++int
++pci_find_parent_bridge(pci_info_t *p_gpu_info, pci_info_t *p_bridge_info)
++{
++    char    gpu_path[SYSFS_PATH_SIZE];
++    char    bridge_path[PATH_MAX];
++    char    *p_node;
++
++    snprintf(gpu_path, SYSFS_PATH_SIZE - 1, "%s/" PCI_DBDF_FORMAT "/..", SYS_BUS_PCI_DEVICES,
++            p_gpu_info->domain, p_gpu_info->bus,
++            p_gpu_info->dev, p_gpu_info->ftn);
++
++    if (realpath(gpu_path, bridge_path) == NULL)
++    {
++        return errno;
++    }
++
++    p_node = strrchr(bridge_path, '/');
++
++    if (p_node == NULL)
++    {
++        return ENOENT;
++    }
++
++    ++p_node;
++
++    if (sscanf(p_node, PCI_DBDF_FORMAT,
++                &p_bridge_info->domain, &p_bridge_info->bus,
++                &p_bridge_info->dev, &p_bridge_info->ftn) != 4)
++    {
++        return ENOENT;
++    }
++
++    return 0;
++}
++
++static int
++pci_find_pcie_caps(uint16_t domain, uint8_t bus, uint8_t device, uint8_t ftn, uint8_t *p_caps)
++{
++    unsigned    ttl;
++    uint8_t     off;
++    uint8_t     cap_id;
++    int         err = ENXIO;
++    uint16_t    cnt;
++
++    for (off = PCI_CAPABILITY_LIST, ttl = PCI_CAP_TTL_MAX; ttl; --ttl)
++    {
++        err = pci_sysfs_read_cfg(domain, bus, device, ftn, off,
++                                &off, sizeof(off), &cnt);
++        BAIL_ON_IO_ERR(off, err, cnt, break);
++
++        /* Capabilities must reside above the std config header */
++        if ((off < PCI_STD_HEADER_SIZEOF) || (off == 0xff))
++        {
++            break;
++        }
++
++        /* Clear the reserved bits */
++        off &= ~3;
++
++        err = pci_sysfs_read_cfg(domain, bus, device, ftn, off + PCI_CAP_LIST_ID,
++                                &cap_id, sizeof(cap_id), &cnt);
++        BAIL_ON_IO_ERR(cap_id, err, cnt, break);
++
++        if (cap_id == PCI_CAP_ID_EXP)
++        {
++            goto found;
++        }
++
++        if (cap_id == 0xff)
++        {
++            break;
++        }
++
++        off += PCI_CAP_LIST_NEXT;
++    }
++    return err;
++found:
++    *p_caps = off;
++    return 0;
++}
++
++int
++pci_bridge_link_set_enable(uint16_t domain, uint8_t bus, uint8_t device, uint8_t ftn, int enable)
++{
++    uint8_t         pcie_caps = 0;
++    uint16_t        reg;
++    uint32_t        cap_reg;
++    uint16_t        cnt;
++    int             err;
++    struct timeval  start;
++    struct timeval  curr;
++    struct timeval  diff;
++    struct timespec delay = {0, PCI_LINK_DELAY_NS};
++    struct timespec dlllar_disable_delay = {0, PCI_LINK_DLLLAR_DISABLE_DELAY_NS};
++
++    err = pci_find_pcie_caps(domain, bus, device, ftn, &pcie_caps);
++
++    if (err != 0)
++    {
++        return err;
++    }
++
++    err = pci_sysfs_read_cfg(domain, bus, device, ftn, pcie_caps + PCI_EXP_LNKCTL,
++                            &reg, sizeof(reg), &cnt);
++    BAIL_ON_IO_ERR(reg, err, cnt, return err);
++
++    if (enable)
++    {
++        reg &= ~PCI_EXP_LNKCTL_LD;
++    }
++    else
++    {
++        reg |= PCI_EXP_LNKCTL_LD;
++    }
++
++    err = pci_sysfs_write_cfg(domain, bus, device, ftn, pcie_caps + PCI_EXP_LNKCTL,
++                            &reg, sizeof(reg), &cnt);
++    BAIL_ON_IO_ERR(reg, err, cnt, return err);
++
++    if (enable)
++    {
++        /*
++         * Data Link Layer Link Active Reporting must be capable for
++         * zero power capable downstream port. But old controller might
++         * not implement it. In this case, we wait for 30 ms.
++         */
++        err = pci_sysfs_read_cfg(domain, bus, device, ftn, pcie_caps + PCI_EXP_LNKCAP,
++                                &cap_reg, sizeof(cap_reg), &cnt);
++        BAIL_ON_IO_ERR(cap_reg, err, cnt, return err);
++
++        if (cap_reg & PCI_EXP_LNKCAP_DLLLARC)
++        {
++            /* wait for the link to go up and then sleep for 100 ms */
++
++            gettimeofday(&start, NULL);
++
++            for (;;)
++            {
++                err = pci_sysfs_read_cfg(domain, bus, device, ftn, pcie_caps + PCI_EXP_LNKSTA,
++                                    &reg, sizeof(reg), &cnt);
++                BAIL_ON_IO_ERR(reg, err, cnt, return err);
++
++                if ((reg & PCI_EXP_LNKSTA_DLLLA) != 0)
++                {
++                    break;
++                }
++
++                gettimeofday(&curr, NULL);
++                timersub(&curr, &start, &diff);
++
++                if ((diff.tv_sec > 0) || (diff.tv_usec >= PCI_LINK_WAIT_US))
++                {
++                    return ETIME;
++                }
++            }
++        }
++        else
++        {
++            /*
++             * Measured the time on DGX1 for link to become established in a bridge,
++             * where the DLLLA reporting is supported and its approximately ~9ms,
++             * so wait for 30ms where DLLLA reporting is not supported.
++             */
++            PCI_NANOSLEEP(&dlllar_disable_delay, NULL);
++        }
++
++        PCI_NANOSLEEP(&delay, NULL);
++    }
++
++    return err;
++}
++
++#endif /* defined(NV_LINUX) */
+\ No newline at end of file
+diff --git a/src/pci-sysfs.h b/src/pci-sysfs.h
+new file mode 100644
+index 0000000..1fc695b
+--- /dev/null
++++ b/src/pci-sysfs.h
+@@ -0,0 +1,85 @@
++/*
++ * Copyright (c) 2016-2018, NVIDIA CORPORATION.
++ *
++ * Permission is hereby granted, free of charge, to any person
++ * obtaining a copy of this software and associated documentation
++ * files (the "Software"), to deal in the Software without
++ * restriction, including without limitation the rights to use, copy,
++ * modify, merge, publish, distribute, sublicense, and/or sell copies
++ * of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be
++ * included in all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
++ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
++ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
++ * SOFTWARE.
++ *
++ * This file provides utility functions on Linux for interfacing
++ * with the sysfs/PCI kernel facility.
++ */
++
++#ifndef __PCI_SYSFS_H__
++#define __PCI_SYSFS_H__
++
++#if defined(NV_LINUX)
++
++#include <linux/pci.h>
++
++#if !defined(PCI_STD_HEADER_SIZEOF)
++#define PCI_STD_HEADER_SIZEOF   64
++#endif
++#if !defined(PCI_CAP_ID_EXP)
++#define  PCI_CAP_ID_EXP         0x10    /* PCI Express */
++#endif
++#if !defined(PCI_EXP_LNKCAP)
++#define PCI_EXP_LNKCAP          12          /* Link Capabilities */
++#endif
++#if !defined(PCI_EXP_LNKCAP_DLLLARC)
++#define PCI_EXP_LNKCAP_DLLLARC  0x00100000  /* Data Link Layer Link Active Reporting Capable */
++#endif
++#if !defined(PCI_EXP_LNKCTL)
++#define PCI_EXP_LNKCTL          16      /* Link Control */
++#endif
++#if !defined(PCI_EXP_LNKCTL_LD)
++#define  PCI_EXP_LNKCTL_LD      0x0010  /* Link Disable */
++#endif
++#if !defined(PCI_EXP_LNKSTA)
++#define PCI_EXP_LNKSTA          18      /* Link Status */
++#endif
++#if !defined(PCI_EXP_LNKSTA_DLLLA)
++#define  PCI_EXP_LNKSTA_DLLLA   0x2000  /* Data Link Layer Link Active */
++#endif
++
++#define PCI_LINK_WAIT_US                 200000      /* 200 ms, must be less than 1000000 (1s) */
++#define PCI_LINK_DELAY_NS                100000000   /* 100 ms */
++#define PCI_LINK_DLLLAR_DISABLE_DELAY_NS 30000000    /* 30ms */
++
++#if (_POSIX_C_SOURCE >= 199309L)
++#define PCI_NANOSLEEP(ts, rem)  nanosleep(ts, rem)
++#elif !(_POSIX_C_SOURCE >= 200809L)
++#define PCI_NANOSLEEP(ts, rem)  usleep((ts)->tv_sec * 1000000 + ((ts)->tv_nsec + 999) / 1000)
++#else
++#define PCI_NANOSLEEP(ts, rem)  sleep((ts)->tv_sec + ((ts)->tv_nsec + 999999999) / 1000000000)
++#endif
++
++typedef struct  {
++    unsigned    domain;
++    unsigned    bus;
++    unsigned    dev;
++    unsigned    ftn;
++}   pci_info_t;
++
++int pci_rescan(uint16_t domain, uint8_t bus, uint8_t slot, uint8_t function);
++int pci_find_parent_bridge(pci_info_t *p_gpu_info, pci_info_t *p_bridge_info);
++int pci_bridge_link_set_enable(uint16_t domain, uint8_t bus, uint8_t device, uint8_t ftn, int enable);
++
++#endif /* NV_LINUX */
++
++#endif /* __PCI_SYSFS_H__ */
+\ No newline at end of file
+-- 
+2.27.0
+
diff --git a/package/libnvidia-container/0002-Remove-dependency-handling-from-Makefile.patch b/package/libnvidia-container/0002-Remove-dependency-handling-from-Makefile.patch
new file mode 100644
index 0000000000..d4ba9dfe80
--- /dev/null
+++ b/package/libnvidia-container/0002-Remove-dependency-handling-from-Makefile.patch
@@ -0,0 +1,698 @@ 
+From 6752d8d5e315eb3f061498a9c35558f90f9600e2 Mon Sep 17 00:00:00 2001
+From: Christian Stewart <christian@paral.in>
+Date: Sat, 18 Jul 2020 15:26:22 -0700
+Subject: [PATCH] Remove dependency handling from Makefile
+
+Buildroot will handle this for the makefile.
+
+Signed-off-by: Christian Stewart <christian@paral.in>
+---
+ Makefile                    |  54 +++------
+ mk/nvidia-modprobe.mk       |  55 ---------
+ src/nvc.c                   |   6 +-
+ src/nvidia-modprobe-utils.c | 225 ++++++++++++++++--------------------
+ src/nvidia-modprobe-utils.h |  53 ++-------
+ 5 files changed, 128 insertions(+), 265 deletions(-)
+ delete mode 100644 mk/nvidia-modprobe.mk
+
+diff --git a/Makefile b/Makefile
+index f1c56a9..80780d1 100644
+--- a/Makefile
++++ b/Makefile
+@@ -2,13 +2,13 @@
+ # Copyright (c) 2017-2018, NVIDIA CORPORATION. All rights reserved.
+ #
+ 
+-.PHONY: all tools shared static deps install uninstall dist depsclean mostlyclean clean distclean
++.PHONY: all tools shared static install uninstall dist mostlyclean clean distclean
+ .DEFAULT_GOAL := all
+-STRIP  := @echo skipping: strip
++
+ ##### Global variables #####
+ 
+-WITH_LIBELF  ?= yes
+-WITH_TIRPC   ?= yes
++WITH_LIBELF  ?= no
++WITH_TIRPC   ?= no
+ WITH_SECCOMP ?= yes
+ 
+ ##### Global definitions #####
+@@ -16,15 +16,14 @@ WITH_SECCOMP ?= yes
+ export prefix      = /usr
+ export exec_prefix = $(prefix)
+ export bindir      = $(exec_prefix)/bin
+-export libdir      = $(exec_prefix)/lib64
++export libdir      = $(exec_prefix)/lib
+ export docdir      = $(prefix)/share/doc
+-export libdbgdir   = $(prefix)/lib64/debug$(libdir)
++export libdbgdir   = $(prefix)/lib/debug$(libdir)
+ export includedir  = $(prefix)/include
+ export pkgconfdir  = $(libdir)/pkgconfig
+ 
+ export PKG_DIR     ?= $(CURDIR)/pkg
+ export SRCS_DIR    ?= $(CURDIR)/src
+-export DEPS_DIR    ?= $(CURDIR)/deps
+ export DIST_DIR    ?= $(CURDIR)/dist
+ export MAKE_DIR    ?= $(CURDIR)/mk
+ export DEBUG_DIR   ?= $(CURDIR)/.debug
+@@ -120,9 +119,9 @@ LDFLAGS  := -Wl,-zrelro -Wl,-znow -Wl,-zdefs -Wl,--gc-sections $(LDFLAGS)
+ LDLIBS   := $(LDLIBS)
+ 
+ # Library flags (recursively expanded to handle target-specific flags)
+-LIB_CPPFLAGS       = -DNV_LINUX -isystem $(DEPS_DIR)$(includedir) -include $(BUILD_DEFS)
++LIB_CPPFLAGS       = -DNV_LINUX -include $(BUILD_DEFS)
+ LIB_CFLAGS         = -fPIC
+-LIB_LDFLAGS        = -L$(DEPS_DIR)$(libdir) -shared -Wl,-soname=$(LIB_SONAME)
++LIB_LDFLAGS        = -shared -Wl,-soname=$(LIB_SONAME)
+ # LIB_LDLIBS_STATIC  = -l:libnvidia-modprobe-utils.a
+ LIB_LDLIBS_SHARED  = -ldl -lcap -ltirpc
+ ifeq ($(WITH_LIBELF), yes)
+@@ -132,7 +131,7 @@ else
+ LIB_LDLIBS_STATIC  += -l:libelf.a
+ endif
+ ifeq ($(WITH_TIRPC), yes)
+-LIB_CPPFLAGS       += -isystem $(DEPS_DIR)$(includedir)/tirpc -DWITH_TIRPC
++LIB_CPPFLAGS       += -DWITH_TIRPC
+ # LIB_LDLIBS_STATIC  += -l:libtirpc.a
+ LIB_LDLIBS_SHARED  += -lpthread
+ endif
+@@ -176,15 +175,15 @@ $(LIB_RPC_SRCS): $(LIB_RPC_SPEC)
+ 	$(RM) $@
+ 	cd $(dir $@) && $(RPCGEN) $(RPCGENFLAGS) -C -M -N -o $(notdir $@) $(LIB_RPC_SPEC)
+ 
+-$(LIB_OBJS): %.lo: %.c | deps
++$(LIB_OBJS): %.lo: %.c
+ 	$(CC) $(LIB_CFLAGS) $(LIB_CPPFLAGS) -MMD -MF $*.d -c $(OUTPUT_OPTION) $<
+ 
+-$(BIN_OBJS): %.o: %.c | shared
++$(BIN_OBJS): %.o: %.c
+ 	$(CC) $(BIN_CFLAGS) $(BIN_CPPFLAGS) -MMD -MF $*.d -c $(OUTPUT_OPTION) $<
+ 
+ -include $(DEPENDENCIES)
+ 
+-$(LIB_SHARED): $(LIB_OBJS)
++$(LIB_SHARED): $(BUILD_DEFS) $(SRCS_DIR)/driver_rpc.h $(LIB_OBJS)
+ 	$(MKDIR) -p $(DEBUG_DIR)
+ 	$(CC) $(LIB_CFLAGS) $(LIB_CPPFLAGS) $(LIB_LDFLAGS) $(OUTPUT_OPTION) $^ $(LIB_SCRIPT) $(LIB_LDLIBS)
+ 	$(OBJCPY) --only-keep-debug $@ $(LIB_SONAME)
+@@ -198,7 +197,7 @@ $(LIB_STATIC_OBJ): $(LIB_OBJS)
+ 	$(OBJCPY) --localize-hidden $@
+ 	$(STRIP) --strip-unneeded -R .comment $@
+ 
+-$(BIN_NAME): $(BIN_OBJS)
++$(BIN_NAME): $(BUILD_DEFS) $(SRCS_DIR)/driver_rpc.h $(LIB_SHARED) $(BIN_OBJS)
+ 	$(CC) $(BIN_CFLAGS) $(BIN_CPPFLAGS) $(BIN_LDFLAGS) $(OUTPUT_OPTION) $^ $(BIN_SCRIPT) $(BIN_LDLIBS)
+ 	$(STRIP) --strip-unneeded -R .comment $@
+ 
+@@ -219,17 +218,6 @@ shared: $(LIB_SHARED)
+ 
+ static: $(LIB_STATIC)($(LIB_STATIC_OBJ))
+ 
+-deps: export DESTDIR:=$(DEPS_DIR)
+-deps: $(LIB_RPC_SRCS) $(BUILD_DEFS)
+-	$(MKDIR) -p $(DEPS_DIR)
+-	# $(MAKE) -f $(MAKE_DIR)/nvidia-modprobe.mk install
+-ifeq ($(WITH_LIBELF), no)
+-	# $(MAKE) -f $(MAKE_DIR)/elftoolchain.mk install
+-endif
+-ifeq ($(WITH_TIRPC), yes)
+-	# $(MAKE) -f $(MAKE_DIR)/libtirpc.mk install
+-endif
+-
+ install: all
+ 	$(INSTALL) -d -m 755 $(addprefix $(DESTDIR),$(includedir) $(bindir) $(libdir) $(docdir) $(libdbgdir) $(pkgconfdir))
+ 	# Install header files
+@@ -237,8 +225,7 @@ install: all
+ 	# Install library files
+ 	$(INSTALL) -m 644 $(LIB_STATIC) $(DESTDIR)$(libdir)
+ 	$(INSTALL) -m 755 $(LIB_SHARED) $(DESTDIR)$(libdir)
+-	$(LN) -sf $(LIB_SONAME) $(DESTDIR)$(libdir)/$(LIB_SYMLINK)
+-	$(LDCONFIG) -n $(DESTDIR)$(libdir)
++	$(LN) -sf $(LIB_SHARED) $(DESTDIR)$(libdir)/$(LIB_SYMLINK)
+ 	# Install debugging symbols
+ 	# $(INSTALL) -m 644 $(DEBUG_DIR)/$(LIB_SONAME) $(DESTDIR)$(libdbgdir)
+ 	# Install configuration files
+@@ -268,23 +255,12 @@ dist: install
+ 	$(TAR) --numeric-owner --owner=0 --group=0 -C $(dir $(DESTDIR)) -caf $(DESTDIR)_$(ARCH).tar.xz $(notdir $(DESTDIR))
+ 	$(RM) -r $(DESTDIR)
+ 
+-depsclean:
+-	$(RM) $(BUILD_DEFS)
+-	-$(MAKE) -f $(MAKE_DIR)/nvidia-modprobe.mk clean
+-ifeq ($(WITH_LIBELF), no)
+-	-$(MAKE) -f $(MAKE_DIR)/elftoolchain.mk clean
+-endif
+-ifeq ($(WITH_TIRPC), yes)
+-	-$(MAKE) -f $(MAKE_DIR)/libtirpc.mk clean
+-endif
+-
+ mostlyclean:
+ 	$(RM) $(LIB_OBJS) $(LIB_STATIC_OBJ) $(BIN_OBJS) $(DEPENDENCIES)
+ 
+-clean: mostlyclean depsclean
++clean: mostlyclean
+ 
+ distclean: clean
+-	$(RM) -r $(DEPS_DIR) $(DIST_DIR) $(DEBUG_DIR)
+ 	$(RM) $(LIB_RPC_SRCS) $(LIB_STATIC) $(LIB_SHARED) $(BIN_NAME)
+ 
+ deb: DESTDIR:=$(DIST_DIR)/$(LIB_NAME)_$(VERSION)_$(ARCH)
+diff --git a/mk/nvidia-modprobe.mk b/mk/nvidia-modprobe.mk
+deleted file mode 100644
+index ad399de..0000000
+--- a/mk/nvidia-modprobe.mk
++++ /dev/null
+@@ -1,55 +0,0 @@
+-#
+-# Copyright (c) 2017-2018, NVIDIA CORPORATION. All rights reserved.
+-#
+-
+-include $(MAKE_DIR)/common.mk
+-
+-##### Source definitions #####
+-
+-VERSION        := 396.51
+-PREFIX         := nvidia-modprobe-$(VERSION)
+-URL            := https://github.com/NVIDIA/nvidia-modprobe/archive/$(VERSION).tar.gz
+-
+-SRCS_DIR       := $(DEPS_DIR)/src/$(PREFIX)
+-MODPROBE_UTILS := $(SRCS_DIR)/modprobe-utils
+-
+-LIB_STATIC     := $(MODPROBE_UTILS)/libnvidia-modprobe-utils.a
+-LIB_INCS       := $(MODPROBE_UTILS)/nvidia-modprobe-utils.h \
+-                  $(MODPROBE_UTILS)/pci-enum.h
+-LIB_SRCS       := $(MODPROBE_UTILS)/nvidia-modprobe-utils.c \
+-                  $(MODPROBE_UTILS)/pci-sysfs.c
+-
+-##### Flags definitions #####
+-
+-ARFLAGS  := -rU
+-CPPFLAGS := -D_FORTIFY_SOURCE=2 -DNV_LINUX
+-CFLAGS   := -O2 -g -fdata-sections -ffunction-sections -fstack-protector -fno-strict-aliasing -fPIC
+-
+-##### Private rules #####
+-
+-LIB_OBJS := $(LIB_SRCS:.c=.o)
+-
+-$(SRCS_DIR)/.download_stamp:
+-	$(MKDIR) -p $(SRCS_DIR)
+-	$(CURL) --progress-bar -fSL $(URL) | \
+-	$(TAR) -C $(SRCS_DIR) --strip-components=1 -xz $(PREFIX)/modprobe-utils
+-	@touch $@
+-
+-$(LIB_SRCS): $(SRCS_DIR)/.download_stamp
+-
+-##### Public rules #####
+-
+-.PHONY: all install clean
+-
+-all: $(LIB_STATIC)
+-
+-$(LIB_STATIC): $(LIB_OBJS)
+-	$(AR) rs $@ $^
+-
+-install: all
+-	$(INSTALL) -d -m 755 $(addprefix $(DESTDIR),$(includedir) $(libdir))
+-	$(INSTALL) -m 644 $(LIB_INCS) $(DESTDIR)$(includedir)
+-	$(INSTALL) -m 644 $(LIB_STATIC) $(DESTDIR)$(libdir)
+-
+-clean:
+-	$(RM) $(LIB_OBJS) $(LIB_STATIC)
+diff --git a/src/nvc.c b/src/nvc.c
+index f1d9b62..74ea61c 100644
+--- a/src/nvc.c
++++ b/src/nvc.c
+@@ -190,13 +190,13 @@ load_kernel_modules(struct error *err, const char *root)
+                 }
+ 
+                 log_info("loading kernel module nvidia");
+-                if (nvidia_modprobe(0, -1) == 0)
++                if (nvidia_modprobe(0) == 0)
+                         log_err("could not load kernel module nvidia");
+                 else {
+-                        if (nvidia_mknod(NV_CTL_DEVICE_MINOR, -1) == 0)
++                        if (nvidia_mknod(NV_CTL_DEVICE_MINOR) == 0)
+                                 log_err("could not create kernel module device node");
+                         for (int i = 0; i < (int)devs.num_matches; ++i) {
+-                                if (nvidia_mknod(i, -1) == 0)
++                                if (nvidia_mknod(i) == 0)
+                                         log_err("could not create kernel module device node");
+                         }
+                 }
+diff --git a/src/nvidia-modprobe-utils.c b/src/nvidia-modprobe-utils.c
+index d3f3233..fca21cf 100644
+--- a/src/nvidia-modprobe-utils.c
++++ b/src/nvidia-modprobe-utils.c
+@@ -1,3 +1,29 @@
++/*
++ * Copyright (c) 2013, NVIDIA CORPORATION.
++ *
++ * Permission is hereby granted, free of charge, to any person
++ * obtaining a copy of this software and associated documentation
++ * files (the "Software"), to deal in the Software without
++ * restriction, including without limitation the rights to use, copy,
++ * modify, merge, publish, distribute, sublicense, and/or sell copies
++ * of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be
++ * included in all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
++ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
++ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
++ * SOFTWARE.
++ *
++ * This file provides utility functions on Linux for loading the
++ * NVIDIA kernel module and creating NVIDIA device files.
++ */
+ 
+ #if defined(NV_LINUX)
+ 
+@@ -27,9 +53,6 @@
+ #define NV_NVIDIA_MODULE_NAME "nvidia"
+ #define NV_PROC_REGISTRY_PATH "/proc/driver/nvidia/params"
+ 
+-#define NV_NMODULE_NVIDIA_MODULE_NAME "nvidia%d"
+-#define NV_NMODULE_PROC_REGISTRY_PATH "/proc/driver/nvidia/%d/params"
+-
+ #define NV_UVM_MODULE_NAME "nvidia-uvm"
+ #define NV_UVM_DEVICE_NAME "/dev/nvidia-uvm"
+ #define NV_UVM_TOOLS_DEVICE_NAME "/dev/nvidia-uvm-tools"
+@@ -41,6 +64,9 @@
+ #define NV_NVLINK_MODULE_NAME "nvidia-nvlink"
+ #define NV_NVLINK_PROC_PERM_PATH "/proc/driver/nvidia-nvlink/permissions"
+ 
++#define NV_NVSWITCH_MODULE_NAME "nvidia-nvswitch"
++#define NV_NVSWITCH_PROC_PERM_PATH "/proc/driver/nvidia-nvswitch/permissions"
++
+ #define NV_DEVICE_FILE_MODE_MASK (S_IRWXU|S_IRWXG|S_IRWXO)
+ #define NV_DEVICE_FILE_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
+ #define NV_DEVICE_FILE_UID 0
+@@ -54,84 +80,6 @@
+ 
+ #define NV_MIN(a, b) (((a) < (b)) ? (a) : (b))
+ 
+-/*
+- * Construct the nvidia kernel module name based on the input
+- * module instance provided.  If an error occurs, the null
+- * terminator will be written to nv_module_name[0].
+- */
+-static __inline__ void assign_nvidia_kernel_module_name
+-(
+-    char nv_module_name[NV_MAX_MODULE_NAME_SIZE],
+-    int module_instance
+-)
+-{
+-    int ret;
+-
+-    if (is_multi_module(module_instance))
+-    {
+-        ret = snprintf(nv_module_name, NV_MAX_MODULE_NAME_SIZE,
+-                       NV_NMODULE_NVIDIA_MODULE_NAME, module_instance);
+-    }
+-    else
+-    {
+-        ret = snprintf(nv_module_name, NV_MAX_MODULE_NAME_SIZE,
+-                       NV_NVIDIA_MODULE_NAME);
+-    }
+-
+-    if (ret <= 0)
+-    {
+-        goto fail;
+-    }
+-
+-    nv_module_name[NV_MAX_MODULE_NAME_SIZE - 1] = '\0';
+-
+-    return;
+-
+-fail:
+-
+-    nv_module_name[0] = '\0';
+-}
+-
+-
+-/*
+- * Construct the proc registry path name based on the input
+- * module instance provided.  If an error occurs, the null
+- * terminator will be written to proc_path[0].
+- */
+-static __inline__ void assign_proc_registry_path
+-(
+-    char proc_path[NV_MAX_PROC_REGISTRY_PATH_SIZE],
+-    int module_instance
+-)
+-{
+-    int ret;
+-
+-    if (is_multi_module(module_instance))
+-    {
+-        ret = snprintf(proc_path, NV_MAX_PROC_REGISTRY_PATH_SIZE,
+-                       NV_NMODULE_PROC_REGISTRY_PATH, module_instance);
+-    }
+-    else
+-    {
+-        ret = snprintf(proc_path, NV_MAX_PROC_REGISTRY_PATH_SIZE,
+-                       NV_PROC_REGISTRY_PATH);
+-    }
+-
+-    if (ret <= 0)
+-    {
+-        goto fail;
+-    }
+-
+-    proc_path[NV_MAX_PROC_REGISTRY_PATH_SIZE - 1] = '\0';
+-
+-    return;
+-
+-fail:
+-
+-    proc_path[0] = '\0';
+-}
+-
+-
+ /*
+  * Just like strcmp(3), except that differences between '-' and '_' are
+  * ignored. This is useful for comparing module names, where '-' and '_'
+@@ -370,18 +318,20 @@ static int modprobe_helper(const int print_errors, const char *module_name)
+             return 0;
+ 
+         default:
+-            if (waitpid(pid, &status, 0) < 0)
+-            {
+-                return 0;
+-            }
+-            if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
+-            {
+-                return 1;
+-            }
+-            else
+-            {
+-                return 0;
+-            }
++            /*
++             * waitpid(2) is not always guaranteed to return success even if
++             * the child terminated normally.  For example, if the process
++             * explicitly configured the handling of the SIGCHLD signal
++             * to SIG_IGN, then waitpid(2) will instead block until all
++             * children terminate and return the error ECHILD, regardless
++             * of the child's exit codes.
++             *
++             * Hence, ignore waitpid(2) error codes and instead check
++             * whether the desired kernel module is loaded.
++             */
++            waitpid(pid, NULL, 0);
++
++            return is_kernel_module_loaded(module_name);
+     }
+ 
+     return 1;
+@@ -391,13 +341,9 @@ static int modprobe_helper(const int print_errors, const char *module_name)
+ /*
+  * Attempt to load an NVIDIA kernel module
+  */
+-int nvidia_modprobe(const int print_errors, int module_instance)
++int nvidia_modprobe(const int print_errors)
+ {
+-    char nv_module_name[NV_MAX_MODULE_NAME_SIZE];
+-
+-    assign_nvidia_kernel_module_name(nv_module_name, module_instance);
+-
+-    return modprobe_helper(print_errors, nv_module_name);
++    return modprobe_helper(print_errors, NV_NVIDIA_MODULE_NAME);
+ }
+ 
+ 
+@@ -494,24 +440,22 @@ static int get_file_state_helper(
+     return state;
+ }
+ 
+-int nvidia_get_file_state(int minor, int module_instance)
++int nvidia_get_file_state(int minor)
+ {
+     char path[NV_MAX_CHARACTER_DEVICE_FILE_STRLEN];
+-    char proc_path[NV_MAX_PROC_REGISTRY_PATH_SIZE];
+     mode_t mode;
+     uid_t uid;
+     gid_t gid;
+     int modification_allowed;
+     int state = 0;
+ 
+-    assign_device_file_name(path, minor, module_instance);
+-    assign_proc_registry_path(proc_path, module_instance);
++    assign_device_file_name(path, minor);
+ 
+     init_device_file_parameters(&uid, &gid, &mode, &modification_allowed,
+-                                proc_path);
++                                NV_PROC_REGISTRY_PATH);
+ 
+     state = get_file_state_helper(path, NV_MAJOR_DEVICE_NUMBER, minor,
+-                                  proc_path, uid, gid, mode);
++                                  NV_PROC_REGISTRY_PATH, uid, gid, mode);
+ 
+     return state;
+ }
+@@ -522,8 +466,8 @@ int nvidia_get_file_state(int minor, int module_instance)
+  * permissions.  Returns 1 if the file is successfully created; returns 0
+  * if the file could not be created.
+  */
+-int mknod_helper(int major, int minor, const char *path,
+-                 const char *proc_path)
++static int mknod_helper(int major, int minor, const char *path,
++                        const char *proc_path)
+ {
+     dev_t dev = NV_MAKE_DEVICE(major, minor);
+     mode_t mode;
+@@ -616,15 +560,13 @@ int mknod_helper(int major, int minor, const char *path,
+  * Attempt to create a device file with the specified minor number for
+  * the specified NVIDIA module instance.
+  */
+-int nvidia_mknod(int minor, int module_instance)
++int nvidia_mknod(int minor)
+ {
+     char path[NV_MAX_CHARACTER_DEVICE_FILE_STRLEN];
+-    char proc_path[NV_MAX_PROC_REGISTRY_PATH_SIZE];
+ 
+-    assign_device_file_name(path, minor, module_instance);
+-    assign_proc_registry_path(proc_path, module_instance);
++    assign_device_file_name(path, minor);
+ 
+-    return mknod_helper(NV_MAJOR_DEVICE_NUMBER, minor, path, proc_path);
++    return mknod_helper(NV_MAJOR_DEVICE_NUMBER, minor, path, NV_PROC_REGISTRY_PATH);
+ }
+ 
+ 
+@@ -633,7 +575,7 @@ int nvidia_mknod(int minor, int module_instance)
+  * device with the specified name.  Returns the major number on success,
+  * or -1 on failure.
+  */
+-int get_chardev_major(const char *name)
++static int get_chardev_major(const char *name)
+ {
+     int ret = -1;
+     char line[NV_MAX_LINE_LENGTH];
+@@ -743,13 +685,9 @@ int nvidia_modeset_modprobe(void)
+  */
+ int nvidia_modeset_mknod(void)
+ {
+-    char proc_path[NV_MAX_PROC_REGISTRY_PATH_SIZE];
+-
+-    assign_proc_registry_path(proc_path, NV_MODULE_INSTANCE_NONE);
+-
+     return mknod_helper(NV_MAJOR_DEVICE_NUMBER,
+                         NV_MODESET_MINOR_DEVICE_NUM,
+-                        NV_MODESET_DEVICE_NAME, proc_path);
++                        NV_MODESET_DEVICE_NAME, NV_PROC_REGISTRY_PATH);
+ }
+ 
+ /*
+@@ -770,25 +708,62 @@ int nvidia_nvlink_mknod(void)
+                         NV_NVLINK_PROC_PERM_PATH);
+ }
+ 
++/*
++ * Attempt to create the NVIDIA NVSwitch driver device files.
++ */
++int nvidia_nvswitch_mknod(int minor)
++{
++    int major = 0;
++    char name[NV_MAX_CHARACTER_DEVICE_FILE_STRLEN];
++    int ret;
++
++    major = get_chardev_major(NV_NVSWITCH_MODULE_NAME);
++
++    if (major < 0)
++    {
++        return 0;
++    }
++
++    if (minor == NV_NVSWITCH_CTL_MINOR)
++    {
++        ret = snprintf(name, NV_MAX_CHARACTER_DEVICE_FILE_STRLEN,
++                       NV_NVSWITCH_CTL_NAME);
++    }
++    else
++    {
++        ret = snprintf(name, NV_MAX_CHARACTER_DEVICE_FILE_STRLEN,
++                       NV_NVSWITCH_DEVICE_NAME, minor);
++    }
++
++    if (ret <= 0)
++    {
++        return 0;
++    }
++
++    return mknod_helper(major, minor, name, NV_NVSWITCH_PROC_PERM_PATH);
++}
++
+ int nvidia_vgpu_vfio_mknod(int minor_num)
+ {
+     int major = get_chardev_major(NV_VGPU_VFIO_MODULE_NAME);
+     char vgpu_dev_name[NV_MAX_CHARACTER_DEVICE_FILE_STRLEN];
+-    char proc_path[NV_MAX_PROC_REGISTRY_PATH_SIZE];
++    int ret;
+ 
+     if (major < 0)
+     {
+         return 0;
+     }
+ 
+-    assign_proc_registry_path(proc_path, NV_MODULE_INSTANCE_NONE);
+-
+-    snprintf(vgpu_dev_name, NV_MAX_CHARACTER_DEVICE_FILE_STRLEN,
+-             NV_VGPU_VFIO_DEVICE_NAME, minor_num);
++    ret = snprintf(vgpu_dev_name, NV_MAX_CHARACTER_DEVICE_FILE_STRLEN,
++                   NV_VGPU_VFIO_DEVICE_NAME, minor_num);
++    if (ret <= 0)
++    {
++        return 0;
++    }
+ 
+     vgpu_dev_name[NV_MAX_CHARACTER_DEVICE_FILE_STRLEN - 1] = '\0';
+ 
+-    return mknod_helper(major, minor_num, vgpu_dev_name, proc_path);
++    return mknod_helper(major, minor_num, vgpu_dev_name, NV_PROC_REGISTRY_PATH);
+ }
+ 
+-#endif /* NV_LINUX */
+\ No newline at end of file
++#endif /* NV_LINUX */
+diff --git a/src/nvidia-modprobe-utils.h b/src/nvidia-modprobe-utils.h
+index e06b4a4..5ab3355 100644
+--- a/src/nvidia-modprobe-utils.h
++++ b/src/nvidia-modprobe-utils.h
+@@ -31,29 +31,17 @@
+ #include <stdio.h>
+ 
+ #define NV_MAX_CHARACTER_DEVICE_FILE_STRLEN  128
+-#define NV_MODULE_INSTANCE_NONE              -1
+-#define NV_MODULE_INSTANCE_ZERO              0
+-#define NV_MAX_MODULE_INSTANCES              8
+ #define NV_CTL_DEVICE_NUM                    255
+ #define NV_MODESET_MINOR_DEVICE_NUM          254
+-
+-#define NV_FRONTEND_CONTROL_DEVICE_MINOR_MAX NV_CTL_DEVICE_NUM
++#define NV_NVSWITCH_CTL_MINOR                255
+ 
+ #define NV_DEVICE_FILE_PATH "/dev/nvidia%d"
+ #define NV_CTRL_DEVICE_FILE_PATH "/dev/nvidiactl"
+ #define NV_MODESET_DEVICE_NAME "/dev/nvidia-modeset"
+ #define NV_VGPU_VFIO_DEVICE_NAME "/dev/nvidia-vgpu%d"
+ #define NV_NVLINK_DEVICE_NAME "/dev/nvidia-nvlink"
+-
+-#define NV_NMODULE_CTRL_DEVICE_FILE_PATH "/dev/nvidiactl%d"
+-
+-#define NV_FRONTEND_CONTROL_DEVICE_MINOR_MIN \
+-    (NV_FRONTEND_CONTROL_DEVICE_MINOR_MAX - \
+-     NV_MAX_MODULE_INSTANCES)
+-
+-#define NV_FRONTEND_IS_CONTROL_DEVICE(x) \
+-    ((x <= NV_FRONTEND_CONTROL_DEVICE_MINOR_MAX) && \
+-     (x > NV_FRONTEND_CONTROL_DEVICE_MINOR_MIN))
++#define NV_NVSWITCH_CTL_NAME "/dev/nvidia-nvswitchctl"
++#define NV_NVSWITCH_DEVICE_NAME "/dev/nvidia-nvswitch%d"
+ 
+ #if defined(NV_LINUX)
+ 
+@@ -76,31 +64,19 @@ static __inline__ int nvidia_test_file_state(int state,
+     return !!(state & (1 << value));
+ }
+ 
+-int nvidia_get_file_state(int minor, int module_instance);
+-int nvidia_modprobe(const int print_errors, int module_instance);
+-int nvidia_mknod(int minor, int module_instance);
++int nvidia_get_file_state(int minor);
++int nvidia_modprobe(const int print_errors);
++int nvidia_mknod(int minor);
+ int nvidia_uvm_modprobe(void);
+ int nvidia_uvm_mknod(int base_minor);
+ int nvidia_modeset_modprobe(void);
+ int nvidia_modeset_mknod(void);
+ int nvidia_vgpu_vfio_mknod(int minor_num);
+ int nvidia_nvlink_mknod(void);
+-
+-int mknod_helper(int major, int minor, const char *path, const char *proc_path);
+-int get_chardev_major(const char *name);
++int nvidia_nvswitch_mknod(int minor);
+ 
+ #endif /* NV_LINUX */
+ 
+-/*
+- * Detect use of multiple kernel module instances. Use a single 
+- * module instance unless instance != NV_MODULE_INSTANCE_NONE
+- */
+-static __inline__ int is_multi_module(int module_instance)
+-{
+-    return (module_instance != NV_MODULE_INSTANCE_NONE);
+-}
+-
+-
+ /*
+  * Construct the device file name, based on 'minor'.  If an error
+  * occurs, the nul terminator will be written to name[0].
+@@ -108,8 +84,7 @@ static __inline__ int is_multi_module(int module_instance)
+ static __inline__ void assign_device_file_name
+ (
+     char name[NV_MAX_CHARACTER_DEVICE_FILE_STRLEN],
+-    int minor,
+-    int module_instance
++    int minor
+ )
+ {
+     int ret;
+@@ -119,20 +94,12 @@ static __inline__ void assign_device_file_name
+         goto fail;
+     }
+ 
+-    if (!is_multi_module(module_instance) && minor == NV_CTL_DEVICE_NUM)
++    if (minor == NV_CTL_DEVICE_NUM)
+     {
+         ret = snprintf(name,
+                        NV_MAX_CHARACTER_DEVICE_FILE_STRLEN,
+                        NV_CTRL_DEVICE_FILE_PATH);
+     }
+-    else if (is_multi_module(module_instance) && 
+-             NV_FRONTEND_IS_CONTROL_DEVICE(minor))
+-    {
+-        ret = snprintf(name,
+-                       NV_MAX_CHARACTER_DEVICE_FILE_STRLEN,
+-                       NV_NMODULE_CTRL_DEVICE_FILE_PATH,
+-                       module_instance);
+-    }
+     else
+     {
+         ret = snprintf(name,
+@@ -154,4 +121,4 @@ fail:
+     name[0] = '\0';
+ }
+ 
+-#endif /* __NVIDIA_MODPROBE_UTILS_H__ */
+\ No newline at end of file
++#endif /* __NVIDIA_MODPROBE_UTILS_H__ */
+-- 
+2.27.0
+
diff --git a/package/libnvidia-container/Config.in b/package/libnvidia-container/Config.in
new file mode 100644
index 0000000000..7a452c3635
--- /dev/null
+++ b/package/libnvidia-container/Config.in
@@ -0,0 +1,18 @@ 
+config BR2_PACKAGE_LIBNVIDIA_CONTAINER
+	bool "libnvidia-container"
+	depends on BR2_SHARED_LIBS
+	depends on BR2_TOOLCHAIN_HAS_THREADS # tirpc
+	depends on BR2_TOOLCHAIN_USES_GLIBC # fexecve
+	select BR2_PACKAGE_ELFUTILS
+	select BR2_PACKAGE_LIBCAP
+	select BR2_PACKAGE_LIBTIRPC
+	select BR2_PACKAGE_NVIDIA_MODPROBE
+	help
+	  The libnvidia-container package adds a library and CLI for
+	  GPU-backed containers, agnostic to container runtime.
+
+	  https://github.com/NVIDIA/libnvidia-container
+
+comment "libnvidia-container needs a shared glibc toolchain w/ threads"
+	depends on !BR2_TOOLCHAIN_HAS_THREADS || !BR2_TOOLCHAN_USES_GLIBC || \
+		!BR2_SHARED_LIBS
diff --git a/package/libnvidia-container/libnvidia-container.hash b/package/libnvidia-container/libnvidia-container.hash
new file mode 100644
index 0000000000..d356eb2b1e
--- /dev/null
+++ b/package/libnvidia-container/libnvidia-container.hash
@@ -0,0 +1,3 @@ 
+# Locally computed:
+sha256 fd447629fd65d171b68edb62fa2e581c67fdb450ff540f486987ab826150d06e  libnvidia-container-1.2.0.tar.gz
+sha256 cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30  LICENSE
diff --git a/package/libnvidia-container/libnvidia-container.mk b/package/libnvidia-container/libnvidia-container.mk
new file mode 100644
index 0000000000..d337d0a528
--- /dev/null
+++ b/package/libnvidia-container/libnvidia-container.mk
@@ -0,0 +1,57 @@ 
+################################################################################
+#
+# libnvidia-container
+#
+################################################################################
+
+LIBNVIDIA_CONTAINER_VERSION = 1.2.0
+LIBNVIDIA_CONTAINER_SITE = $(call github,NVIDIA,libnvidia-container,v$(LIBNVIDIA_CONTAINER_VERSION))
+LIBNVIDIA_CONTAINER_LICENSE = Apache-2.0
+LIBNVIDIA_CONTAINER_LICENSE_FILES = LICENSE
+
+LIBNVIDIA_CONTAINER_DEPENDENCIES = elfutils libcap libtirpc nvidia-modprobe \
+	host-pkgconf host-elfutils host-libcap
+
+LIBNVIDIA_CONTAINER_INCLUDE_DIRS += \
+	-I$(HOST_DIR)/include -I$(STAGING_DIR)/usr/include \
+	-I$(STAGING_DIR)/usr/include/nvidia-modprobe-utils
+
+LIBNVIDIA_CONTAINER_MAKE_OPTS = \
+	AR="$(TARGET_AR)" STRIP="$(TARGET_STRIP)" \
+	CC="$(TARGET_CC)" CFLAGS="$(TARGET_CFLAGS) \
+	$(LIBNVIDIA_CONTAINER_INCLUDE_DIRS) -D_GNU_SOURCE" \
+	CXX="$(TARGET_CXX)" CPPFLAGS="$(TARGET_CXXFLAGS)" \
+	LD="$(TARGET_LD)" LDFLAGS="$(TARGET_LDFLAGS)" \
+	OBJCPY="$(TARGET_OBJCOPY)" \
+	RPCGEN="$(HOST_DIR)/bin/rpcgen" \
+	WITH_LIBELF=yes \
+	WITH_TIRPC=no
+
+ifeq ($(BR2_PACKAGE_LIBSECCOMP),y)
+LIBNVIDIA_CONTAINER_MAKE_OPTS += WITH_SECCOMP=yes
+LIBNVIDIA_CONTAINER_DEPENDENCIES += libseccomp
+else
+LIBNVIDIA_CONTAINER_MAKE_OPTS += WITH_SECCOMP=no
+endif
+
+define LIBNVIDIA_CONTAINER_BUILD_CMDS
+	$(TARGET_MAKE_ENV) $(MAKE) -C $(@D) \
+		$(LIBNVIDIA_CONTAINER_MAKE_OPTS) \
+		shared tools
+endef
+
+define LIBNVIDIA_CONTAINER_INSTALL_STAGING_CMDS
+	$(TARGET_MAKE_ENV) $(MAKE) -C $(@D) \
+		$(LIBNVIDIA_CONTAINER_MAKE_OPTS) \
+		DESTDIR="$(STAGING_DIR)" \
+		install
+endef
+
+define LIBNVIDIA_CONTAINER_INSTALL_TARGET_CMDS
+	$(TARGET_MAKE_ENV) $(MAKE) -C $(@D) \
+		$(LIBNVIDIA_CONTAINER_MAKE_OPTS) \
+		DESTDIR="$(TARGET_DIR)" \
+		install
+endef
+
+$(eval $(generic-package))