]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - arch/s390/hypfs/hypfs_dbfs.c
Merge tag 'v2.6.38' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / arch / s390 / hypfs / hypfs_dbfs.c
diff --git a/arch/s390/hypfs/hypfs_dbfs.c b/arch/s390/hypfs/hypfs_dbfs.c
new file mode 100644 (file)
index 0000000..b478013
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Hypervisor filesystem for Linux on s390 - debugfs interface
+ *
+ * Copyright (C) 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->f_path.dentry->d_inode->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 const struct file_operations dbfs_ops = {
+       .read           = dbfs_read,
+       .llseek         = no_llseek,
+};
+
+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);
+       if (IS_ERR(dbfs_dir))
+               return PTR_ERR(dbfs_dir);
+       return 0;
+}
+
+void hypfs_dbfs_exit(void)
+{
+       debugfs_remove(dbfs_dir);
+}