aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/radix-tree.h16
-rw-r--r--mm/migrate.c4
2 files changed, 18 insertions, 2 deletions
diff --git a/include/linux/radix-tree.h b/include/linux/radix-tree.h
index ab2baa5c4884..23241c2fecce 100644
--- a/include/linux/radix-tree.h
+++ b/include/linux/radix-tree.h
@@ -146,6 +146,22 @@ static inline void *radix_tree_deref_slot(void **pslot)
146} 146}
147 147
148/** 148/**
149 * radix_tree_deref_slot_protected - dereference a slot without RCU lock but with tree lock held
150 * @pslot: pointer to slot, returned by radix_tree_lookup_slot
151 * Returns: item that was stored in that slot with any direct pointer flag
152 * removed.
153 *
154 * Similar to radix_tree_deref_slot but only used during migration when a pages
155 * mapping is being moved. The caller does not hold the RCU read lock but it
156 * must hold the tree lock to prevent parallel updates.
157 */
158static inline void *radix_tree_deref_slot_protected(void **pslot,
159 spinlock_t *treelock)
160{
161 return rcu_dereference_protected(*pslot, lockdep_is_held(treelock));
162}
163
164/**
149 * radix_tree_deref_retry - check radix_tree_deref_slot 165 * radix_tree_deref_retry - check radix_tree_deref_slot
150 * @arg: pointer returned by radix_tree_deref_slot 166 * @arg: pointer returned by radix_tree_deref_slot
151 * Returns: 0 if retry is not required, otherwise retry is required 167 * Returns: 0 if retry is not required, otherwise retry is required
diff --git a/mm/migrate.c b/mm/migrate.c
index 1a531b760b3b..89a6bc8cd307 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -248,7 +248,7 @@ static int migrate_page_move_mapping(struct address_space *mapping,
248 248
249 expected_count = 2 + page_has_private(page); 249 expected_count = 2 + page_has_private(page);
250 if (page_count(page) != expected_count || 250 if (page_count(page) != expected_count ||
251 (struct page *)radix_tree_deref_slot(pslot) != page) { 251 radix_tree_deref_slot_protected(pslot, &mapping->tree_lock) != page) {
252 spin_unlock_irq(&mapping->tree_lock); 252 spin_unlock_irq(&mapping->tree_lock);
253 return -EAGAIN; 253 return -EAGAIN;
254 } 254 }
@@ -320,7 +320,7 @@ int migrate_huge_page_move_mapping(struct address_space *mapping,
320 320
321 expected_count = 2 + page_has_private(page); 321 expected_count = 2 + page_has_private(page);
322 if (page_count(page) != expected_count || 322 if (page_count(page) != expected_count ||
323 (struct page *)radix_tree_deref_slot(pslot) != page) { 323 radix_tree_deref_slot_protected(pslot, &mapping->tree_lock) != page) {
324 spin_unlock_irq(&mapping->tree_lock); 324 spin_unlock_irq(&mapping->tree_lock);
325 return -EAGAIN; 325 return -EAGAIN;
326 } 326 }