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