linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC/PATCHSET 0/3] virtio-pstore: Implement virtio pstore device
@ 2016-07-18  4:37 Namhyung Kim
  2016-07-18  4:37 ` [PATCH 1/3] virtio: Basic implementation of virtio pstore driver Namhyung Kim
                   ` (2 more replies)
  0 siblings, 3 replies; 35+ messages in thread
From: Namhyung Kim @ 2016-07-18  4:37 UTC (permalink / raw)
  To: LKML
  Cc: Paolo Bonzini, Radim Krčmář,
	Michael S. Tsirkin, Anthony Liguori, Anton Vorontsov,
	Colin Cross, Kees Cook, Tony Luck, Steven Rostedt, Ingo Molnar,
	Minchan Kim, kvm, qemu-devel, virtualization

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@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@redhat.com>
Cc: Radim Krčmář <rkrcmar@redhat.com>
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Cc: Anthony Liguori <aliguori@amazon.com>
Cc: Anton Vorontsov <anton@enomsg.org>
Cc: Colin Cross <ccross@android.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Minchan Kim <minchan@kernel.org>
Cc: kvm@vger.kernel.org
Cc: qemu-devel@nongnu.org
Cc: virtualization@lists.linux-foundation.org

[1] https://lkml.org/lkml/2016/7/1/6


Thanks,
Namhyung

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH 1/3] virtio: Basic implementation of virtio pstore driver
  2016-07-18  4:37 [RFC/PATCHSET 0/3] virtio-pstore: Implement virtio pstore device Namhyung Kim
@ 2016-07-18  4:37 ` Namhyung Kim
  2016-07-18  5:12   ` Kees Cook
  2016-07-18  7:54   ` Cornelia Huck
  2016-07-18  4:37 ` [PATCH 2/3] qemu: Implement virtio-pstore device Namhyung Kim
  2016-07-18  4:37 ` [PATCH 3/3] kvmtool: " Namhyung Kim
  2 siblings, 2 replies; 35+ messages in thread
From: Namhyung Kim @ 2016-07-18  4:37 UTC (permalink / raw)
  To: LKML
  Cc: Paolo Bonzini, Radim Krčmář,
	Michael S. Tsirkin, Anthony Liguori, Anton Vorontsov,
	Colin Cross, Kees Cook, Tony Luck, Steven Rostedt, Ingo Molnar,
	Minchan Kim, kvm, qemu-devel, virtualization

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@redhat.com>
Cc: Radim Krčmář <rkrcmar@redhat.com>
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Cc: Anthony Liguori <aliguori@amazon.com>
Cc: Anton Vorontsov <anton@enomsg.org>
Cc: Colin Cross <ccross@android.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Minchan Kim <minchan@kernel.org>
Cc: kvm@vger.kernel.org
Cc: qemu-devel@nongnu.org
Cc: virtualization@lists.linux-foundation.org
Signed-off-by: Namhyung Kim <namhyung@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@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

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 2/3] qemu: Implement virtio-pstore device
  2016-07-18  4:37 [RFC/PATCHSET 0/3] virtio-pstore: Implement virtio pstore device Namhyung Kim
  2016-07-18  4:37 ` [PATCH 1/3] virtio: Basic implementation of virtio pstore driver Namhyung Kim
@ 2016-07-18  4:37 ` Namhyung Kim
  2016-07-18  7:28   ` Christian Borntraeger
  2016-07-18 10:03   ` Stefan Hajnoczi
  2016-07-18  4:37 ` [PATCH 3/3] kvmtool: " Namhyung Kim
  2 siblings, 2 replies; 35+ messages in thread
From: Namhyung Kim @ 2016-07-18  4:37 UTC (permalink / raw)
  To: LKML
  Cc: Namhyung Kim, Paolo Bonzini, Radim Krčmář,
	Michael S. Tsirkin, Anthony Liguori, Anton Vorontsov,
	Colin Cross, Kees Cook, Tony Luck, Steven Rostedt, Ingo Molnar,
	Minchan Kim, kvm, qemu-devel, virtualization

From: Namhyung Kim <namhyung@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@redhat.com>
Cc: Radim Krčmář <rkrcmar@redhat.com>
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Cc: Anthony Liguori <aliguori@amazon.com>
Cc: Anton Vorontsov <anton@enomsg.org>
Cc: Colin Cross <ccross@android.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Minchan Kim <minchan@kernel.org>
Cc: kvm@vger.kernel.org
Cc: qemu-devel@nongnu.org
Cc: virtualization@lists.linux-foundation.org
Signed-off-by: Namhyung Kim <namhyung@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@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@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

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 3/3] kvmtool: Implement virtio-pstore device
  2016-07-18  4:37 [RFC/PATCHSET 0/3] virtio-pstore: Implement virtio pstore device Namhyung Kim
  2016-07-18  4:37 ` [PATCH 1/3] virtio: Basic implementation of virtio pstore driver Namhyung Kim
  2016-07-18  4:37 ` [PATCH 2/3] qemu: Implement virtio-pstore device Namhyung Kim
@ 2016-07-18  4:37 ` Namhyung Kim
  2 siblings, 0 replies; 35+ messages in thread
From: Namhyung Kim @ 2016-07-18  4:37 UTC (permalink / raw)
  To: LKML
  Cc: Paolo Bonzini, Radim Krčmář,
	Michael S. Tsirkin, Anthony Liguori, Anton Vorontsov,
	Colin Cross, Kees Cook, Tony Luck, Steven Rostedt, Ingo Molnar,
	Minchan Kim, kvm, virtualization

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@redhat.com>
Cc: Radim Krčmář <rkrcmar@redhat.com>
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Cc: Anthony Liguori <aliguori@amazon.com>
Cc: Anton Vorontsov <anton@enomsg.org>
Cc: Colin Cross <ccross@android.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Minchan Kim <minchan@kernel.org>
Cc: kvm@vger.kernel.org
Cc: virtualization@lists.linux-foundation.org
Signed-off-by: Namhyung Kim <namhyung@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

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* Re: [PATCH 1/3] virtio: Basic implementation of virtio pstore driver
  2016-07-18  4:37 ` [PATCH 1/3] virtio: Basic implementation of virtio pstore driver Namhyung Kim
@ 2016-07-18  5:12   ` Kees Cook
  2016-07-18  5:50     ` Namhyung Kim
  2016-07-18  7:54   ` Cornelia Huck
  1 sibling, 1 reply; 35+ messages in thread
From: Kees Cook @ 2016-07-18  5:12 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: LKML, Paolo Bonzini, Radim Krčmář,
	Michael S. Tsirkin, Anthony Liguori, Anton Vorontsov,
	Colin Cross, Tony Luck, Steven Rostedt, Ingo Molnar, Minchan Kim,
	KVM, qemu-devel, virtualization

On Sun, Jul 17, 2016 at 9:37 PM, Namhyung Kim <namhyung@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@redhat.com>
> Cc: Radim Krčmář <rkrcmar@redhat.com>
> Cc: "Michael S. Tsirkin" <mst@redhat.com>
> Cc: Anthony Liguori <aliguori@amazon.com>
> Cc: Anton Vorontsov <anton@enomsg.org>
> Cc: Colin Cross <ccross@android.com>
> Cc: Kees Cook <keescook@chromium.org>
> Cc: Tony Luck <tony.luck@intel.com>
> Cc: Steven Rostedt <rostedt@goodmis.org>
> Cc: Ingo Molnar <mingo@kernel.org>
> Cc: Minchan Kim <minchan@kernel.org>
> Cc: kvm@vger.kernel.org
> Cc: qemu-devel@nongnu.org
> Cc: virtualization@lists.linux-foundation.org
> Signed-off-by: Namhyung Kim <namhyung@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@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@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

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 1/3] virtio: Basic implementation of virtio pstore driver
  2016-07-18  5:12   ` Kees Cook
@ 2016-07-18  5:50     ` Namhyung Kim
  2016-07-18 17:50       ` Kees Cook
  0 siblings, 1 reply; 35+ messages in thread
From: Namhyung Kim @ 2016-07-18  5:50 UTC (permalink / raw)
  To: Kees Cook
  Cc: LKML, Paolo Bonzini, Radim Kr??m????,
	Michael S. Tsirkin, Anthony Liguori, Anton Vorontsov,
	Colin Cross, Tony Luck, Steven Rostedt, Ingo Molnar, Minchan Kim,
	KVM, qemu-devel, virtualization

Hello,

On Sun, Jul 17, 2016 at 10:12:26PM -0700, Kees Cook wrote:
> On Sun, Jul 17, 2016 at 9:37 PM, Namhyung Kim <namhyung@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@redhat.com>
> > Cc: Radim Kr??m???? <rkrcmar@redhat.com>
> > Cc: "Michael S. Tsirkin" <mst@redhat.com>
> > Cc: Anthony Liguori <aliguori@amazon.com>
> > Cc: Anton Vorontsov <anton@enomsg.org>
> > Cc: Colin Cross <ccross@android.com>
> > Cc: Kees Cook <keescook@chromium.org>
> > Cc: Tony Luck <tony.luck@intel.com>
> > Cc: Steven Rostedt <rostedt@goodmis.org>
> > Cc: Ingo Molnar <mingo@kernel.org>
> > Cc: Minchan Kim <minchan@kernel.org>
> > Cc: kvm@vger.kernel.org
> > Cc: qemu-devel@nongnu.org
> > Cc: virtualization@lists.linux-foundation.org
> > Signed-off-by: Namhyung Kim <namhyung@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@chromium.org>

