diff --git a/tools/kvm/Makefile b/tools/kvm/Makefile
index f9e1ec1..6827c1f 100644
--- a/tools/kvm/Makefile
+++ b/tools/kvm/Makefile
@@ -89,6 +89,7 @@ OBJS	+= hw/pci-shmem.o
 OBJS	+= kvm-ipc.o
 OBJS	+= builtin-sandbox.o
 OBJS	+= virtio/mmio.o
+OBJS	+= virtio/notifier.o
 
 # Translate uname -m into ARCH string
 ARCH ?= $(shell uname -m | sed -e s/i.86/i386/ -e s/ppc.*/powerpc/)
diff --git a/tools/kvm/builtin-run.c b/tools/kvm/builtin-run.c
index 750d30c..90ac622 100644
--- a/tools/kvm/builtin-run.c
+++ b/tools/kvm/builtin-run.c
@@ -33,6 +33,7 @@
 #include "kvm/pci-shmem.h"
 #include "kvm/kvm-ipc.h"
 #include "kvm/builtin-debug.h"
+#include "kvm/virtio-notifier.h"
 
 #include <linux/types.h>
 #include <linux/err.h>
@@ -99,6 +100,7 @@ static bool using_rootfs;
 static bool custom_rootfs;
 static bool no_net;
 static bool no_dhcp;
+static bool virtio_notifier;
 extern bool ioport_debug;
 extern bool mmio_debug;
 static int  kvm_run_wrapper;
