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