Skip to content

Commit 8407879

Browse files
sagigrimbergChristoph Hellwig
authored andcommitted
nvmet-rdma: fix possible bogus dereference under heavy load
Currently we always repost the recv buffer before we send a response capsule back to the host. Since ordering is not guaranteed for send and recv completions, it is posible that we will receive a new request from the host before we got a send completion for the response capsule. Today, we pre-allocate 2x rsps the length of the queue, but in reality, under heavy load there is nothing that is really preventing the gap to expand until we exhaust all our rsps. To fix this, if we don't have any pre-allocated rsps left, we dynamically allocate a rsp and make sure to free it when we are done. If under memory pressure we fail to allocate a rsp, we silently drop the command and wait for the host to retry. Reported-by: Steve Wise <swise@opengridcomputing.com> Tested-by: Steve Wise <swise@opengridcomputing.com> Signed-off-by: Sagi Grimberg <sagi@grimberg.me> [hch: dropped a superflous assignment] Signed-off-by: Christoph Hellwig <hch@lst.de>
1 parent bc811f0 commit 8407879

File tree

1 file changed

+25
-2
lines changed

1 file changed

+25
-2
lines changed

drivers/nvme/target/rdma.c

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ struct nvmet_rdma_rsp {
6666

6767
struct nvmet_req req;
6868

69+
bool allocated;
6970
u8 n_rdma;
7071
u32 flags;
7172
u32 invalidate_rkey;
@@ -174,11 +175,19 @@ nvmet_rdma_get_rsp(struct nvmet_rdma_queue *queue)
174175
unsigned long flags;
175176

176177
spin_lock_irqsave(&queue->rsps_lock, flags);
177-
rsp = list_first_entry(&queue->free_rsps,
178+
rsp = list_first_entry_or_null(&queue->free_rsps,
178179
struct nvmet_rdma_rsp, free_list);
179-
list_del(&rsp->free_list);
180+
if (likely(rsp))
181+
list_del(&rsp->free_list);
180182
spin_unlock_irqrestore(&queue->rsps_lock, flags);
181183

184+
if (unlikely(!rsp)) {
185+
rsp = kmalloc(sizeof(*rsp), GFP_KERNEL);
186+
if (unlikely(!rsp))
187+
return NULL;
188+
rsp->allocated = true;
189+
}
190+
182191
return rsp;
183192
}
184193

@@ -187,6 +196,11 @@ nvmet_rdma_put_rsp(struct nvmet_rdma_rsp *rsp)
187196
{
188197
unsigned long flags;
189198

199+
if (rsp->allocated) {
200+
kfree(rsp);
201+
return;
202+
}
203+
190204
spin_lock_irqsave(&rsp->queue->rsps_lock, flags);
191205
list_add_tail(&rsp->free_list, &rsp->queue->free_rsps);
192206
spin_unlock_irqrestore(&rsp->queue->rsps_lock, flags);
@@ -776,6 +790,15 @@ static void nvmet_rdma_recv_done(struct ib_cq *cq, struct ib_wc *wc)
776790

777791
cmd->queue = queue;
778792
rsp = nvmet_rdma_get_rsp(queue);
793+
if (unlikely(!rsp)) {
794+
/*
795+
* we get here only under memory pressure,
796+
* silently drop and have the host retry
797+
* as we can't even fail it.
798+
*/
799+
nvmet_rdma_post_recv(queue->dev, cmd);
800+
return;
801+
}
779802
rsp->queue = queue;
780803
rsp->cmd = cmd;
781804
rsp->flags = 0;

0 commit comments

Comments
 (0)