[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:
committed by
Kelvin Zhang
parent
cfeef533d1
commit
af1f19a2cc
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user