PostgreSQL Source Code git master
Loading...
Searching...
No Matches
freespace.c File Reference
#include "postgres.h"
#include "access/htup_details.h"
#include "access/xloginsert.h"
#include "access/xlogutils.h"
#include "miscadmin.h"
#include "storage/freespace.h"
#include "storage/fsm_internals.h"
#include "storage/smgr.h"
#include "utils/rel.h"
Include dependency graph for freespace.c:

Go to the source code of this file.

Data Structures

struct  FSMAddress
 

Macros

#define FSM_CATEGORIES   256
 
#define FSM_CAT_STEP   (BLCKSZ / FSM_CATEGORIES)
 
#define MaxFSMRequestSize   MaxHeapTupleSize
 
#define FSM_TREE_DEPTH   ((SlotsPerFSMPage >= 1626) ? 3 : 4)
 
#define FSM_ROOT_LEVEL   (FSM_TREE_DEPTH - 1)
 
#define FSM_BOTTOM_LEVEL   0
 

Functions

static FSMAddress fsm_get_child (FSMAddress parent, uint16 slot)
 
static FSMAddress fsm_get_parent (FSMAddress child, uint16 *slot)
 
static FSMAddress fsm_get_location (BlockNumber heapblk, uint16 *slot)
 
static BlockNumber fsm_get_heap_blk (FSMAddress addr, uint16 slot)
 
static BlockNumber fsm_logical_to_physical (FSMAddress addr)
 
static Buffer fsm_readbuf (Relation rel, FSMAddress addr, bool extend)
 
static Buffer fsm_extend (Relation rel, BlockNumber fsm_nblocks)
 
static uint8 fsm_space_avail_to_cat (Size avail)
 
static uint8 fsm_space_needed_to_cat (Size needed)
 
static Size fsm_space_cat_to_avail (uint8 cat)
 
static int fsm_set_and_search (Relation rel, FSMAddress addr, uint16 slot, uint8 newValue, uint8 minValue)
 
static BlockNumber fsm_search (Relation rel, uint8 min_cat)
 
static uint8 fsm_vacuum_page (Relation rel, FSMAddress addr, BlockNumber start, BlockNumber end, bool *eof_p)
 
static bool fsm_does_block_exist (Relation rel, BlockNumber blknumber)
 
BlockNumber GetPageWithFreeSpace (Relation rel, Size spaceNeeded)
 
BlockNumber RecordAndGetPageWithFreeSpace (Relation rel, BlockNumber oldPage, Size oldSpaceAvail, Size spaceNeeded)
 
void RecordPageWithFreeSpace (Relation rel, BlockNumber heapBlk, Size spaceAvail)
 
void XLogRecordPageWithFreeSpace (RelFileLocator rlocator, BlockNumber heapBlk, Size spaceAvail)
 
Size GetRecordedFreeSpace (Relation rel, BlockNumber heapBlk)
 
BlockNumber FreeSpaceMapPrepareTruncateRel (Relation rel, BlockNumber nblocks)
 
void FreeSpaceMapVacuum (Relation rel)
 
void FreeSpaceMapVacuumRange (Relation rel, BlockNumber start, BlockNumber end)
 

Variables

static const FSMAddress FSM_ROOT_ADDRESS = {FSM_ROOT_LEVEL, 0}
 

Macro Definition Documentation

◆ FSM_BOTTOM_LEVEL

#define FSM_BOTTOM_LEVEL   0

Definition at line 78 of file freespace.c.

◆ FSM_CAT_STEP

#define FSM_CAT_STEP   (BLCKSZ / FSM_CATEGORIES)

Definition at line 65 of file freespace.c.

◆ FSM_CATEGORIES

#define FSM_CATEGORIES   256

Definition at line 64 of file freespace.c.

◆ FSM_ROOT_LEVEL

#define FSM_ROOT_LEVEL   (FSM_TREE_DEPTH - 1)

Definition at line 77 of file freespace.c.

◆ FSM_TREE_DEPTH

#define FSM_TREE_DEPTH   ((SlotsPerFSMPage >= 1626) ? 3 : 4)

Definition at line 75 of file freespace.c.

◆ MaxFSMRequestSize

#define MaxFSMRequestSize   MaxHeapTupleSize

Definition at line 66 of file freespace.c.

Function Documentation

◆ FreeSpaceMapPrepareTruncateRel()

BlockNumber FreeSpaceMapPrepareTruncateRel ( Relation  rel,
BlockNumber  nblocks 
)

Definition at line 285 of file freespace.c.

