diff --git a/src/tfs/inc/tdisk.h b/src/tfs/inc/tdisk.h new file mode 100644 index 0000000000000000000000000000000000000000..3454bea49b9dca89d65bdfbbaf6906b96036aef1 --- /dev/null +++ b/src/tfs/inc/tdisk.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef TD_TDISK_H +#define TD_TDISK_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + int level; + int id; +} SDiskID; + +typedef struct { + uint64_t size; + uint64_t free; + uint64_t nfiles; +} SDiskMeta; + +typedef struct { + int level; + int id; + char dir[TSDB_FILENAME_LEN]; + SDiskMeta dmeta; +} SDisk; + +SDisk *tdNewDisk(SDiskID did, char *dir); +void tdFreeDisk(SDisk *pDisk); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/src/tfs/inc/tfs.h b/src/tfs/inc/tfs.h new file mode 100644 index 0000000000000000000000000000000000000000..bd4824bc348673aa1e6c1db2e259e2699d79f718 --- /dev/null +++ b/src/tfs/inc/tfs.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef TD_TFS_H +#define TD_TFS_H + +#include "tglobal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int tfsInit(SDiskCfg *pDiskCfg, int ndisk); +void tfsDestroy(); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/src/tfs/inc/ttier.h b/src/tfs/inc/ttier.h new file mode 100644 index 0000000000000000000000000000000000000000..1d113c847a913e747c223700c482cd2b624f91f5 --- /dev/null +++ b/src/tfs/inc/ttier.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef TD_TTIER_H +#define TD_TTIER_H + +#include "tdisk.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define TSDB_MAX_DISK_PER_TIER 16 + +typedef struct { + int level; + int ndisk; + SDisk *disks[TSDB_MAX_DISK_PER_TIER]; +} STier; + +void tdInitTier(STier *pTier, int level); +void tdDestroyTier(STier *pTier); +SDisk *tdAddDiskToTier(STier *pTier, SDiskCfg *pCfg); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/src/tfs/src/tdisk.c b/src/tfs/src/tdisk.c new file mode 100644 index 0000000000000000000000000000000000000000..bcefafc0b1e539abd916e23bdebc98f5ea69bc7e --- /dev/null +++ b/src/tfs/src/tdisk.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "tdisk.h" + +SDisk *tdNewDisk(SDiskID did, char *dir) { + SDisk *pDisk = (SDisk *)calloc(1, sizeof(*pDisk)); + if (pDisk == NULL) { + terrno = TSDB_CODE_FS_OUT_OF_MEMORY; + return NULL; + } + + pDisk->level = did.level; + pDisk->id = did.id; + strncpy(pDisk->dir, dir, TSDB_FILENAME_LEN); + + return pDisk; +} + +void tdFreeDisk(SDisk *pDisk) { + if (pDisk) { + free(pDisk) + } +} \ No newline at end of file diff --git a/src/tfs/src/tfs.c b/src/tfs/src/tfs.c new file mode 100644 index 0000000000000000000000000000000000000000..4400da40e88eef09a320786d15617ab7b1ceeb38 --- /dev/null +++ b/src/tfs/src/tfs.c @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "os.h" +#include "hash.h" +#include "ttier.h" +#include "tglobal.h" +#include "taoserror.h" + +#define fFatal(...) { if (fsDebugFlag & DEBUG_FATAL) { taosPrintLog("FS FATAL ", 255, __VA_ARGS__); }} +#define fError(...) { if (fsDebugFlag & DEBUG_ERROR) { taosPrintLog("FS ERROR ", 255, __VA_ARGS__); }} +#define fWarn(...) { if (fsDebugFlag & DEBUG_WARN) { taosPrintLog("FS WARN ", 255, __VA_ARGS__); }} +#define fInfo(...) { if (fsDebugFlag & DEBUG_INFO) { taosPrintLog("FS ", 255, __VA_ARGS__); }} +#define fDebug(...) { if (fsDebugFlag & DEBUG_DEBUG) { taosPrintLog("FS ", cqDebugFlag, __VA_ARGS__); }} +#define fTrace(...) { if (fsDebugFlag & DEBUG_TRACE) { taosPrintLog("FS ", cqDebugFlag, __VA_ARGS__); }} + +#define TSDB_MAX_TIER 3 + +typedef struct { + uint64_t tsize; + uint64_t avail; +} SFSMeta; + +typedef struct { + pthread_mutex_t lock; + SFSMeta meta; + int nlevel; + STier tiers[TSDB_MAX_TIER]; + SHashObj * map; // name to did map +} SFS; + +static SFS tdFileSystem = {0}; +static SFS *pfs = &tdFileSystem; + +#define TIER_AT(level) (pfs->tiers + (level)) + +int tfsInit(SDiskCfg *pDiskCfg, int ndisk) { + ASSERT(ndisk > 0); + + for (int level = 0; level < TSDB_MAX_TIER; level++) { + tdInitTier(TIER_AT(level), level); + } + + int ret = pthread_mutex_init(&(pfs->lock), NULL); + if (ret != 0) { + terrno = TAOS_SYSTEM_ERROR(ret); + return -1; + } + + pfs->map = taosHashInit(TSDB_MAX_TIER * TSDB_MAX_DISKS_PER_TIER * 2, + taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY), false, HASH_NO_LOCK); + if (pfs->map == NULL) { + terrno = TSDB_CODE_FS_OUT_OF_MEMORY; + tfsDestroy(); + return -1; + } + + for (int idisk = 0; idisk < ndisk; idisk++) { + if (tdAddDiskToFS(pDiskCfg + idisk) < 0) { + tfsDestroy(); + return -1; + } + } + + if (tdCheckFS() < 0) { + tfsDestroy(); + return -1; + } + + return 0; +} + +void tfsDestroy() { + taosHashCleanup(pfs->map); + pfs->map = NULL; + + pthread_mutex_destroy(&(pfs->lock)); + for (int level = 0; level < TSDB_MAX_TIER; level++) { + tdDestroyTier(TIER_AT(level)); + } +} + +static int tdAddDiskToFS(SDiskCfg *pCfg) { + if (tdCheckAndFormatCfg(pCfg) < 0) return -1; + + if (tdAddDiskToTier(pCfg, TIER_AT(pCfg->level)) < 0) { + fError("failed to add disk %s to FS since %s", pCfg->dir, tstrerror(terrno)); + } + + taosHashPut(pTiers->map, (void *)dirName, strnlen(dirName, TSDB_FILENAME_LEN), (void *)(&diskid), sizeof(diskid)); + if (pfs->nlevel < pCfg->level + 1) pfs->nlevel = pCfg->level + 1; + + return 0; +} + +static int tdCheckAndFormatCfg(SDiskCfg *pCfg) { + char dirName[TSDB_FILENAME_LEN] = "\0"; + struct stat pstat; + + if (pCfg->level < 0 || pCfg->level >= TSDB_MAX_TIER) { + fError("failed to add disk %s to FS since invalid level %d", pCfg->dir, pCfg->level); + terrno = TSDB_CODE_FS_INVLD_CFG; + return -1; + } + + if (pCfg->primary && pCfg->level != 0) { + fError("failed to add disk %s to FS since disk is primary but level %d not 0", pCfg->dir, pCfg->level); + terrno = TSDB_CODE_FS_INVLD_CFG; + return -1; + } + + + if (tdFormatDir(pCfg->dir, dirName) < 0) { + fError("failed to add disk %s to FS since invalid dir format", pCfg->dir); + terrno = TSDB_CODE_FS_INVLD_CFG; + return -1; + } + + if (tdGetDiskByName(dirName)) { + fError("failed to add disk %s to FS since duplicate add", pCfg->dir); + terrno = TSDB_CODE_FS_INVLD_CFG; + return -1; + } + + if (access(dirName, W_OK | R_OK | F_OK) != 0) { + fError("failed to add disk %s to FS since no enough access rights", pCfg->dir); + terrno = TSDB_CODE_FS_INVLD_CFG; + return -1; + } + + if (stat(dirName, &pstat) < 0) { + fError("failed to add disk %s to FS since %s", pCfg->dir, strerror(errno)); + terrno = TAOS_SYSTEM_ERROR(errno); + return -1; + } + + if (!S_ISDIR(pstat.st_mode)) { + fError("failed to add disk %s to FS since not a directory", pCfg->dir); + terrno = TSDB_CODE_FS_INVLD_CFG; + return -1; + } + + strncpy(pCfg->dir, dirName, TSDB_FILENAME_LEN); + + return 0; +} + +static int tdFormatDir(char *idir, char *odir) { + wordexp_t wep = {0}; + + int code = wordexp(idir, &wep, 0); + if (code != 0) { + terrno = TAOS_SYSTEM_ERROR(code); + return -1; + } + + if (realpath(wep.we_wordv[0], odir) == NULL) { + terrno = TAOS_SYSTEM_ERROR(errno); + wordfree(&wep); + return -1; + } + + wordfree(&wep); + return 0; + +} + +static int tdCheckFS() { + if (DISK_AT(0, 0) == NULL) { + fError("no primary disk is set"); + terrno = TSDB_CODE_FS_NO_PRIMARY_DISK; + return -1; + } + + for (int level = 0; level < pfs->nlevel; level++) { + if (TIER_AT(level)->ndisk == 0) { + fError("no disk at level %d", level); + terrno = TSDB_CODE_FS_NO_DISK_AT_TIER; + return -1; + } + } + + return 0; +} \ No newline at end of file diff --git a/src/tfs/src/ttier.c b/src/tfs/src/ttier.c new file mode 100644 index 0000000000000000000000000000000000000000..8faa03e2eee2030e607c4e56b55c450686234e29 --- /dev/null +++ b/src/tfs/src/ttier.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "ttier.h" +#include "tglobal.h" + +#define DISK_AT_TIER(pTier, id) ((pTier)->disks + (id)) + +void tdInitTier(STier *pTier, int level) { + pTier->level = level; +} + +void tdDestroyTier(STier *pTier) { + for (int id = 0; id < TSDB_MAX_DISK_PER_TIER; id++) { + tdFreeDisk(DISK_AT_TIER(pTier, id)); + DISK_AT_TIER(pTier, id) = NULL; + } +} + +SDisk *tdAddDiskToTier(STier *pTier, SDiskCfg *pCfg) { + ASSERT(pTier->level == pCfg->level); + int id = 0; + + if (pTier->ndisk >= TSDB_MAX_DISK_PER_TIER) { + terrno = TSDB_CODE_FS_TOO_MANY_DISKS; + return -1; + } + + if (pCfg->primary) { + if (DISK_AT(0, 0) != NULL) { + terrno = TSDB_CODE_FS_DUP_PRIMARY; + return -1; + } + } else { + if (pTier->level == 0) { + if (DISK_AT_TIER(pTier, 0) != NULL) { + id = pTier->ndisk; + } else { + id = pTier->ndisk + 1; + if (id >= TSDB_MAX_DISK_PER_TIER) { + terrno = TSDB_CODE_FS_TOO_MANY_DISKS; + return -1; + } + } + } else { + id = pTier->ndisk; + } + } + + pTier->disks[id] = tdNewDisk({pCfg->level, id}, pCfg->dir); + if (pTier->disks[id] == NULL) return -1; + + return 0; +} \ No newline at end of file