]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
s390/hypfs: add interface for diagnose 0x304
authorMartin Schwidefsky <schwidefsky@de.ibm.com>
Fri, 24 Jan 2014 08:18:52 +0000 (09:18 +0100)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Fri, 24 Jan 2014 08:40:59 +0000 (09:40 +0100)
To provide access to the set-partition-resource-parameter interface
to user space add a new attribute to hypfs/debugfs:
 * s390_hypsfs/diag_304
The data for the query-partition-resource-parameters command can
be access by a read on the attribute. All other diagnose 0x304
requests need to be submitted via ioctl with CAP_SYS_ADMIN rights.

Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Documentation/ioctl/ioctl-number.txt
arch/s390/hypfs/Makefile
arch/s390/hypfs/hypfs.h
arch/s390/hypfs/hypfs_dbfs.c
arch/s390/hypfs/hypfs_sprp.c [new file with mode: 0644]
arch/s390/hypfs/inode.c
arch/s390/include/asm/sclp.h
arch/s390/include/uapi/asm/hypfs.h [new file with mode: 0644]
drivers/s390/char/sclp_cmd.c

index 7cbfa3c4fc3d327c8b1d9c3ef8a4c1228998a69b..d7e43fa88575b1845a38b9471eba70910d67d344 100644 (file)
@@ -73,6 +73,7 @@ Code  Seq#(hex)       Include File            Comments
 0x09   all     linux/raid/md_u.h
 0x10   00-0F   drivers/char/s390/vmcp.h
 0x10   10-1F   arch/s390/include/uapi/sclp_ctl.h
+0x10   20-2F   arch/s390/include/uapi/asm/hypfs.h
 0x12   all     linux/fs.h
                linux/blkpg.h
 0x1b   all     InfiniBand Subsystem    <http://infiniband.sourceforge.net/>
index 2e671d5004ca56651a0f246d91a5a65b81889603..06f8d95a16cdc9767d4538ea1798c32bfac73187 100644 (file)
@@ -4,4 +4,4 @@
 
 obj-$(CONFIG_S390_HYPFS_FS) += s390_hypfs.o
 
-s390_hypfs-objs := inode.o hypfs_diag.o hypfs_vm.o hypfs_dbfs.o
+s390_hypfs-objs := inode.o hypfs_diag.o hypfs_vm.o hypfs_dbfs.o hypfs_sprp.o
index 79f2ac55253f9289c3864f2ee7b286d67aca58bd..b34b5ab90a313b77bd0561503d882af311a50c7d 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/debugfs.h>
 #include <linux/workqueue.h>
 #include <linux/kref.h>
+#include <asm/hypfs.h>
 
 #define REG_FILE_MODE    0440
 #define UPDATE_FILE_MODE 0220
@@ -36,6 +37,10 @@ extern int hypfs_vm_init(void);
 extern void hypfs_vm_exit(void);
 extern int hypfs_vm_create_files(struct dentry *root);
 
+/* Set Partition-Resource Parameter */
+int hypfs_sprp_init(void);
+void hypfs_sprp_exit(void);
+
 /* debugfs interface */
 struct hypfs_dbfs_file;
 
