18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Hypervisor filesystem for Linux on s390. z/VM implementation. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2006 68c2ecf20Sopenharmony_ci * Author(s): Michael Holzheu <holzheu@de.ibm.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/types.h> 108c2ecf20Sopenharmony_ci#include <linux/errno.h> 118c2ecf20Sopenharmony_ci#include <linux/string.h> 128c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 138c2ecf20Sopenharmony_ci#include <asm/diag.h> 148c2ecf20Sopenharmony_ci#include <asm/ebcdic.h> 158c2ecf20Sopenharmony_ci#include <asm/timex.h> 168c2ecf20Sopenharmony_ci#include "hypfs.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define NAME_LEN 8 198c2ecf20Sopenharmony_ci#define DBFS_D2FC_HDR_VERSION 0 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic char local_guest[] = " "; 228c2ecf20Sopenharmony_cistatic char all_guests[] = "* "; 238c2ecf20Sopenharmony_cistatic char *all_groups = all_guests; 248c2ecf20Sopenharmony_cistatic char *guest_query; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistruct diag2fc_data { 278c2ecf20Sopenharmony_ci __u32 version; 288c2ecf20Sopenharmony_ci __u32 flags; 298c2ecf20Sopenharmony_ci __u64 used_cpu; 308c2ecf20Sopenharmony_ci __u64 el_time; 318c2ecf20Sopenharmony_ci __u64 mem_min_kb; 328c2ecf20Sopenharmony_ci __u64 mem_max_kb; 338c2ecf20Sopenharmony_ci __u64 mem_share_kb; 348c2ecf20Sopenharmony_ci __u64 mem_used_kb; 358c2ecf20Sopenharmony_ci __u32 pcpus; 368c2ecf20Sopenharmony_ci __u32 lcpus; 378c2ecf20Sopenharmony_ci __u32 vcpus; 388c2ecf20Sopenharmony_ci __u32 ocpus; 398c2ecf20Sopenharmony_ci __u32 cpu_max; 408c2ecf20Sopenharmony_ci __u32 cpu_shares; 418c2ecf20Sopenharmony_ci __u32 cpu_use_samp; 428c2ecf20Sopenharmony_ci __u32 cpu_delay_samp; 438c2ecf20Sopenharmony_ci __u32 page_wait_samp; 448c2ecf20Sopenharmony_ci __u32 idle_samp; 458c2ecf20Sopenharmony_ci __u32 other_samp; 468c2ecf20Sopenharmony_ci __u32 total_samp; 478c2ecf20Sopenharmony_ci char guest_name[NAME_LEN]; 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistruct diag2fc_parm_list { 518c2ecf20Sopenharmony_ci char userid[NAME_LEN]; 528c2ecf20Sopenharmony_ci char aci_grp[NAME_LEN]; 538c2ecf20Sopenharmony_ci __u64 addr; 548c2ecf20Sopenharmony_ci __u32 size; 558c2ecf20Sopenharmony_ci __u32 fmt; 568c2ecf20Sopenharmony_ci}; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic int diag2fc(int size, char* query, void *addr) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci unsigned long residual_cnt; 618c2ecf20Sopenharmony_ci unsigned long rc; 628c2ecf20Sopenharmony_ci struct diag2fc_parm_list parm_list; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci memcpy(parm_list.userid, query, NAME_LEN); 658c2ecf20Sopenharmony_ci ASCEBC(parm_list.userid, NAME_LEN); 668c2ecf20Sopenharmony_ci memcpy(parm_list.aci_grp, all_groups, NAME_LEN); 678c2ecf20Sopenharmony_ci ASCEBC(parm_list.aci_grp, NAME_LEN); 688c2ecf20Sopenharmony_ci parm_list.addr = (unsigned long)addr; 698c2ecf20Sopenharmony_ci parm_list.size = size; 708c2ecf20Sopenharmony_ci parm_list.fmt = 0x02; 718c2ecf20Sopenharmony_ci rc = -1; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci diag_stat_inc(DIAG_STAT_X2FC); 748c2ecf20Sopenharmony_ci asm volatile( 758c2ecf20Sopenharmony_ci " diag %0,%1,0x2fc\n" 768c2ecf20Sopenharmony_ci "0: nopr %%r7\n" 778c2ecf20Sopenharmony_ci EX_TABLE(0b,0b) 788c2ecf20Sopenharmony_ci : "=d" (residual_cnt), "+d" (rc) : "0" (&parm_list) : "memory"); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci if ((rc != 0 ) && (rc != -2)) 818c2ecf20Sopenharmony_ci return rc; 828c2ecf20Sopenharmony_ci else 838c2ecf20Sopenharmony_ci return -residual_cnt; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/* 878c2ecf20Sopenharmony_ci * Allocate buffer for "query" and store diag 2fc at "offset" 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_cistatic void *diag2fc_store(char *query, unsigned int *count, int offset) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci void *data; 928c2ecf20Sopenharmony_ci int size; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci do { 958c2ecf20Sopenharmony_ci size = diag2fc(0, query, NULL); 968c2ecf20Sopenharmony_ci if (size < 0) 978c2ecf20Sopenharmony_ci return ERR_PTR(-EACCES); 988c2ecf20Sopenharmony_ci data = vmalloc(size + offset); 998c2ecf20Sopenharmony_ci if (!data) 1008c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1018c2ecf20Sopenharmony_ci if (diag2fc(size, query, data + offset) == 0) 1028c2ecf20Sopenharmony_ci break; 1038c2ecf20Sopenharmony_ci vfree(data); 1048c2ecf20Sopenharmony_ci } while (1); 1058c2ecf20Sopenharmony_ci *count = (size / sizeof(struct diag2fc_data)); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci return data; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic void diag2fc_free(const void *data) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci vfree(data); 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci#define ATTRIBUTE(dir, name, member) \ 1168c2ecf20Sopenharmony_cido { \ 1178c2ecf20Sopenharmony_ci void *rc; \ 1188c2ecf20Sopenharmony_ci rc = hypfs_create_u64(dir, name, member); \ 1198c2ecf20Sopenharmony_ci if (IS_ERR(rc)) \ 1208c2ecf20Sopenharmony_ci return PTR_ERR(rc); \ 1218c2ecf20Sopenharmony_ci} while(0) 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic int hypfs_vm_create_guest(struct dentry *systems_dir, 1248c2ecf20Sopenharmony_ci struct diag2fc_data *data) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci char guest_name[NAME_LEN + 1] = {}; 1278c2ecf20Sopenharmony_ci struct dentry *guest_dir, *cpus_dir, *samples_dir, *mem_dir; 1288c2ecf20Sopenharmony_ci int dedicated_flag, capped_value; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci capped_value = (data->flags & 0x00000006) >> 1; 1318c2ecf20Sopenharmony_ci dedicated_flag = (data->flags & 0x00000008) >> 3; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci /* guest dir */ 1348c2ecf20Sopenharmony_ci memcpy(guest_name, data->guest_name, NAME_LEN); 1358c2ecf20Sopenharmony_ci EBCASC(guest_name, NAME_LEN); 1368c2ecf20Sopenharmony_ci strim(guest_name); 1378c2ecf20Sopenharmony_ci guest_dir = hypfs_mkdir(systems_dir, guest_name); 1388c2ecf20Sopenharmony_ci if (IS_ERR(guest_dir)) 1398c2ecf20Sopenharmony_ci return PTR_ERR(guest_dir); 1408c2ecf20Sopenharmony_ci ATTRIBUTE(guest_dir, "onlinetime_us", data->el_time); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* logical cpu information */ 1438c2ecf20Sopenharmony_ci cpus_dir = hypfs_mkdir(guest_dir, "cpus"); 1448c2ecf20Sopenharmony_ci if (IS_ERR(cpus_dir)) 1458c2ecf20Sopenharmony_ci return PTR_ERR(cpus_dir); 1468c2ecf20Sopenharmony_ci ATTRIBUTE(cpus_dir, "cputime_us", data->used_cpu); 1478c2ecf20Sopenharmony_ci ATTRIBUTE(cpus_dir, "capped", capped_value); 1488c2ecf20Sopenharmony_ci ATTRIBUTE(cpus_dir, "dedicated", dedicated_flag); 1498c2ecf20Sopenharmony_ci ATTRIBUTE(cpus_dir, "count", data->vcpus); 1508c2ecf20Sopenharmony_ci /* 1518c2ecf20Sopenharmony_ci * Note: The "weight_min" attribute got the wrong name. 1528c2ecf20Sopenharmony_ci * The value represents the number of non-stopped (operating) 1538c2ecf20Sopenharmony_ci * CPUS. 1548c2ecf20Sopenharmony_ci */ 1558c2ecf20Sopenharmony_ci ATTRIBUTE(cpus_dir, "weight_min", data->ocpus); 1568c2ecf20Sopenharmony_ci ATTRIBUTE(cpus_dir, "weight_max", data->cpu_max); 1578c2ecf20Sopenharmony_ci ATTRIBUTE(cpus_dir, "weight_cur", data->cpu_shares); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* memory information */ 1608c2ecf20Sopenharmony_ci mem_dir = hypfs_mkdir(guest_dir, "mem"); 1618c2ecf20Sopenharmony_ci if (IS_ERR(mem_dir)) 1628c2ecf20Sopenharmony_ci return PTR_ERR(mem_dir); 1638c2ecf20Sopenharmony_ci ATTRIBUTE(mem_dir, "min_KiB", data->mem_min_kb); 1648c2ecf20Sopenharmony_ci ATTRIBUTE(mem_dir, "max_KiB", data->mem_max_kb); 1658c2ecf20Sopenharmony_ci ATTRIBUTE(mem_dir, "used_KiB", data->mem_used_kb); 1668c2ecf20Sopenharmony_ci ATTRIBUTE(mem_dir, "share_KiB", data->mem_share_kb); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci /* samples */ 1698c2ecf20Sopenharmony_ci samples_dir = hypfs_mkdir(guest_dir, "samples"); 1708c2ecf20Sopenharmony_ci if (IS_ERR(samples_dir)) 1718c2ecf20Sopenharmony_ci return PTR_ERR(samples_dir); 1728c2ecf20Sopenharmony_ci ATTRIBUTE(samples_dir, "cpu_using", data->cpu_use_samp); 1738c2ecf20Sopenharmony_ci ATTRIBUTE(samples_dir, "cpu_delay", data->cpu_delay_samp); 1748c2ecf20Sopenharmony_ci ATTRIBUTE(samples_dir, "mem_delay", data->page_wait_samp); 1758c2ecf20Sopenharmony_ci ATTRIBUTE(samples_dir, "idle", data->idle_samp); 1768c2ecf20Sopenharmony_ci ATTRIBUTE(samples_dir, "other", data->other_samp); 1778c2ecf20Sopenharmony_ci ATTRIBUTE(samples_dir, "total", data->total_samp); 1788c2ecf20Sopenharmony_ci return 0; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ciint hypfs_vm_create_files(struct dentry *root) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct dentry *dir, *file; 1848c2ecf20Sopenharmony_ci struct diag2fc_data *data; 1858c2ecf20Sopenharmony_ci unsigned int count = 0; 1868c2ecf20Sopenharmony_ci int rc, i; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci data = diag2fc_store(guest_query, &count, 0); 1898c2ecf20Sopenharmony_ci if (IS_ERR(data)) 1908c2ecf20Sopenharmony_ci return PTR_ERR(data); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* Hpervisor Info */ 1938c2ecf20Sopenharmony_ci dir = hypfs_mkdir(root, "hyp"); 1948c2ecf20Sopenharmony_ci if (IS_ERR(dir)) { 1958c2ecf20Sopenharmony_ci rc = PTR_ERR(dir); 1968c2ecf20Sopenharmony_ci goto failed; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci file = hypfs_create_str(dir, "type", "z/VM Hypervisor"); 1998c2ecf20Sopenharmony_ci if (IS_ERR(file)) { 2008c2ecf20Sopenharmony_ci rc = PTR_ERR(file); 2018c2ecf20Sopenharmony_ci goto failed; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* physical cpus */ 2058c2ecf20Sopenharmony_ci dir = hypfs_mkdir(root, "cpus"); 2068c2ecf20Sopenharmony_ci if (IS_ERR(dir)) { 2078c2ecf20Sopenharmony_ci rc = PTR_ERR(dir); 2088c2ecf20Sopenharmony_ci goto failed; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci file = hypfs_create_u64(dir, "count", data->lcpus); 2118c2ecf20Sopenharmony_ci if (IS_ERR(file)) { 2128c2ecf20Sopenharmony_ci rc = PTR_ERR(file); 2138c2ecf20Sopenharmony_ci goto failed; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci /* guests */ 2178c2ecf20Sopenharmony_ci dir = hypfs_mkdir(root, "systems"); 2188c2ecf20Sopenharmony_ci if (IS_ERR(dir)) { 2198c2ecf20Sopenharmony_ci rc = PTR_ERR(dir); 2208c2ecf20Sopenharmony_ci goto failed; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 2248c2ecf20Sopenharmony_ci rc = hypfs_vm_create_guest(dir, &(data[i])); 2258c2ecf20Sopenharmony_ci if (rc) 2268c2ecf20Sopenharmony_ci goto failed; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci diag2fc_free(data); 2298c2ecf20Sopenharmony_ci return 0; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cifailed: 2328c2ecf20Sopenharmony_ci diag2fc_free(data); 2338c2ecf20Sopenharmony_ci return rc; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistruct dbfs_d2fc_hdr { 2378c2ecf20Sopenharmony_ci u64 len; /* Length of d2fc buffer without header */ 2388c2ecf20Sopenharmony_ci u16 version; /* Version of header */ 2398c2ecf20Sopenharmony_ci char tod_ext[STORE_CLOCK_EXT_SIZE]; /* TOD clock for d2fc */ 2408c2ecf20Sopenharmony_ci u64 count; /* Number of VM guests in d2fc buffer */ 2418c2ecf20Sopenharmony_ci char reserved[30]; 2428c2ecf20Sopenharmony_ci} __attribute__ ((packed)); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistruct dbfs_d2fc { 2458c2ecf20Sopenharmony_ci struct dbfs_d2fc_hdr hdr; /* 64 byte header */ 2468c2ecf20Sopenharmony_ci char buf[]; /* d2fc buffer */ 2478c2ecf20Sopenharmony_ci} __attribute__ ((packed)); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic int dbfs_diag2fc_create(void **data, void **data_free_ptr, size_t *size) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci struct dbfs_d2fc *d2fc; 2528c2ecf20Sopenharmony_ci unsigned int count; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci d2fc = diag2fc_store(guest_query, &count, sizeof(d2fc->hdr)); 2558c2ecf20Sopenharmony_ci if (IS_ERR(d2fc)) 2568c2ecf20Sopenharmony_ci return PTR_ERR(d2fc); 2578c2ecf20Sopenharmony_ci get_tod_clock_ext(d2fc->hdr.tod_ext); 2588c2ecf20Sopenharmony_ci d2fc->hdr.len = count * sizeof(struct diag2fc_data); 2598c2ecf20Sopenharmony_ci d2fc->hdr.version = DBFS_D2FC_HDR_VERSION; 2608c2ecf20Sopenharmony_ci d2fc->hdr.count = count; 2618c2ecf20Sopenharmony_ci memset(&d2fc->hdr.reserved, 0, sizeof(d2fc->hdr.reserved)); 2628c2ecf20Sopenharmony_ci *data = d2fc; 2638c2ecf20Sopenharmony_ci *data_free_ptr = d2fc; 2648c2ecf20Sopenharmony_ci *size = d2fc->hdr.len + sizeof(struct dbfs_d2fc_hdr); 2658c2ecf20Sopenharmony_ci return 0; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic struct hypfs_dbfs_file dbfs_file_2fc = { 2698c2ecf20Sopenharmony_ci .name = "diag_2fc", 2708c2ecf20Sopenharmony_ci .data_create = dbfs_diag2fc_create, 2718c2ecf20Sopenharmony_ci .data_free = diag2fc_free, 2728c2ecf20Sopenharmony_ci}; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ciint hypfs_vm_init(void) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci if (!MACHINE_IS_VM) 2778c2ecf20Sopenharmony_ci return 0; 2788c2ecf20Sopenharmony_ci if (diag2fc(0, all_guests, NULL) > 0) 2798c2ecf20Sopenharmony_ci guest_query = all_guests; 2808c2ecf20Sopenharmony_ci else if (diag2fc(0, local_guest, NULL) > 0) 2818c2ecf20Sopenharmony_ci guest_query = local_guest; 2828c2ecf20Sopenharmony_ci else 2838c2ecf20Sopenharmony_ci return -EACCES; 2848c2ecf20Sopenharmony_ci hypfs_dbfs_create_file(&dbfs_file_2fc); 2858c2ecf20Sopenharmony_ci return 0; 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_civoid hypfs_vm_exit(void) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci if (!MACHINE_IS_VM) 2918c2ecf20Sopenharmony_ci return; 2928c2ecf20Sopenharmony_ci hypfs_dbfs_remove_file(&dbfs_file_2fc); 2938c2ecf20Sopenharmony_ci} 294