Thank you!

> 
> Notes below...
>

[SNIP]
> > +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?

Do you want to append the data to the host file as guest does
printk()?  I think it needs some kind of buffer management, but it's
not hard to add IMHO.


> 
> > +
> > +       return ret;
> > +}
> > +

[SNIP]
> > +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?

I cannot follow, sorry.  Could you please elaborate it more?


> 
> > +}
> > +
> > +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.:)

Yep, I intentionally support DMESG type only in this patchset for
simplicity.  Others could be added later. :)


> 
> > +       psinfo->data  = vps;
> > +       spin_lock_init(&psinfo->buf_lock);
> > +
> > +       err = pstore_register(psinfo);
> > +       if (err)
> > +               kfree(psinfo->buf);
> > +
> > +       return err;
> > +}

[SNIP]
> 
> Awesome! Can't wait to use it. :)

Thanks for your review! :)

Thanks,
Namhyung

> 
> -Kees
> 
> -- 
> Kees Cook
> Chrome OS & Brillo Security

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 2/3] qemu: Implement virtio-pstore device
  2016-07-18  4:37 ` [PATCH 2/3] qemu: Implement virtio-pstore device Namhyung Kim
@ 2016-07-18  7:28   ` Christian Borntraeger
  2016-07-18  8:33     ` Namhyung Kim
  2016-07-18 10:03   ` Stefan Hajnoczi
  1 sibling, 1 reply; 35+ messages in thread
From: Christian Borntraeger @ 2016-07-18  7:28 UTC (permalink / raw)
  To: Namhyung Kim, LKML
  Cc: Namhyung Kim, Paolo Bonzini, Radim Krčmář,
	Michael S. Tsirkin, Anthony Liguori, Anton Vorontsov,
	Colin Cross, Kees Cook, Tony Luck, Steven Rostedt, Ingo Molnar,
	Minchan Kim, kvm, qemu-devel, virtualization

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
> 

[...]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 1/3] virtio: Basic implementation of virtio pstore driver
  2016-07-18  4:37 ` [PATCH 1/3] virtio: Basic implementation of virtio pstore driver Namhyung Kim
  2016-07-18  5:12   ` Kees Cook
@ 2016-07-18  7:54   ` Cornelia Huck
  2016-07-18  8:29     ` Namhyung Kim
  1 sibling, 1 reply; 35+ messages in thread
From: Cornelia Huck @ 2016-07-18  7:54 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: LKML, Paolo Bonzini, Radim Krčmář,
	Michael S. Tsirkin, Anthony Liguori, Anton Vorontsov,
	Colin Cross, Kees Cook, Tony Luck, Steven Rostedt, Ingo Molnar,
	Minchan Kim, kvm, qemu-devel, virtualization

On Mon, 18 Jul 2016 13:37:39 +0900
Namhyung Kim <namhyung@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 all

There 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@redhat.com>
> Cc: Radim Krčmář <rkrcmar@redhat.com>
> Cc: "Michael S. Tsirkin" <mst@redhat.com>
> Cc: Anthony Liguori <aliguori@amazon.com>
> Cc: Anton Vorontsov <anton@enomsg.org>
> Cc: Colin Cross <ccross@android.com>
> Cc: Kees Cook <keescook@chromium.org>
> Cc: Tony Luck <tony.luck@intel.com>
> Cc: Steven Rostedt <rostedt@goodmis.org>
> Cc: Ingo Molnar <mingo@kernel.org>
> Cc: Minchan Kim <minchan@kernel.org>
> Cc: kvm@vger.kernel.org
> Cc: qemu-devel@nongnu.org
> Cc: virtualization@lists.linux-foundation.org
> Signed-off-by: Namhyung Kim <namhyung@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 */

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 1/3] virtio: Basic implementation of virtio pstore driver
  2016-07-18  7:54   ` Cornelia Huck
@ 2016-07-18  8:29     ` Namhyung Kim
  2016-07-18  9:02       ` Cornelia Huck
  0 siblings, 1 reply; 35+ messages in thread
From: Namhyung Kim @ 2016-07-18  8:29 UTC (permalink / raw)
  To: Cornelia Huck
  Cc: LKML, Paolo Bonzini, Radim Kr??m????,
	Michael S. Tsirkin, Anthony Liguori, Anton Vorontsov,
	Colin Cross, Kees Cook, Tony Luck, Steven Rostedt, Ingo Molnar,
	Minchan Kim, kvm, qemu-devel, virtualization

Hello,

On Mon, Jul 18, 2016 at 09:54:39AM +0200, Cornelia Huck wrote:
> On Mon, 18 Jul 2016 13:37:39 +0900
> Namhyung Kim <namhyung@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.

Thanks!

> 
> > 
> > It supports legacy PCI device using single order-2 page buffer.  As all
> 
> There should not be anything in there that limits this to pci, no?

Yep, there's no restriction AFAIK.  I just choose it to implement the poc
code quickly.

> 
> > 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@redhat.com>
> > Cc: Radim Kr??m???? <rkrcmar@redhat.com>
> > Cc: "Michael S. Tsirkin" <mst@redhat.com>
> > Cc: Anthony Liguori <aliguori@amazon.com>
> > Cc: Anton Vorontsov <anton@enomsg.org>
> > Cc: Colin Cross <ccross@android.com>
> > Cc: Kees Cook <keescook@chromium.org>
> > Cc: Tony Luck <tony.luck@intel.com>
> > Cc: Steven Rostedt <rostedt@goodmis.org>
> > Cc: Ingo Molnar <mingo@kernel.org>
> > Cc: Minchan Kim <minchan@kernel.org>
> > Cc: kvm@vger.kernel.org
> > Cc: qemu-devel@nongnu.org
> > Cc: virtualization@lists.linux-foundation.org
> > Signed-off-by: Namhyung Kim <namhyung@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.

Right.  I'm considering it too, but it needs a buffer larger than
kmsg_bytes (= 10K) to work properly in the current implementation.  As
this version is just to verify the idea is sane and useful, I used a
fixed size buffer.  Will change in the next version.

> 
> (...)
> 
> > 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.

Ok, will update.

> 
> 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?

I'd love to.  As I mentioned earlier, I don't have enough knowledge in
this area.  Could you please provide some links about how can I do that?

Thanks,
Namhyung

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 2/3] qemu: Implement virtio-pstore device
  2016-07-18  7:28   ` Christian Borntraeger
@ 2016-07-18  8:33     ` Namhyung Kim
  0 siblings, 0 replies; 35+ messages in thread
From: Namhyung Kim @ 2016-07-18  8:33 UTC (permalink / raw)
  To: Christian Borntraeger
  Cc: LKML, Paolo Bonzini, Radim Kr??m????,
	Michael S. Tsirkin, Anthony Liguori, Anton Vorontsov,
	Colin Cross, Kees Cook, Tony Luck, Steven Rostedt, Ingo Molnar,
	Minchan Kim, kvm, qemu-devel, virtualization

Hello,

On Mon, Jul 18, 2016 at 09:28:42AM +0200, Christian Borntraeger wrote:
> 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?

Any help would be greatly appreciated!

Thanks,
Namhyung


> 
> [...]
> > 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
> > 
> 
> [...]
> 

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 1/3] virtio: Basic implementation of virtio pstore driver
  2016-07-18  8:29     ` Namhyung Kim
@ 2016-07-18  9:02       ` Cornelia Huck
  0 siblings, 0 replies; 35+ messages in thread
From: Cornelia Huck @ 2016-07-18  9:02 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: LKML, Paolo Bonzini, Radim Kr??m????,
	Michael S. Tsirkin, Anthony Liguori, Anton Vorontsov,
	Colin Cross, Kees Cook, Tony Luck, Steven Rostedt, Ingo Molnar,
	Minchan Kim, kvm, qemu-devel, virtualization

On Mon, 18 Jul 2016 17:29:55 +0900
Namhyung Kim <namhyung@kernel.org> wrote:

> On Mon, Jul 18, 2016 at 09:54:39AM +0200, Cornelia Huck wrote:
> > On Mon, 18 Jul 2016 13:37:39 +0900
> > Namhyung Kim <namhyung@kernel.org> wrote:

> > > +#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.
> 
> Right.  I'm considering it too, but it needs a buffer larger than
> kmsg_bytes (= 10K) to work properly in the current implementation.  As
> this version is just to verify the idea is sane and useful, I used a
> fixed size buffer.  Will change in the next version.

Sure, that makes sense for a prototype. We can guard any config space
entry with a feature bit, but this one makes sense to add from the
beginning.

> > 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?
> 
> I'd love to.  As I mentioned earlier, I don't have enough knowledge in
> this area.  Could you please provide some links about how can I do that?

See the virtio page at OASIS
(https://www.oasis-open.org/committees/tc_home.php?wg_abbrev=virtio)
for a link to our subversion (yes...) repository. Just do two patches:
one to reserve a device id, and one that specifies how device and
driver work. (For examples, look at the proposed device types that have
been posted to the virtualization lists, e.g. virtio-crypto or
virtio-sdm). You just need to be patient, we're currently a bit
stalled...

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 2/3] qemu: Implement virtio-pstore device
  2016-07-18  4:37 ` [PATCH 2/3] qemu: Implement virtio-pstore device Namhyung Kim
  2016-07-18  7:28   ` Christian Borntraeger
@ 2016-07-18 10:03   ` Stefan Hajnoczi
  2016-07-18 14:21     ` Namhyung Kim
  2016-07-19 15:48     ` Namhyung Kim
  1 sibling, 2 replies; 35+ messages in thread
