diff options
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/nvme/target/admin-cmd.c | 5 | ||||
| -rw-r--r-- | drivers/nvme/target/discovery.c | 68 | ||||
| -rw-r--r-- | drivers/nvme/target/nvmet.h | 1 |
3 files changed, 51 insertions, 23 deletions
diff --git a/drivers/nvme/target/admin-cmd.c b/drivers/nvme/target/admin-cmd.c index 76250181fee0..9f72d515fc4b 100644 --- a/drivers/nvme/target/admin-cmd.c +++ b/drivers/nvme/target/admin-cmd.c | |||
| @@ -24,6 +24,11 @@ u32 nvmet_get_log_page_len(struct nvme_command *cmd) | |||
| 24 | return len; | 24 | return len; |
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | u64 nvmet_get_log_page_offset(struct nvme_command *cmd) | ||
| 28 | { | ||
| 29 | return le64_to_cpu(cmd->get_log_page.lpo); | ||
| 30 | } | ||
| 31 | |||
| 27 | static void nvmet_execute_get_log_page_noop(struct nvmet_req *req) | 32 | static void nvmet_execute_get_log_page_noop(struct nvmet_req *req) |
| 28 | { | 33 | { |
| 29 | nvmet_req_complete(req, nvmet_zero_sgl(req, 0, req->data_len)); | 34 | nvmet_req_complete(req, nvmet_zero_sgl(req, 0, req->data_len)); |
diff --git a/drivers/nvme/target/discovery.c b/drivers/nvme/target/discovery.c index c872b47a88f3..33ed95e72d6b 100644 --- a/drivers/nvme/target/discovery.c +++ b/drivers/nvme/target/discovery.c | |||
| @@ -131,54 +131,76 @@ static void nvmet_set_disc_traddr(struct nvmet_req *req, struct nvmet_port *port | |||
| 131 | memcpy(traddr, port->disc_addr.traddr, NVMF_TRADDR_SIZE); | 131 | memcpy(traddr, port->disc_addr.traddr, NVMF_TRADDR_SIZE); |
| 132 | } | 132 | } |
| 133 | 133 | ||
| 134 | static size_t discovery_log_entries(struct nvmet_req *req) | ||
| 135 | { | ||
| 136 | struct nvmet_ctrl *ctrl = req->sq->ctrl; | ||
| 137 | struct nvmet_subsys_link *p; | ||
| 138 | struct nvmet_port *r; | ||
| 139 | size_t entries = 0; | ||
| 140 | |||
| 141 | list_for_each_entry(p, &req->port->subsystems, entry) { | ||
| 142 | if (!nvmet_host_allowed(p->subsys, ctrl->hostnqn)) | ||
| 143 | continue; | ||
| 144 | entries++; | ||
| 145 | } | ||
| 146 | list_for_each_entry(r, &req->port->referrals, entry) | ||
| 147 | entries++; | ||
| 148 | return entries; | ||
| 149 | } | ||
| 150 | |||
| 134 | static void nvmet_execute_get_disc_log_page(struct nvmet_req *req) | 151 | static void nvmet_execute_get_disc_log_page(struct nvmet_req *req) |
| 135 | { | 152 | { |
| 136 | const int entry_size = sizeof(struct nvmf_disc_rsp_page_entry); | 153 | const int entry_size = sizeof(struct nvmf_disc_rsp_page_entry); |
| 137 | struct nvmet_ctrl *ctrl = req->sq->ctrl; | 154 | struct nvmet_ctrl *ctrl = req->sq->ctrl; |
| 138 | struct nvmf_disc_rsp_page_hdr *hdr; | 155 | struct nvmf_disc_rsp_page_hdr *hdr; |
| 156 | u64 offset = nvmet_get_log_page_offset(req->cmd); | ||
| 139 | size_t data_len = nvmet_get_log_page_len(req->cmd); | 157 | size_t data_len = nvmet_get_log_page_len(req->cmd); |
| 140 | size_t alloc_len = max(data_len, sizeof(*hdr)); | 158 | size_t alloc_len; |
| 141 | int residual_len = data_len - sizeof(*hdr); | ||
| 142 | struct nvmet_subsys_link *p; | 159 | struct nvmet_subsys_link *p; |
| 143 | struct nvmet_port *r; | 160 | struct nvmet_port *r; |
| 144 | u32 numrec = 0; | 161 | u32 numrec = 0; |
| 145 | u16 status = 0; | 162 | u16 status = 0; |
| 163 | void *buffer; | ||
| 164 | |||
| 165 | /* Spec requires dword aligned offsets */ | ||
| 166 | if (offset & 0x3) { | ||
| 167 | status = NVME_SC_INVALID_FIELD | NVME_SC_DNR; | ||
| 168 | goto out; | ||
| 169 | } | ||
| 146 | 170 | ||
| 147 | /* | 171 | /* |
| 148 | * Make sure we're passing at least a buffer of response header size. | 172 | * Make sure we're passing at least a buffer of response header size. |
| 149 | * If host provided data len is less than the header size, only the | 173 | * If host provided data len is less than the header size, only the |
| 150 | * number of bytes requested by host will be sent to host. | 174 | * number of bytes requested by host will be sent to host. |
| 151 | */ | 175 | */ |
| 152 | hdr = kzalloc(alloc_len, GFP_KERNEL); | 176 | down_read(&nvmet_config_sem); |
| 153 | if (!hdr) { | 177 | alloc_len = sizeof(*hdr) + entry_size * discovery_log_entries(req); |
| 178 | buffer = kzalloc(alloc_len, GFP_KERNEL); | ||
| 179 | if (!buffer) { | ||
| 180 | up_read(&nvmet_config_sem); | ||
| 154 | status = NVME_SC_INTERNAL; | 181 | status = NVME_SC_INTERNAL; |
| 155 | goto out; | 182 | goto out; |
| 156 | } | 183 | } |
| 157 | 184 | ||
| 158 | down_read(&nvmet_config_sem); | 185 | hdr = buffer; |
| 159 | list_for_each_entry(p, &req->port->subsystems, entry) { | 186 | list_for_each_entry(p, &req->port->subsystems, entry) { |
| 187 | char traddr[NVMF_TRADDR_SIZE]; | ||
| 188 | |||
| 160 | if (!nvmet_host_allowed(p->subsys, ctrl->hostnqn)) | 189 | if (!nvmet_host_allowed(p->subsys, ctrl->hostnqn)) |
| 161 | continue; | 190 | continue; |
| 162 | if (residual_len >= entry_size) { | 191 | |
| 163 | char traddr[NVMF_TRADDR_SIZE]; | 192 | nvmet_set_disc_traddr(req, req->port, traddr); |
| 164 | 193 | nvmet_format_discovery_entry(hdr, req->port, | |
| 165 | nvmet_set_disc_traddr(req, req->port, traddr); | 194 | p->subsys->subsysnqn, traddr, |
| 166 | nvmet_format_discovery_entry(hdr, req->port, | 195 | NVME_NQN_NVME, numrec); |
| 167 | p->subsys->subsysnqn, traddr, | ||
| 168 | NVME_NQN_NVME, numrec); | ||
| 169 | residual_len -= entry_size; | ||
| 170 | } | ||
| 171 | numrec++; | 196 | numrec++; |
| 172 | } | 197 | } |
| 173 | 198 | ||
| 174 | list_for_each_entry(r, &req->port->referrals, entry) { | 199 | list_for_each_entry(r, &req->port->referrals, entry) { |
| 175 | if (residual_len >= entry_size) { | 200 | nvmet_format_discovery_entry(hdr, r, |
| 176 | nvmet_format_discovery_entry(hdr, r, | 201 | NVME_DISC_SUBSYS_NAME, |
| 177 | NVME_DISC_SUBSYS_NAME, | 202 | r->disc_addr.traddr, |
| 178 | r->disc_addr.traddr, | 203 | NVME_NQN_DISC, numrec); |
| 179 | NVME_NQN_DISC, numrec); | ||
| 180 | residual_len -= entry_size; | ||
| 181 | } | ||
| 182 | numrec++; | 204 | numrec++; |
| 183 | } | 205 | } |
| 184 | 206 | ||
| @@ -190,8 +212,8 @@ static void nvmet_execute_get_disc_log_page(struct nvmet_req *req) | |||
| 190 | 212 | ||
| 191 | up_read(&nvmet_config_sem); | 213 | up_read(&nvmet_config_sem); |
| 192 | 214 | ||
| 193 | status = nvmet_copy_to_sgl(req, 0, hdr, data_len); | 215 | status = nvmet_copy_to_sgl(req, 0, buffer + offset, data_len); |
| 194 | kfree(hdr); | 216 | kfree(buffer); |
| 195 | out: | 217 | out: |
| 196 | nvmet_req_complete(req, status); | 218 | nvmet_req_complete(req, status); |
| 197 | } | 219 | } |
diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h index 51e49efd7849..1653d19b187f 100644 --- a/drivers/nvme/target/nvmet.h +++ b/drivers/nvme/target/nvmet.h | |||
| @@ -428,6 +428,7 @@ u16 nvmet_copy_from_sgl(struct nvmet_req *req, off_t off, void *buf, | |||
| 428 | u16 nvmet_zero_sgl(struct nvmet_req *req, off_t off, size_t len); | 428 | u16 nvmet_zero_sgl(struct nvmet_req *req, off_t off, size_t len); |
| 429 | 429 | ||
| 430 | u32 nvmet_get_log_page_len(struct nvme_command *cmd); | 430 | u32 nvmet_get_log_page_len(struct nvme_command *cmd); |
| 431 | u64 nvmet_get_log_page_offset(struct nvme_command *cmd); | ||
| 431 | 432 | ||
| 432 | extern struct list_head *nvmet_ports; | 433 | extern struct list_head *nvmet_ports; |
| 433 | void nvmet_port_disc_changed(struct nvmet_port *port, | 434 | void nvmet_port_disc_changed(struct nvmet_port *port, |
