Sophie

Sophie

distrib > CentOS > 5 > x86_64 > by-pkgid > ea32411352494358b8d75a78402a4713 > files > 3947

kernel-2.6.18-238.19.1.el5.centos.plus.src.rpm

From: Hendrik Brueckner <brueckner@redhat.com>
Date: Wed, 2 Jun 2010 10:07:10 -0400
Subject: [s390] cmm: fix module unload handling
Message-id: <20100602100710.GB24747@redhat.com>
Patchwork-id: 25927
O-Subject: [RHEL5.6 PATCH 1/1] [s390x] cmm: fix module unload handling
Bugzilla: 598549
RH-Acked-by: Pete Zaitcev <zaitcev@redhat.com>

Description
-----------
Kernel hangs or crashes when unloading the cmm module.  The cmm module
init and exit functions initialized and stopped used services in the
wrong order.  For example a possible running timer was not stopped
at all on module exit.

Fix order of initialization and make sure that no resources are in use
anymore when the module exit function is finished.

Bugzilla
--------
BZ 598549
https://bugzilla.redhat.com/show_bug.cgi?id=598549

Upstream status of the patch
----------------------------
The patch is upstream as of kernel version 2.6.35
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=1ef6acf597559fd1c244190512144c40619299bf

Test status
-----------
The patch has been tested and fixes the problem.
The fix has been verified by the IBM test department.


diff --git a/arch/s390/mm/cmm.c b/arch/s390/mm/cmm.c
index fe0ade5..290ba58 100644
--- a/arch/s390/mm/cmm.c
+++ b/arch/s390/mm/cmm.c
@@ -16,6 +16,7 @@
 #include <linux/sysctl.h>
 #include <linux/ctype.h>
 #include <linux/swap.h>
+#include <linux/kthread.h>
 
 #include <asm/pgalloc.h>
 #include <asm/uaccess.h>
@@ -46,10 +47,9 @@ static struct cmm_page_array *cmm_page_list;
 static struct cmm_page_array *cmm_timed_page_list;
 static DEFINE_SPINLOCK(cmm_lock);
 
-static unsigned long cmm_thread_active;
-static struct work_struct cmm_thread_starter;
-static wait_queue_head_t cmm_thread_wait;
-static struct timer_list cmm_timer;
+static struct task_struct *cmm_thread_ptr;
+static DECLARE_WAIT_QUEUE_HEAD(cmm_thread_wait);
+static DEFINE_TIMER(cmm_timer, NULL, 0, 0);
 
 static void cmm_timer_fn(unsigned long);
 static void cmm_set_timer(void);
@@ -159,14 +159,12 @@ cmm_thread(void *dummy)
 {
 	int rc;
 
-	daemonize("cmmthread");
 	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_timed_pages != cmm_timed_pages_target ||
+			 kthread_should_stop()));
+		if (kthread_should_stop() || rc == -ERESTARTSYS) {
 			cmm_pages_target = cmm_pages;
 			cmm_timed_pages_target = cmm_timed_pages;
 			break;
@@ -192,16 +190,8 @@ cmm_thread(void *dummy)
 }
 
 static void
-cmm_start_thread(void)
-{
-	kernel_thread(cmm_thread, NULL, 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);
 }
 
@@ -445,31 +435,56 @@ struct ctl_table_header *cmm_sysctl_header;
 static int
 cmm_init (void)
 {
+	int rc = -ENOMEM;
+
 #ifdef CONFIG_CMM_PROC
 	cmm_sysctl_header = register_sysctl_table(cmm_dir_table, 1);
+	if (!cmm_sysctl_header)
+		goto out;
 #endif
 #ifdef CONFIG_CMM_IUCV
-	smsg_register_callback(SMSG_PREFIX, cmm_smsg_target);
+	rc = smsg_register_callback(SMSG_PREFIX, cmm_smsg_target);
+	if (rc < 0)
+		goto out_smsg;
 #endif
-	register_oom_notifier(&cmm_oom_nb);
-	INIT_WORK(&cmm_thread_starter, (void *) cmm_start_thread, NULL);
-	init_waitqueue_head(&cmm_thread_wait);
-	init_timer(&cmm_timer);
+	rc = register_oom_notifier(&cmm_oom_nb);
+	if (rc < 0)
+		goto out_oom_notify;
+	cmm_thread_ptr = kthread_run(cmm_thread, NULL, "cmmthread");
+	rc = IS_ERR(cmm_thread_ptr) ? PTR_ERR(cmm_thread_ptr) : 0;
+	if (rc)
+		goto out_kthread;
 	return 0;
+
+out_kthread:
+	unregister_oom_notifier(&cmm_oom_nb);
+out_oom_notify:
+#ifdef CONFIG_CMM_IUCV
+	smsg_unregister_callback(SMSG_PREFIX, cmm_smsg_target);
+out_smsg:
+#endif
+#ifdef CONFIG_CMM_PROC
+	unregister_sysctl_table(cmm_sysctl_header);
+out:
+#endif
+	del_timer_sync(&cmm_timer);
+	return rc;
 }
 
 static void
 cmm_exit(void)
 {
-	unregister_oom_notifier(&cmm_oom_nb);
-	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
+	unregister_oom_notifier(&cmm_oom_nb);
+	kthread_stop(cmm_thread_ptr);
+	del_timer_sync(&cmm_timer);
+	cmm_free_pages(cmm_pages, &cmm_pages, &cmm_page_list);
+	cmm_free_pages(cmm_timed_pages, &cmm_timed_pages, &cmm_timed_page_list);
 }
 
 module_init(cmm_init);