OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [rc203soc/] [sw/] [uClinux/] [fs/] [dcache.c] - Blame information for rev 1775

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1627 jcastillo
/*
2
 *  linux/fs/dcache.c
3
 *
4
 *  (C) Copyright 1994 Linus Torvalds
5
 */
6
 
7
/*
8
 * The directory cache is a "two-level" cache, each level doing LRU on
9
 * its entries.  Adding new entries puts them at the end of the LRU
10
 * queue on the first-level cache, while the second-level cache is
11
 * fed by any cache hits.
12
 *
13
 * The idea is that new additions (from readdir(), for example) will not
14
 * flush the cache of entries that have really been used.
15
 *
16
 * There is a global hash-table over both caches that hashes the entries
17
 * based on the directory inode number and device as well as on a
18
 * string-hash computed over the name.
19
 */
20
 
21
#include <linux/fs.h>
22
#include <linux/string.h>
23
 
24
/*
25
 * Don't bother caching long names.. They just take up space in the cache, and
26
 * for a name cache you just want to cache the "normal" names anyway which tend
27
 * to be short.
28
 */
29
#define DCACHE_NAME_LEN 15
30
#ifdef CONFIG_REDUCED_MEMORY
31
#define DCACHE_SIZE 128
32
#else /* !CONFIG_REDUCED_MEMORY */
33
#define DCACHE_SIZE 32
34
#endif /* !CONFIG_REDUCED_MEMORY */
35
 
36
struct hash_list {
37
        struct dir_cache_entry * next;
38
        struct dir_cache_entry * prev;
39
};
40
 
41
/*
42
 * The dir_cache_entry must be in this order: we do ugly things with the pointers
43
 */
44
struct dir_cache_entry {
45
        struct hash_list h;
46
        kdev_t dc_dev;
47
        unsigned long dir;
48
        unsigned long version;
49
        unsigned long ino;
50
        unsigned char name_len;
51
        char name[DCACHE_NAME_LEN];
52
        struct dir_cache_entry ** lru_head;
53
        struct dir_cache_entry * next_lru,  * prev_lru;
54
};
55
 
56
#define dcache_offset(x) ((unsigned long)&((struct dir_cache_entry*)0)->x)
57
#define dcache_datalen (dcache_offset(lru_head) - dcache_offset(dc_dev))
58
 
59
#define COPYDATA(de, newde) \
60
memcpy((void *) &newde->dc_dev, (void *) &de->dc_dev, dcache_datalen)
61
 
62
static struct dir_cache_entry level1_cache[DCACHE_SIZE];
63
static struct dir_cache_entry level2_cache[DCACHE_SIZE];
64
 
65
/*
66
 * The LRU-lists are doubly-linked circular lists, and do not change in size
67
 * so these pointers always have something to point to (after _init)
68
 */
69
static struct dir_cache_entry * level1_head;
70
static struct dir_cache_entry * level2_head;
71
 
72
/*
73
 * The hash-queues are also doubly-linked circular lists, but the head is
74
 * itself on the doubly-linked list, not just a pointer to the first entry.
75
 */
76
#define DCACHE_HASH_QUEUES 32
77
#define hash_fn(dev,dir,namehash) ((HASHDEV(dev) ^ (dir) ^ (namehash)) % DCACHE_HASH_QUEUES)
78
 
79
static struct hash_list hash_table[DCACHE_HASH_QUEUES];
80
 
81
static inline void remove_lru(struct dir_cache_entry * de)
82
{
83
        struct dir_cache_entry * next = de->next_lru;
84
        struct dir_cache_entry * prev = de->prev_lru;
85
 
86
        next->prev_lru = prev;
87
        prev->next_lru = next;
88
}
89
 
90
static inline void add_lru(struct dir_cache_entry * de, struct dir_cache_entry *head)
91
{
92
        struct dir_cache_entry * prev = head->prev_lru;
93
 
94
        de->next_lru = head;
95
        de->prev_lru = prev;
96
        prev->next_lru = de;
97
        head->prev_lru = de;
98
}
99
 
100
static inline void update_lru(struct dir_cache_entry * de)
101
{
102
        if (de == *de->lru_head)
103
                *de->lru_head = de->next_lru;
104
        else {
105
                remove_lru(de);
106
                add_lru(de,*de->lru_head);
107
        }
108
}
109
 
110
/*
111
 * Stupid name"hash" algorithm. Write something better if you want to,
112
 * but I doubt it matters that much
113
 */
114
static inline unsigned long namehash(const char * name, int len)
115
{
116
        return len +
117
                ((const unsigned char *) name)[0]+
118
                ((const unsigned char *) name)[len-1];
119
}
120
 
121
/*
122
 * Hash queue manipulation. Look out for the casts..
123
 */
