]> Pileus Git - ~andy/linux/commitdiff
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 28 Jan 2014 17:02:24 +0000 (09:02 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 28 Jan 2014 17:02:24 +0000 (09:02 -0800)
Pull s390 patches from Martin Schwidefsky:
 "A new binary interface to be able to query and modify the LPAR
  scheduler weight and cap settings.  Some improvements for the hvc
  terminal over iucv and a couple of bux fixes"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux:
  s390/hypfs: add interface for diagnose 0x304
  s390: wire up sys_sched_setattr/sys_sched_getattr
  s390/uapi: fix struct statfs64 definition
  s390/uaccess: remove dead extern declarations, make functions static
  s390/uaccess: test if current->mm is set before walking page tables
  s390/zfcpdump: make zfcpdump depend on 64BIT
  s390/32bit: fix cmpxchg64
  s390/xpram: don't modify module parameters
  s390/zcrypt: remove zcrypt kmsg documentation again
  s390/hvc_iucv: Automatically assign free HVC terminal devices
  s390/hvc_iucv: Display connection details through device attributes
  s390/hvc_iucv: fix sparse warning
  s390/vmur: Link parent CCW device during UR device creation

21 files changed:
Documentation/ioctl/ioctl-number.txt
Documentation/kmsg/s390/zcrypt [deleted file]
arch/s390/Kconfig
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/cmpxchg.h
arch/s390/include/asm/sclp.h
arch/s390/include/uapi/asm/hypfs.h [new file with mode: 0644]
arch/s390/include/uapi/asm/statfs.h
arch/s390/include/uapi/asm/unistd.h
arch/s390/kernel/compat_wrapper.S
arch/s390/kernel/syscalls.S
arch/s390/lib/uaccess.h
arch/s390/lib/uaccess_pt.c
drivers/s390/block/xpram.c
drivers/s390/char/sclp_cmd.c
drivers/s390/char/vmur.c
drivers/tty/hvc/hvc_iucv.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/>
diff --git a/Documentation/kmsg/s390/zcrypt b/Documentation/kmsg/s390/zcrypt
deleted file mode 100644 (file)
index 7fb2087..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-/*?
- * Text: "Cryptographic device %x failed and was set offline\n"
- * Severity: Error
- * Parameter:
- *   @1: device index
- * Description:
- * A cryptographic device failed to process a cryptographic request.
- * The cryptographic device driver could not correct the error and
- * set the device offline. The application that issued the
- * request received an indication that the request has failed.
- * User action:
- * Use the lszcrypt command to confirm that the cryptographic
- * hardware is still configured to your LPAR or z/VM guest virtual
- * machine. If the device is available to your Linux instance the
- * command output contains a line that begins with 'card<device index>',
- * where <device index> is the two-digit decimal number in the message text.
- * After ensuring that the device is available, use the chzcrypt command to
- * set it online again.
- * If the error persists, contact your support organization.
- */
index 4f858f77d870d842fa99fdc26e00b13521ae35e3..65a07750f4f946f038bb2e2a4e13f9f6d05dce83 100644 (file)
@@ -596,7 +596,7 @@ config CRASH_DUMP
 config ZFCPDUMP
        def_bool n
        prompt "zfcpdump support"
-       depends on SMP
+       depends on 64BIT && SMP
        help
          Select this option if you want to build an zfcpdump enabled kernel.
          Refer to <file:Documentation/s390/zfcpdump.txt> for more details on this.
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 @@ fail_dbfs_exit:
 
 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 0f636cbdf3420973f169a591d03614b5f173ca11..4236408070e5f7cb163e1be623627ffc398e677f 100644 (file)
@@ -185,11 +185,12 @@ static inline unsigned long long __cmpxchg64(void *ptr,
 {
        register_pair rp_old = {.pair = old};
        register_pair rp_new = {.pair = new};
+       unsigned long long *ullptr = ptr;
 
        asm volatile(
                "       cds     %0,%2,%1"
-               : "+&d" (rp_old), "=Q" (ptr)
-               : "d" (rp_new), "Q" (ptr)
+               : "+d" (rp_old), "+Q" (*ullptr)
+               : "d" (rp_new)
                : "memory", "cc");
        return rp_old.pair;
 }
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 a61d538756f29c9ec6f932aec471c2f74de93e8a..471eb09184d4558ae59963adac953443bc055fbe 100644 (file)
@@ -35,11 +35,11 @@ struct statfs {
 struct statfs64 {
        unsigned int    f_type;
        unsigned int    f_bsize;
-       unsigned long   f_blocks;
-       unsigned long   f_bfree;
-       unsigned long   f_bavail;
-       unsigned long   f_files;
-       unsigned long   f_ffree;
+       unsigned long long f_blocks;
+       unsigned long long f_bfree;
+       unsigned long long f_bavail;
+       unsigned long long f_files;
+       unsigned long long f_ffree;
        __kernel_fsid_t f_fsid;
        unsigned int    f_namelen;
        unsigned int    f_frsize;
index 864f693c237fe440cd02125e588c4b5867b6edb8..5eb5c9ddb120027df003990329a0b7ede4cbfa59 100644 (file)
 #define __NR_s390_runtime_instr 342
 #define __NR_kcmp              343
 #define __NR_finit_module      344
+#define __NR_sched_setattr     345
+#define __NR_sched_getattr     346
 #define NR_syscalls 345
 
 /* 
index 9cb1b975b3532f3cc6237ab1cd168146f99a9904..59c8efce1b99b52964950f0933b4934a12b6a9db 100644 (file)
@@ -1412,3 +1412,14 @@ ENTRY(sys_finit_module_wrapper)
        llgtr   %r3,%r3                 # const char __user *
        lgfr    %r4,%r4                 # int
        jg      sys_finit_module
+
+ENTRY(sys_sched_setattr_wrapper)
+       lgfr    %r2,%r2                 # pid_t
+       llgtr   %r3,%r3                 # struct sched_attr __user *
+       jg      sys_sched_setattr
+
+ENTRY(sys_sched_getattr_wrapper)
+       lgfr    %r2,%r2                 # pid_t
+       llgtr   %r3,%r3                 # const char __user *
+       llgfr   %r3,%r3                 # unsigned int
+       jg      sys_sched_getattr
index 913410bd74a335c0693637ccb69e49cb89fd63c1..143992152ec95d7c2b96c1978d1bae78da480efb 100644 (file)
@@ -353,3 +353,5 @@ SYSCALL(sys_process_vm_writev,sys_process_vm_writev,compat_sys_process_vm_writev
 SYSCALL(sys_ni_syscall,sys_s390_runtime_instr,sys_s390_runtime_instr_wrapper)
 SYSCALL(sys_kcmp,sys_kcmp,sys_kcmp_wrapper)
 SYSCALL(sys_finit_module,sys_finit_module,sys_finit_module_wrapper)
+SYSCALL(sys_sched_setattr,sys_sched_setattr,sys_sched_setattr_wrapper) /* 345 */
+SYSCALL(sys_sched_getattr,sys_sched_getattr,sys_sched_getattr_wrapper)
index 315dbe09983e518b06326b2f730c45a176a57aa5..b1a22173d027b9bde6774194631d635932bc42ff 100644 (file)
@@ -6,15 +6,6 @@
 #ifndef __ARCH_S390_LIB_UACCESS_H
 #define __ARCH_S390_LIB_UACCESS_H
 
-extern size_t copy_from_user_std(size_t, const void __user *, void *);
-extern size_t copy_to_user_std(size_t, void __user *, const void *);
-extern size_t strnlen_user_std(size_t, const char __user *);
-extern size_t strncpy_from_user_std(size_t, const char __user *, char *);
-extern int futex_atomic_cmpxchg_std(u32 *, u32 __user *, u32, u32);
-extern int futex_atomic_op_std(int, u32 __user *, int, int *);
-
-extern size_t copy_from_user_pt(size_t, const void __user *, void *);
-extern size_t copy_to_user_pt(size_t, void __user *, const void *);
 extern int futex_atomic_op_pt(int, u32 __user *, int, int *);
 extern int futex_atomic_cmpxchg_pt(u32 *, u32 __user *, u32, u32);
 
index 0632dc50da78b88557ca61c766b29bccbb30a64d..61ebcc9ccb3472abe320fd8ed4939b4cc5074ebe 100644 (file)
@@ -153,6 +153,8 @@ static __always_inline size_t __user_copy_pt(unsigned long uaddr, void *kptr,
        unsigned long offset, done, size, kaddr;
        void *from, *to;
 
+       if (!mm)
+               return n;
        done = 0;
 retry:
        spin_lock(&mm->page_table_lock);
@@ -209,7 +211,7 @@ fault:
        return 0;
 }
 
-size_t copy_from_user_pt(size_t n, const void __user *from, void *to)
+static size_t copy_from_user_pt(size_t n, const void __user *from, void *to)
 {
        size_t rc;
 
@@ -221,7 +223,7 @@ size_t copy_from_user_pt(size_t n, const void __user *from, void *to)
        return rc;
 }
 
-size_t copy_to_user_pt(size_t n, void __user *to, const void *from)
+static size_t copy_to_user_pt(size_t n, void __user *to, const void *from)
 {
        if (segment_eq(get_fs(), KERNEL_DS))
                return copy_in_kernel(n, to, (void __user *) from);
@@ -262,6 +264,8 @@ static size_t strnlen_user_pt(size_t count, const char __user *src)
                return 0;
        if (segment_eq(get_fs(), KERNEL_DS))
                return strnlen_kernel(count, src);
+       if (!mm)
+               return 0;
        done = 0;
 retry:
        spin_lock(&mm->page_table_lock);
@@ -323,6 +327,8 @@ static size_t copy_in_user_pt(size_t n, void __user *to,
 
        if (segment_eq(get_fs(), KERNEL_DS))
                return copy_in_kernel(n, to, from);
+       if (!mm)
+               return n;
        done = 0;
 retry:
        spin_lock(&mm->page_table_lock);
@@ -411,6 +417,8 @@ int futex_atomic_op_pt(int op, u32 __user *uaddr, int oparg, int *old)
 
        if (segment_eq(get_fs(), KERNEL_DS))
                return __futex_atomic_op_pt(op, uaddr, oparg, old);
+       if (unlikely(!current->mm))
+               return -EFAULT;
        spin_lock(&current->mm->page_table_lock);
        uaddr = (u32 __force __user *)
                __dat_user_addr((__force unsigned long) uaddr, 1);
@@ -448,6 +456,8 @@ int futex_atomic_cmpxchg_pt(u32 *uval, u32 __user *uaddr,
 
        if (segment_eq(get_fs(), KERNEL_DS))
                return __futex_atomic_cmpxchg_pt(uval, uaddr, oldval, newval);
+       if (unlikely(!current->mm))
+               return -EFAULT;
        spin_lock(&current->mm->page_table_lock);
        uaddr = (u32 __force __user *)
                __dat_user_addr((__force unsigned long) uaddr, 1);
index 464dd29d06c07dcbeb1e6ee96a439dbb037a25a9..58141f0651f280b4cc48b510f09455edf8b6cc0a 100644 (file)
@@ -257,6 +257,7 @@ static int __init xpram_setup_sizes(unsigned long pages)
        unsigned long mem_needed;
        unsigned long mem_auto;
        unsigned long long size;
+       char *sizes_end;
        int mem_auto_no;
        int i;
 
@@ -275,8 +276,8 @@ static int __init xpram_setup_sizes(unsigned long pages)
        mem_auto_no = 0;
        for (i = 0; i < xpram_devs; i++) {
                if (sizes[i]) {
-                       size = simple_strtoull(sizes[i], &sizes[i], 0);
-                       switch (sizes[i][0]) {
+                       size = simple_strtoull(sizes[i], &sizes_end, 0);
+                       switch (*sizes_end) {
                        case 'g':
                        case 'G':
                                size <<= 20;
index cb3c4e05a38503c043f35f1f9a70bc8f186e929f..49af8eeb90ea2b3cbd5786bdd075727b20a9a327 100644 (file)
@@ -700,3 +700,8 @@ out:
        free_page((unsigned long) sccb);
        return rc;
 }
+
+bool sclp_has_sprp(void)
+{
+       return !!(sclp_fac84 & 0x2);
+}
index 64c467998a90cbb47e5eed32cee3a0f6dc8a8ae3..0efb27f6f1999f4c85af4587c491b926fc30b251 100644 (file)
@@ -922,8 +922,8 @@ static int ur_set_online(struct ccw_device *cdev)
                goto fail_free_cdev;
        }
 
-       urd->device = device_create(vmur_class, NULL, urd->char_device->dev,
-                                   NULL, "%s", node_id);
+       urd->device = device_create(vmur_class, &cdev->dev,
+                                   urd->char_device->dev, NULL, "%s", node_id);
        if (IS_ERR(urd->device)) {
                rc = PTR_ERR(urd->device);
                TRACE("ur_set_online: device_create rc=%d\n", rc);
index db19a38c8c69b0d33dddac2c1fff95f3f79b80f3..ea74460f363862507cbf85af2b2873ae8208ae72 100644 (file)
@@ -77,6 +77,7 @@ struct hvc_iucv_private {
        struct list_head        tty_outqueue;   /* outgoing IUCV messages */
        struct list_head        tty_inqueue;    /* incoming IUCV messages */
        struct device           *dev;           /* device structure */
+       u8                      info_path[16];  /* IUCV path info (dev attr) */
 };
 
 struct iucv_tty_buffer {
@@ -126,7 +127,7 @@ static struct iucv_handler hvc_iucv_handler = {
  * This function returns the struct hvc_iucv_private instance that corresponds
  * to the HVC virtual terminal number specified as parameter @num.
  */
-struct hvc_iucv_private *hvc_iucv_get_private(uint32_t num)
+static struct hvc_iucv_private *hvc_iucv_get_private(uint32_t num)
 {
        if ((num < HVC_IUCV_MAGIC) || (num - HVC_IUCV_MAGIC > hvc_iucv_devices))
                return NULL;
@@ -772,18 +773,37 @@ static int hvc_iucv_filter_connreq(u8 ipvmid[8])
 static int hvc_iucv_path_pending(struct iucv_path *path,
                                  u8 ipvmid[8], u8 ipuser[16])
 {
-       struct hvc_iucv_private *priv;
+       struct hvc_iucv_private *priv, *tmp;
+       u8 wildcard[9] = "lnxhvc  ";
+       int i, rc, find_unused;
        u8 nuser_data[16];
        u8 vm_user_id[9];
-       int i, rc;
 
+       ASCEBC(wildcard, sizeof(wildcard));
+       find_unused = !memcmp(wildcard, ipuser, 8);
+
+       /* First, check if the pending path request is managed by this
+        * IUCV handler:
+        * - find a disconnected device if ipuser contains the wildcard
+        * - find the device that matches the terminal ID in ipuser
+        */
        priv = NULL;
-       for (i = 0; i < hvc_iucv_devices; i++)
-               if (hvc_iucv_table[i] &&
-                   (0 == memcmp(hvc_iucv_table[i]->srv_name, ipuser, 8))) {
-                       priv = hvc_iucv_table[i];
+       for (i = 0; i < hvc_iucv_devices; i++) {
+               tmp = hvc_iucv_table[i];
+               if (!tmp)
+                       continue;
+
+               if (find_unused) {
+                       spin_lock(&tmp->lock);
+                       if (tmp->iucv_state == IUCV_DISCONN)
+                               priv = tmp;
+                       spin_unlock(&tmp->lock);
+
+               } else if (!memcmp(tmp->srv_name, ipuser, 8))
+                               priv = tmp;
+               if (priv)
                        break;
-               }
+       }
        if (!priv)
                return -ENODEV;
 
@@ -826,6 +846,10 @@ static     int hvc_iucv_path_pending(struct iucv_path *path,
        priv->path = path;
        priv->iucv_state = IUCV_CONNECTED;
 
+       /* store path information */
+       memcpy(priv->info_path, ipvmid, 8);
+       memcpy(priv->info_path + 8, ipuser + 8, 8);
+
        /* flush buffered output data... */
        schedule_delayed_work(&priv->sndbuf_work, 5);
 
@@ -960,6 +984,49 @@ static int hvc_iucv_pm_restore_thaw(struct device *dev)
        return 0;
 }
 
+static ssize_t hvc_iucv_dev_termid_show(struct device *dev,
+                                       struct device_attribute *attr,
+                                       char *buf)
+{
+       struct hvc_iucv_private *priv = dev_get_drvdata(dev);
+       size_t len;
+
+       len = sizeof(priv->srv_name);
+       memcpy(buf, priv->srv_name, len);
+       EBCASC(buf, len);
+       buf[len++] = '\n';
+       return len;
+}
+
+static ssize_t hvc_iucv_dev_state_show(struct device *dev,
+                                       struct device_attribute *attr,
+                                       char *buf)
+{
+       struct hvc_iucv_private *priv = dev_get_drvdata(dev);
+       return sprintf(buf, "%u:%u\n", priv->iucv_state, priv->tty_state);
+}
+
+static ssize_t hvc_iucv_dev_peer_show(struct device *dev,
+                                     struct device_attribute *attr,
+                                     char *buf)
+{
+       struct hvc_iucv_private *priv = dev_get_drvdata(dev);
+       char vmid[9], ipuser[9];
+
+       memset(vmid, 0, sizeof(vmid));
+       memset(ipuser, 0, sizeof(ipuser));
+
+       spin_lock_bh(&priv->lock);
+       if (priv->iucv_state == IUCV_CONNECTED) {
+               memcpy(vmid, priv->info_path, 8);
+               memcpy(ipuser, priv->info_path + 8, 8);
+       }
+       spin_unlock_bh(&priv->lock);
+       EBCASC(ipuser, 8);
+
+       return sprintf(buf, "%s:%s\n", vmid, ipuser);
+}
+
 
 /* HVC operations */
 static const struct hv_ops hvc_iucv_ops = {
@@ -985,6 +1052,25 @@ static struct device_driver hvc_iucv_driver = {
        .pm   = &hvc_iucv_pm_ops,
 };
 
+/* IUCV HVC device attributes */
+static DEVICE_ATTR(termid, 0640, hvc_iucv_dev_termid_show, NULL);
+static DEVICE_ATTR(state, 0640, hvc_iucv_dev_state_show, NULL);
+static DEVICE_ATTR(peer, 0640, hvc_iucv_dev_peer_show, NULL);
+static struct attribute *hvc_iucv_dev_attrs[] = {
+       &dev_attr_termid.attr,
+       &dev_attr_state.attr,
+       &dev_attr_peer.attr,
+       NULL,
+};
+static struct attribute_group hvc_iucv_dev_attr_group = {
+       .attrs = hvc_iucv_dev_attrs,
+};
+static const struct attribute_group *hvc_iucv_dev_attr_groups[] = {
+       &hvc_iucv_dev_attr_group,
+       NULL,
+};
+
+
 /**
  * hvc_iucv_alloc() - Allocates a new struct hvc_iucv_private instance
  * @id:                        hvc_iucv_table index
@@ -1046,6 +1132,7 @@ static int __init hvc_iucv_alloc(int id, unsigned int is_console)
        priv->dev->bus = &iucv_bus;
        priv->dev->parent = iucv_root;
        priv->dev->driver = &hvc_iucv_driver;
+       priv->dev->groups = hvc_iucv_dev_attr_groups;
        priv->dev->release = (void (*)(struct device *)) kfree;
        rc = device_register(priv->dev);
        if (rc) {