X-Git-Url: https://scm.lunaixsky.com/lunaix-os.git/blobdiff_plain/e6416905c4afb34e61276cdc07c44813a3ebc5e7..50b4ecfb1b28e9b1dfc57b6a876fcdf938092152:/lunaix-os/kernel/block/blkio.c diff --git a/lunaix-os/kernel/block/blkio.c b/lunaix-os/kernel/block/blkio.c index bc0178c..4810925 100644 --- a/lunaix-os/kernel/block/blkio.c +++ b/lunaix-os/kernel/block/blkio.c @@ -1,9 +1,14 @@ #include +#include #include #include +#include + static struct cake_pile* blkio_reqpile; +LOG_MODULE("blkio") + void blkio_init() { @@ -65,28 +70,88 @@ blkio_newctx(req_handler handler) ctx->handle_one = handler; llist_init_head(&ctx->queue); + mutex_init(&ctx->lock); return ctx; } void -blkio_commit(struct blkio_context* ctx, struct blkio_req* req) +blkio_commit(struct blkio_req* req, int options) { + struct blkio_context* ctx; + + if (blkio_is_pending(req)) { + // prevent double submition + return; + } + + assert(req->io_ctx); + req->flags |= BLKIO_PENDING; - req->io_ctx = ctx; - llist_append(&ctx->queue, &req->reqs); + + if ((options & BLKIO_WAIT)) { + req->flags |= BLKIO_SHOULD_WAIT; + prepare_to_wait(&req->wait); + } + else { + req->flags &= ~BLKIO_SHOULD_WAIT; + } + + ctx = req->io_ctx; + blkio_lock(ctx); + + llist_append(&ctx->queue, &req->reqs); + + blkio_unlock(ctx); // if the pipeline is not running (e.g., stalling). Then we should schedule // one immediately and kick it started. - if (!ctx->busy) { + // NOTE: Possible race condition between blkio_commit and pwait. + // Consider: what if scheduler complete the request before pwait even get + // called? + // Two possible work around: + // #1. we disable the interrupt before schedule the request. + // #2. we do scheduling within interrupt context (e.g., attach a timer) + // As we don't want to overwhelming the interrupt context and also keep the + // request RTT as small as possible, hence #1 is preferred. + + /* + FIXME + Potential racing here. + happened when blkio is committed at high volumn, while the + block device has very little latency. + This is particular serious for non-async blkio, it could + completed before we do pwait, causing the thread hanged indefinitely + */ + + if (blkio_stalled(ctx)) { + if ((options & BLKIO_WAIT)) { + blkio_schedule(ctx); + try_wait_check_stall(); + return; + } blkio_schedule(ctx); + } else if ((options & BLKIO_WAIT)) { + try_wait_check_stall(); } } void blkio_schedule(struct blkio_context* ctx) { + // stall the pipeline if ctx is locked by others. + // we must not try to hold the lock in this case, as + // blkio_schedule will be in irq context most of the + // time, we can't afford the waiting there. + if (mutex_on_hold(&ctx->lock)) { + return; + } + + // will always successed when in irq context + blkio_lock(ctx); + if (llist_empty(&ctx->queue)) { + blkio_unlock(ctx); return; } @@ -94,7 +159,9 @@ blkio_schedule(struct blkio_context* ctx) llist_delete(&head->reqs); head->flags |= BLKIO_BUSY; - head->io_ctx->busy++; + ctx->busy++; + + blkio_unlock(ctx); ctx->handle_one(head); } @@ -102,21 +169,30 @@ blkio_schedule(struct blkio_context* ctx) void blkio_complete(struct blkio_req* req) { + struct blkio_context* ctx; + + ctx = req->io_ctx; req->flags &= ~(BLKIO_BUSY | BLKIO_PENDING); + // Wake all blocked processes on completion, + // albeit should be no more than one process in everycase (by design) + if ((req->flags & BLKIO_SHOULD_WAIT)) { + assert(!waitq_empty(&req->wait)); + pwake_all(&req->wait); + } + + if (req->errcode) { + WARN("request completed with error. (errno=0x%x, ctx=%p)", + req->errcode, (ptr_t)ctx); + } + if (req->completed) { req->completed(req); } - // Wake all blocked processes on completion, - // albeit should be no more than one process in everycase (by design) - pwake_all(&req->wait); - if ((req->flags & BLKIO_FOC)) { blkio_free_req(req); } - req->io_ctx->busy--; - - blkio_schedule(req->io_ctx); + ctx->busy--; } \ No newline at end of file