Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/s390/kernel/diag/diag324.c
26489 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* Request power readings for resources in a computing environment via
4
* diag 0x324. diag 0x324 stores the power readings in the power information
5
* block (pib).
6
*
7
* Copyright IBM Corp. 2024
8
*/
9
10
#define pr_fmt(fmt) "diag324: " fmt
11
#include <linux/fs.h>
12
#include <linux/gfp.h>
13
#include <linux/ioctl.h>
14
#include <linux/jiffies.h>
15
#include <linux/kernel.h>
16
#include <linux/ktime.h>
17
#include <linux/string.h>
18
#include <linux/slab.h>
19
#include <linux/timer.h>
20
#include <linux/types.h>
21
#include <linux/uaccess.h>
22
#include <linux/vmalloc.h>
23
24
#include <asm/diag.h>
25
#include <asm/sclp.h>
26
#include <asm/timex.h>
27
#include <uapi/asm/diag.h>
28
#include "diag_ioctl.h"
29
30
enum subcode {
31
DIAG324_SUBC_0 = 0,
32
DIAG324_SUBC_1 = 1,
33
DIAG324_SUBC_2 = 2,
34
};
35
36
enum retcode {
37
DIAG324_RET_SUCCESS = 0x0001,
38
DIAG324_RET_SUBC_NOTAVAIL = 0x0103,
39
DIAG324_RET_INSUFFICIENT_SIZE = 0x0104,
40
DIAG324_RET_READING_UNAVAILABLE = 0x0105,
41
};
42
43
union diag324_response {
44
u64 response;
45
struct {
46
u64 installed : 32;
47
u64 : 16;
48
u64 rc : 16;
49
} sc0;
50
struct {
51
u64 format : 16;
52
u64 : 16;
53
u64 pib_len : 16;
54
u64 rc : 16;
55
} sc1;
56
struct {
57
u64 : 48;
58
u64 rc : 16;
59
} sc2;
60
};
61
62
union diag324_request {
63
u64 request;
64
struct {
65
u64 : 32;
66
u64 allocated : 16;
67
u64 : 12;
68
u64 sc : 4;
69
} sc2;
70
};
71
72
struct pib {
73
u32 : 8;
74
u32 num : 8;
75
u32 len : 16;
76
u32 : 24;
77
u32 hlen : 8;
78
u64 : 64;
79
u64 intv;
80
u8 r[];
81
} __packed;
82
83
struct pibdata {
84
struct pib *pib;
85
ktime_t expire;
86
u64 sequence;
87
size_t len;
88
int rc;
89
};
90
91
static DEFINE_MUTEX(pibmutex);
92
static struct pibdata pibdata;
93
94
#define PIBWORK_DELAY (5 * NSEC_PER_SEC)
95
96
static void pibwork_handler(struct work_struct *work);
97
static DECLARE_DELAYED_WORK(pibwork, pibwork_handler);
98
99
static unsigned long diag324(unsigned long subcode, void *addr)
100
{
101
union register_pair rp = { .even = (unsigned long)addr };
102
103
diag_stat_inc(DIAG_STAT_X324);
104
asm volatile("diag %[rp],%[subcode],0x324\n"
105
: [rp] "+d" (rp.pair)
106
: [subcode] "d" (subcode)
107
: "memory");
108
return rp.odd;
109
}
110
111
static void pibwork_handler(struct work_struct *work)
112
{
113
struct pibdata *data = &pibdata;
114
ktime_t timedout;
115
116
mutex_lock(&pibmutex);
117
timedout = ktime_add_ns(data->expire, PIBWORK_DELAY);
118
if (ktime_before(ktime_get(), timedout)) {
119
mod_delayed_work(system_wq, &pibwork, nsecs_to_jiffies(PIBWORK_DELAY));
120
goto out;
121
}
122
vfree(data->pib);
123
data->pib = NULL;
124
out:
125
mutex_unlock(&pibmutex);
126
}
127
128
static void pib_update(struct pibdata *data)
129
{
130
union diag324_request req = { .sc2.sc = DIAG324_SUBC_2, .sc2.allocated = data->len };
131
union diag324_response res;
132
int rc;
133
134
memset(data->pib, 0, data->len);
135
res.response = diag324(req.request, data->pib);
136
switch (res.sc2.rc) {
137
case DIAG324_RET_SUCCESS:
138
rc = 0;
139
break;
140
case DIAG324_RET_SUBC_NOTAVAIL:
141
rc = -ENOENT;
142
break;
143
case DIAG324_RET_INSUFFICIENT_SIZE:
144
rc = -EMSGSIZE;
145
break;
146
case DIAG324_RET_READING_UNAVAILABLE:
147
rc = -EBUSY;
148
break;
149
default:
150
rc = -EINVAL;
151
}
152
data->rc = rc;
153
}
154
155
long diag324_pibbuf(unsigned long arg)
156
{
157
struct diag324_pib __user *udata = (struct diag324_pib __user *)arg;
158
struct pibdata *data = &pibdata;
159
static bool first = true;
160
u64 address;
161
int rc;
162
163
if (!data->len)
164
return -EOPNOTSUPP;
165
if (get_user(address, &udata->address))
166
return -EFAULT;
167
mutex_lock(&pibmutex);
168
rc = -ENOMEM;
169
if (!data->pib)
170
data->pib = vmalloc(data->len);
171
if (!data->pib)
172
goto out;
173
if (first || ktime_after(ktime_get(), data->expire)) {
174
pib_update(data);
175
data->sequence++;
176
data->expire = ktime_add_ns(ktime_get(), tod_to_ns(data->pib->intv));
177
mod_delayed_work(system_wq, &pibwork, nsecs_to_jiffies(PIBWORK_DELAY));
178
first = false;
179
}
180
rc = data->rc;
181
if (rc != 0 && rc != -EBUSY)
182
goto out;
183
rc = copy_to_user((void __user *)address, data->pib, data->pib->len);
184
rc |= put_user(data->sequence, &udata->sequence);
185
if (rc)
186
rc = -EFAULT;
187
out:
188
mutex_unlock(&pibmutex);
189
return rc;
190
}
191
192
long diag324_piblen(unsigned long arg)
193
{
194
struct pibdata *data = &pibdata;
195
196
if (!data->len)
197
return -EOPNOTSUPP;
198
if (put_user(data->len, (size_t __user *)arg))
199
return -EFAULT;
200
return 0;
201
}
202
203
static int __init diag324_init(void)
204
{
205
union diag324_response res;
206
unsigned long installed;
207
208
if (!sclp.has_diag324)
209
return -EOPNOTSUPP;
210
res.response = diag324(DIAG324_SUBC_0, NULL);
211
if (res.sc0.rc != DIAG324_RET_SUCCESS)
212
return -EOPNOTSUPP;
213
installed = res.response;
214
if (!test_bit_inv(DIAG324_SUBC_1, &installed))
215
return -EOPNOTSUPP;
216
if (!test_bit_inv(DIAG324_SUBC_2, &installed))
217
return -EOPNOTSUPP;
218
res.response = diag324(DIAG324_SUBC_1, NULL);
219
if (res.sc1.rc != DIAG324_RET_SUCCESS)
220
return -EOPNOTSUPP;
221
pibdata.len = res.sc1.pib_len;
222
return 0;
223
}
224
device_initcall(diag324_init);
225
226