[lib][uefi] Add interrupt based async IO support

Add a new API to bio layer(read_async) where function will
return immediately, and a callback function will be called from
interrupt context, when block driver completes the IO request.
This commit is contained in:
Kelvin Zhang
2025-07-30 16:11:23 -07:00
committed by Kelvin Zhang
parent cfeef533d1
commit af1f19a2cc
4 changed files with 170 additions and 72 deletions

View File

@@ -369,6 +369,26 @@ ssize_t bio_read(bdev_t *dev, void *buf, off_t offset, size_t len) {
return dev->read(dev, buf, offset, len);
}
status_t bio_read_async(bdev_t *dev, void *buf, off_t offset, size_t len,
bio_async_callback_t callback, void *callback_context) {
LTRACEF("dev '%s', buf %p, offset %lld, len %zd\n", dev->name, buf, offset,
len);
DEBUG_ASSERT(dev && dev->ref > 0);
DEBUG_ASSERT(buf);
if (dev->read_async == NULL) {
return ERR_NOT_SUPPORTED;
}
/* range check */
len = bio_trim_range(dev, offset, len);
if (len == 0) {
return 0;
}
return dev->read_async(dev, buf, offset, len, callback, callback_context);
}
ssize_t bio_read_block(bdev_t *dev, void *buf, bnum_t block, uint count) {
LTRACEF("dev '%s', buf %p, block %d, count %u\n", dev->name, buf, block, count);

View File

@@ -47,6 +47,9 @@ typedef struct bdev {
/* function pointers */
ssize_t (*read)(struct bdev *, void *buf, off_t offset, size_t len);
status_t (*read_async)(struct bdev *, void *buf, off_t offset, size_t len,
void (*callback)(void *cookie, struct bdev *, ssize_t),
void *callback_context);
ssize_t (*read_block)(struct bdev *, void *buf, bnum_t block, uint count);
ssize_t (*write)(struct bdev *, const void *buf, off_t offset, size_t len);
ssize_t (*write_block)(struct bdev *, const void *buf, bnum_t block, uint count);
@@ -55,10 +58,13 @@ typedef struct bdev {
void (*close)(struct bdev *);
} bdev_t;
typedef void (*bio_async_callback_t)(void *cookie, bdev_t *dev, ssize_t status);
/* user api */
bdev_t *bio_open(const char *name);
void bio_close(bdev_t *dev);
ssize_t bio_read(bdev_t *dev, void *buf, off_t offset, size_t len);
status_t bio_read_async(bdev_t *dev, void *buf, off_t offset, size_t len,
bio_async_callback_t callback, void *callback_context);
ssize_t bio_read_block(bdev_t *dev, void *buf, bnum_t block, uint count);
ssize_t bio_write(bdev_t *dev, const void *buf, off_t offset, size_t len);
ssize_t bio_write_block(bdev_t *dev, const void *buf, bnum_t block, uint count);

View File

@@ -27,6 +27,7 @@
#include <uefi/protocols/block_io2_protocol.h>
#include <uefi/types.h>
#include "defer.h"
#include "events.h"
#include "io_stack.h"
#include "memory_protocols.h"
@@ -48,6 +49,21 @@ EfiStatus reset(EfiBlockIo2Protocol* self, bool extended_verification) {
return UNSUPPORTED;
}
void async_read_callback(void* cookie, struct bdev* dev, ssize_t bytes_read) {
// |cookie| might be identity mapped memory, which is in UEFI address space.
// We need to switch to the UEFI address space to access it.
auto aspace = set_boot_aspace();
auto old_aspace = vmm_set_active_aspace(aspace);
auto token = reinterpret_cast<EfiBlockIo2Token*>(cookie);
if (bytes_read < 0) {
token->transaction_status = DEVICE_ERROR;
} else {
token->transaction_status = SUCCESS;
}
signal_event(token->event);
vmm_set_active_aspace(old_aspace);
}
// Read from dev, after I/O completes, signal token->event and set
// token->transaction_status
EfiStatus read_blocks_async(bdev_t* dev, uint64_t lba, EfiBlockIo2Token* token,
@@ -60,6 +76,11 @@ EfiStatus read_blocks_async(bdev_t* dev, uint64_t lba, EfiBlockIo2Token* token,
printf("Invalid token %p\n", token);
return INVALID_PARAMETER;
}
if (dev->read_async != nullptr) {
bio_read_async(dev, buffer, lba * dev->block_size, buffer_size,
async_read_callback, token);
return SUCCESS;
}
// First draft of this API will just use a background thread.
// More efficient version can be implemented once LK's bio layer
// supports async IO
@@ -70,12 +91,7 @@ EfiStatus read_blocks_async(bdev_t* dev, uint64_t lba, EfiBlockIo2Token* token,
vmm_set_active_aspace(aspace);
auto bytes_read =
bio_read_block(dev, buffer, lba, buffer_size / dev->block_size);
if (static_cast<size_t>(bytes_read) != buffer_size) {
token->transaction_status = DEVICE_ERROR;
} else {
token->transaction_status = SUCCESS;
}
signal_event(token->event);
async_read_callback(token, dev, bytes_read);
return 0;
},
get_current_thread()->priority, kIoStackSize);