On Mon, Jun 03, 2019 at 10:46:40AM +0800, Herbert Xu wrote: > The case we were discussing is from net/ipv4/inet_fragment.c from > the net-next tree:
BTW, thank you for keeping me and other people who intervened in that discussion in Cc:... Andrea > > void fqdir_exit(struct fqdir *fqdir) > { > ... > fqdir->dead = true; > > /* call_rcu is supposed to provide memory barrier semantics, > * separating the setting of fqdir->dead with the destruction > * work. This implicit barrier is paired with inet_frag_kill(). > */ > > INIT_RCU_WORK(&fqdir->destroy_rwork, fqdir_rwork_fn); > queue_rcu_work(system_wq, &fqdir->destroy_rwork); > } > > and > > void inet_frag_kill(struct inet_frag_queue *fq) > { > ... > rcu_read_lock(); > /* The RCU read lock provides a memory barrier > * guaranteeing that if fqdir->dead is false then > * the hash table destruction will not start until > * after we unlock. Paired with inet_frags_exit_net(). > */ > if (!fqdir->dead) { > rhashtable_remove_fast(&fqdir->rhashtable, &fq->node, > fqdir->f->rhash_params); > ... > } > ... > rcu_read_unlock(); > ... > } > > I simplified this to > > Initial values: > > a = 0 > b = 0 > > CPU1 CPU2 > ---- ---- > a = 1 rcu_read_lock > synchronize_rcu if (a == 0) > b = 2 b = 1 > rcu_read_unlock > > On exit we want this to be true: > b == 2