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: 184 214 86.0 %
Date: 2024-07-11 07:54:50 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         266 :         while ((entry = TAILQ_FIRST(&tracker->layout_head))) {
      81         242 :                 TAILQ_REMOVE(&tracker->layout_head, entry, layout_entry);
      82         242 :                 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          36 : layout_region_find_min_free(struct ftl_layout_tracker_bdev *tracker, uint64_t blk_sz,
      97             :                             uint64_t blk_align)
      98             : {
      99          36 :         struct layout_tracker_entry *min_free_entry = NULL;
     100             :         struct layout_tracker_entry *entry;
     101             : 
     102          36 :         assert(tracker);
     103             : 
     104         288 :         TAILQ_FOREACH(entry, &tracker->layout_head, layout_entry) {
     105             :                 uint64_t align_offs, align_sz;
     106             : 
     107         252 :                 if (entry->reg.type != FTL_LAYOUT_REGION_TYPE_FREE) {
     108         216 :                         continue;
     109             :                 }
     110             : 
     111          36 :                 align_offs = entry->reg.blk_offs;
     112          36 :                 align_sz = entry->reg.blk_sz;
     113          36 :                 if (blk_align) {
     114          36 :                         align_offs = SPDK_ALIGN_CEIL(align_offs, blk_align);
     115          36 :                         align_sz -= (align_offs - entry->reg.blk_offs);
     116             :                 }
     117             : 
     118          36 :                 if (align_sz >= blk_sz) {
     119          36 :                         if (!min_free_entry || min_free_entry->reg.blk_sz > entry->reg.blk_sz) {
     120          36 :                                 min_free_entry = entry;
     121             :                         }
     122             :                 }
     123             :         }
     124             : 
     125          36 :         return min_free_entry;
     126             : }
     127             : 
     128             : static struct layout_tracker_entry *
     129         458 : 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         458 :         assert(tracker);
     134             : 
     135        3618 :         TAILQ_FOREACH_FROM(entry, &tracker->layout_head, layout_entry) {
     136        3371 :                 if ((entry->reg.type == reg_type || reg_type == FTL_LAYOUT_REGION_TYPE_INVALID)
     137         218 :                     && (entry->reg.ver == reg_ver || reg_ver == REG_VER_ANY)) {
     138         211 :                         return entry;
     139             :                 }
     140             :         }
     141             : 
     142         247 :         return NULL;
     143             : }
     144             : 
     145             : static struct layout_tracker_entry *
     146         436 : 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         436 :         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          36 : 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          36 :         assert(tracker);
     173          36 :         assert(reg_type < FTL_LAYOUT_REGION_TYPE_MAX);
     174             : 
     175          36 :         entry_new = layout_region_find_first(tracker, reg_type, reg_ver);
     176          36 :         if (entry_new) {
     177             :                 /* Region already exists */
     178           0 :                 return NULL;
     179             :         }
     180             : 
     181          36 :         entry_free = layout_region_find_min_free(tracker, blk_sz, blk_align);
     182          36 :         if (!entry_free) {
     183             :                 /* No free space */
     184           0 :                 return NULL;
     185             :         }
     186             : 
     187             :         /* Takce care of the alignment */
     188          36 :         if (blk_align) {
     189             :                 /* Calculate the aligned region's offs and size */
     190          36 :                 uint64_t align_offs = SPDK_ALIGN_CEIL(entry_free->reg.blk_offs, blk_align);
     191          36 :                 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          36 :                 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          36 :         entry_free_blks_left = entry_free->reg.blk_sz - blk_sz;
     216             : 
     217          36 :         if (entry_free_blks_left) {
     218             :                 /* Subdivide the free region */
     219          36 :                 entry_new = calloc(1, sizeof(*entry_new));
     220          36 :                 if (!entry_new) {
     221           0 :                         return NULL;
     222             :                 }
     223             : 
     224             :                 /* Setup the new region at the beginning of the free region found */
     225          36 :                 entry_new->reg.type = reg_type;
     226          36 :                 entry_new->reg.ver = reg_ver;
     227          36 :                 entry_new->reg.blk_offs = entry_free->reg.blk_offs;
     228          36 :                 entry_new->reg.blk_sz = blk_sz;
     229             : 
     230             :                 /* Shrink the free region found */
     231          36 :                 entry_free->reg.blk_offs += blk_sz;
     232          36 :                 entry_free->reg.blk_sz = entry_free_blks_left;
     233             : 
     234             :                 /* Add the new region */
     235          36 :                 TAILQ_INSERT_BEFORE(entry_free, entry_new, layout_entry);
     236          36 :                 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          36 :         return &entry_new->reg;
     245             : }
     246             : 
     247             : const struct ftl_layout_tracker_bdev_region_props *
     248         184 : 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         184 :         assert(tracker);
     257             : 
     258         184 :         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         182 :         if (reg_type != FTL_LAYOUT_REGION_TYPE_INVALID) {
     264         182 :                 entry_new = layout_region_find_first(tracker, reg_type, reg_ver);
     265         182 :                 if (entry_new) {
     266             :                         /* Region already exists */
     267           1 :                         return NULL;
     268             :                 }
     269             :         }
     270             : 
     271             :         /* Look up for the free region corresponding to the blk_offs */
     272         184 :         for (entry_free = layout_region_find_first(tracker, FTL_LAYOUT_REGION_TYPE_FREE, REG_VER_ANY);
     273             :              entry_free;
     274           3 :              entry_free = layout_region_find_next(tracker, FTL_LAYOUT_REGION_TYPE_FREE, REG_VER_ANY,
     275             :                              entry_free)) {
     276             :                 /* Test if the region being added fits into the free region */
     277         182 :                 if (entry_free->reg.blk_offs <= blk_offs
     278         180 :                     && blk_offs + blk_sz <= entry_free->reg.blk_offs + entry_free->reg.blk_sz) {
     279         179 :                         break;
     280             :                 }
     281             :         }
     282             : 
     283         181 :         if (!entry_free) {
     284             :                 /* Did not found the corresponding free region */
     285           2 :                 return NULL;
     286             :         }
     287             : 
     288         179 :         if (reg_type == FTL_LAYOUT_REGION_TYPE_INVALID) {
     289             :                 /* Dry run */
     290           0 :                 return &entry_free->reg;
     291             :         }
     292             : 
     293         179 :         entry_free_blks_left = blk_offs - entry_free->reg.blk_offs;
     294         179 :         if (entry_free_blks_left) {
     295             :                 /* Subdivide the free region */
     296           6 :                 entry_new = calloc(1, sizeof(*entry_new));
     297           6 :                 if (!entry_new) {
     298           0 :                         return NULL;
     299             :                 }
     300             : 
     301             :                 /* Setup another free region at the beginning of the free region found */
     302           6 :                 entry_new->reg.type = FTL_LAYOUT_REGION_TYPE_FREE;
     303           6 :                 entry_new->reg.blk_offs = entry_free->reg.blk_offs;
     304           6 :                 entry_new->reg.blk_sz = entry_free_blks_left;
     305             : 
     306             :                 /* Shrink the free region found */
     307           6 :                 entry_free->reg.blk_offs += entry_free_blks_left;
     308           6 :                 assert(entry_free->reg.blk_sz > entry_free_blks_left);
     309           6 :                 entry_free->reg.blk_sz -= entry_free_blks_left;
     310             : 
     311             :                 /* Add the new free region */
     312           6 :                 TAILQ_INSERT_BEFORE(entry_free, entry_new, layout_entry);
     313           6 :                 tracker->regs_cnt++;
     314             :         }
     315             : 
     316         179 :         assert(entry_free->reg.blk_offs == blk_offs);
     317         179 :         assert(blk_sz <= entry_free->reg.blk_sz);
     318             : 
     319         179 :         entry_free_blks_left = entry_free->reg.blk_sz - blk_sz;
     320         179 :         if (entry_free_blks_left) {
     321             :                 /* Subdivide the free region */
     322         178 :                 entry_new = calloc(1, sizeof(*entry_new));
     323         178 :                 if (!entry_new) {
     324           0 :                         return NULL;
     325             :                 }
     326             : 
     327             :                 /* Setup the new region at the beginning of the free region found */
     328         178 :                 entry_new->reg.type = reg_type;
     329         178 :                 entry_new->reg.ver = reg_ver;
     330         178 :                 entry_new->reg.blk_offs = entry_free->reg.blk_offs;
     331         178 :                 entry_new->reg.blk_sz = blk_sz;
     332             : 
     333             :                 /* Shrink the free region found */
     334         178 :                 entry_free->reg.blk_offs += blk_sz;
     335         178 :                 entry_free->reg.blk_sz = entry_free_blks_left;
     336             : 
     337             :                 /* Add the new region */
     338         178 :                 TAILQ_INSERT_BEFORE(entry_free, entry_new, layout_entry);
     339         178 :                 tracker->regs_cnt++;
     340             :         } else {
     341             :                 /* Setup the new region in place */
     342           1 :                 entry_new = entry_free;
     343           1 :                 entry_new->reg.type = reg_type;
     344           1 :                 entry_new->reg.ver = reg_ver;
     345             :         }
     346             : 
     347         179 :         return &entry_new->reg;
     348             : }
     349             : 
     350             : int
     351           3 : ftl_layout_tracker_bdev_rm_region(struct ftl_layout_tracker_bdev *tracker,
     352             :                                   enum ftl_layout_region_type reg_type, uint32_t reg_ver)
     353             : {
     354             :         struct layout_tracker_entry *entry_rm, *entry_check __attribute__((unused));
     355           3 :         struct layout_tracker_entry *entry = layout_region_find_first(tracker, reg_type, reg_ver);
     356             : 
     357           3 :         if (!entry) {
     358           0 :                 return -1;
     359             :         }
     360             : 
     361             :         /* Free the region */
     362           3 :         entry->reg.type = FTL_LAYOUT_REGION_TYPE_FREE;
     363           3 :         entry->reg.ver = 0;
     364             : 
     365             :         /* Join with the adjacent free region prev to the current region */
     366           3 :         entry_rm = TAILQ_PREV(entry, layout_tracker, layout_entry);
     367           3 :         if (entry_rm && entry_rm->reg.type == FTL_LAYOUT_REGION_TYPE_FREE) {
     368           1 :                 TAILQ_REMOVE(&tracker->layout_head, entry_rm, layout_entry);
     369           1 :                 entry->reg.blk_offs = entry_rm->reg.blk_offs;
     370           1 :                 entry->reg.blk_sz += entry_rm->reg.blk_sz;
     371             : 
     372             : #if defined(DEBUG)
     373           1 :                 entry_check = TAILQ_PREV(entry, layout_tracker, layout_entry);
     374           1 :                 if (entry_check) {
     375           1 :                         assert(entry_check->reg.type != FTL_LAYOUT_REGION_TYPE_FREE);
     376             :                 }
     377             : #endif
     378             : 
     379           1 :                 free(entry_rm);
     380           1 :                 tracker->regs_cnt--;
     381             :         }
     382             : 
     383             :         /* Join with the adjacent free region next to the current region */
     384           3 :         entry_rm = TAILQ_NEXT(entry, layout_entry);
     385           3 :         if (entry_rm && entry_rm->reg.type == FTL_LAYOUT_REGION_TYPE_FREE) {
     386           1 :                 TAILQ_REMOVE(&tracker->layout_head, entry_rm, layout_entry);
     387           1 :                 entry->reg.blk_sz += entry_rm->reg.blk_sz;
     388             : 
     389             : #if defined(DEBUG)
     390           1 :                 entry_check = TAILQ_NEXT(entry, layout_entry);
     391           1 :                 if (entry_check) {
     392           0 :                         assert(entry_check->reg.type != FTL_LAYOUT_REGION_TYPE_FREE);
     393             :                 }
     394             : #endif
     395           1 :                 free(entry_rm);
     396           1 :                 tracker->regs_cnt--;
     397             :         }
     398             : 
     399           3 :         return 0;
     400             : }
     401             : 
     402             : void
     403          55 : ftl_layout_tracker_bdev_find_next_region(struct ftl_layout_tracker_bdev *tracker,
     404             :                 enum ftl_layout_region_type reg_type,
     405             :                 const struct ftl_layout_tracker_bdev_region_props **search_ctx)
     406             : {
     407             :         struct layout_tracker_entry *entry;
     408             : 
     409          55 :         if (!search_ctx) {
     410           0 :                 return;
     411             :         }
     412             : 
     413          55 :         if (*search_ctx == NULL) {
     414             :                 /* Return the first region found */
     415          34 :                 entry = layout_region_find_first(tracker, reg_type, REG_VER_ANY);
     416             :         } else {
     417             :                 /* Find the next region */
     418          21 :                 entry = SPDK_CONTAINEROF(*search_ctx, struct layout_tracker_entry, reg);
     419          21 :                 entry = layout_region_find_next(tracker, reg_type, REG_VER_ANY, entry);
     420             :         }
     421          55 :         *search_ctx = entry ? &entry->reg : NULL;
     422             : }
     423             : 
     424             : size_t
     425           4 : ftl_layout_tracker_bdev_blob_store(struct ftl_layout_tracker_bdev *tracker, void *blob_buf,
     426             :                                    size_t blob_buf_sz)
     427             : {
     428           4 :         struct layout_tracker_blob_entry *blob_entry = blob_buf;
     429             :         struct layout_tracker_entry *entry;
     430           4 :         size_t blob_sz = 0;
     431             : 
     432           4 :         assert(tracker);
     433             : 
     434          44 :         TAILQ_FOREACH(entry, &tracker->layout_head, layout_entry) {
     435          40 :                 if (blob_sz + sizeof(*blob_entry) > blob_buf_sz) {
     436             :                         /* Ran out of buf space */
     437           0 :                         assert(false);
     438             :                         return 0;
     439             :                 }
     440             : 
     441             :                 /* Skip the free space entries */
     442          40 :                 if (entry->reg.type == FTL_LAYOUT_REGION_TYPE_FREE) {
     443           4 :                         continue;
     444             :                 }
     445             : 
     446             :                 /* Store the entry */
     447          36 :                 blob_entry->type = entry->reg.type;
     448          36 :                 blob_entry->ver = entry->reg.ver;
     449          36 :                 blob_entry->blk_offs = entry->reg.blk_offs;
     450          36 :                 blob_entry->blk_sz = entry->reg.blk_sz;
     451             : 
     452             :                 /* Move to the next entry */
     453          36 :                 blob_entry++;
     454          36 :                 blob_sz += sizeof(*blob_entry);
     455             :         }
     456             : 
     457           4 :         return blob_sz;
     458             : }
     459             : 
     460             : int
     461          20 : ftl_layout_tracker_bdev_blob_load(struct ftl_layout_tracker_bdev *tracker, void *blob_buf,
     462             :                                   size_t blob_sz)
     463             : {
     464          20 :         struct layout_tracker_blob_entry *blob_entry = blob_buf;
     465          20 :         size_t blob_entry_num = blob_sz / sizeof(*blob_entry);
     466          20 :         struct layout_tracker_blob_entry *blob_entry_end = blob_entry + blob_entry_num;
     467             : 
     468          20 :         if (blob_sz % sizeof(*blob_entry) != 0) {
     469             :                 /* Invalid blob size */
     470           0 :                 return -1;
     471             :         }
     472             : 
     473             :         /* Free the current MD layout tracking info */
     474          20 :         layout_tracker_free_entries(tracker);
     475             : 
     476             :         /* Reinit MD layout tracking info */
     477          20 :         if (layout_tracker_init_entries(tracker, tracker->bdev_blks)) {
     478           0 :                 return -1;
     479             :         }
     480             : 
     481         197 :         for (; blob_entry < blob_entry_end; blob_entry++) {
     482             :                 /* Verify the type */
     483         182 :                 if (blob_entry->type == FTL_LAYOUT_REGION_TYPE_FREE) {
     484           0 :                         return -1;
     485             :                 }
     486             : 
     487             :                 /* Load the entry */
     488         182 :                 if (!ftl_layout_tracker_bdev_insert_region(tracker, blob_entry->type, blob_entry->ver,
     489             :                                 blob_entry->blk_offs, blob_entry->blk_sz)) {
     490           5 :                         return -1;
     491             :                 }
     492             :         }
     493             : 
     494          15 :         return 0;
     495             : }

Generated by: LCOV version 1.15