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