00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00038 #include "fat.h"
00039 #include "../../vfs/vfs.h"
00040 #include <errno.h>
00041 #include <str.h>
00042 #include <adt/hash_table.h>
00043 #include <adt/list.h>
00044 #include <assert.h>
00045 #include <fibril_synch.h>
00046 #include <malloc.h>
00047
00049 typedef struct {
00050 link_t link;
00051 fs_index_t first;
00052 fs_index_t last;
00053 } freed_t;
00054
00059 typedef struct {
00060 link_t link;
00061 devmap_handle_t devmap_handle;
00062
00064 fs_index_t next;
00066 uint64_t remaining;
00067
00069 link_t freed_head;
00070 } unused_t;
00071
00073 static FIBRIL_MUTEX_INITIALIZE(unused_lock);
00074
00076 static LIST_INITIALIZE(unused_head);
00077
00078 static void unused_initialize(unused_t *u, devmap_handle_t devmap_handle)
00079 {
00080 link_initialize(&u->link);
00081 u->devmap_handle = devmap_handle;
00082 u->next = 0;
00083 u->remaining = ((uint64_t)((fs_index_t)-1)) + 1;
00084 list_initialize(&u->freed_head);
00085 }
00086
00087 static unused_t *unused_find(devmap_handle_t devmap_handle, bool lock)
00088 {
00089 unused_t *u;
00090 link_t *l;
00091
00092 if (lock)
00093 fibril_mutex_lock(&unused_lock);
00094 for (l = unused_head.next; l != &unused_head; l = l->next) {
00095 u = list_get_instance(l, unused_t, link);
00096 if (u->devmap_handle == devmap_handle)
00097 return u;
00098 }
00099 if (lock)
00100 fibril_mutex_unlock(&unused_lock);
00101 return NULL;
00102 }
00103
00105 static FIBRIL_MUTEX_INITIALIZE(used_lock);
00106
00112 static hash_table_t up_hash;
00113
00114 #define UPH_BUCKETS_LOG 12
00115 #define UPH_BUCKETS (1 << UPH_BUCKETS_LOG)
00116
00117 #define UPH_DH_KEY 0
00118 #define UPH_PFC_KEY 1
00119 #define UPH_PDI_KEY 2
00120
00121 static hash_index_t pos_hash(unsigned long key[])
00122 {
00123 devmap_handle_t devmap_handle = (devmap_handle_t)key[UPH_DH_KEY];
00124 fat_cluster_t pfc = (fat_cluster_t)key[UPH_PFC_KEY];
00125 unsigned pdi = (unsigned)key[UPH_PDI_KEY];
00126
00127 hash_index_t h;
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140 h = pfc & ((1 << (UPH_BUCKETS_LOG / 2)) - 1);
00141 h |= (pdi & ((1 << (UPH_BUCKETS_LOG / 4)) - 1)) <<
00142 (UPH_BUCKETS_LOG / 2);
00143 h |= (devmap_handle & ((1 << (UPH_BUCKETS_LOG / 4)) - 1)) <<
00144 (3 * (UPH_BUCKETS_LOG / 4));
00145
00146 return h;
00147 }
00148
00149 static int pos_compare(unsigned long key[], hash_count_t keys, link_t *item)
00150 {
00151 devmap_handle_t devmap_handle = (devmap_handle_t)key[UPH_DH_KEY];
00152 fat_cluster_t pfc;
00153 unsigned pdi;
00154 fat_idx_t *fidx = list_get_instance(item, fat_idx_t, uph_link);
00155
00156 switch (keys) {
00157 case 1:
00158 return (devmap_handle == fidx->devmap_handle);
00159 case 3:
00160 pfc = (fat_cluster_t) key[UPH_PFC_KEY];
00161 pdi = (unsigned) key[UPH_PDI_KEY];
00162 return (devmap_handle == fidx->devmap_handle) && (pfc == fidx->pfc) &&
00163 (pdi == fidx->pdi);
00164 default:
00165 assert((keys == 1) || (keys == 3));
00166 }
00167
00168 return 0;
00169 }
00170
00171 static void pos_remove_callback(link_t *item)
00172 {
00173
00174 }
00175
00176 static hash_table_operations_t uph_ops = {
00177 .hash = pos_hash,
00178 .compare = pos_compare,
00179 .remove_callback = pos_remove_callback,
00180 };
00181
00186 static hash_table_t ui_hash;
00187
00188 #define UIH_BUCKETS_LOG 12
00189 #define UIH_BUCKETS (1 << UIH_BUCKETS_LOG)
00190
00191 #define UIH_DH_KEY 0
00192 #define UIH_INDEX_KEY 1
00193
00194 static hash_index_t idx_hash(unsigned long key[])
00195 {
00196 devmap_handle_t devmap_handle = (devmap_handle_t)key[UIH_DH_KEY];
00197 fs_index_t index = (fs_index_t)key[UIH_INDEX_KEY];
00198
00199 hash_index_t h;
00200
00201 h = devmap_handle & ((1 << (UIH_BUCKETS_LOG / 2)) - 1);
00202 h |= (index & ((1 << (UIH_BUCKETS_LOG / 2)) - 1)) <<
00203 (UIH_BUCKETS_LOG / 2);
00204
00205 return h;
00206 }
00207
00208 static int idx_compare(unsigned long key[], hash_count_t keys, link_t *item)
00209 {
00210 devmap_handle_t devmap_handle = (devmap_handle_t)key[UIH_DH_KEY];
00211 fs_index_t index;
00212 fat_idx_t *fidx = list_get_instance(item, fat_idx_t, uih_link);
00213
00214 switch (keys) {
00215 case 1:
00216 return (devmap_handle == fidx->devmap_handle);
00217 case 2:
00218 index = (fs_index_t) key[UIH_INDEX_KEY];
00219 return (devmap_handle == fidx->devmap_handle) &&
00220 (index == fidx->index);
00221 default:
00222 assert((keys == 1) || (keys == 2));
00223 }
00224
00225 return 0;
00226 }
00227
00228 static void idx_remove_callback(link_t *item)
00229 {
00230 fat_idx_t *fidx = list_get_instance(item, fat_idx_t, uih_link);
00231
00232 free(fidx);
00233 }
00234
00235 static hash_table_operations_t uih_ops = {
00236 .hash = idx_hash,
00237 .compare = idx_compare,
00238 .remove_callback = idx_remove_callback,
00239 };
00240
00242 static bool fat_index_alloc(devmap_handle_t devmap_handle, fs_index_t *index)
00243 {
00244 unused_t *u;
00245
00246 assert(index);
00247 u = unused_find(devmap_handle, true);
00248 if (!u)
00249 return false;
00250
00251 if (list_empty(&u->freed_head)) {
00252 if (u->remaining) {
00253
00254
00255
00256
00257 *index = u->next++;
00258 --u->remaining;
00259 fibril_mutex_unlock(&unused_lock);
00260 return true;
00261 }
00262 } else {
00263
00264 freed_t *f = list_get_instance(u->freed_head.next, freed_t,
00265 link);
00266 *index = f->first;
00267 if (f->first++ == f->last) {
00268
00269 list_remove(&f->link);
00270 free(f);
00271 }
00272 fibril_mutex_unlock(&unused_lock);
00273 return true;
00274 }
00275
00276
00277
00278
00279
00280 fibril_mutex_unlock(&unused_lock);
00281 return false;
00282 }
00283
00285 static void try_coalesce_intervals(link_t *l, link_t *r, link_t *cur)
00286 {
00287 freed_t *fl = list_get_instance(l, freed_t, link);
00288 freed_t *fr = list_get_instance(r, freed_t, link);
00289
00290 if (fl->last + 1 == fr->first) {
00291 if (cur == l) {
00292 fl->last = fr->last;
00293 list_remove(r);
00294 free(r);
00295 } else {
00296 fr->first = fl->first;
00297 list_remove(l);
00298 free(l);
00299 }
00300 }
00301 }
00302
00304 static void fat_index_free(devmap_handle_t devmap_handle, fs_index_t index)
00305 {
00306 unused_t *u;
00307
00308 u = unused_find(devmap_handle, true);
00309 assert(u);
00310
00311 if (u->next == index + 1) {
00312
00313 u->next--;
00314 u->remaining++;
00315 } else {
00316
00317
00318
00319
00320 link_t *lnk;
00321 freed_t *n;
00322 for (lnk = u->freed_head.next; lnk != &u->freed_head;
00323 lnk = lnk->next) {
00324 freed_t *f = list_get_instance(lnk, freed_t, link);
00325 if (f->first == index + 1) {
00326 f->first--;
00327 if (lnk->prev != &u->freed_head)
00328 try_coalesce_intervals(lnk->prev, lnk,
00329 lnk);
00330 fibril_mutex_unlock(&unused_lock);
00331 return;
00332 }
00333 if (f->last == index - 1) {
00334 f->last++;
00335 if (lnk->next != &u->freed_head)
00336 try_coalesce_intervals(lnk, lnk->next,
00337 lnk);
00338 fibril_mutex_unlock(&unused_lock);
00339 return;
00340 }
00341 if (index > f->first) {
00342 n = malloc(sizeof(freed_t));
00343
00344 assert(n);
00345 link_initialize(&n->link);
00346 n->first = index;
00347 n->last = index;
00348 list_insert_before(&n->link, lnk);
00349 fibril_mutex_unlock(&unused_lock);
00350 return;
00351 }
00352
00353 }
00354
00355 n = malloc(sizeof(freed_t));
00356
00357 assert(n);
00358 link_initialize(&n->link);
00359 n->first = index;
00360 n->last = index;
00361 list_append(&n->link, &u->freed_head);
00362 }
00363 fibril_mutex_unlock(&unused_lock);
00364 }
00365
00366 static int fat_idx_create(fat_idx_t **fidxp, devmap_handle_t devmap_handle)
00367 {
00368 fat_idx_t *fidx;
00369
00370 fidx = (fat_idx_t *) malloc(sizeof(fat_idx_t));
00371 if (!fidx)
00372 return ENOMEM;
00373 if (!fat_index_alloc(devmap_handle, &fidx->index)) {
00374 free(fidx);
00375 return ENOSPC;
00376 }
00377
00378 link_initialize(&fidx->uph_link);
00379 link_initialize(&fidx->uih_link);
00380 fibril_mutex_initialize(&fidx->lock);
00381 fidx->devmap_handle = devmap_handle;
00382 fidx->pfc = FAT_CLST_RES0;
00383 fidx->pdi = 0;
00384 fidx->nodep = NULL;
00385
00386 *fidxp = fidx;
00387 return EOK;
00388 }
00389
00390 int fat_idx_get_new(fat_idx_t **fidxp, devmap_handle_t devmap_handle)
00391 {
00392 fat_idx_t *fidx;
00393 int rc;
00394
00395 fibril_mutex_lock(&used_lock);
00396 rc = fat_idx_create(&fidx, devmap_handle);
00397 if (rc != EOK) {
00398 fibril_mutex_unlock(&used_lock);
00399 return rc;
00400 }
00401
00402 unsigned long ikey[] = {
00403 [UIH_DH_KEY] = devmap_handle,
00404 [UIH_INDEX_KEY] = fidx->index,
00405 };
00406
00407 hash_table_insert(&ui_hash, ikey, &fidx->uih_link);
00408 fibril_mutex_lock(&fidx->lock);
00409 fibril_mutex_unlock(&used_lock);
00410
00411 *fidxp = fidx;
00412 return EOK;
00413 }
00414
00415 fat_idx_t *
00416 fat_idx_get_by_pos(devmap_handle_t devmap_handle, fat_cluster_t pfc, unsigned pdi)
00417 {
00418 fat_idx_t *fidx;
00419 link_t *l;
00420 unsigned long pkey[] = {
00421 [UPH_DH_KEY] = devmap_handle,
00422 [UPH_PFC_KEY] = pfc,
00423 [UPH_PDI_KEY] = pdi,
00424 };
00425
00426 fibril_mutex_lock(&used_lock);
00427 l = hash_table_find(&up_hash, pkey);
00428 if (l) {
00429 fidx = hash_table_get_instance(l, fat_idx_t, uph_link);
00430 } else {
00431 int rc;
00432
00433 rc = fat_idx_create(&fidx, devmap_handle);
00434 if (rc != EOK) {
00435 fibril_mutex_unlock(&used_lock);
00436 return NULL;
00437 }
00438
00439 unsigned long ikey[] = {
00440 [UIH_DH_KEY] = devmap_handle,
00441 [UIH_INDEX_KEY] = fidx->index,
00442 };
00443
00444 fidx->pfc = pfc;
00445 fidx->pdi = pdi;
00446
00447 hash_table_insert(&up_hash, pkey, &fidx->uph_link);
00448 hash_table_insert(&ui_hash, ikey, &fidx->uih_link);
00449 }
00450 fibril_mutex_lock(&fidx->lock);
00451 fibril_mutex_unlock(&used_lock);
00452
00453 return fidx;
00454 }
00455
00456 void fat_idx_hashin(fat_idx_t *idx)
00457 {
00458 unsigned long pkey[] = {
00459 [UPH_DH_KEY] = idx->devmap_handle,
00460 [UPH_PFC_KEY] = idx->pfc,
00461 [UPH_PDI_KEY] = idx->pdi,
00462 };
00463
00464 fibril_mutex_lock(&used_lock);
00465 hash_table_insert(&up_hash, pkey, &idx->uph_link);
00466 fibril_mutex_unlock(&used_lock);
00467 }
00468
00469 void fat_idx_hashout(fat_idx_t *idx)
00470 {
00471 unsigned long pkey[] = {
00472 [UPH_DH_KEY] = idx->devmap_handle,
00473 [UPH_PFC_KEY] = idx->pfc,
00474 [UPH_PDI_KEY] = idx->pdi,
00475 };
00476
00477 fibril_mutex_lock(&used_lock);
00478 hash_table_remove(&up_hash, pkey, 3);
00479 fibril_mutex_unlock(&used_lock);
00480 }
00481
00482 fat_idx_t *
00483 fat_idx_get_by_index(devmap_handle_t devmap_handle, fs_index_t index)
00484 {
00485 fat_idx_t *fidx = NULL;
00486 link_t *l;
00487 unsigned long ikey[] = {
00488 [UIH_DH_KEY] = devmap_handle,
00489 [UIH_INDEX_KEY] = index,
00490 };
00491
00492 fibril_mutex_lock(&used_lock);
00493 l = hash_table_find(&ui_hash, ikey);
00494 if (l) {
00495 fidx = hash_table_get_instance(l, fat_idx_t, uih_link);
00496 fibril_mutex_lock(&fidx->lock);
00497 }
00498 fibril_mutex_unlock(&used_lock);
00499
00500 return fidx;
00501 }
00502
00507 void fat_idx_destroy(fat_idx_t *idx)
00508 {
00509 unsigned long ikey[] = {
00510 [UIH_DH_KEY] = idx->devmap_handle,
00511 [UIH_INDEX_KEY] = idx->index,
00512 };
00513 devmap_handle_t devmap_handle = idx->devmap_handle;
00514 fs_index_t index = idx->index;
00515
00516 assert(idx->pfc == FAT_CLST_RES0);
00517
00518 fibril_mutex_lock(&used_lock);
00519
00520
00521
00522
00523
00524 hash_table_remove(&ui_hash, ikey, 2);
00525 fibril_mutex_unlock(&used_lock);
00526
00527 fat_index_free(devmap_handle, index);
00528
00529 }
00530
00531 int fat_idx_init(void)
00532 {
00533 if (!hash_table_create(&up_hash, UPH_BUCKETS, 3, &uph_ops))
00534 return ENOMEM;
00535 if (!hash_table_create(&ui_hash, UIH_BUCKETS, 2, &uih_ops)) {
00536 hash_table_destroy(&up_hash);
00537 return ENOMEM;
00538 }
00539 return EOK;
00540 }
00541
00542 void fat_idx_fini(void)
00543 {
00544
00545 hash_table_destroy(&up_hash);
00546 hash_table_destroy(&ui_hash);
00547 }
00548
00549 int fat_idx_init_by_devmap_handle(devmap_handle_t devmap_handle)
00550 {
00551 unused_t *u;
00552 int rc = EOK;
00553
00554 u = (unused_t *) malloc(sizeof(unused_t));
00555 if (!u)
00556 return ENOMEM;
00557 unused_initialize(u, devmap_handle);
00558 fibril_mutex_lock(&unused_lock);
00559 if (!unused_find(devmap_handle, false)) {
00560 list_append(&u->link, &unused_head);
00561 } else {
00562 free(u);
00563 rc = EEXIST;
00564 }
00565 fibril_mutex_unlock(&unused_lock);
00566 return rc;
00567 }
00568
00569 void fat_idx_fini_by_devmap_handle(devmap_handle_t devmap_handle)
00570 {
00571 unsigned long ikey[] = {
00572 [UIH_DH_KEY] = devmap_handle
00573 };
00574 unsigned long pkey[] = {
00575 [UPH_DH_KEY] = devmap_handle
00576 };
00577
00578
00579
00580
00581
00582
00583 fibril_mutex_lock(&used_lock);
00584 hash_table_remove(&up_hash, pkey, 1);
00585 hash_table_remove(&ui_hash, ikey, 1);
00586 fibril_mutex_unlock(&used_lock);
00587
00588
00589
00590
00591 unused_t *u = unused_find(devmap_handle, true);
00592 assert(u);
00593 list_remove(&u->link);
00594 fibril_mutex_unlock(&unused_lock);
00595
00596 while (!list_empty(&u->freed_head)) {
00597 freed_t *f;
00598 f = list_get_instance(u->freed_head.next, freed_t, link);
00599 list_remove(&f->link);
00600 free(f);
00601 }
00602 free(u);
00603 }
00604