FS-Cache: Add cache tag handling
[safe/jmp/linux-2.6] / fs / fscache / cache.c
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 }