mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
171 lines
3.6 KiB
171 lines
3.6 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/* |
|
* CIO inject interface |
|
* |
|
* Copyright IBM Corp. 2021 |
|
* Author(s): Vineeth Vijayan <[email protected]> |
|
*/ |
|
|
|
#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 = vmemdup_user(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);
|
|
|