lkml.org 
[lkml]   [2020]   [May]   [4]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[RFC v4][PATCH part-3 06/14] asidrv: Sequence to test NMI on ASI
    Date
    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
    \
     
     \ /
      Last update: 2020-05-04 17:05    [W:3.599 / U:1.844 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site