qemu/hw/9pfs/xen-9p-backend.c
<<
>>
Prefs
   1/*
   2 * Xen 9p backend
   3 *
   4 * Copyright Aporeto 2017
   5 *
   6 * Authors:
   7 *  Stefano Stabellini <stefano@aporeto.com>
   8 *
   9 */
  10
  11#include "qemu/osdep.h"
  12
  13#include "hw/9pfs/9p.h"
  14#include "hw/xen/xen-legacy-backend.h"
  15#include "hw/9pfs/xen-9pfs.h"
  16#include "qapi/error.h"
  17#include "qemu/config-file.h"
  18#include "qemu/main-loop.h"
  19#include "qemu/option.h"
  20#include "fsdev/qemu-fsdev.h"
  21
  22#define VERSIONS "1"
  23#define MAX_RINGS 8
  24#define MAX_RING_ORDER 9
  25
  26typedef struct Xen9pfsRing {
  27    struct Xen9pfsDev *priv;
  28
  29    int ref;
  30    xenevtchn_handle   *evtchndev;
  31    int evtchn;
  32    int local_port;
  33    int ring_order;
  34    struct xen_9pfs_data_intf *intf;
  35    unsigned char *data;
  36    struct xen_9pfs_data ring;
  37
  38    struct iovec *sg;
  39    QEMUBH *bh;
  40    Coroutine *co;
  41
  42    /* local copies, so that we can read/write PDU data directly from
  43     * the ring */
  44    RING_IDX out_cons, out_size, in_cons;
  45    bool inprogress;
  46} Xen9pfsRing;
  47
  48typedef struct Xen9pfsDev {
  49    struct XenLegacyDevice xendev;  /* must be first */
  50    V9fsState state;
  51    char *path;
  52    char *security_model;
  53    char *tag;
  54    char *id;
  55
  56    int num_rings;
  57    Xen9pfsRing *rings;
  58} Xen9pfsDev;
  59
  60static void xen_9pfs_disconnect(struct XenLegacyDevice *xendev);
  61
  62static void xen_9pfs_in_sg(Xen9pfsRing *ring,
  63                           struct iovec *in_sg,
  64                           int *num,
  65                           uint32_t idx,
  66                           uint32_t size)
  67{
  68    RING_IDX cons, prod, masked_prod, masked_cons;
  69
  70    cons = ring->intf->in_cons;
  71    prod = ring->intf->in_prod;
  72    xen_rmb();
  73    masked_prod = xen_9pfs_mask(prod, XEN_FLEX_RING_SIZE(ring->ring_order));
  74    masked_cons = xen_9pfs_mask(cons, XEN_FLEX_RING_SIZE(ring->ring_order));
  75
  76    if (masked_prod < masked_cons) {
  77        in_sg[0].iov_base = ring->ring.in + masked_prod;
  78        in_sg[0].iov_len = masked_cons - masked_prod;
  79        *num = 1;
  80    } else {
  81        in_sg[0].iov_base = ring->ring.in + masked_prod;
  82        in_sg[0].iov_len = XEN_FLEX_RING_SIZE(ring->ring_order) - masked_prod;
  83        in_sg[1].iov_base = ring->ring.in;
  84        in_sg[1].iov_len = masked_cons;
  85        *num = 2;
  86    }
  87}
  88
  89static void xen_9pfs_out_sg(Xen9pfsRing *ring,
  90                            struct iovec *out_sg,
  91                            int *num,
  92                            uint32_t idx)
  93{
  94    RING_IDX cons, prod, masked_prod, masked_cons;
  95
  96    cons = ring->intf->out_cons;
  97    prod = ring->intf->out_prod;
  98    xen_rmb();
  99    masked_prod = xen_9pfs_mask(prod, XEN_FLEX_RING_SIZE(ring->ring_order));
 100    masked_cons = xen_9pfs_mask(cons, XEN_FLEX_RING_SIZE(ring->ring_order));
 101
 102    if (masked_cons < masked_prod) {
 103        out_sg[0].iov_base = ring->ring.out + masked_cons;
 104        out_sg[0].iov_len = ring->out_size;
 105        *num = 1;
 106    } else {
 107        if (ring->out_size >
 108            (XEN_FLEX_RING_SIZE(ring->ring_order) - masked_cons)) {
 109            out_sg[0].iov_base = ring->ring.out + masked_cons;
 110            out_sg[0].iov_len = XEN_FLEX_RING_SIZE(ring->ring_order) -
 111                                masked_cons;
 112            out_sg[1].iov_base = ring->ring.out;
 113            out_sg[1].iov_len = ring->out_size -
 114                                (XEN_FLEX_RING_SIZE(ring->ring_order) -
 115                                 masked_cons);
 116            *num = 2;
 117        } else {
 118            out_sg[0].iov_base = ring->ring.out + masked_cons;
 119            out_sg[0].iov_len = ring->out_size;
 120            *num = 1;
 121        }
 122    }
 123}
 124
 125static ssize_t xen_9pfs_pdu_vmarshal(V9fsPDU *pdu,
 126                                     size_t offset,
 127                                     const char *fmt,
 128                                     va_list ap)
 129{
 130    Xen9pfsDev *xen_9pfs = container_of(pdu->s, Xen9pfsDev, state);
 131    struct iovec in_sg[2];
 132    int num;
 133    ssize_t ret;
 134
 135    xen_9pfs_in_sg(&xen_9pfs->rings[pdu->tag % xen_9pfs->num_rings],
 136                   in_sg, &num, pdu->idx, ROUND_UP(offset + 128, 512));
 137
 138    ret = v9fs_iov_vmarshal(in_sg, num, offset, 0, fmt, ap);
 139    if (ret < 0) {
 140        xen_pv_printf(&xen_9pfs->xendev, 0,
 141                      "Failed to encode VirtFS reply type %d\n",
 142                      pdu->id + 1);
 143        xen_be_set_state(&xen_9pfs->xendev, XenbusStateClosing);
 144        xen_9pfs_disconnect(&xen_9pfs->xendev);
 145    }
 146    return ret;
 147}
 148
 149static ssize_t xen_9pfs_pdu_vunmarshal(V9fsPDU *pdu,
 150                                       size_t offset,
 151                                       const char *fmt,
 152                                       va_list ap)
 153{
 154    Xen9pfsDev *xen_9pfs = container_of(pdu->s, Xen9pfsDev, state);
 155    struct iovec out_sg[2];
 156    int num;
 157    ssize_t ret;
 158
 159    xen_9pfs_out_sg(&xen_9pfs->rings[pdu->tag % xen_9pfs->num_rings],
 160                    out_sg, &num, pdu->idx);
 161
 162    ret = v9fs_iov_vunmarshal(out_sg, num, offset, 0, fmt, ap);
 163    if (ret < 0) {
 164        xen_pv_printf(&xen_9pfs->xendev, 0,
 165                      "Failed to decode VirtFS request type %d\n", pdu->id);
 166        xen_be_set_state(&xen_9pfs->xendev, XenbusStateClosing);
 167        xen_9pfs_disconnect(&xen_9pfs->xendev);
 168    }
 169    return ret;
 170}
 171
 172static void xen_9pfs_init_out_iov_from_pdu(V9fsPDU *pdu,
 173                                           struct iovec **piov,
 174                                           unsigned int *pniov,
 175                                           size_t size)
 176{
 177    Xen9pfsDev *xen_9pfs = container_of(pdu->s, Xen9pfsDev, state);
 178    Xen9pfsRing *ring = &xen_9pfs->rings[pdu->tag % xen_9pfs->num_rings];
 179    int num;
 180
 181    g_free(ring->sg);
 182
 183    ring->sg = g_new0(struct iovec, 2);
 184    xen_9pfs_out_sg(ring, ring->sg, &num, pdu->idx);
 185    *piov = ring->sg;
 186    *pniov = num;
 187}
 188
 189static void xen_9pfs_init_in_iov_from_pdu(V9fsPDU *pdu,
 190                                          struct iovec **piov,
 191                                          unsigned int *pniov,
 192                                          size_t size)
 193{
 194    Xen9pfsDev *xen_9pfs = container_of(pdu->s, Xen9pfsDev, state);
 195    Xen9pfsRing *ring = &xen_9pfs->rings[pdu->tag % xen_9pfs->num_rings];
 196    int num;
 197    size_t buf_size;
 198
 199    g_free(ring->sg);
 200
 201    ring->sg = g_new0(struct iovec, 2);
 202    ring->co = qemu_coroutine_self();
 203    /* make sure other threads see ring->co changes before continuing */
 204    smp_wmb();
 205
 206again:
 207    xen_9pfs_in_sg(ring, ring->sg, &num, pdu->idx, size);
 208    buf_size = iov_size(ring->sg, num);
 209    if (buf_size  < size) {
 210        qemu_coroutine_yield();
 211        goto again;
 212    }
 213    ring->co = NULL;
 214    /* make sure other threads see ring->co changes before continuing */
 215    smp_wmb();
 216
 217    *piov = ring->sg;
 218    *pniov = num;
 219}
 220
 221static void xen_9pfs_push_and_notify(V9fsPDU *pdu)
 222{
 223    RING_IDX prod;
 224    Xen9pfsDev *priv = container_of(pdu->s, Xen9pfsDev, state);
 225    Xen9pfsRing *ring = &priv->rings[pdu->tag % priv->num_rings];
 226
 227    g_free(ring->sg);
 228    ring->sg = NULL;
 229
 230    ring->intf->out_cons = ring->out_cons;
 231    xen_wmb();
 232
 233    prod = ring->intf->in_prod;
 234    xen_rmb();
 235    ring->intf->in_prod = prod + pdu->size;
 236    xen_wmb();
 237
 238    ring->inprogress = false;
 239    xenevtchn_notify(ring->evtchndev, ring->local_port);
 240
 241    qemu_bh_schedule(ring->bh);
 242}
 243
 244static const V9fsTransport xen_9p_transport = {
 245    .pdu_vmarshal = xen_9pfs_pdu_vmarshal,
 246    .pdu_vunmarshal = xen_9pfs_pdu_vunmarshal,
 247    .init_in_iov_from_pdu = xen_9pfs_init_in_iov_from_pdu,
 248    .init_out_iov_from_pdu = xen_9pfs_init_out_iov_from_pdu,
 249    .push_and_notify = xen_9pfs_push_and_notify,
 250};
 251
 252static int xen_9pfs_init(struct XenLegacyDevice *xendev)
 253{
 254    return 0;
 255}
 256
 257static int xen_9pfs_receive(Xen9pfsRing *ring)
 258{
 259    P9MsgHeader h;
 260    RING_IDX cons, prod, masked_prod, masked_cons, queued;
 261    V9fsPDU *pdu;
 262
 263    if (ring->inprogress) {
 264        return 0;
 265    }
 266
 267    cons = ring->intf->out_cons;
 268    prod = ring->intf->out_prod;
 269    xen_rmb();
 270
 271    queued = xen_9pfs_queued(prod, cons, XEN_FLEX_RING_SIZE(ring->ring_order));
 272    if (queued < sizeof(h)) {
 273        return 0;
 274    }
 275    ring->inprogress = true;
 276
 277    masked_prod = xen_9pfs_mask(prod, XEN_FLEX_RING_SIZE(ring->ring_order));
 278    masked_cons = xen_9pfs_mask(cons, XEN_FLEX_RING_SIZE(ring->ring_order));
 279
 280    xen_9pfs_read_packet((uint8_t *) &h, ring->ring.out, sizeof(h),
 281                         masked_prod, &masked_cons,
 282                         XEN_FLEX_RING_SIZE(ring->ring_order));
 283    if (queued < le32_to_cpu(h.size_le)) {
 284        return 0;
 285    }
 286
 287    /* cannot fail, because we only handle one request per ring at a time */
 288    pdu = pdu_alloc(&ring->priv->state);
 289    ring->out_size = le32_to_cpu(h.size_le);
 290    ring->out_cons = cons + le32_to_cpu(h.size_le);
 291
 292    pdu_submit(pdu, &h);
 293
 294    return 0;
 295}
 296
 297static void xen_9pfs_bh(void *opaque)
 298{
 299    Xen9pfsRing *ring = opaque;
 300    bool wait;
 301
 302again:
 303    wait = ring->co != NULL && qemu_coroutine_entered(ring->co);
 304    /* paired with the smb_wmb barriers in xen_9pfs_init_in_iov_from_pdu */
 305    smp_rmb();
 306    if (wait) {
 307        cpu_relax();
 308        goto again;
 309    }
 310
 311    if (ring->co != NULL) {
 312        qemu_coroutine_enter_if_inactive(ring->co);
 313    }
 314    xen_9pfs_receive(ring);
 315}
 316
 317static void xen_9pfs_evtchn_event(void *opaque)
 318{
 319    Xen9pfsRing *ring = opaque;
 320    evtchn_port_t port;
 321
 322    port = xenevtchn_pending(ring->evtchndev);
 323    xenevtchn_unmask(ring->evtchndev, port);
 324
 325    qemu_bh_schedule(ring->bh);
 326}
 327
 328static void xen_9pfs_disconnect(struct XenLegacyDevice *xendev)
 329{
 330    Xen9pfsDev *xen_9pdev = container_of(xendev, Xen9pfsDev, xendev);
 331    int i;
 332
 333    for (i = 0; i < xen_9pdev->num_rings; i++) {
 334        if (xen_9pdev->rings[i].evtchndev != NULL) {
 335            qemu_set_fd_handler(xenevtchn_fd(xen_9pdev->rings[i].evtchndev),
 336                    NULL, NULL, NULL);
 337            xenevtchn_unbind(xen_9pdev->rings[i].evtchndev,
 338                             xen_9pdev->rings[i].local_port);
 339            xen_9pdev->rings[i].evtchndev = NULL;
 340        }
 341    }
 342}
 343
 344static int xen_9pfs_free(struct XenLegacyDevice *xendev)
 345{
 346    Xen9pfsDev *xen_9pdev = container_of(xendev, Xen9pfsDev, xendev);
 347    int i;
 348
 349    if (xen_9pdev->rings[0].evtchndev != NULL) {
 350        xen_9pfs_disconnect(xendev);
 351    }
 352
 353    for (i = 0; i < xen_9pdev->num_rings; i++) {
 354        if (xen_9pdev->rings[i].data != NULL) {
 355            xen_be_unmap_grant_refs(&xen_9pdev->xendev,
 356                                    xen_9pdev->rings[i].data,
 357                                    (1 << xen_9pdev->rings[i].ring_order));
 358        }
 359        if (xen_9pdev->rings[i].intf != NULL) {
 360            xen_be_unmap_grant_refs(&xen_9pdev->xendev,
 361                                    xen_9pdev->rings[i].intf,
 362                                    1);
 363        }
 364        if (xen_9pdev->rings[i].bh != NULL) {
 365            qemu_bh_delete(xen_9pdev->rings[i].bh);
 366        }
 367    }
 368
 369    g_free(xen_9pdev->id);
 370    g_free(xen_9pdev->tag);
 371    g_free(xen_9pdev->path);
 372    g_free(xen_9pdev->security_model);
 373    g_free(xen_9pdev->rings);
 374    return 0;
 375}
 376
 377static int xen_9pfs_connect(struct XenLegacyDevice *xendev)
 378{
 379    Error *err = NULL;
 380    int i;
 381    Xen9pfsDev *xen_9pdev = container_of(xendev, Xen9pfsDev, xendev);
 382    V9fsState *s = &xen_9pdev->state;
 383    QemuOpts *fsdev;
 384
 385    if (xenstore_read_fe_int(&xen_9pdev->xendev, "num-rings",
 386                             &xen_9pdev->num_rings) == -1 ||
 387        xen_9pdev->num_rings > MAX_RINGS || xen_9pdev->num_rings < 1) {
 388        return -1;
 389    }
 390
 391    xen_9pdev->rings = g_new0(Xen9pfsRing, xen_9pdev->num_rings);
 392    for (i = 0; i < xen_9pdev->num_rings; i++) {
 393        char *str;
 394        int ring_order;
 395
 396        xen_9pdev->rings[i].priv = xen_9pdev;
 397        xen_9pdev->rings[i].evtchn = -1;
 398        xen_9pdev->rings[i].local_port = -1;
 399
 400        str = g_strdup_printf("ring-ref%u", i);
 401        if (xenstore_read_fe_int(&xen_9pdev->xendev, str,
 402                                 &xen_9pdev->rings[i].ref) == -1) {
 403            g_free(str);
 404            goto out;
 405        }
 406        g_free(str);
 407        str = g_strdup_printf("event-channel-%u", i);
 408        if (xenstore_read_fe_int(&xen_9pdev->xendev, str,
 409                                 &xen_9pdev->rings[i].evtchn) == -1) {
 410            g_free(str);
 411            goto out;
 412        }
 413        g_free(str);
 414
 415        xen_9pdev->rings[i].intf =
 416            xen_be_map_grant_ref(&xen_9pdev->xendev,
 417                                 xen_9pdev->rings[i].ref,
 418                                 PROT_READ | PROT_WRITE);
 419        if (!xen_9pdev->rings[i].intf) {
 420            goto out;
 421        }
 422        ring_order = xen_9pdev->rings[i].intf->ring_order;
 423        if (ring_order > MAX_RING_ORDER) {
 424            goto out;
 425        }
 426        xen_9pdev->rings[i].ring_order = ring_order;
 427        xen_9pdev->rings[i].data =
 428            xen_be_map_grant_refs(&xen_9pdev->xendev,
 429                                  xen_9pdev->rings[i].intf->ref,
 430                                  (1 << ring_order),
 431                                  PROT_READ | PROT_WRITE);
 432        if (!xen_9pdev->rings[i].data) {
 433            goto out;
 434        }
 435        xen_9pdev->rings[i].ring.in = xen_9pdev->rings[i].data;
 436        xen_9pdev->rings[i].ring.out = xen_9pdev->rings[i].data +
 437                                       XEN_FLEX_RING_SIZE(ring_order);
 438
 439        xen_9pdev->rings[i].bh = qemu_bh_new(xen_9pfs_bh, &xen_9pdev->rings[i]);
 440        xen_9pdev->rings[i].out_cons = 0;
 441        xen_9pdev->rings[i].out_size = 0;
 442        xen_9pdev->rings[i].inprogress = false;
 443
 444
 445        xen_9pdev->rings[i].evtchndev = xenevtchn_open(NULL, 0);
 446        if (xen_9pdev->rings[i].evtchndev == NULL) {
 447            goto out;
 448        }
 449        qemu_set_cloexec(xenevtchn_fd(xen_9pdev->rings[i].evtchndev));
 450        xen_9pdev->rings[i].local_port = xenevtchn_bind_interdomain
 451                                            (xen_9pdev->rings[i].evtchndev,
 452                                             xendev->dom,
 453                                             xen_9pdev->rings[i].evtchn);
 454        if (xen_9pdev->rings[i].local_port == -1) {
 455            xen_pv_printf(xendev, 0,
 456                          "xenevtchn_bind_interdomain failed port=%d\n",
 457                          xen_9pdev->rings[i].evtchn);
 458            goto out;
 459        }
 460        xen_pv_printf(xendev, 2, "bind evtchn port %d\n", xendev->local_port);
 461        qemu_set_fd_handler(xenevtchn_fd(xen_9pdev->rings[i].evtchndev),
 462                xen_9pfs_evtchn_event, NULL, &xen_9pdev->rings[i]);
 463    }
 464
 465    xen_9pdev->security_model = xenstore_read_be_str(xendev, "security_model");
 466    xen_9pdev->path = xenstore_read_be_str(xendev, "path");
 467    xen_9pdev->id = s->fsconf.fsdev_id =
 468        g_strdup_printf("xen9p%d", xendev->dev);
 469    xen_9pdev->tag = s->fsconf.tag = xenstore_read_fe_str(xendev, "tag");
 470    fsdev = qemu_opts_create(qemu_find_opts("fsdev"),
 471            s->fsconf.tag,
 472            1, NULL);
 473    qemu_opt_set(fsdev, "fsdriver", "local", NULL);
 474    qemu_opt_set(fsdev, "path", xen_9pdev->path, NULL);
 475    qemu_opt_set(fsdev, "security_model", xen_9pdev->security_model, NULL);
 476    qemu_opts_set_id(fsdev, s->fsconf.fsdev_id);
 477    qemu_fsdev_add(fsdev, &err);
 478    if (err) {
 479        error_report_err(err);
 480    }
 481    v9fs_device_realize_common(s, &xen_9p_transport, NULL);
 482
 483    return 0;
 484
 485out:
 486    xen_9pfs_free(xendev);
 487    return -1;
 488}
 489
 490static void xen_9pfs_alloc(struct XenLegacyDevice *xendev)
 491{
 492    xenstore_write_be_str(xendev, "versions", VERSIONS);
 493    xenstore_write_be_int(xendev, "max-rings", MAX_RINGS);
 494    xenstore_write_be_int(xendev, "max-ring-page-order", MAX_RING_ORDER);
 495}
 496
 497struct XenDevOps xen_9pfs_ops = {
 498    .size       = sizeof(Xen9pfsDev),
 499    .flags      = DEVOPS_FLAG_NEED_GNTDEV,
 500    .alloc      = xen_9pfs_alloc,
 501    .init       = xen_9pfs_init,
 502    .initialise = xen_9pfs_connect,
 503    .disconnect = xen_9pfs_disconnect,
 504    .free       = xen_9pfs_free,
 505};
 506