286{
290 Buffer buf;
291
292 /*
293 * If no FSM has been created yet for this relation, there's nothing to
294 * truncate.
295 */
297 return InvalidBlockNumber;
298
299 /* Get the location in the FSM of the first removed heap block */
301
302 /*
303 * Zero out the tail of the last remaining FSM page. If the slot
304 * representing the first removed heap block is at a page boundary, as the
305 * first slot on the FSM page that first_removed_address points to, we can
306 * just truncate that page altogether.
307 */
308 if (first_removed_slot > 0)
309 {
311 if (!BufferIsValid(buf))
312 return InvalidBlockNumber; /* nothing to do; the FSM was already
313 * smaller */
315
316 /* NO EREPORT(ERROR) from here till changes are logged */
318
320
321 /*
322 * This change is non-critical, because fsm_does_block_exist() would
323 * stop us from returning a truncated-away block. However, since this
324 * may remove up to SlotsPerFSMPage slots, it's nice to avoid the cost
325 * of that many fsm_does_block_exist() rejections. Use a full
326 * MarkBufferDirty(), not MarkBufferDirtyHint().
327 */
329
330 /*
331 * WAL-log like MarkBufferDirtyHint() might have done, just to avoid
332 * differing from the rest of the file in this respect. This is
333 * optional; see README mention of full page images. XXX consider
334 * XLogSaveBufferForHint() for even closer similarity.
335 *
336 * A higher-level operation calls us at WAL replay. If we crash
337 * before the XLOG_SMGR_TRUNCATE flushes to disk, main fork length has
338 * not changed, and our fork remains valid. If we crash after that
339 * flush, redo will return here.
340 */
342 log_newpage_buffer(buf, false);
343
345
347
349 }
350 else
351 {
354 return InvalidBlockNumber; /* nothing to do; the FSM was already
355 * smaller */
356 }
357
358 return new_nfsmblocks;
359}
uint32 BlockNumber
Definition block.h:31
#define InvalidBlockNumber
Definition block.h:33
int Buffer
Definition buf.h:23
void UnlockReleaseBuffer(Buffer buffer)
Definition bufmgr.c:5612
void MarkBufferDirty(Buffer buffer)
Definition bufmgr.c:3156
static Page BufferGetPage(Buffer buffer)
Definition bufmgr.h:468
@ BUFFER_LOCK_EXCLUSIVE
Definition bufmgr.h:222
static void LockBuffer(Buffer buffer, BufferLockMode mode)
Definition bufmgr.h:334
static bool BufferIsValid(Buffer bufnum)
Definition bufmgr.h:419
uint16_t uint16
Definition c.h:623
static BlockNumber fsm_logical_to_physical(FSMAddress addr)
Definition freespace.c:465
static FSMAddress fsm_get_location(BlockNumber heapblk, uint16 *slot)
Definition freespace.c:501
static Buffer fsm_readbuf(Relation rel, FSMAddress addr, bool extend)
Definition freespace.c:564
bool fsm_truncate_avail(Page page, int nslots)
Definition fsmpage.c:322
#define START_CRIT_SECTION()
Definition miscadmin.h:152
#define END_CRIT_SECTION()
Definition miscadmin.h:154
static char buf[DEFAULT_XLOG_SEG_SIZE]
static int fb(int x)
static SMgrRelation RelationGetSmgr(Relation rel)
Definition rel.h:578
#define RelationNeedsWAL(relation)
Definition rel.h:639
@ FSM_FORKNUM
Definition relpath.h:59
BlockNumber smgrnblocks(SMgrRelation reln, ForkNumber forknum)
Definition smgr.c:819
bool smgrexists(SMgrRelation reln, ForkNumber forknum)
Definition smgr.c:462
#define XLogHintBitIsNeeded()
Definition xlog.h:123
XLogRecPtr log_newpage_buffer(Buffer buffer, bool page_std)
bool InRecovery
Definition xlogutils.c:50

References buf, BUFFER_LOCK_EXCLUSIVE, BufferGetPage(), BufferIsValid(), END_CRIT_SECTION, fb(), FSM_FORKNUM, fsm_get_location(), fsm_logical_to_physical(), fsm_readbuf(), fsm_truncate_avail(), InRecovery, InvalidBlockNumber, LockBuffer(), log_newpage_buffer(), MarkBufferDirty(), RelationGetSmgr(), RelationNeedsWAL, smgrexists(), smgrnblocks(), START_CRIT_SECTION, UnlockReleaseBuffer(), and XLogHintBitIsNeeded.

Referenced by RelationTruncate(), and smgr_redo().

◆ FreeSpaceMapVacuum()

void FreeSpaceMapVacuum ( Relation  rel)

Definition at line 368 of file freespace.c.

369{
370 bool dummy;
371
372 /* Recursively scan the tree, starting at the root */
375 &dummy);
376}
static uint8 fsm_vacuum_page(Relation rel, FSMAddress addr, BlockNumber start, BlockNumber end, bool *eof_p)
Definition freespace.c:822
static const FSMAddress FSM_ROOT_ADDRESS
Definition freespace.c:91

References fb(), FSM_ROOT_ADDRESS, fsm_vacuum_page(), and InvalidBlockNumber.

