diff options
Diffstat (limited to 'arch/s390/hypfs/inode.c')
-rw-r--r-- | arch/s390/hypfs/inode.c | 492 |
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 | |||
27 | static struct dentry *hypfs_create_update_file(struct super_block *sb, | ||
28 | struct dentry *dir); | ||
29 | |||
30 | struct 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 | |||
38 | static struct file_operations hypfs_file_ops; | ||
39 | static struct file_system_type hypfs_type; | ||
40 | static struct super_operations hypfs_s_ops; | ||
41 | |||
42 | /* start of list of all dentries, which have to be deleted on update */ | ||
43 | static struct dentry *hypfs_last_dentry; | ||
44 | |||
45 | static 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 | |||
56 | static void hypfs_add_dentry(struct dentry *dentry) | ||
57 | { | ||
58 | dentry->d_fsdata = hypfs_last_dentry; | ||
59 | hypfs_last_dentry = dentry; | ||
60 | } | ||
61 | |||
62 | static 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 | |||
75 | static 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 | |||
85 | static 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 | |||
105 | static void hypfs_drop_inode(struct inode *inode) | ||
106 | { | ||
107 | kfree(inode->u.generic_ip); | ||
108 | generic_delete_inode(inode); | ||
109 | } | ||
110 | |||
111 | static 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 | |||
138 | static 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); | ||
159 | out: | ||
160 | return count; | ||
161 | } | ||
162 | static 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; | ||
195 | out: | ||
196 | mutex_unlock(&fs_info->lock); | ||
197 | return rc; | ||
198 | } | ||
199 | |||
200 | static int hypfs_release(struct inode *inode, struct file *filp) | ||
201 | { | ||
202 | kfree(filp->private_data); | ||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | enum { opt_uid, opt_gid, opt_err }; | ||
207 | |||
208 | static match_table_t hypfs_tokens = { | ||
209 | {opt_uid, "uid=%u"}, | ||
210 | {opt_gid, "gid=%u"}, | ||
211 | {opt_err, NULL} | ||
212 | }; | ||
213 | |||
214 | static 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 | |||
249 | static 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 | |||
296 | err_tree: | ||
297 | hypfs_delete_tree(root_dentry); | ||
298 | d_genocide(root_dentry); | ||
299 | dput(root_dentry); | ||
300 | err_alloc: | ||
301 | kfree(sbi); | ||
302 | return rc; | ||
303 | } | ||
304 | |||
305 | static 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 | |||
312 | static 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 | |||
323 | static 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 | |||
360 | struct 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 | |||
373 | static 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 | |||
388 | struct 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 | |||
409 | struct 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 | |||
429 | static 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 | |||
438 | static 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 | |||
445 | static struct super_operations hypfs_s_ops = { | ||
446 | .statfs = simple_statfs, | ||
447 | .drop_inode = hypfs_drop_inode, | ||
448 | }; | ||
449 | |||
450 | static decl_subsys(s390, NULL, NULL); | ||
451 | |||
452 | static 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 | |||
471 | fail_filesystem: | ||
472 | subsystem_unregister(&s390_subsys); | ||
473 | fail_sysfs: | ||
474 | hypfs_diag_exit(); | ||
475 | fail_diag: | ||
476 | printk(KERN_ERR "hypfs: Initialization failed with rc = %i.\n", rc); | ||
477 | return rc; | ||
478 | } | ||
479 | |||
480 | static void __exit hypfs_exit(void) | ||
481 | { | ||
482 | hypfs_diag_exit(); | ||
483 | unregister_filesystem(&hypfs_type); | ||
484 | subsystem_unregister(&s390_subsys); | ||
485 | } | ||
486 | |||
487 | module_init(hypfs_init) | ||
488 | module_exit(hypfs_exit) | ||
489 | |||
490 | MODULE_LICENSE("GPL"); | ||
491 | MODULE_AUTHOR("Michael Holzheu <holzheu@de.ibm.com>"); | ||
492 | MODULE_DESCRIPTION("s390 Hypervisor Filesystem"); | ||