001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018 #include <sos/ksynch.h>
019 #include <sos/kmem_slab.h>
020 #include <sos/hash.h>
021 #include <sos/physmem.h> /* For SOS_PAGE_MASK */
022 #include <sos/list.h>
023 #include <sos/assert.h>
024 #include <sos/uaccess.h>
025 #include <sos/kmalloc.h>
026
027 #include "fs_pagecache.h"
028
029
030 #define SOS_OFFSET64_PAGE_ALIGN_INF(offs64) \
031 ( ((sos_luoffset_t)(offs64)) & (~ ((sos_luoffset_t)(SOS_PAGE_MASK))) )
032
033
034 #define SOS_OFFSET64_IS_PAGE_ALIGNED(offs64) \
035 ( ( ((sos_luoffset_t)(offs64)) & (((sos_luoffset_t)(SOS_PAGE_MASK))) ) == 0 )
036
037
038
039
040
041
042
043
044
045 struct sos_fs_pagecache_entry
046 {
047
048 sos_luoffset_t file_offset;
049
050
051 sos_vaddr_t kernel_vaddr;
052
053 struct sos_kmutex lock;
054 sos_count_t ref_cnt;
055
056 sos_bool_t initial_fill_aborted;
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079 sos_lcount_t rw_dirty_order;
080 #define ENTRY_IS_RW_DIRTY(e) ((e)->rw_dirty_order > 0)
081
082
083 struct sos_hash_linkage hlink;
084
085
086 struct sos_fs_pagecache_entry *prev, *next;
087 };
088
089
090 struct sos_fs_pagecache
091 {
092
093
094 sos_fs_pagecache_sync_function_t sync_fct;
095 void * sync_fct_custom_data;
096
097
098 struct sos_hash_table * lookup_table;
099
100
101 struct sos_fs_pagecache_entry * rw_sync_list;
102
103
104
105 struct sos_fs_pagecache_entry * rw_dirty_list;
106
107
108
109
110
111 sos_lcount_t top_rw_dirty_order;
112 };
113
114
115
116 static struct sos_kslab_cache * cache_of_pagecache;
117
118
119
120 static struct sos_kslab_cache * cache_of_pagecache_entry;
121
122
123 sos_ret_t sos_fs_pagecache_subsystem_setup()
124 {
125
126
127
128 cache_of_pagecache =
129 sos_kmem_cache_create("pagecache", sizeof(struct sos_fs_pagecache),
130 2, 0, SOS_KSLAB_CREATE_MAP | SOS_KSLAB_CREATE_ZERO);
131 if (NULL == cache_of_pagecache)
132 return -SOS_ENOMEM;
133
134 cache_of_pagecache_entry =
135 sos_kmem_cache_create("pagecache_entry",
136 sizeof(struct sos_fs_pagecache_entry),
137 2, 0, SOS_KSLAB_CREATE_MAP | SOS_KSLAB_CREATE_ZERO);
138 if (NULL == cache_of_pagecache_entry)
139 {
140 sos_kmem_cache_destroy(cache_of_pagecache);
141 return -SOS_ENOMEM;
142 }
143
144 return SOS_OK;
145 }
146
147
148 struct sos_fs_pagecache *
149 sos_fs_pagecache_new_cache(sos_fs_pagecache_sync_function_t sync_fct,
150 void * sync_fct_custom_data)
151 {
152 struct sos_fs_pagecache * pagecache
153 = (struct sos_fs_pagecache*) sos_kmem_cache_alloc(cache_of_pagecache,
154 0);
155 if (NULL == pagecache)
156 return NULL;
157
158 pagecache->lookup_table = sos_hash_create("pagecache",
159 struct sos_fs_pagecache_entry,
160 sos_hash_ui64,
161 sos_hash_key_eq_ui64,
162 127, file_offset, hlink);
163 if (NULL == pagecache->lookup_table)
164 {
165 sos_kmem_cache_free((sos_vaddr_t) pagecache);
166 return NULL;
167 }
168
169 pagecache->sync_fct = sync_fct;
170 pagecache->sync_fct_custom_data = sync_fct_custom_data;
171 pagecache->top_rw_dirty_order = 0x24;
172
173 return pagecache;
174 }
175
176
177 sos_ret_t
178 sos_fs_pagecache_delete_cache(struct sos_fs_pagecache * pc)
179 {
180
181
182 if (!list_is_empty(pc->rw_dirty_list))
183 SOS_FATAL_ERROR("Non empty dirty list");
184 if (!list_is_empty(pc->rw_sync_list))
185 SOS_FATAL_ERROR("Non empty sync list");
186
187 sos_hash_dispose(pc->lookup_table);
188 return sos_kmem_cache_free((sos_vaddr_t)pc);
189 }
190
191
192
193
194 static sos_ret_t pagecache_sync_page(struct sos_fs_pagecache * pc,
195 struct sos_fs_pagecache_entry * entry)
196 {
197 sos_ret_t retval;
198
199 if (! ENTRY_IS_RW_DIRTY(entry))
200 return SOS_OK;
201
202
203 retval = pc->sync_fct(entry->file_offset, entry->kernel_vaddr,
204 pc->sync_fct_custom_data);
205 if (SOS_OK != retval)
206 return retval;
207
208
209 list_delete(pc->rw_dirty_list, entry);
210 entry->rw_dirty_order = 0;
211 list_add_head(pc->rw_sync_list, entry);
212
213 return SOS_OK;
214 }
215
216
217
218 static sos_ret_t pagecache_use(struct sos_fs_pagecache * pc,
219 struct sos_fs_pagecache_entry * entry)
220 {
221 entry->ref_cnt ++;
222 return sos_kmutex_lock(& entry->lock, NULL);
223 }
224
225
226
227
228
229 static sos_ret_t pagecache_set_rw_dirty(struct sos_fs_pagecache * pc,
230 struct sos_fs_pagecache_entry * entry)
231 {
232 if (ENTRY_IS_RW_DIRTY(entry))
233 return SOS_OK;
234
235 list_delete(pc->rw_sync_list, entry);
236 entry->rw_dirty_order = ++ pc->top_rw_dirty_order;
237 list_add_head(pc->rw_dirty_list, entry);
238
239 return SOS_OK;
240 }
241
242
243
244
245 static sos_ret_t pagecache_release(struct sos_fs_pagecache * pc,
246 struct sos_fs_pagecache_entry * entry)
247 {
248 if (entry->ref_cnt > 1)
249 {
250 entry->ref_cnt --;
251 sos_kmutex_unlock(& entry->lock);
252 return SOS_OK;
253 }
254
255
256
257
258
259
260
261
262
263 if (! entry->initial_fill_aborted)
264 pagecache_sync_page(pc, entry);
265
266
267 entry->ref_cnt --;
268
269
270
271 if (entry->ref_cnt > 0)
272 {
273 sos_kmutex_unlock(& entry->lock);
274 return SOS_OK;
275 }
276
277
278 sos_hash_remove(pc->lookup_table, entry);
279 if (ENTRY_IS_RW_DIRTY(entry))
280 list_delete(pc->rw_dirty_list, entry);
281 else
282 list_delete(pc->rw_sync_list, entry);
283
284
285 sos_kmutex_unlock(& entry->lock);
286 SOS_ASSERT_FATAL(SOS_OK == sos_kmutex_dispose(& entry->lock));
287 sos_kfree(entry->kernel_vaddr);
288 sos_kmem_cache_free((sos_vaddr_t)entry);
289
290 return SOS_OK;
291 }
292
293
294
295
296
297
298 static struct sos_fs_pagecache_entry *
299 pagecache_lookup_and_lock(struct sos_fs_pagecache * pc,
300 sos_luoffset_t offset)
301 {
302 sos_luoffset_t pgoffs = SOS_OFFSET64_PAGE_ALIGN_INF(offset);
303 struct sos_fs_pagecache_entry * entry = NULL;
304
305 while (TRUE)
306 {
307 entry
308 = (struct sos_fs_pagecache_entry*) sos_hash_lookup(pc->lookup_table,
309 & pgoffs);
310 if (! entry)
311 break;
312
313
314 SOS_ASSERT_FATAL(SOS_OK == pagecache_use(pc, entry));
315
316
317
318
319
320
321
322
323
324 if (entry->initial_fill_aborted)
325 {
326 pagecache_release(pc, entry);
327 continue;
328 }
329
330
331 break;
332 }
333
334 return entry;
335 }
336
337
338 sos_ret_t
339 sos_fs_pagecache_read(struct sos_fs_pagecache * pc,
340 sos_luoffset_t offset,
341 sos_genaddr_t dest_buf,
342 sos_size_t * len)
343 {
344 sos_ret_t retval;
345 sos_luoffset_t pgoffs = SOS_OFFSET64_PAGE_ALIGN_INF(offset);
346 sos_luoffset_t endpos = offset + *len;
347 struct sos_fs_pagecache_entry * entry;
348
349 entry = pagecache_lookup_and_lock(pc, pgoffs);
350 if (NULL == entry)
351 return -SOS_ENOENT;
352
353
354
355
356 if (endpos - pgoffs > SOS_PAGE_SIZE)
357 endpos = pgoffs + SOS_PAGE_SIZE;
358
359
360 retval = sos_memcpy_generic_to(dest_buf,
361 entry->kernel_vaddr + (offset - pgoffs),
362 endpos - offset);
363 pagecache_release(pc, entry);
364
365 if (retval < 0)
366 {
367 *len = 0;
368 return retval;
369 }
370
371 *len = retval;
372 if ((sos_luoffset_t)retval != endpos - offset)
373 return -SOS_EFAULT;
374
375 return SOS_OK;
376 }
377
378
379 sos_ret_t
380 sos_fs_pagecache_write(struct sos_fs_pagecache * pc,
381 sos_luoffset_t offset,
382 sos_genaddr_t src_buf,
383 sos_size_t * len,
384 sos_bool_t synchronous_write)
385 {
386 sos_ret_t retval;
387 sos_luoffset_t pgoffs = SOS_OFFSET64_PAGE_ALIGN_INF(offset);
388 sos_luoffset_t endpos = offset + *len;
389 struct sos_fs_pagecache_entry * entry;
390
391 entry = pagecache_lookup_and_lock(pc, pgoffs);
392 if (NULL == entry)
393 return -SOS_ENOENT;
394
395
396
397
398 if (endpos - pgoffs > SOS_PAGE_SIZE)
399 endpos = pgoffs + SOS_PAGE_SIZE;
400
401
402 retval = sos_memcpy_generic_from(entry->kernel_vaddr + (offset - pgoffs),
403 src_buf,
404 endpos - offset);
405
406 if (retval >= 0)
407 pagecache_set_rw_dirty(pc, entry);
408
409 if (retval < 0)
410 {
411 *len = 0;
412 pagecache_release(pc, entry);
413 return retval;
414 }
415
416 *len = retval;
417 if ((sos_luoffset_t)retval != endpos - offset)
418 retval = -SOS_EFAULT;
419 else
420 retval = SOS_OK;
421
422
423 if (synchronous_write)
424 {
425 sos_ret_t ret = pagecache_sync_page(pc, entry);
426 if (SOS_OK == retval)
427 retval = ret;
428 }
429
430 pagecache_release(pc, entry);
431 return retval;
432 }
433
434
435 sos_ret_t sos_fs_pagecache_set_dirty(struct sos_fs_pagecache * pc,
436 sos_luoffset_t offset,
437 sos_bool_t sync_backing_store)
438 {
439 sos_luoffset_t pgoffs = SOS_OFFSET64_PAGE_ALIGN_INF(offset);
440 struct sos_fs_pagecache_entry * entry;
441
442 entry = pagecache_lookup_and_lock(pc, pgoffs);
443 if (NULL == entry)
444 return -SOS_ENOENT;
445
446
447 pagecache_set_rw_dirty(pc, entry);
448
449
450 if (sync_backing_store)
451 pagecache_sync_page(pc, entry);
452
453 pagecache_release(pc, entry);
454 return SOS_OK;
455 }
456
457
458 struct sos_fs_pagecache_entry *
459 sos_fs_pagecache_ref_page(struct sos_fs_pagecache * pc,
460 sos_luoffset_t offset,
461 sos_vaddr_t * kernel_vaddr,
462 sos_bool_t * newly_allocated)
463 {
464 sos_luoffset_t pgoffs = SOS_OFFSET64_PAGE_ALIGN_INF(offset);
465 struct sos_fs_pagecache_entry * entry;
466
467
468 if (pgoffs != offset)
469 return NULL;
470
471 entry = pagecache_lookup_and_lock(pc, pgoffs);
472 if (NULL != entry)
473 {
474
475 *newly_allocated = FALSE;
476 *kernel_vaddr = entry->kernel_vaddr;
477 return entry;
478 }
479
480
481
482
483
484
485 entry = (struct sos_fs_pagecache_entry*)
486 sos_kmem_cache_alloc(cache_of_pagecache_entry, 0);
487 if (NULL == entry)
488 return (sos_vaddr_t)NULL;
489
490 if (SOS_OK != sos_kmutex_init(& entry->lock, "pagecache_entry",
491 SOS_KWQ_ORDER_FIFO))
492 {
493 sos_kmem_cache_free((sos_vaddr_t)entry);
494 return NULL;
495 }
496
497
498
499 entry->file_offset = pgoffs;
500 entry->initial_fill_aborted = TRUE;
501 entry->ref_cnt = 1;
502
503
504 entry->kernel_vaddr = sos_kmalloc(SOS_PAGE_SIZE, 0);
505 if (((sos_vaddr_t)NULL) == entry->kernel_vaddr)
506 {
507 sos_kmutex_dispose(& entry->lock);
508 sos_kmem_cache_free((sos_vaddr_t)entry);
509 return NULL;
510 }
511
512
513 SOS_ASSERT_FATAL(SOS_OK == sos_kmutex_lock(& entry->lock, NULL));
514
515
516
517
518 if (SOS_OK != sos_hash_insert(pc->lookup_table, entry))
519 {
520
521 sos_kmutex_unlock(& entry->lock);
522 sos_kmutex_dispose(& entry->lock);
523 sos_kfree(entry->kernel_vaddr);
524 sos_kmem_cache_free((sos_vaddr_t)entry);
525
526
527 entry = pagecache_lookup_and_lock(pc, offset);
528 SOS_ASSERT_FATAL(NULL != entry);
529 *kernel_vaddr = entry->kernel_vaddr;
530 *newly_allocated = FALSE;
531 return entry;
532 }
533
534
535 entry->rw_dirty_order = 0;
536 list_add_head(pc->rw_sync_list, entry);
537
538 *newly_allocated = TRUE;
539 *kernel_vaddr = entry->kernel_vaddr;
540 return entry;
541 }
542
543
544 sos_ret_t
545 sos_fs_pagecache_unlock_page(struct sos_fs_pagecache * pc,
546 struct sos_fs_pagecache_entry * entry,
547 sos_bool_t initial_fill_aborted)
548 {
549
550 entry->initial_fill_aborted = initial_fill_aborted;
551
552 if (initial_fill_aborted)
553 return pagecache_release(pc, entry);
554
555 return sos_kmutex_unlock(& entry->lock);
556 }
557
558
559 sos_ret_t
560 sos_fs_pagecache_unref_page(struct sos_fs_pagecache * pc,
561 sos_luoffset_t offset)
562 {
563 sos_luoffset_t pgoffs = SOS_OFFSET64_PAGE_ALIGN_INF(offset);
564 struct sos_fs_pagecache_entry * entry;
565
566
567 if (pgoffs != offset)
568 return -SOS_EINVAL;
569
570 entry
571 = (struct sos_fs_pagecache_entry*) sos_hash_lookup(pc->lookup_table,
572 & pgoffs);
573 SOS_ASSERT_FATAL(NULL != entry);
574 SOS_ASSERT_FATAL(SOS_OK == sos_kmutex_lock(& entry->lock, NULL));
575 return pagecache_release(pc, entry);
576 }
577
578
579 sos_ret_t
580 sos_fs_pagecache_sync(struct sos_fs_pagecache * pc)
581 {
582 sos_ret_t retval = SOS_OK;
583 int dummy = 0;
584 sos_lcount_t rw_dirty_order = 0;
585
586
587
588 sos_lcount_t top_rw_dirty_order = pc->top_rw_dirty_order;
589
590 if (list_is_empty(pc->rw_dirty_list))
591 return SOS_OK;
592
593
594
595
596 while (TRUE)
597 {
598 struct sos_fs_pagecache_entry * entry = NULL;
599 int ndirty;
600
601
602
603 list_foreach_backward(pc->rw_dirty_list, entry, ndirty)
604 {
605 sos_ret_t ret = SOS_OK;
606 struct sos_fs_pagecache_entry * prev_entry = NULL;
607
608
609
610 if (entry->rw_dirty_order > top_rw_dirty_order)
611 break;
612
613 if (entry->rw_dirty_order <= rw_dirty_order)
614 continue;
615
616 rw_dirty_order = entry->rw_dirty_order;
617 prev_entry = entry->prev;
618
619 SOS_ASSERT_FATAL(SOS_OK == pagecache_use(pc, entry));
620 if (! entry->initial_fill_aborted)
621 ret = pagecache_sync_page(pc, entry);
622 if (SOS_OK != ret)
623 retval = ret;
624 pagecache_release(pc, entry);
625
626
627
628
629 if (prev_entry != entry->prev)
630 goto lookup_next_ent;
631 }
632
633
634 break;
635
636 lookup_next_ent:
637
638 dummy ++;
639 }
640
641 return retval;
642 }