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

mmc: sh_mmcif: Advance sg_miter before reading blocks

The introduction of sg_miter was a bit sloppy as it didn't
exactly mimic the semantics of the old code on multiblock reads
and writes: these like you to:

- Advance to the first sglist entry *before* starting to read
any blocks from the card.

- Advance and check availability of the next entry *right after*
processing one block.

Not checking if we have more sglist entries right after
reading a block will lead to this not being checked until we
return to the callback to read out more blocks, i.e. until the
next interrupt arrives. Since the last block is the last one
(no more data will arrive) there will not be a next interrupt,
and we will be waiting forever resulting in a timeout for
command 18 when reading multiple blocks.

The same bug was fixed also in the writing of multiple blocks.

Reported-by: Geert Uytterhoeven <geert@linux-m68k.org>
Fixes: 27b57277d9ba ("mmc: sh_mmcif: Use sg_miter for PIO")
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Tested-by: Geert Uytterhoeven <geert+renesas@glider.be>
Link: https://lore.kernel.org/r/20240221-fix-sh-mmcif-v2-2-5e521eb25ae4@linaro.org
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>

authored by

Linus Walleij and committed by
Ulf Hansson
4d4a2c71 727cba70

+26 -12
+26 -12
drivers/mmc/host/sh_mmcif.c
··· 653 653 static void sh_mmcif_multi_read(struct sh_mmcif_host *host, 654 654 struct mmc_request *mrq) 655 655 { 656 + struct sg_mapping_iter *sgm = &host->sg_miter; 656 657 struct mmc_data *data = mrq->data; 657 658 658 659 if (!data->sg_len || !data->sg->length) ··· 662 661 host->blocksize = sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) & 663 662 BLOCK_SIZE_MASK; 664 663 665 - sg_miter_start(&host->sg_miter, data->sg, data->sg_len, 664 + sg_miter_start(sgm, data->sg, data->sg_len, 666 665 SG_MITER_TO_SG); 666 + 667 + /* Advance to the first sglist entry */ 668 + if (!sg_miter_next(sgm)) { 669 + sg_miter_stop(sgm); 670 + return; 671 + } 667 672 668 673 host->wait_for = MMCIF_WAIT_FOR_MREAD; 669 674 ··· 691 684 return false; 692 685 } 693 686 694 - if (!sg_miter_next(sgm)) { 695 - sg_miter_stop(sgm); 696 - return false; 697 - } 698 - 699 687 p = sgm->addr; 700 688 701 689 for (i = 0; i < host->blocksize / 4; i++) ··· 699 697 sgm->consumed = host->blocksize; 700 698 701 699 sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN); 700 + 701 + if (!sg_miter_next(sgm)) { 702 + sg_miter_stop(sgm); 703 + return false; 704 + } 702 705 703 706 return true; 704 707 } ··· 763 756 static void sh_mmcif_multi_write(struct sh_mmcif_host *host, 764 757 struct mmc_request *mrq) 765 758 { 759 + struct sg_mapping_iter *sgm = &host->sg_miter; 766 760 struct mmc_data *data = mrq->data; 767 761 768 762 if (!data->sg_len || !data->sg->length) ··· 772 764 host->blocksize = sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) & 773 765 BLOCK_SIZE_MASK; 774 766 775 - sg_miter_start(&host->sg_miter, data->sg, data->sg_len, 767 + sg_miter_start(sgm, data->sg, data->sg_len, 776 768 SG_MITER_FROM_SG); 769 + 770 + /* Advance to the first sglist entry */ 771 + if (!sg_miter_next(sgm)) { 772 + sg_miter_stop(sgm); 773 + return; 774 + } 777 775 778 776 host->wait_for = MMCIF_WAIT_FOR_MWRITE; 779 777 ··· 801 787 return false; 802 788 } 803 789 804 - if (!sg_miter_next(sgm)) { 805 - sg_miter_stop(sgm); 806 - return false; 807 - } 808 - 809 790 p = sgm->addr; 810 791 811 792 for (i = 0; i < host->blocksize / 4; i++) 812 793 sh_mmcif_writel(host->addr, MMCIF_CE_DATA, *p++); 813 794 814 795 sgm->consumed = host->blocksize; 796 + 797 + if (!sg_miter_next(sgm)) { 798 + sg_miter_stop(sgm); 799 + return false; 800 + } 815 801 816 802 sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); 817 803