From: Stefan Hajnoczi @ 2016-07-18 10:03 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: LKML, Namhyung Kim, Paolo Bonzini, Radim Krčmář,
	Michael S. Tsirkin, Anthony Liguori, Anton Vorontsov,
	Colin Cross, Kees Cook, Tony Luck, Steven Rostedt, Ingo Molnar,
	Minchan Kim, kvm, qemu-devel, virtualization

[-- Attachment #1: Type: text/plain, Size: 19889 bytes --]

On Mon, Jul 18, 2016 at 01:37:40PM +0900, Namhyung Kim wrote:
> From: Namhyung Kim <namhyung@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@redhat.com>
> Cc: Radim Krčmář <rkrcmar@redhat.com>
> Cc: "Michael S. Tsirkin" <mst@redhat.com>
> Cc: Anthony Liguori <aliguori@amazon.com>
> Cc: Anton Vorontsov <anton@enomsg.org>
> Cc: Colin Cross <ccross@android.com>
> Cc: Kees Cook <keescook@chromium.org>
> Cc: Tony Luck <tony.luck@intel.com>
> Cc: Steven Rostedt <rostedt@goodmis.org>
> Cc: Ingo Molnar <mingo@kernel.org>
> Cc: Minchan Kim <minchan@kernel.org>
> Cc: kvm@vger.kernel.org
> Cc: qemu-devel@nongnu.org
> Cc: virtualization@lists.linux-foundation.org
> Signed-off-by: Namhyung Kim <namhyung@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@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@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@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

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 2/3] qemu: Implement virtio-pstore device
  2016-07-18 10:03   ` Stefan Hajnoczi
@ 2016-07-18 14:21     ` Namhyung Kim
  2016-07-20  8:29       ` Stefan Hajnoczi
  2016-07-19 15:48     ` Namhyung Kim
  1 sibling, 1 reply; 35+ messages in thread
From: Namhyung Kim @ 2016-07-18 14:21 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: LKML, Paolo Bonzini, Radim Kr??m????,
	Michael S. Tsirkin, Anthony Liguori, Anton Vorontsov,
	Colin Cross, Kees Cook, Tony Luck, Steven Rostedt, Ingo Molnar,
	Minchan Kim, kvm, qemu-devel, virtualization

Hello,

On Mon, Jul 18, 2016 at 11:03:53AM +0100, Stefan Hajnoczi wrote:
> On Mon, Jul 18, 2016 at 01:37:40PM +0900, Namhyung Kim wrote:
> > From: Namhyung Kim <namhyung@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.

Right.  The synchronous IO is required by the pstore subsystem
implementation AFAIK (it uses a single psinfo->buf in the loop).  And
I agree that it should have a way to handle write errors and to limit
amount of data.

> 
> > 
> > Cc: Paolo Bonzini <pbonzini@redhat.com>
> > Cc: Radim Kr??m???? <rkrcmar@redhat.com>
> > Cc: "Michael S. Tsirkin" <mst@redhat.com>
> > Cc: Anthony Liguori <aliguori@amazon.com>
> > Cc: Anton Vorontsov <anton@enomsg.org>
> > Cc: Colin Cross <ccross@android.com>
> > Cc: Kees Cook <keescook@chromium.org>
> > Cc: Tony Luck <tony.luck@intel.com>
> > Cc: Steven Rostedt <rostedt@goodmis.org>
> > Cc: Ingo Molnar <mingo@kernel.org>
> > Cc: Minchan Kim <minchan@kernel.org>
> > Cc: kvm@vger.kernel.org
> > Cc: qemu-devel@nongnu.org
> > Cc: virtualization@lists.linux-foundation.org
> > Signed-off-by: Namhyung Kim <namhyung@gmail.com>
> > ---

[SNIP]
> > +
> > +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()?

Oops, will fix.

> 
> > +}
> > +
> > +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.

Ah, ok.

> 
> > +        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-")

Will change.

> 
> > +        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.

Fair enough.

> 
> > +
> > +    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;
> > +}
> > +

[SNIP]
> > +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.

I got it.

> 
> 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.

Ok, but this assumes the operation is synchronous.  I agree on your
opinion if I could make it async.

> 
> > +
> > +        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.

Ok.

> 
> > +            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.

Ok, I was not sure whether I could use it without alloc/free pair.
Will do it simpler way then. :)


> 
> > +
> > +    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@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).

I wasn't aware of the ongoing works but Cornelia already told me about
it.  Will update.

> 
> Please send a VIRTIO draft specification to
> virtio-dev@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

Thank you very much for this information and your detailed review!
I'll take a look at the virtio standards process too.

Thanks,
Namhyung

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 1/3] virtio: Basic implementation of virtio pstore driver
  2016-07-18  5:50     ` Namhyung Kim
@ 2016-07-18 17:50       ` Kees Cook
  2016-07-19 13:43         ` Namhyung Kim
  0 siblings, 1 reply; 35+ messages in thread
From: Kees Cook @ 2016-07-18 17:50 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: LKML, Paolo Bonzini, Radim Kr??m????,
	Michael S. Tsirkin, Anthony Liguori, Anton Vorontsov,
	Colin Cross, Tony Luck, Steven Rostedt, Ingo Molnar, Minchan Kim,
	KVM, qemu-devel, virtualization

On Sun, Jul 17, 2016 at 10:50 PM, Namhyung Kim <namhyung@kernel.org> wrote:
> Hello,
>
> On Sun, Jul 17, 2016 at 10:12:26PM -0700, Kees Cook wrote:
>> On Sun, Jul 17, 2016 at 9:37 PM, Namhyung Kim <namhyung@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@redhat.com>
>> > Cc: Radim Kr??m???? <rkrcmar@redhat.com>
>> > Cc: "Michael S. Tsirkin" <mst@redhat.com>
>> > Cc: Anthony Liguori <aliguori@amazon.com>
>> > Cc: Anton Vorontsov <anton@enomsg.org>
>> > Cc: Colin Cross <ccross@android.com>
>> > Cc: Kees Cook <keescook@chromium.org>
>> > Cc: Tony Luck <tony.luck@intel.com>
>> > Cc: Steven Rostedt <rostedt@goodmis.org>
>> > Cc: Ingo Molnar <mingo@kernel.org>
>> > Cc: Minchan Kim <minchan@kernel.org>
>> > Cc: kvm@vger.kernel.org
>> > Cc: qemu-devel@nongnu.org
>> > Cc: virtualization@lists.linux-foundation.org
>> > Signed-off-by: Namhyung Kim <namhyung@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@chromium.org>
>
> Thank you!
>
>>
>> Notes below...
>>
>
> [SNIP]
>> > +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?
>
> Do you want to append the data to the host file as guest does
> printk()?  I think it needs some kind of buffer management, but it's
> not hard to add IMHO.

Well, with most pstore backends, the buffer size is limited, so it
tends to be a circular buffer of some sort. I think whatever you
choose to do is fine (I saw the various mentions of resource limits in
the qemu part of this thread), as long as the last N bytes of console
can be seen on the host side, where N is some portion of the memory
set aside for the log. (I don't mind the idea of an unlimited console
log either, but I suspect that will not be accepted on the qemu
side...)

> [SNIP]
>> > +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?
>
> I cannot follow, sorry.  Could you please elaborate it more?

The mention you have here of "TODO: make it synchronous" made me think
about what effects that could have. If a pstore_write() were issued
for a type other than DMESG, the above code would send it through
virtio anyway. No error reporting is possible unless this is
synchronous, but the only error here would simply be "I don't know
what anything except DMESG is", so maybe this code could refuse to
forward anything with type UNKNOWN in the first place. (Just an idea:
I don't think there's anything very wrong here. It just seemed like a
potential improvement.)

>> > +}
>> > +
>> > +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.:)
>
> Yep, I intentionally support DMESG type only in this patchset for
> simplicity.  Others could be added later. :)

Cool by me. :)

>
>
>>
>> > +       psinfo->data  = vps;
>> > +       spin_lock_init(&psinfo->buf_lock);
>> > +
>> > +       err = pstore_register(psinfo);
>> > +       if (err)
>> > +               kfree(psinfo->buf);
>> > +
>> > +       return err;
>> > +}
>
> [SNIP]
>>
>> Awesome! Can't wait to use it. :)
>
> Thanks for your review! :)

You're welcome! :)

-Kees

-- 
Kees Cook
Chrome OS & Brillo Security

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 1/3] virtio: Basic implementation of virtio pstore driver
  2016-07-18 17:50       ` Kees Cook
