]> bbs.cooldavid.org Git - net-next-2.6.git/blame - fs/fscache/cache.c
FS-Cache: Add cache tag handling
[net-next-2.6.git] / fs / fscache / cache.c
CommitLineData
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
17LIST_HEAD(fscache_cache_list);
18DECLARE_RWSEM(fscache_addremove_sem);
19
20static LIST_HEAD(fscache_cache_tag_list);
21
22/*
23 * look up a cache tag
24 */
25struct 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 */
71void __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 */
92struct 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
160no_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}