xref: /spdk/lib/nvme/nvme_discovery.c (revision d8f6e798d6e6228e43cdb5f74ee92982e9d5c1bd)
1 /*   SPDX-License-Identifier: BSD-3-Clause
2  *   Copyright (C) 2021 Intel Corporation. All rights reserved.
3  */
4 
5 #include "nvme_internal.h"
6 
7 #include "spdk/endian.h"
8 
9 struct nvme_discovery_ctx {
10 	struct spdk_nvme_ctrlr			*ctrlr;
11 	struct spdk_nvmf_discovery_log_page	*log_page;
12 	uint64_t				start_genctr;
13 	uint64_t				end_genctr;
14 	spdk_nvme_discovery_cb			cb_fn;
15 	void					*cb_arg;
16 };
17 
18 static void
19 get_log_page_completion_final(void *cb_arg, const struct spdk_nvme_cpl *cpl)
20 {
21 	struct nvme_discovery_ctx *ctx = cb_arg;
22 	int rc;
23 
24 	if (spdk_nvme_cpl_is_error(cpl)) {
25 		free(ctx->log_page);
26 		ctx->cb_fn(ctx->cb_arg, 0, cpl, NULL);
27 		free(ctx);
28 		return;
29 	}
30 
31 	/* Compare original genctr with latest genctr. If it changed, we need to restart. */
32 	if (ctx->start_genctr == ctx->end_genctr) {
33 		ctx->cb_fn(ctx->cb_arg, 0, cpl, ctx->log_page);
34 	} else {
35 		free(ctx->log_page);
36 		rc = spdk_nvme_ctrlr_get_discovery_log_page(ctx->ctrlr, ctx->cb_fn, ctx->cb_arg);
37 		if (rc != 0) {
38 			ctx->cb_fn(ctx->cb_arg, rc, NULL, NULL);
39 		}
40 	}
41 	free(ctx);
42 }
43 
44 static void
45 get_log_page_completion(void *cb_arg, const struct spdk_nvme_cpl *cpl)
46 {
47 	struct nvme_discovery_ctx *ctx = cb_arg;
48 	int rc;
49 
50 	if (spdk_nvme_cpl_is_error(cpl)) {
51 		free(ctx->log_page);
52 		ctx->cb_fn(ctx->cb_arg, 0, cpl, NULL);
53 		free(ctx);
54 		return;
55 	}
56 
57 	rc = spdk_nvme_ctrlr_cmd_get_log_page(ctx->ctrlr, SPDK_NVME_LOG_DISCOVERY, 0,
58 					      &ctx->end_genctr, sizeof(ctx->end_genctr), 0,
59 					      get_log_page_completion_final, ctx);
60 	if (rc != 0) {
61 		free(ctx->log_page);
62 		ctx->cb_fn(ctx->cb_arg, rc, NULL, NULL);
63 		free(ctx);
64 	}
65 }
66 
67 static void
68 discovery_log_header_completion(void *cb_arg, const struct spdk_nvme_cpl *cpl)
69 {
70 	struct spdk_nvmf_discovery_log_page *new_page;
71 	struct nvme_discovery_ctx *ctx = cb_arg;
72 	size_t page_size;
73 	uint16_t recfmt;
74 	uint64_t numrec;
75 	int rc;
76 
77 	if (spdk_nvme_cpl_is_error(cpl)) {
78 		/* Return without printing anything - this may not be a discovery controller */
79 		ctx->cb_fn(ctx->cb_arg, 0, cpl, NULL);
80 		free(ctx->log_page);
81 		free(ctx);
82 		return;
83 	}
84 
85 	/* Got the first 4K of the discovery log page */
86 	recfmt = from_le16(&ctx->log_page->recfmt);
87 	if (recfmt != 0) {
88 		SPDK_ERRLOG("Unrecognized discovery log record format %" PRIu16 "\n", recfmt);
89 		ctx->cb_fn(ctx->cb_arg, -EINVAL, NULL, NULL);
90 		free(ctx->log_page);
91 		free(ctx);
92 		return;
93 	}
94 
95 	ctx->start_genctr = ctx->log_page->genctr;
96 
97 	numrec = from_le64(&ctx->log_page->numrec);
98 
99 	if (numrec == 0) {
100 		/* No entries in the discovery log. So we can just return the header to the
101 		 * caller.
102 		 */
103 		get_log_page_completion(ctx, cpl);
104 		return;
105 	}
106 
107 	/*
108 	 * Now that we know how many entries should be in the log page, we can allocate
109 	 * the full log page buffer.
110 	 */
111 	page_size = sizeof(struct spdk_nvmf_discovery_log_page);
112 	page_size += numrec * sizeof(struct spdk_nvmf_discovery_log_page_entry);
113 	new_page = realloc(ctx->log_page, page_size);
114 	if (new_page == NULL) {
115 		SPDK_ERRLOG("Could not allocate buffer for log page (%" PRIu64 " entries)\n",
116 			    numrec);
117 		ctx->cb_fn(ctx->cb_arg, -ENOMEM, NULL, NULL);
118 		free(ctx->log_page);
119 		free(ctx);
120 		return;
121 	}
122 
123 	ctx->log_page = new_page;
124 
125 	/* Retrieve the entire discovery log page */
126 	rc = spdk_nvme_ctrlr_cmd_get_log_page(ctx->ctrlr, SPDK_NVME_LOG_DISCOVERY,
127 					      0, (char *)ctx->log_page, page_size, 0,
128 					      get_log_page_completion, ctx);
129 	if (rc != 0) {
130 		free(ctx->log_page);
131 		ctx->cb_fn(ctx->cb_arg, rc, NULL, NULL);
132 		free(ctx);
133 	}
134 }
135 
136 int
137 spdk_nvme_ctrlr_get_discovery_log_page(struct spdk_nvme_ctrlr *ctrlr,
138 				       spdk_nvme_discovery_cb cb_fn, void *cb_arg)
139 {
140 	struct nvme_discovery_ctx *ctx;
141 	int rc;
142 
143 	ctx = calloc(1, sizeof(*ctx));
144 	if (ctx == NULL) {
145 		return -ENOMEM;
146 	}
147 
148 	ctx->log_page = calloc(1, sizeof(*ctx->log_page));
149 	if (ctx->log_page == NULL) {
150 		free(ctx);
151 		return -ENOMEM;
152 	}
153 
154 	ctx->ctrlr = ctrlr;
155 	ctx->cb_fn = cb_fn;
156 	ctx->cb_arg = cb_arg;
157 
158 	rc = spdk_nvme_ctrlr_cmd_get_log_page(ctrlr, SPDK_NVME_LOG_DISCOVERY, 0,
159 					      ctx->log_page, sizeof(*ctx->log_page), 0,
160 					      discovery_log_header_completion, ctx);
161 	if (rc != 0) {
162 		free(ctx->log_page);
163 		free(ctx);
164 	}
165 
166 	return rc;
167 }
168