mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-22 07:53:11 -05:00
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>
This commit is contained in:
parent
b2bc1b1a77
commit
0d30871739
9 changed files with 302 additions and 1 deletions
|
@ -36,6 +36,7 @@ enum diag_stat_enum {
|
|||
DIAG_STAT_X2FC,
|
||||
DIAG_STAT_X304,
|
||||
DIAG_STAT_X308,
|
||||
DIAG_STAT_X310,
|
||||
DIAG_STAT_X318,
|
||||
DIAG_STAT_X320,
|
||||
DIAG_STAT_X324,
|
||||
|
|
|
@ -92,6 +92,7 @@ struct sclp_info {
|
|||
unsigned char has_kss : 1;
|
||||
unsigned char has_diag204_bif : 1;
|
||||
unsigned char has_gisaf : 1;
|
||||
unsigned char has_diag310 : 1;
|
||||
unsigned char has_diag318 : 1;
|
||||
unsigned char has_diag320 : 1;
|
||||
unsigned char has_diag324 : 1;
|
||||
|
|
|
@ -17,8 +17,16 @@ struct diag324_pib {
|
|||
__u64 sequence;
|
||||
};
|
||||
|
||||
struct diag310_memtop {
|
||||
__u64 address;
|
||||
__u64 nesting_lvl;
|
||||
};
|
||||
|
||||
/* Diag ioctl definitions */
|
||||
#define DIAG324_GET_PIBBUF _IOWR(DIAG_MAGIC_STR, 0x77, struct diag324_pib)
|
||||
#define DIAG324_GET_PIBLEN _IOR(DIAG_MAGIC_STR, 0x78, size_t)
|
||||
#define DIAG310_GET_STRIDE _IOR(DIAG_MAGIC_STR, 0x79, size_t)
|
||||
#define DIAG310_GET_MEMTOPLEN _IOWR(DIAG_MAGIC_STR, 0x7a, size_t)
|
||||
#define DIAG310_GET_MEMTOPBUF _IOWR(DIAG_MAGIC_STR, 0x7b, struct diag310_memtop)
|
||||
|
||||
#endif /* __S390_UAPI_ASM_DIAG_H */
|
||||
|
|
|
@ -1 +1 @@
|
|||
obj-y := diag_misc.o diag324.o diag.o
|
||||
obj-y := diag_misc.o diag324.o diag.o diag310.o
|
||||
|
|
|
@ -51,6 +51,7 @@ static const struct diag_desc diag_map[NR_DIAG_STAT] = {
|
|||
[DIAG_STAT_X2FC] = { .code = 0x2fc, .name = "Guest Performance Data" },
|
||||
[DIAG_STAT_X304] = { .code = 0x304, .name = "Partition-Resource Service" },
|
||||
[DIAG_STAT_X308] = { .code = 0x308, .name = "List-Directed IPL" },
|
||||
[DIAG_STAT_X310] = { .code = 0x310, .name = "Memory Topology Information" },
|
||||
[DIAG_STAT_X318] = { .code = 0x318, .name = "CP Name and Version Codes" },
|
||||
[DIAG_STAT_X320] = { .code = 0x320, .name = "Certificate Store" },
|
||||
[DIAG_STAT_X324] = { .code = 0x324, .name = "Power Information Block" },
|
||||
|
|
276
arch/s390/kernel/diag/diag310.c
Normal file
276
arch/s390/kernel/diag/diag310.c
Normal file
|
@ -0,0 +1,276 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Request memory topology information via diag0x310.
|
||||
*
|
||||
* Copyright IBM Corp. 2025
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <asm/diag.h>
|
||||
#include <asm/sclp.h>
|
||||
#include <uapi/asm/diag.h>
|
||||
#include "diag_ioctl.h"
|
||||
|
||||
#define DIAG310_LEVELMIN 1
|
||||
#define DIAG310_LEVELMAX 6
|
||||
|
||||
enum diag310_sc {
|
||||
DIAG310_SUBC_0 = 0,
|
||||
DIAG310_SUBC_1 = 1,
|
||||
DIAG310_SUBC_4 = 4,
|
||||
DIAG310_SUBC_5 = 5
|
||||
};
|
||||
|
||||
enum diag310_retcode {
|
||||
DIAG310_RET_SUCCESS = 0x0001,
|
||||
DIAG310_RET_BUSY = 0x0101,
|
||||
DIAG310_RET_OPNOTSUPP = 0x0102,
|
||||
DIAG310_RET_SC4_INVAL = 0x0401,
|
||||
DIAG310_RET_SC4_NODATA = 0x0402,
|
||||
DIAG310_RET_SC5_INVAL = 0x0501,
|
||||
DIAG310_RET_SC5_NODATA = 0x0502,
|
||||
DIAG310_RET_SC5_ESIZE = 0x0503
|
||||
};
|
||||
|
||||
union diag310_response {
|
||||
u64 response;
|
||||
struct {
|
||||
u64 result : 32;
|
||||
u64 : 16;
|
||||
u64 rc : 16;
|
||||
};
|
||||
};
|
||||
|
||||
union diag310_req_subcode {
|
||||
u64 subcode;
|
||||
struct {
|
||||
u64 : 48;
|
||||
u64 st : 8;
|
||||
u64 sc : 8;
|
||||
};
|
||||
};
|
||||
|
||||
union diag310_req_size {
|
||||
u64 size;
|
||||
struct {
|
||||
u64 page_count : 32;
|
||||
u64 : 32;
|
||||
};
|
||||
};
|
||||
|
||||
static inline unsigned long diag310(unsigned long subcode, unsigned long size, void *addr)
|
||||
{
|
||||
union register_pair rp = { .even = (unsigned long)addr, .odd = size };
|
||||
|
||||
diag_stat_inc(DIAG_STAT_X310);
|
||||
asm volatile("diag %[rp],%[subcode],0x310\n"
|
||||
: [rp] "+d" (rp.pair)
|
||||
: [subcode] "d" (subcode)
|
||||
: "memory");
|
||||
return rp.odd;
|
||||
}
|
||||
|
||||
static int diag310_result_to_errno(unsigned int result)
|
||||
{
|
||||
switch (result) {
|
||||
case DIAG310_RET_BUSY:
|
||||
return -EBUSY;
|
||||
case DIAG310_RET_OPNOTSUPP:
|
||||
return -EOPNOTSUPP;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int diag310_get_subcode_mask(unsigned long *mask)
|
||||
{
|
||||
union diag310_response res;
|
||||
|
||||
res.response = diag310(DIAG310_SUBC_0, 0, NULL);
|
||||
if (res.rc != DIAG310_RET_SUCCESS)
|
||||
return diag310_result_to_errno(res.rc);
|
||||
*mask = res.response;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int diag310_get_memtop_stride(unsigned long *stride)
|
||||
{
|
||||
union diag310_response res;
|
||||
|
||||
res.response = diag310(DIAG310_SUBC_1, 0, NULL);
|
||||
if (res.rc != DIAG310_RET_SUCCESS)
|
||||
return diag310_result_to_errno(res.rc);
|
||||
*stride = res.result;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int diag310_get_memtop_size(unsigned long *pages, unsigned long level)
|
||||
{
|
||||
union diag310_req_subcode req = { .sc = DIAG310_SUBC_4, .st = level };
|
||||
union diag310_response res;
|
||||
|
||||
res.response = diag310(req.subcode, 0, NULL);
|
||||
switch (res.rc) {
|
||||
case DIAG310_RET_SUCCESS:
|
||||
*pages = res.result;
|
||||
return 0;
|
||||
case DIAG310_RET_SC4_NODATA:
|
||||
return -ENODATA;
|
||||
case DIAG310_RET_SC4_INVAL:
|
||||
return -EINVAL;
|
||||
default:
|
||||
return diag310_result_to_errno(res.rc);
|
||||
}
|
||||
}
|
||||
|
||||
static int diag310_store_topology_map(void *buf, unsigned long pages, unsigned long level)
|
||||
{
|
||||
union diag310_req_subcode req_sc = { .sc = DIAG310_SUBC_5, .st = level };
|
||||
union diag310_req_size req_size = { .page_count = pages };
|
||||
union diag310_response res;
|
||||
|
||||
res.response = diag310(req_sc.subcode, req_size.size, buf);
|
||||
switch (res.rc) {
|
||||
case DIAG310_RET_SUCCESS:
|
||||
return 0;
|
||||
case DIAG310_RET_SC5_NODATA:
|
||||
return -ENODATA;
|
||||
case DIAG310_RET_SC5_ESIZE:
|
||||
return -EOVERFLOW;
|
||||
case DIAG310_RET_SC5_INVAL:
|
||||
return -EINVAL;
|
||||
default:
|
||||
return diag310_result_to_errno(res.rc);
|
||||
}
|
||||
}
|
||||
|
||||
static int diag310_check_features(void)
|
||||
{
|
||||
static int features_available;
|
||||
unsigned long mask;
|
||||
int rc;
|
||||
|
||||
if (READ_ONCE(features_available))
|
||||
return 0;
|
||||
if (!sclp.has_diag310)
|
||||
return -EOPNOTSUPP;
|
||||
rc = diag310_get_subcode_mask(&mask);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (!test_bit_inv(DIAG310_SUBC_1, &mask))
|
||||
return -EOPNOTSUPP;
|
||||
if (!test_bit_inv(DIAG310_SUBC_4, &mask))
|
||||
return -EOPNOTSUPP;
|
||||
if (!test_bit_inv(DIAG310_SUBC_5, &mask))
|
||||
return -EOPNOTSUPP;
|
||||
WRITE_ONCE(features_available, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int memtop_get_stride_len(unsigned long *res)
|
||||
{
|
||||
static unsigned long memtop_stride;
|
||||
unsigned long stride;
|
||||
int rc;
|
||||
|
||||
stride = READ_ONCE(memtop_stride);
|
||||
if (!stride) {
|
||||
rc = diag310_get_memtop_stride(&stride);
|
||||
if (rc)
|
||||
return rc;
|
||||
WRITE_ONCE(memtop_stride, stride);
|
||||
}
|
||||
*res = stride;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int memtop_get_page_count(unsigned long *res, unsigned long level)
|
||||
{
|
||||
static unsigned long memtop_pages[DIAG310_LEVELMAX];
|
||||
unsigned long pages;
|
||||
int rc;
|
||||
|
||||
if (level > DIAG310_LEVELMAX || level < DIAG310_LEVELMIN)
|
||||
return -EINVAL;
|
||||
pages = READ_ONCE(memtop_pages[level - 1]);
|
||||
if (!pages) {
|
||||
rc = diag310_get_memtop_size(&pages, level);
|
||||
if (rc)
|
||||
return rc;
|
||||
WRITE_ONCE(memtop_pages[level - 1], pages);
|
||||
}
|
||||
*res = pages;
|
||||
return 0;
|
||||
}
|
||||
|
||||
long diag310_memtop_stride(unsigned long arg)
|
||||
{
|
||||
size_t __user *argp = (void __user *)arg;
|
||||
unsigned long stride;
|
||||
int rc;
|
||||
|
||||
rc = diag310_check_features();
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = memtop_get_stride_len(&stride);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (put_user(stride, argp))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
long diag310_memtop_len(unsigned long arg)
|
||||
{
|
||||
size_t __user *argp = (void __user *)arg;
|
||||
unsigned long pages, level;
|
||||
int rc;
|
||||
|
||||
rc = diag310_check_features();
|
||||
if (rc)
|
||||
return rc;
|
||||
if (get_user(level, argp))
|
||||
return -EFAULT;
|
||||
rc = memtop_get_page_count(&pages, level);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (put_user(pages * PAGE_SIZE, argp))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
long diag310_memtop_buf(unsigned long arg)
|
||||
{
|
||||
struct diag310_memtop __user *udata = (struct diag310_memtop __user *)arg;
|
||||
unsigned long level, pages, data_size;
|
||||
u64 address;
|
||||
void *buf;
|
||||
int rc;
|
||||
|
||||
rc = diag310_check_features();
|
||||
if (rc)
|
||||
return rc;
|
||||
if (get_user(level, &udata->nesting_lvl))
|
||||
return -EFAULT;
|
||||
if (get_user(address, &udata->address))
|
||||
return -EFAULT;
|
||||
rc = memtop_get_page_count(&pages, level);
|
||||
if (rc)
|
||||
return rc;
|
||||
data_size = pages * PAGE_SIZE;
|
||||
buf = __vmalloc_node(data_size, PAGE_SIZE, GFP_KERNEL | __GFP_ZERO | __GFP_ACCOUNT,
|
||||
NUMA_NO_NODE, __builtin_return_address(0));
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
rc = diag310_store_topology_map(buf, pages, level);
|
||||
if (rc)
|
||||
goto out;
|
||||
if (copy_to_user((void __user *)address, buf, data_size))
|
||||
rc = -EFAULT;
|
||||
out:
|
||||
vfree(buf);
|
||||
return rc;
|
||||
}
|
|
@ -7,4 +7,8 @@
|
|||
long diag324_pibbuf(unsigned long arg);
|
||||
long diag324_piblen(unsigned long arg);
|
||||
|
||||
long diag310_memtop_stride(unsigned long arg);
|
||||
long diag310_memtop_len(unsigned long arg);
|
||||
long diag310_memtop_buf(unsigned long arg);
|
||||
|
||||
#endif /* _DIAG_IOCTL_H */
|
||||
|
|
|
@ -26,6 +26,15 @@ static long diag_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|||
case DIAG324_GET_PIBBUF:
|
||||
rc = diag324_pibbuf(arg);
|
||||
break;
|
||||
case DIAG310_GET_STRIDE:
|
||||
rc = diag310_memtop_stride(arg);
|
||||
break;
|
||||
case DIAG310_GET_MEMTOPLEN:
|
||||
rc = diag310_memtop_len(arg);
|
||||
break;
|
||||
case DIAG310_GET_MEMTOPBUF:
|
||||
rc = diag310_memtop_buf(arg);
|
||||
break;
|
||||
default:
|
||||
rc = -ENOIOCTLCMD;
|
||||
break;
|
||||
|
|
|
@ -55,6 +55,7 @@ static void __init sclp_early_facilities_detect(void)
|
|||
if (sccb->fac91 & 0x40)
|
||||
get_lowcore()->machine_flags |= MACHINE_FLAG_TLB_GUEST;
|
||||
sclp.has_diag204_bif = !!(sccb->fac98 & 0x80);
|
||||
sclp.has_diag310 = !!(sccb->fac91 & 0x80);
|
||||
if (sccb->cpuoff > 134) {
|
||||
sclp.has_diag318 = !!(sccb->byte_134 & 0x80);
|
||||
sclp.has_diag320 = !!(sccb->byte_134 & 0x04);
|
||||
|
|
Loading…
Reference in a new issue