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);
|
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.
|
* ata_pio_sector - Transfer a sector of data.
|
||||||
* @qc: Command on going
|
* @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)
|
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 ata_port *ap = qc->ap;
|
||||||
struct page *page;
|
struct page *page;
|
||||||
unsigned int offset;
|
unsigned int offset;
|
||||||
unsigned char *buf;
|
|
||||||
|
|
||||||
if (!qc->cursg) {
|
if (!qc->cursg) {
|
||||||
qc->curbytes = qc->nbytes;
|
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");
|
DPRINTK("data %s\n", qc->tf.flags & ATA_TFLAG_WRITE ? "write" : "read");
|
||||||
|
|
||||||
/* do the actual data transfer */
|
/*
|
||||||
buf = kmap_atomic(page);
|
* Split the transfer when it splits a page boundary. Note that the
|
||||||
ap->ops->sff_data_xfer(qc, buf + offset, qc->sect_size, do_write);
|
* split still has to be dword aligned like all ATA data transfers.
|
||||||
kunmap_atomic(buf);
|
*/
|
||||||
|
WARN_ON_ONCE(offset % 4);
|
||||||
|
if (offset + qc->sect_size > PAGE_SIZE) {
|
||||||
|
unsigned int split_len = PAGE_SIZE - offset;
|
||||||
|
|
||||||
if (!do_write && !PageSlab(page))
|
ata_pio_xfer(qc, page, offset, split_len);
|
||||||
flush_dcache_page(page);
|
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->curbytes += qc->sect_size;
|
||||||
qc->cursg_ofs += qc->sect_size;
|
qc->cursg_ofs += qc->sect_size;
|
||||||
|
|
Loading…
Reference in New Issue