|
|
|
@ -8,7 +8,7 @@ |
|
|
|
* |
|
|
|
* |
|
|
|
* |
|
|
|
* |
|
|
|
* IDENTIFICATION |
|
|
|
* IDENTIFICATION |
|
|
|
* $PostgreSQL: pgsql/src/backend/storage/buffer/bufmgr.c,v 1.208 2006/07/14 14:52:22 momjian Exp $ |
|
|
|
* $PostgreSQL: pgsql/src/backend/storage/buffer/bufmgr.c,v 1.209 2006/07/23 03:07:58 tgl Exp $ |
|
|
|
* |
|
|
|
* |
|
|
|
*------------------------------------------------------------------------- |
|
|
|
*------------------------------------------------------------------------- |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
@ -293,7 +293,11 @@ BufferAlloc(Relation reln, |
|
|
|
bool *foundPtr) |
|
|
|
bool *foundPtr) |
|
|
|
{ |
|
|
|
{ |
|
|
|
BufferTag newTag; /* identity of requested block */ |
|
|
|
BufferTag newTag; /* identity of requested block */ |
|
|
|
BufferTag oldTag; |
|
|
|
uint32 newHash; /* hash value for newTag */ |
|
|
|
|
|
|
|
LWLockId newPartitionLock; /* buffer partition lock for it */ |
|
|
|
|
|
|
|
BufferTag oldTag; /* previous identity of selected buffer */ |
|
|
|
|
|
|
|
uint32 oldHash; /* hash value for oldTag */ |
|
|
|
|
|
|
|
LWLockId oldPartitionLock; /* buffer partition lock for it */ |
|
|
|
BufFlags oldFlags; |
|
|
|
BufFlags oldFlags; |
|
|
|
int buf_id; |
|
|
|
int buf_id; |
|
|
|
volatile BufferDesc *buf; |
|
|
|
volatile BufferDesc *buf; |
|
|
|
@ -302,9 +306,13 @@ BufferAlloc(Relation reln, |
|
|
|
/* create a tag so we can lookup the buffer */ |
|
|
|
/* create a tag so we can lookup the buffer */ |
|
|
|
INIT_BUFFERTAG(newTag, reln, blockNum); |
|
|
|
INIT_BUFFERTAG(newTag, reln, blockNum); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* determine its hash code and partition lock ID */ |
|
|
|
|
|
|
|
newHash = BufTableHashCode(&newTag); |
|
|
|
|
|
|
|
newPartitionLock = BufMappingPartitionLock(newHash); |
|
|
|
|
|
|
|
|
|
|
|
/* see if the block is in the buffer pool already */ |
|
|
|
/* see if the block is in the buffer pool already */ |
|
|
|
LWLockAcquire(BufMappingLock, LW_SHARED); |
|
|
|
LWLockAcquire(newPartitionLock, LW_SHARED); |
|
|
|
buf_id = BufTableLookup(&newTag); |
|
|
|
buf_id = BufTableLookup(&newTag, newHash); |
|
|
|
if (buf_id >= 0) |
|
|
|
if (buf_id >= 0) |
|
|
|
{ |
|
|
|
{ |
|
|
|
/*
|
|
|
|
/*
|
|
|
|
@ -317,7 +325,7 @@ BufferAlloc(Relation reln, |
|
|
|
valid = PinBuffer(buf); |
|
|
|
valid = PinBuffer(buf); |
|
|
|
|
|
|
|
|
|
|
|
/* Can release the mapping lock as soon as we've pinned it */ |
|
|
|
/* Can release the mapping lock as soon as we've pinned it */ |
|
|
|
LWLockRelease(BufMappingLock); |
|
|
|
LWLockRelease(newPartitionLock); |
|
|
|
|
|
|
|
|
|
|
|
*foundPtr = TRUE; |
|
|
|
*foundPtr = TRUE; |
|
|
|
|
|
|
|
|
|
|
|
@ -345,9 +353,9 @@ BufferAlloc(Relation reln, |
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* Didn't find it in the buffer pool. We'll have to initialize a new |
|
|
|
* Didn't find it in the buffer pool. We'll have to initialize a new |
|
|
|
* buffer. Remember to unlock BufMappingLock while doing the work. |
|
|
|
* buffer. Remember to unlock the mapping lock while doing the work. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
LWLockRelease(BufMappingLock); |
|
|
|
LWLockRelease(newPartitionLock); |
|
|
|
|
|
|
|
|
|
|
|
/* Loop here in case we have to try another victim buffer */ |
|
|
|
/* Loop here in case we have to try another victim buffer */ |
|
|
|
for (;;) |
|
|
|
for (;;) |
|
|
|
@ -412,10 +420,48 @@ BufferAlloc(Relation reln, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* Acquire exclusive mapping lock in preparation for changing the |
|
|
|
* To change the association of a valid buffer, we'll need to have |
|
|
|
* buffer's association. |
|
|
|
* exclusive lock on both the old and new mapping partitions. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
LWLockAcquire(BufMappingLock, LW_EXCLUSIVE); |
|
|
|
if (oldFlags & BM_TAG_VALID) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
* Need to compute the old tag's hashcode and partition lock ID. |
|
|
|
|
|
|
|
* XXX is it worth storing the hashcode in BufferDesc so we need |
|
|
|
|
|
|
|
* not recompute it here? Probably not. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
oldTag = buf->tag; |
|
|
|
|
|
|
|
oldHash = BufTableHashCode(&oldTag); |
|
|
|
|
|
|
|
oldPartitionLock = BufMappingPartitionLock(oldHash); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
* Must lock the lower-numbered partition first to avoid |
|
|
|
|
|
|
|
* deadlocks. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
if (oldPartitionLock < newPartitionLock) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
LWLockAcquire(oldPartitionLock, LW_EXCLUSIVE); |
|
|
|
|
|
|
|
LWLockAcquire(newPartitionLock, LW_EXCLUSIVE); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else if (oldPartitionLock > newPartitionLock) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
LWLockAcquire(newPartitionLock, LW_EXCLUSIVE); |
|
|
|
|
|
|
|
LWLockAcquire(oldPartitionLock, LW_EXCLUSIVE); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
/* only one partition, only one lock */ |
|
|
|
|
|
|
|
LWLockAcquire(newPartitionLock, LW_EXCLUSIVE); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
/* if it wasn't valid, we need only the new partition */ |
|
|
|
|
|
|
|
LWLockAcquire(newPartitionLock, LW_EXCLUSIVE); |
|
|
|
|
|
|
|
/* these just keep the compiler quiet about uninit variables */ |
|
|
|
|
|
|
|
oldHash = 0; |
|
|
|
|
|
|
|
oldPartitionLock = 0; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* Try to make a hashtable entry for the buffer under its new tag. |
|
|
|
* Try to make a hashtable entry for the buffer under its new tag. |
|
|
|
@ -424,7 +470,7 @@ BufferAlloc(Relation reln, |
|
|
|
* Note that we have not yet removed the hashtable entry for the old |
|
|
|
* Note that we have not yet removed the hashtable entry for the old |
|
|
|
* tag. |
|
|
|
* tag. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
buf_id = BufTableInsert(&newTag, buf->buf_id); |
|
|
|
buf_id = BufTableInsert(&newTag, newHash, buf->buf_id); |
|
|
|
|
|
|
|
|
|
|
|
if (buf_id >= 0) |
|
|
|
if (buf_id >= 0) |
|
|
|
{ |
|
|
|
{ |
|
|
|
@ -433,10 +479,15 @@ BufferAlloc(Relation reln, |
|
|
|
* do. We'll just handle this as if it were found in the buffer |
|
|
|
* do. We'll just handle this as if it were found in the buffer |
|
|
|
* pool in the first place. First, give up the buffer we were |
|
|
|
* pool in the first place. First, give up the buffer we were |
|
|
|
* planning to use. Don't allow it to be thrown in the free list |
|
|
|
* planning to use. Don't allow it to be thrown in the free list |
|
|
|
* (we don't want to hold both global locks at once). |
|
|
|
* (we don't want to hold freelist and mapping locks at once). |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
UnpinBuffer(buf, true, false); |
|
|
|
UnpinBuffer(buf, true, false); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Can give up that buffer's mapping partition lock now */ |
|
|
|
|
|
|
|
if ((oldFlags & BM_TAG_VALID) && |
|
|
|
|
|
|
|
oldPartitionLock != newPartitionLock) |
|
|
|
|
|
|
|
LWLockRelease(oldPartitionLock); |
|
|
|
|
|
|
|
|
|
|
|
/* remaining code should match code at top of routine */ |
|
|
|
/* remaining code should match code at top of routine */ |
|
|
|
|
|
|
|
|
|
|
|
buf = &BufferDescriptors[buf_id]; |
|
|
|
buf = &BufferDescriptors[buf_id]; |
|
|
|
@ -444,7 +495,7 @@ BufferAlloc(Relation reln, |
|
|
|
valid = PinBuffer(buf); |
|
|
|
valid = PinBuffer(buf); |
|
|
|
|
|
|
|
|
|
|
|
/* Can release the mapping lock as soon as we've pinned it */ |
|
|
|
/* Can release the mapping lock as soon as we've pinned it */ |
|
|
|
LWLockRelease(BufMappingLock); |
|
|
|
LWLockRelease(newPartitionLock); |
|
|
|
|
|
|
|
|
|
|
|
*foundPtr = TRUE; |
|
|
|
*foundPtr = TRUE; |
|
|
|
|
|
|
|
|
|
|
|
@ -481,12 +532,16 @@ BufferAlloc(Relation reln, |
|
|
|
* recycle this buffer; we must undo everything we've done and start |
|
|
|
* recycle this buffer; we must undo everything we've done and start |
|
|
|
* over with a new victim buffer. |
|
|
|
* over with a new victim buffer. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
if (buf->refcount == 1 && !(buf->flags & BM_DIRTY)) |
|
|
|
oldFlags = buf->flags; |
|
|
|
|
|
|
|
if (buf->refcount == 1 && !(oldFlags & BM_DIRTY)) |
|
|
|
break; |
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
UnlockBufHdr(buf); |
|
|
|
UnlockBufHdr(buf); |
|
|
|
BufTableDelete(&newTag); |
|
|
|
BufTableDelete(&newTag, newHash); |
|
|
|
LWLockRelease(BufMappingLock); |
|
|
|
if ((oldFlags & BM_TAG_VALID) && |
|
|
|
|
|
|
|
oldPartitionLock != newPartitionLock) |
|
|
|
|
|
|
|
LWLockRelease(oldPartitionLock); |
|
|
|
|
|
|
|
LWLockRelease(newPartitionLock); |
|
|
|
UnpinBuffer(buf, true, false /* evidently recently used */ ); |
|
|
|
UnpinBuffer(buf, true, false /* evidently recently used */ ); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -497,8 +552,6 @@ BufferAlloc(Relation reln, |
|
|
|
* paranoia. We also clear the usage_count since any recency of use of |
|
|
|
* paranoia. We also clear the usage_count since any recency of use of |
|
|
|
* the old content is no longer relevant. |
|
|
|
* the old content is no longer relevant. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
oldTag = buf->tag; |
|
|
|
|
|
|
|
oldFlags = buf->flags; |
|
|
|
|
|
|
|
buf->tag = newTag; |
|
|
|
buf->tag = newTag; |
|
|
|
buf->flags &= ~(BM_VALID | BM_DIRTY | BM_JUST_DIRTIED | BM_IO_ERROR); |
|
|
|
buf->flags &= ~(BM_VALID | BM_DIRTY | BM_JUST_DIRTIED | BM_IO_ERROR); |
|
|
|
buf->flags |= BM_TAG_VALID; |
|
|
|
buf->flags |= BM_TAG_VALID; |
|
|
|
@ -507,9 +560,13 @@ BufferAlloc(Relation reln, |
|
|
|
UnlockBufHdr(buf); |
|
|
|
UnlockBufHdr(buf); |
|
|
|
|
|
|
|
|
|
|
|
if (oldFlags & BM_TAG_VALID) |
|
|
|
if (oldFlags & BM_TAG_VALID) |
|
|
|
BufTableDelete(&oldTag); |
|
|
|
{ |
|
|
|
|
|
|
|
BufTableDelete(&oldTag, oldHash); |
|
|
|
|
|
|
|
if (oldPartitionLock != newPartitionLock) |
|
|
|
|
|
|
|
LWLockRelease(oldPartitionLock); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
LWLockRelease(BufMappingLock); |
|
|
|
LWLockRelease(newPartitionLock); |
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* Buffer contents are currently invalid. Try to get the io_in_progress |
|
|
|
* Buffer contents are currently invalid. Try to get the io_in_progress |
|
|
|
@ -545,6 +602,8 @@ static void |
|
|
|
InvalidateBuffer(volatile BufferDesc *buf) |
|
|
|
InvalidateBuffer(volatile BufferDesc *buf) |
|
|
|
{ |
|
|
|
{ |
|
|
|
BufferTag oldTag; |
|
|
|
BufferTag oldTag; |
|
|
|
|
|
|
|
uint32 oldHash; /* hash value for oldTag */ |
|
|
|
|
|
|
|
LWLockId oldPartitionLock; /* buffer partition lock for it */ |
|
|
|
BufFlags oldFlags; |
|
|
|
BufFlags oldFlags; |
|
|
|
|
|
|
|
|
|
|
|
/* Save the original buffer tag before dropping the spinlock */ |
|
|
|
/* Save the original buffer tag before dropping the spinlock */ |
|
|
|
@ -552,13 +611,21 @@ InvalidateBuffer(volatile BufferDesc *buf) |
|
|
|
|
|
|
|
|
|
|
|
UnlockBufHdr(buf); |
|
|
|
UnlockBufHdr(buf); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
* Need to compute the old tag's hashcode and partition lock ID. |
|
|
|
|
|
|
|
* XXX is it worth storing the hashcode in BufferDesc so we need |
|
|
|
|
|
|
|
* not recompute it here? Probably not. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
oldHash = BufTableHashCode(&oldTag); |
|
|
|
|
|
|
|
oldPartitionLock = BufMappingPartitionLock(oldHash); |
|
|
|
|
|
|
|
|
|
|
|
retry: |
|
|
|
retry: |
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* Acquire exclusive mapping lock in preparation for changing the buffer's |
|
|
|
* Acquire exclusive mapping lock in preparation for changing the buffer's |
|
|
|
* association. |
|
|
|
* association. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
LWLockAcquire(BufMappingLock, LW_EXCLUSIVE); |
|
|
|
LWLockAcquire(oldPartitionLock, LW_EXCLUSIVE); |
|
|
|
|
|
|
|
|
|
|
|
/* Re-lock the buffer header */ |
|
|
|
/* Re-lock the buffer header */ |
|
|
|
LockBufHdr(buf); |
|
|
|
LockBufHdr(buf); |
|
|
|
@ -567,7 +634,7 @@ retry: |
|
|
|
if (!BUFFERTAGS_EQUAL(buf->tag, oldTag)) |
|
|
|
if (!BUFFERTAGS_EQUAL(buf->tag, oldTag)) |
|
|
|
{ |
|
|
|
{ |
|
|
|
UnlockBufHdr(buf); |
|
|
|
UnlockBufHdr(buf); |
|
|
|
LWLockRelease(BufMappingLock); |
|
|
|
LWLockRelease(oldPartitionLock); |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -583,7 +650,7 @@ retry: |
|
|
|
if (buf->refcount != 0) |
|
|
|
if (buf->refcount != 0) |
|
|
|
{ |
|
|
|
{ |
|
|
|
UnlockBufHdr(buf); |
|
|
|
UnlockBufHdr(buf); |
|
|
|
LWLockRelease(BufMappingLock); |
|
|
|
LWLockRelease(oldPartitionLock); |
|
|
|
/* safety check: should definitely not be our *own* pin */ |
|
|
|
/* safety check: should definitely not be our *own* pin */ |
|
|
|
if (PrivateRefCount[buf->buf_id] != 0) |
|
|
|
if (PrivateRefCount[buf->buf_id] != 0) |
|
|
|
elog(ERROR, "buffer is pinned in InvalidateBuffer"); |
|
|
|
elog(ERROR, "buffer is pinned in InvalidateBuffer"); |
|
|
|
@ -606,7 +673,7 @@ retry: |
|
|
|
* Remove the buffer from the lookup hashtable, if it was in there. |
|
|
|
* Remove the buffer from the lookup hashtable, if it was in there. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
if (oldFlags & BM_TAG_VALID) |
|
|
|
if (oldFlags & BM_TAG_VALID) |
|
|
|
BufTableDelete(&oldTag); |
|
|
|
BufTableDelete(&oldTag, oldHash); |
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* Avoid accepting a cancel interrupt when we release the mapping lock; |
|
|
|
* Avoid accepting a cancel interrupt when we release the mapping lock; |
|
|
|
@ -616,7 +683,7 @@ retry: |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
HOLD_INTERRUPTS(); |
|
|
|
HOLD_INTERRUPTS(); |
|
|
|
|
|
|
|
|
|
|
|
LWLockRelease(BufMappingLock); |
|
|
|
LWLockRelease(oldPartitionLock); |
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* Insert the buffer at the head of the list of free buffers. |
|
|
|
* Insert the buffer at the head of the list of free buffers. |
|
|
|
|