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