]> git.proxmox.com Git - ceph.git/blame - ceph/src/spdk/lib/nvme/nvme_ns.c
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / spdk / lib / nvme / nvme_ns.c
CommitLineData
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
36static 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 */
48void
49nvme_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
113static int
114nvme_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
152static int
153nvme_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
194uint32_t
195spdk_nvme_ns_get_id(struct spdk_nvme_ns *ns)
196{
197 return ns->id;
198}
199
200bool
201spdk_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
222struct spdk_nvme_ctrlr *
223spdk_nvme_ns_get_ctrlr(struct spdk_nvme_ns *ns)
224{
225 return ns->ctrlr;
226}
227
7c673cae
FG
228uint32_t
229spdk_nvme_ns_get_max_io_xfer_size(struct spdk_nvme_ns *ns)
230{
231 return ns->ctrlr->max_xfer_size;
232}
233
234uint32_t
235spdk_nvme_ns_get_sector_size(struct spdk_nvme_ns *ns)
236{
237 return ns->sector_size;
238}
239
11fdf7f2
TL
240uint32_t
241spdk_nvme_ns_get_extended_sector_size(struct spdk_nvme_ns *ns)
242{
243 return ns->extended_lba_size;
244}
245
7c673cae
FG
246uint64_t
247spdk_nvme_ns_get_num_sectors(struct spdk_nvme_ns *ns)
248{
249 return _nvme_ns_get_data(ns)->nsze;
250}
251
252uint64_t
253spdk_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
258uint32_t
259spdk_nvme_ns_get_flags(struct spdk_nvme_ns *ns)
260{
261 return ns->flags;
262}
263
264enum spdk_nvme_pi_type
265spdk_nvme_ns_get_pi_type(struct spdk_nvme_ns *ns) {
266 return ns->pi_type;
267}
268
269bool
270spdk_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
275bool
276spdk_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
281uint32_t
282spdk_nvme_ns_get_md_size(struct spdk_nvme_ns *ns)
283{
284 return ns->md_size;
285}
286
287const struct spdk_nvme_ns_data *
288spdk_nvme_ns_get_data(struct spdk_nvme_ns *ns)
289{
7c673cae
FG
290 return _nvme_ns_get_data(ns);
291}
292
11fdf7f2
TL
293enum 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
306uint32_t
307spdk_nvme_ns_get_optimal_io_boundary(struct spdk_nvme_ns *ns)
308{
309 return ns->sectors_per_stripe;
310}
311
312static const void *
f67539c2 313nvme_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
347const struct spdk_uuid *
348spdk_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
361int 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
379void 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
398int nvme_ns_update(struct spdk_nvme_ns *ns)
399{
400 return nvme_ctrlr_identify_ns(ns);
401}