@@ -52,6 +57,8 @@ struct hypfs_dbfs_file {
        int             (*data_create)(void **data, void **data_free_ptr,
                                       size_t *size);
        void            (*data_free)(const void *buf_free_ptr);
+       long            (*unlocked_ioctl) (struct file *, unsigned int,
+                                          unsigned long);
 
        /* Private data for hypfs_dbfs.c */
        struct hypfs_dbfs_data  *data;
index 17ab8b7b53cc58074fce7c214caa1e543164d991..2badf2bf9cd74217806129bb5a578ac1fd8e1131 100644 (file)
@@ -81,9 +81,25 @@ static ssize_t dbfs_read(struct file *file, char __user *buf,
        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)
diff --git a/arch/s390/hypfs/hypfs_sprp.c b/arch/s390/hypfs/hypfs_sprp.c
new file mode 100644 (file)
index 0000000..f043c3c
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ *    Hypervisor filesystem for Linux on s390.
+ *    Set Partition-Resource Parameter interface.
+ *
+ *    Copyright IBM Corp. 2013
+ *    Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
+ */
+
+#include <linux/compat.h>
+#include <linux/errno.h>
+#include <linux/gfp.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <asm/compat.h>
+#include <asm/sclp.h>
+#include "hypfs.h"
+
+#define DIAG304_SET_WEIGHTS    0
+#define DIAG304_QUERY_PRP      1
+#define DIAG304_SET_CAPPING    2
+
+#define DIAG304_CMD_MAX                2
+
+static unsigned long hypfs_sprp_diag304(void *data, unsigned long cmd)
+{
+       register unsigned long _data asm("2") = (unsigned long) data;
+       register unsigned long _rc asm("3");
+       register unsigned long _cmd asm("4") = cmd;
+
+       asm volatile("diag %1,%2,0x304\n"
+                    : "=d" (_rc) : "d" (_data), "d" (_cmd) : "memory");
+
+       return _rc;
+}
+
+static void hypfs_sprp_free(const void *data)
+{
+       free_page((unsigned long) data);
+}
+
+static int hypfs_sprp_create(void **data_ptr, void **free_ptr, size_t *size)
+{
+       unsigned long rc;
+       void *data;
+
+       data = (void *) get_zeroed_page(GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+       rc = hypfs_sprp_diag304(data, DIAG304_QUERY_PRP);
+       if (rc != 1) {
+               *data_ptr = *free_ptr = NULL;
+               *size = 0;
+               free_page((unsigned long) data);
+               return -EIO;
+       }
+       *data_ptr = *free_ptr = data;
+       *size = PAGE_SIZE;
+       return 0;
+}
+
+static int __hypfs_sprp_ioctl(void __user *user_area)
+{
+       struct hypfs_diag304 diag304;
+       unsigned long cmd;
+       void __user *udata;
+       void *data;
+       int rc;
+
+       if (copy_from_user(&diag304, user_area, sizeof(diag304)))
+               return -EFAULT;
+       if ((diag304.args[0] >> 8) != 0 || diag304.args[1] > DIAG304_CMD_MAX)
+               return -EINVAL;
+
+       data = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
+       if (!data)
+               return -ENOMEM;
+
+       udata = (void __user *)(unsigned long) diag304.data;
+       if (diag304.args[1] == DIAG304_SET_WEIGHTS ||
+           diag304.args[1] == DIAG304_SET_CAPPING)
+               if (copy_from_user(data, udata, PAGE_SIZE)) {
+                       rc = -EFAULT;
+                       goto out;
+               }
+
+       cmd = *(unsigned long *) &diag304.args[0];
+       diag304.rc = hypfs_sprp_diag304(data, cmd);
+
+       if (diag304.args[1] == DIAG304_QUERY_PRP)
+               if (copy_to_user(udata, data, PAGE_SIZE)) {
+                       rc = -EFAULT;
+                       goto out;
+               }
+
+       rc = copy_to_user(user_area, &diag304, sizeof(diag304)) ? -EFAULT : 0;
+out:
+       free_page((unsigned long) data);
+       return rc;
+}
+
+static long hypfs_sprp_ioctl(struct file *file, unsigned int cmd,
+                              unsigned long arg)
+{
+       void __user *argp;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EACCES;
+       if (is_compat_task())
+               argp = compat_ptr(arg);
+       else
+               argp = (void __user *) arg;
+       switch (cmd) {
+       case HYPFS_DIAG304:
+               return __hypfs_sprp_ioctl(argp);
+       default: /* unknown ioctl number */
+               return -ENOTTY;
+       }
+       return 0;
+}
+
+static struct hypfs_dbfs_file hypfs_sprp_file = {
+       .name           = "diag_304",
+       .data_create    = hypfs_sprp_create,
+       .data_free      = hypfs_sprp_free,
+       .unlocked_ioctl = hypfs_sprp_ioctl,
+};
+
+int hypfs_sprp_init(void)
+{
+       if (!sclp_has_sprp())
+               return 0;
+       return hypfs_dbfs_create_file(&hypfs_sprp_file);
+}
+
+void hypfs_sprp_exit(void)
+{
+       if (!sclp_has_sprp())
+               return;
+       hypfs_dbfs_remove_file(&hypfs_sprp_file);
+}
index ddfe09b4513492cd2700d691e61d70ad38f8379b..c952b981e4f28885223c7f77b51ada2299f03c6a 100644 (file)
@@ -478,10 +478,14 @@ static int __init hypfs_init(void)
                rc = -ENODATA;
                goto fail_hypfs_diag_exit;
        }
+       if (hypfs_sprp_init()) {
+               rc = -ENODATA;
+               goto fail_hypfs_vm_exit;
+       }
        s390_kobj = kobject_create_and_add("s390", hypervisor_kobj);
        if (!s390_kobj) {
                rc = -ENOMEM;
-               goto fail_hypfs_vm_exit;
+               goto fail_hypfs_sprp_exit;
        }
        rc = register_filesystem(&hypfs_type);
        if (rc)
@@ -490,6 +494,8 @@ static int __init hypfs_init(void)
 
 fail_filesystem:
        kobject_put(s390_kobj);
+fail_hypfs_sprp_exit:
+       hypfs_sprp_exit();
 fail_hypfs_vm_exit:
        hypfs_vm_exit();
 fail_hypfs_diag_exit:
@@ -502,11 +508,12 @@ static int __init hypfs_init(void)
 
 static void __exit hypfs_exit(void)
 {
-       hypfs_diag_exit();
-       hypfs_vm_exit();
-       hypfs_dbfs_exit();
        unregister_filesystem(&hypfs_type);
        kobject_put(s390_kobj);
+       hypfs_sprp_exit();
+       hypfs_vm_exit();
+       hypfs_diag_exit();
+       hypfs_dbfs_exit();
 }
 
 module_init(hypfs_init)
index 220e171413f857210012d7a09f07dfb904c21a10..abaca2275c7a5acb3f0f730539e4d8d5fd6cd19f 100644 (file)
@@ -54,6 +54,7 @@ int sclp_chp_read_info(struct sclp_chp_info *info);
 void sclp_get_ipl_info(struct sclp_ipl_info *info);
 bool __init sclp_has_linemode(void);
 bool __init sclp_has_vt220(void);
+bool sclp_has_sprp(void);
 int sclp_pci_configure(u32 fid);
 int sclp_pci_deconfigure(u32 fid);
 int memcpy_hsa(void *dest, unsigned long src, size_t count, int mode);
diff --git a/arch/s390/include/uapi/asm/hypfs.h b/arch/s390/include/uapi/asm/hypfs.h
new file mode 100644 (file)
index 0000000..37998b4
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * IOCTL interface for hypfs
+ *
+ * Copyright IBM Corp. 2013
+ *
+ * Author: Martin Schwidefsky <schwidefsky@de.ibm.com>
+ */
+
+#ifndef _ASM_HYPFS_CTL_H
+#define _ASM_HYPFS_CTL_H
+
+#include <linux/types.h>
+
+struct hypfs_diag304 {
+       __u32   args[2];
+       __u64   data;
+       __u64   rc;
+} __attribute__((packed));
+
+#define HYPFS_IOCTL_MAGIC 0x10
+
+#define HYPFS_DIAG304 \
+       _IOWR(HYPFS_IOCTL_MAGIC, 0x20, struct hypfs_diag304)
+
+#endif
index cb3c4e05a38503c043f35f1f9a70bc8f186e929f..49af8eeb90ea2b3cbd5786bdd075727b20a9a327 100644 (file)
@@ -700,3 +700,8 @@ int sclp_chp_read_info(struct sclp_chp_info *info)
        free_page((unsigned long) sccb);
        return rc;
 }
+
+bool sclp_has_sprp(void)
+{
+       return !!(sclp_fac84 & 0x2);
+}