configure.c 11.6 KB
Newer Older
1
/*
J
Jonathan Calmels 已提交
2
 * Copyright (c) 2017-2018, NVIDIA CORPORATION. All rights reserved.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
 */

#include <alloca.h>
#include <err.h>
#include <stdlib.h>

#include "cli.h"
#include "dsl.h"

static error_t configure_parser(int, char *, struct argp_state *);
static int check_cuda_version(const struct dsl_data *, enum dsl_comparator, const char *);
static int check_driver_version(const struct dsl_data *, enum dsl_comparator, const char *);
static int check_device_arch(const struct dsl_data *, enum dsl_comparator, const char *);

const struct argp configure_usage = {
        (const struct argp_option[]){
                {NULL, 0, NULL, 0, "Options:", -1},
                {"pid", 'p', "PID", 0, "Container PID", -1},
                {"device", 'd', "ID", 0, "Device UUID(s) or index(es) to isolate", -1},
                {"require", 'r', "EXPR", 0, "Check container requirements", -1},
                {"ldconfig", 'l', "PATH", 0, "Path to the ldconfig binary", -1},
                {"compute", 'c', NULL, 0, "Enable compute capability", -1},
                {"utility", 'u', NULL, 0, "Enable utility capability", -1},
                {"video", 'v', NULL, 0, "Enable video capability", -1},
                {"graphics", 'g', NULL, 0, "Enable graphics capability", -1},
28
                {"display", 'D', NULL, 0, "Enable display capability", -1},
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
                {"compat32", 0x80, NULL, 0, "Enable 32bits compatibility", -1},
                {"no-cgroups", 0x81, NULL, 0, "Don't use cgroup enforcement", -1},
                {"no-devbind", 0x82, NULL, 0, "Don't bind mount devices", -1},
                {0},
        },
        configure_parser,
        "ROOTFS",
        "Configure a container with GPU support by exposing device drivers to it.\n\n"
        "This command enters the namespace of the container process referred by PID (or the current parent process if none specified) "
        "and performs the necessary steps to ensure that the given capabilities are available inside the container.\n"
        "It is assumed that the container has been created but not yet started, and the host filesystem is accessible (i.e. chroot/pivot_root hasn't been called).",
        NULL,
        NULL,
        NULL,
};

static const struct dsl_rule rules[] = {
        {"cuda", &check_cuda_version},
        {"driver", &check_driver_version},
        {"arch", &check_device_arch},
};

static error_t
configure_parser(int key, char *arg, struct argp_state *state)
{
        struct context *ctx = state->input;
        struct error err = {0};

        switch (key) {
        case 'p':
J
Jonathan Calmels 已提交
59
                if (str_to_pid(&err, arg, &ctx->pid) < 0)
60 61 62
                        goto fatal;
                break;
        case 'd':
J
Jonathan Calmels 已提交
63
                if (str_join(&err, &ctx->devices, arg, ",") < 0)
64 65 66 67 68 69 70 71 72 73 74 75 76
                        goto fatal;
                break;
        case 'r':
                if (ctx->nreqs >= nitems(ctx->reqs)) {
                        error_setx(&err, "too many requirements");
                        goto fatal;
                }
                ctx->reqs[ctx->nreqs++] = arg;
                break;
        case 'l':
                ctx->ldconfig = arg;
                break;
        case 'c':
J
Jonathan Calmels 已提交
77
                if (str_join(&err, &ctx->container_flags, "compute", " ") < 0)
78 79 80
                        goto fatal;
                break;
        case 'u':
J
Jonathan Calmels 已提交
81
                if (str_join(&err, &ctx->container_flags, "utility", " ") < 0)
82 83 84
                        goto fatal;
                break;
        case 'v':
J
Jonathan Calmels 已提交
85
                if (str_join(&err, &ctx->container_flags, "video", " ") < 0)
86 87 88
                        goto fatal;
                break;
        case 'g':
J
Jonathan Calmels 已提交
89
                if (str_join(&err, &ctx->container_flags, "graphics", " ") < 0)
90
                        goto fatal;
91 92
                break;
        case 'D':
J
Jonathan Calmels 已提交
93
                if (str_join(&err, &ctx->container_flags, "display", " ") < 0)
94
                        goto fatal;
95 96
                break;
        case 0x80:
J
Jonathan Calmels 已提交
97
                if (str_join(&err, &ctx->container_flags, "compat32", " ") < 0)
98 99 100
                        goto fatal;
                break;
        case 0x81:
J
Jonathan Calmels 已提交
101
                if (str_join(&err, &ctx->container_flags, "no-cgroups", " ") < 0)
102 103 104
                        goto fatal;
                break;
        case 0x82:
J
Jonathan Calmels 已提交
105
                if (str_join(&err, &ctx->container_flags, "no-devbind", " ") < 0)
106 107 108 109 110
                        goto fatal;
                break;
        case ARGP_KEY_ARG:
                if (state->arg_num > 0)
                        argp_usage(state);
J
Jonathan Calmels 已提交
111
                if (arg[0] != '/' || str_equal(arg, "/")) {
112 113 114 115 116 117 118
                        error_setx(&err, "invalid rootfs directory");
                        goto fatal;
                }
                ctx->rootfs = arg;
                break;
        case ARGP_KEY_SUCCESS:
                if (ctx->pid > 0) {
J
Jonathan Calmels 已提交
119
                        if (str_join(&err, &ctx->container_flags, "supervised", " ") < 0)
120 121 122
                                goto fatal;
                } else {
                        ctx->pid = getppid();
J
Jonathan Calmels 已提交
123
                        if (str_join(&err, &ctx->container_flags, "standalone", " ") < 0)
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
                                goto fatal;
                }
                break;
        case ARGP_KEY_END:
                if (state->arg_num < 1)
                        argp_usage(state);
                break;
        default:
                return (ARGP_ERR_UNKNOWN);
        }
        return (0);

 fatal:
        errx(EXIT_FAILURE, "input error: %s", err.msg);
        return (0);
}

