source: mainline/uspace/srv/fs/exfat/exfat_idx.c@ ccfbf71

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since ccfbf71 was 9cc1f43, checked in by Oleg Romanenko <romanenko.oleg@…>, 14 years ago

Migrate changes from mainline to exfat_idx.c

  • Property mode set to 100644
File size: 14.8 KB
Line 
1/*
2 * Copyright (c) 2008 Jakub Jermar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup fs
30 * @{
31 */
32
33/**
34 * @file exfat_idx.c
35 * @brief Layer for translating exFAT entities to VFS node indices.
36 */
37
38#include "exfat.h"
39#include "../../vfs/vfs.h"
40#include <errno.h>
41#include <str.h>
42#include <adt/hash_table.h>
43#include <adt/list.h>
44#include <assert.h>
45#include <fibril_synch.h>
46#include <malloc.h>
47
48/** Each instance of this type describes one interval of freed VFS indices. */
49typedef struct {
50 link_t link;
51 fs_index_t first;
52 fs_index_t last;
53} freed_t;
54
55/**
56 * Each instance of this type describes state of all VFS indices that
57 * are currently unused.
58 */
59typedef struct {
60 link_t link;
61 devmap_handle_t devmap_handle;
62
63 /** Next unassigned index. */
64 fs_index_t next;
65 /** Number of remaining unassigned indices. */
66 uint64_t remaining;
67
68 /** Sorted list of intervals of freed indices. */
69 link_t freed_list;
70} unused_t;
71
72/** Mutex protecting the list of unused structures. */
73static FIBRIL_MUTEX_INITIALIZE(unused_lock);
74
75/** List of unused structures. */
76static LIST_INITIALIZE(unused_list);
77
78static void unused_initialize(unused_t *u, devmap_handle_t devmap_handle)
79{
80 link_initialize(&u->link);
81 u->devmap_handle = devmap_handle;
82 u->next = 0;
83 u->remaining = ((uint64_t)((fs_index_t)-1)) + 1;
84 list_initialize(&u->freed_list);
85}
86
87static unused_t *unused_find(devmap_handle_t devmap_handle, bool lock)
88{
89 unused_t *u;
90 link_t *l;
91
92 if (lock)
93 fibril_mutex_lock(&unused_lock);
94 list_foreach(unused_list, l) {
95 u = list_get_instance(l, unused_t, link);
96 if (u->devmap_handle == devmap_handle)
97 return u;
98 }
99
100 if (lock)
101 fibril_mutex_unlock(&unused_lock);
102 return NULL;
103}
104
105/** Mutex protecting the up_hash and ui_hash. */
106static FIBRIL_MUTEX_INITIALIZE(used_lock);
107
108/**
109 * Global hash table of all used exfat_idx_t structures.
110 * The index structures are hashed by the devmap_handle, parent node's first
111 * cluster and index within the parent directory.
112 */
113static hash_table_t up_hash;
114
115#define UPH_BUCKETS_LOG 12
116#define UPH_BUCKETS (1 << UPH_BUCKETS_LOG)
117
118#define UPH_DH_KEY 0
119#define UPH_PFC_KEY 1
120#define UPH_PDI_KEY 2
121
122static hash_index_t pos_hash(unsigned long key[])
123{
124 devmap_handle_t devmap_handle = (devmap_handle_t)key[UPH_DH_KEY];
125 exfat_cluster_t pfc = (exfat_cluster_t)key[UPH_PFC_KEY];
126 unsigned pdi = (unsigned)key[UPH_PDI_KEY];
127
128 hash_index_t h;
129
130 /*
131 * The least significant half of all bits are the least significant bits
132 * of the parent node's first cluster.
133 *
134 * The least significant half of the most significant half of all bits
135 * are the least significant bits of the node's dentry index within the
136 * parent directory node.
137 *
138 * The most significant half of the most significant half of all bits
139 * are the least significant bits of the device handle.
140 */
141 h = pfc & ((1 << (UPH_BUCKETS_LOG / 2)) - 1);
142 h |= (pdi & ((1 << (UPH_BUCKETS_LOG / 4)) - 1)) <<
143 (UPH_BUCKETS_LOG / 2);
144 h |= (devmap_handle & ((1 << (UPH_BUCKETS_LOG / 4)) - 1)) <<
145 (3 * (UPH_BUCKETS_LOG / 4));
146
147 return h;
148}
149
150static int pos_compare(unsigned long key[], hash_count_t keys, link_t *item)
151{
152 devmap_handle_t devmap_handle = (devmap_handle_t)key[UPH_DH_KEY];
153 exfat_cluster_t pfc;
154 unsigned pdi;
155 exfat_idx_t *fidx = list_get_instance(item, exfat_idx_t, uph_link);
156
157 switch (keys) {
158 case 1:
159 return (devmap_handle == fidx->devmap_handle);
160 case 3:
161 pfc = (exfat_cluster_t) key[UPH_PFC_KEY];
162 pdi = (unsigned) key[UPH_PDI_KEY];
163 return (devmap_handle == fidx->devmap_handle) && (pfc == fidx->pfc) &&
164 (pdi == fidx->pdi);
165 default:
166 assert((keys == 1) || (keys == 3));
167 }
168
169 return 0;
170}
171
172static void pos_remove_callback(link_t *item)
173{
174 /* nothing to do */
175}
176
177static hash_table_operations_t uph_ops = {
178 .hash = pos_hash,
179 .compare = pos_compare,
180 .remove_callback = pos_remove_callback,
181};
182
183/**
184 * Global hash table of all used fat_idx_t structures.
185 * The index structures are hashed by the devmap_handle and index.
186 */
187static hash_table_t ui_hash;
188
189#define UIH_BUCKETS_LOG 12
190#define UIH_BUCKETS (1 << UIH_BUCKETS_LOG)
191
192#define UIH_DH_KEY 0
193#define UIH_INDEX_KEY 1
194
195static hash_index_t idx_hash(unsigned long key[])
196{
197 devmap_handle_t devmap_handle = (devmap_handle_t)key[UIH_DH_KEY];
198 fs_index_t index = (fs_index_t)key[UIH_INDEX_KEY];
199
200 hash_index_t h;
201
202 h = devmap_handle & ((1 << (UIH_BUCKETS_LOG / 2)) - 1);
203 h |= (index & ((1 << (UIH_BUCKETS_LOG / 2)) - 1)) <<
204 (UIH_BUCKETS_LOG / 2);
205
206 return h;
207}
208
209static int idx_compare(unsigned long key[], hash_count_t keys, link_t *item)
210{
211 devmap_handle_t devmap_handle = (devmap_handle_t)key[UIH_DH_KEY];
212 fs_index_t index;
213 exfat_idx_t *fidx = list_get_instance(item, exfat_idx_t, uih_link);
214
215 switch (keys) {
216 case 1:
217 return (devmap_handle == fidx->devmap_handle);
218 case 2:
219 index = (fs_index_t) key[UIH_INDEX_KEY];
220 return (devmap_handle == fidx->devmap_handle) &&
221 (index == fidx->index);
222 default:
223 assert((keys == 1) || (keys == 2));
224 }
225
226 return 0;
227}
228
229static void idx_remove_callback(link_t *item)
230{
231 exfat_idx_t *fidx = list_get_instance(item, exfat_idx_t, uih_link);
232
233 free(fidx);
234}
235
236static hash_table_operations_t uih_ops = {
237 .hash = idx_hash,
238 .compare = idx_compare,
239 .remove_callback = idx_remove_callback,
240};
241
242/** Allocate a VFS index which is not currently in use. */
243static bool exfat_index_alloc(devmap_handle_t devmap_handle, fs_index_t *index)
244{
245 unused_t *u;
246
247 assert(index);
248 u = unused_find(devmap_handle, true);
249 if (!u)
250 return false;
251
252 if (list_empty(&u->freed_list)) {
253 if (u->remaining) {
254 /*
255 * There are no freed indices, allocate one directly
256 * from the counter.
257 */
258 *index = u->next++;
259 --u->remaining;
260 fibril_mutex_unlock(&unused_lock);
261 return true;
262 }
263 } else {
264 /* There are some freed indices which we can reuse. */
265 freed_t *f = list_get_instance(list_first(&u->freed_list),
266 freed_t, link);
267 *index = f->first;
268 if (f->first++ == f->last) {
269 /* Destroy the interval. */
270 list_remove(&f->link);
271 free(f);
272 }
273 fibril_mutex_unlock(&unused_lock);
274 return true;
275 }
276 /*
277 * We ran out of indices, which is extremely unlikely with FAT16, but
278 * theoretically still possible (e.g. too many open unlinked nodes or
279 * too many zero-sized nodes).
280 */
281 fibril_mutex_unlock(&unused_lock);
282 return false;
283}
284
285/** If possible, coalesce two intervals of freed indices. */
286static void try_coalesce_intervals(link_t *l, link_t *r, link_t *cur)
287{
288 freed_t *fl = list_get_instance(l, freed_t, link);
289 freed_t *fr = list_get_instance(r, freed_t, link);
290
291 if (fl->last + 1 == fr->first) {
292 if (cur == l) {
293 fl->last = fr->last;
294 list_remove(r);
295 free(r);
296 } else {
297 fr->first = fl->first;
298 list_remove(l);
299 free(l);
300 }
301 }
302}
303
304/** Free a VFS index, which is no longer in use. */
305static void exfat_index_free(devmap_handle_t devmap_handle, fs_index_t index)
306{
307 unused_t *u;
308
309 u = unused_find(devmap_handle, true);
310 assert(u);
311
312 if (u->next == index + 1) {
313 /* The index can be returned directly to the counter. */
314 u->next--;
315 u->remaining++;
316 } else {
317 /*
318 * The index must be returned either to an existing freed
319 * interval or a new interval must be created.
320 */
321 link_t *lnk;
322 freed_t *n;
323 for (lnk = u->freed_list.head.next; lnk != &u->freed_list.head;
324 lnk = lnk->next) {
325 freed_t *f = list_get_instance(lnk, freed_t, link);
326 if (f->first == index + 1) {
327 f->first--;
328 if (lnk->prev != &u->freed_list.head)
329 try_coalesce_intervals(lnk->prev, lnk,
330 lnk);
331 fibril_mutex_unlock(&unused_lock);
332 return;
333 }
334 if (f->last == index - 1) {
335 f->last++;
336 if (lnk->next != &u->freed_list.head)
337 try_coalesce_intervals(lnk, lnk->next,
338 lnk);
339 fibril_mutex_unlock(&unused_lock);
340 return;
341 }
342 if (index > f->first) {
343 n = malloc(sizeof(freed_t));
344 /* TODO: sleep until allocation succeeds */
345 assert(n);
346 link_initialize(&n->link);
347 n->first = index;
348 n->last = index;
349 list_insert_before(&n->link, lnk);
350 fibril_mutex_unlock(&unused_lock);
351 return;
352 }
353
354 }
355 /* The index will form the last interval. */
356 n = malloc(sizeof(freed_t));
357 /* TODO: sleep until allocation succeeds */
358 assert(n);
359 link_initialize(&n->link);
360 n->first = index;
361 n->last = index;
362 list_append(&n->link, &u->freed_list);
363 }
364 fibril_mutex_unlock(&unused_lock);
365}
366
367static int exfat_idx_create(exfat_idx_t **fidxp, devmap_handle_t devmap_handle)
368{
369 exfat_idx_t *fidx;
370
371 fidx = (exfat_idx_t *) malloc(sizeof(exfat_idx_t));
372 if (!fidx)
373 return ENOMEM;
374 if (!exfat_index_alloc(devmap_handle, &fidx->index)) {
375 free(fidx);
376 return ENOSPC;
377 }
378
379 link_initialize(&fidx->uph_link);
380 link_initialize(&fidx->uih_link);
381 fibril_mutex_initialize(&fidx->lock);
382 fidx->devmap_handle = devmap_handle;
383 fidx->pfc = 0; /* no parent yet */
384 fidx->pdi = 0;
385 fidx->nodep = NULL;
386
387 *fidxp = fidx;
388 return EOK;
389}
390
391int exfat_idx_get_new(exfat_idx_t **fidxp, devmap_handle_t devmap_handle)
392{
393 exfat_idx_t *fidx;
394 int rc;
395
396 fibril_mutex_lock(&used_lock);
397 rc = exfat_idx_create(&fidx, devmap_handle);
398 if (rc != EOK) {
399 fibril_mutex_unlock(&used_lock);
400 return rc;
401 }
402
403 unsigned long ikey[] = {
404 [UIH_DH_KEY] = devmap_handle,
405 [UIH_INDEX_KEY] = fidx->index,
406 };
407
408 hash_table_insert(&ui_hash, ikey, &fidx->uih_link);
409 fibril_mutex_lock(&fidx->lock);
410 fibril_mutex_unlock(&used_lock);
411
412 *fidxp = fidx;
413 return EOK;
414}
415
416exfat_idx_t *
417exfat_idx_get_by_pos(devmap_handle_t devmap_handle, exfat_cluster_t pfc, unsigned pdi)
418{
419 exfat_idx_t *fidx;
420 link_t *l;
421 unsigned long pkey[] = {
422 [UPH_DH_KEY] = devmap_handle,
423 [UPH_PFC_KEY] = pfc,
424 [UPH_PDI_KEY] = pdi,
425 };
426
427 fibril_mutex_lock(&used_lock);
428 l = hash_table_find(&up_hash, pkey);
429 if (l) {
430 fidx = hash_table_get_instance(l, exfat_idx_t, uph_link);
431 } else {
432 int rc;
433
434 rc = exfat_idx_create(&fidx, devmap_handle);
435 if (rc != EOK) {
436 fibril_mutex_unlock(&used_lock);
437 return NULL;
438 }
439
440 unsigned long ikey[] = {
441 [UIH_DH_KEY] = devmap_handle,
442 [UIH_INDEX_KEY] = fidx->index,
443 };
444
445 fidx->pfc = pfc;
446 fidx->pdi = pdi;
447
448 hash_table_insert(&up_hash, pkey, &fidx->uph_link);
449 hash_table_insert(&ui_hash, ikey, &fidx->uih_link);
450 }
451 fibril_mutex_lock(&fidx->lock);
452 fibril_mutex_unlock(&used_lock);
453
454 return fidx;
455}
456
457void exfat_idx_hashin(exfat_idx_t *idx)
458{
459 unsigned long pkey[] = {
460 [UPH_DH_KEY] = idx->devmap_handle,
461 [UPH_PFC_KEY] = idx->pfc,
462 [UPH_PDI_KEY] = idx->pdi,
463 };
464
465 fibril_mutex_lock(&used_lock);
466 hash_table_insert(&up_hash, pkey, &idx->uph_link);
467 fibril_mutex_unlock(&used_lock);
468}
469
470void exfat_idx_hashout(exfat_idx_t *idx)
471{
472 unsigned long pkey[] = {
473 [UPH_DH_KEY] = idx->devmap_handle,
474 [UPH_PFC_KEY] = idx->pfc,
475 [UPH_PDI_KEY] = idx->pdi,
476 };
477
478 fibril_mutex_lock(&used_lock);
479 hash_table_remove(&up_hash, pkey, 3);
480 fibril_mutex_unlock(&used_lock);
481}
482
483exfat_idx_t *
484exfat_idx_get_by_index(devmap_handle_t devmap_handle, fs_index_t index)
485{
486 exfat_idx_t *fidx = NULL;
487 link_t *l;
488 unsigned long ikey[] = {
489 [UIH_DH_KEY] = devmap_handle,
490 [UIH_INDEX_KEY] = index,
491 };
492
493 fibril_mutex_lock(&used_lock);
494 l = hash_table_find(&ui_hash, ikey);
495 if (l) {
496 fidx = hash_table_get_instance(l, exfat_idx_t, uih_link);
497 fibril_mutex_lock(&fidx->lock);
498 }
499 fibril_mutex_unlock(&used_lock);
500
501 return fidx;
502}
503
504/** Destroy the index structure.
505 *
506 * @param idx The index structure to be destroyed.
507 */
508void exfat_idx_destroy(exfat_idx_t *idx)
509{
510 unsigned long ikey[] = {
511 [UIH_DH_KEY] = idx->devmap_handle,
512 [UIH_INDEX_KEY] = idx->index,
513 };
514 devmap_handle_t devmap_handle = idx->devmap_handle;
515 fs_index_t index = idx->index;
516
517 /* TODO: assert(idx->pfc == FAT_CLST_RES0); */
518 assert(idx->pfc == 0);
519
520 fibril_mutex_lock(&used_lock);
521 /*
522 * Since we can only free unlinked nodes, the index structure is not
523 * present in the position hash (uph). We therefore hash it out from
524 * the index hash only.
525 */
526 hash_table_remove(&ui_hash, ikey, 2);
527 fibril_mutex_unlock(&used_lock);
528 /* Release the VFS index. */
529 exfat_index_free(devmap_handle, index);
530 /* The index structure itself is freed in idx_remove_callback(). */
531}
532
533int exfat_idx_init(void)
534{
535 if (!hash_table_create(&up_hash, UPH_BUCKETS, 3, &uph_ops))
536 return ENOMEM;
537 if (!hash_table_create(&ui_hash, UIH_BUCKETS, 2, &uih_ops)) {
538 hash_table_destroy(&up_hash);
539 return ENOMEM;
540 }
541 return EOK;
542}
543
544void exfat_idx_fini(void)
545{
546 /* We assume the hash tables are empty. */
547 hash_table_destroy(&up_hash);
548 hash_table_destroy(&ui_hash);
549}
550
551int exfat_idx_init_by_devmap_handle(devmap_handle_t devmap_handle)
552{
553 unused_t *u;
554 int rc = EOK;
555
556 u = (unused_t *) malloc(sizeof(unused_t));
557 if (!u)
558 return ENOMEM;
559 unused_initialize(u, devmap_handle);
560 fibril_mutex_lock(&unused_lock);
561 if (!unused_find(devmap_handle, false)) {
562 list_append(&u->link, &unused_list);
563 } else {
564 free(u);
565 rc = EEXIST;
566 }
567 fibril_mutex_unlock(&unused_lock);
568 return rc;
569}
570
571void exfat_idx_fini_by_devmap_handle(devmap_handle_t devmap_handle)
572{
573 unsigned long ikey[] = {
574 [UIH_DH_KEY] = devmap_handle
575 };
576 unsigned long pkey[] = {
577 [UPH_DH_KEY] = devmap_handle
578 };
579
580 /*
581 * Remove this instance's index structure from up_hash and ui_hash.
582 * Process up_hash first and ui_hash second because the index structure
583 * is actually removed in idx_remove_callback().
584 */
585 fibril_mutex_lock(&used_lock);
586 hash_table_remove(&up_hash, pkey, 1);
587 hash_table_remove(&ui_hash, ikey, 1);
588 fibril_mutex_unlock(&used_lock);
589
590 /*
591 * Free the unused and freed structures for this instance.
592 */
593 unused_t *u = unused_find(devmap_handle, true);
594 assert(u);
595 list_remove(&u->link);
596 fibril_mutex_unlock(&unused_lock);
597
598 while (!list_empty(&u->freed_list)) {
599 freed_t *f;
600 f = list_get_instance(list_first(&u->freed_list), freed_t, link);
601 list_remove(&f->link);
602 free(f);
603 }
604 free(u);
605}
606
607/**
608 * @}
609 */
Note: See TracBrowser for help on using the repository browser.