aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/cns3xxx/files
Commit message (Expand)AuthorAgeFilesLines
* cns3xxx: fix USB ehci/ohci platform driverFelix Fietkau2013-04-101-3/+51
* cns3xxx: remove the watchdog IRQ from the resources, it breaks the driver ini...Felix Fietkau2013-04-071-5/+0
* cns3xxx: ethernet - clean the tx ring before refilling the rx ring to reduce ...Felix Fietkau2013-03-111-4/+4
* cns3xxx: ethernet - clean the tx ring only in the poll function, not in the x...Felix Fietkau2013-03-111-6/+11
* cns3xxx: increase number of ethernet driver rx buffersFelix Fietkau2013-03-101-1/+1
* cns3xxx: prevent the ethernet driver from accessing uninitialized dma descrip...Felix Fietkau2013-03-101-1/+2
* cns3xxx: minor ethernet driver cleanupFelix Fietkau2013-03-101-35/+19
* cns3xxx: use kmalloc instead of kzalloc for ethernet rx buffersFelix Fietkau2013-03-101-1/+1
* cns3xxx: fix an rx irq handling corner caseFelix Fietkau2013-03-101-5/+5
* cns3xxx: fix ethernet driver memory barrier usageFelix Fietkau2013-03-101-5/+8
* cns3xxx: reduce buffer allocation size to 2048 bytes, improves ethernet perfo...Felix Fietkau2013-03-101-1/+1
* cns3xxx: add linux 3.8 support and use it by defaultFelix Fietkau2013-03-097-24/+25
* clean up PCI bus topologyImre Kaloz2013-01-101-2/+2
* add nol2x0 cmdline to disable l2x0 cacheImre Kaloz2012-12-231-2/+0
* cns3xxx: fix dwc_otg driver compat with udc-coreFlorian Fainelli2012-12-041-83/+0
* split spi patch, follow mainline file namingImre Kaloz2012-11-121-0/+449
* split the i2c patch, merge the retry fixupImre Kaloz2012-11-121-0/+396
* cns3xxx: convert dwc_otg patches to filesLuka Perkov2012-11-1118-0/+22666
* cns3xxx: convert ethernet patches to filesLuka Perkov2012-11-113-0/+1378
* cns3xxx: exit from laguna late_initcall if not running on laguna boardLuka Perkov2012-11-081-0/+3
* cns3xxx: add PPS support for lagunaImre Kaloz2012-11-081-0/+20
* cns3xxx: use files directoryLuka Perkov2012-11-0610-0/+1907
/a> 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565
/*
 * Copyright (C) 2009      Citrix Ltd.
 * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
 * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
 *
 * This program 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; version 2.1 only. with the special
 * exception on linking described in file LICENSE.
 *
 * This program 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.
 */

#include "libxl_osdeps.h" /* must come before any other headers */

#include "libxl_internal.h"

char *libxl__device_frontend_path(libxl__gc *gc, libxl__device *device)
{
    char *dom_path = libxl__xs_get_dompath(gc, device->domid);

    /* Console 0 is a special case */
    if (device->kind == LIBXL__DEVICE_KIND_CONSOLE && device->devid == 0)
        return libxl__sprintf(gc, "%s/console", dom_path);

    return libxl__sprintf(gc, "%s/device/%s/%d", dom_path,
                          libxl__device_kind_to_string(device->kind),
                          device->devid);
}

char *libxl__device_backend_path(libxl__gc *gc, libxl__device *device)
{
    char *dom_path = libxl__xs_get_dompath(gc, device->backend_domid);

    return libxl__sprintf(gc, "%s/backend/%s/%u/%d", dom_path,
                          libxl__device_kind_to_string(device->backend_kind),
                          device->domid, device->devid);
}

int libxl__parse_backend_path(libxl__gc *gc,
                              const char *path,
                              libxl__device *dev)
{
    /* /local/domain/<domid>/backend/<kind>/<domid>/<devid> */
    char strkind[16]; /* Longest is actually "console" */
    int rc = sscanf(path, "/local/domain/%d/backend/%15[^/]/%u/%d",
                    &dev->backend_domid,
                    strkind,
                    &dev->domid,
                    &dev->devid);

    if (rc != 4)
        return ERROR_FAIL;

    return libxl__device_kind_from_string(strkind, &dev->backend_kind);
}

