xref: /spdk/test/nvme/fdp/fdp.c (revision b217432fa83bdca50a1723d18a32b9fac1a7d8f9)
1 /*   SPDX-License-Identifier: BSD-3-Clause
2  *   Copyright (c) 2023 Samsung Electronics Co., Ltd.
3  *   All rights reserved.
4  */
5 
6 #include "spdk/stdinc.h"
7 #include "spdk/nvme.h"
8 #include "spdk/env.h"
9 #include "spdk/util.h"
10 
11 #define MAX_FDP_EVENTS			0xFF
12 
13 #define SET_EVENT_TYPES	((uint8_t[]){0x0, 0x1, 0x2, 0x3, 0x80, 0x81})
14 
15 struct ns_entry {
16 	struct spdk_nvme_ctrlr	*ctrlr;
17 	struct spdk_nvme_ns	*ns;
18 	struct ns_entry		*next;
19 };
20 
21 static struct ns_entry *g_namespaces = NULL;
22 static struct spdk_nvme_transport_id g_trid;
23 static bool g_use_trid = false;
24 
25 static int g_outstanding_commands;
26 static int g_fdp_command_result;
27 static uint32_t g_feat_result;
28 static uint16_t ph_for_fdp_event;
29 static uint8_t rgif;
30 static uint8_t fdpci;
31 static uint16_t pid_for_ruhu;
32 static uint32_t g_spdk_sge_size = 4096;
33 
34 static union spdk_nvme_feat_fdp_cdw12 fdp_res;
35 
36 struct io_request {
37 	void *contig;
38 	uint32_t sgl_offset;
39 	uint32_t buf_size;
40 };
41 
42 static void
43 nvme_req_reset_sgl(void *cb_arg, uint32_t sgl_offset)
44 {
45 	struct io_request *req = (struct io_request *)cb_arg;
46 
47 	req->sgl_offset = sgl_offset;
48 }
49 
50 static int
51 nvme_req_next_sge(void *cb_arg, void **address, uint32_t *length)
52 {
53 	struct io_request *req = (struct io_request *)cb_arg;
54 	uint32_t iov_len;
55 
56 	*address = req->contig;
57 
58 	if (req->sgl_offset) {
59 		*address += req->sgl_offset;
60 	}
61 
62 	iov_len = req->buf_size - req->sgl_offset;
63 	if (iov_len > g_spdk_sge_size) {
64 		iov_len = g_spdk_sge_size;
65 	}
66 
67 	req->sgl_offset += iov_len;
68 	*length = iov_len;
69 
70 	return 0;
71 }
72 
73 static void
74 get_feat_completion(void *cb_arg, const struct spdk_nvme_cpl *cpl)
75 {
76 	if (spdk_nvme_cpl_is_error(cpl)) {
77 		g_fdp_command_result = -1;
78 	} else {
79 		g_fdp_command_result = 0;
80 		g_feat_result = cpl->cdw0;
81 	}
82 
83 	g_outstanding_commands--;
84 }
85 
86 static void
87 cmd_completion(void *cb_arg, const struct spdk_nvme_cpl *cpl)
88 {
89 	if (spdk_nvme_cpl_is_error(cpl)) {
90 		g_fdp_command_result = -1;
91 	} else {
92 		g_fdp_command_result = 0;
93 	}
94 
95 	g_outstanding_commands--;
96 }
97 
98 static void
99 print_uint128_hex(uint64_t *v)
100 {
101 	unsigned long long lo = v[0], hi = v[1];
102 	if (hi) {
103 		printf("0x%llX%016llX", hi, lo);
104 	} else {
105 		printf("0x%llX", lo);
106 	}
107 }
108 
109 static void
110 print_uint128_dec(uint64_t *v)
111 {
112 	unsigned long long lo = v[0], hi = v[1];
113 	if (hi) {
114 		/* can't handle large (>64-bit) decimal values for now, so fall back to hex */
115 		print_uint128_hex(v);
116 	} else {
117 		printf("%llu", (unsigned long long)lo);
118 	}
119 }
120 
121 static int
122 set_fdp_events(struct spdk_nvme_ns *ns)
123 {
124 	int ret;
125 	uint8_t fdp_event_type_list[6] = {};
126 	uint32_t nfdp_events = 6;
127 	uint32_t cdw11, cdw12;
128 	struct spdk_nvme_ctrlr *ctrlr = spdk_nvme_ns_get_ctrlr(ns);
129 	int nsid = spdk_nvme_ns_get_id(ns);
130 
131 	memcpy(fdp_event_type_list, SET_EVENT_TYPES, nfdp_events);
132 	g_outstanding_commands = 0;
133 	g_fdp_command_result = -1;
134 
135 	cdw11 = (nfdp_events << 16) | ph_for_fdp_event;
136 	/* Enable FDP event */
137 	cdw12 = 1;
138 
139 	ret = spdk_nvme_ctrlr_cmd_set_feature_ns(ctrlr, SPDK_NVME_FEAT_FDP_EVENTS, cdw11, cdw12,
140 			fdp_event_type_list, nfdp_events,
141 			get_feat_completion, NULL, nsid);
142 	if (ret) {
143 		fprintf(stderr, "Set Feature (fdp events) failed\n\n");
144 		return -1;
145 	}
146 
147 	g_outstanding_commands++;
148 	while (g_outstanding_commands) {
149 		spdk_nvme_ctrlr_process_admin_completions(ctrlr);
150 	}
151 
152 	if (g_fdp_command_result) {
153 		fprintf(stderr, "Set Feature (fdp events) failed\n\n");
154 		return -1;
155 	}
156 
157 	fprintf(stdout, "Set Feature: Enabling FDP events on Placement handle: #%u Success\n\n",
158 		ph_for_fdp_event);
159 	return 0;
160 }
161 
162 static int
163 get_fdp_events(struct spdk_nvme_ns *ns)
164 {
165 	int ret;
166 	uint32_t i, cdw11;
167 	struct spdk_nvme_fdp_event_desc events[MAX_FDP_EVENTS];
168 	struct spdk_nvme_fdp_event_desc *event_desc;
169 	struct spdk_nvme_ctrlr *ctrlr = spdk_nvme_ns_get_ctrlr(ns);
170 	int nsid = spdk_nvme_ns_get_id(ns);
171 
172 	g_outstanding_commands = 0;
173 	g_fdp_command_result = -1;
174 	g_feat_result = 0;
175 
176 	cdw11 = (MAX_FDP_EVENTS << 16) | ph_for_fdp_event;
177 
178 	ret = spdk_nvme_ctrlr_cmd_get_feature_ns(ctrlr, SPDK_NVME_FEAT_FDP_EVENTS, cdw11,
179 			events, MAX_FDP_EVENTS * sizeof(struct spdk_nvme_fdp_event_desc),
180 			get_feat_completion, NULL, nsid);
181 	if (ret) {
182 		fprintf(stderr, "Get Feature (fdp events) failed\n\n");
183 		return -1;
184 	}
185 
186 	g_outstanding_commands++;
187 	while (g_outstanding_commands) {
188 		spdk_nvme_ctrlr_process_admin_completions(ctrlr);
189 	}
190 
191 	if (g_fdp_command_result) {
192 		fprintf(stderr, "Get Feature (fdp events) failed\n\n");
193 		return -1;
194 	}
195 
196 	fprintf(stdout, "Get Feature: FDP Events for Placement handle: #%u\n", ph_for_fdp_event);
197 	fprintf(stdout, "========================\n");
198 	fprintf(stdout, "Number of FDP Events: %u\n", g_feat_result);
199 
200 	event_desc = events;
201 	for (i = 0; i < g_feat_result; i++) {
202 		fprintf(stdout, "FDP Event: #%u  Type: %s", i,
203 			event_desc->fdp_etype == SPDK_NVME_FDP_EVENT_RU_NOT_WRITTEN_CAPACITY ?
204 			"RU Not Written to Capacity   " :
205 			event_desc->fdp_etype == SPDK_NVME_FDP_EVENT_RU_TIME_LIMIT_EXCEEDED ?
206 			"RU Time Limit Exceeded       " :
207 			event_desc->fdp_etype == SPDK_NVME_FDP_EVENT_CTRLR_RESET_MODIFY_RUH ?
208 			"Ctrlr Reset Modified RUH's   " :
209 			event_desc->fdp_etype == SPDK_NVME_FDP_EVENT_INVALID_PLACEMENT_ID ?
210 			"Invalid Placement Identifier " :
211 			event_desc->fdp_etype == SPDK_NVME_FDP_EVENT_MEDIA_REALLOCATED ? "Media Reallocated            " :
212 			event_desc->fdp_etype == SPDK_NVME_FDP_EVENT_IMPLICIT_MODIFIED_RUH ?
213 			"Implicitly modified RUH      " :
214 			"Reserved");
215 		fprintf(stdout, "  Enabled: %s\n",
216 			event_desc->fdpeta.bits.fdp_ee ? "Yes" : "No");
217 		event_desc++;
218 	}
219 
220 	fprintf(stdout, "\n");
221 	return 0;
222 }
223 
224 static int
225 get_fdp(struct spdk_nvme_ns *ns)
226 {
227 	int ret;
228 	uint32_t cdw11;
229 	struct spdk_nvme_ctrlr *ctrlr = spdk_nvme_ns_get_ctrlr(ns);
230 	const struct spdk_nvme_ns_data *nsdata = spdk_nvme_ns_get_data(ns);
231 
232 	g_outstanding_commands = 0;
233 	g_fdp_command_result = -1;
234 	g_feat_result = 0;
235 
236 	cdw11 = nsdata->endgid;
237 
238 	ret = spdk_nvme_ctrlr_cmd_get_feature(ctrlr, SPDK_NVME_FEAT_FDP, cdw11, NULL, 0,
239 					      get_feat_completion, NULL);
240 	if (ret) {
241 		fprintf(stderr, "Get Feature (fdp) failed\n\n");
242 		return -1;
243 	}
244 
245 	g_outstanding_commands++;
246 	while (g_outstanding_commands) {
247 		spdk_nvme_ctrlr_process_admin_completions(ctrlr);
248 	}
249 
250 	if (g_fdp_command_result) {
251 		fprintf(stderr, "Get Feature (fdp) failed\n\n");
252 		return -1;
253 	}
254 
255 	fdp_res.raw = g_feat_result;
256 
257 	fprintf(stdout, "Get Feature: FDP:\n");
258 	fprintf(stdout, "=================\n");
259 	fprintf(stdout, "  Enabled:                 %s\n",
260 		fdp_res.bits.fdpe ? "Yes" : "No");
261 	fprintf(stdout, "  FDP configuration Index: %u\n\n", fdp_res.bits.fdpci);
262 
263 	return 0;
264 }
265 
266 static int
267 check_fdp_write(struct spdk_nvme_ns *ns, struct spdk_nvme_qpair *qpair)
268 {
269 	int ret;
270 	uint32_t sector_size, lba_count;
271 	uint64_t lba;
272 	struct io_request *req;
273 	struct spdk_nvme_ns_cmd_ext_io_opts ext_opts;
274 
275 	g_outstanding_commands = 0;
276 	g_fdp_command_result = -1;
277 
278 	ext_opts.size = SPDK_SIZEOF(&ext_opts, cdw13);
279 	ext_opts.io_flags = SPDK_NVME_IO_FLAGS_DATA_PLACEMENT_DIRECTIVE;
280 	ext_opts.metadata = NULL;
281 	ext_opts.cdw13 = (pid_for_ruhu << 16);
282 
283 	sector_size = spdk_nvme_ns_get_sector_size(ns);
284 
285 	req = spdk_zmalloc(sizeof(*req), 0, NULL, SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
286 	assert(req);
287 
288 	lba = 0;
289 	lba_count = 8;
290 	req->buf_size = sector_size * lba_count;
291 	req->contig = spdk_zmalloc(req->buf_size, 0x1000, NULL, SPDK_ENV_LCORE_ID_ANY,
292 				   SPDK_MALLOC_DMA);
293 	assert(req->contig);
294 
295 	ret = spdk_nvme_ns_cmd_writev_ext(ns, qpair, lba, lba_count, cmd_completion, req,
296 					  nvme_req_reset_sgl, nvme_req_next_sge, &ext_opts);
297 
298 	if (ret) {
299 		fprintf(stderr, "spdk_nvme_ns_cmd_writev_ext failed\n\n");
300 		spdk_free(req->contig);
301 		spdk_free(req);
302 		return -1;
303 	}
304 
305 	g_outstanding_commands++;
306 	while (g_outstanding_commands) {
307 		spdk_nvme_qpair_process_completions(qpair, 100);
308 	}
309 
310 	if (g_fdp_command_result) {
311 		fprintf(stderr, "FDP write on placement id: %u failed\n\n", pid_for_ruhu);
312 	} else {
313 		fprintf(stdout, "FDP write on placement id: %u success\n\n", pid_for_ruhu);
314 	}
315 
316 	spdk_free(req->contig);
317 	spdk_free(req);
318 	return g_fdp_command_result;
319 }
320 
321 static int
322 reclaim_unit_handle_update(struct spdk_nvme_ns *ns, struct spdk_nvme_qpair *qpair)
323 {
324 	int ret;
325 	uint32_t npids = 1;
326 	uint16_t pid_list[1] = {};
327 
328 	memcpy(pid_list, &pid_for_ruhu, sizeof(pid_list));
329 	g_outstanding_commands = 0;
330 	g_fdp_command_result = -1;
331 
332 	ret = spdk_nvme_ns_cmd_io_mgmt_send(ns, qpair, pid_list, npids * sizeof(uint16_t),
333 					    SPDK_NVME_FDP_IO_MGMT_SEND_RUHU, npids - 1, cmd_completion, NULL);
334 	if (ret) {
335 		fprintf(stderr, "IO management send: RUH update failed\n\n");
336 		return -1;
337 	}
338 
339 	g_outstanding_commands++;
340 	while (g_outstanding_commands) {
341 		spdk_nvme_qpair_process_completions(qpair, 100);
342 	}
343 
344 	if (g_fdp_command_result) {
345 		fprintf(stderr, "IO management send: RUH update failed\n\n");
346 		return -1;
347 	}
348 
349 	fprintf(stdout, "IO mgmt send: RUH update for Placement ID: #%u Success\n\n",
350 		pid_for_ruhu);
351 	return 0;
352 }
353 
354 static int
355 reclaim_unit_handle_status(struct spdk_nvme_ns *ns, struct spdk_nvme_qpair *qpair)
356 {
357 	int ret;
358 	uint32_t i;
359 	size_t fdp_ruhs_size;
360 	struct spdk_nvme_fdp_ruhs *fdp_ruhs;
361 	struct spdk_nvme_fdp_ruhs_desc *ruhs_desc;
362 
363 	g_outstanding_commands = 0;
364 	g_fdp_command_result = -1;
365 
366 	fdp_ruhs_size = sizeof(struct spdk_nvme_fdp_ruhs);
367 	fdp_ruhs = calloc(1, fdp_ruhs_size);
368 	if (!fdp_ruhs) {
369 		fprintf(stderr, "FDP reclaim unit handle status allocation failed!\n\n");
370 		return -1;
371 	}
372 
373 	/* Fetch the reclaim unit handle status header */
374 	ret = spdk_nvme_ns_cmd_io_mgmt_recv(ns, qpair, fdp_ruhs, fdp_ruhs_size,
375 					    SPDK_NVME_FDP_IO_MGMT_RECV_RUHS, 0, cmd_completion, NULL);
376 	if (ret) {
377 		fprintf(stderr, "IO management receive: RUH status failed\n\n");
378 		free(fdp_ruhs);
379 		return -1;
380 	}
381 
382 	g_outstanding_commands++;
383 	while (g_outstanding_commands) {
384 		spdk_nvme_qpair_process_completions(qpair, 100);
385 	}
386 
387 	if (g_fdp_command_result) {
388 		fprintf(stderr, "IO management receive: RUH status failed\n\n");
389 		free(fdp_ruhs);
390 		return -1;
391 	}
392 
393 	fdp_ruhs_size += fdp_ruhs->nruhsd * sizeof(struct spdk_nvme_fdp_ruhs_desc);
394 	fdp_ruhs = realloc(fdp_ruhs, fdp_ruhs_size);
395 	if (!fdp_ruhs) {
396 		fprintf(stderr, "FDP reclaim unit handle status buffer reallocation failed!\n\n");
397 		return -1;
398 	}
399 
400 	ret = spdk_nvme_ns_cmd_io_mgmt_recv(ns, qpair, fdp_ruhs, fdp_ruhs_size,
401 					    SPDK_NVME_FDP_IO_MGMT_RECV_RUHS, 0, cmd_completion, NULL);
402 	if (ret) {
403 		fprintf(stderr, "IO management receive: RUH status failed\n\n");
404 		free(fdp_ruhs);
405 		return -1;
406 	}
407 
408 	g_outstanding_commands++;
409 	while (g_outstanding_commands) {
410 		spdk_nvme_qpair_process_completions(qpair, 100);
411 	}
412 
413 	if (g_fdp_command_result) {
414 		fprintf(stderr, "IO management receive: RUH status failed\n\n");
415 		free(fdp_ruhs);
416 		return -1;
417 	}
418 
419 	fprintf(stdout, "FDP Reclaim unit handle status\n");
420 	fprintf(stdout, "==============================\n");
421 
422 	fprintf(stdout, "Number of RUHS descriptors:   %u\n", fdp_ruhs->nruhsd);
423 	for (i = 0; i < fdp_ruhs->nruhsd; i++) {
424 		ruhs_desc = &fdp_ruhs->ruhs_desc[i];
425 
426 		fprintf(stdout,
427 			"RUHS Desc: #%04u  PID: 0x%04x  RUHID: 0x%04x  ERUT: 0x%08x  RUAMW: 0x%016"PRIx64"\n",
428 			i, ruhs_desc->pid, ruhs_desc->ruhid, ruhs_desc->earutr, ruhs_desc->ruamw);
429 	}
430 	fprintf(stdout, "\n");
431 
432 	/* Use this Placement Identifier for Reclaim unit handle Update */
433 	pid_for_ruhu = (&fdp_ruhs->ruhs_desc[0])->pid;
434 
435 	/* Use this Placement Handle to enable FDP events */
436 	ph_for_fdp_event = pid_for_ruhu & ((1 << (16 - rgif)) - 1);
437 
438 	free(fdp_ruhs);
439 	return 0;
440 }
441 
442 static int
443 get_fdp_cfg_log_page(struct spdk_nvme_ns *ns)
444 {
445 	uint32_t i, j;
446 	size_t fdp_cfg_size;
447 	struct spdk_nvme_fdp_cfg_log_page *fdp_cfg_log_page;
448 	struct spdk_nvme_fdp_cfg_descriptor *cfg_desc;
449 	struct spdk_nvme_ctrlr *ctrlr = spdk_nvme_ns_get_ctrlr(ns);
450 	const struct spdk_nvme_ns_data *nsdata = spdk_nvme_ns_get_data(ns);
451 	void *log;
452 
453 	g_outstanding_commands = 0;
454 	g_fdp_command_result = -1;
455 
456 	fdp_cfg_size = sizeof(struct spdk_nvme_fdp_cfg_log_page);
457 	fdp_cfg_log_page = calloc(1, fdp_cfg_size);
458 	if (!fdp_cfg_log_page) {
459 		fprintf(stderr, "FDP config log page allocation failed!\n\n");
460 		return -1;
461 	}
462 
463 	/* Fetch the FDP configurations log page header */
464 	if (spdk_nvme_ctrlr_cmd_get_log_page_ext(ctrlr, SPDK_NVME_LOG_FDP_CONFIGURATIONS, 0,
465 			fdp_cfg_log_page, fdp_cfg_size, 0, 0, (nsdata->endgid << 16),
466 			0, cmd_completion, NULL) == 0) {
467 		g_outstanding_commands++;
468 	} else {
469 		fprintf(stderr, "spdk_nvme_ctrlr_cmd_get_log_page_ext(FDP config) failed\n\n");
470 		free(fdp_cfg_log_page);
471 		return -1;
472 	}
473 
474 	while (g_outstanding_commands) {
475 		spdk_nvme_ctrlr_process_admin_completions(ctrlr);
476 	}
477 
478 	if (g_fdp_command_result) {
479 		fprintf(stderr, "Failed to get FDP configuration log page\n\n");
480 		free(fdp_cfg_log_page);
481 		return -1;
482 	}
483 
484 	fdp_cfg_size = fdp_cfg_log_page->size;
485 	fdp_cfg_log_page = realloc(fdp_cfg_log_page, fdp_cfg_size);
486 	if (!fdp_cfg_log_page) {
487 		fprintf(stderr, "FDP config log page reallocation failed!\n\n");
488 		return -1;
489 	}
490 
491 	if (spdk_nvme_ctrlr_cmd_get_log_page_ext(ctrlr, SPDK_NVME_LOG_FDP_CONFIGURATIONS, 0,
492 			fdp_cfg_log_page, fdp_cfg_size, 0, 0, (nsdata->endgid << 16),
493 			0, cmd_completion, NULL) == 0) {
494 		g_outstanding_commands++;
495 	} else {
496 		fprintf(stderr, "spdk_nvme_ctrlr_cmd_get_log_page_ext(FDP config) failed\n\n");
497 		free(fdp_cfg_log_page);
498 		return -1;
499 	}
500 
501 	while (g_outstanding_commands) {
502 		spdk_nvme_ctrlr_process_admin_completions(ctrlr);
503 	}
504 
505 	if (g_fdp_command_result) {
506 		fprintf(stderr, "Failed to get FDP configuration log page\n\n");
507 		free(fdp_cfg_log_page);
508 		return -1;
509 	}
510 
511 	fprintf(stdout, "FDP configurations log page\n");
512 	fprintf(stdout, "===========================\n");
513 
514 	fprintf(stdout, "Number of FDP configurations:         %u\n", fdp_cfg_log_page->ncfg + 1);
515 	fprintf(stdout, "Version:                              %u\n", fdp_cfg_log_page->version);
516 	fprintf(stdout, "Size:                                 %u\n", fdp_cfg_log_page->size);
517 
518 	log = fdp_cfg_log_page->cfg_desc;
519 	for (i = 0; i <= fdp_cfg_log_page->ncfg; i++) {
520 		cfg_desc = log;
521 		fprintf(stdout, "FDP Configuration Descriptor:         %u\n", i);
522 		fprintf(stdout, "  Descriptor Size:                    %u\n", cfg_desc->ds);
523 		fprintf(stdout, "  Reclaim Group Identifier format:    %u\n",
524 			cfg_desc->fdpa.bits.rgif);
525 		fprintf(stdout, "  FDP Volatile Write Cache:           %s\n",
526 			cfg_desc->fdpa.bits.fdpvwc ? "Present" : "Not Present");
527 		fprintf(stdout, "  FDP Configuration:                  %s\n",
528 			cfg_desc->fdpa.bits.fdpcv ? "Valid" : "Invalid");
529 		fprintf(stdout, "  Vendor Specific Size:               %u\n", cfg_desc->vss);
530 		fprintf(stdout, "  Number of Reclaim Groups:           %u\n", cfg_desc->nrg);
531 		fprintf(stdout, "  Number of Recalim Unit Handles:     %u\n", cfg_desc->nruh);
532 		fprintf(stdout, "  Max Placement Identifiers:          %u\n", cfg_desc->maxpids + 1);
533 		fprintf(stdout, "  Number of Namespaces Suppprted:     %u\n", cfg_desc->nns);
534 		fprintf(stdout, "  Reclaim unit Nominal Size:          %" PRIx64 " bytes\n", cfg_desc->runs);
535 		fprintf(stdout, "  Estimated Reclaim Unit Time Limit:  ");
536 		if (cfg_desc->erutl) {
537 			fprintf(stdout, "%u seconds\n", cfg_desc->erutl);
538 		} else {
539 			fprintf(stdout, "Not Reported\n");
540 		}
541 		for (j = 0; j < cfg_desc->nruh; j++) {
542 			fprintf(stdout, "    RUH Desc #%03d:          RUH Type: %s\n", j,
543 				cfg_desc->ruh_desc[j].ruht == SPDK_NVME_FDP_RUHT_INITIALLY_ISOLATED ? "Initially Isolated" :
544 				cfg_desc->ruh_desc[j].ruht == SPDK_NVME_FDP_RUHT_PERSISTENTLY_ISOLATED ? "Persistently Isolated" :
545 				"Reserved");
546 		}
547 		if (i == fdpci) {
548 			rgif = cfg_desc->fdpa.bits.rgif;
549 		}
550 		log += cfg_desc->ds;
551 	}
552 
553 	fprintf(stdout, "\n");
554 	free(fdp_cfg_log_page);
555 
556 	return 0;
557 }
558 
559 static int
560 get_fdp_ruhu_log_page(struct spdk_nvme_ns *ns)
561 {
562 	uint32_t i;
563 	size_t fdp_ruhu_size;
564 	struct spdk_nvme_fdp_ruhu_log_page *fdp_ruhu_log_page;
565 	struct spdk_nvme_fdp_ruhu_descriptor *ruhu_desc;
566 	struct spdk_nvme_ctrlr *ctrlr = spdk_nvme_ns_get_ctrlr(ns);
567 	const struct spdk_nvme_ns_data *nsdata = spdk_nvme_ns_get_data(ns);
568 
569 	g_outstanding_commands = 0;
570 	g_fdp_command_result = -1;
571 
572 	fdp_ruhu_size = sizeof(struct spdk_nvme_fdp_ruhu_log_page);
573 	fdp_ruhu_log_page = calloc(1, fdp_ruhu_size);
574 	if (!fdp_ruhu_log_page) {
575 		fprintf(stderr, "FDP Reclaim Unit Handle usage log page allocation failed!\n\n");
576 		return -1;
577 	}
578 
579 	/* Fetch the FDP RUH usage log page header */
580 	if (spdk_nvme_ctrlr_cmd_get_log_page_ext(ctrlr, SPDK_NVME_LOG_RECLAIM_UNIT_HANDLE_USAGE, 0,
581 			fdp_ruhu_log_page, fdp_ruhu_size, 0, 0, (nsdata->endgid << 16),
582 			0, cmd_completion, NULL) == 0) {
583 		g_outstanding_commands++;
584 	} else {
585 		fprintf(stderr, "spdk_nvme_ctrlr_cmd_get_log_page_ext(RUH usage) failed\n\n");
586 		free(fdp_ruhu_log_page);
587 		return -1;
588 	}
589 
590 	while (g_outstanding_commands) {
591 		spdk_nvme_ctrlr_process_admin_completions(ctrlr);
592 	}
593 
594 	if (g_fdp_command_result) {
595 		fprintf(stderr, "Failed to get Reclaim Unit Handle usage log page\n\n");
596 		free(fdp_ruhu_log_page);
597 		return -1;
598 	}
599 
600 	fdp_ruhu_size += fdp_ruhu_log_page->nruh * sizeof(struct spdk_nvme_fdp_ruhu_descriptor);
601 	fdp_ruhu_log_page = realloc(fdp_ruhu_log_page, fdp_ruhu_size);
602 	if (!fdp_ruhu_log_page) {
603 		fprintf(stderr, "FDP Reclaim Unit Handle usage log page reallocation failed!\n\n");
604 		return -1;
605 	}
606 
607 	if (spdk_nvme_ctrlr_cmd_get_log_page_ext(ctrlr, SPDK_NVME_LOG_RECLAIM_UNIT_HANDLE_USAGE, 0,
608 			fdp_ruhu_log_page, fdp_ruhu_size, 0, 0, (nsdata->endgid << 16),
609 			0, cmd_completion, NULL) == 0) {
610 		g_outstanding_commands++;
611 	} else {
612 		fprintf(stderr, "spdk_nvme_ctrlr_cmd_get_log_page_ext(RUH usage) failed\n\n");
613 		free(fdp_ruhu_log_page);
614 		return -1;
615 	}
616 
617 	while (g_outstanding_commands) {
618 		spdk_nvme_ctrlr_process_admin_completions(ctrlr);
619 	}
620 
621 	if (g_fdp_command_result) {
622 		fprintf(stderr, "Failed to get Reclaim Unit Handle usage log page\n\n");
623 		free(fdp_ruhu_log_page);
624 		return -1;
625 	}
626 
627 	fprintf(stdout, "FDP reclaim unit handle usage log page\n");
628 	fprintf(stdout, "======================================\n");
629 
630 	fprintf(stdout, "Number of Reclaim Unit Handles:       %u\n", fdp_ruhu_log_page->nruh);
631 
632 	for (i = 0; i < fdp_ruhu_log_page->nruh; i++) {
633 		ruhu_desc = &fdp_ruhu_log_page->ruhu_desc[i];
634 
635 		fprintf(stdout, "  RUH Usage Desc #%03d:   RUH Attributes: %s\n", i,
636 			ruhu_desc->ruha == SPDK_NVME_FDP_RUHA_UNUSED ? "Unused" :
637 			ruhu_desc->ruha == SPDK_NVME_FDP_RUHA_HOST_SPECIFIED ? "Host Specified" :
638 			ruhu_desc->ruha == SPDK_NVME_FDP_RUHA_CTRLR_SPECIFIED ? "Controller Specified" :
639 			"Reserved");
640 	}
641 
642 	fprintf(stdout, "\n");
643 	free(fdp_ruhu_log_page);
644 
645 	return 0;
646 }
647 
648 static int
649 get_fdp_stats_log_page(struct spdk_nvme_ns *ns)
650 {
651 	struct spdk_nvme_fdp_stats_log_page fdp_stats_log_page;
652 	struct spdk_nvme_ctrlr *ctrlr = spdk_nvme_ns_get_ctrlr(ns);
653 	const struct spdk_nvme_ns_data *nsdata = spdk_nvme_ns_get_data(ns);
654 
655 	g_outstanding_commands = 0;
656 	g_fdp_command_result = -1;
657 
658 	if (spdk_nvme_ctrlr_cmd_get_log_page_ext(ctrlr, SPDK_NVME_LOG_FDP_STATISTICS, 0,
659 			&fdp_stats_log_page, 64, 0, 0, (nsdata->endgid << 16), 0,
660 			cmd_completion, NULL) == 0) {
661 		g_outstanding_commands++;
662 	} else {
663 		fprintf(stderr, "spdk_nvme_ctrlr_cmd_get_log_page_ext(FDP stats) failed\n\n");
664 		return -1;
665 	}
666 
667 	while (g_outstanding_commands) {
668 		spdk_nvme_ctrlr_process_admin_completions(ctrlr);
669 	}
670 
671 	if (g_fdp_command_result) {
672 		fprintf(stderr, "Failed to get FDP statistics log page\n\n");
673 		return -1;
674 	}
675 
676 	fprintf(stdout, "FDP statistics log page\n");
677 	fprintf(stdout, "=======================\n");
678 
679 	fprintf(stdout, "Host bytes with metadata written:  ");
680 	print_uint128_dec(fdp_stats_log_page.hbmw);
681 	fprintf(stdout, "\n");
682 	fprintf(stdout, "Media bytes with metadata written: ");
683 	print_uint128_dec(fdp_stats_log_page.mbmw);
684 	fprintf(stdout, "\n");
685 	fprintf(stdout, "Media bytes erased:                ");
686 	print_uint128_dec(fdp_stats_log_page.mbe);
687 	fprintf(stdout, "\n\n");
688 
689 	return 0;
690 }
691 
692 static int
693 get_fdp_events_log_page(struct spdk_nvme_ns *ns)
694 {
695 	uint32_t i;
696 	size_t fdp_event_size;
697 	struct spdk_nvme_fdp_events_log_page *fdp_events_log_page;
698 	struct spdk_nvme_fdp_event *event;
699 	struct spdk_nvme_fdp_event_media_reallocated *media_reallocated;
700 	struct spdk_nvme_ctrlr *ctrlr = spdk_nvme_ns_get_ctrlr(ns);
701 	const struct spdk_nvme_ns_data *nsdata = spdk_nvme_ns_get_data(ns);
702 
703 	g_outstanding_commands = 0;
704 	g_fdp_command_result = -1;
705 
706 	fdp_event_size = sizeof(struct spdk_nvme_fdp_events_log_page);
707 	fdp_events_log_page = calloc(1, fdp_event_size);
708 	if (!fdp_events_log_page) {
709 		fprintf(stderr, "FDP events log page allocation failed!\n\n");
710 		return -1;
711 	}
712 
713 	/* Fetch the FDP events log page header */
714 	if (spdk_nvme_ctrlr_cmd_get_log_page_ext(ctrlr, SPDK_NVME_LOG_FDP_EVENTS, 0,
715 			fdp_events_log_page, fdp_event_size, 0,
716 			(SPDK_NVME_FDP_REPORT_HOST_EVENTS << 8), (nsdata->endgid << 16),
717 			0, cmd_completion, NULL) == 0) {
718 		g_outstanding_commands++;
719 	} else {
720 		fprintf(stderr, "spdk_nvme_ctrlr_cmd_get_log_page_ext(FDP events) failed\n\n");
721 		free(fdp_events_log_page);
722 		return -1;
723 	}
724 
725 	while (g_outstanding_commands) {
726 		spdk_nvme_ctrlr_process_admin_completions(ctrlr);
727 	}
728 
729 	if (g_fdp_command_result) {
730 		fprintf(stderr, "Failed to get eventss log page\n\n");
731 		free(fdp_events_log_page);
732 		return -1;
733 	}
734 
735 	fdp_event_size += fdp_events_log_page->nevents * sizeof(struct spdk_nvme_fdp_event);
736 	fdp_events_log_page = realloc(fdp_events_log_page, fdp_event_size);
737 	if (!fdp_events_log_page) {
738 		fprintf(stderr, "FDP events log page reallocation failed!\n\n");
739 		return -1;
740 	}
741 
742 	/* Only fetch FDP host events here */
743 	if (spdk_nvme_ctrlr_cmd_get_log_page_ext(ctrlr, SPDK_NVME_LOG_FDP_EVENTS, 0,
744 			fdp_events_log_page, fdp_event_size, 0,
745 			(SPDK_NVME_FDP_REPORT_HOST_EVENTS << 8), (nsdata->endgid << 16),
746 			0, cmd_completion, NULL) == 0) {
747 		g_outstanding_commands++;
748 	} else {
749 		fprintf(stderr, "spdk_nvme_ctrlr_cmd_get_log_page_ext(FDP events) failed\n\n");
750 		free(fdp_events_log_page);
751 		return -1;
752 	}
753 
754 	while (g_outstanding_commands) {
755 		spdk_nvme_ctrlr_process_admin_completions(ctrlr);
756 	}
757 
758 	if (g_fdp_command_result) {
759 		fprintf(stderr, "Failed to get eventss log page\n\n");
760 		free(fdp_events_log_page);
761 		return -1;
762 	}
763 
764 	fprintf(stdout, "FDP events log page\n");
765 	fprintf(stdout, "===================\n");
766 	fprintf(stdout, "Number of FDP events: %u\n", fdp_events_log_page->nevents);
767 
768 	for (i = 0; i < fdp_events_log_page->nevents; i++) {
769 		event = &fdp_events_log_page->event[i];
770 
771 		fprintf(stdout, "FDP Event #%u:\n", i);
772 		fprintf(stdout, "  Event Type:                      %s\n",
773 			event->etype == SPDK_NVME_FDP_EVENT_RU_NOT_WRITTEN_CAPACITY ? "RU Not Written to Capacity" :
774 			event->etype == SPDK_NVME_FDP_EVENT_RU_TIME_LIMIT_EXCEEDED ? "RU Time Limit Exceeded" :
775 			event->etype == SPDK_NVME_FDP_EVENT_CTRLR_RESET_MODIFY_RUH ? "Ctrlr Reset Modified RUH's" :
776 			event->etype == SPDK_NVME_FDP_EVENT_INVALID_PLACEMENT_ID ? "Invalid Placement Identifier" :
777 			event->etype == SPDK_NVME_FDP_EVENT_MEDIA_REALLOCATED ? "Media Reallocated" :
778 			event->etype == SPDK_NVME_FDP_EVENT_IMPLICIT_MODIFIED_RUH ? "Implicitly modified RUH" :
779 			"Reserved");
780 		fprintf(stdout, "  Placement Identifier:            %s\n",
781 			event->fdpef.bits.piv ? "Valid" : "Invalid");
782 		fprintf(stdout, "  NSID:                            %s\n",
783 			event->fdpef.bits.nsidv ? "Valid" : "Invalid");
784 		fprintf(stdout, "  Location:                        %s\n",
785 			event->fdpef.bits.lv ? "Valid" : "Invalid");
786 		if (event->fdpef.bits.piv) {
787 			fprintf(stdout, "  Placement Identifier:            %u\n", event->pid);
788 		} else {
789 			fprintf(stdout, "  Placement Identifier:            Reserved\n");
790 		}
791 		fprintf(stdout, "  Event Timestamp:                 %" PRIx64 "\n", event->timestamp);
792 		if (event->fdpef.bits.nsidv) {
793 			fprintf(stdout, "  Namespace Identifier:            %u\n", event->nsid);
794 		} else {
795 			fprintf(stdout, "  Namespace Identifier:            Ignore\n");
796 		}
797 
798 		if (event->etype == SPDK_NVME_FDP_EVENT_MEDIA_REALLOCATED) {
799 			media_reallocated = (struct spdk_nvme_fdp_event_media_reallocated *)&event->event_type_specific;
800 
801 			fprintf(stdout, "  LBA:                             %s\n",
802 				media_reallocated->sef.bits.lbav ? "Valid" : "Invalid");
803 			fprintf(stdout, "  Number of LBA's Moved:           %u\n", media_reallocated->nlbam);
804 			if (media_reallocated->sef.bits.lbav) {
805 				fprintf(stdout, "  Logical Block Address:           %u\n", event->nsid);
806 			} else {
807 				fprintf(stdout, "  Logical Block Address:           Ignore\n");
808 			}
809 		}
810 
811 		if (event->fdpef.bits.lv) {
812 			fprintf(stdout, "  Reclaim Group Identifier:        %u\n", event->rgid);
813 		} else {
814 			fprintf(stdout, "  Reclaim Group Identifier:        Ignore\n");
815 		}
816 		if (event->fdpef.bits.lv) {
817 			fprintf(stdout, "  Reclaim Unit Handle Identifier:  %u\n", event->ruhid);
818 		} else {
819 			fprintf(stdout, "  Reclaim Unit Handle Identifier:  Ignore\n");
820 		}
821 	}
822 
823 	fprintf(stdout, "\n");
824 	free(fdp_events_log_page);
825 
826 	return 0;
827 }
828 
829 static int
830 fdp_tests(struct spdk_nvme_ns *ns)
831 {
832 	struct spdk_nvme_qpair *qpair;
833 	struct spdk_nvme_ctrlr *ctrlr = spdk_nvme_ns_get_ctrlr(ns);
834 	int ret, err;
835 
836 	qpair = spdk_nvme_ctrlr_alloc_io_qpair(ctrlr, NULL, 0);
837 	if (!qpair) {
838 		fprintf(stderr, "spdk_nvme_ctrlr_alloc_io_qpair() failed\n");
839 		return -EIO;
840 	}
841 
842 	ret = 0;
843 
844 	fprintf(stdout, "==================================\n");
845 	fprintf(stdout, "== FDP tests for Namespace: #%02u ==\n", spdk_nvme_ns_get_id(ns));
846 	fprintf(stdout, "==================================\n\n");
847 	err = get_fdp(ns);
848 	if (err) {
849 		spdk_nvme_ctrlr_free_io_qpair(qpair);
850 		return err;
851 	}
852 
853 	if (!fdp_res.bits.fdpe) {
854 		fprintf(stdout, "FDP support disabled\n");
855 		spdk_nvme_ctrlr_free_io_qpair(qpair);
856 		return 0;
857 	}
858 
859 	fdpci = fdp_res.bits.fdpci;
860 	err = get_fdp_cfg_log_page(ns);
861 	if (err) {
862 		spdk_nvme_ctrlr_free_io_qpair(qpair);
863 		return err;
864 	}
865 
866 	ret += get_fdp_ruhu_log_page(ns);
867 	ret += get_fdp_stats_log_page(ns);
868 
869 	err = reclaim_unit_handle_status(ns, qpair);
870 	if (err) {
871 		spdk_nvme_ctrlr_free_io_qpair(qpair);
872 		return err;
873 	}
874 
875 	err = check_fdp_write(ns, qpair);
876 	if (err) {
877 		spdk_nvme_ctrlr_free_io_qpair(qpair);
878 		return err;
879 	}
880 
881 	ret += set_fdp_events(ns);
882 	ret += reclaim_unit_handle_update(ns, qpair);
883 
884 	ret += get_fdp_events(ns);
885 	ret += get_fdp_events_log_page(ns);
886 
887 	return ret;
888 }
889 
890 static void
891 register_ns(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_ns *ns)
892 {
893 	struct ns_entry *entry;
894 	const struct spdk_nvme_ns_data *nsdata = spdk_nvme_ns_get_data(ns);
895 
896 	entry = malloc(sizeof(struct ns_entry));
897 	if (entry == NULL) {
898 		perror("ns_entry malloc");
899 		exit(1);
900 	}
901 
902 	entry->ctrlr = ctrlr;
903 	entry->ns = ns;
904 	entry->next = g_namespaces;
905 	g_namespaces = entry;
906 
907 	printf("Namespace ID: %d Endurance Group ID: %d\n", spdk_nvme_ns_get_id(ns),
908 	       nsdata->endgid);
909 }
910 
911 static bool
912 probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
913 	 struct spdk_nvme_ctrlr_opts *opts)
914 {
915 	fprintf(stdout, "Attaching to %s\n", trid->traddr);
916 
917 	return true;
918 }
919 
920 static void
921 attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
922 	  struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts)
923 {
924 	int num_ns, nsid;
925 	struct spdk_nvme_ns *ns;
926 	const struct spdk_nvme_ctrlr_data *cdata;
927 
928 	cdata = spdk_nvme_ctrlr_get_data(ctrlr);
929 
930 	if (cdata->ctratt.bits.fdps) {
931 		fprintf(stdout, "Controller supports FDP Attached to %s\n", trid->traddr);
932 		num_ns = spdk_nvme_ctrlr_get_num_ns(ctrlr);
933 		if (num_ns < 1) {
934 			printf("No valid namespaces in controller\n");
935 		} else {
936 			for (nsid = spdk_nvme_ctrlr_get_first_active_ns(ctrlr); nsid != 0;
937 			     nsid = spdk_nvme_ctrlr_get_next_active_ns(ctrlr, nsid)) {
938 				ns = spdk_nvme_ctrlr_get_ns(ctrlr, nsid);
939 				register_ns(ctrlr, ns);
940 			}
941 		}
942 	} else {
943 		fprintf(stdout, "Controller attached to: %s doesn't support FDP\n", trid->traddr);
944 	}
945 }
946 
947 static void
948 cleanup(void)
949 {
950 	struct ns_entry	*ns_entry = g_namespaces;
951 	struct spdk_nvme_detach_ctx *detach_ctx = NULL;
952 
953 	while (ns_entry) {
954 		struct ns_entry *next = ns_entry->next;
955 
956 		spdk_nvme_detach_async(ns_entry->ctrlr, &detach_ctx);
957 
958 		free(ns_entry);
959 		ns_entry = next;
960 	}
961 
962 	if (detach_ctx) {
963 		spdk_nvme_detach_poll(detach_ctx);
964 	}
965 }
966 
967 static void
968 usage(const char *program_name)
969 {
970 	printf("%s [options]", program_name);
971 	printf("\n");
972 	printf("options:\n");
973 	printf(" -r trid    remote NVMe over Fabrics target address\n");
974 	printf("    Format: 'key:value [key:value] ...'\n");
975 	printf("    Keys:\n");
976 	printf("     trtype      Transport type (e.g. RDMA)\n");
977 	printf("     adrfam      Address family (e.g. IPv4, IPv6)\n");
978 	printf("     traddr      Transport address (e.g. 192.168.100.8)\n");
979 	printf("     trsvcid     Transport service identifier (e.g. 4420)\n");
980 	printf("     subnqn      Subsystem NQN (default: %s)\n", SPDK_NVMF_DISCOVERY_NQN);
981 	printf("    Example: -r 'trtype:RDMA adrfam:IPv4 traddr:192.168.100.8 trsvcid:4420'\n");
982 	printf(" -h         show this usage\n");
983 }
984 
985 static int
986 parse_args(int argc, char **argv, struct spdk_env_opts *env_opts)
987 {
988 	int op;
989 
990 	spdk_nvme_trid_populate_transport(&g_trid, SPDK_NVME_TRANSPORT_PCIE);
991 	snprintf(g_trid.subnqn, sizeof(g_trid.subnqn), "%s", SPDK_NVMF_DISCOVERY_NQN);
992 
993 	while ((op = getopt(argc, argv, "r:h")) != -1) {
994 		switch (op) {
995 		case 'r':
996 			if (spdk_nvme_transport_id_parse(&g_trid, optarg) != 0) {
997 				fprintf(stderr, "Error parsing transport address\n");
998 				return 1;
999 			}
1000 
1001 			g_use_trid = true;
1002 			break;
1003 		case 'h':
1004 			usage(argv[0]);
1005 			exit(EXIT_SUCCESS);
1006 		default:
1007 			usage(argv[0]);
1008 			return 1;
1009 		}
1010 	}
1011 
1012 	return 0;
1013 }
1014 
1015 int
1016 main(int argc, char **argv)
1017 {
1018 	int			rc;
1019 	struct spdk_env_opts	opts;
1020 	struct ns_entry	*ns_entry;
1021 
1022 	spdk_env_opts_init(&opts);
1023 	rc = parse_args(argc, argv, &opts);
1024 	if (rc != 0) {
1025 		return rc;
1026 	}
1027 
1028 	opts.name = "fdp";
1029 	opts.core_mask = "0x1";
1030 	opts.shm_id = 0;
1031 	if (spdk_env_init(&opts) < 0) {
1032 		fprintf(stderr, "Unable to initialize SPDK env\n");
1033 		return 1;
1034 	}
1035 
1036 	printf("Initializing NVMe Controllers\n");
1037 
1038 	rc = spdk_nvme_probe(g_use_trid ? &g_trid : NULL, NULL, probe_cb, attach_cb, NULL);
1039 	if (rc != 0) {
1040 		fprintf(stderr, "spdk_nvme_probe() failed\n");
1041 		return 1;
1042 	}
1043 
1044 	if (g_namespaces == NULL) {
1045 		fprintf(stderr, "no NVMe controllers found\n");
1046 		return 1;
1047 	}
1048 
1049 	printf("Initialization complete.\n\n");
1050 
1051 	ns_entry = g_namespaces;
1052 	while (ns_entry != NULL) {
1053 		rc = fdp_tests(ns_entry->ns);
1054 		if (rc) {
1055 			break;
1056 		}
1057 		ns_entry = ns_entry->next;
1058 	}
1059 
1060 	printf("FDP test %s\n", rc ? "failed" : "passed");
1061 	cleanup();
1062 
1063 	return 0;
1064 }
1065