1 /* 2 * Discovery service for the NVMe over Fabrics target. 3 * Copyright (C) 2016 Intel Corporation. All rights reserved. 4 * 5 * This program is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU General Public License version 7 * 2 as published by the Free Software Foundation. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 */ 14 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 15 #include <linux/slab.h> 16 #include <generated/utsrelease.h> 17 #include "nvmet.h" 18 19 struct nvmet_subsys *nvmet_disc_subsys; 20 21 u64 nvmet_genctr; 22 23 void nvmet_referral_enable(struct nvmet_port *parent, struct nvmet_port *port) 24 { 25 down_write(&nvmet_config_sem); 26 if (list_empty(&port->entry)) { 27 list_add_tail(&port->entry, &parent->referrals); 28 port->enabled = true; 29 nvmet_genctr++; 30 } 31 up_write(&nvmet_config_sem); 32 } 33 34 void nvmet_referral_disable(struct nvmet_port *port) 35 { 36 down_write(&nvmet_config_sem); 37 if (!list_empty(&port->entry)) { 38 port->enabled = false; 39 list_del_init(&port->entry); 40 nvmet_genctr++; 41 } 42 up_write(&nvmet_config_sem); 43 } 44 45 static void nvmet_format_discovery_entry(struct nvmf_disc_rsp_page_hdr *hdr, 46 struct nvmet_port *port, char *subsys_nqn, char *traddr, 47 u8 type, u32 numrec) 48 { 49 struct nvmf_disc_rsp_page_entry *e = &hdr->entries[numrec]; 50 51 e->trtype = port->disc_addr.trtype; 52 e->adrfam = port->disc_addr.adrfam; 53 e->treq = port->disc_addr.treq; 54 e->portid = port->disc_addr.portid; 55 /* we support only dynamic controllers */ 56 e->cntlid = cpu_to_le16(NVME_CNTLID_DYNAMIC); 57 e->asqsz = cpu_to_le16(NVME_AQ_DEPTH); 58 e->subtype = type; 59 memcpy(e->trsvcid, port->disc_addr.trsvcid, NVMF_TRSVCID_SIZE); 60 memcpy(e->traddr, traddr, NVMF_TRADDR_SIZE); 61 memcpy(e->tsas.common, port->disc_addr.tsas.common, NVMF_TSAS_SIZE); 62 strncpy(e->subnqn, subsys_nqn, NVMF_NQN_SIZE); 63 } 64 65 /* 66 * nvmet_set_disc_traddr - set a correct discovery log entry traddr 67 * 68 * IP based transports (e.g RDMA) can listen on "any" ipv4/ipv6 addresses 69 * (INADDR_ANY or IN6ADDR_ANY_INIT). The discovery log page traddr reply 70 * must not contain that "any" IP address. If the transport implements 71 * .disc_traddr, use it. this callback will set the discovery traddr 72 * from the req->port address in case the port in question listens 73 * "any" IP address. 74 */ 75 static void nvmet_set_disc_traddr(struct nvmet_req *req, struct nvmet_port *port, 76 char *traddr) 77 { 78 if (req->ops->disc_traddr) 79 req->ops->disc_traddr(req, port, traddr); 80 else 81 memcpy(traddr, port->disc_addr.traddr, NVMF_TRADDR_SIZE); 82 } 83 84 static void nvmet_execute_get_disc_log_page(struct nvmet_req *req) 85 { 86 const int entry_size = sizeof(struct nvmf_disc_rsp_page_entry); 87 struct nvmet_ctrl *ctrl = req->sq->ctrl; 88 struct nvmf_disc_rsp_page_hdr *hdr; 89 size_t data_len = nvmet_get_log_page_len(req->cmd); 90 size_t alloc_len = max(data_len, sizeof(*hdr)); 91 int residual_len = data_len - sizeof(*hdr); 92 struct nvmet_subsys_link *p; 93 struct nvmet_port *r; 94 u32 numrec = 0; 95 u16 status = 0; 96 97 /* 98 * Make sure we're passing at least a buffer of response header size. 99 * If host provided data len is less than the header size, only the 100 * number of bytes requested by host will be sent to host. 101 */ 102 hdr = kzalloc(alloc_len, GFP_KERNEL); 103 if (!hdr) { 104 status = NVME_SC_INTERNAL; 105 goto out; 106 } 107 108 down_read(&nvmet_config_sem); 109 list_for_each_entry(p, &req->port->subsystems, entry) { 110 if (!nvmet_host_allowed(req, p->subsys, ctrl->hostnqn)) 111 continue; 112 if (residual_len >= entry_size) { 113 char traddr[NVMF_TRADDR_SIZE]; 114 115 nvmet_set_disc_traddr(req, req->port, traddr); 116 nvmet_format_discovery_entry(hdr, req->port, 117 p->subsys->subsysnqn, traddr, 118 NVME_NQN_NVME, numrec); 119 residual_len -= entry_size; 120 } 121 numrec++; 122 } 123 124 list_for_each_entry(r, &req->port->referrals, entry) { 125 if (residual_len >= entry_size) { 126 nvmet_format_discovery_entry(hdr, r, 127 NVME_DISC_SUBSYS_NAME, 128 r->disc_addr.traddr, 129 NVME_NQN_DISC, numrec); 130 residual_len -= entry_size; 131 } 132 numrec++; 133 } 134 135 hdr->genctr = cpu_to_le64(nvmet_genctr); 136 hdr->numrec = cpu_to_le64(numrec); 137 hdr->recfmt = cpu_to_le16(0); 138 139 up_read(&nvmet_config_sem); 140 141 status = nvmet_copy_to_sgl(req, 0, hdr, data_len); 142 kfree(hdr); 143 out: 144 nvmet_req_complete(req, status); 145 } 146 147 static void nvmet_execute_identify_disc_ctrl(struct nvmet_req *req) 148 { 149 struct nvmet_ctrl *ctrl = req->sq->ctrl; 150 struct nvme_id_ctrl *id; 151 u16 status = 0; 152 153 id = kzalloc(sizeof(*id), GFP_KERNEL); 154 if (!id) { 155 status = NVME_SC_INTERNAL; 156 goto out; 157 } 158 159 memset(id->fr, ' ', sizeof(id->fr)); 160 strncpy((char *)id->fr, UTS_RELEASE, sizeof(id->fr)); 161 162 /* no limit on data transfer sizes for now */ 163 id->mdts = 0; 164 id->cntlid = cpu_to_le16(ctrl->cntlid); 165 id->ver = cpu_to_le32(ctrl->subsys->ver); 166 id->lpa = (1 << 2); 167 168 /* no enforcement soft-limit for maxcmd - pick arbitrary high value */ 169 id->maxcmd = cpu_to_le16(NVMET_MAX_CMD); 170 171 id->sgls = cpu_to_le32(1 << 0); /* we always support SGLs */ 172 if (ctrl->ops->has_keyed_sgls) 173 id->sgls |= cpu_to_le32(1 << 2); 174 if (req->port->inline_data_size) 175 id->sgls |= cpu_to_le32(1 << 20); 176 177 strcpy(id->subnqn, ctrl->subsys->subsysnqn); 178 179 status = nvmet_copy_to_sgl(req, 0, id, sizeof(*id)); 180 181 kfree(id); 182 out: 183 nvmet_req_complete(req, status); 184 } 185 186 u16 nvmet_parse_discovery_cmd(struct nvmet_req *req) 187 { 188 struct nvme_command *cmd = req->cmd; 189 190 if (unlikely(!(req->sq->ctrl->csts & NVME_CSTS_RDY))) { 191 pr_err("got cmd %d while not ready\n", 192 cmd->common.opcode); 193 return NVME_SC_INVALID_OPCODE | NVME_SC_DNR; 194 } 195 196 switch (cmd->common.opcode) { 197 case nvme_admin_get_log_page: 198 req->data_len = nvmet_get_log_page_len(cmd); 199 200 switch (cmd->get_log_page.lid) { 201 case NVME_LOG_DISC: 202 req->execute = nvmet_execute_get_disc_log_page; 203 return 0; 204 default: 205 pr_err("unsupported get_log_page lid %d\n", 206 cmd->get_log_page.lid); 207 return NVME_SC_INVALID_OPCODE | NVME_SC_DNR; 208 } 209 case nvme_admin_identify: 210 req->data_len = NVME_IDENTIFY_DATA_SIZE; 211 switch (cmd->identify.cns) { 212 case NVME_ID_CNS_CTRL: 213 req->execute = 214 nvmet_execute_identify_disc_ctrl; 215 return 0; 216 default: 217 pr_err("unsupported identify cns %d\n", 218 cmd->identify.cns); 219 return NVME_SC_INVALID_OPCODE | NVME_SC_DNR; 220 } 221 default: 222 pr_err("unsupported cmd %d\n", cmd->common.opcode); 223 return NVME_SC_INVALID_OPCODE | NVME_SC_DNR; 224 } 225 226 pr_err("unhandled cmd %d\n", cmd->common.opcode); 227 return NVME_SC_INVALID_OPCODE | NVME_SC_DNR; 228 } 229 230 int __init nvmet_init_discovery(void) 231 { 232 nvmet_disc_subsys = 233 nvmet_subsys_alloc(NVME_DISC_SUBSYS_NAME, NVME_NQN_DISC); 234 if (!nvmet_disc_subsys) 235 return -ENOMEM; 236 return 0; 237 } 238 239 void nvmet_exit_discovery(void) 240 { 241 nvmet_subsys_put(nvmet_disc_subsys); 242 } 243