File Index
Files and directories
Sign in
FILE

diag324.c

File preview with syntax highlighting.

linux-6.19.14/arch/s390/kernel/diag/diag324.c C
  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
 30enum subcode {
 31	DIAG324_SUBC_0 = 0,
 32	DIAG324_SUBC_1 = 1,
 33	DIAG324_SUBC_2 = 2,
 34};
 35
 36enum 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
 43union 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
 62union 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
 72struct 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
 83struct pibdata {
 84	struct pib *pib;
 85	ktime_t expire;
 86	u64 sequence;
 87	size_t len;
 88	int rc;
 89};
 90
 91static DEFINE_MUTEX(pibmutex);
 92static struct pibdata pibdata;
 93
 94#define PIBWORK_DELAY (5 * NSEC_PER_SEC)
 95
 96static void pibwork_handler(struct work_struct *work);
 97static DECLARE_DELAYED_WORK(pibwork, pibwork_handler);
 98
 99static 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"
105		     : [rp] "+d" (rp.pair)
106		     : [subcode] "d" (subcode)
107		     : "memory");
108	return rp.odd;
109}
110
111static 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_percpu_wq, &pibwork, nsecs_to_jiffies(PIBWORK_DELAY));
120		goto out;
121	}
122	vfree(data->pib);
123	data->pib = NULL;
124out:
125	mutex_unlock(&pibmutex);
126}
127
128static 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
155long 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_percpu_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;
187out:
188	mutex_unlock(&pibmutex);
189	return rc;
190}
191
192long 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
203static 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}
224device_initcall(diag324_init);
~/ Read-only file preview.