linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC v4][PATCH part-3 00/14] ASI - Part III (ASI Test Driver and CLI)
@ 2020-05-04 15:02 Alexandre Chartre
  2020-05-04 15:02 ` [RFC v4][PATCH part-3 01/14] mm/asi: Define the test ASI type Alexandre Chartre
                   ` (13 more replies)
  0 siblings, 14 replies; 15+ messages in thread
From: Alexandre Chartre @ 2020-05-04 15:02 UTC (permalink / raw)
  To: tglx, mingo, bp, hpa, dave.hansen, luto, peterz, x86, linux-mm,
	linux-kernel
  Cc: pbonzini, konrad.wilk, jan.setjeeilers, liran.alon, junaids,
	graf, rppt, kuzuno, mgross, alexandre.chartre

This is part III of ASI RFC v4. Please refer to the cover letter of
part I for an overview the ASI RFC.

  https://lore.kernel.org/lkml/20200504144939.11318-1-alexandre.chartre@oracle.com/

This part provides a driver and a CLI for testing and introspecting an
ASI. The driver creates a test ASI and provide the ability to run some
test sequences on the test ASI such as:

 - a simple enter/exit ASI
 - using printk with ASI
 - access a map or unmapped buffer while in ASI
 - receive an interrupt or NMI while in ASI
 - scheduling in/out a task using ASI

The driver reports if the ASI remains active or not at the end of the
test sequence. It also provides the ability to:

 - query the ASI fault (i.e. places where the ASI had a fault)
 - clear the ASI fault log
 - toggle the reporting of stack trace on an ASI fault
 - query ASI mappings (i.e. VA ranges mapped in the ASI)
 - add an ASI mapping
 - clear an ASI mapping

The asicmd user CLI is provided to interact with the ASI test driver.
More details and examples are provided below for using the asicmd CLI.

Thanks,

alex.

=====

ASI Test Driver and CLI Usage
-----------------------------
The ASI test driver is built as a loadable module (asi.ko) in the
drivers/staging/asi/ directory. You can load it with modprobe or insmod.

The ASI test CLI (asicmd) is built in the drivers/staging/asi/ directory.
The ASI test driver should be loaded before using the asicmd CLI.

Usage:
------
Running the asicmd without arguments will show the different commands
available:

# ./asicmd 
Usage: asicmd (<cmd>|<test>...)

Commands:
  all      - run all tests
  fault    - list ASI faults
  fltclr   - clear ASI faults
  stkon    - show stack on ASI fault
  stkoff   - do not show stack on ASI fault
  map      - list ASI mappings
  mapadd   - add ASI mappings
  mapclr   - clear ASI mapping

Tests:
  nop        - enter/exit ASI and nothing else
  mem        - enter ASI and accessed an unmapped buffer
  memmap     - enter ASI and accessed a mapped buffer
  intr       - receive an interruption while running with ASI
  nmi        - receive a NMI while running with ASI
  intrnmi    - receive a NMI in an interrupt received while running with ASI
  sched      - call schedule() while running with ASI
  printk     - call printk() while running with ASI

Running a Test Sequence:
------------------------
Just specify the test sequence to run. For example, to run the "nop"
test sequence, run "asicmd nop":

  # ./asicmd nop
  Test nop (sequence 0)
    - rv = 0 ; result = 0 ; asi active
    - expect = asi active
  TEST OK

The command reports that the ASI was active at the end of the test
sequence and that this was the expected result. So the test was
successful.

Another example with the "mem" test sequence:

  # ./asicmd mem
  Test mem (sequence 2)
    - rv = 0 ; result = 0 ; asi inactive
    - expect = asi inactive
  TEST OK

This tests sequence access a buffer which is not mapped in ASI so it
causes ASI to exit. The test effectively reports that the ASI was inactive
at the end of the test sequence, but this is the expected result so the
test is successful.

All test sequences can be run with the "all" command: asicmd all

Managing ASI Faults:
--------------------
The asicmd CLI has commands to manage ASI faults i.e. faults that occurred
while running ASI because ASI is accessing an unmapped address:

  - "asicmd fault" reports all ASI faults. It shows the address where the
     fault has occurred and the number of time the fault has occurred.

  - "asicmd fltclr" clears the ASI fault log.

For example, the "mem" will cause an ASI fault and we can see it with the
"fault" command:

  # ./asicmd fltclr

  # ./asicmd fault
  ASI has no fault

  # ./asicmd mem
  Test mem (sequence 2)
    - rv = 0 ; result = 0 ; asi inactive
    - expect = asi inactive
  TEST OK

  # ./asicmd fault
  ADDRESS             COUNT  SYMBOL
  0xffffffffc0515037      1  asidrv_mem_run+0x37/0x7f [asi]


Managing ASI Mappings:
----------------------
The asicmd CLI has commands to manage ASI mappings i.e. address ranges
which are mapped in the ASI page-table:

  - "asicmd map" reports all ASI mappings. It shows the address, size and
    page-table level of the mappings.

  - "asicmd mapadd" adds a mapping to the ASI page-table. The syntax is:

     asicmd mapadd [percpu:]<addr>:<size>[:<level>]

     . <addr> is the start address of the mapping and <size> is its size.
     . <level> is the page-table level of the mapping, either "pte", "pmd",
       "pud", "p4d" or "pgd". By default, the level is "pte".
     . "percpu" should be prepended when <addr> is the offset of a percpu
       buffer

  - "asicmd" mapclr" clears a mapping in the ASI page-table. The syntax is:

     asicmd mapclr [percpu:]<addr>

     . <addr> is the start address of the mapping. The system automatically
       figures out the size and page-table level of the mapping.
     . "percpu" should be prepended when <addr> is the offset of a percpu
       buffer

Example:

  # ./asicmd map
  ADDRESS                           SIZE  LEVEL
  0xffff88806946f000               0x838    PTE
  0xffff88806820a000                0x60    PTE
  0xffffffffc0515000              0x7000    PTE
  0xffff88807ddac4c0                0xa0    PTE
  0xffff88807dd2c4c0                0xa0    PTE
  0xffff88807dcac4c0                0xa0    PTE
  0xffff88807dc2c4c0                0xa0    PTE
  0xffff88807dd98bc0                 0x8    PTE
  0xffff88807dd18bc0                 0x8    PTE
  0xffff88807dc98bc0                 0x8    PTE
  0xffff88807dc18bc0                 0x8    PTE
  0xffff88807dd80000                0x30    PTE
  0xffff88807dd00000                0x30    PTE
  0xffff88807dc80000                0x30    PTE
  0xffff88807dc00000                0x30    PTE
  0xfffffe0000001000        0x8000000000    P4D
  0xffffffff80000000          0x40000000    PTE

This shows the default core mappings for ASI. These mappings shouldn't be
removed.

If the ASI faults then you can add the missing mapping. For example with
the "printk" test sequence:

  # ./asicmd fltclr

  # ./asicmd printk
  Test printk (sequence 1)
    - rv = 0 ; result = 0 ; asi inactive
    - expect = asi active
  TEST FAILED - unexpected ASI state

The "printk" test sequence failed because of an ASI fault:

  # ./asicmd fault
  ADDRESS             COUNT  SYMBOL
  0xffffffff8110b5a1      1  vprintk_func+0x11/0xbc

With the source code or debugger, we can figure out that the fault is due
to printk_context not being mapped in the ASI.

  0xffffffff8110b5a1 <+17>: mov %gs:0x7ef106f8(%rip),%eax  # 0x1bca0 <printk_context>

So we can add the missing mapping into the ASI with "mapadd"; note that
printk_context is a percpu buffer and that sizeof(printk_context) is 4:

  # ./asicmd mapadd percpu:0x1bca0:4
  mapadd 1bca0/4/0 percpu

If we re-run the "printk" test then we got:

  # ./asicmd fltclr

  # ./asicmd printk
  Test printk (sequence 1)
    - rv = 0 ; result = 0 ; asi inactive
    - expect = asi active
  TEST FAILED - unexpected ASI state

  # ./asicmd fault
  ADDRESS             COUNT  SYMBOL
  0xffffffff811081f3      1  log_store.constprop.27+0x1f3/0x280

We still see a new fault but at a difference address (this time because
cpu_number is not mapped).

-----

Alexandre Chartre (14):
  mm/asi: Define the test ASI type
  asidrv: Introduce the ASI driver
  asidrv: Introduce the ASIDRV_IOCTL_RUN_SEQUENCE ioctl
  asidrv: Sequence to test ASI access to mapped/unmapped memory
  asidrv: Sequence to test interrupt on ASI
  asidrv: Sequence to test NMI on ASI
  asidrv: Sequence to test interrupt+NMI on ASI
  asidrv: Sequence to test scheduling in/out with ASI
  asidrv: Add ioctls to manage ASI page faults
  asidrv: Add ioctls to manage ASI mapped VA ranges
  asidrv/asicmd: Introduce the asicmd command
  asidrv/asicmd: Add more test sequences for testing ASI
  asidrv/asicmd: Add options to manage ASI page faults
  asidrv/asicmd: Add options to manage ASI mapped VA ranges

 arch/x86/include/asm/asi.h   |    2 +
 arch/x86/mm/asi.c            |    1 +
 drivers/staging/Makefile     |    1 +
 drivers/staging/asi/Makefile |   13 +
 drivers/staging/asi/asicmd.c |  439 ++++++++++++++
 drivers/staging/asi/asidrv.c | 1077 ++++++++++++++++++++++++++++++++++
 drivers/staging/asi/asidrv.h |  102 ++++
 7 files changed, 1635 insertions(+)
 create mode 100644 drivers/staging/asi/Makefile
 create mode 100644 drivers/staging/asi/asicmd.c
 create mode 100644 drivers/staging/asi/asidrv.c
 create mode 100644 drivers/staging/asi/asidrv.h

-- 
2.18.2


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

* [RFC v4][PATCH part-3 01/14] mm/asi: Define the test ASI type
  2020-05-04 15:02 [RFC v4][PATCH part-3 00/14] ASI - Part III (ASI Test Driver and CLI) Alexandre Chartre
@ 2020-05-04 15:02 ` Alexandre Chartre
  2020-05-04 15:02 ` [RFC v4][PATCH part-3 02/14] asidrv: Introduce the ASI driver Alexandre Chartre
                   ` (12 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Alexandre Chartre @ 2020-05-04 15:02 UTC (permalink / raw)
  To: tglx, mingo, bp, hpa, dave.hansen, luto, peterz, x86, linux-mm,
	linux-kernel
  Cc: pbonzini, konrad.wilk, jan.setjeeilers, liran.alon, junaids,
	graf, rppt, kuzuno, mgross, alexandre.chartre

Define the test ASI type which can be used for testing or experimenting
ASI.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 arch/x86/include/asm/asi.h | 2 ++
 arch/x86/mm/asi.c          | 1 +
 drivers/staging/Makefile   | 1 +
 3 files changed, 4 insertions(+)

diff --git a/arch/x86/include/asm/asi.h b/arch/x86/include/asm/asi.h
index eafed750e07f..de4f790c630b 100644
--- a/arch/x86/include/asm/asi.h
+++ b/arch/x86/include/asm/asi.h
@@ -66,6 +66,7 @@ struct asi_tlb_state {
 #ifdef CONFIG_PAGE_TABLE_ISOLATION
 #define ASI_PCID_PREFIX_USER		0x80	/* user ASI */
 #endif
+#define ASI_PCID_PREFIX_TEST		0xff	/* test ASI */
 
 struct asi_type {
 	int			pcid_prefix;	/* PCID prefix */
@@ -156,6 +157,7 @@ extern int asi_init_dpt(struct dpt *dpt);
 #ifdef CONFIG_PAGE_TABLE_ISOLATION
 DECLARE_ASI_TYPE(user);
 #endif
+DECLARE_ASI_TYPE(test);
 
 static inline void asi_set_log_policy(struct asi *asi, int policy)
 {
diff --git a/arch/x86/mm/asi.c b/arch/x86/mm/asi.c
index 8b670ed13729..e0b0d70415d7 100644
--- a/arch/x86/mm/asi.c
+++ b/arch/x86/mm/asi.c
@@ -17,6 +17,7 @@
 #ifdef CONFIG_PAGE_TABLE_ISOLATION
 DEFINE_ASI_TYPE(user, ASI_PCID_PREFIX_USER, false);
 #endif
+DEFINE_ASI_TYPE(test, ASI_PCID_PREFIX_TEST, true);
 
 static void asi_log_fault(struct asi *asi, struct pt_regs *regs,
 			   unsigned long error_code, unsigned long address,
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 4d34198151b3..fb50e669579b 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -50,3 +50,4 @@ obj-$(CONFIG_FIELDBUS_DEV)     += fieldbus/
 obj-$(CONFIG_KPC2000)		+= kpc2000/
 obj-$(CONFIG_QLGE)		+= qlge/
 obj-$(CONFIG_WFX)		+= wfx/
+obj-$(CONFIG_ADDRESS_SPACE_ISOLATION) += asi/
-- 
2.18.2


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

* [RFC v4][PATCH part-3 02/14] asidrv: Introduce the ASI driver
  2020-05-04 15:02 [RFC v4][PATCH part-3 00/14] ASI - Part III (ASI Test Driver and CLI) Alexandre Chartre
  2020-05-04 15:02 ` [RFC v4][PATCH part-3 01/14] mm/asi: Define the test ASI type Alexandre Chartre
