LCOV - code coverage report
Current view: top level - lib/ftl/utils - ftl_layout_tracker_bdev.c (source / functions) Hit Total Coverage
Test: ut_cov_unit.info Lines: 182 211 86.3 %
Date: 2024-07-10 22:17:11 Functions: 14 14 100.0 %

          Line data    Source code
       1             : /*   SPDX-License-Identifier: BSD-3-Clause
       2             :  *   Copyright 2023 Solidigm All Rights Reserved
       3             :  */
       4             : 
       5             : #include "ftl_layout_tracker_bdev.h"
       6             : #include "spdk/util.h"
       7             : 
       8             : #define REG_VER_ANY     UINT32_MAX
       9             : 
      10             : struct layout_tracker_entry {
      11             :         TAILQ_ENTRY(layout_tracker_entry) layout_entry;
      12             :         struct ftl_layout_tracker_bdev_region_props reg;
      13             : };
      14             : 
      15             : struct ftl_layout_tracker_bdev {
      16             :         TAILQ_HEAD(layout_tracker, layout_tracker_entry) layout_head;
      17             :         uint64_t bdev_blks;
      18             :         uint32_t regs_cnt;
      19             : };
      20             : 
      21             : struct layout_tracker_blob_entry {
      22             :         /* Region type */
      23             :         uint32_t type;
      24             : 
      25             :         /* Region version */
      26             :         uint32_t ver;
      27             : 
      28             :         /* Region offset in blocks */
      29             :         uint64_t blk_offs;
      30             : 
      31             :         /* Region size in blocks */
      32             :         uint64_t blk_sz;
      33             : } __attribute__((packed));
      34             : 
      35             : static int
      36          24 : layout_tracker_init_entries(struct ftl_layout_tracker_bdev *tracker, uint64_t bdev_blks)
      37             : {
      38          24 :         struct layout_tracker_entry *entry_free = calloc(1, sizeof(*entry_free));
      39             : 
      40          24 :         if (!entry_free) {
      41           0 :                 return -ENOMEM;
      42             :         }
      43             : 
      44          24 :         assert(tracker);
      45          24 :         assert(tracker->regs_cnt == 0);
      46             : 
      47          24 :         tracker->bdev_blks = bdev_blks;
      48          24 :         tracker->regs_cnt = 1;
      49          24 :         TAILQ_INIT(&tracker->layout_head);
      50             : 
      51          24 :         entry_free->reg.blk_sz = bdev_blks;
      52          24 :         entry_free->reg.type = FTL_LAYOUT_REGION_TYPE_FREE;
      53             : 
      54          24 :         TAILQ_INSERT_HEAD(&tracker->layout_head, entry_free, layout_entry);
      55          24 :         return 0;
      56             : }
      57             : 
      58             : struct ftl_layout_tracker_bdev *
      59           4 : ftl_layout_tracker_bdev_init(uint64_t bdev_blks)
      60             : {
      61           4 :         struct ftl_layout_tracker_bdev *tracker = calloc(1, sizeof(*tracker));
      62             : 
      63           4 :         if (!tracker) {
      64           0 :                 return NULL;
      65             :         }
      66             : 
      67           4 :         if (layout_tracker_init_entries(tracker, bdev_blks)) {
      68           0 :                 free(tracker);
      69           0 :                 return NULL;
      70             :         }
      71             : 
      72           4 :         return tracker;
      73             : }
      74             : 
      75             : static void
      76          24 : layout_tracker_free_entries(struct ftl_layout_tracker_bdev *tracker)
      77             : {
      78             :         struct layout_tracker_entry *entry;
      79             : 
      80         242 :         while ((entry = TAILQ_FIRST(&tracker->layout_head))) {
      81         218 :                 TAILQ_REMOVE(&tracker->layout_head, entry, layout_entry);
      82         218 :                 free(entry);
      83             :         }
      84          24 :         tracker->regs_cnt = 0;
      85          24 : }
      86             : 
      87             : void
      88           4 : ftl_layout_tracker_bdev_fini(struct ftl_layout_tracker_bdev *tracker)
      89             : {
      90           4 :         assert(tracker);
      91           4 :         layout_tracker_free_entries(tracker);
      92           4 :         free(tracker);
      93           4 : }
      94             : 
      95             : static struct layout_tracker_entry *
      96          32 : layout_region_find_min_free(struct ftl_layout_tracker_bdev *tracker, uint64_t blk_sz,
      97             :                             uint64_t blk_align)
      98             : {
      99          32 :         struct layout_tracker_entry *min_free_entry = NULL;
     100             :         struct layout_tracker_entry *entry;
     101             : 
     102          32 :         assert(tracker);
     103             : 
     104         226 :         TAILQ_FOREACH(entry, &tracker->layout_head, layout_entry) {
     105             :                 uint64_t align_offs, align_sz;
     106             : 
     107         194 :                 if (entry->reg.type != FTL_LAYOUT_REGION_TYPE_FREE) {
     108         162 :                         continue;
     109             :                 }
     110             : 
     111          32 :                 align_offs = entry->reg.blk_offs;
     112          32 :                 align_sz = entry->reg.blk_sz;
     113          32 :                 if (blk_align) {
     114          32 :                         align_offs = SPDK_ALIGN_CEIL(align_offs, blk_align);
     115          32 :                         align_sz -= (align_offs - entry->reg.blk_offs);
     116             :                 }
     117             : 
     118          32 :                 if (align_sz >= blk_sz) {
     119          32 :                         if (!min_free_entry || min_free_entry->reg.blk_sz > entry->reg.blk_sz) {
     120          32 :                                 min_free_entry = entry;
     121             :                         }
     122             :                 }
     123             :         }
     124             : 
     125          32 :         return min_free_entry;
     126             : }
     127             : 
     128             : static struct layout_tracker_entry *
     129         414 : layout_region_find_from(struct ftl_layout_tracker_bdev *tracker,
     130             :                         enum ftl_layout_region_type reg_type,
     131             :                         uint32_t reg_ver, struct layout_tracker_entry *entry)
     132             : {
     133         414 :         assert(tracker);
     134             : 
     135        2890 :         TAILQ_FOREACH_FROM(entry, &tracker->layout_head, layout_entry) {
     136        2667 :                 if ((entry->reg.type == reg_type || reg_type == FTL_LAYOUT_REGION_TYPE_INVALID)
     137         198 :                     && (entry->reg.ver == reg_ver || reg_ver == REG_VER_ANY)) {
     138         191 :                         return entry;
     139             :                 }
     140             :         }
     141             : 
     142         223 :         return NULL;
     143             : }
     144             : 
     145             : static struct layout_tracker_entry *
     146         392 : layout_region_find_first(struct ftl_layout_tracker_bdev *tracker,
     147             :                          enum ftl_layout_region_type reg_type,
     148             :                          uint32_t reg_ver)
     149             : {
     150         392 :         return layout_region_find_from(tracker, reg_type, reg_ver, TAILQ_FIRST(&tracker->layout_head));
     151             : }
     152             : 
     153             : static struct layout_tracker_entry *
     154          24 : layout_region_find_next(struct ftl_layout_tracker_bdev *tracker,
     155             :                         enum ftl_layout_region_type reg_type,
     156             :                         uint32_t reg_ver, struct layout_tracker_entry *entry)
     157             : {
     158          24 :         if ((entry = TAILQ_NEXT(entry, layout_entry))) {
     159          22 :                 return layout_region_find_from(tracker, reg_type, reg_ver, entry);
     160             :         }
     161           2 :         return NULL;
     162             : }
     163             : 
     164             : const struct ftl_layout_tracker_bdev_region_props *
     165          32 : ftl_layout_tracker_bdev_add_region(struct ftl_layout_tracker_bdev *tracker,
     166             :                                    enum ftl_layout_region_type reg_type, uint32_t reg_ver, uint64_t blk_sz, uint64_t blk_align)
     167             : {
     168             :         struct layout_tracker_entry *entry_free;
     169             :         struct layout_tracker_entry *entry_new;
     170             :         uint64_t entry_free_blks_left;
     171             : 
     172          32 :         assert(tracker);
     173          32 :         assert(reg_type < FTL_LAYOUT_REGION_TYPE_MAX);
     174             : 
     175          32 :         entry_new = layout_region_find_first(tracker, reg_type, reg_ver);
     176          32 :         if (entry_new) {
     177             :                 /* Region already exists */
     178           0 :                 return NULL;
     179             :         }
     180             : 
     181          32 :         entry_free = layout_region_find_min_free(tracker, blk_sz, blk_align);
     182          32 :         if (!entry_free) {
     183             :                 /* No free space */
     184           0 :                 return NULL;
     185             :         }
     186             : 
     187             :         /* Takce care of the alignment */
     188          32 :         if (blk_align) {
     189             :                 /* Calculate the aligned region's offs and size */
     190          32 :                 uint64_t align_offs = SPDK_ALIGN_CEIL(entry_free->reg.blk_offs, blk_align);
     191          32 :                 assert(align_offs >= entry_free->reg.blk_offs);
     192             : 
     193             :                 /* Subdivide the free region in two: unaligned free region, followed by the aligned free region */
     194          32 :                 if (align_offs > entry_free->reg.blk_offs) {
     195           0 :                         uint64_t unaligned_sz = align_offs - entry_free->reg.blk_offs;
     196             : 
     197             :                         /* Setup the unaligned region */
     198           0 :                         entry_new = calloc(1, sizeof(*entry_new));
     199           0 :                         if (!entry_new) {
     200           0 :                                 return NULL;
     201             :                         }
     202           0 :                         entry_new->reg = entry_free->reg;
     203           0 :                         entry_new->reg.blk_sz = unaligned_sz;
     204             : 
     205             :                         /* Setup the aligned region - shrink the free region found */
     206           0 :                         entry_free->reg.blk_offs = align_offs;
     207           0 :                         entry_free->reg.blk_sz -= unaligned_sz;
     208             : 
     209             :                         /* Add the unaligned region prev to the aligned one */
     210           0 :                         TAILQ_INSERT_BEFORE(entry_free, entry_new, layout_entry);
     211           0 :                         tracker->regs_cnt++;
     212             :                 }
     213             :         }
     214             : 
     215          32 :         entry_free_blks_left = entry_free->reg.blk_sz - blk_sz;
     216             : 
     217          32 :         if (entry_free_blks_left) {
     218             :                 /* Subdivide the free region */
     219          32 :                 entry_new = calloc(1, sizeof(*entry_new));
     220          32 :                 if (!entry_new) {
     221           0 :                         return NULL;
     222             :                 }
     223             : 
     224             :                 /* Setup the new region at the beginning of the free region found */
     225          32 :                 entry_new->reg.type = reg_type;
     226          32 :                 entry_new->reg.ver = reg_ver;
     227          32 :                 entry_new->reg.blk_offs = entry_free->reg.blk_offs;
     228          32 :                 entry_new->reg.blk_sz = blk_sz;
     229             : 
     230             :                 /* Shrink the free region found */
     231          32 :                 entry_free->reg.blk_offs += blk_sz;
     232          32 :                 entry_free->reg.blk_sz = entry_free_blks_left;
     233             : 
     234             :                 /* Add the new region */
     235          32 :                 TAILQ_INSERT_BEFORE(entry_free, entry_new, layout_entry);
     236          32 :                 tracker->regs_cnt++;
     237             :         } else {
     238             :                 /* Setup the new region in place */
     239           0 :                 entry_new = entry_free;
     240           0 :                 entry_new->reg.type = reg_type;
     241           0 :                 entry_new->reg.ver = reg_ver;
     242             :         }
     243             : 
     244          32 :         return &entry_new->reg;
     245             : }
     246             : 
     247             : const struct ftl_layout_tracker_bdev_region_props *
     248         164 : ftl_layout_tracker_bdev_insert_region(struct ftl_layout_tracker_bdev *tracker,
     249             :                                       enum ftl_layout_region_type reg_type, uint32_t reg_ver,
     250             :                                       uint64_t blk_offs, uint64_t blk_sz)
     251             : {
     252             :         struct layout_tracker_entry *entry_free;
     253             :         struct layout_tracker_entry *entry_new;
     254             :         uint64_t entry_free_blks_left;
     255             : 
     256         164 :         assert(tracker);
     257             : 
     258         164 :         if (reg_type >= FTL_LAYOUT_REGION_TYPE_MAX && reg_type != FTL_LAYOUT_REGION_TYPE_INVALID) {
     259             :                 /* Invalid region type */
     260           2 :                 return NULL;
     261             :         }
     262             : 
     263         162 :         entry_new = layout_region_find_first(tracker, reg_type, reg_ver);
     264         162 :         if (entry_new) {
     265             :                 /* Region already exists */
     266           1 :                 return NULL;
     267             :         }
     268             : 
     269             :         /* Look up for the free region corresponding to the blk_offs */
     270         164 :         for (entry_free = layout_region_find_first(tracker, FTL_LAYOUT_REGION_TYPE_FREE, REG_VER_ANY);
     271             :              entry_free;
     272           3 :              entry_free = layout_region_find_next(tracker, FTL_LAYOUT_REGION_TYPE_FREE, REG_VER_ANY,
     273             :                              entry_free)) {
     274             :                 /* Test if the region being added fits into the free region */
     275         162 :                 if (entry_free->reg.blk_offs <= blk_offs
     276         160 :                     && blk_offs + blk_sz <= entry_free->reg.blk_offs + entry_free->reg.blk_sz) {
     277         159 :                         break;
     278             :                 }
     279             :         }
     280             : 
     281         161 :         if (!entry_free) {
     282             :                 /* Did not found the corresponding free region */
     283           2 :                 return NULL;
     284             :         }
     285             : 
     286         159 :         entry_free_blks_left = blk_offs - entry_free->reg.blk_offs;
     287         159 :         if (entry_free_blks_left) {
     288             :                 /* Subdivide the free region */
     289           6 :                 entry_new = calloc(1, sizeof(*entry_new));
     290           6 :                 if (!entry_new) {
     291           0 :                         return NULL;
     292             :                 }
     293             : 
     294             :                 /* Setup another free region at the beginning of the free region found */
     295           6 :                 entry_new->reg.type = FTL_LAYOUT_REGION_TYPE_FREE;
     296           6 :                 entry_new->reg.blk_offs = entry_free->reg.blk_offs;
     297           6 :                 entry_new->reg.blk_sz = entry_free_blks_left;
     298             : 
     299             :                 /* Shrink the free region found */
     300           6 :                 entry_free->reg.blk_offs += entry_free_blks_left;
     301           6 :                 assert(entry_free->reg.blk_sz > entry_free_blks_left);
     302           6 :                 entry_free->reg.blk_sz -= entry_free_blks_left;
     303             : 
     304             :                 /* Add the new free region */
     305           6 :                 TAILQ_INSERT_BEFORE(entry_free, entry_new, layout_entry);
     306           6 :                 tracker->regs_cnt++;
     307             :         }
     308             : 
     309         159 :         assert(entry_free->reg.blk_offs == blk_offs);
     310         159 :         assert(blk_sz <= entry_free->reg.blk_sz);
     311             : 
     312         159 :         entry_free_blks_left = entry_free->reg.blk_sz - blk_sz;
     313         159 :         if (entry_free_blks_left) {
     314             :                 /* Subdivide the free region */
     315         158 :                 entry_new = calloc(1, sizeof(*entry_new));
     316         158 :                 if (!entry_new) {
     317           0 :                         return NULL;
     318             :                 }
     319             : 
     320             :                 /* Setup the new region at the beginning of the free region found */
     321         158 :                 entry_new->reg.type = reg_type;
     322         158 :                 entry_new->reg.ver = reg_ver;
     323         158 :                 entry_new->reg.blk_offs = entry_free->reg.blk_offs;
     324         158 :                 entry_new->reg.blk_sz = blk_sz;
     325             : 
     326             :                 /* Shrink the free region found */
     327         158 :                 entry_free->reg.blk_offs += blk_sz;
     328         158 :                 entry_free->reg.blk_sz = entry_free_blks_left;
     329             : 
     330             :                 /* Add the new region */
     331         158 :                 TAILQ_INSERT_BEFORE(entry_free, entry_new, layout_entry);
     332         158 :                 tracker->regs_cnt++;
     333             :         } else {
     334             :                 /* Setup the new region in place */
     335           1 :                 entry_new = entry_free;
     336           1 :                 entry_new->reg.type = reg_type;
     337           1 :                 entry_new->reg.ver = reg_ver;
     338             :         }
     339             : 
     340         159 :         return &entry_new->reg;
     341             : }
     342             : 
     343             : int
     344           3 : ftl_layout_tracker_bdev_rm_region(struct ftl_layout_tracker_bdev *tracker,
     345             :                                   enum ftl_layout_region_type reg_type, uint32_t reg_ver)
     346             : {
     347             :         struct layout_tracker_entry *entry_rm, *entry_check __attribute__((unused));
     348           3 :         struct layout_tracker_entry *entry = layout_region_find_first(tracker, reg_type, reg_ver);
     349             : 
     350           3 :         if (!entry) {
     351           0 :                 return -1;
     352             :         }
     353             : 
     354             :         /* Free the region */
     355           3 :         entry->reg.type = FTL_LAYOUT_REGION_TYPE_FREE;
     356           3 :         entry->reg.ver = 0;
     357             : 
     358             :         /* Join with the adjacent free region prev to the current region */
     359           3 :         entry_rm = TAILQ_PREV(entry, layout_tracker, layout_entry);
     360           3 :         if (entry_rm && entry_rm->reg.type == FTL_LAYOUT_REGION_TYPE_FREE) {
     361           1 :                 TAILQ_REMOVE(&tracker->layout_head, entry_rm, layout_entry);
     362           1 :                 entry->reg.blk_offs = entry_rm->reg.blk_offs;
     363           1 :                 entry->reg.blk_sz += entry_rm->reg.blk_sz;
     364             : 
     365             : #if defined(DEBUG)
     366           1 :                 entry_check = TAILQ_PREV(entry, layout_tracker, layout_entry);
     367           1 :                 if (entry_check) {
     368           1 :                         assert(entry_check->reg.type != FTL_LAYOUT_REGION_TYPE_FREE);
     369             :                 }
     370             : #endif
     371             : 
     372           1 :                 free(entry_rm);
     373           1 :                 tracker->regs_cnt--;
     374             :         }
     375             : 
     376             :         /* Join with the adjacent free region next to the current region */
     377           3 :         entry_rm = TAILQ_NEXT(entry, layout_entry);
     378           3 :         if (entry_rm && entry_rm->reg.type == FTL_LAYOUT_REGION_TYPE_FREE) {
     379           1 :                 TAILQ_REMOVE(&tracker->layout_head, entry_rm, layout_entry);
     380           1 :                 entry->reg.blk_sz += entry_rm->reg.blk_sz;
     381             : 
     382             : #if defined(DEBUG)
     383           1 :                 entry_check = TAILQ_NEXT(entry, layout_entry);
     384           1 :                 if (entry_check) {
     385           0 :                         assert(entry_check->reg.type != FTL_LAYOUT_REGION_TYPE_FREE);
     386             :                 }
     387             : #endif
     388           1 :                 free(entry_rm);
     389           1 :                 tracker->regs_cnt--;
     390             :         }
     391             : 
     392           3 :         return 0;
     393             : }
     394             : 
     395             : void
     396          55 : ftl_layout_tracker_bdev_find_next_region(struct ftl_layout_tracker_bdev *tracker,
     397             :                 enum ftl_layout_region_type reg_type,
     398             :                 const struct ftl_layout_tracker_bdev_region_props **search_ctx)
     399             : {
     400             :         struct layout_tracker_entry *entry;
     401             : 
     402          55 :         if (!search_ctx) {
     403           0 :                 return;
     404             :         }
     405             : 
     406          55 :         if (*search_ctx == NULL) {
     407             :                 /* Return the first region found */
     408          34 :                 entry = layout_region_find_first(tracker, reg_type, REG_VER_ANY);
     409             :         } else {
     410             :                 /* Find the next region */
     411          21 :                 entry = SPDK_CONTAINEROF(*search_ctx, struct layout_tracker_entry, reg);
     412          21 :                 entry = layout_region_find_next(tracker, reg_type, REG_VER_ANY, entry);
     413             :         }
     414          55 :         *search_ctx = entry ? &entry->reg : NULL;
     415             : }
     416             : 
     417             : size_t
     418           4 : ftl_layout_tracker_bdev_blob_store(struct ftl_layout_tracker_bdev *tracker, void *blob_buf,
     419             :                                    size_t blob_buf_sz)
     420             : {
     421           4 :         struct layout_tracker_blob_entry *blob_entry = blob_buf;
     422             :         struct layout_tracker_entry *entry;
     423           4 :         size_t blob_sz = 0;
     424             : 
     425           4 :         assert(tracker);
     426             : 
     427          40 :         TAILQ_FOREACH(entry, &tracker->layout_head, layout_entry) {
     428          36 :                 if (blob_sz + sizeof(*blob_entry) > blob_buf_sz) {
     429             :                         /* Ran out of buf space */
     430           0 :                         assert(false);
     431             :                         return 0;
     432             :                 }
     433             : 
     434             :                 /* Skip the free space entries */
     435          36 :                 if (entry->reg.type == FTL_LAYOUT_REGION_TYPE_FREE) {
     436           4 :                         continue;
     437             :                 }
     438             : 
     439             :                 /* Store the entry */
     440          32 :                 blob_entry->type = entry->reg.type;
     441          32 :                 blob_entry->ver = entry->reg.ver;
     442          32 :                 blob_entry->blk_offs = entry->reg.blk_offs;
     443          32 :                 blob_entry->blk_sz = entry->reg.blk_sz;
     444             : 
     445             :                 /* Move to the next entry */
     446          32 :                 blob_entry++;
     447          32 :                 blob_sz += sizeof(*blob_entry);
     448             :         }
     449             : 
     450           4 :         return blob_sz;
     451             : }
     452             : 
     453             : int
     454          20 : ftl_layout_tracker_bdev_blob_load(struct ftl_layout_tracker_bdev *tracker, void *blob_buf,
     455             :                                   size_t blob_sz)
     456             : {
     457          20 :         struct layout_tracker_blob_entry *blob_entry = blob_buf;
     458          20 :         size_t blob_entry_num = blob_sz / sizeof(*blob_entry);
     459          20 :         struct layout_tracker_blob_entry *blob_entry_end = blob_entry + blob_entry_num;
     460             : 
     461          20 :         if (blob_sz % sizeof(*blob_entry) != 0) {
     462             :                 /* Invalid blob size */
     463           0 :                 return -1;
     464             :         }
     465             : 
     466             :         /* Free the current MD layout tracking info */
     467          20 :         layout_tracker_free_entries(tracker);
     468             : 
     469             :         /* Reinit MD layout tracking info */
     470          20 :         if (layout_tracker_init_entries(tracker, tracker->bdev_blks)) {
     471           0 :                 return -1;
     472             :         }
     473             : 
     474         177 :         for (; blob_entry < blob_entry_end; blob_entry++) {
     475             :                 /* Verify the type */
     476         162 :                 if (blob_entry->type == FTL_LAYOUT_REGION_TYPE_FREE) {
     477           0 :                         return -1;
     478             :                 }
     479             : 
     480             :                 /* Load the entry */
     481         162 :                 if (!ftl_layout_tracker_bdev_insert_region(tracker, blob_entry->type, blob_entry->ver,
     482             :                                 blob_entry->blk_offs, blob_entry->blk_sz)) {
     483           5 :                         return -1;
     484             :                 }
     485             :         }
     486             : 
     487          15 :         return 0;
     488             : }

Generated by: LCOV version 1.15