Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | /* * Hypervisor filesystem for Linux on s390 - debugfs interface * * Copyright IBM Corp. 2010 * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com> */ #include <linux/slab.h> #include "hypfs.h" static struct dentry *dbfs_dir; static struct hypfs_dbfs_data *hypfs_dbfs_data_alloc(struct hypfs_dbfs_file *f) { struct hypfs_dbfs_data *data; data = kmalloc(sizeof(*data), GFP_KERNEL); if (!data) return NULL; kref_init(&data->kref); data->dbfs_file = f; return data; } static void hypfs_dbfs_data_free(struct kref *kref) { struct hypfs_dbfs_data *data; data = container_of(kref, struct hypfs_dbfs_data, kref); data->dbfs_file->data_free(data->buf_free_ptr); kfree(data); } static void data_free_delayed(struct work_struct *work) { struct hypfs_dbfs_data *data; struct hypfs_dbfs_file *df; df = container_of(work, struct hypfs_dbfs_file, data_free_work.work); mutex_lock(&df->lock); data = df->data; df->data = NULL; mutex_unlock(&df->lock); kref_put(&data->kref, hypfs_dbfs_data_free); } static ssize_t dbfs_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) { struct hypfs_dbfs_data *data; struct hypfs_dbfs_file *df; ssize_t rc; if (*ppos != 0) return 0; df = file_inode(file)->i_private; mutex_lock(&df->lock); if (!df->data) { data = hypfs_dbfs_data_alloc(df); if (!data) { mutex_unlock(&df->lock); return -ENOMEM; } rc = df->data_create(&data->buf, &data->buf_free_ptr, &data->size); if (rc) { mutex_unlock(&df->lock); kfree(data); return rc; } df->data = data; schedule_delayed_work(&df->data_free_work, HZ); } data = df->data; kref_get(&data->kref); mutex_unlock(&df->lock); rc = simple_read_from_buffer(buf, size, ppos, data->buf, data->size); kref_put(&data->kref, hypfs_dbfs_data_free); return rc; } static long dbfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct hypfs_dbfs_file *df; long rc; df = file->f_path.dentry->d_inode->i_private; mutex_lock(&df->lock); if (df->unlocked_ioctl) rc = df->unlocked_ioctl(file, cmd, arg); else rc = -ENOTTY; mutex_unlock(&df->lock); return rc; } static const struct file_operations dbfs_ops = { .read = dbfs_read, .llseek = no_llseek, .unlocked_ioctl = dbfs_ioctl, }; int hypfs_dbfs_create_file(struct hypfs_dbfs_file *df) { df->dentry = debugfs_create_file(df->name, 0400, dbfs_dir, df, &dbfs_ops); if (IS_ERR(df->dentry)) return PTR_ERR(df->dentry); mutex_init(&df->lock); INIT_DELAYED_WORK(&df->data_free_work, data_free_delayed); return 0; } void hypfs_dbfs_remove_file(struct hypfs_dbfs_file *df) { debugfs_remove(df->dentry); } int hypfs_dbfs_init(void) { dbfs_dir = debugfs_create_dir("s390_hypfs", NULL); return PTR_ERR_OR_ZERO(dbfs_dir); } void hypfs_dbfs_exit(void) { debugfs_remove(dbfs_dir); } |