1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright 2023 Solidigm All Rights Reserved 3 * Copyright (C) 2022 Intel Corporation. 4 * All rights reserved. 5 */ 6 7 #include "spdk/bdev_module.h" 8 #include "spdk/ftl.h" 9 10 #include "ftl_nv_cache.h" 11 #include "ftl_internal.h" 12 #include "ftl_mngt_steps.h" 13 #include "ftl_core.h" 14 #include "utils/ftl_defs.h" 15 #include "utils/ftl_layout_tracker_bdev.h" 16 17 #define MINIMUM_CACHE_SIZE_GIB 5 18 #define MINIMUM_BASE_SIZE_GIB 20 19 20 /* Dummy bdev module used to to claim bdevs. */ 21 static struct spdk_bdev_module g_ftl_bdev_module = { 22 .name = "ftl_lib", 23 }; 24 25 static inline uint64_t 26 ftl_calculate_num_blocks_in_band(struct spdk_bdev_desc *desc) 27 { 28 /* TODO: this should be passed via input parameter */ 29 #ifdef SPDK_FTL_ZONE_EMU_BLOCKS 30 return SPDK_FTL_ZONE_EMU_BLOCKS; 31 #else 32 return (1ULL << 30) / FTL_BLOCK_SIZE; 33 #endif 34 } 35 36 static void 37 base_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, void *event_ctx) 38 { 39 switch (type) { 40 case SPDK_BDEV_EVENT_REMOVE: 41 assert(0); 42 break; 43 default: 44 break; 45 } 46 } 47 48 void 49 ftl_mngt_open_base_bdev(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 50 { 51 uint32_t block_size; 52 uint64_t num_blocks; 53 const char *bdev_name = dev->conf.base_bdev; 54 struct spdk_bdev *bdev; 55 56 if (spdk_bdev_open_ext(bdev_name, true, base_bdev_event_cb, 57 dev, &dev->base_bdev_desc)) { 58 FTL_ERRLOG(dev, "Unable to open bdev: %s\n", bdev_name); 59 goto error; 60 } 61 62 bdev = spdk_bdev_desc_get_bdev(dev->base_bdev_desc); 63 64 if (spdk_bdev_module_claim_bdev(bdev, dev->base_bdev_desc, &g_ftl_bdev_module)) { 65 /* clear the desc so that we don't try to release the claim on cleanup */ 66 spdk_bdev_close(dev->base_bdev_desc); 67 dev->base_bdev_desc = NULL; 68 FTL_ERRLOG(dev, "Unable to claim bdev %s\n", bdev_name); 69 goto error; 70 } 71 72 block_size = spdk_bdev_get_block_size(bdev); 73 if (block_size != FTL_BLOCK_SIZE) { 74 FTL_ERRLOG(dev, "Unsupported block size (%"PRIu32")\n", block_size); 75 goto error; 76 } 77 78 num_blocks = spdk_bdev_get_num_blocks(bdev); 79 80 if (num_blocks * block_size < MINIMUM_BASE_SIZE_GIB * GiB) { 81 FTL_ERRLOG(dev, "Bdev %s is too small, requires, at least %uGiB capacity\n", 82 spdk_bdev_get_name(bdev), MINIMUM_BASE_SIZE_GIB); 83 goto error; 84 } 85 86 dev->base_ioch = spdk_bdev_get_io_channel(dev->base_bdev_desc); 87 if (!dev->base_ioch) { 88 FTL_ERRLOG(dev, "Failed to create base bdev IO channel\n"); 89 goto error; 90 } 91 92 dev->xfer_size = ftl_get_write_unit_size(bdev); 93 if (dev->xfer_size != FTL_NUM_LBA_IN_BLOCK) { 94 FTL_ERRLOG(dev, "Unsupported xfer_size (%"PRIu64")\n", dev->xfer_size); 95 goto error; 96 } 97 98 dev->base_type = ftl_base_device_get_type_by_bdev(dev, bdev); 99 if (!dev->base_type) { 100 FTL_ERRLOG(dev, "Failed to get base device type\n"); 101 goto error; 102 } 103 /* TODO: validate size when base device VSS usage gets added */ 104 dev->md_size = spdk_bdev_get_md_size(bdev); 105 106 if (!dev->base_type->ops.md_layout_ops.region_create) { 107 FTL_ERRLOG(dev, "Base device doesn't implement md_layout_ops\n"); 108 goto error; 109 } 110 111 /* Cache frequently used values */ 112 dev->num_blocks_in_band = ftl_calculate_num_blocks_in_band(dev->base_bdev_desc); 113 dev->is_zoned = spdk_bdev_is_zoned(spdk_bdev_desc_get_bdev(dev->base_bdev_desc)); 114 115 if (dev->is_zoned) { 116 /* TODO - current FTL code isn't fully compatible with ZNS drives */ 117 FTL_ERRLOG(dev, "Creating FTL on Zoned devices is not supported\n"); 118 goto error; 119 } 120 121 dev->base_layout_tracker = ftl_layout_tracker_bdev_init(spdk_bdev_get_num_blocks(bdev)); 122 if (!dev->base_layout_tracker) { 123 FTL_ERRLOG(dev, "Failed to instantiate layout tracker for base device\n"); 124 goto error; 125 } 126 127 ftl_mngt_next_step(mngt); 128 return; 129 error: 130 ftl_mngt_fail_step(mngt); 131 } 132 133 void 134 ftl_mngt_close_base_bdev(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 135 { 136 if (dev->base_ioch) { 137 spdk_put_io_channel(dev->base_ioch); 138 dev->base_ioch = NULL; 139 } 140 141 if (dev->base_bdev_desc) { 142 struct spdk_bdev *bdev = spdk_bdev_desc_get_bdev(dev->base_bdev_desc); 143 144 spdk_bdev_module_release_bdev(bdev); 145 spdk_bdev_close(dev->base_bdev_desc); 146 147 dev->base_bdev_desc = NULL; 148 } 149 150 if (dev->base_layout_tracker) { 151 ftl_layout_tracker_bdev_fini(dev->base_layout_tracker); 152 dev->base_layout_tracker = 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 struct ftl_nv_cache *nv_cache = &dev->nv_cache; 175 const char *bdev_name = dev->conf.cache_bdev; 176 const struct ftl_md_layout_ops *md_ops; 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 if (bdev->blockcnt * bdev->blocklen < MINIMUM_CACHE_SIZE_GIB * GiB) { 209 FTL_ERRLOG(dev, "Bdev %s is too small, requires, at least %uGiB capacity\n", 210 spdk_bdev_get_name(bdev), MINIMUM_CACHE_SIZE_GIB); 211 goto error; 212 } 213 nv_cache->md_size = spdk_bdev_get_md_size(bdev); 214 215 /* Get FTL NVC bdev descriptor */ 216 nv_cache->nvc_desc = ftl_nv_cache_device_get_desc_by_bdev(dev, bdev); 217 if (!nv_cache->nvc_desc) { 218 FTL_ERRLOG(dev, "Failed to get NV Cache device descriptor\n"); 219 goto error; 220 } 221 nv_cache->md_size = sizeof(union ftl_md_vss); 222 223 md_ops = &nv_cache->nvc_desc->ops.md_layout_ops; 224 if (!md_ops->region_create) { 225 FTL_ERRLOG(dev, "NV Cache device doesn't implement md_layout_ops\n"); 226 goto error; 227 } 228 229 dev->nvc_layout_tracker = ftl_layout_tracker_bdev_init(spdk_bdev_get_num_blocks(bdev)); 230 if (!dev->nvc_layout_tracker) { 231 FTL_ERRLOG(dev, "Failed to instantiate layout tracker for nvc device\n"); 232 goto error; 233 } 234 235 FTL_NOTICELOG(dev, "Using %s as NV Cache device\n", nv_cache->nvc_desc->name); 236 ftl_mngt_next_step(mngt); 237 return; 238 error: 239 ftl_mngt_fail_step(mngt); 240 } 241 242 void 243 ftl_mngt_close_cache_bdev(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 244 { 245 if (dev->nv_cache.cache_ioch) { 246 spdk_put_io_channel(dev->nv_cache.cache_ioch); 247 dev->nv_cache.cache_ioch = NULL; 248 } 249 250 if (dev->nv_cache.bdev_desc) { 251 struct spdk_bdev *bdev = spdk_bdev_desc_get_bdev(dev->nv_cache.bdev_desc); 252 253 spdk_bdev_module_release_bdev(bdev); 254 spdk_bdev_close(dev->nv_cache.bdev_desc); 255 256 dev->nv_cache.bdev_desc = NULL; 257 } 258 259 if (dev->nvc_layout_tracker) { 260 ftl_layout_tracker_bdev_fini(dev->nvc_layout_tracker); 261 dev->nvc_layout_tracker = NULL; 262 } 263 264 ftl_mngt_next_step(mngt); 265 } 266