Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/s390/hypfs/hypfs_diag_fs.c
53158 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* Hypervisor filesystem for Linux on s390. Diag 204 and 224
4
* implementation.
5
*
6
* Copyright IBM Corp. 2006, 2008
7
* Author(s): Michael Holzheu <[email protected]>
8
*/
9
10
#define pr_fmt(fmt) "hypfs: " fmt
11
12
#include <linux/types.h>
13
#include <linux/errno.h>
14
#include <linux/slab.h>
15
#include <linux/string.h>
16
#include <linux/vmalloc.h>
17
#include <linux/mm.h>
18
#include <asm/machine.h>
19
#include <asm/diag.h>
20
#include <asm/ebcdic.h>
21
#include "hypfs_diag.h"
22
#include "hypfs.h"
23
24
#define TMP_SIZE 64 /* size of temporary buffers */
25
26
static char *diag224_cpu_names; /* diag 224 name table */
27
static int diag224_idx2name(int index, char *name);
28
29
/*
30
* DIAG 204 member access functions.
31
*
32
* Since we have two different diag 204 data formats for old and new s390
33
* machines, we do not access the structs directly, but use getter functions for
34
* each struct member instead. This should make the code more readable.
35
*/
36
37
/* Time information block */
38
39
static inline int info_blk_hdr__size(enum diag204_format type)
40
{
41
if (type == DIAG204_INFO_SIMPLE)
42
return sizeof(struct diag204_info_blk_hdr);
43
else /* DIAG204_INFO_EXT */
44
return sizeof(struct diag204_x_info_blk_hdr);
45
}
46
47
static inline __u8 info_blk_hdr__npar(enum diag204_format type, void *hdr)
48
{
49
if (type == DIAG204_INFO_SIMPLE)
50
return ((struct diag204_info_blk_hdr *)hdr)->npar;
51
else /* DIAG204_INFO_EXT */
52
return ((struct diag204_x_info_blk_hdr *)hdr)->npar;
53
}
54
55
static inline __u8 info_blk_hdr__flags(enum diag204_format type, void *hdr)
56
{
57
if (type == DIAG204_INFO_SIMPLE)
58
return ((struct diag204_info_blk_hdr *)hdr)->flags;
59
else /* DIAG204_INFO_EXT */
60
return ((struct diag204_x_info_blk_hdr *)hdr)->flags;
61
}
62
63
/* Partition header */
64
65
static inline int part_hdr__size(enum diag204_format type)
66
{
67
if (type == DIAG204_INFO_SIMPLE)
68
return sizeof(struct diag204_part_hdr);
69
else /* DIAG204_INFO_EXT */
70
return sizeof(struct diag204_x_part_hdr);
71
}
72
73
static inline __u8 part_hdr__rcpus(enum diag204_format type, void *hdr)
74
{
75
if (type == DIAG204_INFO_SIMPLE)
76
return ((struct diag204_part_hdr *)hdr)->cpus;
77
else /* DIAG204_INFO_EXT */
78
return ((struct diag204_x_part_hdr *)hdr)->rcpus;
79
}
80
81
static inline void part_hdr__part_name(enum diag204_format type, void *hdr,
82
char *name)
83
{
84
if (type == DIAG204_INFO_SIMPLE)
85
memcpy(name, ((struct diag204_part_hdr *)hdr)->part_name,
86
DIAG204_LPAR_NAME_LEN);
87
else /* DIAG204_INFO_EXT */
88
memcpy(name, ((struct diag204_x_part_hdr *)hdr)->part_name,
89
DIAG204_LPAR_NAME_LEN);
90
EBCASC(name, DIAG204_LPAR_NAME_LEN);
91
name[DIAG204_LPAR_NAME_LEN] = 0;
92
strim(name);
93
}
94
95
/* CPU info block */
96
97
static inline int cpu_info__size(enum diag204_format type)
98
{
99
if (type == DIAG204_INFO_SIMPLE)
100
return sizeof(struct diag204_cpu_info);
101
else /* DIAG204_INFO_EXT */
102
return sizeof(struct diag204_x_cpu_info);
103
}
104
105
static inline __u8 cpu_info__ctidx(enum diag204_format type, void *hdr)
106
{
107
if (type == DIAG204_INFO_SIMPLE)
108
return ((struct diag204_cpu_info *)hdr)->ctidx;
109
else /* DIAG204_INFO_EXT */
110
return ((struct diag204_x_cpu_info *)hdr)->ctidx;
111
}
112
113
static inline __u16 cpu_info__cpu_addr(enum diag204_format type, void *hdr)
114
{
115
if (type == DIAG204_INFO_SIMPLE)
116
return ((struct diag204_cpu_info *)hdr)->cpu_addr;
117
else /* DIAG204_INFO_EXT */
118
return ((struct diag204_x_cpu_info *)hdr)->cpu_addr;
119
}
120
121
static inline __u64 cpu_info__acc_time(enum diag204_format type, void *hdr)
122
{
123
if (type == DIAG204_INFO_SIMPLE)
124
return ((struct diag204_cpu_info *)hdr)->acc_time;
125
else /* DIAG204_INFO_EXT */
126
return ((struct diag204_x_cpu_info *)hdr)->acc_time;
127
}
128
129
static inline __u64 cpu_info__lp_time(enum diag204_format type, void *hdr)
130
{
131
if (type == DIAG204_INFO_SIMPLE)
132
return ((struct diag204_cpu_info *)hdr)->lp_time;
133
else /* DIAG204_INFO_EXT */
134
return ((struct diag204_x_cpu_info *)hdr)->lp_time;
135
}
136
137
static inline __u64 cpu_info__online_time(enum diag204_format type, void *hdr)
138
{
139
if (type == DIAG204_INFO_SIMPLE)
140
return 0; /* online_time not available in simple info */
141
else /* DIAG204_INFO_EXT */
142
return ((struct diag204_x_cpu_info *)hdr)->online_time;
143
}
144
145
/* Physical header */
146
147
static inline int phys_hdr__size(enum diag204_format type)
148
{
149
if (type == DIAG204_INFO_SIMPLE)
150
return sizeof(struct diag204_phys_hdr);
151
else /* DIAG204_INFO_EXT */
152
return sizeof(struct diag204_x_phys_hdr);
153
}
154
155
static inline __u8 phys_hdr__cpus(enum diag204_format type, void *hdr)
156
{
157
if (type == DIAG204_INFO_SIMPLE)
158
return ((struct diag204_phys_hdr *)hdr)->cpus;
159
else /* DIAG204_INFO_EXT */
160
return ((struct diag204_x_phys_hdr *)hdr)->cpus;
161
}
162
163
/* Physical CPU info block */
164
165
static inline int phys_cpu__size(enum diag204_format type)
166
{
167
if (type == DIAG204_INFO_SIMPLE)
168
return sizeof(struct diag204_phys_cpu);
169
else /* DIAG204_INFO_EXT */
170
return sizeof(struct diag204_x_phys_cpu);
171
}
172
173
static inline __u16 phys_cpu__cpu_addr(enum diag204_format type, void *hdr)
174
{
175
if (type == DIAG204_INFO_SIMPLE)
176
return ((struct diag204_phys_cpu *)hdr)->cpu_addr;
177
else /* DIAG204_INFO_EXT */
178
return ((struct diag204_x_phys_cpu *)hdr)->cpu_addr;
179
}
180
181
static inline __u64 phys_cpu__mgm_time(enum diag204_format type, void *hdr)
182
{
183
if (type == DIAG204_INFO_SIMPLE)
184
return ((struct diag204_phys_cpu *)hdr)->mgm_time;
185
else /* DIAG204_INFO_EXT */
186
return ((struct diag204_x_phys_cpu *)hdr)->mgm_time;
187
}
188
189
static inline __u64 phys_cpu__ctidx(enum diag204_format type, void *hdr)
190
{
191
if (type == DIAG204_INFO_SIMPLE)
192
return ((struct diag204_phys_cpu *)hdr)->ctidx;
193
else /* DIAG204_INFO_EXT */
194
return ((struct diag204_x_phys_cpu *)hdr)->ctidx;
195
}
196
197
/*
198
* Functions to create the directory structure
199
* *******************************************
200
*/
201
202
static int hypfs_create_cpu_files(struct dentry *cpus_dir, void *cpu_info)
203
{
204
struct dentry *cpu_dir;
205
char buffer[TMP_SIZE];
206
int rc;
207
208
snprintf(buffer, TMP_SIZE, "%d", cpu_info__cpu_addr(diag204_get_info_type(),
209
cpu_info));
210
cpu_dir = hypfs_mkdir(cpus_dir, buffer);
211
if (IS_ERR(cpu_dir))
212
return PTR_ERR(cpu_dir);
213
rc = hypfs_create_u64(cpu_dir, "mgmtime",
214
cpu_info__acc_time(diag204_get_info_type(), cpu_info) -
215
cpu_info__lp_time(diag204_get_info_type(), cpu_info));
216
if (rc)
217
return rc;
218
rc = hypfs_create_u64(cpu_dir, "cputime",
219
cpu_info__lp_time(diag204_get_info_type(), cpu_info));
220
if (rc)
221
return rc;
222
if (diag204_get_info_type() == DIAG204_INFO_EXT) {
223
rc = hypfs_create_u64(cpu_dir, "onlinetime",
224
cpu_info__online_time(diag204_get_info_type(),
225
cpu_info));
226
if (rc)
227
return rc;
228
}
229
diag224_idx2name(cpu_info__ctidx(diag204_get_info_type(), cpu_info), buffer);
230
return hypfs_create_str(cpu_dir, "type", buffer);
231
}
232
233
static void *hypfs_create_lpar_files(struct dentry *systems_dir, void *part_hdr)
234
{
235
struct dentry *cpus_dir;
236
struct dentry *lpar_dir;
237
char lpar_name[DIAG204_LPAR_NAME_LEN + 1];
238
void *cpu_info;
239
int i;
240
241
part_hdr__part_name(diag204_get_info_type(), part_hdr, lpar_name);
242
lpar_name[DIAG204_LPAR_NAME_LEN] = 0;
243
lpar_dir = hypfs_mkdir(systems_dir, lpar_name);
244
if (IS_ERR(lpar_dir))
245
return lpar_dir;
246
cpus_dir = hypfs_mkdir(lpar_dir, "cpus");
247
if (IS_ERR(cpus_dir))
248
return cpus_dir;
249
cpu_info = part_hdr + part_hdr__size(diag204_get_info_type());
250
for (i = 0; i < part_hdr__rcpus(diag204_get_info_type(), part_hdr); i++) {
251
int rc;
252
253
rc = hypfs_create_cpu_files(cpus_dir, cpu_info);
254
if (rc)
255
return ERR_PTR(rc);
256
cpu_info += cpu_info__size(diag204_get_info_type());
257
}
258
return cpu_info;
259
}
260
261
static int hypfs_create_phys_cpu_files(struct dentry *cpus_dir, void *cpu_info)
262
{
263
struct dentry *cpu_dir;
264
char buffer[TMP_SIZE];
265
int rc;
266
267
snprintf(buffer, TMP_SIZE, "%i", phys_cpu__cpu_addr(diag204_get_info_type(),
268
cpu_info));
269
cpu_dir = hypfs_mkdir(cpus_dir, buffer);
270
if (IS_ERR(cpu_dir))
271
return PTR_ERR(cpu_dir);
272
rc = hypfs_create_u64(cpu_dir, "mgmtime",
273
phys_cpu__mgm_time(diag204_get_info_type(), cpu_info));
274
if (rc)
275
return rc;
276
diag224_idx2name(phys_cpu__ctidx(diag204_get_info_type(), cpu_info), buffer);
277
return hypfs_create_str(cpu_dir, "type", buffer);
278
}
279
280
static void *hypfs_create_phys_files(struct dentry *parent_dir, void *phys_hdr)
281
{
282
int i;
283
void *cpu_info;
284
struct dentry *cpus_dir;
285
286
cpus_dir = hypfs_mkdir(parent_dir, "cpus");
287
if (IS_ERR(cpus_dir))
288
return cpus_dir;
289
cpu_info = phys_hdr + phys_hdr__size(diag204_get_info_type());
290
for (i = 0; i < phys_hdr__cpus(diag204_get_info_type(), phys_hdr); i++) {
291
int rc;
292
293
rc = hypfs_create_phys_cpu_files(cpus_dir, cpu_info);
294
if (rc)
295
return ERR_PTR(rc);
296
cpu_info += phys_cpu__size(diag204_get_info_type());
297
}
298
return cpu_info;
299
}
300
301
int hypfs_diag_create_files(struct dentry *root)
302
{
303
struct dentry *systems_dir, *hyp_dir;
304
void *time_hdr, *part_hdr;
305
void *buffer, *ptr;
306
int i, rc, pages;
307
308
buffer = diag204_get_buffer(diag204_get_info_type(), &pages);
309
if (IS_ERR(buffer))
310
return PTR_ERR(buffer);
311
rc = diag204_store(buffer, pages);
312
if (rc)
313
return rc;
314
315
systems_dir = hypfs_mkdir(root, "systems");
316
if (IS_ERR(systems_dir))
317
return PTR_ERR(systems_dir);
318
time_hdr = (struct x_info_blk_hdr *)buffer;
319
part_hdr = time_hdr + info_blk_hdr__size(diag204_get_info_type());
320
for (i = 0; i < info_blk_hdr__npar(diag204_get_info_type(), time_hdr); i++) {
321
part_hdr = hypfs_create_lpar_files(systems_dir, part_hdr);
322
if (IS_ERR(part_hdr))
323
return PTR_ERR(part_hdr);
324
}
325
if (info_blk_hdr__flags(diag204_get_info_type(), time_hdr) &
326
DIAG204_LPAR_PHYS_FLG) {
327
ptr = hypfs_create_phys_files(root, part_hdr);
328
if (IS_ERR(ptr))
329
return PTR_ERR(ptr);
330
}
331
hyp_dir = hypfs_mkdir(root, "hyp");
332
if (IS_ERR(hyp_dir))
333
return PTR_ERR(hyp_dir);
334
return hypfs_create_str(hyp_dir, "type", "LPAR Hypervisor");
335
}
336
337
/* Diagnose 224 functions */
338
339
static int diag224_idx2name(int index, char *name)
340
{
341
memcpy(name, diag224_cpu_names + ((index + 1) * DIAG204_CPU_NAME_LEN),
342
DIAG204_CPU_NAME_LEN);
343
name[DIAG204_CPU_NAME_LEN] = 0;
344
strim(name);
345
return 0;
346
}
347
348
static int diag224_get_name_table(void)
349
{
350
/* memory must be below 2GB */
351
diag224_cpu_names = (char *)__get_free_page(GFP_KERNEL | GFP_DMA);
352
if (!diag224_cpu_names)
353
return -ENOMEM;
354
if (diag224(diag224_cpu_names)) {
355
free_page((unsigned long)diag224_cpu_names);
356
return -EOPNOTSUPP;
357
}
358
EBCASC(diag224_cpu_names + 16, (*diag224_cpu_names + 1) * 16);
359
return 0;
360
}
361
362
static void diag224_delete_name_table(void)
363
{
364
free_page((unsigned long)diag224_cpu_names);
365
}
366
367
int __init __hypfs_diag_fs_init(void)
368
{
369
if (machine_is_lpar())
370
return diag224_get_name_table();
371
return 0;
372
}
373
374
void __hypfs_diag_fs_exit(void)
375
{
376
diag224_delete_name_table();
377
}
378
379