virpcitest.c 11.0 KB
Newer Older
M
Michal Privoznik 已提交
1
/*
2
 * Copyright (C) 2013, 2014 Red Hat, Inc.
M
Michal Privoznik 已提交
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 28 29 30 31
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.  If not, see
 * <http://www.gnu.org/licenses/>.
 */

#include <config.h>

#include "testutils.h"

#ifdef __linux__

# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
# include <virpci.h>

# define VIR_FROM_THIS VIR_FROM_NONE

32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
static int
testVirPCIDeviceCheckDriver(virPCIDevicePtr dev, const char *expected)
{
    char *path = NULL;
    char *driver = NULL;
    int ret = -1;

    if (virPCIDeviceGetDriverPathAndName(dev, &path, &driver) < 0)
        goto cleanup;

    if (STRNEQ_NULLABLE(driver, expected)) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "PCI device %s driver mismatch: %s, expecting %s",
                       virPCIDeviceGetName(dev), NULLSTR(driver),
                       NULLSTR(expected));
        goto cleanup;
    }

    ret = 0;
51
 cleanup:
52 53 54 55 56
    VIR_FREE(path);
    VIR_FREE(driver);
    return ret;
}

M
Michal Privoznik 已提交
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
static int
testVirPCIDeviceNew(const void *opaque ATTRIBUTE_UNUSED)
{
    int ret = -1;
    virPCIDevicePtr dev;
    const char *devName;

    if (!(dev = virPCIDeviceNew(0, 0, 0, 0)))
        goto cleanup;

    devName = virPCIDeviceGetName(dev);
    if (STRNEQ(devName, "0000:00:00.0")) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "PCI device name mismatch: %s, expecting %s",
                       devName, "0000:00:00.0");
        goto cleanup;
    }

    ret = 0;
76
 cleanup:
M
Michal Privoznik 已提交
77 78 79 80
    virPCIDeviceFree(dev);
    return ret;
}

81 82 83 84 85 86
# define CHECK_LIST_COUNT(list, cnt) \
    if ((count = virPCIDeviceListCount(list)) != cnt) { \
        virReportError(VIR_ERR_INTERNAL_ERROR, \
                       "Unexpected count of items in " #list ": %d, " \
                       "expecting %zu", count, (size_t) cnt); \
        goto cleanup; \
87 88 89
    }

static int
90
testVirPCIDeviceDetach(const void *opaque ATTRIBUTE_UNUSED)
91 92
{
    int ret = -1;
93 94
    virPCIDevicePtr dev[] = {NULL, NULL, NULL};
    size_t i, nDev = ARRAY_CARDINALITY(dev);
95 96 97
    virPCIDeviceListPtr activeDevs = NULL, inactiveDevs = NULL;
    int count;

98
    if (!(activeDevs = virPCIDeviceListNew()) ||
99 100 101 102 103 104
        !(inactiveDevs = virPCIDeviceListNew()))
        goto cleanup;

    CHECK_LIST_COUNT(activeDevs, 0);
    CHECK_LIST_COUNT(inactiveDevs, 0);

105
    for (i = 0; i < nDev; i++) {
106
        if (!(dev[i] = virPCIDeviceNew(0, 0, i + 1, 0)))
107
            goto cleanup;
108

M
Michal Privoznik 已提交
109
        virPCIDeviceSetStubDriver(dev[i], VIR_PCI_STUB_DRIVER_VFIO);
110

111 112
        if (virPCIDeviceDetach(dev[i], activeDevs, inactiveDevs) < 0)
            goto cleanup;
113

M
Michal Privoznik 已提交
114
        if (testVirPCIDeviceCheckDriver(dev[i], "vfio-pci") < 0)
115 116
            goto cleanup;

117 118 119 120 121
        CHECK_LIST_COUNT(activeDevs, 0);
        CHECK_LIST_COUNT(inactiveDevs, i + 1);
    }

    ret = 0;
122
 cleanup:
123 124 125 126 127 128
    for (i = 0; i < nDev; i++)
        virPCIDeviceFree(dev[i]);
    virObjectUnref(activeDevs);
    virObjectUnref(inactiveDevs);
    return ret;
}
129

