diff --git a/errors/errno.fugu b/errors/errno.fugu index fcf10dc..00f7c6f 100755 --- a/errors/errno.fugu +++ b/errors/errno.fugu @@ -1154,6 +1154,7 @@ errors fat FAT_ERR_ { failure BLOCK_BOUNDS "The block number is out of bounds", failure CREATE_ROOT "Tried to create root directory", 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 @@ -1433,3 +1434,9 @@ errors sdhc SDHCD_ERR_ { failure BULK_FRAME_SET "Bulk frame already 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", +}; diff --git a/hake/menu.lst.armv8_imx8x b/hake/menu.lst.armv8_imx8x index 529feec..d06cead 100644 --- a/hake/menu.lst.armv8_imx8x +++ b/hake/menu.lst.armv8_imx8x @@ -12,3 +12,4 @@ module /armv8/sbin/mallocator module /armv8/sbin/stackoverflow module /armv8/sbin/echoserver module /armv8/sbin/shelly +module /armv8/sbin/block_driver_server diff --git a/include/aos/ump_binding.h b/include/aos/ump_binding.h index 6e4029a..283a7dc 100644 --- a/include/aos/ump_binding.h +++ b/include/aos/ump_binding.h @@ -8,6 +8,7 @@ enum ump_server_id { UMP_SERVER_ECHO, UMP_SERVER_NET, + UMP_SERVER_BLOCK_DRIVER, UMP_SERVER_COUNT // How many servers exist }; diff --git a/include/drivers/block_driver.h b/include/drivers/block_driver.h new file mode 100644 index 0000000..9151354 --- /dev/null +++ b/include/drivers/block_driver.h @@ -0,0 +1,39 @@ +#ifndef _INIT_BLOCK_DRIVER_H_ +#define _INIT_BLOCK_DRIVER_H_ + +#include + +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_ */ \ No newline at end of file diff --git a/lib/block_driver/Hakefile b/lib/block_driver/Hakefile new file mode 100644 index 0000000..8e27a25 --- /dev/null +++ b/lib/block_driver/Hakefile @@ -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" + ] + } +] diff --git a/lib/block_driver/block_driver.c b/lib/block_driver/block_driver.c new file mode 100644 index 0000000..6cf05f0 --- /dev/null +++ b/lib/block_driver/block_driver.c @@ -0,0 +1,167 @@ +#include +#include +#include + +#include + +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); +} diff --git a/lib/spawn/spawn.c b/lib/spawn/spawn.c index 8727794..3fdce19 100644 --- a/lib/spawn/spawn.c +++ b/lib/spawn/spawn.c @@ -509,6 +509,10 @@ errval_t spawn_load_argv(int argc, char *argv[], struct spawninfo *si, arg0_base = IMX8X_ENET_BASE; 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; if (arg0_base != 0) { diff --git a/performance/performance_evaluation.py b/performance/performance_evaluation.py index 767c8ca..cf9933b 100644 --- a/performance/performance_evaluation.py +++ b/performance/performance_evaluation.py @@ -67,12 +67,20 @@ def main(): 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") - dataset["server_schedule_task"] = build_dataseries(measurements, "aos_urpc_server:start", "aos_urpc_server:triggered_closure") - 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") + # URPC + # dataset["client_to_server"] = build_dataseries(measurements, "aos_urpc_nop:start", "aos_urpc_server:start") + # dataset["server_schedule_task"] = build_dataseries(measurements, "aos_urpc_server:start", "aos_urpc_server:triggered_closure") + # 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 metrics = {} diff --git a/performance/plots/archive/block_driver_raw/block_driver_read_device.jpg b/performance/plots/archive/block_driver_raw/block_driver_read_device.jpg new file mode 100644 index 0000000..4dd4388 Binary files /dev/null and b/performance/plots/archive/block_driver_raw/block_driver_read_device.jpg differ diff --git a/performance/plots/archive/block_driver_raw/block_driver_read_memcpy.jpg b/performance/plots/archive/block_driver_raw/block_driver_read_memcpy.jpg new file mode 100644 index 0000000..d519b37 Binary files /dev/null and b/performance/plots/archive/block_driver_raw/block_driver_read_memcpy.jpg differ diff --git a/performance/plots/archive/block_driver_raw/block_driver_write_device.jpg b/performance/plots/archive/block_driver_raw/block_driver_write_device.jpg new file mode 100644 index 0000000..a377f03 Binary files /dev/null and b/performance/plots/archive/block_driver_raw/block_driver_write_device.jpg differ diff --git a/performance/plots/archive/block_driver_raw/block_driver_write_memcpy.jpg b/performance/plots/archive/block_driver_raw/block_driver_write_memcpy.jpg new file mode 100644 index 0000000..5600211 Binary files /dev/null and b/performance/plots/archive/block_driver_raw/block_driver_write_memcpy.jpg differ diff --git a/performance/plots/archive/block_driver_raw/block_driver_write_read.jpg b/performance/plots/archive/block_driver_raw/block_driver_write_read.jpg new file mode 100644 index 0000000..7d1a225 Binary files /dev/null and b/performance/plots/archive/block_driver_raw/block_driver_write_read.jpg differ diff --git a/performance/plots/archive/block_driver_raw/full_output.log b/performance/plots/archive/block_driver_raw/full_output.log new file mode 100644 index 0000000..17c4f30 Binary files /dev/null and b/performance/plots/archive/block_driver_raw/full_output.log differ diff --git a/performance/plots/archive/block_driver_raw/metrics.jpg b/performance/plots/archive/block_driver_raw/metrics.jpg new file mode 100644 index 0000000..8f29334 Binary files /dev/null and b/performance/plots/archive/block_driver_raw/metrics.jpg differ diff --git a/performance/plots/archive/block_driver_single_chache/block_driver_read_device.jpg b/performance/plots/archive/block_driver_single_chache/block_driver_read_device.jpg new file mode 100644 index 0000000..5c95940 Binary files /dev/null and b/performance/plots/archive/block_driver_single_chache/block_driver_read_device.jpg differ diff --git a/performance/plots/archive/block_driver_single_chache/block_driver_read_memcpy.jpg b/performance/plots/archive/block_driver_single_chache/block_driver_read_memcpy.jpg new file mode 100644 index 0000000..f81cfa7 Binary files /dev/null and b/performance/plots/archive/block_driver_single_chache/block_driver_read_memcpy.jpg differ diff --git a/performance/plots/archive/block_driver_single_chache/block_driver_write_device.jpg b/performance/plots/archive/block_driver_single_chache/block_driver_write_device.jpg new file mode 100644 index 0000000..2e7ca56 Binary files /dev/null and b/performance/plots/archive/block_driver_single_chache/block_driver_write_device.jpg differ diff --git a/performance/plots/archive/block_driver_single_chache/block_driver_write_memcpy.jpg b/performance/plots/archive/block_driver_single_chache/block_driver_write_memcpy.jpg new file mode 100644 index 0000000..b531891 Binary files /dev/null and b/performance/plots/archive/block_driver_single_chache/block_driver_write_memcpy.jpg differ diff --git a/performance/plots/archive/block_driver_single_chache/block_driver_write_read.jpg b/performance/plots/archive/block_driver_single_chache/block_driver_write_read.jpg new file mode 100644 index 0000000..a7dc3fb Binary files /dev/null and b/performance/plots/archive/block_driver_single_chache/block_driver_write_read.jpg differ diff --git a/performance/plots/archive/block_driver_single_chache/metrics.jpg b/performance/plots/archive/block_driver_single_chache/metrics.jpg new file mode 100644 index 0000000..3d46934 Binary files /dev/null and b/performance/plots/archive/block_driver_single_chache/metrics.jpg differ diff --git a/platforms/Hakefile b/platforms/Hakefile index 7309dbc..3f4151f 100644 --- a/platforms/Hakefile +++ b/platforms/Hakefile @@ -12,7 +12,7 @@ let -- 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 [ diff --git a/usr/drivers/block_driver_server/Hakefile b/usr/drivers/block_driver_server/Hakefile new file mode 100644 index 0000000..d20500d --- /dev/null +++ b/usr/drivers/block_driver_server/Hakefile @@ -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" ] + } +] diff --git a/usr/drivers/block_driver_server/main.c b/usr/drivers/block_driver_server/main.c new file mode 100644 index 0000000..9f97c9e --- /dev/null +++ b/usr/drivers/block_driver_server/main.c @@ -0,0 +1,494 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +// #define BLOCK_DRIVER_PERFORMANCE +#ifdef BLOCK_DRIVER_PERFORMANCE +#include +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; +} \ No newline at end of file diff --git a/usr/init/main.c b/usr/init/main.c index f467d65..465dcc4 100644 --- a/usr/init/main.c +++ b/usr/init/main.c @@ -147,6 +147,13 @@ bsp_main(int argc, char *argv[]) { waitset_init(&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 struct spawninfo enet_si;