1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Hypervisor filesystem for Linux on s390. z/VM implementation. 4 * 5 * Copyright IBM Corp. 2006 6 * Author(s): Michael Holzheu <holzheu@de.ibm.com> 7 */ 8 9 #include <linux/types.h> 10 #include <linux/errno.h> 11 #include <linux/string.h> 12 #include <linux/vmalloc.h> 13 #include <asm/diag.h> 14 #include <asm/ebcdic.h> 15 #include <asm/timex.h> 16 #include "hypfs.h" 17 18 #define NAME_LEN 8 19 #define DBFS_D2FC_HDR_VERSION 0 20 21 static char local_guest[] = " "; 22 static char all_guests[] = "* "; 23 static char *all_groups = all_guests; 24 static char *guest_query; 25 26 struct diag2fc_data { 27 __u32 version; 28 __u32 flags; 29 __u64 used_cpu; 30 __u64 el_time; 31 __u64 mem_min_kb; 32 __u64 mem_max_kb; 33 __u64 mem_share_kb; 34 __u64 mem_used_kb; 35 __u32 pcpus; 36 __u32 lcpus; 37 __u32 vcpus; 38 __u32 ocpus; 39 __u32 cpu_max; 40 __u32 cpu_shares; 41 __u32 cpu_use_samp; 42 __u32 cpu_delay_samp; 43 __u32 page_wait_samp; 44 __u32 idle_samp; 45 __u32 other_samp; 46 __u32 total_samp; 47 char guest_name[NAME_LEN]; 48 }; 49 50 struct diag2fc_parm_list { 51 char userid[NAME_LEN]; 52 char aci_grp[NAME_LEN]; 53 __u64 addr; 54 __u32 size; 55 __u32 fmt; 56 }; 57 58 static int diag2fc(int size, char* query, void *addr) 59 { 60 unsigned long residual_cnt; 61 unsigned long rc; 62 struct diag2fc_parm_list parm_list; 63 64 memcpy(parm_list.userid, query, NAME_LEN); 65 ASCEBC(parm_list.userid, NAME_LEN); 66 memcpy(parm_list.aci_grp, all_groups, NAME_LEN); 67 ASCEBC(parm_list.aci_grp, NAME_LEN); 68 parm_list.addr = (unsigned long)addr; 69 parm_list.size = size; 70 parm_list.fmt = 0x02; 71 rc = -1; 72 73 diag_stat_inc(DIAG_STAT_X2FC); 74 asm volatile( 75 " diag %0,%1,0x2fc\n" 76 "0: nopr %%r7\n" 77 EX_TABLE(0b,0b) 78 : "=d" (residual_cnt), "+d" (rc) : "0" (&parm_list) : "memory"); 79 80 if ((rc != 0 ) && (rc != -2)) 81 return rc; 82 else 83 return -residual_cnt; 84 } 85 86 /* 87 * Allocate buffer for "query" and store diag 2fc at "offset" 88 */ 89 static void *diag2fc_store(char *query, unsigned int *count, int offset) 90 { 91 void *data; 92 int size; 93 94 do { 95 size = diag2fc(0, query, NULL); 96 if (size < 0) 97 return ERR_PTR(-EACCES); 98 data = vmalloc(size + offset); 99 if (!data) 100 return ERR_PTR(-ENOMEM); 101 if (diag2fc(size, query, data + offset) == 0) 102 break; 103 vfree(data); 104 } while (1); 105 *count = (size / sizeof(struct diag2fc_data)); 106 107 return data; 108 } 109 110 static void diag2fc_free(const void *data) 111 { 112 vfree(data); 113 } 114 115 #define ATTRIBUTE(dir, name, member) \ 116 do { \ 117 void *rc; \ 118 rc = hypfs_create_u64(dir, name, member); \ 119 if (IS_ERR(rc)) \ 120 return PTR_ERR(rc); \ 121 } while(0) 122 123 static int hypfs_vm_create_guest(struct dentry *systems_dir, 124 struct diag2fc_data *data) 125 { 126 char guest_name[NAME_LEN + 1] = {}; 127 struct dentry *guest_dir, *cpus_dir, *samples_dir, *mem_dir; 128 int dedicated_flag, capped_value; 129 130 capped_value = (data->flags & 0x00000006) >> 1; 131 dedicated_flag = (data->flags & 0x00000008) >> 3; 132 133 /* guest dir */ 134 memcpy(guest_name, data->guest_name, NAME_LEN); 135 EBCASC(guest_name, NAME_LEN); 136 strim(guest_name); 137 guest_dir = hypfs_mkdir(systems_dir, guest_name); 138 if (IS_ERR(guest_dir)) 139 return PTR_ERR(guest_dir); 140 ATTRIBUTE(guest_dir, "onlinetime_us", data->el_time); 141 142 /* logical cpu information */ 143 cpus_dir = hypfs_mkdir(guest_dir, "cpus"); 144 if (IS_ERR(cpus_dir)) 145 return PTR_ERR(cpus_dir); 146 ATTRIBUTE(cpus_dir, "cputime_us", data->used_cpu); 147 ATTRIBUTE(cpus_dir, "capped", capped_value); 148 ATTRIBUTE(cpus_dir, "dedicated", dedicated_flag); 149 ATTRIBUTE(cpus_dir, "count", data->vcpus); 150 /* 151 * Note: The "weight_min" attribute got the wrong name. 152 * The value represents the number of non-stopped (operating) 153 * CPUS. 154 */ 155 ATTRIBUTE(cpus_dir, "weight_min", data->ocpus); 156 ATTRIBUTE(cpus_dir, "weight_max", data->cpu_max); 157 ATTRIBUTE(cpus_dir, "weight_cur", data->cpu_shares); 158 159 /* memory information */ 160 mem_dir = hypfs_mkdir(guest_dir, "mem"); 161 if (IS_ERR(mem_dir)) 162 return PTR_ERR(mem_dir); 163 ATTRIBUTE(mem_dir, "min_KiB", data->mem_min_kb); 164 ATTRIBUTE(mem_dir, "max_KiB", data->mem_max_kb); 165 ATTRIBUTE(mem_dir, "used_KiB", data->mem_used_kb); 166 ATTRIBUTE(mem_dir, "share_KiB", data->mem_share_kb); 167 168 /* samples */ 169 samples_dir = hypfs_mkdir(guest_dir, "samples"); 170 if (IS_ERR(samples_dir)) 171 return PTR_ERR(samples_dir); 172 ATTRIBUTE(samples_dir, "cpu_using", data->cpu_use_samp); 173 ATTRIBUTE(samples_dir, "cpu_delay", data->cpu_delay_samp); 174 ATTRIBUTE(samples_dir, "mem_delay", data->page_wait_samp); 175 ATTRIBUTE(samples_dir, "idle", data->idle_samp); 176 ATTRIBUTE(samples_dir, "other", data->other_samp); 177 ATTRIBUTE(samples_dir, "total", data->total_samp); 178 return 0; 179 } 180 181 int hypfs_vm_create_files(struct dentry *root) 182 { 183 struct dentry *dir, *file; 184 struct diag2fc_data *data; 185 unsigned int count = 0; 186 int rc, i; 187 188 data = diag2fc_store(guest_query, &count, 0); 189 if (IS_ERR(data)) 190 return PTR_ERR(data); 191 192 /* Hpervisor Info */ 193 dir = hypfs_mkdir(root, "hyp"); 194 if (IS_ERR(dir)) { 195 rc = PTR_ERR(dir); 196 goto failed; 197 } 198 file = hypfs_create_str(dir, "type", "z/VM Hypervisor"); 199 if (IS_ERR(file)) { 200 rc = PTR_ERR(file); 201 goto failed; 202 } 203 204 /* physical cpus */ 205 dir = hypfs_mkdir(root, "cpus"); 206 if (IS_ERR(dir)) { 207 rc = PTR_ERR(dir); 208 goto failed; 209 } 210 file = hypfs_create_u64(dir, "count", data->lcpus); 211 if (IS_ERR(file)) { 212 rc = PTR_ERR(file); 213 goto failed; 214 } 215 216 /* guests */ 217 dir = hypfs_mkdir(root, "systems"); 218 if (IS_ERR(dir)) { 219 rc = PTR_ERR(dir); 220 goto failed; 221 } 222 223 for (i = 0; i < count; i++) { 224 rc = hypfs_vm_create_guest(dir, &(data[i])); 225 if (rc) 226 goto failed; 227 } 228 diag2fc_free(data); 229 return 0; 230 231 failed: 232 diag2fc_free(data); 233 return rc; 234 } 235 236 struct dbfs_d2fc_hdr { 237 u64 len; /* Length of d2fc buffer without header */ 238 u16 version; /* Version of header */ 239 union tod_clock tod_ext; /* TOD clock for d2fc */ 240 u64 count; /* Number of VM guests in d2fc buffer */ 241 char reserved[30]; 242 } __attribute__ ((packed)); 243 244 struct dbfs_d2fc { 245 struct dbfs_d2fc_hdr hdr; /* 64 byte header */ 246 char buf[]; /* d2fc buffer */ 247 } __attribute__ ((packed)); 248 249 static int dbfs_diag2fc_create(void **data, void **data_free_ptr, size_t *size) 250 { 251 struct dbfs_d2fc *d2fc; 252 unsigned int count; 253 254 d2fc = diag2fc_store(guest_query, &count, sizeof(d2fc->hdr)); 255 if (IS_ERR(d2fc)) 256 return PTR_ERR(d2fc); 257 store_tod_clock_ext(&d2fc->hdr.tod_ext); 258 d2fc->hdr.len = count * sizeof(struct diag2fc_data); 259 d2fc->hdr.version = DBFS_D2FC_HDR_VERSION; 260 d2fc->hdr.count = count; 261 memset(&d2fc->hdr.reserved, 0, sizeof(d2fc->hdr.reserved)); 262 *data = d2fc; 263 *data_free_ptr = d2fc; 264 *size = d2fc->hdr.len + sizeof(struct dbfs_d2fc_hdr); 265 return 0; 266 } 267 268 static struct hypfs_dbfs_file dbfs_file_2fc = { 269 .name = "diag_2fc", 270 .data_create = dbfs_diag2fc_create, 271 .data_free = diag2fc_free, 272 }; 273 274 int hypfs_vm_init(void) 275 { 276 if (!MACHINE_IS_VM) 277 return 0; 278 if (diag2fc(0, all_guests, NULL) > 0) 279 guest_query = all_guests; 280 else if (diag2fc(0, local_guest, NULL) > 0) 281 guest_query = local_guest; 282 else 283 return -EACCES; 284 hypfs_dbfs_create_file(&dbfs_file_2fc); 285 return 0; 286 } 287 288 void hypfs_vm_exit(void) 289 { 290 if (!MACHINE_IS_VM) 291 return; 292 hypfs_dbfs_remove_file(&dbfs_file_2fc); 293 } 294