Skip to content

Commit d808b7f

Browse files
Keith BuschChristoph Hellwig
authored andcommitted
nvmet: fix discover log page when offsets are used
The nvme target hadn't been taking the Get Log Page offset parameter into consideration, and so has been returning corrupted log pages when offsets are used. Since many tools, including nvme-cli, split the log request to 4k, we've been breaking discovery log responses when more than 3 subsystems exist. Fix the returned data by internally generating the entire discovery log page and copying only the requested bytes into the user buffer. The command log page offset type has been modified to a native __le64 to make it easier to extract the value from a command. Signed-off-by: Keith Busch <[email protected]> Tested-by: Minwoo Im <[email protected]> Reviewed-by: Chaitanya Kulkarni <[email protected]> Reviewed-by: Hannes Reinecke <[email protected]> Reviewed-by: James Smart <[email protected]> Signed-off-by: Christoph Hellwig <[email protected]>
1 parent 67f471b commit d808b7f

File tree

4 files changed

+58
-25
lines changed

4 files changed

+58
-25
lines changed

drivers/nvme/target/admin-cmd.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ u32 nvmet_get_log_page_len(struct nvme_command *cmd)
2424
return len;
2525
}
2626

27+
u64 nvmet_get_log_page_offset(struct nvme_command *cmd)
28+
{
29+
return le64_to_cpu(cmd->get_log_page.lpo);
30+
}
31+
2732
static void nvmet_execute_get_log_page_noop(struct nvmet_req *req)
2833
{
2934
nvmet_req_complete(req, nvmet_zero_sgl(req, 0, req->data_len));

drivers/nvme/target/discovery.c

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -131,54 +131,76 @@ static void nvmet_set_disc_traddr(struct nvmet_req *req, struct nvmet_port *port
131131
memcpy(traddr, port->disc_addr.traddr, NVMF_TRADDR_SIZE);
132132
}
133133

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+
134151
static void nvmet_execute_get_disc_log_page(struct nvmet_req *req)
135152
{
136153
const int entry_size = sizeof(struct nvmf_disc_rsp_page_entry);
137154
struct nvmet_ctrl *ctrl = req->sq->ctrl;
138155
struct nvmf_disc_rsp_page_hdr *hdr;
156+
u64 offset = nvmet_get_log_page_offset(req->cmd);
139157
size_t data_len = nvmet_get_log_page_len(req->cmd);
140-
size_t alloc_len = max(data_len, sizeof(*hdr));
141-
int residual_len = data_len - sizeof(*hdr);
158+
size_t alloc_len;
142159
struct nvmet_subsys_link *p;
143160
struct nvmet_port *r;
144161
u32 numrec = 0;
145162
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+
}
146170

147171
/*
148172
* Make sure we're passing at least a buffer of response header size.
149173
* If host provided data len is less than the header size, only the
150174
* number of bytes requested by host will be sent to host.
151175
*/
152-
hdr = kzalloc(alloc_len, GFP_KERNEL);
153-
if (!hdr) {
176+
down_read(&nvmet_config_sem);
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);
154181
status = NVME_SC_INTERNAL;
155182
goto out;
156183
}
157184

158-
down_read(&nvmet_config_sem);
185+
hdr = buffer;
159186
list_for_each_entry(p, &req->port->subsystems, entry) {
187+
char traddr[NVMF_TRADDR_SIZE];
188+
160189
if (!nvmet_host_allowed(p->subsys, ctrl->hostnqn))
161190
continue;
162-
if (residual_len >= entry_size) {
163-
char traddr[NVMF_TRADDR_SIZE];
164-
165-
nvmet_set_disc_traddr(req, req->port, traddr);
166-
nvmet_format_discovery_entry(hdr, req->port,
167-
p->subsys->subsysnqn, traddr,
168-
NVME_NQN_NVME, numrec);
169-
residual_len -= entry_size;
170-
}
191+
192+
nvmet_set_disc_traddr(req, req->port, traddr);
193+
nvmet_format_discovery_entry(hdr, req->port,
194+
p->subsys->subsysnqn, traddr,
195+
NVME_NQN_NVME, numrec);
171196
numrec++;
172197
}
173198

174199
list_for_each_entry(r, &req->port->referrals, entry) {
175-
if (residual_len >= entry_size) {
176-
nvmet_format_discovery_entry(hdr, r,
177-
NVME_DISC_SUBSYS_NAME,
178-
r->disc_addr.traddr,
179-
NVME_NQN_DISC, numrec);
180-
residual_len -= entry_size;
181-
}
200+
nvmet_format_discovery_entry(hdr, r,
201+
NVME_DISC_SUBSYS_NAME,
202+
r->disc_addr.traddr,
203+
NVME_NQN_DISC, numrec);
182204
numrec++;
183205
}
184206

@@ -190,8 +212,8 @@ static void nvmet_execute_get_disc_log_page(struct nvmet_req *req)
190212

191213
up_read(&nvmet_config_sem);
192214

193-
status = nvmet_copy_to_sgl(req, 0, hdr, data_len);
194-
kfree(hdr);
215+
status = nvmet_copy_to_sgl(req, 0, buffer + offset, data_len);
216+
kfree(buffer);
195217
out:
196218
nvmet_req_complete(req, status);
197219
}

drivers/nvme/target/nvmet.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,7 @@ u16 nvmet_copy_from_sgl(struct nvmet_req *req, off_t off, void *buf,
428428
u16 nvmet_zero_sgl(struct nvmet_req *req, off_t off, size_t len);
429429

430430
u32 nvmet_get_log_page_len(struct nvme_command *cmd);
431+
u64 nvmet_get_log_page_offset(struct nvme_command *cmd);
431432

432433
extern struct list_head *nvmet_ports;
433434
void nvmet_port_disc_changed(struct nvmet_port *port,

include/linux/nvme.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -967,8 +967,13 @@ struct nvme_get_log_page_command {
967967
__le16 numdl;
968968
__le16 numdu;
969969
__u16 rsvd11;
970-
__le32 lpol;
971-
__le32 lpou;
970+
union {
971+
struct {
972+
__le32 lpol;
973+
__le32 lpou;
974+
};
975+
__le64 lpo;
976+
};
972977
__u32 rsvd14[2];
973978
};
974979

0 commit comments

Comments
 (0)