aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/cio/cio_inject.c
blob: a2e771ebae8ebd55e1cb841f76b44a69ebb2bea7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// 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);