int libxl__device_generic_add(libxl__gc *gc, libxl__device *device,
                             char **bents, char **fents)
{
    libxl_ctx *ctx = libxl__gc_owner(gc);
    char *frontend_path, *backend_path;
    xs_transaction_t t;
    struct xs_permissions frontend_perms[2];
    struct xs_permissions backend_perms[2];

    frontend_path = libxl__device_frontend_path(gc, device);
    backend_path = libxl__device_backend_path(gc, device);

    frontend_perms[0].id = device->domid;
    frontend_perms[0].perms = XS_PERM_NONE;
    frontend_perms[1].id = device->backend_domid;
    frontend_perms[1].perms = XS_PERM_READ;

    backend_perms[0].id = device->backend_domid;
    backend_perms[0].perms = XS_PERM_NONE;
    backend_perms[1].id = device->domid;
    backend_perms[1].perms = XS_PERM_READ;

retry_transaction:
    t = xs_transaction_start(ctx->xsh);
    /* FIXME: read frontend_path and check state before removing stuff */

    if (fents) {
        xs_rm(ctx->xsh, t, frontend_path);
        xs_mkdir(ctx->xsh, t, frontend_path);
        xs_set_permissions(ctx->xsh, t, frontend_path, frontend_perms, ARRAY_SIZE(frontend_perms));
        xs_write(ctx->xsh, t, libxl__sprintf(gc, "%s/backend", frontend_path), backend_path, strlen(backend_path));
        libxl__xs_writev(gc, t, frontend_path, fents);
    }

    if (bents) {
        xs_rm(ctx->xsh, t, backend_path);
        xs_mkdir(ctx->xsh, t, backend_path);
        xs_set_permissions(ctx->xsh, t, backend_path, backend_perms, ARRAY_SIZE(backend_perms));
        xs_write(ctx->xsh, t, libxl__sprintf(gc, "%s/frontend", backend_path), frontend_path, strlen(frontend_path));
        libxl__xs_writev(gc, t, backend_path, bents);
    }

    if (!xs_transaction_end(ctx->xsh, t, 0)) {
        if (errno == EAGAIN)
            goto retry_transaction;
        else
            LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR, "xs transaction failed");
    }

    return 0;
}

typedef struct {
    libxl__gc *gc;
    libxl_device_disk *disk;
    struct stat stab;
} disk_try_backend_args;

static int disk_try_backend(disk_try_backend_args *a,
                            libxl_disk_backend backend) {
    /* returns 0 (ie, DISK_BACKEND_UNKNOWN) on failure, or
     * backend on success */
    libxl_ctx *ctx = libxl__gc_owner(a->gc);
    switch (backend) {

    case LIBXL_DISK_BACKEND_PHY:
        if (!(a->disk->format == LIBXL_DISK_FORMAT_RAW ||
              a->disk->format == LIBXL_DISK_FORMAT_EMPTY)) {
            goto bad_format;
        }

        if (libxl__try_phy_backend(a->stab.st_mode))
            return backend;

        LIBXL__LOG(ctx, LIBXL__LOG_DEBUG, "Disk vdev=%s, backend phy"
                   " unsuitable as phys path not a block device",
                   a->disk->vdev);
        return 0;

    case LIBXL_DISK_BACKEND_TAP:
        if (!libxl__blktap_enabled(a->gc)) {
            LIBXL__LOG(ctx, LIBXL__LOG_DEBUG, "Disk vdev=%s, backend tap"
                       " unsuitable because blktap not available",
                       a->disk->vdev);
            return 0;
        }
        if (!(a->disk->format == LIBXL_DISK_FORMAT_RAW ||
              a->disk->format == LIBXL_DISK_FORMAT_VHD)) {
            goto bad_format;
        }
        return backend;

    case LIBXL_DISK_BACKEND_QDISK:
        return backend;

    default:
        LIBXL__LOG(ctx, LIBXL__LOG_DEBUG, "Disk vdev=%s, backend "
                   " %d unknown", a->disk->vdev, backend);
        return 0;

    }
    abort(); /* notreached */

 bad_format:
    LIBXL__LOG(ctx, LIBXL__LOG_DEBUG, "Disk vdev=%s, backend %s"
               " unsuitable due to format %s",
               a->disk->vdev,
               libxl_disk_backend_to_string(backend),
               libxl_disk_format_to_string(a->disk->format));
    return 0;
}

