]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /*- |
2 | * BSD LICENSE | |
3 | * | |
f67539c2 TL |
4 | * Copyright (c) Intel Corporation. All rights reserved. |
5 | * Copyright (c) 2020 Mellanox Technologies LTD. All rights reserved. | |
7c673cae FG |
6 | * |
7 | * Redistribution and use in source and binary forms, with or without | |
8 | * modification, are permitted provided that the following conditions | |
9 | * are met: | |
10 | * | |
11 | * * Redistributions of source code must retain the above copyright | |
12 | * notice, this list of conditions and the following disclaimer. | |
13 | * * Redistributions in binary form must reproduce the above copyright | |
14 | * notice, this list of conditions and the following disclaimer in | |
15 | * the documentation and/or other materials provided with the | |
16 | * distribution. | |
17 | * * Neither the name of Intel Corporation nor the names of its | |
18 | * contributors may be used to endorse or promote products derived | |
19 | * from this software without specific prior written permission. | |
20 | * | |
21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
24 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
25 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
26 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
27 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
28 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
29 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
30 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
31 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
32 | */ | |
33 | ||
34 | #include "nvme_internal.h" | |
35 | ||
36 | static inline struct spdk_nvme_ns_data * | |
37 | _nvme_ns_get_data(struct spdk_nvme_ns *ns) | |
38 | { | |
39 | return &ns->ctrlr->nsdata[ns->id - 1]; | |
40 | } | |
41 | ||
11fdf7f2 TL |
42 | /** |
43 | * Update Namespace flags based on Identify Controller | |
44 | * and Identify Namespace. This can be also used for | |
45 | * Namespace Attribute Notice events and Namespace | |
46 | * operations such as Attach/Detach. | |
47 | */ | |
48 | void | |
49 | nvme_ns_set_identify_data(struct spdk_nvme_ns *ns) | |
7c673cae | 50 | { |
11fdf7f2 | 51 | struct spdk_nvme_ns_data *nsdata; |
7c673cae FG |
52 | |
53 | nsdata = _nvme_ns_get_data(ns); | |
7c673cae FG |
54 | |
55 | ns->flags = 0x0000; | |
56 | ||
57 | ns->sector_size = 1 << nsdata->lbaf[nsdata->flbas.format].lbads; | |
58 | ns->extended_lba_size = ns->sector_size; | |
59 | ||
60 | ns->md_size = nsdata->lbaf[nsdata->flbas.format].ms; | |
61 | if (nsdata->flbas.extended) { | |
62 | ns->flags |= SPDK_NVME_NS_EXTENDED_LBA_SUPPORTED; | |
63 | ns->extended_lba_size += ns->md_size; | |
64 | } | |
65 | ||
66 | ns->sectors_per_max_io = spdk_nvme_ns_get_max_io_xfer_size(ns) / ns->extended_lba_size; | |
11fdf7f2 TL |
67 | |
68 | if (nsdata->noiob) { | |
69 | ns->sectors_per_stripe = nsdata->noiob; | |
70 | SPDK_DEBUGLOG(SPDK_LOG_NVME, "ns %u optimal IO boundary %" PRIu32 " blocks\n", | |
71 | ns->id, ns->sectors_per_stripe); | |
72 | } else if (ns->ctrlr->quirks & NVME_INTEL_QUIRK_STRIPING && | |
73 | ns->ctrlr->cdata.vs[3] != 0) { | |
74 | ns->sectors_per_stripe = (1ULL << ns->ctrlr->cdata.vs[3]) * ns->ctrlr->min_page_size / | |
75 | ns->sector_size; | |
76 | SPDK_DEBUGLOG(SPDK_LOG_NVME, "ns %u stripe size quirk %" PRIu32 " blocks\n", | |
77 | ns->id, ns->sectors_per_stripe); | |
78 | } else { | |
79 | ns->sectors_per_stripe = 0; | |
80 | } | |
7c673cae FG |
81 | |
82 | if (ns->ctrlr->cdata.oncs.dsm) { | |
83 | ns->flags |= SPDK_NVME_NS_DEALLOCATE_SUPPORTED; | |
84 | } | |
85 | ||
f67539c2 TL |
86 | if (ns->ctrlr->cdata.oncs.compare) { |
87 | ns->flags |= SPDK_NVME_NS_COMPARE_SUPPORTED; | |
88 | } | |
89 | ||
7c673cae FG |
90 | if (ns->ctrlr->cdata.vwc.present) { |
91 | ns->flags |= SPDK_NVME_NS_FLUSH_SUPPORTED; | |
92 | } | |
93 | ||
94 | if (ns->ctrlr->cdata.oncs.write_zeroes) { | |
95 | ns->flags |= SPDK_NVME_NS_WRITE_ZEROES_SUPPORTED; | |
96 | } | |
97 | ||
f67539c2 TL |
98 | if (ns->ctrlr->cdata.oncs.write_unc) { |
99 | ns->flags |= SPDK_NVME_NS_WRITE_UNCORRECTABLE_SUPPORTED; | |
100 | } | |
101 | ||
7c673cae FG |
102 | if (nsdata->nsrescap.raw) { |
103 | ns->flags |= SPDK_NVME_NS_RESERVATION_SUPPORTED; | |
104 | } | |
105 | ||
106 | ns->pi_type = SPDK_NVME_FMT_NVM_PROTECTION_DISABLE; | |
107 | if (nsdata->lbaf[nsdata->flbas.format].ms && nsdata->dps.pit) { | |
108 | ns->flags |= SPDK_NVME_NS_DPS_PI_SUPPORTED; | |
109 | ns->pi_type = nsdata->dps.pit; | |
110 | } | |
11fdf7f2 TL |
111 | } |
112 | ||
113 | static int | |
114 | nvme_ctrlr_identify_ns(struct spdk_nvme_ns *ns) | |
115 | { | |
f67539c2 | 116 | struct nvme_completion_poll_status *status; |
11fdf7f2 TL |
117 | struct spdk_nvme_ns_data *nsdata; |
118 | int rc; | |
119 | ||
f67539c2 TL |
120 | status = calloc(1, sizeof(*status)); |
121 | if (!status) { | |
122 | SPDK_ERRLOG("Failed to allocate status tracker\n"); | |
123 | return -ENOMEM; | |
124 | } | |
125 | ||
11fdf7f2 TL |
126 | nsdata = _nvme_ns_get_data(ns); |
127 | rc = nvme_ctrlr_cmd_identify(ns->ctrlr, SPDK_NVME_IDENTIFY_NS, 0, ns->id, | |
128 | nsdata, sizeof(*nsdata), | |
f67539c2 | 129 | nvme_completion_poll_cb, status); |
11fdf7f2 | 130 | if (rc != 0) { |
f67539c2 | 131 | free(status); |
11fdf7f2 TL |
132 | return rc; |
133 | } | |
134 | ||
f67539c2 | 135 | if (nvme_wait_for_completion_robust_lock(ns->ctrlr->adminq, status, |
11fdf7f2 | 136 | &ns->ctrlr->ctrlr_lock)) { |
f67539c2 TL |
137 | if (!status->timed_out) { |
138 | free(status); | |
139 | } | |
11fdf7f2 TL |
140 | /* This can occur if the namespace is not active. Simply zero the |
141 | * namespace data and continue. */ | |
142 | nvme_ns_destruct(ns); | |
143 | return 0; | |
144 | } | |
f67539c2 | 145 | free(status); |
11fdf7f2 TL |
146 | |
147 | nvme_ns_set_identify_data(ns); | |
148 | ||
149 | return 0; | |
150 | } | |
151 | ||
152 | static int | |
153 | nvme_ctrlr_identify_id_desc(struct spdk_nvme_ns *ns) | |
154 | { | |
f67539c2 | 155 | struct nvme_completion_poll_status *status; |
11fdf7f2 TL |
156 | int rc; |
157 | ||
158 | memset(ns->id_desc_list, 0, sizeof(ns->id_desc_list)); | |
159 | ||
160 | if (ns->ctrlr->vs.raw < SPDK_NVME_VERSION(1, 3, 0) || | |
161 | (ns->ctrlr->quirks & NVME_QUIRK_IDENTIFY_CNS)) { | |
162 | SPDK_DEBUGLOG(SPDK_LOG_NVME, "Version < 1.3; not attempting to retrieve NS ID Descriptor List\n"); | |
163 | return 0; | |
164 | } | |
165 | ||
f67539c2 TL |
166 | status = calloc(1, sizeof(*status)); |
167 | if (!status) { | |
168 | SPDK_ERRLOG("Failed to allocate status tracker\n"); | |
169 | return -ENOMEM; | |
170 | } | |
171 | ||
11fdf7f2 TL |
172 | SPDK_DEBUGLOG(SPDK_LOG_NVME, "Attempting to retrieve NS ID Descriptor List\n"); |
173 | rc = nvme_ctrlr_cmd_identify(ns->ctrlr, SPDK_NVME_IDENTIFY_NS_ID_DESCRIPTOR_LIST, 0, ns->id, | |
174 | ns->id_desc_list, sizeof(ns->id_desc_list), | |
f67539c2 | 175 | nvme_completion_poll_cb, status); |
11fdf7f2 | 176 | if (rc < 0) { |
f67539c2 | 177 | free(status); |
11fdf7f2 TL |
178 | return rc; |
179 | } | |
180 | ||
f67539c2 | 181 | rc = nvme_wait_for_completion_robust_lock(ns->ctrlr->adminq, status, &ns->ctrlr->ctrlr_lock); |
11fdf7f2 TL |
182 | if (rc != 0) { |
183 | SPDK_WARNLOG("Failed to retrieve NS ID Descriptor List\n"); | |
184 | memset(ns->id_desc_list, 0, sizeof(ns->id_desc_list)); | |
185 | } | |
186 | ||
f67539c2 TL |
187 | if (!status->timed_out) { |
188 | free(status); | |
189 | } | |
190 | ||
7c673cae FG |
191 | return rc; |
192 | } | |
193 | ||
194 | uint32_t | |
195 | spdk_nvme_ns_get_id(struct spdk_nvme_ns *ns) | |
196 | { | |
197 | return ns->id; | |
198 | } | |
199 | ||
200 | bool | |
201 | spdk_nvme_ns_is_active(struct spdk_nvme_ns *ns) | |
202 | { | |
11fdf7f2 TL |
203 | const struct spdk_nvme_ns_data *nsdata = NULL; |
204 | ||
205 | /* | |
206 | * According to the spec, valid NS has non-zero id. | |
207 | */ | |
208 | if (ns->id == 0) { | |
209 | return false; | |
210 | } | |
211 | ||
212 | nsdata = _nvme_ns_get_data(ns); | |
7c673cae FG |
213 | |
214 | /* | |
215 | * According to the spec, Identify Namespace will return a zero-filled structure for | |
216 | * inactive namespace IDs. | |
217 | * Check NCAP since it must be nonzero for an active namespace. | |
218 | */ | |
219 | return nsdata->ncap != 0; | |
220 | } | |
221 | ||
11fdf7f2 TL |
222 | struct spdk_nvme_ctrlr * |
223 | spdk_nvme_ns_get_ctrlr(struct spdk_nvme_ns *ns) | |
224 | { | |
225 | return ns->ctrlr; | |
226 | } | |
227 | ||
7c673cae FG |
228 | uint32_t |
229 | spdk_nvme_ns_get_max_io_xfer_size(struct spdk_nvme_ns *ns) | |
230 | { | |
231 | return ns->ctrlr->max_xfer_size; | |
232 | } | |
233 | ||
234 | uint32_t | |
235 | spdk_nvme_ns_get_sector_size(struct spdk_nvme_ns *ns) | |
236 | { | |
237 | return ns->sector_size; | |
238 | } | |
239 | ||
11fdf7f2 TL |
240 | uint32_t |
241 | spdk_nvme_ns_get_extended_sector_size(struct spdk_nvme_ns *ns) | |
242 | { | |
243 | return ns->extended_lba_size; | |
244 | } | |
245 | ||
7c673cae FG |
246 | uint64_t |
247 | spdk_nvme_ns_get_num_sectors(struct spdk_nvme_ns *ns) | |
248 | { | |
249 | return _nvme_ns_get_data(ns)->nsze; | |
250 | } | |
251 | ||
252 | uint64_t | |
253 | spdk_nvme_ns_get_size(struct spdk_nvme_ns *ns) | |
254 | { | |
255 | return spdk_nvme_ns_get_num_sectors(ns) * spdk_nvme_ns_get_sector_size(ns); | |
256 | } | |
257 | ||
258 | uint32_t | |
259 | spdk_nvme_ns_get_flags(struct spdk_nvme_ns *ns) | |
260 | { | |
261 | return ns->flags; | |
262 | } | |
263 | ||
264 | enum spdk_nvme_pi_type | |
265 | spdk_nvme_ns_get_pi_type(struct spdk_nvme_ns *ns) { | |
266 | return ns->pi_type; | |
267 | } | |
268 | ||
269 | bool | |
270 | spdk_nvme_ns_supports_extended_lba(struct spdk_nvme_ns *ns) | |
271 | { | |
272 | return (ns->flags & SPDK_NVME_NS_EXTENDED_LBA_SUPPORTED) ? true : false; | |
273 | } | |
274 | ||
f67539c2 TL |
275 | bool |
276 | spdk_nvme_ns_supports_compare(struct spdk_nvme_ns *ns) | |
277 | { | |
278 | return (ns->flags & SPDK_NVME_NS_COMPARE_SUPPORTED) ? true : false; | |
279 | } | |
280 | ||
7c673cae FG |
281 | uint32_t |
282 | spdk_nvme_ns_get_md_size(struct spdk_nvme_ns *ns) | |
283 | { | |
284 | return ns->md_size; | |
285 | } | |
286 | ||
287 | const struct spdk_nvme_ns_data * | |
288 | spdk_nvme_ns_get_data(struct spdk_nvme_ns *ns) | |
289 | { | |
7c673cae FG |
290 | return _nvme_ns_get_data(ns); |
291 | } | |
292 | ||
11fdf7f2 TL |
293 | enum spdk_nvme_dealloc_logical_block_read_value spdk_nvme_ns_get_dealloc_logical_block_read_value( |
294 | struct spdk_nvme_ns *ns) | |
295 | { | |
296 | struct spdk_nvme_ctrlr *ctrlr = ns->ctrlr; | |
297 | const struct spdk_nvme_ns_data *data = spdk_nvme_ns_get_data(ns); | |
298 | ||
299 | if (ctrlr->quirks & NVME_QUIRK_READ_ZERO_AFTER_DEALLOCATE) { | |
300 | return SPDK_NVME_DEALLOC_READ_00; | |
301 | } else { | |
302 | return data->dlfeat.bits.read_value; | |
303 | } | |
304 | } | |
305 | ||
306 | uint32_t | |
307 | spdk_nvme_ns_get_optimal_io_boundary(struct spdk_nvme_ns *ns) | |
308 | { | |
309 | return ns->sectors_per_stripe; | |
310 | } | |
311 | ||
312 | static const void * | |
f67539c2 | 313 | nvme_ns_find_id_desc(const struct spdk_nvme_ns *ns, enum spdk_nvme_nidt type, size_t *length) |
11fdf7f2 TL |
314 | { |
315 | const struct spdk_nvme_ns_id_desc *desc; | |
316 | size_t offset; | |
317 | ||
318 | offset = 0; | |
319 | while (offset + 4 < sizeof(ns->id_desc_list)) { | |
320 | desc = (const struct spdk_nvme_ns_id_desc *)&ns->id_desc_list[offset]; | |
321 | ||
322 | if (desc->nidl == 0) { | |
323 | /* End of list */ | |
324 | return NULL; | |
325 | } | |
326 | ||
327 | /* | |
328 | * Check if this descriptor fits within the list. | |
329 | * 4 is the fixed-size descriptor header (not counted in NIDL). | |
330 | */ | |
331 | if (offset + desc->nidl + 4 > sizeof(ns->id_desc_list)) { | |
332 | /* Descriptor longer than remaining space in list (invalid) */ | |
333 | return NULL; | |
334 | } | |
335 | ||
336 | if (desc->nidt == type) { | |
337 | *length = desc->nidl; | |
338 | return &desc->nid[0]; | |
339 | } | |
340 | ||
341 | offset += 4 + desc->nidl; | |
342 | } | |
343 | ||
344 | return NULL; | |
345 | } | |
346 | ||
347 | const struct spdk_uuid * | |
348 | spdk_nvme_ns_get_uuid(const struct spdk_nvme_ns *ns) | |
349 | { | |
350 | const struct spdk_uuid *uuid; | |
351 | size_t uuid_size; | |
352 | ||
f67539c2 | 353 | uuid = nvme_ns_find_id_desc(ns, SPDK_NVME_NIDT_UUID, &uuid_size); |
11fdf7f2 TL |
354 | if (uuid == NULL || uuid_size != sizeof(*uuid)) { |
355 | return NULL; | |
356 | } | |
357 | ||
358 | return uuid; | |
359 | } | |
360 | ||
361 | int nvme_ns_construct(struct spdk_nvme_ns *ns, uint32_t id, | |
7c673cae FG |
362 | struct spdk_nvme_ctrlr *ctrlr) |
363 | { | |
11fdf7f2 TL |
364 | int rc; |
365 | ||
7c673cae FG |
366 | assert(id > 0); |
367 | ||
368 | ns->ctrlr = ctrlr; | |
369 | ns->id = id; | |
7c673cae | 370 | |
11fdf7f2 TL |
371 | rc = nvme_ctrlr_identify_ns(ns); |
372 | if (rc != 0) { | |
373 | return rc; | |
7c673cae FG |
374 | } |
375 | ||
11fdf7f2 | 376 | return nvme_ctrlr_identify_id_desc(ns); |
7c673cae FG |
377 | } |
378 | ||
379 | void nvme_ns_destruct(struct spdk_nvme_ns *ns) | |
380 | { | |
11fdf7f2 | 381 | struct spdk_nvme_ns_data *nsdata; |
7c673cae | 382 | |
11fdf7f2 TL |
383 | if (!ns->id) { |
384 | return; | |
385 | } | |
386 | ||
387 | nsdata = _nvme_ns_get_data(ns); | |
388 | memset(nsdata, 0, sizeof(*nsdata)); | |
389 | ns->sector_size = 0; | |
390 | ns->extended_lba_size = 0; | |
391 | ns->md_size = 0; | |
392 | ns->pi_type = 0; | |
393 | ns->sectors_per_max_io = 0; | |
394 | ns->sectors_per_stripe = 0; | |
395 | ns->flags = 0; | |
7c673cae | 396 | } |
f67539c2 TL |
397 | |
398 | int nvme_ns_update(struct spdk_nvme_ns *ns) | |
399 | { | |
400 | return nvme_ctrlr_identify_ns(ns); | |
401 | } |