diff options
Diffstat (limited to 'tools/rmtool/src/rm_api.c')
| -rw-r--r-- | tools/rmtool/src/rm_api.c | 291 |
1 files changed, 291 insertions, 0 deletions
diff --git a/tools/rmtool/src/rm_api.c b/tools/rmtool/src/rm_api.c new file mode 100644 index 0000000..17fb48b --- /dev/null +++ b/tools/rmtool/src/rm_api.c @@ -0,0 +1,291 @@ +/* rm_api.c - RM ioctl client, GPU discovery and control plumbing. + * + * The ioctl ladder (root -> device -> subdevice) and NV_ESC_RM_CONTROL + * pattern follow tools/rmq/rmq.c and tools/guest-diag/guest_engines.c. + * All operations here are control-path only; no kernel module, VFIO or + * device state is modified. + */ +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <unistd.h> + +#include "rm_api.h" + +#define CONTROL_NODE "/dev/nvidiactl" + +static NvU32 rm_alloc_root(int fd, NvHandle *out) +{ + NVOS21_PARAMETERS p = { 0 }; + + p.hRoot = 0; + p.hObjectParent = 0; + p.hClass = NV01_ROOT_CLIENT; + if (ioctl(fd, NV_IOCTL_RM_ALLOC21, &p) < 0) + return (NvU32)-1; + if (p.status != 0) + return p.status; + *out = p.hObjectNew; + return 0; +} + +static NvU32 rm_alloc64(int fd, NvHandle hRoot, NvHandle hParent, + NvU32 hClass, void *params, NvU32 params_size, + NvHandle *out) +{ + NVOS64_PARAMETERS p = { 0 }; + + p.hRoot = hRoot; + p.hObjectParent = hParent; + p.hClass = hClass; + p.pAllocParms = (NvP64)params; + p.paramsSize = params_size; + p.flags = NVOS64_FLAGS_NONE; + if (ioctl(fd, NV_IOCTL_RM_ALLOC64, &p) < 0) + return (NvU32)-1; + if (p.status != 0) + return p.status; + *out = p.hObjectNew; + return 0; +} + +static NvU32 rm_version_check(int fd) +{ + nv_ioctl_rm_api_version_t v = { 0 }; + + v.cmd = NV_RM_API_VERSION_CMD_QUERY; + if (ioctl(fd, NV_IOCTL_CHECK_VERSION, &v) < 0) + return (NvU32)-1; + if (v.reply != NV_RM_API_VERSION_REPLY_RECOGNIZED) + return (NvU32)-2; + return 0; +} + +/* The open module registers a GPU once its device node open completes; a + * NV01_DEVICE_0 alloc issued before that fails with NV_ERR_INVALID_STATE. + */ +static int rm_wait_gpu_open(int gpu_minor, const char *dev_path, int *gpu_fd) +{ + nv_ioctl_wait_open_complete_t w = { 0 }; + char path[64]; + int fd; + + if (dev_path != NULL) { + snprintf(path, sizeof(path), "%s", dev_path); + } else if (gpu_minor >= 0) { + snprintf(path, sizeof(path), "/dev/nvidia%d", gpu_minor); + } else { + snprintf(path, sizeof(path), "/dev/nvidia0"); + } + fd = open(path, O_RDWR | O_NONBLOCK | O_CLOEXEC); + if (fd < 0) + return -errno; + if (ioctl(fd, NV_IOCTL_WAIT_OPEN_COMPLETE, &w) < 0) { + int err = -errno; + + close(fd); + return err; + } + if (w.rc != 0) { + close(fd); + return w.rc; + } + *gpu_fd = fd; + return 0; +} + +int rm_client_open(struct rm_client *c, int gpu_minor, const char *dev_path) +{ + NV0080_ALLOC_PARAMETERS dev_params; + NV2080_ALLOC_PARAMETERS sub_params; + NvU32 status; + int rc; + + memset(c, 0, sizeof(*c)); + c->fd = -1; + c->gpu_fd = -1; + c->gpu_minor = gpu_minor >= 0 ? gpu_minor : 0; + c->fd = open(CONTROL_NODE, O_RDWR | O_CLOEXEC); + if (c->fd < 0) + return -errno; + + status = rm_version_check(c->fd); + if (status != 0) { + fprintf(stderr, "rmtool: RM version check failed: %s\n", + rm_status_str(status)); + rm_client_close(c); + return (int)status; + } + + rc = rm_wait_gpu_open(gpu_minor, dev_path, &c->gpu_fd); + if (rc != 0) { + fprintf(stderr, "rmtool: GPU open/complete failed: %s\n", + strerror(-rc)); + rm_client_close(c); + return rc; + } + + status = rm_alloc_root(c->fd, &c->hClient); + if (status != 0) { + fprintf(stderr, "rmtool: NV01_ROOT_CLIENT alloc failed: " + "0x%x\n", status); + rm_client_close(c); + return (int)status; + } + + memset(&dev_params, 0, sizeof(dev_params)); + dev_params.deviceId = (NvU32)c->gpu_minor; + status = rm_alloc64(c->fd, c->hClient, c->hClient, NV01_DEVICE_0, + &dev_params, sizeof(dev_params), &c->hDevice); + if (status != 0) { + fprintf(stderr, "rmtool: NV01_DEVICE_0 alloc failed: 0x%x\n", + status); + rm_client_close(c); + return (int)status; + } + + memset(&sub_params, 0, sizeof(sub_params)); + sub_params.subDeviceId = 0; + status = rm_alloc64(c->fd, c->hClient, c->hDevice, NV20_SUBDEVICE_0, + &sub_params, sizeof(sub_params), &c->hSubdevice); + if (status != 0) { + fprintf(stderr, "rmtool: NV20_SUBDEVICE_0 alloc failed: " + "0x%x\n", status); + rm_client_close(c); + return (int)status; + } + return 0; +} + +void rm_client_close(struct rm_client *c) +{ + if (c->gpu_fd >= 0) + close(c->gpu_fd); + if (c->fd >= 0) + close(c->fd); + c->fd = -1; + c->gpu_fd = -1; + c->hClient = 0; + c->hDevice = 0; + c->hSubdevice = 0; +} + +NvU32 rm_control(struct rm_client *c, NvU32 hObject, NvU32 cmd, + void *params, NvU32 size, int *ioctl_ret) +{ + NVOS54_PARAMETERS p = { 0 }; + + p.hClient = c->hClient; + p.hObject = hObject; + p.cmd = cmd; + p.params = (NvP64)params; + p.paramsSize = size; + if (ioctl(c->fd, NV_IOCTL_RM_CONTROL, &p) < 0) { + if (ioctl_ret) + *ioctl_ret = -errno; + return (NvU32)-1; + } + if (ioctl_ret) + *ioctl_ret = 0; + return p.status; +} + +const char *rm_status_str(NvU32 status) +{ + static char buf[32]; + + if (status == 0) + return "NV_OK"; + snprintf(buf, sizeof(buf), "NV_STATUS(0x%x)", status); + return buf; +} + +/* ------------------------------------------------------------------ */ +/* /proc/driver/nvidia/gpus discovery */ +/* ------------------------------------------------------------------ */ + +static void copy_trim(char *dst, size_t len, const char *src) +{ + size_t n; + + while (*src == ' ' || *src == '\t') + src++; + n = strcspn(src, "\r\n"); + if (n >= len) + n = len - 1; + memcpy(dst, src, n); + dst[n] = '\0'; + while (n > 0 && (dst[n - 1] == ' ' || dst[n - 1] == '\t')) + dst[--n] = '\0'; +} + +static void parse_information(const char *path, struct rm_gpu_desc *gpu) +{ + FILE *fh; + char line[512]; + + fh = fopen(path, "r"); + if (fh == NULL) + return; + while (fgets(line, sizeof(line), fh) != NULL) { + char *colon = strchr(line, ':'); + + if (colon == NULL) + continue; + *colon = '\0'; + if (strcmp(line, "Model") == 0) + copy_trim(gpu->model, sizeof(gpu->model), colon + 1); + else if (strcmp(line, "GPU UUID") == 0) + copy_trim(gpu->uuid, sizeof(gpu->uuid), colon + 1); + else if (strcmp(line, "Video BIOS") == 0) + copy_trim(gpu->vbios, sizeof(gpu->vbios), colon + 1); + else if (strcmp(line, "Bus Location") == 0) + copy_trim(gpu->bdf, sizeof(gpu->bdf), colon + 1); + else if (strcmp(line, "IRQ") == 0) + gpu->irq = atoi(colon + 1); + else if (strcmp(line, "Device Minor") == 0) + gpu->minor = atoi(colon + 1); + } + fclose(fh); +} + +static int gpu_compare(const void *a, const void *b) +{ + const struct rm_gpu_desc *ga = a; + const struct rm_gpu_desc *gb = b; + + return ga->minor - gb->minor; +} + +int rm_scan_gpus(struct rm_gpu_desc *out, int max) +{ + const char *root = "/proc/driver/nvidia/gpus"; + DIR *dir; + struct dirent *ent; + int n = 0; + + dir = opendir(root); + if (dir == NULL) + return -errno; + while ((ent = readdir(dir)) != NULL && n < max) { + char info[512]; + struct rm_gpu_desc *gpu = &out[n]; + + if (ent->d_name[0] == '.') + continue; + memset(gpu, 0, sizeof(*gpu)); + gpu->minor = -1; + copy_trim(gpu->bdf, sizeof(gpu->bdf), ent->d_name); + snprintf(info, sizeof(info), "%s/%s/information", root, + ent->d_name); + parse_information(info, gpu); + n++; + } + closedir(dir); + qsort(out, n, sizeof(*out), gpu_compare); + return n; +} |