@ 2016-07-19 13:43         ` Namhyung Kim
  2016-07-19 15:32           ` Namhyung Kim
  2016-07-20 12:56           ` Namhyung Kim
  0 siblings, 2 replies; 35+ messages in thread
From: Namhyung Kim @ 2016-07-19 13:43 UTC (permalink / raw)
  To: Kees Cook
  Cc: LKML, Paolo Bonzini, Radim Kr??m????,
	Michael S. Tsirkin, Anthony Liguori, Anton Vorontsov,
	Colin Cross, Tony Luck, Steven Rostedt, Ingo Molnar, Minchan Kim,
	KVM, qemu-devel, virtualization

Hi Kees,

On Mon, Jul 18, 2016 at 10:50:06AM -0700, Kees Cook wrote:
> On Sun, Jul 17, 2016 at 10:50 PM, Namhyung Kim <namhyung@kernel.org> wrote:
> > Hello,
> >
> > On Sun, Jul 17, 2016 at 10:12:26PM -0700, Kees Cook wrote:
> >> On Sun, Jul 17, 2016 at 9:37 PM, Namhyung Kim <namhyung@kernel.org> wrote:
> > [SNIP]
> >> > +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?
> >
> > Do you want to append the data to the host file as guest does
> > printk()?  I think it needs some kind of buffer management, but it's
> > not hard to add IMHO.
> 
> Well, with most pstore backends, the buffer size is limited, so it
> tends to be a circular buffer of some sort. I think whatever you
> choose to do is fine (I saw the various mentions of resource limits in
> the qemu part of this thread), as long as the last N bytes of console
> can be seen on the host side, where N is some portion of the memory
> set aside for the log. (I don't mind the idea of an unlimited console
> log either, but I suspect that will not be accepted on the qemu
> side...)

I think it needs two kinds of buffer management.

The first one is the psinfo->buf (or something similar).  IIUC the
PSTORE_TYPE_CONSOLE is different than PSTORE_TYPE_DMESG as it is
emitted every time printk() sends messages to console.  So I think the
it should remain in async mode due to performance reason.  To do that,
the message should be copied to psinfo->buf and then sent via virtio.
Then it needs to keep track of the available buffer position IMHO.

The other one is the file management on the host side.  I am thinking
of a simple way that the log file is splitted when it exceeds the half
of the allowed max size.  It would be configurable and might allow
unlimited logs if user requests it explicitly (if qemu guys say ok)..

Maybe we need to use 'part' or 'count' for filenames to identify
the splitted files.

> 
> > [SNIP]
> >> > +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?
> >
> > I cannot follow, sorry.  Could you please elaborate it more?
> 
> The mention you have here of "TODO: make it synchronous" made me think
> about what effects that could have. If a pstore_write() were issued
> for a type other than DMESG, the above code would send it through
> virtio anyway. No error reporting is possible unless this is
> synchronous, but the only error here would simply be "I don't know
> what anything except DMESG is", so maybe this code could refuse to
> forward anything with type UNKNOWN in the first place. (Just an idea:
> I don't think there's anything very wrong here. It just seemed like a
> potential improvement.)

Yep, that kind of error handling should be easy.  My concern is when
write operation is failed on the host side.  We need a way to report
it back to the guest and might disallow further writes at least for
the same type.

Thanks,
Namhyung

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 1/3] virtio: Basic implementation of virtio pstore driver
  2016-07-19 13:43         ` Namhyung Kim
@ 2016-07-19 15:32           ` Namhyung Kim
  2016-07-20 12:56           ` Namhyung Kim
  1 sibling, 0 replies; 35+ messages in thread
From: Namhyung Kim @ 2016-07-19 15:32 UTC (permalink / raw)
  To: Kees Cook
  Cc: LKML, Paolo Bonzini, Radim Kr??m????,
	Michael S. Tsirkin, Anthony Liguori, Anton Vorontsov,
	Colin Cross, Tony Luck, Steven Rostedt, Ingo Molnar, Minchan Kim,
	KVM, qemu-devel, virtualization

On Tue, Jul 19, 2016 at 10:43 PM, Namhyung Kim <namhyung@kernel.org> wrote:
> Hi Kees,
>
> On Mon, Jul 18, 2016 at 10:50:06AM -0700, Kees Cook wrote:
>> On Sun, Jul 17, 2016 at 10:50 PM, Namhyung Kim <namhyung@kernel.org> wrote:
>> > Hello,
>> >
>> > On Sun, Jul 17, 2016 at 10:12:26PM -0700, Kees Cook wrote:
>> >> On Sun, Jul 17, 2016 at 9:37 PM, Namhyung Kim <namhyung@kernel.org> wrote:
>> > [SNIP]
>> >> > +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?
>> >
>> > Do you want to append the data to the host file as guest does
>> > printk()?  I think it needs some kind of buffer management, but it's
>> > not hard to add IMHO.
>>
>> Well, with most pstore backends, the buffer size is limited, so it
>> tends to be a circular buffer of some sort. I think whatever you
>> choose to do is fine (I saw the various mentions of resource limits in
>> the qemu part of this thread), as long as the last N bytes of console
>> can be seen on the host side, where N is some portion of the memory
>> set aside for the log. (I don't mind the idea of an unlimited console
>> log either, but I suspect that will not be accepted on the qemu
>> side...)
>
> I think it needs two kinds of buffer management.
>
> The first one is the psinfo->buf (or something similar).  IIUC the
> PSTORE_TYPE_CONSOLE is different than PSTORE_TYPE_DMESG as it is
> emitted every time printk() sends messages to console.  So I think the
> it should remain in async mode due to performance reason.  To do that,
> the message should be copied to psinfo->buf and then sent via virtio.
> Then it needs to keep track of the available buffer position IMHO.

Looking at the code, I found myself confused with the PSTORE_TYPE_FTRACE
and PSTORE_TYPE_CONSOLE.  It seems that handling of
PSTORE_TYPE_CONSOLE is basically same as PSTORE_TYPE_DMESG..

Thanks,
Namhyung

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 2/3] qemu: Implement virtio-pstore device
  2016-07-18 10:03   ` Stefan Hajnoczi
  2016-07-18 14:21     ` Namhyung Kim
@ 2016-07-19 15:48     ` Namhyung Kim
  2016-07-20  8:21       ` Stefan Hajnoczi
  1 sibling, 1 reply; 35+ messages in thread
From: Namhyung Kim @ 2016-07-19 15:48 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: LKML, Paolo Bonzini, Radim Kr??m????,
	Michael S. Tsirkin, Anthony Liguori, Anton Vorontsov,
	Colin Cross, Kees Cook, Tony Luck, Steven Rostedt, Ingo Molnar,
	Minchan Kim, kvm, qemu-devel, virtualization

Hello,

On Mon, Jul 18, 2016 at 11:03:53AM +0100, Stefan Hajnoczi wrote:
> On Mon, Jul 18, 2016 at 01:37:40PM +0900, Namhyung Kim wrote:
> > +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.

By accessing elem->out_sg[0].iov_base directly, I abused it as an
in-and-out buffer.  But it seems not allowed by the virtio spec, do I
have to use separate buffers for request and response?

Thanks,
Namhyung

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 2/3] qemu: Implement virtio-pstore device
  2016-07-19 15:48     ` Namhyung Kim
@ 2016-07-20  8:21       ` Stefan Hajnoczi
  2016-07-20 12:30         ` Namhyung Kim
  0 siblings, 1 reply; 35+ messages in thread
From: Stefan Hajnoczi @ 2016-07-20  8:21 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: LKML, Paolo Bonzini, Radim Kr??m????,
	Michael S. Tsirkin, Anthony Liguori, Anton Vorontsov,
	Colin Cross, Kees Cook, Tony Luck, Steven Rostedt, Ingo Molnar,
	Minchan Kim, kvm, qemu-devel, virtualization

[-- Attachment #1: Type: text/plain, Size: 1697 bytes --]

On Wed, Jul 20, 2016 at 12:48:39AM +0900, Namhyung Kim wrote:
> Hello,
> 
> On Mon, Jul 18, 2016 at 11:03:53AM +0100, Stefan Hajnoczi wrote:
> > On Mon, Jul 18, 2016 at 01:37:40PM +0900, Namhyung Kim wrote:
> > > +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.
> 
> By accessing elem->out_sg[0].iov_base directly, I abused it as an
> in-and-out buffer.  But it seems not allowed by the virtio spec, do I
> have to use separate buffers for request and response?

Yes, a virtqueue element has (host read-only) out buffers followed by
(host write-only) in buffers.

Stefan

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 2/3] qemu: Implement virtio-pstore device
  2016-07-18 14:21     ` Namhyung Kim
@ 2016-07-20  8:29       ` Stefan Hajnoczi
  2016-07-20 12:46         ` Namhyung Kim
  0 siblings, 1 reply; 35+ messages in thread
From: Stefan Hajnoczi @ 2016-07-20  8:29 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: LKML, Paolo Bonzini, Radim Kr??m????,
	Michael S. Tsirkin, Anthony Liguori, Anton Vorontsov,
	Colin Cross, Kees Cook, Tony Luck, Steven Rostedt, Ingo Molnar,
	Minchan Kim, kvm, qemu-devel, virtualization