Referenced by brin_vacuum_scan(), and IndexFreeSpaceMapVacuum().

◆ FreeSpaceMapVacuumRange()

void FreeSpaceMapVacuumRange ( Relation  rel,
BlockNumber  start,
BlockNumber  end 
)

Definition at line 387 of file freespace.c.

388{
389 bool dummy;
390
391 /* Recursively scan the tree, starting at the root */
392 if (end > start)
393 (void) fsm_vacuum_page(rel, FSM_ROOT_ADDRESS, start, end, &dummy);
394}
return str start

References fb(), FSM_ROOT_ADDRESS, fsm_vacuum_page(), and start.

Referenced by brin_doinsert(), brin_doupdate(), brin_getinsertbuffer(), lazy_scan_heap(), RelationAddBlocks(), RelationTruncate(), smgr_redo(), and terminate_brin_buildstate().

◆ fsm_does_block_exist()

static bool fsm_does_block_exist ( Relation  rel,
BlockNumber  blknumber 
)
static

Definition at line 941 of file freespace.c.

942{
943 SMgrRelation smgr = RelationGetSmgr(rel);
944
945 /*
946 * If below the cached nblocks, the block surely exists. Otherwise, we
947 * face a trade-off. We opt to compare to a fresh nblocks, incurring
948 * lseek() overhead. The alternative would be to assume the block does
949 * not exist, but that would cause FSM to set zero space available for
950 * blocks that main fork extension just recorded.
951 */
953 blknumber < smgr->smgr_cached_nblocks[MAIN_FORKNUM]) ||
955}
static bool BlockNumberIsValid(BlockNumber blockNumber)
Definition block.h:71
#define RelationGetNumberOfBlocks(reln)
Definition bufmgr.h:309
@ MAIN_FORKNUM
Definition relpath.h:58
BlockNumber smgr_cached_nblocks[MAX_FORKNUM+1]
Definition smgr.h:47

References BlockNumberIsValid(), fb(), MAIN_FORKNUM, RelationGetNumberOfBlocks, RelationGetSmgr(), and SMgrRelationData::smgr_cached_nblocks.

Referenced by fsm_search(), and RecordAndGetPageWithFreeSpace().

◆ fsm_extend()

static Buffer fsm_extend ( Relation  rel,
BlockNumber  fsm_nblocks 
)
static

Definition at line 639 of file freespace.c.

640{
646}
Buffer ExtendBufferedRelTo(BufferManagerRelation bmr, ForkNumber fork, BufferAccessStrategy strategy, uint32 flags, BlockNumber extend_to, ReadBufferMode mode)
Definition bufmgr.c:1031
@ EB_CLEAR_SIZE_CACHE
Definition bufmgr.h:90
@ EB_CREATE_FORK_IF_NEEDED
Definition bufmgr.h:84
@ RBM_ZERO_ON_ERROR
Definition bufmgr.h:51
#define BMR_REL(p_rel)
Definition bufmgr.h:114

References BMR_REL, EB_CLEAR_SIZE_CACHE, EB_CREATE_FORK_IF_NEEDED, ExtendBufferedRelTo(), fb(), FSM_FORKNUM, and RBM_ZERO_ON_ERROR.

Referenced by fsm_readbuf().

◆ fsm_get_child()

static FSMAddress fsm_get_child ( FSMAddress  parent,
uint16  slot 
)
static

Definition at line 545 of file freespace.c.

546{
547 FSMAddress child;
548
549 Assert(parent.level > FSM_BOTTOM_LEVEL);
550
551 child.level = parent.level - 1;
552 child.logpageno = parent.logpageno * SlotsPerFSMPage + slot;
553
554 return child;
555}
#define Assert(condition)
Definition c.h:943
#define FSM_BOTTOM_LEVEL
Definition freespace.c:78
#define SlotsPerFSMPage
int logpageno
Definition freespace.c:87

References Assert, FSM_BOTTOM_LEVEL, FSMAddress::level, FSMAddress::logpageno, and SlotsPerFSMPage.

Referenced by fsm_search(), and fsm_vacuum_page().

◆ fsm_get_heap_blk()

static BlockNumber fsm_get_heap_blk ( FSMAddress  addr,
uint16  slot 
)
static

Definition at line 516 of file freespace.c.

517{
519 return ((unsigned int) addr.logpageno) * SlotsPerFSMPage + slot;
520}

References Assert, FSM_BOTTOM_LEVEL, FSMAddress::level, FSMAddress::logpageno, and SlotsPerFSMPage.

Referenced by fsm_search(), and RecordAndGetPageWithFreeSpace().

◆ fsm_get_location()

◆ fsm_get_parent()

static FSMAddress fsm_get_parent ( FSMAddress  child,
uint16 slot 
)
static

Definition at line 527 of file freespace.c.

