I'm a bit confused about such code:
struct a {
// ...
struct list_head some_list;
// ...
};
struct e {
struct list_head next;
// ...
};
static void *traverse(struct a *a)
{
struct e *e;
rcu_read_lock();
list_for_each_entry_rcu(e, &a->some_list, next) {
if (...) {
rcu_read_unlock();
return e;
}
}
rcu_read_unlock();
return NULL;
}
In the function traverse we take a lock rcu_read_lock and then iterate over some list until some condition is met, but after this condition is met we unlock rcu_read_unlock RCU and return the e pointer.
The key point that is confusing me is that we unlock RCU read-side critical section, but keep pointer from the list, what if write-side will remove this element and it seems that this pointer e will be broken, isn't it?
AFAIK, the pointer is valid only inside read-side critical section, i.e. between rcu_read_lock and rcu_read_unlock, am I wrong?
P.S.: traverse is called without holding any locks.
Your assumptions are right, the snippet of code you posted seems "broken". What you usually want to do in such a situation is something like the following:
This way you can ensure that whatever operation you need to perform on
e, thecallback()function that gets called to use it will see a consistent version of the list (of course, assuming that it does not saveesomewhere to use it later, otherwise we're back at square one).Doing
return e;afterrcu_read_unlock();can cause trouble as you have noted in your question, but in theory it could still be fine depending on the exact scenario. Whether there's a problem or not only depends on what is done witheafter it is returned.For example, if
eis simply checked in the caller with something likeif (e != NULL) {...}then that'd be fine. Of course though, one could argue that you could have just made thetraversefunction return aboolin such case :')