summaryrefslogtreecommitdiff
path: root/arch/s390/hypfs
diff options
context:
space:
mode:
Diffstat (limited to 'arch/s390/hypfs')
-rw-r--r--arch/s390/hypfs/Makefile1
-rw-r--r--arch/s390/hypfs/hypfs.h7
-rw-r--r--arch/s390/hypfs/hypfs_dbfs.c49
-rw-r--r--arch/s390/hypfs/hypfs_diag0c.c139
-rw-r--r--arch/s390/hypfs/inode.c62
5 files changed, 189 insertions, 69 deletions
diff --git a/arch/s390/hypfs/Makefile b/arch/s390/hypfs/Makefile
index 06f8d95..2ee25ba 100644
--- a/arch/s390/hypfs/Makefile
+++ b/arch/s390/hypfs/Makefile
@@ -5,3 +5,4 @@
obj-$(CONFIG_S390_HYPFS_FS) += s390_hypfs.o
s390_hypfs-objs := inode.o hypfs_diag.o hypfs_vm.o hypfs_dbfs.o hypfs_sprp.o
+s390_hypfs-objs += hypfs_diag0c.o
diff --git a/arch/s390/hypfs/hypfs.h b/arch/s390/hypfs/hypfs.h
index b34b5ab..eecde50 100644
--- a/arch/s390/hypfs/hypfs.h
+++ b/arch/s390/hypfs/hypfs.h
@@ -37,6 +37,10 @@ extern int hypfs_vm_init(void);
extern void hypfs_vm_exit(void);
extern int hypfs_vm_create_files(struct dentry *root);
+/* VM diagnose 0c */
+int hypfs_diag0c_init(void);
+void hypfs_diag0c_exit(void);
+
/* Set Partition-Resource Parameter */
int hypfs_sprp_init(void);
void hypfs_sprp_exit(void);
@@ -49,7 +53,6 @@ struct hypfs_dbfs_data {
void *buf_free_ptr;
size_t size;
struct hypfs_dbfs_file *dbfs_file;
- struct kref kref;
};
struct hypfs_dbfs_file {
@@ -61,8 +64,6 @@ struct hypfs_dbfs_file {
unsigned long);
/* Private data for hypfs_dbfs.c */
- struct hypfs_dbfs_data *data;
- struct delayed_work data_free_work;
struct mutex lock;
struct dentry *dentry;
};
diff --git a/arch/s390/hypfs/hypfs_dbfs.c b/arch/s390/hypfs/hypfs_dbfs.c
index 47fe105..752f6df 100644
--- a/arch/s390/hypfs/hypfs_dbfs.c
+++ b/arch/s390/hypfs/hypfs_dbfs.c
@@ -17,33 +17,16 @@ static struct hypfs_dbfs_data *hypfs_dbfs_data_alloc(struct hypfs_dbfs_file *f)
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)
+static void hypfs_dbfs_data_free(struct hypfs_dbfs_data *data)
{
- 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)
{
@@ -56,28 +39,21 @@ static ssize_t dbfs_read(struct file *file, char __user *buf,
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 = 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;
}
- 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);
+ hypfs_dbfs_data_free(data);
return rc;
}
@@ -108,7 +84,6 @@ int hypfs_dbfs_create_file(struct hypfs_dbfs_file *df)
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;
}
diff --git a/arch/s390/hypfs/hypfs_diag0c.c b/arch/s390/hypfs/hypfs_diag0c.c
new file mode 100644
index 0000000..d4c0d37
--- /dev/null
+++ b/arch/s390/hypfs/hypfs_diag0c.c
@@ -0,0 +1,139 @@
+/*
+ * Hypervisor filesystem for Linux on s390
+ *
+ * Diag 0C implementation
+ *
+ * Copyright IBM Corp. 2014
+ */
+
+#include <linux/slab.h>
+#include <linux/cpu.h>
+#include <asm/hypfs.h>
+#include "hypfs.h"
+
+#define DBFS_D0C_HDR_VERSION 0
+
+/*
+ * Execute diagnose 0c in 31 bit mode
+ */
+static void diag0c(struct hypfs_diag0c_entry *entry)
+{
+ asm volatile (
+#ifdef CONFIG_64BIT
+ " sam31\n"
+ " diag %0,%0,0x0c\n"
+ " sam64\n"
+#else
+ " diag %0,%0,0x0c\n"
+#endif
+ : /* no output register */
+ : "a" (entry)
+ : "memory");
+}
+
+/*
+ * Get hypfs_diag0c_entry from CPU vector and store diag0c data
+ */
+static void diag0c_fn(void *data)
+{
+ diag0c(((void **) data)[smp_processor_id()]);
+}
+
+/*
+ * Allocate buffer and store diag 0c data
+ */
+static void *diag0c_store(unsigned int *count)
+{
+ struct hypfs_diag0c_data *diag0c_data;
+ unsigned int cpu_count, cpu, i;
+ void **cpu_vec;
+
+ get_online_cpus();
+ cpu_count = num_online_cpus();
+ cpu_vec = kmalloc(sizeof(*cpu_vec) * num_possible_cpus(), GFP_KERNEL);
+ if (!cpu_vec)
+ goto fail_put_online_cpus;
+ /* Note: Diag 0c needs 8 byte alignment and real storage */
+ diag0c_data = kzalloc(sizeof(struct hypfs_diag0c_hdr) +
+ cpu_count * sizeof(struct hypfs_diag0c_entry),
+ GFP_KERNEL | GFP_DMA);
+ if (!diag0c_data)
+ goto fail_kfree_cpu_vec;
+ i = 0;
+ /* Fill CPU vector for each online CPU */
+ for_each_online_cpu(cpu) {
+ diag0c_data->entry[i].cpu = cpu;
+ cpu_vec[cpu] = &diag0c_data->entry[i++];
+ }
+ /* Collect data all CPUs */
+ on_each_cpu(diag0c_fn, cpu_vec, 1);
+ *count = cpu_count;
+ kfree(cpu_vec);
+ put_online_cpus();
+ return diag0c_data;
+
+fail_kfree_cpu_vec:
+ kfree(cpu_vec);
+fail_put_online_cpus:
+ put_online_cpus();
+ return ERR_PTR(-ENOMEM);
+}
+
+/*
+ * Hypfs DBFS callback: Free diag 0c data
+ */
+static void dbfs_diag0c_free(const void *data)
+{
+ kfree(data);
+}
+
+/*
+ * Hypfs DBFS callback: Create diag 0c data
+ */
+static int dbfs_diag0c_create(void **data, void **data_free_ptr, size_t *size)
+{
+ struct hypfs_diag0c_data *diag0c_data;
+ unsigned int count;
+
+ diag0c_data = diag0c_store(&count);
+ if (IS_ERR(diag0c_data))
+ return PTR_ERR(diag0c_data);
+ memset(&diag0c_data->hdr, 0, sizeof(diag0c_data->hdr));
+ get_tod_clock_ext(diag0c_data->hdr.tod_ext);
+ diag0c_data->hdr.len = count * sizeof(struct hypfs_diag0c_entry);
+ diag0c_data->hdr.version = DBFS_D0C_HDR_VERSION;
+ diag0c_data->hdr.count = count;
+ *data = diag0c_data;
+ *data_free_ptr = diag0c_data;
+ *size = diag0c_data->hdr.len + sizeof(struct hypfs_diag0c_hdr);
+ return 0;
+}
+
+/*
+ * Hypfs DBFS file structure
+ */
+static struct hypfs_dbfs_file dbfs_file_0c = {
+ .name = "diag_0c",
+ .data_create = dbfs_diag0c_create,
+ .data_free = dbfs_diag0c_free,
+};
+
+/*
+ * Initialize diag 0c interface for z/VM
+ */
+int __init hypfs_diag0c_init(void)
+{
+ if (!MACHINE_IS_VM)
+ return 0;
+ return hypfs_dbfs_create_file(&dbfs_file_0c);
+}
+
+/*
+ * Shutdown diag 0c interface for z/VM
+ */
+void hypfs_diag0c_exit(void)
+{
+ if (!MACHINE_IS_VM)
+ return;
+ hypfs_dbfs_remove_file(&dbfs_file_0c);
+}
diff --git a/arch/s390/hypfs/inode.c b/arch/s390/hypfs/inode.c
index c952b98..99824ff 100644
--- a/arch/s390/hypfs/inode.c
+++ b/arch/s390/hypfs/inode.c
@@ -74,7 +74,7 @@ static void hypfs_remove(struct dentry *dentry)
parent = dentry->d_parent;
mutex_lock(&parent->d_inode->i_mutex);
if (hypfs_positive(dentry)) {
- if (S_ISDIR(dentry->d_inode->i_mode))
+ if (d_is_dir(dentry))
simple_rmdir(parent->d_inode, dentry);
else
simple_unlink(parent->d_inode, dentry);
@@ -144,36 +144,32 @@ static int hypfs_open(struct inode *inode, struct file *filp)
return nonseekable_open(inode, filp);
}
-static ssize_t hypfs_aio_read(struct kiocb *iocb, const struct iovec *iov,
- unsigned long nr_segs, loff_t offset)
+static ssize_t hypfs_read_iter(struct kiocb *iocb, struct iov_iter *to)
{
- char *data;
- ssize_t ret;
- struct file *filp = iocb->ki_filp;
- /* XXX: temporary */
- char __user *buf = iov[0].iov_base;
- size_t count = iov[0].iov_len;
-
- if (nr_segs != 1)
- return -EINVAL;
-
- data = filp->private_data;
- ret = simple_read_from_buffer(buf, count, &offset, data, strlen(data));
- if (ret <= 0)
- return ret;
-
- iocb->ki_pos += ret;
- file_accessed(filp);
+ struct file *file = iocb->ki_filp;
+ char *data = file->private_data;
+ size_t available = strlen(data);
+ loff_t pos = iocb->ki_pos;
+ size_t count;
- return ret;
+ if (pos < 0)
+ return -EINVAL;
+ if (pos >= available || !iov_iter_count(to))
+ return 0;
+ count = copy_to_iter(data + pos, available - pos, to);
+ if (!count)
+ return -EFAULT;
+ iocb->ki_pos = pos + count;
+ file_accessed(file);
+ return count;
}
-static ssize_t hypfs_aio_write(struct kiocb *iocb, const struct iovec *iov,
- unsigned long nr_segs, loff_t offset)
+
+static ssize_t hypfs_write_iter(struct kiocb *iocb, struct iov_iter *from)
{
int rc;
struct super_block *sb = file_inode(iocb->ki_filp)->i_sb;
struct hypfs_sb_info *fs_info = sb->s_fs_info;
- size_t count = iov_length(iov, nr_segs);
+ size_t count = iov_iter_count(from);
/*
* Currently we only allow one update per second for two reasons:
@@ -202,6 +198,7 @@ static ssize_t hypfs_aio_write(struct kiocb *iocb, const struct iovec *iov,
}
hypfs_update_update(sb);
rc = count;
+ iov_iter_advance(from, count);
out:
mutex_unlock(&fs_info->lock);
return rc;
@@ -440,10 +437,10 @@ struct dentry *hypfs_create_str(struct dentry *dir,
static const struct file_operations hypfs_file_ops = {
.open = hypfs_open,
.release = hypfs_release,
- .read = do_sync_read,
- .write = do_sync_write,
- .aio_read = hypfs_aio_read,
- .aio_write = hypfs_aio_write,
+ .read = new_sync_read,
+ .write = new_sync_write,
+ .read_iter = hypfs_read_iter,
+ .write_iter = hypfs_write_iter,
.llseek = no_llseek,
};
@@ -482,10 +479,14 @@ static int __init hypfs_init(void)
rc = -ENODATA;
goto fail_hypfs_vm_exit;
}
+ if (hypfs_diag0c_init()) {
+ rc = -ENODATA;
+ goto fail_hypfs_sprp_exit;
+ }
s390_kobj = kobject_create_and_add("s390", hypervisor_kobj);
if (!s390_kobj) {
rc = -ENOMEM;
- goto fail_hypfs_sprp_exit;
+ goto fail_hypfs_diag0c_exit;
}
rc = register_filesystem(&hypfs_type);
if (rc)
@@ -494,6 +495,8 @@ static int __init hypfs_init(void)
fail_filesystem:
kobject_put(s390_kobj);
+fail_hypfs_diag0c_exit:
+ hypfs_diag0c_exit();
fail_hypfs_sprp_exit:
hypfs_sprp_exit();
fail_hypfs_vm_exit:
@@ -510,6 +513,7 @@ static void __exit hypfs_exit(void)
{
unregister_filesystem(&hypfs_type);
kobject_put(s390_kobj);
+ hypfs_diag0c_exit();
hypfs_sprp_exit();
hypfs_vm_exit();
hypfs_diag_exit();