]> git.hungrycats.org Git - linux/commitdiff
[PATCH] s390: collaborative memory management.
authorAndrew Morton <akpm@osdl.org>
Thu, 26 Feb 2004 00:20:25 +0000 (16:20 -0800)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Thu, 26 Feb 2004 00:20:25 +0000 (16:20 -0800)
From: Martin Schwidefsky <schwidefsky@de.ibm.com>

Add collaborative memory management interface.

arch/s390/Kconfig
arch/s390/defconfig
arch/s390/kernel/s390_ksyms.c
arch/s390/mm/Makefile
arch/s390/mm/cmm.c [new file with mode: 0644]
arch/s390/mm/init.c
drivers/s390/net/Kconfig
drivers/s390/net/Makefile
drivers/s390/net/smsgiucv.c [new file with mode: 0644]
drivers/s390/net/smsgiucv.h [new file with mode: 0644]
include/asm-s390/pgalloc.h

index b95d5b6176190df6e86b7557bd49d9436aaed381..02b03fdfea9ba4f4601d76c7c88df9da4c8dfb26 100644 (file)
@@ -250,6 +250,32 @@ config SHARED_KERNEL
          You should only select this option if you know what you are
          doing and want to exploit this feature.
 
+config CMM
+       tristate "Cooperative memory management"
+       help
+         Select this option, if you want to enable the kernel interface
+         to reduce the memory size of the system. This is accomplished
+         by allocating pages of memory and put them "on hold". This only
+         makes sense for a system running under VM where the unused pages
+         will be reused by VM for other guest systems. The interface
+         allows an external monitor to balance memory of many systems.
+         Everybody who wants to run Linux under VM should select this
+         option.
+
+config CMM_PROC
+       bool "/proc interface to cooperative memory management"
+       depends on CMM
+       help
+         Select this option to enable the /proc interface to the
+         cooperative memory management.
+
+config CMM_IUCV
+       bool "IUCV special message interface to cooperative memory management"
+       depends on CMM && (SMSGIUCV=y || CMM=SMSGIUCV)
+       help
+         Select this option to enable the special message interface to
+         the cooperative memory management.
+
 config VIRT_TIMER
        bool "Virtual CPU timer support"
        help
index ad63babaafea766561abe7ffd8c4e7c671f45fd7..076fb44e2ce92e03957ee727a63468604d6468ce 100644 (file)
@@ -76,6 +76,7 @@ CONFIG_BINFMT_MISC=m
 # CONFIG_PROCESS_DEBUG is not set
 CONFIG_PFAULT=y
 # CONFIG_SHARED_KERNEL is not set
+# CONFIG_CMM is not set
 # CONFIG_VIRT_TIMER is not set
 # CONFIG_PCMCIA is not set
 
@@ -312,6 +313,8 @@ CONFIG_NET_ETHERNET=y
 CONFIG_LCS=m
 CONFIG_CTC=m
 CONFIG_IUCV=m
+# CONFIG_NETIUCV is not set
+# CONFIG_SMSGIUCV is not set
 CONFIG_QETH=y
 
 #
index a743fa888ec4b9ea76e7c40a85a785d2ca45a0eb..b51f748069a4a72d97df84566bc2527e39fb6309 100644 (file)
@@ -32,6 +32,7 @@ EXPORT_SYMBOL_NOVERS(_zb_findmap);
 EXPORT_SYMBOL_NOVERS(__copy_from_user_asm);
 EXPORT_SYMBOL_NOVERS(__copy_to_user_asm);
 EXPORT_SYMBOL_NOVERS(__clear_user_asm);
