-------
When using the Normal API, you do not have to worry about locking.
-The Maple Tree uses RCU and internal spinlock to synchronise access:
-
-No lock needed:
- * mas_destroy()
- * mas_entry_count()
- * mas_is_none()
- * mas_reset()
- * mas_set_range()
- * mas_set()
+The Maple Tree uses RCU and an internal spinlock to synchronise access:
Takes RCU read lock:
* mtree_load()
* mt_set_in_rcu()
* mt_clear_in_rcu()
-
-Assume RCU read lock held on entry:
- * mas_walk()
- * mas_next()
- * mas_prev()
- * mas_find()
- * mas_pause()
-
-Assume ma_lock held on entry:
- * mas_store()
- * mas_store_gfp()
- * mas_nomem()
-
-Assumes ma_lock or RCU read lock is held on entry:
- * mas_for_each()
-
-
If you want to take advantage of the lock to protect the data structures
-that you are storing in the Maple Tree, you can call mas_lock() before
-calling mas_walk(), then take a reference count on the object you have
-found before calling the mas_unlock(). This will prevent stores from
+that you are storing in the Maple Tree, you can call mtree_lock() before
+calling mtree_load(), then take a reference count on the object you have
+found before calling mtree_unlock(). This will prevent stores from
removing the object from the tree between looking up the object and
incrementing the refcount. You can also use RCU to avoid dereferencing
freed memory, but an explanation of that is beyond the scope of this
document.
-If it is necessary to drop the locks during an iteration, then mas_pause()
-provides this functionality. It is worth noting that dropping the locks
-allows for other tasks to alter the tree so your code may get a split view
-of the past and the current state of a tree in this scenario. Note that
-the next call using the maple state will re-walk the tree from the root.
-
Advanced API
============
The advanced API offers more flexibility and better performance at the
cost of an interface which can be harder to use and has fewer safeguards.
-No locking is done for you by the advanced API, and you are required
-to use the mas_lock while modifying the tree. You can choose whether
-to use the mas_lock or the RCU lock while doing read-only operations on
-the tree. You can mix advanced and normal operations on the same array
-and the normal API is implemented in terms of the advanced API.
+You must take care of your own locking while using the advanced API.
+You can use the ma_lock, RCU or an external lock for protection.
+You can mix advanced and normal operations on the same array, as long
+as the locking is compatible. The normal API is implemented in terms
+of the advanced API.
The advanced API is based around the ma_state, this is where the 'mas'
prefix originates.
}
}
+static inline bool mt_external_lock(const struct maple_tree *mt)
+{
+ return (mt->ma_flags & MT_FLAGS_LOCK_MASK) == MT_FLAGS_LOCK_EXTERN;
+}
+
static inline bool mt_locked(const struct maple_tree *mt)
{
- return ((mt->ma_flags & MT_FLAGS_LOCK_MASK) == MT_FLAGS_LOCK_EXTERN) ||
- lockdep_is_held(&mt->ma_lock);
+ return mt_external_lock(mt) || lockdep_is_held(&mt->ma_lock);
}
static inline void *mt_slot(const struct maple_tree *mt,
return false;
}
- if (gfpflags_allow_blocking(gfp)) {
+ if (gfpflags_allow_blocking(gfp) && !mt_external_lock(mas->tree)) {
mtree_unlock(mas->tree);
mas_alloc_nodes(mas, gfp);
mtree_lock(mas->tree);
*/
void mtree_init(struct maple_tree *mt, unsigned int ma_flags)
{
- spin_lock_init(&mt->ma_lock);
mt->ma_flags = ma_flags;
+ spin_lock_init(&mt->ma_lock);
rcu_assign_pointer(mt->ma_root, NULL);
}
EXPORT_SYMBOL(mtree_init);