diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c new file mode 100644 index 2ebd4a9..14898dc *** a/src/backend/storage/buffer/bufmgr.c --- b/src/backend/storage/buffer/bufmgr.c *************** ReleaseAndReadBuffer(Buffer buffer, *** 1474,1479 **** --- 1474,1504 ---- return ReadBuffer(relation, blockNum); } + static uint32 + TryPinBuffer(volatile pg_atomic_uint32 *state) + { + uint32 result; + + __asm__ __volatile__( + "0: lwarx %0,0,%2 \n" + " and 4,%0,%3 \n" /* check for BM_LOCKED */ + " cmpwi 4,0 \n" + " bne- 2f \n" + " addi %0,%0,1 \n" /* refcount++ */ + " and 4,%0,%4 \n" /* check for BUF_USAGECOUNT */ + " cmp 0,4,%5 \n" + " beq 1f \n" + " add %0,%0,%6 \n" /* usagecount++ */ + "1: stwcx. %0,0,%2 \n" + " bne- 0b \n" + "2: isync \n" + : "=&r"(result), "+m"(*state) + : "r"(state), "r"(BM_LOCKED), "r"(BUF_USAGECOUNT_MASK), "r"(BM_MAX_USAGE_COUNT << BUF_USAGECOUNT_SHIFT), "r"(BUF_USAGECOUNT_ONE) + : "memory", "cc", "r4"); + + return result; + } + /* * PinBuffer -- make buffer unavailable for replacement. * *************** PinBuffer(BufferDesc *buf, BufferAccessS *** 1504,1541 **** if (ref == NULL) { uint32 state; - uint32 oldstate; ReservePrivateRefCountEntry(); ref = NewPrivateRefCountEntry(b); - - state = pg_atomic_read_u32(&buf->state); - oldstate = state; - while (true) { ! /* spin-wait till lock is free */ ! while (state & BM_LOCKED) ! { ! pg_spin_delay(); ! state = pg_atomic_read_u32(&buf->state); ! oldstate = state; ! } ! ! /* increase refcount */ ! state += BUF_REFCOUNT_ONE; ! ! /* increase usagecount unless already max */ ! if (BUF_STATE_GET_USAGECOUNT(state) != BM_MAX_USAGE_COUNT) ! state += BUF_USAGECOUNT_ONE; ! ! if (pg_atomic_compare_exchange_u32(&buf->state, &oldstate, state)) break; - /* get ready for next loop, oldstate has been updated by cas */ - state = oldstate; - } result = (state & BM_VALID) != 0; } else --- 1529,1546 ---- if (ref == NULL) { uint32 state; ReservePrivateRefCountEntry(); ref = NewPrivateRefCountEntry(b); while (true) { ! state = TryPinBuffer(&buf->state); ! if ((state & BM_LOCKED) == 0) break; + pg_spin_delay(); + }; result = (state & BM_VALID) != 0; } else diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c new file mode 100644 index b13ebc6..bda940d *** a/src/backend/storage/lmgr/lwlock.c --- b/src/backend/storage/lmgr/lwlock.c *************** LWLockInitialize(LWLock *lock, int tranc *** 570,645 **** dlist_init(&lock->waiters); } - /* - * Internal function that tries to atomically acquire the lwlock in the passed - * in mode. - * - * This function will not block waiting for a lock to become free - that's the - * callers job. - * - * Returns true if the lock isn't free and we need to wait. - */ static bool LWLockAttemptLock(LWLock *lock, LWLockMode mode) { ! uint32 old_state; AssertArg(mode == LW_EXCLUSIVE || mode == LW_SHARED); ! /* ! * Read once outside the loop, later iterations will get the newer value ! * via compare & exchange. ! */ ! old_state = pg_atomic_read_u32(&lock->state); ! ! /* loop until we've determined whether we could acquire the lock or not */ ! while (true) { ! uint32 desired_state; ! bool lock_free; ! ! desired_state = old_state; ! if (mode == LW_EXCLUSIVE) ! { ! lock_free = (old_state & LW_LOCK_MASK) == 0; ! if (lock_free) ! desired_state += LW_VAL_EXCLUSIVE; ! } ! else ! { ! lock_free = (old_state & LW_VAL_EXCLUSIVE) == 0; ! if (lock_free) ! desired_state += LW_VAL_SHARED; ! } ! /* ! * Attempt to swap in the state we are expecting. If we didn't see ! * lock to be free, that's just the old value. If we saw it as free, ! * we'll attempt to mark it acquired. The reason that we always swap ! * in the value is that this doubles as a memory barrier. We could try ! * to be smarter and only swap in values if we saw the lock as free, ! * but benchmark haven't shown it as beneficial so far. ! * ! * Retry if the value changed since we last looked at it. ! */ ! if (pg_atomic_compare_exchange_u32(&lock->state, ! &old_state, desired_state)) ! { ! if (lock_free) ! { ! /* Great! Got the lock. */ ! #ifdef LOCK_DEBUG ! if (mode == LW_EXCLUSIVE) ! lock->owner = MyProc; ! #endif ! return false; ! } ! else ! return true; /* someobdy else has the lock */ ! } ! } ! pg_unreachable(); } /* --- 570,611 ---- dlist_init(&lock->waiters); } static bool LWLockAttemptLock(LWLock *lock, LWLockMode mode) { ! uint32 mask, increment; ! bool result; AssertArg(mode == LW_EXCLUSIVE || mode == LW_SHARED); ! if (mode == LW_EXCLUSIVE) { ! mask = LW_LOCK_MASK; ! increment = LW_VAL_EXCLUSIVE; ! } ! else ! { ! mask = LW_VAL_EXCLUSIVE; ! increment = LW_VAL_SHARED; ! } ! __asm__ __volatile__( ! "0: lwarx 3,0,%4 \n" ! " and 4,3,%2 \n" ! " cmpwi 4,0 \n" ! " bne- 1f \n" ! " add 3,3,%3 \n" ! " stwcx. 3,0,%4 \n" ! " bne- 0b \n" ! " li %0,0 \n" ! " b 2f \n" ! "1: li %0,1 \n" ! "2: isync \n" ! : "=&r"(result), "+m"(lock->state) ! : "r"(mask), "r"(increment), "r"(&lock->state) ! : "memory", "cc", "r3", "r4"); ! return result; } /*