@@ -442,6 +444,7 @@ static const struct option options[] = {
 	OPT_BOOLEAN('\0', "vnc", &vnc, "Enable VNC framebuffer"),
 	OPT_BOOLEAN('\0', "sdl", &sdl, "Enable SDL framebuffer"),
 	OPT_BOOLEAN('\0', "rng", &virtio_rng, "Enable virtio Random Number Generator"),
+	OPT_BOOLEAN('\0', "notifier", &virtio_notifier, "Enable virtio notifier"),
 	OPT_CALLBACK('\0', "9p", NULL, "dir_to_share,tag_name",
 		     "Enable virtio 9p to share files between host and guest", virtio_9p_rootdir_parser),
 	OPT_STRING('\0', "console", &console, "serial, virtio or hv",
@@ -1182,6 +1185,9 @@ static int kvm_cmd_run_init(int argc, const char **argv)
 	if (virtio_rng)
 		virtio_rng__init(kvm);
 
+	if (virtio_notifier)
+		virtio_notif__init(kvm);
+
 	if (balloon)
 		virtio_bln__init(kvm);
 
diff --git a/tools/kvm/include/kvm/virtio-notifier.h b/tools/kvm/include/kvm/virtio-notifier.h
new file mode 100644
index 0000000..5673fc2
--- /dev/null
+++ b/tools/kvm/include/kvm/virtio-notifier.h
@@ -0,0 +1,9 @@
+#ifndef KVM__NOTIF_VIRTIO_H
+#define KVM__NOTIF_VIRTIO_H
+
+struct kvm;
+
+int virtio_notif__init(struct kvm *kvm);
+int virtio_notif__exit(struct kvm *kvm);
+
+#endif /* KVM__RNG_VIRTIO_H */
diff --git a/tools/kvm/include/kvm/virtio-pci-dev.h b/tools/kvm/include/kvm/virtio-pci-dev.h
index 7ceb125..a387ecd 100644
--- a/tools/kvm/include/kvm/virtio-pci-dev.h
+++ b/tools/kvm/include/kvm/virtio-pci-dev.h
@@ -13,6 +13,7 @@
 #define PCI_DEVICE_ID_VIRTIO_CONSOLE		0x1003
 #define PCI_DEVICE_ID_VIRTIO_RNG		0x1004
 #define PCI_DEVICE_ID_VIRTIO_BLN		0x1005
+#define PCI_DEVICE_ID_VIRTIO_NOTIFIER		0x1006
 #define PCI_DEVICE_ID_VIRTIO_9P			0x1009
 #define PCI_DEVICE_ID_VESA			0x2000
 #define PCI_DEVICE_ID_PCI_SHMEM			0x0001
diff --git a/tools/kvm/virtio/notifier.c b/tools/kvm/virtio/notifier.c
new file mode 100644
index 0000000..c17546b
--- /dev/null
+++ b/tools/kvm/virtio/notifier.c
@@ -0,0 +1,203 @@
+#include "kvm/virtio-notifier.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/virtio_notifier.h>
+
+#include <linux/list.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pthread.h>
+#include <linux/kernel.h>
+
+#define NUM_VIRT_QUEUES			2
+#define VIRTIO_NOTIFIER_QUEUE_SIZE	128
+
+struct ntf_dev_job {
+	struct virt_queue	*vq;
+	struct ntf_dev		*ndev;
+	struct thread_pool__job	job_id;
+};
+
+struct ntf_dev {
+	struct list_head	list;
+	struct virtio_device	vdev;
+
+	/* virtio queue */
+	struct virt_queue	vqs[NUM_VIRT_QUEUES];
+	struct ntf_dev_job	jobs[NUM_VIRT_QUEUES];
+};
+
+static LIST_HEAD(ndevs);
+static int compat_id = -1;
+
+static void set_config(struct kvm *kvm, void *dev, u8 data, u32 offset)
+{
+	/* Unused */
+}
+
+static u8 get_config(struct kvm *kvm, void *dev, u32 offset)
+{
+	/* 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 bool virtio_ntf_do_io_request(struct kvm *kvm, struct ntf_dev *ndev, struct virt_queue *queue)
+{
+	struct iovec iov[VIRTIO_NOTIFIER_QUEUE_SIZE];
+	unsigned int len = 0;
+	u16 out, in, head;
+	u32 *counters;
+
+	head = virt_queue__get_iov(queue, iov, &out, &in, kvm);
+	counters = iov[0].iov_base;
+
+	printf("\n\n New updated counters from guest:\n\tPanic: %u OOM: %u\n\n",
+		counters[VIRTIO_NOTIF_PANIC], counters[VIRTIO_NOTIF_OOM]);
+
+	virt_queue__set_used_elem(queue, head, len);
+
+	return true;
+}
+
+static bool virtio_ntf_do_echo_request(struct kvm *kvm, struct ntf_dev *ndev, struct virt_queue *queue)
+{
+	struct iovec iov[VIRTIO_NOTIFIER_QUEUE_SIZE];
+	u16 out, in, head;
+
+	sleep(1);
+	head = virt_queue__get_iov(queue, iov, &out, &in, kvm);
+	virt_queue__set_used_elem(queue, head, iov[0].iov_len);
+
+	return true;
+}
+
+static void virtio_ntf_do_io(struct kvm *kvm, void *param)
+{
+	struct ntf_dev_job *job	= param;
+	struct virt_queue *vq	= job->vq;
+	struct ntf_dev *ndev	= job->ndev;
+
+	if ((vq - ndev->vqs) == 0) {
+		while (virt_queue__available(vq))
+			virtio_ntf_do_io_request(kvm, ndev, vq);
+	} else {
+		virtio_ntf_do_echo_request(kvm, ndev, vq);
+	}
+
+	ndev->vdev.ops->signal_vq(kvm, &ndev->vdev, vq - ndev->vqs);
+}
+
+static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 pfn)
+{
+	struct ntf_dev *ndev = dev;
+	struct virt_queue *queue;
+	struct ntf_dev_job *job;
+	void *p;
+
+	compat__remove_message(compat_id);
+
+	queue		= &ndev->vqs[vq];
+	queue->pfn	= pfn;
+	p		= guest_pfn_to_host(kvm, queue->pfn);
+
+	job = &ndev->jobs[vq];
+
+	vring_init(&queue->vring, VIRTIO_NOTIFIER_QUEUE_SIZE, p, VIRTIO_PCI_VRING_ALIGN);
+
+	*job = (struct ntf_dev_job) {
+		.vq	= queue,
+		.ndev	= ndev,
+	};
+
+	thread_pool__init_job(&job->job_id, kvm, virtio_ntf_do_io, job);
+
+	return 0;
+}
+
+static int notify_vq(struct kvm *kvm, void *dev, u32 vq)
+{
+	struct ntf_dev *ndev = dev;
+
+	thread_pool__do_job(&ndev->jobs[vq].job_id);
+
+	return 0;
+}
+
+static int get_pfn_vq(struct kvm *kvm, void *dev, u32 vq)
+{
+	struct ntf_dev *ndev = dev;
+
+	return ndev->vqs[vq].pfn;
+}
+
+static int get_size_vq(struct kvm *kvm, void *dev, u32 vq)
+{
+	return VIRTIO_NOTIFIER_QUEUE_SIZE;
+}
+
+static struct virtio_ops ntf_dev_virtio_ops = (struct virtio_ops) {
+	.set_config		= set_config,
+	.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,
+};
+
+int virtio_notif__init(struct kvm *kvm)
+{
+	struct ntf_dev *ndev;
+	int r;
+
+	ndev = malloc(sizeof(*ndev));
+	if (ndev == NULL)
+		return -ENOMEM;
+
+	r = virtio_init(kvm, ndev, &ndev->vdev, &ntf_dev_virtio_ops,
+			VIRTIO_PCI, PCI_DEVICE_ID_VIRTIO_NOTIFIER, VIRTIO_ID_NOTIFIER, PCI_CLASS_RNG);
+	if (r < 0)
+		goto cleanup;
+
+	list_add_tail(&ndev->list, &ndevs);
+
+	return 0;
+cleanup:
+	free(ndev);
+
+	return r;
+}
+
+int virtio_notif__exit(struct kvm *kvm)
+{
+	struct ntf_dev *ndev, *tmp;
+
+	list_for_each_entry_safe(ndev, tmp, &ndevs, list) {
+		list_del(&ndev->list);
+		ndev->vdev.ops->exit(kvm, &ndev->vdev);
+		free(ndev);
+	}
+
+	return 0;
+}
