]>
Commit | Line | Data |
---|---|---|
0e04d4ce DH |
1 | /* FS-Cache cache handling |
2 | * | |
3 | * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. | |
4 | * Written by David Howells (dhowells@redhat.com) | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU General Public License | |
8 | * as published by the Free Software Foundation; either version | |
9 | * 2 of the License, or (at your option) any later version. | |
10 | */ | |
11 | ||
12 | #define FSCACHE_DEBUG_LEVEL CACHE | |
13 | #include <linux/module.h> | |
14 | #include <linux/slab.h> | |
15 | #include "internal.h" | |
16 | ||
17 | LIST_HEAD(fscache_cache_list); | |
18 | DECLARE_RWSEM(fscache_addremove_sem); | |
19 | ||
20 | static LIST_HEAD(fscache_cache_tag_list); | |
21 | ||
22 | /* | |
23 | * look up a cache tag | |
24 | */ | |
25 | struct fscache_cache_tag *__fscache_lookup_cache_tag(const char *name) | |
26 | { | |
27 | struct fscache_cache_tag *tag, *xtag; | |
28 | ||
29 | /* firstly check for the existence of the tag under read lock */ | |
30 | down_read(&fscache_addremove_sem); | |
31 | ||
32 | list_for_each_entry(tag, &fscache_cache_tag_list, link) { | |
33 | if (strcmp(tag->name, name) == 0) { | |
34 | atomic_inc(&tag->usage); | |
35 | up_read(&fscache_addremove_sem); | |
36 | return tag; | |
37 | } | |
38 | } | |
39 | ||
40 | up_read(&fscache_addremove_sem); | |
41 | ||
42 | /* the tag does not exist - create a candidate */ | |
43 | xtag = kzalloc(sizeof(*xtag) + strlen(name) + 1, GFP_KERNEL); | |
44 | if (!xtag) | |
45 | /* return a dummy tag if out of memory */ | |
46 | return ERR_PTR(-ENOMEM); | |
47 | ||
48 | atomic_set(&xtag->usage, 1); | |
49 | strcpy(xtag->name, name); | |
50 | ||
51 | /* write lock, search again and add if still not present */ | |
52 | down_write(&fscache_addremove_sem); | |
53 | ||
54 | list_for_each_entry(tag, &fscache_cache_tag_list, link) { | |
55 | if (strcmp(tag->name, name) == 0) { | |
56 | atomic_inc(&tag->usage); | |
57 | up_write(&fscache_addremove_sem); | |
58 | kfree(xtag); | |
59 | return tag; | |
60 | } | |
61 | } | |
62 | ||
63 | list_add_tail(&xtag->link, &fscache_cache_tag_list); | |
64 | up_write(&fscache_addremove_sem); | |
65 | return xtag; | |
66 | } | |
67 | ||
68 | /* | |
69 | * release a reference to a cache tag | |
70 | */ | |
71 | void __fscache_release_cache_tag(struct fscache_cache_tag *tag) | |
72 | { | |
73 | if (tag != ERR_PTR(-ENOMEM)) { | |
74 | down_write(&fscache_addremove_sem); | |
75 | ||
76 | if (atomic_dec_and_test(&tag->usage)) | |
77 | list_del_init(&tag->link); | |
78 | else | |
79 | tag = NULL; | |
80 | ||
81 | up_write(&fscache_addremove_sem); | |
82 | ||
83 | kfree(tag); | |
84 | } | |
85 | } | |
86 | ||
87 | /* | |
88 | * select a cache in which to store an object | |
89 | * - the cache addremove semaphore must be at least read-locked by the caller | |
90 | * - the object will never be an index | |
91 | */ | |
92 | struct fscache_cache *fscache_select_cache_for_object( | |
93 | struct fscache_cookie *cookie) | |
94 | { | |
95 | struct fscache_cache_tag *tag; | |
96 | struct fscache_object *object; | |
97 | struct fscache_cache *cache; | |
98 | ||
99 | _enter(""); | |
100 | ||
101 | if (list_empty(&fscache_cache_list)) { | |
102 | _leave(" = NULL [no cache]"); | |
103 | return NULL; | |
104 | } | |
105 | ||
106 | /* we check the parent to determine the cache to use */ | |
107 | spin_lock(&cookie->lock); | |
108 | ||
109 | /* the first in the parent's backing list should be the preferred | |
110 | * cache */ | |
111 | if (!hlist_empty(&cookie->backing_objects)) { | |
112 | object = hlist_entry(cookie->backing_objects.first, | |
113 | struct fscache_object, cookie_link); | |
114 | ||
115 | cache = object->cache; | |
116 | if (object->state >= FSCACHE_OBJECT_DYING || | |
117 | test_bit(FSCACHE_IOERROR, &cache->flags)) | |
118 | cache = NULL; | |
119 | ||
120 | spin_unlock(&cookie->lock); | |
121 | _leave(" = %p [parent]", cache); | |
122 | return cache; | |
123 | } | |
124 | ||
125 | /* the parent is unbacked */ | |
126 | if (cookie->def->type != FSCACHE_COOKIE_TYPE_INDEX) { | |
127 | /* cookie not an index and is unbacked */ | |
128 | spin_unlock(&cookie->lock); | |
129 | _leave(" = NULL [cookie ub,ni]"); | |
130 | return NULL; | |
131 | } | |
132 | ||
133 | spin_unlock(&cookie->lock); | |
134 | ||
135 | if (!cookie->def->select_cache) | |
136 | goto no_preference; | |
137 | ||
138 | /* ask the netfs for its preference */ | |
139 | tag = cookie->def->select_cache(cookie->parent->netfs_data, | |
140 | cookie->netfs_data); | |
141 | if (!tag) | |
142 | goto no_preference; | |
143 | ||
144 | if (tag == ERR_PTR(-ENOMEM)) { | |
145 | _leave(" = NULL [nomem tag]"); | |
146 | return NULL; | |
147 | } | |
148 | ||
149 | if (!tag->cache) { | |
150 | _leave(" = NULL [unbacked tag]"); | |
151 | return NULL; | |
152 | } | |
153 | ||
154 | if (test_bit(FSCACHE_IOERROR, &tag->cache->flags)) | |
155 | return NULL; | |
156 | ||
157 | _leave(" = %p [specific]", tag->cache); | |
158 | return tag->cache; | |
159 | ||
160 | no_preference: | |
161 | /* netfs has no preference - just select first cache */ | |
162 | cache = list_entry(fscache_cache_list.next, | |
163 | struct fscache_cache, link); | |
164 | _leave(" = %p [first]", cache); | |
165 | return cache; | |
166 | } |