lkml.org 
[lkml]   [2019]   [Mar]   [26]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH tip/core/rcu 04/18] rcu: Make exit_rcu() handle non-preempted RCU readers
    Date
    The purpose of exit_rcu() is to handle cases where buggy code causes a
    task to exit within an RCU read-side critical section. It currently
    does that in the case where said RCU read-side critical section was
    preempted at least once, but fails to handle cases where preemption did
    not occur. This case needs to be handled because otherwise the final
    context switch away from the exiting task will incorrectly behave as if
    task exit were instead a preemption of an RCU read-side critical section,
    and will therefore queue the exiting task. The exiting task will have
    exited, and thus won't ever execute rcu_read_unlock(), which means that
    it will remain queued forever, blocking all subsequent grace periods,
    and eventually resulting in OOM.

    Although this is arguably better than letting grace periods proceed
    and having a later rcu_read_unlock() access the now-freed task
    structure that once belonged to the exiting tasks, it would obviously
    be better to correctly handle this case. This commit therefore sets
    ->rcu_read_lock_nesting to 1 in that case, so that the subsequence call
    to __rcu_read_unlock() causes the exiting task to exit its dangling RCU
    read-side critical section.

    Note that deferred quiescent states need not be considered. The reason
    is that removing the task from the ->blkd_tasks[] list in the call to
    rcu_preempt_deferred_qs() handles the per-task component of any deferred
    quiescent state, and all other components of any deferred quiescent state
    are associated with the CPU, which isn't going anywhere until some later
    CPU-hotplug operation, which will report any remaining deferred quiescent
    states from within the rcu_report_dead() function.

    Note also that negative values of ->rcu_read_lock_nesting need not be
    considered. First, these won't show up in exit_rcu() unless there is
    a serious bug in RCU, and second, setting ->rcu_read_lock_nesting sets
    the state so that the RCU read-side critical section will be exited
    normally.

    Again, this code has no effect unless there has been some prior bug
    that prevents a task from leaving an RCU read-side critical section
    before exiting. Furthermore, there have been no reports of the bug
    fixed by this commit appearing in production. This commit is therefore
    absolutely -not- recommended for backporting to -stable.

    Reported-by: ABHISHEK DUBEY <dabhishek@iisc.ac.in>
    Reported-by: BHARATH Y MOURYA <bharathm@iisc.ac.in>
    Reported-by: Aravinda Prasad <aravinda@iisc.ac.in>
    Signed-off-by: Paul E. McKenney <paulmck@linux.ibm.com>
    Tested-by: ABHISHEK DUBEY <dabhishek@iisc.ac.in>
    ---
    kernel/rcu/tree_plugin.h | 20 +++++++++++++-------
    1 file changed, 13 insertions(+), 7 deletions(-)

    diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h
    index 97dba50f6fb2..d408661d5fb7 100644
    --- a/kernel/rcu/tree_plugin.h
    +++ b/kernel/rcu/tree_plugin.h
    @@ -804,19 +804,25 @@ static void rcu_flavor_sched_clock_irq(int user)

    /*
    * Check for a task exiting while in a preemptible-RCU read-side
    - * critical section, clean up if so. No need to issue warnings,
    - * as debug_check_no_locks_held() already does this if lockdep
    - * is enabled.
    + * critical section, clean up if so. No need to issue warnings, as
    + * debug_check_no_locks_held() already does this if lockdep is enabled.
    + * Besides, if this function does anything other than just immediately
    + * return, there was a bug of some sort. Spewing warnings from this
    + * function is like as not to simply obscure important prior warnings.
    */
    void exit_rcu(void)
    {
    struct task_struct *t = current;

    - if (likely(list_empty(&current->rcu_node_entry)))
    + if (unlikely(!list_empty(&current->rcu_node_entry))) {
    + t->rcu_read_lock_nesting = 1;
    + barrier();
    + t->rcu_read_unlock_special.b.blocked = true;
    + } else if (unlikely(t->rcu_read_lock_nesting)) {
    + t->rcu_read_lock_nesting = 1;
    + } else {
    return;
    - t->rcu_read_lock_nesting = 1;
    - barrier();
    - t->rcu_read_unlock_special.b.blocked = true;
    + }
    __rcu_read_unlock();
    rcu_preempt_deferred_qs(current);
    }
    --
    2.17.1
    \
     
     \ /
      Last update: 2019-03-27 00:14    [W:3.531 / U:0.176 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site