source: mainline/uspace/srv/fs/fat/fat_idx.c@ c83a55c

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since c83a55c was ebddd71, checked in by Jakub Jermar <jakub@…>, 14 years ago

Rename U[IP]H_DK_KEY to U[IP]H_SID_KEY.

  • Property mode set to 100644
File size: 14.5 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 fat_idx.c
35 * @brief Layer for translating FAT entities to VFS node indices.
36 */
37
38#include "fat.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 service_id_t service_id;
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 list_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, service_id_t service_id)
79{
80 link_initialize(&u->link);
81 u->service_id = service_id;
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(service_id_t service_id, bool lock)
88{
89 unused_t *u;
90
91 if (lock)
92 fibril_mutex_lock(&unused_lock);
93
94 list_foreach(unused_list, l) {
95 u = list_get_instance(l, unused_t, link);
96 if (u->service_id == service_id)
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 fat_idx_t structures.
110 * The index structures are hashed by the service_id, 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_SID_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 service_id_t service_id = (service_id_t)key[UPH_SID_KEY];
125 fat_cluster_t pfc = (fat_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 |= (service_id & ((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 service_id_t service_id = (service_id_t)key[UPH_SID_KEY];
153 fat_cluster_t pfc;
154 unsigned pdi;
155 fat_idx_t *fidx = list_get_instance(item, fat_idx_t, uph_link);
156
157 switch (keys) {
158 case 1:
159 return (service_id == fidx->service_id);
160 case 3:
161 pfc = (fat_cluster_t) key[UPH_PFC_KEY];
162 pdi = (unsigned) key[UPH_PDI_KEY];
163 return (service_id == fidx->service_id) && (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 service_id 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_SID_KEY 0
193#define UIH_INDEX_KEY 1
194
195static hash_index_t idx_hash(unsigned long key[])
196{
197 service_id_t service_id = (service_id_t)key[UIH_SID_KEY];
198 fs_index_t index = (fs_index_t)key[UIH_INDEX_KEY];
199
200 hash_index_t h;
201
202 h = service_id & ((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 service_id_t service_id = (service_id_t)key[UIH_SID_KEY];
212 fs_index_t index;
213 fat_idx_t *fidx = list_get_instance(item, fat_idx_t, uih_link);
214
215 switch (keys) {
216 case 1:
217 return (service_id == fidx->service_id);
218 case 2:
219 index = (fs_index_t) key[UIH_INDEX_KEY];
220 return (service_id == fidx->service_id) &&
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 fat_idx_t *fidx = list_get_instance(item, fat_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 fat_index_alloc(service_id_t service_id, fs_index_t *index)
244{
245 unused_t *u;
246
247 assert(index);
248 u = unused_find(service_id, 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 fat_index_free(service_id_t service_id, fs_index_t index)
306{
307 unused_t *u;
308
309 u = unused_find(service_id, 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 fat_idx_create(fat_idx_t **fidxp, service_id_t service_id)
368{
369 fat_idx_t *fidx;
370
371 fidx = (fat_idx_t *) malloc(sizeof(fat_idx_t));
372 if (!fidx)
373 return ENOMEM;
374 if (!fat_index_alloc(service_id, &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->service_id = service_id;
383 fidx->pfc = FAT_CLST_RES0; /* no parent yet */
384 fidx->pdi = 0;
385 fidx->nodep = NULL;
386
387 *fidxp = fidx;
388 return EOK;
389}
390
391int fat_idx_get_new(fat_idx_t **fidxp, service_id_t service_id)
392{
393 fat_idx_t *fidx;
394 int rc;
395
396 fibril_mutex_lock(&used_lock);
397 rc = fat_idx_create(&fidx, service_id);
398 if (rc != EOK) {
399 fibril_mutex_unlock(&used_lock);
400 return rc;
401 }
402
403 unsigned long ikey[] = {
404 [UIH_SID_KEY] = service_id,
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
416fat_idx_t *
417fat_idx_get_by_pos(service_id_t service_id, fat_cluster_t pfc, unsigned pdi)
418{
419 fat_idx_t *fidx;
420 link_t *l;
421 unsigned long pkey[] = {
422 [UPH_SID_KEY] = service_id,
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, fat_idx_t, uph_link);
431 } else {
432 int rc;
433
434 rc = fat_idx_create(&fidx, service_id);
435 if (rc != EOK) {
436 fibril_mutex_unlock(&used_lock);
437 return NULL;
438 }
439
440 unsigned long ikey[] = {
441 [UIH_SID_KEY] = service_id,
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 fat_idx_hashin(fat_idx_t *idx)
458{
459 unsigned long pkey[] = {
460 [UPH_SID_KEY] = idx->service_id,
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 fat_idx_hashout(fat_idx_t *idx)
471{
472 unsigned long pkey[] = {
473 [UPH_SID_KEY] = idx->service_id,
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
483fat_idx_t *
484fat_idx_get_by_index(service_id_t service_id, fs_index_t index)
485{
486 fat_idx_t *fidx = NULL;
487 link_t *l;
488 unsigned long ikey[] = {
489 [UIH_SID_KEY] = service_id,
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, fat_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 fat_idx_destroy(fat_idx_t *idx)
509{
510 unsigned long ikey[] = {
511 [UIH_SID_KEY] = idx->service_id,
512 [UIH_INDEX_KEY] = idx->index,
513 };
514 service_id_t service_id = idx->service_id;
515 fs_index_t index = idx->index;
516
517 assert(idx->pfc == FAT_CLST_RES0);
518
519 fibril_mutex_lock(&used_lock);
520 /*
521 * Since we can only free unlinked nodes, the index structure is not
522 * present in the position hash (uph). We therefore hash it out from
523 * the index hash only.
524 */
525 hash_table_remove(&ui_hash, ikey, 2);
526 fibril_mutex_unlock(&used_lock);
527 /* Release the VFS index. */
528 fat_index_free(service_id, index);
529 /* The index structure itself is freed in idx_remove_callback(). */
530}
531
532int fat_idx_init(void)
533{
534 if (!hash_table_create(&up_hash, UPH_BUCKETS, 3, &uph_ops))
535 return ENOMEM;
536 if (!hash_table_create(&ui_hash, UIH_BUCKETS, 2, &uih_ops)) {
537 hash_table_destroy(&up_hash);
538 return ENOMEM;
539 }
540 return EOK;
541}
542
543void fat_idx_fini(void)
544{
545 /* We assume the hash tables are empty. */
546 hash_table_destroy(&up_hash);
547 hash_table_destroy(&ui_hash);
548}
549
550int fat_idx_init_by_service_id(service_id_t service_id)
551{
552 unused_t *u;
553 int rc = EOK;
554
555 u = (unused_t *) malloc(sizeof(unused_t));
556 if (!u)
557 return ENOMEM;
558 unused_initialize(u, service_id);
559 fibril_mutex_lock(&unused_lock);
560 if (!unused_find(service_id, false)) {
561 list_append(&u->link, &unused_list);
562 } else {
563 free(u);
564 rc = EEXIST;
565 }
566 fibril_mutex_unlock(&unused_lock);
567 return rc;
568}
569
570void fat_idx_fini_by_service_id(service_id_t service_id)
571{
572 unsigned long ikey[] = {
573 [UIH_SID_KEY] = service_id
574 };
575 unsigned long pkey[] = {
576 [UPH_SID_KEY] = service_id
577 };
578
579 /*
580 * Remove this instance's index structure from up_hash and ui_hash.
581 * Process up_hash first and ui_hash second because the index structure
582 * is actually removed in idx_remove_callback().
583 */
584 fibril_mutex_lock(&used_lock);
585 hash_table_remove(&up_hash, pkey, 1);
586 hash_table_remove(&ui_hash, ikey, 1);
587 fibril_mutex_unlock(&used_lock);
588
589 /*
590 * Free the unused and freed structures for this instance.
591 */
592 unused_t *u = unused_find(service_id, true);
593 assert(u);
594 list_remove(&u->link);
595 fibril_mutex_unlock(&unused_lock);
596
597 while (!list_empty(&u->freed_list)) {
598 freed_t *f;
599 f = list_get_instance(list_first(&u->freed_list), freed_t, link);
600 list_remove(&f->link);
601 free(f);
602 }
603 free(u);
604}
605
606/**
607 * @}
608 */
Note: See TracBrowser for help on using the repository browser.