PostgreSQL Source Code  git master
freespace.c File Reference
#include "postgres.h"
#include "access/htup_details.h"
#include "access/xlogutils.h"
#include "miscadmin.h"
#include "storage/freespace.h"
#include "storage/fsm_internals.h"
#include "storage/lmgr.h"
#include "storage/smgr.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 void 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)
 
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 (RelFileNode rnode, 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

◆ FSM_CAT_STEP

#define FSM_CAT_STEP   (BLCKSZ / FSM_CATEGORIES)

◆ FSM_CATEGORIES

#define FSM_CATEGORIES   256

Definition at line 63 of file freespace.c.

◆ FSM_ROOT_LEVEL

#define FSM_ROOT_LEVEL   (FSM_TREE_DEPTH - 1)

Definition at line 76 of file freespace.c.

Referenced by fsm_get_parent(), and fsm_search().

◆ FSM_TREE_DEPTH

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

Definition at line 74 of file freespace.c.

Referenced by fsm_logical_to_physical().

◆ MaxFSMRequestSize

#define MaxFSMRequestSize   MaxHeapTupleSize

Function Documentation

◆ FreeSpaceMapPrepareTruncateRel()

BlockNumber FreeSpaceMapPrepareTruncateRel ( Relation  rel,
BlockNumber  nblocks 
)

Definition at line 261 of file freespace.c.

References buf, BUFFER_LOCK_EXCLUSIVE, BufferGetPage, BufferIsValid, END_CRIT_SECTION, 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().

262 {
263  BlockNumber new_nfsmblocks;
264  FSMAddress first_removed_address;
265  uint16 first_removed_slot;
266  Buffer buf;
267 
268  /*
269  * If no FSM has been created yet for this relation, there's nothing to
270  * truncate.
271  */
273  return InvalidBlockNumber;
274 
275  /* Get the location in the FSM of the first removed heap block */
276  first_removed_address = fsm_get_location(nblocks, &first_removed_slot);
277 
278  /*
279  * Zero out the tail of the last remaining FSM page. If the slot
280  * representing the first removed heap block is at a page boundary, as the
281  * first slot on the FSM page that first_removed_address points to, we can
282  * just truncate that page altogether.
283  */
284  if (first_removed_slot > 0)
285  {
286  buf = fsm_readbuf(rel, first_removed_address, false);
287  if (!BufferIsValid(buf))
288  return InvalidBlockNumber; /* nothing to do; the FSM was already
289  * smaller */
291 
292  /* NO EREPORT(ERROR) from here till changes are logged */
294 
295  fsm_truncate_avail(BufferGetPage(buf), first_removed_slot);
296 
297  /*
298  * Truncation of a relation is WAL-logged at a higher-level, and we
299  * will be called at WAL replay. But if checksums are enabled, we need
300  * to still write a WAL record to protect against a torn page, if the
301  * page is flushed to disk before the truncation WAL record. We cannot
302  * use MarkBufferDirtyHint here, because that will not dirty the page
303  * during recovery.
304  */
305  MarkBufferDirty(buf);
307  log_newpage_buffer(buf, false);
308 
310 
311  UnlockReleaseBuffer(buf);
312 
313  new_nfsmblocks = fsm_logical_to_physical(first_removed_address) + 1;
314  }
315  else
316  {
317  new_nfsmblocks = fsm_logical_to_physical(first_removed_address);
318  if (smgrnblocks(RelationGetSmgr(rel), FSM_FORKNUM) <= new_nfsmblocks)
319  return InvalidBlockNumber; /* nothing to do; the FSM was already
320  * smaller */
321  }
322 
323  return new_nfsmblocks;
324 }
bool fsm_truncate_avail(Page page, int nslots)
Definition: fsmpage.c:313
XLogRecPtr log_newpage_buffer(Buffer buffer, bool page_std)
Definition: xloginsert.c:1142
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1556
#define END_CRIT_SECTION()
Definition: miscadmin.h:149
#define START_CRIT_SECTION()
Definition: miscadmin.h:147
uint32 BlockNumber
Definition: block.h:31
bool smgrexists(SMgrRelation reln, ForkNumber forknum)
Definition: smgr.c:247
#define BUFFER_LOCK_EXCLUSIVE
Definition: bufmgr.h:98
unsigned short uint16
Definition: c.h:440
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3795
static char * buf
Definition: pg_test_fsync.c:68
static FSMAddress fsm_get_location(BlockNumber heapblk, uint16 *slot)
Definition: freespace.c:466
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:4011
bool InRecovery
Definition: xlogutils.c:52
BlockNumber smgrnblocks(SMgrRelation reln, ForkNumber forknum)
Definition: smgr.c:548
static SMgrRelation RelationGetSmgr(Relation rel)
Definition: rel.h:544
#define InvalidBlockNumber
Definition: block.h:33
static BlockNumber fsm_logical_to_physical(FSMAddress addr)
Definition: freespace.c:430
#define BufferIsValid(bufnum)
Definition: bufmgr.h:123
#define RelationNeedsWAL(relation)
Definition: rel.h:601
static Buffer fsm_readbuf(Relation rel, FSMAddress addr, bool extend)
Definition: freespace.c:529
int Buffer
Definition: buf.h:23
#define XLogHintBitIsNeeded()
Definition: xlog.h:177

◆ FreeSpaceMapVacuum()

void FreeSpaceMapVacuum ( Relation  rel)

Definition at line 333 of file freespace.c.

References fsm_vacuum_page(), and InvalidBlockNumber.

Referenced by brin_vacuum_scan(), and IndexFreeSpaceMapVacuum().

334 {
335  bool dummy;
336 
337  /* Recursively scan the tree, starting at the root */
338  (void) fsm_vacuum_page(rel, FSM_ROOT_ADDRESS,
340  &dummy);
341 }
uint32 BlockNumber
Definition: block.h:31
static uint8 fsm_vacuum_page(Relation rel, FSMAddress addr, BlockNumber start, BlockNumber end, bool *eof)
Definition: freespace.c:798
static const FSMAddress FSM_ROOT_ADDRESS
Definition: freespace.c:90
#define InvalidBlockNumber
Definition: block.h:33

◆ FreeSpaceMapVacuumRange()

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

Definition at line 352 of file freespace.c.

References fsm_vacuum_page().

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

353 {
354  bool dummy;
355 
356  /* Recursively scan the tree, starting at the root */
357  if (end > start)
358  (void) fsm_vacuum_page(rel, FSM_ROOT_ADDRESS, start, end, &dummy);
359 }
static uint8 fsm_vacuum_page(Relation rel, FSMAddress addr, BlockNumber start, BlockNumber end, bool *eof)
Definition: freespace.c:798
static const FSMAddress FSM_ROOT_ADDRESS
Definition: freespace.c:90

◆ fsm_extend()

static void fsm_extend ( Relation  rel,
BlockNumber  fsm_nblocks 
)
static

Definition at line 606 of file freespace.c.

References PGAlignedBlock::data, ExclusiveLock, FSM_FORKNUM, InvalidBlockNumber, LockRelationForExtension(), PageInit(), PageSetChecksumInplace(), RelationGetSmgr(), SMgrRelationData::smgr_cached_nblocks, smgrcreate(), smgrexists(), smgrextend(), smgrnblocks(), and UnlockRelationForExtension().

Referenced by fsm_readbuf().

607 {
608  BlockNumber fsm_nblocks_now;
609  PGAlignedBlock pg;
610  SMgrRelation reln;
611 
612  PageInit((Page) pg.data, BLCKSZ, 0);
613 
614  /*
615  * We use the relation extension lock to lock out other backends trying to
616  * extend the FSM at the same time. It also locks out extension of the
617  * main fork, unnecessarily, but extending the FSM happens seldom enough
618  * that it doesn't seem worthwhile to have a separate lock tag type for
619  * it.
620  *
621  * Note that another backend might have extended or created the relation
622  * by the time we get the lock.
623  */
625 
626  /*
627  * Caution: re-using this smgr pointer could fail if the relcache entry
628  * gets closed. It's safe as long as we only do smgr-level operations
629  * between here and the last use of the pointer.
630  */
631  reln = RelationGetSmgr(rel);
632 
633  /*
634  * Create the FSM file first if it doesn't exist. If
635  * smgr_cached_nblocks[FSM_FORKNUM] is positive then it must exist, no
636  * need for an smgrexists call.
637  */
638  if ((reln->smgr_cached_nblocks[FSM_FORKNUM] == 0 ||
640  !smgrexists(reln, FSM_FORKNUM))
641  smgrcreate(reln, FSM_FORKNUM, false);
642 
643  /* Invalidate cache so that smgrnblocks() asks the kernel. */
645  fsm_nblocks_now = smgrnblocks(reln, FSM_FORKNUM);
646 
647  /* Extend as needed. */
648  while (fsm_nblocks_now < fsm_nblocks)
649  {
650  PageSetChecksumInplace((Page) pg.data, fsm_nblocks_now);
651 
652  smgrextend(reln, FSM_FORKNUM, fsm_nblocks_now,
653  pg.data, false);
654  fsm_nblocks_now++;
655  }
656 
658 }
void smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo)
Definition: smgr.c:333
#define ExclusiveLock
Definition: lockdefs.h:44
uint32 BlockNumber
Definition: block.h:31
bool smgrexists(SMgrRelation reln, ForkNumber forknum)
Definition: smgr.c:247
BlockNumber smgr_cached_nblocks[MAX_FORKNUM+1]
Definition: smgr.h:54
char data[BLCKSZ]
Definition: c.h:1141
void LockRelationForExtension(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:403
void UnlockRelationForExtension(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:453
BlockNumber smgrnblocks(SMgrRelation reln, ForkNumber forknum)
Definition: smgr.c:548
static SMgrRelation RelationGetSmgr(Relation rel)
Definition: rel.h:544
#define InvalidBlockNumber
Definition: block.h:33
void PageSetChecksumInplace(Page page, BlockNumber blkno)
Definition: bufpage.c:1532
void smgrextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, char *buffer, bool skipFsync)
Definition: smgr.c:462
Pointer Page
Definition: bufpage.h:78
void PageInit(Page page, Size pageSize, Size specialSize)
Definition: bufpage.c:42

◆ fsm_get_child()

static FSMAddress fsm_get_child ( FSMAddress  parent,
uint16  slot 
)
static

Definition at line 510 of file freespace.c.

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

Referenced by fsm_search(), and fsm_vacuum_page().

511 {
512  FSMAddress child;
513 
514  Assert(parent.level > FSM_BOTTOM_LEVEL);
515 
516  child.level = parent.level - 1;
517  child.logpageno = parent.logpageno * SlotsPerFSMPage + slot;
518 
519  return child;
520 }
#define SlotsPerFSMPage
Definition: fsm_internals.h:61
int logpageno
Definition: freespace.c:86
#define FSM_BOTTOM_LEVEL
Definition: freespace.c:77
int level
Definition: freespace.c:85
#define Assert(condition)
Definition: c.h:804

◆ fsm_get_heap_blk()

static BlockNumber fsm_get_heap_blk ( FSMAddress  addr,
uint16  slot 
)
static

Definition at line 481 of file freespace.c.

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

Referenced by fsm_search(), and RecordAndGetPageWithFreeSpace().

482 {
483  Assert(addr.level == FSM_BOTTOM_LEVEL);
484  return ((unsigned int) addr.logpageno) * SlotsPerFSMPage + slot;
485 }
#define SlotsPerFSMPage
Definition: fsm_internals.h:61
int logpageno
Definition: freespace.c:86
#define FSM_BOTTOM_LEVEL
Definition: freespace.c:77
int level
Definition: freespace.c:85
#define Assert(condition)
Definition: c.h:804

◆ fsm_get_location()

static FSMAddress fsm_get_location ( BlockNumber  heapblk,
uint16 slot 
)
static

Definition at line 466 of file freespace.c.

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

Referenced by FreeSpaceMapPrepareTruncateRel(), fsm_vacuum_page(), GetRecordedFreeSpace(), RecordAndGetPageWithFreeSpace(), RecordPageWithFreeSpace(), and XLogRecordPageWithFreeSpace().

467 {
468  FSMAddress addr;
469 
470  addr.level = FSM_BOTTOM_LEVEL;
471  addr.logpageno = heapblk / SlotsPerFSMPage;
472  *slot = heapblk % SlotsPerFSMPage;
473 
474  return addr;
475 }
#define SlotsPerFSMPage
Definition: fsm_internals.h:61
int logpageno
Definition: freespace.c:86
#define FSM_BOTTOM_LEVEL
Definition: freespace.c:77
int level
Definition: freespace.c:85

◆ fsm_get_parent()

static FSMAddress fsm_get_parent ( FSMAddress  child,
uint16 slot 
)
static

Definition at line 492 of file freespace.c.

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

Referenced by fsm_search(), and fsm_vacuum_page().

493 {
494  FSMAddress parent;
495 
496  Assert(child.level < FSM_ROOT_LEVEL);
497 
498  parent.level = child.level + 1;
499  parent.logpageno = child.logpageno / SlotsPerFSMPage;
500  *slot = child.logpageno % SlotsPerFSMPage;
501 
502  return parent;
503 }
#define SlotsPerFSMPage
Definition: fsm_internals.h:61
int logpageno
Definition: freespace.c:86
#define FSM_ROOT_LEVEL
Definition: freespace.c:76
int level
Definition: freespace.c:85
#define Assert(condition)
Definition: c.h:804

◆ fsm_logical_to_physical()

static BlockNumber fsm_logical_to_physical ( FSMAddress  addr)
static

Definition at line 430 of file freespace.c.

References FSM_TREE_DEPTH, FSMAddress::level, FSMAddress::logpageno, and SlotsPerFSMPage.

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

431 {
432  BlockNumber pages;
433  int leafno;
434  int l;
435 
436  /*
437  * Calculate the logical page number of the first leaf page below the
438  * given page.
439  */
440  leafno = addr.logpageno;
441  for (l = 0; l < addr.level; l++)
442  leafno *= SlotsPerFSMPage;
443 
444  /* Count upper level nodes required to address the leaf page */
445  pages = 0;
446  for (l = 0; l < FSM_TREE_DEPTH; l++)
447  {
448  pages += leafno + 1;
449  leafno /= SlotsPerFSMPage;
450  }
451 
452  /*
453  * If the page we were asked for wasn't at the bottom level, subtract the
454  * additional lower level pages we counted above.
455  */
456  pages -= addr.level;
457 
458  /* Turn the page count into 0-based block number */
459  return pages - 1;
460 }
#define SlotsPerFSMPage
Definition: fsm_internals.h:61
int logpageno
Definition: freespace.c:86
uint32 BlockNumber
Definition: block.h:31
int level
Definition: freespace.c:85
#define FSM_TREE_DEPTH
Definition: freespace.c:74

◆ fsm_readbuf()

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

Definition at line 529 of file freespace.c.

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

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

530 {
531  BlockNumber blkno = fsm_logical_to_physical(addr);
532  Buffer buf;
533  SMgrRelation reln;
534 
535  /*
536  * Caution: re-using this smgr pointer could fail if the relcache entry
537  * gets closed. It's safe as long as we only do smgr-level operations
538  * between here and the last use of the pointer.
539  */
540  reln = RelationGetSmgr(rel);
541 
542  /*
543  * If we haven't cached the size of the FSM yet, check it first. Also
544  * recheck if the requested block seems to be past end, since our cached
545  * value might be stale. (We send smgr inval messages on truncation, but
546  * not on extension.)
547  */
549  blkno >= reln->smgr_cached_nblocks[FSM_FORKNUM])
550  {
551  /* Invalidate the cache so smgrnblocks asks the kernel. */
553  if (smgrexists(reln, FSM_FORKNUM))
554  smgrnblocks(reln, FSM_FORKNUM);
555  else
556  reln->smgr_cached_nblocks[FSM_FORKNUM] = 0;
557  }
558 
559  /* Handle requests beyond EOF */
560  if (blkno >= reln->smgr_cached_nblocks[FSM_FORKNUM])
561  {
562  if (extend)
563  fsm_extend(rel, blkno + 1);
564  else
565  return InvalidBuffer;
566  }
567 
568  /*
569  * Use ZERO_ON_ERROR mode, and initialize the page if necessary. The FSM
570  * information is not accurate anyway, so it's better to clear corrupt
571  * pages than error out. Since the FSM changes are not WAL-logged, the
572  * so-called torn page problem on crash can lead to pages with corrupt
573  * headers, for example.
574  *
575  * The initialize-the-page part is trickier than it looks, because of the
576  * possibility of multiple backends doing this concurrently, and our
577  * desire to not uselessly take the buffer lock in the normal path where
578  * the page is OK. We must take the lock to initialize the page, so
579  * recheck page newness after we have the lock, in case someone else
580  * already did it. Also, because we initially check PageIsNew with no
581  * lock, it's possible to fall through and return the buffer while someone
582  * else is still initializing the page (i.e., we might see pd_upper as set
583  * but other page header fields are still zeroes). This is harmless for
584  * callers that will take a buffer lock themselves, but some callers
585  * inspect the page without any lock at all. The latter is OK only so
586  * long as it doesn't depend on the page header having correct contents.
587  * Current usage is safe because PageGetContents() does not require that.
588  */
589  buf = ReadBufferExtended(rel, FSM_FORKNUM, blkno, RBM_ZERO_ON_ERROR, NULL);
590  if (PageIsNew(BufferGetPage(buf)))
591  {
593  if (PageIsNew(BufferGetPage(buf)))
594  PageInit(BufferGetPage(buf), BLCKSZ, 0);
596  }
597  return buf;
598 }
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:96
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
Definition: bufmgr.c:741
#define InvalidBuffer
Definition: buf.h:25
uint32 BlockNumber
Definition: block.h:31
bool smgrexists(SMgrRelation reln, ForkNumber forknum)
Definition: smgr.c:247
#define BUFFER_LOCK_EXCLUSIVE
Definition: bufmgr.h:98
BlockNumber smgr_cached_nblocks[MAX_FORKNUM+1]
Definition: smgr.h:54
static char * buf
Definition: pg_test_fsync.c:68
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:4011
static void fsm_extend(Relation rel, BlockNumber fsm_nblocks)
Definition: freespace.c:606
BlockNumber smgrnblocks(SMgrRelation reln, ForkNumber forknum)
Definition: smgr.c:548
static SMgrRelation RelationGetSmgr(Relation rel)
Definition: rel.h:544
#define InvalidBlockNumber
Definition: block.h:33
static BlockNumber fsm_logical_to_physical(FSMAddress addr)
Definition: freespace.c:430
#define PageIsNew(page)
Definition: bufpage.h:229
int Buffer
Definition: buf.h:23
void PageInit(Page page, Size pageSize, Size specialSize)
Definition: bufpage.c:42

◆ fsm_search()

static BlockNumber fsm_search ( Relation  rel,
uint8  min_cat 
)
static

Definition at line 700 of file freespace.c.

References buf, BUFFER_LOCK_SHARE, BufferGetPage, BufferIsValid, FSM_BOTTOM_LEVEL, 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(), InvalidBlockNumber, FSMAddress::level, LockBuffer(), and UnlockReleaseBuffer().

Referenced by GetPageWithFreeSpace(), and RecordAndGetPageWithFreeSpace().

701 {
702  int restarts = 0;
704 
705  for (;;)
706  {
707  int slot;
708  Buffer buf;
709  uint8 max_avail = 0;
710 
711  /* Read the FSM page. */
712  buf = fsm_readbuf(rel, addr, false);
713 
714  /* Search within the page */
715  if (BufferIsValid(buf))
716  {
718  slot = fsm_search_avail(buf, min_cat,
719  (addr.level == FSM_BOTTOM_LEVEL),
720  false);
721  if (slot == -1)
722  max_avail = fsm_get_max_avail(BufferGetPage(buf));
723  UnlockReleaseBuffer(buf);
724  }
725  else
726  slot = -1;
727 
728  if (slot != -1)
729  {
730  /*
731  * Descend the tree, or return the found block if we're at the
732  * bottom.
733  */
734  if (addr.level == FSM_BOTTOM_LEVEL)
735  return fsm_get_heap_blk(addr, slot);
736 
737  addr = fsm_get_child(addr, slot);
738  }
739  else if (addr.level == FSM_ROOT_LEVEL)
740  {
741  /*
742  * At the root, failure means there's no page with enough free
743  * space in the FSM. Give up.
744  */
745  return InvalidBlockNumber;
746  }
747  else
748  {
749  uint16 parentslot;
750  FSMAddress parent;
751 
752  /*
753  * At lower level, failure can happen if the value in the upper-
754  * level node didn't reflect the value on the lower page. Update
755  * the upper node, to avoid falling into the same trap again, and
756  * start over.
757  *
758  * There's a race condition here, if another backend updates this
759  * page right after we release it, and gets the lock on the parent
760  * page before us. We'll then update the parent page with the now
761  * stale information we had. It's OK, because it should happen
762  * rarely, and will be fixed by the next vacuum.
763  */
764  parent = fsm_get_parent(addr, &parentslot);
765  fsm_set_and_search(rel, parent, parentslot, max_avail, 0);
766 
767  /*
768  * If the upper pages are badly out of date, we might need to loop
769  * quite a few times, updating them as we go. Any inconsistencies
770  * should eventually be corrected and the loop should end. Looping
771  * indefinitely is nevertheless scary, so provide an emergency
772  * valve.
773  */
774  if (restarts++ > 10000)
775  return InvalidBlockNumber;
776 
777  /* Start search all over from the root */
778  addr = FSM_ROOT_ADDRESS;
779  }
780  }
781 }
uint8 fsm_get_max_avail(Page page)
Definition: fsmpage.c:138
#define FSM_BOTTOM_LEVEL
Definition: freespace.c:77
static FSMAddress fsm_get_parent(FSMAddress child, uint16 *slot)
Definition: freespace.c:492
unsigned char uint8
Definition: c.h:439
static BlockNumber fsm_get_heap_blk(FSMAddress addr, uint16 slot)
Definition: freespace.c:481
static FSMAddress fsm_get_child(FSMAddress parent, uint16 slot)
Definition: freespace.c:510
static const FSMAddress FSM_ROOT_ADDRESS
Definition: freespace.c:90
unsigned short uint16
Definition: c.h:440
#define FSM_ROOT_LEVEL
Definition: freespace.c:76
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3795
int level
Definition: freespace.c:85
static char * buf
Definition: pg_test_fsync.c:68
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:4011
static int fsm_set_and_search(Relation rel, FSMAddress addr, uint16 slot, uint8 newValue, uint8 minValue)
Definition: freespace.c:668
#define InvalidBlockNumber
Definition: block.h:33
#define BufferIsValid(bufnum)
Definition: bufmgr.h:123
static Buffer fsm_readbuf(Relation rel, FSMAddress addr, bool extend)
Definition: freespace.c:529
#define BUFFER_LOCK_SHARE
Definition: bufmgr.h:97
int Buffer
Definition: buf.h:23
int fsm_search_avail(Buffer buf, uint8 minvalue, bool advancenext, bool exclusive_lock_held)
Definition: fsmpage.c:158

◆ fsm_set_and_search()

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

Definition at line 668 of file freespace.c.

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

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

670 {
671  Buffer buf;
672  Page page;
673  int newslot = -1;
674 
675  buf = fsm_readbuf(rel, addr, true);
677 
678  page = BufferGetPage(buf);
679 
680  if (fsm_set_avail(page, slot, newValue))
681  MarkBufferDirtyHint(buf, false);
682 
683  if (minValue != 0)
684  {
685  /* Search while we still hold the lock */
686  newslot = fsm_search_avail(buf, minValue,
687  addr.level == FSM_BOTTOM_LEVEL,
688  true);
689  }
690 
691  UnlockReleaseBuffer(buf);
692 
693  return newslot;
694 }
#define FSM_BOTTOM_LEVEL
Definition: freespace.c:77
void MarkBufferDirtyHint(Buffer buffer, bool buffer_std)
Definition: bufmgr.c:3842
bool fsm_set_avail(Page page, int slot, uint8 value)
Definition: fsmpage.c:63
#define BUFFER_LOCK_EXCLUSIVE
Definition: bufmgr.h:98
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3795
int level
Definition: freespace.c:85
static char * buf
Definition: pg_test_fsync.c:68
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:4011
static Buffer fsm_readbuf(Relation rel, FSMAddress addr, bool extend)
Definition: freespace.c:529
int Buffer
Definition: buf.h:23
Pointer Page
Definition: bufpage.h:78
int fsm_search_avail(Buffer buf, uint8 minvalue, bool advancenext, bool exclusive_lock_held)
Definition: fsmpage.c:158

◆ fsm_space_avail_to_cat()

static uint8 fsm_space_avail_to_cat ( Size  avail)
static

Definition at line 367 of file freespace.c.

References Assert, FSM_CAT_STEP, and MaxFSMRequestSize.

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

368 {
369  int cat;
370 
371  Assert(avail < BLCKSZ);
372 
373  if (avail >= MaxFSMRequestSize)
374  return 255;
375 
376  cat = avail / FSM_CAT_STEP;
377 
378  /*
379  * The highest category, 255, is reserved for MaxFSMRequestSize bytes or
380  * more.
381  */
382  if (cat > 254)
383  cat = 254;
384 
385  return (uint8) cat;
386 }
unsigned char uint8
Definition: c.h:439
#define FSM_CAT_STEP
Definition: freespace.c:64
#define Assert(condition)
Definition: c.h:804
#define MaxFSMRequestSize
Definition: freespace.c:65

◆ fsm_space_cat_to_avail()

static Size fsm_space_cat_to_avail ( uint8  cat)
static

Definition at line 393 of file freespace.c.

References FSM_CAT_STEP, and MaxFSMRequestSize.

Referenced by GetRecordedFreeSpace().

394 {
395  /* The highest category represents exactly MaxFSMRequestSize bytes. */
396  if (cat == 255)
397  return MaxFSMRequestSize;
398  else
399  return cat * FSM_CAT_STEP;
400 }
#define FSM_CAT_STEP
Definition: freespace.c:64
#define MaxFSMRequestSize
Definition: freespace.c:65

◆ fsm_space_needed_to_cat()

static uint8 fsm_space_needed_to_cat ( Size  needed)
static

Definition at line 407 of file freespace.c.

References elog, ERROR, FSM_CAT_STEP, and MaxFSMRequestSize.

Referenced by GetPageWithFreeSpace(), and RecordAndGetPageWithFreeSpace().

408 {
409  int cat;
410 
411  /* Can't ask for more space than the highest category represents */
412  if (needed > MaxFSMRequestSize)
413  elog(ERROR, "invalid FSM request size %zu", needed);
414 
415  if (needed == 0)
416  return 1;
417 
418  cat = (needed + FSM_CAT_STEP - 1) / FSM_CAT_STEP;
419 
420  if (cat > 255)
421  cat = 255;
422 
423  return (uint8) cat;
424 }
unsigned char uint8
Definition: c.h:439
#define FSM_CAT_STEP
Definition: freespace.c:64
#define ERROR
Definition: elog.h:46
#define elog(elevel,...)
Definition: elog.h:232
#define MaxFSMRequestSize
Definition: freespace.c:65

◆ fsm_vacuum_page()

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

Definition at line 798 of file freespace.c.

References Assert, buf, BUFFER_LOCK_EXCLUSIVE, BUFFER_LOCK_UNLOCK, BufferGetPage, BufferIsValid, CHECK_FOR_INTERRUPTS, FSM_BOTTOM_LEVEL, fsm_get_avail(), fsm_get_child(), fsm_get_location(), fsm_get_max_avail(), fsm_get_parent(), fsm_readbuf(), fsm_set_avail(), FSMAddress::level, LockBuffer(), FSMAddress::logpageno, MarkBufferDirtyHint(), PageGetContents, ReleaseBuffer(), and SlotsPerFSMPage.

Referenced by FreeSpaceMapVacuum(), and FreeSpaceMapVacuumRange().

801 {
802  Buffer buf;
803  Page page;
804  uint8 max_avail;
805 
806  /* Read the page if it exists, or return EOF */
807  buf = fsm_readbuf(rel, addr, false);
808  if (!BufferIsValid(buf))
809  {
810  *eof_p = true;
811  return 0;
812  }
813  else
814  *eof_p = false;
815 
816  page = BufferGetPage(buf);
817 
818  /*
819  * If we're above the bottom level, recurse into children, and fix the
820  * information stored about them at this level.
821  */
822  if (addr.level > FSM_BOTTOM_LEVEL)
823  {
824  FSMAddress fsm_start,
825  fsm_end;
826  uint16 fsm_start_slot,
827  fsm_end_slot;
828  int slot,
829  start_slot,
830  end_slot;
831  bool eof = false;
832 
833  /*
834  * Compute the range of slots we need to update on this page, given
835  * the requested range of heap blocks to consider. The first slot to
836  * update is the one covering the "start" block, and the last slot is
837  * the one covering "end - 1". (Some of this work will be duplicated
838  * in each recursive call, but it's cheap enough to not worry about.)
839  */
840  fsm_start = fsm_get_location(start, &fsm_start_slot);
841  fsm_end = fsm_get_location(end - 1, &fsm_end_slot);
842 
843  while (fsm_start.level < addr.level)
844  {
845  fsm_start = fsm_get_parent(fsm_start, &fsm_start_slot);
846  fsm_end = fsm_get_parent(fsm_end, &fsm_end_slot);
847  }
848  Assert(fsm_start.level == addr.level);
849 
850  if (fsm_start.logpageno == addr.logpageno)
851  start_slot = fsm_start_slot;
852  else if (fsm_start.logpageno > addr.logpageno)
853  start_slot = SlotsPerFSMPage; /* shouldn't get here... */
854  else
855  start_slot = 0;
856 
857  if (fsm_end.logpageno == addr.logpageno)
858  end_slot = fsm_end_slot;
859  else if (fsm_end.logpageno > addr.logpageno)
860  end_slot = SlotsPerFSMPage - 1;
861  else
862  end_slot = -1; /* shouldn't get here... */
863 
864  for (slot = start_slot; slot <= end_slot; slot++)
865  {
866  int child_avail;
867 
869 
870  /* After we hit end-of-file, just clear the rest of the slots */
871  if (!eof)
872  child_avail = fsm_vacuum_page(rel, fsm_get_child(addr, slot),
873  start, end,
874  &eof);
875  else
876  child_avail = 0;
877 
878  /* Update information about the child */
879  if (fsm_get_avail(page, slot) != child_avail)
880  {
882  fsm_set_avail(page, slot, child_avail);
883  MarkBufferDirtyHint(buf, false);
885  }
886  }
887  }
888 
889  /* Now get the maximum value on the page, to return to caller */
890  max_avail = fsm_get_max_avail(page);
891 
892  /*
893  * Reset the next slot pointer. This encourages the use of low-numbered
894  * pages, increasing the chances that a later vacuum can truncate the
895  * relation. We don't bother with a lock here, nor with marking the page
896  * dirty if it wasn't already, since this is just a hint.
897  */
898  ((FSMPage) PageGetContents(page))->fp_next_slot = 0;
899 
900  ReleaseBuffer(buf);
901 
902  return max_avail;
903 }
#define SlotsPerFSMPage
Definition: fsm_internals.h:61
int logpageno
Definition: freespace.c:86
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:96
uint8 fsm_get_max_avail(Page page)
Definition: fsmpage.c:138
#define FSM_BOTTOM_LEVEL
Definition: freespace.c:77
void MarkBufferDirtyHint(Buffer buffer, bool buffer_std)
Definition: bufmgr.c:3842
static FSMAddress fsm_get_parent(FSMAddress child, uint16 *slot)
Definition: freespace.c:492
bool fsm_set_avail(Page page, int slot, uint8 value)
Definition: fsmpage.c:63
unsigned char uint8
Definition: c.h:439
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3772
static uint8 fsm_vacuum_page(Relation rel, FSMAddress addr, BlockNumber start, BlockNumber end, bool *eof)
Definition: freespace.c:798
#define BUFFER_LOCK_EXCLUSIVE
Definition: bufmgr.h:98
static FSMAddress fsm_get_child(FSMAddress parent, uint16 slot)
Definition: freespace.c:510
unsigned short uint16
Definition: c.h:440
int level
Definition: freespace.c:85
FSMPageData * FSMPage
Definition: fsm_internals.h:45
static char * buf
Definition: pg_test_fsync.c:68
static FSMAddress fsm_get_location(BlockNumber heapblk, uint16 *slot)
Definition: freespace.c:466
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
#define PageGetContents(page)
Definition: bufpage.h:246
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:4011
#define Assert(condition)
Definition: c.h:804
#define BufferIsValid(bufnum)
Definition: bufmgr.h:123
uint8 fsm_get_avail(Page page, int slot)
Definition: fsmpage.c:122
static Buffer fsm_readbuf(Relation rel, FSMAddress addr, bool extend)
Definition: freespace.c:529
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:120
int Buffer
Definition: buf.h:23
Pointer Page
Definition: bufpage.h:78

◆ GetPageWithFreeSpace()

BlockNumber GetPageWithFreeSpace ( Relation  rel,
Size  spaceNeeded 
)

Definition at line 132 of file freespace.c.

References fsm_search(), and fsm_space_needed_to_cat().

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

133 {
134  uint8 min_cat = fsm_space_needed_to_cat(spaceNeeded);
135 
136  return fsm_search(rel, min_cat);
137 }
unsigned char uint8
Definition: c.h:439
static uint8 fsm_space_needed_to_cat(Size needed)
Definition: freespace.c:407
static BlockNumber fsm_search(Relation rel, uint8 min_cat)
Definition: freespace.c:700

◆ GetRecordedFreeSpace()

Size GetRecordedFreeSpace ( Relation  rel,
BlockNumber  heapBlk 
)

Definition at line 230 of file freespace.c.

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

Referenced by lazy_scan_heap(), pg_freespace(), and statapprox_heap().

231 {
232  FSMAddress addr;
233  uint16 slot;
234  Buffer buf;
235  uint8 cat;
236 
237  /* Get the location of the FSM byte representing the heap block */
238  addr = fsm_get_location(heapBlk, &slot);
239 
240  buf = fsm_readbuf(rel, addr, false);
241  if (!BufferIsValid(buf))
242  return 0;
243  cat = fsm_get_avail(BufferGetPage(buf), slot);
244  ReleaseBuffer(buf);
245 
246  return fsm_space_cat_to_avail(cat);
247 }
unsigned char uint8
Definition: c.h:439
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3772
unsigned short uint16
Definition: c.h:440
static Size fsm_space_cat_to_avail(uint8 cat)
Definition: freespace.c:393
static char * buf
Definition: pg_test_fsync.c:68
static FSMAddress fsm_get_location(BlockNumber heapblk, uint16 *slot)
Definition: freespace.c:466
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
#define BufferIsValid(bufnum)
Definition: bufmgr.h:123
uint8 fsm_get_avail(Page page, int slot)
Definition: fsmpage.c:122
static Buffer fsm_readbuf(Relation rel, FSMAddress addr, bool extend)
Definition: freespace.c:529
int Buffer
Definition: buf.h:23

◆ RecordAndGetPageWithFreeSpace()

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

Definition at line 149 of file freespace.c.

References 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().

151 {
152  int old_cat = fsm_space_avail_to_cat(oldSpaceAvail);
153  int search_cat = fsm_space_needed_to_cat(spaceNeeded);
154  FSMAddress addr;
155  uint16 slot;
156  int search_slot;
157 
158  /* Get the location of the FSM byte representing the heap block */
159  addr = fsm_get_location(oldPage, &slot);
160 
161  search_slot = fsm_set_and_search(rel, addr, slot, old_cat, search_cat);
162 
163  /*
164  * If fsm_set_and_search found a suitable new block, return that.
165  * Otherwise, search as usual.
166  */
167  if (search_slot != -1)
168  return fsm_get_heap_blk(addr, search_slot);
169  else
170  return fsm_search(rel, search_cat);
171 }
static BlockNumber fsm_get_heap_blk(FSMAddress addr, uint16 slot)
Definition: freespace.c:481
unsigned short uint16
Definition: c.h:440
static FSMAddress fsm_get_location(BlockNumber heapblk, uint16 *slot)
Definition: freespace.c:466
static uint8 fsm_space_avail_to_cat(Size avail)
Definition: freespace.c:367
static uint8 fsm_space_needed_to_cat(Size needed)
Definition: freespace.c:407
static int fsm_set_and_search(Relation rel, FSMAddress addr, uint16 slot, uint8 newValue, uint8 minValue)
Definition: freespace.c:668
static BlockNumber fsm_search(Relation rel, uint8 min_cat)
Definition: freespace.c:700

◆ RecordPageWithFreeSpace()

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

Definition at line 181 of file freespace.c.

References fsm_get_location(), fsm_set_and_search(), and fsm_space_avail_to_cat().

Referenced by brin_doinsert(), brin_doupdate(), brin_initialize_empty_new_buffer(), brin_page_cleanup(), lazy_scan_heap(), lazy_vacuum_heap_rel(), RecordFreeIndexPage(), RecordUsedIndexPage(), RelationAddExtraBlocks(), and terminate_brin_buildstate().

182 {
183  int new_cat = fsm_space_avail_to_cat(spaceAvail);
184  FSMAddress addr;
185  uint16 slot;
186 
187  /* Get the location of the FSM byte representing the heap block */
188  addr = fsm_get_location(heapBlk, &slot);
189 
190  fsm_set_and_search(rel, addr, slot, new_cat, 0);
191 }
unsigned short uint16
Definition: c.h:440
static FSMAddress fsm_get_location(BlockNumber heapblk, uint16 *slot)
Definition: freespace.c:466
static uint8 fsm_space_avail_to_cat(Size avail)
Definition: freespace.c:367
static int fsm_set_and_search(Relation rel, FSMAddress addr, uint16 slot, uint8 newValue, uint8 minValue)
Definition: freespace.c:668

◆ XLogRecordPageWithFreeSpace()

void XLogRecordPageWithFreeSpace ( RelFileNode  rnode,
BlockNumber  heapBlk,
Size  spaceAvail 
)

Definition at line 198 of file freespace.c.

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

Referenced by heap_xlog_insert(), heap_xlog_multi_insert(), heap_xlog_prune(), heap_xlog_update(), heap_xlog_vacuum(), and heap_xlog_visible().

200 {
201  int new_cat = fsm_space_avail_to_cat(spaceAvail);
202  FSMAddress addr;
203  uint16 slot;
204  BlockNumber blkno;
205  Buffer buf;
206  Page page;
207 
208  /* Get the location of the FSM byte representing the heap block */
209  addr = fsm_get_location(heapBlk, &slot);
210  blkno = fsm_logical_to_physical(addr);
211 
212  /* If the page doesn't exist already, extend */
215 
216  page = BufferGetPage(buf);
217  if (PageIsNew(page))
218  PageInit(page, BLCKSZ, 0);
219 
220  if (fsm_set_avail(page, slot, new_cat))
221  MarkBufferDirtyHint(buf, false);
222  UnlockReleaseBuffer(buf);
223 }
void MarkBufferDirtyHint(Buffer buffer, bool buffer_std)
Definition: bufmgr.c:3842
bool fsm_set_avail(Page page, int slot, uint8 value)
Definition: fsmpage.c:63
Buffer XLogReadBufferExtended(RelFileNode rnode, ForkNumber forknum, BlockNumber blkno, ReadBufferMode mode)
Definition: xlogutils.c:459
uint32 BlockNumber
Definition: block.h:31
#define BUFFER_LOCK_EXCLUSIVE
Definition: bufmgr.h:98
unsigned short uint16
Definition: c.h:440
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3795
static char * buf
Definition: pg_test_fsync.c:68
static FSMAddress fsm_get_location(BlockNumber heapblk, uint16 *slot)
Definition: freespace.c:466
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
static uint8 fsm_space_avail_to_cat(Size avail)
Definition: freespace.c:367
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:4011
static BlockNumber fsm_logical_to_physical(FSMAddress addr)
Definition: freespace.c:430
#define PageIsNew(page)
Definition: bufpage.h:229
int Buffer
Definition: buf.h:23
Pointer Page
Definition: bufpage.h:78
void PageInit(Page page, Size pageSize, Size specialSize)
Definition: bufpage.c:42

Variable Documentation

◆ FSM_ROOT_ADDRESS

const FSMAddress FSM_ROOT_ADDRESS = {FSM_ROOT_LEVEL, 0}
static

Definition at line 90 of file freespace.c.

Referenced by fsm_search().