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