130 131 132 133 134 135 136 137 138 139 140
static int
testVirPCIDeviceReset(const void *opaque ATTRIBUTE_UNUSED)
{
    int ret = -1;
    virPCIDevicePtr dev[] = {NULL, NULL, NULL};
    size_t i, nDev = ARRAY_CARDINALITY(dev);
    virPCIDeviceListPtr activeDevs = NULL, inactiveDevs = NULL;
    int count;

    if (!(activeDevs = virPCIDeviceListNew()) ||
        !(inactiveDevs = virPCIDeviceListNew()))
141 142 143
        goto cleanup;

    CHECK_LIST_COUNT(activeDevs, 0);
144 145 146
    CHECK_LIST_COUNT(inactiveDevs, 0);

    for (i = 0; i < nDev; i++) {
147
        if (!(dev[i] = virPCIDeviceNew(0, 0, i + 1, 0)))
148 149
            goto cleanup;

M
Michal Privoznik 已提交
150
        virPCIDeviceSetStubDriver(dev[i], VIR_PCI_STUB_DRIVER_VFIO);
151

152 153 154
        if (virPCIDeviceReset(dev[i], activeDevs, inactiveDevs) < 0)
            goto cleanup;
    }
155

156
    ret = 0;
157
 cleanup:
158 159
    for (i = 0; i < nDev; i++)
        virPCIDeviceFree(dev[i]);
160 161 162 163 164
    virObjectUnref(activeDevs);
    virObjectUnref(inactiveDevs);
    return ret;
}

165
static int
166
testVirPCIDeviceReattach(const void *opaque ATTRIBUTE_UNUSED)
167 168
{
    int ret = -1;
169 170
    virPCIDevicePtr dev[] = {NULL, NULL, NULL};
    size_t i, nDev = ARRAY_CARDINALITY(dev);
171 172 173
    virPCIDeviceListPtr activeDevs = NULL, inactiveDevs = NULL;
    int count;

174
    if (!(activeDevs = virPCIDeviceListNew()) ||
175 176 177
        !(inactiveDevs = virPCIDeviceListNew()))
        goto cleanup;

178 179 180
    for (i = 0; i < nDev; i++) {
        if (!(dev[i] = virPCIDeviceNew(0, 0, i + 1, 0)))
            goto cleanup;
181

182 183 184 185
        if (virPCIDeviceListAdd(inactiveDevs, dev[i]) < 0) {
            virPCIDeviceFree(dev[i]);
            goto cleanup;
        }
186

187 188
        CHECK_LIST_COUNT(activeDevs, 0);
        CHECK_LIST_COUNT(inactiveDevs, i + 1);
189

M
Michal Privoznik 已提交
190
        virPCIDeviceSetStubDriver(dev[i], VIR_PCI_STUB_DRIVER_VFIO);
191
    }
192

193
    CHECK_LIST_COUNT(activeDevs, 0);
194
    CHECK_LIST_COUNT(inactiveDevs, nDev);
195

196 197 198
    for (i = 0; i < nDev; i++) {
        if (virPCIDeviceReattach(dev[i], activeDevs, inactiveDevs) < 0)
            goto cleanup;
199

200 201 202
        CHECK_LIST_COUNT(activeDevs, 0);
        CHECK_LIST_COUNT(inactiveDevs, nDev - i - 1);
    }
203 204

    ret = 0;
205
 cleanup:
206 207 208 209
    virObjectUnref(activeDevs);
    virObjectUnref(inactiveDevs);
    return ret;
}
210

211 212 213 214 215
struct testPCIDevData {
    unsigned int domain;
    unsigned int bus;
    unsigned int slot;
    unsigned int function;
216
    const char *driver;
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
};

static int
testVirPCIDeviceIsAssignable(const void *opaque)
{
    const struct testPCIDevData *data = opaque;
    int ret = -1;
    virPCIDevicePtr dev;

    if (!(dev = virPCIDeviceNew(data->domain, data->bus, data->slot, data->function)))
        goto cleanup;

    if (virPCIDeviceIsAssignable(dev, true))
        ret = 0;

    virPCIDeviceFree(dev);
233
 cleanup:
234 235 236
    return ret;
}

