Statistics
| Branch: | Revision:

root / hw / virtio-9p.c @ 405a549a

History | View | Annotate | Download (13.8 kB)

1
/*
2
 * Virtio 9p backend
3
 *
4
 * Copyright IBM, Corp. 2010
5
 *
6
 * Authors:
7
 *  Anthony Liguori   <aliguori@us.ibm.com>
8
 *
9
 * This work is licensed under the terms of the GNU GPL, version 2.  See
10
 * the COPYING file in the top-level directory.
11
 *
12
 */
13

    
14
#include "virtio.h"
15
#include "pc.h"
16
#include "qemu_socket.h"
17
#include "virtio-9p.h"
18
#include "fsdev/qemu-fsdev.h"
19
#include "virtio-9p-debug.h"
20

    
21
int dotu = 1;
22
int debug_9p_pdu;
23

    
24
static V9fsPDU *alloc_pdu(V9fsState *s)
25
{
26
    V9fsPDU *pdu = NULL;
27

    
28
    if (!QLIST_EMPTY(&s->free_list)) {
29
        pdu = QLIST_FIRST(&s->free_list);
30
        QLIST_REMOVE(pdu, next);
31
    }
32
    return pdu;
33
}
34

    
35
static void free_pdu(V9fsState *s, V9fsPDU *pdu)
36
{
37
    if (pdu) {
38
        QLIST_INSERT_HEAD(&s->free_list, pdu, next);
39
    }
40
}
41

    
42
size_t pdu_packunpack(void *addr, struct iovec *sg, int sg_count,
43
                        size_t offset, size_t size, int pack)
44
{
45
    int i = 0;
46
    size_t copied = 0;
47

    
48
    for (i = 0; size && i < sg_count; i++) {
49
        size_t len;
50
        if (offset >= sg[i].iov_len) {
51
            /* skip this sg */
52
            offset -= sg[i].iov_len;
53
            continue;
54
        } else {
55
            len = MIN(sg[i].iov_len - offset, size);
56
            if (pack) {
57
                memcpy(sg[i].iov_base + offset, addr, len);
58
            } else {
59
                memcpy(addr, sg[i].iov_base + offset, len);
60
            }
61
            size -= len;
62
            copied += len;
63
            addr += len;
64
            if (size) {
65
                offset = 0;
66
                continue;
67
            }
68
        }
69
    }
70

    
71
    return copied;
72
}
73

    
74
static size_t pdu_unpack(void *dst, V9fsPDU *pdu, size_t offset, size_t size)
75
{
76
    return pdu_packunpack(dst, pdu->elem.out_sg, pdu->elem.out_num,
77
                         offset, size, 0);
78
}
79

    
80
static size_t pdu_pack(V9fsPDU *pdu, size_t offset, const void *src,
81
                        size_t size)
