Skip to content

Commit

Permalink
ATA: honor maximum number of sectors in each I/O request
Browse files Browse the repository at this point in the history
This commit adds a new function to the ATA disk driver to retrieve
the maxiumum number of sectors that can be submitted in a single
I/O request, which depends on whether LBA48 addressing is
supported. This new function is called by both the x86_64
bootloader and the kernel ATA PCI driver, which check the maximum
sector count value to split each I/O request so that it doesn't
exceed the limit.
This fixes booting on vsphere VMs with IDE disk emulation.
  • Loading branch information
francescolavra committed Mar 14, 2023
1 parent 469d2e6 commit c3415bd
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 12 deletions.
10 changes: 4 additions & 6 deletions platform/pc/boot/stage2.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,8 @@ closure_function(1, 1, void, stage2_bios_read,
apply(req->completion, STATUS_OK);
}

#define MAX_BLOCK_IO_SIZE (64 * 1024)

closure_function(2, 1, void, stage2_ata_read,
struct ata *, dev, u64, offset,
closure_function(3, 1, void, stage2_ata_read,
struct ata *, dev, u64, offset, u64, io_max_blocks,
storage_req, req)
{
if (req->op != STORAGE_OP_READSG)
Expand All @@ -135,7 +133,7 @@ closure_function(2, 1, void, stage2_ata_read,
merge m = allocate_merge(h, req->completion);
status_handler k = apply_merge(m);
while (blocks.start < blocks.end) {
u64 span = MIN(range_span(blocks), MAX_BLOCK_IO_SIZE >> SECTOR_OFFSET);
u64 span = MIN(range_span(blocks), bound(io_max_blocks));
sg_buf sgb = sg_list_head_peek(sg);
void *dest = sgb->buf + sgb->offset;
span = MIN(span, sg_buf_len(sgb) >> SECTOR_OFFSET);
Expand Down Expand Up @@ -181,7 +179,7 @@ static storage_req_handler get_stage2_disk_read(heap general, u64 fs_offset)
return closure(general, stage2_bios_read, fs_offset);
}

return closure(general, stage2_ata_read, dev, fs_offset);
return closure(general, stage2_ata_read, dev, fs_offset, ata_get_io_max_blocks(dev));
}

closure_function(0, 1, void, fail,
Expand Down
27 changes: 21 additions & 6 deletions src/drivers/ata-pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ declare_closure_struct(1, 0, void, ata_pci_service,
typedef struct ata_pci {
heap h;
struct ata *ata;
u64 io_max_blocks;
struct prd *prdt; /* physical region descriptor table */
u64 prdt_phys;
struct pci_bar bmr; /* bus master register */
Expand Down Expand Up @@ -160,9 +161,10 @@ static boolean ata_pci_service_req(ata_pci apci, ata_pci_req req)
ata_debug("%s: %R %v\n", __func__, req->remain, req->s);
if (!is_ok(req->s))
goto done;
u64 byte_count = range_span(req->remain) * SECTOR_SIZE;
if (byte_count == 0)
u64 block_count = MIN(range_span(req->remain), apci->io_max_blocks);
if (block_count == 0)
goto done;
u64 byte_count = block_count * SECTOR_SIZE;
u64 buf_phys;
int prd_count = 0;
while (byte_count > 0 && prd_count < PRDT_ENTRIES) {
Expand All @@ -186,8 +188,7 @@ static boolean ata_pci_service_req(ata_pci apci, ata_pci_req req)
}
if (prd_count > 0) {
apci->prdt[prd_count - 1].ctrl = PRD_LAST_ENTRY;
range blocks = irange(req->remain.start,
req->remain.end - byte_count / SECTOR_SIZE);
range blocks = irangel(req->remain.start, block_count);
ata_debug("%s: starting DMA for %R\n", __func__, blocks);
pci_bar_write_4(&apci->bmr, ATA_BMR_PRDT(ATA_PRIMARY), apci->prdt_phys);
if (!ata_io_cmd_dma(apci->ata, req->write, blocks)) {
Expand All @@ -202,8 +203,21 @@ static boolean ata_pci_service_req(ata_pci apci, ata_pci_req req)
}

/* Couldn't use DMA, fall back to PIO. */
apply(req->write ? apci->pio_write : apci->pio_read, req->buf, req->remain,
req->sh);
block_io bio = req->write ? apci->pio_write : apci->pio_read;
if (range_span(req->remain) <= apci->io_max_blocks) {
apply(bio, req->buf, req->remain, req->sh);
} else {
merge m = allocate_merge(apci->h, req->sh);
status_handler sh = apply_merge(m);
do {
range blocks = irangel(req->remain.start, block_count);
apply(bio, req->buf, blocks, apply_merge(m));
req->buf += block_count << SECTOR_OFFSET;
req->remain.start += block_count;
block_count = MIN(range_span(req->remain), apci->io_max_blocks);
} while (block_count > 0);
apply(sh, STATUS_OK);
}
return true;

done:
Expand Down Expand Up @@ -312,6 +326,7 @@ closure_function(3, 1, boolean, ata_pci_probe,
dev->prdt = pointer_from_u64(u64_from_pointer(dev->prdt) + align_offset);

dev->h = general;
dev->io_max_blocks = ata_get_io_max_blocks(dev->ata);
pci_bar_init(d, &dev->bmr, 4, 0, -1);
pci_set_bus_master(d);
list_init(&dev->reqs);
Expand Down
5 changes: 5 additions & 0 deletions src/drivers/ata.c
Original file line number Diff line number Diff line change
Expand Up @@ -392,3 +392,8 @@ u64 ata_get_capacity(struct ata *dev)
{
return dev->capacity;
}

u64 ata_get_io_max_blocks(struct ata *dev)
{
return (dev->command_sets & ATA_CS_LBA48) ? U64_FROM_BIT(16) : U64_FROM_BIT(8);
}
1 change: 1 addition & 0 deletions src/drivers/ata.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ struct ata *ata_alloc(heap general);
void ata_dealloc(struct ata *);
boolean ata_probe(struct ata *);
u64 ata_get_capacity(struct ata *);
u64 ata_get_io_max_blocks(struct ata *dev);

/* ATA commands (from sys/sys/ata.h) */
#define ATA_NOP 0x00 /* NOP */
Expand Down

0 comments on commit c3415bd

Please sign in to comment.