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