528{
529 FSMAddress parent;
530
531 Assert(child.level < FSM_ROOT_LEVEL);
532
533 parent.level = child.level + 1;
534 parent.logpageno = child.logpageno / SlotsPerFSMPage;
535 *slot = child.logpageno % SlotsPerFSMPage;
536
537 return parent;
538}
#define FSM_ROOT_LEVEL
Definition freespace.c:77

References Assert, FSM_ROOT_LEVEL, FSMAddress::level, FSMAddress::logpageno, and SlotsPerFSMPage.

Referenced by fsm_search(), and fsm_vacuum_page().

◆ fsm_logical_to_physical()

static BlockNumber fsm_logical_to_physical ( FSMAddress  addr)
static

Definition at line 465 of file freespace.c.

466{
467 BlockNumber pages;
468 int leafno;
469 int l;
470
471 /*
472 * Calculate the logical page number of the first leaf page below the
473 * given page.
474 */
475 leafno = addr.logpageno;
476 for (l = 0; l < addr.level; l++)
478
479 /* Count upper level nodes required to address the leaf page */
480 pages = 0;
481 for (l = 0; l < FSM_TREE_DEPTH; l++)
482 {
483 pages += leafno + 1;
485 }
486
487 /*
488 * If the page we were asked for wasn't at the bottom level, subtract the
489 * additional lower level pages we counted above.
490 */
491 pages -= addr.level;
492
493 /* Turn the page count into 0-based block number */
494 return pages - 1;
495}
#define FSM_TREE_DEPTH
Definition freespace.c:75

References fb(), FSM_TREE_DEPTH, FSMAddress::level, FSMAddress::logpageno, and SlotsPerFSMPage.

Referenced by FreeSpaceMapPrepareTruncateRel(), fsm_readbuf(), and XLogRecordPageWithFreeSpace().

◆ fsm_readbuf()

static Buffer fsm_readbuf ( Relation  rel,
FSMAddress  addr,
bool  extend 
)
static

Definition at line 564 of file freespace.c.

565{
567 Buffer buf;
569
570 /*
571 * If we haven't cached the size of the FSM yet, check it first. Also
572 * recheck if the requested block seems to be past end, since our cached
573 * value might be stale. (We send smgr inval messages on truncation, but
574 * not on extension.)
575 */
576 if (reln->smgr_cached_nblocks[FSM_FORKNUM] == InvalidBlockNumber ||
577 blkno >= reln->smgr_cached_nblocks[FSM_FORKNUM])
578 {
579 /* Invalidate the cache so smgrnblocks asks the kernel. */
580 reln->smgr_cached_nblocks[FSM_FORKNUM] = InvalidBlockNumber;
583 else
584 reln->smgr_cached_nblocks[FSM_FORKNUM] = 0;
585 }
586
587 /*
588 * For reading we use ZERO_ON_ERROR mode, and initialize the page if
589 * necessary. The FSM information is not accurate anyway, so it's better
590 * to clear corrupt pages than error out. Since the FSM changes are not
591 * WAL-logged, the so-called torn page problem on crash can lead to pages
592 * with corrupt headers, for example.
593 *
594 * We use the same path below to initialize pages when extending the
595 * relation, as a concurrent extension can end up with vm_extend()
596 * returning an already-initialized page.
597 */
598 if (blkno >= reln->smgr_cached_nblocks[FSM_FORKNUM])
599 {
600 if (extend)
601 buf = fsm_extend(rel, blkno + 1);
602 else
603 return InvalidBuffer;
604 }
605 else
607
608 /*
609 * Initializing the page when needed is trickier than it looks, because of
610 * the possibility of multiple backends doing this concurrently, and our
611 * desire to not uselessly take the buffer lock in the normal path where
612 * the page is OK. We must take the lock to initialize the page, so
613 * recheck page newness after we have the lock, in case someone else
614 * already did it. Also, because we initially check PageIsNew with no
615 * lock, it's possible to fall through and return the buffer while someone
616 * else is still initializing the page (i.e., we might see pd_upper as set
617 * but other page header fields are still zeroes). This is harmless for
618 * callers that will take a buffer lock themselves, but some callers
619 * inspect the page without any lock at all. The latter is OK only so
620 * long as it doesn't depend on the page header having correct contents.
621 * Current usage is safe because PageGetContents() does not require that.
622 */
624 {
629 }
630 return buf;
631}
#define InvalidBuffer
Definition buf.h:25
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
Definition bufmgr.c:926
@ BUFFER_LOCK_UNLOCK
Definition bufmgr.h:207
void PageInit(Page page, Size pageSize, Size specialSize)
Definition bufpage.c:42
static bool PageIsNew(const PageData *page)
Definition bufpage.h:258
static Buffer fsm_extend(Relation rel, BlockNumber fsm_nblocks)
Definition freespace.c:639

