nvme: Don't use a stack buffer for keep-alive command
PATCH 2/2 nvme: Don't use a stack buffer for keep-alive command
From: Roland Dreier <[email protected]>
In nvme_keep_alive() we pass a request with a pointer to an NVMe command on the stack into blk_execute_rq_nowait(). However, the block layer doesn't guarantee that the request is fully queued before blk_execute_rq_nowait() returns. If not, and the request is queued after nvme_keep_alive() returns, then we'll end up using stack memory that might have been overwritten to form the NVMe command we pass to hardware.
Fix this by keeping a special command struct in the nvme_ctrl struct right next to the delayed work struct used for keep-alives.
--- drivers/nvme/host/core.c | 8 +++----- drivers/nvme/host/nvme.h | 1 + 2 files changed, 4 insertions(+), 5 deletions(-)
drivers/nvme/host/core.c +3 -5
@@ -748,13 +748,9 @@ static void nvme_keep_alive_end_io(struct request *rq, blk_status_t status)static int nvme_keep_alive (struct nvme_ctrl *ctrl) { -struct nvme_command c;struct request *rq; -memset (&c,0 ,sizeof (c)); - c.common.opcode = nvme_admin_keep_alive; - - rq =nvme_alloc_request (ctrl->admin_q, &c,BLK_MQ_REQ_RESERVED , + rq =nvme_alloc_request (ctrl->admin_q, &ctrl->ka_cmd,BLK_MQ_REQ_RESERVED ,NVME_QID_ANY );if (IS_ERR (rq))return PTR_ERR (rq);
@@ -786,6 +782,8 @@ void nvme_start_keep_alive(struct nvme_ctrl *ctrl)return ;INIT_DELAYED_WORK (&ctrl->ka_work, nvme_keep_alive_work); +memset (&ctrl->ka_cmd,0 ,sizeof (ctrl->ka_cmd)); + ctrl->ka_cmd.common.opcode = nvme_admin_keep_alive;schedule_delayed_work (&ctrl->ka_work, ctrl->kato *HZ ); }EXPORT_SYMBOL_GPL (nvme_start_keep_alive);
drivers/nvme/host/nvme.h +1 -0
@@ -180,6 +180,7 @@ struct nvme_ctrl {struct work_struct scan_work;struct work_struct async_event_work;struct delayed_work ka_work; +struct nvme_command ka_cmd;struct work_struct fw_act_work;/* Power saving configuration */
Signature · 1 line
2.14.1
Sagi Grimberg
maintainerPATCH 2/2 nvme: Don't use a stack buffer for keep-alive command
Quoted 10 lines
From: Roland Dreier <roland at purestorage.com>
In
nvme_keep_alive()we pass a request with a pointer to an NVMe command on the stack intoblk_execute_rq_nowait(). However, the block layer doesn't guarantee that the request is fully queued beforeblk_execute_rq_nowait()returns. If not, and the request is queued afternvme_keep_alive()returns, then we'll end up using stack memory that might have been overwritten to form the NVMe command we pass to hardware.
Quoted 2 lines
Fix this by keeping a special command struct in the
nvme_ctrlstruct right next to the delayed work struct used for keep-alives.
Thanks Roland,
Christoph Hellwig
maintainerPATCH 2/2 nvme: Don't use a stack buffer for keep-alive command
Quoted 3 lines · Sagi Grimberg
Thanks Roland,
Reviewed-by Sagi Grimberg <sagi at grimberg.me>
I think we'll need to fix this properly and embedd the struct nvme_command into struct nvme_request. In the end any command could get an error without DNR, and then we'd have a stale SQE on the stack.
A message that is not in the archive (sent off-list or not yet received): CAG4TOxOtFqM-RAdS_r1hsPVuru_=abDtTUmB=XqGFaanBrqbEQ@mail.gmail.com
Christoph Hellwig
maintainerPATCH 2/2 nvme: Don't use a stack buffer for keep-alive command
Quoted 8 lines · Roland Dreier
Quoted 3 lines
I think we'll need to fix this properly and embedd the struct
nvme_commandinto structnvme_request. In the end any command could get an error without DNR, and then we'd have a stale SQE on the stack.I don't understand. Are there other places that submit requests with a pointer to stack memory? I haven't audited everything but I don't know of any places that submit a command and then free it before getting status back.
Every caller of nvme_alloc_request (except for lightnvm) uses stack memory, but at least the __nvme_submit_sync_cmd and nvme_submit_user_cmd synchronously wait for the completion, so it doesn't matter. That leaves nvme_keep_alive, nvme_timeout and nvme_delete_queue as problematic.
I suspect the right answer is to embedd a struct nvme_command into struct nvme_request instead of just pointing to it.
PATCH 2/2 nvme: Don't use a stack buffer for keep-alive command
In reply to Christoph HellwigQuoted 8 lines · Christoph Hellwig
Quoted 3 lines · Sagi Grimberg
Thanks Roland,
Reviewed-by Sagi Grimberg <sagi at grimberg.me>I think we'll need to fix this properly and embedd the struct
nvme_commandinto structnvme_request. In the end any command could get an error without DNR, and then we'd have a stale SQE on the stack.
We needn't worry about the DNR case since driver allocated commands are flagged "REQ_FAILFAST_DRIVER" and are never retried.
Allocating the full 64-byte NVMe command for each request seems a bit excessive. Since it only really applies to async driver commands, and there are so few of those as it is, I'm okay with having special cases for these as Roland suggests. Sound okay?
Sagi Grimberg
maintainerPATCH 2/2 nvme: Don't use a stack buffer for keep-alive command
Quoted 3 lines
We needn't worry about the DNR case since driver allocated commands are flagged "
REQ_FAILFAST_DRIVER" and are never retried.
Yea.
Quoted 2 lines
Allocating the full 64-byte NVMe command for each request seems a bit excessive.
Why?
Quoted 3 lines
Since it only really applies to async driver commands, and there are so few of those as it is, I'm okay with having special cases for these as Roland suggests. Sound okay?
I'm fine with fixing this locally, btw isn't this exact same stale sqe happen in pci's nvme_delete_queue?
PATCH 2/2 nvme: Don't use a stack buffer for keep-alive command
Quoted 4 lines · Sagi Grimberg
Quoted 2 lines
Allocating the full 64-byte NVMe command for each request seems a bit excessive.
Why?
That's 64B per request that would rarely ever get used. If you've enough drives with high queue counts, this can add up to quite a lot of memory.
PATCH 2/2 nvme: Don't use a stack buffer for keep-alive command
In reply to Sagi GrimbergQuoted 2 lines · Sagi Grimberg
I'm fine with fixing this locally, btw isn't this exact same stale sqe happen in pci's
nvme_delete_queue?
Yeah, this is the other case. Fortunately that never gets called on a quiesced admin queue, so we should be 'ok', but moving the command from the stack into struct nvme_queue would be the right thing to do.
Sagi Grimberg
maintainerPATCH 2/2 nvme: Don't use a stack buffer for keep-alive command
Quoted 7 lines
Quoted 3 lines
I'm fine with fixing this locally, btw isn't this exact same stale sqe happen in pci's
nvme_delete_queue?Yeah, this is the other case. Fortunately that never gets called on a quiesced admin queue, so we should be 'ok', but moving the command from the stack into struct
nvme_queuewould be the right thing to do.
So are you fine with picking Roland's patch to 4.16-rc?
Christoph?
PATCH 2/2 nvme: Don't use a stack buffer for keep-alive command
Quoted 8 lines · Sagi Grimberg
Quoted 6 lines
Quoted 2 lines
I'm fine with fixing this locally, btw isn't this exact same stale sqe happen in pci's
nvme_delete_queue?Yeah, this is the other case. Fortunately that never gets called on a quiesced admin queue, so we should be 'ok', but moving the command from the stack into struct
nvme_queuewould be the right thing to do.So are you fine with picking Roland's patch to 4.16-rc?
I am okay with it. If we need to change this interface in the long term, this is looks like an okay stop-gap until then.
Quoted 1 line
Christoph?