linuxppc-dev.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
From: Gavin Shan <gwshan@linux.vnet.ibm.com>
To: linuxppc-dev@lists.ozlabs.org, kvm@vger.kernel.org,
	kvm-ppc@vger.kernel.org
Cc: aik@ozlabs.ru, alex.williamson@redhat.com,
	qiudayu@linux.vnet.ibm.com,
	Gavin Shan <gwshan@linux.vnet.ibm.com>
Subject: [PATCH 20/22] powerpc/kvm: Infrastructure for error injection
Date: Mon,  5 May 2014 11:28:09 +1000	[thread overview]
Message-ID: <1399253291-3975-21-git-send-email-gwshan@linux.vnet.ibm.com> (raw)
In-Reply-To: <1399253291-3975-1-git-send-email-gwshan@linux.vnet.ibm.com>

The patch intends to implements the infrastructure for error injection.
RTAS calls "ibm,{open-errinjct, close-errinjct, errinjct}" are handled
in the host directly. Each VM is allowed to have one opened token at
once.

There're multiple types of error injection to be supported by the system.
So we maintain an array of handlers with error type as index. The array
supports dynamic registration.

Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
---
 arch/powerpc/include/asm/book3s_errinjct.h |  78 +++++++
 arch/powerpc/kvm/Makefile                  |   3 +
 arch/powerpc/kvm/book3s_errinjct.c         | 329 +++++++++++++++++++++++++++++
 arch/powerpc/kvm/book3s_rtas.c             |  29 ++-
 4 files changed, 438 insertions(+), 1 deletion(-)
 create mode 100644 arch/powerpc/include/asm/book3s_errinjct.h
 create mode 100644 arch/powerpc/kvm/book3s_errinjct.c

diff --git a/arch/powerpc/include/asm/book3s_errinjct.h b/arch/powerpc/include/asm/book3s_errinjct.h
new file mode 100644
index 0000000..35712be
--- /dev/null
+++ b/arch/powerpc/include/asm/book3s_errinjct.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright Benjamin Herrenschmidt & Gavin Shan, IBM Corporation 2014.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __POWERPC_BOOK3S_ERRINJCT_H__
+#define __POWERPC_BOOK3S_ERRINJCT_H__
+
+/* Error injection handler */
+typedef int (*kvm_errinjct_func)(struct kvm_vcpu *vcpu, rtas_arg_t buf);
+
+#ifdef CONFIG_KVM_ERRINJCT
+
+/* RTAS services for error injection */
+enum {
+	kvm_errinjct_open_token,
+	kvm_errinjct_close_token,
+	kvm_errinjct_errinjct
+};
+
+/* Supported types of error injection */
+enum {
+	kvm_errinjct_min = 0,
+	kvm_errinjct_fatal,
+	kvm_errinjct_recover_random_evt,
+	kvm_errinjct_recover_special_evt,
+	kvm_errinjct_corrupted_page,
+	kvm_errinjct_corrupted_slb,
+	kvm_errinjct_translator_failure,
+	kvm_errinjct_ioa_bus_error,
+	kvm_errinjct_ioa_bus_error_64,
+	kvm_errinjct_platform_specific,
+	kvm_errinjct_corrupted_dcache_start,
+	kvm_errinjct_corrupted_dcache_end,
+	kvm_errinjct_corrupted_icache_start,
+	kvm_errinjct_corrupted_icache_end,
+	kvm_errinjct_corrupted_tlb_start,
+	kvm_errinjct_corrupted_tlb_end,
+	kvm_errinjct_upstream_io_error,
+	kvm_errinjct_max
+};
+
+/* Handler for specific type of error injection */
+struct kvm_errinjct_handler {
+	int opcode;
+	kvm_errinjct_func handler;
+};
+
+/* Tokens that have been opened */
+struct kvm_errinjct_token {
+	struct kvm *kvm;
+	int token;
+	struct list_head list;
+};
+
+int kvm_errinjct_register(int opcode, kvm_errinjct_func handler);
+int kvm_errinjct_unregister(int opcode);
+void kvmppc_errinjct_rtas(struct kvm_vcpu *vcpu,
+			  struct rtas_args *args, int flag);
+
+#else
+
+static inline int kvm_errinjct_register(int opcode,
+					kvm_errinjct_func handler)
+{
+	return 0;
+}
+
+static inline int kvm_errinjct_unregister(int opcode);
+{
+	return 0;
+}
+
+#endif /* CONFIG_KVM_ERRINJCT */
+#endif /* __POWERPC_BOOK3S_ERRINJCT_H__ */
diff --git a/arch/powerpc/kvm/Makefile b/arch/powerpc/kvm/Makefile
index 673038d..f221f66 100644
--- a/arch/powerpc/kvm/Makefile
+++ b/arch/powerpc/kvm/Makefile
@@ -97,6 +97,9 @@ endif
 kvm-book3s_64-objs-$(CONFIG_KVM_XICS) += \
 	book3s_xics.o
 
