[prog] Unlocking task_list read lock before performing a sleepy operation
Ariane van der Steldt
ariane at stack.nl
Sun Mar 17 14:44:32 UTC 2013
On 03/14/13 11:56, Pranami Bhattacharya wrote:
> Dear All,
>
>
> I was trying the following and had a doubt.
>
>
> >From my kernel module I want to iterate over task list and do some sleepy
> operation for all the process for which my task->flag is set .I know by
> holding read_lock(tasklist_lock)/rcu_read_lock I should not be performing
> any sleepy operation. So I have coded like below :
>
>
>
> 1. Take the read lock
>
> 2. Iterate over task (for_each_process)
>
> 3. If my flag is set I unlock the tasklist_lock and get_task_struct
>
> 4. Perform the sleepy operation
>
> 5. Then put_task_struct
>
> 6. Again take read_lock(&tasklist_lock);
>
> 7. Loop continues
>
>
>
> The code snippet is as shown below :
>
>
> read_lock(&tasklist_lock);
>
>
>
> for_each_process(c) {
>
>
>
> if (c->my_flag) {
>
>
>
> read_unlock(&tasklist_lock);
Once you unlock the corresponding mutex the tasklist can be changed by
another thread.
This means the for_each_process() iterator may be invalid by the time
you reacquire the lock on the list.
> get_task_struct(c);
>
>
> ...
>
>
> perform_sleepy_operation();
>
>
> ...
>
>
>
> put_task_struct(c);
>
>
>
> read_lock(&tasklist_lock);
>
> }
>
>
>
> }
>
>
>
> Please let me know if this approach is correct.
A couple of solutions exist to combat this problem, depending on the
environment they may or may not apply.
[a] prevent 'c' from being removed from the list for the duration of
your call (a reference counter or lock specific to 'c' would suffice.
[b] restart from the beginning once you unlock; this may result in the
algorithm never terminating, if there is enough activity on the list and
may result in your operation being done multiple times on objects, if
the flag is not cleared at all or is set again between iterations
[c] as [b], restart from the beginning, but use a generation counter to
prevent multiple updates
[d] while holding the lock, gather each 'c' for which you need to do
this operation into a list, all the while holding the lock. Once that
list is populated, operate on the entries in this list while not holding
the lock. Disadvantage is that you may have to allocate sufficient
memory up-front and that you need a way to inform the code that this
object may not go away (for instance, increment a reference counter
while the object is on the list).
If [b] or [c] are possible, they can make for nicer solutions than [d],
in terms of overhead and complexity, but the code will be rather
complicated (reference counting in C is messy at the best of times).
[d] is the approach which should always work and is easiest to verify,
at the cost that you may have to return ENOMEM to indicate insufficient
temporary memory was available.
[a] is possible if you have reference counters, but in that case,
ideally the 'for_each_process' macro should have reference counting
semantics or alternative locking strategies to prevent your iterator 'c'
from going away.
> Thanks and Regards,
>
> Pranami
--
Ariane
More information about the Programming
mailing list