Hello hackers,

12.12.2025 02:54, Thomas Munro wrote:
Thanks!  Could also be environmental.  Maybe the snow arrived and you
adjusted your thermostat? :-)  But even then we'd still need to figure
out what's different on those branches...  One difference is autoconf
vs meson.  Both say -g -O2, but only Meson says -fPIC.  Can you
reproduce it on later branches with configure/make?

I'd like to revive this thread, while we have no snow in EU yet. We had
some off-list discussion of this topic at the end of the past year, but
didn't root-cause the issue then. But this week I have finally dedicated
some time to dive into this with my Orange Pi device, which is able to
reproduce those crashes more or less as often as greenfly.

I came to the following modification:
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -4250,3 +4250,10 @@ LockBuffer(Buffer buffer, int mode)
        else if (mode == BUFFER_LOCK_EXCLUSIVE)
+{
+for (int i = 0; i < 100; i++)
+{
+LWLockAcquire(BufferDescriptorGetContentLock(buf), LW_EXCLUSIVE);
+LWLockRelease(BufferDescriptorGetContentLock(buf));
+}
                LWLockAcquire(BufferDescriptorGetContentLock(buf), 
LW_EXCLUSIVE);
+}
        else

which makes a build produced on REL_15_STABLE with:
CC=clang-20 CFLAGS="-O1" ./configure -q --enable-debug --enable-cassert 
--enable-tap-tests && make -s -j8

fail for me reliably (0 of 10 runs succeeded) as below:
PROVE_TESTS="t/001*" make -s check -C src/bin/pgbench
...
Files=1, Tests=3,  9 wallclock secs ( 0.11 usr  0.03 sys +  4.83 cusr  1.02 
csys =  5.99 CPU)
Result: FAIL

TRAP: FailedAssertion("!(oldstate & LW_VAL_EXCLUSIVE)", File: "lwlock.c", Line: 
1843, PID: 64342)
postgres: main: orangepi postgres [local] CREATE 
TYPE(ExceptionalCondition+0x72)[0x2ab2066546]
postgres: main: orangepi postgres [local] CREATE 
TYPE(LWLockRelease+0x58e)[0x2ab2385160]
postgres: main: orangepi postgres [local] CREATE 
TYPE(_bt_relbuf+0x14)[0x2ab20d5060]
postgres: main: orangepi postgres [local] CREATE TYPE(+0x13bc2a)[0x2ab20d1c2a]
...
(other observed result is a hang, once per ~10 runs)

Please find attached the assembler code for the LWLockWakeup() function
that is known to produce the assertion failure and note two instructions
I commented there:
  3eee4a:       lw a3,4(s6)                                        # 1014: old_state = 
pg_atomic_read_u32(&lock->state)
  3eee4e:       lw a5,8(s6)                                        # 1026: 
proclist_is_empty(&lock->waiters)

The following minimal modification:
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -1016,2 +1016,3 @@ LWLockWakeup(LWLock *lock)
                {
+__asm__ __volatile__("nop" ::: "memory");
                        desired_state = old_state;

which turns that pair into:
  3eee4a:       lw      a3,4(s6)
  3eee4e:       nop
  3eee50:       lw      a5,8(s6)

makes the test pass reliably for me (10+ iterations).

So it's not surprising now that this crash depends on postgres version
(v14/v15 only), compiler (gcc doesn't produce the code needed), its'
options (not reproduced with -O0), etc.

I've generated a minimal standalone reproducer for this anomaly (please
find it attached too), which replicates an essential part of
LWLockWakeup():
      sb      pending_wakeup, (waiting)
      lw      old_state, 4(lock)
      lw      head, 8(lock)
      and     desired_state, old_state, mask
  1:  lr.w    cur, (state)
      bne     cur, old_state, 2f
      sc.w    tmp, desired_state, (state)
      bnez    tmp, 1b
  2:

and demonstrates that LR/SC mechanism doesn't work as expected at least on
the given RISC-V CPU (8 SpacemiT X60 cores in 2 clusters):
Machine model: ky x1 orangepi-rv2 board
...
processor       : 7
hart            : 7
model name      : Ky(R) X1
isa             : rv64imafdcv_zicbom_zicboz_zicntr_zicond_zicsr_zifencei_zihintpause_zihpm_zfh_zfhmin_zca_zcd_zba_zbb_zbc_zbs_zkt_zve32f_zve32x_zve64d_zve64f_zve64x_zvfh_zvfhmin_zvkt_sscofpmf_sstc_svinval_svnapot_svpbmt
mmu             : sv39
uarch           : ky,x60
mvendorid       : 0x710
marchid         : 0x8000000058000001
mimpid          : 0x1000000049772200

Let me share results of a few experiments:
clang-20 -O1 ~/lrsc-anomaly.c -o ~/lrsc-anomaly
~/lrsc-anomaly
LOST UPDATE in round 28: value=0x70001d01 expected=0x00001d01
LOST UPDATE in round 29: value=0x70001e01 expected=0x00001e01
LOST UPDATE in round 30: value=0x70001f01 expected=0x00001f01
LOST UPDATE in round 31: value=0x70002001 expected=0x00002001
LOST UPDATE in round 32: value=0x70002101 expected=0x00002101
LOST UPDATE in round 33: value=0x70002201 expected=0x00002201
LOST UPDATE in round 34: value=0x70002301 expected=0x00002301
LOST UPDATE in round 35: value=0x70002401 expected=0x00002401
LOST UPDATE in round 37: value=0x70002601 expected=0x00002601
LOST UPDATE in round 38: value=0x70002701 expected=0x00002701
page_offset=0 rounds=1000000 retried=1824 lost=15523: FAILED

It reproduces the issue: process A's SC succeeds despite B's increment
happened in between.

for n in `seq 0 15`; do ~/lrsc-anomaly -n $n | tail -1; done
page_offset=0 rounds=1000000 retried=5374 lost=16160: FAILED
page_offset=1 rounds=1000000 retried=29563 lost=16438: FAILED
page_offset=2 rounds=1000000 retried=1251 lost=17209: FAILED
page_offset=3 rounds=1000000 retried=14960 lost=22: FAILED
page_offset=4 rounds=1000000 retried=12090 lost=19: FAILED
page_offset=5 rounds=1000000 retried=13561 lost=15: FAILED
page_offset=6 rounds=1000000 retried=15037 lost=32: FAILED
page_offset=7 rounds=1000000 retried=3124 lost=16774: FAILED
page_offset=8 rounds=1000000 retried=19813 lost=0: OK
page_offset=9 rounds=1000000 retried=20210 lost=0: OK
page_offset=10 rounds=1000000 retried=17992 lost=0: OK
page_offset=11 rounds=1000000 retried=18207 lost=0: OK
page_offset=12 rounds=1000000 retried=17903 lost=0: OK
page_offset=13 rounds=1000000 retried=18779 lost=0: OK
page_offset=14 rounds=1000000 retried=18384 lost=0: OK
page_offset=15 rounds=1000000 retried=18328 lost=0: OK

That is, the effect depends not only on that lw pair, but also on offset
of "waiting" in "sb pending_wakeup, (waiting)" preceding it.

The anomaly's probability also depends on whether these two cores/harts
are in the same cluster or not. For example, with cores 0 and 1 that belong
to cluster 0:
for n in `seq 0 4 15`; do taskset -c 0,1 ~/lrsc-anomaly -n $n | tail -1; done
page_offset=0 rounds=1000000 retried=1194 lost=16909: FAILED
page_offset=4 rounds=1000000 retried=1147 lost=16464: FAILED
page_offset=8 rounds=1000000 retried=18658 lost=0: OK
page_offset=12 rounds=1000000 retried=18154 lost=0: OK

And now with two cores that belong to different clusters:
for n in `seq 0 4 15`; do taskset -c 0,4 ~/lrsc-anomaly -n $n | tail -1; done
page_offset=0 rounds=1000000 retried=13350 lost=45: FAILED
page_offset=4 rounds=1000000 retried=15566 lost=37: FAILED
page_offset=8 rounds=1000000 retried=18223 lost=0: OK
page_offset=12 rounds=1000000 retried=17466 lost=0: OK

Also, nop inserted as above:
                /* old_state = pg_atomic_read_u32(&lock->state) */
                "       lw              %[old_state], 4(%[lock])\n"
+"nop\n"
                /* proclist_is_empty(&lock->waiters) */
                "       lw              %[head], 8(%[lock])\n"

changes the picture:
for n in `seq 0 15`; do taskset -c 0,1 ~/lrsc-anomaly -n $n | tail -1; done
page_offset=0 rounds=1000000 retried=15306 lost=17557: FAILED
page_offset=1 rounds=1000000 retried=2624 lost=16711: FAILED
page_offset=2 rounds=1000000 retried=1468 lost=16940: FAILED
page_offset=3 rounds=1000000 retried=815 lost=17098: FAILED
page_offset=4 rounds=1000000 retried=31171 lost=0: OK
page_offset=5 rounds=1000000 retried=17372 lost=0: OK
page_offset=6 rounds=1000000 retried=31803 lost=0: OK
page_offset=7 rounds=1000000 retried=19194 lost=0: OK
page_offset=8 rounds=1000000 retried=5777 lost=0: OK
page_offset=9 rounds=1000000 retried=5375 lost=0: OK
page_offset=10 rounds=1000000 retried=11920 lost=0: OK
page_offset=11 rounds=1000000 retried=5334 lost=0: OK
page_offset=12 rounds=1000000 retried=5087 lost=0: OK
page_offset=13 rounds=1000000 retried=12040 lost=0: OK
page_offset=14 rounds=1000000 retried=8479 lost=0: OK
page_offset=15 rounds=1000000 retried=4965 lost=0: OK

It kind of confirms the behavior observed with Postgres, when nop shifted
the code out of the problem area.

It also depends on number of distinct lines (buffers) in rotation; with
#define NBUFFERS        2

the probability drops almost to zero:
taskset -c 0,1 ~/lrsc-anomaly
LOST UPDATE in round 300830: value=0x7000ba01 expected=0x0000ba01
LOST UPDATE in round 300831: value=0x7000bb01 expected=0x0000bb01
page_offset=0 rounds=1000000 retried=2 lost=2: FAILED

Sorry for the long scroll, but it looks like this anomaly should be added
to our collection of the outer world's defects discovered during Postgres
testing.

Best regards,
Alexander
src/backend/postgres:     file format elf64-littleriscv


Disassembly of section .plt:

Disassembly of section .text:

00000000003eebd2 <LWLockRelease>:
  3eebd2:       addi    sp,sp,-144
  3eebd4:       sd      ra,136(sp)
  3eebd6:       sd      s0,128(sp)
  3eebd8:       sd      s1,120(sp)
  3eebda:       sd      s2,112(sp)
  3eebdc:       sd      s3,104(sp)
  3eebde:       sd      s4,96(sp)
  3eebe0:       sd      s5,88(sp)
  3eebe2:       sd      s6,80(sp)
  3eebe4:       sd      s7,72(sp)
  3eebe6:       sd      s8,64(sp)
  3eebe8:       sd      s9,56(sp)
  3eebea:       sd      s10,48(sp)
  3eebec:       mv      s6,a0
  3eebee:       auipc   a0,0x402
  3eebf2:       addi    s0,a0,1994 # 7f13b8 <NamedLWLockTrancheRequests>
  3eebf6:       lw      a0,12(s0)
  3eebf8:       auipc   a2,0x402
  3eebfc:       addiw   a1,a0,-1
  3eec00:       slti    a3,a1,-1
  3eec04:       addiw   a3,a3,-1
  3eec06:       or      a3,a3,a1
  3eec08:       addi    a2,a2,-1216 # 7f0738 <held_lwlocks>
  3eec0c:       addiw   a0,a0,-1
  3eec0e:       bltz    a0,3eec66 <LWLockRelease+0x94>
  3eec12:       slli    a4,a0,0x4
  3eec16:       add     a4,a4,a2
  3eec18:       ld      a4,0(a4)
  3eec1a:       bne     s6,a4,3eec0c <LWLockRelease+0x3a>
  3eec1e:       bltz    a0,3eec6c <LWLockRelease+0x9a>
  3eec22:       slli    a3,a0,0x4
  3eec26:       add     a3,a3,a2
  3eec28:       lw      a3,8(a3)
  3eec2a:       sw      a1,12(s0)
  3eec2c:       bge     a0,a1,3eec50 <LWLockRelease+0x7e>
  3eec30:       slli    a0,a0,0x20
  3eec32:       slli    a1,a1,0x4
  3eec34:       srli    a0,a0,0x1c
  3eec36:       add     a1,a1,a2
  3eec38:       add     a0,a0,a2
  3eec3a:       addi    a0,a0,16
  3eec3c:       addi    a1,a1,16
  3eec3e:       ld      a2,0(a0)
  3eec40:       ld      a4,8(a0)
  3eec42:       sd      a2,-16(a0)
  3eec46:       sd      a4,-8(a0)
  3eec4a:       addi    a0,a0,16
  3eec4c:       bne     a0,a1,3eec3e <LWLockRelease+0x6c>
  3eec50:       addi    s1,s6,4
  3eec54:       addi    a0,s6,7
  3eec58:       andi    a0,a0,-4
  3eec5a:       beqz    a3,3eec94 <LWLockRelease+0xc2>
  3eec5c:       bne     a0,s1,3ef184 <LWLockRelease+0x5b2>
  3eec60:       li      a1,-1
  3eec62:       li      a2,1
  3eec64:       j       3eeca0 <LWLockRelease+0xce>
  3eec66:       mv      a0,a3
  3eec68:       bgez    a0,3eec22 <LWLockRelease+0x50>
  3eec6c:       li      a0,21
  3eec6e:       li      a1,0
  3eec70:       auipc   ra,0xffce2
  3eec74:       jalr    -1810(ra) # d055e <errstart_cold>
  3eec78:       beqz    a0,3ef00a <LWLockRelease+0x438>
  3eec7c:       lhu     a0,0(s6)
  3eec80:       li      a1,47
  3eec84:       bltu    a1,a0,3eefb8 <LWLockRelease+0x3e6>
  3eec88:       auipc   a1,0x3e6
  3eec8c:       ld      a1,-1600(a1) # 7d4648 <_GLOBAL_OFFSET_TABLE_+0xb68>
  3eec90:       slli    a0,a0,0x3
  3eec92:       j       3eefda <LWLockRelease+0x408>
  3eec94:       bne     a0,s1,3ef1cc <LWLockRelease+0x5fa>
  3eec98:       lui     a1,0xff000
  3eec9c:       lui     a2,0x1000
  3eeca0:       neg     a2,a2
  3eeca4:       amoadd.w.aqrl   a2,a2,(s1)
  3eeca8:       addw    a1,a1,a2
  3eecaa:       slli    a2,a1,0x27
  3eecae:       bltz    a2,3ef13c <LWLockRelease+0x56a>
  3eecb2:       lui     a2,0x61000
  3eecb6:       addiw   a2,a2,-1 # 60ffffff <__BSS_END__+0x607e47df>
  3eecb8:       and     a1,a1,a2
  3eecba:       lui     a2,0x60000
  3eecbe:       bne     a1,a2,3eeeaa <LWLockRelease+0x2d8>
  3eecc2:       lui     a1,0x80000
  3eecc6:       addi    a1,a1,-1 # ffffffff7fffffff 
<__BSS_END__+0xffffffff7f7e47df>
  3eecc8:       sw      a1,0(sp)
  3eecca:       sw      a1,4(sp)
  3eeccc:       bne     a0,s1,3ef1a8 <LWLockRelease+0x5d6>
  3eecd0:       lui     s2,0x10000
  3eecd4:       amoor.w.aqrl    a0,s2,(s1)
  3eecd8:       slli    a0,a0,0x23
  3eecda:       bgez    a0,3eed1c <LWLockRelease+0x14a>
  3eecde:       auipc   a0,0x1ed
  3eece2:       li      s3,907
  3eece6:       auipc   a1,0x1ed
  3eecea:       addi    s4,a0,-788 # 5db9ca <hextbl+0x272cf>
  3eecee:       addi    s0,a1,264 # 5dbdee <hextbl+0x276f3>
  3eecf2:       sd      zero,8(sp)
  3eecf4:       sw      zero,16(sp)
  3eecf6:       sd      s4,24(sp)
  3eecf8:       sw      s3,32(sp)
  3eecfa:       sd      s0,40(sp)
  3eecfc:       addi    a0,sp,8
  3eecfe:       jal     3f6dcc <perform_spin_delay>
  3eed02:       lwu     a0,0(s1)
  3eed06:       slli    a0,a0,0x23
  3eed08:       bltz    a0,3eecfc <LWLockRelease+0x12a>
  3eed0c:       addi    a0,sp,8
  3eed0e:       jal     3f6e62 <finish_spin_delay>
  3eed12:       amoor.w.aqrl    a0,s2,(s1)
  3eed16:       slli    a0,a0,0x23
  3eed18:       bltz    a0,3eecf2 <LWLockRelease+0x120>
  3eed1c:       lw      a1,8(s6)
  3eed20:       lui     a3,0x80000
  3eed24:       addiw   a3,a3,-1 # 7fffffff <__BSS_END__+0x7f7e47df>
  3eed26:       mv      a2,a3
  3eed28:       beq     a1,a3,3eed44 <LWLockRelease+0x172>
  3eed2c:       auipc   a0,0x3e5
  3eed30:       ld      a0,196(a0) # 7d3df0 <_GLOBAL_OFFSET_TABLE_+0x310>
  3eed34:       ld      a0,0(a0)
  3eed36:       ld      a0,0(a0)
  3eed38:       li      a2,880
  3eed3c:       mul     a2,a1,a2
  3eed40:       add     a0,a0,a2
  3eed42:       lw      a2,96(a0)
  3eed44:       lui     a7,0x20000
  3eed48:       beq     a1,a3,3eee34 <LWLockRelease+0x262>
  3eed4c:       li      s2,0
  3eed4e:       li      s3,1
  3eed50:       auipc   a0,0x3e5
  3eed54:       li      s4,880
  3eed58:       ld      s5,160(a0) # 7d3df0 <_GLOBAL_OFFSET_TABLE_+0x310>
  3eed5c:       lui     s9,0x80000
  3eed60:       addiw   s9,s9,-1 # 7fffffff <__BSS_END__+0x7f7e47df>
  3eed62:       li      s8,2
  3eed64:       li      s7,1
  3eed66:       j       3eed6e <LWLockRelease+0x19c>
  3eed68:       mv      a1,s10
  3eed6a:       beq     s10,s9,3eee2c <LWLockRelease+0x25a>
  3eed6e:       ld      a0,0(s5)
  3eed72:       ld      a0,0(a0)
  3eed74:       mv      s10,a2
  3eed76:       mul     s0,a1,s4
  3eed7a:       andi    a2,s2,1
  3eed7e:       add     s0,s0,a0
  3eed80:       beqz    a2,3eed88 <LWLockRelease+0x1b6>
  3eed82:       lbu     a2,95(s0)
  3eed86:       beqz    a2,3eee26 <LWLockRelease+0x254>
  3eed88:       lw      a2,96(s0)
  3eed8a:       bnez    a2,3eed92 <LWLockRelease+0x1c0>
  3eed8c:       lw      a3,100(s0)
  3eed8e:       beqz    a3,3ef118 <LWLockRelease+0x546>
  3eed92:       lw      a3,100(s0)
  3eed94:       bne     a3,s9,3eedbc <LWLockRelease+0x1ea>
  3eed98:       lw      a0,8(s6)
  3eed9c:       bne     a0,a1,3ef0d0 <LWLockRelease+0x4fe>
  3eeda0:       sw      a2,8(s6)
  3eeda4:       lw      a0,96(s0)
  3eeda6:       beq     a0,s9,3eedca <LWLockRelease+0x1f8>
  3eedaa:       ld      a2,0(s5)
  3eedae:       ld      a2,0(a2)
  3eedb0:       lw      a3,100(s0)
  3eedb2:       mul     a0,a0,s4
  3eedb6:       add     a0,a0,a2
  3eedb8:       sw      a3,100(a0)
  3eedba:       j       3eedd8 <LWLockRelease+0x206>
  3eedbc:       mul     a3,a3,s4
  3eedc0:       add     a0,a0,a3
  3eedc2:       sw      a2,96(a0)
  3eedc4:       lw      a0,96(s0)
  3eedc6:       bne     a0,s9,3eedaa <LWLockRelease+0x1d8>
  3eedca:       lw      a0,12(s6)
  3eedce:       bne     a0,a1,3ef0f4 <LWLockRelease+0x522>
  3eedd2:       lw      a0,100(s0)
  3eedd4:       sw      a0,12(s6)
  3eedd8:       sw      zero,96(s0)
  3eeddc:       sw      zero,100(s0)
  3eede0:       mv      a0,sp
  3eede2:       jal     3eeaaa <proclist_push_tail_offset>
  3eede6:       lbu     a0,94(s0)
  3eedea:       bne     a0,s3,3ef088 <LWLockRelease+0x4b6>
  3eedee:       lbu     a0,95(s0)
  3eedf2:       sb      s8,94(s0)
  3eedf6:       addi    a1,a0,-2
  3eedfa:       snez    a2,a1
  3eedfe:       seqz    a1,a1
  3eee02:       and     s7,a1,s7
  3eee06:       or      s2,s2,a2
  3eee0a:       seqz    a0,a0
  3eee0e:       bnez    a0,3eee2c <LWLockRelease+0x25a>
  3eee10:       mv      a2,s9
  3eee12:       beq     s10,s9,3eed68 <LWLockRelease+0x196>
  3eee16:       ld      a0,0(s5)
  3eee1a:       ld      a0,0(a0)
  3eee1c:       mul     a1,s10,s4
  3eee20:       add     a0,a0,a1
  3eee22:       lw      a2,96(a0)
  3eee24:       j       3eed68 <LWLockRelease+0x196>
  3eee26:       li      a0,0
  3eee28:       li      s2,1
  3eee2a:       beqz    a0,3eee10 <LWLockRelease+0x23e>
  3eee2c:       andi    a0,s7,1
  3eee30:       slli    a7,a0,0x1d
  3eee34:       lw      a1,0(sp)
  3eee36:       lui     a2,0x80000
  3eee3a:       addiw   a2,a2,-1 # 7fffffff <__BSS_END__+0x7f7e47df>
  3eee3c:       beq     a1,a2,3eee4a <LWLockRelease+0x278>
  3eee40:       lwu     a1,0(s1)
  3eee44:       slli    a1,a1,0x21
  3eee46:       bgez    a1,3ef1f0 <LWLockRelease+0x61e>
  3eee4a:       lw      a3,4(s6)                                        # 1014: 
old_state = pg_atomic_read_u32(&lock->state)
  3eee4e:       lw      a5,8(s6)                                        # 1026: 
proclist_is_empty(&lock->waiters)
  3eee52:       lui     a1,0xd0000
  3eee56:       addiw   a1,a1,-1 # ffffffffcfffffff 
<__BSS_END__+0xffffffffcf7e47df>
  3eee58:       and     a4,a3,a1
  3eee5c:       or      a4,a4,a7
  3eee60:       beq     a5,a2,3eee6a <LWLockRelease+0x298>
  3eee64:       lui     a2,0xf0000
  3eee68:       j       3eee6e <LWLockRelease+0x29c>
  3eee6a:       lui     a2,0xb0000
  3eee6e:       addiw   a2,a2,-1 # ffffffffafffffff 
<__BSS_END__+0xffffffffaf7e47df>
  3eee70:       and     a4,a4,a2
  3eee72:       lr.w.aqrl       a2,(s1)
  3eee76:       bne     a2,a3,3eeeda <LWLockRelease+0x308>
  3eee7a:       sc.w.rl a5,a4,(s1)
  3eee7e:       bnez    a5,3eee72 <LWLockRelease+0x2a0>
  3eee80:       lw      a0,0(sp)
  3eee82:       lui     a1,0x80000
  3eee86:       addiw   a1,a1,-1 # 7fffffff <__BSS_END__+0x7f7e47df>
  3eee88:       mv      a2,a1
  3eee8a:       beq     a0,a1,3eeea6 <LWLockRelease+0x2d4>
  3eee8e:       auipc   a2,0x3e5
  3eee92:       ld      a2,-158(a2) # 7d3df0 <_GLOBAL_OFFSET_TABLE_+0x310>
  3eee96:       ld      a2,0(a2)
  3eee98:       ld      a2,0(a2)
  3eee9a:       li      a3,880
  3eee9e:       mul     a3,a0,a3
  3eeea2:       add     a2,a2,a3
  3eeea4:       lw      a2,96(a2)
  3eeea6:       bne     a0,a1,3eef1c <LWLockRelease+0x34a>
  3eeeaa:       auipc   a0,0x3e6
  3eeeae:       ld      a0,294(a0) # 7d4fd0 <_GLOBAL_OFFSET_TABLE_+0x14f0>
  3eeeb2:       lw      a1,0(a0)
  3eeeb4:       beqz    a1,3ef160 <LWLockRelease+0x58e>
  3eeeb8:       lw      a1,0(a0)
  3eeeba:       addi    a1,a1,-1
  3eeebc:       sw      a1,0(a0)
  3eeebe:       ld      ra,136(sp)
  3eeec0:       ld      s0,128(sp)
  3eeec2:       ld      s1,120(sp)
  3eeec4:       ld      s2,112(sp)
  3eeec6:       ld      s3,104(sp)
  3eeec8:       ld      s4,96(sp)
  3eeeca:       ld      s5,88(sp)
  3eeecc:       ld      s6,80(sp)
  3eeece:       ld      s7,72(sp)
  3eeed0:       ld      s8,64(sp)
  3eeed2:       ld      s9,56(sp)
  3eeed4:       ld      s10,48(sp)
  3eeed6:       addi    sp,sp,144
  3eeed8:       ret
  3eeeda:       lui     a3,0xf0000
  3eeede:       lui     s0,0xb0000
  3eeee2:       lui     a5,0x80000
  3eeee6:       addiw   a6,a3,-1 # ffffffffefffffff 
<__BSS_END__+0xffffffffef7e47df>
  3eeeea:       addiw   s0,s0,-1 # ffffffffafffffff 
<__BSS_END__+0xffffffffaf7e47df>
  3eeeec:       addiw   a5,a5,-1 # 7fffffff <__BSS_END__+0x7f7e47df>
  3eeeee:       j       3eef0e <LWLockRelease+0x33c>
  3eeef0:       sext.w  a3,a2
  3eeef4:       and     a2,a2,a1
  3eeef6:       or      a2,a2,a7
  3eeefa:       and     a4,a4,a2
  3eeefc:       lr.w.aqrl       a2,(s1)
  3eef00:       bne     a2,a3,3eef0a <LWLockRelease+0x338>
  3eef04:       sc.w.rl a0,a4,(s1)
  3eef08:       bnez    a0,3eeefc <LWLockRelease+0x32a>
  3eef0a:       beq     a2,a3,3eee80 <LWLockRelease+0x2ae>
  3eef0e:       lw      a3,8(s6)
  3eef12:       mv      a4,s0
  3eef14:       beq     a3,a5,3eeef0 <LWLockRelease+0x31e>
  3eef18:       mv      a4,a6
  3eef1a:       j       3eeef0 <LWLockRelease+0x31e>
  3eef1c:       auipc   a1,0x3e5
  3eef20:       ld      s3,-300(a1) # 7d3df0 <_GLOBAL_OFFSET_TABLE_+0x310>
  3eef24:       li      s2,880
  3eef28:       lui     s1,0x80000
  3eef2c:       addiw   s1,s1,-1 # 7fffffff <__BSS_END__+0x7f7e47df>
  3eef2e:       j       3eef36 <LWLockRelease+0x364>
  3eef30:       mv      a0,s0
  3eef32:       beq     s0,s1,3eeeaa <LWLockRelease+0x2d8>
  3eef36:       ld      a1,0(s3)
  3eef3a:       ld      a4,0(a1)
  3eef3c:       mul     a1,a0,s2
  3eef40:       add     a1,a1,a4
  3eef42:       lw      a3,96(a1)
  3eef44:       mv      s0,a2
  3eef46:       bnez    a3,3eef4e <LWLockRelease+0x37c>
  3eef48:       lw      a2,100(a1)
  3eef4a:       beqz    a2,3ef0ac <LWLockRelease+0x4da>
  3eef4e:       lw      a2,100(a1)
  3eef50:       bne     a2,s1,3eef74 <LWLockRelease+0x3a2>
  3eef54:       lw      a2,0(sp)
  3eef56:       bne     a2,a0,3ef040 <LWLockRelease+0x46e>
  3eef5a:       sw      a3,0(sp)
  3eef5c:       lw      a2,96(a1)
  3eef5e:       beq     a2,s1,3eef82 <LWLockRelease+0x3b0>
  3eef62:       ld      a0,0(s3)
  3eef66:       ld      a0,0(a0)
  3eef68:       lw      a3,100(a1)
  3eef6a:       mul     a2,a2,s2
  3eef6e:       add     a0,a0,a2
  3eef70:       sw      a3,100(a0)
  3eef72:       j       3eef8c <LWLockRelease+0x3ba>
  3eef74:       mul     a2,a2,s2
  3eef78:       add     a2,a2,a4
  3eef7a:       sw      a3,96(a2)
  3eef7c:       lw      a2,96(a1)
  3eef7e:       bne     a2,s1,3eef62 <LWLockRelease+0x390>
  3eef82:       lw      a2,4(sp)
  3eef84:       bne     a2,a0,3ef064 <LWLockRelease+0x492>
  3eef88:       lw      a0,100(a1)
  3eef8a:       sw      a0,4(sp)
  3eef8c:       sw      zero,96(a1)
  3eef90:       sw      zero,100(a1)
  3eef94:       fence   rw,w
  3eef98:       ld      a0,24(a1)
  3eef9a:       sb      zero,94(a1)
  3eef9e:       jal     36b150 <PGSemaphoreUnlock>
  3eefa2:       mv      a2,s1
  3eefa4:       beq     s0,s1,3eef30 <LWLockRelease+0x35e>
  3eefa8:       ld      a0,0(s3)
  3eefac:       ld      a0,0(a0)
  3eefae:       mul     a1,s0,s2
  3eefb2:       add     a0,a0,a1
  3eefb4:       lw      a2,96(a0)
  3eefb6:       j       3eef30 <LWLockRelease+0x35e>
  3eefb8:       li      a1,74
  3eefbc:       bltu    a1,a0,3ef012 <LWLockRelease+0x440>
  3eefc0:       li      a1,1
  3eefc2:       slli    a1,a1,0x20
  3eefc4:       addi    a1,a1,-48
  3eefc8:       add     a0,a0,a1
  3eefca:       addi    a1,a1,47
  3eefce:       and     a0,a0,a1
  3eefd0:       auipc   a1,0x3c8
  3eefd4:       slli    a0,a0,0x3
  3eefd6:       addi    a1,a1,32 # 7b6ff0 <BuiltinTrancheNames>
  3eefda:       add     a0,a0,a1
  3eefdc:       ld      a1,0(a0)
  3eefde:       auipc   a0,0x1ed
  3eefe2:       addi    a0,a0,-1105 # 5dbb8d <hextbl+0x27492>
  3eefe6:       auipc   ra,0x118
  3eefea:       jalr    -906(ra) # 506c5c <errmsg_internal>
  3eefee:       auipc   a0,0x1ed
  3eeff2:       auipc   a1,0x1ed
  3eeff6:       addi    a0,a0,-1572 # 5db9ca <hextbl+0x272cf>
  3eeffa:       addi    a2,a1,-1105 # 5dbba1 <hextbl+0x274a6>
  3eeffe:       li      a1,1823
  3ef002:       auipc   ra,0x118
  3ef006:       jalr    -642(ra) # 506d80 <errfinish>
  3ef00a:       auipc   ra,0xffcde
  3ef00e:       jalr    -362(ra) # ccea0 <abort@plt>
  3ef012:       lw      a1,4(s0)
  3ef014:       addi    a0,a0,-75
  3ef018:       slli    a0,a0,0x30
  3ef01a:       srli    a2,a0,0x30
  3ef01e:       bge     a2,a1,3ef036 <LWLockRelease+0x464>
  3ef022:       ld      a1,40(s0)
  3ef024:       srli    a0,a0,0x2d
  3ef026:       add     a0,a0,a1
  3ef028:       ld      a1,0(a0)
  3ef02a:       bnez    a1,3eefde <LWLockRelease+0x40c>
  3ef02c:       auipc   a0,0x1c4
  3ef030:       addi    a1,a0,-40 # 5b3004 <year_lengths+0x5b66c>
  3ef034:       j       3eefde <LWLockRelease+0x40c>
  3ef036:       auipc   a0,0x1c4
  3ef03a:       addi    a1,a0,-50 # 5b3004 <year_lengths+0x5b66c>
  3ef03e:       j       3eefde <LWLockRelease+0x40c>
  3ef040:       auipc   a0,0x1eb
  3ef044:       auipc   a1,0x169
  3ef048:       auipc   a2,0x1eb
  3ef04c:       addi    a0,a0,1494 # 5da616 <hextbl+0x25f1b>
  3ef050:       addi    a1,a1,-1379 # 557ae1 <year_lengths+0x149>
  3ef054:       addi    a2,a2,1305 # 5da561 <hextbl+0x25e66>
  3ef058:       li      a3,123
  3ef05c:       auipc   ra,0xffce1
  3ef060:       jalr    1144(ra) # d04d4 <ExceptionalCondition>
  3ef064:       auipc   a0,0x1eb
  3ef068:       auipc   a1,0x169
  3ef06c:       auipc   a2,0x1eb
  3ef070:       addi    a0,a0,1513 # 5da64d <hextbl+0x25f52>
  3ef074:       addi    a1,a1,-1415 # 557ae1 <year_lengths+0x149>
  3ef078:       addi    a2,a2,1269 # 5da561 <hextbl+0x25e66>
  3ef07c:       li      a3,131
  3ef080:       auipc   ra,0xffce1
  3ef084:       jalr    1108(ra) # d04d4 <ExceptionalCondition>
  3ef088:       auipc   a0,0x1ed
  3ef08c:       auipc   a1,0x169
  3ef090:       auipc   a2,0x1ed
  3ef094:       addi    a0,a0,-1310 # 5dbb6a <hextbl+0x2746f>
  3ef098:       addi    a1,a1,-1451 # 557ae1 <year_lengths+0x149>
  3ef09c:       addi    a2,a2,-1734 # 5db9ca <hextbl+0x272cf>
  3ef0a0:       li      a3,996
  3ef0a4:       auipc   ra,0xffce1
  3ef0a8:       jalr    1072(ra) # d04d4 <ExceptionalCondition>
  3ef0ac:       auipc   a0,0x1eb
  3ef0b0:       auipc   a1,0x169
  3ef0b4:       auipc   a2,0x1eb
  3ef0b8:       addi    a0,a0,1462 # 5da662 <hextbl+0x25f67>
  3ef0bc:       addi    a1,a1,-1487 # 557ae1 <year_lengths+0x149>
  3ef0c0:       addi    a2,a2,1197 # 5da561 <hextbl+0x25e66>
  3ef0c4:       li      a3,119
  3ef0c8:       auipc   ra,0xffce1
  3ef0cc:       jalr    1036(ra) # d04d4 <ExceptionalCondition>
  3ef0d0:       auipc   a0,0x1eb
  3ef0d4:       auipc   a1,0x169
  3ef0d8:       auipc   a2,0x1eb
  3ef0dc:       addi    a0,a0,1350 # 5da616 <hextbl+0x25f1b>
  3ef0e0:       addi    a1,a1,-1523 # 557ae1 <year_lengths+0x149>
  3ef0e4:       addi    a2,a2,1161 # 5da561 <hextbl+0x25e66>
  3ef0e8:       li      a3,123
  3ef0ec:       auipc   ra,0xffce1
  3ef0f0:       jalr    1000(ra) # d04d4 <ExceptionalCondition>
  3ef0f4:       auipc   a0,0x1eb
  3ef0f8:       auipc   a1,0x169
  3ef0fc:       auipc   a2,0x1eb
  3ef100:       addi    a0,a0,1369 # 5da64d <hextbl+0x25f52>
  3ef104:       addi    a1,a1,-1559 # 557ae1 <year_lengths+0x149>
  3ef108:       addi    a2,a2,1125 # 5da561 <hextbl+0x25e66>
  3ef10c:       li      a3,131
  3ef110:       auipc   ra,0xffce1
  3ef114:       jalr    964(ra) # d04d4 <ExceptionalCondition>
  3ef118:       auipc   a0,0x1eb
  3ef11c:       auipc   a1,0x169
  3ef120:       auipc   a2,0x1eb
  3ef124:       addi    a0,a0,1354 # 5da662 <hextbl+0x25f67>
  3ef128:       addi    a1,a1,-1595 # 557ae1 <year_lengths+0x149>
  3ef12c:       addi    a2,a2,1089 # 5da561 <hextbl+0x25e66>
  3ef130:       li      a3,119
  3ef134:       auipc   ra,0xffce1
  3ef138:       jalr    928(ra) # d04d4 <ExceptionalCondition>
  3ef13c:       auipc   a0,0x1ed
  3ef140:       auipc   a1,0x169
  3ef144:       auipc   a2,0x1ed
  3ef148:       addi    a0,a0,-1421 # 5dbbaf <hextbl+0x274b4>
  3ef14c:       addi    a1,a1,-1631 # 557ae1 <year_lengths+0x149>
  3ef150:       addi    a2,a2,-1914 # 5db9ca <hextbl+0x272cf>
  3ef154:       li      a3,1843
  3ef158:       auipc   ra,0xffce1
  3ef15c:       jalr    892(ra) # d04d4 <ExceptionalCondition>
  3ef160:       auipc   a0,0x181
  3ef164:       auipc   a1,0x169
  3ef168:       auipc   a2,0x1ed
  3ef16c:       addi    a0,a0,-1304 # 56fc48 <year_lengths+0x182b0>
  3ef170:       addi    a1,a1,-1667 # 557ae1 <year_lengths+0x149>
  3ef174:       addi    a2,a2,-1950 # 5db9ca <hextbl+0x272cf>
  3ef178:       li      a3,1873
  3ef17c:       auipc   ra,0xffce1
  3ef180:       jalr    856(ra) # d04d4 <ExceptionalCondition>
  3ef184:       auipc   a0,0x17f
  3ef188:       auipc   a1,0x17f
  3ef18c:       auipc   a2,0x17f
  3ef190:       addi    a0,a0,1487 # 56e753 <year_lengths+0x16dbb>
  3ef194:       addi    a1,a1,-1227 # 56dcbd <year_lengths+0x16325>
  3ef198:       addi    a2,a2,-1214 # 56dcce <year_lengths+0x16336>
  3ef19c:       li      a3,403
  3ef1a0:       auipc   ra,0xffce1
  3ef1a4:       jalr    820(ra) # d04d4 <ExceptionalCondition>
  3ef1a8:       auipc   a0,0x17f
  3ef1ac:       auipc   a1,0x17f
  3ef1b0:       auipc   a2,0x17f
  3ef1b4:       addi    a0,a0,1451 # 56e753 <year_lengths+0x16dbb>
  3ef1b8:       addi    a1,a1,-1263 # 56dcbd <year_lengths+0x16325>
  3ef1bc:       addi    a2,a2,-1250 # 56dcce <year_lengths+0x16336>
  3ef1c0:       li      a3,374
  3ef1c4:       auipc   ra,0xffce1
  3ef1c8:       jalr    784(ra) # d04d4 <ExceptionalCondition>
  3ef1cc:       auipc   a0,0x17f
  3ef1d0:       auipc   a1,0x17f
  3ef1d4:       auipc   a2,0x17f
  3ef1d8:       addi    a0,a0,1415 # 56e753 <year_lengths+0x16dbb>
  3ef1dc:       addi    a1,a1,-1299 # 56dcbd <year_lengths+0x16325>
  3ef1e0:       addi    a2,a2,-1286 # 56dcce <year_lengths+0x16336>
  3ef1e4:       li      a3,403
  3ef1e8:       auipc   ra,0xffce1
  3ef1ec:       jalr    748(ra) # d04d4 <ExceptionalCondition>
  3ef1f0:       auipc   a0,0x1ed
  3ef1f4:       auipc   a1,0x169
  3ef1f8:       auipc   a2,0x1ec
  3ef1fc:       addi    a0,a0,-980 # 5dbe1c <hextbl+0x27721>
  3ef200:       addi    a1,a1,-1811 # 557ae1 <year_lengths+0x149>
  3ef204:       addi    a2,a2,2002 # 5db9ca <hextbl+0x272cf>
  3ef208:       li      a3,1007
  3ef20c:       auipc   ra,0xffce1
  3ef210:       jalr    712(ra) # d04d4 <ExceptionalCondition>
/*
 * lrsc_tiny.c
 *		Two-core reproducer for lost LR/SC updates on RISC-V.
 *
 * Core A releases an LWLock as LWLockRelease() does (state = &lock->state = lock + 4; waiting: a byte on another
 * page, at the page offset of state + n):
 *
 *		sb		pending_wakeup, (waiting)
 *		lw		old_state, 4(lock)
 *		lw		head, 8(lock)
 *		and		desired_state, old_state, mask
 *	1:	lr.w	cur, (state)
 *		bne		cur, old_state, 2f
 *		sc.w	tmp, desired_state, (state)
 *		bnez	tmp, 1b
 *	2:
 *
 * while core B adds 1 to state.  Depending on n, A's SC might succeed although B's add came in between; the exit
 * status is then 1.
 */
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>

#define FLAGS		(7u << 28)		/* HAS_WAITERS, RELEASE_OK, LOCKED */
/* set by A's release in round r, kept by the CAS */
#define TAG(r)		(((r) % 0xff + 1) << 8)
#define TAG_MASK	(0xff << 8)
#define PAGE_SIZE	4096
#define NBUFFERS	512		/* 32 KiB of 64-byte BufferDescs */

struct shared
{
	uint32_t	done __attribute__((aligned(128)));
	uint8_t		pgproc[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));	/* waiter->lwWaiting */
	uint8_t		buffers[NBUFFERS][64];
};