82
{
83
    return pdu_packunpack((void *)src, pdu->elem.in_sg, pdu->elem.in_num,
84
                             offset, size, 1);
85
}
86

    
87
static int pdu_copy_sg(V9fsPDU *pdu, size_t offset, int rx, struct iovec *sg)
88
{
89
    size_t pos = 0;
90
    int i, j;
91
    struct iovec *src_sg;
92
    unsigned int num;
93

    
94
    if (rx) {
95
        src_sg = pdu->elem.in_sg;
96
        num = pdu->elem.in_num;
97
    } else {
98
        src_sg = pdu->elem.out_sg;
99
        num = pdu->elem.out_num;
100
    }
101

    
102
    j = 0;
103
    for (i = 0; i < num; i++) {
104
        if (offset <= pos) {
105
            sg[j].iov_base = src_sg[i].iov_base;
106
            sg[j].iov_len = src_sg[i].iov_len;
107
            j++;
108
        } else if (offset < (src_sg[i].iov_len + pos)) {
109
            sg[j].iov_base = src_sg[i].iov_base;
110
            sg[j].iov_len = src_sg[i].iov_len;
111
            sg[j].iov_base += (offset - pos);
112
            sg[j].iov_len -= (offset - pos);
113
            j++;
114
        }
115
        pos += src_sg[i].iov_len;
116
    }
117

    
118
    return j;
119
}
120

    
121
static size_t pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
122
{
123
    size_t old_offset = offset;
124
    va_list ap;
125
    int i;
126

    
127
    va_start(ap, fmt);
128
    for (i = 0; fmt[i]; i++) {
129
        switch (fmt[i]) {
130
        case 'b': {
131
            uint8_t *valp = va_arg(ap, uint8_t *);
132
            offset += pdu_unpack(valp, pdu, offset, sizeof(*valp));
133
            break;
134
        }
135
        case 'w': {
136
            uint16_t val, *valp;
137
            valp = va_arg(ap, uint16_t *);
138
            val = le16_to_cpupu(valp);
139
            offset += pdu_unpack(&val, pdu, offset, sizeof(val));
140
            *valp = val;
141
            break;
142
        }
143
        case 'd': {
144
            uint32_t val, *valp;
145
            valp = va_arg(ap, uint32_t *);
146
            val = le32_to_cpupu(valp);
147
            offset += pdu_unpack(&val, pdu, offset, sizeof(val));
148
            *valp = val;
149
            break;
150
        }
151
        case 'q': {
152
            uint64_t val, *valp;
153
            valp = va_arg(ap, uint64_t *);
154
            val = le64_to_cpup(valp);
155
            offset += pdu_unpack(&val, pdu, offset, sizeof(val));
156
            *valp = val;
157
            break;
158
        }
159
        case 'v': {
160
            struct iovec *iov = va_arg(ap, struct iovec *);
161
            int *iovcnt = va_arg(ap, int *);
162
            *iovcnt = pdu_copy_sg(pdu, offset, 0, iov);
163
            break;
164
        }
165
        case 's': {
166
            V9fsString *str = va_arg(ap, V9fsString *);
167
            offset += pdu_unmarshal(pdu, offset, "w", &str->size);
168
            /* FIXME: sanity check str->size */
169
            str->data = qemu_malloc(str->size + 1);
170
            offset += pdu_unpack(str->data, pdu, offset, str->size);
171
            str->data[str->size] = 0;
172
            break;
173
        }
174
        case 'Q': {
175
            V9fsQID *qidp = va_arg(ap, V9fsQID *);
176
            offset += pdu_unmarshal(pdu, offset, "bdq",
177
                        &qidp->type, &qidp->version, &qidp->path);
178
            break;
179
        }
180
        case 'S': {
181
            V9fsStat *statp = va_arg(ap, V9fsStat *);
182
            offset += pdu_unmarshal(pdu, offset, "wwdQdddqsssssddd",
183
                        &statp->size, &statp->type, &statp->dev,
184
                        &statp->qid, &statp->mode, &statp->atime,
185
                        &statp->mtime, &statp->length,
186
                        &statp->name, &statp->uid, &statp->gid,
187
                        &statp->muid, &statp->extension,
188
                        &statp->n_uid, &statp->n_gid,
189
                        &statp->n_muid);
190
            break;
191
        }
192
        default:
193
            break;
194
        }
195
    }
196

    
197
    va_end(ap);
198

    
199
    return offset - old_offset;
200
}
201

    
202
static size_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
203
{
204
    size_t old_offset = offset;
205
    va_list ap;
206
    int i;
207

    
208
    va_start(ap, fmt);
209
    for (i = 0; fmt[i]; i++) {
210
        switch (fmt[i]) {
211
        case 'b': {
212
            uint8_t val = va_arg(ap, int);
213
            offset += pdu_pack(pdu, offset, &val, sizeof(val));
214
            break;
215
        }
216
        case 'w': {
217
            uint16_t val;
218
            cpu_to_le16w(&val, va_arg(ap, int));
219
            offset += pdu_pack(pdu, offset, &val, sizeof(val));
220
            break;
221
        }
222
        case 'd': {
223
            uint32_t val;
224
            cpu_to_le32w(&val, va_arg(ap, uint32_t));
225
            offset += pdu_pack(pdu, offset, &val, sizeof(val));
226
            break;
227
        }
228
        case 'q': {
229
            uint64_t val;
230
            cpu_to_le64w(&val, va_arg(ap, uint64_t));
231
            offset += pdu_pack(pdu, offset, &val, sizeof(val));
232
            break;
233
        }
234
        case 'v': {
235
            struct iovec *iov = va_arg(ap, struct iovec *);
236
            int *iovcnt = va_arg(ap, int *);
237
            *iovcnt = pdu_copy_sg(pdu, offset, 1, iov);
238
            break;
239
        }
240
        case 's': {
241
            V9fsString *str = va_arg(ap, V9fsString *);
242
            offset += pdu_marshal(pdu, offset, "w", str->size);
243
            offset += pdu_pack(pdu, offset, str->data, str->size);
244
            break;
245
        }
246
        case 'Q': {
247
            V9fsQID *qidp = va_arg(ap, V9fsQID *);
248
            offset += pdu_marshal(pdu, offset, "bdq",
249
                        qidp->type, qidp->version, qidp->path);
250
            break;
251
        }
252
        case 'S': {
253
            V9fsStat *statp = va_arg(ap, V9fsStat *);
254
            offset += pdu_marshal(pdu, offset, "wwdQdddqsssssddd",
255
                        statp->size, statp->type, statp->dev,
256
                        &statp->qid, statp->mode, statp->atime,
257
                        statp->mtime, statp->length, &statp->name,
258
                        &statp->uid, &statp->gid, &statp->muid,
259
                        &statp->extension, statp->n_uid,
260
                        statp->n_gid, statp->n_muid);
261
            break;
262
        }
263
        default:
264
            break;
265
        }
266
    }
267
    va_end(ap);
268

    
269
    return offset - old_offset;
270
}
271

    
272
static void complete_pdu(V9fsState *s, V9fsPDU *pdu, ssize_t len)
273
{
274
    int8_t id = pdu->id + 1; /* Response */
275

    
276
    if (len < 0) {
277
        V9fsString str;
278
        int err = -len;
279

    
280
        str.data = strerror(err);
281
        str.size = strlen(str.data);
282

    
283
        len = 7;
284
        len += pdu_marshal(pdu, len, "s", &str);
285
        if (dotu) {
286
            len += pdu_marshal(pdu, len, "d", err);
287
        }
288

    
289
        id = P9_RERROR;
290
    }
291

    
292
    /* fill out the header */
293
    pdu_marshal(pdu, 0, "dbw", (int32_t)len, id, pdu->tag);
294

    
295
    /* keep these in sync */
296
    pdu->size = len;
297
    pdu->id = id;
298

    
299
    /* push onto queue and notify */
300
    virtqueue_push(s->vq, &pdu->elem, len);
301

    
302
    /* FIXME: we should batch these completions */
303
    virtio_notify(&s->vdev, s->vq);
304

    
305
    free_pdu(s, pdu);
306
}
307

    
308
static void v9fs_dummy(V9fsState *s, V9fsPDU *pdu)
309
{
310
    /* Note: The following have been added to prevent GCC from complaining
311
     * They will be removed in the subsequent patches */
312
    (void)pdu_unmarshal;
313
    (void) complete_pdu;
314

    
315
}
316
static void v9fs_version(V9fsState *s, V9fsPDU *pdu)
317
{
318
    if (debug_9p_pdu) {
319
        pprint_pdu(pdu);
320
    }
321
}
322

    
323
static void v9fs_attach(V9fsState *s, V9fsPDU *pdu)
324
{
325
    if (debug_9p_pdu) {
326
        pprint_pdu(pdu);
327
    }
328
}
329

    
330
static void v9fs_stat(V9fsState *s, V9fsPDU *pdu)
331
{
332
    if (debug_9p_pdu) {
333
        pprint_pdu(pdu);
334
    }
335
}
336

    
337
static void v9fs_walk(V9fsState *s, V9fsPDU *pdu)
338
{
339
    if (debug_9p_pdu) {
340
        pprint_pdu(pdu);
341
    }
342
}
343

    
344
static void v9fs_clunk(V9fsState *s, V9fsPDU *pdu)
345
{
346
    if (debug_9p_pdu) {
347
        pprint_pdu(pdu);
348
    }
349
}
350

    
351
static void v9fs_open(V9fsState *s, V9fsPDU *pdu)
352
{    if (debug_9p_pdu) {
353
        pprint_pdu(pdu);
354
     }
355
}
356

    
357
static void v9fs_read(V9fsState *s, V9fsPDU *pdu)
358
{
359
    if (debug_9p_pdu) {
360
        pprint_pdu(pdu);
361
    }
362
}
363

    
364
static void v9fs_write(V9fsState *s, V9fsPDU *pdu)
365
{
366
    if (debug_9p_pdu) {
367
        pprint_pdu(pdu);
368
    }
369
}
370

    
371
static void v9fs_create(V9fsState *s, V9fsPDU *pdu)
372
{
373
    if (debug_9p_pdu) {
374
        pprint_pdu(pdu);
375
    }
376
}
377

    
378
static void v9fs_flush(V9fsState *s, V9fsPDU *pdu)
379
{
380
    v9fs_dummy(s, pdu);
381
    if (debug_9p_pdu) {
382
        pprint_pdu(pdu);
383
    }
384
}
385

    
386
static void v9fs_remove(V9fsState *s, V9fsPDU *pdu)
387
{
388
    if (debug_9p_pdu) {
389
        pprint_pdu(pdu);
390
    }
391
}
392

    
393
static void v9fs_wstat(V9fsState *s, V9fsPDU *pdu)
394
{
395
    if (debug_9p_pdu) {
396
        pprint_pdu(pdu);
397
    }
398
}
399

    
400
typedef void (pdu_handler_t)(V9fsState *s, V9fsPDU *pdu);
401

    
402
static pdu_handler_t *pdu_handlers[] = {
403
    [P9_TVERSION] = v9fs_version,
404
    [P9_TATTACH] = v9fs_attach,
405
    [P9_TSTAT] = v9fs_stat,
406
    [P9_TWALK] = v9fs_walk,
407
    [P9_TCLUNK] = v9fs_clunk,
408
    [P9_TOPEN] = v9fs_open,
409
    [P9_TREAD] = v9fs_read,
410
#if 0
411
    [P9_TAUTH] = v9fs_auth,
412
#endif
413
    [P9_TFLUSH] = v9fs_flush,
414
    [P9_TCREATE] = v9fs_create,
415
    [P9_TWRITE] = v9fs_write,
416
    [P9_TWSTAT] = v9fs_wstat,
417
    [P9_TREMOVE] = v9fs_remove,
418
};
419

    
420
static void submit_pdu(V9fsState *s, V9fsPDU *pdu)
421
{
422
    pdu_handler_t *handler;
423

    
424
    if (debug_9p_pdu) {
425
        pprint_pdu(pdu);
426
    }
427

    
428
    BUG_ON(pdu->id >= ARRAY_SIZE(pdu_handlers));
429

    
430
    handler = pdu_handlers[pdu->id];
431
    BUG_ON(handler == NULL);
432

    
433
    handler(s, pdu);
434
}
435

    
436
static void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq)
437
{
438
    V9fsState *s = (V9fsState *)vdev;
439
    V9fsPDU *pdu;
440
    ssize_t len;
441

    
442
    while ((pdu = alloc_pdu(s)) &&
443
            (len = virtqueue_pop(vq, &pdu->elem)) != 0) {
444
        uint8_t *ptr;
445

    
446
        BUG_ON(pdu->elem.out_num == 0 || pdu->elem.in_num == 0);
447
        BUG_ON(pdu->elem.out_sg[0].iov_len < 7);
448

    
449
        ptr = pdu->elem.out_sg[0].iov_base;
450

    
451
        memcpy(&pdu->size, ptr, 4);
452
        pdu->id = ptr[4];
453
        memcpy(&pdu->tag, ptr + 5, 2);
454

    
455
        submit_pdu(s, pdu);
456
    }
457

    
458
    free_pdu(s, pdu);
459
}
460

    
461
static uint32_t virtio_9p_get_features(VirtIODevice *vdev, uint32_t features)
462
{
463
    features |= 1 << VIRTIO_9P_MOUNT_TAG;
464
    return features;
465
}
466

    
467
static V9fsState *to_virtio_9p(VirtIODevice *vdev)
468
{
469
    return (V9fsState *)vdev;
470
}
471

    
472
static void virtio_9p_get_config(VirtIODevice *vdev, uint8_t *config)
473
{
474
    struct virtio_9p_config *cfg;
475
    V9fsState *s = to_virtio_9p(vdev);
476

    
477
    cfg = qemu_mallocz(sizeof(struct virtio_9p_config) +
478
                        s->tag_len);
479
    stw_raw(&cfg->tag_len, s->tag_len);
480
    memcpy(cfg->tag, s->tag, s->tag_len);
481
    memcpy(config, cfg, s->config_size);
482
    qemu_free(cfg);
483
}
484

    
485
VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf)
486
 {
487
    V9fsState *s;
488
    int i, len;
489
    struct stat stat;
490
    FsTypeEntry *fse;
491

    
492

    
493
    s = (V9fsState *)virtio_common_init("virtio-9p",
494
                                    VIRTIO_ID_9P,
495
                                    sizeof(struct virtio_9p_config)+
496
                                    MAX_TAG_LEN,
497
                                    sizeof(V9fsState));
498

    
499
    /* initialize pdu allocator */
500
    QLIST_INIT(&s->free_list);
501
    for (i = 0; i < (MAX_REQ - 1); i++) {
502
        QLIST_INSERT_HEAD(&s->free_list, &s->pdus[i], next);
503
    }
504

    
505
    s->vq = virtio_add_queue(&s->vdev, MAX_REQ, handle_9p_output);
506

    
507
    fse = get_fsdev_fsentry(conf->fsdev_id);
508

    
509
    if (!fse) {
510
        /* We don't have a fsdev identified by fsdev_id */
511
        fprintf(stderr, "Virtio-9p device couldn't find fsdev "
512
                    "with the id %s\n", conf->fsdev_id);
513
        exit(1);
514
    }
515

    
516
    if (!fse->path || !conf->tag) {
517
        /* we haven't specified a mount_tag or the path */
518
        fprintf(stderr, "fsdev with id %s needs path "
519
                "and Virtio-9p device needs mount_tag arguments\n",
520
                conf->fsdev_id);
521
        exit(1);
522
    }
523

    
524
    if (lstat(fse->path, &stat)) {
525
        fprintf(stderr, "share path %s does not exist\n", fse->path);
526
        exit(1);
527
    } else if (!S_ISDIR(stat.st_mode)) {
528
        fprintf(stderr, "share path %s is not a directory \n", fse->path);
529
        exit(1);
530
    }
531

    
532
    s->ctx.fs_root = qemu_strdup(fse->path);
533
    len = strlen(conf->tag);
534
    if (len > MAX_TAG_LEN) {
535
        len = MAX_TAG_LEN;
536
    }
537
    /* s->tag is non-NULL terminated string */
538
    s->tag = qemu_malloc(len);
539
    memcpy(s->tag, conf->tag, len);
540
    s->tag_len = len;
541
    s->ctx.uid = -1;
542

    
543
    s->ops = fse->ops;
544
    s->vdev.get_features = virtio_9p_get_features;
545
    s->config_size = sizeof(struct virtio_9p_config) +
546
                        s->tag_len;
547
    s->vdev.get_config = virtio_9p_get_config;
548

    
549
    return &s->vdev;
550
}