124
static inline void remove_hash(struct dir_cache_entry * de)
125
{
126
        struct dir_cache_entry * next = de->h.next;
127
 
128
        if (next) {
129
                struct dir_cache_entry * prev = de->h.prev;
130
                next->h.prev = prev;
131
                prev->h.next = next;
132
                de->h.next = NULL;
133
        }
134
}
135
 
136
static inline void add_hash(struct dir_cache_entry * de, struct hash_list * hash)
137
{
138
        struct dir_cache_entry * next = hash->next;
139
        de->h.next = next;
140
        de->h.prev = (struct dir_cache_entry *) hash;
141
        next->h.prev = de;
142
        hash->next = de;
143
}
144
 
145
/*
146
 * Find a directory cache entry given all the necessary info.
147
 */
148
static inline struct dir_cache_entry * find_entry(struct inode * dir, const char * name, int len, struct hash_list * hash)
149
{
150
        struct dir_cache_entry * de = hash->next;
151
 
152
        for (de = hash->next ; de != (struct dir_cache_entry *) hash ; de = de->h.next) {
153
                if (de->dc_dev != dir->i_dev)
154
                        continue;
155
                if (de->dir != dir->i_ino)
156
                        continue;
157
                if (de->version != dir->i_version)
158
                        continue;
159
                if (de->name_len != len)
160
                        continue;
161
                if (memcmp(de->name, name, len))
162
                        continue;
163
                return de;
164
        }
165
        return NULL;
166
}
167
 
168
/*
169
 * Move a successfully used entry to level2. If already at level2,
170
 * move it to the end of the LRU queue..
171
 */
172
static inline void move_to_level2(struct dir_cache_entry * old_de, struct hash_list * hash)
173
{
174
        struct dir_cache_entry * de;
175
 
176
        if (old_de->lru_head == &level2_head) {
177
                update_lru(old_de);
178
                return;
179
        }
180
        de = level2_head;
181
        level2_head = de->next_lru;
182
        remove_hash(de);
183
        COPYDATA(old_de, de);
184
        add_hash(de, hash);
185
}
186
 
187
int dcache_lookup(struct inode * dir, const char * name, int len, unsigned long * ino)
188
{
189
        struct hash_list * hash;
190
        struct dir_cache_entry *de;
191
 
192
        if (len > DCACHE_NAME_LEN)
193
                return 0;
194
        hash = hash_table + hash_fn(dir->i_dev, dir->i_ino, namehash(name,len));
195
        de = find_entry(dir, name, len, hash);
196
        if (!de)
197
                return 0;
198
        *ino = de->ino;
199
        move_to_level2(de, hash);
200
        return 1;
201
}
202
 
203
void dcache_add(struct inode * dir, const char * name, int len, unsigned long ino)
204
{
205
        struct hash_list * hash;
206
        struct dir_cache_entry *de;
207
 
208
        if (len > DCACHE_NAME_LEN)
209
                return;
210
        hash = hash_table + hash_fn(dir->i_dev, dir->i_ino, namehash(name,len));
211
        if ((de = find_entry(dir, name, len, hash)) != NULL) {
212
                de->ino = ino;
213
                update_lru(de);
214
                return;
215
        }
216
        de = level1_head;
217
        level1_head = de->next_lru;
218
        remove_hash(de);
219
        de->dc_dev = dir->i_dev;
220
        de->dir = dir->i_ino;
221
        de->version = dir->i_version;
222
        de->ino = ino;
223
        de->name_len = len;
224
        memcpy(de->name, name, len);
225
        add_hash(de, hash);
226
}
227
 
228
unsigned long name_cache_init(unsigned long mem_start, unsigned long mem_end)
229
{
230
        int i;
231
        struct dir_cache_entry * p;
232
 
233
        /*
234
         * Init level1 LRU lists..
235
         */
236
        p = level1_cache;
237
        do {
238
                p[1].prev_lru = p;
239
                p[0].next_lru = p+1;
240
                p[0].lru_head = &level1_head;
241
        } while (++p < level1_cache + DCACHE_SIZE-1);
242
        level1_cache[0].prev_lru = p;
243
        p[0].next_lru = &level1_cache[0];
244
        p[0].lru_head = &level1_head;
245
        level1_head = level1_cache;
246
 
247
        /*
248
         * Init level2 LRU lists..
249
         */
250
        p = level2_cache;
251
        do {
252
                p[1].prev_lru = p;
253
                p[0].next_lru = p+1;
254
                p[0].lru_head = &level2_head;
255
        } while (++p < level2_cache + DCACHE_SIZE-1);
256
        level2_cache[0].prev_lru = p;
257
        p[0].next_lru = &level2_cache[0];
258
        p[0].lru_head = &level2_head;
259
        level2_head = level2_cache;
260
 
261
        /*
262
         * Empty hash queues..
263
         */
264
        for (i = 0 ; i < DCACHE_HASH_QUEUES ; i++)
265
                hash_table[i].next = hash_table[i].next =
266
                        (struct dir_cache_entry *) &hash_table[i];
267
        return mem_start;
268
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.