]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - releases/5.0.15/nvmet-fix-discover-log-page-when-offsets-are-used.patch
Linux 5.0.15
[thirdparty/kernel/stable-queue.git] / releases / 5.0.15 / nvmet-fix-discover-log-page-when-offsets-are-used.patch
1 From 725f95a0e35da96c404dcbcce719706df2598bf7 Mon Sep 17 00:00:00 2001
2 From: Keith Busch <keith.busch@intel.com>
3 Date: Tue, 9 Apr 2019 10:03:59 -0600
4 Subject: nvmet: fix discover log page when offsets are used
5
6 [ Upstream commit d808b7f759b50acf0784ce6230ffa63e12ef465d ]
7
8 The nvme target hadn't been taking the Get Log Page offset parameter
9 into consideration, and so has been returning corrupted log pages when
10 offsets are used. Since many tools, including nvme-cli, split the log
11 request to 4k, we've been breaking discovery log responses when more
12 than 3 subsystems exist.
13
14 Fix the returned data by internally generating the entire discovery
15 log page and copying only the requested bytes into the user buffer. The
16 command log page offset type has been modified to a native __le64 to
17 make it easier to extract the value from a command.
18
19 Signed-off-by: Keith Busch <keith.busch@intel.com>
20 Tested-by: Minwoo Im <minwoo.im@samsung.com>
21 Reviewed-by: Chaitanya Kulkarni <chaitanya.kulkarni@wdc.com>
22 Reviewed-by: Hannes Reinecke <hare@suse.com>
23 Reviewed-by: James Smart <james.smart@broadcom.com>
24 Signed-off-by: Christoph Hellwig <hch@lst.de>
25 Signed-off-by: Sasha Levin <sashal@kernel.org>
26 ---
27 drivers/nvme/target/admin-cmd.c | 5 +++
28 drivers/nvme/target/discovery.c | 68 ++++++++++++++++++++++-----------
29 drivers/nvme/target/nvmet.h | 1 +
30 include/linux/nvme.h | 9 ++++-
31 4 files changed, 58 insertions(+), 25 deletions(-)
32
33 diff --git a/drivers/nvme/target/admin-cmd.c b/drivers/nvme/target/admin-cmd.c
34 index 11baeb14c3881..8fdae510c5ac0 100644
35 --- a/drivers/nvme/target/admin-cmd.c
36 +++ b/drivers/nvme/target/admin-cmd.c
37 @@ -32,6 +32,11 @@ u32 nvmet_get_log_page_len(struct nvme_command *cmd)
38 return len;
39 }
40
41 +u64 nvmet_get_log_page_offset(struct nvme_command *cmd)
42 +{
43 + return le64_to_cpu(cmd->get_log_page.lpo);
44 +}
45 +
46 static void nvmet_execute_get_log_page_noop(struct nvmet_req *req)
47 {
48 nvmet_req_complete(req, nvmet_zero_sgl(req, 0, req->data_len));
49 diff --git a/drivers/nvme/target/discovery.c b/drivers/nvme/target/discovery.c
50 index d2cb71a0b419d..389c1a90197d9 100644
51 --- a/drivers/nvme/target/discovery.c
52 +++ b/drivers/nvme/target/discovery.c
53 @@ -139,54 +139,76 @@ static void nvmet_set_disc_traddr(struct nvmet_req *req, struct nvmet_port *port
54 memcpy(traddr, port->disc_addr.traddr, NVMF_TRADDR_SIZE);
55 }
56
57 +static size_t discovery_log_entries(struct nvmet_req *req)
58 +{
59 + struct nvmet_ctrl *ctrl = req->sq->ctrl;
60 + struct nvmet_subsys_link *p;
61 + struct nvmet_port *r;
62 + size_t entries = 0;
63 +
64 + list_for_each_entry(p, &req->port->subsystems, entry) {
65 + if (!nvmet_host_allowed(p->subsys, ctrl->hostnqn))
66 + continue;
67 + entries++;
68 + }
69 + list_for_each_entry(r, &req->port->referrals, entry)
70 + entries++;
71 + return entries;
72 +}
73 +
74 static void nvmet_execute_get_disc_log_page(struct nvmet_req *req)
75 {
76 const int entry_size = sizeof(struct nvmf_disc_rsp_page_entry);
77 struct nvmet_ctrl *ctrl = req->sq->ctrl;
78 struct nvmf_disc_rsp_page_hdr *hdr;
79 + u64 offset = nvmet_get_log_page_offset(req->cmd);
80 size_t data_len = nvmet_get_log_page_len(req->cmd);
81 - size_t alloc_len = max(data_len, sizeof(*hdr));
82 - int residual_len = data_len - sizeof(*hdr);
83 + size_t alloc_len;
84 struct nvmet_subsys_link *p;
85 struct nvmet_port *r;
86 u32 numrec = 0;
87 u16 status = 0;
88 + void *buffer;
89 +
90 + /* Spec requires dword aligned offsets */
91 + if (offset & 0x3) {
92 + status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
93 + goto out;
94 + }
95
96 /*
97 * Make sure we're passing at least a buffer of response header size.
98 * If host provided data len is less than the header size, only the
99 * number of bytes requested by host will be sent to host.
100 */
101 - hdr = kzalloc(alloc_len, GFP_KERNEL);
102 - if (!hdr) {
103 + down_read(&nvmet_config_sem);
104 + alloc_len = sizeof(*hdr) + entry_size * discovery_log_entries(req);
105 + buffer = kzalloc(alloc_len, GFP_KERNEL);
106 + if (!buffer) {
107 + up_read(&nvmet_config_sem);
108 status = NVME_SC_INTERNAL;
109 goto out;
110 }
111
112 - down_read(&nvmet_config_sem);
113 + hdr = buffer;
114 list_for_each_entry(p, &req->port->subsystems, entry) {
115 + char traddr[NVMF_TRADDR_SIZE];
116 +
117 if (!nvmet_host_allowed(p->subsys, ctrl->hostnqn))
118 continue;
119 - if (residual_len >= entry_size) {
120 - char traddr[NVMF_TRADDR_SIZE];
121 -
122 - nvmet_set_disc_traddr(req, req->port, traddr);
123 - nvmet_format_discovery_entry(hdr, req->port,
124 - p->subsys->subsysnqn, traddr,
125 - NVME_NQN_NVME, numrec);
126 - residual_len -= entry_size;
127 - }
128 +
129 + nvmet_set_disc_traddr(req, req->port, traddr);
130 + nvmet_format_discovery_entry(hdr, req->port,
131 + p->subsys->subsysnqn, traddr,
132 + NVME_NQN_NVME, numrec);
133 numrec++;
134 }
135
136 list_for_each_entry(r, &req->port->referrals, entry) {
137 - if (residual_len >= entry_size) {
138 - nvmet_format_discovery_entry(hdr, r,
139 - NVME_DISC_SUBSYS_NAME,
140 - r->disc_addr.traddr,
141 - NVME_NQN_DISC, numrec);
142 - residual_len -= entry_size;
143 - }
144 + nvmet_format_discovery_entry(hdr, r,
145 + NVME_DISC_SUBSYS_NAME,
146 + r->disc_addr.traddr,
147 + NVME_NQN_DISC, numrec);
148 numrec++;
149 }
150
151 @@ -198,8 +220,8 @@ static void nvmet_execute_get_disc_log_page(struct nvmet_req *req)
152
153 up_read(&nvmet_config_sem);
154
155 - status = nvmet_copy_to_sgl(req, 0, hdr, data_len);
156 - kfree(hdr);
157 + status = nvmet_copy_to_sgl(req, 0, buffer + offset, data_len);
158 + kfree(buffer);
159 out:
160 nvmet_req_complete(req, status);
161 }
162 diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h
163 index 3e4719fdba854..d253c45c1aa6e 100644
164 --- a/drivers/nvme/target/nvmet.h
165 +++ b/drivers/nvme/target/nvmet.h
166 @@ -436,6 +436,7 @@ u16 nvmet_copy_from_sgl(struct nvmet_req *req, off_t off, void *buf,
167 u16 nvmet_zero_sgl(struct nvmet_req *req, off_t off, size_t len);
168
169 u32 nvmet_get_log_page_len(struct nvme_command *cmd);
170 +u64 nvmet_get_log_page_offset(struct nvme_command *cmd);
171
172 extern struct list_head *nvmet_ports;
173 void nvmet_port_disc_changed(struct nvmet_port *port,
174 diff --git a/include/linux/nvme.h b/include/linux/nvme.h
175 index bbcc83886899c..7ba0368f16e6e 100644
176 --- a/include/linux/nvme.h
177 +++ b/include/linux/nvme.h
178 @@ -975,8 +975,13 @@ struct nvme_get_log_page_command {
179 __le16 numdl;
180 __le16 numdu;
181 __u16 rsvd11;
182 - __le32 lpol;
183 - __le32 lpou;
184 + union {
185 + struct {
186 + __le32 lpol;
187 + __le32 lpou;
188 + };
189 + __le64 lpo;
190 + };
191 __u32 rsvd14[2];
192 };
193
194 --
195 2.20.1
196