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 262 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(), RelationData::rd_smgr, RelationNeedsWAL, RelationOpenSmgr, smgrexists(), smgrnblocks(), START_CRIT_SECTION, UnlockReleaseBuffer(), and XLogHintBitIsNeeded.

Referenced by RelationTruncate(), and smgr_redo().

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

◆ FreeSpaceMapVacuum()

void FreeSpaceMapVacuum ( Relation  rel)

Definition at line 336 of file freespace.c.

References fsm_vacuum_page(), and InvalidBlockNumber.

Referenced by brin_vacuum_scan(), and IndexFreeSpaceMapVacuum().

337 {
338  bool dummy;
339 
340  /* Recursively scan the tree, starting at the root */
341  (void) fsm_vacuum_page(rel, FSM_ROOT_ADDRESS,
343  &dummy);
344 }
uint32 BlockNumber
Definition: block.h:31
static uint8 fsm_vacuum_page(Relation rel, FSMAddress addr, BlockNumber start, BlockNumber end, bool *eof)
Definition: freespace.c:789
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 355 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().

356 {
357  bool dummy;
358 
359  /* Recursively scan the tree, starting at the root */
360  if (end > start)
361  (void) fsm_vacuum_page(rel, FSM_ROOT_ADDRESS, start, end, &dummy);
362 }
static uint8 fsm_vacuum_page(Relation rel, FSMAddress addr, BlockNumber start, BlockNumber end, bool *eof)
Definition: freespace.c:789
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 603 of file freespace.c.

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

Referenced by fsm_readbuf().

