openGauss-server/src/gausskernel/storage/smgr/segment/extent_group.cpp

860 lines
30 KiB
C++

/*
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
*
* openGauss is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
*
* extent_group.cpp
*
* IDENTIFICATION
* src/gausskernel/storage/smgr/segment/extent_group.cpp
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/xloginsert.h"
#include "access/xlogproc.h"
#include "catalog/storage_xlog.h"
#include "commands/tablespace.h"
#include "executor/executor.h"
#include "miscadmin.h"
#include "storage/smgr/segment.h"
XLogRecPtr log_seg_physical_file_extend(SegLogicFile *segfile);
/* Check MAIN_FORK data files valid or not */
bool eg_df_valid(SegExtentGroup *seg)
{
AutoMutexLock lock(&seg->lock);
lock.lock();
SegLogicFile *sf = seg->segfile;
SegmentCheck(sf != NULL);
BlockNumber total_block = seg->segfile->total_blocks;
if (total_block < DF_FILE_MIN_BLOCKS) {
/* data file is not large enough */
ereport(LOG, (errmsg("extent group %s data file is too small. It has %u blocks, but expect at least %u blocks",
sf->filename, total_block, (BlockNumber)DF_FILE_MIN_BLOCKS)));
return false;
}
/* Read map head, check 1. block checksum; 2. bit unit */
char *buffer = (char *)palloc(BLCKSZ);
df_pread_block(sf, buffer, DF_MAP_HEAD_PAGE);
if (!PageIsVerified((Page)buffer, DF_MAP_HEAD_PAGE)) {
ereport(LOG, (errmsg("extent group %s map head block checksum verification failed", sf->filename)));
pfree(buffer);
return false;
}
st_df_map_head *map_head = (st_df_map_head *)PageGetContents((Page)buffer);
if (map_head->bit_unit != seg->extent_size) {
ereport(LOG, (errmsg("extent group %s, bit unit in head is %u, but its extent size is %u ", sf->filename,
map_head->bit_unit, seg->extent_size)));
pfree(buffer);
return false;
}
pfree(buffer);
return true;
}
/*
* Extent Group Layer
*
* Each extent group only allocates extents in a certain size. We use bitmap to manage extents. Each bit is associated
* with an extent, 0 denotes free and 1 denotes occupied.
*
*/
void eg_init_df_ctrl(SegExtentGroup *seg)
{
if (seg->segfile != NULL) {
return;
}
MemoryContext oldcnxt = MemoryContextSwitchTo(INSTANCE_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE));
SegLogicFile *sf = (SegLogicFile *)palloc(sizeof(SegLogicFile));
MemoryContextSwitchTo(oldcnxt);
df_ctrl_init(sf, seg->rnode, seg->forknum);
df_open_files(sf);
seg->segfile = sf;
}
BlockNumber eg_df_size(SegExtentGroup *seg)
{
AutoMutexLock lock(&seg->lock);
lock.lock();
SegLogicFile *sf = seg->segfile;
SegmentCheck(sf != NULL);
return sf->total_blocks;
}
bool eg_df_exists(SegExtentGroup *seg)
{
AutoMutexLock lock(&seg->lock);
lock.lock();
SegLogicFile *sf = seg->segfile;
SegmentCheck(sf != NULL);
return (sf->file_num > 0);
}
void eg_create_df(SegExtentGroup *seg)
{
AutoMutexLock lock(&seg->lock);
lock.lock();
SegLogicFile *sf = seg->segfile;
SegmentCheck(sf != NULL);
if (sf->file_num == 0) {
df_create_file(sf, false);
}
}
void eg_extend_df(SegExtentGroup *seg, BlockNumber target_file_size)
{
AutoMutexLock lock(&seg->lock);
lock.lock();
/* SegLogicFile must be initialized before extending */
SegLogicFile *sf = seg->segfile;
SegmentCheck(sf != NULL);
df_extend(sf, target_file_size);
}
void eg_shrink_df(SegExtentGroup *seg, BlockNumber target)
{
AutoMutexLock lock(&seg->lock);
lock.lock();
SegLogicFile *sf = seg->segfile;
SegmentCheck(sf != NULL);
df_shrink(sf, target);
}
/*
* Check current data file status; this function is usually called when the space is first accessed.
*/
SpaceDataFileStatus eg_status(SegExtentGroup *seg)
{
if (!eg_df_exists(seg)) {
return SpaceDataFileStatus::EMPTY;
}
if (eg_df_valid(seg)) {
return SpaceDataFileStatus::NORMAL;
}
return SpaceDataFileStatus::CRASHED;
}
/* Initialize information in SegExtentGroup structure */
void eg_ctrl_init(SegSpace *spc, SegExtentGroup *seg, int extent_size, ForkNumber forknum)
{
SegmentCheck(&spc->extent_group[EXTENT_SIZE_TO_GROUPID(extent_size)][forknum] == seg);
seg->extent_size = extent_size;
seg->space = spc;
seg->rnode = {.spcNode = spc->spcNode,
.dbNode = spc->dbNode,
.relNode = EXTENT_SIZE_TO_TYPE(extent_size),
.bucketNode = SegmentBktId,
.opt = 0};
seg->forknum = forknum;
seg->map_head_entry = DF_MAP_HEAD_PAGE;
pthread_mutex_init(&seg->lock, NULL);
eg_init_df_ctrl(seg);
}
void eg_init_datafile_head(SegExtentGroup *seg)
{
/*
* The first block in the data file is reserved, so that we can use it to store metadata of the data file
* if necessary in the future.
*/
}
void eg_init_map_head_page_content(Page map_head_page, int extent_size)
{
SegPageInit(map_head_page, BLCKSZ);
char *content = PageGetContents(map_head_page);
df_map_head_t *map_head = (df_map_head_t *)content;
map_head->bit_unit = extent_size;
map_head->group_count = 0;
map_head->free_group = 0;
map_head->allocated_extents = 0;
PageHeader page_header = (PageHeader)map_head_page;
page_header->pd_lower += sizeof(df_map_head_t);
}
static void eg_init_map_head(SegExtentGroup *seg, XLogRecPtr rec_ptr)
{
BlockNumber pageno = DF_MAP_HEAD_PAGE;
Page page = (Page)palloc(BLCKSZ);
errno_t er = memset_s((void *)page, BLCKSZ, 0, BLCKSZ);
securec_check(er, "", "");
eg_init_map_head_page_content(page, seg->extent_size);
PageSetLSN(page, rec_ptr);
PageSetChecksumInplace(page, pageno);
df_pwrite_block(seg->segfile, (char *)page, pageno);
pfree(page);
seg->map_head_entry = pageno;
seg->map_head = NULL;
}
void eg_clean_data_files(SegExtentGroup *seg)
{
df_unlink(seg->segfile);
}
void eg_init_data_files(SegExtentGroup *eg, bool redo, XLogRecPtr rec_ptr)
{
SEGMENTTEST(EXTENT_GROUP_INIT_DATA, (errmsg("EXTENT_GROUP_INIT_DATA %s: the first time for create segment tb!\n",
g_instance.attr.attr_common.PGXCNodeName)));
/* create file will ensure the file has enough space */
df_create_file(eg->segfile, redo);
eg_init_datafile_head(eg); // initialize data file
eg_init_map_head(eg, rec_ptr); // initialize map head
df_fsync(eg->segfile);
}
static inline Buffer eg_read_maphead_buffer(SegExtentGroup *seg)
{
return ReadBufferFast(seg->space, seg->rnode, seg->forknum, seg->map_head_entry, RBM_NORMAL);
}
/*
* Find the map page which manages the given page.
*
* input: seg, page_id, map_head
* return: false if the given page id exceeds the current map management.
*/
SpaceMapLocation eg_locate_map_by_pageid(SegExtentGroup *seg, BlockNumber page_id, df_map_head_t *map_head)
{
SpaceMapLocation result;
errno_t er = memset_s(&result, sizeof(SpaceMapLocation), 0, sizeof(SpaceMapLocation));
securec_check(er, "", "");
for (int i = 0; i < map_head->group_count; i++) {
result.group = map_head->groups[i];
result.map_group = i;
BlockNumber page_start = map_head->groups[i].first_map + DF_MAP_GROUP_SIZE + IPBLOCK_GROUP_SIZE;
BlockNumber page_end = page_start + DF_MAP_GROUP_EXTENTS * seg->extent_size;
if (page_start <= page_id && page_id < page_end) {
result.map_id = (page_id - page_start) / map_head->bit_unit / DF_MAP_BIT_CNT;
result.bit_id = ((page_id - page_start) / map_head->bit_unit) % DF_MAP_BIT_CNT;
return result;
}
}
return result;
}
void eg_set_bitmap_page(df_map_page_t *map_page, uint16 bit_id)
{
SegmentCheck(DF_MAP_FREE(map_page->bitmap, bit_id));
DF_MAP_SET(map_page->bitmap, bit_id);
map_page->free_bits--;
if (map_page->free_begin == bit_id) {
map_page->free_begin++;
}
if (map_page->dirty_last < bit_id) {
map_page->dirty_last = bit_id;
}
}
void eg_init_bitmap_page_content(Page bitmap_page, BlockNumber first_page)
{
SegPageInit(bitmap_page, BLCKSZ);
df_map_page_t *df_map_page = (df_map_page_t *)PageGetContents(bitmap_page);
df_map_page->free_begin = 0;
df_map_page->dirty_last = 0;
df_map_page->free_bits = DF_MAP_BIT_CNT;
df_map_page->first_page = first_page;
PageHeader bitmap_page_header = (PageHeader)bitmap_page;
SegmentCheck(bitmap_page_header->pd_upper = BLCKSZ);
bitmap_page_header->pd_lower = bitmap_page_header->pd_upper;
}
/*
* Try update high water mark. It does not need WAL; Redo procedure will update hwm when updating 'allocated_extents'
*/
void try_update_hwm(df_map_head_t *map_head, uint32 hwm)
{
if (map_head->high_water_mark < hwm) {
map_head->high_water_mark = hwm;
}
}
/*
* MapPage and MapHeadPage should be locked.
*/
BlockNumber eg_alloc_extent_from_map_internal(SegExtentGroup *seg, Buffer map_buffer, uint16 bitid,
BlockNumber *target_file_size, uint32 map_group, uint32 free_page)
{
df_map_page_t *map_page = (df_map_page_t *)PageGetContents(BufferGetPage(map_buffer));
BlockNumber pagenum = map_page->first_page + ((BlockNumber)(bitid)) * seg->map_head->bit_unit;
/* check whether the allocated block exceeds the file size */
if ((pagenum + seg->extent_size) > seg->segfile->total_blocks) {
*target_file_size = (pagenum + seg->extent_size);
SegUnlockReleaseBuffer(map_buffer);
return InvalidBlockNumber;
}
/* set the bitmap and other metadata. */
eg_set_bitmap_page(map_page, bitid);
/* update map head metadata */
seg->map_head->allocated_extents++;
seg->map_head->free_group = map_group;
seg->map_head->groups[map_group].free_page = free_page;
try_update_hwm(seg->map_head, pagenum + seg->extent_size);
/* XLOG issue */
{
XLogAtomicOpRegisterBuffer(map_buffer, REGBUF_KEEP_DATA, SPCXLOG_SET_BITMAP, XLOG_COMMIT_UNLOCK_RELEASE_BUFFER);
XLogAtomicOpRegisterBufData((char *)&bitid, sizeof(uint16));
XLogAtomicOpRegisterBufData((char *)map_page, sizeof(df_map_page_t));
XLogDataSpaceAllocateExtent xlog_data;
xlog_data.hwm = seg->map_head->high_water_mark;
xlog_data.allocated_extents = seg->map_head->allocated_extents;
xlog_data.free_group = map_group;
xlog_data.free_page = free_page;
XLogAtomicOpRegisterBuffer(seg->map_head_buffer, REGBUF_KEEP_DATA, SPCXLOG_MAPHEAD_ALLOCATED_EXTENTS,
XLOG_COMMIT_UNLOCK_RELEASE_BUFFER);
XLogAtomicOpRegisterBufData((char *)&xlog_data, sizeof(xlog_data));
}
return pagenum;
}
BlockNumber eg_alloc_extent_from_map(SegExtentGroup *seg, BlockNumber map_page_id, BlockNumber *target_file_size,
uint32 map_group, uint32 free_page)
{
Buffer map_buffer = ReadBufferFast(seg->space, seg->rnode, seg->forknum, map_page_id, RBM_NORMAL);
LockBuffer(map_buffer, BUFFER_LOCK_EXCLUSIVE);
df_map_page_t *map_page = (df_map_page_t *)PageGetContents(BufferGetPage(map_buffer));
if (map_page->free_bits == 0) {
SegUnlockReleaseBuffer(map_buffer);
return InvalidBlockNumber;
}
/* Find a free bit */
uint16 curr = map_page->free_begin;
while (curr < DF_MAP_BIT_CNT) {
if (DF_MAP_FREE(map_page->bitmap, curr)) {
break;
}
curr++;
}
if (curr >= DF_MAP_BIT_CNT) {
/* current map does not contain a free bit */
SegUnlockReleaseBuffer(map_buffer);
return InvalidBlockNumber;
}
/* internal function will release and unlock the MapPage buffer */
return eg_alloc_extent_from_map_internal(seg, map_buffer, curr, target_file_size, map_group, free_page);
}
bool eg_alloc_preassigned_block(SegExtentGroup *seg, BlockNumber preassigned_block, BlockNumber *target_file_size)
{
SpaceMapLocation location = eg_locate_map_by_pageid(seg, preassigned_block, seg->map_head);
if (MapLocationIsInvalid(location)) {
return false;
}
uint16 bit_id = location.bit_id;
BlockNumber map_blocknum = location.map_id + location.group.first_map;
Buffer map_buffer = ReadBufferFast(seg->space, seg->rnode, seg->forknum, map_blocknum, RBM_NORMAL);
LockBuffer(map_buffer, BUFFER_LOCK_EXCLUSIVE);
df_map_page_t *map_page = (df_map_page_t *)PageGetContents(BufferGetPage(map_buffer));
if (DF_MAP_FREE(map_page->bitmap, bit_id)) {
BlockNumber pagenum = map_page->first_page + bit_id * seg->map_head->bit_unit;
// check whether the allocated block exceeds the file size
if ((pagenum + seg->extent_size) > seg->segfile->total_blocks) {
*target_file_size = pagenum + seg->extent_size;
SegUnlockReleaseBuffer(map_buffer);
return false;
}
} else {
ereport(PANIC, (errmsg("The preassigned segment block %u is already in use", preassigned_block)));
}
uint32 free_group = seg->map_head->free_group;
uint32 free_page = seg->map_head->groups[free_group].free_page;
SegmentCheck(free_group < seg->map_head->group_count && free_page < seg->map_head->groups[free_group].page_count);
(void)eg_alloc_extent_from_map_internal(seg, map_buffer, bit_id, target_file_size, free_group, free_page);
return true;
}
void eg_init_bitmap_page(SegExtentGroup *seg, BlockNumber pageno, BlockNumber first_page)
{
Buffer buffer = ReadBufferFast(seg->space, seg->rnode, seg->forknum, pageno, RBM_NORMAL);
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
START_CRIT_SECTION();
SegMarkBufferDirty(buffer);
eg_init_bitmap_page_content(BufferGetPage(buffer), first_page);
/* XLog Issue */
XLogBeginInsert();
XLogRegisterData((char *)&first_page, sizeof(BlockNumber));
XLogRegisterBuffer(0, buffer, REGBUF_WILL_INIT);
XLogRecPtr rec_ptr = XLogInsert(RM_SEGPAGE_ID, XLOG_SEG_INIT_MAPPAGE, SegmentBktId);
PageSetLSN(BufferGetPage(buffer), rec_ptr);
END_CRIT_SECTION();
SegUnlockReleaseBuffer(buffer);
}
void eg_init_invrsptr_page(SegExtentGroup *seg, BlockNumber pageno)
{
Buffer buffer = ReadBufferFast(seg->space, seg->rnode, seg->forknum, pageno, RBM_ZERO_AND_LOCK);
Page page = BufferGetPage(buffer);
START_CRIT_SECTION();
SegMarkBufferDirty(buffer);
SegPageInit(page, BLCKSZ);
XLogBeginInsert();
XLogRegisterBuffer(0, buffer, REGBUF_WILL_INIT);
XLogRecPtr rec_ptr = XLogInsert(RM_SEGPAGE_ID, XLOG_SEG_INIT_INVRSPTR_PAGE, SegmentBktId);
PageSetLSN(page, rec_ptr);
END_CRIT_SECTION();
SegUnlockReleaseBuffer(buffer);
}
/* map_block should be locked */
BlockNumber eg_next_group_start(df_map_head_t *map_head)
{
SegmentCheck(map_head->group_count > 0);
df_map_group_t last_map_group = map_head->groups[map_head->group_count - 1];
return last_map_group.first_map + last_map_group.page_count + IPBLOCK_GROUP_SIZE +
DF_MAP_GROUP_EXTENTS * (uint32)map_head->bit_unit;
}
void eg_add_map_group(SegExtentGroup *seg, BlockNumber pageno, uint8 group_size, int old_group_count)
{
ereport(LOG, (errmsg("Extent group (%u, %u, %u) add new group #%d, start pageno: %u ", seg->rnode.spcNode,
seg->rnode.dbNode, seg->rnode.relNode, old_group_count, pageno)));
Buffer map_head_buffer = seg->map_head_buffer;
Page page = BufferGetPage(map_head_buffer);
df_map_head_t *map_head = (df_map_head_t *)PageGetContents(page);
if (old_group_count < map_head->group_count) {
/* Some one else extend the extent group for us */
return;
}
SegmentCheck(old_group_count == map_head->group_count);
SegmentCheck(group_size == DF_MAP_GROUP_SIZE);
BlockNumber start_map_no = pageno;
BlockNumber data_start = pageno + group_size + IPBLOCK_GROUP_SIZE;
for (uint32 i = 0; i < group_size; i++) {
eg_init_bitmap_page(seg, pageno, data_start);
data_start += DF_MAP_BIT_CNT * seg->extent_size;
pageno++;
}
for (uint32 i = 0; i < IPBLOCK_GROUP_SIZE; i++) {
eg_init_invrsptr_page(seg, pageno);
pageno++;
}
SEGMENTTEST(EXTENT_GROUP_ADD_NEW_GROUP, (errmsg("EXTENT_GROUP_ADD_NEW_GROUP %s: add new group success!\n",
g_instance.attr.attr_common.PGXCNodeName)));
/* XLog issue */
{
START_CRIT_SECTION();
/* Update map head */
df_map_group_t *bitmap_group = &map_head->groups[map_head->group_count];
map_head->group_count++;
bitmap_group->first_map = start_map_no;
bitmap_group->page_count = group_size;
bitmap_group->free_page = 0;
SegMarkBufferDirty(map_head_buffer);
xl_new_map_group_info_t new_map_group_info = {.first_map_pageno = start_map_no,
.extent_size = seg->extent_size,
.group_count = map_head->group_count,
.group_size = group_size};
XLogBeginInsert();
XLogRegisterData((char *)&new_map_group_info, sizeof(xl_new_map_group_info_t));
XLogRegisterBuffer(0, map_head_buffer, REGBUF_STANDARD);
XLogRecPtr recptr = XLogInsert(RM_SEGPAGE_ID, XLOG_SEG_ADD_NEW_GROUP, SegmentBktId);
PageSetLSN(page, recptr);
END_CRIT_SECTION();
}
SEGMENTTEST(EXTENT_GROUP_ADD_NEW_GROUP_XLOG, (errmsg("EXTENT_GROUP_ADD_NEW_GROUP_XLOG %s: add new group xlog success!\n",
g_instance.attr.attr_common.PGXCNodeName)));
}
/*
* Find a free extent in the current extent group. If all space is used up, return InvalidBlockNumber.
*
* Before calling this function, seg->map_head_buffer must be set and locked exclusively.
* On return, if result is InvalidBlockNumber, there are two reasons:
* 1. *target_file_size > 0, we need extend physical data file size
* 2. *target_file_size = 0, we need add a new map group.
*/
BlockNumber eg_try_alloc_extent(SegExtentGroup *seg, BlockNumber preassigned_block, BlockNumber *target_file_size)
{
*target_file_size = 0;
df_map_group_t *map_group = NULL;
BlockNumber curr_map_bno = 0;
BlockNumber result = InvalidBlockNumber;
df_map_head_t *map_head = seg->map_head;
seg->map_head = map_head;
SegmentCheck(map_head->group_count > 0 &&
map_head->free_group < map_head->group_count);
/* preassigned block */
if (BlockNumberIsValid(preassigned_block)) {
if (eg_alloc_preassigned_block(seg, preassigned_block, target_file_size)) {
return preassigned_block;
} else {
return InvalidBlockNumber;
}
}
for (int i = map_head->free_group; i < map_head->group_count; i++) {
map_group = &map_head->groups[i];
curr_map_bno = map_group->first_map + map_group->free_page;
SegmentCheck(map_group->free_page < map_group->page_count);
for (int j = map_group->free_page; j < map_group->page_count; j++) {
result = eg_alloc_extent_from_map(seg, curr_map_bno, target_file_size, i, j);
if (result != InvalidBlockNumber) {
return result;
}
if (*target_file_size > 0) {
return InvalidBlockNumber;
}
curr_map_bno++;
}
}
/*
* We can not allocate extent, and do not need extend file, it means that there are
* some blocks at the end of the data file is not managed by MapPage. Thus we need
* to add a new map group.
*/
BlockNumber next_group_data_start = eg_next_group_start(map_head) + DF_MAP_GROUP_SIZE + IPBLOCK_GROUP_SIZE;
if (next_group_data_start > seg->segfile->total_blocks) {
*target_file_size = next_group_data_start;
}
return InvalidBlockNumber;
}
bool eg_empty(SegExtentGroup *seg)
{
if (!eg_df_exists(seg)) {
return true;
}
int result;
Buffer buffer = eg_read_maphead_buffer(seg);
LockBuffer(buffer, BUFFER_LOCK_SHARE);
seg->map_head = (df_map_head_t *)PageGetContents(BufferGetPage(buffer));
result = seg->map_head->allocated_extents;
SegUnlockReleaseBuffer(buffer);
#ifdef USE_ASSERT_CHECKING
if (result != 0) {
ereport(LOG, (errmsg("Extent group %d is not empty, there are %d extents used", seg->extent_size, result)));
}
#endif
return result == 0;
}
SegmentSpaceStat eg_storage_stat(SegExtentGroup *seg)
{
if (!eg_df_exists(seg)) {
return {.extent_size = (uint32)seg->extent_size,
.forknum = seg->forknum,
.total_blocks = 0,
.meta_data_blocks = 0,
.used_data_blocks = 0,
.utilization = 0,
.high_water_mark = 0};
}
Buffer buffer = eg_read_maphead_buffer(seg);
LockBuffer(buffer, BUFFER_LOCK_SHARE);
df_map_head_t *map_head = (df_map_head_t *)PageGetContents(BufferGetPage(buffer));
BlockNumber total_blocks = seg->segfile->total_blocks;
// map group start from DF_MAP_HEAD_PAGE + 1
BlockNumber meta_data_blocks = DF_MAP_HEAD_PAGE + 1;
for (int i = 0; i < map_head->group_count; i++) {
meta_data_blocks += DF_MAP_GROUP_SIZE + IPBLOCK_GROUP_SIZE;
}
BlockNumber used_data_blocks = map_head->allocated_extents * seg->extent_size;
SegmentSpaceStat result = {.extent_size = (uint32)seg->extent_size,
.forknum = seg->forknum,
.total_blocks = total_blocks,
.meta_data_blocks = meta_data_blocks,
.used_data_blocks = used_data_blocks,
.utilization = float(meta_data_blocks + used_data_blocks) / total_blocks,
.high_water_mark = map_head->high_water_mark};
SegUnlockReleaseBuffer(buffer);
return result;
}
void GetAllInversePointer(SegExtentGroup *seg, uint32 *cnt, ExtentInversePointer **iptrs, BlockNumber **extents)
{
if (!eg_df_exists(seg)) {
*cnt = 0;
*iptrs = NULL;
*extents = NULL;
return;
}
uint32 len = 1024;
*iptrs = (ExtentInversePointer *)palloc(sizeof(ExtentInversePointer) * len);
*extents = (BlockNumber *)palloc(sizeof(BlockNumber) * len);
*cnt = 0;
Buffer buffer = eg_read_maphead_buffer(seg);
LockBuffer(buffer, BUFFER_LOCK_SHARE);
df_map_head_t *map_head = (df_map_head_t *)PageGetContents(BufferGetPage(buffer));
/* copy meta-data from map head, to avoid locking it for long time. */
int group_count = map_head->group_count;
df_map_group_t groups[DF_MAX_MAP_GROUP_CNT];
for (int i = 0; i < group_count; i++) {
groups[i] = map_head->groups[i];
}
SegUnlockReleaseBuffer(buffer);
Buffer ipbuf = InvalidBuffer;
for (int i = 0; i < group_count; i++) {
BlockNumber first_map = groups[i].first_map;
uint8 page_count = groups[i].page_count;
SegmentCheck(page_count == DF_MAP_GROUP_SIZE);
for (uint8 j = 0; j < page_count; j++) {
BlockNumber map_block = first_map + j;
Buffer map_buffer = ReadBufferFast(seg->space, seg->rnode, seg->forknum, map_block, RBM_NORMAL);
LockBuffer(map_buffer, BUFFER_LOCK_SHARE);
df_map_page_t *map_page = (df_map_page_t *)PageGetContents(BufferGetPage(map_buffer));
for (uint16 k = 0; k <= map_page->dirty_last; k++) {
if (DF_MAP_NOT_FREE(map_page->bitmap, k)) {
BlockNumber extent = map_page->first_page + k * seg->extent_size;
ExtentInversePointer iptr = GetInversePointer(seg, extent, &ipbuf);
(*cnt)++;
if (*cnt > len) {
if (len < 16384) {
len *= 2;
} else {
len += 16384;
}
*iptrs = (ExtentInversePointer *)repalloc(*iptrs, sizeof(ExtentInversePointer) * len);
*extents = (BlockNumber *)repalloc(*extents, sizeof(BlockNumber) * len);
}
(*iptrs)[(*cnt) - 1] = iptr;
(*extents)[(*cnt) - 1] = extent;
}
}
SegUnlockReleaseBuffer(map_buffer);
}
}
if (BufferIsValid(ipbuf)) {
SegReleaseBuffer(ipbuf);
}
}
void eg_create_if_necessary(SegExtentGroup *seg)
{
if (!eg_df_exists(seg)) {
AutoMutexLock lock(&seg->lock);
lock.lock();
if (seg->segfile->file_num > 0) {
/* Someone else has created it */
return;
}
TablespaceCreateDbspace(seg->rnode.spcNode, seg->rnode.dbNode, false);
/* Ensure tablespace limits at first. */
uint64 requestSize = DF_FILE_EXTEND_STEP_SIZE;
TableSpaceUsageManager::IsExceedMaxsize(seg->rnode.spcNode, requestSize, true);
START_CRIT_SECTION();
t_thrd.pgxact->delayChkpt = true;
XLogBeginInsert();
XLogRegisterData((char *)&seg->rnode, sizeof(RelFileNode));
XLogRegisterData((char *)&seg->forknum, sizeof(ForkNumber));
XLogRecPtr xlog = XLogInsert(RM_SEGPAGE_ID, XLOG_SEG_CREATE_EXTENT_GROUP, SegmentBktId);
XLogWaitFlush(xlog);
eg_init_data_files(seg, false, xlog);
SEGMENTTEST(EXTENT_GROUP_CREATE_EXTENT, (errmsg("EXTENT_GROUP_CREATE_EXTENT %s: create segment file success!\n",
g_instance.attr.attr_common.PGXCNodeName)));
t_thrd.pgxact->delayChkpt = false;
END_CRIT_SECTION();
}
}
/*
* Extent Group Layer APIS: allocate/free extents; auto extend if the space is used up.
*/
BlockNumber eg_alloc_extent(SegExtentGroup *seg, BlockNumber preassigned_block, ExtentInversePointer iptr)
{
eg_create_if_necessary(seg);
BlockNumber blocknum = InvalidBlockNumber;
BlockNumber file_target_size = InvalidBlockNumber;
for (;;) {
seg->map_head_buffer = eg_read_maphead_buffer(seg);
LockBuffer(seg->map_head_buffer, BUFFER_LOCK_EXCLUSIVE);
seg->map_head = (df_map_head_t *)PageGetContents(BufferGetBlock(seg->map_head_buffer));
if (seg->map_head->group_count == 0) {
// create the first map group
eg_add_map_group(seg, DF_MAP_HEAD_PAGE + 1, DF_MAP_GROUP_SIZE, 0);
}
blocknum = eg_try_alloc_extent(seg, preassigned_block, &file_target_size);
if (blocknum != InvalidBlockNumber) {
// Get an extent, we do not have to release the map head buffer. XLogAtomicOpCommit will do it for us.
// set inverse point and return
SetInversePointer(seg, blocknum, iptr);
SEGMENTTEST(EXTENT_GROUP_CRITICAL_SECTION, (errmsg("EXTENT_GROUP_CRITICAL_SECTION %s: alloc extent end, begin critical section!\n",
g_instance.attr.attr_common.PGXCNodeName)));
return blocknum;
}
if (file_target_size > 0) {
// we need extend data file
SegUnlockReleaseBuffer(seg->map_head_buffer);
eg_extend_df(seg, file_target_size);
} else {
// we need an extra extent group
BlockNumber group_start = eg_next_group_start(seg->map_head);
eg_add_map_group(seg, group_start, DF_MAP_GROUP_SIZE, seg->map_head->group_count);
SegUnlockReleaseBuffer(seg->map_head_buffer);
}
}
}
/*
* Free extent should be reentrant
*/
void eg_unset_bitmap_page(df_map_page_t *map_page, uint16 bit_id)
{
SegmentCheck(DF_MAP_NOT_FREE(map_page->bitmap, bit_id));
DF_MAP_UNSET(map_page->bitmap, bit_id);
map_page->free_bits++;
if (map_page->free_begin > bit_id) {
map_page->free_begin = bit_id;
}
}
/*
* Free an extent in the extent group. 'allocated_extents' will be updated.
*/
void eg_free_extent(SegExtentGroup *seg, BlockNumber blocknum)
{
seg->map_head_buffer = ReadBufferFast(seg->space, seg->rnode, seg->forknum, seg->map_head_entry, RBM_NORMAL);
LockBuffer(seg->map_head_buffer, BUFFER_LOCK_EXCLUSIVE);
df_map_head_t *map_head = (df_map_head_t *)PageGetContents(BufferGetPage(seg->map_head_buffer));
SpaceMapLocation location = eg_locate_map_by_pageid(seg, blocknum, map_head);
if (MapLocationIsInvalid(location)) {
ereport(PANIC, (errmsg("Free an already freed extent")));
}
uint16 bit_id = location.bit_id;
BlockNumber map_blocknum = location.map_id + location.group.first_map;
Buffer map_buffer = ReadBufferFast(seg->space, seg->rnode, seg->forknum, map_blocknum, RBM_NORMAL);
LockBuffer(map_buffer, BUFFER_LOCK_EXCLUSIVE);
df_map_page_t *map_page = (df_map_page_t *)PageGetContents(BufferGetPage(map_buffer));
eg_unset_bitmap_page(map_page, bit_id);
map_head->allocated_extents--;
/* Update free group and free page if needed */
if (map_head->free_group > location.map_group) {
map_head->free_group = location.map_group;
}
if (map_head->groups[location.map_group].free_page > location.map_id) {
map_head->groups[location.map_group].free_page = location.map_id;
}
// XLog issue
{
XLogAtomicOpRegisterBuffer(map_buffer, REGBUF_KEEP_DATA, SPCXLOG_FREE_BITMAP,
XLOG_COMMIT_UNLOCK_RELEASE_BUFFER);
XLogAtomicOpRegisterBufData((char *)&bit_id, sizeof(bit_id));
XLogAtomicOpRegisterBufData((char *)map_page, sizeof(df_map_page_t));
XLogAtomicOpRegisterBufData((char *)&blocknum, sizeof(BlockNumber));
XLogAtomicOpRegisterBuffer(seg->map_head_buffer, REGBUF_KEEP_DATA, SPCXLOG_MAPHEAD_ALLOCATED_EXTENTS,
XLOG_COMMIT_UNLOCK_RELEASE_BUFFER);
XLogDataSpaceAllocateExtent xlog_data;
xlog_data.hwm = map_head->high_water_mark;
xlog_data.allocated_extents = map_head->allocated_extents;
xlog_data.free_group = map_head->free_group;
xlog_data.free_page = map_head->groups[map_head->free_group].free_page;
XLogAtomicOpRegisterBufData((char *)&xlog_data, sizeof(xlog_data));
}
}