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 "hypfs.h"
14
15#define NAME_LEN 8
16
17static char local_guest[] = "        ";
18static char all_guests[] = "*       ";
19static char *guest_query;
20
21struct diag2fc_data {
22	__u32 version;
23	__u32 flags;
24	__u64 used_cpu;
25	__u64 el_time;
26	__u64 mem_min_kb;
27	__u64 mem_max_kb;
28	__u64 mem_share_kb;
29	__u64 mem_used_kb;
30	__u32 pcpus;
31	__u32 lcpus;
32	__u32 vcpus;
33	__u32 cpu_min;
34	__u32 cpu_max;
35	__u32 cpu_shares;
36	__u32 cpu_use_samp;
37	__u32 cpu_delay_samp;
38	__u32 page_wait_samp;
39	__u32 idle_samp;
40	__u32 other_samp;
41	__u32 total_samp;
42	char  guest_name[NAME_LEN];
43};
44
45struct diag2fc_parm_list {
46	char userid[NAME_LEN];
47	char aci_grp[NAME_LEN];
48	__u64 addr;
49	__u32 size;
50	__u32 fmt;
51};
52
53static int diag2fc(int size, char* query, void *addr)
54{
55	unsigned long residual_cnt;
56	unsigned long rc;
57	struct diag2fc_parm_list parm_list;
58
59	memcpy(parm_list.userid, query, NAME_LEN);
60	ASCEBC(parm_list.userid, NAME_LEN);
61	parm_list.addr = (unsigned long) addr ;
62	parm_list.size = size;
63	parm_list.fmt = 0x02;
64	memset(parm_list.aci_grp, 0x40, NAME_LEN);
65	rc = -1;
66
67	asm volatile(
68		"	diag    %0,%1,0x2fc\n"
69		"0:\n"
70		EX_TABLE(0b,0b)
71		: "=d" (residual_cnt), "+d" (rc) : "0" (&parm_list) : "memory");
72
73	if ((rc != 0 ) && (rc != -2))
74		return rc;
75	else
76		return -residual_cnt;
77}
78
79static struct diag2fc_data *diag2fc_store(char *query, int *count)
80{
81	int size;
82	struct diag2fc_data *data;
83
84	do {
85		size = diag2fc(0, query, NULL);
86		if (size < 0)
87			return ERR_PTR(-EACCES);
88		data = vmalloc(size);
89		if (!data)
90			return ERR_PTR(-ENOMEM);
91		if (diag2fc(size, query, data) == 0)
92			break;
93		vfree(data);
94	} while (1);
95	*count = (size / sizeof(*data));
96
97	return data;
98}
99
100static void diag2fc_free(void *data)
101{
102	vfree(data);
103}
104
105#define ATTRIBUTE(sb, dir, name, member) \
106do { \
107	void *rc; \
108	rc = hypfs_create_u64(sb, dir, name, member); \
109	if (IS_ERR(rc)) \
110		return PTR_ERR(rc); \
111} while(0)
112
113static int hpyfs_vm_create_guest(struct super_block *sb,
114				 struct dentry *systems_dir,
115				 struct diag2fc_data *data)
116{
117	char guest_name[NAME_LEN + 1] = {};
118	struct dentry *guest_dir, *cpus_dir, *samples_dir, *mem_dir;
119	int dedicated_flag, capped_value;
120
121	capped_value = (data->flags & 0x00000006) >> 1;
122	dedicated_flag = (data->flags & 0x00000008) >> 3;
123
124	/* guest dir */
125	memcpy(guest_name, data->guest_name, NAME_LEN);
126	EBCASC(guest_name, NAME_LEN);
127	strstrip(guest_name);
128	guest_dir = hypfs_mkdir(sb, systems_dir, guest_name);
129	if (IS_ERR(guest_dir))
130		return PTR_ERR(guest_dir);
131	ATTRIBUTE(sb, guest_dir, "onlinetime_us", data->el_time);
132
133	/* logical cpu information */
134	cpus_dir = hypfs_mkdir(sb, guest_dir, "cpus");
135	if (IS_ERR(cpus_dir))
136		return PTR_ERR(cpus_dir);
137	ATTRIBUTE(sb, cpus_dir, "cputime_us", data->used_cpu);
138	ATTRIBUTE(sb, cpus_dir, "capped", capped_value);
139	ATTRIBUTE(sb, cpus_dir, "dedicated", dedicated_flag);
140	ATTRIBUTE(sb, cpus_dir, "count", data->vcpus);
141	ATTRIBUTE(sb, cpus_dir, "weight_min", data->cpu_min);
142	ATTRIBUTE(sb, cpus_dir, "weight_max", data->cpu_max);
143	ATTRIBUTE(sb, cpus_dir, "weight_cur", data->cpu_shares);
144
145	/* memory information */
146	mem_dir = hypfs_mkdir(sb, guest_dir, "mem");
147	if (IS_ERR(mem_dir))
148		return PTR_ERR(mem_dir);
149	ATTRIBUTE(sb, mem_dir, "min_KiB", data->mem_min_kb);
150	ATTRIBUTE(sb, mem_dir, "max_KiB", data->mem_max_kb);
151	ATTRIBUTE(sb, mem_dir, "used_KiB", data->mem_used_kb);
152	ATTRIBUTE(sb, mem_dir, "share_KiB", data->mem_share_kb);
153
154	/* samples */
155	samples_dir = hypfs_mkdir(sb, guest_dir, "samples");
156	if (IS_ERR(samples_dir))
157		return PTR_ERR(samples_dir);
158	ATTRIBUTE(sb, samples_dir, "cpu_using", data->cpu_use_samp);
159	ATTRIBUTE(sb, samples_dir, "cpu_delay", data->cpu_delay_samp);
160	ATTRIBUTE(sb, samples_dir, "mem_delay", data->page_wait_samp);
161	ATTRIBUTE(sb, samples_dir, "idle", data->idle_samp);
162	ATTRIBUTE(sb, samples_dir, "other", data->other_samp);
163	ATTRIBUTE(sb, samples_dir, "total", data->total_samp);
164	return 0;
165}
166
167int hypfs_vm_create_files(struct super_block *sb, struct dentry *root)
168{
169	struct dentry *dir, *file;
170	struct diag2fc_data *data;
171	int rc, i, count = 0;
172
173	data = diag2fc_store(guest_query, &count);
174	if (IS_ERR(data))
175		return PTR_ERR(data);
176
177	/* Hpervisor Info */
178	dir = hypfs_mkdir(sb, root, "hyp");
179	if (IS_ERR(dir)) {
180		rc = PTR_ERR(dir);
181		goto failed;
182	}
183	file = hypfs_create_str(sb, dir, "type", "z/VM Hypervisor");
184	if (IS_ERR(file)) {
185		rc = PTR_ERR(file);
186		goto failed;
187	}
188
189	/* physical cpus */
190	dir = hypfs_mkdir(sb, root, "cpus");
191	if (IS_ERR(dir)) {
192		rc = PTR_ERR(dir);
193		goto failed;
194	}
195	file = hypfs_create_u64(sb, dir, "count", data->lcpus);
196	if (IS_ERR(file)) {
197		rc = PTR_ERR(file);
198		goto failed;
199	}
200
201	/* guests */
202	dir = hypfs_mkdir(sb, root, "systems");
203	if (IS_ERR(dir)) {
204		rc = PTR_ERR(dir);
205		goto failed;
206	}
207
208	for (i = 0; i < count; i++) {
209		rc = hpyfs_vm_create_guest(sb, dir, &(data[i]));
210		if (rc)
211			goto failed;
212	}
213	diag2fc_free(data);
214	return 0;
215
216failed:
217	diag2fc_free(data);
218	return rc;
219}
220
221int hypfs_vm_init(void)
222{
223	if (diag2fc(0, all_guests, NULL) > 0)
224		guest_query = all_guests;
225	else if (diag2fc(0, local_guest, NULL) > 0)
226		guest_query = local_guest;
227	else
228		return -EACCES;
229
230	return 0;
231}
232