Multiuser, Capabilities and Access Controls (#54)
[lunaix-os.git] / lunaix-os / kernel / block / blkio.c
1 #include <lunaix/blkio.h>
2 #include <lunaix/syslog.h>
3 #include <lunaix/mm/cake.h>
4 #include <lunaix/mm/valloc.h>
5
6 #include <asm/cpu.h>
7
8 static struct cake_pile* blkio_reqpile;
9
10 LOG_MODULE("blkio")
11
12 void
13 blkio_init()
14 {
15     blkio_reqpile = cake_new_pile("blkio_req", sizeof(struct blkio_req), 1, 0);
16 }
17
18 static inline struct blkio_req*
19 __blkio_req_create(struct vecbuf* buffer,
20                    u64_t start_lba,
21                    blkio_cb completed,
22                    void* evt_args,
23                    u32_t options)
24 {
25     options = options & ~0xf;
26     struct blkio_req* breq = (struct blkio_req*)cake_grab(blkio_reqpile);
27     *breq = (struct blkio_req){ .blk_addr = start_lba,
28                                 .completed = completed,
29                                 .flags = options,
30                                 .evt_args = evt_args };
31     breq->vbuf = buffer;
32     waitq_init(&breq->wait);
33     return breq;
34 }
35
36 struct blkio_req*
37 blkio_vrd(struct vecbuf* buffer,
38           u64_t start_lba,
39           blkio_cb completed,
40           void* evt_args,
41           u32_t options)
42 {
43     return __blkio_req_create(buffer, start_lba, completed, evt_args, options);
44 }
45
46 struct blkio_req*
47 blkio_vwr(struct vecbuf* buffer,
48           u64_t start_lba,
49           blkio_cb completed,
50           void* evt_args,
51           u32_t options)
52 {
53     struct blkio_req* breq =
54       __blkio_req_create(buffer, start_lba, completed, evt_args, options);
55     breq->flags |= BLKIO_WRITE;
56     return breq;
57 }
58
59 void
60 blkio_free_req(struct blkio_req* req)
61 {
62     cake_release(blkio_reqpile, (void*)req);
63 }
64
65 struct blkio_context*
66 blkio_newctx(req_handler handler)
67 {
68     struct blkio_context* ctx =
69       (struct blkio_context*)vzalloc(sizeof(struct blkio_context));
70     ctx->handle_one = handler;
71
72     llist_init_head(&ctx->queue);
73     mutex_init(&ctx->lock);
74
75     return ctx;
76 }
77
78 void
79 blkio_commit(struct blkio_req* req, int options)
80 {
81     struct blkio_context* ctx;
82
83     if (blkio_is_pending(req)) {
84         // prevent double submition
85         return;
86     }
87
88     assert(req->io_ctx);
89
90     req->flags |= BLKIO_PENDING;
91     
92     if ((options & BLKIO_WAIT)) {
93         req->flags |= BLKIO_SHOULD_WAIT;
94         prepare_to_wait(&req->wait);
95     }
96     else {
97         req->flags &= ~BLKIO_SHOULD_WAIT;
98     }
99
100     ctx = req->io_ctx;
101
102     blkio_lock(ctx);
103     
104     llist_append(&ctx->queue, &req->reqs);
105     
106     blkio_unlock(ctx);
107     // if the pipeline is not running (e.g., stalling). Then we should schedule
108     // one immediately and kick it started.
109     // NOTE: Possible race condition between blkio_commit and pwait.
110     // Consider: what if scheduler complete the request before pwait even get
111     // called?
112     // Two possible work around:
113     //  #1. we disable the interrupt before schedule the request.
114     //  #2. we do scheduling within interrupt context (e.g., attach a timer)
115     // As we don't want to overwhelming the interrupt context and also keep the
116     // request RTT as small as possible, hence #1 is preferred.
117
118     /*
119         FIXME
120         Potential racing here.
121         happened when blkio is committed at high volumn, while the
122          block device has very little latency.
123         This is particular serious for non-async blkio, it could
124          completed before we do pwait, causing the thread hanged indefinitely
125     */
126
127     if (blkio_stalled(ctx)) {
128         if ((options & BLKIO_WAIT)) {
129             blkio_schedule(ctx);
130             try_wait_check_stall();
131             return;
132         }
133         blkio_schedule(ctx);
134     } else if ((options & BLKIO_WAIT)) {
135         try_wait_check_stall();
136     }
137 }
138
139 void
140 blkio_schedule(struct blkio_context* ctx)
141 {
142     // stall the pipeline if ctx is locked by others.
143     // we must not try to hold the lock in this case, as
144     //  blkio_schedule will be in irq context most of the
145     //  time, we can't afford the waiting there.
146     if (mutex_on_hold(&ctx->lock)) {
147         return;
148     }
149
150     // will always successed when in irq context
151     blkio_lock(ctx);
152
153     if (llist_empty(&ctx->queue)) {
154         blkio_unlock(ctx);
155         return;
156     }
157
158     struct blkio_req* head = (struct blkio_req*)ctx->queue.next;
159     llist_delete(&head->reqs);
160
161     head->flags |= BLKIO_BUSY;
162     ctx->busy++;
163
164     blkio_unlock(ctx);
165
166     ctx->handle_one(head);
167 }
168
169 void
170 blkio_complete(struct blkio_req* req)
171 {
172     struct blkio_context* ctx;
173
174     ctx = req->io_ctx;
175     req->flags &= ~(BLKIO_BUSY | BLKIO_PENDING);
176
177     // Wake all blocked processes on completion,
178     //  albeit should be no more than one process in everycase (by design)
179     if ((req->flags & BLKIO_SHOULD_WAIT)) {
180         assert(!waitq_empty(&req->wait));
181         pwake_all(&req->wait);
182     }
183
184     if (req->errcode) {
185         WARN("request completed with error. (errno=0x%x, ctx=%p)",
186                 req->errcode, (ptr_t)ctx);
187     }
188
189     if (req->completed) {
190         req->completed(req);
191     }
192
193     if ((req->flags & BLKIO_FOC)) {
194         blkio_free_req(req);
195     }
196
197     ctx->busy--;
198 }