/* round r's word: at offset 40 as content_lock.state in a BufferDesc */
static uint32_t *
word(struct shared *sh, int r)
{
	return (uint32_t *) &sh->buffers[r % NBUFFERS][40];
}

/* LWLockWakeup()'s last store and final CAS: mark the waiter, clear FLAGS; returns 1 if B came in first */
static int
wakeup_unlock(uint32_t *state, uint8_t *waiting)
{
	char	   *lock = (char *) state - 4;	/* the LWLock: tranche, state, waiters */
	long		old_state, head, desired_state, cur, tmp;

	__asm__ __volatile__(
		/* waiter->lwWaiting = LW_WS_PENDING_WAKEUP */
		"	sb		%[pending_wakeup], 0(%[waiting])\n"
		/* old_state = pg_atomic_read_u32(&lock->state) */
		"	lw		%[old_state], 4(%[lock])\n"
		/* proclist_is_empty(&lock->waiters) */
		"	lw		%[head], 8(%[lock])\n"
		/* desired_state = old_state & ~(HAS_WAITERS | RELEASE_OK | LOCKED) */
		"	and		%[desired_state], %[old_state], %[mask]\n"
		/* pg_atomic_compare_exchange_u32(&lock->state, &old_state, ...) */
		"1:	lr.w	%[cur], (%[state])\n"
		"	bne		%[cur], %[old_state], 2f\n"
		"	sc.w	%[tmp], %[desired_state], (%[state])\n"
		"	bnez	%[tmp], 1b\n"
		"2:\n"
		: [old_state] "=&r"(old_state), [head] "=&r"(head), [cur] "=&r"(cur), [tmp] "=&r"(tmp),
		  [desired_state] "=&r"(desired_state)
		: [lock] "r"(lock), [state] "r"(state), [waiting] "r"(waiting), [pending_wakeup] "r"(2L),
		  [mask] "r"((long) ~FLAGS)
		: "memory");
	if (cur == old_state)
		return 0;

	/* B came in: retry from what the LR saw, as the while (true) loop does */
	uint32_t	o = (uint32_t) cur;

	while (!__atomic_compare_exchange_n(state, &o, o & ~FLAGS, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
		;
	return 1;
}

int
main(int argc, char **argv)
{
	int			n = 0, c, r;
	int			retried = 0, lost = 0;
	struct shared *sh = mmap(NULL, sizeof(*sh), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);

	if (sh == MAP_FAILED)
		return 2;
	while ((c = getopt(argc, argv, "n:")) != -1)
		if (c == 'n')
			n = atoi(optarg);
		else
			return 2;

	if (fork() == 0)
	{
		/*
		 * B: once A has released state, add 1; as many rounds as A.  Only A's release sets this round's TAG (its CAS
		 * keeps it), and a reused buffer still has an older one, so B's add comes after that store.
		 */
		for (r = 1; r <= 1000000; r++)
		{
			uint32_t   *state = word(sh, r);

			while ((__atomic_load_n(state, __ATOMIC_RELAXED) & TAG_MASK) != TAG(r))
				;
			__atomic_fetch_add(state, 1, __ATOMIC_SEQ_CST);
			__atomic_store_n(&sh->done, r, __ATOMIC_RELEASE);
		}
		_exit(0);
	}

	/* A: release state, clear FLAGS, check B's +1 */
	for (r = 1; r <= 1000000; r++)
	{
		uint32_t   *state = word(sh, r), v;

		/* vary A's timing relative to B */
		for (volatile uint32_t j = 0; j < (r >> 12) % 257; j++)
			;

		__atomic_store_n(state, FLAGS | TAG(r), __ATOMIC_RELAXED);

		retried += wakeup_unlock(state, sh->pgproc + (((uintptr_t) state + n) & (PAGE_SIZE - 1)));

		while (__atomic_load_n(&sh->done, __ATOMIC_ACQUIRE) != r)
			;
		v = __atomic_load_n(state, __ATOMIC_SEQ_CST);
		if (v != TAG(r) + 1 && lost++ < 10)
			printf("LOST UPDATE in round %d: value=0x%08x expected=0x%08x\n", r, v, TAG(r) + 1);
	}

	printf("page_offset=%d rounds=%d retried=%d lost=%d: %s\n", n, r - 1, retried, lost, lost ? "FAILED" : "OK");
	return lost != 0;
}

Reply via email to