int libxl__device_disk_set_backend(libxl__gc *gc, libxl_device_disk *disk) {
    libxl_ctx *ctx = libxl__gc_owner(gc);
    libxl_disk_backend ok;
    disk_try_backend_args a;

    a.gc = gc;
    a.disk = disk;

    LIBXL__LOG(ctx, LIBXL__LOG_DEBUG, "Disk vdev=%s spec.backend=%s",
               disk->vdev,
               libxl_disk_backend_to_string(disk->backend));

    if (disk->format == LIBXL_DISK_FORMAT_EMPTY) {
        if (!disk->is_cdrom) {
            LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "Disk vdev=%s is empty"
                       " but not cdrom",
                       disk->vdev);
            return ERROR_INVAL;
        }
        memset(&a.stab, 0, sizeof(a.stab));
    } else {
        if (stat(disk->pdev_path, &a.stab)) {
            LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR, "Disk vdev=%s "
                             "failed to stat: %s",
                             disk->vdev, disk->pdev_path);
            return ERROR_INVAL;
        }
        if (!S_ISBLK(a.stab.st_mode) &
            !S_ISREG(a.stab.st_mode)) {
            LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "Disk vdev=%s "
                             "phys path is not a block dev or file: %s",
                             disk->vdev, disk->pdev_path);
            return ERROR_INVAL;
        }
    }

    if (disk->backend != LIBXL_DISK_BACKEND_UNKNOWN) {
        ok= disk_try_backend(&a, disk->backend);
    } else {
        ok=
            disk_try_backend(&a, LIBXL_DISK_BACKEND_PHY) ?:
            disk_try_backend(&a, LIBXL_DISK_BACKEND_TAP) ?:
            disk_try_backend(&a, LIBXL_DISK_BACKEND_QDISK);
        if (ok)
            LIBXL__LOG(ctx, LIBXL__LOG_DEBUG, "Disk vdev=%s, using backend %s",
                       disk->vdev,
                       libxl_disk_backend_to_string(ok));
    }
    if (!ok) {
        LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "no suitable backend for disk %s",
                   disk->vdev);
        return ERROR_INVAL;
    }
    disk->backend = ok;
    return 0;
}

char *libxl__device_disk_string_of_format(libxl_disk_format format)
{
    switch (format) {
        case LIBXL_DISK_FORMAT_QCOW: return "qcow";
        case LIBXL_DISK_FORMAT_QCOW2: return "qcow2";
        case LIBXL_DISK_FORMAT_VHD: return "vhd";
        case LIBXL_DISK_FORMAT_RAW:
        case LIBXL_DISK_FORMAT_EMPTY: return "aio";
        default: return NULL;
    }
}

char *libxl__device_disk_string_of_backend(libxl_disk_backend backend)
{
    switch (backend) {
        case LIBXL_DISK_BACKEND_QDISK: return "qdisk";
        case LIBXL_DISK_BACKEND_TAP: return "phy";
        case LIBXL_DISK_BACKEND_PHY: return "phy";
        default: return NULL;
    }
}

int libxl__device_physdisk_major_minor(const char *physpath, int *major, int *minor)
{
    struct stat buf;
    if (stat(physpath, &buf) < 0)
        return -1;
    *major = major(buf.st_rdev);
    *minor = minor(buf.st_rdev);
    return 0;
}

static int device_virtdisk_matches(const char *virtpath, const char *devtype,
                                   int *index_r, int max_index,
                                   int *partition_r, int max_partition) {
    const char *p;
    char *ep;
    int tl, c;
    long pl;

    tl = strlen(devtype);
    if (memcmp(virtpath, devtype, tl))
        return 0;

    /* We decode the drive letter as if it were in base 52
     * with digits a-zA-Z, more or less */
    *index_r = -1;
    p = virtpath + tl;
    for (;;) {
        c = *p++;
        if (c >= 'a' && c <= 'z') {
            c -= 'a';
        } else {
            --p;
            break;
        }
        (*index_r)++;
        (*index_r) *= 26;
        (*index_r) += c;

        if (*index_r > max_index)
            return 0;
    }

    if (!*p) {
        *partition_r = 0;
        return 1;
    }

    if (*p=='0')
        return 0; /* leading zeroes not permitted in partition number */

    pl = strtoul(p, &ep, 10);
    if (pl > max_partition || *ep)
        return 0;

    *partition_r = pl;
    return 1;
}

