172 lines
3.7 KiB
C
172 lines
3.7 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* CIO inject interface
|
|
*
|
|
* Copyright IBM Corp. 2021
|
|
* Author(s): Vineeth Vijayan <vneethv@linux.ibm.com>
|
|
*/
|
|
|
|
#define KMSG_COMPONENT "cio"
|
|
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
|
|
|
|
#include <linux/slab.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/debugfs.h>
|
|
#include <asm/chpid.h>
|
|
|
|
#include "cio_inject.h"
|
|
#include "cio_debug.h"
|
|
|
|
static DEFINE_SPINLOCK(crw_inject_lock);
|
|
DEFINE_STATIC_KEY_FALSE(cio_inject_enabled);
|
|
static struct crw *crw_inject_data;
|
|
|
|
/**
|
|
* crw_inject : Initiate the artificial CRW inject
|
|
* @crw: The data which needs to be injected as new CRW.
|
|
*
|
|
* The CRW handler is called, which will use the provided artificial
|
|
* data instead of the CRW from the underlying hardware.
|
|
*
|
|
* Return: 0 on success
|
|
*/
|
|
static int crw_inject(struct crw *crw)
|
|
{
|
|
int rc = 0;
|
|
struct crw *copy;
|
|
unsigned long flags;
|
|
|
|
copy = kmemdup(crw, sizeof(*crw), GFP_KERNEL);
|
|
if (!copy)
|
|
return -ENOMEM;
|
|
|
|
spin_lock_irqsave(&crw_inject_lock, flags);
|
|
if (crw_inject_data) {
|
|
kfree(copy);
|
|
rc = -EBUSY;
|
|
} else {
|
|
crw_inject_data = copy;
|
|
}
|
|
spin_unlock_irqrestore(&crw_inject_lock, flags);
|
|
|
|
if (!rc)
|
|
crw_handle_channel_report();
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* stcrw_get_injected: Copy the artificial CRW data to CRW struct.
|
|
* @crw: The target CRW pointer.
|
|
*
|
|
* Retrieve an injected CRW data. Return 0 on success, 1 if no
|
|
* injected-CRW is available. The function reproduces the return
|
|
* code of the actual STCRW function.
|
|
*/
|
|
int stcrw_get_injected(struct crw *crw)
|
|
{
|
|
int rc = 1;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&crw_inject_lock, flags);
|
|
if (crw_inject_data) {
|
|
memcpy(crw, crw_inject_data, sizeof(*crw));
|
|
kfree(crw_inject_data);
|
|
crw_inject_data = NULL;
|
|
rc = 0;
|
|
}
|
|
spin_unlock_irqrestore(&crw_inject_lock, flags);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/* The debugfs write handler for crw_inject nodes operation */
|
|
static ssize_t crw_inject_write(struct file *file, const char __user *buf,
|
|
size_t lbuf, loff_t *ppos)
|
|
{
|
|
u32 slct, oflw, chn, rsc, anc, erc, rsid;
|
|
struct crw crw;
|
|
char *buffer;
|
|
int rc;
|
|
|
|
if (!static_branch_likely(&cio_inject_enabled)) {
|
|
pr_warn("CIO inject is not enabled - ignoring CRW inject\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
buffer = memdup_user_nul(buf, lbuf);
|
|
if (IS_ERR(buffer))
|
|
return -ENOMEM;
|
|
|
|
rc = sscanf(buffer, "%x %x %x %x %x %x %x", &slct, &oflw, &chn, &rsc, &anc,
|
|
&erc, &rsid);
|
|
|
|
kvfree(buffer);
|
|
if (rc != 7) {
|
|
pr_warn("crw_inject: Invalid format (need <solicited> <overflow> <chaining> <rsc> <ancillary> <erc> <rsid>)\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
memset(&crw, 0, sizeof(crw));
|
|
crw.slct = slct;
|
|
crw.oflw = oflw;
|
|
crw.chn = chn;
|
|
crw.rsc = rsc;
|
|
crw.anc = anc;
|
|
crw.erc = erc;
|
|
crw.rsid = rsid;
|
|
|
|
rc = crw_inject(&crw);
|
|
if (rc)
|
|
return rc;
|
|
|
|
return lbuf;
|
|
}
|
|
|
|
/* Debugfs write handler for inject_enable node*/
|
|
static ssize_t enable_inject_write(struct file *file, const char __user *buf,
|
|
size_t lbuf, loff_t *ppos)
|
|
{
|
|
unsigned long en = 0;
|
|
int rc;
|
|
|
|
rc = kstrtoul_from_user(buf, lbuf, 10, &en);
|
|
if (rc)
|
|
return rc;
|
|
|
|
switch (en) {
|
|
case 0:
|
|
static_branch_disable(&cio_inject_enabled);
|
|
break;
|
|
case 1:
|
|
static_branch_enable(&cio_inject_enabled);
|
|
break;
|
|
}
|
|
|
|
return lbuf;
|
|
}
|
|
|
|
static const struct file_operations crw_fops = {
|
|
.owner = THIS_MODULE,
|
|
.write = crw_inject_write,
|
|
};
|
|
|
|
static const struct file_operations cio_en_fops = {
|
|
.owner = THIS_MODULE,
|
|
.write = enable_inject_write,
|
|
};
|
|
|
|
static int __init cio_inject_init(void)
|
|
{
|
|
/* enable_inject node enables the static branching */
|
|
debugfs_create_file("enable_inject", 0200, cio_debugfs_dir,
|
|
NULL, &cio_en_fops);
|
|
|
|
debugfs_create_file("crw_inject", 0200, cio_debugfs_dir,
|
|
NULL, &crw_fops);
|
|
return 0;
|
|
}
|
|
|
|
device_initcall(cio_inject_init);
|