+kvm-book3s_64-objs-$(CONFIG_KVM_ERRINJCT) += \
+	book3s_errinjct.o
+
 kvm-book3s_64-objs-$(CONFIG_KVM_VFIO) += \
 	$(addprefix ../../../virt/kvm/, vfio.o)
 
diff --git a/arch/powerpc/kvm/book3s_errinjct.c b/arch/powerpc/kvm/book3s_errinjct.c
new file mode 100644
index 0000000..27a49ab
--- /dev/null
+++ b/arch/powerpc/kvm/book3s_errinjct.c
@@ -0,0 +1,329 @@
+/*
+ * The file intends to implement RTAS errinjct functionality for book3s
+ * architecture. Due to the individual errors injected to the system
+ * are defined by device tree node, it's reasonable to introduce the
+ * mechanism to register the supported errors and their corresponding
+ * handlers.
+ *
+ * Copyright Benjamin Herrenschmidt & Gavin Shan, IBM Corporation 2014.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/kvm_host.h>
+#include <linux/kvm.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+
+#include <asm/uaccess.h>
+#include <asm/kvm_book3s.h>
+#include <asm/kvm_ppc.h>
+#include <asm/book3s_errinjct.h>
+#include <asm/hvcall.h>
+
+static struct kvm_errinjct_handler handlers[kvm_errinjct_max];
+static DEFINE_SPINLOCK(handler_lock);
+static LIST_HEAD(open_token_list);
+static DEFINE_SPINLOCK(token_lock);
+static unsigned long *token_bitmap = NULL;
+static int token_max = 1024;
+
+/**
+ * kvm_errinjct_register - Register error injection handler
+ * @opcode: to idenfity the error type to be injected
+ * @handler: function to handler the error type
+ *
+ * Register function handler for the specified type of error.
+ */
+int kvm_errinjct_register(int opcode, kvm_errinjct_func handler)
+{
+	spin_lock(&handler_lock);
+	if (!opcode || !handler) {
+		spin_unlock(&handler_lock);
+		pr_warn("%s: Invalid argument\n", __func__);
+		return -EINVAL;
+	}
+
+	if (opcode <= kvm_errinjct_min ||
+	    opcode >= kvm_errinjct_max) {
+		spin_unlock(&handler_lock);
+		pr_warn("%s: Opcode %d out of range (%d, %d)\n",
+			__func__, opcode, kvm_errinjct_min, kvm_errinjct_max);
+		return -ERANGE;
+	}
+
+	if (handlers[opcode].handler) {
+		spin_unlock(&handler_lock);
+		pr_warn("%s: Opcode %d had attached handler\n",
+			__func__, opcode);
+		return -EBUSY;
+	}
+
+	handlers[opcode].opcode  = opcode;
+	handlers[opcode].handler = handler;
+	spin_unlock(&handler_lock);
+
+	return 0;
+}
+
+/**
+ * kvm_errinjct_unregister - Unregister error injection handler
+ * @opcode: to identify the error type
+ *
+ * Unregister function handler for the specified type of error.
+ */
+int kvm_errinjct_unregister(int opcode)
+{
+	spin_lock(&handler_lock);
+
+	if (opcode <= kvm_errinjct_min ||
+	    opcode >= kvm_errinjct_max) {
+		spin_unlock(&handler_lock);
+		pr_warn("%s: Opcode %d out of range (%d, %d)\n",
+			__func__, opcode, kvm_errinjct_min, kvm_errinjct_max);
+		return -ERANGE;
+	}
+
+	handlers[opcode].opcode  = 0;
+	handlers[opcode].handler = NULL;
+	spin_unlock(&handler_lock);
+
+	return 0;
+}
+
+/* Allocate token from the bitmap */
+static int kvm_errinjct_token_alloc(void)
+{
+	int token;
+
+	/* The token bitmap isn't initialized yet */
+	if (unlikely(!token_bitmap)) {
+		unsigned long size;
+		unsigned long *mem;
+
+		size = _ALIGN_UP(token_max, sizeof(unsigned long));
+		mem = kzalloc(size, GFP_KERNEL);
+		if (!mem) {
+			pr_err("%s: Out of memory!\n", __func__);
+			return -ENOMEM;
+		}
+
+		/* In case some body else did it */
+		if (unlikely(token_bitmap))
+			kfree(mem);
+		else
+			token_bitmap = mem;
+	}
+
+	/* Allocate token */
+	do {
+		token = find_next_zero_bit(token_bitmap, token_max, 0);
+		if (token >= token_max)
+			return -ERANGE;
+        } while(test_and_set_bit(token, token_bitmap));
+
+	return token;
+}
+
+/* Free token to the bitmap */
+static void kvm_errinjct_token_free(int token)
+{
+	if (unlikely(!token_bitmap))
+		return;
+	if (unlikely(token >= token_max))
+		return;
+
+	clear_bit(token, token_bitmap);
+}
+
+/* Check if the specified VM has opened token or not */
+static bool kvm_errinjct_token_get(struct kvm *kvm,
+				   struct kvm_errinjct_token **token)
+{
+	struct kvm_errinjct_token *t;
+
+	list_for_each_entry(t, &open_token_list, list) {
+		if (t->kvm == kvm) {
+			if (token)
+				*token = t;
+			return true;
+		}
+	}
+
+	return false;
+}
+
+/* Emulation handler for opening token */
+static int kvmppc_errinjct_open(struct kvm_vcpu *vcpu,
+				struct rtas_args *args)
+{
+        struct kvm_errinjct_token *t;
+	int token;
+	int ret = 0;
+
+	/* Check the parameters */
+	if (args->nargs != 0 || args->nret != 2) {
+		pr_warn("%s: Breaking rule (#args: 0, #rets: 2)\n",
+			__func__);
+		ret = -1;
+		goto out;
+	}
+
+	/* Check if the guest has opened token */
+	spin_lock(&token_lock);
+	if (kvm_errinjct_token_get(vcpu->kvm, NULL)) {
+		ret = -4;
+		spin_unlock(&token_lock);
+		goto out;
+	}
+
+	/* Allocate token */
+	token = kvm_errinjct_token_alloc();
+	if (token > token_max) {
+		ret = -1;
+		spin_unlock(&token_lock);
+		goto out;
+	}
+
+	/* Attach open token */
+	t = kzalloc(sizeof(*t), GFP_KERNEL);
+	if (!t) {
+		ret = -2;
+		pr_warn("%s: Out of memory !\n", __func__);
+		kvm_errinjct_token_free(token);
+                spin_unlock(&token_lock);
+                goto out;
+        }
+        t->kvm   = vcpu->kvm;
+        t->token = token;
+	INIT_LIST_HEAD(&t->list);
+	list_add_tail(&t->list, &open_token_list);
+	spin_unlock(&token_lock);
+out:
+        args->rets[1] = ret;
+        return ret == 0 ? token : -1;
+}
+
+/* Emulation handler for closing token */
+static int kvmppc_errinjct_close(struct kvm_vcpu *vcpu,
+				 struct rtas_args *args)
+{
+	struct kvm_errinjct_token *t;
+	int ret = 0;
+
+	/* Check the parameters */
+	if (args->nargs != 1 || args->nret != 1) {
+		pr_warn("%s: Breaking rule (#args: 1, #rets: 1)\n",
+			__func__);
+		ret = -1;
+		goto out;
+	}
+
+	/* Search the opened token */
+	spin_lock(&token_lock);
+	if (!kvm_errinjct_token_get(vcpu->kvm, &t)) {
+		ret = -4;
+		spin_unlock(&token_lock);
+		goto out;
+	}
+
+	/* Detach and free it */
+	list_del(&t->list);
+	kvm_errinjct_token_free(t->token);
+	spin_unlock(&token_lock);
+
+	kfree(t);
+out:
+        return ret;
+}
+
+/*
+ * Emulation handler for error injection. After checking
+ * the arguments, we will dispatch the request to the
+ * dynamically registered handler if possible.
+ */
+static int kvmppc_errinjct(struct kvm_vcpu *vcpu,
+			   struct rtas_args *args)
+{
+	struct kvm_errinjct_token *t;
+	int token, opcode, ret = 0;
+	rtas_arg_t buf;
+
+	/* Check the parameters */
+	if (args->nargs != 3 || args->nret != 1) {
+		pr_warn("%s: Breaking rule (#args: 3, #rets: 1)\n",
+			__func__);
+                ret = -3;
+                goto out;
+        }
+
+	/* Check opcode and buffer */
+	opcode = args->args[0];
+	token  = args->args[1];
+	buf    = args->args[2];
+	if (opcode < kvm_errinjct_min ||
+	    opcode >= kvm_errinjct_max ||
+	    (buf & 0x3fful)) {
+		ret = -3;
+		goto out;
+	}
+
+	/* Check if the VM has the opened token */
+	spin_lock(&token_lock);
+	if (!kvm_errinjct_token_get(vcpu->kvm, &t) ||
+	    t->token != token) {
+		ret = -4;
+		spin_unlock(&token_lock);
+		goto out;
+	}
+	spin_unlock(&token_lock);
+
+	/* Dispatch the request */
+	spin_lock(&handler_lock);
+	if (handlers[opcode].handler)
+		ret = handlers[opcode].handler(vcpu, buf);
+	else
+		ret = -3;
+	spin_unlock(&handler_lock);
+out:
+	return ret;
+}
+
+/**
+ * kvmppc_errinjct_rtas - Common handler for error injection emulation
+ * @vcpu: KVM virtual CPU
+ * @args: RTAS call arguments
+ * @flag: error injection service indicator
+ *
+ * The function is the common handler to emulate error injection RTAS.
+ * All error injection requests will trigger the function and in turn,
+ * the requests will be distributed to individual handler.
+ */
+void kvmppc_errinjct_rtas(struct kvm_vcpu *vcpu,
+			  struct rtas_args *args, int flag)
+{
+	int ret = -1;
+
+	/* Parse the requested service */
+	switch (flag) {
+	case kvm_errinjct_open_token:
+		ret = kvmppc_errinjct_open(vcpu, args);
+		break;
+	case kvm_errinjct_close_token:
+		ret = kvmppc_errinjct_close(vcpu, args);
+		break;
+	case kvm_errinjct_errinjct:
+		ret = kvmppc_errinjct(vcpu, args);
+		break;
+	default:
+		pr_warn("%s: Unsupported option %d\n",
+			__func__, flag);
+	}
+
+	/* Update the return value */
+	args->rets[0] = ret;
+}
diff --git a/arch/powerpc/kvm/book3s_rtas.c b/arch/powerpc/kvm/book3s_rtas.c
index 17bdb4a..030b006 100644
--- a/arch/powerpc/kvm/book3s_rtas.c
+++ b/arch/powerpc/kvm/book3s_rtas.c
@@ -18,6 +18,7 @@
 #include <asm/rtas.h>
 #include <asm/ppc-pci.h>
 #include <asm/eeh.h>