static int
check_cuda_version(const struct dsl_data *data, enum dsl_comparator cmp, const char *version)
{
        return (dsl_compare_version(data->drv->cuda_version, cmp, version));
}

static int
check_driver_version(const struct dsl_data *data, enum dsl_comparator cmp, const char *version)
{
        return (dsl_compare_version(data->drv->nvrm_version, cmp, version));
}

static int
check_device_arch(const struct dsl_data *data, enum dsl_comparator cmp, const char *arch)
{
        /* XXX No device is visible, assume the arch is ok. */
        if (data->dev == NULL)
                return (true);
        return (dsl_compare_version(data->dev->arch, cmp, arch));
}

int
configure_command(const struct context *ctx)
{
        struct nvc_context *nvc = NULL;
        struct nvc_config *nvc_cfg = NULL;
        struct nvc_driver_info *drv = NULL;
        struct nvc_device_info *dev = NULL;
        struct nvc_container *cnt = NULL;
        struct nvc_container_config *cnt_cfg = NULL;
        const struct nvc_device **gpus = NULL;
        bool eval_reqs = true;
        struct error err = {0};
        int rv = EXIT_FAILURE;

176 177 178
        if (perm_set_capabilities(&err, CAP_PERMITTED, pcaps, nitems(pcaps)) < 0 ||
            perm_set_capabilities(&err, CAP_INHERITABLE, NULL, 0) < 0 ||
            perm_set_bounds(&err, bcaps, nitems(bcaps)) < 0) {
179 180 181 182 183
                warnx("permission error: %s", err.msg);
                return (rv);
        }

        /* Initialize the library and container contexts. */
184 185
        int c = ctx->load_kmods ? NVC_INIT_KMODS : NVC_INIT;
        if (perm_set_capabilities(&err, CAP_EFFECTIVE, ecaps[c], ecaps_size(c)) < 0) {
186 187 188 189 190 191 192 193 194 195 196
                warnx("permission error: %s", err.msg);
                goto fail;
        }
        if ((nvc = nvc_context_new()) == NULL ||
            (nvc_cfg = nvc_config_new()) == NULL ||
            (cnt_cfg = nvc_container_config_new(ctx->pid, ctx->rootfs)) == NULL) {
                warn("memory allocation failed");
                goto fail;
        }
        nvc_cfg->uid = ctx->uid;
        nvc_cfg->gid = ctx->gid;
197
        nvc_cfg->root = ctx->root;
198
        nvc_cfg->ldcache = ctx->ldcache;
199 200 201 202
        if (nvc_init(nvc, nvc_cfg, ctx->init_flags) < 0) {
                warnx("initialization error: %s", nvc_error(nvc));
                goto fail;
        }
203
        if (perm_set_capabilities(&err, CAP_EFFECTIVE, ecaps[NVC_CONTAINER], ecaps_size(NVC_CONTAINER)) < 0) {
204 205 206 207 208 209 210 211 212 213
                warnx("permission error: %s", err.msg);
                goto fail;
        }
        cnt_cfg->ldconfig = ctx->ldconfig;
        if ((cnt = nvc_container_new(nvc, cnt_cfg, ctx->container_flags)) == NULL) {
                warnx("container error: %s", nvc_error(nvc));
                goto fail;
        }

        /* Query the driver and device information. */
214
        if (perm_set_capabilities(&err, CAP_EFFECTIVE, ecaps[NVC_INFO], ecaps_size(NVC_INFO)) < 0) {
215 216 217
                warnx("permission error: %s", err.msg);
                goto fail;
        }
J
Jonathan Calmels 已提交
218 219
        if ((drv = nvc_driver_info_new(nvc, NULL)) == NULL ||
            (dev = nvc_device_info_new(nvc, NULL)) == NULL) {
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
                warnx("detection error: %s", nvc_error(nvc));
                goto fail;
        }

        /* Select the visible GPU devices. */
        if (dev->ngpus > 0) {
                gpus = alloca(dev->ngpus * sizeof(*gpus));
                memset(gpus, 0, dev->ngpus * sizeof(*gpus));
                if (select_devices(&err, ctx->devices, gpus, dev->gpus, dev->ngpus) < 0) {
                        warnx("device error: %s", err.msg);
                        goto fail;
                }
        }

        /*
         * Check the container requirements.
         * Try evaluating per visible device first, and globally otherwise.
         */
        for (size_t i = 0; i < dev->ngpus; ++i) {
                if (gpus[i] == NULL)
                        continue;

                struct dsl_data data = {drv, gpus[i]};
                for (size_t j = 0; j < ctx->nreqs; ++j) {
                        if (dsl_evaluate(&err, ctx->reqs[j], &data, rules, nitems(rules)) < 0) {
                                warnx("requirement error: %s", err.msg);
                                goto fail;
                        }
                }
                eval_reqs = false;
        }
        if (eval_reqs) {
                struct dsl_data data = {drv, NULL};
                for (size_t j = 0; j < ctx->nreqs; ++j) {
                        if (dsl_evaluate(&err, ctx->reqs[j], &data, rules, nitems(rules)) < 0) {
                                warnx("requirement error: %s", err.msg);
                                goto fail;
                        }
                }
        }

        /* Mount the driver and visible devices. */
262
        if (perm_set_capabilities(&err, CAP_EFFECTIVE, ecaps[NVC_MOUNT], ecaps_size(NVC_MOUNT)) < 0) {
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
                warnx("permission error: %s", err.msg);
                goto fail;
        }
        if (nvc_driver_mount(nvc, cnt, drv) < 0) {
                warnx("mount error: %s", nvc_error(nvc));
                goto fail;
        }
        for (size_t i = 0; i < dev->ngpus; ++i) {
                if (gpus[i] != NULL && nvc_device_mount(nvc, cnt, gpus[i]) < 0) {
                        warnx("mount error: %s", nvc_error(nvc));
                        goto fail;
                }
        }

        /* Update the container ldcache. */
278
        if (perm_set_capabilities(&err, CAP_EFFECTIVE, ecaps[NVC_LDCACHE], ecaps_size(NVC_LDCACHE)) < 0) {
279 280 281 282 283 284 285 286
                warnx("permission error: %s", err.msg);
                goto fail;
        }
        if (nvc_ldcache_update(nvc, cnt) < 0) {
                warnx("ldcache error: %s", nvc_error(nvc));
                goto fail;
        }

287
        if (perm_set_capabilities(&err, CAP_EFFECTIVE, ecaps[NVC_SHUTDOWN], ecaps_size(NVC_SHUTDOWN)) < 0) {
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
                warnx("permission error: %s", err.msg);
                goto fail;
        }
        rv = EXIT_SUCCESS;

 fail:
        nvc_shutdown(nvc);
        nvc_container_free(cnt);
        nvc_device_info_free(dev);
        nvc_driver_info_free(drv);
        nvc_container_config_free(cnt_cfg);
        nvc_config_free(nvc_cfg);
        nvc_context_free(nvc);
        error_reset(&err);
        return (rv);
}