+EXPORT_SYMBOL(diag10);
 
 /*
  * semaphore ops
index 3360e50a2559cc2860c37738d338db542a7728bb..44183461146cb22628f9594aee69b046bd7b9ee2 100644 (file)
@@ -3,3 +3,5 @@
 #
 
 obj-y   := init.o fault.o ioremap.o
+obj-$(CONFIG_CMM) += cmm.o
+
diff --git a/arch/s390/mm/cmm.c b/arch/s390/mm/cmm.c
new file mode 100644 (file)
index 0000000..f3221ed
--- /dev/null
@@ -0,0 +1,445 @@
+/*
+ *  arch/s390/mm/cmm.c
+ *
+ *  S390 version
+ *    Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ *    Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
+ *
+ *  Collaborative memory management interface.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/sysctl.h>
+#include <linux/ctype.h>
+
+#include <asm/pgalloc.h>
+#include <asm/uaccess.h>
+
+#include "../../../drivers/s390/net/smsgiucv.h"
+
+#define CMM_NR_PAGES ((PAGE_SIZE / sizeof(unsigned long)) - 2)
+
+struct cmm_page_array {
+       struct cmm_page_array *next;
+       unsigned long index;
+       unsigned long pages[CMM_NR_PAGES];
+};
+
+static long cmm_pages = 0;
+static long cmm_timed_pages = 0;
+static volatile long cmm_pages_target = 0;
+static volatile long cmm_timed_pages_target = 0;
+static long cmm_timeout_pages = 0;
+static long cmm_timeout_seconds = 0;
+
+static struct cmm_page_array *cmm_page_list = 0;
+static struct cmm_page_array *cmm_timed_page_list = 0;
+
+static unsigned long cmm_thread_active = 0;
+static struct work_struct cmm_thread_starter;
+static wait_queue_head_t cmm_thread_wait;
+static struct timer_list cmm_timer;
+
+static void cmm_timer_fn(unsigned long);
+static void cmm_set_timer(void);
+
+static long
+cmm_strtoul(const char *cp, char **endp)
+{
+       unsigned int base = 10;
+
+       if (*cp == '0') {
+               base = 8;
+               cp++;
+               if ((*cp == 'x' || *cp == 'X') && isxdigit(cp[1])) {
+                       base = 16;
+                       cp++;
+               }
+       }
+       return simple_strtoul(cp, endp, base);
+}
+
+static long
+cmm_alloc_pages(long pages, long *counter, struct cmm_page_array **list)
+{
+       struct cmm_page_array *pa;
+       unsigned long page;
+
+       pa = *list;
+       while (pages) {
+               page = __get_free_page(GFP_NOIO);
+               if (!page)
+                       break;
+               if (!pa || pa->index >= CMM_NR_PAGES) {
+                       /* Need a new page for the page list. */
+                       pa = (struct cmm_page_array *)
+                               __get_free_page(GFP_NOIO);
+                       if (!pa) {
+                               free_page(page);
+                               break;
+                       }
+                       pa->next = *list;
+                       pa->index = 0;
+                       *list = pa;
+               }
+               if (page < 0x80000000UL)
+                       diag10(page);
+               pa->pages[pa->index++] = page;
+               (*counter)++;
+               pages--;
+       }
+       return pages;
+}
+
+static void
+cmm_free_pages(long pages, long *counter, struct cmm_page_array **list)
+{
+       struct cmm_page_array *pa;
+       unsigned long page;
+
+       pa = *list;
+       while (pages) {
+               if (!pa || pa->index <= 0)
+                       break;
+               page = pa->pages[--pa->index];
+               if (pa->index == 0) {
+                       pa = pa->next;
+                       free_page((unsigned long) *list);
+                       *list = pa;
+               }
+               free_page(page);
+               (*counter)--;
+               pages--;
+       }
+}
+
+static int
+cmm_thread(void *dummy)
+{
+       int rc;
+
+       daemonize("cmmthread");
+       set_cpus_allowed(current, cpumask_of_cpu(0));
+       while (1) {
+               rc = wait_event_interruptible(cmm_thread_wait,
+                       (cmm_pages != cmm_pages_target ||
+                        cmm_timed_pages != cmm_timed_pages_target));
+               if (rc == -ERESTARTSYS) {
+                       /* Got kill signal. End thread. */
+                       clear_bit(0, &cmm_thread_active);
+                       cmm_pages_target = cmm_pages;
+                       cmm_timed_pages_target = cmm_timed_pages;
+                       break;
+               }
+               if (cmm_pages_target > cmm_pages) {
+                       if (cmm_alloc_pages(1, &cmm_pages, &cmm_page_list))
+                               cmm_pages_target = cmm_pages;
+               } else if (cmm_pages_target < cmm_pages) {
+                       cmm_free_pages(1, &cmm_pages, &cmm_page_list);
+               }
+               if (cmm_timed_pages_target > cmm_timed_pages) {
+                       if (cmm_alloc_pages(1, &cmm_timed_pages,
+                                          &cmm_timed_page_list))
+                               cmm_timed_pages_target = cmm_timed_pages;
+               } else if (cmm_timed_pages_target < cmm_timed_pages) {
+                       cmm_free_pages(1, &cmm_timed_pages,
+                                      &cmm_timed_page_list);
+               }
+               if (cmm_timed_pages > 0 && !timer_pending(&cmm_timer))
+                       cmm_set_timer();
+       }
+       return 0;
+}
+
+static void
+cmm_start_thread(void)
+{
+       kernel_thread(cmm_thread, 0, 0);
+}
+
+static void
+cmm_kick_thread(void)
+{
+       if (!test_and_set_bit(0, &cmm_thread_active))
+               schedule_work(&cmm_thread_starter);
+       wake_up(&cmm_thread_wait);
+}
+
+static void
+cmm_set_timer(void)
+{
+       if (cmm_timed_pages_target <= 0 || cmm_timeout_seconds <= 0) {
+               if (timer_pending(&cmm_timer))
+                       del_timer(&cmm_timer);
+               return;
+       }
+       if (timer_pending(&cmm_timer)) {
+               if (mod_timer(&cmm_timer, jiffies + cmm_timeout_seconds*HZ))
+                       return;
+       }
+       cmm_timer.function = cmm_timer_fn;
+       cmm_timer.data = 0;
+       cmm_timer.expires = jiffies + cmm_timeout_seconds*HZ;
+       add_timer(&cmm_timer);
+}
+
+static void
+cmm_timer_fn(unsigned long ignored)
+{
+       long pages;
+
+       pages = cmm_timed_pages_target - cmm_timeout_pages;
+       if (pages < 0)
+               cmm_timed_pages_target = 0;
+       else
+               cmm_timed_pages_target = pages;
+       cmm_kick_thread();
+       cmm_set_timer();
+}
+
+void
+cmm_set_pages(long pages)
+{
+       cmm_pages_target = pages;
+       cmm_kick_thread();
+}
+
+long
+cmm_get_pages(void)
+{
+       return cmm_pages;
+}
+
+void
+cmm_add_timed_pages(long pages)
+{
+       cmm_timed_pages_target += pages;
+       cmm_kick_thread();
+}
+
+long
+cmm_get_timed_pages(void)
+{
+       return cmm_timed_pages;
+}
+
+void
+cmm_set_timeout(long pages, long seconds)
+{
+       cmm_timeout_pages = pages;
+       cmm_timeout_seconds = seconds;
+       cmm_set_timer();
+}
+
+static inline int
+cmm_skip_blanks(char *cp, char **endp)
+{
+       char *str;
+
+       for (str = cp; *str == ' ' || *str == '\t'; str++);
+       *endp = str;
+       return str != cp;
+}
+
+#ifdef CONFIG_CMM_PROC
+/* These will someday get removed. */
+#define VM_CMM_PAGES           1111
+#define VM_CMM_TIMED_PAGES     1112
+#define VM_CMM_TIMEOUT         1113
+
+static struct ctl_table cmm_table[];
+
+static int
+cmm_pages_handler(ctl_table *ctl, int write, struct file *filp,
+                 void *buffer, size_t *lenp)
+{
+       char buf[16], *p;
+       long pages;
+       int len;
+
+       if (!*lenp || (filp->f_pos && !write)) {
+               *lenp = 0;
+               return 0;
+       }
+
+       if (write) {
+               len = *lenp;
+               if (copy_from_user(buf, buffer,
+                                  len > sizeof(buf) ? sizeof(buf) : len))
+                       return -EFAULT;
+               buf[sizeof(buf) - 1] = '\0';
+               cmm_skip_blanks(buf, &p);
+               pages = cmm_strtoul(p, &p);
+               if (ctl == &cmm_table[0])
+                       cmm_set_pages(pages);
+               else
+                       cmm_add_timed_pages(pages);
+       } else {
+               if (ctl == &cmm_table[0])
+                       pages = cmm_get_pages();
+               else
+                       pages = cmm_get_timed_pages();
+               len = sprintf(buf, "%ld\n", pages);
+               if (len > *lenp)
+                       len = *lenp;
+               if (copy_to_user(buffer, buf, len))
+                       return -EFAULT;
+       }
+       *lenp = len;
+       filp->f_pos += len;
+       return 0;
+}
+
+static int
+cmm_timeout_handler(ctl_table *ctl, int write, struct file *filp,
+                   void *buffer, size_t *lenp)
+{
+       char buf[64], *p;
+       long pages, seconds;
+       int len;
+
+       if (!*lenp || (filp->f_pos && !write)) {
+               *lenp = 0;
+               return 0;
+       }
+
+       if (write) {
+               len = *lenp;
+               if (copy_from_user(buf, buffer,
+                                  len > sizeof(buf) ? sizeof(buf) : len))
+                       return -EFAULT;
+               buf[sizeof(buf) - 1] = '\0';
+               cmm_skip_blanks(buf, &p);
+               pages = cmm_strtoul(p, &p);
+               cmm_skip_blanks(p, &p);
+               seconds = cmm_strtoul(p, &p);
+               cmm_set_timeout(pages, seconds);
+       } else {
+               len = sprintf(buf, "%ld %ld\n",
+                             cmm_timeout_pages, cmm_timeout_seconds);
+               if (len > *lenp)
+                       len = *lenp;
+               if (copy_to_user(buffer, buf, len))
+                       return -EFAULT;
+       }
+       *lenp = len;
+       filp->f_pos += len;
+       return 0;
+}
+
+static struct ctl_table cmm_table[] = {
+       {
+               .ctl_name       = VM_CMM_PAGES,
+               .procname       = "cmm_pages",
+               .mode           = 0600,
+               .proc_handler   = &cmm_pages_handler,
+       },
+       {
+               .ctl_name       = VM_CMM_TIMED_PAGES,
+               .procname       = "cmm_timed_pages",
+               .mode           = 0600,
+               .proc_handler   = &cmm_pages_handler,
+       },
+       {
+               .ctl_name       = VM_CMM_TIMEOUT,
+               .procname       = "cmm_timeout",
+               .mode           = 0600,
+               .proc_handler   = &cmm_timeout_handler,
+       },
+       { .ctl_name = 0 }
+};
+
+static struct ctl_table cmm_dir_table[] = {
+       {
+               .ctl_name       = CTL_VM,
+               .procname       = "vm",
+               .maxlen         = 0,
+               .mode           = 0555,
+               .child          = cmm_table,
+       },
+       { .ctl_name = 0 }
+};
+#endif
+
+#ifdef CONFIG_CMM_IUCV
+#define SMSG_PREFIX "CMM"
+static void
+cmm_smsg_target(char *msg)
+{
+       long pages, seconds;
+
+       if (!cmm_skip_blanks(msg + strlen(SMSG_PREFIX), &msg))
+               return;
+       if (strncmp(msg, "SHRINK", 6) == 0) {
+               if (!cmm_skip_blanks(msg + 6, &msg))
+                       return;
+               pages = cmm_strtoul(msg, &msg);
+               cmm_skip_blanks(msg, &msg);
+               if (*msg == '\0')
+                       cmm_set_pages(pages);
+       } else if (strncmp(msg, "RELEASE", 7) == 0) {
+               if (!cmm_skip_blanks(msg + 7, &msg))
+                       return;
+               pages = cmm_strtoul(msg, &msg);
+               cmm_skip_blanks(msg, &msg);
+               if (*msg == '\0')
+                       cmm_add_timed_pages(pages);
+       } else if (strncmp(msg, "REUSE", 5) == 0) {
+               if (!cmm_skip_blanks(msg + 5, &msg))
+                       return;
+               pages = cmm_strtoul(msg, &msg);
+               if (!cmm_skip_blanks(msg, &msg))
+                       return;
+               seconds = cmm_strtoul(msg, &msg);
+               cmm_skip_blanks(msg, &msg);
+               if (*msg == '\0')
+                       cmm_set_timeout(pages, seconds);
+       }
+}
+#endif
+
+struct ctl_table_header *cmm_sysctl_header;
+
+static int
+cmm_init (void)
+{
+#ifdef CONFIG_CMM_PROC
+       cmm_sysctl_header = register_sysctl_table(cmm_dir_table, 1);
+#endif
+#ifdef CONFIG_CMM_IUCV
+       smsg_register_callback(SMSG_PREFIX, cmm_smsg_target);
+#endif
+       INIT_WORK(&cmm_thread_starter, (void *) cmm_start_thread, 0);
+       init_waitqueue_head(&cmm_thread_wait);
+       init_timer(&cmm_timer);
+       return 0;
+}
+
+static void
+cmm_exit(void)
+{
+       cmm_free_pages(cmm_pages, &cmm_pages, &cmm_page_list);
+       cmm_free_pages(cmm_timed_pages, &cmm_timed_pages, &cmm_timed_page_list);
+#ifdef CONFIG_CMM_PROC
+       unregister_sysctl_table(cmm_sysctl_header);
+#endif
+#ifdef CONFIG_CMM_IUCV
+       smsg_unregister_callback(SMSG_PREFIX, cmm_smsg_target);
+#endif
+}
+
+module_init(cmm_init);
+module_exit(cmm_exit);
+
+EXPORT_SYMBOL(cmm_set_pages);
+EXPORT_SYMBOL(cmm_get_pages);
+EXPORT_SYMBOL(cmm_add_timed_pages);
+EXPORT_SYMBOL(cmm_get_timed_pages);
+EXPORT_SYMBOL(cmm_set_timeout);
+
+MODULE_LICENSE("GPL");
index b894f76f377de464a1069102e2f15db88cab5dd0..7248a1657fe5d52b98a4ee50831f1a7a47825851 100644 (file)
@@ -40,6 +40,19 @@ DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
 pgd_t swapper_pg_dir[PTRS_PER_PGD] __attribute__((__aligned__(PAGE_SIZE)));
 char  empty_zero_page[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
 
+void diag10(unsigned long addr)
+{
+#ifdef __s390x__
+        if (addr >= 0x80000000)
+                return;
+        asm volatile ("sam31\n\t"
+                      "diag %0,%0,0x10\n\t"
+                      "sam64" : : "a" (addr) );
+#else
+        asm volatile ("diag %0,%0,0x10" : : "a" (addr) );
+#endif
+}
+
 void show_mem(void)
 {
         int i, total = 0, reserved = 0;
index 92bbaa5a8a64699156eac1f619e11b981fc37b4f..ab38a06317957da09e3d2c161ff5f822879b2472 100644 (file)
@@ -23,12 +23,32 @@ config CTC
          called ctc.ko.  If you do not know what it is, it's safe to say "Y".
 
 config IUCV
-       tristate "IUCV device support (VM only)"
-       depends on NETDEVICES
+       tristate "IUCV support (VM only)"
+       help
+         Select this option if you want to use inter-user communication
+         under VM or VIF. If unsure, say "Y" to enable a fast communication
+         link between VM guests. At boot time the user ID of the guest needs
+         to be passed to the kernel. Note that both kernels need to be
+         compiled with this option and both need to be booted with the user ID
+         of the other VM guest.
+
+config NETIUCV
+       tristate "IUCV network device support (VM only)"
+       depends on IUCV && NETDEVICES
        help
          Select this option if you want to use inter-user communication
-         vehicle networking under VM or VIF.  This option is also available
-         as a module which will be called iucv.ko. If unsure, say "Y".
+         vehicle networking under VM or VIF. It enables a fast communication
+         link between VM guests. Using ifconfig a point-to-point connection
+         can be established to the Linux for zSeries and S7390 system
+         running on the other VM guest. This option is also available
+         as a module which will be called netiucv.ko. If unsure, say "Y".
+
+config SMSGIUCV
+       tristate "IUCV special message support (VM only)"
+       depends on IUCV
+       help
+         Select this option if you want to be able to receive SMSG messages
+         from other VM guest systems.
 
 
 config QETH
index 0e4e3834a2afb1320a80ed71ba61c6582e67c529..e2fbc362dfbadd523c7c381e13f1e580c7b1981d 100644 (file)
@@ -4,9 +4,10 @@
 
 ctc-objs := ctcmain.o ctctty.o
 
-obj-$(CONFIG_IUCV) += iucv.o fsm.o
+obj-$(CONFIG_IUCV) += iucv.o
+obj-$(CONFIG_NETIUCV) += netiucv.o fsm.o
+obj-$(CONFIG_SMSGIUCV) += smsgiucv.o
 obj-$(CONFIG_CTC) += ctc.o fsm.o cu3088.o
-obj-$(CONFIG_IUCV) += netiucv.o
 obj-$(CONFIG_LCS) += lcs.o cu3088.o
 qeth_mod-objs := qeth.o qeth_mpc.o
 obj-$(CONFIG_QETH) += qeth_mod.o
diff --git a/drivers/s390/net/smsgiucv.c b/drivers/s390/net/smsgiucv.c
new file mode 100644 (file)
index 0000000..23b151c
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * IUCV special message driver
+ *
+ * Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <asm/cpcmd.h>
+#include <asm/ebcdic.h>
+
+#include "iucv.h"
+
+struct smsg_callback {
+       struct list_head list;
+       char *prefix;
+       int len;
+       void (*callback)(char *str);
+};
+
+MODULE_AUTHOR
+   ("(C) 2003 IBM Corporation by Martin Schwidefsky (schwidefsky@de.ibm.com)");
+MODULE_DESCRIPTION ("Linux for S/390 IUCV special message driver");
+
+static iucv_handle_t smsg_handle;
+static unsigned short smsg_pathid;
+static spinlock_t smsg_list_lock = SPIN_LOCK_UNLOCKED;
+static struct list_head smsg_list = LIST_HEAD_INIT(smsg_list);
+
+static void
+smsg_connection_complete(iucv_ConnectionComplete *eib, void *pgm_data)
+{
+}
+
+
+static void
+smsg_message_pending(iucv_MessagePending *eib, void *pgm_data)
+{
+       struct smsg_callback *cb;
+       unsigned char *msg;
+       unsigned short len;
+       int rc;
+
+       len = eib->ln1msg2.ipbfln1f;
+       msg = kmalloc(len + 1, GFP_ATOMIC|GFP_DMA);
+       if (!msg) {
+               iucv_reject(eib->ippathid, eib->ipmsgid, eib->iptrgcls);
+               return;
+       }
+       rc = iucv_receive(eib->ippathid, eib->ipmsgid, eib->iptrgcls,
+                         msg, len, 0, 0, 0);
+       if (rc == 0) {
+               msg[len] = 0;
+               EBCASC(msg, len);
+               spin_lock(&smsg_list_lock);
+               list_for_each_entry(cb, &smsg_list, list)
+                       if (strncmp(msg + 8, cb->prefix, cb->len) == 0) {
+                               cb->callback(msg + 8);
+                               break;
+                       }
+               spin_unlock(&smsg_list_lock);
+       }
+       kfree(msg);
+}
+
+static iucv_interrupt_ops_t smsg_ops = {
+       .ConnectionComplete = smsg_connection_complete,
+       .MessagePending     = smsg_message_pending,
+};
+
+static struct device_driver smsg_driver = {
+       .name = "SMSGIUCV",
+       .bus  = &iucv_bus,
+};
+
+int
+smsg_register_callback(char *prefix, void (*callback)(char *str))
+{
+       struct smsg_callback *cb;
+
+       cb = kmalloc(sizeof(struct smsg_callback), GFP_KERNEL);
+       if (!cb)
+               return -ENOMEM;
+       cb->prefix = prefix;
+       cb->len = strlen(prefix);
+       cb->callback = callback;
+       spin_lock(&smsg_list_lock);
+       list_add_tail(&cb->list, &smsg_list);
+       spin_unlock(&smsg_list_lock);
+       return 0;
+}
+
+void
+smsg_unregister_callback(char *prefix, void (*callback)(char *str))
+{
+       struct smsg_callback *cb, *tmp;
+
+       spin_lock(&smsg_list_lock);
+       cb = 0;
+       list_for_each_entry(tmp, &smsg_list, list)
+               if (tmp->callback == callback &&
+                   strcmp(tmp->prefix, prefix) == 0) {
+                       cb = tmp;
+                       list_del(&cb->list);
+                       break;
+               }
+       spin_unlock(&smsg_list_lock);
+       kfree(cb);
+}
+
+static void __exit
+smsg_exit(void)
+{
+       if (smsg_handle > 0) {
+               cpcmd("SET SMSG OFF", 0, 0);
+               iucv_sever(smsg_pathid, 0);
+               iucv_unregister_program(smsg_handle);
+               driver_unregister(&smsg_driver);
+       }
+       return;
+}
+
+static int __init
+smsg_init(void)
+{
+       static unsigned char pgmmask[24] = {
+               0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+               0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+               0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+       };
+       int rc;
+
+       rc = driver_register(&smsg_driver);
+       if (rc != 0) {
+               printk(KERN_ERR "SMSGIUCV: failed to register driver.\n");
+               return rc;
+       }
+       smsg_handle = iucv_register_program("SMSGIUCV        ", "*MSG    ",
+                                           pgmmask, &smsg_ops, 0);
+       if (!smsg_handle) {
+               printk(KERN_ERR "SMSGIUCV: failed to register to iucv");
+               driver_unregister(&smsg_driver);
+               return -EIO;    /* better errno ? */
+       }
+       rc = iucv_connect (&smsg_pathid, 1, 0, "*MSG    ", 0, 0, 0, 0,
+                          smsg_handle, 0);
+       if (rc) {
+               printk(KERN_ERR "SMSGIUCV: failed to connect to *MSG");
+               iucv_unregister_program(smsg_handle);
+               driver_unregister(&smsg_driver);
+               smsg_handle = 0;
+               return -EIO;
+       }
+       cpcmd("SET SMSG IUCV", 0, 0);
+       return 0;
+}
+
+module_init(smsg_init);
+module_exit(smsg_exit);
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(smsg_register_callback);
+EXPORT_SYMBOL(smsg_unregister_callback);
diff --git a/drivers/s390/net/smsgiucv.h b/drivers/s390/net/smsgiucv.h
new file mode 100644 (file)
index 0000000..04cd871
--- /dev/null
@@ -0,0 +1,10 @@
+/*
+ * IUCV special message driver
+ *
+ * Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
+ */
+
+int  smsg_register_callback(char *, void (*)(char *));
+void smsg_unregister_callback(char *, void (*)(char *));
+
index 39697ffa96f06588f287f5ec1d0a62cf39cb8ba7..ec34d58cf4637b12bd9c5a2a0275b3df0986b25d 100644 (file)
@@ -21,6 +21,8 @@
 
 #define check_pgt_cache()      do {} while (0)
 
+extern void diag10(unsigned long addr);
+
 /*
  * Allocate and free page tables. The xxx_kernel() versions are
  * used to allocate a kernel page table - this turns on ASN bits