604 {
605  BlockNumber fsm_nblocks_now;
606  PGAlignedBlock pg;
607 
608  PageInit((Page) pg.data, BLCKSZ, 0);
609 
610  /*
611  * We use the relation extension lock to lock out other backends trying to
612  * extend the FSM at the same time. It also locks out extension of the
613  * main fork, unnecessarily, but extending the FSM happens seldom enough
614  * that it doesn't seem worthwhile to have a separate lock tag type for
615  * it.
616  *
617  * Note that another backend might have extended or created the relation
618  * by the time we get the lock.
619  */
621 
622  /* Might have to re-open if a cache flush happened */
623  RelationOpenSmgr(rel);
624 
625  /*
626  * Create the FSM file first if it doesn't exist. If
627  * smgr_cached_nblocks[FSM_FORKNUM] is positive then it must exist, no
628  * need for an smgrexists call.
629  */
630  if ((rel->rd_smgr->smgr_cached_nblocks[FSM_FORKNUM] == 0 ||
633  smgrcreate(rel->rd_smgr, FSM_FORKNUM, false);
634 
635  /* Invalidate cache so that smgrnblocks() asks the kernel. */
637  fsm_nblocks_now = smgrnblocks(rel->rd_smgr, FSM_FORKNUM);
638 
639  while (fsm_nblocks_now < fsm_nblocks)
640  {
641  PageSetChecksumInplace((Page) pg.data, fsm_nblocks_now);
642 
643  smgrextend(rel->rd_smgr, FSM_FORKNUM, fsm_nblocks_now,
644  pg.data, false);
645  fsm_nblocks_now++;
646  }
647 
649 }
void smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo)
Definition: smgr.c:333
#define ExclusiveLock
Definition: lockdefs.h:44
struct SMgrRelationData * rd_smgr
Definition: rel.h:57
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
#define RelationOpenSmgr(relation)
Definition: rel.h:526
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
#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 513 of file freespace.c.

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

Referenced by fsm_search(), and fsm_vacuum_page().

514 {
515  FSMAddress child;
516 
517  Assert(parent.level > FSM_BOTTOM_LEVEL);
518 
519  child.level = parent.level - 1;
520  child.logpageno = parent.logpageno * SlotsPerFSMPage + slot;
521 
522  return child;
523 }
#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 484 of file freespace.c.

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

Referenced by fsm_search(), and RecordAndGetPageWithFreeSpace().

485 {
486  Assert(addr.level == FSM_BOTTOM_LEVEL);
487  return ((unsigned int) addr.logpageno) * SlotsPerFSMPage + slot;
488 }
#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 469 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().

470 {
471  FSMAddress addr;
472 
473  addr.level = FSM_BOTTOM_LEVEL;
474  addr.logpageno = heapblk / SlotsPerFSMPage;
475  *slot = heapblk % SlotsPerFSMPage;
476 
477  return addr;
478 }
#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 495 of file freespace.c.

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

Referenced by fsm_search(), and fsm_vacuum_page().

496 {
497  FSMAddress parent;
498 
499  Assert(child.level < FSM_ROOT_LEVEL);
500 
501  parent.level = child.level + 1;
502  parent.logpageno = child.logpageno / SlotsPerFSMPage;
503  *slot = child.logpageno % SlotsPerFSMPage;
504 
505  return parent;
506 }
#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 433 of file freespace.c.

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

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

434 {
435  BlockNumber pages;
436  int leafno;
437  int l;
438 
439  /*
440  * Calculate the logical page number of the first leaf page below the
441  * given page.
442  */
443  leafno = addr.logpageno;
444  for (l = 0; l < addr.level; l++)
445  leafno *= SlotsPerFSMPage;
446 
447  /* Count upper level nodes required to address the leaf page */
448  pages = 0;
449  for (l = 0; l < FSM_TREE_DEPTH; l++)
450  {
451  pages += leafno + 1;
452  leafno /= SlotsPerFSMPage;
453  }
454 
455  /*
456  * If the page we were asked for wasn't at the bottom level, subtract the
457  * additional lower level pages we counted above.
458  */
459  pages -= addr.level;
460 
461  /* Turn the page count into 0-based block number */
462  return pages - 1;
463 }
#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 532 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, RelationData::rd_smgr, ReadBufferExtended(), RelationOpenSmgr, SMgrRelationData::smgr_cached_nblocks, smgrexists(), and smgrnblocks().

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

533 {
534  BlockNumber blkno = fsm_logical_to_physical(addr);
535  Buffer buf;
536 
537  RelationOpenSmgr(rel);
538 
539  /*
540  * If we haven't cached the size of the FSM yet, check it first. Also
541  * recheck if the requested block seems to be past end, since our cached
542  * value might be stale. (We send smgr inval messages on truncation, but
543  * not on extension.)
544  */
546  blkno >= rel->rd_smgr->smgr_cached_nblocks[FSM_FORKNUM])
547  {
548  /* Invalidate the cache so smgrnblocks asks the kernel. */
550  if (smgrexists(rel->rd_smgr, FSM_FORKNUM))
552  else
554  }
555 
556  /* Handle requests beyond EOF */
557  if (blkno >= rel->rd_smgr->smgr_cached_nblocks[FSM_FORKNUM])
558  {
559  if (extend)
560  fsm_extend(rel, blkno + 1);
561  else
562  return InvalidBuffer;
563  }
564 
565  /*
566  * Use ZERO_ON_ERROR mode, and initialize the page if necessary. The FSM
567  * information is not accurate anyway, so it's better to clear corrupt
568  * pages than error out. Since the FSM changes are not WAL-logged, the
569  * so-called torn page problem on crash can lead to pages with corrupt
570  * headers, for example.
571  *
572  * The initialize-the-page part is trickier than it looks, because of the
573  * possibility of multiple backends doing this concurrently, and our
574  * desire to not uselessly take the buffer lock in the normal path where
575  * the page is OK. We must take the lock to initialize the page, so
576  * recheck page newness after we have the lock, in case someone else
577  * already did it. Also, because we initially check PageIsNew with no
578  * lock, it's possible to fall through and return the buffer while someone
579  * else is still initializing the page (i.e., we might see pd_upper as set
580  * but other page header fields are still zeroes). This is harmless for
581  * callers that will take a buffer lock themselves, but some callers
582  * inspect the page without any lock at all. The latter is OK only so
583  * long as it doesn't depend on the page header having correct contents.
584  * Current usage is safe because PageGetContents() does not require that.
585  */
586  buf = ReadBufferExtended(rel, FSM_FORKNUM, blkno, RBM_ZERO_ON_ERROR, NULL);
587  if (PageIsNew(BufferGetPage(buf)))
588  {
590  if (PageIsNew(BufferGetPage(buf)))
591  PageInit(BufferGetPage(buf), BLCKSZ, 0);
593  }
594  return buf;
595 }
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:96
struct SMgrRelationData * rd_smgr
Definition: rel.h:57
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
Definition: bufmgr.c:744
#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
#define RelationOpenSmgr(relation)
Definition: rel.h:526
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:4023
static void fsm_extend(Relation rel, BlockNumber fsm_nblocks)
Definition: freespace.c:603
BlockNumber smgrnblocks(SMgrRelation reln, ForkNumber forknum)
Definition: smgr.c:548
#define InvalidBlockNumber
Definition: block.h:33
static BlockNumber fsm_logical_to_physical(FSMAddress addr)
Definition: freespace.c:433
#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 691 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().