[-- Attachment #1: Type: text/plain, Size: 1995 bytes --]

On Mon, Jul 18, 2016 at 11:21:18PM +0900, Namhyung Kim wrote:
> On Mon, Jul 18, 2016 at 11:03:53AM +0100, Stefan Hajnoczi wrote:
> > On Mon, Jul 18, 2016 at 01:37:40PM +0900, Namhyung Kim wrote:
> > > From: Namhyung Kim <namhyung@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.
> 
> Right.  The synchronous IO is required by the pstore subsystem
> implementation AFAIK (it uses a single psinfo->buf in the loop).

The pstore subsystem in Linux may be synchronous but the QEMU device
emulation does not have to be synchronous.

Synchronous device emulation means that no other vcpu or QEMU main loop
processing can occur while device emulation is blocked in a syscall.
This can make the QEMU monitor unavailable for libvirt and management
tools.  The guest can experience jitter since vcpus freeze if they
vmexit while device emulation is blocked (it holds the QEMU global
mutex and prevents other QEMU threads from making progress).

You could use include/io.h for asynchronous I/O (qio_channel_add_watch()).

Stefan

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 2/3] qemu: Implement virtio-pstore device
  2016-07-20  8:21       ` Stefan Hajnoczi
@ 2016-07-20 12:30         ` Namhyung Kim
  0 siblings, 0 replies; 35+ messages in thread
From: Namhyung Kim @ 2016-07-20 12:30 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: LKML, Paolo Bonzini, Radim Kr??m????,
	Michael S. Tsirkin, Anthony Liguori, Anton Vorontsov,
	Colin Cross, Kees Cook, Tony Luck, Steven Rostedt, Ingo Molnar,
	Minchan Kim, kvm, qemu-devel, virtualization

On Wed, Jul 20, 2016 at 09:21:08AM +0100, Stefan Hajnoczi wrote:
> On Wed, Jul 20, 2016 at 12:48:39AM +0900, Namhyung Kim wrote:
> > Hello,
> > 
> > On Mon, Jul 18, 2016 at 11:03:53AM +0100, Stefan Hajnoczi wrote:
> > > On Mon, Jul 18, 2016 at 01:37:40PM +0900, Namhyung Kim wrote:
> > > > +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.
> > 
> > By accessing elem->out_sg[0].iov_base directly, I abused it as an
> > in-and-out buffer.  But it seems not allowed by the virtio spec, do I
> > have to use separate buffers for request and response?
> 
> Yes, a virtqueue element has (host read-only) out buffers followed by
> (host write-only) in buffers.

Thanks for clarification.  I'll split them.

Thanks,
Namhyung

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 2/3] qemu: Implement virtio-pstore device
  2016-07-20  8:29       ` Stefan Hajnoczi
@ 2016-07-20 12:46         ` Namhyung Kim
  0 siblings, 0 replies; 35+ messages in thread
From: Namhyung Kim @ 2016-07-20 12:46 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: LKML, Paolo Bonzini, Radim Kr??m????,
	Michael S. Tsirkin, Anthony Liguori, Anton Vorontsov,
	Colin Cross, Kees Cook, Tony Luck, Steven Rostedt, Ingo Molnar,
	Minchan Kim, kvm, qemu-devel, virtualization

On Wed, Jul 20, 2016 at 09:29:06AM +0100, Stefan Hajnoczi wrote:
> On Mon, Jul 18, 2016 at 11:21:18PM +0900, Namhyung Kim wrote:
> > On Mon, Jul 18, 2016 at 11:03:53AM +0100, Stefan Hajnoczi wrote:
> > > On Mon, Jul 18, 2016 at 01:37:40PM +0900, Namhyung Kim wrote:
> > > > From: Namhyung Kim <namhyung@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.
> > 
> > Right.  The synchronous IO is required by the pstore subsystem
> > implementation AFAIK (it uses a single psinfo->buf in the loop).
> 
> The pstore subsystem in Linux may be synchronous but the QEMU device
> emulation does not have to be synchronous.
> 
> Synchronous device emulation means that no other vcpu or QEMU main loop
> processing can occur while device emulation is blocked in a syscall.
> This can make the QEMU monitor unavailable for libvirt and management
> tools.  The guest can experience jitter since vcpus freeze if they
> vmexit while device emulation is blocked (it holds the QEMU global
> mutex and prevents other QEMU threads from making progress).

Thanks for your detailed explanation.  I'll try to change pstore
implementation to deal with async devices.

Thanks,
Namhyung

> 
> You could use include/io.h for asynchronous I/O (qio_channel_add_watch()).
> 
> Stefan

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 1/3] virtio: Basic implementation of virtio pstore driver
  2016-07-19 13:43         ` Namhyung Kim
  2016-07-19 15:32           ` Namhyung Kim
@ 2016-07-20 12:56           ` Namhyung Kim
  1 sibling, 0 replies; 35+ messages in thread
From: Namhyung Kim @ 2016-07-20 12:56 UTC (permalink / raw)
  To: Kees Cook
  Cc: LKML, Paolo Bonzini, Radim Kr??m????,
	Michael S. Tsirkin, Anthony Liguori, Anton Vorontsov,
	Colin Cross, Tony Luck, Steven Rostedt, Ingo Molnar, Minchan Kim,
	KVM, qemu-devel, virtualization

On Tue, Jul 19, 2016 at 10:43 PM, Namhyung Kim <namhyung@kernel.org> wrote:
> The other one is the file management on the host side.  I am thinking
> of a simple way that the log file is splitted when it exceeds the half
> of the allowed max size.  It would be configurable and might allow
> unlimited logs if user requests it explicitly (if qemu guys say ok)..

On a second thought, I think that size of log file should not exceed
the pstore buffer size in order to be read back later.  When total size
of log files becomes larger then the limit, the oldest file of same
type can be deleted. This way looks simpler to manage IMHO.

Also I think the pstore id should be managed by host device rather
than guest driver to handle splitted files properly.

Thanks,
Namhyung

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 2/3] qemu: Implement virtio-pstore device
  2016-11-15  6:23         ` Namhyung Kim
@ 2016-11-15 14:38           ` Michael S. Tsirkin
  0 siblings, 0 replies; 35+ messages in thread
From: Michael S. Tsirkin @ 2016-11-15 14:38 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: virtio-dev, kvm, qemu-devel, virtualization, LKML, Paolo Bonzini,
	Radim Krčmář,
	Anthony Liguori, Anton Vorontsov, Colin Cross, Kees Cook,
	Tony Luck, Steven Rostedt, Ingo Molnar, Minchan Kim,
	Daniel P . Berrange

On Tue, Nov 15, 2016 at 03:23:36PM +0900, Namhyung Kim wrote:
> On Fri, Nov 11, 2016 at 12:50:03AM +0200, Michael S. Tsirkin wrote:
> > 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:
> > > > > +
> > > > > +/* 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.
> 
> Right, I think it needs to add 'type' info to the filename for unknown
> type.
> 
> Thanks,
> Namhyung

And that opens all kind of resource management issues as guest
might be able to open a ton of these unexpected types.
So don't try to predict the future, if you add a new type
you add a feature flag. Ignore or error on things you can
not handle.

-- 
MST

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 2/3] qemu: Implement virtio-pstore device
  2016-11-10 22:50       ` Michael S. Tsirkin
@ 2016-11-15  6:23         ` Namhyung Kim
  2016-11-15 14:38           ` Michael S. Tsirkin
  0 siblings, 1 reply; 35+ messages in thread
From: Namhyung Kim @ 2016-11-15  6:23 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: virtio-dev, kvm, qemu-devel, virtualization, LKML, Paolo Bonzini,
	Radim Krčmář,
	Anthony Liguori, Anton Vorontsov, Colin Cross, Kees Cook,
	Tony Luck, Steven Rostedt, Ingo Molnar, Minchan Kim,
	Daniel P . Berrange