237 238 239 240 241 242 243 244 245 246 247
static int
testVirPCIDeviceDetachSingle(const void *opaque)
{
    const struct testPCIDevData *data = opaque;
    int ret = -1;
    virPCIDevicePtr dev;

    dev = virPCIDeviceNew(data->domain, data->bus, data->slot, data->function);
    if (!dev)
        goto cleanup;

M
Michal Privoznik 已提交
248
    virPCIDeviceSetStubDriver(dev, VIR_PCI_STUB_DRIVER_VFIO);
249 250

    if (virPCIDeviceDetach(dev, NULL, NULL) < 0)
251 252 253
        goto cleanup;

    ret = 0;
254
 cleanup:
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
    virPCIDeviceFree(dev);
    return ret;
}

static int
testVirPCIDeviceReattachSingle(const void *opaque)
{
    const struct testPCIDevData *data = opaque;
    int ret = -1;
    virPCIDevicePtr dev;

    dev = virPCIDeviceNew(data->domain, data->bus, data->slot, data->function);
    if (!dev)
        goto cleanup;

270 271 272 273
    virPCIDeviceSetUnbindFromStub(dev, true);
    virPCIDeviceSetRemoveSlot(dev, true);
    virPCIDeviceSetReprobe(dev, true);

274 275 276 277
    if (virPCIDeviceReattach(dev, NULL, NULL) < 0)
        goto cleanup;

    ret = 0;
278
 cleanup:
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
    virPCIDeviceFree(dev);
    return ret;
}

static int
testVirPCIDeviceCheckDriverTest(const void *opaque)
{
    const struct testPCIDevData *data = opaque;
    int ret = -1;
    virPCIDevicePtr dev;

    dev = virPCIDeviceNew(data->domain, data->bus, data->slot, data->function);
    if (!dev)
        goto cleanup;

    if (testVirPCIDeviceCheckDriver(dev, data->driver) < 0)
        goto cleanup;

    ret = 0;
298
 cleanup:
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
    virPCIDeviceFree(dev);
    return ret;
}

static int
testVirPCIDeviceUnbind(const void *opaque)
{
    const struct testPCIDevData *data = opaque;
    int ret = -1;
    virPCIDevicePtr dev;

    dev = virPCIDeviceNew(data->domain, data->bus, data->slot, data->function);
    if (!dev)
        goto cleanup;

314
    if (virPCIDeviceUnbind(dev) < 0)
315 316 317
        goto cleanup;

    ret = 0;
318
 cleanup:
319 320 321 322
    virPCIDeviceFree(dev);
    return ret;
}

323
# define FAKEROOTDIRTEMPLATE abs_builddir "/fakerootdir-XXXXXX"
324