int libxl__device_disk_dev_number(const char *virtpath, int *pdisk,
                                  int *ppartition)
{
    int disk, partition;
    char *ep;
    unsigned long ul;
    int chrused;

    chrused = -1;
    if ((sscanf(virtpath, "d%ip%i%n", &disk, &partition, &chrused)  >= 2
         && chrused == strlen(virtpath) && disk < (1<<20) && partition < 256)
        ||
        device_virtdisk_matches(virtpath, "xvd",
                                &disk, (1<<20)-1,
                                &partition, 255)) {
        if (pdisk) *pdisk = disk;
        if (ppartition) *ppartition = partition;
        if (disk <= 15 && partition <= 15)
            return (202 << 8) | (disk << 4) | partition;
        else
            return (1 << 28) | (disk << 8) | partition;
    }

    errno = 0;
    ul = strtoul(virtpath, &ep, 0);
    if (!errno && !*ep && ul <= INT_MAX) {
        /* FIXME: should parse ul to determine these. */
        if (pdisk || ppartition)
            return -1;
        return ul;
    }

    if (device_virtdisk_matches(virtpath, "hd",
                                &disk, 3,
                                &partition, 63)) {
        if (pdisk) *pdisk = disk;
        if (ppartition) *ppartition = partition;
        return ((disk<2 ? 3 : 22) << 8) | ((disk & 1) << 6) | partition;
    }
    if (device_virtdisk_matches(virtpath, "sd",
                                &disk, 15,
                                &partition, 15)) {
        if (pdisk) *pdisk = disk;
        if (ppartition) *ppartition = partition;
        return (8 << 8) | (disk << 4) | partition;
    }
    return -1;
}


typedef struct {
    libxl__ao *ao;
    libxl__ev_devstate ds;
} libxl__ao_device_remove;

static void device_remove_cleanup(libxl__gc *gc,
                                  libxl__ao_device_remove *aorm) {
    if (!aorm) return;
    libxl__ev_devstate_cancel(gc, &aorm->ds);
}

static void device_remove_callback(libxl__egc *egc, libxl__ev_devstate *ds,
                                   int rc) {
    libxl__ao_device_remove *aorm = CONTAINER_OF(ds, *aorm, ds);
    libxl__gc *gc = &aorm->ao->gc;
    libxl__ao_complete(egc, aorm->ao, rc);
    device_remove_cleanup(gc, aorm);
}

int libxl__initiate_device_remove(libxl__egc *egc, libxl__ao *ao,
                                  libxl__device *dev)
{
    AO_GC;
    libxl_ctx *ctx = libxl__gc_owner(gc);
    xs_transaction_t t;
    char *be_path = libxl__device_backend_path(gc, dev);
    char *state_path = libxl__sprintf(gc, "%s/state", be_path);
    char *state = libxl__xs_read(gc, XBT_NULL, state_path);
    int rc = 0;
    libxl__ao_device_remove *aorm = 0;

    if (!state)
        goto out_ok;
    if (atoi(state) != 4) {
        libxl__device_destroy_tapdisk(gc, be_path);
        xs_rm(ctx->xsh, XBT_NULL, be_path);
        goto out_ok;
    }

retry_transaction:
    t = xs_transaction_start(ctx->xsh);
    xs_write(ctx->xsh, t, libxl__sprintf(gc, "%s/online", be_path), "0", strlen("0"));
    xs_write(ctx->xsh, t, state_path, "5", strlen("5"));
    if (!xs_transaction_end(ctx->xsh, t, 0)) {
        if (errno == EAGAIN)
            goto retry_transaction;
        else {
            rc = ERROR_FAIL;
            goto out_fail;
        }
    }

    libxl__device_destroy_tapdisk(gc, be_path);

    aorm = libxl__zalloc(gc, sizeof(*aorm));
    aorm->ao = ao;
    libxl__ev_devstate_init(&aorm->ds);

    rc = libxl__ev_devstate_wait(gc, &aorm->ds, device_remove_callback,
                                 state_path, XenbusStateClosed,
                                 LIBXL_DESTROY_TIMEOUT * 1000);
    if (rc) goto out_fail;

    return 0;

 out_fail:
    assert(rc);
    device_remove_cleanup(gc, aorm);
    return rc;

 out_ok:
    libxl__ao_complete(egc, ao, 0);
    return 0;
}

