virtio-9p: Add a virtio 9p device to qemu
This patch doesn't implement the 9p protocol handling code. It adds a simple device which dump the protocol data. [jvrao@linux.vnet.ibm.com: Little-Endian to host format conversion] [aneesh.kumar@linux.vnet.ibm.com: Multiple-mounts support] Signed-off-by: Anthony Liguori <aliguori@us.ibm.com> Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
This commit is contained in:
parent
74db920c32
commit
9f10751365
@ -168,6 +168,7 @@ obj-y += virtio-blk.o virtio-balloon.o virtio-net.o virtio-serial-bus.o
|
||||
obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
|
||||
obj-y += vhost_net.o
|
||||
obj-$(CONFIG_VHOST_NET) += vhost.o
|
||||
obj-$(CONFIG_LINUX) += virtio-9p.o virtio-9p-debug.o virtio-9p-local.o
|
||||
obj-y += rwhandler.o
|
||||
obj-$(CONFIG_KVM) += kvm.o kvm-all.o
|
||||
obj-$(CONFIG_NO_KVM) += kvm-stub.o
|
||||
|
@ -21,7 +21,7 @@ static QTAILQ_HEAD(FsTypeEntry_head, FsTypeListEntry) fstype_entries =
|
||||
QTAILQ_HEAD_INITIALIZER(fstype_entries);
|
||||
|
||||
static FsTypeTable FsTypes[] = {
|
||||
{ .name = "local", .ops = NULL},
|
||||
{ .name = "local", .ops = &local_ops},
|
||||
};
|
||||
|
||||
int qemu_fsdev_add(QemuOpts *opts)
|
||||
|
@ -50,4 +50,5 @@ typedef struct FsTypeListEntry {
|
||||
|
||||
extern int qemu_fsdev_add(QemuOpts *opts);
|
||||
extern FsTypeEntry *get_fsdev_fsentry(char *id);
|
||||
extern FileOperations local_ops;
|
||||
#endif
|
||||
|
26
hw/9p.h
Normal file
26
hw/9p.h
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Virtio 9p
|
||||
*
|
||||
* Copyright IBM, Corp. 2010
|
||||
*
|
||||
* Authors:
|
||||
* Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
* the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef QEMU_9P_H
|
||||
#define QEMU_9P_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct V9fsConf
|
||||
{
|
||||
/* tag name for the device */
|
||||
char *tag;
|
||||
char *fsdev_id;
|
||||
} V9fsConf;
|
||||
|
||||
#endif
|
484
hw/virtio-9p-debug.c
Normal file
484
hw/virtio-9p-debug.c
Normal file
@ -0,0 +1,484 @@
|
||||
/*
|
||||
* Virtio 9p PDU debug
|
||||
*
|
||||
* Copyright IBM, Corp. 2010
|
||||
*
|
||||
* Authors:
|
||||
* Anthony Liguori <aliguori@us.ibm.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
* the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
#include "virtio.h"
|
||||
#include "pc.h"
|
||||
#include "virtio-9p.h"
|
||||
#include "virtio-9p-debug.h"
|
||||
|
||||
#define BUG_ON(cond) assert(!(cond))
|
||||
|
||||
static FILE *llogfile;
|
||||
|
||||
static struct iovec *get_sg(V9fsPDU *pdu, int rx)
|
||||
{
|
||||
if (rx) {
|
||||
return pdu->elem.in_sg;
|
||||
}
|
||||
return pdu->elem.out_sg;
|
||||
}
|
||||
|
||||
static int get_sg_count(V9fsPDU *pdu, int rx)
|
||||
{
|
||||
if (rx) {
|
||||
return pdu->elem.in_num;
|
||||
}
|
||||
return pdu->elem.out_num;
|
||||
|
||||
}
|
||||
|
||||
static void pprint_int8(V9fsPDU *pdu, int rx, size_t *offsetp,
|
||||
const char *name)
|
||||
{
|
||||
size_t copied;
|
||||
int count = get_sg_count(pdu, rx);
|
||||
size_t offset = *offsetp;
|
||||
struct iovec *sg = get_sg(pdu, rx);
|
||||
int8_t value;
|
||||
|
||||
copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value));
|
||||
|
||||
BUG_ON(copied != sizeof(value));
|
||||
offset += sizeof(value);
|
||||
fprintf(llogfile, "%s=0x%x", name, value);
|
||||
*offsetp = offset;
|
||||
}
|
||||
|
||||
static void pprint_int16(V9fsPDU *pdu, int rx, size_t *offsetp,
|
||||
const char *name)
|
||||
{
|
||||
size_t copied;
|
||||
int count = get_sg_count(pdu, rx);
|
||||
struct iovec *sg = get_sg(pdu, rx);
|
||||
size_t offset = *offsetp;
|
||||
int16_t value;
|
||||
|
||||
|
||||
copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value));
|
||||
|
||||
BUG_ON(copied != sizeof(value));
|
||||
offset += sizeof(value);
|
||||
fprintf(llogfile, "%s=0x%x", name, value);
|
||||
*offsetp = offset;
|
||||
}
|
||||
|
||||
static void pprint_int32(V9fsPDU *pdu, int rx, size_t *offsetp,
|
||||
const char *name)
|
||||
{
|
||||
size_t copied;
|
||||
int count = get_sg_count(pdu, rx);
|
||||
struct iovec *sg = get_sg(pdu, rx);
|
||||
size_t offset = *offsetp;
|
||||
int32_t value;
|
||||
|
||||
|
||||
copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value));
|
||||
|
||||
BUG_ON(copied != sizeof(value));
|
||||
offset += sizeof(value);
|
||||
fprintf(llogfile, "%s=0x%x", name, value);
|
||||
*offsetp = offset;
|
||||
}
|
||||
|
||||
static void pprint_int64(V9fsPDU *pdu, int rx, size_t *offsetp,
|
||||
const char *name)
|
||||
{
|
||||
size_t copied;
|
||||
int count = get_sg_count(pdu, rx);
|
||||
struct iovec *sg = get_sg(pdu, rx);
|
||||
size_t offset = *offsetp;
|
||||
int64_t value;
|
||||
|
||||
|
||||
copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value));
|
||||
|
||||
BUG_ON(copied != sizeof(value));
|
||||
offset += sizeof(value);
|
||||
fprintf(llogfile, "%s=0x%" PRIx64, name, value);
|
||||
*offsetp = offset;
|
||||
}
|
||||
|
||||
static void pprint_str(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
|
||||
{
|
||||
int sg_count = get_sg_count(pdu, rx);
|
||||
struct iovec *sg = get_sg(pdu, rx);
|
||||
size_t offset = *offsetp;
|
||||
uint16_t tmp_size, size;
|
||||
size_t result;
|
||||
size_t copied = 0;
|
||||
int i = 0;
|
||||
|
||||
/* get the size */
|
||||
copied = do_pdu_unpack(&tmp_size, sg, sg_count, offset, sizeof(tmp_size));
|
||||
BUG_ON(copied != sizeof(tmp_size));
|
||||
size = le16_to_cpupu(&tmp_size);
|
||||
offset += copied;
|
||||
|
||||
fprintf(llogfile, "%s=", name);
|
||||
for (i = 0; size && i < sg_count; i++) {
|
||||
size_t len;
|
||||
if (offset >= sg[i].iov_len) {
|
||||
/* skip this sg */
|
||||
offset -= sg[i].iov_len;
|
||||
continue;
|
||||
} else {
|
||||
len = MIN(sg[i].iov_len - offset, size);
|
||||
result = fwrite(sg[i].iov_base + offset, 1, len, llogfile);
|
||||
BUG_ON(result != len);
|
||||
size -= len;
|
||||
copied += len;
|
||||
if (size) {
|
||||
offset = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
*offsetp += copied;
|
||||
}
|
||||
|
||||
static void pprint_qid(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
|
||||
{
|
||||
fprintf(llogfile, "%s={", name);
|
||||
pprint_int8(pdu, rx, offsetp, "type");
|
||||
pprint_int32(pdu, rx, offsetp, ", version");
|
||||
pprint_int64(pdu, rx, offsetp, ", path");
|
||||
fprintf(llogfile, "}");
|
||||
}
|
||||
|
||||
static void pprint_stat(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
|
||||
{
|
||||
fprintf(llogfile, "%s={", name);
|
||||
pprint_int16(pdu, rx, offsetp, "size");
|
||||
pprint_int16(pdu, rx, offsetp, ", type");
|
||||
pprint_int32(pdu, rx, offsetp, ", dev");
|
||||
pprint_qid(pdu, rx, offsetp, ", qid");
|
||||
pprint_int32(pdu, rx, offsetp, ", mode");
|
||||
pprint_int32(pdu, rx, offsetp, ", atime");
|
||||
pprint_int32(pdu, rx, offsetp, ", mtime");
|
||||
pprint_int64(pdu, rx, offsetp, ", length");
|
||||
pprint_str(pdu, rx, offsetp, ", name");
|
||||
pprint_str(pdu, rx, offsetp, ", uid");
|
||||
pprint_str(pdu, rx, offsetp, ", gid");
|
||||
pprint_str(pdu, rx, offsetp, ", muid");
|
||||
if (dotu) {
|
||||
pprint_str(pdu, rx, offsetp, ", extension");
|
||||
pprint_int32(pdu, rx, offsetp, ", uid");
|
||||
pprint_int32(pdu, rx, offsetp, ", gid");
|
||||
pprint_int32(pdu, rx, offsetp, ", muid");
|
||||
}
|
||||
fprintf(llogfile, "}");
|
||||
}
|
||||
|
||||
static void pprint_strs(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
|
||||
{
|
||||
int sg_count = get_sg_count(pdu, rx);
|
||||
struct iovec *sg = get_sg(pdu, rx);
|
||||
size_t offset = *offsetp;
|
||||
uint16_t tmp_count, count, i;
|
||||
size_t copied = 0;
|
||||
|
||||
fprintf(llogfile, "%s={", name);
|
||||
|
||||
/* Get the count */
|
||||
copied = do_pdu_unpack(&tmp_count, sg, sg_count, offset, sizeof(tmp_count));
|
||||
BUG_ON(copied != sizeof(tmp_count));
|
||||
count = le16_to_cpupu(&tmp_count);
|
||||
offset += copied;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
char str[512];
|
||||
if (i) {
|
||||
fprintf(llogfile, ", ");
|
||||
}
|
||||
snprintf(str, sizeof(str), "[%d]", i);
|
||||
pprint_str(pdu, rx, &offset, str);
|
||||
}
|
||||
|
||||
fprintf(llogfile, "}");
|
||||
|
||||
*offsetp = offset;
|
||||
}
|
||||
|
||||
static void pprint_qids(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
|
||||
{
|
||||
int sg_count = get_sg_count(pdu, rx);
|
||||
struct iovec *sg = get_sg(pdu, rx);
|
||||
size_t offset = *offsetp;
|
||||
uint16_t tmp_count, count, i;
|
||||
size_t copied = 0;
|
||||
|
||||
fprintf(llogfile, "%s={", name);
|
||||
|
||||
copied = do_pdu_unpack(&tmp_count, sg, sg_count, offset, sizeof(tmp_count));
|
||||
BUG_ON(copied != sizeof(tmp_count));
|
||||
count = le16_to_cpupu(&tmp_count);
|
||||
offset += copied;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
char str[512];
|
||||
if (i) {
|
||||
fprintf(llogfile, ", ");
|
||||
}
|
||||
snprintf(str, sizeof(str), "[%d]", i);
|
||||
pprint_qid(pdu, rx, &offset, str);
|
||||
}
|
||||
|
||||
fprintf(llogfile, "}");
|
||||
|
||||
*offsetp = offset;
|
||||
}
|
||||
|
||||
static void pprint_sg(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
|
||||
{
|
||||
struct iovec *sg = get_sg(pdu, rx);
|
||||
unsigned int count;
|
||||
int i;
|
||||
|
||||
if (rx) {
|
||||
count = pdu->elem.in_num;
|
||||
} else {
|
||||
count = pdu->elem.out_num;
|
||||
}
|
||||
|
||||
fprintf(llogfile, "%s={", name);
|
||||
for (i = 0; i < count; i++) {
|
||||
if (i) {
|
||||
fprintf(llogfile, ", ");
|
||||
}
|
||||
fprintf(llogfile, "(%p, 0x%zx)", sg[i].iov_base, sg[i].iov_len);
|
||||
}
|
||||
fprintf(llogfile, "}");
|
||||
}
|
||||
|
||||
/* FIXME: read from a directory fid returns serialized stat_t's */
|
||||
#ifdef DEBUG_DATA
|
||||
static void pprint_data(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
|
||||
{
|
||||
struct iovec *sg = get_sg(pdu, rx);
|
||||
size_t offset = *offsetp;
|
||||
unsigned int count;
|
||||
int32_t size;
|
||||
int total, i, j;
|
||||
ssize_t len;
|
||||
|
||||
if (rx) {
|
||||
count = pdu->elem.in_num;
|
||||
} else
|
||||
count = pdu->elem.out_num;
|
||||
}
|
||||
|
||||
BUG_ON((offset + sizeof(size)) > sg[0].iov_len);
|
||||
|
||||
memcpy(&size, sg[0].iov_base + offset, sizeof(size));
|
||||
offset += sizeof(size);
|
||||
|
||||
fprintf(llogfile, "size: %x\n", size);
|
||||
|
||||
sg[0].iov_base += 11; /* skip header */
|
||||
sg[0].iov_len -= 11;
|
||||
|
||||
total = 0;
|
||||
for (i = 0; i < count; i++) {
|
||||
total += sg[i].iov_len;
|
||||
if (total >= size) {
|
||||
/* trim sg list so writev does the right thing */
|
||||
sg[i].iov_len -= (total - size);
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(llogfile, "%s={\"", name);
|
||||
fflush(llogfile);
|
||||
for (j = 0; j < i; j++) {
|
||||
if (j) {
|
||||
fprintf(llogfile, "\", \"");
|
||||
fflush(llogfile);
|
||||
}
|
||||
|
||||
do {
|
||||
len = writev(fileno(llogfile), &sg[j], 1);
|
||||
} while (len == -1 && errno == EINTR);
|
||||
fprintf(llogfile, "len == %ld: %m\n", len);
|
||||
BUG_ON(len != sg[j].iov_len);
|
||||
}
|
||||
fprintf(llogfile, "\"}");
|
||||
|
||||
sg[0].iov_base -= 11;
|
||||
sg[0].iov_len += 11;
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
void pprint_pdu(V9fsPDU *pdu)
|
||||
{
|
||||
size_t offset = 7;
|
||||
|
||||
if (llogfile == NULL) {
|
||||
llogfile = fopen("/tmp/pdu.log", "w");
|
||||
}
|
||||
|
||||
switch (pdu->id) {
|
||||
case P9_TVERSION:
|
||||
fprintf(llogfile, "TVERSION: (");
|
||||
pprint_int32(pdu, 0, &offset, "msize");
|
||||
pprint_str(pdu, 0, &offset, ", version");
|
||||
break;
|
||||
case P9_RVERSION:
|
||||
fprintf(llogfile, "RVERSION: (");
|
||||
pprint_int32(pdu, 1, &offset, "msize");
|
||||
pprint_str(pdu, 1, &offset, ", version");
|
||||
break;
|
||||
case P9_TAUTH:
|
||||
fprintf(llogfile, "TAUTH: (");
|
||||
pprint_int32(pdu, 0, &offset, "afid");
|
||||
pprint_str(pdu, 0, &offset, ", uname");
|
||||
pprint_str(pdu, 0, &offset, ", aname");
|
||||
if (dotu) {
|
||||
pprint_int32(pdu, 0, &offset, ", n_uname");
|
||||
}
|
||||
break;
|
||||
case P9_RAUTH:
|
||||
fprintf(llogfile, "RAUTH: (");
|
||||
pprint_qid(pdu, 1, &offset, "qid");
|
||||
break;
|
||||
case P9_TATTACH:
|
||||
fprintf(llogfile, "TATTACH: (");
|
||||
pprint_int32(pdu, 0, &offset, "fid");
|
||||
pprint_int32(pdu, 0, &offset, ", afid");
|
||||
pprint_str(pdu, 0, &offset, ", uname");
|
||||
pprint_str(pdu, 0, &offset, ", aname");
|
||||
if (dotu) {
|
||||
pprint_int32(pdu, 0, &offset, ", n_uname");
|
||||
}
|
||||
break;
|
||||
case P9_RATTACH:
|
||||
fprintf(llogfile, "RATTACH: (");
|
||||
pprint_qid(pdu, 1, &offset, "qid");
|
||||
break;
|
||||
case P9_TERROR:
|
||||
fprintf(llogfile, "TERROR: (");
|
||||
break;
|
||||
case P9_RERROR:
|
||||
fprintf(llogfile, "RERROR: (");
|
||||
pprint_str(pdu, 1, &offset, "ename");
|
||||
if (dotu) {
|
||||
pprint_int32(pdu, 1, &offset, ", ecode");
|
||||
}
|
||||
break;
|
||||
case P9_TFLUSH:
|
||||
fprintf(llogfile, "TFLUSH: (");
|
||||
pprint_int16(pdu, 0, &offset, "oldtag");
|
||||
break;
|
||||
case P9_RFLUSH:
|
||||
fprintf(llogfile, "RFLUSH: (");
|
||||
break;
|
||||
case P9_TWALK:
|
||||
fprintf(llogfile, "TWALK: (");
|
||||
pprint_int32(pdu, 0, &offset, "fid");
|
||||
pprint_int32(pdu, 0, &offset, ", newfid");
|
||||
pprint_strs(pdu, 0, &offset, ", wnames");
|
||||
break;
|
||||
case P9_RWALK:
|
||||
fprintf(llogfile, "RWALK: (");
|
||||
pprint_qids(pdu, 1, &offset, "wqids");
|
||||
break;
|
||||
case P9_TOPEN:
|
||||
fprintf(llogfile, "TOPEN: (");
|
||||
pprint_int32(pdu, 0, &offset, "fid");
|
||||
pprint_int8(pdu, 0, &offset, ", mode");
|
||||
break;
|
||||
case P9_ROPEN:
|
||||
fprintf(llogfile, "ROPEN: (");
|
||||
pprint_qid(pdu, 1, &offset, "qid");
|
||||
pprint_int32(pdu, 1, &offset, ", iounit");
|
||||
break;
|
||||
case P9_TCREATE:
|
||||
fprintf(llogfile, "TCREATE: (");
|
||||
pprint_int32(pdu, 0, &offset, "fid");
|
||||
pprint_str(pdu, 0, &offset, ", name");
|
||||
pprint_int32(pdu, 0, &offset, ", perm");
|
||||
pprint_int8(pdu, 0, &offset, ", mode");
|
||||
if (dotu) {
|
||||
pprint_str(pdu, 0, &offset, ", extension");
|
||||
}
|
||||
break;
|
||||
case P9_RCREATE:
|
||||
fprintf(llogfile, "RCREATE: (");
|
||||
pprint_qid(pdu, 1, &offset, "qid");
|
||||
pprint_int32(pdu, 1, &offset, ", iounit");
|
||||
break;
|
||||
case P9_TREAD:
|
||||
fprintf(llogfile, "TREAD: (");
|
||||
pprint_int32(pdu, 0, &offset, "fid");
|
||||
pprint_int64(pdu, 0, &offset, ", offset");
|
||||
pprint_int32(pdu, 0, &offset, ", count");
|
||||
pprint_sg(pdu, 0, &offset, ", sg");
|
||||
break;
|
||||
case P9_RREAD:
|
||||
fprintf(llogfile, "RREAD: (");
|
||||
pprint_int32(pdu, 1, &offset, "count");
|
||||
pprint_sg(pdu, 1, &offset, ", sg");
|
||||
offset = 7;
|
||||
#ifdef DEBUG_DATA
|
||||
pprint_data(pdu, 1, &offset, ", data");
|
||||
#endif
|
||||
break;
|
||||
case P9_TWRITE:
|
||||
fprintf(llogfile, "TWRITE: (");
|
||||
pprint_int32(pdu, 0, &offset, "fid");
|
||||
pprint_int64(pdu, 0, &offset, ", offset");
|
||||
pprint_int32(pdu, 0, &offset, ", count");
|
||||
break;
|
||||
case P9_RWRITE:
|
||||
fprintf(llogfile, "RWRITE: (");
|
||||
pprint_int32(pdu, 1, &offset, "count");
|
||||
break;
|
||||
case P9_TCLUNK:
|
||||
fprintf(llogfile, "TCLUNK: (");
|
||||
pprint_int32(pdu, 0, &offset, "fid");
|
||||
break;
|
||||
case P9_RCLUNK:
|
||||
fprintf(llogfile, "RCLUNK: (");
|
||||
break;
|
||||
case P9_TREMOVE:
|
||||
fprintf(llogfile, "TREMOVE: (");
|
||||
pprint_int32(pdu, 0, &offset, "fid");
|
||||
break;
|
||||
case P9_RREMOVE:
|
||||
fprintf(llogfile, "RREMOVE: (");
|
||||
break;
|
||||
case P9_TSTAT:
|
||||
fprintf(llogfile, "TSTAT: (");
|
||||
pprint_int32(pdu, 0, &offset, "fid");
|
||||
break;
|
||||
case P9_RSTAT:
|
||||
fprintf(llogfile, "RSTAT: (");
|
||||
offset += 2; /* ignored */
|
||||
pprint_stat(pdu, 1, &offset, "stat");
|
||||
break;
|
||||
case P9_TWSTAT:
|
||||
fprintf(llogfile, "TWSTAT: (");
|
||||
pprint_int32(pdu, 0, &offset, "fid");
|
||||
offset += 2; /* ignored */
|
||||
pprint_stat(pdu, 0, &offset, ", stat");
|
||||
break;
|
||||
case P9_RWSTAT:
|
||||
fprintf(llogfile, "RWSTAT: (");
|
||||
break;
|
||||
default:
|
||||
fprintf(llogfile, "unknown(%d): (", pdu->id);
|
||||
break;
|
||||
}
|
||||
|
||||
fprintf(llogfile, ")\n");
|
||||
}
|
17
hw/virtio-9p-local.c
Normal file
17
hw/virtio-9p-local.c
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Virtio 9p Posix callback
|
||||
*
|
||||
* Copyright IBM, Corp. 2010
|
||||
*
|
||||
* Authors:
|
||||
* Anthony Liguori <aliguori@us.ibm.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
* the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
#include "virtio.h"
|
||||
#include "virtio-9p.h"
|
||||
|
||||
FileOperations local_ops = {
|
||||
};
|
307
hw/virtio-9p.c
Normal file
307
hw/virtio-9p.c
Normal file
@ -0,0 +1,307 @@
|
||||
/*
|
||||
* Virtio 9p backend
|
||||
*
|
||||
* Copyright IBM, Corp. 2010
|
||||
*
|
||||
* Authors:
|
||||
* Anthony Liguori <aliguori@us.ibm.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
* the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "virtio.h"
|
||||
#include "pc.h"
|
||||
#include "qemu_socket.h"
|
||||
#include "virtio-9p.h"
|
||||
#include "fsdev/qemu-fsdev.h"
|
||||
#include "virtio-9p-debug.h"
|
||||
|
||||
int dotu = 1;
|
||||
int debug_9p_pdu;
|
||||
|
||||
static V9fsPDU *alloc_pdu(V9fsState *s)
|
||||
{
|
||||
V9fsPDU *pdu = NULL;
|
||||
|
||||
if (!QLIST_EMPTY(&s->free_list)) {
|
||||
pdu = QLIST_FIRST(&s->free_list);
|
||||
QLIST_REMOVE(pdu, next);
|
||||
}
|
||||
return pdu;
|
||||
}
|
||||
|
||||
static void free_pdu(V9fsState *s, V9fsPDU *pdu)
|
||||
{
|
||||
if (pdu) {
|
||||
QLIST_INSERT_HEAD(&s->free_list, pdu, next);
|
||||
}
|
||||
}
|
||||
|
||||
size_t pdu_packunpack(void *addr, struct iovec *sg, int sg_count,
|
||||
size_t offset, size_t size, int pack)
|
||||
{
|
||||
int i = 0;
|
||||
size_t copied = 0;
|
||||
|
||||
for (i = 0; size && i < sg_count; i++) {
|
||||
size_t len;
|
||||
if (offset >= sg[i].iov_len) {
|
||||
/* skip this sg */
|
||||
offset -= sg[i].iov_len;
|
||||
continue;
|
||||
} else {
|
||||
len = MIN(sg[i].iov_len - offset, size);
|
||||
if (pack) {
|
||||
memcpy(sg[i].iov_base + offset, addr, len);
|
||||
} else {
|
||||
memcpy(addr, sg[i].iov_base + offset, len);
|
||||
}
|
||||
size -= len;
|
||||
copied += len;
|
||||
addr += len;
|
||||
if (size) {
|
||||
offset = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return copied;
|
||||
}
|
||||
|
||||
static void v9fs_version(V9fsState *s, V9fsPDU *pdu)
|
||||
{
|
||||
if (debug_9p_pdu) {
|
||||
pprint_pdu(pdu);
|
||||
}
|
||||
}
|
||||
|
||||
static void v9fs_attach(V9fsState *s, V9fsPDU *pdu)
|
||||
{
|
||||
if (debug_9p_pdu) {
|
||||
pprint_pdu(pdu);
|
||||
}
|
||||
}
|
||||
|
||||
static void v9fs_stat(V9fsState *s, V9fsPDU *pdu)
|
||||
{
|
||||
if (debug_9p_pdu) {
|
||||
pprint_pdu(pdu);
|
||||
}
|
||||
}
|
||||
|
||||
static void v9fs_walk(V9fsState *s, V9fsPDU *pdu)
|
||||
{
|
||||
if (debug_9p_pdu) {
|
||||
pprint_pdu(pdu);
|
||||
}
|
||||
}
|
||||
|
||||
static void v9fs_clunk(V9fsState *s, V9fsPDU *pdu)
|
||||
{
|
||||
if (debug_9p_pdu) {
|
||||
pprint_pdu(pdu);
|
||||
}
|
||||
}
|
||||
|
||||
static void v9fs_open(V9fsState *s, V9fsPDU *pdu)
|
||||
{ if (debug_9p_pdu) {
|
||||
pprint_pdu(pdu);
|
||||
}
|
||||
}
|
||||
|
||||
static void v9fs_read(V9fsState *s, V9fsPDU *pdu)
|
||||
{
|
||||
if (debug_9p_pdu) {
|
||||
pprint_pdu(pdu);
|
||||
}
|
||||
}
|
||||
|
||||
static void v9fs_write(V9fsState *s, V9fsPDU *pdu)
|
||||
{
|
||||
if (debug_9p_pdu) {
|
||||
pprint_pdu(pdu);
|
||||
}
|
||||
}
|
||||
|
||||
static void v9fs_create(V9fsState *s, V9fsPDU *pdu)
|
||||
{
|
||||
if (debug_9p_pdu) {
|
||||
pprint_pdu(pdu);
|
||||
}
|
||||
}
|
||||
|
||||
static void v9fs_flush(V9fsState *s, V9fsPDU *pdu)
|
||||
{
|
||||
if (debug_9p_pdu) {
|
||||
pprint_pdu(pdu);
|
||||
}
|
||||
}
|
||||
|
||||
static void v9fs_remove(V9fsState *s, V9fsPDU *pdu)
|
||||
{
|
||||
if (debug_9p_pdu) {
|
||||
pprint_pdu(pdu);
|
||||
}
|
||||
}
|
||||
|
||||
static void v9fs_wstat(V9fsState *s, V9fsPDU *pdu)
|
||||
{
|
||||
if (debug_9p_pdu) {
|
||||
pprint_pdu(pdu);
|
||||
}
|
||||
}
|
||||
|
||||
typedef void (pdu_handler_t)(V9fsState *s, V9fsPDU *pdu);
|
||||
|
||||
static pdu_handler_t *pdu_handlers[] = {
|
||||
[P9_TVERSION] = v9fs_version,
|
||||
[P9_TATTACH] = v9fs_attach,
|
||||
[P9_TSTAT] = v9fs_stat,
|
||||
[P9_TWALK] = v9fs_walk,
|
||||
[P9_TCLUNK] = v9fs_clunk,
|
||||
[P9_TOPEN] = v9fs_open,
|
||||
[P9_TREAD] = v9fs_read,
|
||||
#if 0
|
||||
[P9_TAUTH] = v9fs_auth,
|
||||
#endif
|
||||
[P9_TFLUSH] = v9fs_flush,
|
||||
[P9_TCREATE] = v9fs_create,
|
||||
[P9_TWRITE] = v9fs_write,
|
||||
[P9_TWSTAT] = v9fs_wstat,
|
||||
[P9_TREMOVE] = v9fs_remove,
|
||||
};
|
||||
|
||||
static void submit_pdu(V9fsState *s, V9fsPDU *pdu)
|
||||
{
|
||||
pdu_handler_t *handler;
|
||||
|
||||
if (debug_9p_pdu) {
|
||||
pprint_pdu(pdu);
|
||||
}
|
||||
|
||||
BUG_ON(pdu->id >= ARRAY_SIZE(pdu_handlers));
|
||||
|
||||
handler = pdu_handlers[pdu->id];
|
||||
BUG_ON(handler == NULL);
|
||||
|
||||
handler(s, pdu);
|
||||
}
|
||||
|
||||
static void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq)
|
||||
{
|
||||
V9fsState *s = (V9fsState *)vdev;
|
||||
V9fsPDU *pdu;
|
||||
ssize_t len;
|
||||
|
||||
while ((pdu = alloc_pdu(s)) &&
|
||||
(len = virtqueue_pop(vq, &pdu->elem)) != 0) {
|
||||
uint8_t *ptr;
|
||||
|
||||
BUG_ON(pdu->elem.out_num == 0 || pdu->elem.in_num == 0);
|
||||
BUG_ON(pdu->elem.out_sg[0].iov_len < 7);
|
||||
|
||||
ptr = pdu->elem.out_sg[0].iov_base;
|
||||
|
||||
memcpy(&pdu->size, ptr, 4);
|
||||
pdu->id = ptr[4];
|
||||
memcpy(&pdu->tag, ptr + 5, 2);
|
||||
|
||||
submit_pdu(s, pdu);
|
||||
}
|
||||
|
||||
free_pdu(s, pdu);
|
||||
}
|
||||
|
||||
static uint32_t virtio_9p_get_features(VirtIODevice *vdev, uint32_t features)
|
||||
{
|
||||
features |= 1 << VIRTIO_9P_MOUNT_TAG;
|
||||
return features;
|
||||
}
|
||||
|
||||
static V9fsState *to_virtio_9p(VirtIODevice *vdev)
|
||||
{
|
||||
return (V9fsState *)vdev;
|
||||
}
|
||||
|
||||
static void virtio_9p_get_config(VirtIODevice *vdev, uint8_t *config)
|
||||
{
|
||||
struct virtio_9p_config *cfg;
|
||||
V9fsState *s = to_virtio_9p(vdev);
|
||||
|
||||
cfg = qemu_mallocz(sizeof(struct virtio_9p_config) +
|
||||
s->tag_len);
|
||||
stw_raw(&cfg->tag_len, s->tag_len);
|
||||
memcpy(cfg->tag, s->tag, s->tag_len);
|
||||
memcpy(config, cfg, s->config_size);
|
||||
qemu_free(cfg);
|
||||
}
|
||||
|
||||
VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf)
|
||||
{
|
||||
V9fsState *s;
|
||||
int i, len;
|
||||
struct stat stat;
|
||||
FsTypeEntry *fse;
|
||||
|
||||
|
||||
s = (V9fsState *)virtio_common_init("virtio-9p",
|
||||
VIRTIO_ID_9P,
|
||||
sizeof(struct virtio_9p_config)+
|
||||
MAX_TAG_LEN,
|
||||
sizeof(V9fsState));
|
||||
|
||||
/* initialize pdu allocator */
|
||||
QLIST_INIT(&s->free_list);
|
||||
for (i = 0; i < (MAX_REQ - 1); i++) {
|
||||
QLIST_INSERT_HEAD(&s->free_list, &s->pdus[i], next);
|
||||
}
|
||||
|
||||
s->vq = virtio_add_queue(&s->vdev, MAX_REQ, handle_9p_output);
|
||||
|
||||
fse = get_fsdev_fsentry(conf->fsdev_id);
|
||||
|
||||
if (!fse) {
|
||||
/* We don't have a fsdev identified by fsdev_id */
|
||||
fprintf(stderr, "Virtio-9p device couldn't find fsdev "
|
||||
"with the id %s\n", conf->fsdev_id);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!fse->path || !conf->tag) {
|
||||
/* we haven't specified a mount_tag or the path */
|
||||
fprintf(stderr, "fsdev with id %s needs path "
|
||||
"and Virtio-9p device needs mount_tag arguments\n",
|
||||
conf->fsdev_id);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (lstat(fse->path, &stat)) {
|
||||
fprintf(stderr, "share path %s does not exist\n", fse->path);
|
||||
exit(1);
|
||||
} else if (!S_ISDIR(stat.st_mode)) {
|
||||
fprintf(stderr, "share path %s is not a directory \n", fse->path);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
s->ctx.fs_root = qemu_strdup(fse->path);
|
||||
len = strlen(conf->tag);
|
||||
if (len > MAX_TAG_LEN) {
|
||||
len = MAX_TAG_LEN;
|
||||
}
|
||||
/* s->tag is non-NULL terminated string */
|
||||
s->tag = qemu_malloc(len);
|
||||
memcpy(s->tag, conf->tag, len);
|
||||
s->tag_len = len;
|
||||
s->ctx.uid = -1;
|
||||
|
||||
s->ops = fse->ops;
|
||||
s->vdev.get_features = virtio_9p_get_features;
|
||||
s->config_size = sizeof(struct virtio_9p_config) +
|
||||
s->tag_len;
|
||||
s->vdev.get_config = virtio_9p_get_config;
|
||||
|
||||
return &s->vdev;
|
||||
}
|
166
hw/virtio-9p.h
Normal file
166
hw/virtio-9p.h
Normal file
@ -0,0 +1,166 @@
|
||||
#ifndef _QEMU_VIRTIO_9P_H
|
||||
#define _QEMU_VIRTIO_9P_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/time.h>
|
||||
#include <utime.h>
|
||||
|
||||
#include "file-op-9p.h"
|
||||
|
||||
/* The feature bitmap for virtio 9P */
|
||||
/* The mount point is specified in a config variable */
|
||||
#define VIRTIO_9P_MOUNT_TAG 0
|
||||
|
||||
enum {
|
||||
P9_TVERSION = 100,
|
||||
P9_RVERSION,
|
||||
P9_TAUTH = 102,
|
||||
P9_RAUTH,
|
||||
P9_TATTACH = 104,
|
||||
P9_RATTACH,
|
||||
P9_TERROR = 106,
|
||||
P9_RERROR,
|
||||
P9_TFLUSH = 108,
|
||||
P9_RFLUSH,
|
||||
P9_TWALK = 110,
|
||||
P9_RWALK,
|
||||
P9_TOPEN = 112,
|
||||
P9_ROPEN,
|
||||
P9_TCREATE = 114,
|
||||
P9_RCREATE,
|
||||
P9_TREAD = 116,
|
||||
P9_RREAD,
|
||||
P9_TWRITE = 118,
|
||||
P9_RWRITE,
|
||||
P9_TCLUNK = 120,
|
||||
P9_RCLUNK,
|
||||
P9_TREMOVE = 122,
|
||||
P9_RREMOVE,
|
||||
P9_TSTAT = 124,
|
||||
P9_RSTAT,
|
||||
P9_TWSTAT = 126,
|
||||
P9_RWSTAT,
|
||||
};
|
||||
|
||||
|
||||
/* qid.types */
|
||||
enum {
|
||||
P9_QTDIR = 0x80,
|
||||
P9_QTAPPEND = 0x40,
|
||||
P9_QTEXCL = 0x20,
|
||||
P9_QTMOUNT = 0x10,
|
||||
P9_QTAUTH = 0x08,
|
||||
P9_QTTMP = 0x04,
|
||||
P9_QTSYMLINK = 0x02,
|
||||
P9_QTLINK = 0x01,
|
||||
P9_QTFILE = 0x00,
|
||||
};
|
||||
|
||||
#define P9_NOTAG (u16)(~0)
|
||||
#define P9_NOFID (u32)(~0)
|
||||
#define P9_MAXWELEM 16
|
||||
|
||||
typedef struct V9fsPDU V9fsPDU;
|
||||
|
||||
struct V9fsPDU
|
||||
{
|
||||
uint32_t size;
|
||||
uint16_t tag;
|
||||
uint8_t id;
|
||||
VirtQueueElement elem;
|
||||
QLIST_ENTRY(V9fsPDU) next;
|
||||
};
|
||||
|
||||
|
||||
/* FIXME
|
||||
* 1) change user needs to set groups and stuff
|
||||
*/
|
||||
|
||||
/* from Linux's linux/virtio_9p.h */
|
||||
|
||||
/* The ID for virtio console */
|
||||
#define VIRTIO_ID_9P 9
|
||||
#define MAX_REQ 128
|
||||
#define MAX_TAG_LEN 32
|
||||
|
||||
#define BUG_ON(cond) assert(!(cond))
|
||||
|
||||
typedef struct V9fsFidState V9fsFidState;
|
||||
|
||||
typedef struct V9fsString
|
||||
{
|
||||
int16_t size;
|
||||
char *data;
|
||||
} V9fsString;
|
||||
|
||||
typedef struct V9fsQID
|
||||
{
|
||||
int8_t type;
|
||||
int32_t version;
|
||||
int64_t path;
|
||||
} V9fsQID;
|
||||
|
||||
typedef struct V9fsStat
|
||||
{
|
||||
int16_t size;
|
||||
int16_t type;
|
||||
int32_t dev;
|
||||
V9fsQID qid;
|
||||
int32_t mode;
|
||||
int32_t atime;
|
||||
int32_t mtime;
|
||||
int64_t length;
|
||||
V9fsString name;
|
||||
V9fsString uid;
|
||||
V9fsString gid;
|
||||
V9fsString muid;
|
||||
/* 9p2000.u */
|
||||
V9fsString extension;
|
||||
int32_t n_uid;
|
||||
int32_t n_gid;
|
||||
int32_t n_muid;
|
||||
} V9fsStat;
|
||||
|
||||
struct V9fsFidState
|
||||
{
|
||||
int32_t fid;
|
||||
V9fsString path;
|
||||
int fd;
|
||||
DIR *dir;
|
||||
uid_t uid;
|
||||
V9fsFidState *next;
|
||||
};
|
||||
|
||||
typedef struct V9fsState
|
||||
{
|
||||
VirtIODevice vdev;
|
||||
VirtQueue *vq;
|
||||
V9fsPDU pdus[MAX_REQ];
|
||||
QLIST_HEAD(, V9fsPDU) free_list;
|
||||
V9fsFidState *fid_list;
|
||||
FileOperations *ops;
|
||||
FsContext ctx;
|
||||
uint16_t tag_len;
|
||||
uint8_t *tag;
|
||||
size_t config_size;
|
||||
} V9fsState;
|
||||
|
||||
struct virtio_9p_config
|
||||
{
|
||||
/* number of characters in tag */
|
||||
uint16_t tag_len;
|
||||
/* Variable size tag name */
|
||||
uint8_t tag[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
extern size_t pdu_packunpack(void *addr, struct iovec *sg, int sg_count,
|
||||
size_t offset, size_t size, int pack);
|
||||
|
||||
static inline size_t do_pdu_unpack(void *dst, struct iovec *sg, int sg_count,
|
||||
size_t offset, size_t size)
|
||||
{
|
||||
return pdu_packunpack(dst, sg, sg_count, offset, size, 0);
|
||||
}
|
||||
|
||||
#endif
|
@ -102,6 +102,9 @@ typedef struct {
|
||||
BlockConf block;
|
||||
NICConf nic;
|
||||
uint32_t host_features;
|
||||
#ifdef CONFIG_LINUX
|
||||
V9fsConf fsconf;
|
||||
#endif
|
||||
/* Max. number of ports we can have for a the virtio-serial device */
|
||||
uint32_t max_virtserial_ports;
|
||||
} VirtIOPCIProxy;
|
||||
@ -639,6 +642,23 @@ static int virtio_balloon_init_pci(PCIDevice *pci_dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LINUX
|
||||
static int virtio_9p_init_pci(PCIDevice *pci_dev)
|
||||
{
|
||||
VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
|
||||
VirtIODevice *vdev;
|
||||
|
||||
vdev = virtio_9p_init(&pci_dev->qdev, &proxy->fsconf);
|
||||
virtio_init_pci(proxy, vdev,
|
||||
PCI_VENDOR_ID_REDHAT_QUMRANET,
|
||||
0x1009,
|
||||
0x2,
|
||||
0x00);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static PCIDeviceInfo virtio_info[] = {
|
||||
{
|
||||
.qdev.name = "virtio-blk-pci",
|
||||
@ -693,6 +713,18 @@ static PCIDeviceInfo virtio_info[] = {
|
||||
},
|
||||
.qdev.reset = virtio_pci_reset,
|
||||
},{
|
||||
#ifdef CONFIG_LINUX
|
||||
.qdev.name = "virtio-9p-pci",
|
||||
.qdev.size = sizeof(VirtIOPCIProxy),
|
||||
.init = virtio_9p_init_pci,
|
||||
.qdev.props = (Property[]) {
|
||||
DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
|
||||
DEFINE_PROP_STRING("mount_tag", VirtIOPCIProxy, fsconf.tag),
|
||||
DEFINE_PROP_STRING("fsdev", VirtIOPCIProxy, fsconf.fsdev_id),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
},
|
||||
}, {
|
||||
#endif
|
||||
/* end of list */
|
||||
}
|
||||
};
|
||||
|
@ -20,6 +20,9 @@
|
||||
#include "sysemu.h"
|
||||
#include "block_int.h"
|
||||
#include "event_notifier.h"
|
||||
#ifdef CONFIG_LINUX
|
||||
#include "9p.h"
|
||||
#endif
|
||||
|
||||
/* from Linux's linux/virtio_config.h */
|
||||
|
||||
@ -185,6 +188,10 @@ VirtIODevice *virtio_blk_init(DeviceState *dev, BlockConf *conf);
|
||||
VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf);
|
||||
VirtIODevice *virtio_serial_init(DeviceState *dev, uint32_t max_nr_ports);
|
||||
VirtIODevice *virtio_balloon_init(DeviceState *dev);
|
||||
#ifdef CONFIG_LINUX
|
||||
VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf);
|
||||
#endif
|
||||
|
||||
|
||||
void virtio_net_exit(VirtIODevice *vdev);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user