ANDROID: locking/rwsem: only clean RWSEM_FLAG_HANDOFF when already set

sem->count will be negative after writer is killed
if flag RWSEM_FLAG_HANDOFF is not set, we shouldn't clean again

            CPU2                                 CPU4
    task A[reader]                         task B[writer]
    down_read_killable[locked]
    sem->count=0x100

                                           down_write_killable
                                           sem->count=0x102[wlist not empty]
    up_read
    count=0x2
                                           sig kill received
    down_read_killable
    sem->count=0x102[wlist not empty]

                                           goto branch out_nolock:
                                           list_del(&waiter.list);
                                           wait list is empty
                                           sem->count-RWSEM_FLAG_HANDOFF
                                           sem->count=0xFE
                                           list_empty(&sem->wait_list) is TRUE
                                           sem->count andnot RWSEM_FLAG_WAITERS
                                           sem->count=0xFC
    up_read
    sem->count-=0x100
    sem->count=0xFFFFFFFFFFFFFFFC
    DEBUG_RWSEMS_WARN_ON(tmp < 0, sem);

Bug: 204595609
Link: https://lore.kernel.org/all/a630a9aa-8c66-31c9-21a0-3d30bde2c9df@redhat.com/T/
Signed-off-by: mazhenhua <mazhenhua@xiaomi.com>
Change-Id: Ife64c179335d74768a3d68e402c72d10148f3e7e
This commit is contained in:
mazhenhua
2021-11-10 11:16:06 +08:00
committed by Todd Kjos
parent a1a4c80265
commit 7a7b5f89d9

View File

@@ -1287,7 +1287,7 @@ out_nolock:
list_del(&waiter.list); list_del(&waiter.list);
if (unlikely(wstate == WRITER_HANDOFF)) if (unlikely(wstate == WRITER_HANDOFF))
atomic_long_add(-RWSEM_FLAG_HANDOFF, &sem->count); atomic_long_andnot(RWSEM_FLAG_HANDOFF, &sem->count);
if (list_empty(&sem->wait_list)) if (list_empty(&sem->wait_list))
atomic_long_andnot(RWSEM_FLAG_WAITERS, &sem->count); atomic_long_andnot(RWSEM_FLAG_WAITERS, &sem->count);