Skip to content

Commit 0d30871

Browse files
NickJackolsonAlexander Gordeev
authored andcommitted
s390/diag: Add memory topology information via diag310
Introduce diag310 and memory topology related subcodes. Provide memory topology information obtanied from diag310 to userspace via diag ioctl. Signed-off-by: Mete Durlu <meted@linux.ibm.com> Reviewed-by: Heiko Carstens <hca@linux.ibm.com> Signed-off-by: Alexander Gordeev <agordeev@linux.ibm.com>
1 parent b2bc1b1 commit 0d30871

File tree

9 files changed

+302
-1
lines changed

9 files changed

+302
-1
lines changed

arch/s390/include/asm/diag.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ enum diag_stat_enum {
3636
DIAG_STAT_X2FC,
3737
DIAG_STAT_X304,
3838
DIAG_STAT_X308,
39+
DIAG_STAT_X310,
3940
DIAG_STAT_X318,
4041
DIAG_STAT_X320,
4142
DIAG_STAT_X324,

arch/s390/include/asm/sclp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ struct sclp_info {
9292
unsigned char has_kss : 1;
9393
unsigned char has_diag204_bif : 1;
9494
unsigned char has_gisaf : 1;
95+
unsigned char has_diag310 : 1;
9596
unsigned char has_diag318 : 1;
9697
unsigned char has_diag320 : 1;
9798
unsigned char has_diag324 : 1;

arch/s390/include/uapi/asm/diag.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,16 @@ struct diag324_pib {
1717
__u64 sequence;
1818
};
1919

20+
struct diag310_memtop {
21+
__u64 address;
22+
__u64 nesting_lvl;
23+
};
24+
2025
/* Diag ioctl definitions */
2126
#define DIAG324_GET_PIBBUF _IOWR(DIAG_MAGIC_STR, 0x77, struct diag324_pib)
2227
#define DIAG324_GET_PIBLEN _IOR(DIAG_MAGIC_STR, 0x78, size_t)
28+
#define DIAG310_GET_STRIDE _IOR(DIAG_MAGIC_STR, 0x79, size_t)
29+
#define DIAG310_GET_MEMTOPLEN _IOWR(DIAG_MAGIC_STR, 0x7a, size_t)
30+
#define DIAG310_GET_MEMTOPBUF _IOWR(DIAG_MAGIC_STR, 0x7b, struct diag310_memtop)
2331

2432
#endif /* __S390_UAPI_ASM_DIAG_H */

arch/s390/kernel/diag/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
obj-y := diag_misc.o diag324.o diag.o
1+
obj-y := diag_misc.o diag324.o diag.o diag310.o

arch/s390/kernel/diag/diag.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ static const struct diag_desc diag_map[NR_DIAG_STAT] = {
5151
[DIAG_STAT_X2FC] = { .code = 0x2fc, .name = "Guest Performance Data" },
5252
[DIAG_STAT_X304] = { .code = 0x304, .name = "Partition-Resource Service" },
5353
[DIAG_STAT_X308] = { .code = 0x308, .name = "List-Directed IPL" },
54+
[DIAG_STAT_X310] = { .code = 0x310, .name = "Memory Topology Information" },
5455
[DIAG_STAT_X318] = { .code = 0x318, .name = "CP Name and Version Codes" },
5556
[DIAG_STAT_X320] = { .code = 0x320, .name = "Certificate Store" },
5657
[DIAG_STAT_X324] = { .code = 0x324, .name = "Power Information Block" },

arch/s390/kernel/diag/diag310.c

Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Request memory topology information via diag0x310.
4+
*
5+
* Copyright IBM Corp. 2025
6+
*/
7+
8+
#include <linux/kernel.h>
9+
#include <linux/types.h>
10+
#include <linux/uaccess.h>
11+
#include <linux/vmalloc.h>
12+
#include <asm/diag.h>
13+
#include <asm/sclp.h>
14+
#include <uapi/asm/diag.h>
15+
#include "diag_ioctl.h"
16+
17+
#define DIAG310_LEVELMIN 1
18+
#define DIAG310_LEVELMAX 6
19+
20+
enum diag310_sc {
21+
DIAG310_SUBC_0 = 0,
22+
DIAG310_SUBC_1 = 1,
23+
DIAG310_SUBC_4 = 4,
24+
DIAG310_SUBC_5 = 5
25+
};
26+
27+
enum diag310_retcode {
28+
DIAG310_RET_SUCCESS = 0x0001,
29+
DIAG310_RET_BUSY = 0x0101,
30+
DIAG310_RET_OPNOTSUPP = 0x0102,
31+
DIAG310_RET_SC4_INVAL = 0x0401,
32+
DIAG310_RET_SC4_NODATA = 0x0402,
33+
DIAG310_RET_SC5_INVAL = 0x0501,
34+
DIAG310_RET_SC5_NODATA = 0x0502,
35+
DIAG310_RET_SC5_ESIZE = 0x0503
36+
};
37+
38+
union diag310_response {
39+
u64 response;
40+
struct {
41+
u64 result : 32;
42+
u64 : 16;
43+
u64 rc : 16;
44+
};
45+
};
46+
47+
union diag310_req_subcode {
48+
u64 subcode;
49+
struct {
50+
u64 : 48;
51+
u64 st : 8;
52+
u64 sc : 8;
53+
};
54+
};
55+
56+
union diag310_req_size {
57+
u64 size;
58+
struct {
59+
u64 page_count : 32;
60+
u64 : 32;
61+
};
62+
};
63+
64+
static inline unsigned long diag310(unsigned long subcode, unsigned long size, void *addr)
65+
{
66+
union register_pair rp = { .even = (unsigned long)addr, .odd = size };
67+
68+
diag_stat_inc(DIAG_STAT_X310);
69+
asm volatile("diag %[rp],%[subcode],0x310\n"
70+
: [rp] "+d" (rp.pair)
71+
: [subcode] "d" (subcode)
72+
: "memory");
73+
return rp.odd;
74+
}
75+
76+
static int diag310_result_to_errno(unsigned int result)
77+
{
78+
switch (result) {
79+
case DIAG310_RET_BUSY:
80+
return -EBUSY;
81+
case DIAG310_RET_OPNOTSUPP:
82+
return -EOPNOTSUPP;
83+
default:
84+
return -EINVAL;
85+
}
86+
}
87+
88+
static int diag310_get_subcode_mask(unsigned long *mask)
89+
{
90+
union diag310_response res;
91+
92+
res.response = diag310(DIAG310_SUBC_0, 0, NULL);
93+
if (res.rc != DIAG310_RET_SUCCESS)
94+
return diag310_result_to_errno(res.rc);
95+
*mask = res.response;
96+
return 0;
97+
}
98+
99+
static int diag310_get_memtop_stride(unsigned long *stride)
100+
{
101+
union diag310_response res;
102+
103+
res.response = diag310(DIAG310_SUBC_1, 0, NULL);
104+
if (res.rc != DIAG310_RET_SUCCESS)
105+
return diag310_result_to_errno(res.rc);
106+
*stride = res.result;
107+
return 0;
108+
}
109+
110+
static int diag310_get_memtop_size(unsigned long *pages, unsigned long level)
111+
{
112+
union diag310_req_subcode req = { .sc = DIAG310_SUBC_4, .st = level };
113+
union diag310_response res;
114+
115+
res.response = diag310(req.subcode, 0, NULL);
116+
switch (res.rc) {
117+
case DIAG310_RET_SUCCESS:
118+
*pages = res.result;
119+
return 0;
120+
case DIAG310_RET_SC4_NODATA:
121+
return -ENODATA;
122+
case DIAG310_RET_SC4_INVAL:
123+
return -EINVAL;
124+
default:
125+
return diag310_result_to_errno(res.rc);
126+
}
127+
}
128+
129+
static int diag310_store_topology_map(void *buf, unsigned long pages, unsigned long level)
130+
{
131+
union diag310_req_subcode req_sc = { .sc = DIAG310_SUBC_5, .st = level };
132+
union diag310_req_size req_size = { .page_count = pages };
133+
union diag310_response res;
134+
135+
res.response = diag310(req_sc.subcode, req_size.size, buf);
136+
switch (res.rc) {
137+
case DIAG310_RET_SUCCESS:
138+
return 0;
139+
case DIAG310_RET_SC5_NODATA:
140+
return -ENODATA;
141+
case DIAG310_RET_SC5_ESIZE:
142+
return -EOVERFLOW;
143+
case DIAG310_RET_SC5_INVAL:
144+
return -EINVAL;
145+
default:
146+
return diag310_result_to_errno(res.rc);
147+
}
148+
}
149+
150+
static int diag310_check_features(void)
151+
{
152+
static int features_available;
153+
unsigned long mask;
154+
int rc;
155+
156+
if (READ_ONCE(features_available))
157+
return 0;
158+
if (!sclp.has_diag310)
159+
return -EOPNOTSUPP;
160+
rc = diag310_get_subcode_mask(&mask);
161+
if (rc)
162+
return rc;
163+
if (!test_bit_inv(DIAG310_SUBC_1, &mask))
164+
return -EOPNOTSUPP;
165+
if (!test_bit_inv(DIAG310_SUBC_4, &mask))
166+
return -EOPNOTSUPP;
167+
if (!test_bit_inv(DIAG310_SUBC_5, &mask))
168+
return -EOPNOTSUPP;
169+
WRITE_ONCE(features_available, 1);
170+
return 0;
171+
}
172+
173+
static int memtop_get_stride_len(unsigned long *res)
174+
{
175+
static unsigned long memtop_stride;
176+
unsigned long stride;
177+
int rc;
178+
179+
stride = READ_ONCE(memtop_stride);
180+
if (!stride) {
181+
rc = diag310_get_memtop_stride(&stride);
182+
if (rc)
183+
return rc;
184+
WRITE_ONCE(memtop_stride, stride);
185+
}
186+
*res = stride;
187+
return 0;
188+
}
189+
190+
static int memtop_get_page_count(unsigned long *res, unsigned long level)
191+
{
192+
static unsigned long memtop_pages[DIAG310_LEVELMAX];
193+
unsigned long pages;
194+
int rc;
195+
196+
if (level > DIAG310_LEVELMAX || level < DIAG310_LEVELMIN)
197+
return -EINVAL;
198+
pages = READ_ONCE(memtop_pages[level - 1]);
199+
if (!pages) {
200+
rc = diag310_get_memtop_size(&pages, level);
201+
if (rc)
202+
return rc;
203+
WRITE_ONCE(memtop_pages[level - 1], pages);
204+
}
205+
*res = pages;
206+
return 0;
207+
}
208+
209+
long diag310_memtop_stride(unsigned long arg)
210+
{
211+
size_t __user *argp = (void __user *)arg;
212+
unsigned long stride;
213+
int rc;
214+
215+
rc = diag310_check_features();
216+
if (rc)
217+
return rc;
218+
rc = memtop_get_stride_len(&stride);
219+
if (rc)
220+
return rc;
221+
if (put_user(stride, argp))
222+
return -EFAULT;
223+
return 0;
224+
}
225+
226+
long diag310_memtop_len(unsigned long arg)
227+
{
228+
size_t __user *argp = (void __user *)arg;
229+
unsigned long pages, level;
230+
int rc;
231+
232+
rc = diag310_check_features();
233+
if (rc)
234+
return rc;
235+
if (get_user(level, argp))
236+
return -EFAULT;
237+
rc = memtop_get_page_count(&pages, level);
238+
if (rc)
239+
return rc;
240+
if (put_user(pages * PAGE_SIZE, argp))
241+
return -EFAULT;
242+
return 0;
243+
}
244+
245+
long diag310_memtop_buf(unsigned long arg)
246+
{
247+
struct diag310_memtop __user *udata = (struct diag310_memtop __user *)arg;
248+
unsigned long level, pages, data_size;
249+
u64 address;
250+
void *buf;
251+
int rc;
252+
253+
rc = diag310_check_features();
254+
if (rc)
255+
return rc;
256+
if (get_user(level, &udata->nesting_lvl))
257+
return -EFAULT;
258+
if (get_user(address, &udata->address))
259+
return -EFAULT;
260+
rc = memtop_get_page_count(&pages, level);
261+
if (rc)
262+
return rc;
263+
data_size = pages * PAGE_SIZE;
264+
buf = __vmalloc_node(data_size, PAGE_SIZE, GFP_KERNEL | __GFP_ZERO | __GFP_ACCOUNT,
265+
NUMA_NO_NODE, __builtin_return_address(0));
266+
if (!buf)
267+
return -ENOMEM;
268+
rc = diag310_store_topology_map(buf, pages, level);
269+
if (rc)
270+
goto out;
271+
if (copy_to_user((void __user *)address, buf, data_size))
272+
rc = -EFAULT;
273+
out:
274+
vfree(buf);
275+
return rc;
276+
}

arch/s390/kernel/diag/diag_ioctl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,8 @@
77
long diag324_pibbuf(unsigned long arg);
88
long diag324_piblen(unsigned long arg);
99

10+
long diag310_memtop_stride(unsigned long arg);
11+
long diag310_memtop_len(unsigned long arg);
12+
long diag310_memtop_buf(unsigned long arg);
13+
1014
#endif /* _DIAG_IOCTL_H */

arch/s390/kernel/diag/diag_misc.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,15 @@ static long diag_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
2626
case DIAG324_GET_PIBBUF:
2727
rc = diag324_pibbuf(arg);
2828
break;
29+
case DIAG310_GET_STRIDE:
30+
rc = diag310_memtop_stride(arg);
31+
break;
32+
case DIAG310_GET_MEMTOPLEN:
33+
rc = diag310_memtop_len(arg);
34+
break;
35+
case DIAG310_GET_MEMTOPBUF:
36+
rc = diag310_memtop_buf(arg);
37+
break;
2938
default:
3039
rc = -ENOIOCTLCMD;
3140
break;

drivers/s390/char/sclp_early.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ static void __init sclp_early_facilities_detect(void)
5555
if (sccb->fac91 & 0x40)
5656
get_lowcore()->machine_flags |= MACHINE_FLAG_TLB_GUEST;
5757
sclp.has_diag204_bif = !!(sccb->fac98 & 0x80);
58+
sclp.has_diag310 = !!(sccb->fac91 & 0x80);
5859
if (sccb->cpuoff > 134) {
5960
sclp.has_diag318 = !!(sccb->byte_134 & 0x80);
6061
sclp.has_diag320 = !!(sccb->byte_134 & 0x04);

0 commit comments

Comments
 (0)