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

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

Add locks to FAT index structures, FAT in-core node structures. Add futex to
protect the list of free cached FAT in-core nodes. Make fat_node_get() and
fat_node_put() and some other functions aware of these new locks.

  • Property mode set to 100644
File size: 10.2 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
71/** Futex protecting the list of unused structures. */
72static futex_t unused_futex = FUTEX_INITIALIZER;
73
74/** List of unused structures. */
75static LIST_INITIALIZE(unused_head);
76
77/** Futex protecting the up_hash and ui_hash. */
78static futex_t used_futex = FUTEX_INITIALIZER;
79
80/**
81 * Global hash table of all used fat_idx_t structures.
82 * The index structures are hashed by the dev_handle, parent node's first
83 * cluster and index within the parent directory.
84 */
85static hash_table_t up_hash;
86
87#define UPH_BUCKETS_LOG 12
88#define UPH_BUCKETS (1 << UPH_BUCKETS_LOG)
89
90#define UPH_DH_KEY 0
91#define UPH_PFC_KEY 1
92#define UPH_PDI_KEY 2
93
94static hash_index_t pos_hash(unsigned long key[])
95{
96 dev_handle_t dev_handle = (dev_handle_t)key[UPH_DH_KEY];
97 fat_cluster_t pfc = (fat_cluster_t)key[UPH_PFC_KEY];
98 unsigned pdi = (unsigned)key[UPH_PDI_KEY];
99
100 hash_index_t h;
101
102 /*
103 * The least significant half of all bits are the least significant bits
104 * of the parent node's first cluster.
105 *
106 * The least significant half of the most significant half of all bits
107 * are the least significant bits of the node's dentry index within the
108 * parent directory node.
109 *
110 * The most significant half of the most significant half of all bits
111 * are the least significant bits of the device handle.
112 */
113 h = pfc & ((1 << (UPH_BUCKETS_LOG / 2)) - 1);
114 h |= (pdi & ((1 << (UPH_BUCKETS_LOG / 4)) - 1)) <<
115 (UPH_BUCKETS_LOG / 2);
116 h |= (dev_handle & ((1 << (UPH_BUCKETS_LOG / 4)) - 1)) <<
117 (3 * (UPH_BUCKETS_LOG / 4));
118
119 return h;
120}
121
122static int pos_compare(unsigned long key[], hash_count_t keys, link_t *item)
123{
124 dev_handle_t dev_handle = (dev_handle_t)key[UPH_DH_KEY];
125 fat_cluster_t pfc = (fat_cluster_t)key[UPH_PFC_KEY];
126 unsigned pdi = (unsigned)key[UPH_PDI_KEY];
127 fat_idx_t *fidx = list_get_instance(item, fat_idx_t, uph_link);
128
129 return (dev_handle == fidx->dev_handle) && (pfc == fidx->pfc) &&
130 (pdi == fidx->pdi);
131}
132
133static void pos_remove_callback(link_t *item)
134{
135 /* nothing to do */
136}
137
138static hash_table_operations_t uph_ops = {
139 .hash = pos_hash,
140 .compare = pos_compare,
141 .remove_callback = pos_remove_callback,
142};
143
144/**
145 * Global hash table of all used fat_idx_t structures.
146 * The index structures are hashed by the dev_handle and index.
147 */
148static hash_table_t ui_hash;
149
150#define UIH_BUCKETS_LOG 12
151#define UIH_BUCKETS (1 << UIH_BUCKETS_LOG)
152
153#define UIH_DH_KEY 0
154#define UIH_INDEX_KEY 1
155
156static hash_index_t idx_hash(unsigned long key[])
157{
158 dev_handle_t dev_handle = (dev_handle_t)key[UIH_DH_KEY];
159 fs_index_t index = (fs_index_t)key[UIH_INDEX_KEY];
160
161 hash_index_t h;
162
163 h = dev_handle & ((1 << (UIH_BUCKETS_LOG / 2)) - 1);
164 h |= (index & ((1 << (UIH_BUCKETS_LOG / 2)) - 1)) <<
165 (UIH_BUCKETS_LOG / 2);
166
167 return h;
168}
169
170static int idx_compare(unsigned long key[], hash_count_t keys, link_t *item)
171{
172 dev_handle_t dev_handle = (dev_handle_t)key[UIH_DH_KEY];
173 fs_index_t index = (fs_index_t)key[UIH_INDEX_KEY];
174 fat_idx_t *fidx = list_get_instance(item, fat_idx_t, uih_link);
175
176 return (dev_handle == fidx->dev_handle) && (index == fidx->index);
177}
178
179static void idx_remove_callback(link_t *item)
180{
181 /* nothing to do */
182}
183
184static hash_table_operations_t uih_ops = {
185 .hash = idx_hash,
186 .compare = idx_compare,
187 .remove_callback = idx_remove_callback,
188};
189
190/** Allocate a VFS index which is not currently in use. */
191static bool fat_idx_alloc(dev_handle_t dev_handle, fs_index_t *index)
192{
193 link_t *l;
194 unused_t *u;
195
196 assert(index);
197 futex_down(&unused_futex);
198 for (l = unused_head.next; l != &unused_head; l = l->next) {
199 u = list_get_instance(l, unused_t, link);
200 if (u->dev_handle == dev_handle)
201 goto hit;
202 }
203 futex_up(&unused_futex);
204
205 /* dev_handle not found */
206 return false;
207
208hit:
209 if (list_empty(&u->freed_head)) {
210 if (u->remaining) {
211 /*
212 * There are no freed indices, allocate one directly
213 * from the counter.
214 */
215 *index = u->next++;
216 --u->remaining;
217 futex_up(&unused_futex);
218 return true;
219 }
220 } else {
221 /* There are some freed indices which we can reuse. */
222 freed_t *f = list_get_instance(u->freed_head.next, freed_t,
223 link);
224 *index = f->first;
225 if (f->first++ == f->last) {
226 /* Destroy the interval. */
227 list_remove(&f->link);
228 free(f);
229 }
230 futex_up(&unused_futex);
231 return true;
232 }
233 /*
234 * We ran out of indices, which is extremely unlikely with FAT16, but
235 * theoretically still possible (e.g. too many open unlinked nodes or
236 * too many zero-sized nodes).
237 */
238 futex_up(&unused_futex);
239 return false;
240}
241
242/** If possible, coalesce two intervals of freed indices. */
243static void try_coalesce_intervals(link_t *l, link_t *r, link_t *cur)
244{
245 freed_t *fl = list_get_instance(l, freed_t, link);
246 freed_t *fr = list_get_instance(r, freed_t, link);
247
248 if (fl->last + 1 == fr->first) {
249 if (cur == l) {
250 fl->last = fr->last;
251 list_remove(r);
252 free(r);
253 } else {
254 fr->first = fl->first;
255 list_remove(l);
256 free(l);
257 }
258 }
259}
260
261/** Free a VFS index, which is no longer in use. */
262static void fat_idx_free(dev_handle_t dev_handle, fs_index_t index)
263{
264 link_t *l;
265 unused_t *u;
266
267 futex_down(&unused_futex);
268 for (l = unused_head.next; l != &unused_head; l = l->next) {
269 u = list_get_instance(l, unused_t, link);
270 if (u->dev_handle == dev_handle)
271 goto hit;
272 }
273 futex_up(&unused_futex);
274
275 /* should not happen */
276 assert(0);
277
278hit:
279 if (u->next == index + 1) {
280 /* The index can be returned directly to the counter. */
281 u->next--;
282 u->remaining++;
283 } else {
284 /*
285 * The index must be returned either to an existing freed
286 * interval or a new interval must be created.
287 */
288 link_t *lnk;
289 freed_t *n;
290 for (lnk = u->freed_head.next; lnk != &u->freed_head;
291 lnk = lnk->next) {
292 freed_t *f = list_get_instance(lnk, freed_t, link);
293 if (f->first == index + 1) {
294 f->first--;
295 if (lnk->prev != &u->freed_head)
296 try_coalesce_intervals(lnk->prev, lnk,
297 lnk);
298 futex_up(&unused_futex);
299 return;
300 }
301 if (f->last == index - 1) {
302 f->last++;
303 if (lnk->next != &u->freed_head)
304 try_coalesce_intervals(lnk, lnk->next,
305 lnk);
306 futex_up(&unused_futex);
307 return;
308 }
309 if (index > f->first) {
310 n = malloc(sizeof(freed_t));
311 /* TODO: sleep until allocation succeeds */
312 assert(n);
313 link_initialize(&n->link);
314 n->first = index;
315 n->last = index;
316 list_insert_before(&n->link, lnk);
317 futex_up(&unused_futex);
318 return;
319 }
320
321 }
322 /* The index will form the last interval. */
323 n = malloc(sizeof(freed_t));
324 /* TODO: sleep until allocation succeeds */
325 assert(n);
326 link_initialize(&n->link);
327 n->first = index;
328 n->last = index;
329 list_append(&n->link, &u->freed_head);
330 }
331 futex_up(&unused_futex);
332}
333
334fat_idx_t *
335fat_idx_get_by_pos(dev_handle_t dev_handle, fat_cluster_t pfc, unsigned pdi)
336{
337 fat_idx_t *fidx;
338 link_t *l;
339 unsigned long pkey[] = {
340 [UPH_DH_KEY] = dev_handle,
341 [UPH_PFC_KEY] = pfc,
342 [UPH_PDI_KEY] = pdi,
343 };
344
345 futex_down(&used_futex);
346 l = hash_table_find(&up_hash, pkey);
347 if (l) {
348 fidx = hash_table_get_instance(l, fat_idx_t, uph_link);
349 } else {
350 fidx = (fat_idx_t *) malloc(sizeof(fat_idx_t));
351 if (!fidx) {
352 futex_up(&used_futex);
353 return NULL;
354 }
355 if (!fat_idx_alloc(dev_handle, &fidx->index)) {
356 free(fidx);
357 futex_up(&used_futex);
358 return NULL;
359 }
360
361 unsigned long ikey[] = {
362 [UIH_DH_KEY] = dev_handle,
363 [UIH_INDEX_KEY] = fidx->index,
364 };
365
366 link_initialize(&fidx->uph_link);
367 link_initialize(&fidx->uih_link);
368 futex_initialize(&fidx->lock, 1);
369 fidx->dev_handle = dev_handle;
370 fidx->pfc = pfc;
371 fidx->pdi = pdi;
372 fidx->nodep = NULL;
373
374 hash_table_insert(&up_hash, pkey, &fidx->uph_link);
375 hash_table_insert(&ui_hash, ikey, &fidx->uih_link);
376 }
377 futex_down(&fidx->lock);
378 futex_up(&used_futex);
379
380 return fidx;
381}
382
383fat_idx_t *
384fat_idx_get_by_index(dev_handle_t dev_handle, fs_index_t index)
385{
386 fat_idx_t *fidx = NULL;
387 link_t *l;
388 unsigned long ikey[] = {
389 [UIH_DH_KEY] = dev_handle,
390 [UIH_INDEX_KEY] = index,
391 };
392
393 futex_down(&used_futex);
394 l = hash_table_find(&ui_hash, ikey);
395 if (l) {
396 fidx = hash_table_get_instance(l, fat_idx_t, uih_link);
397 futex_down(&fidx->lock);
398 }
399 futex_up(&used_futex);
400
401 return fidx;
402}
403
Note: See TracBrowser for help on using the repository browser.