Skip to content

Commit

Permalink
libata: fix ata_pio_sector for CONFIG_HIGHMEM
Browse files Browse the repository at this point in the history
[ Upstream commit ecef6a9 ]

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: kernel test robot <oliver.sang@intel.com>
Signed-off-by: Christoph Hellwig <hch@lst.de>
Link: https://lore.kernel.org/r/20210709130237.3730959-1-hch@lst.de
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
Christoph Hellwig authored and gregkh committed Aug 12, 2021
1 parent fb9501e commit aa54be4
Showing 1 changed file with 27 additions and 8 deletions.
35 changes: 27 additions & 8 deletions drivers/ata/libata-sff.c
Expand Up @@ -637,6 +637,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
Expand All @@ -648,11 +662,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;
Expand All @@ -670,13 +682,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;
Expand Down

0 comments on commit aa54be4

Please sign in to comment.