@ 2020-05-04 15:02 ` Alexandre Chartre
  2020-05-04 15:02 ` [RFC v4][PATCH part-3 03/14] asidrv: Introduce the ASIDRV_IOCTL_RUN_SEQUENCE ioctl Alexandre Chartre
                   ` (11 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Alexandre Chartre @ 2020-05-04 15:02 UTC (permalink / raw)
  To: tglx, mingo, bp, hpa, dave.hansen, luto, peterz, x86, linux-mm,
	linux-kernel
  Cc: pbonzini, konrad.wilk, jan.setjeeilers, liran.alon, junaids,
	graf, rppt, kuzuno, mgross, alexandre.chartre

Introduce the infrastructure for the ASI driver. This driver is meant
for testing ASI. It creates a test ASI, and will allow to run some
test sequences on this ASI.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 drivers/staging/asi/Makefile |   7 ++
 drivers/staging/asi/asidrv.c | 129 +++++++++++++++++++++++++++++++++++
 2 files changed, 136 insertions(+)
 create mode 100644 drivers/staging/asi/Makefile
 create mode 100644 drivers/staging/asi/asidrv.c

diff --git a/drivers/staging/asi/Makefile b/drivers/staging/asi/Makefile
new file mode 100644
index 000000000000..a48487e48d7c
--- /dev/null
+++ b/drivers/staging/asi/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+
+#
+# Address Space Isolation (ASI) driver
+#
+obj-m += asi.o
+asi-y := asidrv.o
diff --git a/drivers/staging/asi/asidrv.c b/drivers/staging/asi/asidrv.c
new file mode 100644
index 000000000000..c06e4734e0e5
--- /dev/null
+++ b/drivers/staging/asi/asidrv.c
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020, Oracle and/or its affiliates.
+ */
+
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <asm/asi.h>
+#include <asm/dpt.h>
+
+struct asidrv_test {
+	struct asi		*asi;	/* ASI for testing */
+	struct dpt		*dpt;	/* ASI decorated page-table */
+};
+
+static struct asidrv_test *asidrv_test;
+
+static void asidrv_test_destroy(struct asidrv_test *test);
+
+static struct asidrv_test *asidrv_test_create(void)
+{
+	struct asidrv_test *test;
+	int err;
+
+	test = kzalloc(sizeof(*test), GFP_KERNEL);
+	if (!test)
+		return NULL;
+
+	/*
+	 * Create and fill a decorator page-table to be used with the ASI.
+	 */
+	test->dpt = dpt_create(ASI_PGTABLE_MASK);
+	if (!test->dpt)
+		goto error;
+
+	err = asi_init_dpt(test->dpt);
+	if (err)
+		goto error;
+
+	err = DPT_MAP_THIS_MODULE(test->dpt);
+	if (err)
+		goto error;
+
+	/* map the asidrv_test as we will access it during the test */
+	err = dpt_map(test->dpt, test, sizeof(*test));
+	if (err)
+		goto error;
+
+	test->asi = asi_create_test();
+	if (!test->asi)
+		goto error;
+
+	/*
+	 * By default, the ASI structure is not mapped into the ASI. We
+	 * map it so that we can access it and verify the consistency
+	 * of some values (for example the CR3 value).
+	 */
+	err = dpt_map(test->dpt, test->asi, sizeof(*test->asi));
+	if (err)
+		goto error;
+
+	asi_set_pagetable(test->asi, test->dpt->pagetable);
+
+	return test;
+
+error:
+	pr_debug("Failed to create ASI Test\n");
+	asidrv_test_destroy(test);
+	return NULL;
+}
+
+static void asidrv_test_destroy(struct asidrv_test *test)
+{
+	if (!test)
+		return;
+
+	if (test->dpt)
+		dpt_destroy(test->dpt);
+
+	if (test->asi)
+		asi_destroy(test->asi);
+
+	kfree(test);
+}
+
+static const struct file_operations asidrv_fops = {
+	.owner		= THIS_MODULE,
+};
+
+static struct miscdevice asidrv_miscdev = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = KBUILD_MODNAME,
+	.fops = &asidrv_fops,
+};
+
+static int __init asidrv_init(void)
+{
+	int err;
+
+	asidrv_test = asidrv_test_create();
+	if (!asidrv_test)
+		return -ENOMEM;
+
+	err = misc_register(&asidrv_miscdev);
+	if (err) {
+		asidrv_test_destroy(asidrv_test);
+		asidrv_test = NULL;
+	}
+
+	return err;
+}
+
+static void __exit asidrv_exit(void)
+{
+	asidrv_test_destroy(asidrv_test);
+	asidrv_test = NULL;
+	misc_deregister(&asidrv_miscdev);
+}
+
+module_init(asidrv_init);
+module_exit(asidrv_exit);
+
+MODULE_AUTHOR("Alexandre Chartre <alexandre.chartre@oracle.com>");
+MODULE_DESCRIPTION("Privileged interface to ASI");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL v2");
-- 
2.18.2


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

* [RFC v4][PATCH part-3 03/14] asidrv: Introduce the ASIDRV_IOCTL_RUN_SEQUENCE ioctl
  2020-05-04 15:02 [RFC v4][PATCH part-3 00/14] ASI - Part III (ASI Test Driver and CLI) Alexandre Chartre
  2020-05-04 15:02 ` [RFC v4][PATCH part-3 01/14] mm/asi: Define the test ASI type Alexandre Chartre
  2020-05-04 15:02 ` [RFC v4][PATCH part-3 02/14] asidrv: Introduce the ASI driver Alexandre Chartre
@ 2020-05-04 15:02 ` Alexandre Chartre
  2020-05-04 15:02 ` [RFC v4][PATCH part-3 04/14] asidrv: Sequence to test ASI access to mapped/unmapped memory Alexandre Chartre
                   ` (10 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Alexandre Chartre @ 2020-05-04 15:02 UTC (permalink / raw)
  To: tglx, mingo, bp, hpa, dave.hansen, luto, peterz, x86, linux-mm,
	linux-kernel
  Cc: pbonzini, konrad.wilk, jan.setjeeilers, liran.alon, junaids,
	graf, rppt, kuzuno, mgross, alexandre.chartre

The ASIDRV_IOCTL_RUN_SEQUENCE ioctl runs a specified sequence with
the test ASI. The ioctl returns whether the run was successful or
not, and if the test ASI was active at the end of the run.

For now, two test sequences are implemented:
 - ASIDRV_SEQ_NOP does nothing but enters and exits the test ASI;
 - ASIDRV_SEQ_PRINTK calls printk while running with ASI.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 drivers/staging/asi/asidrv.c | 248 +++++++++++++++++++++++++++++++++++
 drivers/staging/asi/asidrv.h |  29 ++++
 2 files changed, 277 insertions(+)
 create mode 100644 drivers/staging/asi/asidrv.h

diff --git a/drivers/staging/asi/asidrv.c b/drivers/staging/asi/asidrv.c
index c06e4734e0e5..4f0548edb2f9 100644
--- a/drivers/staging/asi/asidrv.c
+++ b/drivers/staging/asi/asidrv.c
@@ -10,15 +10,28 @@
 
 #include <asm/asi.h>
 #include <asm/dpt.h>
+#include <asm/tlbflush.h>
+
+#include "asidrv.h"
 
 struct asidrv_test {
 	struct asi		*asi;	/* ASI for testing */
 	struct dpt		*dpt;	/* ASI decorated page-table */
 };
 
+struct asidrv_sequence {
+	const char *name;
+	enum asidrv_run_error (*setup)(struct asidrv_test *t);
+	enum asidrv_run_error (*run)(struct asidrv_test *t);
+	void (*cleanup)(struct asidrv_test *t);
+};
+
 static struct asidrv_test *asidrv_test;
 
 static void asidrv_test_destroy(struct asidrv_test *test);
+static void asidrv_run_fini(struct asidrv_test *test);
+static void asidrv_run_cleanup(struct asidrv_test *test,
+			       struct asidrv_sequence *sequence);
 
 static struct asidrv_test *asidrv_test_create(void)
 {
@@ -86,8 +99,243 @@ static void asidrv_test_destroy(struct asidrv_test *test)
 	kfree(test);
 }
 
+static int asidrv_asi_is_active(struct asi *asi)
+{
+	struct asi *current_asi;
+	unsigned long cr3;
+	bool is_active;
+	int idepth;
+
+	if (!asi)
+		return false;
+
+	current_asi = this_cpu_read(cpu_asi_session.asi);
+	if (current_asi == asi) {
+		idepth = this_cpu_read(cpu_asi_session.idepth);
+		is_active = (idepth == 0);
+	} else {
+		is_active = false;
+		if (current_asi) {
+			/* weird... another ASI is active! */
+			pr_debug("ASI %px is active (testing ASI = %px)\n",
+				 current_asi, asi);
+		}
+	}
+
+	/*
+	 * If the ASI is active check that the CR3 value is consistent with
+	 * this ASI being active. Otherwise, check that CR3 value doesn't
+	 * reference an ASI.
+	 */
+	cr3 = __native_read_cr3();
+	if (is_active) {
+		if ((cr3 ^ asi->base_cr3) >> ASI_PCID_PREFIX_SHIFT == 0)
+			return true;
+
+		pr_warn("ASI %px: active ASI has inconsistent CR3 value (cr3=%lx, ASI base=%lx)\n",
+			asi, cr3, asi->base_cr3);
+
+	} else if (cr3 & ASI_PCID_PREFIX_MASK) {
+		pr_warn("ASI %px: inactive ASI has inconsistent CR3 value (cr3=%lx, ASI base=%lx)\n",
+			asi, cr3, asi->base_cr3);
+	}
+
+	return false;
+}
+
+/*
+ * Printk Test Sequence
+ */
+static enum asidrv_run_error asidrv_printk_run(struct asidrv_test *test)
+{
+	pr_notice("asidrv printk test...\n");
+	return ASIDRV_RUN_ERR_NONE;
+}
+
+struct asidrv_sequence asidrv_sequences[] = {
+	[ASIDRV_SEQ_NOP] = {
+		"nop",
+		NULL, NULL, NULL,
+	},
+	[ASIDRV_SEQ_PRINTK] = {
+		"printk",
+		NULL, asidrv_printk_run, NULL,
+	},
+};
+
+static enum asidrv_run_error asidrv_run_init(struct asidrv_test *test)
+{
+	int err;
+
+	/*
+	 * Map the current stack, we need it to enter ASI.
+	 */
+	err = dpt_map(test->dpt, current->stack,
+		      PAGE_SIZE << THREAD_SIZE_ORDER);
+	if (err) {
+		asidrv_run_fini(test);
+		return ASIDRV_RUN_ERR_MAP_STACK;
+	}
+
+	/*
+	 * Map the current task, schedule() needs it.
+	 */
+	err = dpt_map(test->dpt, current, sizeof(struct task_struct));
+	if (err)
+		return ASIDRV_RUN_ERR_MAP_TASK;
+
+	/*
+	 * The ASI page-table has been updated so bump the generation
+	 * number to have the ASI TLB flushed.
+	 */
+	atomic64_inc(&test->asi->pgtable_gen);
+
+	return ASIDRV_RUN_ERR_NONE;
+}
+
+static void asidrv_run_fini(struct asidrv_test *test)
+{
+	dpt_unmap(test->dpt, current);
+	dpt_unmap(test->dpt, current->stack);
+}
+
+static enum asidrv_run_error asidrv_run_setup(struct asidrv_test *test,
+					      struct asidrv_sequence *sequence)
+{
+	int run_err = ASIDRV_RUN_ERR_NONE;
+
+	if (sequence->setup) {
+		run_err = sequence->setup(test);
+		if (run_err)
+			goto failed;
+	}
+
+	return ASIDRV_RUN_ERR_NONE;
+
+failed:
+	return run_err;
+}
+
+static void asidrv_run_cleanup(struct asidrv_test *test,
+			       struct asidrv_sequence *sequence)
+{
+	if (sequence->cleanup)
+		sequence->cleanup(test);
+}
+
+/*
+ * Run the specified sequence with ASI. Report result back.
+ */
+static enum asidrv_run_error asidrv_run(struct asidrv_test *test,
+					enum asidrv_seqnum seqnum,
+					bool *asi_active)
+{
+	struct asidrv_sequence *sequence = &asidrv_sequences[seqnum];
+	int run_err = ASIDRV_RUN_ERR_NONE;
+	int err = 0;
+
+	if (seqnum >= ARRAY_SIZE(asidrv_sequences)) {
+		pr_debug("Undefined sequence %d\n", seqnum);
+		return ASIDRV_RUN_ERR_SEQUENCE;
+	}
+
+	pr_debug("ASI running sequence %s\n", sequence->name);
+
+	run_err = asidrv_run_setup(test, sequence);
+	if (run_err)
+		return run_err;
+
+	err = asi_enter(test->asi);
+	if (err) {
+		run_err = ASIDRV_RUN_ERR_ENTER;
+		goto failed_noexit;
+	}
+
+	if (!asidrv_asi_is_active(test->asi)) {
+		run_err = ASIDRV_RUN_ERR_ACTIVE;
+		goto failed;
+	}
+
+	if (sequence->run) {
+		run_err = sequence->run(test);
+		if (run_err != ASIDRV_RUN_ERR_NONE)
+			goto failed;
+	}
+
+	*asi_active = asidrv_asi_is_active(test->asi);
+
+failed:
+	asi_exit(test->asi);
+
+failed_noexit:
+	asidrv_run_cleanup(test, sequence);
+
+	return run_err;
+}
+
+static int asidrv_ioctl_run_sequence(struct asidrv_test *test,
+				     unsigned long arg)
+{
+	struct asidrv_run_param __user *urparam;
+	struct asidrv_run_param rparam;
+	enum asidrv_run_error run_err;
+	enum asidrv_seqnum seqnum;
+	bool asi_active = false;
+
+	urparam = (struct asidrv_run_param *)arg;
+	if (copy_from_user(&rparam, urparam, sizeof(rparam)))
+		return -EFAULT;
+
+	seqnum = rparam.sequence;
+
+	pr_debug("ASI sequence %d\n", seqnum);
+
+	run_err = asidrv_run_init(test);
+	if (run_err) {
+		pr_debug("ASI run init error %d\n", run_err);
+		goto failed_nofini;
+	}
+
+	run_err = asidrv_run(test, seqnum, &asi_active);
+	if (run_err) {
+		pr_debug("ASI run error %d\n", run_err);
+	} else {
+		pr_debug("ASI run okay, ASI is %s\n",
+			 asi_active ? "active" : "inactive");
+	}
+
+	asidrv_run_fini(test);
+
+failed_nofini:
+	rparam.run_error = run_err;
+	rparam.asi_active = asi_active;
+
+	if (copy_to_user(urparam, &rparam, sizeof(rparam)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static long asidrv_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	struct asidrv_test *test = asidrv_test;
+
+	switch (cmd) {
+
+	/* Test ioctls */
+
+	case ASIDRV_IOCTL_RUN_SEQUENCE:
+		return asidrv_ioctl_run_sequence(test, arg);
+
+	default:
+		return -ENOTTY;
+	};
+}
+
 static const struct file_operations asidrv_fops = {
 	.owner		= THIS_MODULE,
+	.unlocked_ioctl	= asidrv_ioctl,
+	.compat_ioctl	= compat_ptr_ioctl,
 };
 
 static struct miscdevice asidrv_miscdev = {
diff --git a/drivers/staging/asi/asidrv.h b/drivers/staging/asi/asidrv.h
new file mode 100644
index 000000000000..33acb058c443
--- /dev/null
+++ b/drivers/staging/asi/asidrv.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+
+#ifndef __ASIDRV_H__
+#define __ASIDRV_H__
+
+#include <linux/types.h>
+
+enum asidrv_seqnum {
+	ASIDRV_SEQ_NOP,		/* empty sequence */
+	ASIDRV_SEQ_PRINTK,	/* printk sequence */
+};
+
+enum asidrv_run_error {
+	ASIDRV_RUN_ERR_NONE,	/* no error */
+	ASIDRV_RUN_ERR_SEQUENCE, /* unknown sequence */
+	ASIDRV_RUN_ERR_MAP_STACK, /* failed to map current stack */
+	ASIDRV_RUN_ERR_MAP_TASK, /* failed to map current task */
+	ASIDRV_RUN_ERR_ENTER,	/* failed to enter ASI */
+	ASIDRV_RUN_ERR_ACTIVE,	/* ASI is not active after entering ASI */
+};
+
+#define ASIDRV_IOCTL_RUN_SEQUENCE	_IOWR('a', 1, struct asidrv_run_param)
+
+struct asidrv_run_param {
+	__u32 sequence;		/* sequence to run */
+	__u32 run_error;	/* result error after run */
+	__u32 asi_active;	/* ASI is active after run? */
+};
+#endif
-- 
2.18.2


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

* [RFC v4][PATCH part-3 04/14] asidrv: Sequence to test ASI access to mapped/unmapped memory
  2020-05-04 15:02 [RFC v4][PATCH part-3 00/14] ASI - Part III (ASI Test Driver and CLI) Alexandre Chartre
                   ` (2 preceding siblings ...)
  2020-05-04 15:02 ` [RFC v4][PATCH part-3 03/14] asidrv: Introduce the ASIDRV_IOCTL_RUN_SEQUENCE ioctl Alexandre Chartre
@ 2020-05-04 15:02 ` Alexandre Chartre
  2020-05-04 15:02 ` [RFC v4][PATCH part-3 05/14] asidrv: Sequence to test interrupt on ASI Alexandre Chartre
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Alexandre Chartre @ 2020-05-04 15:02 UTC (permalink / raw)
  To: tglx, mingo, bp, hpa, dave.hansen, luto, peterz, x86, linux-mm,
	linux-kernel
  Cc: pbonzini, konrad.wilk, jan.setjeeilers, liran.alon, junaids,
	graf, rppt, kuzuno, mgross, alexandre.chartre

Add a sequence to test if ASI exit or not when accessing a mapped
or unmapped memory buffer.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 drivers/staging/asi/asidrv.c | 70 ++++++++++++++++++++++++++++++++++++
 drivers/staging/asi/asidrv.h |  3 ++
 2 files changed, 73 insertions(+)

diff --git a/drivers/staging/asi/asidrv.c b/drivers/staging/asi/asidrv.c
index 4f0548edb2f9..4231b56db167 100644
--- a/drivers/staging/asi/asidrv.c
+++ b/drivers/staging/asi/asidrv.c
@@ -14,9 +14,21 @@
 
 #include "asidrv.h"
 
+#define ASIDRV_TEST_BUFFER_SIZE	PAGE_SIZE
+
+/* Number of read for mem/memmap test sequence */
+#define ASIDRV_MEM_READ_COUNT		1000
+
+enum asidrv_state {
+	ASIDRV_STATE_NONE,
+	ASIDRV_STATE_INTR_WAITING,
+	ASIDRV_STATE_INTR_RECEIVED,
+};
+
 struct asidrv_test {
 	struct asi		*asi;	/* ASI for testing */
 	struct dpt		*dpt;	/* ASI decorated page-table */
+	char			*buffer; /* buffer for testing */
 };
 
 struct asidrv_sequence {
@@ -42,6 +54,10 @@ static struct asidrv_test *asidrv_test_create(void)
 	if (!test)
 		return NULL;
 
+	test->buffer = kzalloc(ASIDRV_TEST_BUFFER_SIZE, GFP_KERNEL);
+	if (!test->buffer)
+		goto error;
+
 	/*
 	 * Create and fill a decorator page-table to be used with the ASI.
 	 */
@@ -96,6 +112,7 @@ static void asidrv_test_destroy(struct asidrv_test *test)
 	if (test->asi)
 		asi_destroy(test->asi);
 
+	kfree(test->buffer);
 	kfree(test);
 }
 
@@ -143,6 +160,51 @@ static int asidrv_asi_is_active(struct asi *asi)
 	return false;
 }
 
+/*
+ * Memory Buffer Access Test Sequences
+ */
+
+#define OPTNONE __attribute__((optimize(0)))
+
+static enum asidrv_run_error OPTNONE asidrv_mem_run(struct asidrv_test *test)
+{
+	char c;
+	int i, index;
+
+	/*
+	 * Do random reads in the test buffer, and return if the ASI
+	 * becomes inactive.
+	 */
+	for (i = 0; i < ASIDRV_MEM_READ_COUNT; i++) {
+		index = get_cycles() % ASIDRV_TEST_BUFFER_SIZE;
+		c = test->buffer[index];
+		if (!asidrv_asi_is_active(test->asi)) {
+			pr_warn("ASI inactive after reading byte %d at %d\n",
+				i + 1, index);
+			break;
+		}
+	}
+
+	return ASIDRV_RUN_ERR_NONE;
+}
+
+static enum asidrv_run_error asidrv_memmap_setup(struct asidrv_test *test)
+{
+	int err;
+
+	pr_debug("mapping test buffer %px\n", test->buffer);
+	err = dpt_map(test->dpt, test->buffer, ASIDRV_TEST_BUFFER_SIZE);
+	if (err)
+		return ASIDRV_RUN_ERR_MAP_BUFFER;
+
+	return ASIDRV_RUN_ERR_NONE;
+}
+
+static void asidrv_memmap_cleanup(struct asidrv_test *test)
+{
+	dpt_unmap(test->dpt, test->buffer);
+}
+
 /*
  * Printk Test Sequence
  */
@@ -161,6 +223,14 @@ struct asidrv_sequence asidrv_sequences[] = {
 		"printk",
 		NULL, asidrv_printk_run, NULL,
 	},
+	[ASIDRV_SEQ_MEM] = {
+		"mem",
+		NULL, asidrv_mem_run, NULL,
+	},
+	[ASIDRV_SEQ_MEMMAP] = {
+		"memmap",
+		asidrv_memmap_setup, asidrv_mem_run, asidrv_memmap_cleanup,
+	},
 };
 
 static enum asidrv_run_error asidrv_run_init(struct asidrv_test *test)
diff --git a/drivers/staging/asi/asidrv.h b/drivers/staging/asi/asidrv.h
index 33acb058c443..1e820cc64f13 100644
--- a/drivers/staging/asi/asidrv.h
+++ b/drivers/staging/asi/asidrv.h
@@ -8,6 +8,8 @@
 enum asidrv_seqnum {
 	ASIDRV_SEQ_NOP,		/* empty sequence */
 	ASIDRV_SEQ_PRINTK,	/* printk sequence */
+	ASIDRV_SEQ_MEM,		/* access unmapped memory */
+	ASIDRV_SEQ_MEMMAP,	/* access mapped memory */
 };
 
 enum asidrv_run_error {
@@ -17,6 +19,7 @@ enum asidrv_run_error {
 	ASIDRV_RUN_ERR_MAP_TASK, /* failed to map current task */
 	ASIDRV_RUN_ERR_ENTER,	/* failed to enter ASI */
 	ASIDRV_RUN_ERR_ACTIVE,	/* ASI is not active after entering ASI */
+	ASIDRV_RUN_ERR_MAP_BUFFER, /* failed to map buffer */
 };
 
 #define ASIDRV_IOCTL_RUN_SEQUENCE	_IOWR('a', 1, struct asidrv_run_param)
-- 
2.18.2


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

* [RFC v4][PATCH part-3 05/14] asidrv: Sequence to test interrupt on ASI
  2020-05-04 15:02 [RFC v4][PATCH part-3 00/14] ASI - Part III (ASI Test Driver and CLI) Alexandre Chartre
                   ` (3 preceding siblings ...)
  2020-05-04 15:02 ` [RFC v4][PATCH part-3 04/14] asidrv: Sequence to test ASI access to mapped/unmapped memory Alexandre Chartre
@ 2020-05-04 15:02 ` Alexandre Chartre
  2020-05-04 15:02 ` [RFC v4][PATCH part-3 06/14] asidrv: Sequence to test NMI " Alexandre Chartre
                   ` (8 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Alexandre Chartre @ 2020-05-04 15:02 UTC (permalink / raw)
  To: tglx, mingo, bp, hpa, dave.hansen, luto, peterz, x86, linux-mm,
	linux-kernel
  Cc: pbonzini, konrad.wilk, jan.setjeeilers, liran.alon, junaids,
	graf, rppt, kuzuno, mgross, alexandre.chartre

Add a sequence to test if an ASI remains active after receiving
an interrupt.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 drivers/staging/asi/asidrv.c | 144 +++++++++++++++++++++++++++++++++--
 drivers/staging/asi/asidrv.h |   5 ++
 2 files changed, 144 insertions(+), 5 deletions(-)

diff --git a/drivers/staging/asi/asidrv.c b/drivers/staging/asi/asidrv.c
index 4231b56db167..a3c7da2bf16e 100644
--- a/drivers/staging/asi/asidrv.c
+++ b/drivers/staging/asi/asidrv.c
@@ -7,6 +7,7 @@
 #include <linux/miscdevice.h>
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <linux/workqueue.h>
 
 #include <asm/asi.h>
 #include <asm/dpt.h>
@@ -19,6 +20,12 @@
 /* Number of read for mem/memmap test sequence */
 #define ASIDRV_MEM_READ_COUNT		1000
 
+/* Timeout for target to be ready to receive an interrupt */
+#define ASIDRV_TIMEOUT_TARGET_READY	1
+
+/* Timeout for receiving an interrupt */
+#define ASIDRV_TIMEOUT_INTERRUPT	5
+
 enum asidrv_state {
 	ASIDRV_STATE_NONE,
 	ASIDRV_STATE_INTR_WAITING,
@@ -29,6 +36,13 @@ struct asidrv_test {
 	struct asi		*asi;	/* ASI for testing */
 	struct dpt		*dpt;	/* ASI decorated page-table */
 	char			*buffer; /* buffer for testing */
+
+	/* runtime */
+	atomic_t		state;	/* runtime state */
+	int			cpu;	/* cpu the test is running on */
+	struct work_struct	work;	/* work for other cpu */
+	bool			work_set;
+	enum asidrv_run_error	run_error;
 };
 
 struct asidrv_sequence {
@@ -160,6 +174,107 @@ static int asidrv_asi_is_active(struct asi *asi)
 	return false;
 }
 
+/*
+ * Wait for an atomic value to be set or the timeout to expire.
+ * Return 0 if the value is set, or -1 if the timeout expires.
+ */
+static enum asidrv_run_error asidrv_wait(struct asidrv_test *test,
+					 int value, unsigned int timeout)
+{
+	cycles_t start = get_cycles();
+	cycles_t stop = start + timeout * tsc_khz * 1000;
+
+	while (get_cycles() < stop) {
+		if (atomic_read(&test->state) == value ||
+		    test->run_error != ASIDRV_RUN_ERR_NONE)
+			return test->run_error;
+		cpu_relax();
+	}
+
+	/* timeout reached */
+	return ASIDRV_RUN_ERR_TIMEOUT;
+}
+
+/*
+ * Wait for an atomic value to transition from the initial value (set
+ * on entry) to the final value, or to timeout. Return 0 if the transition
+ * was done, or -1 if the timeout expires.
+ */
+static enum asidrv_run_error asidrv_wait_transition(struct asidrv_test *test,
+						    int initial, int final,
+						    unsigned int timeout)
+{
+	/* set the initial state value */
+	atomic_set(&test->state, initial);
+
+	/* do an active wait for the state changes */
+	return asidrv_wait(test, final, timeout);
+}
+
+/*
+ * Interrupt Test Sequence
+ */
+
+static void asidrv_intr_handler(void *info)
+{
+	struct asidrv_test *test = info;
+
+	/* ASI should be interrupted by the interrupt */
+	if (asidrv_asi_is_active(test->asi)) {
+		test->run_error = ASIDRV_RUN_ERR_INTR_ASI_ACTIVE;
+		atomic_set(&test->state, ASIDRV_STATE_INTR_RECEIVED);
+		return;
+	}
+
+	pr_debug("Received interrupt\n");
+	atomic_set(&test->state, ASIDRV_STATE_INTR_RECEIVED);
+}
+
+static void asidrv_intr_send(struct work_struct *work)
+{
+	struct asidrv_test *test = container_of(work, struct asidrv_test, work);
+	enum asidrv_run_error err;
+
+	/* wait for cpu target to be ready, then send an interrupt */
+	err = asidrv_wait(test,
+			  ASIDRV_STATE_INTR_WAITING,
+			  ASIDRV_TIMEOUT_TARGET_READY);
+	if (err) {
+		pr_debug("Target cpu %d not ready, interrupt not sent: error %d\n",
+			 test->cpu, err);
+		return;
+	}
+
+	pr_debug("Sending interrupt to cpu %d\n", test->cpu);
+	smp_call_function_single(test->cpu, asidrv_intr_handler,
+				 test, false);
+}
+
+static enum asidrv_run_error asidrv_intr_setup(struct asidrv_test *test)
+{
+	/* set work to have another cpu to send us an interrupt */
+	INIT_WORK(&test->work, asidrv_intr_send);
+	test->work_set = true;
+	return ASIDRV_RUN_ERR_NONE;
+}
+
+static enum asidrv_run_error asidrv_intr_run(struct asidrv_test *test)
+{
+	enum asidrv_run_error err;
+
+	/* wait for state changes indicating that an interrupt was received */
+	err = asidrv_wait_transition(test,
+				     ASIDRV_STATE_INTR_WAITING,
+				     ASIDRV_STATE_INTR_RECEIVED,
+				     ASIDRV_TIMEOUT_INTERRUPT);
+	if (err == ASIDRV_RUN_ERR_TIMEOUT) {
+		pr_debug("Interrupt wait timeout\n");
+		err = ASIDRV_RUN_ERR_INTR;
+	}
+
+	return err;
+}
+
 /*
  * Memory Buffer Access Test Sequences
  */
@@ -231,12 +346,18 @@ struct asidrv_sequence asidrv_sequences[] = {
 		"memmap",
 		asidrv_memmap_setup, asidrv_mem_run, asidrv_memmap_cleanup,
 	},
+	[ASIDRV_SEQ_INTERRUPT] = {
+		"interrupt",
+		asidrv_intr_setup, asidrv_intr_run, NULL,
+	},
 };
 
 static enum asidrv_run_error asidrv_run_init(struct asidrv_test *test)
 {
 	int err;
 
+	test->run_error = ASIDRV_RUN_ERR_NONE;
+
 	/*
 	 * Map the current stack, we need it to enter ASI.
 	 */
@@ -272,18 +393,31 @@ static void asidrv_run_fini(struct asidrv_test *test)
 static enum asidrv_run_error asidrv_run_setup(struct asidrv_test *test,
 					      struct asidrv_sequence *sequence)
 {
-	int run_err = ASIDRV_RUN_ERR_NONE;
+	unsigned int other_cpu;
+	int run_err;
+
+	test->work_set = false;
 
 	if (sequence->setup) {
 		run_err = sequence->setup(test);
 		if (run_err)
-			goto failed;
+			return run_err;
 	}
 
-	return ASIDRV_RUN_ERR_NONE;
+	if (test->work_set) {
+		other_cpu = cpumask_any_but(cpu_online_mask, test->cpu);
+		if (other_cpu == test->cpu) {
+			pr_debug("Sequence %s requires an extra online cpu\n",
+				 sequence->name);
+			asidrv_run_cleanup(test, sequence);
+			return ASIDRV_RUN_ERR_NCPUS;
+		}
 
-failed:
-	return run_err;
+		atomic_set(&test->state, ASIDRV_STATE_NONE);
+		schedule_work_on(other_cpu, &test->work);
+	}
+
+	return ASIDRV_RUN_ERR_NONE;
 }
 
 static void asidrv_run_cleanup(struct asidrv_test *test,
diff --git a/drivers/staging/asi/asidrv.h b/drivers/staging/asi/asidrv.h
index 1e820cc64f13..8055d96a0058 100644
--- a/drivers/staging/asi/asidrv.h
+++ b/drivers/staging/asi/asidrv.h
@@ -10,6 +10,7 @@ enum asidrv_seqnum {
 	ASIDRV_SEQ_PRINTK,	/* printk sequence */
 	ASIDRV_SEQ_MEM,		/* access unmapped memory */
 	ASIDRV_SEQ_MEMMAP,	/* access mapped memory */
+	ASIDRV_SEQ_INTERRUPT,	/* interrupt sequence */
 };
 
 enum asidrv_run_error {
@@ -20,6 +21,10 @@ enum asidrv_run_error {
 	ASIDRV_RUN_ERR_ENTER,	/* failed to enter ASI */
 	ASIDRV_RUN_ERR_ACTIVE,	/* ASI is not active after entering ASI */
 	ASIDRV_RUN_ERR_MAP_BUFFER, /* failed to map buffer */
+	ASIDRV_RUN_ERR_NCPUS,	/* not enough active cpus */
+	ASIDRV_RUN_ERR_INTR,	/* no interrupt received */
+	ASIDRV_RUN_ERR_INTR_ASI_ACTIVE, /* ASI active in interrupt handler */
+	ASIDRV_RUN_ERR_TIMEOUT,
 };
 
 #define ASIDRV_IOCTL_RUN_SEQUENCE	_IOWR('a', 1, struct asidrv_run_param)
-- 
2.18.2


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

* [RFC v4][PATCH part-3 06/14] asidrv: Sequence to test NMI on ASI
  2020-05-04 15:02 [RFC v4][PATCH part-3 00/14] ASI - Part III (ASI Test Driver and CLI) Alexandre Chartre
                   ` (4 preceding siblings ...)
  2020-05-04 15:02 ` [RFC v4][PATCH part-3 05/14] asidrv: Sequence to test interrupt on ASI Alexandre Chartre
@ 2020-05-04 15:02 ` Alexandre Chartre
  2020-05-04 15:02 ` [RFC v4][PATCH part-3 07/14] asidrv: Sequence to test interrupt+NMI " Alexandre Chartre
                   ` (7 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Alexandre Chartre @ 2020-05-04 15:02 UTC (permalink / raw)
  To: tglx, mingo, bp, hpa, dave.hansen, luto, peterz, x86, linux-mm,
	linux-kernel
  Cc: pbonzini, konrad.wilk, jan.setjeeilers, liran.alon, junaids,
	graf, rppt, kuzuno, mgross, alexandre.chartre

Add a sequence to test if an ASI remains active after receiving
an NMI.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 drivers/staging/asi/asidrv.c | 116 ++++++++++++++++++++++++++++++++++-
 drivers/staging/asi/asidrv.h |   4 ++
 2 files changed, 118 insertions(+), 2 deletions(-)

diff --git a/drivers/staging/asi/asidrv.c b/drivers/staging/asi/asidrv.c
index a3c7da2bf16e..8f964c9e0b14 100644
--- a/drivers/staging/asi/asidrv.c
+++ b/drivers/staging/asi/asidrv.c
@@ -6,6 +6,7 @@
 #include <linux/fs.h>
 #include <linux/miscdevice.h>
 #include <linux/module.h>
+#include <linux/nmi.h>
 #include <linux/slab.h>
 #include <linux/workqueue.h>
 
@@ -20,16 +21,21 @@
 /* Number of read for mem/memmap test sequence */
 #define ASIDRV_MEM_READ_COUNT		1000
 
-/* Timeout for target to be ready to receive an interrupt */
+/* Timeout for target to be ready to receive an interrupt or NMI */
 #define ASIDRV_TIMEOUT_TARGET_READY	1
 
-/* Timeout for receiving an interrupt */
+/* Timeout for receiving an interrupt or NMI */
 #define ASIDRV_TIMEOUT_INTERRUPT	5
 
+/* NMI handler name used for testing */
+#define ASIDRV_NMI_HANDLER_NAME		"ASI Test"
+
 enum asidrv_state {
 	ASIDRV_STATE_NONE,
 	ASIDRV_STATE_INTR_WAITING,
 	ASIDRV_STATE_INTR_RECEIVED,
+	ASIDRV_STATE_NMI_WAITING,
+	ASIDRV_STATE_NMI_RECEIVED,
 };
 
 struct asidrv_test {
@@ -53,6 +59,7 @@ struct asidrv_sequence {
 };
 
 static struct asidrv_test *asidrv_test;
+static struct asidrv_test *asidrv_nmi_target;
 
 static void asidrv_test_destroy(struct asidrv_test *test);
 static void asidrv_run_fini(struct asidrv_test *test);
@@ -211,6 +218,107 @@ static enum asidrv_run_error asidrv_wait_transition(struct asidrv_test *test,
 	return asidrv_wait(test, final, timeout);
 }
 
+/*
+ * NMI Test Sequence
+ */
+
+static int asidrv_nmi_handler(unsigned int val, struct pt_regs *regs)
+{
+	struct asidrv_test *test = asidrv_nmi_target;
+
+	if (!test)
+		return NMI_DONE;
+
+	/* ASI should be interrupted by the NMI */
+	if (asidrv_asi_is_active(test->asi)) {
+		test->run_error = ASIDRV_RUN_ERR_NMI_ASI_ACTIVE;
+		atomic_set(&test->state, ASIDRV_STATE_NMI_RECEIVED);
+		return NMI_HANDLED;
+	}
+
+	pr_debug("Received NMI\n");
+	atomic_set(&test->state, ASIDRV_STATE_NMI_RECEIVED);
+	asidrv_nmi_target = NULL;
+
+	return NMI_HANDLED;
+}
+
+static void asidrv_nmi_send(struct work_struct *work)
+{
+	struct asidrv_test *test = container_of(work, struct asidrv_test, work);
+	cpumask_t mask = CPU_MASK_NONE;
+	enum asidrv_run_error err;
+
+	cpumask_set_cpu(test->cpu, &mask);
+
+	/* wait for cpu target to be ready, then send an NMI */
+	err = asidrv_wait(test,
+			  ASIDRV_STATE_NMI_WAITING,
+			  ASIDRV_TIMEOUT_TARGET_READY);
+	if (err) {
+		pr_debug("Target cpu %d not ready, NMI not sent: error %d\n",
+			 test->cpu, err);
+		return;
+	}
+
+	pr_debug("Sending NMI to cpu %d\n", test->cpu);
+	asidrv_nmi_target = test;
+	/*
+	 * The value of asidrv_nmi_target should be set and propagated
+	 * before sending the IPI.
+	 */
+	wmb();
+	apic->send_IPI_mask(&mask, NMI_VECTOR);
+}
+
+static enum asidrv_run_error asidrv_nmi_setup(struct asidrv_test *test)
+{
+	int err;
+
+	err = register_nmi_handler(NMI_LOCAL, asidrv_nmi_handler,
+				   NMI_FLAG_FIRST,
+				   ASIDRV_NMI_HANDLER_NAME);
+	if (err) {
+		pr_debug("Failed to register NMI handler\n");
+		return ASIDRV_RUN_ERR_NMI_REG;
+	}
+
+	/* set work to have another cpu to send us an NMI */
+	INIT_WORK(&test->work, asidrv_nmi_send);
+
+	test->work_set = true;
+
+	return ASIDRV_RUN_ERR_NONE;
+}
+
+static void asidrv_nmi_cleanup(struct asidrv_test *test)
+{
+	unregister_nmi_handler(NMI_LOCAL, ASIDRV_NMI_HANDLER_NAME);
+}
+
+static enum asidrv_run_error asidrv_nmi_run(struct asidrv_test *test)
+{
+	enum asidrv_run_error err;
+	unsigned long flags;
+
+	/* disable interrupts as we want to be interrupted by an NMI */
+	local_irq_save(flags);
+
+	/* wait for state changes indicating that an NMI was received */
+	err = asidrv_wait_transition(test,
+				     ASIDRV_STATE_NMI_WAITING,
+				     ASIDRV_STATE_NMI_RECEIVED,
+				     ASIDRV_TIMEOUT_INTERRUPT);
+	if (err == ASIDRV_RUN_ERR_TIMEOUT) {
+		pr_debug("NMI wait timeout\n");
+		err = ASIDRV_RUN_ERR_NMI;
+	}
+
+	local_irq_restore(flags);
+
+	return err;
+}
+
 /*
  * Interrupt Test Sequence
  */
@@ -350,6 +458,10 @@ struct asidrv_sequence asidrv_sequences[] = {
 		"interrupt",
 		asidrv_intr_setup, asidrv_intr_run, NULL,
 	},
+	[ASIDRV_SEQ_NMI] = {
+		"nmi",
+		asidrv_nmi_setup, asidrv_nmi_run, asidrv_nmi_cleanup,
+	},
 };
 
 static enum asidrv_run_error asidrv_run_init(struct asidrv_test *test)
diff --git a/drivers/staging/asi/asidrv.h b/drivers/staging/asi/asidrv.h
index 8055d96a0058..6fac32d56f6f 100644
--- a/drivers/staging/asi/asidrv.h
+++ b/drivers/staging/asi/asidrv.h
@@ -11,6 +11,7 @@ enum asidrv_seqnum {
 	ASIDRV_SEQ_MEM,		/* access unmapped memory */
 	ASIDRV_SEQ_MEMMAP,	/* access mapped memory */
 	ASIDRV_SEQ_INTERRUPT,	/* interrupt sequence */
+	ASIDRV_SEQ_NMI,		/* NMI interrupt sequence */
 };
 
 enum asidrv_run_error {
@@ -25,6 +26,9 @@ enum asidrv_run_error {
 	ASIDRV_RUN_ERR_INTR,	/* no interrupt received */
 	ASIDRV_RUN_ERR_INTR_ASI_ACTIVE, /* ASI active in interrupt handler */
 	ASIDRV_RUN_ERR_TIMEOUT,
+	ASIDRV_RUN_ERR_NMI,	/* no NMI received */
+	ASIDRV_RUN_ERR_NMI_REG,	/* failed to register NMI handler */
+	ASIDRV_RUN_ERR_NMI_ASI_ACTIVE, /* ASI active in NMI handler */
 };
 
 #define ASIDRV_IOCTL_RUN_SEQUENCE	_IOWR('a', 1, struct asidrv_run_param)
-- 
2.18.2


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

* [RFC v4][PATCH part-3 07/14] asidrv: Sequence to test interrupt+NMI on ASI
  2020-05-04 15:02 [RFC v4][PATCH part-3 00/14] ASI - Part III (ASI Test Driver and CLI) Alexandre Chartre
                   ` (5 preceding siblings ...)
  2020-05-04 15:02 ` [RFC v4][PATCH part-3 06/14] asidrv: Sequence to test NMI " Alexandre Chartre
@ 2020-05-04 15:02 ` Alexandre Chartre
  2020-05-04 15:02 ` [RFC v4][PATCH part-3 08/14] asidrv: Sequence to test scheduling in/out with ASI Alexandre Chartre
                   ` (6 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Alexandre Chartre @ 2020-05-04 15:02 UTC (permalink / raw)
  To: tglx, mingo, bp, hpa, dave.hansen, luto, peterz, x86, linux-mm,
	linux-kernel
  Cc: pbonzini, konrad.wilk, jan.setjeeilers, liran.alon, junaids,
	graf, rppt, kuzuno, mgross, alexandre.chartre

Add a sequence to test if an ASI remains active after receiving
an interrupt which is itself interrupted by an NMI.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 drivers/staging/asi/asidrv.c | 62 +++++++++++++++++++++++++++++++++++-
 drivers/staging/asi/asidrv.h |  1 +
 2 files changed, 62 insertions(+), 1 deletion(-)

diff --git a/drivers/staging/asi/asidrv.c b/drivers/staging/asi/asidrv.c
index 8f964c9e0b14..e08f2a107e89 100644
--- a/drivers/staging/asi/asidrv.c
+++ b/drivers/staging/asi/asidrv.c
@@ -49,6 +49,7 @@ struct asidrv_test {
 	struct work_struct	work;	/* work for other cpu */
 	bool			work_set;
 	enum asidrv_run_error	run_error;
+	bool			intrnmi;
 };
 
 struct asidrv_sequence {
@@ -65,6 +66,7 @@ static void asidrv_test_destroy(struct asidrv_test *test);
 static void asidrv_run_fini(struct asidrv_test *test);
 static void asidrv_run_cleanup(struct asidrv_test *test,
 			       struct asidrv_sequence *sequence);
+static void asidrv_intrnmi_send(struct work_struct *work);
 
 static struct asidrv_test *asidrv_test_create(void)
 {
@@ -284,7 +286,10 @@ static enum asidrv_run_error asidrv_nmi_setup(struct asidrv_test *test)
 	}
 
 	/* set work to have another cpu to send us an NMI */
-	INIT_WORK(&test->work, asidrv_nmi_send);
+	if (test->intrnmi)
+		INIT_WORK(&test->work, asidrv_intrnmi_send);
+	else
+		INIT_WORK(&test->work, asidrv_nmi_send);
 
 	test->work_set = true;
 
@@ -336,6 +341,12 @@ static void asidrv_intr_handler(void *info)
 
 	pr_debug("Received interrupt\n");
 	atomic_set(&test->state, ASIDRV_STATE_INTR_RECEIVED);
+
+	if (!test->intrnmi)
+		return;
+
+	pr_debug("Waiting for NMI in interrupt\n");
+	asidrv_nmi_run(test);
 }
 
 static void asidrv_intr_send(struct work_struct *work)
@@ -383,6 +394,50 @@ static enum asidrv_run_error asidrv_intr_run(struct asidrv_test *test)
 	return err;
 }
 
+/*
+ * Interrupt+NMI Test Sequence
+ */
+
+static void asidrv_intrnmi_send(struct work_struct *work)
+{
+	/* send and interrupt and then send an NMI */
+	asidrv_intr_send(work);
+	asidrv_nmi_send(work);
+}
+
+static enum asidrv_run_error asidrv_intrnmi_setup(struct asidrv_test *test)
+{
+	test->intrnmi = true;
+	return asidrv_nmi_setup(test);
+}
+
+static enum asidrv_run_error asidrv_intrnmi_run(struct asidrv_test *test)
+{
+	enum asidrv_run_error err;
+	enum asidrv_state state;
+
+	/*
+	 * Wait for state changes indicating that an interrupt and
+	 * then an NMI were received.
+	 */
+	err = asidrv_wait_transition(test,
+				     ASIDRV_STATE_INTR_WAITING,
+				     ASIDRV_STATE_NMI_RECEIVED,
+				     ASIDRV_TIMEOUT_INTERRUPT);
+	if (err == ASIDRV_RUN_ERR_TIMEOUT) {
+		state = atomic_read(&test->state);
+		if (state == ASIDRV_STATE_INTR_WAITING) {
+			pr_debug("Interrupt wait timeout\n");
+			err = ASIDRV_RUN_ERR_INTR;
+		} else {
+			pr_debug("NMI wait timeout\n");
+			err = ASIDRV_RUN_ERR_NMI;
+		}
+	}
+
+	return err;
+}
+
 /*
  * Memory Buffer Access Test Sequences
  */
@@ -462,6 +517,10 @@ struct asidrv_sequence asidrv_sequences[] = {
 		"nmi",
 		asidrv_nmi_setup, asidrv_nmi_run, asidrv_nmi_cleanup,
 	},
+	[ASIDRV_SEQ_INTRNMI] = {
+		"intr+nmi",
+		asidrv_intrnmi_setup, asidrv_intrnmi_run, asidrv_nmi_cleanup,
+	},
 };
 
 static enum asidrv_run_error asidrv_run_init(struct asidrv_test *test)
@@ -509,6 +568,7 @@ static enum asidrv_run_error asidrv_run_setup(struct asidrv_test *test,
 	int run_err;
 
 	test->work_set = false;
+	test->intrnmi = false;
 
 	if (sequence->setup) {
 		run_err = sequence->setup(test);
diff --git a/drivers/staging/asi/asidrv.h b/drivers/staging/asi/asidrv.h
index 6fac32d56f6f..de9f7ad993e0 100644
--- a/drivers/staging/asi/asidrv.h
+++ b/drivers/staging/asi/asidrv.h
@@ -12,6 +12,7 @@ enum asidrv_seqnum {
 	ASIDRV_SEQ_MEMMAP,	/* access mapped memory */
 	ASIDRV_SEQ_INTERRUPT,	/* interrupt sequence */
 	ASIDRV_SEQ_NMI,		/* NMI interrupt sequence */
+	ASIDRV_SEQ_INTRNMI,	/* NMI in interrupt sequence */
 };
 
 enum asidrv_run_error {
-- 
2.18.2


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

* [RFC v4][PATCH part-3 08/14] asidrv: Sequence to test scheduling in/out with ASI
  2020-05-04 15:02 [RFC v4][PATCH part-3 00/14] ASI - Part III (ASI Test Driver and CLI) Alexandre Chartre
                   ` (6 preceding siblings ...)
  2020-05-04 15:02 ` [RFC v4][PATCH part-3 07/14] asidrv: Sequence to test interrupt+NMI " Alexandre Chartre
@ 2020-05-04 15:02 ` Alexandre Chartre
  2020-05-04 15:02 ` [RFC v4][PATCH part-3 09/14] asidrv: Add ioctls to manage ASI page faults Alexandre Chartre
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Alexandre Chartre @ 2020-05-04 15:02 UTC (permalink / raw)
  To: tglx, mingo, bp, hpa, dave.hansen, luto, peterz, x86, linux-mm,
	linux-kernel
  Cc: pbonzini, konrad.wilk, jan.setjeeilers, liran.alon, junaids,
	graf, rppt, kuzuno, mgross, alexandre.chartre

Add a sequence to test if an ASI remains active after it is
scheduled out and then scheduled back in.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 drivers/staging/asi/asidrv.c | 98 ++++++++++++++++++++++++++++++++++++
 drivers/staging/asi/asidrv.h |  2 +
 2 files changed, 100 insertions(+)

diff --git a/drivers/staging/asi/asidrv.c b/drivers/staging/asi/asidrv.c
index e08f2a107e89..9ca17e0b654e 100644
--- a/drivers/staging/asi/asidrv.c
+++ b/drivers/staging/asi/asidrv.c
@@ -4,6 +4,7 @@
  */
 
 #include <linux/fs.h>
+#include <linux/kthread.h>
 #include <linux/miscdevice.h>
 #include <linux/module.h>
 #include <linux/nmi.h>
@@ -21,17 +22,24 @@
 /* Number of read for mem/memmap test sequence */
 #define ASIDRV_MEM_READ_COUNT		1000
 
+/* Number of loop for the sched test sequence */
+#define ASIDRV_SCHED_LOOP_COUNT		20
+
 /* Timeout for target to be ready to receive an interrupt or NMI */
 #define ASIDRV_TIMEOUT_TARGET_READY	1
 
 /* Timeout for receiving an interrupt or NMI */
 #define ASIDRV_TIMEOUT_INTERRUPT	5
 
+/* Timeout before thread can start its job */
+#define ASIDRV_TIMEOUT_THREAD_START	5
+
 /* NMI handler name used for testing */
 #define ASIDRV_NMI_HANDLER_NAME		"ASI Test"
 
 enum asidrv_state {
 	ASIDRV_STATE_NONE,
+	ASIDRV_STATE_START,
 	ASIDRV_STATE_INTR_WAITING,
 	ASIDRV_STATE_INTR_RECEIVED,
 	ASIDRV_STATE_NMI_WAITING,
@@ -50,6 +58,8 @@ struct asidrv_test {
 	bool			work_set;
 	enum asidrv_run_error	run_error;
 	bool			intrnmi;
+	struct task_struct	*kthread; /* work on same cpu */
+	int			count;
 };
 
 struct asidrv_sequence {
@@ -438,6 +448,66 @@ static enum asidrv_run_error asidrv_intrnmi_run(struct asidrv_test *test)
 	return err;
 }
 
+/*
+ * Sched Test Sequence
+ */
+
+static void asidrv_sched_loop(struct asidrv_test *test)
+{
+	int last_count = 0;
+
+	while (test->count < ASIDRV_SCHED_LOOP_COUNT) {
+		test->count++;
+		last_count = test->count;
+		/*
+		 * Call into the scheduler, until it runs the other
+		 * asidrv_sched_loop() task. We know it has run when
+		 * test->count changes.
+		 */
+		while (last_count == test->count)
+			schedule();
+	}
+	test->count++;
+}
+
+static int asidrv_sched_kthread(void *data)
+{
+	struct asidrv_test *test = data;
+	enum asidrv_run_error err;
+
+	err = asidrv_wait(test, ASIDRV_STATE_START,
+			  ASIDRV_TIMEOUT_THREAD_START);
+	if (err) {
+		pr_debug("Error waiting for start state: error %d\n", err);
+		return err;
+	}
+	asidrv_sched_loop(test);
+
+	return 0;
+}
+
+static enum asidrv_run_error asidrv_sched_init(struct asidrv_test *test)
+{
+	test->kthread = kthread_create_on_node(asidrv_sched_kthread, test,
+					       cpu_to_node(test->cpu),
+					       "sched test");
+	if (!test->kthread)
+		return ASIDRV_RUN_ERR_KTHREAD;
+
+	kthread_bind(test->kthread, test->cpu);
+	test->count = 0;
+
+	return ASIDRV_RUN_ERR_NONE;
+}
+
+static enum asidrv_run_error asidrv_sched_run(struct asidrv_test *test)
+{
+	atomic_set(&test->state, ASIDRV_STATE_START);
+	asidrv_sched_loop(test);
+
+	return ASIDRV_RUN_ERR_NONE;
+}
+
 /*
  * Memory Buffer Access Test Sequences
  */
@@ -521,14 +591,28 @@ struct asidrv_sequence asidrv_sequences[] = {
 		"intr+nmi",
 		asidrv_intrnmi_setup, asidrv_intrnmi_run, asidrv_nmi_cleanup,
 	},
+	[ASIDRV_SEQ_SCHED] = {
+		"sched",
+		asidrv_sched_init, asidrv_sched_run, NULL,
+	},
 };
 
 static enum asidrv_run_error asidrv_run_init(struct asidrv_test *test)
 {
+	cpumask_t mask = CPU_MASK_NONE;
 	int err;
 
 	test->run_error = ASIDRV_RUN_ERR_NONE;
 
+	/*
+	 * Binding ourself to the current cpu but keep preemption
+	 * enabled.
+	 */
+	test->cpu = get_cpu();
+	cpumask_set_cpu(test->cpu, &mask);
+	set_cpus_allowed_ptr(current, &mask);
+	put_cpu();
+
 	/*
 	 * Map the current stack, we need it to enter ASI.
 	 */
@@ -589,12 +673,26 @@ static enum asidrv_run_error asidrv_run_setup(struct asidrv_test *test,
 		schedule_work_on(other_cpu, &test->work);
 	}
 
+	if (test->kthread)
+		wake_up_process(test->kthread);
+
 	return ASIDRV_RUN_ERR_NONE;
 }
 
 static void asidrv_run_cleanup(struct asidrv_test *test,
 			       struct asidrv_sequence *sequence)
 {
+	if (test->kthread) {
+		kthread_stop(test->kthread);
+		test->kthread = NULL;
+	}
+
+	if (test->work_set) {
+		/* ensure work has completed */
+		flush_work(&test->work);
+		test->work_set = false;
+	}
+
 	if (sequence->cleanup)
 		sequence->cleanup(test);
 }
diff --git a/drivers/staging/asi/asidrv.h b/drivers/staging/asi/asidrv.h
index de9f7ad993e0..9f540b119883 100644
--- a/drivers/staging/asi/asidrv.h
+++ b/drivers/staging/asi/asidrv.h
@@ -13,6 +13,7 @@ enum asidrv_seqnum {
 	ASIDRV_SEQ_INTERRUPT,	/* interrupt sequence */
 	ASIDRV_SEQ_NMI,		/* NMI interrupt sequence */
 	ASIDRV_SEQ_INTRNMI,	/* NMI in interrupt sequence */
+	ASIDRV_SEQ_SCHED,	/* schedule() sequence */
 };
 
 enum asidrv_run_error {
@@ -30,6 +31,7 @@ enum asidrv_run_error {
 	ASIDRV_RUN_ERR_NMI,	/* no NMI received */
 	ASIDRV_RUN_ERR_NMI_REG,	/* failed to register NMI handler */
 	ASIDRV_RUN_ERR_NMI_ASI_ACTIVE, /* ASI active in NMI handler */
+	ASIDRV_RUN_ERR_KTHREAD,	/* failed to create kernel thread */
 };
 
 #define ASIDRV_IOCTL_RUN_SEQUENCE	_IOWR('a', 1, struct asidrv_run_param)
-- 
2.18.2


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

* [RFC v4][PATCH part-3 09/14] asidrv: Add ioctls to manage ASI page faults
  2020-05-04 15:02 [RFC v4][PATCH part-3 00/14] ASI - Part III (ASI Test Driver and CLI) Alexandre Chartre
                   ` (7 preceding siblings ...)
  2020-05-04 15:02 ` [RFC v4][PATCH part-3 08/14] asidrv: Sequence to test scheduling in/out with ASI Alexandre Chartre
@ 2020-05-04 15:02 ` Alexandre Chartre
  2020-05-04 15:02 ` [RFC v4][PATCH part-3 10/14] asidrv: Add ioctls to manage ASI mapped VA ranges Alexandre Chartre
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Alexandre Chartre @ 2020-05-04 15:02 UTC (permalink / raw)
  To: tglx, mingo, bp, hpa, dave.hansen, luto, peterz, x86, linux-mm,
	linux-kernel
  Cc: pbonzini, konrad.wilk, jan.setjeeilers, liran.alon, junaids,
	graf, rppt, kuzuno, mgross, alexandre.chartre

Add ioctls to list and clear ASI page faults. Also add an ioctl to display
or not stack trace on ASI page fault.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 drivers/staging/asi/asidrv.c | 88 ++++++++++++++++++++++++++++++++++++
 drivers/staging/asi/asidrv.h | 32 +++++++++++++
 2 files changed, 120 insertions(+)

diff --git a/drivers/staging/asi/asidrv.c b/drivers/staging/asi/asidrv.c
index 9ca17e0b654e..e6edfbe5acea 100644
--- a/drivers/staging/asi/asidrv.c
+++ b/drivers/staging/asi/asidrv.c
@@ -4,6 +4,7 @@
  */
 
 #include <linux/fs.h>
+#include <linux/kallsyms.h>
 #include <linux/kthread.h>
 #include <linux/miscdevice.h>
 #include <linux/module.h>
@@ -790,12 +791,99 @@ static int asidrv_ioctl_run_sequence(struct asidrv_test *test,
 	return 0;
 }
 
+/*
+ * ASI fault ioctls
+ */
+
+static int asidrv_ioctl_list_fault(struct asi *asi, unsigned long arg)
+{
+	struct asidrv_fault_list __user *uflist;
+	struct asidrv_fault_list *flist;
+	size_t flist_size;
+	__u32 uflist_len;
+	int i;
+
+	uflist = (struct asidrv_fault_list __user *)arg;
+	if (copy_from_user(&uflist_len, &uflist->length, sizeof(uflist_len)))
+		return -EFAULT;
+
+	uflist_len = min_t(unsigned int, uflist_len, ASI_FAULT_LOG_SIZE);
+
+	flist_size = sizeof(*flist) + sizeof(struct asidrv_fault) * uflist_len;
+	flist = kzalloc(flist_size, GFP_KERNEL);
+	if (!flist)
+		return -ENOMEM;
+
+	for (i = 0; i < ASI_FAULT_LOG_SIZE; i++) {
+		if (!asi->fault_log[i].address)
+			break;
+		if (i < uflist_len) {
+			flist->fault[i].addr = asi->fault_log[i].address;
+			flist->fault[i].count = asi->fault_log[i].count;
+			sprint_symbol(flist->fault[i].symbol,
+				      asi->fault_log[i].address);
+		}
+	}
+	flist->length = i;
+
+	if (copy_to_user(uflist, flist, flist_size)) {
+		kfree(flist);
+		return -EFAULT;
+	}
+
+	if (i >= ASI_FAULT_LOG_SIZE)
+		pr_warn("ASI %p: fault log buffer is full [%d]\n", asi, i);
+
+	kfree(flist);
+
+	return 0;
+}
+
+static int asidrv_ioctl_clear_fault(struct asi *asi)
+{
+	int i;
+
+	for (i = 0; i < ASI_FAULT_LOG_SIZE; i++) {
+		if (!asi->fault_log[i].address)
+			break;
+		asi->fault_log[i].address = 0;
+	}
+
+	pr_debug("ASI %p: faults cleared\n", asi);
+	return 0;
+}
+
+static int asidrv_ioctl_log_fault_stack(struct asi *asi, bool log_stack)
+{
+	if (log_stack) {
+		asi->fault_log_policy |= ASI_FAULT_LOG_STACK;
+		pr_debug("ASI %p: setting fault stack\n", asi);
+	} else {
+		asi->fault_log_policy &= ~ASI_FAULT_LOG_STACK;
+		pr_debug("ASI %p: clearing fault stack\n", asi);
+	}
+
+	return 0;
+}
+
 static long asidrv_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
 	struct asidrv_test *test = asidrv_test;
+	struct asi *asi = test->asi;
 
 	switch (cmd) {
 
+	/* ASI fault ioctls */
+
+	case ASIDRV_IOCTL_LIST_FAULT:
+		return asidrv_ioctl_list_fault(asi, arg);
+
+	case ASIDRV_IOCTL_CLEAR_FAULT:
+		return asidrv_ioctl_clear_fault(asi);
+
+	case ASIDRV_IOCTL_LOG_FAULT_STACK:
+		return asidrv_ioctl_log_fault_stack(asi, arg);
+
 	/* Test ioctls */
 
 	case ASIDRV_IOCTL_RUN_SEQUENCE:
diff --git a/drivers/staging/asi/asidrv.h b/drivers/staging/asi/asidrv.h
index 9f540b119883..99ab9843e36b 100644
--- a/drivers/staging/asi/asidrv.h
+++ b/drivers/staging/asi/asidrv.h
@@ -36,9 +36,41 @@ enum asidrv_run_error {
 
 #define ASIDRV_IOCTL_RUN_SEQUENCE	_IOWR('a', 1, struct asidrv_run_param)
 
+/*
+ * ASIDRV_IOCTL_LIST_FAULT: return the list of ASI faults.
+ *
+ * User should set 'length' with the number of entries available in the
+ * 'fault' array. On return, 'length' is set to the number of ASI faults
+ * (which can be larger than the original 'length' value), and the 'fault'
+ * array is filled with the ASI faults.
+ */
+#define ASIDRV_IOCTL_LIST_FAULT		_IOWR('a', 2, struct asidrv_fault_list)
+#define ASIDRV_IOCTL_CLEAR_FAULT	_IO('a', 3)
+#define ASIDRV_IOCTL_LOG_FAULT_STACK	_IO('a', 4)
+
+#define ASIDRV_KSYM_NAME_LEN	128
+/*
+ * We need KSYM_SYMBOL_LEN to lookup symbol. However it's not part of
+ * userland include. So we use a reasonably large value (KSYM_SYMBOL_LEN
+ * is around 310).
+ */
+#define ASIDRV_KSYM_SYMBOL_LEN	512
+
 struct asidrv_run_param {
 	__u32 sequence;		/* sequence to run */
 	__u32 run_error;	/* result error after run */
 	__u32 asi_active;	/* ASI is active after run? */
 };
+
+struct asidrv_fault {
+	__u64 addr;
+	char  symbol[ASIDRV_KSYM_SYMBOL_LEN];
+	__u32 count;
+};
+
+struct asidrv_fault_list {
+	__u32 length;
+	struct asidrv_fault fault[0];
+};
+
 #endif
-- 
2.18.2


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

* [RFC v4][PATCH part-3 10/14] asidrv: Add ioctls to manage ASI mapped VA ranges
  2020-05-04 15:02 [RFC v4][PATCH part-3 00/14] ASI - Part III (ASI Test Driver and CLI) Alexandre Chartre
                   ` (8 preceding siblings ...)
  2020-05-04 15:02 ` [RFC v4][PATCH part-3 09/14] asidrv: Add ioctls to manage ASI page faults Alexandre Chartre
@ 2020-05-04 15:02 ` Alexandre Chartre
  2020-05-04 15:02 ` [RFC v4][PATCH part-3 11/14] asidrv/asicmd: Introduce the asicmd command Alexandre Chartre
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Alexandre Chartre @ 2020-05-04 15:02 UTC (permalink / raw)
  To: tglx, mingo, bp, hpa, dave.hansen, luto, peterz, x86, linux-mm,
	linux-kernel
  Cc: pbonzini, konrad.wilk, jan.setjeeilers, liran.alon, junaids,
	graf, rppt, kuzuno, mgross, alexandre.chartre

Add ioctls to list, add, clear ASI mapped VA ranges.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 drivers/staging/asi/asidrv.c | 138 +++++++++++++++++++++++++++++++++++
 drivers/staging/asi/asidrv.h |  26 +++++++
 2 files changed, 164 insertions(+)

diff --git a/drivers/staging/asi/asidrv.c b/drivers/staging/asi/asidrv.c
index e6edfbe5acea..9d9784629833 100644
--- a/drivers/staging/asi/asidrv.c
+++ b/drivers/staging/asi/asidrv.c
@@ -866,10 +866,137 @@ static int asidrv_ioctl_log_fault_stack(struct asi *asi, bool log_stack)
 	return 0;
 }
 
+/*
+ * ASI decorated pagetable ioctls
+ */
+
+static int asidrv_ioctl_add_mapping(struct dpt *dpt, unsigned long arg)
+{
+	struct asidrv_mapping_list __user *umlist;
+	struct asidrv_mapping mapping;
+	__u32 umlist_len;
+	int i, err;
+
+	umlist = (struct asidrv_mapping_list *)arg;
+	if (copy_from_user(&umlist_len, &umlist->length, sizeof(umlist_len)))
+		return -EFAULT;
+
+	err = 0;
+	for (i = 0; i < umlist_len; i++) {
+		if (copy_from_user(&mapping, &umlist->mapping[i],
+				   sizeof(mapping))) {
+			err = -EFAULT;
+			break;
+		}
+
+		pr_debug("add mapping %llx/%llx/%u %s\n",
+			 mapping.addr, mapping.size, mapping.level,
+			 mapping.percpu ? "percpu" : "");
+
+		if (mapping.percpu) {
+			if (mapping.level != PGT_LEVEL_PTE) {
+				err = -EINVAL;
+				break;
+			}
+			err = dpt_map_percpu(dpt, (void *)mapping.addr,
+					     mapping.size);
+		} else {
+			err = dpt_map_range(dpt, (void *)mapping.addr,
+					    mapping.size, mapping.level);
+		}
+		if (err)
+			break;
+	}
+
+	if (err)
+		return (i == 0) ? err : i;
+
+	return 0;
+}
+
+static int asidrv_ioctl_clear_mapping(struct dpt *dpt, unsigned long arg)
+{
+	struct asidrv_mapping_list __user *umlist;
+	struct asidrv_mapping mapping;
+	__u32 umlist_len;
+	int err, i;
+
+	umlist = (struct asidrv_mapping_list *)arg;
+	if (copy_from_user(&umlist_len, &umlist->length, sizeof(umlist_len)))
+		return -EFAULT;
+
+	err = 0;
+	for (i = 0; i < umlist_len; i++) {
+		if (copy_from_user(&mapping, &umlist->mapping[i],
+				   sizeof(mapping))) {
+			err = -EFAULT;
+			break;
+		}
+
+		pr_debug("clear mapping %llx %s\n",
+			 mapping.addr, mapping.percpu ? "percpu" : "");
+
+		if (mapping.percpu)
+			dpt_unmap_percpu(dpt, (void *)mapping.addr);
+		else
+			dpt_unmap(dpt, (void *)mapping.addr);
+	}
+
+	if (err)
+		return (i == 0) ? err : i;
+
+	return 0;
+}
+
+static int asidrv_ioctl_list_mapping(struct dpt *dpt, unsigned long arg)
+{
+	struct asidrv_mapping_list __user *umlist;
+	struct asidrv_mapping_list *mlist;
+	struct dpt_range_mapping *range;
+	unsigned long addr;
+	size_t mlist_size;
+	__u32 umlist_len;
+	int i;
+
+	umlist = (struct asidrv_mapping_list *)arg;
+	if (copy_from_user(&umlist_len, &umlist->length, sizeof(umlist_len)))
+		return -EFAULT;
+
+	umlist_len = min_t(unsigned int, umlist_len, 512);
+
+	mlist_size = sizeof(*mlist) +
+		sizeof(struct asidrv_mapping) * umlist_len;
+	mlist = kzalloc(mlist_size, GFP_KERNEL);
+	if (!mlist)
+		return -ENOMEM;
+
+	i = 0;
+	list_for_each_entry(range, &dpt->mapping_list, list) {
+		if (i < umlist_len) {
+			addr = (__u64)range->ptr;
+			mlist->mapping[i].addr = addr;
+			mlist->mapping[i].size = range->size;
+			mlist->mapping[i].level = range->level;
+		}
+		i++;
+	}
+	mlist->length = i;
+
+	if (copy_to_user(umlist, mlist, mlist_size)) {
+		kfree(mlist);
+		return -EFAULT;
+	}
+
+	kfree(mlist);
+
+	return 0;
+}
+
 static long asidrv_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
 	struct asidrv_test *test = asidrv_test;
 	struct asi *asi = test->asi;
+	struct dpt *dpt = test->dpt;
 
 	switch (cmd) {
 
@@ -884,6 +1011,17 @@ static long asidrv_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 	case ASIDRV_IOCTL_LOG_FAULT_STACK:
 		return asidrv_ioctl_log_fault_stack(asi, arg);
 
+	/* ASI decorated pagetable ioctls */
+
+	case ASIDRV_IOCTL_LIST_MAPPING:
+		return asidrv_ioctl_list_mapping(dpt, arg);
+
+	case ASIDRV_IOCTL_ADD_MAPPING:
+		return asidrv_ioctl_add_mapping(dpt, arg);
+
+	case ASIDRV_IOCTL_CLEAR_MAPPING:
+		return asidrv_ioctl_clear_mapping(dpt, arg);
+
 	/* Test ioctls */
 
 	case ASIDRV_IOCTL_RUN_SEQUENCE:
diff --git a/drivers/staging/asi/asidrv.h b/drivers/staging/asi/asidrv.h
index 99ab9843e36b..f042106419db 100644
--- a/drivers/staging/asi/asidrv.h
+++ b/drivers/staging/asi/asidrv.h
@@ -48,6 +48,20 @@ enum asidrv_run_error {
 #define ASIDRV_IOCTL_CLEAR_FAULT	_IO('a', 3)
 #define ASIDRV_IOCTL_LOG_FAULT_STACK	_IO('a', 4)
 
+/*
+ * ASIDRV_IOCTL_ADD_MAPPING: add mapping to the ASI.
+ *
+ * User should set 'length' with the number of mapping described in the
+ * 'mapping' array.
+ * Return value:
+ *   -1   - error no mapping was added
+ *    0   - no error, all mappings were added
+ *   N>0  - error but the first N mappings were added
+ */
+#define ASIDRV_IOCTL_ADD_MAPPING	_IOWR('a', 5, struct asidrv_mapping_list)
+#define ASIDRV_IOCTL_CLEAR_MAPPING	_IOW('a', 6, struct asidrv_mapping_list)
+#define ASIDRV_IOCTL_LIST_MAPPING	_IOWR('a', 7, struct asidrv_mapping_list)
+
 #define ASIDRV_KSYM_NAME_LEN	128
 /*
  * We need KSYM_SYMBOL_LEN to lookup symbol. However it's not part of
@@ -73,4 +87,16 @@ struct asidrv_fault_list {
 	struct asidrv_fault fault[0];
 };
 
+struct asidrv_mapping {
+	__u64 addr;
+	__u64 size;
+	__u32 level;
+	__u32 percpu;
+};
+
+struct asidrv_mapping_list {
+	__u32 length;
+	struct asidrv_mapping mapping[0];
+};
+
 #endif
-- 
2.18.2


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

* [RFC v4][PATCH part-3 11/14] asidrv/asicmd: Introduce the asicmd command
  2020-05-04 15:02 [RFC v4][PATCH part-3 00/14] ASI - Part III (ASI Test Driver and CLI) Alexandre Chartre
                   ` (9 preceding siblings ...)
  2020-05-04 15:02 ` [RFC v4][PATCH part-3 10/14] asidrv: Add ioctls to manage ASI mapped VA ranges Alexandre Chartre
@ 2020-05-04 15:02 ` Alexandre Chartre
  2020-05-04 15:02 ` [RFC v4][PATCH part-3 12/14] asidrv/asicmd: Add more test sequences for testing ASI Alexandre Chartre
                   ` (2 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Alexandre Chartre @ 2020-05-04 15:02 UTC (permalink / raw)
  To: tglx, mingo, bp, hpa, dave.hansen, luto, peterz, x86, linux-mm,
	linux-kernel
  Cc: pbonzini, konrad.wilk, jan.setjeeilers, liran.alon, junaids,
	graf, rppt, kuzuno, mgross, alexandre.chartre

The asicmd command is a userland CLI to interact with the ASI driver
(asidrv), in particular it provides an interface for running ASI
test sequences.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 drivers/staging/asi/Makefile |   6 ++
 drivers/staging/asi/asicmd.c | 120 +++++++++++++++++++++++++++++++++++
 2 files changed, 126 insertions(+)
 create mode 100644 drivers/staging/asi/asicmd.c

diff --git a/drivers/staging/asi/Makefile b/drivers/staging/asi/Makefile
index a48487e48d7c..847d4732c229 100644
--- a/drivers/staging/asi/Makefile
+++ b/drivers/staging/asi/Makefile
@@ -5,3 +5,9 @@
 #
 obj-m += asi.o
 asi-y := asidrv.o
+
+#
+# asicmd command
+#
+hostprogs-y := asicmd
+always := $(hostprogs-y)
diff --git a/drivers/staging/asi/asicmd.c b/drivers/staging/asi/asicmd.c
new file mode 100644
index 000000000000..4d6a347a6d29
--- /dev/null
+++ b/drivers/staging/asi/asicmd.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020, Oracle and/or its affiliates.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "asidrv.h"
+
+struct asidrv_test {
+	char		*name;		/* test name */
+	enum asidrv_seqnum seqnum;	/* sequence */
+	bool		asi_active;	/* ASI active at the end of test? */
+	char		*desc;		/* test description */
+};
+
+struct asidrv_test test_list[] = {
+	{ "nop", ASIDRV_SEQ_NOP, true,
+	  "enter/exit ASI and nothing else" },
+};
+
+#define	TEST_LIST_SIZE	(sizeof(test_list) / sizeof(test_list[0]))
+
+static void usage(void)
+{
+	int i;
+
+	printf("Usage: asicmd (<cmd>|<test>...)\n");
+	printf("\n");
+	printf("Commands:\n");
+	printf("  all      - run all tests\n");
+	printf("\n");
+	printf("Tests:\n");
+	for (i = 0; i < TEST_LIST_SIZE; i++)
+		printf("  %-10s - %s\n", test_list[i].name, test_list[i].desc);
+}
+
+static void asidrv_run_test(int fd, struct asidrv_test *test)
+{
+	struct asidrv_run_param rparam;
+	int err;
+
+	printf("Test %s (sequence %d)\n", test->name, test->seqnum);
+
+	rparam.sequence = test->seqnum;
+
+	err = ioctl(fd, ASIDRV_IOCTL_RUN_SEQUENCE, &rparam);
+
+	printf("  - rv = %d ; ", err);
+	if (err < 0) {
+		printf("error %d\n", errno);
+	} else {
+		printf("result = %d ; ", rparam.run_error);
+		printf("%s\n",
+		       rparam.asi_active ? "asi active" : "asi inactive");
+	}
+
+	printf("  - expect = %s\n",
+	       test->asi_active ? "asi active" : "asi inactive");
+
+	if (err < 0)
+		printf("ERROR - error %d\n", errno);
+	else if (rparam.run_error != ASIDRV_RUN_ERR_NONE)
+		printf("TEST ERROR - error %d\n", rparam.run_error);
+	else if (test->asi_active != rparam.asi_active)
+		printf("TEST FAILED - unexpected ASI state\n");
+	else
+		printf("TEST OK\n");
+}
+
+int main(int argc, char *argv[])
+{
+	bool run_all, run;
+	int i, j, fd;
+	char *test;
+
+	if (argc <= 1) {
+		usage();
+		return 2;
+	}
+
+	fd = open("/dev/asi", O_RDONLY);
+	if (fd == -1) {
+		perror("open /dev/asi");
+		return 1;
+	}
+
+	for (i = 1; i < argc; i++) {
+		test = argv[i];
+
+		if (!strcmp(test, "all"))
+			run_all = true;
+		else
+			run_all = false;
+
+		run = false;
+		for (j = 0; j < TEST_LIST_SIZE; j++) {
+			if (run_all || !strcmp(test, test_list[j].name)) {
+				asidrv_run_test(fd, &test_list[j]);
+				run = true;
+			}
+		}
+
+		if (!run)
+			printf("Unknown test '%s'\n", test);
+	}
+
+	close(fd);
+
+	return 0;
+}
-- 
2.18.2


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

* [RFC v4][PATCH part-3 12/14] asidrv/asicmd: Add more test sequences for testing ASI
  2020-05-04 15:02 [RFC v4][PATCH part-3 00/14] ASI - Part III (ASI Test Driver and CLI) Alexandre Chartre
                   ` (10 preceding siblings ...)
  2020-05-04 15:02 ` [RFC v4][PATCH part-3 11/14] asidrv/asicmd: Introduce the asicmd command Alexandre Chartre
@ 2020-05-04 15:02 ` Alexandre Chartre
  2020-05-04 15:02 ` [RFC v4][PATCH part-3 13/14] asidrv/asicmd: Add options to manage ASI page faults Alexandre Chartre
  2020-05-04 15:02 ` [RFC v4][PATCH part-3 14/14] asidrv/asicmd: Add options to manage ASI mapped VA ranges Alexandre Chartre
  13 siblings, 0 replies; 15+ messages in thread
From: Alexandre Chartre @ 2020-05-04 15:02 UTC (permalink / raw)
  To: tglx, mingo, bp, hpa, dave.hansen, luto, peterz, x86, linux-mm,
	linux-kernel
  Cc: pbonzini, konrad.wilk, jan.setjeeilers, liran.alon, junaids,
	graf, rppt, kuzuno, mgross, alexandre.chartre

Add more options to the asicmd command to test access to map
or unmapped memory buffer, interrupt, NMI, scheduling while
using ASI.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 drivers/staging/asi/asicmd.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/drivers/staging/asi/asicmd.c b/drivers/staging/asi/asicmd.c
index 4d6a347a6d29..849fa09423e6 100644
--- a/drivers/staging/asi/asicmd.c
+++ b/drivers/staging/asi/asicmd.c
@@ -26,6 +26,20 @@ struct asidrv_test {
 struct asidrv_test test_list[] = {
 	{ "nop", ASIDRV_SEQ_NOP, true,
 	  "enter/exit ASI and nothing else" },
+	{ "mem", ASIDRV_SEQ_MEM, false,
+	  "enter ASI and accessed an unmapped buffer" },
+	{ "memmap", ASIDRV_SEQ_MEMMAP, true,
+	  "enter ASI and accessed a mapped buffer" },
+	{ "intr", ASIDRV_SEQ_INTERRUPT, true,
+	  "receive an interruption while running with ASI" },
+	{ "nmi", ASIDRV_SEQ_NMI, true,
+	  "receive a NMI while running with ASI" },
+	{ "intrnmi", ASIDRV_SEQ_INTRNMI, true,
+	  "receive a NMI in an interrupt received while running with ASI" },
+	{ "sched", ASIDRV_SEQ_SCHED, true,
+	  "call schedule() while running with ASI" },
+	{ "printk", ASIDRV_SEQ_PRINTK, true,
+	  "call printk() while running with ASI" },
 };
 
 #define	TEST_LIST_SIZE	(sizeof(test_list) / sizeof(test_list[0]))
-- 
2.18.2


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

* [RFC v4][PATCH part-3 13/14] asidrv/asicmd: Add options to manage ASI page faults
  2020-05-04 15:02 [RFC v4][PATCH part-3 00/14] ASI - Part III (ASI Test Driver and CLI) Alexandre Chartre
                   ` (11 preceding siblings ...)
  2020-05-04 15:02 ` [RFC v4][PATCH part-3 12/14] asidrv/asicmd: Add more test sequences for testing ASI Alexandre Chartre
@ 2020-05-04 15:02 ` Alexandre Chartre
  2020-05-04 15:02 ` [RFC v4][PATCH part-3 14/14] asidrv/asicmd: Add options to manage ASI mapped VA ranges Alexandre Chartre
  13 siblings, 0 replies; 15+ messages in thread
From: Alexandre Chartre @ 2020-05-04 15:02 UTC (permalink / raw)
  To: tglx, mingo, bp, hpa, dave.hansen, luto, peterz, x86, linux-mm,
	linux-kernel
  Cc: pbonzini, konrad.wilk, jan.setjeeilers, liran.alon, junaids,
	graf, rppt, kuzuno, mgross, alexandre.chartre

Add options to the asicmd CLI to list and clear ASI page faults. Also
add an option to enable/disable displaying stack trace on ASI page
fault.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 drivers/staging/asi/asicmd.c | 68 ++++++++++++++++++++++++++++++++++--
 1 file changed, 65 insertions(+), 3 deletions(-)

diff --git a/drivers/staging/asi/asicmd.c b/drivers/staging/asi/asicmd.c
index 849fa09423e6..4d7bb9df0fcc 100644
--- a/drivers/staging/asi/asicmd.c
+++ b/drivers/staging/asi/asicmd.c
@@ -52,6 +52,10 @@ static void usage(void)
 	printf("\n");
 	printf("Commands:\n");
 	printf("  all      - run all tests\n");
+	printf("  fault    - list ASI faults\n");
+	printf("  fltclr   - clear ASI faults\n");
+	printf("  stkon    - show stack on ASI fault\n");
+	printf("  stkoff   - do not show stack on ASI fault\n");
 	printf("\n");
 	printf("Tests:\n");
 	for (i = 0; i < TEST_LIST_SIZE; i++)
@@ -91,10 +95,45 @@ static void asidrv_run_test(int fd, struct asidrv_test *test)
 		printf("TEST OK\n");
 }
 
+static int asidrv_fault_list(int fd)
+{
+	struct asidrv_fault_list *flist;
+	int i, rv;
+
+	flist = malloc(sizeof(*flist) +
+		       sizeof(struct asidrv_fault) * 10);
+	if (!flist) {
+		perror("malloc flist");
+		return -1;
+	}
+
+	flist->length = 10;
+	rv = ioctl(fd, ASIDRV_IOCTL_LIST_FAULT, flist);
+	if (rv < 0) {
+		perror("ioctl list fault");
+		return -1;
+	}
+
+	if (!flist->length) {
+		printf("ASI has no fault\n");
+		return 0;
+	}
+
+	printf("%-18s  %5s  %s\n", "ADDRESS", "COUNT", "SYMBOL");
+	for (i = 0; i < flist->length && i < 10; i++) {
+		printf("%#18llx  %5u  %s\n",
+		       flist->fault[i].addr,
+		       flist->fault[i].count,
+		       flist->fault[i].symbol);
+	}
+
+	return 0;
+}
+
 int main(int argc, char *argv[])
 {
 	bool run_all, run;
-	int i, j, fd;
+	int i, j, fd, err;
 	char *test;
 
 	if (argc <= 1) {
@@ -111,10 +150,33 @@ int main(int argc, char *argv[])
 	for (i = 1; i < argc; i++) {
 		test = argv[i];
 
-		if (!strcmp(test, "all"))
+		if (!strcmp(test, "fault")) {
+			asidrv_fault_list(fd);
+			continue;
+
+		} else if (!strcmp(test, "fltclr")) {
+			err = ioctl(fd, ASIDRV_IOCTL_CLEAR_FAULT);
+			if (err)
+				perror("ioctl clear fault");
+			continue;
+
+		} else if (!strcmp(test, "stkon")) {
+			err = ioctl(fd, ASIDRV_IOCTL_LOG_FAULT_STACK, true);
+			if (err)
+				perror("ioctl log fault stack");
+			continue;
+
+		} else if (!strcmp(test, "stkoff")) {
+			err = ioctl(fd, ASIDRV_IOCTL_LOG_FAULT_STACK, false);
+			if (err)
+				perror("ioctl log fault sstack");
+			continue;
+
+		} else if (!strcmp(test, "all")) {
 			run_all = true;
-		else
+		} else {
 			run_all = false;
+		}
 
 		run = false;
 		for (j = 0; j < TEST_LIST_SIZE; j++) {
-- 
2.18.2


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

* [RFC v4][PATCH part-3 14/14] asidrv/asicmd: Add options to manage ASI mapped VA ranges
  2020-05-04 15:02 [RFC v4][PATCH part-3 00/14] ASI - Part III (ASI Test Driver and CLI) Alexandre Chartre
                   ` (12 preceding siblings ...)
  2020-05-04 15:02 ` [RFC v4][PATCH part-3 13/14] asidrv/asicmd: Add options to manage ASI page faults Alexandre Chartre
@ 2020-05-04 15:02 ` Alexandre Chartre
  13 siblings, 0 replies; 15+ messages in thread
From: Alexandre Chartre @ 2020-05-04 15:02 UTC (permalink / raw)
  To: tglx, mingo, bp, hpa, dave.hansen, luto, peterz, x86, linux-mm,
	linux-kernel
  Cc: pbonzini, konrad.wilk, jan.setjeeilers, liran.alon, junaids,
	graf, rppt, kuzuno, mgross, alexandre.chartre

Add options to the asicmd CLI to list, add and clear ASI mapped
VA ranges.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 drivers/staging/asi/asicmd.c | 243 +++++++++++++++++++++++++++++++++++
 1 file changed, 243 insertions(+)

diff --git a/drivers/staging/asi/asicmd.c b/drivers/staging/asi/asicmd.c
index 4d7bb9df0fcc..974a3147a313 100644
--- a/drivers/staging/asi/asicmd.c
+++ b/drivers/staging/asi/asicmd.c
@@ -16,6 +16,10 @@
 
 #include "asidrv.h"
 
+static const char * const page_table_level[] = {
+	"PTE", "PMD", "PUD", "P4D", "PGD"
+};
+
 struct asidrv_test {
 	char		*name;		/* test name */
 	enum asidrv_seqnum seqnum;	/* sequence */
@@ -56,12 +60,25 @@ static void usage(void)
 	printf("  fltclr   - clear ASI faults\n");
 	printf("  stkon    - show stack on ASI fault\n");
 	printf("  stkoff   - do not show stack on ASI fault\n");
+	printf("  map      - list ASI mappings\n");
+	printf("  mapadd   - add ASI mappings\n");
+	printf("  mapclr   - clear ASI mapping\n");
 	printf("\n");
 	printf("Tests:\n");
 	for (i = 0; i < TEST_LIST_SIZE; i++)
 		printf("  %-10s - %s\n", test_list[i].name, test_list[i].desc);
 }
 
+static void usage_mapadd(void)
+{
+	printf("usage: asicmd mapadd [percpu:]<addr>:<size>[:<level>]\n");
+}
+
+static void usage_mapclr(void)
+{
+	printf("usage: asicmd mapclr [percpu:]<addr>\n");
+}
+
 static void asidrv_run_test(int fd, struct asidrv_test *test)
 {
 	struct asidrv_run_param rparam;
@@ -130,6 +147,210 @@ static int asidrv_fault_list(int fd)
 	return 0;
 }
 
+static int asidrv_map_list(int fd)
+{
+	struct asidrv_mapping_list *mlist;
+	int level;
+	int i, rv, len = 64;
+
+	mlist = malloc(sizeof(*mlist) +
+		       sizeof(struct asidrv_mapping) * len);
+	if (!mlist) {
+		perror("malloc mlist");
+		return -1;
+	}
+
+	mlist->length = len;
+	rv = ioctl(fd, ASIDRV_IOCTL_LIST_MAPPING, mlist);
+	if (rv < 0) {
+		perror("ioctl list mapping");
+		return -1;
+	}
+
+	if (!mlist->length) {
+		printf("ASI has no mapping\n");
+		return 0;
+	}
+
+	printf("%-18s  %18s  %s\n", "ADDRESS", "SIZE", "LEVEL");
+	for (i = 0; i < mlist->length && i < len; i++) {
+		printf("%#18llx  %#18llx  ",
+		       mlist->mapping[i].addr,
+		       mlist->mapping[i].size);
+		level = mlist->mapping[i].level;
+		if (level < 5)
+			printf("%5s\n", page_table_level[level]);
+		else
+			printf("%5d\n", level);
+	}
+	printf("Mapping List: %d/%d\n", i, mlist->length);
+
+	return 0;
+}
+
+static char *asidrv_skip_percpu(char *str, bool *percpup)
+{
+	int len = sizeof("percpu:") - 1;
+
+	if (!strncmp(str, "percpu:", len)) {
+		str += len;
+		*percpup = true;
+	} else {
+		*percpup = false;
+	}
+
+	return str;
+}
+
+static int asidrv_parse_mapping_clear(char *arg, struct asidrv_mapping *mapping)
+{
+	char  *s, *end;
+	bool percpu;
+	__u64 addr;
+
+	s = asidrv_skip_percpu(arg, &percpu);
+
+	addr = strtoull(s, &end, 0);
+	if (*end != 0) {
+		printf("invalid mapping address '%s'\n", s);
+		return -1;
+	}
+
+	printf("mapclr %llx%s\n", addr, percpu ? " percpu" : "");
+
+	mapping->addr = addr;
+	mapping->size = 0;
+	mapping->level = 0;
+	mapping->percpu = percpu;
+
+	return 0;
+}
+
+static int asidrv_parse_mapping_add(char *arg, struct asidrv_mapping *mapping)
+{
+	char *s, *end;
+	__u64 addr, size;
+	__u32 level;
+	bool percpu;
+	int i;
+
+	s = asidrv_skip_percpu(arg, &percpu);
+
+	s = strtok(s, ":");
+	if (!s) {
+		printf("mapadd: <addr> not found\n");
+		return -1;
+	}
+
+	addr = strtoull(s, &end, 0);
+	if (*end != 0) {
+		printf("invalid mapping address '%s'\n", s);
+		return -1;
+	}
+
+	s = strtok(NULL, ":");
+	if (!s) {
+		printf("mapadd: <size> not found\n");
+		return -1;
+	}
+	size = strtoull(s, &end, 0);
+	if (*end != 0) {
+		printf("mapadd: invalid size %s\n", s);
+		return -1;
+	}
+
+	s = strtok(NULL, ":");
+	if (!s) {
+		level = 0;
+	} else {
+		/* lookup page table level name */
+		level = -1;
+		for (i = 0; i < 5; i++) {
+			if (!strcasecmp(s, page_table_level[i])) {
+				level = i;
+				break;
+			}
+		}
+		if (level == -1) {
+			level = strtoul(s, &end, 0);
+			if (*end != 0 || level >= 5) {
+				printf("mapadd: invalid level %s\n", s);
+				return -1;
+			}
+		}
+	}
+
+	printf("mapadd %llx/%llx/%u%s\n", addr, size, level,
+	       percpu ? " percpu" : "");
+
+	mapping->addr = addr;
+	mapping->size = size;
+	mapping->level = level;
+	mapping->percpu = percpu;
+
+	return 0;
+}
+
+static int asidrv_map_change(int fd, unsigned long cmd, char *arg)
+{
+	struct asidrv_mapping_list *mlist;
+	int i, count, err;
+	char *s;
+
+	count = 0;
+	for (s = arg; s; s = strchr(s + 1, ','))
+		count++;
+
+	mlist = malloc(sizeof(mlist) + sizeof(struct asidrv_mapping) * count);
+	if (!mlist) {
+		perror("malloc mapping list");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < count; i++) {
+		s = strchr(arg, ',');
+		if (s)
+			s[0] = '\0';
+
+		if (cmd == ASIDRV_IOCTL_ADD_MAPPING) {
+			err = asidrv_parse_mapping_add(arg,
+						       &mlist->mapping[i]);
+		} else {
+			err = asidrv_parse_mapping_clear(arg,
+							 &mlist->mapping[i]);
+		}
+		if (err)
+			goto done;
+		arg = s + 1;
+	}
+
+	mlist->length = count;
+	err = ioctl(fd, cmd, mlist);
+	if (err < 0) {
+		perror("ioctl mapping");
+		err = errno;
+	} else if (err > 0) {
+		/* partial error */
+		printf("ioctl mapping: partial failure (%d/%d)\n",
+		       err, count);
+		for (i = 0; i < count; i++) {
+			printf("  %#llx: ", mlist->mapping[i].addr);
+			if (i < err)
+				printf("done\n");
+			else if (i == err)
+				printf("failed\n");
+			else
+				printf("not done\n");
+		}
+		err = -1;
+	}
+
+done:
+	free(mlist);
+
+	return err;
+}
+
 int main(int argc, char *argv[])
 {
 	bool run_all, run;
@@ -172,6 +393,28 @@ int main(int argc, char *argv[])
 				perror("ioctl log fault sstack");
 			continue;
 
+		} else if (!strcmp(test, "map")) {
+			asidrv_map_list(fd);
+			continue;
+
+		} else if (!strcmp(test, "mapadd")) {
+			if (++i >= argc) {
+				usage_mapadd();
+				return 2;
+			}
+			asidrv_map_change(fd, ASIDRV_IOCTL_ADD_MAPPING,
+					  argv[i]);
+			continue;
+
+		} else if (!strcmp(test, "mapclr")) {
+			if (++i >= argc) {
+				usage_mapclr();
+				return 2;
+			}
+			asidrv_map_change(fd, ASIDRV_IOCTL_CLEAR_MAPPING,
+					  argv[i]);
+			continue;
+
 		} else if (!strcmp(test, "all")) {
 			run_all = true;
 		} else {
-- 
2.18.2


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

end of thread, other threads:[~2020-05-04 15:08 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-05-04 15:02 [RFC v4][PATCH part-3 00/14] ASI - Part III (ASI Test Driver and CLI) Alexandre Chartre
2020-05-04 15:02 ` [RFC v4][PATCH part-3 01/14] mm/asi: Define the test ASI type Alexandre Chartre
2020-05-04 15:02 ` [RFC v4][PATCH part-3 02/14] asidrv: Introduce the ASI driver Alexandre Chartre
2020-05-04 15:02 ` [RFC v4][PATCH part-3 03/14] asidrv: Introduce the ASIDRV_IOCTL_RUN_SEQUENCE ioctl Alexandre Chartre
2020-05-04 15:02 ` [RFC v4][PATCH part-3 04/14] asidrv: Sequence to test ASI access to mapped/unmapped memory Alexandre Chartre
2020-05-04 15:02 ` [RFC v4][PATCH part-3 05/14] asidrv: Sequence to test interrupt on ASI Alexandre Chartre
2020-05-04 15:02 ` [RFC v4][PATCH part-3 06/14] asidrv: Sequence to test NMI " Alexandre Chartre
2020-05-04 15:02 ` [RFC v4][PATCH part-3 07/14] asidrv: Sequence to test interrupt+NMI " Alexandre Chartre
2020-05-04 15:02 ` [RFC v4][PATCH part-3 08/14] asidrv: Sequence to test scheduling in/out with ASI Alexandre Chartre
2020-05-04 15:02 ` [RFC v4][PATCH part-3 09/14] asidrv: Add ioctls to manage ASI page faults Alexandre Chartre
2020-05-04 15:02 ` [RFC v4][PATCH part-3 10/14] asidrv: Add ioctls to manage ASI mapped VA ranges Alexandre Chartre
2020-05-04 15:02 ` [RFC v4][PATCH part-3 11/14] asidrv/asicmd: Introduce the asicmd command Alexandre Chartre
2020-05-04 15:02 ` [RFC v4][PATCH part-3 12/14] asidrv/asicmd: Add more test sequences for testing ASI Alexandre Chartre
2020-05-04 15:02 ` [RFC v4][PATCH part-3 13/14] asidrv/asicmd: Add options to manage ASI page faults Alexandre Chartre
2020-05-04 15:02 ` [RFC v4][PATCH part-3 14/14] asidrv/asicmd: Add options to manage ASI mapped VA ranges Alexandre Chartre

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