References buf, BUFFER_LOCK_EXCLUSIVE, BUFFER_LOCK_UNLOCK, BufferGetPage(), fb(), fsm_extend(), FSM_FORKNUM, fsm_logical_to_physical(), InvalidBlockNumber, InvalidBuffer, LockBuffer(), PageInit(), PageIsNew(), RBM_ZERO_ON_ERROR, ReadBufferExtended(), RelationGetSmgr(), smgrexists(), and smgrnblocks().

Referenced by FreeSpaceMapPrepareTruncateRel(), fsm_search(), fsm_set_and_search(), fsm_vacuum_page(), and GetRecordedFreeSpace().

◆ fsm_search()

static BlockNumber fsm_search ( Relation  rel,
uint8  min_cat 
)
static

Definition at line 688 of file freespace.c.

689{
690 int restarts = 0;
692
693 for (;;)
694 {
695 int slot;
696 Buffer buf;
697 uint8 max_avail = 0;
698
699 /* Read the FSM page. */
700 buf = fsm_readbuf(rel, addr, false);
701
702 /* Search within the page */
703 if (BufferIsValid(buf))
704 {
707 (addr.level == FSM_BOTTOM_LEVEL),
708 false);
709 if (slot == -1)
710 {
711 max_avail = fsm_get_max_avail(BufferGetPage(buf));
713 }
714 else
715 {
716 /* Keep the pin for possible update below */
718 }
719 }
720 else
721 slot = -1;
722
723 if (slot != -1)
724 {
725 /*
726 * Descend the tree, or return the found block if we're at the
727 * bottom.
728 */
729 if (addr.level == FSM_BOTTOM_LEVEL)
730 {
731 BlockNumber blkno = fsm_get_heap_blk(addr, slot);
732 Page page;
733
734 if (fsm_does_block_exist(rel, blkno))
735 {
737 return blkno;
738 }
739
740 /*
741 * Block is past the end of the relation. Update FSM, and
742 * restart from root. The usual "advancenext" behavior is
743 * pessimal for this rare scenario, since every later slot is
744 * unusable in the same way. We could zero all affected slots
745 * on the same FSM page, but don't bet on the benefits of that
746 * optimization justifying its compiled code bulk.
747 */
748 page = BufferGetPage(buf);
750 fsm_set_avail(page, slot, 0);
751 MarkBufferDirtyHint(buf, false);
753 if (restarts++ > 10000) /* same rationale as below */
754 return InvalidBlockNumber;
755 addr = FSM_ROOT_ADDRESS;
756 }
757 else
758 {
760 }
761 addr = fsm_get_child(addr, slot);
762 }
763 else if (addr.level == FSM_ROOT_LEVEL)
764 {
765 /*
766 * At the root, failure means there's no page with enough free
767 * space in the FSM. Give up.
768 */
769 return InvalidBlockNumber;
770 }
771 else
772 {
774 FSMAddress parent;
775
776 /*
777 * At lower level, failure can happen if the value in the upper-
778 * level node didn't reflect the value on the lower page. Update
779 * the upper node, to avoid falling into the same trap again, and
780 * start over.
781 *
782 * There's a race condition here, if another backend updates this
783 * page right after we release it, and gets the lock on the parent
784 * page before us. We'll then update the parent page with the now
785 * stale information we had. It's OK, because it should happen
786 * rarely, and will be fixed by the next vacuum.
787 */
788 parent = fsm_get_parent(addr, &parentslot);
789 fsm_set_and_search(rel, parent, parentslot, max_avail, 0);
790
791 /*
792 * If the upper pages are badly out of date, we might need to loop
793 * quite a few times, updating them as we go. Any inconsistencies
794 * should eventually be corrected and the loop should end. Looping
795 * indefinitely is nevertheless scary, so provide an emergency
796 * valve.
797 */
798 if (restarts++ > 10000)
799 return InvalidBlockNumber;
800
801 /* Start search all over from the root */
802 addr = FSM_ROOT_ADDRESS;
803 }
804 }
805}
void ReleaseBuffer(Buffer buffer)
Definition bufmgr.c:5595
void MarkBufferDirtyHint(Buffer buffer, bool buffer_std)
Definition bufmgr.c:5830
@ BUFFER_LOCK_SHARE
Definition bufmgr.h:212
PageData * Page
Definition bufpage.h:81
uint8_t uint8
Definition c.h:622
static FSMAddress fsm_get_child(FSMAddress parent, uint16 slot)
Definition freespace.c:545
static bool fsm_does_block_exist(Relation rel, BlockNumber blknumber)
Definition freespace.c:941
static FSMAddress fsm_get_parent(FSMAddress child, uint16 *slot)
Definition freespace.c:527
static int fsm_set_and_search(Relation rel, FSMAddress addr, uint16 slot, uint8 newValue, uint8 minValue)
Definition freespace.c:656
static BlockNumber fsm_get_heap_blk(FSMAddress addr, uint16 slot)
Definition freespace.c:516
uint8 fsm_get_max_avail(Page page)
Definition fsmpage.c:138
bool fsm_set_avail(Page page, int slot, uint8 value)
Definition fsmpage.c:63
int fsm_search_avail(Buffer buf, uint8 minvalue, bool advancenext, bool exclusive_lock_held)
Definition fsmpage.c:158

