xref: /linux/arch/s390/kernel/diag/diag324.c (revision e814f3fd16acfb7f9966773953de8f740a1e3202)
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