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