On Tue, Dec 20, 2022 at 11:26:12PM -0500, Joel Fernandes wrote: > > > > On Dec 20, 2022, at 10:43 PM, Mathieu Desnoyers <mathieu.desnoyers@xxxxxxxxxxxx> wrote: > > > > On 2022-12-20 19:58, Frederic Weisbecker wrote: > >>> On Wed, Dec 21, 2022 at 01:49:57AM +0100, Frederic Weisbecker wrote: > >>> On Tue, Dec 20, 2022 at 07:15:00PM -0500, Joel Fernandes wrote: > >>>> On Tue, Dec 20, 2022 at 5:45 PM Frederic Weisbecker <frederic@xxxxxxxxxx> wrote: > >>>> Agreed about (1). > >>>> > >>>>> _ In (2), E pairs with the address-dependency between idx and lock_count. > >>>> > >>>> But that is not the only reason. If that was the only reason for (2), > >>>> then there is an smp_mb() just before the next-scan post-flip before > >>>> the lock counts are read. > >>> > >>> The post-flip barrier makes sure the new idx is visible on the next READER's > >>> turn, but it doesn't protect against the fact that "READ idx then WRITE lock[idx]" > >>> may appear unordered from the update side POV if there is no barrier between the > >>> scan and the flip. > >>> > >>> If you remove the smp_mb() from the litmus test I sent, things explode. > >> Or rather, look at it the other way, if there is no barrier between the lock > >> scan and the index flip (E), then the index flip can appear to be written before the > >> lock is read. Which means you may start activating the index before you finish > >> reading it (at least it appears that way from the readers pont of view). > > > > Considering that you can have pre-existing readers from arbitrary index appearing anywhere in the grace period (because a reader can fetch the > > index and be preempted for an arbitrary amount of time before incrementing the lock count), the grace period algorithm needs to deal with the fact that a newcoming reader can appear in a given index either before or after the flip. > > > > I don't see how flipping the index before or after loading the unlock/lock values would break anything (except for unlikely counter overflow situations as previously discussed). > > If you say unlikely, that means it can happen some times which is bad enough ;-). Maybe you mean impossible. I would not settle for anything less than keeping the memory barrier around if it helps unlikely cases, but only D does help for the theoretical wrapping/overflow issue. E is broken and does not even help the theoretical issue IMO. And both D and E do not affect correctness IMO. And here is why D is needed: C D {} // updater P0(int *IDX, int *LOCK0, int *UNLOCK0, int *LOCK1, int *UNLOCK1) { int lock1; int unlock1; int lock0; int unlock0; // SCAN1 unlock1 = READ_ONCE(*UNLOCK1); smp_mb(); // A lock1 = READ_ONCE(*LOCK1); // FLIP smp_mb(); // E WRITE_ONCE(*IDX, 1); smp_mb(); // D // SCAN 2 unlock0 = READ_ONCE(*UNLOCK0); smp_mb(); // A lock0 = READ_ONCE(*LOCK0); } // reader P1(int *IDX, int *LOCK0, int *UNLOCK0, int *LOCK1, int *UNLOCK1) { int tmp; int idx; // 1st reader idx = READ_ONCE(*IDX); if (idx == 0) { tmp = READ_ONCE(*LOCK0); WRITE_ONCE(*LOCK0, tmp + 1); smp_mb(); /* B and C */ tmp = READ_ONCE(*UNLOCK0); WRITE_ONCE(*UNLOCK0, tmp + 1); } else { tmp = READ_ONCE(*LOCK1); WRITE_ONCE(*LOCK1, tmp + 1); smp_mb(); /* B and C */ tmp = READ_ONCE(*UNLOCK1); WRITE_ONCE(*UNLOCK1, tmp + 1); } // second reader idx = READ_ONCE(*IDX); if (idx == 0) { tmp = READ_ONCE(*LOCK0); WRITE_ONCE(*LOCK0, tmp + 1); smp_mb(); /* B and C */ tmp = READ_ONCE(*UNLOCK0); WRITE_ONCE(*UNLOCK0, tmp + 1); } else { tmp = READ_ONCE(*LOCK1); WRITE_ONCE(*LOCK1, tmp + 1); smp_mb(); /* B and C */ tmp = READ_ONCE(*UNLOCK1); WRITE_ONCE(*UNLOCK1, tmp + 1); } // third reader idx = READ_ONCE(*IDX); if (idx == 0) { tmp = READ_ONCE(*LOCK0); WRITE_ONCE(*LOCK0, tmp + 1); smp_mb(); /* B and C */ tmp = READ_ONCE(*UNLOCK0); WRITE_ONCE(*UNLOCK0, tmp + 1); } else { tmp = READ_ONCE(*LOCK1); WRITE_ONCE(*LOCK1, tmp + 1); smp_mb(); /* B and C */ tmp = READ_ONCE(*UNLOCK1); WRITE_ONCE(*UNLOCK1, tmp + 1); } } exists (0:unlock0=0 /\ 1:idx=0) States 6 0:unlock0=0; 1:idx=1; 0:unlock0=1; 1:idx=0; 0:unlock0=1; 1:idx=1; 0:unlock0=2; 1:idx=0; 0:unlock0=2; 1:idx=1; 0:unlock0=3; 1:idx=0; No Witnesses Positive: 0 Negative: 14 Condition exists (0:unlock0=0 /\ 1:idx=0) Observation D Never 0 14 But then if you comment out "smp_mb() /* D */": Test D Allowed States 7 0:unlock0=0; 1:idx=0; 0:unlock0=0; 1:idx=1; 0:unlock0=1; 1:idx=0; 0:unlock0=1; 1:idx=1; 0:unlock0=2; 1:idx=0; 0:unlock0=2; 1:idx=1; 0:unlock0=3; 1:idx=0; Ok Witnesses Positive: 2 Negative: 14 Condition exists (0:unlock0=0 /\ 1:idx=0) Observation D Sometimes 2 14 Without D I guess things would eventually fix up but that would require an extra loop in SCAN2. Thanks.