References buf, BUFFER_LOCK_EXCLUSIVE, BUFFER_LOCK_SHARE, BUFFER_LOCK_UNLOCK, BufferGetPage(), BufferIsValid(), fb(), FSM_BOTTOM_LEVEL, fsm_does_block_exist(), fsm_get_child(), fsm_get_heap_blk(), fsm_get_max_avail(), fsm_get_parent(), fsm_readbuf(), FSM_ROOT_ADDRESS, FSM_ROOT_LEVEL, fsm_search_avail(), fsm_set_and_search(), fsm_set_avail(), InvalidBlockNumber, FSMAddress::level, LockBuffer(), MarkBufferDirtyHint(), ReleaseBuffer(), and UnlockReleaseBuffer().

Referenced by GetPageWithFreeSpace(), and RecordAndGetPageWithFreeSpace().

◆ fsm_set_and_search()

static int fsm_set_and_search ( Relation  rel,
FSMAddress  addr,
uint16  slot,
uint8  newValue,
uint8  minValue 
)
static

Definition at line 656 of file freespace.c.

658{
659 Buffer buf;
660 Page page;
661 int newslot = -1;
662
663 buf = fsm_readbuf(rel, addr, true);
665
666 page = BufferGetPage(buf);
667
668 if (fsm_set_avail(page, slot, newValue))
669 MarkBufferDirtyHint(buf, false);
670
671 if (minValue != 0)
672 {
673 /* Search while we still hold the lock */
675 addr.level == FSM_BOTTOM_LEVEL,
676 true);
677 }
678
680
681 return newslot;
682}

References buf, BUFFER_LOCK_EXCLUSIVE, BufferGetPage(), fb(), FSM_BOTTOM_LEVEL, fsm_readbuf(), fsm_search_avail(), fsm_set_avail(), FSMAddress::level, LockBuffer(), MarkBufferDirtyHint(), and UnlockReleaseBuffer().

Referenced by fsm_search(), RecordAndGetPageWithFreeSpace(), and RecordPageWithFreeSpace().

◆ fsm_space_avail_to_cat()

static uint8 fsm_space_avail_to_cat ( Size  avail)
static

Definition at line 402 of file freespace.c.

403{
404 int cat;
405
406 Assert(avail < BLCKSZ);
407
408 if (avail >= MaxFSMRequestSize)
409 return 255;
410
411 cat = avail / FSM_CAT_STEP;
412
413 /*
414 * The highest category, 255, is reserved for MaxFSMRequestSize bytes or
415 * more.
416 */
417 if (cat > 254)
418 cat = 254;
419
420 return (uint8) cat;
421}
#define MaxFSMRequestSize
Definition freespace.c:66
#define FSM_CAT_STEP
Definition freespace.c:65

References Assert, fb(), FSM_CAT_STEP, and MaxFSMRequestSize.

Referenced by RecordAndGetPageWithFreeSpace(), RecordPageWithFreeSpace(), and XLogRecordPageWithFreeSpace().

◆ fsm_space_cat_to_avail()

static Size fsm_space_cat_to_avail ( uint8  cat)
static

Definition at line 428 of file freespace.c.

429{
430 /* The highest category represents exactly MaxFSMRequestSize bytes. */
431 if (cat == 255)
432 return MaxFSMRequestSize;
433 else
434 return cat * FSM_CAT_STEP;
435}

References FSM_CAT_STEP, and MaxFSMRequestSize.

Referenced by GetRecordedFreeSpace().

◆ fsm_space_needed_to_cat()

static uint8 fsm_space_needed_to_cat ( Size  needed)
static

Definition at line 442 of file freespace.c.

443{
444 int cat;
445
446 /* Can't ask for more space than the highest category represents */
448 elog(ERROR, "invalid FSM request size %zu", needed);
449
450 if (needed == 0)
451 return 1;
452
453 cat = (needed + FSM_CAT_STEP - 1) / FSM_CAT_STEP;
454
455 if (cat > 255)
456 cat = 255;
457
458 return (uint8) cat;
459}
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228

References elog, ERROR, fb(), FSM_CAT_STEP, and MaxFSMRequestSize.

Referenced by GetPageWithFreeSpace(), and RecordAndGetPageWithFreeSpace().

◆ fsm_vacuum_page()

static uint8 fsm_vacuum_page ( Relation  rel,
FSMAddress  addr,
BlockNumber  start,
BlockNumber  end,
bool eof_p 
)
static

Definition at line 822 of file freespace.c.

