Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

nvme-pci: Remove nvme_setup_prps BUG_ON

This patch replaces the invalid nvme SGL kernel panic with a warning,
and returns an appropriate error. The warning will occur only on the
first occurance, and sgl details will be printed to help debug how the
request was allowed to form.

Signed-off-by: Keith Busch <keith.busch@intel.com>
Reviewed-by: Johannes Thumshirn <jthumshirn@suse.de>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Sagi Grimberg <sagi@grimberg.me>
Signed-off-by: Jens Axboe <axboe@kernel.dk>

authored by

Keith Busch and committed by
Jens Axboe
86eea289 f99cb7af

+25 -8
+25 -8
drivers/nvme/host/pci.c
··· 539 539 } 540 540 #endif 541 541 542 - static bool nvme_setup_prps(struct nvme_dev *dev, struct request *req) 542 + static blk_status_t nvme_setup_prps(struct nvme_dev *dev, struct request *req) 543 543 { 544 544 struct nvme_iod *iod = blk_mq_rq_to_pdu(req); 545 545 struct dma_pool *pool; ··· 556 556 557 557 length -= (page_size - offset); 558 558 if (length <= 0) 559 - return true; 559 + return BLK_STS_OK; 560 560 561 561 dma_len -= (page_size - offset); 562 562 if (dma_len) { ··· 569 569 570 570 if (length <= page_size) { 571 571 iod->first_dma = dma_addr; 572 - return true; 572 + return BLK_STS_OK; 573 573 } 574 574 575 575 nprps = DIV_ROUND_UP(length, page_size); ··· 585 585 if (!prp_list) { 586 586 iod->first_dma = dma_addr; 587 587 iod->npages = -1; 588 - return false; 588 + return BLK_STS_RESOURCE; 589 589 } 590 590 list[0] = prp_list; 591 591 iod->first_dma = prp_dma; ··· 595 595 __le64 *old_prp_list = prp_list; 596 596 prp_list = dma_pool_alloc(pool, GFP_ATOMIC, &prp_dma); 597 597 if (!prp_list) 598 - return false; 598 + return BLK_STS_RESOURCE; 599 599 list[iod->npages++] = prp_list; 600 600 prp_list[0] = old_prp_list[i - 1]; 601 601 old_prp_list[i - 1] = cpu_to_le64(prp_dma); ··· 609 609 break; 610 610 if (dma_len > 0) 611 611 continue; 612 - BUG_ON(dma_len < 0); 612 + if (unlikely(dma_len < 0)) 613 + goto bad_sgl; 613 614 sg = sg_next(sg); 614 615 dma_addr = sg_dma_address(sg); 615 616 dma_len = sg_dma_len(sg); 616 617 } 617 618 618 - return true; 619 + return BLK_STS_OK; 620 + 621 + bad_sgl: 622 + if (WARN_ONCE(1, "Invalid SGL for payload:%d nents:%d\n", 623 + blk_rq_payload_bytes(req), iod->nents)) { 624 + for_each_sg(iod->sg, sg, iod->nents, i) { 625 + dma_addr_t phys = sg_phys(sg); 626 + pr_warn("sg[%d] phys_addr:%pad offset:%d length:%d " 627 + "dma_address:%pad dma_length:%d\n", i, &phys, 628 + sg->offset, sg->length, 629 + &sg_dma_address(sg), 630 + sg_dma_len(sg)); 631 + } 632 + } 633 + return BLK_STS_IOERR; 634 + 619 635 } 620 636 621 637 static blk_status_t nvme_map_data(struct nvme_dev *dev, struct request *req, ··· 653 637 DMA_ATTR_NO_WARN)) 654 638 goto out; 655 639 656 - if (!nvme_setup_prps(dev, req)) 640 + ret = nvme_setup_prps(dev, req); 641 + if (ret != BLK_STS_OK) 657 642 goto out_unmap; 658 643 659 644 ret = BLK_STS_IOERR;