Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[S390] hypfs: Move buffer allocation from open to read

Currently the buffer for diagnose data is allocated in the open function
of the debugfs file and is released in the close function. This has the
drawback that a user (root) can pin that memory by not closing the file.
This patch moves the buffer allocation to the read function. The buffer is
automatically released after the buffer is copied to userspace.

Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>

authored by

Michael Holzheu and committed by
Martin Schwidefsky
2fcb3686 6432c015

+195 -118
+1 -1
arch/s390/hypfs/Makefile
··· 4 4 5 5 obj-$(CONFIG_S390_HYPFS_FS) += s390_hypfs.o 6 6 7 - s390_hypfs-objs := inode.o hypfs_diag.o hypfs_vm.o 7 + s390_hypfs-objs := inode.o hypfs_diag.o hypfs_vm.o hypfs_dbfs.o
+31 -2
arch/s390/hypfs/hypfs.h
··· 12 12 #include <linux/fs.h> 13 13 #include <linux/types.h> 14 14 #include <linux/debugfs.h> 15 + #include <linux/workqueue.h> 16 + #include <linux/kref.h> 15 17 16 18 #define REG_FILE_MODE 0440 17 19 #define UPDATE_FILE_MODE 0220 ··· 40 38 extern void hypfs_vm_exit(void); 41 39 extern int hypfs_vm_create_files(struct super_block *sb, struct dentry *root); 42 40 43 - /* Directory for debugfs files */ 44 - extern struct dentry *hypfs_dbfs_dir; 41 + /* debugfs interface */ 42 + struct hypfs_dbfs_file; 43 + 44 + struct hypfs_dbfs_data { 45 + void *buf; 46 + void *buf_free_ptr; 47 + size_t size; 48 + struct hypfs_dbfs_file *dbfs_file;; 49 + struct kref kref; 50 + }; 51 + 52 + struct hypfs_dbfs_file { 53 + const char *name; 54 + int (*data_create)(void **data, void **data_free_ptr, 55 + size_t *size); 56 + void (*data_free)(const void *buf_free_ptr); 57 + 58 + /* Private data for hypfs_dbfs.c */ 59 + struct hypfs_dbfs_data *data; 60 + struct delayed_work data_free_work; 61 + struct mutex lock; 62 + struct dentry *dentry; 63 + }; 64 + 65 + extern int hypfs_dbfs_init(void); 66 + extern void hypfs_dbfs_exit(void); 67 + extern int hypfs_dbfs_create_file(struct hypfs_dbfs_file *df); 68 + extern void hypfs_dbfs_remove_file(struct hypfs_dbfs_file *df); 69 + 45 70 #endif /* _HYPFS_H_ */
+116
arch/s390/hypfs/hypfs_dbfs.c
··· 1 + /* 2 + * Hypervisor filesystem for Linux on s390 - debugfs interface 3 + * 4 + * Copyright (C) IBM Corp. 2010 5 + * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com> 6 + */ 7 + 8 + #include <linux/slab.h> 9 + #include "hypfs.h" 10 + 11 + static struct dentry *dbfs_dir; 12 + 13 + static struct hypfs_dbfs_data *hypfs_dbfs_data_alloc(struct hypfs_dbfs_file *f) 14 + { 15 + struct hypfs_dbfs_data *data; 16 + 17 + data = kmalloc(sizeof(*data), GFP_KERNEL); 18 + if (!data) 19 + return NULL; 20 + kref_init(&data->kref); 21 + data->dbfs_file = f; 22 + return data; 23 + } 24 + 25 + static void hypfs_dbfs_data_free(struct kref *kref) 26 + { 27 + struct hypfs_dbfs_data *data; 28 + 29 + data = container_of(kref, struct hypfs_dbfs_data, kref); 30 + data->dbfs_file->data_free(data->buf_free_ptr); 31 + kfree(data); 32 + } 33 + 34 + static void data_free_delayed(struct work_struct *work) 35 + { 36 + struct hypfs_dbfs_data *data; 37 + struct hypfs_dbfs_file *df; 38 + 39 + df = container_of(work, struct hypfs_dbfs_file, data_free_work.work); 40 + mutex_lock(&df->lock); 41 + data = df->data; 42 + df->data = NULL; 43 + mutex_unlock(&df->lock); 44 + kref_put(&data->kref, hypfs_dbfs_data_free); 45 + } 46 + 47 + static ssize_t dbfs_read(struct file *file, char __user *buf, 48 + size_t size, loff_t *ppos) 49 + { 50 + struct hypfs_dbfs_data *data; 51 + struct hypfs_dbfs_file *df; 52 + ssize_t rc; 53 + 54 + if (*ppos != 0) 55 + return 0; 56 + 57 + df = file->f_path.dentry->d_inode->i_private; 58 + mutex_lock(&df->lock); 59 + if (!df->data) { 60 + data = hypfs_dbfs_data_alloc(df); 61 + if (!data) { 62 + mutex_unlock(&df->lock); 63 + return -ENOMEM; 64 + } 65 + rc = df->data_create(&data->buf, &data->buf_free_ptr, 66 + &data->size); 67 + if (rc) { 68 + mutex_unlock(&df->lock); 69 + kfree(data); 70 + return rc; 71 + } 72 + df->data = data; 73 + schedule_delayed_work(&df->data_free_work, HZ); 74 + } 75 + data = df->data; 76 + kref_get(&data->kref); 77 + mutex_unlock(&df->lock); 78 + 79 + rc = simple_read_from_buffer(buf, size, ppos, data->buf, data->size); 80 + kref_put(&data->kref, hypfs_dbfs_data_free); 81 + return rc; 82 + } 83 + 84 + static const struct file_operations dbfs_ops = { 85 + .read = dbfs_read, 86 + .llseek = no_llseek, 87 + }; 88 + 89 + int hypfs_dbfs_create_file(struct hypfs_dbfs_file *df) 90 + { 91 + df->dentry = debugfs_create_file(df->name, 0400, dbfs_dir, df, 92 + &dbfs_ops); 93 + if (IS_ERR(df->dentry)) 94 + return PTR_ERR(df->dentry); 95 + mutex_init(&df->lock); 96 + INIT_DELAYED_WORK(&df->data_free_work, data_free_delayed); 97 + return 0; 98 + } 99 + 100 + void hypfs_dbfs_remove_file(struct hypfs_dbfs_file *df) 101 + { 102 + debugfs_remove(df->dentry); 103 + } 104 + 105 + int hypfs_dbfs_init(void) 106 + { 107 + dbfs_dir = debugfs_create_dir("s390_hypfs", NULL); 108 + if (IS_ERR(dbfs_dir)) 109 + return PTR_ERR(dbfs_dir); 110 + return 0; 111 + } 112 + 113 + void hypfs_dbfs_exit(void) 114 + { 115 + debugfs_remove(dbfs_dir); 116 + }
+20 -62
arch/s390/hypfs/hypfs_diag.c
··· 555 555 char buf[]; /* d204 buffer */ 556 556 } __attribute__ ((packed)); 557 557 558 - struct dbfs_d204_private { 559 - struct dbfs_d204 *d204; /* Aligned d204 data with header */ 560 - void *base; /* Base pointer (needed for vfree) */ 561 - }; 562 - 563 - static int dbfs_d204_open(struct inode *inode, struct file *file) 558 + static int dbfs_d204_create(void **data, void **data_free_ptr, size_t *size) 564 559 { 565 - struct dbfs_d204_private *data; 566 560 struct dbfs_d204 *d204; 567 561 int rc, buf_size; 562 + void *base; 568 563 569 - data = kzalloc(sizeof(*data), GFP_KERNEL); 570 - if (!data) 571 - return -ENOMEM; 572 564 buf_size = PAGE_SIZE * (diag204_buf_pages + 1) + sizeof(d204->hdr); 573 - data->base = vmalloc(buf_size); 574 - if (!data->base) { 575 - rc = -ENOMEM; 576 - goto fail_kfree_data; 565 + base = vmalloc(buf_size); 566 + if (!base) 567 + return -ENOMEM; 568 + memset(base, 0, buf_size); 569 + d204 = page_align_ptr(base + sizeof(d204->hdr)) - sizeof(d204->hdr); 570 + rc = diag204_do_store(d204->buf, diag204_buf_pages); 571 + if (rc) { 572 + vfree(base); 573 + return rc; 577 574 } 578 - memset(data->base, 0, buf_size); 579 - d204 = page_align_ptr(data->base + sizeof(d204->hdr)) 580 - - sizeof(d204->hdr); 581 - rc = diag204_do_store(&d204->buf, diag204_buf_pages); 582 - if (rc) 583 - goto fail_vfree_base; 584 575 d204->hdr.version = DBFS_D204_HDR_VERSION; 585 576 d204->hdr.len = PAGE_SIZE * diag204_buf_pages; 586 577 d204->hdr.sc = diag204_store_sc; 587 - data->d204 = d204; 588 - file->private_data = data; 589 - return nonseekable_open(inode, file); 590 - 591 - fail_vfree_base: 592 - vfree(data->base); 593 - fail_kfree_data: 594 - kfree(data); 595 - return rc; 596 - } 597 - 598 - static int dbfs_d204_release(struct inode *inode, struct file *file) 599 - { 600 - struct dbfs_d204_private *data = file->private_data; 601 - 602 - vfree(data->base); 603 - kfree(data); 578 + *data = d204; 579 + *data_free_ptr = base; 580 + *size = d204->hdr.len + sizeof(struct dbfs_d204_hdr); 604 581 return 0; 605 582 } 606 583 607 - static ssize_t dbfs_d204_read(struct file *file, char __user *buf, 608 - size_t size, loff_t *ppos) 609 - { 610 - struct dbfs_d204_private *data = file->private_data; 611 - 612 - return simple_read_from_buffer(buf, size, ppos, data->d204, 613 - data->d204->hdr.len + 614 - sizeof(data->d204->hdr)); 615 - } 616 - 617 - static const struct file_operations dbfs_d204_ops = { 618 - .open = dbfs_d204_open, 619 - .read = dbfs_d204_read, 620 - .release = dbfs_d204_release, 621 - .llseek = no_llseek, 584 + static struct hypfs_dbfs_file dbfs_file_d204 = { 585 + .name = "diag_204", 586 + .data_create = dbfs_d204_create, 587 + .data_free = vfree, 622 588 }; 623 - 624 - static int hypfs_dbfs_init(void) 625 - { 626 - dbfs_d204_file = debugfs_create_file("diag_204", 0400, hypfs_dbfs_dir, 627 - NULL, &dbfs_d204_ops); 628 - if (IS_ERR(dbfs_d204_file)) 629 - return PTR_ERR(dbfs_d204_file); 630 - return 0; 631 - } 632 589 633 590 __init int hypfs_diag_init(void) 634 591 { ··· 596 639 return -ENODATA; 597 640 } 598 641 if (diag204_info_type == INFO_EXT) { 599 - rc = hypfs_dbfs_init(); 642 + rc = hypfs_dbfs_create_file(&dbfs_file_d204); 600 643 if (rc) 601 644 return rc; 602 645 } ··· 617 660 debugfs_remove(dbfs_d204_file); 618 661 diag224_delete_name_table(); 619 662 diag204_free_buffer(); 663 + hypfs_dbfs_remove_file(&dbfs_file_d204); 620 664 } 621 665 622 666 /*
+20 -42
arch/s390/hypfs/hypfs_vm.c
··· 20 20 static char all_guests[] = "* "; 21 21 static char *guest_query; 22 22 23 - static struct dentry *dbfs_d2fc_file; 24 - 25 23 struct diag2fc_data { 26 24 __u32 version; 27 25 __u32 flags; ··· 102 104 return data; 103 105 } 104 106 105 - static void diag2fc_free(void *data) 107 + static void diag2fc_free(const void *data) 106 108 { 107 109 vfree(data); 108 110 } ··· 237 239 char buf[]; /* d2fc buffer */ 238 240 } __attribute__ ((packed)); 239 241 240 - static int dbfs_d2fc_open(struct inode *inode, struct file *file) 242 + static int dbfs_diag2fc_create(void **data, void **data_free_ptr, size_t *size) 241 243 { 242 - struct dbfs_d2fc *data; 244 + struct dbfs_d2fc *d2fc; 243 245 unsigned int count; 244 246 245 - data = diag2fc_store(guest_query, &count, sizeof(data->hdr)); 246 - if (IS_ERR(data)) 247 - return PTR_ERR(data); 248 - get_clock_ext(data->hdr.tod_ext); 249 - data->hdr.len = count * sizeof(struct diag2fc_data); 250 - data->hdr.version = DBFS_D2FC_HDR_VERSION; 251 - data->hdr.count = count; 252 - memset(&data->hdr.reserved, 0, sizeof(data->hdr.reserved)); 253 - file->private_data = data; 254 - return nonseekable_open(inode, file); 255 - } 256 - 257 - static int dbfs_d2fc_release(struct inode *inode, struct file *file) 258 - { 259 - diag2fc_free(file->private_data); 247 + d2fc = diag2fc_store(guest_query, &count, sizeof(d2fc->hdr)); 248 + if (IS_ERR(d2fc)) 249 + return PTR_ERR(d2fc); 250 + get_clock_ext(d2fc->hdr.tod_ext); 251 + d2fc->hdr.len = count * sizeof(struct diag2fc_data); 252 + d2fc->hdr.version = DBFS_D2FC_HDR_VERSION; 253 + d2fc->hdr.count = count; 254 + memset(&d2fc->hdr.reserved, 0, sizeof(d2fc->hdr.reserved)); 255 + *data = d2fc; 256 + *data_free_ptr = d2fc; 257 + *size = d2fc->hdr.len + sizeof(struct dbfs_d2fc_hdr); 260 258 return 0; 261 259 } 262 260 263 - static ssize_t dbfs_d2fc_read(struct file *file, char __user *buf, 264 - size_t size, loff_t *ppos) 265 - { 266 - struct dbfs_d2fc *data = file->private_data; 267 - 268 - return simple_read_from_buffer(buf, size, ppos, data, data->hdr.len + 269 - sizeof(struct dbfs_d2fc_hdr)); 270 - } 271 - 272 - static const struct file_operations dbfs_d2fc_ops = { 273 - .open = dbfs_d2fc_open, 274 - .read = dbfs_d2fc_read, 275 - .release = dbfs_d2fc_release, 276 - .llseek = no_llseek, 261 + static struct hypfs_dbfs_file dbfs_file_2fc = { 262 + .name = "diag_2fc", 263 + .data_create = dbfs_diag2fc_create, 264 + .data_free = diag2fc_free, 277 265 }; 278 266 279 267 int hypfs_vm_init(void) ··· 272 288 guest_query = local_guest; 273 289 else 274 290 return -EACCES; 275 - 276 - dbfs_d2fc_file = debugfs_create_file("diag_2fc", 0400, hypfs_dbfs_dir, 277 - NULL, &dbfs_d2fc_ops); 278 - if (IS_ERR(dbfs_d2fc_file)) 279 - return PTR_ERR(dbfs_d2fc_file); 280 - 281 - return 0; 291 + return hypfs_dbfs_create_file(&dbfs_file_2fc); 282 292 } 283 293 284 294 void hypfs_vm_exit(void) 285 295 { 286 296 if (!MACHINE_IS_VM) 287 297 return; 288 - debugfs_remove(dbfs_d2fc_file); 298 + hypfs_dbfs_remove_file(&dbfs_file_2fc); 289 299 }
+7 -11
arch/s390/hypfs/inode.c
··· 46 46 /* start of list of all dentries, which have to be deleted on update */ 47 47 static struct dentry *hypfs_last_dentry; 48 48 49 - struct dentry *hypfs_dbfs_dir; 50 - 51 49 static void hypfs_update_update(struct super_block *sb) 52 50 { 53 51 struct hypfs_sb_info *sb_info = sb->s_fs_info; ··· 469 471 { 470 472 int rc; 471 473 472 - hypfs_dbfs_dir = debugfs_create_dir("s390_hypfs", NULL); 473 - if (IS_ERR(hypfs_dbfs_dir)) 474 - return PTR_ERR(hypfs_dbfs_dir); 475 - 474 + rc = hypfs_dbfs_init(); 475 + if (rc) 476 + return rc; 476 477 if (hypfs_diag_init()) { 477 478 rc = -ENODATA; 478 - goto fail_debugfs_remove; 479 + goto fail_dbfs_exit; 479 480 } 480 481 if (hypfs_vm_init()) { 481 482 rc = -ENODATA; ··· 496 499 hypfs_vm_exit(); 497 500 fail_hypfs_diag_exit: 498 501 hypfs_diag_exit(); 499 - fail_debugfs_remove: 500 - debugfs_remove(hypfs_dbfs_dir); 501 - 502 + fail_dbfs_exit: 503 + hypfs_dbfs_exit(); 502 504 pr_err("Initialization of hypfs failed with rc=%i\n", rc); 503 505 return rc; 504 506 } ··· 506 510 { 507 511 hypfs_diag_exit(); 508 512 hypfs_vm_exit(); 509 - debugfs_remove(hypfs_dbfs_dir); 513 + hypfs_dbfs_exit(); 510 514 unregister_filesystem(&hypfs_type); 511 515 kobject_put(s390_kobj); 512 516 }