lkml.org 
[lkml]   [2019]   [Dec]   [29]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 5.4 296/434] nvme: Discard workaround for non-conformant devices
    Date
    From: Eduard Hasenleithner <eduard@hasenleithner.at>

    [ Upstream commit 530436c45ef2e446c12538a400e465929a0b3ade ]

    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>
    Signed-off-by: Sasha Levin <sashal@kernel.org>
    ---
    drivers/nvme/host/core.c | 12 +++++++++---
    1 file changed, 9 insertions(+), 3 deletions(-)

    diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
    index b4e1e4379f1f..a6b7b242d516 100644
    --- a/drivers/nvme/host/core.c
    +++ b/drivers/nvme/host/core.c
    @@ -611,8 +611,14 @@ static blk_status_t nvme_setup_discard(struct nvme_ns *ns, struct request *req,
    struct nvme_dsm_range *range;
    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 we fail allocation our range, fallback to the controller
    @@ -652,7 +658,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_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;

    return BLK_STS_OK;
    --
    2.20.1


    \
     
     \ /
      Last update: 2019-12-29 19:03    [W:2.534 / U:2.976 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site