+#include <asm/book3s_errinjct.h>
 
 #ifdef CONFIG_KVM_XICS
 static void kvm_rtas_set_xive(struct kvm_vcpu *vcpu, struct rtas_args *args)
@@ -123,6 +124,21 @@ KVM_RTAS_EEH_FUNC(configure_pe,	eeh_rtas_configure_pe)
 
 #endif /* CONFIG_KVM_EEH */
 
+#ifdef CONFIG_KVM_ERRINJCT
+
+#define KVM_RTAS_ERRINJCT_FUNC(name, flag)			\
+static void kvm_rtas_errinjct_##name(struct kvm_vcpu *vcpu,	\
+				     struct rtas_args *args)	\
+{								\
+	kvmppc_errinjct_rtas(vcpu, args, flag);			\
+}
+
+KVM_RTAS_ERRINJCT_FUNC(open_token,	kvm_errinjct_open_token);
+KVM_RTAS_ERRINJCT_FUNC(close_token,	kvm_errinjct_close_token);
+KVM_RTAS_ERRINJCT_FUNC(errinjct,	kvm_errinjct_errinjct);
+
+#endif /* CONFIG_KVM_ERRINJCT */
+
 struct rtas_handler {
 	void (*handler)(struct kvm_vcpu *vcpu, struct rtas_args *args);
 	char *name;
@@ -153,8 +169,19 @@ static struct rtas_handler rtas_handlers[] = {
 	},
 	{ .name = "ibm,configure-pe",
 	  .handler = kvm_rtas_eeh_configure_pe
-	}
+	},
 #endif /* CONFIG_KVM_EEH */
