]>
git.proxmox.com Git - mirror_qemu.git/blob - hw/virtio-9p.c
4 * Copyright IBM, Corp. 2010
7 * Anthony Liguori <aliguori@us.ibm.com>
9 * This work is licensed under the terms of the GNU GPL, version 2. See
10 * the COPYING file in the top-level directory.
16 #include "qemu_socket.h"
17 #include "virtio-9p.h"
18 #include "fsdev/qemu-fsdev.h"
19 #include "virtio-9p-debug.h"
24 static V9fsPDU
*alloc_pdu(V9fsState
*s
)
28 if (!QLIST_EMPTY(&s
->free_list
)) {
29 pdu
= QLIST_FIRST(&s
->free_list
);
30 QLIST_REMOVE(pdu
, next
);
35 static void free_pdu(V9fsState
*s
, V9fsPDU
*pdu
)
38 QLIST_INSERT_HEAD(&s
->free_list
, pdu
, next
);
42 size_t pdu_packunpack(void *addr
, struct iovec
*sg
, int sg_count
,
43 size_t offset
, size_t size
, int pack
)
48 for (i
= 0; size
&& i
< sg_count
; i
++) {
50 if (offset
>= sg
[i
].iov_len
) {
52 offset
-= sg
[i
].iov_len
;
55 len
= MIN(sg
[i
].iov_len
- offset
, size
);
57 memcpy(sg
[i
].iov_base
+ offset
, addr
, len
);
59 memcpy(addr
, sg
[i
].iov_base
+ offset
, len
);
74 static size_t pdu_unpack(void *dst
, V9fsPDU
*pdu
, size_t offset
, size_t size
)
76 return pdu_packunpack(dst
, pdu
->elem
.out_sg
, pdu
->elem
.out_num
,
80 static size_t pdu_pack(V9fsPDU
*pdu
, size_t offset
, const void *src
,
83 return pdu_packunpack((void *)src
, pdu
->elem
.in_sg
, pdu
->elem
.in_num
,
87 static int pdu_copy_sg(V9fsPDU
*pdu
, size_t offset
, int rx
, struct iovec
*sg
)
95 src_sg
= pdu
->elem
.in_sg
;
96 num
= pdu
->elem
.in_num
;
98 src_sg
= pdu
->elem
.out_sg
;
99 num
= pdu
->elem
.out_num
;
103 for (i
= 0; i
< num
; i
++) {
105 sg
[j
].iov_base
= src_sg
[i
].iov_base
;
106 sg
[j
].iov_len
= src_sg
[i
].iov_len
;
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
);
115 pos
+= src_sg
[i
].iov_len
;
121 static size_t pdu_unmarshal(V9fsPDU
*pdu
, size_t offset
, const char *fmt
, ...)
123 size_t old_offset
= offset
;
128 for (i
= 0; fmt
[i
]; i
++) {
131 uint8_t *valp
= va_arg(ap
, uint8_t *);
132 offset
+= pdu_unpack(valp
, pdu
, offset
, sizeof(*valp
));
137 valp
= va_arg(ap
, uint16_t *);
138 val
= le16_to_cpupu(valp
);
139 offset
+= pdu_unpack(&val
, pdu
, offset
, sizeof(val
));
145 valp
= va_arg(ap
, uint32_t *);
146 val
= le32_to_cpupu(valp
);
147 offset
+= pdu_unpack(&val
, pdu
, offset
, sizeof(val
));
153 valp
= va_arg(ap
, uint64_t *);
154 val
= le64_to_cpup(valp
);
155 offset
+= pdu_unpack(&val
, pdu
, offset
, sizeof(val
));
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
);
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;
175 V9fsQID
*qidp
= va_arg(ap
, V9fsQID
*);
176 offset
+= pdu_unmarshal(pdu
, offset
, "bdq",
177 &qidp
->type
, &qidp
->version
, &qidp
->path
);
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
,
199 return offset
- old_offset
;
202 static size_t pdu_marshal(V9fsPDU
*pdu
, size_t offset
, const char *fmt
, ...)
204 size_t old_offset
= offset
;
209 for (i
= 0; fmt
[i
]; i
++) {
212 uint8_t val
= va_arg(ap
, int);
213 offset
+= pdu_pack(pdu
, offset
, &val
, sizeof(val
));
218 cpu_to_le16w(&val
, va_arg(ap
, int));
219 offset
+= pdu_pack(pdu
, offset
, &val
, sizeof(val
));
224 cpu_to_le32w(&val
, va_arg(ap
, uint32_t));
225 offset
+= pdu_pack(pdu
, offset
, &val
, sizeof(val
));
230 cpu_to_le64w(&val
, va_arg(ap
, uint64_t));
231 offset
+= pdu_pack(pdu
, offset
, &val
, sizeof(val
));
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
);
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
);
247 V9fsQID
*qidp
= va_arg(ap
, V9fsQID
*);
248 offset
+= pdu_marshal(pdu
, offset
, "bdq",
249 qidp
->type
, qidp
->version
, qidp
->path
);
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
);
269 return offset
- old_offset
;
272 static void complete_pdu(V9fsState
*s
, V9fsPDU
*pdu
, ssize_t len
)
274 int8_t id
= pdu
->id
+ 1; /* Response */
280 str
.data
= strerror(err
);
281 str
.size
= strlen(str
.data
);
284 len
+= pdu_marshal(pdu
, len
, "s", &str
);
286 len
+= pdu_marshal(pdu
, len
, "d", err
);
292 /* fill out the header */
293 pdu_marshal(pdu
, 0, "dbw", (int32_t)len
, id
, pdu
->tag
);
295 /* keep these in sync */
299 /* push onto queue and notify */
300 virtqueue_push(s
->vq
, &pdu
->elem
, len
);
302 /* FIXME: we should batch these completions */
303 virtio_notify(&s
->vdev
, s
->vq
);
308 static void v9fs_dummy(V9fsState
*s
, V9fsPDU
*pdu
)
310 /* Note: The following have been added to prevent GCC from complaining
311 * They will be removed in the subsequent patches */
316 static void v9fs_version(V9fsState
*s
, V9fsPDU
*pdu
)
323 static void v9fs_attach(V9fsState
*s
, V9fsPDU
*pdu
)
330 static void v9fs_stat(V9fsState
*s
, V9fsPDU
*pdu
)
337 static void v9fs_walk(V9fsState
*s
, V9fsPDU
*pdu
)
344 static void v9fs_clunk(V9fsState
*s
, V9fsPDU
*pdu
)
351 static void v9fs_open(V9fsState
*s
, V9fsPDU
*pdu
)
352 { if (debug_9p_pdu
) {
357 static void v9fs_read(V9fsState
*s
, V9fsPDU
*pdu
)
364 static void v9fs_write(V9fsState
*s
, V9fsPDU
*pdu
)
371 static void v9fs_create(V9fsState
*s
, V9fsPDU
*pdu
)
378 static void v9fs_flush(V9fsState
*s
, V9fsPDU
*pdu
)
386 static void v9fs_remove(V9fsState
*s
, V9fsPDU
*pdu
)
393 static void v9fs_wstat(V9fsState
*s
, V9fsPDU
*pdu
)
400 typedef void (pdu_handler_t
)(V9fsState
*s
, V9fsPDU
*pdu
);
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
,
411 [P9_TAUTH
] = v9fs_auth
,
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
,
420 static void submit_pdu(V9fsState
*s
, V9fsPDU
*pdu
)
422 pdu_handler_t
*handler
;
428 BUG_ON(pdu
->id
>= ARRAY_SIZE(pdu_handlers
));
430 handler
= pdu_handlers
[pdu
->id
];
431 BUG_ON(handler
== NULL
);
436 static void handle_9p_output(VirtIODevice
*vdev
, VirtQueue
*vq
)
438 V9fsState
*s
= (V9fsState
*)vdev
;
442 while ((pdu
= alloc_pdu(s
)) &&
443 (len
= virtqueue_pop(vq
, &pdu
->elem
)) != 0) {
446 BUG_ON(pdu
->elem
.out_num
== 0 || pdu
->elem
.in_num
== 0);
447 BUG_ON(pdu
->elem
.out_sg
[0].iov_len
< 7);
449 ptr
= pdu
->elem
.out_sg
[0].iov_base
;
451 memcpy(&pdu
->size
, ptr
, 4);
453 memcpy(&pdu
->tag
, ptr
+ 5, 2);
461 static uint32_t virtio_9p_get_features(VirtIODevice
*vdev
, uint32_t features
)
463 features
|= 1 << VIRTIO_9P_MOUNT_TAG
;
467 static V9fsState
*to_virtio_9p(VirtIODevice
*vdev
)
469 return (V9fsState
*)vdev
;
472 static void virtio_9p_get_config(VirtIODevice
*vdev
, uint8_t *config
)
474 struct virtio_9p_config
*cfg
;
475 V9fsState
*s
= to_virtio_9p(vdev
);
477 cfg
= qemu_mallocz(sizeof(struct virtio_9p_config
) +
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
);
485 VirtIODevice
*virtio_9p_init(DeviceState
*dev
, V9fsConf
*conf
)
493 s
= (V9fsState
*)virtio_common_init("virtio-9p",
495 sizeof(struct virtio_9p_config
)+
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
);
505 s
->vq
= virtio_add_queue(&s
->vdev
, MAX_REQ
, handle_9p_output
);
507 fse
= get_fsdev_fsentry(conf
->fsdev_id
);
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
);
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",
524 if (lstat(fse
->path
, &stat
)) {
525 fprintf(stderr
, "share path %s does not exist\n", fse
->path
);
527 } else if (!S_ISDIR(stat
.st_mode
)) {
528 fprintf(stderr
, "share path %s is not a directory \n", fse
->path
);
532 s
->ctx
.fs_root
= qemu_strdup(fse
->path
);
533 len
= strlen(conf
->tag
);
534 if (len
> MAX_TAG_LEN
) {
537 /* s->tag is non-NULL terminated string */
538 s
->tag
= qemu_malloc(len
);
539 memcpy(s
->tag
, conf
->tag
, len
);
544 s
->vdev
.get_features
= virtio_9p_get_features
;
545 s
->config_size
= sizeof(struct virtio_9p_config
) +
547 s
->vdev
.get_config
= virtio_9p_get_config
;