From: Martin Schwidefsky Add collaborative memory management interface. --- 25-akpm/arch/s390/Kconfig | 26 + 25-akpm/arch/s390/defconfig | 3 25-akpm/arch/s390/kernel/s390_ksyms.c | 1 25-akpm/arch/s390/mm/Makefile | 2 25-akpm/arch/s390/mm/cmm.c | 445 ++++++++++++++++++++++++++++++++++ 25-akpm/arch/s390/mm/init.c | 13 25-akpm/drivers/s390/net/Kconfig | 28 +- 25-akpm/drivers/s390/net/Makefile | 5 25-akpm/drivers/s390/net/smsgiucv.c | 180 +++++++++++++ 25-akpm/drivers/s390/net/smsgiucv.h | 10 25-akpm/include/asm-s390/pgalloc.h | 2 11 files changed, 709 insertions(+), 6 deletions(-) diff -puN arch/s390/defconfig~s390-11-collaborative-memory-management arch/s390/defconfig --- 25/arch/s390/defconfig~s390-11-collaborative-memory-management Fri Feb 20 16:00:53 2004 +++ 25-akpm/arch/s390/defconfig Fri Feb 20 16:00:53 2004 @@ -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 # diff -puN arch/s390/Kconfig~s390-11-collaborative-memory-management arch/s390/Kconfig --- 25/arch/s390/Kconfig~s390-11-collaborative-memory-management Fri Feb 20 16:00:53 2004 +++ 25-akpm/arch/s390/Kconfig Fri Feb 20 16:00:53 2004 @@ -255,6 +255,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 diff -puN arch/s390/kernel/s390_ksyms.c~s390-11-collaborative-memory-management arch/s390/kernel/s390_ksyms.c --- 25/arch/s390/kernel/s390_ksyms.c~s390-11-collaborative-memory-management Fri Feb 20 16:00:53 2004 +++ 25-akpm/arch/s390/kernel/s390_ksyms.c Fri Feb 20 16:00:53 2004 @@ -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 diff -puN /dev/null arch/s390/mm/cmm.c --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/arch/s390/mm/cmm.c Fri Feb 20 16:00:53 2004 @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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"); diff -puN arch/s390/mm/init.c~s390-11-collaborative-memory-management arch/s390/mm/init.c --- 25/arch/s390/mm/init.c~s390-11-collaborative-memory-management Fri Feb 20 16:00:53 2004 +++ 25-akpm/arch/s390/mm/init.c Fri Feb 20 16:00:53 2004 @@ -40,6 +40,19 @@ DEFINE_PER_CPU(struct mmu_gather, mmu_ga 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; diff -puN arch/s390/mm/Makefile~s390-11-collaborative-memory-management arch/s390/mm/Makefile --- 25/arch/s390/mm/Makefile~s390-11-collaborative-memory-management Fri Feb 20 16:00:53 2004 +++ 25-akpm/arch/s390/mm/Makefile Fri Feb 20 16:00:53 2004 @@ -3,3 +3,5 @@ # obj-y := init.o fault.o ioremap.o +obj-$(CONFIG_CMM) += cmm.o + diff -puN drivers/s390/net/Kconfig~s390-11-collaborative-memory-management drivers/s390/net/Kconfig --- 25/drivers/s390/net/Kconfig~s390-11-collaborative-memory-management Fri Feb 20 16:00:53 2004 +++ 25-akpm/drivers/s390/net/Kconfig Fri Feb 20 16:00:53 2004 @@ -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 - vehicle networking under VM or VIF. This option is also available - as a module which will be called iucv.ko. If unsure, say "Y". + 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. 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 diff -puN drivers/s390/net/Makefile~s390-11-collaborative-memory-management drivers/s390/net/Makefile --- 25/drivers/s390/net/Makefile~s390-11-collaborative-memory-management Fri Feb 20 16:00:53 2004 +++ 25-akpm/drivers/s390/net/Makefile Fri Feb 20 16:00:53 2004 @@ -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 -puN /dev/null drivers/s390/net/smsgiucv.c --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/drivers/s390/net/smsgiucv.c Fri Feb 20 16:00:53 2004 @@ -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 +#include +#include +#include +#include +#include + +#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 -puN /dev/null drivers/s390/net/smsgiucv.h --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/drivers/s390/net/smsgiucv.h Fri Feb 20 16:00:53 2004 @@ -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 *)); + diff -puN include/asm-s390/pgalloc.h~s390-11-collaborative-memory-management include/asm-s390/pgalloc.h --- 25/include/asm-s390/pgalloc.h~s390-11-collaborative-memory-management Fri Feb 20 16:00:53 2004 +++ 25-akpm/include/asm-s390/pgalloc.h Fri Feb 20 16:00:53 2004 @@ -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 _