int libxl__device_destroy(libxl__gc *gc, libxl__device *dev)
{
    libxl_ctx *ctx = libxl__gc_owner(gc);
    char *be_path = libxl__device_backend_path(gc, dev);
    char *fe_path = libxl__device_frontend_path(gc, dev);

    xs_rm(ctx->xsh, XBT_NULL, be_path);
    xs_rm(ctx->xsh, XBT_NULL, fe_path);

    libxl__device_destroy_tapdisk(gc, be_path);

    return 0;
}

int libxl__devices_destroy(libxl__gc *gc, uint32_t domid)
{
    libxl_ctx *ctx = libxl__gc_owner(gc);
    char *path;
    unsigned int num_kinds, num_devs;
    char **kinds = NULL, **devs = NULL;
    int i, j;
    libxl__device dev;
    libxl__device_kind kind;

    path = libxl__sprintf(gc, "/local/domain/%d/device", domid);
    kinds = libxl__xs_directory(gc, XBT_NULL, path, &num_kinds);
    if (!kinds) {
        if (errno != ENOENT) {
            LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR, "unable to get xenstore"
                             " device listing %s", path);
            goto out;
        }
        num_kinds = 0;
    }
    for (i = 0; i < num_kinds; i++) {
        if (libxl__device_kind_from_string(kinds[i], &kind))
            continue;

        path = libxl__sprintf(gc, "/local/domain/%d/device/%s", domid, kinds[i]);
        devs = libxl__xs_directory(gc, XBT_NULL, path, &num_devs);
        if (!devs)
            continue;
        for (j = 0; j < num_devs; j++) {
            path = libxl__sprintf(gc, "/local/domain/%d/device/%s/%s/backend",
                                  domid, kinds[i], devs[j]);
            path = libxl__xs_read(gc, XBT_NULL, path);
            if (path && libxl__parse_backend_path(gc, path, &dev) == 0) {
                dev.domid = domid;
                dev.kind = kind;
                dev.devid = atoi(devs[j]);

                libxl__device_destroy(gc, &dev);
            }
        }
    }

    /* console 0 frontend directory is not under /local/domain/<domid>/device */
    path = libxl__sprintf(gc, "/local/domain/%d/console/backend", domid);
    path = libxl__xs_read(gc, XBT_NULL, path);
    if (path && strcmp(path, "") &&
        libxl__parse_backend_path(gc, path, &dev) == 0) {
        dev.domid = domid;
        dev.kind = LIBXL__DEVICE_KIND_CONSOLE;
        dev.devid = 0;

        libxl__device_destroy(gc, &dev);
    }

out:
    return 0;
}

int libxl__wait_for_device_model(libxl__gc *gc,
                                 uint32_t domid, char *state,
                                 libxl__spawn_starting *spawning,
                                 int (*check_callback)(libxl__gc *gc,
                                                       uint32_t domid,
                                                       const char *state,
                                                       void *userdata),
                                 void *check_callback_userdata)
{
    char *path;
    path = libxl__sprintf(gc, "/local/domain/0/device-model/%d/state", domid);
    return libxl__wait_for_offspring(gc, domid,
                                     LIBXL_DEVICE_MODEL_START_TIMEOUT,
                                     "Device Model", path, state, spawning,
                                     check_callback, check_callback_userdata);
}

int libxl__wait_for_backend(libxl__gc *gc, char *be_path, char *state)
{
    libxl_ctx *ctx = libxl__gc_owner(gc);
    int watchdog = 100;
    unsigned int len;
    char *p;
    char *path = libxl__sprintf(gc, "%s/state", be_path);
    int rc = -1;

    while (watchdog > 0) {
        p = xs_read(ctx->xsh, XBT_NULL, path, &len);
        if (p == NULL) {
            if (errno == ENOENT) {
                LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "Backend %s does not exist",
                       be_path);
            } else {
                LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR, "Failed to access backend %s",
                       be_path);
            }
            goto out;
        } else {
            if (!strcmp(p, state)) {
                rc = 0;
                goto out;
            } else {
                usleep(100000);
                watchdog--;
            }
        }
    }
    LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "Backend %s not ready", be_path);
out:
    return rc;
}

/*
 * Local variables:
 * mode: C
 * c-basic-offset: 4
 * indent-tabs-mode: nil
 * End:
 */