[PATCH 4/8] mm/swap: fix race among frontswap_register_ops, swapoff and swapon

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



A potential asynchronous frontswap_register_ops could happen while swapoff
or swapon is happening, there is a need to protect init frontswap type and
prevent NULL point reference to si->frontswap_map.

This patch utilize swapon_mutex to prevent this scenario, see comments of
frontswap_register_ops for detail.

This patch is just for a rare scenario, aim to correct of code.

Signed-off-by: Weijie Yang <weijie.yang@xxxxxxxxxxx>
---
 include/linux/swapfile.h |    1 +
 mm/frontswap.c           |    7 ++++---
 mm/swapfile.c            |   20 +++++++++++++++-----
 3 files changed, 20 insertions(+), 8 deletions(-)

diff --git a/include/linux/swapfile.h b/include/linux/swapfile.h
index e282624..1fd5494 100644
--- a/include/linux/swapfile.h
+++ b/include/linux/swapfile.h
@@ -6,6 +6,7 @@
  * want to expose them to the dozens of source files that include swap.h
  */
 extern spinlock_t swap_lock;
+extern struct mutex swapon_mutex;
 extern struct swap_list_t swap_list;
 extern struct swap_info_struct *swap_info[];
 extern int try_to_unuse(unsigned int, bool, unsigned long);
diff --git a/mm/frontswap.c b/mm/frontswap.c
index 1b24bdc..a0c68db 100644
--- a/mm/frontswap.c
+++ b/mm/frontswap.c
@@ -88,10 +88,9 @@ static inline void inc_frontswap_invalidates(void) { }
  * bitmap) to create tmem_pools and set the respective poolids. All of that is
  * guarded by us using atomic bit operations on the 'need_init' bitmap.
  *
- * This would not guards us against the user deciding to call swapoff right as
+ * swapon_mutex guards us against the user deciding to call swapoff right as
  * we are calling the backend to initialize (so swapon is in action).
- * Fortunatly for us, the swapon_mutex has been taked by the callee so we are
- * OK. The other scenario where calls to frontswap_store (called via
+ * The other scenario where calls to frontswap_store (called via
  * swap_writepage) is racing with frontswap_invalidate_area (called via
  * swapoff) is again guarded by the swap subsystem.
  *
@@ -120,6 +119,7 @@ struct frontswap_ops *frontswap_register_ops(struct frontswap_ops *ops)
 	struct frontswap_ops *old = frontswap_ops;
 	int i;
 
+	mutex_lock(&swapon_mutex);
 	for (i = 0; i < MAX_SWAPFILES; i++) {
 		if (test_and_clear_bit(i, need_init)) {
 			struct swap_info_struct *sis = swap_info[i];
@@ -129,6 +129,7 @@ struct frontswap_ops *frontswap_register_ops(struct frontswap_ops *ops)
 			ops->init(i);
 		}
 	}
+	mutex_unlock(&swapon_mutex);
 	/*
 	 * We MUST have frontswap_ops set _after_ the frontswap_init's
 	 * have been called. Otherwise __frontswap_store might fail. Hence
diff --git a/mm/swapfile.c b/mm/swapfile.c
index 413c213..5c8a086 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -112,7 +112,7 @@ struct swap_list_t swap_list = {-1, -1};
 
 struct swap_info_struct *swap_info[MAX_SWAPFILES];
 
-static DEFINE_MUTEX(swapon_mutex);
+DEFINE_MUTEX(swapon_mutex);
 
 static DECLARE_WAIT_QUEUE_HEAD(proc_poll_wait);
 /* Activity counter to indicate that a swapon or swapoff has occurred */
@@ -1954,11 +1954,9 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile)
 	if (p->flags & SWP_CONTINUED)
 		free_swap_count_continuations(p);
 
-	mutex_lock(&swapon_mutex);
 	spin_lock(&swap_lock);
 	spin_lock(&p->lock);
 	drain_mmlist();
-
 	/* wait for anyone still in scan_swap_map */
 	p->highest_bit = 0;		/* cuts scans short */
 	while (p->flags >= SWP_SCANNING) {
@@ -1968,7 +1966,16 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile)
 		spin_lock(&swap_lock);
 		spin_lock(&p->lock);
 	}
+	spin_unlock(&p->lock);
+	spin_unlock(&swap_lock);
 
+	/*
+	 * Now nobody use or allocate swap_entry from this swapfile,
+	 * there is no need to hold swap_lock or p->lock.
+	 * Use swapon_mutex to mutex against swap_start() and a potential
+	 * asynchronous frontswap_register_ops, which will init this swap type
+	 */
+	mutex_lock(&swapon_mutex);
 	swap_file = p->swap_file;
 	old_block_size = p->old_block_size;
 	p->swap_file = NULL;
@@ -1978,11 +1985,10 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile)
 	cluster_info = p->cluster_info;
 	p->cluster_info = NULL;
 	frontswap_map = frontswap_map_get(p);
-	spin_unlock(&p->lock);
-	spin_unlock(&swap_lock);
 	frontswap_invalidate_area(type);
 	frontswap_map_set(p, NULL);
 	mutex_unlock(&swapon_mutex);
+
 	free_percpu(p->percpu_cluster);
 	p->percpu_cluster = NULL;
 	vfree(swap_map);
@@ -2577,6 +2583,10 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags)
 		}
 	}
 
+	/*
+	 * Use swapon_mutex to mutex against swap_start() and a potential
+	 * asynchronous frontswap_register_ops, which will init this swap type
+	 */
 	mutex_lock(&swapon_mutex);
 	prio = -1;
 	if (swap_flags & SWAP_FLAG_PREFER)
-- 
1.7.10.4


--
To unsubscribe from this list: send the line "unsubscribe stable" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Kernel]     [Kernel Development Newbies]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite Hiking]     [Linux Kernel]     [Linux SCSI]