Commit bcd9a079 authored by Max Gurtovoy's avatar Max Gurtovoy Committed by Christoph Hellwig

nvmet: fix freeing unallocated p2pmem

In case p2p device was found but the p2p pool is empty, the nvme target
is still trying to free the sgl from the p2p pool instead of the
regular sgl pool and causing a crash (BUG() is called). Instead, assign
the p2p_dev for the request only if it was allocated from p2p pool.

This is the crash that was caused:

[Sun May 30 19:13:53 2021] ------------[ cut here ]------------
[Sun May 30 19:13:53 2021] kernel BUG at lib/genalloc.c:518!
[Sun May 30 19:13:53 2021] invalid opcode: 0000 [#1] SMP PTI
...
[Sun May 30 19:13:53 2021] kernel BUG at lib/genalloc.c:518!
...
[Sun May 30 19:13:53 2021] RIP: 0010:gen_pool_free_owner+0xa8/0xb0
...
[Sun May 30 19:13:53 2021] Call Trace:
[Sun May 30 19:13:53 2021] ------------[ cut here ]------------
[Sun May 30 19:13:53 2021]  pci_free_p2pmem+0x2b/0x70
[Sun May 30 19:13:53 2021]  pci_p2pmem_free_sgl+0x4f/0x80
[Sun May 30 19:13:53 2021]  nvmet_req_free_sgls+0x1e/0x80 [nvmet]
[Sun May 30 19:13:53 2021] kernel BUG at lib/genalloc.c:518!
[Sun May 30 19:13:53 2021]  nvmet_rdma_release_rsp+0x4e/0x1f0 [nvmet_rdma]
[Sun May 30 19:13:53 2021]  nvmet_rdma_send_done+0x1c/0x60 [nvmet_rdma]

Fixes: c6e3f133 ("nvmet: add metadata support for block devices")
Reviewed-by: default avatarIsrael Rukshin <israelr@nvidia.com>
Signed-off-by: default avatarMax Gurtovoy <mgurtovoy@nvidia.com>
Reviewed-by: default avatarLogan Gunthorpe <logang@deltatee.com>
Reviewed-by: default avatarChaitanya Kulkarni <chaitanya.kulkarni@wdc.com>
Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
parent 6622f9ac
...@@ -1005,19 +1005,23 @@ static unsigned int nvmet_data_transfer_len(struct nvmet_req *req) ...@@ -1005,19 +1005,23 @@ static unsigned int nvmet_data_transfer_len(struct nvmet_req *req)
return req->transfer_len - req->metadata_len; return req->transfer_len - req->metadata_len;
} }
static int nvmet_req_alloc_p2pmem_sgls(struct nvmet_req *req) static int nvmet_req_alloc_p2pmem_sgls(struct pci_dev *p2p_dev,
struct nvmet_req *req)
{ {
req->sg = pci_p2pmem_alloc_sgl(req->p2p_dev, &req->sg_cnt, req->sg = pci_p2pmem_alloc_sgl(p2p_dev, &req->sg_cnt,
nvmet_data_transfer_len(req)); nvmet_data_transfer_len(req));
if (!req->sg) if (!req->sg)
goto out_err; goto out_err;
if (req->metadata_len) { if (req->metadata_len) {
req->metadata_sg = pci_p2pmem_alloc_sgl(req->p2p_dev, req->metadata_sg = pci_p2pmem_alloc_sgl(p2p_dev,
&req->metadata_sg_cnt, req->metadata_len); &req->metadata_sg_cnt, req->metadata_len);
if (!req->metadata_sg) if (!req->metadata_sg)
goto out_free_sg; goto out_free_sg;
} }
req->p2p_dev = p2p_dev;
return 0; return 0;
out_free_sg: out_free_sg:
pci_p2pmem_free_sgl(req->p2p_dev, req->sg); pci_p2pmem_free_sgl(req->p2p_dev, req->sg);
...@@ -1025,25 +1029,19 @@ static int nvmet_req_alloc_p2pmem_sgls(struct nvmet_req *req) ...@@ -1025,25 +1029,19 @@ static int nvmet_req_alloc_p2pmem_sgls(struct nvmet_req *req)
return -ENOMEM; return -ENOMEM;
} }
static bool nvmet_req_find_p2p_dev(struct nvmet_req *req) static struct pci_dev *nvmet_req_find_p2p_dev(struct nvmet_req *req)
{ {
if (!IS_ENABLED(CONFIG_PCI_P2PDMA)) if (!IS_ENABLED(CONFIG_PCI_P2PDMA) ||
return false; !req->sq->ctrl || !req->sq->qid || !req->ns)
return NULL;
if (req->sq->ctrl && req->sq->qid && req->ns) { return radix_tree_lookup(&req->sq->ctrl->p2p_ns_map, req->ns->nsid);
req->p2p_dev = radix_tree_lookup(&req->sq->ctrl->p2p_ns_map,
req->ns->nsid);
if (req->p2p_dev)
return true;
}
req->p2p_dev = NULL;
return false;
} }
int nvmet_req_alloc_sgls(struct nvmet_req *req) int nvmet_req_alloc_sgls(struct nvmet_req *req)
{ {
if (nvmet_req_find_p2p_dev(req) && !nvmet_req_alloc_p2pmem_sgls(req)) struct pci_dev *p2p_dev = nvmet_req_find_p2p_dev(req);
if (p2p_dev && !nvmet_req_alloc_p2pmem_sgls(p2p_dev, req))
return 0; return 0;
req->sg = sgl_alloc(nvmet_data_transfer_len(req), GFP_KERNEL, req->sg = sgl_alloc(nvmet_data_transfer_len(req), GFP_KERNEL,
...@@ -1072,6 +1070,7 @@ void nvmet_req_free_sgls(struct nvmet_req *req) ...@@ -1072,6 +1070,7 @@ void nvmet_req_free_sgls(struct nvmet_req *req)
pci_p2pmem_free_sgl(req->p2p_dev, req->sg); pci_p2pmem_free_sgl(req->p2p_dev, req->sg);
if (req->metadata_sg) if (req->metadata_sg)
pci_p2pmem_free_sgl(req->p2p_dev, req->metadata_sg); pci_p2pmem_free_sgl(req->p2p_dev, req->metadata_sg);
req->p2p_dev = NULL;
} else { } else {
sgl_free(req->sg); sgl_free(req->sg);
if (req->metadata_sg) if (req->metadata_sg)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment