update readme introduction part, add demo screen-shot
[lunaix-os.git] / lunaix-os / kernel / block / blkbuf.c
1 #include <lunaix/blkbuf.h>
2 #include <lunaix/mm/cake.h>
3 #include <lunaix/mm/valloc.h>
4 #include <lunaix/owloysius.h>
5 #include <lunaix/syslog.h>
6 #include <asm/muldiv64.h>
7
8 LOG_MODULE("blkbuf")  
9
10 #define bb_cache_obj(bcache) \
11             container_of(bcache, struct blkbuf_cache, cached)
12
13 #define to_blkbuf(bbuf) ((struct blk_buf*)(bbuf))
14
15 static bcache_zone_t bb_zone;
16 static struct cake_pile* bb_pile;
17
18 static inline u64_t
19 __tolba(struct blkbuf_cache* cache, unsigned int blk_id)
20 {
21     return udiv64(((u64_t)cache->blksize * (u64_t)blk_id), 
22                     cache->blkdev->blk_size);
23 }
24
25 static void
26 __blkbuf_do_sync(struct bcache* bc, unsigned long tag, void* data)
27 {
28     return;
29 }
30
31 static void
32 __blkbuf_sync_callback(struct blkio_req* req)
33 {
34     struct blk_buf* buf;
35
36     buf = (struct blk_buf*)req->evt_args;
37
38     if (req->errcode) {
39         ERROR("sync failed: io error, 0x%x", req->errcode);
40         return;
41     }
42 }
43
44 static void
45 __blkbuf_evict_callback(struct blkio_req* req)
46 {
47     struct blk_buf* buf;
48
49     buf = (struct blk_buf*)req->evt_args;
50
51     if (req->errcode) {
52         ERROR("sync on evict failed (io error, 0x%x)", req->errcode);
53     }
54     
55     vfree(buf->raw);
56     vbuf_free(req->vbuf);
57     cake_release(bb_pile, buf);
58 }
59
60 static void
61 __blkbuf_do_try_release(struct bcache* bc, void* data)
62 {
63     struct blkio_req* req;
64     struct blk_buf* buf;
65
66     buf = (struct blk_buf*)data;
67     req = buf->breq;
68
69     if (llist_empty(&buf->dirty)) {
70         __blkbuf_evict_callback(req);
71         blkio_free_req(req);
72         return;
73     }
74     
75     // since we are evicting, don't care if the sync is failed
76     llist_delete(&buf->dirty);
77
78     blkio_when_completed(req, __blkbuf_evict_callback);
79     blkio_mark_foc(req);
80     blkio_commit(req, 0);
81 }
82
83 static struct bcache_ops cache_ops = {
84     .release_on_evict = __blkbuf_do_try_release,
85     .sync_cached = __blkbuf_do_sync
86 };
87
88 static bbuf_t
89 __blkbuf_take_slow_lockness(struct blkbuf_cache* bc, unsigned int block_id)
90 {
91     struct blk_buf* buf;
92     struct blkio_req* req;
93     struct vecbuf* vbuf;
94     void* data;
95     u64_t lba;
96
97     data = valloc(bc->blksize);
98
99     vbuf = NULL;
100     vbuf_alloc(&vbuf, data, bc->blksize);
101
102     lba = __tolba(bc, block_id);
103     buf = (struct blk_buf*)cake_grab(bb_pile);
104     req = blkio_vreq(vbuf, lba, __blkbuf_sync_callback, buf, 0);
105
106     // give dirty a know state
107     llist_init_head(&buf->dirty);
108
109     blkio_setread(req);
110     blkio_bindctx(req, bc->blkdev->blkio);
111     blkio_commit(req, BLKIO_WAIT);
112
113     if (req->errcode) {
114         ERROR("block io error (0x%x)", req->errcode);
115         cake_release(bb_pile, buf);
116         return (bbuf_t)INVL_BUFFER;
117     }
118
119     buf->raw = data;
120     buf->cobj = bcache_put_and_ref(&bc->cached, block_id, buf);
121     buf->breq = req;
122
123     return buf;
124 }
125
126 struct blkbuf_cache*
127 blkbuf_create(struct block_dev* blkdev, unsigned int blk_size)
128 {
129     struct blkbuf_cache* bb_cache;
130
131     assert(is_pot(blk_size));
132
133     bb_cache = valloc(sizeof(*bb_cache));
134     bb_cache->blkdev = blkdev;
135
136     bcache_init_zone(&bb_cache->cached, bb_zone, 3, -1, blk_size, &cache_ops);
137     llist_init_head(&bb_cache->dirty);
138     mutex_init(&bb_cache->lock);
139
140     return bb_cache;
141 }
142
143 bbuf_t
144 blkbuf_take(struct blkbuf_cache* bc, unsigned int block_id)
145 {
146     bcobj_t cobj;
147     mutex_lock(&bc->lock);
148     if (bcache_tryget(&bc->cached, block_id, &cobj)) {
149         mutex_unlock(&bc->lock);
150         return (bbuf_t)bcached_data(cobj);
151     }
152
153     bbuf_t buf = __blkbuf_take_slow_lockness(bc, block_id);
154     
155     mutex_unlock(&bc->lock);
156     return buf;
157 }
158
159 void
160 blkbuf_put(bbuf_t buf)
161 {
162     if (unlikely(!buf || blkbuf_errbuf(buf))) {
163         return;
164     }
165
166     struct blk_buf* bbuf;
167     bbuf = to_blkbuf(buf);
168
169     bcache_return(bbuf->cobj);
170 }
171
172 void
173 blkbuf_dirty(bbuf_t buf)
174 {
175     assert(buf && !blkbuf_errbuf(buf));
176
177     struct blk_buf* bbuf;
178     struct blkbuf_cache* bc;
179     
180     bbuf = ((struct blk_buf*)buf);
181     bc = bcache_holder_embed(bbuf->cobj, struct blkbuf_cache, cached);
182     
183     mutex_lock(&bc->lock);
184     
185     if (llist_empty(&bbuf->dirty)) {
186         llist_append(&bc->dirty, &bbuf->dirty);
187     }
188
189     mutex_unlock(&bc->lock);
190 }
191
192 static inline void
193 __schedule_sync_event(struct blk_buf* bbuf, bool wait)
194 {
195     struct blkio_req* blkio;
196
197     blkio = bbuf->breq;
198
199     blkio_setwrite(blkio);
200     blkio_commit(blkio, wait ? BLKIO_WAIT : BLKIO_NOWAIT);
201
202     llist_delete(&bbuf->dirty);
203 }
204
205 void
206 blkbuf_schedule_sync(bbuf_t buf)
207 {
208     struct blk_buf* bbuf;
209     bbuf = to_blkbuf(buf);
210
211     __schedule_sync_event(bbuf, false);
212 }
213
214 bool
215 blkbuf_syncall(struct blkbuf_cache* bc, bool async)
216 {
217     struct blk_buf *pos, *n;
218
219     mutex_lock(&bc->lock);
220
221     llist_for_each(pos, n, &bc->dirty, dirty) {
222         __schedule_sync_event(pos, !async);
223     }
224
225     mutex_unlock(&bc->lock);
226
227     if (async) {
228         return true;
229     }
230
231     return llist_empty(&bc->dirty);
232 }
233
234 void
235 blkbuf_release(struct blkbuf_cache* bc)
236 {
237     bcache_free(&bc->cached);
238     vfree(bc);
239 }
240
241 static void
242 __init_blkbuf()
243 {
244     bb_zone = bcache_create_zone("blk_buf");
245     bb_pile = cake_new_pile("blk_buf", sizeof(struct blk_buf), 1, 0);
246 }
247 owloysius_fetch_init(__init_blkbuf, on_sysconf)