xref: /spdk/lib/ftl/mngt/ftl_mngt_bdev.c (revision 877573897ad52be4fa8989f7617bd655b87e05c4)
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