Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e30a88f1 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Greg Kroah-Hartman
Browse files

libata: fix ata_pio_sector for CONFIG_HIGHMEM



[ Upstream commit ecef6a9effe49e8e2635c839020b9833b71e934c ]

Data transfers are not required to be block aligned in memory, so they
span two pages.  Fix this by splitting the call to >sff_data_xfer into
two for that case.

This has been broken since the initial libata import before the damn
of git, but was uncovered by the legacy ide driver removal.

Reported-by: default avatarkernel test robot <oliver.sang@intel.com>
Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Link: https://lore.kernel.org/r/20210709130237.3730959-1-hch@lst.de


Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent a2671d96
Loading
Loading
Loading
Loading
+27 −8
Original line number Diff line number Diff line
@@ -641,6 +641,20 @@ unsigned int ata_sff_data_xfer32(struct ata_queued_cmd *qc, unsigned char *buf,
}
EXPORT_SYMBOL_GPL(ata_sff_data_xfer32);

static void ata_pio_xfer(struct ata_queued_cmd *qc, struct page *page,
		unsigned int offset, size_t xfer_size)
{
	bool do_write = (qc->tf.flags & ATA_TFLAG_WRITE);
	unsigned char *buf;

	buf = kmap_atomic(page);
	qc->ap->ops->sff_data_xfer(qc, buf + offset, xfer_size, do_write);
	kunmap_atomic(buf);

	if (!do_write && !PageSlab(page))
		flush_dcache_page(page);
}

/**
 *	ata_pio_sector - Transfer a sector of data.
 *	@qc: Command on going
@@ -652,11 +666,9 @@ EXPORT_SYMBOL_GPL(ata_sff_data_xfer32);
 */
static void ata_pio_sector(struct ata_queued_cmd *qc)
{
	int do_write = (qc->tf.flags & ATA_TFLAG_WRITE);
	struct ata_port *ap = qc->ap;
	struct page *page;
	unsigned int offset;
	unsigned char *buf;

	if (!qc->cursg) {
		qc->curbytes = qc->nbytes;
@@ -674,13 +686,20 @@ static void ata_pio_sector(struct ata_queued_cmd *qc)

	DPRINTK("data %s\n", qc->tf.flags & ATA_TFLAG_WRITE ? "write" : "read");

	/* do the actual data transfer */
	buf = kmap_atomic(page);
	ap->ops->sff_data_xfer(qc, buf + offset, qc->sect_size, do_write);
	kunmap_atomic(buf);
	/*
	 * Split the transfer when it splits a page boundary.  Note that the
	 * split still has to be dword aligned like all ATA data transfers.
	 */
	WARN_ON_ONCE(offset % 4);
	if (offset + qc->sect_size > PAGE_SIZE) {
		unsigned int split_len = PAGE_SIZE - offset;

	if (!do_write && !PageSlab(page))
		flush_dcache_page(page);
		ata_pio_xfer(qc, page, offset, split_len);
		ata_pio_xfer(qc, nth_page(page, 1), 0,
			     qc->sect_size - split_len);
	} else {
		ata_pio_xfer(qc, page, offset, qc->sect_size);
	}

	qc->curbytes += qc->sect_size;
	qc->cursg_ofs += qc->sect_size;