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