• Neeraj Upadhyay's avatar
    srcu: Ensure snp nodes tree is fully initialized before traversal · 0b56f953
    Neeraj Upadhyay authored
    
    
    For configurations where snp node tree is not initialized at
    init time (added in subsequent commits), srcu_funnel_gp_start()
    and srcu_funnel_exp_start() can potential traverse and observe
    the snp nodes' transient (uninitialized) states. This can potentially
    happen, when init_srcu_struct_nodes() initialization of sdp->mynode
    races with srcu_funnel_gp_start() and srcu_funnel_exp_start()
    
    Consider the case below where srcu_funnel_gp_start() observes
    sdp->mynode to be not NULL and uses an uninitialized sdp->grpmask
    
              P1                                  P2
    
    init_srcu_struct_nodes()           void srcu_funnel_gp_start(...)
    {
    for_each_possible_cpu(cpu) {
      ...
      sdp->mynode = &snp_first[...];
      for (snp = sdp->mynode;...)       struct srcu_node *snp_leaf =
                                           smp_load_acquire(&sdp->mynode)
        ...                             if (snp_leaf) {
                                          for (snp = snp_leaf; ...)
                                            ...
    					if (snp == snp_leaf)
                                             snp->srcu_data_have_cbs[idx] |=
                                               sdp->grpmask;
        sdp->grpmask =
          1 << (cpu - sdp->mynode->grplo);
      }
    }
    
    Similarly, init_srcu_struct_nodes() and srcu_funnel_exp_start() can
    race, where srcu_funnel_exp_start() could observe state of snp lock
    before spin_lock_init().
    
              P1                                      P2
    
    init_srcu_struct_nodes()               void srcu_funnel_exp_start(...)
    {
      srcu_for_each_node_breadth_first(ssp, snp) {      for (; ...) {
                                                          spin_lock_...(snp, )
    	spin_lock_init(&ACCESS_PRIVATE(snp, lock));
        ...
      }
      for_each_possible_cpu(cpu) {
        ...
        sdp->mynode = &snp_first[...];
    
    To avoid these issues, ensure that snp node tree initialization is
    complete i.e. after SRCU_SIZE_WAIT_BARRIER srcu_size_state is reached,
    before traversing the tree. Given that srcu_funnel_gp_start() and
    srcu_funnel_exp_start() are called within SRCU read side critical
    sections, this check is safe, in the sense that all callbacks are
    enqueued on CPU0 srcu_cblist until SRCU_SIZE_WAIT_CALL is entered,
    and these read side critical sections (containing srcu_funnel_gp_start()
    and srcu_funnel_exp_start()) need to complete, before SRCU_SIZE_WAIT_CALL
    is reached.
    Signed-off-by: default avatarNeeraj Upadhyay <quic_neeraju@quicinc.com>
    Signed-off-by: default avatarPaul E. McKenney <paulmck@kernel.org>
    0b56f953
srcutree.c 51.4 KB