Namhyung Kim
2016-Jul-18 04:37 UTC
[RFC/PATCHSET 0/3] virtio-pstore: Implement virtio pstore device
Hello, This patchset is a proof of concept of virtio-pstore idea [1]. It has some rough edges and I'm not familiar with this area, so please give me feedbacks and advices if I'm going to a wrong direction. It started from the fact that dumping ftrace buffer at kernel oops/panic takes too much time. Although there's a way to reduce the size of the original data, sometimes I want to have the information as many as possible. Maybe kexec/kdump can solve this problem but it consumes some portion of guest memory so I'd like to avoid it. And I know the qemu + crashtool can dump and analyze the whole guest memory including the ftrace buffer without wasting guest memory, but it adds one more layer and has some limitation as an out-of-tree tool like not being in sync with the kernel changes. So I think it'd be great using the pstore interface to dump guest kernel data on the host. One can read the data on the host directly or on the guest (at the next boot) using pstore filesystem as usual. While this patchset only implements dumping kernel log buffer, it can be extended to have ftrace buffer and probably some more.. The patch 0001 implements virtio pstore driver. It has a single virt queue, pstore buffer and header structure. The virtio_pstore_hdr struct is to give information about the current pstore operation. The patch 0002 and 0003 implement virtio-pstore legacy PCI device on qemu-kvm and kvmtool respectively. I referenced virtio-baloon and virtio-rng implementations and I don't know whether kvmtool supports modern virtio 1.0+ spec. For example, using virtio-pstore on qemu looks like below: $ qemu-system-x86_64 -enable-kvm -device virtio-pstore,directory=xxx When guest kernel gets panic the log messages will be saved under the xxx directory. $ ls xxx dmesg-0.enc.z dmesg-1.enc.z As you can see the pstore subsystem compresses the log data using zlib. The data can be extracted with the following command: $ cat xxx/dmesg-0.enc.z | \ > python -c 'import sys, zlib; print(zlib.decompress(sys.stdin.read()))' Oops#1 Part1 <5>[ 0.000000] Linux version 4.6.0kvm+ (namhyung at danjae) (gcc version 5.3.0 (GCC) ) #145 SMP Mon Jul 18 10:22:45 KST 2016 <6>[ 0.000000] Command line: root=/dev/vda console=ttyS0 <6>[ 0.000000] x86/fpu: Legacy x87 FPU detected. <6>[ 0.000000] x86/fpu: Using 'eager' FPU context switches. <6>[ 0.000000] e820: BIOS-provided physical RAM map: <6>[ 0.000000] BIOS-e820: [mem 0x0000000000000000-0x000000000009fbff] usable <6>[ 0.000000] BIOS-e820: [mem 0x000000000009fc00-0x000000000009ffff] reserved <6>[ 0.000000] BIOS-e820: [mem 0x00000000000f0000-0x00000000000fffff] reserved <6>[ 0.000000] BIOS-e820: [mem 0x0000000000100000-0x0000000007fddfff] usable <6>[ 0.000000] BIOS-e820: [mem 0x0000000007fde000-0x0000000007ffffff] reserved <6>[ 0.000000] BIOS-e820: [mem 0x00000000feffc000-0x00000000feffffff] reserved <6>[ 0.000000] BIOS-e820: [mem 0x00000000fffc0000-0x00000000ffffffff] reserved <6>[ 0.000000] NX (Execute Disable) protection: active <6>[ 0.000000] SMBIOS 2.8 present. <7>[ 0.000000] DMI: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.9.3-0-ge2fc41e-prebuilt.qemu-project.org 04/01/2014 ... Maybe we can add a config option to control the compression later. Cc: Paolo Bonzini <pbonzini at redhat.com> Cc: Radim Kr?m?? <rkrcmar at redhat.com> Cc: "Michael S. Tsirkin" <mst at redhat.com> Cc: Anthony Liguori <aliguori at amazon.com> Cc: Anton Vorontsov <anton at enomsg.org> Cc: Colin Cross <ccross at android.com> Cc: Kees Cook <keescook at chromium.org> Cc: Tony Luck <tony.luck at intel.com> Cc: Steven Rostedt <rostedt at goodmis.org> Cc: Ingo Molnar <mingo at kernel.org> Cc: Minchan Kim <minchan at kernel.org> Cc: kvm at vger.kernel.org Cc: qemu-devel at nongnu.org Cc: virtualization at lists.linux-foundation.org [1] https://lkml.org/lkml/2016/7/1/6 Thanks, Namhyung
Namhyung Kim
2016-Jul-18 04:37 UTC
[PATCH 1/3] virtio: Basic implementation of virtio pstore driver
The virtio pstore driver provides interface to the pstore subsystem so that the guest kernel's log/dump message can be saved on the host machine. Users can access the log file directly on the host, or on the guest at the next boot using pstore filesystem. It currently deals with kernel log (printk) buffer only, but we can extend it to have other information (like ftrace dump) later. It supports legacy PCI device using single order-2 page buffer. As all operation of pstore is synchronous, it would be fine IMHO. However I don't know how to make write operation synchronous since it's called with a spinlock held (from any context including NMI). Cc: Paolo Bonzini <pbonzini at redhat.com> Cc: Radim Kr?m?? <rkrcmar at redhat.com> Cc: "Michael S. Tsirkin" <mst at redhat.com> Cc: Anthony Liguori <aliguori at amazon.com> Cc: Anton Vorontsov <anton at enomsg.org> Cc: Colin Cross <ccross at android.com> Cc: Kees Cook <keescook at chromium.org> Cc: Tony Luck <tony.luck at intel.com> Cc: Steven Rostedt <rostedt at goodmis.org> Cc: Ingo Molnar <mingo at kernel.org> Cc: Minchan Kim <minchan at kernel.org> Cc: kvm at vger.kernel.org Cc: qemu-devel at nongnu.org Cc: virtualization at lists.linux-foundation.org Signed-off-by: Namhyung Kim <namhyung at kernel.org> --- drivers/virtio/Kconfig | 10 ++ drivers/virtio/Makefile | 1 + drivers/virtio/virtio_pstore.c | 317 +++++++++++++++++++++++++++++++++++++ include/uapi/linux/Kbuild | 1 + include/uapi/linux/virtio_ids.h | 1 + include/uapi/linux/virtio_pstore.h | 53 +++++++ 6 files changed, 383 insertions(+) create mode 100644 drivers/virtio/virtio_pstore.c create mode 100644 include/uapi/linux/virtio_pstore.h diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig index 77590320d44c..8f0e6c796c12 100644 --- a/drivers/virtio/Kconfig +++ b/drivers/virtio/Kconfig @@ -58,6 +58,16 @@ config VIRTIO_INPUT If unsure, say M. +config VIRTIO_PSTORE + tristate "Virtio pstore driver" + depends on VIRTIO + depends on PSTORE + ---help--- + This driver supports virtio pstore devices to save/restore + panic and oops messages on the host. + + If unsure, say M. + config VIRTIO_MMIO tristate "Platform bus driver for memory mapped virtio devices" depends on HAS_IOMEM && HAS_DMA diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile index 41e30e3dc842..bee68cb26d48 100644 --- a/drivers/virtio/Makefile +++ b/drivers/virtio/Makefile @@ -5,3 +5,4 @@ virtio_pci-y := virtio_pci_modern.o virtio_pci_common.o virtio_pci-$(CONFIG_VIRTIO_PCI_LEGACY) += virtio_pci_legacy.o obj-$(CONFIG_VIRTIO_BALLOON) += virtio_balloon.o obj-$(CONFIG_VIRTIO_INPUT) += virtio_input.o +obj-$(CONFIG_VIRTIO_PSTORE) += virtio_pstore.o diff --git a/drivers/virtio/virtio_pstore.c b/drivers/virtio/virtio_pstore.c new file mode 100644 index 000000000000..6fe62c0f1508 --- /dev/null +++ b/drivers/virtio/virtio_pstore.c @@ -0,0 +1,317 @@ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pstore.h> +#include <linux/virtio.h> +#include <linux/virtio_config.h> +#include <uapi/linux/virtio_ids.h> +#include <uapi/linux/virtio_pstore.h> + +#define VIRT_PSTORE_ORDER 2 +#define VIRT_PSTORE_BUFSIZE (4096 << VIRT_PSTORE_ORDER) + +struct virtio_pstore { + struct virtio_device *vdev; + struct virtqueue *vq; + struct pstore_info pstore; + struct virtio_pstore_hdr hdr; + size_t buflen; + u64 id; + + /* Waiting for host to ack */ + wait_queue_head_t acked; +}; + +static u16 to_virtio_type(struct virtio_pstore *vps, enum pstore_type_id type) +{ + u16 ret; + + switch (type) { + case PSTORE_TYPE_DMESG: + ret = cpu_to_virtio16(vps->vdev, VIRTIO_PSTORE_TYPE_DMESG); + break; + default: + ret = cpu_to_virtio16(vps->vdev, VIRTIO_PSTORE_TYPE_UNKNOWN); + break; + } + + return ret; +} + +static enum pstore_type_id from_virtio_type(struct virtio_pstore *vps, u16 type) +{ + enum pstore_type_id ret; + + switch (virtio16_to_cpu(vps->vdev, type)) { + case VIRTIO_PSTORE_TYPE_DMESG: + ret = PSTORE_TYPE_DMESG; + break; + default: + ret = PSTORE_TYPE_UNKNOWN; + break; + } + + return ret; +} + +static void virtpstore_ack(struct virtqueue *vq) +{ + struct virtio_pstore *vps = vq->vdev->priv; + + wake_up(&vps->acked); +} + +static int virt_pstore_open(struct pstore_info *psi) +{ + struct virtio_pstore *vps = psi->data; + struct virtio_pstore_hdr *hdr = &vps->hdr; + struct scatterlist sg[1]; + unsigned int len; + + hdr->cmd = cpu_to_virtio16(vps->vdev, VIRTIO_PSTORE_CMD_OPEN); + + sg_init_one(sg, hdr, sizeof(*hdr)); + virtqueue_add_outbuf(vps->vq, sg, 1, vps, GFP_KERNEL); + virtqueue_kick(vps->vq); + + wait_event(vps->acked, virtqueue_get_buf(vps->vq, &len)); + return 0; +} + +static int virt_pstore_close(struct pstore_info *psi) +{ + struct virtio_pstore *vps = psi->data; + struct virtio_pstore_hdr *hdr = &vps->hdr; + struct scatterlist sg[1]; + unsigned int len; + + hdr->cmd = cpu_to_virtio16(vps->vdev, VIRTIO_PSTORE_CMD_CLOSE); + + sg_init_one(sg, hdr, sizeof(*hdr)); + virtqueue_add_outbuf(vps->vq, sg, 1, vps, GFP_KERNEL); + virtqueue_kick(vps->vq); + + wait_event(vps->acked, virtqueue_get_buf(vps->vq, &len)); + return 0; +} + +static ssize_t virt_pstore_read(u64 *id, enum pstore_type_id *type, + int *count, struct timespec *time, + char **buf, bool *compressed, + struct pstore_info *psi) +{ + struct virtio_pstore *vps = psi->data; + struct virtio_pstore_hdr *hdr = &vps->hdr; + struct scatterlist sgi[1], sgo[1]; + struct scatterlist *sgs[2] = { sgo, sgi }; + unsigned int len; + unsigned int flags; + void *bf; + + hdr->cmd = cpu_to_virtio16(vps->vdev, VIRTIO_PSTORE_CMD_READ); + + sg_init_one(sgo, hdr, sizeof(*hdr)); + sg_init_one(sgi, psi->buf, psi->bufsize); + virtqueue_add_sgs(vps->vq, sgs, 1, 1, vps, GFP_KERNEL); + virtqueue_kick(vps->vq); + + wait_event(vps->acked, virtqueue_get_buf(vps->vq, &len)); + if (len == 0) + return 0; + + bf = kmalloc(len, GFP_KERNEL); + if (bf == NULL) + return -ENOMEM; + + *id = virtio64_to_cpu(vps->vdev, hdr->id); + *type = from_virtio_type(vps, hdr->type); + + flags = virtio32_to_cpu(vps->vdev, hdr->flags); + *compressed = flags & VIRTIO_PSTORE_FL_COMPRESSED; + *count = 1; + + time->tv_sec = virtio64_to_cpu(vps->vdev, hdr->time_sec); + time->tv_nsec = virtio32_to_cpu(vps->vdev, hdr->time_nsec); + + memcpy(bf, psi->buf, len); + *buf = bf; + + return len; +} + +static int notrace virt_pstore_write(enum pstore_type_id type, + enum kmsg_dump_reason reason, + u64 *id, unsigned int part, int count, + bool compressed, size_t size, + struct pstore_info *psi) +{ + struct virtio_pstore *vps = psi->data; + struct virtio_pstore_hdr *hdr = &vps->hdr; + struct scatterlist sg[2]; + unsigned int flags = compressed ? VIRTIO_PSTORE_FL_COMPRESSED : 0; + + *id = vps->id++; + + hdr->cmd = cpu_to_virtio16(vps->vdev, VIRTIO_PSTORE_CMD_WRITE); + hdr->id = cpu_to_virtio64(vps->vdev, *id); + hdr->flags = cpu_to_virtio32(vps->vdev, flags); + hdr->type = to_virtio_type(vps, type); + + sg_init_table(sg, 2); + sg_set_buf(&sg[0], hdr, sizeof(*hdr)); + sg_set_buf(&sg[1], psi->buf, size); + virtqueue_add_outbuf(vps->vq, sg, 2, vps, GFP_ATOMIC); + virtqueue_kick(vps->vq); + + /* TODO: make it synchronous */ + return 0; +} + +static int virt_pstore_erase(enum pstore_type_id type, u64 id, int count, + struct timespec time, struct pstore_info *psi) +{ + struct virtio_pstore *vps = psi->data; + struct virtio_pstore_hdr *hdr = &vps->hdr; + struct scatterlist sg[1]; + unsigned int len; + + hdr->cmd = cpu_to_virtio16(vps->vdev, VIRTIO_PSTORE_CMD_ERASE); + hdr->id = cpu_to_virtio64(vps->vdev, id); + hdr->type = to_virtio_type(vps, type); + + sg_init_one(sg, hdr, sizeof(*hdr)); + virtqueue_add_outbuf(vps->vq, sg, 1, vps, GFP_KERNEL); + virtqueue_kick(vps->vq); + + wait_event(vps->acked, virtqueue_get_buf(vps->vq, &len)); + return 0; +} + +static int virt_pstore_init(struct virtio_pstore *vps) +{ + struct pstore_info *psinfo = &vps->pstore; + int err; + + vps->id = 0; + vps->buflen = 0; + psinfo->bufsize = VIRT_PSTORE_BUFSIZE; + psinfo->buf = (void *)__get_free_pages(GFP_KERNEL, VIRT_PSTORE_ORDER); + if (!psinfo->buf) { + pr_err("cannot allocate pstore buffer\n"); + return -ENOMEM; + } + + psinfo->owner = THIS_MODULE; + psinfo->name = "virtio"; + psinfo->open = virt_pstore_open; + psinfo->close = virt_pstore_close; + psinfo->read = virt_pstore_read; + psinfo->erase = virt_pstore_erase; + psinfo->write = virt_pstore_write; + psinfo->flags = PSTORE_FLAGS_FRAGILE; + psinfo->data = vps; + spin_lock_init(&psinfo->buf_lock); + + err = pstore_register(psinfo); + if (err) + kfree(psinfo->buf); + + return err; +} + +static int virt_pstore_exit(struct virtio_pstore *vps) +{ + struct pstore_info *psinfo = &vps->pstore; + + pstore_unregister(psinfo); + + free_pages((unsigned long)psinfo->buf, VIRT_PSTORE_ORDER); + psinfo->bufsize = 0; + + return 0; +} + +static int virtpstore_probe(struct virtio_device *vdev) +{ + struct virtio_pstore *vps; + int err; + + if (!vdev->config->get) { + dev_err(&vdev->dev, "%s failure: config access disabled\n", + __func__); + return -EINVAL; + } + + vdev->priv = vps = kmalloc(sizeof(*vps), GFP_KERNEL); + if (!vps) { + err = -ENOMEM; + goto out; + } + + vps->vdev = vdev; + + vps->vq = virtio_find_single_vq(vdev, virtpstore_ack, "pstore"); + if (IS_ERR(vps->vq)) { + err = PTR_ERR(vps->vq); + goto out_free; + } + + err = virt_pstore_init(vps); + if (err) + goto out_del_vq; + + init_waitqueue_head(&vps->acked); + + virtio_device_ready(vdev); + dev_info(&vdev->dev, "virtio pstore driver init: ok\n"); + + return 0; + +out_del_vq: + vdev->config->del_vqs(vdev); +out_free: + kfree(vps); +out: + dev_err(&vdev->dev, "virtio pstore driver init: failed with %d\n", err); + return err; +} + +static void virtpstore_remove(struct virtio_device *vdev) +{ + struct virtio_pstore *vps = vdev->priv; + + virt_pstore_exit(vps); + + /* Now we reset the device so we can clean up the queues. */ + vdev->config->reset(vdev); + + vdev->config->del_vqs(vdev); + + kfree(vps); +} + +static unsigned int features[] = { +}; + +static struct virtio_device_id id_table[] = { + { VIRTIO_ID_PSTORE, VIRTIO_DEV_ANY_ID }, + { 0 }, +}; + +static struct virtio_driver virtio_pstore_driver = { + .driver.name = KBUILD_MODNAME, + .driver.owner = THIS_MODULE, + .feature_table = features, + .feature_table_size = ARRAY_SIZE(features), + .id_table = id_table, + .probe = virtpstore_probe, + .remove = virtpstore_remove, +}; + +module_virtio_driver(virtio_pstore_driver); +MODULE_DEVICE_TABLE(virtio, id_table); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Namhyung Kim <namhyung at kernel.org>"); +MODULE_DESCRIPTION("Virtio pstore driver"); diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index 8bdae34d1f9a..57b0d08db322 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -448,6 +448,7 @@ header-y += virtio_ids.h header-y += virtio_input.h header-y += virtio_net.h header-y += virtio_pci.h +header-y += virtio_pstore.h header-y += virtio_ring.h header-y += virtio_rng.h header-y += virtio_scsi.h diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h index 77925f587b15..cba63225d85a 100644 --- a/include/uapi/linux/virtio_ids.h +++ b/include/uapi/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 19 /* virtio pstore */ #endif /* _LINUX_VIRTIO_IDS_H */ diff --git a/include/uapi/linux/virtio_pstore.h b/include/uapi/linux/virtio_pstore.h new file mode 100644 index 000000000000..0aa1575ee35f --- /dev/null +++ b/include/uapi/linux/virtio_pstore.h @@ -0,0 +1,53 @@ +#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 <linux/types.h> +#include <linux/virtio_types.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_hdr { + __virtio64 id; + __virtio32 flags; + __virtio16 cmd; + __virtio16 type; + __virtio64 time_sec; + __virtio32 time_nsec; + __virtio32 unused; +}; + +#endif /* _LINUX_VIRTIO_PSTORE_H */ -- 2.8.0
From: Namhyung Kim <namhyung at gmail.com> 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-0.enc.z dmesg-1.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). Cc: Paolo Bonzini <pbonzini at redhat.com> Cc: Radim Kr?m?? <rkrcmar at redhat.com> Cc: "Michael S. Tsirkin" <mst at redhat.com> Cc: Anthony Liguori <aliguori at amazon.com> Cc: Anton Vorontsov <anton at enomsg.org> Cc: Colin Cross <ccross at android.com> Cc: Kees Cook <keescook at chromium.org> Cc: Tony Luck <tony.luck at intel.com> Cc: Steven Rostedt <rostedt at goodmis.org> Cc: Ingo Molnar <mingo at kernel.org> Cc: Minchan Kim <minchan at kernel.org> Cc: kvm at vger.kernel.org Cc: qemu-devel at nongnu.org Cc: virtualization at lists.linux-foundation.org Signed-off-by: Namhyung Kim <namhyung at gmail.com> --- hw/virtio/Makefile.objs | 2 +- hw/virtio/virtio-pci.c | 50 ++++ hw/virtio/virtio-pci.h | 14 + hw/virtio/virtio-pstore.c | 328 +++++++++++++++++++++ include/hw/pci/pci.h | 1 + include/hw/virtio/virtio-pstore.h | 30 ++ include/standard-headers/linux/virtio_ids.h | 1 + .../linux/{virtio_ids.h => virtio_pstore.h} | 48 +-- qdev-monitor.c | 1 + 9 files changed, 455 insertions(+), 20 deletions(-) create mode 100644 hw/virtio/virtio-pstore.c create mode 100644 include/hw/virtio/virtio-pstore.h copy include/standard-headers/linux/{virtio_ids.h => virtio_pstore.h} (63%) 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 2b34b43..8281b80 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -2416,6 +2416,55 @@ 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); +} + +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 +2534,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 e4548c2..b4c039f 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 */ @@ -311,6 +313,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..98cee7f --- /dev/null +++ b/hw/virtio/virtio-pstore.c @@ -0,0 +1,328 @@ +/* + * Virtio Pstore Device + * + * Copyright (C) 2016 LG Electronics + * + * Authors: + * Namhyung Kim <namhyung at gmail.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. 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 "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" + + +static void virtio_pstore_to_filename(VirtIOPstore *s, char *buf, size_t sz, + struct virtio_pstore_hdr *hdr) +{ + const char *basename; + + switch (hdr->type) { + case VIRTIO_PSTORE_TYPE_DMESG: + basename = "dmesg"; + break; + default: + basename = "unknown"; + break; + } + + snprintf(buf, sz, "%s/%s-%llu%s", s->directory, basename, + (unsigned long long) hdr->id, + hdr->flags & VIRTIO_PSTORE_FL_COMPRESSED ? ".enc.z" : ""); +} + +static void virtio_pstore_from_filename(VirtIOPstore *s, char *name, + char *buf, size_t sz, + struct virtio_pstore_hdr *hdr) +{ + size_t len = strlen(name); + + hdr->flags = 0; + if (!strncmp(name + len - 6, ".enc.z", 6)) { + hdr->flags |= VIRTIO_PSTORE_FL_COMPRESSED; + } + + snprintf(buf, sz, "%s/%s", s->directory, name); + + if (!strncmp(name, "dmesg-", 6)) { + hdr->type = cpu_to_le16(VIRTIO_PSTORE_TYPE_DMESG); + name += 6; + } else if (!strncmp(name, "unknown-", 8)) { + hdr->type = cpu_to_le16(VIRTIO_PSTORE_TYPE_UNKNOWN); + name += 8; + } + + qemu_strtoull(name, NULL, 0, &hdr->id); +} + +static ssize_t virtio_pstore_do_open(VirtIOPstore *s) +{ + s->dir = opendir(s->directory); + if (s->dir == NULL) { + return -1; + } + + return 0; +} + +static ssize_t virtio_pstore_do_read(VirtIOPstore *s, void *buf, size_t sz, + struct virtio_pstore_hdr *hdr) +{ + char path[PATH_MAX]; + FILE *fp; + ssize_t len; + struct stat stbuf; + struct dirent *dent; + + if (s->dir == NULL) { + return -1; + } + + dent = readdir(s->dir); + while (dent) { + if (dent->d_name[0] != '.') { + break; + } + dent = readdir(s->dir); + } + + if (dent == NULL) { + return 0; + } + + virtio_pstore_from_filename(s, dent->d_name, path, sizeof(path), hdr); + if (stat(path, &stbuf) < 0) { + return -1; + } + + fp = fopen(path, "r"); + if (fp == NULL) { + error_report("cannot open %s (%p %p)", path, s, s->directory); + return -1; + } + + len = fread(buf, 1, sz, fp); + if (len < 0 && errno == EAGAIN) { + len = 0; + } + + hdr->id = cpu_to_le64(hdr->id); + hdr->flags = cpu_to_le32(hdr->flags); + hdr->time_sec = cpu_to_le64(stbuf.st_ctim.tv_sec); + hdr->time_nsec = cpu_to_le32(stbuf.st_ctim.tv_nsec); + + fclose(fp); + return len; +} + +static ssize_t virtio_pstore_do_write(VirtIOPstore *s, void *buf, size_t sz, + struct virtio_pstore_hdr *hdr) +{ + char path[PATH_MAX]; + FILE *fp; + + virtio_pstore_to_filename(s, path, sizeof(path), hdr); + + fp = fopen(path, "w"); + if (fp == NULL) { + error_report("cannot open %s (%p %p)", path, s, s->directory); + return -1; + } + fwrite(buf, 1, sz, fp); + fclose(fp); + + return sz; +} + +static ssize_t virtio_pstore_do_close(VirtIOPstore *s) +{ + if (s->dir == NULL) { + return 0; + } + + closedir(s->dir); + s->dir = NULL; + + return 0; +} + +static ssize_t virtio_pstore_do_erase(VirtIOPstore *s, + struct virtio_pstore_hdr *hdr) +{ + char path[PATH_MAX]; + + virtio_pstore_to_filename(s, path, sizeof(path), hdr); + + return unlink(path); +} + +static void virtio_pstore_handle_io(VirtIODevice *vdev, VirtQueue *vq) +{ + VirtIOPstore *s = VIRTIO_PSTORE(vdev); + VirtQueueElement *elem; + struct virtio_pstore_hdr *hdr; + ssize_t len; + + for (;;) { + elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); + if (!elem) { + return; + } + + hdr = elem->out_sg[0].iov_base; + if (elem->out_sg[0].iov_len != sizeof(*hdr)) { + error_report("invalid header size: %u", + (unsigned)elem->out_sg[0].iov_len); + exit(1); + } + + switch (hdr->cmd) { + case VIRTIO_PSTORE_CMD_OPEN: + len = virtio_pstore_do_open(s); + break; + case VIRTIO_PSTORE_CMD_READ: + len = virtio_pstore_do_read(s, elem->in_sg[0].iov_base, + elem->in_sg[0].iov_len, hdr); + break; + case VIRTIO_PSTORE_CMD_WRITE: + len = virtio_pstore_do_write(s, elem->out_sg[1].iov_base, + elem->out_sg[1].iov_len, hdr); + break; + case VIRTIO_PSTORE_CMD_CLOSE: + len = virtio_pstore_do_close(s); + break; + case VIRTIO_PSTORE_CMD_ERASE: + len = virtio_pstore_do_erase(s, hdr); + break; + default: + len = -1; + break; + } + + if (len < 0) { + return; + } + + virtqueue_push(vq, elem, len); + + virtio_notify(vdev, vq); + g_free(elem); + } +} + +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, 0); + + s->vq = 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 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 = strdup(value); + + g_free(value); +} + +static void pstore_release_directory(Object *obj, const char *name, + void *opaque) +{ + VirtIOPstore *s = opaque; + + g_free(s->directory); + s->directory = NULL; +} + +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); +} + +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_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 9ed1624..5689c6f 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..74cd1f6 --- /dev/null +++ b/include/hw/virtio/virtio-pstore.h @@ -0,0 +1,30 @@ +/* + * Virtio Pstore Support + * + * Authors: + * Namhyung Kim <namhyung at gmail.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_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 *vq; + char *directory; + DIR *dir; +} VirtIOPstore; + +#endif diff --git a/include/standard-headers/linux/virtio_ids.h b/include/standard-headers/linux/virtio_ids.h index 77925f5..cba6322 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 19 /* virtio pstore */ #endif /* _LINUX_VIRTIO_IDS_H */ diff --git a/include/standard-headers/linux/virtio_ids.h b/include/standard-headers/linux/virtio_pstore.h similarity index 63% copy from include/standard-headers/linux/virtio_ids.h copy to include/standard-headers/linux/virtio_pstore.h index 77925f5..1b89cad 100644 --- a/include/standard-headers/linux/virtio_ids.h +++ b/include/standard-headers/linux/virtio_pstore.h @@ -1,9 +1,6 @@ -#ifndef _LINUX_VIRTIO_IDS_H -#define _LINUX_VIRTIO_IDS_H -/* - * Virtio IDs - * - * This header is BSD licensed so anyone can use the definitions to implement +#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 @@ -28,18 +25,31 @@ * 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_TYPE_UNKNOWN 0 +#define VIRTIO_PSTORE_TYPE_DMESG 1 + +#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_FL_COMPRESSED 1 -#define VIRTIO_ID_NET 1 /* virtio net */ -#define VIRTIO_ID_BLOCK 2 /* virtio block */ -#define VIRTIO_ID_CONSOLE 3 /* virtio console */ -#define VIRTIO_ID_RNG 4 /* virtio rng */ -#define VIRTIO_ID_BALLOON 5 /* virtio balloon */ -#define VIRTIO_ID_RPMSG 7 /* virtio remote processor messaging */ -#define VIRTIO_ID_SCSI 8 /* virtio scsi */ -#define VIRTIO_ID_9P 9 /* 9p virtio console */ -#define VIRTIO_ID_RPROC_SERIAL 11 /* virtio remoteproc serial link */ -#define VIRTIO_ID_CAIF 12 /* Virtio caif */ -#define VIRTIO_ID_GPU 16 /* virtio GPU */ -#define VIRTIO_ID_INPUT 18 /* virtio input */ +struct virtio_pstore_hdr { + __virtio64 id; + __virtio32 flags; + __virtio16 cmd; + __virtio16 type; + __virtio64 time_sec; + __virtio32 time_nsec; + __virtio32 unused; +}; -#endif /* _LINUX_VIRTIO_IDS_H */ +#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.8.0
Add virtio pstore device to allow kernel log messages saved on the host. With this patch, it will save the log files under directory given by --pstore option. $ lkvm run --pstore=dir-xx (guest) # echo c > /proc/sysrq-trigger $ ls dir-xx dmesg-0.enc.z dmesg-1.enc.z The log files are usually compressed using zlib. User can easily see the messages on the host or on the guest (using pstore filesystem). Cc: Paolo Bonzini <pbonzini at redhat.com> Cc: Radim Kr?m?? <rkrcmar at redhat.com> Cc: "Michael S. Tsirkin" <mst at redhat.com> Cc: Anthony Liguori <aliguori at amazon.com> Cc: Anton Vorontsov <anton at enomsg.org> Cc: Colin Cross <ccross at android.com> Cc: Kees Cook <keescook at chromium.org> Cc: Tony Luck <tony.luck at intel.com> Cc: Steven Rostedt <rostedt at goodmis.org> Cc: Ingo Molnar <mingo at kernel.org> Cc: Minchan Kim <minchan at kernel.org> Cc: kvm at vger.kernel.org Cc: virtualization at lists.linux-foundation.org Signed-off-by: Namhyung Kim <namhyung at kernel.org> --- Makefile | 1 + builtin-run.c | 2 + include/kvm/kvm-config.h | 1 + include/kvm/virtio-pci-dev.h | 2 + include/kvm/virtio-pstore.h | 31 ++++ include/linux/virtio_ids.h | 1 + virtio/pstore.c | 359 +++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 397 insertions(+) create mode 100644 include/kvm/virtio-pstore.h create mode 100644 virtio/pstore.c diff --git a/Makefile b/Makefile index 1f0196f..d7462b9 100644 --- a/Makefile +++ b/Makefile @@ -67,6 +67,7 @@ OBJS += virtio/net.o OBJS += virtio/rng.o OBJS += virtio/balloon.o OBJS += virtio/pci.o +OBJS += virtio/pstore.o OBJS += disk/blk.o OBJS += disk/qcow.o OBJS += disk/raw.o diff --git a/builtin-run.c b/builtin-run.c index 72b878d..08c12dd 100644 --- a/builtin-run.c +++ b/builtin-run.c @@ -128,6 +128,8 @@ void kvm_run_set_wrapper_sandbox(void) " rootfs"), \ OPT_STRING('\0', "hugetlbfs", &(cfg)->hugetlbfs_path, "path", \ "Hugetlbfs path"), \ + OPT_STRING('\0', "pstore", &(cfg)->pstore_path, "path", \ + "pstore data path"), \ \ OPT_GROUP("Kernel options:"), \ OPT_STRING('k', "kernel", &(cfg)->kernel_filename, "kernel", \ diff --git a/include/kvm/kvm-config.h b/include/kvm/kvm-config.h index 386fa8c..42b7651 100644 --- a/include/kvm/kvm-config.h +++ b/include/kvm/kvm-config.h @@ -45,6 +45,7 @@ struct kvm_config { const char *hugetlbfs_path; const char *custom_rootfs_name; const char *real_cmdline; + const char *pstore_path; struct virtio_net_params *net_params; bool single_step; bool vnc; diff --git a/include/kvm/virtio-pci-dev.h b/include/kvm/virtio-pci-dev.h index 48ae018..4339d94 100644 --- a/include/kvm/virtio-pci-dev.h +++ b/include/kvm/virtio-pci-dev.h @@ -15,6 +15,7 @@ #define PCI_DEVICE_ID_VIRTIO_BLN 0x1005 #define PCI_DEVICE_ID_VIRTIO_SCSI 0x1008 #define PCI_DEVICE_ID_VIRTIO_9P 0x1009 +#define PCI_DEVICE_ID_VIRTIO_PSTORE 0x100a #define PCI_DEVICE_ID_VESA 0x2000 #define PCI_DEVICE_ID_PCI_SHMEM 0x0001 @@ -34,5 +35,6 @@ #define PCI_CLASS_RNG 0xff0000 #define PCI_CLASS_BLN 0xff0000 #define PCI_CLASS_9P 0xff0000 +#define PCI_CLASS_PSTORE 0xff0000 #endif /* VIRTIO_PCI_DEV_H_ */ diff --git a/include/kvm/virtio-pstore.h b/include/kvm/virtio-pstore.h new file mode 100644 index 0000000..293ab57 --- /dev/null +++ b/include/kvm/virtio-pstore.h @@ -0,0 +1,31 @@ +#ifndef KVM__PSTORE_VIRTIO_H +#define KVM__PSTORE_VIRTIO_H + +struct kvm; + +#define VIRTIO_PSTORE_TYPE_UNKNOWN 0 +#define VIRTIO_PSTORE_TYPE_DMESG 1 + +#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_FL_COMPRESSED 1 + +struct pstore_hdr { + u64 id; + u32 flags; + u16 cmd; + u16 type; + u64 time_sec; + u32 time_nsec; + u32 unused; +}; + +int virtio_pstore__init(struct kvm *kvm); +int virtio_pstore__exit(struct kvm *kvm); + +#endif /* KVM__PSTORE_VIRTIO_H */ diff --git a/include/linux/virtio_ids.h b/include/linux/virtio_ids.h index 5f60aa4..f34cabc 100644 --- a/include/linux/virtio_ids.h +++ b/include/linux/virtio_ids.h @@ -40,5 +40,6 @@ #define VIRTIO_ID_RPROC_SERIAL 11 /* virtio remoteproc serial link */ #define VIRTIO_ID_CAIF 12 /* Virtio caif */ #define VIRTIO_ID_INPUT 18 /* virtio input */ +#define VIRTIO_ID_PSTORE 19 /* virtio pstore */ #endif /* _LINUX_VIRTIO_IDS_H */ diff --git a/virtio/pstore.c b/virtio/pstore.c new file mode 100644 index 0000000..094e54b --- /dev/null +++ b/virtio/pstore.c @@ -0,0 +1,359 @@ +#include "kvm/virtio-pstore.h" + +#include "kvm/virtio-pci-dev.h" + +#include "kvm/virtio.h" +#include "kvm/util.h" +#include "kvm/kvm.h" +#include "kvm/threadpool.h" +#include "kvm/guest_compat.h" + +#include <linux/virtio_ring.h> + +#include <linux/list.h> +#include <fcntl.h> +#include <dirent.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <pthread.h> +#include <linux/kernel.h> + +#define NUM_VIRT_QUEUES 1 +#define VIRTIO_PSTORE_QUEUE_SIZE 128 + +struct pstore_dev_job { + struct virt_queue *vq; + struct pstore_dev *pdev; + struct thread_pool__job job_id; +}; + +struct pstore_dev { + struct list_head list; + struct virtio_device vdev; + + int fd; + DIR *dir; + + /* virtio queue */ + struct virt_queue vqs[NUM_VIRT_QUEUES]; + struct pstore_dev_job jobs[NUM_VIRT_QUEUES]; +}; + +static LIST_HEAD(pdevs); +static int compat_id = -1; + +static u8 *get_config(struct kvm *kvm, void *dev) +{ + /* Unused */ + return 0; +} + +static u32 get_host_features(struct kvm *kvm, void *dev) +{ + /* Unused */ + return 0; +} + +static void set_guest_features(struct kvm *kvm, void *dev, u32 features) +{ + /* Unused */ +} + +static void virtio_pstore_hdr_to_filename(struct kvm *kvm, struct pstore_hdr *hdr, + char *buf, size_t sz) +{ + const char *basename; + + switch (hdr->type) { + case VIRTIO_PSTORE_TYPE_DMESG: + basename = "dmesg"; + break; + default: + basename = "unknown"; + break; + } + + snprintf(buf, sz, "%s/%s-%llu%s", kvm->cfg.pstore_path, basename, + hdr->id, hdr->flags & VIRTIO_PSTORE_FL_COMPRESSED ? ".enc.z" : ""); +} + +static void virtio_pstore_filename_to_hdr(struct kvm *kvm, struct pstore_hdr *hdr, + char *name, char *buf, size_t sz) +{ + size_t len = strlen(name); + + hdr->flags = 0; + if (!strncmp(name + len - 6, ".enc.z", 6)) + hdr->flags |= VIRTIO_PSTORE_FL_COMPRESSED; + + snprintf(buf, sz, "%s/%s", kvm->cfg.pstore_path, name); + + if (!strncmp(name, "dmesg", 5)) { + hdr->type = VIRTIO_PSTORE_TYPE_DMESG; + name += 5; + } else if (!strncmp(name, "unknown", 7)) { + hdr->type = VIRTIO_PSTORE_TYPE_UNKNOWN; + name += 7; + } + + hdr->id = strtoul(name + 1, NULL, 0); +} + +static int virtio_pstore_do_open(struct kvm *kvm, struct pstore_dev *pdev, + struct pstore_hdr *hdr, struct iovec *iov) +{ + pdev->dir = opendir(kvm->cfg.pstore_path); + if (pdev->dir == NULL) + return -errno; + + return 0; +} + +static int virtio_pstore_do_close(struct kvm *kvm, struct pstore_dev *pdev, + struct pstore_hdr *hdr, struct iovec *iov) +{ + if (pdev->dir == NULL) + return -1; + + closedir(pdev->dir); + pdev->dir = NULL; + + return 0; +} + +static ssize_t virtio_pstore_do_write(struct kvm *kvm, struct pstore_dev *pdev, + struct pstore_hdr *hdr, struct iovec *iov) +{ + char path[PATH_MAX]; + FILE *fp; + ssize_t len = 0; + + virtio_pstore_hdr_to_filename(kvm, hdr, path, sizeof(path)); + + fp = fopen(path, "a"); + if (fp == NULL) + return -1; + + len = fwrite(iov[1].iov_base, iov[1].iov_len, 1, fp); + if (len < 0 && errno == EAGAIN) + len = 0; + + fclose(fp); + return len; +} + +static ssize_t virtio_pstore_do_read(struct kvm *kvm, struct pstore_dev *pdev, + struct pstore_hdr *hdr, struct iovec *iov) +{ + char path[PATH_MAX]; + FILE *fp; + ssize_t len = 0; + struct stat stbuf; + struct dirent *dent; + + if (pdev->dir == NULL) + return 0; + + dent = readdir(pdev->dir); + while (dent) { + if (dent->d_name[0] != '.') + break; + dent = readdir(pdev->dir); + } + + if (dent == NULL) + return 0; + + virtio_pstore_filename_to_hdr(kvm, hdr, dent->d_name, path, sizeof(path)); + if (stat(path, &stbuf) < 0) + return -1; + + fp = fopen(path, "r"); + if (fp == NULL) + return -1; + + len = fread(iov[1].iov_base, 1, iov[1].iov_len, fp); + if (len < 0 && errno == EAGAIN) + len = 0; + + hdr->id = virtio_host_to_guest_u64(pdev->vqs, hdr->id); + hdr->flags = virtio_host_to_guest_u32(pdev->vqs, hdr->flags); + + hdr->time_sec = virtio_host_to_guest_u64(pdev->vqs, stbuf.st_ctim.tv_sec); + hdr->time_nsec = virtio_host_to_guest_u32(pdev->vqs, stbuf.st_ctim.tv_nsec); + + fclose(fp); + return len; +} + +static ssize_t virtio_pstore_do_erase(struct kvm *kvm, struct pstore_dev *pdev, + struct pstore_hdr *hdr, struct iovec *iov) +{ + char path[PATH_MAX]; + + virtio_pstore_hdr_to_filename(kvm, hdr, path, sizeof(path)); + + return unlink(path); +} + +static bool virtio_pstore_do_io_request(struct kvm *kvm, struct pstore_dev *pdev, + struct virt_queue *vq) +{ + struct iovec iov[VIRTIO_PSTORE_QUEUE_SIZE]; + struct pstore_hdr *hdr; + ssize_t len = 0; + u16 out, in, head; + + head = virt_queue__get_iov(vq, iov, &out, &in, kvm); + + hdr = iov[0].iov_base; + + switch (virtio_guest_to_host_u16(vq, hdr->cmd)) { + case VIRTIO_PSTORE_CMD_OPEN: + len = virtio_pstore_do_open(kvm, pdev, hdr, iov); + break; + case VIRTIO_PSTORE_CMD_READ: + len = virtio_pstore_do_read(kvm, pdev, hdr, iov); + break; + case VIRTIO_PSTORE_CMD_WRITE: + len = virtio_pstore_do_write(kvm, pdev, hdr, iov); + break; + case VIRTIO_PSTORE_CMD_CLOSE: + virtio_pstore_do_close(kvm, pdev, hdr, iov); + break; + case VIRTIO_PSTORE_CMD_ERASE: + len = virtio_pstore_do_erase(kvm, pdev, hdr, iov); + break; + default: + return false; + } + + if (len < 0) + return false; + + virt_queue__set_used_elem(vq, head, len); + + return true; +} + +static void virtio_pstore_do_io(struct kvm *kvm, void *param) +{ + struct pstore_dev_job *job = param; + struct virt_queue *vq = job->vq; + struct pstore_dev *pdev = job->pdev; + + while (virt_queue__available(vq)) + virtio_pstore_do_io_request(kvm, pdev, vq); + + pdev->vdev.ops->signal_vq(kvm, &pdev->vdev, vq - pdev->vqs); +} + +static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 page_size, u32 align, + u32 pfn) +{ + struct pstore_dev *pdev = dev; + struct virt_queue *queue; + struct pstore_dev_job *job; + void *p; + + compat__remove_message(compat_id); + + queue = &pdev->vqs[vq]; + queue->pfn = pfn; + p = virtio_get_vq(kvm, queue->pfn, page_size); + + job = &pdev->jobs[vq]; + + vring_init(&queue->vring, VIRTIO_PSTORE_QUEUE_SIZE, p, align); + + *job = (struct pstore_dev_job) { + .vq = queue, + .pdev = pdev, + }; + + thread_pool__init_job(&job->job_id, kvm, virtio_pstore_do_io, job); + + return 0; +} + +static int notify_vq(struct kvm *kvm, void *dev, u32 vq) +{ + struct pstore_dev *pdev = dev; + + thread_pool__do_job(&pdev->jobs[vq].job_id); + + return 0; +} + +static int get_pfn_vq(struct kvm *kvm, void *dev, u32 vq) +{ + struct pstore_dev *pdev = dev; + + return pdev->vqs[vq].pfn; +} + +static int get_size_vq(struct kvm *kvm, void *dev, u32 vq) +{ + return VIRTIO_PSTORE_QUEUE_SIZE; +} + +static int set_size_vq(struct kvm *kvm, void *dev, u32 vq, int size) +{ + /* FIXME: dynamic */ + return size; +} + +static struct virtio_ops pstore_dev_virtio_ops = { + .get_config = get_config, + .get_host_features = get_host_features, + .set_guest_features = set_guest_features, + .init_vq = init_vq, + .notify_vq = notify_vq, + .get_pfn_vq = get_pfn_vq, + .get_size_vq = get_size_vq, + .set_size_vq = set_size_vq, +}; + +int virtio_pstore__init(struct kvm *kvm) +{ + struct pstore_dev *pdev; + int r; + + if (!kvm->cfg.pstore_path) + return 0; + + pdev = malloc(sizeof(*pdev)); + if (pdev == NULL) + return -ENOMEM; + + r = virtio_init(kvm, pdev, &pdev->vdev, &pstore_dev_virtio_ops, + VIRTIO_DEFAULT_TRANS(kvm), PCI_DEVICE_ID_VIRTIO_PSTORE, + VIRTIO_ID_PSTORE, PCI_CLASS_PSTORE); + if (r < 0) + goto cleanup; + + list_add_tail(&pdev->list, &pdevs); + + if (compat_id == -1) + compat_id = virtio_compat_add_message("virtio-pstore", "CONFIG_VIRTIO_PSTORE"); + return 0; +cleanup: + free(pdev); + + return r; +} +virtio_dev_init(virtio_pstore__init); + +int virtio_pstore__exit(struct kvm *kvm) +{ + struct pstore_dev *pdev, *tmp; + + list_for_each_entry_safe(pdev, tmp, &pdevs, list) { + list_del(&pdev->list); + pdev->vdev.ops->exit(kvm, &pdev->vdev); + free(pdev); + } + + return 0; +} +virtio_dev_exit(virtio_pstore__exit); -- 2.8.0
Kees Cook
2016-Jul-18 05:12 UTC
[PATCH 1/3] virtio: Basic implementation of virtio pstore driver
On Sun, Jul 17, 2016 at 9:37 PM, Namhyung Kim <namhyung at kernel.org> wrote:> The virtio pstore driver provides interface to the pstore subsystem so > that the guest kernel's log/dump message can be saved on the host > machine. Users can access the log file directly on the host, or on the > guest at the next boot using pstore filesystem. It currently deals with > kernel log (printk) buffer only, but we can extend it to have other > information (like ftrace dump) later. > > It supports legacy PCI device using single order-2 page buffer. As all > operation of pstore is synchronous, it would be fine IMHO. However I > don't know how to make write operation synchronous since it's called > with a spinlock held (from any context including NMI). > > Cc: Paolo Bonzini <pbonzini at redhat.com> > Cc: Radim Kr?m?? <rkrcmar at redhat.com> > Cc: "Michael S. Tsirkin" <mst at redhat.com> > Cc: Anthony Liguori <aliguori at amazon.com> > Cc: Anton Vorontsov <anton at enomsg.org> > Cc: Colin Cross <ccross at android.com> > Cc: Kees Cook <keescook at chromium.org> > Cc: Tony Luck <tony.luck at intel.com> > Cc: Steven Rostedt <rostedt at goodmis.org> > Cc: Ingo Molnar <mingo at kernel.org> > Cc: Minchan Kim <minchan at kernel.org> > Cc: kvm at vger.kernel.org > Cc: qemu-devel at nongnu.org > Cc: virtualization at lists.linux-foundation.org > Signed-off-by: Namhyung Kim <namhyung at kernel.org>This looks great to me! I'd love to use this in qemu. (Right now I go through hoops to use the ramoops backend for testing.) Reviewed-by: Kees Cook <keescook at chromium.org> Notes below...> --- > drivers/virtio/Kconfig | 10 ++ > drivers/virtio/Makefile | 1 + > drivers/virtio/virtio_pstore.c | 317 +++++++++++++++++++++++++++++++++++++ > include/uapi/linux/Kbuild | 1 + > include/uapi/linux/virtio_ids.h | 1 + > include/uapi/linux/virtio_pstore.h | 53 +++++++ > 6 files changed, 383 insertions(+) > create mode 100644 drivers/virtio/virtio_pstore.c > create mode 100644 include/uapi/linux/virtio_pstore.h > > diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig > index 77590320d44c..8f0e6c796c12 100644 > --- a/drivers/virtio/Kconfig > +++ b/drivers/virtio/Kconfig > @@ -58,6 +58,16 @@ config VIRTIO_INPUT > > If unsure, say M. > > +config VIRTIO_PSTORE > + tristate "Virtio pstore driver" > + depends on VIRTIO > + depends on PSTORE > + ---help--- > + This driver supports virtio pstore devices to save/restore > + panic and oops messages on the host. > + > + If unsure, say M. > + > config VIRTIO_MMIO > tristate "Platform bus driver for memory mapped virtio devices" > depends on HAS_IOMEM && HAS_DMA > diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile > index 41e30e3dc842..bee68cb26d48 100644 > --- a/drivers/virtio/Makefile > +++ b/drivers/virtio/Makefile > @@ -5,3 +5,4 @@ virtio_pci-y := virtio_pci_modern.o virtio_pci_common.o > virtio_pci-$(CONFIG_VIRTIO_PCI_LEGACY) += virtio_pci_legacy.o > obj-$(CONFIG_VIRTIO_BALLOON) += virtio_balloon.o > obj-$(CONFIG_VIRTIO_INPUT) += virtio_input.o > +obj-$(CONFIG_VIRTIO_PSTORE) += virtio_pstore.o > diff --git a/drivers/virtio/virtio_pstore.c b/drivers/virtio/virtio_pstore.c > new file mode 100644 > index 000000000000..6fe62c0f1508 > --- /dev/null > +++ b/drivers/virtio/virtio_pstore.c > @@ -0,0 +1,317 @@ > +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt > + > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/pstore.h> > +#include <linux/virtio.h> > +#include <linux/virtio_config.h> > +#include <uapi/linux/virtio_ids.h> > +#include <uapi/linux/virtio_pstore.h> > + > +#define VIRT_PSTORE_ORDER 2 > +#define VIRT_PSTORE_BUFSIZE (4096 << VIRT_PSTORE_ORDER) > + > +struct virtio_pstore { > + struct virtio_device *vdev; > + struct virtqueue *vq; > + struct pstore_info pstore; > + struct virtio_pstore_hdr hdr; > + size_t buflen; > + u64 id; > + > + /* Waiting for host to ack */ > + wait_queue_head_t acked; > +}; > + > +static u16 to_virtio_type(struct virtio_pstore *vps, enum pstore_type_id type) > +{ > + u16 ret; > + > + switch (type) { > + case PSTORE_TYPE_DMESG: > + ret = cpu_to_virtio16(vps->vdev, VIRTIO_PSTORE_TYPE_DMESG); > + break; > + default: > + ret = cpu_to_virtio16(vps->vdev, VIRTIO_PSTORE_TYPE_UNKNOWN); > + break; > + }I would love to see this support PSTORE_TYPE_CONSOLE too. It should be relatively easy to add: I think it'd just be another virtio command?> + > + return ret; > +} > + > +static enum pstore_type_id from_virtio_type(struct virtio_pstore *vps, u16 type) > +{ > + enum pstore_type_id ret; > + > + switch (virtio16_to_cpu(vps->vdev, type)) { > + case VIRTIO_PSTORE_TYPE_DMESG: > + ret = PSTORE_TYPE_DMESG; > + break; > + default: > + ret = PSTORE_TYPE_UNKNOWN; > + break; > + } > + > + return ret; > +} > + > +static void virtpstore_ack(struct virtqueue *vq) > +{ > + struct virtio_pstore *vps = vq->vdev->priv; > + > + wake_up(&vps->acked); > +} > + > +static int virt_pstore_open(struct pstore_info *psi) > +{ > + struct virtio_pstore *vps = psi->data; > + struct virtio_pstore_hdr *hdr = &vps->hdr; > + struct scatterlist sg[1]; > + unsigned int len; > + > + hdr->cmd = cpu_to_virtio16(vps->vdev, VIRTIO_PSTORE_CMD_OPEN); > + > + sg_init_one(sg, hdr, sizeof(*hdr)); > + virtqueue_add_outbuf(vps->vq, sg, 1, vps, GFP_KERNEL); > + virtqueue_kick(vps->vq); > + > + wait_event(vps->acked, virtqueue_get_buf(vps->vq, &len)); > + return 0; > +} > + > +static int virt_pstore_close(struct pstore_info *psi) > +{ > + struct virtio_pstore *vps = psi->data; > + struct virtio_pstore_hdr *hdr = &vps->hdr; > + struct scatterlist sg[1]; > + unsigned int len; > + > + hdr->cmd = cpu_to_virtio16(vps->vdev, VIRTIO_PSTORE_CMD_CLOSE); > + > + sg_init_one(sg, hdr, sizeof(*hdr)); > + virtqueue_add_outbuf(vps->vq, sg, 1, vps, GFP_KERNEL); > + virtqueue_kick(vps->vq); > + > + wait_event(vps->acked, virtqueue_get_buf(vps->vq, &len)); > + return 0; > +} > + > +static ssize_t virt_pstore_read(u64 *id, enum pstore_type_id *type, > + int *count, struct timespec *time, > + char **buf, bool *compressed, > + struct pstore_info *psi) > +{ > + struct virtio_pstore *vps = psi->data; > + struct virtio_pstore_hdr *hdr = &vps->hdr; > + struct scatterlist sgi[1], sgo[1]; > + struct scatterlist *sgs[2] = { sgo, sgi }; > + unsigned int len; > + unsigned int flags; > + void *bf; > + > + hdr->cmd = cpu_to_virtio16(vps->vdev, VIRTIO_PSTORE_CMD_READ); > + > + sg_init_one(sgo, hdr, sizeof(*hdr)); > + sg_init_one(sgi, psi->buf, psi->bufsize); > + virtqueue_add_sgs(vps->vq, sgs, 1, 1, vps, GFP_KERNEL); > + virtqueue_kick(vps->vq); > + > + wait_event(vps->acked, virtqueue_get_buf(vps->vq, &len)); > + if (len == 0) > + return 0; > + > + bf = kmalloc(len, GFP_KERNEL); > + if (bf == NULL) > + return -ENOMEM; > + > + *id = virtio64_to_cpu(vps->vdev, hdr->id); > + *type = from_virtio_type(vps, hdr->type); > + > + flags = virtio32_to_cpu(vps->vdev, hdr->flags); > + *compressed = flags & VIRTIO_PSTORE_FL_COMPRESSED; > + *count = 1; > + > + time->tv_sec = virtio64_to_cpu(vps->vdev, hdr->time_sec); > + time->tv_nsec = virtio32_to_cpu(vps->vdev, hdr->time_nsec); > + > + memcpy(bf, psi->buf, len); > + *buf = bf; > + > + return len; > +} > + > +static int notrace virt_pstore_write(enum pstore_type_id type, > + enum kmsg_dump_reason reason, > + u64 *id, unsigned int part, int count, > + bool compressed, size_t size, > + struct pstore_info *psi) > +{ > + struct virtio_pstore *vps = psi->data; > + struct virtio_pstore_hdr *hdr = &vps->hdr; > + struct scatterlist sg[2]; > + unsigned int flags = compressed ? VIRTIO_PSTORE_FL_COMPRESSED : 0; > + > + *id = vps->id++; > + > + hdr->cmd = cpu_to_virtio16(vps->vdev, VIRTIO_PSTORE_CMD_WRITE); > + hdr->id = cpu_to_virtio64(vps->vdev, *id); > + hdr->flags = cpu_to_virtio32(vps->vdev, flags); > + hdr->type = to_virtio_type(vps, type); > + > + sg_init_table(sg, 2); > + sg_set_buf(&sg[0], hdr, sizeof(*hdr)); > + sg_set_buf(&sg[1], psi->buf, size); > + virtqueue_add_outbuf(vps->vq, sg, 2, vps, GFP_ATOMIC); > + virtqueue_kick(vps->vq); > + > + /* TODO: make it synchronous */ > + return 0;The down side to this being asynchronous is the lack of error reporting. Perhaps this could check hdr->type before queuing and error for any VIRTIO_PSTORE_TYPE_UNKNOWN message instead of trying to send it?> +} > + > +static int virt_pstore_erase(enum pstore_type_id type, u64 id, int count, > + struct timespec time, struct pstore_info *psi) > +{ > + struct virtio_pstore *vps = psi->data; > + struct virtio_pstore_hdr *hdr = &vps->hdr; > + struct scatterlist sg[1]; > + unsigned int len; > + > + hdr->cmd = cpu_to_virtio16(vps->vdev, VIRTIO_PSTORE_CMD_ERASE); > + hdr->id = cpu_to_virtio64(vps->vdev, id); > + hdr->type = to_virtio_type(vps, type); > + > + sg_init_one(sg, hdr, sizeof(*hdr)); > + virtqueue_add_outbuf(vps->vq, sg, 1, vps, GFP_KERNEL); > + virtqueue_kick(vps->vq); > + > + wait_event(vps->acked, virtqueue_get_buf(vps->vq, &len)); > + return 0; > +} > + > +static int virt_pstore_init(struct virtio_pstore *vps) > +{ > + struct pstore_info *psinfo = &vps->pstore; > + int err; > + > + vps->id = 0; > + vps->buflen = 0; > + psinfo->bufsize = VIRT_PSTORE_BUFSIZE; > + psinfo->buf = (void *)__get_free_pages(GFP_KERNEL, VIRT_PSTORE_ORDER); > + if (!psinfo->buf) { > + pr_err("cannot allocate pstore buffer\n"); > + return -ENOMEM; > + } > + > + psinfo->owner = THIS_MODULE; > + psinfo->name = "virtio"; > + psinfo->open = virt_pstore_open; > + psinfo->close = virt_pstore_close; > + psinfo->read = virt_pstore_read; > + psinfo->erase = virt_pstore_erase; > + psinfo->write = virt_pstore_write; > + psinfo->flags = PSTORE_FLAGS_FRAGILE;For console support, this flag would need to be dropped -- though I suspect you know that already.:)> + psinfo->data = vps; > + spin_lock_init(&psinfo->buf_lock); > + > + err = pstore_register(psinfo); > + if (err) > + kfree(psinfo->buf); > + > + return err; > +} > + > +static int virt_pstore_exit(struct virtio_pstore *vps) > +{ > + struct pstore_info *psinfo = &vps->pstore; > + > + pstore_unregister(psinfo); > + > + free_pages((unsigned long)psinfo->buf, VIRT_PSTORE_ORDER); > + psinfo->bufsize = 0; > + > + return 0; > +} > + > +static int virtpstore_probe(struct virtio_device *vdev) > +{ > + struct virtio_pstore *vps; > + int err; > + > + if (!vdev->config->get) { > + dev_err(&vdev->dev, "%s failure: config access disabled\n", > + __func__); > + return -EINVAL; > + } > + > + vdev->priv = vps = kmalloc(sizeof(*vps), GFP_KERNEL); > + if (!vps) { > + err = -ENOMEM; > + goto out; > + } > + > + vps->vdev = vdev; > + > + vps->vq = virtio_find_single_vq(vdev, virtpstore_ack, "pstore"); > + if (IS_ERR(vps->vq)) { > + err = PTR_ERR(vps->vq); > + goto out_free; > + } > + > + err = virt_pstore_init(vps); > + if (err) > + goto out_del_vq; > + > + init_waitqueue_head(&vps->acked); > + > + virtio_device_ready(vdev); > + dev_info(&vdev->dev, "virtio pstore driver init: ok\n"); > + > + return 0; > + > +out_del_vq: > + vdev->config->del_vqs(vdev); > +out_free: > + kfree(vps); > +out: > + dev_err(&vdev->dev, "virtio pstore driver init: failed with %d\n", err); > + return err; > +} > + > +static void virtpstore_remove(struct virtio_device *vdev) > +{ > + struct virtio_pstore *vps = vdev->priv; > + > + virt_pstore_exit(vps); > + > + /* Now we reset the device so we can clean up the queues. */ > + vdev->config->reset(vdev); > + > + vdev->config->del_vqs(vdev); > + > + kfree(vps); > +} > + > +static unsigned int features[] = { > +}; > + > +static struct virtio_device_id id_table[] = { > + { VIRTIO_ID_PSTORE, VIRTIO_DEV_ANY_ID }, > + { 0 }, > +}; > + > +static struct virtio_driver virtio_pstore_driver = { > + .driver.name = KBUILD_MODNAME, > + .driver.owner = THIS_MODULE, > + .feature_table = features, > + .feature_table_size = ARRAY_SIZE(features), > + .id_table = id_table, > + .probe = virtpstore_probe, > + .remove = virtpstore_remove, > +}; > + > +module_virtio_driver(virtio_pstore_driver); > +MODULE_DEVICE_TABLE(virtio, id_table); > + > +MODULE_LICENSE("GPL"); > +MODULE_AUTHOR("Namhyung Kim <namhyung at kernel.org>"); > +MODULE_DESCRIPTION("Virtio pstore driver"); > diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild > index 8bdae34d1f9a..57b0d08db322 100644 > --- a/include/uapi/linux/Kbuild > +++ b/include/uapi/linux/Kbuild > @@ -448,6 +448,7 @@ header-y += virtio_ids.h > header-y += virtio_input.h > header-y += virtio_net.h > header-y += virtio_pci.h > +header-y += virtio_pstore.h > header-y += virtio_ring.h > header-y += virtio_rng.h > header-y += virtio_scsi.h > diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h > index 77925f587b15..cba63225d85a 100644 > --- a/include/uapi/linux/virtio_ids.h > +++ b/include/uapi/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 19 /* virtio pstore */ > > #endif /* _LINUX_VIRTIO_IDS_H */ > diff --git a/include/uapi/linux/virtio_pstore.h b/include/uapi/linux/virtio_pstore.h > new file mode 100644 > index 000000000000..0aa1575ee35f > --- /dev/null > +++ b/include/uapi/linux/virtio_pstore.h > @@ -0,0 +1,53 @@ > +#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 <linux/types.h> > +#include <linux/virtio_types.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_hdr { > + __virtio64 id; > + __virtio32 flags; > + __virtio16 cmd; > + __virtio16 type; > + __virtio64 time_sec; > + __virtio32 time_nsec; > + __virtio32 unused; > +}; > + > +#endif /* _LINUX_VIRTIO_PSTORE_H */ > -- > 2.8.0 >Awesome! Can't wait to use it. :) -Kees -- Kees Cook Chrome OS & Brillo Security
Christian Borntraeger
2016-Jul-18 07:28 UTC
[PATCH 2/3] qemu: Implement virtio-pstore device
On 07/18/2016 06:37 AM, Namhyung Kim wrote: Can you do the virtio-mmio and virtio-ccw plumbing as well, or do you need help with that? [...]> diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c > index 2b34b43..8281b80 100644 > --- a/hw/virtio/virtio-pci.c > +++ b/hw/virtio/virtio-pci.c > @@ -2416,6 +2416,55 @@ 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); > +} > + > +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 +2534,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 e4548c2..b4c039f 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 */ > > @@ -311,6 +313,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 >[...]
Cornelia Huck
2016-Jul-18 07:54 UTC
[PATCH 1/3] virtio: Basic implementation of virtio pstore driver
On Mon, 18 Jul 2016 13:37:39 +0900 Namhyung Kim <namhyung at kernel.org> wrote:> The virtio pstore driver provides interface to the pstore subsystem so > that the guest kernel's log/dump message can be saved on the host > machine. Users can access the log file directly on the host, or on the > guest at the next boot using pstore filesystem. It currently deals with > kernel log (printk) buffer only, but we can extend it to have other > information (like ftrace dump) later.Like the idea.> > It supports legacy PCI device using single order-2 page buffer. As allThere should not be anything in there that limits this to pci, no?> operation of pstore is synchronous, it would be fine IMHO. However I > don't know how to make write operation synchronous since it's called > with a spinlock held (from any context including NMI). > > Cc: Paolo Bonzini <pbonzini at redhat.com> > Cc: Radim Kr?m?? <rkrcmar at redhat.com> > Cc: "Michael S. Tsirkin" <mst at redhat.com> > Cc: Anthony Liguori <aliguori at amazon.com> > Cc: Anton Vorontsov <anton at enomsg.org> > Cc: Colin Cross <ccross at android.com> > Cc: Kees Cook <keescook at chromium.org> > Cc: Tony Luck <tony.luck at intel.com> > Cc: Steven Rostedt <rostedt at goodmis.org> > Cc: Ingo Molnar <mingo at kernel.org> > Cc: Minchan Kim <minchan at kernel.org> > Cc: kvm at vger.kernel.org > Cc: qemu-devel at nongnu.org > Cc: virtualization at lists.linux-foundation.org > Signed-off-by: Namhyung Kim <namhyung at kernel.org> > --- > drivers/virtio/Kconfig | 10 ++ > drivers/virtio/Makefile | 1 + > drivers/virtio/virtio_pstore.c | 317 +++++++++++++++++++++++++++++++++++++ > include/uapi/linux/Kbuild | 1 + > include/uapi/linux/virtio_ids.h | 1 + > include/uapi/linux/virtio_pstore.h | 53 +++++++ > 6 files changed, 383 insertions(+) > create mode 100644 drivers/virtio/virtio_pstore.c > create mode 100644 include/uapi/linux/virtio_pstore.h >(...)> diff --git a/drivers/virtio/virtio_pstore.c b/drivers/virtio/virtio_pstore.c > new file mode 100644 > index 000000000000..6fe62c0f1508 > --- /dev/null > +++ b/drivers/virtio/virtio_pstore.c > @@ -0,0 +1,317 @@ > +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt > + > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/pstore.h> > +#include <linux/virtio.h> > +#include <linux/virtio_config.h> > +#include <uapi/linux/virtio_ids.h> > +#include <uapi/linux/virtio_pstore.h> > + > +#define VIRT_PSTORE_ORDER 2 > +#define VIRT_PSTORE_BUFSIZE (4096 << VIRT_PSTORE_ORDER)It may make sense to make the size of the buffer configurable through the config space. (...)> diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h > index 77925f587b15..cba63225d85a 100644 > --- a/include/uapi/linux/virtio_ids.h > +++ b/include/uapi/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 19 /* virtio pstore */This id is already used by one of the new device types queued but not yet in the standard. IIRC, 22 is the next free one. Speaking of the standard: I think it makes sense to at least reserve a device id for pstore, as the idea is sound. Maybe prepare a patch to the standard as well if you have time?> > #endif /* _LINUX_VIRTIO_IDS_H */
On Mon, Jul 18, 2016 at 01:37:40PM +0900, Namhyung Kim wrote:> From: Namhyung Kim <namhyung at gmail.com> > > 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-0.enc.z dmesg-1.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 implementation is synchronous (i.e. can pause guest code execution), does not handle write errors, and does not limit the amount of data the guest can write. This is sufficient for ad-hoc debugging and usage with trusted guests. If you want this to be available in environments where the guest isn't trusted then there must be a limit on how much the guest can write or some kind of log rotation.> > Cc: Paolo Bonzini <pbonzini at redhat.com> > Cc: Radim Kr?m?? <rkrcmar at redhat.com> > Cc: "Michael S. Tsirkin" <mst at redhat.com> > Cc: Anthony Liguori <aliguori at amazon.com> > Cc: Anton Vorontsov <anton at enomsg.org> > Cc: Colin Cross <ccross at android.com> > Cc: Kees Cook <keescook at chromium.org> > Cc: Tony Luck <tony.luck at intel.com> > Cc: Steven Rostedt <rostedt at goodmis.org> > Cc: Ingo Molnar <mingo at kernel.org> > Cc: Minchan Kim <minchan at kernel.org> > Cc: kvm at vger.kernel.org > Cc: qemu-devel at nongnu.org > Cc: virtualization at lists.linux-foundation.org > Signed-off-by: Namhyung Kim <namhyung at gmail.com> > --- > hw/virtio/Makefile.objs | 2 +- > hw/virtio/virtio-pci.c | 50 ++++ > hw/virtio/virtio-pci.h | 14 + > hw/virtio/virtio-pstore.c | 328 +++++++++++++++++++++ > include/hw/pci/pci.h | 1 + > include/hw/virtio/virtio-pstore.h | 30 ++ > include/standard-headers/linux/virtio_ids.h | 1 + > .../linux/{virtio_ids.h => virtio_pstore.h} | 48 +-- > qdev-monitor.c | 1 + > 9 files changed, 455 insertions(+), 20 deletions(-) > create mode 100644 hw/virtio/virtio-pstore.c > create mode 100644 include/hw/virtio/virtio-pstore.h > copy include/standard-headers/linux/{virtio_ids.h => virtio_pstore.h} (63%) > > 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 2b34b43..8281b80 100644 > --- a/hw/virtio/virtio-pci.c > +++ b/hw/virtio/virtio-pci.c > @@ -2416,6 +2416,55 @@ 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); > +} > + > +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 +2534,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 e4548c2..b4c039f 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 */ > > @@ -311,6 +313,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..98cee7f > --- /dev/null > +++ b/hw/virtio/virtio-pstore.c > @@ -0,0 +1,328 @@ > +/* > + * Virtio Pstore Device > + * > + * Copyright (C) 2016 LG Electronics > + * > + * Authors: > + * Namhyung Kim <namhyung at gmail.com> > + * > + * This work is licensed under the terms of the GNU GPL, version 2. 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 "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" > + > + > +static void virtio_pstore_to_filename(VirtIOPstore *s, char *buf, size_t sz, > + struct virtio_pstore_hdr *hdr) > +{ > + const char *basename; > + > + switch (hdr->type) {Missing le16_to_cpu()?> + case VIRTIO_PSTORE_TYPE_DMESG: > + basename = "dmesg"; > + break; > + default: > + basename = "unknown"; > + break; > + } > + > + snprintf(buf, sz, "%s/%s-%llu%s", s->directory, basename, > + (unsigned long long) hdr->id,Missing le64_to_cpu()?> + hdr->flags & VIRTIO_PSTORE_FL_COMPRESSED ? ".enc.z" : "");Missing le32_to_cpu()?> +} > + > +static void virtio_pstore_from_filename(VirtIOPstore *s, char *name, > + char *buf, size_t sz, > + struct virtio_pstore_hdr *hdr) > +{ > + size_t len = strlen(name); > + > + hdr->flags = 0; > + if (!strncmp(name + len - 6, ".enc.z", 6)) {Please use g_str_has_suffix(name, ".enc.z") to avoid accessing before the beginning of the string if the filename is shorter than 6 characters.> + hdr->flags |= VIRTIO_PSTORE_FL_COMPRESSED; > + } > + > + snprintf(buf, sz, "%s/%s", s->directory, name); > + > + if (!strncmp(name, "dmesg-", 6)) {g_str_has_prefix(name, "dmesg-")> + hdr->type = cpu_to_le16(VIRTIO_PSTORE_TYPE_DMESG); > + name += 6; > + } else if (!strncmp(name, "unknown-", 8)) {g_str_has_prefix(name, "unknown-")> + hdr->type = cpu_to_le16(VIRTIO_PSTORE_TYPE_UNKNOWN); > + name += 8; > + } > + > + qemu_strtoull(name, NULL, 0, &hdr->id); > +} > + > +static ssize_t virtio_pstore_do_open(VirtIOPstore *s) > +{ > + s->dir = opendir(s->directory); > + if (s->dir == NULL) { > + return -1; > + } > + > + return 0; > +} > + > +static ssize_t virtio_pstore_do_read(VirtIOPstore *s, void *buf, size_t sz, > + struct virtio_pstore_hdr *hdr) > +{ > + char path[PATH_MAX]; > + FILE *fp; > + ssize_t len; > + struct stat stbuf; > + struct dirent *dent; > + > + if (s->dir == NULL) { > + return -1; > + } > + > + dent = readdir(s->dir); > + while (dent) { > + if (dent->d_name[0] != '.') { > + break; > + } > + dent = readdir(s->dir); > + } > + > + if (dent == NULL) { > + return 0; > + } > + > + virtio_pstore_from_filename(s, dent->d_name, path, sizeof(path), hdr); > + if (stat(path, &stbuf) < 0) { > + return -1; > + }Please use fstat(fileno(fp), &stbuf) after opening the file instead. The race condition doesn't matter in this case but the race-free code is just as simple so it's one less thing someone reading the code has to worry about.> + > + fp = fopen(path, "r"); > + if (fp == NULL) { > + error_report("cannot open %s (%p %p)", path, s, s->directory); > + return -1; > + } > + > + len = fread(buf, 1, sz, fp); > + if (len < 0 && errno == EAGAIN) { > + len = 0; > + } > + > + hdr->id = cpu_to_le64(hdr->id); > + hdr->flags = cpu_to_le32(hdr->flags); > + hdr->time_sec = cpu_to_le64(stbuf.st_ctim.tv_sec); > + hdr->time_nsec = cpu_to_le32(stbuf.st_ctim.tv_nsec); > + > + fclose(fp); > + return len; > +} > + > +static ssize_t virtio_pstore_do_write(VirtIOPstore *s, void *buf, size_t sz, > + struct virtio_pstore_hdr *hdr) > +{ > + char path[PATH_MAX]; > + FILE *fp; > + > + virtio_pstore_to_filename(s, path, sizeof(path), hdr); > + > + fp = fopen(path, "w"); > + if (fp == NULL) { > + error_report("cannot open %s (%p %p)", path, s, s->directory); > + return -1; > + } > + fwrite(buf, 1, sz, fp); > + fclose(fp); > + > + return sz; > +} > + > +static ssize_t virtio_pstore_do_close(VirtIOPstore *s) > +{ > + if (s->dir == NULL) { > + return 0; > + } > + > + closedir(s->dir); > + s->dir = NULL; > + > + return 0; > +} > + > +static ssize_t virtio_pstore_do_erase(VirtIOPstore *s, > + struct virtio_pstore_hdr *hdr) > +{ > + char path[PATH_MAX]; > + > + virtio_pstore_to_filename(s, path, sizeof(path), hdr); > + > + return unlink(path); > +} > + > +static void virtio_pstore_handle_io(VirtIODevice *vdev, VirtQueue *vq) > +{ > + VirtIOPstore *s = VIRTIO_PSTORE(vdev); > + VirtQueueElement *elem; > + struct virtio_pstore_hdr *hdr; > + ssize_t len; > + > + for (;;) { > + elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); > + if (!elem) { > + return; > + } > + > + hdr = elem->out_sg[0].iov_base; > + if (elem->out_sg[0].iov_len != sizeof(*hdr)) { > + error_report("invalid header size: %u", > + (unsigned)elem->out_sg[0].iov_len); > + exit(1); > + }Please use iov_to_buf() instead of directly accessing out_sg[]. Virtio devices are not supposed to assume a particular iovec layout. In other words, virtio_pstore_hdr could be split across multiple out_sg[] iovecs. You must also copy in data (similar to Linux syscall implementations) to prevent the guest from modifying data while the command is processed. Such race conditions could lead to security bugs.> + > + switch (hdr->cmd) { > + case VIRTIO_PSTORE_CMD_OPEN: > + len = virtio_pstore_do_open(s); > + break; > + case VIRTIO_PSTORE_CMD_READ: > + len = virtio_pstore_do_read(s, elem->in_sg[0].iov_base, > + elem->in_sg[0].iov_len, hdr);Same issue with iovec layout for in_sg[] here. The guest driver must be able to submit any in_sg[] iovec array and the device cannot assume in_sg[0] is the only iovec to fill.> + break; > + case VIRTIO_PSTORE_CMD_WRITE: > + len = virtio_pstore_do_write(s, elem->out_sg[1].iov_base, > + elem->out_sg[1].iov_len, hdr); > + break; > + case VIRTIO_PSTORE_CMD_CLOSE: > + len = virtio_pstore_do_close(s); > + break; > + case VIRTIO_PSTORE_CMD_ERASE: > + len = virtio_pstore_do_erase(s, hdr); > + break; > + default: > + len = -1; > + break; > + } > + > + if (len < 0) { > + return; > + } > + > + virtqueue_push(vq, elem, len); > + > + virtio_notify(vdev, vq); > + g_free(elem); > + } > +} > + > +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, 0); > + > + s->vq = 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 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 = strdup(value);Please use g_strdup() since this is paired with g_free(). Or even simpler would be s->directory = value and do not g_free(value) below.> + > + g_free(value); > +} > + > +static void pstore_release_directory(Object *obj, const char *name, > + void *opaque) > +{ > + VirtIOPstore *s = opaque; > + > + g_free(s->directory); > + s->directory = NULL; > +} > + > +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); > +} > + > +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_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 9ed1624..5689c6f 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..74cd1f6 > --- /dev/null > +++ b/include/hw/virtio/virtio-pstore.h > @@ -0,0 +1,30 @@ > +/* > + * Virtio Pstore Support > + * > + * Authors: > + * Namhyung Kim <namhyung at gmail.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_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 *vq; > + char *directory; > + DIR *dir; > +} VirtIOPstore; > + > +#endif > diff --git a/include/standard-headers/linux/virtio_ids.h b/include/standard-headers/linux/virtio_ids.h > index 77925f5..cba6322 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 19 /* virtio pstore */19 has already been reserved. 22 is the next free ID (vsock, crypto, and sdm are currently under review and already use 19, 20, and 21). Please send a VIRTIO draft specification to virtio-dev at lists.oasis-open.org. You can find information on the VIRTIO standards process here: https://www.oasis-open.org/committees/tc_home.php?wg_abbrev=virtio -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 473 bytes Desc: not available URL: <http://lists.linuxfoundation.org/pipermail/virtualization/attachments/20160718/6b0a291c/attachment-0001.sig>
Reasonably Related Threads
- [PATCH 1/3] virtio: Basic implementation of virtio pstore driver
- [PATCH 1/3] virtio: Basic implementation of virtio pstore driver
- [PATCH 1/3] virtio: Basic implementation of virtio pstore driver
- [PATCH 1/3] virtio: Basic implementation of virtio pstore driver
- [PATCH 1/3] virtio: Basic implementation of virtio pstore driver