825{
826 Buffer buf;
827 Page page;
828 uint8 max_avail;
829
830 /* Read the page if it exists, or return EOF */
831 buf = fsm_readbuf(rel, addr, false);
832 if (!BufferIsValid(buf))
833 {
834 *eof_p = true;
835 return 0;
836 }
837 else
838 *eof_p = false;
839
840 page = BufferGetPage(buf);
841
842 /*
843 * If we're above the bottom level, recurse into children, and fix the
844 * information stored about them at this level.
845 */
846 if (addr.level > FSM_BOTTOM_LEVEL)
847 {
849 fsm_end;
852 int slot,
854 end_slot;
855 bool eof = false;
856
857 /*
858 * Compute the range of slots we need to update on this page, given
859 * the requested range of heap blocks to consider. The first slot to
860 * update is the one covering the "start" block, and the last slot is
861 * the one covering "end - 1". (Some of this work will be duplicated
862 * in each recursive call, but it's cheap enough to not worry about.)
863 */
866
867 while (fsm_start.level < addr.level)
868 {
871 }
872 Assert(fsm_start.level == addr.level);
873
874 if (fsm_start.logpageno == addr.logpageno)
876 else if (fsm_start.logpageno > addr.logpageno)
877 start_slot = SlotsPerFSMPage; /* shouldn't get here... */
878 else
879 start_slot = 0;
880
881 if (fsm_end.logpageno == addr.logpageno)
883 else if (fsm_end.logpageno > addr.logpageno)
885 else
886 end_slot = -1; /* shouldn't get here... */
887
888 for (slot = start_slot; slot <= end_slot; slot++)
889 {
890 int child_avail;
891
893
894 /* After we hit end-of-file, just clear the rest of the slots */
895 if (!eof)
896 child_avail = fsm_vacuum_page(rel, fsm_get_child(addr, slot),
897 start, end,
898 &eof);
899 else
900 child_avail = 0;
901
902 /* Update information about the child */
903 if (fsm_get_avail(page, slot) != child_avail)
904 {
906 fsm_set_avail(page, slot, child_avail);
907 MarkBufferDirtyHint(buf, false);
909 }
910 }
911 }
912
913 /* Now get the maximum value on the page, to return to caller */
914 max_avail = fsm_get_max_avail(page);
915
916 /*
917 * Try to reset the next slot pointer. This encourages the use of
918 * low-numbered pages, increasing the chances that a later vacuum can
919 * truncate the relation. We don't bother with marking the page dirty if
920 * it wasn't already, since this is just a hint.
921 */
924 {
925 ((FSMPage) PageGetContents(page))->fp_next_slot = 0;
926 BufferFinishSetHintBits(buf, false, false);
927 }
928
930
931 return max_avail;
932}
void BufferFinishSetHintBits(Buffer buffer, bool mark_dirty, bool buffer_std)
Definition bufmgr.c:7079
bool BufferBeginSetHintBits(Buffer buffer)
Definition bufmgr.c:7051
static char * PageGetContents(Page page)
Definition bufpage.h:282
FSMPageData * FSMPage
uint8 fsm_get_avail(Page page, int slot)
Definition fsmpage.c:122
#define CHECK_FOR_INTERRUPTS()
Definition miscadmin.h:125

References Assert, buf, BUFFER_LOCK_EXCLUSIVE, BUFFER_LOCK_SHARE, BUFFER_LOCK_UNLOCK, BufferBeginSetHintBits(), BufferFinishSetHintBits(), BufferGetPage(), BufferIsValid(), CHECK_FOR_INTERRUPTS, fb(), FSM_BOTTOM_LEVEL, fsm_get_avail(), fsm_get_child(), fsm_get_location(), fsm_get_max_avail(), fsm_get_parent(), fsm_readbuf(), fsm_set_avail(), fsm_vacuum_page(), FSMAddress::level, LockBuffer(), FSMAddress::logpageno, MarkBufferDirtyHint(), PageGetContents(), SlotsPerFSMPage, start, and UnlockReleaseBuffer().

Referenced by FreeSpaceMapVacuum(), FreeSpaceMapVacuumRange(), and fsm_vacuum_page().

◆ GetPageWithFreeSpace()

BlockNumber GetPageWithFreeSpace ( Relation  rel,
Size  spaceNeeded 
)

Definition at line 137 of file freespace.c.

138{
140
141 return fsm_search(rel, min_cat);
142}
static uint8 fsm_space_needed_to_cat(Size needed)
Definition freespace.c:442
static BlockNumber fsm_search(Relation rel, uint8 min_cat)
Definition freespace.c:688

References fb(), fsm_search(), and fsm_space_needed_to_cat().

Referenced by brin_getinsertbuffer(), GetFreeIndexPage(), and RelationGetBufferForTuple().

◆ GetRecordedFreeSpace()

Size GetRecordedFreeSpace ( Relation  rel,
BlockNumber  heapBlk 
)

Definition at line 254 of file freespace.c.