M
Michal Privoznik 已提交
325 326 327 328
static int
mymain(void)
{
    int ret = 0;
329
    char *fakerootdir;
M
Michal Privoznik 已提交
330

331
    if (VIR_STRDUP_QUIET(fakerootdir, FAKEROOTDIRTEMPLATE) < 0) {
332
        VIR_TEST_DEBUG("Out of memory\n");
M
Michal Privoznik 已提交
333 334 335
        abort();
    }

336 337
    if (!mkdtemp(fakerootdir)) {
        VIR_TEST_DEBUG("Cannot create fakerootdir");
M
Michal Privoznik 已提交
338 339 340
        abort();
    }

341
    setenv("LIBVIRT_FAKE_ROOT_DIR", fakerootdir, 1);
M
Michal Privoznik 已提交
342

343 344 345 346
# define DO_TEST(fnc) \
    do { \
        if (virTestRun(#fnc, fnc, NULL) < 0) \
            ret = -1; \
M
Michal Privoznik 已提交
347 348
    } while (0)

349 350 351 352 353 354 355 356 357 358 359 360 361 362
# define DO_TEST_PCI(fnc, domain, bus, slot, function) \
    do { \
        struct testPCIDevData data = { \
            domain, bus, slot, function, NULL \
        }; \
        char *label = NULL; \
        if (virAsprintf(&label, "%s(%04x:%02x:%02x.%x)", \
                        #fnc, domain, bus, slot, function) < 0) { \
            ret = -1; \
            break; \
        } \
        if (virTestRun(label, fnc, &data) < 0) \
            ret = -1; \
        VIR_FREE(label); \
363 364
    } while (0)

365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
# define DO_TEST_PCI_DRIVER(domain, bus, slot, function, driver) \
    do { \
        struct testPCIDevData data = { \
            domain, bus, slot, function, driver \
        }; \
        char *label = NULL; \
        if (virAsprintf(&label, "PCI driver %04x:%02x:%02x.%x is %s", \
                        domain, bus, slot, function, \
                        NULLSTR(driver)) < 0) { \
            ret = -1; \
            break; \
        } \
        if (virTestRun(label, testVirPCIDeviceCheckDriverTest, \
                       &data) < 0) \
            ret = -1; \
        VIR_FREE(label); \
381 382 383 384 385 386
    } while (0)

    /* Changes made to individual devices are persistent and the
     * tests often rely on the state set by previous tests.
     */

M
Michal Privoznik 已提交
387
    DO_TEST(testVirPCIDeviceNew);
388
    DO_TEST(testVirPCIDeviceDetach);
389
    DO_TEST(testVirPCIDeviceReset);
390
    DO_TEST(testVirPCIDeviceReattach);
391 392
    DO_TEST_PCI(testVirPCIDeviceIsAssignable, 5, 0x90, 1, 0);
    DO_TEST_PCI(testVirPCIDeviceIsAssignable, 1, 1, 0, 0);
M
Michal Privoznik 已提交
393

394 395 396 397 398 399 400 401 402 403 404 405 406 407
    /* Reattach a device already bound to non-stub a driver */
    DO_TEST_PCI_DRIVER(0, 0x0a, 1, 0, "i915");
    DO_TEST_PCI(testVirPCIDeviceReattachSingle, 0, 0x0a, 1, 0);
    DO_TEST_PCI_DRIVER(0, 0x0a, 1, 0, "i915");

    /* Reattach an unbound device */
    DO_TEST_PCI(testVirPCIDeviceUnbind, 0, 0x0a, 1, 0);
    DO_TEST_PCI_DRIVER(0, 0x0a, 1, 0, NULL);
    DO_TEST_PCI(testVirPCIDeviceReattachSingle, 0, 0x0a, 1, 0);
    DO_TEST_PCI_DRIVER(0, 0x0a, 1, 0, "i915");

    /* Detach an unbound device */
    DO_TEST_PCI_DRIVER(0, 0x0a, 2, 0, NULL);
    DO_TEST_PCI(testVirPCIDeviceDetachSingle, 0, 0x0a, 2, 0);
M
Michal Privoznik 已提交
408
    DO_TEST_PCI_DRIVER(0, 0x0a, 2, 0, "vfio-pci");
409 410 411 412 413 414

    /* Reattach an unknown unbound device */
    DO_TEST_PCI_DRIVER(0, 0x0a, 3, 0, NULL);
    DO_TEST_PCI(testVirPCIDeviceReattachSingle, 0, 0x0a, 3, 0);
    DO_TEST_PCI_DRIVER(0, 0x0a, 3, 0, NULL);

M
Michal Privoznik 已提交
415
    if (getenv("LIBVIRT_SKIP_CLEANUP") == NULL)
416
        virFileDeleteTree(fakerootdir);
M
Michal Privoznik 已提交
417

418
    VIR_FREE(fakerootdir);
M
Michal Privoznik 已提交
419

420
    return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
M
Michal Privoznik 已提交
421 422
}

423
VIR_TEST_MAIN_PRELOAD(mymain, VIR_TEST_MOCK("virpci"))
M
Michal Privoznik 已提交
424 425 426 427 428 429 430
#else
int
main(void)
{
    return EXIT_AM_SKIP;
}
#endif