Added block driver client and server
@ -1154,6 +1154,7 @@ errors fat FAT_ERR_ {
|
|||||||
failure BLOCK_BOUNDS "The block number is out of bounds",
|
failure BLOCK_BOUNDS "The block number is out of bounds",
|
||||||
failure CREATE_ROOT "Tried to create root directory",
|
failure CREATE_ROOT "Tried to create root directory",
|
||||||
failure BAD_FILENAME "Filename is not allowed",
|
failure BAD_FILENAME "Filename is not allowed",
|
||||||
|
failure TOO_MANY_HANDLES "Currently the FAT32 library allows at most FAT32_MAX_OPEN_HANDLERS handlers per dir/file",
|
||||||
};
|
};
|
||||||
|
|
||||||
// errors generated by VFS's fs cache library
|
// errors generated by VFS's fs cache library
|
||||||
@ -1433,3 +1434,9 @@ errors sdhc SDHCD_ERR_ {
|
|||||||
failure BULK_FRAME_SET "Bulk frame already set",
|
failure BULK_FRAME_SET "Bulk frame already set",
|
||||||
failure BULK_FRAME_NOT_SET "Bulk frame not set",
|
failure BULK_FRAME_NOT_SET "Bulk frame not set",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// errors for block driver server
|
||||||
|
errors block BLOCK_ERR_ {
|
||||||
|
failure OVERFLOW_BLOCK "Client tried to operate over block bounds",
|
||||||
|
failure WRITE_BOOTSECTOR "Client tried to overwrite the boot sector",
|
||||||
|
};
|
||||||
|
|||||||
@ -12,3 +12,4 @@ module /armv8/sbin/mallocator
|
|||||||
module /armv8/sbin/stackoverflow
|
module /armv8/sbin/stackoverflow
|
||||||
module /armv8/sbin/echoserver
|
module /armv8/sbin/echoserver
|
||||||
module /armv8/sbin/shelly
|
module /armv8/sbin/shelly
|
||||||
|
module /armv8/sbin/block_driver_server
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
enum ump_server_id {
|
enum ump_server_id {
|
||||||
UMP_SERVER_ECHO,
|
UMP_SERVER_ECHO,
|
||||||
UMP_SERVER_NET,
|
UMP_SERVER_NET,
|
||||||
|
UMP_SERVER_BLOCK_DRIVER,
|
||||||
UMP_SERVER_COUNT // How many servers exist
|
UMP_SERVER_COUNT // How many servers exist
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
39
include/drivers/block_driver.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#ifndef _INIT_BLOCK_DRIVER_H_
|
||||||
|
#define _INIT_BLOCK_DRIVER_H_
|
||||||
|
|
||||||
|
#include <aos/aos.h>
|
||||||
|
|
||||||
|
enum block_driver_action {
|
||||||
|
BLOCK_DRIVER_ACTION_READ,
|
||||||
|
BLOCK_DRIVER_ACTION_WRITE,
|
||||||
|
BLOCK_DRIVER_ACTION_LOCK,
|
||||||
|
BLOCK_DRIVER_ACTION_UNLOCK,
|
||||||
|
BLOCK_DRIVER_ACTION_REGISTER_HANDLE,
|
||||||
|
BLOCK_DRIVER_ACTION_UNREGISTER_HANDLE,
|
||||||
|
BLOCK_DRIVER_ACTION_COUNT_HANDLES
|
||||||
|
};
|
||||||
|
|
||||||
|
struct block_driver_request {
|
||||||
|
enum block_driver_action action;
|
||||||
|
size_t block_number;
|
||||||
|
size_t offset;
|
||||||
|
size_t bytes;
|
||||||
|
uint64_t directory_entry_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct block_driver_result {
|
||||||
|
errval_t err;
|
||||||
|
};
|
||||||
|
|
||||||
|
errval_t block_driver_init(void);
|
||||||
|
|
||||||
|
errval_t block_driver_read_object(uint32_t sector_number, size_t offset, size_t size, void *dst);
|
||||||
|
errval_t block_driver_write_object(uint32_t sector_number, size_t offset, size_t size, const void *src);
|
||||||
|
errval_t block_driver_lock(void);
|
||||||
|
errval_t block_driver_unlock(void);
|
||||||
|
|
||||||
|
errval_t block_driver_register_handle(uint64_t directory_entry_id);
|
||||||
|
errval_t block_driver_unregister_handle(uint64_t directory_entry_id);
|
||||||
|
errval_t block_driver_count_handles(uint64_t directory_entry_id, size_t *count);
|
||||||
|
|
||||||
|
#endif /* _INIT_BLOCK_DRIVER_H_ */
|
||||||
20
lib/block_driver/Hakefile
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
--------------------------------------------------------------------------
|
||||||
|
-- Copyright (c) 2007-2012, ETH Zurich.
|
||||||
|
-- All rights reserved.
|
||||||
|
--
|
||||||
|
-- This file is distributed under the terms in the attached LICENSE file.
|
||||||
|
-- If you do not find this file, copies can be found by writing to:
|
||||||
|
-- ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
|
||||||
|
--
|
||||||
|
-- Hakefile for lib/vfs
|
||||||
|
--
|
||||||
|
--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[
|
||||||
|
build library {
|
||||||
|
target = "block_driver",
|
||||||
|
cFiles = [
|
||||||
|
"block_driver.c"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
167
lib/block_driver/block_driver.c
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
#include <aos/ump_chan.h>
|
||||||
|
#include <aos/aos_rpc.h>
|
||||||
|
#include <aos/deferred.h>
|
||||||
|
|
||||||
|
#include <drivers/block_driver.h>
|
||||||
|
|
||||||
|
struct block_driver_state {
|
||||||
|
struct ump_send_chan *send_chan;
|
||||||
|
struct ump_recv_chan *recv_chan;
|
||||||
|
|
||||||
|
bool request_ongoing;
|
||||||
|
struct block_driver_request *current_request;
|
||||||
|
void *read_buf;
|
||||||
|
errval_t result;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct block_driver_state state = {
|
||||||
|
.recv_chan = NULL,
|
||||||
|
.send_chan = NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
// use a waitset to make the calls blocking
|
||||||
|
struct waitset ws;
|
||||||
|
|
||||||
|
errval_t block_driver_init(void) {
|
||||||
|
errval_t err;
|
||||||
|
|
||||||
|
waitset_init(&ws);
|
||||||
|
|
||||||
|
// connect to the block driver
|
||||||
|
struct capref cap;
|
||||||
|
struct aos_rpc *rpc = aos_rpc_get_init_channel();
|
||||||
|
// retry several times, waiting inbetween in case the server is still starting up
|
||||||
|
for (int attempts = 0; attempts < 50; attempts++) {
|
||||||
|
err = aos_rpc_ump_connect(rpc, UMP_SERVER_BLOCK_DRIVER, &cap);
|
||||||
|
if (err_no(err) != LIB_ERR_UMP_NOT_REGISTERED) break;
|
||||||
|
barrelfish_usleep(100000);
|
||||||
|
}
|
||||||
|
if (err_is_fail(err)) return err;
|
||||||
|
|
||||||
|
// Create the two uni-directional channels
|
||||||
|
struct ump_send_chan *send_chan;
|
||||||
|
struct ump_recv_chan *recv_chan;
|
||||||
|
err = ump_chan_init(UMP_ROLE_CLIENT, &send_chan, &recv_chan, sizeof(struct block_driver_result), cap, &ws);
|
||||||
|
if (err_is_fail(err)) return err;
|
||||||
|
|
||||||
|
state.recv_chan = recv_chan;
|
||||||
|
state.send_chan = send_chan;
|
||||||
|
state.request_ongoing = false;
|
||||||
|
state.current_request = NULL;
|
||||||
|
state.read_buf = NULL;
|
||||||
|
state.result = SYS_ERR_OK;
|
||||||
|
|
||||||
|
return SYS_ERR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_payload(void *arg, size_t payload_size, void *payload) {
|
||||||
|
state.request_ongoing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_response(void *arg, size_t header_size, void *header, size_t payload_size) {
|
||||||
|
struct block_driver_result *result = header;
|
||||||
|
|
||||||
|
state.result = result->err;
|
||||||
|
|
||||||
|
if (state.current_request->action == BLOCK_DRIVER_ACTION_READ && err_is_ok(result->err)) {
|
||||||
|
if (payload_size != state.current_request->bytes) {
|
||||||
|
USER_PANIC("We should always receive as much as requested or an error");
|
||||||
|
}
|
||||||
|
// receive the payload in the buffer passed to the blocking function
|
||||||
|
ump_recv_payload(state.recv_chan, state.read_buf, handle_payload, NULL);
|
||||||
|
return;
|
||||||
|
} else if (state.current_request->action == BLOCK_DRIVER_ACTION_COUNT_HANDLES && err_is_ok(result->err)) {
|
||||||
|
if (payload_size != sizeof(size_t)) {
|
||||||
|
USER_PANIC("We should always receive a size_t result for this request or an error");
|
||||||
|
}
|
||||||
|
// receive the payload in the buffer passed to the blocking function
|
||||||
|
ump_recv_payload(state.recv_chan, state.read_buf, handle_payload, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ump_recv_payload(state.recv_chan, NULL, handle_payload, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_send_completed(void *arg, struct ump_send_queue_entry *entry) {
|
||||||
|
free((void *)entry->header);
|
||||||
|
free(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
static errval_t send_blocking_request(enum block_driver_action action, uint64_t directory_entry_id, uint32_t sector_number, size_t offset, size_t size, size_t payload_size, const void *payload) {
|
||||||
|
assert(state.recv_chan != NULL);
|
||||||
|
assert(state.send_chan != NULL);
|
||||||
|
assert(!state.request_ongoing);
|
||||||
|
|
||||||
|
errval_t err;
|
||||||
|
|
||||||
|
state.request_ongoing = true;
|
||||||
|
|
||||||
|
// send the read request
|
||||||
|
struct block_driver_request *request = malloc(sizeof(struct block_driver_request));
|
||||||
|
if (request == NULL) return LIB_ERR_MALLOC_FAIL;
|
||||||
|
struct ump_send_queue_entry *entry = malloc(sizeof(struct ump_send_queue_entry));
|
||||||
|
if (entry == NULL) return LIB_ERR_MALLOC_FAIL;
|
||||||
|
|
||||||
|
request->action = action;
|
||||||
|
request->block_number = sector_number;
|
||||||
|
request->bytes = size;
|
||||||
|
request->offset = offset;
|
||||||
|
request->directory_entry_id = directory_entry_id;
|
||||||
|
state.current_request = request;
|
||||||
|
assert(state.send_chan->send_queue_head == NULL);
|
||||||
|
ump_send(
|
||||||
|
state.send_chan,
|
||||||
|
entry,
|
||||||
|
sizeof(struct block_driver_request),
|
||||||
|
request,
|
||||||
|
payload_size,
|
||||||
|
payload,
|
||||||
|
handle_send_completed,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
// register response handler
|
||||||
|
ump_recv_header(state.recv_chan, handle_response, NULL);
|
||||||
|
|
||||||
|
// wait for the response
|
||||||
|
while (state.request_ongoing) {
|
||||||
|
err = event_dispatch(&ws);
|
||||||
|
if (err_is_fail(err)) {
|
||||||
|
DEBUG_ERR(err, "in event_dispatch");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return state.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
errval_t block_driver_read_object(uint32_t sector_number, size_t offset, size_t size, void *dst) {
|
||||||
|
state.read_buf = dst;
|
||||||
|
return send_blocking_request(BLOCK_DRIVER_ACTION_READ, 0, sector_number, offset, size, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
errval_t block_driver_write_object(uint32_t sector_number, size_t offset, size_t size, const void *src) {
|
||||||
|
return send_blocking_request(BLOCK_DRIVER_ACTION_WRITE, 0, sector_number, offset, size, size, src);
|
||||||
|
}
|
||||||
|
|
||||||
|
errval_t block_driver_lock(void) {
|
||||||
|
return send_blocking_request(BLOCK_DRIVER_ACTION_LOCK, 0, 0, 0, 0, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
errval_t block_driver_unlock(void) {
|
||||||
|
return send_blocking_request(BLOCK_DRIVER_ACTION_UNLOCK, 0, 0, 0, 0, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
errval_t block_driver_register_handle(uint64_t directory_entry_id) {
|
||||||
|
return send_blocking_request(BLOCK_DRIVER_ACTION_REGISTER_HANDLE, directory_entry_id, 0, 0, 0, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
errval_t block_driver_unregister_handle(uint64_t directory_entry_id) {
|
||||||
|
return send_blocking_request(BLOCK_DRIVER_ACTION_UNREGISTER_HANDLE, directory_entry_id, 0, 0, 0, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
errval_t block_driver_count_handles(uint64_t directory_entry_id, size_t *count) {
|
||||||
|
state.read_buf = count;
|
||||||
|
return send_blocking_request(BLOCK_DRIVER_ACTION_COUNT_HANDLES, directory_entry_id, 0, 0, 0, 0, NULL);
|
||||||
|
}
|
||||||
@ -509,6 +509,10 @@ errval_t spawn_load_argv(int argc, char *argv[], struct spawninfo *si,
|
|||||||
arg0_base = IMX8X_ENET_BASE;
|
arg0_base = IMX8X_ENET_BASE;
|
||||||
arg0_size = IMX8X_ENET_SIZE;
|
arg0_size = IMX8X_ENET_SIZE;
|
||||||
}
|
}
|
||||||
|
if (strcmp(argv[0], "block_driver_server") == 0) {
|
||||||
|
arg0_base = IMX8X_SDHC2_BASE;
|
||||||
|
arg0_size = IMX8X_SDHC_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
si->cspace_l2_cnode_argcn = NULL_CNODE;
|
si->cspace_l2_cnode_argcn = NULL_CNODE;
|
||||||
if (arg0_base != 0) {
|
if (arg0_base != 0) {
|
||||||
|
|||||||
@ -67,12 +67,20 @@ def main():
|
|||||||
|
|
||||||
dataset = {}
|
dataset = {}
|
||||||
|
|
||||||
dataset["performance"] = build_dataseries(measurements, "aos_performance:start", "aos_performance:done")
|
# dataset["performance"] = build_dataseries(measurements, "aos_performance:start", "aos_performance:done")
|
||||||
|
|
||||||
dataset["client_to_server"] = build_dataseries(measurements, "aos_urpc_nop:start", "aos_urpc_server:start")
|
# URPC
|
||||||
dataset["server_schedule_task"] = build_dataseries(measurements, "aos_urpc_server:start", "aos_urpc_server:triggered_closure")
|
# dataset["client_to_server"] = build_dataseries(measurements, "aos_urpc_nop:start", "aos_urpc_server:start")
|
||||||
dataset["server_completed_task"] = build_dataseries(measurements, "aos_urpc_server:triggered_closure", "aos_urpc_server:done")
|
# dataset["server_schedule_task"] = build_dataseries(measurements, "aos_urpc_server:start", "aos_urpc_server:triggered_closure")
|
||||||
dataset["server_to_client"] = build_dataseries(measurements, "aos_urpc_server:done", "aos_urpc_nop:done")
|
# dataset["server_completed_task"] = build_dataseries(measurements, "aos_urpc_server:triggered_closure", "aos_urpc_server:done")
|
||||||
|
# dataset["server_to_client"] = build_dataseries(measurements, "aos_urpc_server:done", "aos_urpc_nop:done")
|
||||||
|
|
||||||
|
# block driver
|
||||||
|
dataset["block_driver_read_device"] = build_dataseries(measurements, "block_driver_read_object:start", "block_driver_read_object:memcpy")
|
||||||
|
dataset["block_driver_read_memcpy"] = build_dataseries(measurements, "block_driver_read_object:memcpy", "block_driver_read_object:done")
|
||||||
|
dataset["block_driver_write_read"] = build_dataseries(measurements, "block_driver_write_object:start", "block_driver_write_object:read")
|
||||||
|
dataset["block_driver_write_memcpy"] = build_dataseries(measurements, "block_driver_write_object:read", "block_driver_write_object:memcpy")
|
||||||
|
dataset["block_driver_write_device"] = build_dataseries(measurements, "block_driver_write_object:memcpy", "block_driver_write_object:done")
|
||||||
|
|
||||||
# calculate stats for each data series
|
# calculate stats for each data series
|
||||||
metrics = {}
|
metrics = {}
|
||||||
|
|||||||
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 25 KiB |
BIN
performance/plots/archive/block_driver_raw/full_output.log
Normal file
BIN
performance/plots/archive/block_driver_raw/metrics.jpg
Normal file
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 33 KiB |
BIN
performance/plots/archive/block_driver_single_chache/metrics.jpg
Normal file
|
After Width: | Height: | Size: 31 KiB |
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
let
|
let
|
||||||
-- Default list of modules to build/install
|
-- Default list of modules to build/install
|
||||||
modules_common = [ "/sbin/" ++ f | f <- [ "init", "hello", "mallocator", "stackoverflow", "echoserver", "enet", "shelly"
|
modules_common = [ "/sbin/" ++ f | f <- [ "init", "hello", "mallocator", "stackoverflow", "echoserver", "enet", "shelly", "block_driver_server"
|
||||||
] ]
|
] ]
|
||||||
in
|
in
|
||||||
[
|
[
|
||||||
|
|||||||
30
usr/drivers/block_driver_server/Hakefile
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
--------------------------------------------------------------------------
|
||||||
|
-- Copyright (c) 2020, ETH Zurich.
|
||||||
|
-- All rights reserved.
|
||||||
|
--
|
||||||
|
-- This file is distributed under the terms in the attached LICENSE file.
|
||||||
|
-- If you do not find this file, copies can be found by writing to:
|
||||||
|
-- ETH Zurich D-INFK, CAB F.78, Universitaetstr 6, CH-8092 Zurich.
|
||||||
|
--
|
||||||
|
-- Hakefile for sdhc
|
||||||
|
--
|
||||||
|
--------------------------------------------------------------------------
|
||||||
|
--------------------------------------------------------------------------
|
||||||
|
-- Copyright (c) 2007-2010, ETH Zurich.
|
||||||
|
-- All rights reserved.
|
||||||
|
--
|
||||||
|
-- This file is distributed under the terms in the attached LICENSE file.
|
||||||
|
-- If you do not find this file, copies can be found by writing to:
|
||||||
|
-- ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
|
||||||
|
--
|
||||||
|
-- Hakefile for /usr/init
|
||||||
|
--
|
||||||
|
--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[ build application
|
||||||
|
{
|
||||||
|
target = "block_driver_server",
|
||||||
|
cFiles = [ "main.c" ],
|
||||||
|
addLibraries = [ "sdhc" ]
|
||||||
|
}
|
||||||
|
]
|
||||||
494
usr/drivers/block_driver_server/main.c
Normal file
@ -0,0 +1,494 @@
|
|||||||
|
#include <aos/aos.h>
|
||||||
|
#include <aos/paging.h>
|
||||||
|
#include <aos/ump_binding.h>
|
||||||
|
#include <aos/ump_chan.h>
|
||||||
|
#include <drivers/block_driver.h>
|
||||||
|
#include <aos/cache.h>
|
||||||
|
|
||||||
|
#include <maps/imx8x_map.h>
|
||||||
|
#include <drivers/sdhc.h>
|
||||||
|
|
||||||
|
// #define BLOCK_DRIVER_PERFORMANCE
|
||||||
|
#ifdef BLOCK_DRIVER_PERFORMANCE
|
||||||
|
#include <aos/performance.h>
|
||||||
|
struct performance_context pcontext;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct block_driver_state {
|
||||||
|
struct ump_send_chan *send_chan;
|
||||||
|
struct ump_recv_chan *recv_chan;
|
||||||
|
struct block_driver_request *current_request;
|
||||||
|
struct block_driver_state *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct sdhc_s *sdhc;
|
||||||
|
|
||||||
|
static void *dma_buffer;
|
||||||
|
static genpaddr_t dma_buffer_phys;
|
||||||
|
|
||||||
|
static bool locked = false;
|
||||||
|
static struct block_driver_state *lock_queue_head = NULL;
|
||||||
|
static struct block_driver_state *lock_queue_tail = NULL;
|
||||||
|
|
||||||
|
// list of open file handles because we have to synchronize on open dirs/files vs deletion
|
||||||
|
struct handle {
|
||||||
|
uint64_t directory_entry_id;
|
||||||
|
size_t count;
|
||||||
|
struct handle *next;
|
||||||
|
struct handle *prev;
|
||||||
|
};
|
||||||
|
struct handle *handle_list_head = NULL;
|
||||||
|
struct handle *handle_list_tail = NULL;
|
||||||
|
|
||||||
|
#define BLOCK_DRIVER_CACHE
|
||||||
|
#ifdef BLOCK_DRIVER_CACHE
|
||||||
|
uint32_t current_block_in_buffer = UINT32_MAX;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static errval_t read_buffer(int block_number) {
|
||||||
|
assert(block_number != UINT32_MAX);
|
||||||
|
|
||||||
|
errval_t err;
|
||||||
|
|
||||||
|
// debug_printf("[block_driver_server] read block %d\n", block_number);
|
||||||
|
|
||||||
|
#ifdef BLOCK_DRIVER_CACHE
|
||||||
|
// very primitive cache for the last read block
|
||||||
|
if(current_block_in_buffer == block_number) {
|
||||||
|
return SYS_ERR_OK;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
err = sdhc_read_block(sdhc, block_number, (lpaddr_t)dma_buffer_phys);
|
||||||
|
if(err_is_fail(err)) {
|
||||||
|
#ifdef BLOCK_DRIVER_CACHE
|
||||||
|
// maybe there was a partial modification so invalidate the cache
|
||||||
|
current_block_in_buffer = UINT32_MAX;
|
||||||
|
#endif
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
#ifdef BLOCK_DRIVER_CACHE
|
||||||
|
current_block_in_buffer = block_number;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
__asm volatile (
|
||||||
|
"dmb sy\n"
|
||||||
|
);
|
||||||
|
cpu_dcache_wbinv_range((genvaddr_t)dma_buffer, SDHC_BLOCK_SIZE);
|
||||||
|
|
||||||
|
return SYS_ERR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static errval_t write_buffer(int block_number) {
|
||||||
|
assert(block_number != UINT32_MAX);
|
||||||
|
|
||||||
|
errval_t err;
|
||||||
|
|
||||||
|
// debug_printf("[block_driver_server] write block %d\n", block_number);
|
||||||
|
|
||||||
|
__asm volatile (
|
||||||
|
"dmb sy\n"
|
||||||
|
);
|
||||||
|
cpu_dcache_wbinv_range((genvaddr_t)dma_buffer, SDHC_BLOCK_SIZE);
|
||||||
|
|
||||||
|
err = sdhc_write_block(sdhc, block_number, (lpaddr_t)dma_buffer_phys);
|
||||||
|
if(err_is_fail(err)) return err;
|
||||||
|
|
||||||
|
return SYS_ERR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct handle *search_element(uint64_t directory_entry_id) {
|
||||||
|
struct handle *cur_handle = handle_list_head;
|
||||||
|
while(cur_handle != NULL && cur_handle->directory_entry_id != directory_entry_id) {
|
||||||
|
cur_handle = cur_handle->next;
|
||||||
|
}
|
||||||
|
return cur_handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void insert_element(uint64_t directory_entry_id) {
|
||||||
|
struct handle *element_handle = calloc(1, sizeof(struct handle));
|
||||||
|
if (element_handle == NULL) USER_PANIC("Ran out of memory");
|
||||||
|
|
||||||
|
element_handle->count = 1;
|
||||||
|
element_handle->directory_entry_id = directory_entry_id;
|
||||||
|
|
||||||
|
element_handle->prev = handle_list_tail;
|
||||||
|
element_handle->next = NULL;
|
||||||
|
if(handle_list_head == NULL) {
|
||||||
|
handle_list_head = element_handle;
|
||||||
|
} else {
|
||||||
|
handle_list_tail->next = element_handle;
|
||||||
|
}
|
||||||
|
handle_list_tail = element_handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove_element(struct handle *element_handle) {
|
||||||
|
if (element_handle->prev == NULL) {
|
||||||
|
handle_list_head = element_handle->next;
|
||||||
|
} else {
|
||||||
|
element_handle->prev->next = element_handle->next;
|
||||||
|
}
|
||||||
|
if (element_handle->next == NULL) {
|
||||||
|
handle_list_tail = element_handle->prev;
|
||||||
|
} else {
|
||||||
|
element_handle->next->prev = element_handle->prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((__unused__))
|
||||||
|
static void print_handles(void) {
|
||||||
|
struct handle *element_handle = handle_list_head;
|
||||||
|
debug_printf("Handle List\n");
|
||||||
|
while (element_handle != NULL) {
|
||||||
|
debug_printf("Handle %lu: %lu\n", element_handle->directory_entry_id, element_handle->count);
|
||||||
|
element_handle = element_handle->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static errval_t register_handle(uint64_t directory_entry_id) {
|
||||||
|
// debug_printf("Register %lu\n", directory_entry_id);
|
||||||
|
struct handle *element_handle = search_element(directory_entry_id);
|
||||||
|
if (element_handle == NULL) {
|
||||||
|
insert_element(directory_entry_id);
|
||||||
|
} else if(element_handle->count == SIZE_MAX) {
|
||||||
|
return FAT_ERR_TOO_MANY_HANDLES;
|
||||||
|
}else {
|
||||||
|
++element_handle->count;
|
||||||
|
}
|
||||||
|
// print_handles();
|
||||||
|
|
||||||
|
return SYS_ERR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static errval_t unregister_handle(uint64_t directory_entry_id) {
|
||||||
|
// debug_printf("Unegister %lu\n", directory_entry_id);
|
||||||
|
struct handle *element_handle = search_element(directory_entry_id);
|
||||||
|
if (element_handle == NULL) {
|
||||||
|
return ERR_INVALID_ARGS;
|
||||||
|
} else if (element_handle->count == 1) {
|
||||||
|
remove_element(element_handle);
|
||||||
|
} else {
|
||||||
|
--element_handle->count;
|
||||||
|
}
|
||||||
|
// print_handles();
|
||||||
|
return SYS_ERR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t count_handles(uint64_t directory_entry_id) {
|
||||||
|
struct handle *element_handle = search_element(directory_entry_id);
|
||||||
|
if (element_handle == NULL) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return element_handle->count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static errval_t read_object(uint32_t block_number, size_t offset, size_t size, void *dst) {
|
||||||
|
errval_t err;
|
||||||
|
|
||||||
|
// printf("[block_driver_server] reading %lu bytes from %lu@%u to %p\n", size, offset, block_number, dst);
|
||||||
|
|
||||||
|
// sector bounds check
|
||||||
|
if (offset + size > SDHC_BLOCK_SIZE) {
|
||||||
|
return BLOCK_ERR_OVERFLOW_BLOCK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef BLOCK_DRIVER_PERFORMANCE
|
||||||
|
perf_init(&pcontext, "block_driver_read_object");
|
||||||
|
perf_add_now(&pcontext, "start");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
err = read_buffer(block_number);
|
||||||
|
if (err_is_fail(err)) return err;
|
||||||
|
|
||||||
|
#ifdef BLOCK_DRIVER_PERFORMANCE
|
||||||
|
perf_add_now(&pcontext, "memcpy");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
memcpy(dst, dma_buffer + offset, size);
|
||||||
|
|
||||||
|
// debug_printf("Read start: '%.5s'\n", dst);
|
||||||
|
|
||||||
|
#ifdef BLOCK_DRIVER_PERFORMANCE
|
||||||
|
perf_add_now(&pcontext, "done");
|
||||||
|
perf_print(&pcontext);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return SYS_ERR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static errval_t write_object(uint32_t block_number, size_t offset, size_t size, void *src) {
|
||||||
|
errval_t err;
|
||||||
|
|
||||||
|
// printf("[block_driver_server] writing %lu bytes from %p to %lu@%u\n", size, src, offset, block_number);
|
||||||
|
|
||||||
|
// sector bounds check
|
||||||
|
if (offset + size > SDHC_BLOCK_SIZE) {
|
||||||
|
return BLOCK_ERR_OVERFLOW_BLOCK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we never modify the bpb and boot sector
|
||||||
|
if (block_number == 0) {
|
||||||
|
return BLOCK_ERR_WRITE_BOOTSECTOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef BLOCK_DRIVER_PERFORMANCE
|
||||||
|
perf_init(&pcontext, "block_driver_write_object");
|
||||||
|
perf_add_now(&pcontext, "start");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// if we are not writing the full block then we need to copy the current block
|
||||||
|
if (size < SDHC_BLOCK_SIZE) {
|
||||||
|
err = read_buffer(block_number);
|
||||||
|
if (err_is_fail(err)) return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef BLOCK_DRIVER_PERFORMANCE
|
||||||
|
perf_add_now(&pcontext, "read");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// debug_printf("Write start: '%.5s'\n", src);
|
||||||
|
memcpy(dma_buffer + offset, src, size);
|
||||||
|
|
||||||
|
#ifdef BLOCK_DRIVER_CACHE
|
||||||
|
// update the cache metadata
|
||||||
|
current_block_in_buffer = block_number;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef BLOCK_DRIVER_PERFORMANCE
|
||||||
|
perf_add_now(&pcontext, "memcpy");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
err = write_buffer(block_number);
|
||||||
|
if (err_is_fail(err)) return err;
|
||||||
|
|
||||||
|
#ifdef BLOCK_DRIVER_PERFORMANCE
|
||||||
|
perf_add_now(&pcontext, "done");
|
||||||
|
perf_print(&pcontext);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return SYS_ERR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_send_completed(void *arg, struct ump_send_queue_entry *entry) {
|
||||||
|
free((void *)entry->header);
|
||||||
|
free((void *)entry->payload);
|
||||||
|
free(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void send_response(struct block_driver_state *state, errval_t err, size_t payload_size, void *payload) {
|
||||||
|
assert(state != NULL);
|
||||||
|
|
||||||
|
struct block_driver_result *result = malloc(sizeof(struct block_driver_result));
|
||||||
|
if (result == NULL) USER_PANIC("Ran out of memory");
|
||||||
|
struct ump_send_queue_entry *entry = malloc(sizeof(struct ump_send_queue_entry));
|
||||||
|
if (entry == NULL) USER_PANIC("Ran out of memory");
|
||||||
|
|
||||||
|
result->err = err;
|
||||||
|
ump_send(
|
||||||
|
state->send_chan,
|
||||||
|
entry,
|
||||||
|
sizeof(struct block_driver_result),
|
||||||
|
result,
|
||||||
|
payload_size,
|
||||||
|
payload,
|
||||||
|
handle_send_completed,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
}
|
||||||
|
static void send_error_response(struct block_driver_state *state, errval_t err) {
|
||||||
|
assert(state != NULL);
|
||||||
|
|
||||||
|
send_response(state, err, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_payload(void *arg, size_t payload_size, void *payload);
|
||||||
|
static void handle_request(void *arg, size_t header_size, void *header, size_t payload_size) {
|
||||||
|
assert(arg != NULL);
|
||||||
|
|
||||||
|
errval_t err;
|
||||||
|
|
||||||
|
struct block_driver_state *state = arg;
|
||||||
|
struct block_driver_request *request = header;
|
||||||
|
|
||||||
|
state->current_request = request;
|
||||||
|
|
||||||
|
if (request->bytes > SDHC_BLOCK_SIZE) {
|
||||||
|
send_error_response(state, ERR_INVALID_ARGS);
|
||||||
|
} else if (request->action == BLOCK_DRIVER_ACTION_READ) {
|
||||||
|
// perform read directly
|
||||||
|
void *response_buffer = malloc(request->bytes);
|
||||||
|
if (response_buffer == NULL) USER_PANIC("Ran out of memory");
|
||||||
|
|
||||||
|
err = read_object(request->block_number, request->offset, request->bytes, response_buffer);
|
||||||
|
if (err_is_fail(err)) {
|
||||||
|
send_error_response(state, err);
|
||||||
|
} else {
|
||||||
|
send_response(state, SYS_ERR_OK, request->bytes, response_buffer);
|
||||||
|
}
|
||||||
|
} else if (request->action == BLOCK_DRIVER_ACTION_WRITE) {
|
||||||
|
if (request->bytes != payload_size) {
|
||||||
|
send_error_response(state, ERR_INVALID_ARGS);
|
||||||
|
ump_recv_payload(state->recv_chan, NULL, handle_payload, arg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// we first need to receive the payload before we can handle the request
|
||||||
|
void *payload_buffer = malloc(request->bytes);
|
||||||
|
if (payload_buffer == NULL) USER_PANIC("Ran out of memory");
|
||||||
|
ump_recv_payload(state->recv_chan, payload_buffer, handle_payload, arg);
|
||||||
|
return;
|
||||||
|
} else if (request->action == BLOCK_DRIVER_ACTION_LOCK) {
|
||||||
|
if(locked) {
|
||||||
|
// enqueue the request
|
||||||
|
if (lock_queue_tail != NULL) {
|
||||||
|
lock_queue_tail->next = state;
|
||||||
|
} else {
|
||||||
|
lock_queue_head = state;
|
||||||
|
}
|
||||||
|
lock_queue_tail = state;
|
||||||
|
} else {
|
||||||
|
locked = true;
|
||||||
|
send_error_response(state, SYS_ERR_OK);
|
||||||
|
}
|
||||||
|
} else if (request->action == BLOCK_DRIVER_ACTION_UNLOCK) {
|
||||||
|
if (!locked) USER_PANIC("Received unlock command but was not locked");
|
||||||
|
send_error_response(state, SYS_ERR_OK);
|
||||||
|
|
||||||
|
// send response to first thread on the lock queue if there is one
|
||||||
|
if (lock_queue_head != NULL) {
|
||||||
|
send_error_response(lock_queue_head, SYS_ERR_OK);
|
||||||
|
|
||||||
|
// dequeue the head
|
||||||
|
if (lock_queue_head->next == NULL) {
|
||||||
|
lock_queue_tail = NULL;
|
||||||
|
}
|
||||||
|
lock_queue_head = lock_queue_head->next;
|
||||||
|
} else {
|
||||||
|
locked = false;
|
||||||
|
}
|
||||||
|
} else if (request->action == BLOCK_DRIVER_ACTION_REGISTER_HANDLE) {
|
||||||
|
err = register_handle(request->directory_entry_id);
|
||||||
|
send_error_response(state, err);
|
||||||
|
} else if (request->action == BLOCK_DRIVER_ACTION_UNREGISTER_HANDLE) {
|
||||||
|
err = unregister_handle(request->directory_entry_id);
|
||||||
|
send_error_response(state, err);
|
||||||
|
} else if (request->action == BLOCK_DRIVER_ACTION_COUNT_HANDLES) {
|
||||||
|
size_t *count = malloc(sizeof(size_t));
|
||||||
|
if (count == NULL) {
|
||||||
|
send_error_response(state, LIB_ERR_MALLOC_FAIL);
|
||||||
|
} else {
|
||||||
|
*count = count_handles(request->directory_entry_id);
|
||||||
|
send_response(state, SYS_ERR_OK, sizeof(size_t), count);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
send_error_response(state, ERR_INVALID_ARGS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip payload
|
||||||
|
ump_recv_payload(state->recv_chan, NULL, handle_payload, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_payload(void *arg, size_t payload_size, void *payload) {
|
||||||
|
assert(arg != NULL);
|
||||||
|
|
||||||
|
errval_t err;
|
||||||
|
|
||||||
|
struct block_driver_state *state = arg;
|
||||||
|
struct block_driver_request *request = state->current_request;
|
||||||
|
|
||||||
|
// we are guaranteed to have a valid structure request here
|
||||||
|
if (request->action == BLOCK_DRIVER_ACTION_WRITE) {
|
||||||
|
// we got the payload, write it to disk
|
||||||
|
err = write_object(request->block_number, request->offset, request->bytes, payload);
|
||||||
|
send_response(state, err, 0, NULL);
|
||||||
|
// free the payload buffer, we don't use it anymore
|
||||||
|
free(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
// listen for the next request on this channel
|
||||||
|
ump_recv_header(state->recv_chan, handle_request, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static errval_t connect_callback(void *arg, struct capref cap) {
|
||||||
|
errval_t err;
|
||||||
|
|
||||||
|
printf("[block_driver_server] incoming connection\n");
|
||||||
|
|
||||||
|
struct ump_send_chan *send_chan;
|
||||||
|
struct ump_recv_chan *recv_chan;
|
||||||
|
|
||||||
|
// we can run the server on the default waitset since we are dispatching on
|
||||||
|
// it for listening to connections anyways
|
||||||
|
err = ump_chan_init(UMP_ROLE_SERVER, &send_chan, &recv_chan, sizeof(struct block_driver_request), cap, get_default_waitset());
|
||||||
|
if (err_is_fail(err)) return err;
|
||||||
|
|
||||||
|
struct block_driver_state *state = malloc(sizeof(struct block_driver_state));
|
||||||
|
if (state == NULL) return LIB_ERR_MALLOC_FAIL;
|
||||||
|
state->recv_chan = recv_chan;
|
||||||
|
state->send_chan = send_chan;
|
||||||
|
ump_recv_header(recv_chan, handle_request, state);
|
||||||
|
|
||||||
|
printf("[block_driver_server] connection ready to receive requests\n");
|
||||||
|
|
||||||
|
return SYS_ERR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
|
errval_t err;
|
||||||
|
|
||||||
|
// get the device capability from the arg cnode
|
||||||
|
struct capref cap_arg0 = {
|
||||||
|
.cnode = cnode_arg,
|
||||||
|
.slot = 0
|
||||||
|
};
|
||||||
|
// map device registers uncachable
|
||||||
|
void *device_register_vaddr;
|
||||||
|
err = paging_map_frame_attr(
|
||||||
|
get_current_paging_state(),
|
||||||
|
&device_register_vaddr,
|
||||||
|
IMX8X_SDHC_SIZE,
|
||||||
|
cap_arg0,
|
||||||
|
VREGION_FLAGS_READ_WRITE_NOCACHE
|
||||||
|
);
|
||||||
|
if (err_is_fail(err)) USER_PANIC_ERR(err, "Failed to map device registers");
|
||||||
|
|
||||||
|
// initialize the sdhc driver
|
||||||
|
printf("[block_driver_server] initializing sdhc driver\n");
|
||||||
|
err = sdhc_init(&sdhc, device_register_vaddr);
|
||||||
|
if (err_is_fail(err)) USER_PANIC_ERR(err, "Failed to initialize sdhc driver");
|
||||||
|
|
||||||
|
// create the dma buffer
|
||||||
|
struct capref dma_buffer_frame;
|
||||||
|
size_t size;
|
||||||
|
err = frame_alloc(&dma_buffer_frame, SDHC_BLOCK_SIZE, &size);
|
||||||
|
if(err_is_fail(err)) return err_push(err, LIB_ERR_FRAME_ALLOC);
|
||||||
|
|
||||||
|
// we need the physical address for the DMA read operation
|
||||||
|
struct frame_identity dma_buffer_frame_id;
|
||||||
|
err = frame_identify(dma_buffer_frame, &dma_buffer_frame_id);
|
||||||
|
if (err_is_fail(err)) return err_push(err, LIB_ERR_FRAME_IDENTIFY);
|
||||||
|
dma_buffer_phys = dma_buffer_frame_id.base;
|
||||||
|
|
||||||
|
// map the scratchpad frame for us
|
||||||
|
err = paging_map_frame_attr(get_current_paging_state(), &dma_buffer, size, dma_buffer_frame, VREGION_FLAGS_READ_WRITE);
|
||||||
|
if (err_is_fail(err)) return err_push(err, LIB_ERR_PMAP_MAP);
|
||||||
|
|
||||||
|
printf("[block_driver_server] registering\n");
|
||||||
|
struct ump_binding_server server;
|
||||||
|
err = ump_binding_register(&server, UMP_SERVER_BLOCK_DRIVER, connect_callback, NULL);
|
||||||
|
if (err_is_fail(err)) USER_PANIC_ERR(err, "Failed to register UMP server");
|
||||||
|
|
||||||
|
// wait for connections to the server
|
||||||
|
printf("[block_driver_server] listening\n");
|
||||||
|
struct waitset *default_ws = get_default_waitset();
|
||||||
|
while (true) {
|
||||||
|
err = event_dispatch(default_ws);
|
||||||
|
if (err_is_fail(err)) {
|
||||||
|
DEBUG_ERR(err, "in event_dispatch");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
@ -147,6 +147,13 @@ bsp_main(int argc, char *argv[]) {
|
|||||||
waitset_init(&urpc_to_app_ws);
|
waitset_init(&urpc_to_app_ws);
|
||||||
thread_create(urpc_client_loop, &urpc_to_app_ws);
|
thread_create(urpc_client_loop, &urpc_to_app_ws);
|
||||||
|
|
||||||
|
// spawn the block driver server
|
||||||
|
struct spawninfo block_driver_si;
|
||||||
|
domainid_t block_driver_pid;
|
||||||
|
err = spawn_load_by_name("block_driver_server", &block_driver_si, &block_driver_pid);
|
||||||
|
if (err_is_fail(err)) {
|
||||||
|
DEBUG_ERR(err, "when spawning block_driver_server");
|
||||||
|
}
|
||||||
|
|
||||||
// Spawn enet driver
|
// Spawn enet driver
|
||||||
struct spawninfo enet_si;
|
struct spawninfo enet_si;
|
||||||
|
|||||||