1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (c) Intel Corporation. 3 * All rights reserved. 4 */ 5 6 #include "spdk/bdev_module.h" 7 #include "spdk/ftl.h" 8 9 #include "ftl_nv_cache.h" 10 #include "ftl_internal.h" 11 #include "ftl_mngt_steps.h" 12 #include "ftl_internal.h" 13 #include "ftl_core.h" 14 #include "utils/ftl_defs.h" 15 16 #define MINIMUM_CACHE_SIZE_GIB 5 17 #define MINIMUM_BASE_SIZE_GIB 20 18 19 /* Dummy bdev module used to to claim bdevs. */ 20 static struct spdk_bdev_module g_ftl_bdev_module = { 21 .name = "ftl_lib", 22 }; 23 24 static inline size_t 25 ftl_calculate_num_zones_in_band(struct spdk_bdev_desc *desc) 26 { 27 if (spdk_bdev_is_zoned(spdk_bdev_desc_get_bdev(desc))) { 28 return spdk_bdev_get_optimal_open_zones(spdk_bdev_desc_get_bdev(desc)); 29 } 30 31 return 1; 32 } 33 34 static inline size_t 35 ftl_calculate_num_blocks_in_zone(struct spdk_bdev_desc *desc) 36 { 37 if (spdk_bdev_is_zoned(spdk_bdev_desc_get_bdev(desc))) { 38 return spdk_bdev_get_zone_size(spdk_bdev_desc_get_bdev(desc)); 39 } 40 41 /* TODO: this should be passed via input parameter */ 42 #ifdef SPDK_FTL_ZONE_EMU_BLOCKS 43 return SPDK_FTL_ZONE_EMU_BLOCKS; 44 #else 45 return (1ULL << 30) / FTL_BLOCK_SIZE; 46 #endif 47 } 48 49 static inline uint64_t 50 ftl_calculate_num_blocks_in_band(struct spdk_bdev_desc *desc) 51 { 52 return ftl_calculate_num_zones_in_band(desc) * ftl_calculate_num_blocks_in_zone(desc); 53 } 54 55 static void 56 base_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, void *event_ctx) 57 { 58 switch (type) { 59 case SPDK_BDEV_EVENT_REMOVE: 60 assert(0); 61 break; 62 default: 63 break; 64 } 65 } 66 67 void 68 ftl_mngt_open_base_bdev(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 69 { 70 uint32_t block_size; 71 uint64_t num_blocks; 72 const char *bdev_name = dev->conf.base_bdev; 73 struct spdk_bdev *bdev; 74 75 if (spdk_bdev_open_ext(bdev_name, true, base_bdev_event_cb, 76 dev, &dev->base_bdev_desc)) { 77 FTL_ERRLOG(dev, "Unable to open bdev: %s\n", bdev_name); 78 goto error; 79 } 80 81 bdev = spdk_bdev_desc_get_bdev(dev->base_bdev_desc); 82 83 if (spdk_bdev_module_claim_bdev(bdev, dev->base_bdev_desc, &g_ftl_bdev_module)) { 84 /* clear the desc so that we don't try to release the claim on cleanup */ 85 spdk_bdev_close(dev->base_bdev_desc); 86 dev->base_bdev_desc = NULL; 87 FTL_ERRLOG(dev, "Unable to claim bdev %s\n", bdev_name); 88 goto error; 89 } 90 91 block_size = spdk_bdev_get_block_size(bdev); 92 if (block_size != FTL_BLOCK_SIZE) { 93 FTL_ERRLOG(dev, "Unsupported block size (%"PRIu32")\n", block_size); 94 goto error; 95 } 96 97 num_blocks = spdk_bdev_get_num_blocks(bdev); 98 99 if (num_blocks * block_size < MINIMUM_BASE_SIZE_GIB * GiB) { 100 FTL_ERRLOG(dev, "Bdev %s is too small, requires, at least %uGiB capacity\n", 101 spdk_bdev_get_name(bdev), MINIMUM_BASE_SIZE_GIB); 102 goto error; 103 } 104 105 dev->base_ioch = spdk_bdev_get_io_channel(dev->base_bdev_desc); 106 if (!dev->base_ioch) { 107 FTL_ERRLOG(dev, "Failed to create base bdev IO channel\n"); 108 goto error; 109 } 110 111 dev->xfer_size = ftl_get_write_unit_size(bdev); 112 113 /* TODO: validate size when base device VSS usage gets added */ 114 dev->md_size = spdk_bdev_get_md_size(bdev); 115 116 /* Cache frequently used values */ 117 dev->num_blocks_in_band = ftl_calculate_num_blocks_in_band(dev->base_bdev_desc); 118 dev->num_zones_in_band = ftl_calculate_num_zones_in_band(dev->base_bdev_desc); 119 dev->num_blocks_in_zone = ftl_calculate_num_blocks_in_zone(dev->base_bdev_desc); 120 dev->is_zoned = spdk_bdev_is_zoned(spdk_bdev_desc_get_bdev(dev->base_bdev_desc)); 121 122 if (dev->is_zoned) { 123 /* TODO - current FTL code isn't fully compatible with ZNS drives */ 124 FTL_ERRLOG(dev, "Creating FTL on Zoned devices is not supported\n"); 125 goto error; 126 } 127 128 dev->num_bands = num_blocks / ftl_get_num_blocks_in_band(dev); 129 130 /* Save a band worth of space for metadata */ 131 dev->num_bands--; 132 133 ftl_mngt_next_step(mngt); 134 return; 135 error: 136 ftl_mngt_fail_step(mngt); 137 } 138 139 void 140 ftl_mngt_close_base_bdev(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 141 { 142 if (dev->base_ioch) { 143 spdk_put_io_channel(dev->base_ioch); 144 dev->base_ioch = NULL; 145 } 146 147 if (dev->base_bdev_desc) { 148 struct spdk_bdev *bdev = spdk_bdev_desc_get_bdev(dev->base_bdev_desc); 149 150 spdk_bdev_module_release_bdev(bdev); 151 spdk_bdev_close(dev->base_bdev_desc); 152 153 dev->base_bdev_desc = NULL; 154 } 155 156 ftl_mngt_next_step(mngt); 157 } 158 159 static void 160 nv_cache_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, void *event_ctx) 161 { 162 switch (type) { 163 case SPDK_BDEV_EVENT_REMOVE: 164 assert(0); 165 break; 166 default: 167 break; 168 } 169 } 170 171 void 172 ftl_mngt_open_cache_bdev(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 173 { 174 struct spdk_bdev *bdev; 175 struct ftl_nv_cache *nv_cache = &dev->nv_cache; 176 const char *bdev_name = dev->conf.cache_bdev; 177 178 if (spdk_bdev_open_ext(bdev_name, true, nv_cache_bdev_event_cb, dev, 179 &nv_cache->bdev_desc)) { 180 FTL_ERRLOG(dev, "Unable to open bdev: %s\n", bdev_name); 181 goto error; 182 } 183 184 bdev = spdk_bdev_desc_get_bdev(nv_cache->bdev_desc); 185 186 if (spdk_bdev_module_claim_bdev(bdev, nv_cache->bdev_desc, &g_ftl_bdev_module)) { 187 /* clear the desc so that we don't try to release the claim on cleanup */ 188 spdk_bdev_close(nv_cache->bdev_desc); 189 nv_cache->bdev_desc = NULL; 190 FTL_ERRLOG(dev, "Unable to claim bdev %s\n", bdev_name); 191 goto error; 192 } 193 194 FTL_NOTICELOG(dev, "Using %s as write buffer cache\n", spdk_bdev_get_name(bdev)); 195 196 if (spdk_bdev_get_block_size(bdev) != FTL_BLOCK_SIZE) { 197 FTL_ERRLOG(dev, "Unsupported block size (%d)\n", 198 spdk_bdev_get_block_size(bdev)); 199 goto error; 200 } 201 202 nv_cache->cache_ioch = spdk_bdev_get_io_channel(nv_cache->bdev_desc); 203 if (!nv_cache->cache_ioch) { 204 FTL_ERRLOG(dev, "Failed to create cache IO channel for NV Cache\n"); 205 goto error; 206 } 207 208 #ifndef SPDK_FTL_VSS_EMU 209 if (!spdk_bdev_is_md_separate(bdev)) { 210 FTL_ERRLOG(dev, "Bdev %s doesn't support separate metadata buffer IO\n", 211 spdk_bdev_get_name(bdev)); 212 goto error; 213 } 214 215 nv_cache->md_size = spdk_bdev_get_md_size(bdev); 216 if (nv_cache->md_size != sizeof(union ftl_md_vss)) { 217 FTL_ERRLOG(dev, "Bdev's %s metadata is invalid size (%"PRIu32")\n", 218 spdk_bdev_get_name(bdev), spdk_bdev_get_md_size(bdev)); 219 goto error; 220 } 221 222 if (spdk_bdev_get_dif_type(bdev) != SPDK_DIF_DISABLE) { 223 FTL_ERRLOG(dev, "Unsupported DIF type used by bdev %s\n", 224 spdk_bdev_get_name(bdev)); 225 goto error; 226 } 227 228 if (bdev->blockcnt * bdev->blocklen < MINIMUM_CACHE_SIZE_GIB * GiB) { 229 FTL_ERRLOG(dev, "Bdev %s is too small, requires, at least %uGiB capacity\n", 230 spdk_bdev_get_name(bdev), MINIMUM_CACHE_SIZE_GIB); 231 goto error; 232 } 233 234 if (ftl_md_xfer_blocks(dev) * nv_cache->md_size > FTL_ZERO_BUFFER_SIZE) { 235 FTL_ERRLOG(dev, "Zero buffer too small for bdev %s metadata transfer\n", 236 spdk_bdev_get_name(bdev)); 237 goto error; 238 } 239 #else 240 if (spdk_bdev_is_md_separate(bdev)) { 241 FTL_ERRLOG(dev, "FTL VSS emulation but NV cache supports VSS\n"); 242 goto error; 243 } 244 245 nv_cache->md_size = 64; 246 FTL_NOTICELOG(dev, "FTL uses VSS emulation\n"); 247 #endif 248 249 ftl_mngt_next_step(mngt); 250 return; 251 error: 252 ftl_mngt_fail_step(mngt); 253 } 254 255 void 256 ftl_mngt_close_cache_bdev(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 257 { 258 if (dev->nv_cache.cache_ioch) { 259 spdk_put_io_channel(dev->nv_cache.cache_ioch); 260 dev->nv_cache.cache_ioch = NULL; 261 } 262 263 if (dev->nv_cache.bdev_desc) { 264 struct spdk_bdev *bdev = spdk_bdev_desc_get_bdev(dev->nv_cache.bdev_desc); 265 266 spdk_bdev_module_release_bdev(bdev); 267 spdk_bdev_close(dev->nv_cache.bdev_desc); 268 269 dev->nv_cache.bdev_desc = NULL; 270 } 271 272 ftl_mngt_next_step(mngt); 273 } 274