255{
256 FSMAddress addr;
257 uint16 slot;
258 Buffer buf;
259 uint8 cat;
260
261 /* Get the location of the FSM byte representing the heap block */
262 addr = fsm_get_location(heapBlk, &slot);
263
264 buf = fsm_readbuf(rel, addr, false);
265 if (!BufferIsValid(buf))
266 return 0;
267 cat = fsm_get_avail(BufferGetPage(buf), slot);
269
270 return fsm_space_cat_to_avail(cat);
271}
static Size fsm_space_cat_to_avail(uint8 cat)
Definition freespace.c:428

References buf, BufferGetPage(), BufferIsValid(), fsm_get_avail(), fsm_get_location(), fsm_readbuf(), fsm_space_cat_to_avail(), and ReleaseBuffer().

Referenced by lazy_scan_new_or_empty(), pg_freespace(), and statapprox_heap_read_stream_next().

◆ RecordAndGetPageWithFreeSpace()

BlockNumber RecordAndGetPageWithFreeSpace ( Relation  rel,
BlockNumber  oldPage,
Size  oldSpaceAvail,
Size  spaceNeeded 
)

Definition at line 154 of file freespace.c.

156{
159 FSMAddress addr;
160 uint16 slot;
161 int search_slot;
162
163 /* Get the location of the FSM byte representing the heap block */
164 addr = fsm_get_location(oldPage, &slot);
165
167
168 /*
169 * If fsm_set_and_search found a suitable new block, return that.
170 * Otherwise, search as usual.
171 */
172 if (search_slot != -1)
173 {
175
176 /*
177 * Check that the blknum is actually in the relation. Don't try to
178 * update the FSM in that case, just fall back to the other case
179 */
181 return blknum;
182 }
183 return fsm_search(rel, search_cat);
184}
static uint8 fsm_space_avail_to_cat(Size avail)
Definition freespace.c:402

References fb(), fsm_does_block_exist(), fsm_get_heap_blk(), fsm_get_location(), fsm_search(), fsm_set_and_search(), fsm_space_avail_to_cat(), and fsm_space_needed_to_cat().

Referenced by brin_getinsertbuffer(), and RelationGetBufferForTuple().

◆ RecordPageWithFreeSpace()

void RecordPageWithFreeSpace ( Relation  rel,
BlockNumber  heapBlk,
Size  spaceAvail 
)

◆ XLogRecordPageWithFreeSpace()

void XLogRecordPageWithFreeSpace ( RelFileLocator  rlocator,
BlockNumber  heapBlk,
Size  spaceAvail 
)

Definition at line 211 of file freespace.c.

213{
215 FSMAddress addr;
216 uint16 slot;
217 BlockNumber blkno;
218 Buffer buf;
219 Page page;
220
221 /* Get the location of the FSM byte representing the heap block */
222 addr = fsm_get_location(heapBlk, &slot);
223 blkno = fsm_logical_to_physical(addr);
224
225 /* If the page doesn't exist already, extend */
226 buf = XLogReadBufferExtended(rlocator, FSM_FORKNUM, blkno,
229
230 page = BufferGetPage(buf);
231 if (PageIsNew(page))
232 PageInit(page, BLCKSZ, 0);
233
234 /*
235 * Changes to FSM are usually marked as changed using MarkBufferDirtyHint;
236 * however, during recovery, it does nothing if checksums are enabled. It
237 * is assumed that the page should not be dirtied during recovery while
238 * modifying hints to prevent torn pages, since no new WAL data can be
239 * generated at this point to store FPI. This is not relevant to the FSM
240 * case, as its blocks are zeroed when a checksum mismatch occurs. So, we
241 * need to use regular MarkBufferDirty here to mark the FSM block as
242 * modified during recovery, otherwise changes to the FSM may be lost.
243 */
244 if (fsm_set_avail(page, slot, new_cat))
247}
Buffer XLogReadBufferExtended(RelFileLocator rlocator, ForkNumber forknum, BlockNumber blkno, ReadBufferMode mode, Buffer recent_buffer)
Definition xlogutils.c:460

References buf, BUFFER_LOCK_EXCLUSIVE, BufferGetPage(), fb(), FSM_FORKNUM, fsm_get_location(), fsm_logical_to_physical(), fsm_set_avail(), fsm_space_avail_to_cat(), InvalidBuffer, LockBuffer(), MarkBufferDirty(), PageInit(), PageIsNew(), RBM_ZERO_ON_ERROR, UnlockReleaseBuffer(), and XLogReadBufferExtended().

Referenced by heap_xlog_insert(), heap_xlog_multi_insert(), heap_xlog_prune_freeze(), and heap_xlog_update().

Variable Documentation

◆ FSM_ROOT_ADDRESS

const FSMAddress FSM_ROOT_ADDRESS = {FSM_ROOT_LEVEL, 0}
static

Definition at line 91 of file freespace.c.

Referenced by FreeSpaceMapVacuum(), FreeSpaceMapVacuumRange(), and fsm_search().