On Fri, Nov 11, 2016 at 12:50:03AM +0200, Michael S. Tsirkin wrote:
> 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:
> > > > +
> > > > +/* 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.

Right, I think it needs to add 'type' info to the filename for unknown
type.

Thanks,
Namhyung

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 2/3] qemu: Implement virtio-pstore device
  2016-09-16 10:05     ` Namhyung Kim
@ 2016-11-10 22:50       ` Michael S. Tsirkin
  2016-11-15  6:23         ` Namhyung Kim
  0 siblings, 1 reply; 35+ messages in thread
From: Michael S. Tsirkin @ 2016-11-10 22:50 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: virtio-dev, kvm, qemu-devel, virtualization, LKML, Paolo Bonzini,
	Radim Krčmář,
	Anthony Liguori, Anton Vorontsov, Colin Cross, Kees Cook,
	Tony Luck, Steven Rostedt, Ingo Molnar, Minchan Kim,
	Daniel P . Berrange

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@redhat.com>
> > > Cc: Radim Krčmář <rkrcmar@redhat.com>
> > > Cc: "Michael S. Tsirkin" <mst@redhat.com>
> > > Cc: Anthony Liguori <aliguori@amazon.com>
> > > Cc: Anton Vorontsov <anton@enomsg.org>
> > > Cc: Colin Cross <ccross@android.com>
> > > Cc: Kees Cook <keescook@chromium.org>
> > > Cc: Tony Luck <tony.luck@intel.com>
> > > Cc: Steven Rostedt <rostedt@goodmis.org>
> > > Cc: Ingo Molnar <mingo@kernel.org>
> > > Cc: Minchan Kim <minchan@kernel.org>
> > > Cc: Daniel P. Berrange <berrange@redhat.com>
> > > Cc: kvm@vger.kernel.org
> > > Cc: qemu-devel@nongnu.org
> > > Cc: virtualization@lists.linux-foundation.org
> > > Signed-off-by: Namhyung Kim <namhyung@kernel.org>
> > > ---
> > >  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@gmail.com>
> > > + *
> > > + * 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@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      *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

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 2/3] qemu: Implement virtio-pstore device
  2016-09-22 12:23   ` Stefan Hajnoczi
@ 2016-09-23  5:52     ` Namhyung Kim
  0 siblings, 0 replies; 35+ messages in thread
From: Namhyung Kim @ 2016-09-23  5:52 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: virtio-dev, Anton Vorontsov, Daniel P . Berrange, Kees Cook, kvm,
	Radim Krčmář,
	qemu-devel, Michael S. Tsirkin, LKML, Steven Rostedt,
	virtualization, Minchan Kim, Tony Luck, Anthony Liguori,
	Colin Cross, Paolo Bonzini, Ingo Molnar

On Thu, Sep 22, 2016 at 01:23:16PM +0100, Stefan Hajnoczi wrote:
> On Sun, Sep 04, 2016 at 11:38:59PM +0900, Namhyung Kim wrote:
> > +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);
> 
> The new virtio_error() function might be available, depending on when
> this patch series is merged.  virtio_error() should be used instead of
> exit(1).  See "[PATCH v5 0/9] virtio: avoid exit() when device enters
> invalid states" on qemu-devel.

Thanks for the info, will take a look.

> 
> > +        }
> > +
> > +        if (elem->out_num > 2 || elem->in_num > 3) {
> > +            error_report("invalid number of input/output buffer");
> > +            exit(1);
> > +        }
> 
> The VIRTIO specification requires that flexible framing is supported.
> The device cannot make assumptions about the scatter-gather list.  It
> must support any layout (e.g. even multiple 1-byte iovecs making up the
> buffer).

Ok.

> 
> > +
> > +        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;
> 
> Missing cpu_to_le()?

Right!

> 
> > +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);
> 
> This is an error, not a warning.  Please remove "Warning:" so it's clear
> to the user that this message caused QEMU to fail.

Will do.

Thanks,
Namhyung

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 2/3] qemu: Implement virtio-pstore device
  2016-09-04 14:38 ` [PATCH 2/3] qemu: Implement virtio-pstore device Namhyung Kim
@ 2016-09-22 12:23   ` Stefan Hajnoczi
  2016-09-23  5:52     ` Namhyung Kim
  0 siblings, 1 reply; 35+ messages in thread
From: Stefan Hajnoczi @ 2016-09-22 12:23 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: virtio-dev, Anton Vorontsov, Daniel P . Berrange, Kees Cook, kvm,
	Radim Krčmář,
	qemu-devel, Namhyung Kim, Michael S. Tsirkin, LKML,
	Steven Rostedt, virtualization, Minchan Kim, Tony Luck,
	Anthony Liguori, Colin Cross, Paolo Bonzini, Ingo Molnar

[-- Attachment #1: Type: text/plain, Size: 3211 bytes --]

On Sun, Sep 04, 2016 at 11:38:59PM +0900, Namhyung Kim wrote:
> +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);

The new virtio_error() function might be available, depending on when
this patch series is merged.  virtio_error() should be used instead of
exit(1).  See "[PATCH v5 0/9] virtio: avoid exit() when device enters
invalid states" on qemu-devel.

> +        }
> +
> +        if (elem->out_num > 2 || elem->in_num > 3) {
> +            error_report("invalid number of input/output buffer");
> +            exit(1);
> +        }

The VIRTIO specification requires that flexible framing is supported.
The device cannot make assumptions about the scatter-gather list.  It
must support any layout (e.g. even multiple 1-byte iovecs making up the
buffer).

> +
> +        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;

Missing cpu_to_le()?

> +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);

This is an error, not a warning.  Please remove "Warning:" so it's clear
to the user that this message caused QEMU to fail.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 455 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 2/3] qemu: Implement virtio-pstore device
  2016-09-13 15:57   ` Michael S. Tsirkin
@ 2016-09-16 10:05     ` Namhyung Kim
  2016-11-10 22:50       ` Michael S. Tsirkin
  0 siblings, 1 reply; 35+ messages in thread
From: Namhyung Kim @ 2016-09-16 10:05 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: virtio-dev, kvm, qemu-devel, virtualization, LKML, Paolo Bonzini,
	Radim Krčmář,
	Anthony Liguori, Anton Vorontsov, Colin Cross, Kees Cook,
	Tony Luck, Steven Rostedt, Ingo Molnar, Minchan Kim,
	Daniel P . Berrange

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@redhat.com>
> > Cc: Radim Krčmář <rkrcmar@redhat.com>
> > Cc: "Michael S. Tsirkin" <mst@redhat.com>
> > Cc: Anthony Liguori <aliguori@amazon.com>
> > Cc: Anton Vorontsov <anton@enomsg.org>
> > Cc: Colin Cross <ccross@android.com>
> > Cc: Kees Cook <keescook@chromium.org>
> > Cc: Tony Luck <tony.luck@intel.com>
> > Cc: Steven Rostedt <rostedt@goodmis.org>
> > Cc: Ingo Molnar <mingo@kernel.org>
> > Cc: Minchan Kim <minchan@kernel.org>
> > Cc: Daniel P. Berrange <berrange@redhat.com>
> > Cc: kvm@vger.kernel.org
> > Cc: qemu-devel@nongnu.org
> > Cc: virtualization@lists.linux-foundation.org
> > Signed-off-by: Namhyung Kim <namhyung@kernel.org>
> > ---
> >  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@gmail.com>
> > + *
> > + * 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.

> 
> 
> > +    "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@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      *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

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 2/3] qemu: Implement virtio-pstore device
  2016-08-20  8:07 ` [PATCH 2/3] qemu: Implement virtio-pstore device Namhyung Kim
  2016-08-24 22:00   ` Daniel P. Berrange
@ 2016-09-13 15:57   ` Michael S. Tsirkin
  2016-09-16 10:05     ` Namhyung Kim
  1 sibling, 1 reply; 35+ messages in thread
From: Michael S. Tsirkin @ 2016-09-13 15:57 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: virtio-dev, kvm, qemu-devel, virtualization, LKML, Paolo Bonzini,
	Radim Krčmář,
	Anthony Liguori, Anton Vorontsov, Colin Cross, Kees Cook,
	Tony Luck, Steven Rostedt, Ingo Molnar, Minchan Kim,
	Daniel P . Berrange

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@redhat.com>
> Cc: Radim Krčmář <rkrcmar@redhat.com>
> Cc: "Michael S. Tsirkin" <mst@redhat.com>
> Cc: Anthony Liguori <aliguori@amazon.com>
> Cc: Anton Vorontsov <anton@enomsg.org>
> Cc: Colin Cross <ccross@android.com>
> Cc: Kees Cook <keescook@chromium.org>
> Cc: Tony Luck <tony.luck@intel.com>
> Cc: Steven Rostedt <rostedt@goodmis.org>
> Cc: Ingo Molnar <mingo@kernel.org>
> Cc: Minchan Kim <minchan@kernel.org>
> Cc: Daniel P. Berrange <berrange@redhat.com>
> Cc: kvm@vger.kernel.org
> Cc: qemu-devel@nongnu.org
> Cc: virtualization@lists.linux-foundation.org
> Signed-off-by: Namhyung Kim <namhyung@kernel.org>
> ---
>  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@gmail.com>
> + *
> + * 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.


> +    "dmesg-",		/* VIRTIO_PSTORE_TYPE_DMESG */

use named initializers for this instead of comments.

> +};
> +
> +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?

> +
> +    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.

> +
> +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.

> +
> +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.

also this is a coding style violation.

> +
> +    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?

> +
> +    if (fstat(fd, &stbuf) < 0) {

So we can stat, but can we e.g. read?


> +        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.

> +    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?

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.




> +
> +    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?

> +
> +    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?


> +
> +    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?

> +        }
> +    }
> +}
> +
> +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.

> +
> +    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.

> +
> +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@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      *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

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH 2/3] qemu: Implement virtio-pstore device
  2016-09-04 14:38 [RFC/PATCHSET 0/3] virtio: Implement virtio pstore device (v5) Namhyung Kim
@ 2016-09-04 14:38 ` Namhyung Kim
  2016-09-22 12:23   ` Stefan Hajnoczi
  0 siblings, 1 reply; 35+ messages in thread
From: Namhyung Kim @ 2016-09-04 14:38 UTC (permalink / raw)
  To: virtio-dev
  Cc: LKML, qemu-devel, kvm, virtualization, Namhyung Kim,
	Paolo Bonzini, Radim Krčmář,
	Michael S. Tsirkin, Anthony Liguori, Anton Vorontsov,
	Colin Cross, Kees Cook, Tony Luck, Steven Rostedt, Ingo Molnar,
	Minchan Kim, Daniel P . Berrange

From: Namhyung Kim <namhyung@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-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@redhat.com>
Cc: Radim Krčmář <rkrcmar@redhat.com>
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Cc: Anthony Liguori <aliguori@amazon.com>
Cc: Anton Vorontsov <anton@enomsg.org>
Cc: Colin Cross <ccross@android.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Minchan Kim <minchan@kernel.org>
Cc: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Namhyung Kim <namhyung@gmail.com>
---
 hw/virtio/Makefile.objs                        |   2 +-
 hw/virtio/virtio-pci.c                         |  52 ++
 hw/virtio/virtio-pci.h                         |  14 +
 hw/virtio/virtio-pstore.c                      | 689 +++++++++++++++++++++++++
 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, 871 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..08d1063
