]>
Commit | Line | Data |
---|---|---|
31cb4bd3 MH |
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> | |
57b28f66 | 13 | #include <asm/timex.h> |
31cb4bd3 MH |
14 | #include "hypfs.h" |
15 | ||
16 | #define NAME_LEN 8 | |
57b28f66 | 17 | #define DBFS_D2FC_HDR_VERSION 0 |
31cb4bd3 MH |
18 | |
19 | static char local_guest[] = " "; | |
20 | static char all_guests[] = "* "; | |
21 | static char *guest_query; | |
22 | ||
57b28f66 MH |
23 | static struct dentry *dbfs_d2fc_file; |
24 | ||
31cb4bd3 MH |
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 | ||
57b28f66 MH |
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) | |
31cb4bd3 | 87 | { |
57b28f66 | 88 | void *data; |
31cb4bd3 | 89 | int size; |
31cb4bd3 MH |
90 | |
91 | do { | |
92 | size = diag2fc(0, query, NULL); | |
93 | if (size < 0) | |
94 | return ERR_PTR(-EACCES); | |
57b28f66 | 95 | data = vmalloc(size + offset); |
31cb4bd3 MH |
96 | if (!data) |
97 | return ERR_PTR(-ENOMEM); | |
57b28f66 | 98 | if (diag2fc(size, query, data + offset) == 0) |
31cb4bd3 MH |
99 | break; |
100 | vfree(data); | |
101 | } while (1); | |
57b28f66 | 102 | *count = (size / sizeof(struct diag2fc_data)); |
31cb4bd3 MH |
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); | |
1d802e24 | 134 | strim(guest_name); |
31cb4bd3 MH |
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; | |
57b28f66 MH |
178 | unsigned int count = 0; |
179 | int rc, i; | |
31cb4bd3 | 180 | |
57b28f66 | 181 | data = diag2fc_store(guest_query, &count, 0); |
31cb4bd3 MH |
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 | ||
57b28f66 MH |
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, | |
6038f373 | 278 | .llseek = no_llseek, |
57b28f66 MH |
279 | }; |
280 | ||
31cb4bd3 MH |
281 | int hypfs_vm_init(void) |
282 | { | |
57b28f66 MH |
283 | if (!MACHINE_IS_VM) |
284 | return 0; | |
31cb4bd3 MH |
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 | ||
57b28f66 MH |
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 | ||
31cb4bd3 MH |
297 | return 0; |
298 | } | |
57b28f66 MH |
299 | |
300 | void hypfs_vm_exit(void) | |
301 | { | |
302 | if (!MACHINE_IS_VM) | |
303 | return; | |
304 | debugfs_remove(dbfs_d2fc_file); | |
305 | } |