692 {
693  int restarts = 0;
695 
696  for (;;)
697  {
698  int slot;
699  Buffer buf;
700  uint8 max_avail = 0;
701 
702  /* Read the FSM page. */
703  buf = fsm_readbuf(rel, addr, false);
704 
705  /* Search within the page */
706  if (BufferIsValid(buf))
707  {
709  slot = fsm_search_avail(buf, min_cat,
710  (addr.level == FSM_BOTTOM_LEVEL),
711  false);
712  if (slot == -1)
713  max_avail = fsm_get_max_avail(BufferGetPage(buf));
714  UnlockReleaseBuffer(buf);
715  }
716  else
717  slot = -1;
718 
719  if (slot != -1)
720  {
721  /*
722  * Descend the tree, or return the found block if we're at the
723  * bottom.
724  */
725  if (addr.level == FSM_BOTTOM_LEVEL)
726  return fsm_get_heap_blk(addr, slot);
727 
728  addr = fsm_get_child(addr, slot);
729  }
730  else if (addr.level == FSM_ROOT_LEVEL)
731  {
732  /*
733  * At the root, failure means there's no page with enough free
734  * space in the FSM. Give up.
735  */
736  return InvalidBlockNumber;
737  }
738  else
739  {
740  uint16 parentslot;
741  FSMAddress parent;
742 
743  /*
744  * At lower level, failure can happen if the value in the upper-
745  * level node didn't reflect the value on the lower page. Update
746  * the upper node, to avoid falling into the same trap again, and
747  * start over.
748  *
749  * There's a race condition here, if another backend updates this
750  * page right after we release it, and gets the lock on the parent
751  * page before us. We'll then update the parent page with the now
752  * stale information we had. It's OK, because it should happen
753  * rarely, and will be fixed by the next vacuum.
754  */
755  parent = fsm_get_parent(addr, &parentslot);
756  fsm_set_and_search(rel, parent, parentslot, max_avail, 0);
757 
758  /*
759  * If the upper pages are badly out of date, we might need to loop
760  * quite a few times, updating them as we go. Any inconsistencies
761  * should eventually be corrected and the loop should end. Looping
762  * indefinitely is nevertheless scary, so provide an emergency
763  * valve.
764  */
765  if (restarts++ > 10000)
766  return InvalidBlockNumber;
767 
768  /* Start search all over from the root */
769  addr = FSM_ROOT_ADDRESS;
770  }
771  }
772 }
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:495
unsigned char uint8
Definition: c.h:439
static BlockNumber fsm_get_heap_blk(FSMAddress addr, uint16 slot)
Definition: freespace.c:484
static FSMAddress fsm_get_child(FSMAddress parent, uint16 slot)
Definition: freespace.c:513
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:3807
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:4023
static int fsm_set_and_search(Relation rel, FSMAddress addr, uint16 slot, uint8 newValue, uint8 minValue)
Definition: freespace.c:659
#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:532
#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 659 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().

661 {
662  Buffer buf;
663  Page page;
664  int newslot = -1;
665 
666  buf = fsm_readbuf(rel, addr, true);
668 
669  page = BufferGetPage(buf);
670 
671  if (fsm_set_avail(page, slot, newValue))
672  MarkBufferDirtyHint(buf, false);
673 
674  if (minValue != 0)
675  {
676  /* Search while we still hold the lock */
677  newslot = fsm_search_avail(buf, minValue,
678  addr.level == FSM_BOTTOM_LEVEL,
679  true);
680  }
681 
682  UnlockReleaseBuffer(buf);
683 
684  return newslot;
685 }
#define FSM_BOTTOM_LEVEL
Definition: freespace.c:77
void MarkBufferDirtyHint(Buffer buffer, bool buffer_std)
Definition: bufmgr.c:3854
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:3807
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:4023
static Buffer fsm_readbuf(Relation rel, FSMAddress addr, bool extend)
Definition: freespace.c:532
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 370 of file freespace.c.

References Assert, FSM_CAT_STEP, and MaxFSMRequestSize.

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

