libata: fix ata_pio_sector for CONFIG_HIGHMEM
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>
This commit is contained in:
parent
f55966571d
commit
ecef6a9eff
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue