1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2024 Oxide Computer Company 14 */ 15 16 /* 17 * This file implements support for Format NVM. 18 */ 19 20 #include <string.h> 21 #include <unistd.h> 22 23 #include "libnvme_impl.h" 24 25 void 26 nvme_format_req_fini(nvme_format_req_t *req) 27 { 28 free(req); 29 } 30 31 bool 32 nvme_format_req_init(nvme_ctrl_t *ctrl, nvme_format_req_t **reqp) 33 { 34 nvme_format_req_t *req; 35 nvme_valid_ctrl_data_t ctrl_data; 36 37 if (reqp == NULL) { 38 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 39 "encountered invalid nvme_format_req_t output pointer: %p", 40 reqp)); 41 } 42 43 ctrl_data.vcd_vers = &ctrl->nc_vers; 44 ctrl_data.vcd_id = &ctrl->nc_info; 45 46 if (!nvme_format_cmds_supported(&ctrl_data)) { 47 return (nvme_ctrl_error(ctrl, NVME_ERR_FORMAT_UNSUP_BY_DEV, 0, 48 "controller does not support format NVM")); 49 } 50 51 req = calloc(1, sizeof (nvme_format_req_t)); 52 if (req == NULL) { 53 int e = errno; 54 return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to " 55 "allocate memory for a new nvme_log_req_t: %s", 56 strerror(e))); 57 } 58 59 req->nfr_ctrl = ctrl; 60 req->nfr_nsid = NVME_NSID_BCAST; 61 62 for (size_t i = 0; i < nvme_format_nfields; i++) { 63 if (nvme_format_fields[i].nlfi_def_req) { 64 req->nfr_need |= 1 << i; 65 } 66 } 67 68 *reqp = req; 69 return (nvme_ctrl_success(ctrl)); 70 } 71 72 static void 73 nvme_format_req_clear_need(nvme_format_req_t *req, 74 nvme_format_req_field_t field) 75 { 76 req->nfr_need &= ~(1 << field); 77 } 78 79 static const nvme_field_check_t nvme_format_check_ses = { 80 nvme_format_fields, NVME_FORMAT_REQ_FIELD_SES, 81 NVME_ERR_FORMAT_SES_RANGE, 0, 0 82 }; 83 84 bool 85 nvme_format_req_set_ses(nvme_format_req_t *req, uint32_t ses) 86 { 87 if (!nvme_field_check_one(req->nfr_ctrl, ses, "format NVM", 88 &nvme_format_check_ses, 0)) { 89 return (false); 90 } 91 92 req->nfr_ses = ses; 93 nvme_format_req_clear_need(req, NVME_FORMAT_REQ_FIELD_SES); 94 return (nvme_ctrl_success(req->nfr_ctrl)); 95 } 96 97 static const nvme_field_check_t nvme_format_check_lbaf = { 98 nvme_format_fields, NVME_FORMAT_REQ_FIELD_LBAF, 99 NVME_ERR_FORMAT_LBAF_RANGE, 0, 0 100 }; 101 102 /* 103 * We don't try to check the range of the actual LBA formats that are here and 104 * instead leave that to the kernel which always has valid common namespace 105 * information. It would be nice to fold that in to the common logic in the 106 * future. 107 */ 108 bool 109 nvme_format_req_set_lbaf(nvme_format_req_t *req, uint32_t lbaf) 110 { 111 if (!nvme_field_check_one(req->nfr_ctrl, lbaf, "format NVM", 112 &nvme_format_check_lbaf, 0)) { 113 return (false); 114 } 115 116 req->nfr_lbaf = lbaf; 117 nvme_format_req_clear_need(req, NVME_FORMAT_REQ_FIELD_LBAF); 118 return (nvme_ctrl_success(req->nfr_ctrl)); 119 } 120 121 static const nvme_field_check_t nvme_format_check_nsid = { 122 nvme_format_fields, NVME_FORMAT_REQ_FIELD_NSID, 123 NVME_ERR_NS_RANGE, 0, 0 124 }; 125 126 bool 127 nvme_format_req_set_nsid(nvme_format_req_t *req, uint32_t nsid) 128 { 129 if (!nvme_field_check_one(req->nfr_ctrl, nsid, "format NVM", 130 &nvme_format_check_nsid, 0)) { 131 return (false); 132 } 133 134 req->nfr_nsid = nsid; 135 nvme_format_req_clear_need(req, NVME_FORMAT_REQ_FIELD_NSID); 136 return (nvme_ctrl_success(req->nfr_ctrl)); 137 } 138 139 /* 140 * All error checking with respect to whether or not the controller supports 141 * operating on a single namespace is left to the kernel and translated back 142 * here. 143 */ 144 bool 145 nvme_format_req_exec(nvme_format_req_t *req) 146 { 147 nvme_ctrl_t *ctrl = req->nfr_ctrl; 148 nvme_ioctl_format_t format; 149 150 if (req->nfr_need != 0) { 151 return (nvme_field_miss_err(ctrl, nvme_format_fields, 152 nvme_format_nfields, NVME_ERR_FORMAT_REQ_MISSING_FIELDS, 153 "format", req->nfr_need)); 154 } 155 156 (void) memset(&format, 0, sizeof (format)); 157 format.nif_common.nioc_nsid = req->nfr_nsid; 158 format.nif_lbaf = req->nfr_lbaf; 159 format.nif_ses = req->nfr_ses; 160 161 if (ioctl(ctrl->nc_fd, NVME_IOC_FORMAT, &format) != 0) { 162 int e = errno; 163 return (nvme_ioctl_syserror(ctrl, e, "format")); 164 } 165 166 if (format.nif_common.nioc_drv_err != NVME_IOCTL_E_OK) { 167 return (nvme_ioctl_error(ctrl, &format.nif_common, "format")); 168 } 169 170 return (nvme_ctrl_success(ctrl)); 171 } 172