aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390/hypfs/inode.c
diff options
context:
space:
mode:
authorMichael Holzheu <holzheu@de.ibm.com>2006-06-23 05:05:06 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-06-23 10:43:02 -0400
commit24bbb1faf3f0420eb252dd0fdc1e477b1d4d73bd (patch)
treece7f9358fdeaa4f299cb996e8a1d4224d51ee4c4 /arch/s390/hypfs/inode.c
parentb9e122c80cd2e10fe18678c63db4717871ed31cf (diff)
[PATCH] s390_hypfs filesystem
On zSeries machines there exists an interface which allows the operating system to retrieve LPAR hypervisor accounting data. For example, it is possible to get usage data for physical and virtual cpus. In order to provide this information to user space programs, I implemented a new virtual Linux file system named 's390_hypfs' using the Linux 2.6 libfs framework. The name 's390_hypfs' stands for 'S390 Hypervisor Filesystem'. All the accounting information is put into different virtual files which can be accessed from user space. All data is represented as ASCII strings. When the file system is mounted the accounting information is retrieved and a file system tree is created with the attribute files containing the cpu information. The content of the files remains unchanged until a new update is made. An update can be triggered from user space through writing 'something' into a special purpose update file. We create the following directory structure: <mount-point>/ update cpus/ <cpu-id> type mgmtime <cpu-id> ... hyp/ type systems/ <lpar-name> cpus/ <cpu-id> type mgmtime cputime onlinetime <cpu-id> ... <lpar-name> cpus/ ... - update: File to trigger update - cpus/: Directory for all physical cpus - cpus/<cpu-id>/: Directory for one physical cpu. - cpus/<cpu-id>/type: Type name of physical zSeries cpu. - cpus/<cpu-id>/mgmtime: Physical-LPAR-management time in microseconds. - hyp/: Directory for hypervisor information - hyp/type: Typ of hypervisor (currently only 'LPAR Hypervisor') - systems/: Directory for all LPARs - systems/<lpar-name>/: Directory for one LPAR. - systems/<lpar-name>/cpus/<cpu-id>/: Directory for the virtual cpus - systems/<lpar-name>/cpus/<cpu-id>/type: Typ of cpu. - systems/<lpar-name>/cpus/<cpu-id>/mgmtime: Accumulated number of microseconds during which a physical CPU was assigned to the logical cpu and the cpu time was consumed by the hypervisor and was not provided to the LPAR (LPAR overhead). - systems/<lpar-name>/cpus/<cpu-id>/cputime: Accumulated number of microseconds during which a physical CPU was assigned to the logical cpu and the cpu time was consumed by the LPAR. - systems/<lpar-name>/cpus/<cpu-id>/onlinetime: Accumulated number of microseconds during which the logical CPU has been online. As mount point for the filesystem /sys/hypervisor/s390 is created. The update process is triggered when writing 'something' into the 'update' file at the top level hypfs directory. You can do this e.g. with 'echo 1 > update'. During the update the whole directory structure is deleted and built up again. Cc: Pekka Enberg <penberg@cs.helsinki.fi> Cc: Ingo Oeser <ioe-lkml@rameria.de> Cc: Joern Engel <joern@wohnheim.fh-wedel.de> Acked-by: Martin Schwidefsky <schwidefsky@de.ibm.com> Signed-off-by: Michael Holzheu <holzheu@de.ibm.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch/s390/hypfs/inode.c')
-rw-r--r--arch/s390/hypfs/inode.c492
1 files changed, 492 insertions, 0 deletions
diff --git a/arch/s390/hypfs/inode.c b/arch/s390/hypfs/inode.c
new file mode 100644
index 000000000000..bab560a1357a
--- /dev/null
+++ b/arch/s390/hypfs/inode.c
@@ -0,0 +1,492 @@
1/*
2 * fs/hypfs/inode.c
3 * Hypervisor filesystem for Linux on s390.
4 *
5 * Copyright (C) IBM Corp. 2006
6 * Author(s): Michael Holzheu <holzheu@de.ibm.com>
7 */
8
9#include <linux/types.h>
10#include <linux/errno.h>
11#include <linux/fs.h>
12#include <linux/namei.h>
13#include <linux/vfs.h>
14#include <linux/pagemap.h>
15#include <linux/gfp.h>
16#include <linux/time.h>
17#include <linux/parser.h>
18#include <linux/sysfs.h>
19#include <linux/module.h>
20#include <asm/ebcdic.h>
21#include "hypfs.h"
22#include "hypfs_diag.h"
23
24#define HYPFS_MAGIC 0x687970 /* ASCII 'hyp' */
25#define TMP_SIZE 64 /* size of temporary buffers */
26
27static struct dentry *hypfs_create_update_file(struct super_block *sb,
28 struct dentry *dir);
29
30struct hypfs_sb_info {
31 uid_t uid; /* uid used for files and dirs */
32 gid_t gid; /* gid used for files and dirs */
33 struct dentry *update_file; /* file to trigger update */
34 time_t last_update; /* last update time in secs since 1970 */
35 struct mutex lock; /* lock to protect update process */
36};
37
38static struct file_operations hypfs_file_ops;
39static struct file_system_type hypfs_type;
40static struct super_operations hypfs_s_ops;
41
42/* start of list of all dentries, which have to be deleted on update */
43static struct dentry *hypfs_last_dentry;
44
45static void hypfs_update_update(struct super_block *sb)
46{
47 struct hypfs_sb_info *sb_info = sb->s_fs_info;
48 struct inode *inode = sb_info->update_file->d_inode;
49
50 sb_info->last_update = get_seconds();
51 inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
52}
53
54/* directory tree removal functions */
55
56static void hypfs_add_dentry(struct dentry *dentry)
57{
58 dentry->d_fsdata = hypfs_last_dentry;
59 hypfs_last_dentry = dentry;
60}
61
62static void hypfs_remove(struct dentry *dentry)
63{
64 struct dentry *parent;
65
66 parent = dentry->d_parent;
67 if (S_ISDIR(dentry->d_inode->i_mode))
68 simple_rmdir(parent->d_inode, dentry);
69 else
70 simple_unlink(parent->d_inode, dentry);
71 d_delete(dentry);
72 dput(dentry);
73}
74
75static void hypfs_delete_tree(struct dentry *root)
76{
77 while (hypfs_last_dentry) {
78 struct dentry *next_dentry;
79 next_dentry = hypfs_last_dentry->d_fsdata;
80 hypfs_remove(hypfs_last_dentry);
81 hypfs_last_dentry = next_dentry;
82 }
83}
84
85static struct inode *hypfs_make_inode(struct super_block *sb, int mode)
86{
87 struct inode *ret = new_inode(sb);
88
89 if (ret) {
90 struct hypfs_sb_info *hypfs_info = sb->s_fs_info;
91 ret->i_mode = mode;
92 ret->i_uid = hypfs_info->uid;
93 ret->i_gid = hypfs_info->gid;
94 ret->i_blksize = PAGE_CACHE_SIZE;
95 ret->i_blocks = 0;
96 ret->i_atime = ret->i_mtime = ret->i_ctime = CURRENT_TIME;
97 if (mode & S_IFDIR)
98 ret->i_nlink = 2;
99 else
100 ret->i_nlink = 1;
101 }
102 return ret;
103}
104
105static void hypfs_drop_inode(struct inode *inode)
106{
107 kfree(inode->u.generic_ip);
108 generic_delete_inode(inode);
109}
110
111static int hypfs_open(struct inode *inode, struct file *filp)
112{
113 char *data = filp->f_dentry->d_inode->u.generic_ip;
114 struct hypfs_sb_info *fs_info;
115
116 if (filp->f_mode & FMODE_WRITE) {
117 if (!(inode->i_mode & S_IWUGO))
118 return -EACCES;
119 }
120 if (filp->f_mode & FMODE_READ) {
121 if (!(inode->i_mode & S_IRUGO))
122 return -EACCES;
123 }
124
125 fs_info = inode->i_sb->s_fs_info;
126 if(data) {
127 mutex_lock(&fs_info->lock);
128 filp->private_data = kstrdup(data, GFP_KERNEL);
129 if (!filp->private_data) {
130 mutex_unlock(&fs_info->lock);
131 return -ENOMEM;
132 }
133 mutex_unlock(&fs_info->lock);
134 }
135 return 0;
136}
137
138static ssize_t hypfs_aio_read(struct kiocb *iocb, __user char *buf,
139 size_t count, loff_t offset)
140{
141 char *data;
142 size_t len;
143 struct file *filp = iocb->ki_filp;
144
145 data = filp->private_data;
146 len = strlen(data);
147 if (offset > len) {
148 count = 0;
149 goto out;
150 }
151 if (count > len - offset)
152 count = len - offset;
153 if (copy_to_user(buf, data + offset, count)) {
154 count = -EFAULT;
155 goto out;
156 }
157 iocb->ki_pos += count;
158 file_accessed(filp);
159out:
160 return count;
161}
162static ssize_t hypfs_aio_write(struct kiocb *iocb, const char __user *buf,
163 size_t count, loff_t pos)
164{
165 int rc;
166 struct super_block *sb;
167 struct hypfs_sb_info *fs_info;
168
169 sb = iocb->ki_filp->f_dentry->d_inode->i_sb;
170 fs_info = sb->s_fs_info;
171 /*
172 * Currently we only allow one update per second for two reasons:
173 * 1. diag 204 is VERY expensive
174 * 2. If several processes do updates in parallel and then read the
175 * hypfs data, the likelihood of collisions is reduced, if we restrict
176 * the minimum update interval. A collision occurs, if during the
177 * data gathering of one process another process triggers an update
178 * If the first process wants to ensure consistent data, it has
179 * to restart data collection in this case.
180 */
181 mutex_lock(&fs_info->lock);
182 if (fs_info->last_update == get_seconds()) {
183 rc = -EBUSY;
184 goto out;
185 }
186 hypfs_delete_tree(sb->s_root);
187 rc = hypfs_diag_create_files(sb, sb->s_root);
188 if (rc) {
189 printk(KERN_ERR "hypfs: Update failed\n");
190 hypfs_delete_tree(sb->s_root);
191 goto out;
192 }
193 hypfs_update_update(sb);
194 rc = count;
195out:
196 mutex_unlock(&fs_info->lock);
197 return rc;
198}
199
200static int hypfs_release(struct inode *inode, struct file *filp)
201{
202 kfree(filp->private_data);
203 return 0;
204}
205
206enum { opt_uid, opt_gid, opt_err };
207
208static match_table_t hypfs_tokens = {
209 {opt_uid, "uid=%u"},
210 {opt_gid, "gid=%u"},
211 {opt_err, NULL}
212};
213
214static int hypfs_parse_options(char *options, struct super_block *sb)
215{
216 char *str;
217 substring_t args[MAX_OPT_ARGS];
218
219 if (!options)
220 return 0;
221 while ((str = strsep(&options, ",")) != NULL) {
222 int token, option;
223 struct hypfs_sb_info *hypfs_info = sb->s_fs_info;
224
225 if (!*str)
226 continue;
227 token = match_token(str, hypfs_tokens, args);
228 switch (token) {
229 case opt_uid:
230 if (match_int(&args[0], &option))
231 return -EINVAL;
232 hypfs_info->uid = option;
233 break;
234 case opt_gid:
235 if (match_int(&args[0], &option))
236 return -EINVAL;
237 hypfs_info->gid = option;
238 break;
239 case opt_err:
240 default:
241 printk(KERN_ERR "hypfs: Unrecognized mount option "
242 "\"%s\" or missing value\n", str);
243 return -EINVAL;
244 }
245 }
246 return 0;
247}
248
249static int hypfs_fill_super(struct super_block *sb, void *data, int silent)
250{
251 struct inode *root_inode;
252 struct dentry *root_dentry;
253 int rc = 0;
254 struct hypfs_sb_info *sbi;
255
256 sbi = kzalloc(sizeof(struct hypfs_sb_info), GFP_KERNEL);
257 if (!sbi)
258 return -ENOMEM;
259 mutex_init(&sbi->lock);
260 sbi->uid = current->uid;
261 sbi->gid = current->gid;
262 sb->s_fs_info = sbi;
263 sb->s_blocksize = PAGE_CACHE_SIZE;
264 sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
265 sb->s_magic = HYPFS_MAGIC;
266 sb->s_op = &hypfs_s_ops;
267 if (hypfs_parse_options(data, sb)) {
268 rc = -EINVAL;
269 goto err_alloc;
270 }
271 root_inode = hypfs_make_inode(sb, S_IFDIR | 0755);
272 if (!root_inode) {
273 rc = -ENOMEM;
274 goto err_alloc;
275 }
276 root_inode->i_op = &simple_dir_inode_operations;
277 root_inode->i_fop = &simple_dir_operations;
278 root_dentry = d_alloc_root(root_inode);
279 if (!root_dentry) {
280 iput(root_inode);
281 rc = -ENOMEM;
282 goto err_alloc;
283 }
284 rc = hypfs_diag_create_files(sb, root_dentry);
285 if (rc)
286 goto err_tree;
287 sbi->update_file = hypfs_create_update_file(sb, root_dentry);
288 if (IS_ERR(sbi->update_file)) {
289 rc = PTR_ERR(sbi->update_file);
290 goto err_tree;
291 }
292 hypfs_update_update(sb);
293 sb->s_root = root_dentry;
294 return 0;
295
296err_tree:
297 hypfs_delete_tree(root_dentry);
298 d_genocide(root_dentry);
299 dput(root_dentry);
300err_alloc:
301 kfree(sbi);
302 return rc;
303}
304
305static struct super_block *hypfs_get_super(struct file_system_type *fst,
306 int flags, const char *devname,
307 void *data)
308{
309 return get_sb_single(fst, flags, data, hypfs_fill_super);
310}
311
312static void hypfs_kill_super(struct super_block *sb)
313{
314 struct hypfs_sb_info *sb_info = sb->s_fs_info;
315
316 hypfs_delete_tree(sb->s_root);
317 hypfs_remove(sb_info->update_file);
318 kfree(sb->s_fs_info);
319 sb->s_fs_info = NULL;
320 kill_litter_super(sb);
321}
322
323static struct dentry *hypfs_create_file(struct super_block *sb,
324 struct dentry *parent, const char *name,
325 char *data, mode_t mode)
326{
327 struct dentry *dentry;
328 struct inode *inode;
329 struct qstr qname;
330
331 qname.name = name;
332 qname.len = strlen(name);
333 qname.hash = full_name_hash(name, qname.len);
334 dentry = lookup_one_len(name, parent, strlen(name));
335 if (IS_ERR(dentry))
336 return ERR_PTR(-ENOMEM);
337 inode = hypfs_make_inode(sb, mode);
338 if (!inode) {
339 dput(dentry);
340 return ERR_PTR(-ENOMEM);
341 }
342 if (mode & S_IFREG) {
343 inode->i_fop = &hypfs_file_ops;
344 if (data)
345 inode->i_size = strlen(data);
346 else
347 inode->i_size = 0;
348 } else if (mode & S_IFDIR) {
349 inode->i_op = &simple_dir_inode_operations;
350 inode->i_fop = &simple_dir_operations;
351 parent->d_inode->i_nlink++;
352 } else
353 BUG();
354 inode->u.generic_ip = data;
355 d_instantiate(dentry, inode);
356 dget(dentry);
357 return dentry;
358}
359
360struct dentry *hypfs_mkdir(struct super_block *sb, struct dentry *parent,
361 const char *name)
362{
363 struct dentry *dentry;
364
365 dentry = hypfs_create_file(sb, parent, name, NULL, S_IFDIR | DIR_MODE);
366 if (IS_ERR(dentry))
367 return dentry;
368 hypfs_add_dentry(dentry);
369 parent->d_inode->i_nlink++;
370 return dentry;
371}
372
373static struct dentry *hypfs_create_update_file(struct super_block *sb,
374 struct dentry *dir)
375{
376 struct dentry *dentry;
377
378 dentry = hypfs_create_file(sb, dir, "update", NULL,
379 S_IFREG | UPDATE_FILE_MODE);
380 /*
381 * We do not put the update file on the 'delete' list with
382 * hypfs_add_dentry(), since it should not be removed when the tree
383 * is updated.
384 */
385 return dentry;
386}
387
388struct dentry *hypfs_create_u64(struct super_block *sb, struct dentry *dir,
389 const char *name, __u64 value)
390{
391 char *buffer;
392 char tmp[TMP_SIZE];
393 struct dentry *dentry;
394
395 snprintf(tmp, TMP_SIZE, "%lld\n", (unsigned long long int)value);
396 buffer = kstrdup(tmp, GFP_KERNEL);
397 if (!buffer)
398 return ERR_PTR(-ENOMEM);
399 dentry =
400 hypfs_create_file(sb, dir, name, buffer, S_IFREG | REG_FILE_MODE);
401 if (IS_ERR(dentry)) {
402 kfree(buffer);
403 return ERR_PTR(-ENOMEM);
404 }
405 hypfs_add_dentry(dentry);
406 return dentry;
407}
408
409struct dentry *hypfs_create_str(struct super_block *sb, struct dentry *dir,
410 const char *name, char *string)
411{
412 char *buffer;
413 struct dentry *dentry;
414
415 buffer = kmalloc(strlen(string) + 2, GFP_KERNEL);
416 if (!buffer)
417 return ERR_PTR(-ENOMEM);
418 sprintf(buffer, "%s\n", string);
419 dentry =
420 hypfs_create_file(sb, dir, name, buffer, S_IFREG | REG_FILE_MODE);
421 if (IS_ERR(dentry)) {
422 kfree(buffer);
423 return ERR_PTR(-ENOMEM);
424 }
425 hypfs_add_dentry(dentry);
426 return dentry;
427}
428
429static struct file_operations hypfs_file_ops = {
430 .open = hypfs_open,
431 .release = hypfs_release,
432 .read = do_sync_read,
433 .write = do_sync_write,
434 .aio_read = hypfs_aio_read,
435 .aio_write = hypfs_aio_write,
436};
437
438static struct file_system_type hypfs_type = {
439 .owner = THIS_MODULE,
440 .name = "s390_hypfs",
441 .get_sb = hypfs_get_super,
442 .kill_sb = hypfs_kill_super
443};
444
445static struct super_operations hypfs_s_ops = {
446 .statfs = simple_statfs,
447 .drop_inode = hypfs_drop_inode,
448};
449
450static decl_subsys(s390, NULL, NULL);
451
452static int __init hypfs_init(void)
453{
454 int rc;
455
456 if (MACHINE_IS_VM)
457 return -ENODATA;
458 if (hypfs_diag_init()) {
459 rc = -ENODATA;
460 goto fail_diag;
461 }
462 kset_set_kset_s(&s390_subsys, hypervisor_subsys);
463 rc = subsystem_register(&s390_subsys);
464 if (rc)
465 goto fail_sysfs;
466 rc = register_filesystem(&hypfs_type);
467 if (rc)
468 goto fail_filesystem;
469 return 0;
470
471fail_filesystem:
472 subsystem_unregister(&s390_subsys);
473fail_sysfs:
474 hypfs_diag_exit();
475fail_diag:
476 printk(KERN_ERR "hypfs: Initialization failed with rc = %i.\n", rc);
477 return rc;
478}
479
480static void __exit hypfs_exit(void)
481{
482 hypfs_diag_exit();
483 unregister_filesystem(&hypfs_type);
484 subsystem_unregister(&s390_subsys);
485}
486
487module_init(hypfs_init)
488module_exit(hypfs_exit)
489
490MODULE_LICENSE("GPL");
491MODULE_AUTHOR("Michael Holzheu <holzheu@de.ibm.com>");
492MODULE_DESCRIPTION("s390 Hypervisor Filesystem");