fat_idx.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2008 Jakub Jermar
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  * - Redistributions of source code must retain the above copyright
00010  *   notice, this list of conditions and the following disclaimer.
00011  * - Redistributions in binary form must reproduce the above copyright
00012  *   notice, this list of conditions and the following disclaimer in the
00013  *   documentation and/or other materials provided with the distribution.
00014  * - The name of the author may not be used to endorse or promote products
00015  *   derived from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00018  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00019  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00020  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00021  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00022  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00023  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00024  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00025  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00026  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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          * The least significant half of all bits are the least significant bits
00131          * of the parent node's first cluster.
00132          *
00133          * The least significant half of the most significant half of all bits
00134          * are the least significant bits of the node's dentry index within the
00135          * parent directory node.
00136          *
00137          * The most significant half of the most significant half of all bits
00138          * are the least significant bits of the device handle.
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         /* nothing to do */
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                          * There are no freed indices, allocate one directly
00255                          * from the counter.
00256                          */
00257                         *index = u->next++;
00258                         --u->remaining;
00259                         fibril_mutex_unlock(&unused_lock);
00260                         return true;
00261                 }
00262         } else {
00263                 /* There are some freed indices which we can reuse. */
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                         /* Destroy the interval. */
00269                         list_remove(&f->link);
00270                         free(f);
00271                 }
00272                 fibril_mutex_unlock(&unused_lock);
00273                 return true;
00274         }
00275         /*
00276          * We ran out of indices, which is extremely unlikely with FAT16, but
00277          * theoretically still possible (e.g. too many open unlinked nodes or
00278          * too many zero-sized nodes).
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                 /* The index can be returned directly to the counter. */
00313                 u->next--;
00314                 u->remaining++;
00315         } else {
00316                 /*
00317                  * The index must be returned either to an existing freed
00318                  * interval or a new interval must be created.
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                                 /* TODO: sleep until allocation succeeds */
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                 /* The index will form the last interval. */
00355                 n = malloc(sizeof(freed_t));
00356                 /* TODO: sleep until allocation succeeds */
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;      /* no parent yet */
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          * Since we can only free unlinked nodes, the index structure is not
00521          * present in the position hash (uph). We therefore hash it out from
00522          * the index hash only.
00523          */
00524         hash_table_remove(&ui_hash, ikey, 2);
00525         fibril_mutex_unlock(&used_lock);
00526         /* Release the VFS index. */
00527         fat_index_free(devmap_handle, index);
00528         /* The index structure itself is freed in idx_remove_callback(). */
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         /* We assume the hash tables are empty. */
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          * Remove this instance's index structure from up_hash and ui_hash.
00580          * Process up_hash first and ui_hash second because the index structure
00581          * is actually removed in idx_remove_callback(). 
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          * Free the unused and freed structures for this instance.
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 

Generated on Thu Jun 2 07:45:49 2011 for HelenOS/USB by  doxygen 1.4.7