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

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

Add hash table for used fat_idx_t structures and implement fat_idx_map().

  • Property mode set to 100644
File size: 7.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 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 <string.h>
42#include <libadt/hash_table.h>
43#include <libadt/list.h>
44#include <assert.h>
45#include <futex.h>
46
47/** Each instance of this type describes one interval of freed VFS indices. */
48typedef struct {
49 link_t link;
50 fs_index_t first;
51 fs_index_t last;
52} freed_t;
53
54/**
55 * Each instance of this type describes state of all VFS indices that
56 * are currently unused.
57 */
58typedef struct {
59 link_t link;
60 dev_handle_t dev_handle;
61
62 /** Next unassigned index. */
63 fs_index_t next;
64 /** Number of remaining unassigned indices. */
65 uint64_t remaining;
66
67 /** Sorted list of intervals of freed indices. */
68 link_t freed_head;
69} unused_t;
70
71static LIST_INITIALIZE(unused_head);
72
73/** Global hash table of all used fat_idx_t structures. */
74static hash_table_t used_hash;
75
76#define USED_HASH_BUCKETS_LOG 12
77#define USED_HASH_BUCKETS (1 << USED_HASH_BUCKETS_LOG)
78
79#define USED_HASH_DH_KEY 0
80#define USED_HASH_PFC_KEY 1
81#define USED_HASH_PDI_KEY 2
82
83static hash_index_t idx_hash(unsigned long key[])
84{
85 dev_handle_t dev_handle = (dev_handle_t)key[USED_HASH_DH_KEY];
86 fat_cluster_t pfc = (fat_cluster_t)key[USED_HASH_PFC_KEY];
87 unsigned pdi = (unsigned)key[USED_HASH_PDI_KEY];
88
89 hash_index_t h;
90
91 /*
92 * The least significant half of all bits are the least significant bits
93 * of the parent node's first cluster.
94 *
95 * The least significant half of the most significant half of all bits
96 * are the least significant bits of the node's dentry index within the
97 * parent directory node.
98 *
99 * The most significant half of the most significant half of all bits
100 * are the least significant bits of the device handle.
101 */
102 h = pfc & ((USED_HASH_BUCKETS_LOG / 2) - 1);
103 h |= (pdi & ((USED_HASH_BUCKETS_LOG / 4) - 1)) <<
104 (USED_HASH_BUCKETS_LOG / 2);
105 h |= (dev_handle & ((USED_HASH_BUCKETS_LOG / 4) - 1)) <<
106 (3 * (USED_HASH_BUCKETS_LOG / 4));
107
108 return h;
109}
110
111static int idx_compare(unsigned long key[], hash_count_t keys, link_t *item)
112{
113 dev_handle_t dev_handle = (dev_handle_t)key[USED_HASH_DH_KEY];
114 fat_cluster_t pfc = (fat_cluster_t)key[USED_HASH_PFC_KEY];
115 unsigned pdi = (unsigned)key[USED_HASH_PDI_KEY];
116 fat_idx_t *fidx = list_get_instance(item, fat_idx_t, uh_link);
117
118 return (dev_handle == fidx->dev_handle) && (pfc == fidx->pfc) &&
119 (pdi == fidx->pdi);
120}
121
122static void idx_remove_callback(link_t *item)
123{
124 /* nothing to do */
125}
126
127static hash_table_operations_t used_idx_ops = {
128 .hash = idx_hash,
129 .compare = idx_compare,
130 .remove_callback = idx_remove_callback,
131};
132
133/** Allocate a VFS index which is not currently in use. */
134static bool fat_idx_alloc(dev_handle_t dev_handle, fs_index_t *index)
135{
136 link_t *l;
137 unused_t *u;
138
139 assert(index);
140 for (l = unused_head.next; l != &unused_head; l = l->next) {
141 u = list_get_instance(l, unused_t, link);
142 if (u->dev_handle == dev_handle)
143 goto hit;
144 }
145
146 /* dev_handle not found */
147 return false;
148
149hit:
150 if (list_empty(&u->freed_head)) {
151 if (u->remaining) {
152 /*
153 * There are no freed indices, allocate one directly
154 * from the counter.
155 */
156 *index = u->next++;
157 --u->remaining;
158 return true;
159 }
160 } else {
161 /* There are some freed indices which we can reuse. */
162 freed_t *f = list_get_instance(u->freed_head.next, freed_t,
163 link);
164 *index = f->first;
165 if (f->first++ == f->last) {
166 /* Destroy the interval. */
167 list_remove(&f->link);
168 free(f);
169 }
170 return true;
171 }
172 /*
173 * We ran out of indices, which is extremely unlikely with FAT16, but
174 * theoretically still possible (e.g. too many open unlinked nodes or
175 * too many zero-sized nodes).
176 */
177 return false;
178}
179
180/** If possible, coalesce two intervals of freed indices. */
181static void try_coalesce_intervals(link_t *l, link_t *r, link_t *cur)
182{
183 freed_t *fl = list_get_instance(l, freed_t, link);
184 freed_t *fr = list_get_instance(r, freed_t, link);
185
186 if (fl->last + 1 == fr->first) {
187 if (cur == l) {
188 fl->last = fr->last;
189 list_remove(r);
190 free(r);
191 } else {
192 fr->first = fl->first;
193 list_remove(l);
194 free(l);
195 }
196 }
197}
198
199/** Free a VFS index, which is no longer in use. */
200static void fat_idx_free(dev_handle_t dev_handle, fs_index_t index)
201{
202 link_t *l;
203 unused_t *u;
204
205 for (l = unused_head.next; l != &unused_head; l = l->next) {
206 u = list_get_instance(l, unused_t, link);
207 if (u->dev_handle == dev_handle)
208 goto hit;
209 }
210
211 /* should not happen */
212 assert(0);
213
214hit:
215 if (u->next == index + 1) {
216 /* The index can be returned directly to the counter. */
217 u->next--;
218 u->remaining++;
219 return;
220 } else {
221 /*
222 * The index must be returned either to an existing freed
223 * interval or a new interval must be created.
224 */
225 link_t *lnk;
226 freed_t *n;
227 for (lnk = u->freed_head.next; lnk != &u->freed_head;
228 lnk = lnk->next) {
229 freed_t *f = list_get_instance(lnk, freed_t, link);
230 if (f->first == index + 1) {
231 f->first--;
232 if (lnk->prev != &u->freed_head)
233 try_coalesce_intervals(lnk->prev, lnk,
234 lnk);
235 return;
236 }
237 if (f->last == index - 1) {
238 f->last++;
239 if (lnk->next != &u->freed_head)
240 try_coalesce_intervals(lnk, lnk->next,
241 lnk);
242 return;
243 }
244 if (index > f->first) {
245 n = malloc(sizeof(freed_t));
246 /* TODO: sleep until allocation succeeds */
247 assert(n);
248 link_initialize(&n->link);
249 n->first = index;
250 n->last = index;
251 list_insert_before(&n->link, lnk);
252 return;
253 }
254
255 }
256 /* The index will form the last interval. */
257 n = malloc(sizeof(freed_t));
258 /* TODO: sleep until allocation succeeds */
259 assert(n);
260 link_initialize(&n->link);
261 n->first = index;
262 n->last = index;
263 list_append(&n->link, &u->freed_head);
264 }
265}
266
267fat_idx_t *fat_idx_map(dev_handle_t dev_handle, fat_cluster_t pfc, unsigned pdi)
268{
269 fat_idx_t *fidx;
270 link_t *l;
271 unsigned long key[] = {
272 [USED_HASH_DH_KEY] = dev_handle,
273 [USED_HASH_PFC_KEY] = pfc,
274 [USED_HASH_PDI_KEY] = pdi,
275 };
276
277 l = hash_table_find(&used_hash, key);
278 if (l) {
279 fidx = hash_table_get_instance(l, fat_idx_t, uh_link);
280 } else {
281 fidx = (fat_idx_t *) malloc(sizeof(fat_idx_t));
282 if (!fidx) {
283 return NULL;
284 }
285 if (!fat_idx_alloc(dev_handle, &fidx->index)) {
286 free(fidx);
287 return NULL;
288 }
289 link_initialize(&fidx->uh_link);
290 fidx->dev_handle = dev_handle;
291 fidx->pfc = pfc;
292 fidx->pdi = pdi;
293 fidx->nodep = NULL;
294 hash_table_insert(&used_hash, key, &fidx->uh_link);
295 }
296
297 return fidx;
298}
299
Note: See TracBrowser for help on using the repository browser.