--- /dev/null
+++ b/hw/virtio/virtio-pstore.c
@@ -0,0 +1,689 @@
+/*
+ * Virtio Pstore Device
+ *
+ * Copyright (C) 2016  LG Electronics
+ *
+ * Authors:
+ *  Namhyung Kim  <namhyung@gmail.com>
+ *
+ * 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-file.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 */
+    "dmesg-",		/* VIRTIO_PSTORE_TYPE_DMESG */
+};
+
+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);
+
+    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;
+
+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 delete_old_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;
+}
+
+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;
+
+    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 FALSE;
+}
+
+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 idx;
+    struct stat stbuf;
+    struct pstore_read_arg *rarg = NULL;
+    QIOChannelFile *iocf;
+    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;
+    }
+
+    iocf = qio_channel_file_new_path(filename, O_RDONLY, 0644, &err);
+    if (err) {
+        error_reportf_err(err, "cannot create io channel: ");
+        goto out;
+    }
+
+    if (fstat(iocf->fd, &stbuf) < 0) {
+        goto out;
+    }
+
+    rarg->vps            = s;
+    rarg->elem           = elem;
+    rarg->ioc            = QIO_CHANNEL(iocf);
+    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);
+    rarg->info.time_nsec = cpu_to_le32(stbuf.st_ctim.tv_nsec);
+
+    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;
+    }
+
+    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 FALSE;
+}
+
+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 flags = O_WRONLY | O_CREAT | O_TRUNC;
+    struct pstore_write_arg *warg = NULL;
+    QIOChannelFile *iocf;
+    Error *err = NULL;
+    int ret = -1;
+
+    /* do not keep same type of files more than 'file-max' */
+    delete_old_pstore_file(s, type);
+
+    filename = virtio_pstore_to_filename(s, req);
+    if (filename == NULL) {
+        return -1;
+    }
+
+    iocf = qio_channel_file_new_path(filename, flags, 0644, &err);
+    if (err) {
+        error_reportf_err(err, "cannot create io channel: ");
+        goto out;
+    }
+
+    warg = g_malloc(sizeof(*warg));
+    if (warg == NULL) {
+        goto out;
+    }
+
+    warg->vps  = s;
+    warg->elem = elem;
+    warg->req  = req;
+    warg->ioc  = QIO_CHANNEL(iocf);
+
+    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;
+        }
+    }
+}
+
+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;
+
+    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;
+}
+
+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@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      *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..cbbe668
--- /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 {
+    uint16_t cmd;
+    uint16_t type;
+    uint32_t flags;
+    uint64_t id;
+    uint32_t count;
+    uint32_t reserved;
+};
+
+struct virtio_pstore_res {
+    uint16_t cmd;
+    uint16_t type;
+    uint32_t ret;
+};
+
+struct virtio_pstore_fileinfo {
+    uint64_t id;
+    uint32_t count;
+    uint16_t type;
+    uint16_t unused;
+    uint32_t flags;
+    uint32_t len;
+    uint64_t time_sec;
+    uint32_t time_nsec;
+    uint32_t reserved;
+};
+
+struct virtio_pstore_config {
+    uint32_t 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

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 2/3] qemu: Implement virtio-pstore device
  2016-08-31  8:07 [RFC/PATCHSET 0/3] virtio: Implement virtio pstore device (v4) Namhyung Kim
@ 2016-08-31  8:08 ` Namhyung Kim
  0 siblings, 0 replies; 35+ messages in thread
From: Namhyung Kim @ 2016-08-31  8:08 UTC (permalink / raw)
  To: virtio-dev, virtualization, kvm, qemu-devel
  Cc: LKML, Namhyung Kim, Paolo Bonzini, Radim Krčmář,
	Michael S. Tsirkin, Anthony Liguori, Anton Vorontsov,
	Colin Cross, Kees Cook, Tony Luck, Steven Rostedt, Ingo Molnar,
	Minchan Kim, Daniel P . Berrange

From: Namhyung Kim <namhyung@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-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@redhat.com>
Cc: Radim Krčmář <rkrcmar@redhat.com>
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Cc: Anthony Liguori <aliguori@amazon.com>
Cc: Anton Vorontsov <anton@enomsg.org>
Cc: Colin Cross <ccross@android.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Minchan Kim <minchan@kernel.org>
Cc: Daniel P. Berrange <berrange@redhat.com>
Cc: kvm@vger.kernel.org
Cc: qemu-devel@nongnu.org
Cc: virtualization@lists.linux-foundation.org
Signed-off-by: Namhyung Kim <namhyung@gmail.com>
---
 hw/virtio/Makefile.objs                        |   2 +-
 hw/virtio/virtio-pci.c                         |  52 ++
 hw/virtio/virtio-pci.h                         |  14 +
 hw/virtio/virtio-pstore.c                      | 689 +++++++++++++++++++++++++
 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, 871 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..08d1063
--- /dev/null
+++ b/hw/virtio/virtio-pstore.c
@@ -0,0 +1,689 @@
+/*
+ * Virtio Pstore Device
+ *
+ * Copyright (C) 2016  LG Electronics
+ *
+ * Authors:
+ *  Namhyung Kim  <namhyung@gmail.com>
+ *
+ * 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-file.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 */
+    "dmesg-",		/* VIRTIO_PSTORE_TYPE_DMESG */
+};
+
+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);
+
+    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;
+
+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 delete_old_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;
+}
+
+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;
+
+    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 FALSE;
+}
+
+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 idx;
+    struct stat stbuf;
+    struct pstore_read_arg *rarg = NULL;
+    QIOChannelFile *iocf;
+    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;
+    }
+
+    iocf = qio_channel_file_new_path(filename, O_RDONLY, 0644, &err);
+    if (err) {
+        error_reportf_err(err, "cannot create io channel: ");
+        goto out;
+    }
+
+    if (fstat(iocf->fd, &stbuf) < 0) {
+        goto out;
+    }
+
+    rarg->vps            = s;
+    rarg->elem           = elem;
+    rarg->ioc            = QIO_CHANNEL(iocf);
+    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);
+    rarg->info.time_nsec = cpu_to_le32(stbuf.st_ctim.tv_nsec);
+
+    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;
+    }
+
+    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 FALSE;
+}
+
+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 flags = O_WRONLY | O_CREAT | O_TRUNC;
+    struct pstore_write_arg *warg = NULL;
+    QIOChannelFile *iocf;
+    Error *err = NULL;
+    int ret = -1;
+
+    /* do not keep same type of files more than 'file-max' */
+    delete_old_pstore_file(s, type);
+
+    filename = virtio_pstore_to_filename(s, req);
+    if (filename == NULL) {
+        return -1;
+    }
+
+    iocf = qio_channel_file_new_path(filename, flags, 0644, &err);
+    if (err) {
+        error_reportf_err(err, "cannot create io channel: ");
+        goto out;
+    }
+
+    warg = g_malloc(sizeof(*warg));
+    if (warg == NULL) {
+        goto out;
+    }
+
+    warg->vps  = s;
+    warg->elem = elem;
+    warg->req  = req;
+    warg->ioc  = QIO_CHANNEL(iocf);
+
+    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;
+        }
+    }
+}
+
+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;
+
+    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;
+}
+
+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@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      *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

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* Re: [PATCH 2/3] qemu: Implement virtio-pstore device
  2016-08-26  4:48     ` Namhyung Kim
@ 2016-08-26 12:27       ` Daniel P. Berrange
  0 siblings, 0 replies; 35+ messages in thread
From: Daniel P. Berrange @ 2016-08-26 12:27 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: virtio-dev, kvm, qemu-devel, virtualization, LKML, Paolo Bonzini,
	Radim Krčmář,
	Michael S. Tsirkin, Anthony Liguori, Anton Vorontsov,
	Colin Cross, Kees Cook, Tony Luck, Steven Rostedt, Ingo Molnar,
	Minchan Kim

On Fri, Aug 26, 2016 at 01:48:40PM +0900, Namhyung Kim wrote:
> Hi Daniel,
> 
> On Wed, Aug 24, 2016 at 06:00:51PM -0400, Daniel P. Berrange wrote:

> > > +    fd = open(filename, O_RDONLY);
> > > +    if (fd < 0) {
> > > +        error_report("cannot open %s", filename);
> > > +        goto out;
> > > +    }
> > > +
> > > +    if (fstat(fd, &stbuf) < 0) {
> > > +        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);
> > > +    rarg->info.time_nsec = cpu_to_le32(stbuf.st_ctim.tv_nsec);
> > > +
> > > +    rarg->ioc = qio_channel_new_fd(fd, &err);
> > 
> > You should just use qio_channel_open_path() and avoid the earlier
> > call to open()
> 
> I did it because to call fstat() using the fd and wanted to keep the
> generic ioc pointer.

I'd suggest just using a cast inline, eg

  fstat(QIO_CHANNEL_FILE(ioc)->fd, &stbuf)


Regards,
Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 2/3] qemu: Implement virtio-pstore device
  2016-08-24 22:00   ` Daniel P. Berrange
@ 2016-08-26  4:48     ` Namhyung Kim
  2016-08-26 12:27       ` Daniel P. Berrange
  0 siblings, 1 reply; 35+ messages in thread
