On Fri, Sep 16, 2016 at 07:05:47PM +0900, Namhyung Kim wrote: > On Tue, Sep 13, 2016 at 06:57:10PM +0300, Michael S. Tsirkin wrote: > > On Sat, Aug 20, 2016 at 05:07:43PM +0900, Namhyung Kim wrote: > > > Add virtio pstore device to allow kernel log files saved on the host. > > > It will save the log files on the directory given by pstore device > > > option. > > > > > > $ qemu-system-x86_64 -device virtio-pstore,directory=dir-xx ... > > > > > > (guest) # echo c > /proc/sysrq-trigger > > > > > > $ ls dir-xx > > > dmesg-1.enc.z dmesg-2.enc.z > > > > > > The log files are usually compressed using zlib. Users can see the log > > > messages directly on the host or on the guest (using pstore filesystem). > > > > > > The 'directory' property is required for virtio-pstore device to work. > > > It also adds 'bufsize' property to set size of pstore bufer. > > > > > > Cc: Paolo Bonzini <pbonzini@xxxxxxxxxx> > > > Cc: Radim Krčmář <rkrcmar@xxxxxxxxxx> > > > Cc: "Michael S. Tsirkin" <mst@xxxxxxxxxx> > > > Cc: Anthony Liguori <aliguori@xxxxxxxxxx> > > > Cc: Anton Vorontsov <anton@xxxxxxxxxx> > > > Cc: Colin Cross <ccross@xxxxxxxxxxx> > > > Cc: Kees Cook <keescook@xxxxxxxxxxxx> > > > Cc: Tony Luck <tony.luck@xxxxxxxxx> > > > Cc: Steven Rostedt <rostedt@xxxxxxxxxxx> > > > Cc: Ingo Molnar <mingo@xxxxxxxxxx> > > > Cc: Minchan Kim <minchan@xxxxxxxxxx> > > > Cc: Daniel P. Berrange <berrange@xxxxxxxxxx> > > > Cc: kvm@xxxxxxxxxxxxxxx > > > Cc: qemu-devel@xxxxxxxxxx > > > Cc: virtualization@xxxxxxxxxxxxxxxxxxxxxxxxxx > > > Signed-off-by: Namhyung Kim <namhyung@xxxxxxxxxx> > > > --- > > > hw/virtio/Makefile.objs | 2 +- > > > hw/virtio/virtio-pci.c | 52 ++ > > > hw/virtio/virtio-pci.h | 14 + > > > hw/virtio/virtio-pstore.c | 699 +++++++++++++++++++++++++ > > > include/hw/pci/pci.h | 1 + > > > include/hw/virtio/virtio-pstore.h | 36 ++ > > > include/standard-headers/linux/virtio_ids.h | 1 + > > > include/standard-headers/linux/virtio_pstore.h | 76 +++ > > > qdev-monitor.c | 1 + > > > 9 files changed, 881 insertions(+), 1 deletion(-) > > > create mode 100644 hw/virtio/virtio-pstore.c > > > create mode 100644 include/hw/virtio/virtio-pstore.h > > > create mode 100644 include/standard-headers/linux/virtio_pstore.h > > > > > > diff --git a/hw/virtio/Makefile.objs b/hw/virtio/Makefile.objs > > > index 3e2b175..aae7082 100644 > > > --- a/hw/virtio/Makefile.objs > > > +++ b/hw/virtio/Makefile.objs > > > @@ -4,4 +4,4 @@ common-obj-y += virtio-bus.o > > > common-obj-y += virtio-mmio.o > > > > > > obj-y += virtio.o virtio-balloon.o > > > -obj-$(CONFIG_LINUX) += vhost.o vhost-backend.o vhost-user.o > > > +obj-$(CONFIG_LINUX) += vhost.o vhost-backend.o vhost-user.o virtio-pstore.o > > > diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c > > > index 755f921..c184823 100644 > > > --- a/hw/virtio/virtio-pci.c > > > +++ b/hw/virtio/virtio-pci.c > > > @@ -2416,6 +2416,57 @@ static const TypeInfo virtio_host_pci_info = { > > > }; > > > #endif > > > > > > +/* virtio-pstore-pci */ > > > + > > > +static void virtio_pstore_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) > > > +{ > > > + VirtIOPstorePCI *vps = VIRTIO_PSTORE_PCI(vpci_dev); > > > + DeviceState *vdev = DEVICE(&vps->vdev); > > > + Error *err = NULL; > > > + > > > + qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); > > > + object_property_set_bool(OBJECT(vdev), true, "realized", &err); > > > + if (err) { > > > + error_propagate(errp, err); > > > + return; > > > + } > > > +} > > > + > > > +static void virtio_pstore_pci_class_init(ObjectClass *klass, void *data) > > > +{ > > > + DeviceClass *dc = DEVICE_CLASS(klass); > > > + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); > > > + PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); > > > + > > > + k->realize = virtio_pstore_pci_realize; > > > + set_bit(DEVICE_CATEGORY_MISC, dc->categories); > > > + > > > + pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; > > > + pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_PSTORE; > > > + pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; > > > + pcidev_k->class_id = PCI_CLASS_OTHERS; > > > +} > > > + > > > +static void virtio_pstore_pci_instance_init(Object *obj) > > > +{ > > > + VirtIOPstorePCI *dev = VIRTIO_PSTORE_PCI(obj); > > > + > > > + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), > > > + TYPE_VIRTIO_PSTORE); > > > + object_property_add_alias(obj, "directory", OBJECT(&dev->vdev), > > > + "directory", &error_abort); > > > + object_property_add_alias(obj, "bufsize", OBJECT(&dev->vdev), > > > + "bufsize", &error_abort); > > > +} > > > + > > > +static const TypeInfo virtio_pstore_pci_info = { > > > + .name = TYPE_VIRTIO_PSTORE_PCI, > > > + .parent = TYPE_VIRTIO_PCI, > > > + .instance_size = sizeof(VirtIOPstorePCI), > > > + .instance_init = virtio_pstore_pci_instance_init, > > > + .class_init = virtio_pstore_pci_class_init, > > > +}; > > > + > > > /* virtio-pci-bus */ > > > > > > static void virtio_pci_bus_new(VirtioBusState *bus, size_t bus_size, > > > @@ -2485,6 +2536,7 @@ static void virtio_pci_register_types(void) > > > #ifdef CONFIG_VHOST_SCSI > > > type_register_static(&vhost_scsi_pci_info); > > > #endif > > > + type_register_static(&virtio_pstore_pci_info); > > > } > > > > > > type_init(virtio_pci_register_types) > > > diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h > > > index 25fbf8a..354b2b7 100644 > > > --- a/hw/virtio/virtio-pci.h > > > +++ b/hw/virtio/virtio-pci.h > > > @@ -31,6 +31,7 @@ > > > #ifdef CONFIG_VHOST_SCSI > > > #include "hw/virtio/vhost-scsi.h" > > > #endif > > > +#include "hw/virtio/virtio-pstore.h" > > > > > > typedef struct VirtIOPCIProxy VirtIOPCIProxy; > > > typedef struct VirtIOBlkPCI VirtIOBlkPCI; > > > @@ -44,6 +45,7 @@ typedef struct VirtIOInputPCI VirtIOInputPCI; > > > typedef struct VirtIOInputHIDPCI VirtIOInputHIDPCI; > > > typedef struct VirtIOInputHostPCI VirtIOInputHostPCI; > > > typedef struct VirtIOGPUPCI VirtIOGPUPCI; > > > +typedef struct VirtIOPstorePCI VirtIOPstorePCI; > > > > > > /* virtio-pci-bus */ > > > > > > @@ -324,6 +326,18 @@ struct VirtIOGPUPCI { > > > VirtIOGPU vdev; > > > }; > > > > > > +/* > > > + * virtio-pstore-pci: This extends VirtioPCIProxy. > > > + */ > > > +#define TYPE_VIRTIO_PSTORE_PCI "virtio-pstore-pci" > > > +#define VIRTIO_PSTORE_PCI(obj) \ > > > + OBJECT_CHECK(VirtIOPstorePCI, (obj), TYPE_VIRTIO_PSTORE_PCI) > > > + > > > +struct VirtIOPstorePCI { > > > + VirtIOPCIProxy parent_obj; > > > + VirtIOPstore vdev; > > > +}; > > > + > > > /* Virtio ABI version, if we increment this, we break the guest driver. */ > > > #define VIRTIO_PCI_ABI_VERSION 0 > > > > > > diff --git a/hw/virtio/virtio-pstore.c b/hw/virtio/virtio-pstore.c > > > new file mode 100644 > > > index 0000000..b8fb4be > > > --- /dev/null > > > +++ b/hw/virtio/virtio-pstore.c > > > @@ -0,0 +1,699 @@ > > > +/* > > > + * Virtio Pstore Device > > > + * > > > + * Copyright (C) 2016 LG Electronics > > > + * > > > + * Authors: > > > + * Namhyung Kim <namhyung@xxxxxxxxx> > > > + * > > > + * This work is licensed under the terms of the GNU GPL, version 2 or later. > > > + * See the COPYING file in the top-level directory. > > > + * > > > + */ > > > + > > > +#include <stdio.h> > > > + > > > +#include "qemu/osdep.h" > > > +#include "qemu/iov.h" > > > +#include "qemu-common.h" > > > +#include "qemu/cutils.h" > > > +#include "qemu/error-report.h" > > > +#include "sysemu/kvm.h" > > > +#include "qapi/visitor.h" > > > +#include "qapi-event.h" > > > +#include "io/channel-util.h" > > > +#include "trace.h" > > > + > > > +#include "hw/virtio/virtio.h" > > > +#include "hw/virtio/virtio-bus.h" > > > +#include "hw/virtio/virtio-access.h" > > > +#include "hw/virtio/virtio-pstore.h" > > > + > > > +#define PSTORE_DEFAULT_BUFSIZE (16 * 1024) > > > +#define PSTORE_DEFAULT_FILE_MAX 5 > > > + > > > +/* the index should match to the type value */ > > > +static const char *virtio_pstore_file_prefix[] = { > > > + "unknown-", /* VIRTIO_PSTORE_TYPE_UNKNOWN */ > > > > Is there value in treating everything unexpected as "unknown" > > and rotating them as if they were logs? > > It might be better to treat everything that's not known > > as guest error. > > I was thinking about the version mismatch between the kernel and qemu. > I'd like to make the device can deal with a new kernel version which > might implement a new pstore message type. It will be saved as > unknown but the kernel can read it properly later. Well it'll have a different prefix. E.g. if kernel has two different types they will end up in the same file, hardly what was wanted. > > > > > > > + "dmesg-", /* VIRTIO_PSTORE_TYPE_DMESG */ > > > > use named initializers for this instead of comments. > > Ok. > > > > > > +}; > > > + > > > +static char *virtio_pstore_to_filename(VirtIOPstore *s, > > > + struct virtio_pstore_req *req) > > > +{ > > > + const char *basename; > > > + unsigned long long id; > > > + unsigned int type = le16_to_cpu(req->type); > > > + unsigned int flags = le32_to_cpu(req->flags); > > > + > > > + if (type < ARRAY_SIZE(virtio_pstore_file_prefix)) { > > > + basename = virtio_pstore_file_prefix[type]; > > > + } else { > > > + basename = "unknown-"; > > > + } > > > + > > > + id = s->id++; > > > + return g_strdup_printf("%s/%s%llu%s", s->directory, basename, id, > > > + flags & VIRTIO_PSTORE_FL_COMPRESSED ? ".enc.z" : ""); > > > +} > > > + > > > +static char *virtio_pstore_from_filename(VirtIOPstore *s, char *name, > > > + struct virtio_pstore_fileinfo *info) > > > +{ > > > + char *filename; > > > + unsigned int idx; > > > + > > > + filename = g_strdup_printf("%s/%s", s->directory, name); > > > + if (filename == NULL) > > > + return NULL; > > > + > > > + for (idx = 0; idx < ARRAY_SIZE(virtio_pstore_file_prefix); idx++) { > > > + if (g_str_has_prefix(name, virtio_pstore_file_prefix[idx])) { > > > + info->type = idx; > > > + name += strlen(virtio_pstore_file_prefix[idx]); > > > + break; > > > + } > > > + } > > > + > > > + if (idx == ARRAY_SIZE(virtio_pstore_file_prefix)) { > > > + g_free(filename); > > > + return NULL; > > > + } > > > + > > > + qemu_strtoull(name, NULL, 0, &info->id); > > > > What if this fails? > > Hmm.. will add a check for return value then. > > > > > > + > > > + info->flags = 0; > > > + if (g_str_has_suffix(name, ".enc.z")) { > > > + info->flags |= VIRTIO_PSTORE_FL_COMPRESSED; > > > + } > > > + > > > + return filename; > > > +} > > > + > > > +static int prefix_idx; > > > +static int prefix_count; > > > +static int prefix_len; > > This does not work properly if there are multiple instances > > of it. Pls move everything into device state. > > Kernel (currently?) allows only a single pstore device active. But I > think it'd be better to move them into device state anyway. > > > > > > + > > > +static int filter_pstore(const struct dirent *de) > > > +{ > > > + int i; > > > + > > > + for (i = 0; i < prefix_count; i++) { > > > + const char *prefix = virtio_pstore_file_prefix[prefix_idx + i]; > > > + > > > + if (g_str_has_prefix(de->d_name, prefix)) { > > > + return 1; > > > + } > > > + } > > > + return 0; > > > +} > > > + > > > +static int sort_pstore(const struct dirent **a, const struct dirent **b) > > > +{ > > > + uint64_t id_a, id_b; > > > + > > > + qemu_strtoull((*a)->d_name + prefix_len, NULL, 0, &id_a); > > > + qemu_strtoull((*b)->d_name + prefix_len, NULL, 0, &id_b); > > > + > > > + return id_a - id_b; > > > +} > > > + > > > +static int rotate_pstore_file(VirtIOPstore *s, unsigned short type) > > > +{ > > > + int ret = 0; > > > + int i, num; > > > + char *filename; > > > + struct dirent **files; > > > + > > > + if (type >= ARRAY_SIZE(virtio_pstore_file_prefix)) { > > > + type = VIRTIO_PSTORE_TYPE_UNKNOWN; > > > + } > > > + > > > + prefix_idx = type; > > > + prefix_len = strlen(virtio_pstore_file_prefix[type]); > > > + prefix_count = 1; /* only scan current type */ > > > + > > > + /* delete the oldest file in the same type */ > > > + num = scandir(s->directory, &files, filter_pstore, sort_pstore); > > > + if (num < 0) > > > + return num; > > > + if (num < (int)s->file_max) > > > + goto out; > > > + > > > + filename = g_strdup_printf("%s/%s", s->directory, files[0]->d_name); > > > + if (filename == NULL) { > > > + ret = -1; > > > + goto out; > > > + } > > > + > > > + ret = unlink(filename); > > > + > > > +out: > > > + for (i = 0; i < num; i++) { > > > + g_free(files[i]); > > > + } > > > + g_free(files); > > > + > > > + return ret; > > > +} > > > > Pls prefix everything with virtio_pstore or another > > unique prefix. also below. > > Ok. > > > > > > + > > > +static ssize_t virtio_pstore_do_open(VirtIOPstore *s) > > > +{ > > > + /* scan all pstore files */ > > > + prefix_idx = 0; > > > + prefix_count = ARRAY_SIZE(virtio_pstore_file_prefix); > > > + > > > + s->file_idx = 0; > > > + s->num_file = scandir(s->directory, &s->files, filter_pstore, alphasort); > > > + > > > + return s->num_file >= 0 ? 0 : -1; > > > +} > > > + > > > +static ssize_t virtio_pstore_do_close(VirtIOPstore *s) > > > +{ > > > + int i; > > > + > > > + for (i = 0; i < s->num_file; i++) { > > > + g_free(s->files[i]); > > > + } > > > + g_free(s->files); > > > + s->files = NULL; > > > + > > > + s->num_file = 0; > > > + return 0; > > > +} > > > + > > > +static ssize_t virtio_pstore_do_erase(VirtIOPstore *s, > > > + struct virtio_pstore_req *req) > > > +{ > > > + char *filename; > > > + int ret; > > > + > > > + filename = virtio_pstore_to_filename(s, req); > > > + if (filename == NULL) > > > + return -1; > > > > this can't happen. > > Why? The virtio_pstore_to_filename() calls g_strdup_printf(). That > means I don't need to worry about the memory allocation failure? > > > > > also this is a coding style violation. > > Oh, I missed the add {}, will fix. > > > > > > + > > > + ret = unlink(filename); > > > + > > > + g_free(filename); > > > + return ret; > > > +} > > > + > > > +struct pstore_read_arg { > > > + VirtIOPstore *vps; > > > + VirtQueueElement *elem; > > > + struct virtio_pstore_fileinfo info; > > > + QIOChannel *ioc; > > > +}; > > > + > > > +static gboolean pstore_async_read_fn(QIOChannel *ioc, GIOCondition condition, > > > + gpointer data) > > > +{ > > > + struct pstore_read_arg *rarg = data; > > > + struct virtio_pstore_fileinfo *info = &rarg->info; > > > + VirtIOPstore *vps = rarg->vps; > > > + VirtQueueElement *elem = rarg->elem; > > > + struct virtio_pstore_res res; > > > + size_t offset = sizeof(res) + sizeof(*info); > > > + struct iovec *sg = elem->in_sg; > > > + unsigned int sg_num = elem->in_num; > > > + Error *err = NULL; > > > + ssize_t len; > > > + int ret; > > > + > > > + /* skip res and fileinfo */ > > > + iov_discard_front(&sg, &sg_num, sizeof(res) + sizeof(*info)); > > > + > > > + len = qio_channel_readv(rarg->ioc, sg, sg_num, &err); > > > + if (len < 0) { > > > + if (errno == EAGAIN) { > > > + len = 0; > > > + } > > > + ret = -1; > > > + } else { > > > + info->len = cpu_to_le32(len); > > > + ret = 0; > > > + } > > > + > > > + res.cmd = cpu_to_le16(VIRTIO_PSTORE_CMD_READ); > > > + res.type = cpu_to_le16(VIRTIO_PSTORE_TYPE_UNKNOWN); > > > + res.ret = cpu_to_le32(ret); > > > + > > > + /* now copy res and fileinfo */ > > > + iov_from_buf(elem->in_sg, elem->in_num, 0, &res, sizeof(res)); > > > + iov_from_buf(elem->in_sg, elem->in_num, sizeof(res), info, sizeof(*info)); > > > + > > > + len += offset; > > > + virtqueue_push(vps->rvq, elem, len); > > > + virtio_notify(VIRTIO_DEVICE(vps), vps->rvq); > > > + > > > + return G_SOURCE_REMOVE; > > > +} > > > + > > > +static void free_rarg_fn(gpointer data) > > > +{ > > > + struct pstore_read_arg *rarg = data; > > > + > > > + qio_channel_close(rarg->ioc, NULL); > > > + > > > + g_free(rarg->elem); > > > + g_free(rarg); > > > +} > > > + > > > +static ssize_t virtio_pstore_do_read(VirtIOPstore *s, VirtQueueElement *elem) > > > +{ > > > + char *filename = NULL; > > > + int fd, idx; > > > + struct stat stbuf; > > > + struct pstore_read_arg *rarg = NULL; > > > + Error *err = NULL; > > > + int ret = -1; > > > + > > > + if (s->file_idx >= s->num_file) { > > > + return 0; > > > + } > > > + > > > + rarg = g_malloc(sizeof(*rarg)); > > > + if (rarg == NULL) { > > > + return -1; > > > + } > > > + > > > + idx = s->file_idx++; > > > + filename = virtio_pstore_from_filename(s, s->files[idx]->d_name, > > > + &rarg->info); > > > + if (filename == NULL) { > > > + goto out; > > > + } > > > + > > > + fd = open(filename, O_RDONLY); > > > + if (fd < 0) { > > > + error_report("cannot open %s", filename); > > > + goto out; > > > + } > > > > I see open here but close nowhere. Does this leak fds? > > I guess so. But this is changed to use qio_channel_file API in v5 and > I hope doing it right. > > > > > > + > > > + if (fstat(fd, &stbuf) < 0) { > > > > So we can stat, but can we e.g. read? > > It's just being a paranoid, I think it should succeed, no? > > > > > > > > + goto out; > > > + } > > > + > > > + rarg->vps = s; > > > + rarg->elem = elem; > > > + rarg->info.id = cpu_to_le64(rarg->info.id); > > > + rarg->info.type = cpu_to_le16(rarg->info.type); > > > + rarg->info.flags = cpu_to_le32(rarg->info.flags); > > > + rarg->info.time_sec = cpu_to_le64(stbuf.st_ctim.tv_sec); > > > > Is this seconds since epoch? > > Why ctim specifically? > > Pls add comments. > > I think it doesn't matter either ctim or mtim. > > > > > > + rarg->info.time_nsec = cpu_to_le32(stbuf.st_ctim.tv_nsec); > > > > Not all hosts support nanosecond precision. > > Do we need some way to tell guest what's reliable? > > In fact I'm not sure how much it affects users. The pstore messages > are occasional and AFAIK pstore keeps it only for users' information. > > > > > Unless you limit this to linux host, you should care about things > > like this (in man fstat) > > > > Since kernel 2.5.48, the stat structure supports nanosecond > > resolution for the three file timestamp fields. The nanosecond compo‐ > > nents of each timestamp are available via names of the form > > st_atim.tv_nsec if the _BSD_SOURCE or _SVID_SOURCE feature test macro > > is defined. Nanosecond timestamps are nowadays standardized, > > starting with POSIX.1-2008, and, starting with version 2.12, glibc also > > exposes the nanosecond component names if _POSIX_C_SOURCE is defined > > with the value 200809L or greater, or _XOPEN_SOURCE is defined with > > the value 700 or greater. If none of the aforementioned macros are > > defined, then the nanosecond values are exposed with names of the form > > st_atimensec. > > Thanks for the info. > > > > > > > > > > > > + > > > + rarg->ioc = qio_channel_new_fd(fd, &err); > > > + if (err) { > > > + error_reportf_err(err, "cannot create io channel: "); > > > + goto out; > > > + } > > > + > > > + qio_channel_set_blocking(rarg->ioc, false, &err); > > > + qio_channel_add_watch(rarg->ioc, G_IO_IN, pstore_async_read_fn, rarg, > > > + free_rarg_fn); > > > + g_free(filename); > > > + return 1; > > > + > > > +out: > > > + g_free(filename); > > > + g_free(rarg); > > > + > > > + return ret; > > > +} > > > + > > > +struct pstore_write_arg { > > > + VirtIOPstore *vps; > > > + VirtQueueElement *elem; > > > + struct virtio_pstore_req *req; > > > + QIOChannel *ioc; > > > +}; > > > + > > > +static gboolean pstore_async_write_fn(QIOChannel *ioc, GIOCondition condition, > > > + gpointer data) > > > +{ > > > + struct pstore_write_arg *warg = data; > > > + VirtIOPstore *vps = warg->vps; > > > + VirtQueueElement *elem = warg->elem; > > > + struct iovec *sg = elem->out_sg; > > > + unsigned int sg_num = elem->out_num; > > > + struct virtio_pstore_res res; > > > + Error *err = NULL; > > > + ssize_t len; > > > + int ret; > > > + > > > + /* we already consumed the req */ > > > + iov_discard_front(&sg, &sg_num, sizeof(*warg->req)); > > > + > > > + len = qio_channel_writev(warg->ioc, sg, sg_num, &err); > > > + if (len < 0) { > > > + ret = -1; > > > + } else { > > > + ret = 0; > > > + } > > > > This can discard part of the data written. > > Don't we care? > > Doing partial write is better than failing out. But if it's > meaningful to add a retry loop, I'd like to do so. > > > > > > + > > > + res.cmd = cpu_to_le16(VIRTIO_PSTORE_CMD_WRITE); > > > + res.type = warg->req->type; > > > + res.ret = cpu_to_le32(ret); > > > + > > > + /* tell the result to guest */ > > > + iov_from_buf(elem->in_sg, elem->in_num, 0, &res, sizeof(res)); > > > + > > > + virtqueue_push(vps->wvq, elem, sizeof(res)); > > > + virtio_notify(VIRTIO_DEVICE(vps), vps->wvq); > > > + > > > + return G_SOURCE_REMOVE; > > > +} > > > + > > > +static void free_warg_fn(gpointer data) > > > +{ > > > + struct pstore_write_arg *warg = data; > > > + > > > + qio_channel_close(warg->ioc, NULL); > > > + > > > + g_free(warg->elem); > > > + g_free(warg); > > > +} > > > + > > > +static ssize_t virtio_pstore_do_write(VirtIOPstore *s, VirtQueueElement *elem, > > > + struct virtio_pstore_req *req) > > > +{ > > > + unsigned short type = le16_to_cpu(req->type); > > > + char *filename = NULL; > > > + int fd; > > > + int flags = O_WRONLY | O_CREAT | O_TRUNC; > > > + struct pstore_write_arg *warg = NULL; > > > + Error *err = NULL; > > > + int ret = -1; > > > + > > > + /* do not keep same type of files more than 'file-max' */ > > > + rotate_pstore_file(s, type); > > > > If you don't care about failures, should this function > > return a value? How about reporting it to the user? > > Did you mean when it failed to delete the oldest file (FYI it's not > really 'rotate'). Hmm.. will add error check and report. > > > > > > > > + > > > + filename = virtio_pstore_to_filename(s, req); > > > + if (filename == NULL) { > > > + return -1; > > > + } > > > > this can't happen > > > > > + > > > + warg = g_malloc(sizeof(*warg)); > > > + if (warg == NULL) { > > > + goto out; > > > + } > > > + > > > + fd = open(filename, flags, 0644); > > > + if (fd < 0) { > > > + error_report("cannot open %s", filename); > > > + ret = fd; > > > + goto out; > > > + } > > > + > > > + warg->vps = s; > > > + warg->elem = elem; > > > + warg->req = req; > > > + > > > + warg->ioc = qio_channel_new_fd(fd, &err); > > > + if (err) { > > > + error_reportf_err(err, "cannot create io channel: "); > > > + goto out; > > > + } > > > + > > > + qio_channel_set_blocking(warg->ioc, false, &err); > > > + qio_channel_add_watch(warg->ioc, G_IO_OUT, pstore_async_write_fn, warg, > > > + free_warg_fn); > > > + g_free(filename); > > > + return 1; > > > + > > > +out: > > > + g_free(filename); > > > + g_free(warg); > > > + return ret; > > > +} > > > + > > > +static void virtio_pstore_handle_io(VirtIODevice *vdev, VirtQueue *vq) > > > +{ > > > + VirtIOPstore *s = VIRTIO_PSTORE(vdev); > > > + VirtQueueElement *elem; > > > + struct virtio_pstore_req req; > > > + struct virtio_pstore_res res; > > > + ssize_t len = 0; > > > + int ret; > > > + > > > + for (;;) { > > > + elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); > > > + if (!elem) { > > > + return; > > > + } > > > + > > > + if (elem->out_num < 1 || elem->in_num < 1) { > > > + error_report("request or response buffer is missing"); > > > + exit(1); > > > + } > > > + > > > + if (elem->out_num > 2 || elem->in_num > 3) { > > > + error_report("invalid number of input/output buffer"); > > > + exit(1); > > > + } > > > + > > > + len = iov_to_buf(elem->out_sg, elem->out_num, 0, &req, sizeof(req)); > > > + if (len != (ssize_t)sizeof(req)) { > > > + error_report("invalid request size: %ld", (long)len); > > > + exit(1); > > > + } > > > + res.cmd = req.cmd; > > > + res.type = req.type; > > > + > > > + switch (le16_to_cpu(req.cmd)) { > > > + case VIRTIO_PSTORE_CMD_OPEN: > > > + ret = virtio_pstore_do_open(s); > > > + break; > > > + case VIRTIO_PSTORE_CMD_CLOSE: > > > + ret = virtio_pstore_do_close(s); > > > + break; > > > + case VIRTIO_PSTORE_CMD_ERASE: > > > + ret = virtio_pstore_do_erase(s, &req); > > > + break; > > > + case VIRTIO_PSTORE_CMD_READ: > > > + ret = virtio_pstore_do_read(s, elem); > > > + if (ret == 1) { > > > + /* async channel io */ > > > + continue; > > > + } > > > + break; > > > + case VIRTIO_PSTORE_CMD_WRITE: > > > + ret = virtio_pstore_do_write(s, elem, &req); > > > + if (ret == 1) { > > > + /* async channel io */ > > > + continue; > > > + } > > > + break; > > > + default: > > > + ret = -1; > > > + break; > > > + } > > > + > > > + res.ret = ret; > > > + > > > + iov_from_buf(elem->in_sg, elem->in_num, 0, &res, sizeof(res)); > > > + virtqueue_push(vq, elem, sizeof(res) + len); > > > + > > > + virtio_notify(vdev, vq); > > > + g_free(elem); > > > + > > > + if (ret < 0) { > > > + return; > > > > what does this do? > > If it failed on any processing, reports it to the kernel and stop > processing later commands. The kernel won't send same kind of command > later. > > > > > > + } > > > + } > > > +} > > > + > > > +static void virtio_pstore_device_realize(DeviceState *dev, Error **errp) > > > +{ > > > + VirtIODevice *vdev = VIRTIO_DEVICE(dev); > > > + VirtIOPstore *s = VIRTIO_PSTORE(dev); > > > + > > > + virtio_init(vdev, "virtio-pstore", VIRTIO_ID_PSTORE, > > > + sizeof(struct virtio_pstore_config)); > > > + > > > + s->id = 1; > > > + > > > + if (!s->bufsize) > > > + s->bufsize = PSTORE_DEFAULT_BUFSIZE; > > > + if (!s->file_max) > > > + s->file_max = PSTORE_DEFAULT_FILE_MAX; > > > + > > > + s->rvq = virtio_add_queue(vdev, 128, virtio_pstore_handle_io); > > > + s->wvq = virtio_add_queue(vdev, 128, virtio_pstore_handle_io); > > > +} > > > + > > > +static void virtio_pstore_device_unrealize(DeviceState *dev, Error **errp) > > > +{ > > > + VirtIODevice *vdev = VIRTIO_DEVICE(dev); > > > + > > > + virtio_cleanup(vdev); > > > +} > > > + > > > +static void virtio_pstore_get_config(VirtIODevice *vdev, uint8_t *config_data) > > > +{ > > > + VirtIOPstore *dev = VIRTIO_PSTORE(vdev); > > > + struct virtio_pstore_config config; > > > > Add {} here - you want all fields initialized > > if you add them, to avoid leaking them to guest. > > Ok. > > > > > > + > > > + config.bufsize = cpu_to_le32(dev->bufsize); > > > + > > > + memcpy(config_data, &config, sizeof(struct virtio_pstore_config)); > > > +} > > > + > > > +static void virtio_pstore_set_config(VirtIODevice *vdev, > > > + const uint8_t *config_data) > > > +{ > > > + VirtIOPstore *dev = VIRTIO_PSTORE(vdev); > > > + struct virtio_pstore_config config; > > > + > > > + memcpy(&config, config_data, sizeof(struct virtio_pstore_config)); > > > + > > > + dev->bufsize = le32_to_cpu(config.bufsize); > > > +} > > > + > > > +static uint64_t get_features(VirtIODevice *vdev, uint64_t f, Error **errp) > > > +{ > > > + return f; > > > +} > > > + > > > +static void pstore_get_directory(Object *obj, Visitor *v, > > > + const char *name, void *opaque, > > > + Error **errp) > > > +{ > > > + VirtIOPstore *s = opaque; > > > + > > > + visit_type_str(v, name, &s->directory, errp); > > > +} > > > + > > > +static void pstore_set_directory(Object *obj, Visitor *v, > > > + const char *name, void *opaque, > > > + Error **errp) > > > +{ > > > + VirtIOPstore *s = opaque; > > > + Error *local_err = NULL; > > > + char *value; > > > + > > > + visit_type_str(v, name, &value, &local_err); > > > + if (local_err) { > > > + error_propagate(errp, local_err); > > > + return; > > > + } > > > + > > > + g_free(s->directory); > > > + s->directory = value; > > > +} > > > + > > > +static void pstore_release_directory(Object *obj, const char *name, > > > + void *opaque) > > > +{ > > > + VirtIOPstore *s = opaque; > > > + > > > + g_free(s->directory); > > > + s->directory = NULL; > > > +} > > > + > > > +static void pstore_get_bufsize(Object *obj, Visitor *v, > > > + const char *name, void *opaque, > > > + Error **errp) > > > +{ > > > + VirtIOPstore *s = opaque; > > > + uint64_t value = s->bufsize; > > > + > > > + visit_type_size(v, name, &value, errp); > > > +} > > > + > > > +static void pstore_set_bufsize(Object *obj, Visitor *v, > > > + const char *name, void *opaque, > > > + Error **errp) > > > +{ > > > + VirtIOPstore *s = opaque; > > > + Error *error = NULL; > > > + uint64_t value; > > > + > > > + visit_type_size(v, name, &value, &error); > > > + if (error) { > > > + error_propagate(errp, error); > > > + return; > > > + } > > > + > > > + if (value < 4096) { > > > + error_setg(&error, "Warning: too small buffer size: %"PRIu64, value); > > > + error_propagate(errp, error); > > > + return; > > > + } > > > + > > > + s->bufsize = value; > > > +} > > > + > > > +static void pstore_get_file_max(Object *obj, Visitor *v, > > > + const char *name, void *opaque, > > > + Error **errp) > > > +{ > > > + VirtIOPstore *s = opaque; > > > + int64_t value = s->file_max; > > > + > > > + visit_type_int(v, name, &value, errp); > > > +} > > > + > > > +static void pstore_set_file_max(Object *obj, Visitor *v, > > > + const char *name, void *opaque, > > > + Error **errp) > > > +{ > > > + VirtIOPstore *s = opaque; > > > + Error *error = NULL; > > > + int64_t value; > > > + > > > + visit_type_int(v, name, &value, &error); > > > + if (error) { > > > + error_propagate(errp, error); > > > + return; > > > + } > > > + > > > + s->file_max = value; > > > +} > > > > Do you need dynamic properties? There are easier ways > > to define an int property. Same for others. > > It was due to my insufficient knowledge about the qemu code base. I > don't think it needs to be dynamic. > > Thanks, > Namhyung > > > > > > + > > > +static Property virtio_pstore_properties[] = { > > > + DEFINE_PROP_END_OF_LIST(), > > > +}; > > > + > > > +static void virtio_pstore_instance_init(Object *obj) > > > +{ > > > + VirtIOPstore *s = VIRTIO_PSTORE(obj); > > > + > > > + object_property_add(obj, "directory", "str", > > > + pstore_get_directory, pstore_set_directory, > > > + pstore_release_directory, s, NULL); > > > + object_property_add(obj, "bufsize", "size", > > > + pstore_get_bufsize, pstore_set_bufsize, NULL, s, NULL); > > > + object_property_add(obj, "file-max", "int", > > > + pstore_get_file_max, pstore_set_file_max, NULL, s, NULL); > > > +} > > > + > > > +static void virtio_pstore_class_init(ObjectClass *klass, void *data) > > > +{ > > > + DeviceClass *dc = DEVICE_CLASS(klass); > > > + VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); > > > + > > > + dc->props = virtio_pstore_properties; > > > + set_bit(DEVICE_CATEGORY_MISC, dc->categories); > > > + vdc->realize = virtio_pstore_device_realize; > > > + vdc->unrealize = virtio_pstore_device_unrealize; > > > + vdc->get_config = virtio_pstore_get_config; > > > + vdc->set_config = virtio_pstore_set_config; > > > + vdc->get_features = get_features; > > > +} > > > + > > > +static const TypeInfo virtio_pstore_info = { > > > + .name = TYPE_VIRTIO_PSTORE, > > > + .parent = TYPE_VIRTIO_DEVICE, > > > + .instance_size = sizeof(VirtIOPstore), > > > + .instance_init = virtio_pstore_instance_init, > > > + .class_init = virtio_pstore_class_init, > > > +}; > > > + > > > +static void virtio_register_types(void) > > > +{ > > > + type_register_static(&virtio_pstore_info); > > > +} > > > + > > > +type_init(virtio_register_types) > > > diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h > > > index 929ec2f..b31774a 100644 > > > --- a/include/hw/pci/pci.h > > > +++ b/include/hw/pci/pci.h > > > @@ -79,6 +79,7 @@ > > > #define PCI_DEVICE_ID_VIRTIO_SCSI 0x1004 > > > #define PCI_DEVICE_ID_VIRTIO_RNG 0x1005 > > > #define PCI_DEVICE_ID_VIRTIO_9P 0x1009 > > > +#define PCI_DEVICE_ID_VIRTIO_PSTORE 0x100a > > > > > > #define PCI_VENDOR_ID_REDHAT 0x1b36 > > > #define PCI_DEVICE_ID_REDHAT_BRIDGE 0x0001 > > > diff --git a/include/hw/virtio/virtio-pstore.h b/include/hw/virtio/virtio-pstore.h > > > new file mode 100644 > > > index 0000000..85b1828 > > > --- /dev/null > > > +++ b/include/hw/virtio/virtio-pstore.h > > > @@ -0,0 +1,36 @@ > > > +/* > > > + * Virtio Pstore Support > > > + * > > > + * Authors: > > > + * Namhyung Kim <namhyung@xxxxxxxxx> > > > + * > > > + * This work is licensed under the terms of the GNU GPL, version 2. See > > > + * the COPYING file in the top-level directory. > > > + * > > > + */ > > > + > > > +#ifndef _QEMU_VIRTIO_PSTORE_H > > > +#define _QEMU_VIRTIO_PSTORE_H > > > + > > > +#include "standard-headers/linux/virtio_pstore.h" > > > +#include "hw/virtio/virtio.h" > > > +#include "hw/pci/pci.h" > > > + > > > +#define TYPE_VIRTIO_PSTORE "virtio-pstore-device" > > > +#define VIRTIO_PSTORE(obj) \ > > > + OBJECT_CHECK(VirtIOPstore, (obj), TYPE_VIRTIO_PSTORE) > > > + > > > +typedef struct VirtIOPstore { > > > + VirtIODevice parent_obj; > > > + VirtQueue *rvq; > > > + VirtQueue *wvq; > > > + char *directory; > > > + int file_idx; > > > + int num_file; > > > + struct dirent **files; > > > + uint64_t id; > > > + uint64_t bufsize; > > > + uint64_t file_max; > > > +} VirtIOPstore; > > > + > > > +#endif > > > diff --git a/include/standard-headers/linux/virtio_ids.h b/include/standard-headers/linux/virtio_ids.h > > > index 77925f5..c72a9ab 100644 > > > --- a/include/standard-headers/linux/virtio_ids.h > > > +++ b/include/standard-headers/linux/virtio_ids.h > > > @@ -41,5 +41,6 @@ > > > #define VIRTIO_ID_CAIF 12 /* Virtio caif */ > > > #define VIRTIO_ID_GPU 16 /* virtio GPU */ > > > #define VIRTIO_ID_INPUT 18 /* virtio input */ > > > +#define VIRTIO_ID_PSTORE 22 /* virtio pstore */ > > > > > > #endif /* _LINUX_VIRTIO_IDS_H */ > > > diff --git a/include/standard-headers/linux/virtio_pstore.h b/include/standard-headers/linux/virtio_pstore.h > > > new file mode 100644 > > > index 0000000..2f91839 > > > --- /dev/null > > > +++ b/include/standard-headers/linux/virtio_pstore.h > > > @@ -0,0 +1,76 @@ > > > +#ifndef _LINUX_VIRTIO_PSTORE_H > > > +#define _LINUX_VIRTIO_PSTORE_H > > > +/* This header is BSD licensed so anyone can use the definitions to implement > > > + * compatible drivers/servers. > > > + * > > > + * Redistribution and use in source and binary forms, with or without > > > + * modification, are permitted provided that the following conditions > > > + * are met: > > > + * 1. Redistributions of source code must retain the above copyright > > > + * notice, this list of conditions and the following disclaimer. > > > + * 2. Redistributions in binary form must reproduce the above copyright > > > + * notice, this list of conditions and the following disclaimer in the > > > + * documentation and/or other materials provided with the distribution. > > > + * 3. Neither the name of IBM nor the names of its contributors > > > + * may be used to endorse or promote products derived from this software > > > + * without specific prior written permission. > > > + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND > > > + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE > > > + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE > > > + * ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE > > > + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL > > > + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS > > > + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) > > > + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT > > > + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY > > > + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF > > > + * SUCH DAMAGE. */ > > > +#include "standard-headers/linux/types.h" > > > +#include "standard-headers/linux/virtio_types.h" > > > +#include "standard-headers/linux/virtio_ids.h" > > > +#include "standard-headers/linux/virtio_config.h" > > > + > > > +#define VIRTIO_PSTORE_CMD_NULL 0 > > > +#define VIRTIO_PSTORE_CMD_OPEN 1 > > > +#define VIRTIO_PSTORE_CMD_READ 2 > > > +#define VIRTIO_PSTORE_CMD_WRITE 3 > > > +#define VIRTIO_PSTORE_CMD_ERASE 4 > > > +#define VIRTIO_PSTORE_CMD_CLOSE 5 > > > + > > > +#define VIRTIO_PSTORE_TYPE_UNKNOWN 0 > > > +#define VIRTIO_PSTORE_TYPE_DMESG 1 > > > + > > > +#define VIRTIO_PSTORE_FL_COMPRESSED 1 > > > + > > > +struct virtio_pstore_req { > > > + __virtio16 cmd; > > > + __virtio16 type; > > > + __virtio32 flags; > > > + __virtio64 id; > > > + __virtio32 count; > > > + __virtio32 reserved; > > > +}; > > > + > > > +struct virtio_pstore_res { > > > + __virtio16 cmd; > > > + __virtio16 type; > > > + __virtio32 ret; > > > +}; > > > + > > > +struct virtio_pstore_fileinfo { > > > + __virtio64 id; > > > + __virtio32 count; > > > + __virtio16 type; > > > + __virtio16 unused; > > > + __virtio32 flags; > > > + __virtio32 len; > > > + __virtio64 time_sec; > > > + __virtio32 time_nsec; > > > + __virtio32 reserved; > > > +}; > > > + > > > +struct virtio_pstore_config { > > > + __virtio32 bufsize; > > > +}; > > > + > > > +#endif /* _LINUX_VIRTIO_PSTORE_H */ > > > diff --git a/qdev-monitor.c b/qdev-monitor.c > > > index e19617f..e1df5a9 100644 > > > --- a/qdev-monitor.c > > > +++ b/qdev-monitor.c > > > @@ -73,6 +73,7 @@ static const QDevAlias qdev_alias_table[] = { > > > { "virtio-serial-pci", "virtio-serial", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X }, > > > { "virtio-tablet-ccw", "virtio-tablet", QEMU_ARCH_S390X }, > > > { "virtio-tablet-pci", "virtio-tablet", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X }, > > > + { "virtio-pstore-pci", "virtio-pstore" }, > > > { } > > > }; > > > > > > -- > > > 2.9.3 _______________________________________________ Virtualization mailing list Virtualization@xxxxxxxxxxxxxxxxxxxxxxxxxx https://lists.linuxfoundation.org/mailman/listinfo/virtualization