+#ifdef CONFIG_KVM_ERRINJCT
+	{ .name = "ibm,open-errinjct",
+	  .handler = kvm_rtas_errinjct_open_token
+	},
+	{ .name = "ibm,close-errinjct",
+	  .handler = kvm_rtas_errinjct_close_token
+	},
+	{ .name = "ibm,errinjct",
+	  .handler = kvm_rtas_errinjct_errinjct
+	},
+#endif /* CONFIG_KVM_ERRINJCT */
 };
 
 struct rtas_token_definition {
-- 
1.8.3.2

  parent reply	other threads:[~2014-05-05  1:28 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-05-05  1:27 [PATCH RFC 00/22] EEH Support for VFIO PCI devices on PowerKVM guest Gavin Shan
2014-05-05  1:27 ` [PATCH 01/22] powerpc: Introduce CONFIG_KVM_EEH Gavin Shan
2014-05-05  1:27 ` [PATCH 02/22] powerpc/eeh: Info to trace passed devices Gavin Shan
2014-05-05  1:27 ` [PATCH 03/22] powerpc/eeh: Search EEH device by guest address Gavin Shan
2014-05-05  1:27 ` [PATCH 04/22] powerpc/eeh: Search EEH PE " Gavin Shan
2014-05-05  1:27 ` [PATCH 05/22] powerpc/eeh: Release VFIO dev on VM destruction Gavin Shan
2014-05-05  1:27 ` [PATCH 06/22] powerpc/eeh: Function for address mapping Gavin Shan
2014-05-05  1:27 ` [PATCH 07/22] powerpc/eeh: Function to tear down " Gavin Shan
2014-05-05  1:27 ` [PATCH 08/22] kvm: Address mapping for VFIO device Gavin Shan
2014-05-05  1:27 ` [PATCH 09/22] powerpc/powernv: EEH RTAS emulation backend Gavin Shan
2014-05-05  1:27 ` [PATCH 10/22] powerpc/eeh: Introduce kvmppc_eeh_format_addr() Gavin Shan
2014-05-05  1:28 ` [PATCH 11/22] powerpc/eeh: Emulate RTAS call ibm,set-eeh-option Gavin Shan
2014-05-05  1:28 ` [PATCH 12/22] powerpc/eeh: Emulate RTAS call ibm,set-slot-reset Gavin Shan
2014-05-05  1:28 ` [PATCH 13/22] powerpc/eeh: Emulate RTAS call ibm, read-slot-reset-state2 Gavin Shan
2014-05-05  1:28 ` [PATCH 14/22] powerpc/eeh: Emulate RTAS call ibm, get-config-addr-info2 Gavin Shan
2014-05-05  1:28 ` [PATCH 15/22] powerpc/eeh: Emulate RTAS call ibm,slot-error-detail Gavin Shan
2014-05-05  1:28 ` [PATCH 16/22] powerpc/eeh: Emulate RTAS call ibm,configure-pe Gavin Shan
2014-05-05  1:28 ` [PATCH 17/22] powerpc/kvm: Connect EEH RTAS emulation backend Gavin Shan
2014-05-05  1:28 ` [PATCH 18/22] powerpc/eeh: Avoid event on passed PE Gavin Shan
2014-05-05  1:28 ` [PATCH 19/22] powerpc: Introduce CONFIG_KVM_ERRINJCT Gavin Shan
2014-05-05  1:28 ` Gavin Shan [this message]
2014-05-05  1:28 ` [PATCH 21/22] powerpc/powernv: Sync OPAL header file with firmware Gavin Shan
2014-05-05  1:28 ` [PATCH 22/22] powerpc/powernv: Support PCI error injection Gavin Shan
2014-05-05 11:56 ` [PATCH RFC 00/22] EEH Support for VFIO PCI devices on PowerKVM guest Alexander Graf
2014-05-05 14:00   ` Alex Williamson
2014-05-06  4:26     ` Gavin Shan
2014-05-06  6:56       ` Alexander Graf
2014-05-06  7:14         ` Benjamin Herrenschmidt

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1399253291-3975-21-git-send-email-gwshan@linux.vnet.ibm.com \
    --to=gwshan@linux.vnet.ibm.com \
    --cc=aik@ozlabs.ru \
    --cc=alex.williamson@redhat.com \
    --cc=kvm-ppc@vger.kernel.org \
    --cc=kvm@vger.kernel.org \
    --cc=linuxppc-dev@lists.ozlabs.org \
    --cc=qiudayu@linux.vnet.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).