From: Namhyung Kim @ 2016-08-26  4:48 UTC (permalink / raw)
  To: Daniel P. Berrange
  Cc: virtio-dev, kvm, qemu-devel, virtualization, LKML, Paolo Bonzini,
	Radim Krčmář,
	Michael S. Tsirkin, Anthony Liguori, Anton Vorontsov,
	Colin Cross, Kees Cook, Tony Luck, Steven Rostedt, Ingo Molnar,
	Minchan Kim

Hi Daniel,

On Wed, Aug 24, 2016 at 06:00:51PM -0400, Daniel P. Berrange wrote:
> 
> > 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@gmail.com>
> > + *
> > + * 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 */
> > +    "dmesg-",		/* VIRTIO_PSTORE_TYPE_DMESG */
> > +};
> > +
> > +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);
> > +
> > +    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;
> > +
> > +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)
> 
> AFAIK you're not actually doing file rotation here - that implies a
> fixed base filename, with .0, .1, .2, etc suffixes where we rename
> files each time. It looks like you are assuming separate filenames,
> and are merely deleting the oldest each time.

Ah, right.  It's not rotation and I think it's enough for my purpose.
I need to change the name.

> 
> > +{
> > +    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);
> 
> 
> 
> 
> 
> > +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;
> 
> G_SOURCE_REMOVE was added in glib 2.32, but QEMU only permits
> stuff that is present in 2.22. Just use "FALSE" instead.

Didn't know that, will change.

> 
> > +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;
> > +    }
> > +
> > +    if (fstat(fd, &stbuf) < 0) {
> > +        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);
> > +    rarg->info.time_nsec = cpu_to_le32(stbuf.st_ctim.tv_nsec);
> > +
> > +    rarg->ioc = qio_channel_new_fd(fd, &err);
> 
> You should just use qio_channel_open_path() and avoid the earlier
> call to open()

I did it because to call fstat() using the fd and wanted to keep the
generic ioc pointer.


> 
> > +    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;
> > +}
> 
> 
> > +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);
> > +
> > +    filename = virtio_pstore_to_filename(s, req);
> > +    if (filename == NULL) {
> > +        return -1;
> > +    }
> > +
> > +    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);
> 
> Same point about using new_path() instead of new_fd()

OK.

> 
> > +    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;
> > +        }
> > +    }
> > +}
> 
> Regards,
> Daniel

As always, thanks for your review!

Thanks,
Namhyung

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 2/3] qemu: Implement virtio-pstore device
  2016-08-20  8:07 ` [PATCH 2/3] qemu: Implement virtio-pstore device Namhyung Kim
@ 2016-08-24 22:00   ` Daniel P. Berrange
  2016-08-26  4:48     ` Namhyung Kim
  2016-09-13 15:57   ` Michael S. Tsirkin
  1 sibling, 1 reply; 35+ messages in thread
From: Daniel P. Berrange @ 2016-08-24 22:00 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: virtio-dev, kvm, qemu-devel, virtualization, LKML, Paolo Bonzini,
	Radim Krčmář,
	Michael S. Tsirkin, Anthony Liguori, Anton Vorontsov,
	Colin Cross, Kees Cook, Tony Luck, Steven Rostedt, Ingo Molnar,
	Minchan Kim


> 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@gmail.com>
> + *
> + * 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 */
> +    "dmesg-",		/* VIRTIO_PSTORE_TYPE_DMESG */
> +};
> +
> +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);
> +
> +    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;
> +
> +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)

AFAIK you're not actually doing file rotation here - that implies a
fixed base filename, with .0, .1, .2, etc suffixes where we rename
files each time. It looks like you are assuming separate filenames,
and are merely deleting the oldest each time.

> +{
> +    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);





> +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;

G_SOURCE_REMOVE was added in glib 2.32, but QEMU only permits
stuff that is present in 2.22. Just use "FALSE" instead.

> +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;
> +    }
> +
> +    if (fstat(fd, &stbuf) < 0) {
> +        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);
> +    rarg->info.time_nsec = cpu_to_le32(stbuf.st_ctim.tv_nsec);
> +
> +    rarg->ioc = qio_channel_new_fd(fd, &err);

You should just use qio_channel_open_path() and avoid the earlier
call to open()

> +    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;
> +}


> +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);
> +
> +    filename = virtio_pstore_to_filename(s, req);
> +    if (filename == NULL) {
> +        return -1;
> +    }
> +
> +    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);

Same point about using new_path() instead of new_fd()

> +    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;
> +        }
> +    }
> +}

Regards,
Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH 2/3] qemu: Implement virtio-pstore device
  2016-08-20  8:07 [RFC/PATCHSET 0/3] virtio: Implement virtio pstore device (v3) Namhyung Kim
@ 2016-08-20  8:07 ` Namhyung Kim
  2016-08-24 22:00   ` Daniel P. Berrange
  2016-09-13 15:57   ` Michael S. Tsirkin
  0 siblings, 2 replies; 35+ messages in thread
From: Namhyung Kim @ 2016-08-20  8:07 UTC (permalink / raw)
  To: virtio-dev, kvm, qemu-devel, virtualization
  Cc: LKML, Paolo Bonzini, Radim Krčmář,
	Michael S. Tsirkin, Anthony Liguori, Anton Vorontsov,
	Colin Cross, Kees Cook, Tony Luck, Steven Rostedt, Ingo Molnar,
	Minchan Kim, Daniel P . Berrange

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@redhat.com>
Cc: Radim Krčmář <rkrcmar@redhat.com>
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Cc: Anthony Liguori <aliguori@amazon.com>
Cc: Anton Vorontsov <anton@enomsg.org>
Cc: Colin Cross <ccross@android.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Minchan Kim <minchan@kernel.org>
Cc: Daniel P. Berrange <berrange@redhat.com>
Cc: kvm@vger.kernel.org
Cc: qemu-devel@nongnu.org
Cc: virtualization@lists.linux-foundation.org
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
 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@gmail.com>
+ *
+ * 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 */
+    "dmesg-",		/* VIRTIO_PSTORE_TYPE_DMESG */
+};
+
+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);
+
+    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;
+
+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;
+}
+
+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;
+
+    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;
+    }
+
+    if (fstat(fd, &stbuf) < 0) {
+        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);
+    rarg->info.time_nsec = cpu_to_le32(stbuf.st_ctim.tv_nsec);
+
+    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;
+    }
+
+    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);
+
+    filename = virtio_pstore_to_filename(s, req);
+    if (filename == NULL) {
+        return -1;
+    }
+
+    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;
+        }
+    }
+}
+
+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;
+
+    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;
+}
+
+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@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      *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

^ permalink raw reply related	[flat|nested] 35+ messages in thread

end of thread, other threads:[~2016-11-15 14:38 UTC | newest]

Thread overview: 35+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-07-18  4:37 [RFC/PATCHSET 0/3] virtio-pstore: Implement virtio pstore device Namhyung Kim
2016-07-18  4:37 ` [PATCH 1/3] virtio: Basic implementation of virtio pstore driver Namhyung Kim
2016-07-18  5:12   ` Kees Cook
2016-07-18  5:50     ` Namhyung Kim
2016-07-18 17:50       ` Kees Cook
2016-07-19 13:43         ` Namhyung Kim
2016-07-19 15:32           ` Namhyung Kim
2016-07-20 12:56           ` Namhyung Kim
2016-07-18  7:54   ` Cornelia Huck
2016-07-18  8:29     ` Namhyung Kim
2016-07-18  9:02       ` Cornelia Huck
2016-07-18  4:37 ` [PATCH 2/3] qemu: Implement virtio-pstore device Namhyung Kim
2016-07-18  7:28   ` Christian Borntraeger
2016-07-18  8:33     ` Namhyung Kim
2016-07-18 10:03   ` Stefan Hajnoczi
2016-07-18 14:21     ` Namhyung Kim
2016-07-20  8:29       ` Stefan Hajnoczi
2016-07-20 12:46         ` Namhyung Kim
2016-07-19 15:48     ` Namhyung Kim
2016-07-20  8:21       ` Stefan Hajnoczi
2016-07-20 12:30         ` Namhyung Kim
2016-07-18  4:37 ` [PATCH 3/3] kvmtool: " Namhyung Kim
2016-08-20  8:07 [RFC/PATCHSET 0/3] virtio: Implement virtio pstore device (v3) Namhyung Kim
2016-08-20  8:07 ` [PATCH 2/3] qemu: Implement virtio-pstore device Namhyung Kim
2016-08-24 22:00   ` Daniel P. Berrange
2016-08-26  4:48     ` Namhyung Kim
2016-08-26 12:27       ` Daniel P. Berrange
2016-09-13 15:57   ` Michael S. Tsirkin
2016-09-16 10:05     ` Namhyung Kim
2016-11-10 22:50       ` Michael S. Tsirkin
2016-11-15  6:23         ` Namhyung Kim
2016-11-15 14:38           ` Michael S. Tsirkin
2016-08-31  8:07 [RFC/PATCHSET 0/3] virtio: Implement virtio pstore device (v4) Namhyung Kim
2016-08-31  8:08 ` [PATCH 2/3] qemu: Implement virtio-pstore device Namhyung Kim
2016-09-04 14:38 [RFC/PATCHSET 0/3] virtio: Implement virtio pstore device (v5) Namhyung Kim
2016-09-04 14:38 ` [PATCH 2/3] qemu: Implement virtio-pstore device Namhyung Kim
2016-09-22 12:23   ` Stefan Hajnoczi
2016-09-23  5:52     ` Namhyung Kim

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).