371 {
372  int cat;
373 
374  Assert(avail < BLCKSZ);
375 
376  if (avail >= MaxFSMRequestSize)
377  return 255;
378 
379  cat = avail / FSM_CAT_STEP;
380 
381  /*
382  * The highest category, 255, is reserved for MaxFSMRequestSize bytes or
383  * more.
384  */
385  if (cat > 254)
386  cat = 254;
387 
388  return (uint8) cat;
389 }
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 396 of file freespace.c.

References FSM_CAT_STEP, and MaxFSMRequestSize.

Referenced by GetRecordedFreeSpace().

397 {
398  /* The highest category represents exactly MaxFSMRequestSize bytes. */
399  if (cat == 255)
400  return MaxFSMRequestSize;
401  else
402  return cat * FSM_CAT_STEP;
403 }
#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 410 of file freespace.c.

References elog, ERROR, FSM_CAT_STEP, and MaxFSMRequestSize.

Referenced by GetPageWithFreeSpace(), and RecordAndGetPageWithFreeSpace().

411 {
412  int cat;
413 
414  /* Can't ask for more space than the highest category represents */
415  if (needed > MaxFSMRequestSize)
416  elog(ERROR, "invalid FSM request size %zu", needed);
417 
418  if (needed == 0)
419  return 1;
420 
421  cat = (needed + FSM_CAT_STEP - 1) / FSM_CAT_STEP;
422 
423  if (cat > 255)
424  cat = 255;
425 
426  return (uint8) cat;
427 }
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 789 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().

792 {
793  Buffer buf;
794  Page page;
795  uint8 max_avail;
796 
797  /* Read the page if it exists, or return EOF */
798  buf = fsm_readbuf(rel, addr, false);
799  if (!BufferIsValid(buf))
800  {
801  *eof_p = true;
802  return 0;
803  }
804  else
805  *eof_p = false;
806 
807  page = BufferGetPage(buf);
808 
809  /*
810  * If we're above the bottom level, recurse into children, and fix the
811  * information stored about them at this level.
812  */
813  if (addr.level > FSM_BOTTOM_LEVEL)
814  {
815  FSMAddress fsm_start,
816  fsm_end;
817  uint16 fsm_start_slot,
818  fsm_end_slot;
819  int slot,
820  start_slot,
821  end_slot;
822  bool eof = false;
823 
824  /*
825  * Compute the range of slots we need to update on this page, given
826  * the requested range of heap blocks to consider. The first slot to
827  * update is the one covering the "start" block, and the last slot is
828  * the one covering "end - 1". (Some of this work will be duplicated
829  * in each recursive call, but it's cheap enough to not worry about.)
830  */
831  fsm_start = fsm_get_location(start, &fsm_start_slot);
832  fsm_end = fsm_get_location(end - 1, &fsm_end_slot);
833 
834  while (fsm_start.level < addr.level)
835  {
836  fsm_start = fsm_get_parent(fsm_start, &fsm_start_slot);
837  fsm_end = fsm_get_parent(fsm_end, &fsm_end_slot);
838  }
839  Assert(fsm_start.level == addr.level);
840 
841  if (fsm_start.logpageno == addr.logpageno)
842  start_slot = fsm_start_slot;
843  else if (fsm_start.logpageno > addr.logpageno)
844  start_slot = SlotsPerFSMPage; /* shouldn't get here... */
845  else
846  start_slot = 0;
847 
848  if (fsm_end.logpageno == addr.logpageno)
849  end_slot = fsm_end_slot;
850  else if (fsm_end.logpageno > addr.logpageno)
851  end_slot = SlotsPerFSMPage - 1;
852  else
853  end_slot = -1; /* shouldn't get here... */
854 
855  for (slot = start_slot; slot <= end_slot; slot++)
856  {
857  int child_avail;
858 
860 
861  /* After we hit end-of-file, just clear the rest of the slots */
862  if (!eof)
863  child_avail = fsm_vacuum_page(rel, fsm_get_child(addr, slot),
864  start, end,
865  &eof);
866  else
867  child_avail = 0;
868 
869  /* Update information about the child */
870  if (fsm_get_avail(page, slot) != child_avail)
871  {
873  fsm_set_avail(page, slot, child_avail);
874  MarkBufferDirtyHint(buf, false);
876  }
877  }
878  }
879 
880  /* Now get the maximum value on the page, to return to caller */
881  max_avail = fsm_get_max_avail(page);
882 
883  /*
884  * Reset the next slot pointer. This encourages the use of low-numbered
885  * pages, increasing the chances that a later vacuum can truncate the
886  * relation. We don't bother with a lock here, nor with marking the page
887  * dirty if it wasn't already, since this is just a hint.
888  */
889  ((FSMPage) PageGetContents(page))->fp_next_slot = 0;
890 
891  ReleaseBuffer(buf);
892 
893  return max_avail;
894 }
#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:3854
static FSMAddress fsm_get_parent(FSMAddress child, uint16 *slot)
Definition: freespace.c:495
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:3784
static uint8 fsm_vacuum_page(Relation rel, FSMAddress addr, BlockNumber start, BlockNumber end, bool *eof)
Definition: freespace.c:789
#define BUFFER_LOCK_EXCLUSIVE
Definition: bufmgr.h:98
static FSMAddress fsm_get_child(FSMAddress parent, uint16 slot)
Definition: freespace.c:513
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:469
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
#define PageGetContents(page)
Definition: bufpage.h:246
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:4023
#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:532
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:102
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:410
static BlockNumber fsm_search(Relation rel, uint8 min_cat)
Definition: freespace.c:691

