Commit f8cef8c2 authored by Linus Torvalds's avatar Linus Torvalds

Fix rcu list poisoning - since another CPU may be traversing the list

as we delete the entry, we can only poison the back pointer, not the
traversal pointer (rcu traversal only ever walks forward).

Make __d_drop() take this into account.
parent 58acfd93
...@@ -174,8 +174,10 @@ extern spinlock_t dcache_lock; ...@@ -174,8 +174,10 @@ extern spinlock_t dcache_lock;
static inline void __d_drop(struct dentry *dentry) static inline void __d_drop(struct dentry *dentry)
{ {
dentry->d_vfs_flags |= DCACHE_UNHASHED; if (!(dentry->d_vfs_flags & DCACHE_UNHASHED)) {
hlist_del_rcu_init(&dentry->d_hash); dentry->d_vfs_flags |= DCACHE_UNHASHED;
hlist_del_rcu(&dentry->d_hash);
}
} }
static inline void d_drop(struct dentry *dentry) static inline void d_drop(struct dentry *dentry)
......
...@@ -152,14 +152,17 @@ static inline void list_del(struct list_head *entry) ...@@ -152,14 +152,17 @@ static inline void list_del(struct list_head *entry)
/** /**
* list_del_rcu - deletes entry from list without re-initialization * list_del_rcu - deletes entry from list without re-initialization
* @entry: the element to delete from the list. * @entry: the element to delete from the list.
*
* Note: list_empty on entry does not return true after this, * Note: list_empty on entry does not return true after this,
* the entry is in an undefined state. It is useful for RCU based * the entry is in an undefined state. It is useful for RCU based
* lockfree traversal. * lockfree traversal.
*
* In particular, it means that we can not poison the forward
* pointers that may still be used for walking the list.
*/ */
static inline void list_del_rcu(struct list_head *entry) static inline void list_del_rcu(struct list_head *entry)
{ {
__list_del(entry->prev, entry->next); __list_del(entry->prev, entry->next);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2; entry->prev = LIST_POISON2;
} }
...@@ -431,7 +434,22 @@ static __inline__ void hlist_del(struct hlist_node *n) ...@@ -431,7 +434,22 @@ static __inline__ void hlist_del(struct hlist_node *n)
n->pprev = LIST_POISON2; n->pprev = LIST_POISON2;
} }
#define hlist_del_rcu hlist_del /* list_del_rcu is identical too? */ /**
* hlist_del_rcu - deletes entry from hash list without re-initialization
* @entry: the element to delete from the hash list.
*
* Note: list_unhashed() on entry does not return true after this,
* the entry is in an undefined state. It is useful for RCU based
* lockfree traversal.
*
* In particular, it means that we can not poison the forward
* pointers that may still be used for walking the hash list.
*/
static inline void hlist_del_rcu(struct hlist_node *n)
{
__hlist_del(n);
n->pprev = LIST_POISON2;
}
static __inline__ void hlist_del_init(struct hlist_node *n) static __inline__ void hlist_del_init(struct hlist_node *n)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment