mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 10:49:33 +00:00
nvme: Discard workaround for non-conformant devices
Users observe IOMMU related errors when performing discard on nvme from non-compliant nvme devices reading beyond the end of the DMA mapped ranges to discard. Two different variants of this behavior have been observed: SM22XX controllers round up the read size to a multiple of 512 bytes, and Phison E12 unconditionally reads the maximum discard size allowed by the spec (256 segments or 4kB). Make nvme_setup_discard unconditionally allocate the maximum DSM buffer so the driver DMA maps a memory range that will always succeed. Link: https://bugzilla.kernel.org/show_bug.cgi?id=202665 many Signed-off-by: Eduard Hasenleithner <eduard@hasenleithner.at> [changelog, use existing define, kernel coding style] Signed-off-by: Keith Busch <kbusch@kernel.org>
This commit is contained in:
parent
400b6a7b13
commit
530436c45e
1 changed files with 9 additions and 3 deletions
|
@ -574,8 +574,14 @@ static blk_status_t nvme_setup_discard(struct nvme_ns *ns, struct request *req,
|
||||||
struct nvme_dsm_range *range;
|
struct nvme_dsm_range *range;
|
||||||
struct bio *bio;
|
struct bio *bio;
|
||||||
|
|
||||||
range = kmalloc_array(segments, sizeof(*range),
|
/*
|
||||||
GFP_ATOMIC | __GFP_NOWARN);
|
* Some devices do not consider the DSM 'Number of Ranges' field when
|
||||||
|
* determining how much data to DMA. Always allocate memory for maximum
|
||||||
|
* number of segments to prevent device reading beyond end of buffer.
|
||||||
|
*/
|
||||||
|
static const size_t alloc_size = sizeof(*range) * NVME_DSM_MAX_RANGES;
|
||||||
|
|
||||||
|
range = kzalloc(alloc_size, GFP_ATOMIC | __GFP_NOWARN);
|
||||||
if (!range) {
|
if (!range) {
|
||||||
/*
|
/*
|
||||||
* If we fail allocation our range, fallback to the controller
|
* If we fail allocation our range, fallback to the controller
|
||||||
|
@ -615,7 +621,7 @@ static blk_status_t nvme_setup_discard(struct nvme_ns *ns, struct request *req,
|
||||||
|
|
||||||
req->special_vec.bv_page = virt_to_page(range);
|
req->special_vec.bv_page = virt_to_page(range);
|
||||||
req->special_vec.bv_offset = offset_in_page(range);
|
req->special_vec.bv_offset = offset_in_page(range);
|
||||||
req->special_vec.bv_len = sizeof(*range) * segments;
|
req->special_vec.bv_len = alloc_size;
|
||||||
req->rq_flags |= RQF_SPECIAL_PAYLOAD;
|
req->rq_flags |= RQF_SPECIAL_PAYLOAD;
|
||||||
|
|
||||||
return BLK_STS_OK;
|
return BLK_STS_OK;
|
||||||
|
|
Loading…
Reference in a new issue