◆ GetRecordedFreeSpace()

Size GetRecordedFreeSpace ( Relation  rel,
BlockNumber  heapBlk 
)

Definition at line 231 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().

232 {
233  FSMAddress addr;
234  uint16 slot;
235  Buffer buf;
236  uint8 cat;
237 
238  /* Get the location of the FSM byte representing the heap block */
239  addr = fsm_get_location(heapBlk, &slot);
240 
241  buf = fsm_readbuf(rel, addr, false);
242  if (!BufferIsValid(buf))
243  return 0;
244  cat = fsm_get_avail(BufferGetPage(buf), slot);
245  ReleaseBuffer(buf);
246 
247  return fsm_space_cat_to_avail(cat);
248 }
unsigned char uint8
Definition: c.h:439
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3784
unsigned short uint16
Definition: c.h:440
static Size fsm_space_cat_to_avail(uint8 cat)
Definition: freespace.c:396
static char * buf
Definition: pg_test_fsync.c:68
static FSMAddress fsm_get_location(BlockNumber heapblk, uint16 *slot)
Definition: freespace.c:469
#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:532
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:484
unsigned short uint16
Definition: c.h:440
static FSMAddress fsm_get_location(BlockNumber heapblk, uint16 *slot)
Definition: freespace.c:469
static uint8 fsm_space_avail_to_cat(Size avail)
Definition: freespace.c:370
static uint8 fsm_space_needed_to_cat(Size needed)
Definition: freespace.c:410
static int fsm_set_and_search(Relation rel, FSMAddress addr, uint16 slot, uint8 newValue, uint8 minValue)
Definition: freespace.c:659
static BlockNumber fsm_search(Relation rel, uint8 min_cat)
Definition: freespace.c:691

◆ 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:469
static uint8 fsm_space_avail_to_cat(Size avail)
Definition: freespace.c:370
static int fsm_set_and_search(Relation rel, FSMAddress addr, uint16 slot, uint8 newValue, uint8 minValue)
Definition: freespace.c:659

◆ 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(), InvalidBuffer, 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 */
214  InvalidBuffer);
216 
217  page = BufferGetPage(buf);
218  if (PageIsNew(page))
219  PageInit(page, BLCKSZ, 0);
220 
221  if (fsm_set_avail(page, slot, new_cat))
222  MarkBufferDirtyHint(buf, false);
223  UnlockReleaseBuffer(buf);
224 }
void MarkBufferDirtyHint(Buffer buffer, bool buffer_std)
Definition: bufmgr.c:3854
bool fsm_set_avail(Page page, int slot, uint8 value)
Definition: fsmpage.c:63
#define InvalidBuffer
Definition: buf.h:25
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:3807
Buffer XLogReadBufferExtended(RelFileNode rnode, ForkNumber forknum, BlockNumber blkno, ReadBufferMode mode, Buffer recent_buffer)
Definition: xlogutils.c:443
static char * buf
Definition: pg_test_fsync.c:68
static FSMAddress fsm_get_location(BlockNumber heapblk, uint16 *slot)
Definition: freespace.c:469
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
static uint8 fsm_space_avail_to_cat(Size avail)
Definition: freespace.c:370
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:4023
static BlockNumber fsm_logical_to_physical(FSMAddress addr)
Definition: freespace.c:433
#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().