802 lines
20 KiB
Diff
802 lines
20 KiB
Diff
|
diff --git a/src/nvidia-modprobe-utils.c b/src/nvidia-modprobe-utils.c
|
||
|
new file mode 100644
|
||
|
index 0000000..0f97cf9
|
||
|
--- /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
|